~ubuntu-branches/ubuntu/intrepid/schroot/intrepid

« back to all changes in this revision

Viewing changes to schroot/sbuild-session.cc

  • Committer: Bazaar Package Importer
  • Author(s): Reinhard Tartler
  • Date: 2006-07-08 18:33:28 UTC
  • mfrom: (1.1.4 upstream)
  • Revision ID: james.westby@ubuntu.com-20060708183328-rlo4mpldmyoda55q
Tags: 0.99.2-2ubuntu1
* remerge ubuntu changes:
  + debian/control: libpam-dev (>> 0.79-3ubuntu6)

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
/* Copyright © 2005-2006  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 2 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, write to the Free Software
15
 
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
16
 
 * MA  02111-1307  USA
17
 
 *
18
 
 *********************************************************************/
19
 
 
20
 
#include <config.h>
21
 
 
22
 
#include "sbuild.h"
23
 
 
24
 
#include <cassert>
25
 
#include <cerrno>
26
 
#include <cstdlib>
27
 
#include <cstring>
28
 
#include <iostream>
29
 
#include <memory>
30
 
 
31
 
#include <unistd.h>
32
 
 
33
 
#include <syslog.h>
34
 
 
35
 
#include <boost/format.hpp>
36
 
 
37
 
#include <uuid/uuid.h>
38
 
 
39
 
using std::cout;
40
 
using std::endl;
41
 
using boost::format;
42
 
using namespace sbuild;
43
 
 
44
 
namespace
45
 
{
46
 
 
47
 
  /**
48
 
   * Check group membership.
49
 
   *
50
 
   * @param group the group to check for.
51
 
   * @returns true if the user is a member of group, otherwise false.
52
 
   */
53
 
  bool
54
 
  is_group_member (std::string const& group)
55
 
  {
56
 
    errno = 0;
57
 
    struct group *groupbuf = getgrnam(group.c_str());
58
 
    if (groupbuf == NULL)
59
 
      {
60
 
        if (errno == 0)
61
 
          log_error() << format(_("%1%: group not found")) % group << endl;
62
 
        else
63
 
          log_error() << format(_("%1%: group not found: %2%"))
64
 
            % group % strerror(errno)
65
 
                      << endl;
66
 
        exit (EXIT_FAILURE);
67
 
      }
68
 
 
69
 
    bool group_member = false;
70
 
    if (groupbuf->gr_gid == getgid())
71
 
      {
72
 
        group_member = true;
73
 
      }
74
 
    else
75
 
      {
76
 
        int supp_group_count = getgroups(0, NULL);
77
 
        if (supp_group_count < 0)
78
 
          {
79
 
            log_error() << format(_("can't get supplementary group count: %1%"))
80
 
              % strerror(errno)
81
 
                        << endl;
82
 
            exit (EXIT_FAILURE);
83
 
          }
84
 
        if (supp_group_count > 0)
85
 
          {
86
 
            gid_t *supp_groups = new gid_t[supp_group_count];
87
 
            if (getgroups(supp_group_count, supp_groups) < 1)
88
 
              {
89
 
                log_error() << format(_("can't get supplementary groups: %1%"))
90
 
                  % strerror(errno)
91
 
                            << endl;
92
 
                exit (EXIT_FAILURE);
93
 
              }
94
 
 
95
 
            for (int i = 0; i < supp_group_count; ++i)
96
 
              {
97
 
                if (groupbuf->gr_gid == supp_groups[i])
98
 
                  group_member = true;
99
 
              }
100
 
            delete[] supp_groups;
101
 
          }
102
 
      }
103
 
 
104
 
    return group_member;
105
 
  }
106
 
 
107
 
  volatile bool sighup_called = false;
108
 
 
109
 
  /**
110
 
   * Handle the SIGALRM signal.
111
 
   *
112
 
   * @param ignore the signal number.
113
 
   */
114
 
  void
115
 
  sighup_handler (int ignore)
116
 
  {
117
 
    /* This exists so that system calls get interrupted. */
118
 
    sighup_called = true;
119
 
  }
120
 
 
121
 
#ifdef SBUILD_DEBUG
122
 
  volatile bool child_wait = true;
123
 
#endif
124
 
 
125
 
}
126
 
 
127
 
session::session (std::string const&         service,
128
 
                  config_ptr&                config,
129
 
                  operation                  operation,
130
 
                  sbuild::string_list const& chroots):
131
 
  auth(service),
132
 
  config(config),
133
 
  chroots(chroots),
134
 
  chroot_status(true),
135
 
  child_status(0),
136
 
  session_operation(operation),
137
 
  session_id(),
138
 
  force(false),
139
 
  saved_signals()
140
 
{
141
 
}
142
 
 
143
 
session::~session ()
144
 
{
145
 
}
146
 
 
147
 
session::config_ptr&
148
 
session::get_config ()
149
 
{
150
 
  return this->config;
151
 
}
152
 
 
153
 
void
154
 
session::set_config (config_ptr& config)
155
 
{
156
 
  this->config = config;
157
 
}
158
 
 
159
 
string_list const&
160
 
session::get_chroots () const
161
 
{
162
 
  return this->chroots;
163
 
}
164
 
 
165
 
void
166
 
session::set_chroots (string_list const& chroots)
167
 
{
168
 
  this->chroots = chroots;
169
 
}
170
 
 
171
 
session::operation
172
 
session::get_operation () const
173
 
{
174
 
  return this->session_operation;
175
 
}
176
 
 
177
 
void
178
 
session::set_operation (operation operation)
179
 
{
180
 
  this->session_operation = operation;
181
 
}
182
 
 
183
 
std::string const&
184
 
session::get_session_id () const
185
 
{
186
 
  return this->session_id;
187
 
}
188
 
 
189
 
void
190
 
session::set_session_id (std::string const& session_id)
191
 
{
192
 
  this->session_id = session_id;
193
 
}
194
 
 
195
 
bool
196
 
session::get_force () const
197
 
{
198
 
  return this->force;
199
 
}
200
 
 
201
 
void
202
 
session::set_force (bool force)
203
 
{
204
 
  this->force = force;
205
 
}
206
 
 
207
 
int
208
 
session::get_child_status () const
209
 
{
210
 
  return this->child_status;
211
 
}
212
 
 
213
 
auth::status
214
 
session::get_auth_status () const
215
 
{
216
 
  assert(!this->chroots.empty());
217
 
  if (this->config.get() == 0) return auth::STATUS_FAIL;
218
 
 
219
 
  /*
220
 
   * Note that the root user can't escape authentication.  This is
221
 
   * because pam_rootok.so should be used in the PAM configuration if
222
 
   * root should automatically be granted access.  The only exception
223
 
   * is that the root group doesn't need to be added to the groups or
224
 
   * root groups lists.
225
 
   */
226
 
 
227
 
  auth::status status = auth::STATUS_NONE;
228
 
 
229
 
  /* @todo Use set difference rather than iteration and
230
 
     is_group_member. */
231
 
  for (string_list::const_iterator cur = this->chroots.begin();
232
 
       cur != this->chroots.end();
233
 
       ++cur)
234
 
    {
235
 
      const chroot::ptr chroot = this->config->find_alias(*cur);
236
 
      if (!chroot) // Should never happen, but cater for it anyway.
237
 
        {
238
 
          log_warning() << format(_("No chroot found matching alias '%1%'"))
239
 
            % *cur
240
 
                        << endl;
241
 
          status = change_auth(status, auth::STATUS_FAIL);
242
 
        }
243
 
 
244
 
      string_list const& groups = chroot->get_groups();
245
 
      string_list const& root_groups = chroot->get_root_groups();
246
 
 
247
 
      if (!groups.empty())
248
 
        {
249
 
          bool in_groups = false;
250
 
          bool in_root_groups = false;
251
 
 
252
 
          if (!groups.empty())
253
 
            {
254
 
              for (string_list::const_iterator gp = groups.begin();
255
 
                   gp != groups.end();
256
 
                   ++gp)
257
 
                if (is_group_member(*gp))
258
 
                  in_groups = true;
259
 
            }
260
 
 
261
 
          if (!root_groups.empty())
262
 
            {
263
 
              for (string_list::const_iterator gp = root_groups.begin();
264
 
                   gp != root_groups.end();
265
 
                   ++gp)
266
 
                if (is_group_member(*gp))
267
 
                  in_root_groups = true;
268
 
            }
269
 
 
270
 
          /*
271
 
           * No auth required if in root groups and changing to root,
272
 
           * or if the uid is not changing.  If not in a group,
273
 
           * authentication fails immediately.
274
 
           */
275
 
          if (in_groups == true &&
276
 
              ((this->get_uid() == 0 && in_root_groups == true) ||
277
 
               (this->get_ruid() == this->get_uid())))
278
 
            {
279
 
              status = change_auth(status, auth::STATUS_NONE);
280
 
            }
281
 
          else if (in_groups == true) // Auth required if not in root group
282
 
            {
283
 
              status = change_auth(status, auth::STATUS_USER);
284
 
            }
285
 
          else // Not in any groups
286
 
            {
287
 
              if (this->get_ruid() == 0)
288
 
                status = change_auth(status, auth::STATUS_USER);
289
 
              else
290
 
                status = change_auth(status, auth::STATUS_FAIL);
291
 
            }
292
 
        }
293
 
      else // No available groups entries means no access to anyone
294
 
        {
295
 
          if (this->get_ruid() == 0)
296
 
            status = change_auth(status, auth::STATUS_USER);
297
 
          else
298
 
            status = change_auth(status, auth::STATUS_FAIL);
299
 
        }
300
 
    }
301
 
 
302
 
  return status;
303
 
}
304
 
 
305
 
void
306
 
session::run_impl ()
307
 
{
308
 
  assert(this->config.get() != NULL);
309
 
  assert(!this->chroots.empty());
310
 
 
311
 
try
312
 
  {
313
 
    sighup_called = false;
314
 
    set_sighup_handler();
315
 
 
316
 
    for (string_list::const_iterator cur = this->chroots.begin();
317
 
         cur != this->chroots.end();
318
 
         ++cur)
319
 
      {
320
 
        log_debug(DEBUG_NOTICE)
321
 
          << format("Running session in %1% chroot:") % *cur
322
 
          << endl;
323
 
        const chroot::ptr ch = this->config->find_alias(*cur);
324
 
        if (!ch) // Should never happen, but cater for it anyway.
325
 
          {
326
 
            format fmt(_("%1%: Failed to find chroot"));
327
 
            fmt % *cur;
328
 
            throw error(fmt);
329
 
          }
330
 
 
331
 
        chroot::ptr chroot(ch->clone());
332
 
 
333
 
        /* If restoring a session, set the session ID from the
334
 
           chroot name, or else generate it.  Only chroots which
335
 
           support session creation append a UUID to the session
336
 
           ID. */
337
 
        if (chroot->get_active() ||
338
 
            !(chroot->get_session_flags() & chroot::SESSION_CREATE))
339
 
          {
340
 
            set_session_id(chroot->get_name());
341
 
          }
342
 
        else
343
 
          {
344
 
            uuid_t uuid;
345
 
            char uuid_str[37];
346
 
            uuid_generate(uuid);
347
 
            uuid_unparse(uuid, uuid_str);
348
 
            uuid_clear(uuid);
349
 
            std::string session_id(chroot->get_name() + "-" + uuid_str);
350
 
            set_session_id(session_id);
351
 
          }
352
 
 
353
 
        /* Activate chroot. */
354
 
        chroot->set_active(true);
355
 
 
356
 
        /* If a chroot mount location has not yet been set, and the
357
 
           chroot is not a plain chroot, set a mount location with the
358
 
           session id. */
359
 
        {
360
 
          chroot_plain *plain = dynamic_cast<chroot_plain *>(chroot.get());
361
 
          if (chroot->get_mount_location().empty() &&
362
 
              (plain == 0 || plain->get_run_setup_scripts() == true))
363
 
            {
364
 
              std::string location(std::string(SCHROOT_MOUNT_DIR) + "/" +
365
 
                                   this->session_id);
366
 
              chroot->set_mount_location(location);
367
 
            }
368
 
        }
369
 
 
370
 
        /* Chroot types which create a session (e.g. LVM devices)
371
 
           need the chroot name respecifying. */
372
 
        if (chroot->get_session_flags() & chroot::SESSION_CREATE)
373
 
          {
374
 
            chroot->set_name(this->session_id);
375
 
            chroot->set_aliases(string_list());
376
 
          }
377
 
 
378
 
        /* LVM devices need the snapshot device name specifying. */
379
 
        chroot_lvm_snapshot *snapshot = 0;
380
 
        if ((snapshot = dynamic_cast<chroot_lvm_snapshot *>(chroot.get())) != 0)
381
 
          {
382
 
            std::string dir(dirname(snapshot->get_device(), '/'));
383
 
            std::string device(dir + "/" + this->session_id);
384
 
            snapshot->set_snapshot_device(device);
385
 
          }
386
 
 
387
 
        try
388
 
          {
389
 
            /* Run setup-start chroot setup scripts. */
390
 
            setup_chroot(chroot, chroot::SETUP_START);
391
 
            if (this->session_operation == OPERATION_BEGIN)
392
 
              cout << this->session_id << endl;
393
 
 
394
 
            /* Run recover scripts. */
395
 
            setup_chroot(chroot, chroot::SETUP_RECOVER);
396
 
 
397
 
            try
398
 
              {
399
 
                /* Run exec-start scripts. */
400
 
                setup_chroot(chroot, chroot::EXEC_START);
401
 
 
402
 
                /* Run session if setup succeeded. */
403
 
                if (this->session_operation == OPERATION_AUTOMATIC ||
404
 
                    this->session_operation == OPERATION_RUN)
405
 
                  run_chroot(chroot);
406
 
 
407
 
                /* Run exec-stop scripts whether or not there was an
408
 
                   error. */
409
 
                setup_chroot(chroot, chroot::EXEC_STOP);
410
 
              }
411
 
            catch (error const& e)
412
 
              {
413
 
                setup_chroot(chroot, chroot::EXEC_STOP);
414
 
                throw;
415
 
              }
416
 
 
417
 
          }
418
 
        catch (error const& e)
419
 
          {
420
 
            try
421
 
              {
422
 
                setup_chroot(chroot, chroot::SETUP_STOP);
423
 
              }
424
 
            catch (error const& discard)
425
 
              {
426
 
              }
427
 
            chroot->set_active(false);
428
 
            throw;
429
 
          }
430
 
 
431
 
        /* Run setup-stop chroot setup scripts whether or not there
432
 
           was an error. */
433
 
        setup_chroot(chroot, chroot::SETUP_STOP);
434
 
 
435
 
        /* Deactivate chroot. */
436
 
        chroot->set_active(false);
437
 
      }
438
 
 
439
 
    clear_sighup_handler();
440
 
  }
441
 
catch (error const& e)
442
 
  {
443
 
    clear_sighup_handler();
444
 
 
445
 
    /* If a command was not run, but something failed, the exit
446
 
       status still needs setting. */
447
 
    if (this->child_status == 0)
448
 
      this->child_status = EXIT_FAILURE;
449
 
    throw;
450
 
  }
451
 
}
452
 
 
453
 
void
454
 
session::setup_chroot (sbuild::chroot::ptr&       session_chroot,
455
 
                       sbuild::chroot::setup_type setup_type)
456
 
{
457
 
  assert(!session_chroot->get_name().empty());
458
 
 
459
 
  if (!((this->session_operation == OPERATION_BEGIN &&
460
 
         setup_type == chroot::SETUP_START) ||
461
 
        (this->session_operation == OPERATION_RECOVER &&
462
 
         setup_type == chroot::SETUP_RECOVER) ||
463
 
        (this->session_operation == OPERATION_END &&
464
 
         setup_type == chroot::SETUP_STOP) ||
465
 
        (this->session_operation == OPERATION_RUN &&
466
 
         (setup_type == chroot::EXEC_START ||
467
 
          setup_type == chroot::EXEC_STOP)) ||
468
 
        (this->session_operation == OPERATION_AUTOMATIC &&
469
 
         (setup_type == chroot::SETUP_START ||
470
 
          setup_type == chroot::SETUP_STOP  ||
471
 
          setup_type == chroot::EXEC_START   ||
472
 
          setup_type == chroot::EXEC_STOP))))
473
 
    return;
474
 
 
475
 
  if (((setup_type == chroot::SETUP_START   ||
476
 
        setup_type == chroot::SETUP_RECOVER ||
477
 
        setup_type == chroot::SETUP_STOP) &&
478
 
       session_chroot->get_run_setup_scripts() == false) ||
479
 
      ((setup_type == chroot::EXEC_START ||
480
 
        setup_type == chroot::EXEC_STOP) &&
481
 
       session_chroot->get_run_exec_scripts() == false))
482
 
    return;
483
 
 
484
 
  if (setup_type == chroot::SETUP_START)
485
 
    this->chroot_status = true;
486
 
 
487
 
  try
488
 
    {
489
 
      session_chroot->lock(setup_type);
490
 
    }
491
 
  catch (chroot::error const& e)
492
 
    {
493
 
      this->chroot_status = false;
494
 
      try
495
 
        {
496
 
          // Release lock, which also removes session metadata.
497
 
          session_chroot->unlock(setup_type, 0);
498
 
        }
499
 
      catch (chroot::error const& ignore)
500
 
        {
501
 
        }
502
 
      format fmt(_("Chroot setup failed to lock chroot: %1%"));
503
 
      fmt % e.what();
504
 
      throw error(fmt);
505
 
    }
506
 
 
507
 
  std::string setup_type_string;
508
 
  if (setup_type == chroot::SETUP_START)
509
 
    setup_type_string = "setup-start";
510
 
  else if (setup_type == chroot::SETUP_RECOVER)
511
 
    setup_type_string = "setup-recover";
512
 
  else if (setup_type == chroot::SETUP_STOP)
513
 
    setup_type_string = "setup-stop";
514
 
  else if (setup_type == chroot::EXEC_START)
515
 
    setup_type_string = "exec-start";
516
 
  else if (setup_type == chroot::EXEC_STOP)
517
 
    setup_type_string = "exec-stop";
518
 
 
519
 
  std::string chroot_status_string;
520
 
  if (this->chroot_status)
521
 
    chroot_status_string = "ok";
522
 
  else
523
 
    chroot_status_string = "fail";
524
 
 
525
 
  string_list arg_list;
526
 
  arg_list.push_back(RUN_PARTS); // Run run-parts(8)
527
 
  if (get_verbosity() == auth::VERBOSITY_VERBOSE)
528
 
    arg_list.push_back("--verbose");
529
 
  arg_list.push_back("--lsbsysinit");
530
 
  arg_list.push_back("--exit-on-error");
531
 
  if (setup_type == chroot::SETUP_STOP ||
532
 
      setup_type == chroot::EXEC_STOP)
533
 
    arg_list.push_back("--reverse");
534
 
  format arg_fmt1("--arg=%1%");
535
 
  arg_fmt1 % setup_type_string;
536
 
  arg_list.push_back(arg_fmt1.str());
537
 
  format arg_fmt2("--arg=%1%");
538
 
  arg_fmt2 % chroot_status_string;
539
 
  arg_list.push_back(arg_fmt2.str());
540
 
  if (setup_type == chroot::SETUP_START ||
541
 
      setup_type == chroot::SETUP_RECOVER ||
542
 
      setup_type == chroot::SETUP_STOP)
543
 
    arg_list.push_back(SCHROOT_CONF_SETUP_D); // Setup directory
544
 
  else
545
 
    arg_list.push_back(SCHROOT_CONF_EXEC_D); // Run directory
546
 
 
547
 
  /* Get a complete list of environment variables to set.  We need to
548
 
     query the chroot here, since this can vary depending upon the
549
 
     chroot type. */
550
 
  environment env;
551
 
  session_chroot->setup_env(env);
552
 
  env.add("AUTH_USER", get_user());
553
 
  {
554
 
    const char *verbosity = NULL;
555
 
    switch (get_verbosity())
556
 
      {
557
 
      case auth::VERBOSITY_QUIET:
558
 
        verbosity = "quiet";
559
 
        break;
560
 
      case auth::VERBOSITY_NORMAL:
561
 
        verbosity = "normal";
562
 
        break;
563
 
      case auth::VERBOSITY_VERBOSE:
564
 
        verbosity = "verbose";
565
 
        break;
566
 
      default:
567
 
        log_debug(DEBUG_CRITICAL) << format(_("Invalid verbosity level: %1%, falling back to \"normal\""))
568
 
          % static_cast<int>(get_verbosity())
569
 
                     << endl;
570
 
        verbosity = "normal";
571
 
        break;
572
 
      }
573
 
    env.add("AUTH_VERBOSITY", verbosity);
574
 
  }
575
 
 
576
 
  env.add("MOUNT_DIR", SCHROOT_MOUNT_DIR);
577
 
  env.add("LIBEXEC_DIR", SCHROOT_LIBEXEC_DIR);
578
 
  env.add("PID", getpid());
579
 
  env.add("SESSION_ID", this->session_id);
580
 
 
581
 
  int exit_status = 0;
582
 
  pid_t pid;
583
 
 
584
 
  if ((pid = fork()) == -1)
585
 
    {
586
 
      this->chroot_status = false;
587
 
      format fmt(_("Failed to fork child: %1%"));
588
 
      fmt % strerror(errno);
589
 
      throw error(fmt);
590
 
    }
591
 
  else if (pid == 0)
592
 
    {
593
 
      // The setup scripts don't use our syslog fd.
594
 
      closelog();
595
 
 
596
 
      chdir("/");
597
 
      /* This is required to ensure the scripts run with uid=0 and gid=0,
598
 
         otherwise setuid programs such as mount(8) will fail.  This
599
 
         should always succeed, because our euid=0 and egid=0.*/
600
 
      setuid(0);
601
 
      setgid(0);
602
 
      initgroups("root", 0);
603
 
      if (exec (arg_list[0], arg_list, env))
604
 
        {
605
 
          log_error() << format(_("Could not exec \"%1%\": %2%"))
606
 
            % arg_list[0] % strerror(errno)
607
 
                      << endl;
608
 
          exit (EXIT_FAILURE);
609
 
        }
610
 
      exit (EXIT_FAILURE); /* Should never be reached. */
611
 
    }
612
 
  else
613
 
    {
614
 
      wait_for_child(pid, exit_status);
615
 
    }
616
 
 
617
 
  try
618
 
    {
619
 
      session_chroot->unlock(setup_type, exit_status);
620
 
    }
621
 
  catch (chroot::error const& e)
622
 
    {
623
 
      this->chroot_status = false;
624
 
      format fmt(_("Chroot setup failed to unlock chroot: %1%"));
625
 
      fmt % e.what();
626
 
      throw error(fmt);
627
 
    }
628
 
 
629
 
  if (exit_status != 0)
630
 
    {
631
 
      this->chroot_status = false;
632
 
      format fmt(_("Chroot setup failed during chroot \"%1%\" stage"));
633
 
      fmt % setup_type_string;
634
 
      throw error(fmt);
635
 
    }
636
 
}
637
 
 
638
 
void
639
 
session::run_child (sbuild::chroot::ptr& session_chroot)
640
 
{
641
 
  assert(!session_chroot->get_name().empty());
642
 
 
643
 
  assert(!get_user().empty());
644
 
  assert(!get_shell().empty());
645
 
  assert(auth::pam != NULL); // PAM must be initialised
646
 
 
647
 
  std::string location(session_chroot->get_path());
648
 
  std::string cwd;
649
 
  {
650
 
    char *raw_cwd = getcwd (NULL, 0);
651
 
    if (raw_cwd)
652
 
      cwd = raw_cwd;
653
 
    else
654
 
      cwd = "/";
655
 
    free(raw_cwd);
656
 
  }
657
 
 
658
 
  /* Child errors result in immediate exit().  Errors are not
659
 
     propagated back via an exception, because there is no longer any
660
 
     higher-level handler to catch them. */
661
 
  try
662
 
    {
663
 
      open_session();
664
 
    }
665
 
  catch (auth::error const& e)
666
 
    {
667
 
      log_error() << format(_("PAM error: %1%")) % e.what()
668
 
                  << endl;
669
 
      exit (EXIT_FAILURE);
670
 
    }
671
 
 
672
 
  /* Set group ID and supplementary groups */
673
 
  if (setgid (get_gid()))
674
 
    {
675
 
      log_error() << format(_("Could not set gid to '%1%'")) % get_gid()
676
 
                  << endl;
677
 
      exit (EXIT_FAILURE);
678
 
    }
679
 
  if (initgroups (get_user().c_str(), get_gid()))
680
 
    {
681
 
      log_error() << _("Could not set supplementary group IDs") << endl;
682
 
      exit (EXIT_FAILURE);
683
 
    }
684
 
 
685
 
  /* Set the process execution domain. */
686
 
  try
687
 
    {
688
 
      session_chroot->get_persona().set();
689
 
    }
690
 
  catch (personality::error const& e)
691
 
    {
692
 
      log_error() << e.what() << endl;
693
 
      exit (EXIT_FAILURE);
694
 
    }
695
 
 
696
 
  /* Enter the chroot */
697
 
  if (chdir (location.c_str()))
698
 
    {
699
 
      log_error() << format(_("Could not chdir to '%1%': %2%"))
700
 
        % location % strerror(errno)
701
 
                  << endl;
702
 
      exit (EXIT_FAILURE);
703
 
    }
704
 
  if (::chroot (location.c_str()))
705
 
    {
706
 
      log_error() << format(_("Could not chroot to '%1%': %2%"))
707
 
        % location % strerror(errno)
708
 
                  << endl;
709
 
      exit (EXIT_FAILURE);
710
 
    }
711
 
 
712
 
  /* Set uid and check we are not still root */
713
 
  if (setuid (get_uid()))
714
 
    {
715
 
      log_error() << format(_("Could not set uid to '%1%'")) % get_uid()
716
 
                  << endl;
717
 
      exit (EXIT_FAILURE);
718
 
    }
719
 
  if (!setuid (0) && get_uid())
720
 
    {
721
 
      log_error() << _("Failed to drop root permissions.")
722
 
                  << endl;
723
 
      exit (EXIT_FAILURE);
724
 
    }
725
 
 
726
 
  std::string file;
727
 
 
728
 
  string_list command(get_command());
729
 
 
730
 
  /* chdir to current directory */
731
 
  if (chdir (cwd.c_str()))
732
 
    {
733
 
      /* Fall back to home directory, but only for a login shell,
734
 
         since for a command we require deterministic behaviour. */
735
 
      if (command.empty() ||
736
 
          command[0].empty()) // No command
737
 
        {
738
 
          log_warning() << format(_("Could not chdir to '%1%': %2%"))
739
 
            % cwd % strerror(errno)
740
 
                        << endl;
741
 
 
742
 
          if (chdir (get_home().c_str()))
743
 
            log_warning() << format(_("Falling back to '%1%'"))
744
 
              % "/"
745
 
                          << endl;
746
 
          else
747
 
            log_warning() << format(_("Falling back to home directory '%1%'"))
748
 
              % get_home()
749
 
                          << endl;
750
 
        }
751
 
      else
752
 
        {
753
 
          log_error() << format(_("Could not chdir to '%1%': %2%"))
754
 
            % cwd % strerror(errno)
755
 
                      << endl;
756
 
          exit (EXIT_FAILURE);
757
 
        }
758
 
    }
759
 
 
760
 
  /* Set up environment */
761
 
  environment env = get_pam_environment();
762
 
    log_debug(DEBUG_INFO)
763
 
      << "Set environment:\n" << env;
764
 
 
765
 
  /* Run login shell */
766
 
  if (command.empty() ||
767
 
      command[0].empty()) // No command
768
 
    {
769
 
      assert (!get_shell().empty());
770
 
 
771
 
      file = get_shell();
772
 
      if (get_environment().empty() &&
773
 
          session_chroot->get_command_prefix().empty())
774
 
        // Not keeping environment and can setup argv correctly; login shell
775
 
        {
776
 
          std::string shellbase = basename(get_shell(), '/');
777
 
          std::string loginshell = "-" + shellbase;
778
 
          command.push_back(loginshell);
779
 
          log_debug(DEBUG_INFO)
780
 
            << format("Login shell: %1%") % command[0] << endl;
781
 
        }
782
 
      else
783
 
        {
784
 
          command.push_back(get_shell());
785
 
        }
786
 
 
787
 
      if (get_environment().empty() &&
788
 
          session_chroot->get_command_prefix().empty())
789
 
        {
790
 
          log_debug(DEBUG_NOTICE)
791
 
            << format("Running login shell: %1%") % get_shell() << endl;
792
 
          syslog(LOG_USER|LOG_NOTICE, "[%s chroot] (%s->%s) Running login shell: \"%s\"",
793
 
                 session_chroot->get_name().c_str(), get_ruser().c_str(), get_user().c_str(), get_shell().c_str());
794
 
        }
795
 
      else
796
 
        {
797
 
          log_debug(DEBUG_NOTICE)
798
 
            << format("Running shell: %1%") % get_shell() << endl;
799
 
          syslog(LOG_USER|LOG_NOTICE, "[%s chroot] (%s->%s) Running shell: \"%s\"",
800
 
                 session_chroot->get_name().c_str(), get_ruser().c_str(), get_user().c_str(), get_shell().c_str());
801
 
        }
802
 
 
803
 
      if (get_verbosity() != auth::VERBOSITY_QUIET)
804
 
        {
805
 
          if (get_ruid() == get_uid())
806
 
            log_info()
807
 
              << format((get_environment().empty() &&
808
 
                         session_chroot->get_command_prefix().empty() ?
809
 
                         _("[%1% chroot] Running login shell: \"%2%\"") :
810
 
                         _("[%1% chroot] Running shell: \"%2%\"")))
811
 
              % session_chroot->get_name() % get_shell()
812
 
              << endl;
813
 
          else
814
 
            log_info()
815
 
              << format((get_environment().empty() &&
816
 
                         session_chroot->get_command_prefix().empty() ?
817
 
                         _("[%1% chroot] (%2%->%3%) Running login shell: \"%4%\"") :
818
 
                         _("[%1% chroot] (%2%->%3%) Running shell: \"%4%\"")))
819
 
              % session_chroot->get_name()
820
 
              % get_ruser() % get_user()
821
 
              % get_shell()
822
 
              << endl;
823
 
        }
824
 
    }
825
 
  else
826
 
    {
827
 
      /* Search for program in path. */
828
 
      file = find_program_in_path(command[0], getenv("PATH"), "");
829
 
      if (file.empty())
830
 
        file = command[0];
831
 
      std::string commandstring = string_list_to_string(command, " ");
832
 
      log_debug(DEBUG_NOTICE)
833
 
        << format("Running command: %1%") % commandstring << endl;
834
 
      syslog(LOG_USER|LOG_NOTICE, "[%s chroot] (%s->%s) Running command: \"%s\"",
835
 
             session_chroot->get_name().c_str(), get_ruser().c_str(), get_user().c_str(), commandstring.c_str());
836
 
      if (get_verbosity() != auth::VERBOSITY_QUIET)
837
 
        {
838
 
          if (get_ruid() == get_uid())
839
 
            log_info() << format(_("[%1% chroot] Running command: \"%2%\""))
840
 
              % session_chroot->get_name() % commandstring
841
 
                       << endl;
842
 
          else
843
 
            log_info() << format(_("[%1% chroot] (%2%->%3%) Running command: \"%4%\""))
844
 
              % session_chroot->get_name()
845
 
              % get_ruser() % get_user()
846
 
              % commandstring
847
 
                       << endl;
848
 
        }
849
 
    }
850
 
 
851
 
  // The user's command does not use our syslog fd.
852
 
  closelog();
853
 
 
854
 
  // Add command prefix.
855
 
  string_list full_command(session_chroot->get_command_prefix());
856
 
  if (full_command.size() > 0)
857
 
    file = full_command[0];
858
 
  for (string_list::const_iterator pos = command.begin();
859
 
       pos != command.end();
860
 
       ++pos)
861
 
    full_command.push_back(*pos);
862
 
 
863
 
  /* Execute */
864
 
  if (exec (file, full_command, env))
865
 
    {
866
 
      log_error() << format(_("Could not exec \"%1%\": %2%"))
867
 
        % command[0] % strerror(errno)
868
 
                  << endl;
869
 
      exit (EXIT_FAILURE);
870
 
    }
871
 
  /* This should never be reached */
872
 
  exit(EXIT_FAILURE);
873
 
}
874
 
 
875
 
void
876
 
session::wait_for_child (int  pid,
877
 
                         int& child_status)
878
 
{
879
 
  child_status = EXIT_FAILURE; // Default exit status
880
 
 
881
 
  int status;
882
 
  bool child_killed = false;
883
 
 
884
 
  while (1)
885
 
    {
886
 
      if (sighup_called && !child_killed)
887
 
        {
888
 
          log_error() << _("caught hangup signal, terminating...")
889
 
                      << endl;
890
 
          kill(pid, SIGHUP);
891
 
          this->chroot_status = false;
892
 
          child_killed = true;
893
 
        }
894
 
 
895
 
      if (wait(&status) != pid)
896
 
        {
897
 
          if (errno == EINTR && sighup_called)
898
 
            continue; // Kill child and wait again.
899
 
          else
900
 
            {
901
 
              format fmt(_("wait for child failed: %1%"));
902
 
              fmt % strerror(errno);
903
 
              throw error(fmt);
904
 
            }
905
 
        }
906
 
      else if (sighup_called)
907
 
        {
908
 
          sighup_called = false;
909
 
          throw error(_("caught hangup signal, terminating..."));
910
 
        }
911
 
      else
912
 
        break;
913
 
    }
914
 
 
915
 
  try
916
 
    {
917
 
      close_session();
918
 
    }
919
 
  catch (auth::error const& e)
920
 
    {
921
 
      throw error(e.what());
922
 
    }
923
 
 
924
 
  if (!WIFEXITED(status))
925
 
    {
926
 
      if (WIFSIGNALED(status))
927
 
        {
928
 
          format fmt(_("Child terminated by signal %1%"));
929
 
          fmt % strsignal(WTERMSIG(status));
930
 
          throw error(fmt);
931
 
        }
932
 
      else if (WCOREDUMP(status))
933
 
        throw error(_("Child dumped core"));
934
 
      else
935
 
        throw error(_("Child exited abnormally (reason unknown; not a signal or core dump)"));
936
 
    }
937
 
 
938
 
  child_status = WEXITSTATUS(status);
939
 
 
940
 
  if (child_status)
941
 
    {
942
 
      format fmt(_("Child exited abnormally with status '%1%'"));
943
 
      fmt % child_status;
944
 
      throw error(fmt);
945
 
    }
946
 
}
947
 
 
948
 
void
949
 
session::run_chroot (sbuild::chroot::ptr& session_chroot)
950
 
{
951
 
  assert(!session_chroot->get_name().empty());
952
 
 
953
 
  pid_t pid;
954
 
  if ((pid = fork()) == -1)
955
 
    {
956
 
      format fmt(_("Failed to fork child: %1%"));
957
 
      fmt % strerror(errno);
958
 
      throw error(fmt);
959
 
    }
960
 
  else if (pid == 0)
961
 
    {
962
 
#ifdef SBUILD_DEBUG
963
 
      while (child_wait)
964
 
        ;
965
 
#endif
966
 
      run_child(session_chroot);
967
 
      exit (EXIT_FAILURE); /* Should never be reached. */
968
 
    }
969
 
  else
970
 
    {
971
 
      wait_for_child(pid, this->child_status);
972
 
    }
973
 
}
974
 
 
975
 
int
976
 
session::exec (std::string const& file,
977
 
               string_list const& command,
978
 
               environment const& env)
979
 
{
980
 
  char **argv = string_list_to_strv(command);
981
 
  char **envp = env.get_strv();
982
 
  int status;
983
 
 
984
 
  if ((status = execve(file.c_str(), argv, envp)) != 0)
985
 
    {
986
 
      strv_delete(argv);
987
 
      strv_delete(envp);
988
 
    }
989
 
 
990
 
  return status;
991
 
}
992
 
 
993
 
void
994
 
session::set_sighup_handler ()
995
 
{
996
 
  struct sigaction new_sa;
997
 
  sigemptyset(&new_sa.sa_mask);
998
 
  new_sa.sa_flags = 0;
999
 
  new_sa.sa_handler = sighup_handler;
1000
 
 
1001
 
  if (sigaction(SIGHUP, &new_sa, &this->saved_signals) != 0)
1002
 
    {
1003
 
      format fmt(_("failed to set hangup handler: %1%"));
1004
 
      fmt % strerror(errno);
1005
 
      throw error(fmt);
1006
 
    }
1007
 
}
1008
 
 
1009
 
void
1010
 
session::clear_sighup_handler ()
1011
 
{
1012
 
  /* Restore original handler */
1013
 
  sigaction (SIGHUP, &this->saved_signals, NULL);
1014
 
}
1015
 
 
1016
 
/*
1017
 
 * Local Variables:
1018
 
 * mode:C++
1019
 
 * End:
1020
 
 */