Handle different levels of rdcd privilege

Depending on how a user starts rdcd, rdcd will either have
full monitor/control capabilities or have just monitoring
capabilties.

The only 2 user ids allowed are "rdc" and root.

Change-Id: Ie296a2f68c9723bef5945b1af1070ef99eeea93b
This commit is contained in:
Chris Freehill
2020-03-20 10:32:25 -05:00
parent 7ee29b6cdd
commit a6acf24ae7
5 changed files with 235 additions and 41 deletions
+174 -30
View File
@@ -30,7 +30,7 @@ THE SOFTWARE.
#include <sys/stat.h>
#include <sys/capability.h>
#include <getopt.h>
#include <pwd.h>
#include <iostream>
#include <memory>
#include <string>
@@ -58,7 +58,8 @@ static bool sShutDownServer = false;
static bool sRestartServer = false;
static const char *kDaemonName = "rdcd";
static const char *kRDCDHomeDir = "/";
static const char *kDaemonLockFile = "/var/run/rdcd.lock";
static const char *kDaemonLockFileRoot = "/var/run/rdcd.lock";
static const char *kDaemonLockFile = "/tmp/rdcd.lock";
// Pinned certificates
static const char * kDefaultRDCServerCertPinPath =
@@ -77,6 +78,7 @@ static const char * kDefaultRDCClientCACertPemPkiPath =
"/etc/rdc/client/certs/rdc_cacert.pem";
static const char *kDefaultListenPort = "50051";
static const uint32_t kRSMIUMask = 027;
RDCServer::RDCServer() : server_address_("0.0.0.0:"),
secure_creds_(false), rsmi_service_(nullptr), rdc_admin_service_(nullptr) {
@@ -269,6 +271,61 @@ static void InitializeSignalHandling(void) {
signal(SIGTERM, HandleSignal);
}
static int
FileOwner(const char *fn, std::string *owner) {
struct stat info;
int ret;
assert(owner);
if (owner == nullptr) {
return EINVAL;
}
ret = stat(fn, &info);
if (ret) {
perror("Failed to stat lock file");
return errno;
}
struct passwd pw;
struct passwd *result;
char buf[20];
ret = getpwuid_r(info.st_uid, &pw, buf, 20, &result);
if (ret == 0) {
*owner = buf;
} else {
return ret;
perror("Failed to determine owner of file");
}
return 0;
}
static int UserID(const char *un, uid_t *uid) {
int ret;
struct passwd pw;
struct passwd *result;
char *buf;
int bufsize;
assert(uid != nullptr);
bufsize = sysconf(_SC_GETPW_R_SIZE_MAX);
if (bufsize == -1) {
bufsize = 16384;
}
buf = new char[bufsize];
ret = getpwnam_r(un, &pw, buf, bufsize, &result);
delete []buf;
if (ret == 0) {
*uid = pw.pw_uid;
} else {
perror("Failed to determine user id for given name");
return 1;
}
return 0;
}
void
RDCServer::ShutDown(void) {
server_->Shutdown();
@@ -310,35 +367,106 @@ static void * ProcessSignalLoop(void *server_ptr) {
pthread_exit(0);
}
static void ExitIfAlreadyRunning(void) {
int single_proc_fh;
ssize_t fsz;
static bool FileIsLocked(std::string fn) {
struct flock fl;
int fh;
int ret;
single_proc_fh = open(kDaemonLockFile, O_RDWR|O_CREAT, 0640);
if (single_proc_fh < 0) {
std::cerr << "Failed to open file lock:" << kDaemonLockFile << std::endl;
auto close_fh = [&]() {
ret = close(fh);
if (ret) {
perror(fn.c_str());
}
};
(void)memset(&fl, 0, sizeof(struct flock));
errno = 0;
fh = open(fn.c_str(), O_RDONLY);
if (fh == -1 && errno == ENOENT) {
// In this case, there was no previous lock.
close_fh();
return false;
}
ret = fcntl(fh, F_GETLK, &fl);
if (ret) {
perror("Failed to get file-lock status. Is rdcd already running?");
exit(1);
}
if (lockf(single_proc_fh, F_TLOCK, 0) < 0) {
if (fl.l_type == F_UNLCK) {
close_fh();
return false;
}
close_fh();
return true;
}
static void ExitIfAlreadyRunning(bool is_root) {
const char *lock_fn;
int lock_fh;
std::string lf_user(kDaemonLockFile);
std::string lf_root(kDaemonLockFileRoot);
ssize_t fsz;
auto chk_if_locked = [&](std::string lock_file) {
bool is_locked = FileIsLocked(lock_file);
if (is_locked) {
std::cerr << "File " << lock_file <<
" is locked. Is rdcd already running?" << std::endl;
exit(1);
}
};
chk_if_locked(lf_root);
chk_if_locked(lf_user);
if (is_root) {
lock_fn = kDaemonLockFileRoot;
} else {
lock_fn = kDaemonLockFile;
}
// Temporarily adjust file-mask to create file with right permissions
umask(023);
lock_fh = open(lock_fn, O_RDWR|O_CREAT, 0644);
if (lock_fh < 0) {
std::string user;
int ret = FileOwner(lock_fn, &user);
if (ret) {
perror("Failed to determine owner of lock file.");
exit(ret);
}
std::cerr << "Failed to open file lock:" << lock_fn << " owned by user: "
<< user << ". If starting rdcd as a different user, delete this "
"lock-file first." << std::endl;
// asserting below since this should have been prevented in main()
assert(!"Unexpected user invoking rdcd");
exit(1);
}
if (lockf(lock_fh, F_TLOCK, 0) < 0) {
std::cerr << "Daemon already running. Exiting this instance." << std::endl;
exit(0);
}
umask(kRSMIUMask);
std::string pid_str = std::to_string(getpid());
fsz = write(single_proc_fh, pid_str.c_str(), pid_str.size());
fsz = write(lock_fh, pid_str.c_str(), pid_str.size());
assert(static_cast<unsigned int>(fsz) == pid_str.size());
}
static void
MakeDaemon() {
MakeDaemon(bool is_root) {
int fd0;
struct rlimit max_files;
// RSMI, for one thing, will need to be able to read/write files
// Note that umask turns *off* permission for a given bit, so you we want
// the complement of the permissions we want files to have.
umask(027);
umask(kRSMIUMask);
// To Do; Make this optional based on CL option. By default, don't do this.
// Instead rely on serviced to make it a daemon.
@@ -390,7 +518,7 @@ MakeDaemon() {
exit(1);
}
ExitIfAlreadyRunning();
ExitIfAlreadyRunning(is_root);
InitializeSignalHandling();
}
@@ -419,9 +547,9 @@ static void PrintHelp(void) {
"default is to listen on port 50051\n"
"--unauth_comm, -u don't do authentication with communications"
" with client. When this flag is not specified, by default, "
"PKI authentication is used\n"
"PKI authentication is used\n"
"--pinned_cert, -i used \"pinned\" certificates instead of PKI "
"authentication. This is for test purposes.\n"
"authentication. This is for test purposes.\n"
"--debug, -d output debug messages\n"
"--help, -h print this message\n";
}
@@ -495,11 +623,27 @@ int main(int argc, char** argv) {
RDCServer rdc_server;
RdcdCmdLineOpts cmd_line_opts;
int err;
uid_t rdc_uid;
uid_t caller_id = geteuid();
bool is_root = (caller_id == 0);
if (!is_root) {
// Ensure user is calling as "rdc"
err = UserID("rdc", &rdc_uid);
if (err != 0) {
return 1;
}
if (rdc_uid != caller_id) {
std::cerr << "Only user \"rdc\" or root can start rdcd." << std::endl;
exit(1);
}
}
init_cmd_line_opts(&cmd_line_opts);
ProcessCmdline(&cmd_line_opts, argc, argv);
MakeDaemon();
MakeDaemon(is_root);
rdc_server.Initialize(&cmd_line_opts);
@@ -511,23 +655,23 @@ int main(int argc, char** argv) {
std::cerr << "Failed to get capability" << std::endl;
return 1;
}
if (!cap_enabled) {
std::cerr <<
"Expected CAP_DAC_OVERRIDE CAP_EFFECTIVE to be enabled, but it not." <<
std::endl;
return 1;
}
err = amd::rdc::GetCapability(CAP_DAC_OVERRIDE, CAP_PERMITTED, &cap_enabled);
if (err) {
std::cerr << "Failed to get capability" << std::endl;
return 1;
if (cap_enabled) {
err =
amd::rdc::GetCapability(CAP_DAC_OVERRIDE, CAP_PERMITTED, &cap_enabled);
if (err) {
std::cerr << "Failed to get capability" << std::endl;
return 1;
}
if (!cap_enabled) {
std::cerr <<
"CAP_DAC_OVERRIDE CAP_PERMITTED is not enabled" << std::endl;
}
} else {
std::cerr << "CAP_DAC_OVERRIDE CAP_EFFECTIVE is not enabled." << std::endl;
}
if (!cap_enabled) {
std::cerr <<
"Expected CAP_DAC_OVERRIDE CAP_PERMITTED to be enabled, but it not." <<
std::endl;
return 1;
std::cerr << "rdcd functionality is limited to read-only" << std::endl;
}
// Don't allow rwx access to all files to ever be inheritable. We may need