4 #if !defined(timespecisset) || \
5 !defined(timespeccmp) || \
23 # define TIMESTAMP_DIR "/tmp/doas"
26 #if defined(TIMESTAMP_TMPFS) && defined(__linux__)
28 # define TMPFS_MAGIC 0x01021994
33 /* Use tty_nr from /proc/self/stat instead of using
34 * ttyname(3), stdin, stdout and stderr are user
35 * controllable and would allow to reuse timestamps
36 * from another writable terminal.
37 * See https://www.sudo.ws/alerts/tty_tickets.html
49 if ((fd = open("/proc/self/stat", O_RDONLY)) == -1)
52 while ((n = read(fd, p, buf + sizeof buf - p)) != 0) {
54 if (errno == EAGAIN || errno == EINTR)
59 if (p >= buf + sizeof buf)
64 /* error if it contains NULL bytes */
65 if (n != 0 || memchr(buf, '\0', p - buf))
68 /* Get the 7th field, 5 fields after the last ')',
69 * because the 5th field 'comm' can include spaces
70 * and closing paranthesis too.
71 * See https://www.sudo.ws/alerts/linux_tty.html
73 if ((p = strrchr(buf, ')')) == NULL)
75 for ((p = strtok_r(p, " ", &saveptr)), n = 0; p && n < 5;
76 (p = strtok_r(NULL, " ", &saveptr)), n++)
78 if (p == NULL || n != 5)
81 n = strtonum(p, INT_MIN, INT_MAX, &errstr);
88 #error "ttynr not implemented"
97 if ((tty = ttynr()) == -1)
98 errx(1, "failed to get tty number");
100 if ((sid = getsid(0)) == -1)
102 if (snprintf(buf, sizeof buf, ".%d_%d_%d", tty, ppid, sid) == -1)
112 if (fstat(fd, &st) == -1)
115 if ((st.st_mode & S_IFMT) != S_IFDIR)
116 errx(0, "timestamp directory is not a directory");
117 if ((st.st_mode & (S_IWGRP|S_IRGRP|S_IXGRP|S_IWOTH|S_IROTH|S_IXOTH)) != 0)
118 errx(1, "timestamp directory permissions wrong");
119 if (st.st_uid != 0 || st.st_gid != 0)
120 errx(1, "timestamp directory is not owned by root");
122 #if defined(TIMESTAMP_TMPFS) && defined(__linux__)
124 if (fstatfs(fd, &sf) == -1)
127 if (sf.f_type != TMPFS_MAGIC)
128 errx(1, "timestamp directory not on tmpfs");
141 if ((fd = open(TIMESTAMP_DIR, O_RDONLY | O_DIRECTORY)) == -1) {
142 if (errno == ENOENT) {
146 if (mkdir(TIMESTAMP_DIR, (S_IRUSR|S_IWUSR|S_IXUSR)) != 0)
148 if (setegid(gid) != 0)
152 err(1, "failed to open timestamp directory: %s", TIMESTAMP_DIR);
156 if (checktsdir(fd) != 0)
163 checktsfile(int fd, size_t *tssize)
168 if (fstat(fd, &st) == -1)
170 if ((st.st_mode & S_IFMT) != S_IFREG)
171 errx(1, "timestamp is not a regular file");
172 if ((st.st_mode & (S_IWGRP|S_IRGRP|S_IXGRP|S_IWOTH|S_IROTH|S_IXOTH)) != 0)
173 errx(1, "timestamp has wrong permissions");
176 if (st.st_uid != 0 || st.st_gid != gid)
177 errx(1, "timestamp has wrong owner");
179 *tssize = st.st_size;
185 validts(int fd, int secs)
187 struct timespec mono, real, ts_mono, ts_real, timeout;
189 if (read(fd, &ts_mono, sizeof ts_mono) != sizeof ts_mono ||
190 read(fd, &ts_real, sizeof ts_real) != sizeof ts_real)
192 if (!timespecisset(&ts_mono) || !timespecisset(&ts_real))
193 errx(1, "timespecisset");
195 if (clock_gettime(CLOCK_MONOTONIC_RAW, &mono) == -1 ||
196 clock_gettime(CLOCK_REALTIME, &real) == -1)
197 err(1, "clock_gettime");
199 if (timespeccmp(&mono, &ts_mono, >) ||
200 timespeccmp(&real, &ts_real, >))
203 memset(&timeout, 0, sizeof timeout);
204 timeout.tv_sec = secs;
205 timespecadd(&timeout, &mono, &mono);
206 timespecadd(&timeout, &real, &real);
208 if (timespeccmp(&mono, &ts_mono, <) ||
209 timespeccmp(&real, &ts_real, <))
210 errx(1, "timestamp is too far in the future");
216 persist_set(int fd, int secs)
218 struct timespec mono, real, ts_mono, ts_real, timeout;
220 if (clock_gettime(CLOCK_MONOTONIC_RAW, &mono) == -1 ||
221 clock_gettime(CLOCK_REALTIME, &real) == -1)
222 err(1, "clock_gettime");
224 memset(&timeout, 0, sizeof timeout);
225 timeout.tv_sec = secs;
226 timespecadd(&timeout, &mono, &ts_mono);
227 timespecadd(&timeout, &real, &ts_real);
229 if (lseek(fd, 0, SEEK_SET) == -1)
231 if (write(fd, (void *)&ts_mono, sizeof ts_mono) != sizeof ts_mono ||
232 write(fd, (void *)&ts_real, sizeof ts_real) != sizeof ts_real)
239 persist_open(int *valid, int secs)
246 if ((name = tsname()) == NULL)
247 errx(1, "failed to get timestamp name");
248 if ((dirfd = opentsdir()) == -1)
249 errx(1, "opentsdir");
251 if ((fd = openat(dirfd, name, (O_RDWR), (S_IRUSR|S_IWUSR))) == -1)
253 err(1, "open timestamp file");
256 if ((fd = openat(dirfd, name, (O_RDWR|O_CREAT|O_EXCL|O_NOFOLLOW),
257 (S_IRUSR|S_IWUSR))) == -1)
258 err(1, "open timestamp file");
262 if (checktsfile(fd, &tssize) == -1)
263 err(1, "checktsfile");
265 /* The timestamp size is 0 if its a new file or a
266 * timestamp that was never set, its not valid but
267 * can be used to write the new timestamp.
268 * If the size does not match the expected size it
269 * is incomplete and should never be used
271 if (tssize == sizeof(struct timespec) * 2)
272 *valid = validts(fd, secs) == 0;
273 else if (tssize != 0)
274 errx(1, "corrupt timestamp file");
285 if ((name = tsname()) == NULL)
286 errx(1, "failed to get timestamp name");
287 if ((dirfd = opentsdir()) == -1)
288 errx(1, "opentsdir");
289 if (unlinkat(dirfd, name, 0) == -1 && errno != ENOENT)