sbuild-auth.cc

Go to the documentation of this file.
00001 /* Copyright © 2005-2006  Roger Leigh <rleigh@debian.org>
00002  *
00003  * schroot is free software; you can redistribute it and/or modify it
00004  * under the terms of the GNU General Public License as published by
00005  * the Free Software Foundation; either version 2 of the License, or
00006  * (at your option) any later version.
00007  *
00008  * schroot is distributed in the hope that it will be useful, but
00009  * WITHOUT ANY WARRANTY; without even the implied warranty of
00010  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00011  * General Public License for more details.
00012  *
00013  * You should have received a copy of the GNU General Public License
00014  * along with this program; if not, write to the Free Software
00015  * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
00016  * MA  02111-1307  USA
00017  *
00018  *********************************************************************/
00019 
00020 #include <config.h>
00021 
00022 #include "sbuild-auth.h"
00023 #include "sbuild-auth-conv.h"
00024 #include "sbuild-auth-conv-tty.h"
00025 
00026 #include <cassert>
00027 #include <cerrno>
00028 #include <cstdlib>
00029 #include <cstring>
00030 #include <iostream>
00031 #include <sstream>
00032 
00033 #include <syslog.h>
00034 
00035 #include <boost/format.hpp>
00036 
00037 using std::cerr;
00038 using std::endl;
00039 using boost::format;
00040 using namespace sbuild;
00041 
00042 namespace
00043 {
00044 
00045   typedef std::pair<sbuild::auth::error_code,const char *> emap;
00046 
00051   emap init_errors[] =
00052     {
00053       emap(auth::HOSTNAME,        N_("Failed to get hostname")),
00054       // TRANSLATORS: %1% = user name or user ID
00055       emap(auth::USER,            N_("User '%1%' not found")),
00056       emap(auth::AUTHENTICATION,  N_("Authentication failed")),
00057       emap(auth::AUTHORISATION,   N_("Access not authorised")),
00058       emap(auth::PAM_DOUBLE_INIT, N_("PAM is already initialised")),
00059       emap(auth::PAM,             N_("PAM error"))
00060     };
00061 
00062 }
00063 
00064 template<>
00065 error<auth::error_code>::map_type
00066 error<auth::error_code>::error_strings
00067 (init_errors,
00068  init_errors + (sizeof(init_errors) / sizeof(init_errors[0])));
00069 
00070 namespace
00071 {
00072 
00073   /* This is the glue to link PAM user interaction with auth_conv. */
00074   int
00075   auth_conv_hook (int                        num_msg,
00076                   const struct pam_message **msgm,
00077                   struct pam_response      **response,
00078                   void                      *appdata_ptr)
00079   {
00080     log_debug(DEBUG_NOTICE) << "PAM conversation hook started" << endl;
00081 
00082     try
00083       {
00084         if (appdata_ptr == 0)
00085           return PAM_CONV_ERR;
00086 
00087         auth_conv *conv = static_cast<auth_conv *>(appdata_ptr);
00088         assert (conv != 0);
00089 
00090         /* Construct a message vector */
00091         auth_conv::message_list messages;
00092         for (int i = 0; i < num_msg; ++i)
00093           {
00094             const struct pam_message *source = msgm[i];
00095 
00096             auth_message
00097               message(static_cast<auth_message::message_type>(source->msg_style),
00098                       source->msg);
00099             messages.push_back(message);
00100           }
00101 
00102         /* Do the conversation; an exception will be thrown on failure */
00103         conv->conversation(messages);
00104 
00105         /* Copy response into **reponse */
00106         struct pam_response *reply =
00107           static_cast<struct pam_response *>
00108           (malloc(sizeof(struct pam_response) * num_msg));
00109 
00110         for (int i = 0; i < num_msg; ++i)
00111           {
00112             reply[i].resp_retcode = 0;
00113             reply[i].resp = strdup(messages[i].response.c_str());
00114           }
00115 
00116         *response = reply;
00117         reply = 0;
00118 
00119         return PAM_SUCCESS;
00120       }
00121     catch (std::exception const& e)
00122       {
00123         sbuild::log_exception_error(e);
00124       }
00125     catch (...)
00126       {
00127         sbuild::log_error() << _("An unknown exception occurred") << endl;
00128       }
00129 
00130     return PAM_CONV_ERR;
00131   }
00132 
00133 }
00134 
00135 
00136 auth::auth (std::string const& service_name):
00137   pam(),
00138   service(service_name),
00139   uid(0),
00140   gid(0),
00141   user(),
00142   command(),
00143   home(),
00144   wd(),
00145   shell(),
00146   user_environment(),
00147   ruid(),
00148   ruser(),
00149   conv(new auth_conv_tty),
00150   message_verbosity(VERBOSITY_NORMAL)
00151 {
00152   this->ruid = getuid();
00153   struct passwd *pwent = getpwuid(this->ruid);
00154   if (pwent == 0)
00155     {
00156       if (errno)
00157         throw error(this->ruid, USER, strerror(errno));
00158       else
00159         throw error(this->ruid, USER);
00160     }
00161   this->ruser = pwent->pw_name;
00162 
00163   /* By default, the auth user is the same as the remote user. */
00164   set_user(this->ruser);
00165 }
00166 
00167 auth::~auth ()
00168 {
00169   // Shutdown PAM.
00170   try
00171     {
00172       stop();
00173     }
00174   catch (error const& e)
00175     {
00176       sbuild::log_exception_error(e);
00177     }
00178 }
00179 
00180 std::string const&
00181 auth::get_service () const
00182 {
00183   return this->service;
00184 }
00185 
00186 uid_t
00187 auth::get_uid () const
00188 {
00189   return this->uid;
00190 }
00191 
00192 gid_t
00193 auth::get_gid () const
00194 {
00195   return this->gid;
00196 }
00197 
00198 std::string const&
00199 auth::get_user () const
00200 {
00201   return this->user;
00202 }
00203 
00204 void
00205 auth::set_user (std::string const& user)
00206 {
00207   this->uid = getuid();
00208   this->gid = getgid();
00209   this->home = "/";
00210   this->shell = "/bin/false";
00211 
00212   this->user = user;
00213 
00214   struct passwd *pwent = getpwnam(this->user.c_str());
00215   if (pwent == 0)
00216     {
00217       if (errno)
00218         throw error(user, USER, strerror(errno));
00219       else
00220         throw error(user, USER);
00221     }
00222   this->uid = pwent->pw_uid;
00223   this->gid = pwent->pw_gid;
00224   this->home = pwent->pw_dir;
00225   this->shell = pwent->pw_shell;
00226   log_debug(DEBUG_INFO)
00227     << format("auth uid = %1%, gid = %2%") % this->uid % this->gid
00228     << endl;
00229 }
00230 
00231 string_list const&
00232 auth::get_command () const
00233 {
00234   return this->command;
00235 }
00236 
00237 void
00238 auth::set_command (string_list const& command)
00239 {
00240   this->command = command;
00241 }
00242 
00243 std::string const&
00244 auth::get_home () const
00245 {
00246   return this->home;
00247 }
00248 
00249 std::string const&
00250 auth::get_wd () const
00251 {
00252   return this->wd;
00253 }
00254 
00255 void
00256 auth::set_wd (std::string const& wd)
00257 {
00258   this->wd = wd;
00259 }
00260 
00261 std::string const&
00262 auth::get_shell () const
00263 {
00264   return this->shell;
00265 }
00266 
00267 environment const&
00268 auth::get_environment () const
00269 {
00270   return this->user_environment;
00271 }
00272 
00273 void
00274 auth::set_environment (char **environment)
00275 {
00276   set_environment(sbuild::environment(environment));
00277 }
00278 
00279 void
00280 auth::set_environment (environment const& environment)
00281 {
00282   this->user_environment = environment;
00283 }
00284 
00285 environment
00286 auth::get_pam_environment () const
00287 {
00288   return environment(pam_getenvlist(this->pam));
00289 }
00290 
00291 uid_t
00292 auth::get_ruid () const
00293 {
00294   return this->ruid;
00295 }
00296 
00297 std::string const&
00298 auth::get_ruser () const
00299 {
00300   return this->ruser;
00301 }
00302 
00303 auth::verbosity
00304 auth::get_verbosity () const
00305 {
00306   return this->message_verbosity;
00307 }
00308 
00309 void
00310 auth::set_verbosity (auth::verbosity verbosity)
00311 {
00312   this->message_verbosity = verbosity;
00313 }
00314 
00315 auth::conv_ptr&
00316 auth::get_conv ()
00317 {
00318   return this->conv;
00319 }
00320 
00321 void
00322 auth::set_conv (conv_ptr& conv)
00323 {
00324   this->conv = conv;
00325 }
00326 
00327 void
00328 auth::run ()
00329 {
00330   try
00331     {
00332       start();
00333       authenticate();
00334       setupenv();
00335       account();
00336       try
00337         {
00338           cred_establish();
00339 
00340           const char *authuser = 0;
00341           const void *tmpcast = reinterpret_cast<const void *>(authuser);
00342           pam_get_item(this->pam, PAM_USER, &tmpcast);
00343           log_debug(DEBUG_INFO)
00344             << format("PAM authentication succeeded for user %1%") % authuser
00345             << endl;
00346 
00347           run_impl();
00348 
00349           /* The session is now finished, either
00350              successfully or not.  All PAM operations are
00351              now for cleanup and shutdown, and we must
00352              clean up whether or not errors were raised at
00353              any previous point.  This means only the
00354              first error is reported back to the user. */
00355 
00356           /* Don't cope with failure, since we are now
00357              already bailing out, and an error may already
00358              have been raised */
00359         }
00360       catch (error const& e)
00361         {
00362           try
00363             {
00364               cred_delete();
00365             }
00366           catch (error const& discard)
00367             {
00368             }
00369           throw;
00370         }
00371       cred_delete();
00372     }
00373   catch (error const& e)
00374     {
00375       try
00376         {
00377           /* Don't cope with failure, since we are now already bailing out,
00378              and an error may already have been raised */
00379           stop();
00380         }
00381       catch (error const& discard)
00382         {
00383         }
00384       throw;
00385     }
00386   stop();
00387 }
00388 
00389 void
00390 auth::start ()
00391 {
00392   assert(!this->user.empty());
00393 
00394   if (this->pam != 0)
00395     {
00396       log_debug(DEBUG_CRITICAL)
00397         << "pam_start FAIL (already initialised)" << endl;
00398       throw error("Init PAM", PAM_DOUBLE_INIT);
00399     }
00400 
00401   struct pam_conv conv_hook =
00402     {
00403       auth_conv_hook,
00404       reinterpret_cast<void *>(this->conv.get())
00405     };
00406 
00407   int pam_status;
00408 
00409   if ((pam_status =
00410        pam_start(this->service.c_str(), this->user.c_str(),
00411                  &conv_hook, &this->pam)) != PAM_SUCCESS)
00412     {
00413       log_debug(DEBUG_WARNING) << "pam_start FAIL" << endl;
00414       throw error(PAM, pam_strerror(pam_status));
00415     }
00416 
00417   log_debug(DEBUG_NOTICE) << "pam_start OK" << endl;
00418 }
00419 
00420 void
00421 auth::stop ()
00422 {
00423   if (this->pam); // PAM must be initialised
00424   {
00425     int pam_status;
00426 
00427     if ((pam_status =
00428          pam_end(this->pam, PAM_SUCCESS)) != PAM_SUCCESS)
00429       {
00430         log_debug(DEBUG_WARNING) << "pam_end FAIL" << endl;
00431         throw error(PAM, pam_strerror(pam_status));
00432       }
00433 
00434     this->pam = 0;
00435     log_debug(DEBUG_NOTICE) << "pam_end OK" << endl;
00436   }
00437 }
00438 
00439 void
00440 auth::authenticate ()
00441 {
00442   assert(!this->user.empty());
00443   assert(this->pam != 0); // PAM must be initialised
00444 
00445   int pam_status;
00446 
00447   if ((pam_status =
00448        pam_set_item(this->pam, PAM_RUSER, this->ruser.c_str())) != PAM_SUCCESS)
00449     {
00450       log_debug(DEBUG_WARNING) << "pam_set_item (PAM_RUSER) FAIL" << endl;
00451       throw error(_("Set RUSER"), PAM, pam_strerror(pam_status));
00452     }
00453 
00454   long hl = 256; /* sysconf(_SC_HOST_NAME_MAX); BROKEN with Debian libc6 2.3.2.ds1-22 */
00455 
00456   char *hostname = new char[hl];
00457   try
00458     {
00459       if (gethostname(hostname, hl) != 0)
00460         {
00461           log_debug(DEBUG_CRITICAL) << "gethostname FAIL" << endl;
00462           throw error(HOSTNAME, strerror(errno));
00463         }
00464 
00465       if ((pam_status =
00466            pam_set_item(this->pam, PAM_RHOST, hostname)) != PAM_SUCCESS)
00467         {
00468           log_debug(DEBUG_WARNING) << "pam_set_item (PAM_RHOST) FAIL" << endl;
00469           throw error(_("Set RHOST"), PAM, pam_strerror(pam_status));
00470         }
00471     }
00472   catch (error const& e)
00473     {
00474       delete[] hostname;
00475       hostname = 0;
00476       throw;
00477     }
00478   delete[] hostname;
00479   hostname = 0;
00480 
00481   const char *tty = ttyname(STDIN_FILENO);
00482   if (tty)
00483     {
00484       if ((pam_status =
00485            pam_set_item(this->pam, PAM_TTY, tty)) != PAM_SUCCESS)
00486         {
00487           log_debug(DEBUG_WARNING) << "pam_set_item (PAM_TTY) FAIL" << endl;
00488           throw error(_("Set TTY"), PAM, pam_strerror(pam_status));
00489         }
00490     }
00491 
00492   /* Authenticate as required. */
00493   switch (get_auth_status())
00494     {
00495     case STATUS_NONE:
00496       if ((pam_status = pam_set_item(this->pam, PAM_USER, this->user.c_str()))
00497           != PAM_SUCCESS)
00498         {
00499           log_debug(DEBUG_WARNING) << "pam_set_item (PAM_USER) FAIL" << endl;
00500           throw error(_("Set USER"), PAM, pam_strerror(pam_status));
00501         }
00502       break;
00503 
00504     case STATUS_USER:
00505       if ((pam_status = pam_authenticate(this->pam, 0)) != PAM_SUCCESS)
00506         {
00507           log_debug(DEBUG_INFO) << "pam_authenticate FAIL" << endl;
00508           syslog(LOG_AUTH|LOG_WARNING, "%s->%s Authentication failure",
00509                  this->ruser.c_str(), this->user.c_str());
00510           throw error(AUTHENTICATION, pam_strerror(pam_status));
00511         }
00512       log_debug(DEBUG_NOTICE) << "pam_authenticate OK" << endl;
00513       break;
00514 
00515     case STATUS_FAIL:
00516         {
00517           log_debug(DEBUG_INFO) << "PAM auth premature FAIL" << endl;
00518           syslog(LOG_AUTH|LOG_WARNING,
00519                  "%s->%s Unauthorised",
00520                  this->ruser.c_str(), this->user.c_str());
00521           error e(AUTHORISATION);
00522           // TRANSLATORS: %1% = program name (PAM service name)
00523           std::string reason(_("You do not have permission to access the %1% service."));
00524           reason += '\n';
00525           reason += _("This failure will be reported.");
00526           format fmt(reason);
00527           fmt % this->service;
00528           e.set_reason(fmt.str());
00529           throw e;
00530         }
00531     default:
00532       break;
00533     }
00534 }
00535 
00536 void
00537 auth::setupenv ()
00538 {
00539   assert(this->pam != 0); // PAM must be initialised
00540 
00541   int pam_status;
00542 
00543   environment environment;
00544   if (!this->user_environment.empty())
00545     environment = this->user_environment;
00546 
00547   // For security, PATH is always set to a sane state for root, but
00548   // only set in other cases if not preserving the environment.
00549   if (this->uid == 0)
00550     environment.add(std::make_pair("PATH", "/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/bin/X11"));
00551   else if (this->user_environment.empty())
00552     environment.add(std::make_pair("PATH", "/usr/local/bin:/usr/bin:/bin:/usr/bin/X11:/usr/games"));
00553 
00554   if (this->user_environment.empty())
00555     {
00556       if (!this->home.empty() )
00557         environment.add(std::make_pair("HOME", this->home));
00558       else
00559         environment.add(std::make_pair("HOME", "/"));
00560       if (!this->user.empty())
00561         {
00562           environment.add(std::make_pair("LOGNAME", this->user));
00563           environment.add(std::make_pair("USER", this->user));
00564         }
00565       {
00566         const char *term = getenv("TERM");
00567         if (term)
00568           environment.add(std::make_pair("TERM", term));
00569       }
00570       if (!this->shell.empty())
00571         environment.add(std::make_pair("SHELL", this->shell));
00572     }
00573 
00574   // Sanitise environment.
00575   environment.remove("BASH_ENV");
00576   environment.remove("CDPATH");
00577   environment.remove("ENV");
00578   environment.remove("HOSTALIASES");
00579   environment.remove("IFS");
00580   environment.remove("KRB5_CONFIG");
00581   environment.remove("KRBCONFDIR");
00582   environment.remove("KRBTKFILE");
00583   environment.remove("KRB_CONF");
00584   environment.remove("LOCALDOMAIN");
00585   environment.remove("NLSPATH");
00586   environment.remove("PATH_LOCALE");
00587   environment.remove("RES_OPTIONS");
00588   environment.remove("TERMINFO");
00589   environment.remove("TERMINFO_DIRS");
00590   environment.remove("TERMPATH");
00591 
00592   // Find and remove LD_.*,
00593   string_list ldvars;
00594   for (environment::const_iterator cur = environment.begin();
00595        cur != environment.end();)
00596     {
00597       environment::const_iterator next = cur;
00598       next++;
00599 
00600       if (cur->first.substr(0,3) == "LD_")
00601         environment.remove(cur->first);
00602 
00603       cur = next;
00604     }
00605 
00606   // Move into PAM environment.
00607   for (environment::const_iterator cur = environment.begin();
00608        cur != environment.end();
00609        ++cur)
00610     {
00611       std::string env_string = cur->first + "=" + cur->second;
00612       if ((pam_status =
00613            pam_putenv(this->pam, env_string.c_str())) != PAM_SUCCESS)
00614         {
00615           log_debug(DEBUG_WARNING) << "pam_putenv FAIL" << endl;
00616           throw error(PAM, pam_strerror(pam_status));
00617         }
00618       log_debug(DEBUG_INFO)
00619         << format("pam_putenv: set %1%=%2%") % cur->first % cur->second
00620         << endl;
00621     }
00622 
00623   log_debug(DEBUG_NOTICE) << "pam_putenv OK" << endl;
00624 }
00625 
00626 void
00627 auth::account ()
00628 {
00629   assert(this->pam != 0); // PAM must be initialised
00630 
00631   int pam_status;
00632 
00633   if ((pam_status =
00634        pam_acct_mgmt(this->pam, 0)) != PAM_SUCCESS)
00635     {
00636       /* We don't handle changing expired passwords here, since we are
00637          not login or ssh. */
00638       log_debug(DEBUG_WARNING) << "pam_acct_mgmt FAIL" << endl;
00639       throw error(PAM, pam_strerror(pam_status));
00640     }
00641 
00642   log_debug(DEBUG_NOTICE) << "pam_acct_mgmt OK" << endl;
00643 }
00644 
00645 void
00646 auth::cred_establish ()
00647 {
00648   assert(this->pam != 0); // PAM must be initialised
00649 
00650   int pam_status;
00651 
00652   if ((pam_status =
00653        pam_setcred(this->pam, PAM_ESTABLISH_CRED)) != PAM_SUCCESS)
00654     {
00655       log_debug(DEBUG_WARNING) << "pam_setcred FAIL" << endl;
00656       throw error(PAM, pam_strerror(pam_status));
00657     }
00658 
00659   log_debug(DEBUG_NOTICE) << "pam_setcred OK" << endl;
00660 }
00661 
00662 void
00663 auth::cred_delete ()
00664 {
00665   assert(this->pam != 0); // PAM must be initialised
00666 
00667   int pam_status;
00668 
00669   if ((pam_status =
00670        pam_setcred(this->pam, PAM_DELETE_CRED)) != PAM_SUCCESS)
00671     {
00672       log_debug(DEBUG_WARNING) << "pam_setcred (delete) FAIL" << endl;
00673       throw error(PAM, pam_strerror(pam_status));
00674     }
00675 
00676   log_debug(DEBUG_NOTICE) << "pam_setcred (delete) OK" << endl;
00677 }
00678 
00679 void
00680 auth::open_session ()
00681 {
00682   assert(this->pam != 0); // PAM must be initialised
00683 
00684   int pam_status;
00685 
00686   if ((pam_status =
00687        pam_open_session(this->pam, 0)) != PAM_SUCCESS)
00688     {
00689       log_debug(DEBUG_WARNING) << "pam_open_session FAIL" << endl;
00690       throw error(PAM, pam_strerror(pam_status));
00691     }
00692 
00693   log_debug(DEBUG_NOTICE) << "pam_open_session OK" << endl;
00694 }
00695 
00696 void
00697 auth::close_session ()
00698 {
00699   assert(this->pam != 0); // PAM must be initialised
00700 
00701   int pam_status;
00702 
00703   if ((pam_status =
00704        pam_close_session(this->pam, 0)) != PAM_SUCCESS)
00705     {
00706       log_debug(DEBUG_WARNING) << "pam_close_session FAIL" << endl;
00707       throw error(PAM, pam_strerror(pam_status));
00708     }
00709 
00710   log_debug(DEBUG_NOTICE) << "pam_close_session OK" << endl;
00711 }
00712 
00713 auth::status
00714 auth::get_auth_status () const
00715 {
00716   status authtype = STATUS_NONE;
00717 
00718   authtype = change_auth(authtype, STATUS_USER);
00719 
00720   return authtype;
00721 }
00722 
00723 const char *
00724 auth::pam_strerror (int pam_error)
00725 {
00726   assert(this->pam != 0); // PAM must be initialised
00727 
00728   return ::pam_strerror (this->pam, pam_error);
00729 }

Generated on Sat Jan 27 16:11:03 2007 for schroot by  doxygen 1.5.1