blob: 2a59dcc1b934e6ff0180effd9ebf8fef2defe12e [file] [log] [blame]
// Copyright 2016 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 <linux/capability.h>
#include <pwd.h>
#include <sys/capability.h>
#include <sys/prctl.h>
#include <sysexits.h>
#include <memory>
#include <base/at_exit.h>
#include <base/sys_info.h>
#include <brillo/syslog_logging.h>
#include <brillo/daemons/dbus_daemon.h>
#include <chromeos/dbus/service_constants.h>
#include <install_attributes/libinstallattributes.h>
#include "authpolicy/authpolicy.h"
#include "authpolicy/constants.h"
namespace ac = authpolicy::constants;
uid_t ac::kAuthPolicydUid = ac::kInvalidUid;
uid_t ac::kAuthPolicyExecUid = ac::kInvalidUid;
namespace {
const char kObjectServicePath[] = "/org/chromium/AuthPolicy/ObjectManager";
const char kChromeOSReleaseTrack[] = "CHROMEOS_RELEASE_TRACK";
const char kBetaChannel[] = "beta-channel";
const char kStableChannel[] = "stable-channel";
const int kDefaultBufLen = 65536;
const char kAuthPolicydUser[] = "authpolicyd";
const char kAuthPolicydExecUser[] = "authpolicyd-exec";
// Gets a user id by name.
bool GetUserId(const char* user_name, uid_t* user_id) {
// Load the passwd entry.
int buf_len = sysconf(_SC_GETPW_R_SIZE_MAX);
if (buf_len == -1)
buf_len = kDefaultBufLen;
struct passwd user_info, *user_infop;
std::vector<char> buf(buf_len);
if (getpwnam_r(user_name, &user_info, buf.data(), buf_len, &user_infop))
return false;
*user_id = user_info.pw_uid;
return true;
}
// Sets authpolicy-exec as saved uid and drops caps. This way, Samba executables
// can be executed as authpolicy-exec even without keeping caps around. That's
// more secure.
bool SetSavedUserAndDropCaps() {
// Initialize user ids.
if (!GetUserId(kAuthPolicydUser, &ac::kAuthPolicydUid) ||
!GetUserId(kAuthPolicydExecUser, &ac::kAuthPolicyExecUid)) {
PLOG(ERROR) << "Failed to verify user ids";
return false;
}
DCHECK_NE(ac::kAuthPolicydUid, ac::kInvalidUid);
DCHECK_NE(ac::kAuthPolicyExecUid, ac::kInvalidUid);
// Set authpolicyd-exec as saved uid.
if (setresuid(ac::kAuthPolicydUid, ac::kAuthPolicydUid,
ac::kAuthPolicyExecUid)) {
PLOG(ERROR) << "setresuid failed";
return false;
}
// Drop capabilities from bounding set.
if (prctl(PR_CAPBSET_DROP, CAP_SETUID) ||
prctl(PR_CAPBSET_DROP, CAP_SETPCAP)) {
PLOG(ERROR) << "Failed to drop caps from bounding set";
return false;
}
// Clear capabilities.
cap_t caps = cap_get_proc();
if (!caps ||
cap_clear_flag(caps, CAP_INHERITABLE) ||
cap_clear_flag(caps, CAP_EFFECTIVE) ||
cap_clear_flag(caps, CAP_PERMITTED) ||
cap_set_proc(caps)) {
PLOG(ERROR) << "Clearing caps failed";
return false;
}
return true;
}
} // namespace
namespace authpolicy {
class Daemon : public brillo::DBusServiceDaemon {
public:
explicit Daemon(bool expect_config)
: DBusServiceDaemon(kAuthPolicyServiceName, kObjectServicePath),
expect_config_(expect_config) {}
protected:
void RegisterDBusObjectsAsync(AsyncEventSequencer* sequencer) override {
auth_policy_.reset(new AuthPolicy(object_manager_.get()));
auth_policy_->RegisterAsync(
sequencer->GetHandler("AuthPolicy.RegisterAsync() failed.", true));
}
void OnShutdown(int* return_code) override {
DBusServiceDaemon::OnShutdown(return_code);
auth_policy_.reset();
}
int OnInit() override {
int return_code = brillo::DBusServiceDaemon::OnInit();
if (return_code != EX_OK)
return return_code;
if (!auth_policy_->Initialize(expect_config_)) {
// Exit with "success" to prevent respawn by upstart.
exit(0);
}
return EX_OK;
}
private:
bool expect_config_;
std::unique_ptr<AuthPolicy> auth_policy_;
DISALLOW_COPY_AND_ASSIGN(Daemon);
};
} // namespace authpolicy
int main(int argc, const char* const* argv) {
brillo::OpenLog("authpolicyd", true);
brillo::InitLog(brillo::kLogToSyslog);
// Make it possible to switch to authpolicyd-exec without caps and drop caps.
if (!SetSavedUserAndDropCaps()) {
// Exit with "success" to prevent respawn by upstart.
exit(0);
}
// Disable on beta and stable (for now).
// TODO(ljusten): Reenable after launch reviews, see crbug.com/668119.
{
// base::SysInfo internally creates a singleton that adds itself to the
// current AtExitManager, so that it is destroyed when the manager goes out
// of scope. Daemon creates its own, so it has to be destroyed before the
// daemon is run.
base::AtExitManager at_exit_manager;
std::string channel;
if (!base::SysInfo::GetLsbReleaseValue(kChromeOSReleaseTrack, &channel)) {
LOG(ERROR) << "Failed to retrieve release track from sys info.";
// Exit with "success" to prevent respawn by upstart.
exit(0);
}
if (channel == kBetaChannel || channel == kStableChannel) {
LOG(ERROR) << "Not allowed to run on '" << kBetaChannel << "' and '"
<< kStableChannel << "'.";
// Exit with "success" to prevent respawn by upstart.
exit(0);
}
}
// Safety check to ensure that authpolicyd cannot run after the device has
// been locked to a mode other than enterprise_ad. (The lifetime management
// of authpolicyd happens through upstart, this check only serves as a second
// line of defense.)
bool expect_config = false;
InstallAttributesReader install_attributes_reader;
if (install_attributes_reader.IsLocked()) {
const std::string& mode =
install_attributes_reader.GetAttribute(
InstallAttributesReader::kAttrMode);
if (mode != InstallAttributesReader::kDeviceModeEnterpriseAD) {
LOG(ERROR) << "OOBE completed but device not in Active Directory "
"management mode.";
// Exit with "success" to prevent respawn by upstart.
exit(0);
} else {
LOG(INFO) << "Install attributes locked to Active Directory mode.";
// A configuration file should be present in this case.
expect_config = true;
}
} else {
LOG(INFO) << "No install attributes found.";
}
// Run daemon.
LOG(INFO) << "authpolicyd starting";
authpolicy::Daemon daemon(expect_config);
int res = daemon.Run();
LOG(INFO) << "authpolicyd stopping with exit code " << res;
return res;
}