source: trunk/debathena/third/schroot/sbuild/sbuild-auth-pam.cc @ 24167

Revision 24167, 10.5 KB checked in by broder, 15 years ago (diff)
Import schroot upstream into subversion.
Line 
1/* Copyright © 2005-2007  Roger Leigh <rleigh@debian.org>
2 *
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 3 of the License, or
6 * (at your option) any later version.
7 *
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.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program.  If not, see
15 * <http://www.gnu.org/licenses/>.
16 *
17 *********************************************************************/
18
19#include <config.h>
20
21#include "sbuild-auth-pam.h"
22#include "sbuild-auth-pam-conv.h"
23#include "sbuild-auth-pam-conv-tty.h"
24
25#include <cassert>
26#include <cerrno>
27#include <cstdlib>
28#include <cstring>
29#include <iostream>
30#include <sstream>
31
32#include <syslog.h>
33
34#include <boost/format.hpp>
35
36using std::cerr;
37using std::endl;
38using boost::format;
39using namespace sbuild;
40
41#if defined(__LINUX_PAM__)
42#define PAM_TEXT_DOMAIN "Linux-PAM"
43#elif defined(__sun__)
44#define PAM_TEXT_DOMAIN "SUNW_OST_SYSOSPAM"
45#endif
46
47namespace
48{
49
50  /* This is the glue to link PAM user interaction with auth_pam_conv. */
51  int
52  auth_pam_conv_hook (int                        num_msg,
53                      const struct pam_message **msgm,
54                      struct pam_response      **response,
55                      void                      *appdata_ptr)
56  {
57    log_debug(DEBUG_NOTICE) << "PAM conversation hook started" << endl;
58
59    try
60      {
61        if (appdata_ptr == 0)
62          return PAM_CONV_ERR;
63
64        auth_pam_conv *conv = static_cast<auth_pam_conv *>(appdata_ptr);
65        assert (conv != 0);
66
67        /* Construct a message vector */
68        auth_pam_conv::message_list messages;
69        for (int i = 0; i < num_msg; ++i)
70          {
71            const struct pam_message *source = msgm[i];
72
73            auth_pam_message
74              message(static_cast<auth_pam_message::message_type>(source->msg_style),
75                      source->msg);
76
77            /* Replace PAM prompt */
78            if (message.message == dgettext(PAM_TEXT_DOMAIN, "Password: ") ||
79                message.message == dgettext(PAM_TEXT_DOMAIN, "Password:"))
80              {
81                std::string user = "unknown"; // Set in case auth is void
82                std::tr1::shared_ptr<auth_pam> auth = conv->get_auth();
83                assert(auth && auth.get() != 0); // Check auth is not void
84                if (auth && auth.get() != 0)
85                  user = auth->get_user();
86                format fmt(_("[schroot] password for %1%: "));
87                fmt % user;
88                message.message = fmt.str();
89              }
90
91            messages.push_back(message);
92          }
93
94        /* Do the conversation; an exception will be thrown on failure */
95        conv->conversation(messages);
96
97        /* Copy response into **reponse */
98        struct pam_response *reply =
99          static_cast<struct pam_response *>
100          (malloc(sizeof(struct pam_response) * num_msg));
101
102        for (int i = 0; i < num_msg; ++i)
103          {
104            reply[i].resp_retcode = 0;
105            reply[i].resp = strdup(messages[i].response.c_str());
106          }
107
108        *response = reply;
109        reply = 0;
110
111        return PAM_SUCCESS;
112      }
113    catch (std::exception const& e)
114      {
115        sbuild::log_exception_error(e);
116      }
117    catch (...)
118      {
119        sbuild::log_error() << _("An unknown exception occurred") << endl;
120      }
121
122    return PAM_CONV_ERR;
123  }
124
125}
126
127auth_pam::auth_pam (std::string const& service_name):
128  auth(service_name),
129  pam(),
130  conv()
131{
132}
133
134auth_pam::~auth_pam ()
135{
136  // Shutdown PAM.
137  try
138    {
139      stop();
140    }
141  catch (error const& e)
142    {
143      sbuild::log_exception_error(e);
144    }
145}
146
147auth::ptr
148auth_pam::create (std::string const& service_name)
149{
150  return ptr(new auth_pam(service_name));
151}
152
153environment
154auth_pam::get_auth_environment () const
155{
156  return environment(pam_getenvlist(this->pam));
157}
158
159auth_pam_conv::ptr&
160auth_pam::get_conv ()
161{
162  return this->conv;
163}
164
165void
166auth_pam::set_conv (auth_pam_conv::ptr& conv)
167{
168  this->conv = conv;
169}
170
171void
172auth_pam::start ()
173{
174  assert(!this->user.empty());
175
176  if (this->pam != 0)
177    {
178      log_debug(DEBUG_CRITICAL)
179        << "pam_start FAIL (already initialised)" << endl;
180      throw error("Init PAM", PAM_DOUBLE_INIT);
181    }
182
183  struct pam_conv conv_hook =
184    {
185      auth_pam_conv_hook,
186      reinterpret_cast<void *>(this->conv.get())
187    };
188
189  int pam_status;
190
191  if ((pam_status =
192       pam_start(this->service.c_str(), this->user.c_str(),
193                 &conv_hook, &this->pam)) != PAM_SUCCESS)
194    {
195      log_debug(DEBUG_WARNING) << "pam_start FAIL" << endl;
196      throw error(PAM, pam_strerror(pam_status));
197    }
198
199  log_debug(DEBUG_NOTICE) << "pam_start OK" << endl;
200}
201
202void
203auth_pam::stop ()
204{
205  if (this->pam); // PAM must be initialised
206  {
207    int pam_status;
208
209    if ((pam_status =
210         pam_end(this->pam, PAM_SUCCESS)) != PAM_SUCCESS)
211      {
212        log_debug(DEBUG_WARNING) << "pam_end FAIL" << endl;
213        throw error(PAM, pam_strerror(pam_status));
214      }
215
216    this->pam = 0;
217    log_debug(DEBUG_NOTICE) << "pam_end OK" << endl;
218  }
219}
220
221void
222auth_pam::authenticate (status auth_status)
223{
224  assert(!this->user.empty());
225  assert(this->pam != 0); // PAM must be initialised
226
227  int pam_status;
228
229  if ((pam_status =
230       pam_set_item(this->pam, PAM_RUSER, this->ruser.c_str())) != PAM_SUCCESS)
231    {
232      log_debug(DEBUG_WARNING) << "pam_set_item (PAM_RUSER) FAIL" << endl;
233      throw error(_("Set RUSER"), PAM, pam_strerror(pam_status));
234    }
235
236  long hl = 256; /* sysconf(_SC_HOST_NAME_MAX); BROKEN with Debian libc6 2.3.2.ds1-22 */
237
238  char *hostname = new char[hl];
239  try
240    {
241      if (gethostname(hostname, hl) != 0)
242        {
243          log_debug(DEBUG_CRITICAL) << "gethostname FAIL" << endl;
244          throw error(HOSTNAME, strerror(errno));
245        }
246
247      if ((pam_status =
248           pam_set_item(this->pam, PAM_RHOST, hostname)) != PAM_SUCCESS)
249        {
250          log_debug(DEBUG_WARNING) << "pam_set_item (PAM_RHOST) FAIL" << endl;
251          throw error(_("Set RHOST"), PAM, pam_strerror(pam_status));
252        }
253    }
254  catch (error const& e)
255    {
256      delete[] hostname;
257      hostname = 0;
258      throw;
259    }
260  delete[] hostname;
261  hostname = 0;
262
263  const char *tty = ttyname(STDIN_FILENO);
264  if (tty)
265    {
266      if ((pam_status =
267           pam_set_item(this->pam, PAM_TTY, tty)) != PAM_SUCCESS)
268        {
269          log_debug(DEBUG_WARNING) << "pam_set_item (PAM_TTY) FAIL" << endl;
270          throw error(_("Set TTY"), PAM, pam_strerror(pam_status));
271        }
272    }
273
274  /* Authenticate as required. */
275  switch (auth_status)
276    {
277    case STATUS_NONE:
278      if ((pam_status = pam_set_item(this->pam, PAM_USER, this->user.c_str()))
279          != PAM_SUCCESS)
280        {
281          log_debug(DEBUG_WARNING) << "pam_set_item (PAM_USER) FAIL" << endl;
282          throw error(_("Set USER"), PAM, pam_strerror(pam_status));
283        }
284      break;
285
286    case STATUS_USER:
287      if ((pam_status = pam_authenticate(this->pam, 0)) != PAM_SUCCESS)
288        {
289          log_debug(DEBUG_INFO) << "pam_authenticate FAIL" << endl;
290          syslog(LOG_AUTH|LOG_WARNING, "%s->%s Authentication failure",
291                 this->ruser.c_str(), this->user.c_str());
292          throw error(AUTHENTICATION, pam_strerror(pam_status));
293        }
294      log_debug(DEBUG_NOTICE) << "pam_authenticate OK" << endl;
295      break;
296
297    case STATUS_FAIL:
298        {
299          log_debug(DEBUG_INFO) << "PAM auth premature FAIL" << endl;
300          syslog(LOG_AUTH|LOG_WARNING,
301                 "%s->%s Unauthorised",
302                 this->ruser.c_str(), this->user.c_str());
303          error e(AUTHORISATION);
304          // TRANSLATORS: %1% = program name (PAM service name)
305          std::string reason(_("You do not have permission to access the %1% service."));
306          reason += '\n';
307          reason += _("This failure will be reported.");
308          format fmt(reason);
309          fmt % this->service;
310          e.set_reason(fmt.str());
311          throw e;
312        }
313    default:
314      break;
315    }
316}
317
318void
319auth_pam::setupenv ()
320{
321  assert(this->pam != 0); // PAM must be initialised
322
323  int pam_status;
324
325  environment minimal(get_minimal_environment());
326
327  // Move into PAM environment.
328  for (environment::const_iterator cur = minimal.begin();
329       cur != minimal.end();
330       ++cur)
331    {
332      std::string env_string = cur->first + "=" + cur->second;
333      if ((pam_status =
334           pam_putenv(this->pam, env_string.c_str())) != PAM_SUCCESS)
335        {
336          log_debug(DEBUG_WARNING) << "pam_putenv FAIL" << endl;
337          throw error(PAM, pam_strerror(pam_status));
338        }
339      log_debug(DEBUG_INFO)
340        << format("pam_putenv: set %1%=%2%") % cur->first % cur->second
341        << endl;
342    }
343
344  log_debug(DEBUG_NOTICE) << "pam_putenv OK" << endl;
345}
346
347void
348auth_pam::account ()
349{
350  assert(this->pam != 0); // PAM must be initialised
351
352  int pam_status;
353
354  if ((pam_status =
355       pam_acct_mgmt(this->pam, 0)) != PAM_SUCCESS)
356    {
357      /* We don't handle changing expired passwords here, since we are
358         not login or ssh. */
359      log_debug(DEBUG_WARNING) << "pam_acct_mgmt FAIL" << endl;
360      throw error(PAM, pam_strerror(pam_status));
361    }
362
363  log_debug(DEBUG_NOTICE) << "pam_acct_mgmt OK" << endl;
364}
365
366void
367auth_pam::cred_establish ()
368{
369  assert(this->pam != 0); // PAM must be initialised
370
371  int pam_status;
372
373  if ((pam_status =
374       pam_setcred(this->pam, PAM_ESTABLISH_CRED)) != PAM_SUCCESS)
375    {
376      log_debug(DEBUG_WARNING) << "pam_setcred FAIL" << endl;
377      throw error(PAM, pam_strerror(pam_status));
378    }
379
380  log_debug(DEBUG_NOTICE) << "pam_setcred OK" << endl;
381
382  const char *authuser = 0;
383  const void *tmpcast = reinterpret_cast<const void *>(authuser);
384  pam_get_item(this->pam, PAM_USER, &tmpcast);
385  log_debug(DEBUG_INFO)
386    << format("PAM authentication succeeded for user %1%") % authuser
387    << endl;
388}
389
390void
391auth_pam::cred_delete ()
392{
393  assert(this->pam != 0); // PAM must be initialised
394
395  int pam_status;
396
397  if ((pam_status =
398       pam_setcred(this->pam, PAM_DELETE_CRED)) != PAM_SUCCESS)
399    {
400      log_debug(DEBUG_WARNING) << "pam_setcred (delete) FAIL" << endl;
401      throw error(PAM, pam_strerror(pam_status));
402    }
403
404  log_debug(DEBUG_NOTICE) << "pam_setcred (delete) OK" << endl;
405}
406
407void
408auth_pam::open_session ()
409{
410  assert(this->pam != 0); // PAM must be initialised
411
412  int pam_status;
413
414  if ((pam_status =
415       pam_open_session(this->pam, 0)) != PAM_SUCCESS)
416    {
417      log_debug(DEBUG_WARNING) << "pam_open_session FAIL" << endl;
418      throw error(PAM, pam_strerror(pam_status));
419    }
420
421  log_debug(DEBUG_NOTICE) << "pam_open_session OK" << endl;
422}
423
424void
425auth_pam::close_session ()
426{
427  assert(this->pam != 0); // PAM must be initialised
428
429  int pam_status;
430
431  if ((pam_status =
432       pam_close_session(this->pam, 0)) != PAM_SUCCESS)
433    {
434      log_debug(DEBUG_WARNING) << "pam_close_session FAIL" << endl;
435      throw error(PAM, pam_strerror(pam_status));
436    }
437
438  log_debug(DEBUG_NOTICE) << "pam_close_session OK" << endl;
439}
440
441bool
442auth_pam::is_initialised () const
443{
444  return this->pam != 0;
445}
446
447const char *
448auth_pam::pam_strerror (int pam_error)
449{
450  assert(this->pam != 0); // PAM must be initialised
451
452  return ::pam_strerror (this->pam, pam_error);
453}
Note: See TracBrowser for help on using the repository browser.