00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020 #include <config.h>
00021
00022 #include "sbuild-chroot-plain.h"
00023 #include "sbuild-chroot-lvm-snapshot.h"
00024 #include "sbuild-run-parts.h"
00025 #include "sbuild-session.h"
00026 #include "sbuild-util.h"
00027
00028 #include <cassert>
00029 #include <cerrno>
00030 #include <cstdlib>
00031 #include <cstring>
00032 #include <iostream>
00033 #include <memory>
00034
00035 #include <sys/types.h>
00036 #include <sys/stat.h>
00037 #include <fcntl.h>
00038 #include <termios.h>
00039 #include <unistd.h>
00040
00041 #include <syslog.h>
00042
00043 #include <boost/format.hpp>
00044
00045 #include <uuid/uuid.h>
00046
00047 using std::cout;
00048 using std::endl;
00049 using boost::format;
00050 using namespace sbuild;
00051
00052 namespace
00053 {
00054
00055 typedef std::pair<sbuild::session::error_code,const char *> emap;
00056
00061 emap init_errors[] =
00062 {
00063
00064 emap(session::CHDIR, N_("Failed to change to directory '%1%'")),
00065
00066 emap(session::CHDIR_FB, N_("Falling back to directory '%4%'")),
00067 emap(session::CHILD_CORE, N_("Child dumped core")),
00068 emap(session::CHILD_FAIL, N_("Child exited abnormally (reason unknown; not a signal or core dump)")),
00069 emap(session::CHILD_FORK, N_("Failed to fork child")),
00070
00071 emap(session::CHILD_SIGNAL, N_("Child terminated by signal '%4%'")),
00072 emap(session::CHILD_WAIT, N_("Wait for child failed")),
00073
00074 emap(session::CHROOT, N_("Failed to change root to directory '%1%'")),
00075
00076 emap(session::CHROOT_ALIAS, N_("No chroot found matching name or alias '%1%'")),
00077 emap(session::CHROOT_LOCK, N_("Failed to lock chroot")),
00078 emap(session::CHROOT_SETUP, N_("Chroot setup failed")),
00079
00080 emap(session::CHROOT_UNKNOWN, N_("Failed to find chroot '%1%'")),
00081 emap(session::CHROOT_UNLOCK, N_("Failed to unlock chroot")),
00082
00083 emap(session::COMMAND_ABS, N_("Command \"%1%\" must have an absolute path")),
00084
00085 emap(session::EXEC, N_("Failed to execute \"%1%\"")),
00086
00087
00088
00089 emap(session::GROUP_GET_SUP, N_("Failed to get supplementary groups")),
00090
00091
00092
00093 emap(session::GROUP_GET_SUPC, N_("Failed to get supplementary group count")),
00094
00095 emap(session::GROUP_SET, N_("Failed to set group '%1%'")),
00096 emap(session::GROUP_SET_SUP, N_("Failed to set supplementary groups")),
00097
00098 emap(session::GROUP_UNKNOWN, N_("Group '%1%' not found")),
00099 emap(session::PAM, N_("PAM error")),
00100 emap(session::ROOT_DROP, N_("Failed to drop root permissions")),
00101
00102 emap(session::SHELL, N_("Shell '%1%' not available")),
00103
00104 emap(session::SHELL_FB, N_("Falling back to shell '%4%'")),
00105
00106 emap(session::SIGNAL_CATCH, N_("Caught signal '%4%'")),
00107
00108 emap(session::SIGNAL_SET, N_("Failed to set signal handler '%4%'")),
00109
00110 emap(session::USER_SET, N_("Failed to set user '%1%'")),
00111
00112
00113
00114 emap(session::USER_SWITCH, N_("(%1%->%2%): User switching is not permitted")),
00115 };
00116
00123 std::string
00124 getcwd ()
00125 {
00126 std::string cwd;
00127
00128 char *raw_cwd = ::getcwd (0, 0);
00129 if (raw_cwd)
00130 cwd = raw_cwd;
00131 else
00132 cwd = "/";
00133 free(raw_cwd);
00134
00135 return cwd;
00136 }
00137
00144 bool
00145 is_group_member (std::string const& group)
00146 {
00147 errno = 0;
00148 struct group *groupbuf = getgrnam(group.c_str());
00149 if (groupbuf == 0)
00150 {
00151 if (errno == 0)
00152 {
00153 session::error e(group, session::GROUP_UNKNOWN);
00154 log_exception_warning(e);
00155 }
00156 else
00157 {
00158 session::error e(group, session::GROUP_UNKNOWN, strerror(errno));
00159 log_exception_warning(e);
00160 }
00161 return false;
00162 }
00163
00164 bool group_member = false;
00165 if (groupbuf->gr_gid == getgid())
00166 {
00167 group_member = true;
00168 }
00169 else
00170 {
00171 int supp_group_count = getgroups(0, 0);
00172 if (supp_group_count < 0)
00173 throw session::error(session::GROUP_GET_SUPC, strerror(errno));
00174 if (supp_group_count > 0)
00175 {
00176 gid_t *supp_groups = new gid_t[supp_group_count];
00177 assert (supp_groups);
00178 if (getgroups(supp_group_count, supp_groups) < 1)
00179 throw session::error(session::GROUP_GET_SUP, strerror(errno));
00180
00181 for (int i = 0; i < supp_group_count; ++i)
00182 {
00183 if (groupbuf->gr_gid == supp_groups[i])
00184 group_member = true;
00185 }
00186 delete[] supp_groups;
00187 }
00188 }
00189
00190 return group_member;
00191 }
00192
00193 volatile bool sighup_called = false;
00194 volatile bool sigterm_called = false;
00195
00201 void
00202 sighup_handler (int ignore)
00203 {
00204
00205 sighup_called = true;
00206 }
00207
00213 void
00214 sigterm_handler (int ignore)
00215 {
00216
00217 sigterm_called = true;
00218 }
00219
00220 #ifdef SBUILD_DEBUG
00221 volatile bool child_wait = true;
00222 #endif
00223
00224 }
00225
00226 template<>
00227 error<session::error_code>::map_type
00228 error<session::error_code>::error_strings
00229 (init_errors,
00230 init_errors + (sizeof(init_errors) / sizeof(init_errors[0])));
00231
00232 session::session (std::string const& service,
00233 config_ptr& config,
00234 operation operation,
00235 sbuild::string_list const& chroots):
00236 auth(service),
00237 config(config),
00238 chroots(chroots),
00239 chroot_status(true),
00240 child_status(0),
00241 session_operation(operation),
00242 session_id(),
00243 force(false),
00244 saved_sighup_signal(),
00245 saved_sigterm_signal(),
00246 saved_termios(),
00247 termios_ok(false),
00248 cwd(getcwd())
00249 {
00250 }
00251
00252 session::~session ()
00253 {
00254 }
00255
00256 session::config_ptr const&
00257 session::get_config () const
00258 {
00259 return this->config;
00260 }
00261
00262 void
00263 session::set_config (config_ptr& config)
00264 {
00265 this->config = config;
00266 }
00267
00268 string_list const&
00269 session::get_chroots () const
00270 {
00271 return this->chroots;
00272 }
00273
00274 void
00275 session::set_chroots (string_list const& chroots)
00276 {
00277 this->chroots = chroots;
00278 }
00279
00280 session::operation
00281 session::get_operation () const
00282 {
00283 return this->session_operation;
00284 }
00285
00286 void
00287 session::set_operation (operation operation)
00288 {
00289 this->session_operation = operation;
00290 }
00291
00292 std::string const&
00293 session::get_session_id () const
00294 {
00295 return this->session_id;
00296 }
00297
00298 void
00299 session::set_session_id (std::string const& session_id)
00300 {
00301 this->session_id = session_id;
00302 }
00303
00304 bool
00305 session::get_force () const
00306 {
00307 return this->force;
00308 }
00309
00310 void
00311 session::set_force (bool force)
00312 {
00313 this->force = force;
00314 }
00315
00316 void
00317 session::save_termios ()
00318 {
00319 int ctty = open("/dev/tty", O_RDONLY|O_NOCTTY);
00320 string_list const& command(auth::get_command());
00321
00322 this->termios_ok = false;
00323
00324
00325 if (ctty >= 0 &&
00326 (command.empty() || command[0].empty()))
00327 {
00328 if (tcgetattr(ctty, &this->saved_termios) < 0)
00329 {
00330 sbuild::log_warning()
00331 << _("Error saving terminal settings")
00332 << endl;
00333 }
00334 else
00335 this->termios_ok = true;
00336 }
00337
00338 if (ctty >= 0 && close(ctty))
00339 log_debug(DEBUG_WARNING) << "Failed to close CTTY fd " << ctty << endl;
00340 }
00341
00342 void
00343 session::restore_termios ()
00344 {
00345 int ctty = open("/dev/tty", O_WRONLY|O_NOCTTY);
00346 string_list const& command(auth::get_command());
00347
00348
00349
00350 if (ctty >= 0 &&
00351 (command.empty() || command[0].empty()) &&
00352 termios_ok)
00353 {
00354 if (tcsetattr(ctty, TCSANOW, &this->saved_termios) < 0)
00355 sbuild::log_warning()
00356 << _("Error restoring terminal settings")
00357 << endl;
00358 }
00359
00360 if (ctty >= 0 && close(ctty))
00361 log_debug(DEBUG_WARNING) << "Failed to close CTTY fd " << ctty << endl;
00362 }
00363
00364 int
00365 session::get_child_status () const
00366 {
00367 return this->child_status;
00368 }
00369
00370 auth::status
00371 session::get_chroot_auth_status (auth::status status,
00372 chroot::ptr const& chroot) const
00373 {
00374 string_list const& users = chroot->get_users();
00375 string_list const& root_users = chroot->get_root_users();
00376 string_list const& groups = chroot->get_groups();
00377 string_list const& root_groups = chroot->get_root_groups();
00378
00379 bool in_users = false;
00380 bool in_root_users = false;
00381 bool in_groups = false;
00382 bool in_root_groups = false;
00383
00384 sbuild::string_list::const_iterator upos =
00385 find(users.begin(), users.end(), get_ruser());
00386 if (upos != users.end())
00387 in_users = true;
00388
00389 sbuild::string_list::const_iterator rupos =
00390 find(root_users.begin(), root_users.end(), get_ruser());
00391 if (rupos != root_users.end())
00392 in_root_users = true;
00393
00394 if (!groups.empty())
00395 {
00396 for (string_list::const_iterator gp = groups.begin();
00397 gp != groups.end();
00398 ++gp)
00399 if (is_group_member(*gp))
00400 in_groups = true;
00401 }
00402
00403 if (!root_groups.empty())
00404 {
00405 for (string_list::const_iterator gp = root_groups.begin();
00406 gp != root_groups.end();
00407 ++gp)
00408 if (is_group_member(*gp))
00409 in_root_groups = true;
00410 }
00411
00412 log_debug(DEBUG_INFO)
00413 << "In users: " << in_users << endl
00414 << "In groups: " << in_groups << endl
00415 << "In root-users: " << in_root_users << endl
00416 << "In root-groups: " << in_root_groups << endl;
00417
00418
00419
00420
00421
00422
00423 if ((in_users == true || in_groups == true ||
00424 in_root_users == true || in_root_groups == true) &&
00425 this->get_ruid() == this->get_uid())
00426 {
00427 status = change_auth(status, auth::STATUS_NONE);
00428 }
00429 else if ((in_root_users == true || in_root_groups == true) &&
00430 this->get_uid() == 0)
00431 {
00432 status = change_auth(status, auth::STATUS_NONE);
00433 }
00434 else if (in_users == true || in_groups == true)
00435
00436 {
00437 status = change_auth(status, auth::STATUS_USER);
00438 }
00439 else
00440 {
00441 if (this->get_ruid() == 0)
00442 status = change_auth(status, auth::STATUS_USER);
00443 else
00444 status = change_auth(status, auth::STATUS_FAIL);
00445 }
00446
00447 return status;
00448 }
00449
00450 auth::status
00451 session::get_auth_status () const
00452 {
00453 assert(!this->chroots.empty());
00454 if (this->config.get() == 0) return auth::STATUS_FAIL;
00455
00456
00457
00458
00459
00460
00461
00462
00463
00464 auth::status status = auth::STATUS_NONE;
00465
00466
00467
00468 for (string_list::const_iterator cur = this->chroots.begin();
00469 cur != this->chroots.end();
00470 ++cur)
00471 {
00472 const chroot::ptr chroot = this->config->find_alias(*cur);
00473 if (!chroot)
00474 {
00475 error e(*cur, CHROOT_ALIAS);
00476 log_exception_warning(e);
00477 status = change_auth(status, auth::STATUS_FAIL);
00478 }
00479
00480 status = change_auth(status, get_chroot_auth_status(status, chroot));
00481 }
00482
00483 return status;
00484 }
00485
00486 void
00487 session::run_impl ()
00488 {
00489 assert(this->config.get() != 0);
00490 assert(!this->chroots.empty());
00491
00492 try
00493 {
00494 sighup_called = false;
00495 set_sighup_handler();
00496 sigterm_called = false;
00497 set_sigterm_handler();
00498
00499 for (string_list::const_iterator cur = this->chroots.begin();
00500 cur != this->chroots.end();
00501 ++cur)
00502 {
00503 log_debug(DEBUG_NOTICE)
00504 << format("Running session in %1% chroot:") % *cur
00505 << endl;
00506
00507 const chroot::ptr ch = this->config->find_alias(*cur);
00508 if (!ch)
00509 throw error(*cur, CHROOT_UNKNOWN);
00510
00511 chroot::ptr chroot(ch->clone());
00512
00513
00514
00515
00516
00517 if (chroot->get_active() ||
00518 !(chroot->get_session_flags() & chroot::SESSION_CREATE))
00519 {
00520 set_session_id(chroot->get_name());
00521 }
00522 else
00523 {
00524 uuid_t uuid;
00525 char uuid_str[37];
00526 uuid_generate(uuid);
00527 uuid_unparse(uuid, uuid_str);
00528 uuid_clear(uuid);
00529 std::string session_id(chroot->get_name() + "-" + uuid_str);
00530 set_session_id(session_id);
00531 }
00532
00533 log_debug(DEBUG_INFO)
00534 << format("Session ID: %1%") % get_session_id() << endl;
00535
00536
00537 chroot->set_active(true);
00538
00539
00540
00541
00542 {
00543 chroot_plain *plain = dynamic_cast<chroot_plain *>(chroot.get());
00544 if (chroot->get_mount_location().empty() &&
00545 (plain == 0 || plain->get_run_setup_scripts() == true))
00546 {
00547 std::string location(std::string(SCHROOT_MOUNT_DIR) + "/" +
00548 this->session_id);
00549 chroot->set_mount_location(location);
00550 }
00551 }
00552
00553 log_debug(DEBUG_NOTICE)
00554 << format("Mount Location: %1%") % chroot->get_mount_location()
00555 << endl;
00556
00557
00558
00559 if (chroot->get_session_flags() & chroot::SESSION_CREATE)
00560 {
00561 chroot->set_name(this->session_id);
00562 chroot->set_aliases(string_list());
00563 }
00564
00565
00566 chroot_lvm_snapshot *snapshot = 0;
00567 if ((snapshot = dynamic_cast<chroot_lvm_snapshot *>(chroot.get())) != 0)
00568 {
00569 std::string dir(dirname(snapshot->get_device(), '/'));
00570 std::string device(dir + "/" + this->session_id);
00571 snapshot->set_snapshot_device(device);
00572 }
00573
00574 try
00575 {
00576
00577 setup_chroot(chroot, chroot::SETUP_START);
00578 if (this->session_operation == OPERATION_BEGIN)
00579 cout << this->session_id << endl;
00580
00581
00582 setup_chroot(chroot, chroot::SETUP_RECOVER);
00583
00584 try
00585 {
00586
00587 setup_chroot(chroot, chroot::EXEC_START);
00588
00589
00590 if (this->session_operation == OPERATION_AUTOMATIC ||
00591 this->session_operation == OPERATION_RUN)
00592 {
00593 try
00594 {
00595 open_session();
00596 save_termios();
00597 run_chroot(chroot);
00598 }
00599 catch (std::runtime_error const& e)
00600 {
00601 log_debug(DEBUG_WARNING)
00602 << "Chroot session failed" << endl;
00603 restore_termios();
00604 close_session();
00605 throw;
00606 }
00607 restore_termios();
00608 close_session();
00609 }
00610
00611 }
00612 catch (error const& e)
00613 {
00614 log_debug(DEBUG_WARNING)
00615 << "Chroot exec scripts or session failed" << endl;
00616 setup_chroot(chroot, chroot::EXEC_STOP);
00617 throw;
00618 }
00619
00620
00621
00622 setup_chroot(chroot, chroot::EXEC_STOP);
00623 }
00624 catch (error const& e)
00625 {
00626 log_debug(DEBUG_WARNING)
00627 << "Chroot setup scripts, exec scripts or session failed" << endl;
00628 try
00629 {
00630 setup_chroot(chroot, chroot::SETUP_STOP);
00631 }
00632 catch (error const& discard)
00633 {
00634 log_debug(DEBUG_WARNING)
00635 << "Chroot setup scripts failed during stop" << endl;
00636 }
00637 chroot->set_active(false);
00638 throw;
00639 }
00640
00641
00642
00643 setup_chroot(chroot, chroot::SETUP_STOP);
00644
00645
00646 chroot->set_active(false);
00647 }
00648 }
00649 catch (error const& e)
00650 {
00651 clear_sigterm_handler();
00652 clear_sighup_handler();
00653
00654
00655
00656 if (this->child_status == 0)
00657 this->child_status = EXIT_FAILURE;
00658 throw;
00659 }
00660
00661 clear_sigterm_handler();
00662 clear_sighup_handler();
00663 }
00664
00665 string_list
00666 session::get_login_directories () const
00667 {
00668 string_list ret;
00669
00670 std::string const& wd(get_wd());
00671 if (!wd.empty())
00672 {
00673
00674 ret.push_back(wd);
00675 }
00676 else
00677 {
00678
00679 ret.push_back(this->cwd);
00680
00681
00682 environment env = get_pam_environment();
00683 std::string home;
00684 if (env.get("HOME", home) &&
00685 std::find(ret.begin(), ret.end(), home) == ret.end())
00686 ret.push_back(home);
00687
00688
00689 if (std::find(ret.begin(), ret.end(), get_home()) == ret.end())
00690 ret.push_back(get_home());
00691
00692
00693 if (std::find(ret.begin(), ret.end(), "/") == ret.end())
00694 ret.push_back("/");
00695 }
00696
00697 return ret;
00698 }
00699
00700 string_list
00701 session::get_command_directories () const
00702 {
00703 string_list ret;
00704
00705 std::string const& wd(get_wd());
00706 if (!wd.empty())
00707
00708 ret.push_back(wd);
00709 else
00710
00711 ret.push_back(this->cwd);
00712
00713 return ret;
00714 }
00715
00716 std::string
00717 session::get_shell () const
00718 {
00719 assert (!auth::get_shell().empty());
00720 std::string shell = auth::get_shell();
00721
00722 struct stat statbuf;
00723 if (stat(shell.c_str(), &statbuf) < 0)
00724 {
00725 if (shell != "/bin/sh")
00726 {
00727 error e1(shell, SHELL, strerror(errno));
00728 log_exception_warning(e1);
00729 shell = "/bin/sh";
00730 error e2(SHELL_FB, shell);
00731 log_exception_warning(e2);
00732 }
00733 }
00734
00735 return shell;
00736 }
00737
00738 void
00739 session::get_command (sbuild::chroot::ptr& session_chroot,
00740 std::string& file,
00741 string_list& command) const
00742 {
00743
00744 if (command.empty() ||
00745 command[0].empty())
00746 get_login_command(session_chroot, file, command);
00747 else
00748 get_user_command(session_chroot, file, command);
00749 }
00750
00751 void
00752 session::get_login_command (sbuild::chroot::ptr& session_chroot,
00753 std::string& file,
00754 string_list& command) const
00755 {
00756 command.clear();
00757
00758 std::string shell = get_shell();
00759 file = shell;
00760
00761 if (get_environment().empty() &&
00762 session_chroot->get_command_prefix().empty())
00763
00764 {
00765 std::string shellbase = basename(shell, '/');
00766 std::string loginshell = "-" + shellbase;
00767 command.push_back(loginshell);
00768
00769 log_debug(DEBUG_NOTICE)
00770 << format("Running login shell: %1%") % shell << endl;
00771 if (get_uid() == 0 || get_ruid() != get_uid())
00772 syslog(LOG_USER|LOG_NOTICE,
00773 "[%s chroot] (%s->%s) Running login shell: '%s'",
00774 session_chroot->get_name().c_str(),
00775 get_ruser().c_str(), get_user().c_str(),
00776 shell.c_str());
00777 }
00778 else
00779 {
00780 command.push_back(shell);
00781 log_debug(DEBUG_NOTICE)
00782 << format("Running shell: %1%") % shell << endl;
00783 if (get_uid() == 0 || get_ruid() != get_uid())
00784 syslog(LOG_USER|LOG_NOTICE,
00785 "[%s chroot] (%s->%s) Running shell: '%s'",
00786 session_chroot->get_name().c_str(),
00787 get_ruser().c_str(), get_user().c_str(),
00788 shell.c_str());
00789 }
00790
00791 if (get_verbosity() != auth::VERBOSITY_QUIET)
00792 {
00793 std::string format_string;
00794 if (get_ruid() == get_uid())
00795 {
00796 if (get_environment().empty() &&
00797 session_chroot->get_command_prefix().empty())
00798
00799
00800 format_string = _("[%1% chroot] Running login shell: '%4%'");
00801 else
00802
00803
00804 format_string = _("[%1% chroot] Running shell: '%4%'");
00805 }
00806 else
00807 {
00808 if (get_environment().empty() &&
00809 session_chroot->get_command_prefix().empty())
00810
00811
00812
00813
00814
00815 format_string = _("[%1% chroot] (%2%->%3%) Running login shell: '%4%'");
00816 else
00817
00818
00819
00820
00821
00822 format_string = _("[%1% chroot] (%2%->%3%) Running shell: '%4%'");
00823 }
00824
00825 format fmt(format_string);
00826 fmt % session_chroot->get_name()
00827 % get_ruser() % get_user()
00828 % shell;
00829 log_info() << fmt << endl;
00830 }
00831 }
00832
00833 void
00834 session::get_user_command (sbuild::chroot::ptr& session_chroot,
00835 std::string& file,
00836 string_list& command) const
00837 {
00838
00839 environment env = get_pam_environment();
00840 std::string path;
00841 if (!env.get("PATH", path))
00842 path.clear();
00843
00844 file = find_program_in_path(command[0], path, "");
00845 if (file.empty())
00846 file = command[0];
00847 std::string commandstring = string_list_to_string(command, " ");
00848 log_debug(DEBUG_NOTICE)
00849 << format("Running command: %1%") % commandstring << endl;
00850 if (get_uid() == 0 || get_ruid() != get_uid())
00851 syslog(LOG_USER|LOG_NOTICE, "[%s chroot] (%s->%s) Running command: \"%s\"",
00852 session_chroot->get_name().c_str(), get_ruser().c_str(), get_user().c_str(), commandstring.c_str());
00853
00854 if (get_verbosity() != auth::VERBOSITY_QUIET)
00855 {
00856 std::string format_string;
00857 if (get_ruid() == get_uid())
00858
00859
00860 format_string = _("[%1% chroot] Running command: \"%4%\"");
00861 else
00862
00863
00864
00865
00866
00867 format_string = (_("[%1% chroot] (%2%->%3%) Running command: \"%4%\""));
00868
00869 format fmt(format_string);
00870 fmt % session_chroot->get_name()
00871 % get_ruser() % get_user()
00872 % commandstring;
00873 log_info() << fmt << endl;
00874 }
00875 }
00876
00877 void
00878 session::setup_chroot (sbuild::chroot::ptr& session_chroot,
00879 sbuild::chroot::setup_type setup_type)
00880 {
00881 assert(!session_chroot->get_name().empty());
00882
00883 if (!((this->session_operation == OPERATION_BEGIN &&
00884 setup_type == chroot::SETUP_START) ||
00885 (this->session_operation == OPERATION_RECOVER &&
00886 setup_type == chroot::SETUP_RECOVER) ||
00887 (this->session_operation == OPERATION_END &&
00888 setup_type == chroot::SETUP_STOP) ||
00889 (this->session_operation == OPERATION_RUN &&
00890 (setup_type == chroot::EXEC_START ||
00891 setup_type == chroot::EXEC_STOP)) ||
00892 (this->session_operation == OPERATION_AUTOMATIC &&
00893 (setup_type == chroot::SETUP_START ||
00894 setup_type == chroot::SETUP_STOP ||
00895 setup_type == chroot::EXEC_START ||
00896 setup_type == chroot::EXEC_STOP))))
00897 return;
00898
00899 if (((setup_type == chroot::SETUP_START ||
00900 setup_type == chroot::SETUP_RECOVER ||
00901 setup_type == chroot::SETUP_STOP) &&
00902 session_chroot->get_run_setup_scripts() == false) ||
00903 ((setup_type == chroot::EXEC_START ||
00904 setup_type == chroot::EXEC_STOP) &&
00905 session_chroot->get_run_exec_scripts() == false))
00906 return;
00907
00908 if (setup_type == chroot::SETUP_START)
00909 this->chroot_status = true;
00910
00911 try
00912 {
00913 session_chroot->lock(setup_type);
00914 }
00915 catch (chroot::error const& e)
00916 {
00917 this->chroot_status = false;
00918 try
00919 {
00920
00921 session_chroot->unlock(setup_type, 0);
00922 }
00923 catch (chroot::error const& ignore)
00924 {
00925 }
00926 throw error(session_chroot->get_name(), CHROOT_LOCK, e);
00927 }
00928
00929 std::string setup_type_string;
00930 if (setup_type == chroot::SETUP_START)
00931 setup_type_string = "setup-start";
00932 else if (setup_type == chroot::SETUP_RECOVER)
00933 setup_type_string = "setup-recover";
00934 else if (setup_type == chroot::SETUP_STOP)
00935 setup_type_string = "setup-stop";
00936 else if (setup_type == chroot::EXEC_START)
00937 setup_type_string = "exec-start";
00938 else if (setup_type == chroot::EXEC_STOP)
00939 setup_type_string = "exec-stop";
00940
00941 std::string chroot_status_string;
00942 if (this->chroot_status)
00943 chroot_status_string = "ok";
00944 else
00945 chroot_status_string = "fail";
00946
00947 string_list arg_list;
00948 arg_list.push_back(setup_type_string);
00949 arg_list.push_back(chroot_status_string);
00950
00951
00952
00953
00954 environment env;
00955 session_chroot->setup_env(env);
00956 env.add("AUTH_USER", get_user());
00957 {
00958 const char *verbosity = 0;
00959 switch (get_verbosity())
00960 {
00961 case auth::VERBOSITY_QUIET:
00962 verbosity = "quiet";
00963 break;
00964 case auth::VERBOSITY_NORMAL:
00965 verbosity = "normal";
00966 break;
00967 case auth::VERBOSITY_VERBOSE:
00968 verbosity = "verbose";
00969 break;
00970 default:
00971 log_debug(DEBUG_CRITICAL) << format("Invalid verbosity level: %1%, falling back to 'normal'")
00972 % static_cast<int>(get_verbosity())
00973 << endl;
00974 verbosity = "normal";
00975 break;
00976 }
00977 env.add("AUTH_VERBOSITY", verbosity);
00978 }
00979
00980 env.add("MOUNT_DIR", SCHROOT_MOUNT_DIR);
00981 env.add("LIBEXEC_DIR", SCHROOT_LIBEXEC_DIR);
00982 env.add("PID", getpid());
00983 env.add("SESSION_ID", this->session_id);
00984
00985 run_parts rp((setup_type == chroot::SETUP_START ||
00986 setup_type == chroot::SETUP_RECOVER ||
00987 setup_type == chroot::SETUP_STOP)
00988 ? SCHROOT_CONF_SETUP_D
00989 : SCHROOT_CONF_EXEC_D,
00990 true, true, 022);
00991 rp.set_reverse((setup_type == chroot::SETUP_STOP ||
00992 setup_type == chroot::EXEC_STOP));
00993 rp.set_verbose(get_verbosity() == auth::VERBOSITY_VERBOSE);
00994
00995 log_debug(DEBUG_INFO) << rp << std::endl;
00996
00997 int exit_status = 0;
00998 pid_t pid;
00999
01000 if ((pid = fork()) == -1)
01001 {
01002 this->chroot_status = false;
01003 throw error(session_chroot->get_name(), CHILD_FORK, strerror(errno));
01004 }
01005 else if (pid == 0)
01006 {
01007 try
01008 {
01009
01010 closelog();
01011
01012 chdir("/");
01013
01014
01015
01016 setuid(0);
01017 setgid(0);
01018 initgroups("root", 0);
01019
01020 int status = rp.run(arg_list, env);
01021
01022 _exit (status);
01023 }
01024 catch (std::exception const& e)
01025 {
01026 sbuild::log_exception_error(e);
01027 }
01028 catch (...)
01029 {
01030 sbuild::log_error()
01031 << _("An unknown exception occurred") << std::endl;
01032 }
01033 _exit(EXIT_FAILURE);
01034 }
01035 else
01036 {
01037 wait_for_child(pid, exit_status);
01038 }
01039
01040 try
01041 {
01042 session_chroot->unlock(setup_type, exit_status);
01043 }
01044 catch (chroot::error const& e)
01045 {
01046 this->chroot_status = false;
01047 throw error(session_chroot->get_name(), CHROOT_UNLOCK, e);
01048 }
01049
01050 if (exit_status != 0)
01051 {
01052 this->chroot_status = false;
01053
01054 format fmt(_("stage=%1%"));
01055 fmt % setup_type_string;
01056 throw error(session_chroot->get_name(), CHROOT_SETUP, fmt.str());
01057 }
01058 }
01059
01060 void
01061 session::run_child (sbuild::chroot::ptr& session_chroot)
01062 {
01063 assert(!session_chroot->get_name().empty());
01064
01065 assert(!get_user().empty());
01066 assert(!get_shell().empty());
01067 assert(auth::pam != 0);
01068
01069
01070 this->cwd = getcwd();
01071 log_debug(DEBUG_INFO) << "CWD=" << this->cwd << std::endl;
01072
01073 std::string location(session_chroot->get_path());
01074 log_debug(DEBUG_INFO) << "location=" << location << std::endl;
01075
01076
01077 if (setgid (get_gid()))
01078 throw error(get_gid(), GROUP_SET, strerror(errno));
01079 log_debug(DEBUG_NOTICE) << "Set GID=" << get_gid() << std::endl;
01080 if (initgroups (get_user().c_str(), get_gid()))
01081 throw error(GROUP_SET_SUP, strerror(errno));
01082 log_debug(DEBUG_NOTICE) << "Set supplementary groups" << std::endl;
01083
01084
01085
01086 session_chroot->get_persona().set();
01087 log_debug(DEBUG_NOTICE) << "Set personality="
01088 << session_chroot->get_persona()<< std::endl;
01089
01090
01091 if (chdir (location.c_str()))
01092 throw error(location, CHDIR, strerror(errno));
01093 log_debug(DEBUG_NOTICE) << "Changed directory to " << location << std::endl;
01094 if (::chroot (location.c_str()))
01095 throw error(location, CHROOT, strerror(errno));
01096 log_debug(DEBUG_NOTICE) << "Changed root to " << location << std::endl;
01097
01098
01099 if (setuid (get_uid()))
01100 throw error(get_uid(), USER_SET, strerror(errno));
01101 log_debug(DEBUG_NOTICE) << "Set UID=" << get_uid() << std::endl;
01102 if (!setuid (0) && get_uid())
01103 throw error(ROOT_DROP);
01104 if (get_uid())
01105 log_debug(DEBUG_NOTICE) << "Dropped root privileges" << std::endl;
01106
01107 std::string file;
01108 string_list command(auth::get_command());
01109
01110 string_list dlist;
01111 if (command.empty() ||
01112 command[0].empty())
01113 dlist = get_login_directories();
01114 else
01115 dlist = get_command_directories();
01116 log_debug(DEBUG_INFO)
01117 << format("Directory fallbacks: %1%") % string_list_to_string(dlist, ", ") << endl;
01118
01119
01120 for (string_list::const_iterator dpos = dlist.begin();
01121 dpos != dlist.end();
01122 ++dpos)
01123 {
01124 if (chdir ((*dpos).c_str()) < 0)
01125 {
01126 error e(*dpos, CHDIR, strerror(errno));
01127
01128 if (dpos + 1 == dlist.end())
01129 throw e;
01130 else
01131 log_exception_warning(e);
01132 }
01133 else
01134 {
01135 log_debug(DEBUG_NOTICE) << "Changed directory to "
01136 << *dpos << std::endl;
01137 if (dpos != dlist.begin())
01138 {
01139 error e(CHDIR_FB, *dpos);
01140 log_exception_warning(e);
01141 }
01142 break;
01143 }
01144 }
01145
01146
01147 get_command(session_chroot, file, command);
01148 log_debug(DEBUG_NOTICE) << "command="
01149 << string_list_to_string(command, ", ")
01150 << std::endl;
01151
01152
01153 environment env = get_pam_environment();
01154 log_debug(DEBUG_INFO) << "Set environment:\n" << env;
01155
01156
01157 closelog();
01158
01159
01160 string_list full_command(session_chroot->get_command_prefix());
01161 if (full_command.size() > 0)
01162 file = full_command[0];
01163 for (string_list::const_iterator pos = command.begin();
01164 pos != command.end();
01165 ++pos)
01166 full_command.push_back(*pos);
01167
01168
01169 if (exec (file, full_command, env))
01170 throw error(file, EXEC, strerror(errno));
01171
01172
01173 _exit(EXIT_FAILURE);
01174 }
01175
01176 void
01177 session::wait_for_child (pid_t pid,
01178 int& child_status)
01179 {
01180 child_status = EXIT_FAILURE;
01181
01182 int status;
01183 bool child_killed = false;
01184
01185 while (1)
01186 {
01187 if ((sighup_called || sigterm_called) && !child_killed)
01188 {
01189 if (sighup_called)
01190 {
01191 error e(SIGNAL_CATCH, strsignal(SIGHUP),
01192 _("terminating immediately"));
01193 log_exception_error(e);
01194 kill(pid, SIGHUP);
01195 }
01196 else
01197 {
01198 error e(SIGNAL_CATCH, strsignal(SIGTERM),
01199 _("terminating immediately"));
01200 log_exception_error(e);
01201 kill(pid, SIGTERM);
01202 }
01203 this->chroot_status = false;
01204 child_killed = true;
01205 }
01206
01207 if (wait(&status) != pid)
01208 {
01209 if (errno == EINTR && (sighup_called || sigterm_called))
01210 continue;
01211 else
01212 throw error(CHILD_WAIT, strerror(errno));
01213 }
01214 else if (sighup_called)
01215 {
01216 sighup_called = false;
01217 throw error(SIGNAL_CATCH, strsignal(SIGHUP));
01218 }
01219 else if (sigterm_called)
01220 {
01221 sigterm_called = false;
01222 throw error(SIGNAL_CATCH, strsignal(SIGTERM));
01223 }
01224 else
01225 break;
01226 }
01227
01228 if (!WIFEXITED(status))
01229 {
01230 if (WIFSIGNALED(status))
01231 throw error(CHILD_SIGNAL, strsignal(WTERMSIG(status)));
01232 else if (WCOREDUMP(status))
01233 throw error(CHILD_CORE);
01234 else
01235 throw error(CHILD_FAIL);
01236 }
01237
01238 child_status = WEXITSTATUS(status);
01239 }
01240
01241 void
01242 session::run_chroot (sbuild::chroot::ptr& session_chroot)
01243 {
01244 assert(!session_chroot->get_name().empty());
01245
01246 pid_t pid;
01247 if ((pid = fork()) == -1)
01248 {
01249 throw error(CHILD_FORK, strerror(errno));
01250 }
01251 else if (pid == 0)
01252 {
01253 #ifdef SBUILD_DEBUG
01254 while (child_wait)
01255 ;
01256 #endif
01257 try
01258 {
01259 run_child(session_chroot);
01260 }
01261 catch (std::runtime_error const& e)
01262 {
01263 log_exception_error(e);
01264 }
01265 catch (...)
01266 {
01267 sbuild::log_error()
01268 << _("An unknown exception occurred") << std::endl;
01269 }
01270 _exit (EXIT_FAILURE);
01271 }
01272 else
01273 {
01274 wait_for_child(pid, this->child_status);
01275 }
01276 }
01277
01278 void
01279 session::set_sighup_handler ()
01280 {
01281 set_signal_handler(SIGHUP, &this->saved_sighup_signal, sighup_handler);
01282 }
01283
01284 void
01285 session::clear_sighup_handler ()
01286 {
01287 clear_signal_handler(SIGHUP, &this->saved_sighup_signal);
01288 }
01289
01290 void
01291 session::set_sigterm_handler ()
01292 {
01293 set_signal_handler(SIGTERM, &this->saved_sigterm_signal, sigterm_handler);
01294 }
01295
01296 void
01297 session::clear_sigterm_handler ()
01298 {
01299 clear_signal_handler(SIGTERM, &this->saved_sigterm_signal);
01300 }
01301
01302 void
01303 session::set_signal_handler (int signal,
01304 struct sigaction *saved_signal,
01305 void (*handler)(int))
01306 {
01307 struct sigaction new_sa;
01308 sigemptyset(&new_sa.sa_mask);
01309 new_sa.sa_flags = 0;
01310 new_sa.sa_handler = handler;
01311
01312 if (sigaction(signal, &new_sa, saved_signal) != 0)
01313 throw error(SIGNAL_SET, strsignal(signal), strerror(errno));
01314 }
01315
01316 void
01317 session::clear_signal_handler (int signal,
01318 struct sigaction *saved_signal)
01319 {
01320
01321 sigaction (signal, saved_signal, 0);
01322 }