1
/* Copyright © 2005-2006 Roger Leigh <rleigh@debian.org>
3
* schroot is free software; you can redistribute it and/or modify it
4
* under the terms of the GNU General Public License as published by
5
* the Free Software Foundation; either version 2 of the License, or
6
* (at your option) any later version.
8
* schroot is distributed in the hope that it will be useful, but
9
* WITHOUT ANY WARRANTY; without even the implied warranty of
10
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11
* General Public License for more details.
13
* You should have received a copy of the GNU General Public License
14
* along with this program; if not, write to the Free Software
15
* Foundation, Inc., 59 Temple Place, Suite 330, Boston,
18
*********************************************************************/
22
#include "sbuild-auth.h"
23
#include "sbuild-auth-conv.h"
24
#include "sbuild-auth-conv-tty.h"
35
#include <boost/format.hpp>
40
using namespace sbuild;
45
typedef std::pair<sbuild::auth::error_code,const char *> emap;
48
* This is a list of the supported error codes. It's used to
49
* construct the real error codes map.
53
emap(auth::HOSTNAME, N_("Failed to get hostname")),
54
emap(auth::USER, N_("User not found")),
55
emap(auth::AUTHENTICATION, N_("Authentication failed")),
56
emap(auth::AUTHORISATION, N_("Access not authorised")),
57
emap(auth::PAM_DOUBLE_INIT, N_("PAM is already initialised")),
58
emap(auth::PAM, N_("PAM error"))
64
custom_error<auth::error_code>::map_type
65
custom_error<auth::error_code>::error_strings
67
init_errors + (sizeof(init_errors) / sizeof(init_errors[0])));
72
/* This is the glue to link PAM user interaction with auth_conv. */
74
auth_conv_hook (int num_msg,
75
const struct pam_message **msgm,
76
struct pam_response **response,
82
auth_conv *conv = static_cast<auth_conv *>(appdata_ptr);
85
/* Construct a message vector */
86
auth_conv::message_list messages;
87
for (int i = 0; i < num_msg; ++i)
89
const struct pam_message *source = msgm[i];
92
message(static_cast<auth_message::message_type>(source->msg_style),
94
messages.push_back(message);
97
/* Do the conversation */
98
bool status = conv->conversation(messages);
102
/* Copy response into **reponse */
103
struct pam_response *reply =
104
static_cast<struct pam_response *>
105
(malloc(sizeof(struct pam_response) * num_msg));
107
for (int i = 0; i < num_msg; ++i)
109
reply[i].resp_retcode = 0;
110
reply[i].resp = strdup(messages[i].response.c_str());
125
auth::auth (std::string const& service_name):
127
service(service_name),
137
conv(dynamic_cast<auth_conv *>(new auth_conv_tty)),
138
message_verbosity(VERBOSITY_NORMAL)
140
this->ruid = getuid();
141
struct passwd *pwent = getpwuid(this->ruid);
144
// TODO: Convert to using a lexical cast.
145
std::ostringstream str;
147
throw error(str.str(), USER, errno);
149
this->ruser = pwent->pw_name;
151
/* By default, the auth user is the same as the remote user. */
152
set_user(this->ruser);
168
auth::get_service () const
170
return this->service;
174
auth::get_uid () const
180
auth::get_gid () const
186
auth::get_user () const
192
auth::set_user (std::string const& user)
197
this->shell = "/bin/false";
201
struct passwd *pwent = getpwnam(this->user.c_str());
204
throw error(user, USER, errno);
206
this->uid = pwent->pw_uid;
207
this->gid = pwent->pw_gid;
208
this->home = pwent->pw_dir;
209
this->shell = pwent->pw_shell;
210
log_debug(DEBUG_INFO)
211
<< format("auth uid = %1%, gid = %2%") % this->uid % this->gid
216
auth::get_command () const
218
return this->command;
222
auth::set_command (string_list const& command)
224
this->command = command;
228
auth::get_home () const
234
auth::get_shell () const
240
auth::get_environment () const
242
return this->user_environment;
246
auth::set_environment (char **environment)
248
set_environment(sbuild::environment(environment));
252
auth::set_environment (environment const& environment)
254
this->user_environment = environment;
258
auth::get_pam_environment () const
260
return environment(pam_getenvlist(this->pam));
264
auth::get_ruid () const
270
auth::get_ruser () const
276
auth::get_verbosity () const
278
return this->message_verbosity;
282
auth::set_verbosity (auth::verbosity verbosity)
284
this->message_verbosity = verbosity;
294
auth::set_conv (conv_ptr& conv)
312
const char *authuser = 0;
313
const void *tmpcast = static_cast<const void *>(authuser);
314
pam_get_item(this->pam, PAM_USER, &tmpcast);
315
log_debug(DEBUG_INFO)
316
<< format("PAM authentication succeeded for user %1%") % authuser
321
/* The session is now finished, either
322
successfully or not. All PAM operations are
323
now for cleanup and shutdown, and we must
324
clean up whether or not errors were raised at
325
any previous point. This means only the
326
first error is reported back to the user. */
328
/* Don't cope with failure, since we are now
329
already bailing out, and an error may already
332
catch (error const& e)
338
catch (error const& discard)
344
catch (error const& e)
348
/* Don't cope with failure, since we are now already bailing out,
349
and an error may already have been raised */
352
catch (error const& discard)
362
assert(!this->user.empty());
366
log_debug(DEBUG_CRITICAL)
367
<< "pam_start FAIL (already initialised)" << endl;
368
throw error("Init PAM", PAM_DOUBLE_INIT);
371
struct pam_conv conv_hook =
374
static_cast<void *>(this->conv.get())
380
pam_start(this->service.c_str(), this->user.c_str(),
381
&conv_hook, &this->pam)) != PAM_SUCCESS)
383
log_debug(DEBUG_WARNING) << "pam_start FAIL" << endl;
384
throw error(PAM, pam_strerror(pam_status));
387
log_debug(DEBUG_NOTICE) << "pam_start OK" << endl;
393
if (this->pam); // PAM must be initialised
398
pam_end(this->pam, PAM_SUCCESS)) != PAM_SUCCESS)
400
log_debug(DEBUG_WARNING) << "pam_end FAIL" << endl;
401
throw error(PAM, pam_strerror(pam_status));
405
log_debug(DEBUG_NOTICE) << "pam_end OK" << endl;
410
auth::authenticate ()
412
assert(!this->user.empty());
413
assert(this->pam != 0); // PAM must be initialised
418
pam_set_item(this->pam, PAM_RUSER, this->ruser.c_str())) != PAM_SUCCESS)
420
log_debug(DEBUG_WARNING) << "pam_set_item (PAM_RUSER) FAIL" << endl;
421
throw error(_("Set RUSER"), PAM, pam_strerror(pam_status));
424
long hl = 256; /* sysconf(_SC_HOST_NAME_MAX); BROKEN with Debian libc6 2.3.2.ds1-22 */
426
char *hostname = new char[hl];
427
if (gethostname(hostname, hl) != 0)
429
log_debug(DEBUG_CRITICAL) << "gethostname FAIL" << endl;
430
throw error(HOSTNAME, errno);
434
pam_set_item(this->pam, PAM_RHOST, hostname)) != PAM_SUCCESS)
436
log_debug(DEBUG_WARNING) << "pam_set_item (PAM_RHOST) FAIL" << endl;
437
throw error(_("Set RHOST"), PAM, pam_strerror(pam_status));
443
const char *tty = ttyname(STDIN_FILENO);
447
pam_set_item(this->pam, PAM_TTY, tty)) != PAM_SUCCESS)
449
log_debug(DEBUG_WARNING) << "pam_set_item (PAM_TTY) FAIL" << endl;
450
throw error(_("Set TTY"), PAM, pam_strerror(pam_status));
454
/* Authenticate as required. */
455
switch (get_auth_status())
458
if ((pam_status = pam_set_item(this->pam, PAM_USER, this->user.c_str()))
461
log_debug(DEBUG_WARNING) << "pam_set_item (PAM_USER) FAIL" << endl;
462
throw error(_("Set USER"), PAM, pam_strerror(pam_status));
467
if ((pam_status = pam_authenticate(this->pam, 0)) != PAM_SUCCESS)
469
log_debug(DEBUG_INFO) << "pam_authenticate FAIL" << endl;
470
syslog(LOG_AUTH|LOG_WARNING, "%s->%s Authentication failure",
471
this->ruser.c_str(), this->user.c_str());
472
throw error(AUTHENTICATION, pam_strerror(pam_status));
474
log_debug(DEBUG_NOTICE) << "pam_authenticate OK" << endl;
479
log_debug(DEBUG_INFO) << "PAM auth premature FAIL" << endl;
480
cerr << format(_("You do not have permission to access the %1% service."))
483
<< _("This failure will be reported.")
485
syslog(LOG_AUTH|LOG_WARNING,
486
"%s->%s Unauthorised",
487
this->ruser.c_str(), this->user.c_str());
488
throw error(AUTHORISATION);
498
assert(this->pam != 0); // PAM must be initialised
502
environment environment;
503
if (!this->user_environment.empty())
504
environment = this->user_environment;
506
// For security, PATH is always set to a sane state for root, but
507
// only set in other cases if not preserving the environment.
509
environment.add(std::make_pair("PATH", "/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/bin/X11"));
510
else if (this->user_environment.empty())
511
environment.add(std::make_pair("PATH", "/usr/local/bin:/usr/bin:/bin:/usr/bin/X11:/usr/games"));
513
if (this->user_environment.empty())
515
if (!this->home.empty() )
516
environment.add(std::make_pair("HOME", this->home));
518
environment.add(std::make_pair("HOME", "/"));
519
if (!this->user.empty())
521
environment.add(std::make_pair("LOGNAME", this->user));
522
environment.add(std::make_pair("USER", this->user));
525
const char *term = getenv("TERM");
527
environment.add(std::make_pair("TERM", term));
529
if (!this->shell.empty())
530
environment.add(std::make_pair("SHELL", this->shell));
533
// Sanitise environment.
534
environment.remove("BASH_ENV");
535
environment.remove("CDPATH");
536
environment.remove("ENV");
537
environment.remove("HOSTALIASES");
538
environment.remove("IFS");
539
environment.remove("KRB5_CONFIG");
540
environment.remove("KRBCONFDIR");
541
environment.remove("KRBTKFILE");
542
environment.remove("KRB_CONF");
543
environment.remove("LOCALDOMAIN");
544
environment.remove("NLSPATH");
545
environment.remove("PATH_LOCALE");
546
environment.remove("RES_OPTIONS");
547
environment.remove("TERMINFO");
548
environment.remove("TERMINFO_DIRS");
549
environment.remove("TERMPATH");
551
// Find and remove LD_.*,
553
for (environment::const_iterator cur = environment.begin();
554
cur != environment.end();)
556
environment::const_iterator next = cur;
559
if (cur->first.substr(0,3) == "LD_")
560
environment.remove(cur->first);
565
// Move into PAM environment.
566
for (environment::const_iterator cur = environment.begin();
567
cur != environment.end();
570
std::string env_string = cur->first + "=" + cur->second;
572
pam_putenv(this->pam, env_string.c_str())) != PAM_SUCCESS)
574
log_debug(DEBUG_WARNING) << "pam_putenv FAIL" << endl;
575
throw error(PAM, pam_strerror(pam_status));
577
log_debug(DEBUG_INFO)
578
<< format("pam_putenv: set %1%=%2%") % cur->first % cur->second
582
log_debug(DEBUG_NOTICE) << "pam_putenv OK" << endl;
588
assert(this->pam != 0); // PAM must be initialised
593
pam_acct_mgmt(this->pam, 0)) != PAM_SUCCESS)
595
/* We don't handle changing expired passwords here, since we are
597
log_debug(DEBUG_WARNING) << "pam_acct_mgmt FAIL" << endl;
598
throw error(PAM, pam_strerror(pam_status));
601
log_debug(DEBUG_NOTICE) << "pam_acct_mgmt OK" << endl;
605
auth::cred_establish ()
607
assert(this->pam != 0); // PAM must be initialised
612
pam_setcred(this->pam, PAM_ESTABLISH_CRED)) != PAM_SUCCESS)
614
log_debug(DEBUG_WARNING) << "pam_setcred FAIL" << endl;
615
throw error(PAM, pam_strerror(pam_status));
618
log_debug(DEBUG_NOTICE) << "pam_setcred OK" << endl;
624
assert(this->pam != 0); // PAM must be initialised
629
pam_setcred(this->pam, PAM_DELETE_CRED)) != PAM_SUCCESS)
631
log_debug(DEBUG_WARNING) << "pam_setcred (delete) FAIL" << endl;
632
throw error(PAM, pam_strerror(pam_status));
635
log_debug(DEBUG_NOTICE) << "pam_setcred (delete) OK" << endl;
639
auth::open_session ()
641
assert(this->pam != 0); // PAM must be initialised
646
pam_open_session(this->pam, 0)) != PAM_SUCCESS)
648
log_debug(DEBUG_WARNING) << "pam_open_session FAIL" << endl;
649
throw error(PAM, pam_strerror(pam_status));
652
log_debug(DEBUG_NOTICE) << "pam_open_session OK" << endl;
656
auth::close_session ()
658
assert(this->pam != 0); // PAM must be initialised
663
pam_close_session(this->pam, 0)) != PAM_SUCCESS)
665
log_debug(DEBUG_WARNING) << "pam_close_session FAIL" << endl;
666
throw error(PAM, pam_strerror(pam_status));
669
log_debug(DEBUG_NOTICE) << "pam_close_session OK" << endl;
673
auth::get_auth_status () const
675
status authtype = STATUS_NONE;
677
authtype = change_auth(authtype, STATUS_USER);
683
auth::pam_strerror (int pam_error)
685
return ::pam_strerror (this->pam, pam_error);