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.
This commit is contained in:
sw1tchbl4d3 2021-07-15 23:47:27 +02:00
parent 2f4ff0812a
commit 24638bc999
3 changed files with 151 additions and 3 deletions

14
rdo.c
View file

@ -7,6 +7,7 @@
#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;
@ -45,8 +46,8 @@ int runprog(int argc, char** argv) {
}
int main(int argc, char** argv) {
char username[64], wrong_pw_sleep[64], password[128];
unsigned int sleep_ms, tries;
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");
@ -62,10 +63,15 @@ int main(int argc, char** argv) {
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");
@ -84,8 +90,10 @@ int main(int argc, char** argv) {
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)
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");

View file

@ -1,2 +1,3 @@
username=sw1tchbl4d3
wrong_pw_sleep=1000
session_ttl=5

139
sessions.h Normal file
View file

@ -0,0 +1,139 @@
#pragma once
#include <limits.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <time.h>
int getpstartts(int pid, unsigned long long* startts) {
char path[255], fc[1024];
char* ptr = fc;
snprintf(path, sizeof(path), "/proc/%d/stat", pid);
int fd = open(path, O_RDONLY);
if (fd < 0)
err(1, "Could not open %s", path);
int bytes_read = read(fd, fc, sizeof(fc));
close(fd);
if (memchr(ptr, '\0', bytes_read) != NULL)
return -1;
ptr = strrchr(fc, ')');
char* token = strtok(ptr, " ");
for (short i = 0; i<20 && token; i++)
token = strtok(NULL, " ");
if (!token)
return -1;
unsigned long long temp_ts = strtoull(token, NULL, 10);
if (temp_ts == 0 || temp_ts == ULLONG_MAX)
return -1;
*startts = temp_ts;
return 0;
}
int gethandle(int recur) {
if (recur >= 2)
errx(1, "Too many recursions in gethandle()");
struct stat st;
int fd = open("/run/rdo", O_RDONLY, O_DIRECTORY | O_NOFOLLOW);
if (fd < 0) {
if (errno == ENOENT) {
if (mkdir("/run/rdo", 0700) < 0)
err(1, "Could not create /run/rdo");
return gethandle(++recur);
}
else
err(1, "Could not open /run/rdo");
} else {
if (fstat(fd, &st) < 0)
err(1, "Could not fstat /run/rdo");
if (st.st_uid != 0 || st.st_mode != (0700 | S_IFDIR))
return -1;
}
return fd;
}
void setsession(int pid, unsigned int ts_ttl) {
if (ts_ttl == 0)
return;
unsigned long long startts;
char path[1024], ts_str[32];
int dirfd = gethandle(0);
if (dirfd < 0 && errno == 0)
return;
if (getpstartts(pid, &startts) < 0)
return;
snprintf(path, sizeof(path), "/run/rdo/%d-%llu", pid, startts);
int fd = openat(dirfd, path, O_CREAT | O_EXCL | O_WRONLY, 0700);
if (fd < 0) {
if (errno == EEXIST)
return;
err(1, "Could not open %s", path);
}
snprintf(ts_str, sizeof(ts_str), "%llu", (unsigned long long)time(NULL));
if (write(fd, ts_str, sizeof(ts_str)) < 0)
err(1, "Could not write to %s", path);
close(fd);
return;
}
int getsession(int pid, unsigned int ts_ttl) {
if (ts_ttl == 0)
return -1;
unsigned long long startts, current;
char path[1024], ts_str[32];
int dirfd = gethandle(0);
if (dirfd < 0 && errno == 0)
return -1;
if (getpstartts(pid, &startts) < 0)
return -1;
snprintf(path, sizeof(path), "/run/rdo/%d-%llu", pid, startts);
int fd = openat(dirfd, path, O_RDONLY);
if (fd < 0) {
if (errno == ENOENT)
return -1;
err(1, "Could not open %s", path);
}
printf("%d", fd);
if (read(fd, ts_str, sizeof(ts_str)) < 0)
err(1, "Could not read %s", path);
startts = strtoull(ts_str, NULL, 10);
current = time(NULL);
if (current - startts > ts_ttl) {
unlink(path);
return -1;
}
return 0;
}