20 # define TIMESTAMP_DIR "/tmp/doas"
23 #if defined(TIMESTAMP_TMPFS) && defined(__linux__)
25 # define TMPFS_MAGIC 0x01021994
29 #define timespecisset(tsp) ((tsp)->tv_sec || (tsp)->tv_nsec)
30 #define timespeccmp(tsp, usp, cmp) \
31 (((tsp)->tv_sec == (usp)->tv_sec) ? \
32 ((tsp)->tv_nsec cmp (usp)->tv_nsec) : \
33 ((tsp)->tv_sec cmp (usp)->tv_sec))
34 #define timespecadd(tsp, usp, vsp) do { \
35 (vsp)->tv_sec = (tsp)->tv_sec + (usp)->tv_sec; \
36 (vsp)->tv_nsec = (tsp)->tv_nsec + (usp)->tv_nsec; \
37 if ((vsp)->tv_nsec >= 1000000000L) { \
39 (vsp)->tv_nsec -= 1000000000L; \
45 /* Use tty_nr from /proc/self/stat instead of using
46 * ttyname(3), stdin, stdout and stderr are user
47 * controllable and would allow to reuse timestamps
48 * from another writable terminal.
49 * See https://www.sudo.ws/alerts/tty_tickets.html
61 if ((fd = open("/proc/self/stat", O_RDONLY)) == -1)
64 while ((n = read(fd, p, buf + sizeof buf - p)) != 0) {
66 if (errno == EAGAIN || errno == EINTR)
71 if (p >= buf + sizeof buf)
76 /* error if it contains NULL bytes */
77 if (n != 0 || memchr(buf, '\0', p - buf))
80 /* Get the 7th field, 5 fields after the last ')',
81 * because the 5th field 'comm' can include spaces
82 * and closing paranthesis too.
83 * See https://www.sudo.ws/alerts/linux_tty.html
85 if ((p = strrchr(buf, ')')) == NULL)
87 for ((p = strtok_r(p, " ", &saveptr)), n = 0; p && n < 5;
88 (p = strtok_r(NULL, " ", &saveptr)), n++)
90 if (p == NULL || n != 5)
93 n = strtonum(p, INT_MIN, INT_MAX, &errstr);
100 #error "ttynr not implemented"
106 static char pathbuf[PATH_MAX];
109 if ((tty = ttynr()) == -1)
110 errx(1, "failed to get tty number");
112 if ((sid = getsid(0)) == -1)
114 if (snprintf(pathbuf, sizeof pathbuf, "%s/.%d_%d_%d",
115 TIMESTAMP_DIR, tty, ppid, sid) == -1)
121 checktsdir(const char *path)
128 if (!(buf = strdup(path)))
133 if (lstat(dir, &st) == -1) {
134 if (errno == ENOENT) {
138 if (mkdir(dir, (S_IRUSR|S_IWUSR|S_IXUSR)) != 0)
140 if (setegid(gid) != 0)
148 if ((st.st_mode & S_IFMT) != S_IFDIR)
149 errx(1, "timestamp directory is not a directory");
150 if ((st.st_mode & (S_IWGRP|S_IRGRP|S_IXGRP|S_IWOTH|S_IROTH|S_IXOTH)) != 0)
151 errx(1, "timestamp directory permissions wrong");
152 if (st.st_uid != 0 || st.st_gid != 0)
153 errx(1, "timestamp directory is not owned by root");
154 if (statfs(dir, &sf) == -1)
157 #if defined(TIMESTAMP_TMPFS) && defined(__linux__)
158 if (sf.f_type != TMPFS_MAGIC)
159 errx(1, "timestamp directory not on tmpfs");
167 persist_check(int fd, int secs)
169 struct timespec mono, real, ts_mono, ts_real, timeout;
171 if (read(fd, &ts_mono, sizeof ts_mono) != sizeof ts_mono ||
172 read(fd, &ts_real, sizeof ts_real) != sizeof ts_mono)
174 if (!timespecisset(&ts_mono) || !timespecisset(&ts_real))
175 errx(1, "timespecisset");
177 if (clock_gettime(CLOCK_MONOTONIC_RAW, &mono) == -1 ||
178 clock_gettime(CLOCK_REALTIME, &real) == -1)
179 err(1, "clock_gettime");
181 if (timespeccmp(&mono, &ts_mono, >) ||
182 timespeccmp(&real, &ts_real, >))
185 memset(&timeout, 0, sizeof timeout);
186 timeout.tv_sec = secs;
187 timespecadd(&timeout, &mono, &mono);
188 timespecadd(&timeout, &real, &real);
190 if (timespeccmp(&mono, &ts_mono, <) ||
191 timespeccmp(&real, &ts_real, <))
192 errx(1, "timestamp is too far in the future");
198 persist_set(int fd, int secs)
200 struct timespec mono, real, ts_mono, ts_real, timeout;
202 if (clock_gettime(CLOCK_MONOTONIC_RAW, &mono) == -1 ||
203 clock_gettime(CLOCK_REALTIME, &real) == -1)
204 err(1, "clock_gettime");
206 memset(&timeout, 0, sizeof timeout);
207 timeout.tv_sec = secs;
208 timespecadd(&timeout, &mono, &ts_mono);
209 timespecadd(&timeout, &real, &ts_real);
211 if (lseek(fd, 0, SEEK_SET) == -1)
213 if (write(fd, (void *)&ts_mono, sizeof ts_mono) != sizeof ts_mono ||
214 write(fd, (void *)&ts_real, sizeof ts_real) != sizeof ts_real)
221 persist_open(int *valid, int secs)
228 if ((path = tspath()) == NULL)
229 errx(1, "failed to get timestamp path");
230 if (checktsdir(path))
231 errx(1, "checktsdir");
233 if ((fd = open(path, (O_RDWR), (S_IRUSR|S_IWUSR))) == -1)
235 err(1, "open: %s", path);
238 if ((fd = open(path, (O_RDWR|O_CREAT|O_EXCL), (S_IRUSR|S_IWUSR))) == -1)
239 err(1, "open: %s", path);
244 if (fstat(fd, &st) == -1)
246 if ((st.st_mode & S_IFMT) != S_IFREG)
247 errx(1, "timestamp is not a file");
248 if ((st.st_mode & (S_IWGRP|S_IRGRP|S_IXGRP|S_IWOTH|S_IROTH|S_IXOTH)) != 0)
249 errx(1, "timestamp permissions wrong");
252 if (st.st_uid != 0 || st.st_gid != gid)
253 errx(1, "timestamp has wrong owner");
255 if (st.st_size == 0) {
260 if (st.st_size != sizeof(struct timespec) * 2)
261 errx(1, "corrupt timestamp file");
263 *valid = persist_check(fd, secs) == 0;
272 if ((path = tspath()) == NULL)
273 errx(1, "failed to get timestamp path");
274 if (unlink(path) == -1 && errno != ENOENT)