rdo/rdo.c
sw1tchbl4d3 24638bc999 Add sessions feature
This is by far the heaviest feature of rdo, justifying
its own file for its 140loc.

It creates sessions, inspired by the way doas does it.
We use the /run/rdo temporary folder to store files in the
format of /run/rdo/pid-ts, pid being the PID of the process
that executed rdo, and ts being the timestamp at which said
process started.

As no 2 processes can have the exact same PID and startup time
(startup time is measured in the milliseconds), this seems secure.

Closes #4.
2021-07-15 23:47:27 +02:00

104 lines
2.4 KiB
C

#include <pwd.h>
#include <err.h>
#include <shadow.h>
#include <crypt.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <bsd/readpassphrase.h>
#include "sessions.h"
void getconf(FILE* fp, const char* entry, char* result, size_t len_result) {
char* line = NULL;
size_t len = 0;
fseek(fp, 0, SEEK_SET);
while (getline(&line, &len, fp) != -1) {
if (strncmp(entry, line, strlen(entry)) == 0) {
strtok(line, "=");
char* token = strtok(NULL, "=");
if (token) {
strncpy(result, token, len_result);
result[strcspn(result, "\n")] = 0;
return;
}
}
}
errx(1, "Could not get '%s' entry in config", entry);
}
int runprog(int argc, char** argv) {
for(int i=0; i<argc; i++)
argv[i] = argv[i + 1];
if (setuid(0) == -1)
err(1, "Could not setuid");
if (setgid(0) == -1)
err(1, "Could not setgid");
if (execvp(argv[0], argv) != 0)
perror(argv[0]);
return 0;
}
int main(int argc, char** argv) {
char username[64], wrong_pw_sleep[64], session_ttl[64], password[128];
unsigned int sleep_ms, tries, ts_ttl;
if (argc < 2)
errx(1, "Please specify a program to run");
int ruid = getuid();
if (ruid == 0)
return runprog(argc, argv);
FILE* fp = fopen("/etc/rdo.conf", "r");
if (!fp)
err(1, "Could not open /etc/rdo.conf");
getconf(fp, "username", username, sizeof(username));
getconf(fp, "wrong_pw_sleep", wrong_pw_sleep, sizeof(wrong_pw_sleep));
getconf(fp, "session_ttl", session_ttl, sizeof(session_ttl));
sleep_ms = atoi(wrong_pw_sleep) * 1000;
ts_ttl = atoi(session_ttl) * 60 * 100;
fclose(fp);
if (getsession(getppid(), ts_ttl) == 0)
return runprog(argc, argv);
struct passwd* p = getpwnam(username);
if (!p)
err(1, "Could not get user info");
int uid = p->pw_uid;
if (uid != ruid && ruid != 0)
errx(1, "You are not in the username file");
struct spwd* shadowEntry = getspnam(username);
if (!shadowEntry)
err(1, "Could not get shadow entry");
tries = 0;
while (tries < 3) {
if (!readpassphrase("(rdo) Password: ", password, sizeof(password), RPP_REQUIRE_TTY))
err(1, "Could not get passphrase");
if (strcmp(shadowEntry->sp_pwdp, crypt(password, shadowEntry->sp_pwdp)) == 0) {
setsession(getppid(), ts_ttl);
return runprog(argc, argv);
}
usleep(sleep_ms);
fprintf(stderr, "Wrong password.\n");
tries++;
}
errx(1, "Too many wrong password attempts.");
return 1;
}