blob: c9c2efe3b48ce69f3cbed463ab4182228448f06d [file] [log] [blame]
// Copyright (c) 2014 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "login_manager/child_job.h"
#include <errno.h>
#include <grp.h>
#include <pwd.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <base/logging.h>
#include <base/time/time.h>
#include <base/memory/scoped_ptr.h>
#include "login_manager/session_manager_service.h"
#include "login_manager/system_utils.h"
namespace login_manager {
const int ChildJobInterface::kCantSetUid = 127;
const int ChildJobInterface::kCantSetGid = 128;
const int ChildJobInterface::kCantSetGroups = 129;
const int ChildJobInterface::kCantSetEnv = 130;
const int ChildJobInterface::kCantExec = 255;
ChildJobInterface::Subprocess::Subprocess(uid_t desired_uid,
SystemUtils* system)
: pid_(-1),
desired_uid_(desired_uid),
system_(system) {
}
ChildJobInterface::Subprocess::~Subprocess() {}
bool ChildJobInterface::Subprocess::ForkAndExec(
const std::vector<std::string>& args,
const std::map<std::string, std::string>& environment_variables) {
pid_ = system_->fork();
if (pid_ == 0) {
SessionManagerService::RevertHandlers();
// We try to set our UID/GID to the desired UID, and then exec
// the command passed in.
int exit_code = SetIDs();
if (exit_code)
exit(exit_code);
logging::CloseLogFile(); // So browser does not inherit logging FD.
if (clearenv() != 0) {
PLOG(ERROR) << "Error clearing environment";
exit(kCantSetEnv);
}
for (std::map<std::string, std::string>::const_iterator it =
environment_variables.begin();
it != environment_variables.end(); ++it) {
if (setenv(it->first.c_str(), it->second.c_str(), 1) != 0) {
PLOG(ERROR) << "Error exporting " << it->first << "=" << it->second;
exit(kCantSetEnv);
}
}
scoped_ptr<char const*[]> argv(new char const*[args.size() + 1]);
for (size_t i = 0; i < args.size(); ++i)
argv[i] = args[i].c_str();
argv[args.size()] = 0;
execv(argv[0], const_cast<char* const*>(argv.get()));
// Should never get here, unless we couldn't exec the command.
PLOG(ERROR) << "Error executing " << argv[0];
exit(kCantExec);
} else if (pid_ < 0) {
return false;
}
return true;
}
void ChildJobInterface::Subprocess::KillEverything(int signal) {
DCHECK_GT(pid_, 0);
system_->kill(-pid_, desired_uid_, signal);
}
void ChildJobInterface::Subprocess::Kill(int signal) {
DCHECK_GT(pid_, 0);
system_->kill(pid_, desired_uid_, signal);
}
int ChildJobInterface::Subprocess::SetIDs() {
int to_return = 0;
struct passwd* entry = getpwuid(desired_uid_);
endpwent();
if (initgroups(entry->pw_name, entry->pw_gid) == -1)
to_return = kCantSetGroups;
if (setgid(entry->pw_gid) == -1)
to_return = kCantSetGid;
if (setuid(desired_uid_) == -1)
to_return = kCantSetUid;
if (setsid() == -1)
LOG(ERROR) << "can't setsid: " << strerror(errno);
return to_return;
}
}; // namespace login_manager