diff --git a/us.c b/us.c index a6e2e52..2c7aac1 100644 --- a/us.c +++ b/us.c @@ -39,6 +39,8 @@ #include #include #include +#include +#include #if !defined(_XOPEN_CRYPT) || _XOPEN_CRYPT == -1 #include @@ -55,6 +57,8 @@ #define FLAG_PERSIST 0x1 #define FLAG_NOPASS 0x2 #define FLAG_NOLOG 0x4 +#define SESSION_FILE_DIR "/var/run" +#define SESSION_TIMEOUT (60*5) struct env_elem { char *name; @@ -84,7 +88,7 @@ void *erealloc(void *, size_t); static void usage(void); static void die(const char *, ...); static int perm_set(struct passwd *, struct group *); -static int authenticate(uid_t, char); +static int authenticate(uid_t, int, int); static struct passwd* user_to_passwd(const char *, struct user_info *); static struct group* group_to_grp(const char *, struct user_info *); static int get_config(struct config **, int *); @@ -159,6 +163,8 @@ int main(int argc, char *argv[]) /* From now on most actions require root */ if (setuid(0) == -1) die("setuid:"); + if (setgid(0) == -1) + die("setgid:"); /* get info from the config file and check if the action we want to * do is permitted */ @@ -236,7 +242,7 @@ int main(int argc, char *argv[]) /* Authenticate, we will be root from now on */ if (!(conf_flags & FLAG_NOPASS)) - if (authenticate(my_pw->pw_uid, askpass)) + if (authenticate(my_pw->pw_uid, askpass, conf_flags & FLAG_PERSIST)) exit(EXIT_FAILURE); /* Get target user's shell */ @@ -394,9 +400,57 @@ static int perm_set(struct passwd *pw, struct group *gr) return 0; } -static int authenticate(uid_t uid, char ask) +static int authenticate(uid_t uid, int ask, int persist) { // TODO: implement u2f compat + // TODO: check root access, maybe + /* try to check if a valid saved session exists */ + char tmp_file[512] = {0}; + if (persist) { + pid_t sid = getsid(0); + if (sid == (pid_t)-1) + die("getsid:"); + if (snprintf(tmp_file, 512, SESSION_FILE_DIR "/us.%d", sid) >= 512) + die("snprintf: output truncated"); + int session_file = open(tmp_file, O_RDONLY | O_CLOEXEC); + int valid_session = 1; + struct timespec now_t[2] = {0}, old_t[2] = {0}; + if (session_file != -1) { + struct stat st; + if (fstat(session_file, &st) == -1) { + valid_session = 0; + } else if (st.st_uid != 0 || st.st_gid != 0) { + valid_session = 0; + } else if (!S_ISREG(st.st_mode)) { + valid_session = 0; + } else if (st.st_mode & S_IRWXO || + st.st_mode & S_IROTH || + st.st_mode & S_IWOTH || st.st_mode & S_IXOTH) { + valid_session = 0; + } else { + int r = read(session_file, old_t, sizeof(struct timespec)*2); + if (r != sizeof(struct timespec)*2) + valid_session = 0; + close(session_file); + } + } else { + valid_session = 0; + } + + if (valid_session && session_file != -1) { + if (clock_gettime(CLOCK_MONOTONIC, &(now_t[0])) == -1) + die("clock_gettime:"); + if (clock_gettime(CLOCK_REALTIME, &(now_t[1])) == -1) + die("clock_gettime:"); + if (now_t[0].tv_sec <= old_t[0].tv_sec || + old_t[0].tv_sec < now_t[0].tv_sec - SESSION_TIMEOUT || + now_t[1].tv_sec <= old_t[1].tv_sec || + old_t[1].tv_sec < now_t[1].tv_sec - SESSION_TIMEOUT) + valid_session = 0; + } + if (valid_session) + return 0; + } /* get the encrypted password */ struct passwd *pw = getpwuid(uid); char *hash_p, hash[MAX_HASH]; @@ -511,6 +565,22 @@ static int authenticate(uid_t uid, char ask) return -1; } printf("\n"); + + if (persist) { + if (!tmp_file[0]) + return 0; + int session_file = creat(tmp_file, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP); + if (session_file == -1) + return 0; + chown(tmp_file, 0, 0); + struct timespec t[2] = {0}; + if (clock_gettime(CLOCK_MONOTONIC, &(t[0])) == -1) + die("clock_gettime:"); + if (clock_gettime(CLOCK_REALTIME, &(t[1])) == -1) + die("clock_gettime:"); + write(session_file, t, sizeof(struct timespec)*2); + close(session_file); + } return 0; }