persist.c (2609B)
1 #include <errno.h> 2 #include <fcntl.h> 3 #include <limits.h> 4 #include <stdio.h> 5 #include <stdlib.h> 6 #include <string.h> 7 #include <sys/stat.h> 8 #include <sys/types.h> 9 #include <time.h> 10 #include <unistd.h> 11 12 #include "doas.h" 13 14 #define PERSIST_DIR "/run/doas" 15 #define PERSIST_TIMEOUT 5 * 60 16 17 static int 18 ttyid(dev_t *tty) 19 { 20 int fd, i; 21 char buf[BUFSIZ], *p; 22 ssize_t n; 23 24 fd = open("/proc/self/stat", O_RDONLY); 25 if (fd == -1) 26 return -1; 27 n = read(fd, buf, sizeof(buf) - 1); 28 if (n >= 0) 29 buf[n] = '\0'; 30 /* check that we read the whole file */ 31 n = read(fd, buf, 1); 32 close(fd); 33 if (n != 0) 34 return -1; 35 p = strrchr(buf, ')'); 36 if (!p) 37 return -1; 38 ++p; 39 /* ttr_nr is the 5th field after executable name, so skip the next 4 */ 40 for (i = 0; i < 4; ++i) { 41 p = strchr(++p, ' '); 42 if (!p) 43 return -1; 44 } 45 *tty = strtol(p, &p, 10); 46 if (*p != ' ') 47 return -1; 48 return 0; 49 } 50 51 static int 52 persistpath(char *buf, size_t len) 53 { 54 dev_t tty; 55 int n; 56 57 if (ttyid(&tty) < 0) 58 return -1; 59 n = snprintf(buf, len, PERSIST_DIR "/%ju-%ju", (uintmax_t)getuid(), (uintmax_t)tty); 60 if (n < 0 || n >= (int)len) 61 return -1; 62 return 0; 63 } 64 65 int 66 openpersist(int *valid) 67 { 68 char path[256]; 69 struct stat st; 70 struct timespec ts; 71 int fd; 72 73 if (stat(PERSIST_DIR, &st) < 0) { 74 if (errno != ENOENT) 75 return -1; 76 if (mkdir(PERSIST_DIR, 0700) < 0) 77 return -1; 78 } else if (st.st_uid != 0 || st.st_mode != (S_IFDIR | 0700)) { 79 return -1; 80 } 81 if (persistpath(path, sizeof(path)) < 0) 82 return -1; 83 fd = open(path, O_RDONLY); 84 if (fd == -1) { 85 char tmp[256]; 86 struct timespec ts[2] = { { .tv_nsec = UTIME_OMIT }, { 0 } }; 87 int n; 88 89 n = snprintf(tmp, sizeof(tmp), PERSIST_DIR "/.tmp-%d", getpid()); 90 if (n < 0 || n >= (int)sizeof(tmp)) 91 return -1; 92 fd = open(tmp, O_RDONLY | O_CREAT | O_EXCL, 0); 93 if (fd == -1) 94 return -1; 95 if (futimens(fd, ts) < 0 || rename(tmp, path) < 0) { 96 close(fd); 97 unlink(tmp); 98 return -1; 99 } 100 *valid = 0; 101 } else { 102 *valid = clock_gettime(CLOCK_BOOTTIME, &ts) == 0 && 103 fstat(fd, &st) == 0 && 104 (ts.tv_sec < st.st_mtim.tv_sec || 105 (ts.tv_sec == st.st_mtim.tv_sec && ts.tv_nsec < st.st_mtim.tv_nsec)) && 106 st.st_mtime - ts.tv_sec <= PERSIST_TIMEOUT; 107 } 108 return fd; 109 } 110 111 int 112 setpersist(int fd) 113 { 114 struct timespec times[2]; 115 116 if (clock_gettime(CLOCK_BOOTTIME, ×[1]) < 0) 117 return -1; 118 times[0].tv_nsec = UTIME_OMIT; 119 times[1].tv_sec += PERSIST_TIMEOUT; 120 return futimens(fd, times); 121 } 122 123 int 124 clearpersist(void) 125 { 126 char path[256]; 127 128 if (persistpath(path, sizeof(path)) < 0) 129 return -1; 130 if (unlink(path) < 0 && errno != ENOENT) 131 return -1; 132 return 0; 133 }