#include "includes.h"
#ifndef TIMESTAMP_DIR
-# define TIMESTAMP_DIR "/tmp/doas"
+# define TIMESTAMP_DIR "/tmp/doas"
#endif
-#ifndef TMPFS_MAGIC
-# define TMPFS_MAGIC 0x01021994
+
+#if defined(TIMESTAMP_TMPFS) && defined(__linux__)
+# ifndef TMPFS_MAGIC
+# define TMPFS_MAGIC 0x01021994
+# endif
#endif
#define timespecisset(tsp) ((tsp)->tv_sec || (tsp)->tv_nsec)
ttynr()
{
char buf[1024];
- char *p, *p1, *saveptr;
+ char *p, *saveptr;
const char *errstr;
int fd, n;
if (n == -1) {
if (errno == EAGAIN || errno == EINTR)
continue;
- else
- break;
+ break;
}
p += n;
if (p >= buf + sizeof buf)
break;
}
close(fd);
+
/* error if it contains NULL bytes */
if (n != 0 || memchr(buf, '\0', p - buf))
return -1;
*/
if ((p = strrchr(buf, ')')) == NULL)
return -1;
- for ((p1 = strtok_r(p, " ", &saveptr)), n = 0; p1;
- (p1 = strtok_r(NULL, " ", &saveptr)), n++)
- if (n == 5)
- break;
- if (p1 == NULL || n != 5)
+ for ((p = strtok_r(p, " ", &saveptr)), n = 0; p && n < 5;
+ (p = strtok_r(NULL, " ", &saveptr)), n++)
+ ;
+ if (p == NULL || n != 5)
return -1;
- n = strtonum(p1, INT_MIN, INT_MAX, &errstr);
+ n = strtonum(p, INT_MIN, INT_MAX, &errstr);
if (errstr)
return -1;
#error "ttynr not implemented"
#endif
-static char pathbuf[PATH_MAX];
-
-static int
-tspath(const char **path)
+static const char *
+tsname()
{
+ static char buf[128];
int tty;
- pid_t ppid;
- if (*pathbuf == '\0') {
- if ((tty = ttynr()) == -1)
- errx(1, "failed to get tty number");
- ppid = getppid();
- if (snprintf(pathbuf, sizeof pathbuf, "%s/.%d_%d",
- TIMESTAMP_DIR, tty, ppid) == -1)
- return -1;
- }
- *path = pathbuf;
- return 0;
+ pid_t ppid, sid;
+ if ((tty = ttynr()) == -1)
+ errx(1, "failed to get tty number");
+ ppid = getppid();
+ if ((sid = getsid(0)) == -1)
+ err(1, "getsid");
+ if (snprintf(buf, sizeof buf, ".%d_%d_%d", tty, ppid, sid) == -1)
+ return NULL;
+ return buf;
}
static int
-checktsdir(const char *path)
+checktsdir(int fd)
{
- char *dir, *buf;
struct stat st;
+
+ if (fstat(fd, &st) == -1)
+ err(1, "fstatat");
+
+ if ((st.st_mode & S_IFMT) != S_IFDIR)
+ errx(0, "timestamp directory is not a directory");
+ if ((st.st_mode & (S_IWGRP|S_IRGRP|S_IXGRP|S_IWOTH|S_IROTH|S_IXOTH)) != 0)
+ errx(1, "timestamp directory permissions wrong");
+ if (st.st_uid != 0 || st.st_gid != 0)
+ errx(1, "timestamp directory is not owned by root");
+
+#if defined(TIMESTAMP_TMPFS) && defined(__linux__)
struct statfs sf;
- gid_t gid;
+ if (fstatfs(fd, &sf) == -1)
+ err(1, "statfs");
- if (!(buf = strdup(path)))
- err(1, "strdup");
- dir = dirname(buf);
+ if (sf.f_type != TMPFS_MAGIC)
+ errx(1, "timestamp directory not on tmpfs");
+#endif
+
+ return 0;
+}
-check:
- if (lstat(dir, &st) == -1) {
+static int
+opentsdir()
+{
+ gid_t gid;
+ int fd;
+
+reopen:
+ if ((fd = open(TIMESTAMP_DIR, O_RDONLY | O_DIRECTORY)) == -1) {
if (errno == ENOENT) {
gid = getegid();
if (setegid(0) != 0)
err(1, "setegid");
- if (mkdir(dir, (S_IRUSR|S_IWUSR|S_IXUSR)) != 0)
+ if (mkdir(TIMESTAMP_DIR, (S_IRUSR|S_IWUSR|S_IXUSR)) != 0)
err(1, "mkdir");
if (setegid(gid) != 0)
err(1, "setegid");
- goto check;
+ goto reopen;
} else {
- err(1, "stat");
+ err(1, "failed to open timestamp directory: %s", TIMESTAMP_DIR);
}
}
- if ((st.st_mode & S_IFMT) != S_IFDIR)
- errx(1, "timestamp directory is not a directory");
- if ((st.st_mode & (S_IWGRP|S_IRGRP|S_IWOTH|S_IROTH)) != 0)
- errx(1, "timestamp directory permissions wrong");
- if (st.st_uid != 0 || st.st_gid != 0)
- errx(1, "timestamp directory is not owned by root");
- if (statfs(dir, &sf) == -1)
- err(1, "statfs");
- if (sf.f_type != TMPFS_MAGIC)
- errx(1, "timestamp directory not on tmpfs");
+ if (checktsdir(fd) != 0)
+ return -1;
- free(buf);
- return 0;
+ return fd;
}
static int
-timestamp_read(int fd, struct timespec *mono, struct timespec *real)
+checktsfile(int fd, size_t *tssize)
{
- if (read(fd, (void *)mono, sizeof *mono) != sizeof *mono ||
- read(fd, (void *)real, sizeof *real) != sizeof *mono)
- err(1, "read");
- if (!timespecisset(mono) || !timespecisset(real))
- errx(1, "timespecisset");
+ struct stat st;
+ gid_t gid;
+
+ if (fstat(fd, &st) == -1)
+ err(1, "stat");
+ if ((st.st_mode & S_IFMT) != S_IFREG)
+ errx(1, "timestamp is not a regular file");
+ if ((st.st_mode & (S_IWGRP|S_IRGRP|S_IXGRP|S_IWOTH|S_IROTH|S_IXOTH)) != 0)
+ errx(1, "timestamp has wrong permissions");
+
+ gid = getegid();
+ if (st.st_uid != 0 || st.st_gid != gid)
+ errx(1, "timestamp has wrong owner");
+
+ *tssize = st.st_size;
+
return 0;
}
-int
-persist_check(int fd, int secs)
+static int
+validts(int fd, int secs)
{
struct timespec mono, real, ts_mono, ts_real, timeout;
- if (timestamp_read(fd, &ts_mono, &ts_real) != 0)
- return 1;
+ if (read(fd, &ts_mono, sizeof ts_mono) != sizeof ts_mono ||
+ read(fd, &ts_real, sizeof ts_real) != sizeof ts_real)
+ err(1, "read");
+ if (!timespecisset(&ts_mono) || !timespecisset(&ts_real))
+ errx(1, "timespecisset");
- if (clock_gettime(CLOCK_MONOTONIC, &mono) != 0 || !timespecisset(&mono))
- err(1, "clock_gettime(CLOCK_MONOTONIC, ?)");
- if (clock_gettime(CLOCK_REALTIME, &real) != 0 || !timespecisset(&real))
- err(1, "clock_gettime(CLOCK_REALTIME, ?)");
+ if (clock_gettime(CLOCK_MONOTONIC_RAW, &mono) == -1 ||
+ clock_gettime(CLOCK_REALTIME, &real) == -1)
+ err(1, "clock_gettime");
if (timespeccmp(&mono, &ts_mono, >) ||
timespeccmp(&real, &ts_real, >))
- return 1;
+ return -1;
memset(&timeout, 0, sizeof timeout);
timeout.tv_sec = secs;
{
struct timespec mono, real, ts_mono, ts_real, timeout;
- if (clock_gettime(CLOCK_MONOTONIC, &mono) != 0 || !timespecisset(&mono))
- err(1, "clock_gettime(XLOCK_MONOTONIC, ?)");
- if (clock_gettime(CLOCK_REALTIME, &real) != 0 || !timespecisset(&real))
- err(1, "clock_gettime(CLOCK_REALTIME, ?)");
+ if (clock_gettime(CLOCK_MONOTONIC_RAW, &mono) == -1 ||
+ clock_gettime(CLOCK_REALTIME, &real) == -1)
+ err(1, "clock_gettime");
memset(&timeout, 0, sizeof timeout);
timeout.tv_sec = secs;
int
persist_open(int *valid, int secs)
{
- struct stat st;
- int fd;
- gid_t gid;
- const char *path;
+ int dirfd, fd;
+ const char *name;
+
+ *valid = 0;
- if (tspath(&path) == -1)
- errx(1, "failed to get timestamp path");
- if (checktsdir(path))
- errx(1, "checktsdir");
+ if ((name = tsname()) == NULL)
+ errx(1, "failed to get timestamp name");
+ if ((dirfd = opentsdir()) == -1)
+ errx(1, "opentsdir");
- if ((fd = open(path, (O_RDWR), (S_IRUSR|S_IWUSR))) == -1)
+ if ((fd = openat(dirfd, name, (O_RDWR), (S_IRUSR|S_IWUSR))) == -1)
if (errno != ENOENT)
- err(1, "open: %s", path);
+ err(1, "open: %s", name);
if (fd == -1) {
- if ((fd = open(path, (O_RDWR|O_CREAT|O_EXCL), (S_IRUSR|S_IWUSR))) == -1)
- err(1, "open: %s", path);
- *valid = 0;
- return fd;
+ if ((fd = openat(dirfd, name, (O_RDWR|O_CREAT|O_EXCL), (S_IRUSR|S_IWUSR))) == -1)
+ err(1, "open: %s", name);
}
- if (fstat(fd, &st) == -1)
- err(1, "stat");
- if ((st.st_mode & S_IFMT) != S_IFREG)
- errx(1, "timestamp is not a file");
- if ((st.st_mode & (S_IWGRP|S_IRGRP|S_IXGRP|S_IWOTH|S_IROTH|S_IXOTH)) != 0)
- errx(1, "timestamp permissions wrong");
-
- gid = getegid();
- if (st.st_uid != 0 || st.st_gid != gid)
- errx(1, "timestamp has wrong owner");
+ size_t tssize;
+ if (checktsfile(fd, &tssize) == -1)
+ err(1, "checktsfile");
- if (st.st_size == 0) {
- *valid = 0;
- return fd;
- }
-
- if (st.st_size != sizeof(struct timespec) * 2)
+ /* The timestamp size is 0 if its a new file or a
+ * timestamp that was never set, its not valid but
+ * can be used to write the new timestamp.
+ * If the size does not match the expected size it
+ * is incomplete and should never be used
+ */
+ if (tssize == 0)
+ goto ret;
+ else if (tssize != sizeof(struct timespec) * 2)
errx(1, "corrupt timestamp file");
- *valid = persist_check(fd, secs) == 0;
-
+ *valid = validts(fd, secs) == 0;
+ret:
+ close(dirfd);
return fd;
}
int
persist_clear()
{
- const char *path;
- if (tspath(&path) == -1)
- errx(1, "failed to get timestamp path");
- if (unlink(path) == -1 && errno != ENOENT)
+ const char *name;
+ int dirfd;
+ if ((name = tsname()) == NULL)
+ errx(1, "failed to get timestamp name");
+ if ((dirfd = opentsdir()) == -1)
+ errx(1, "opentsdir");
+ if (unlinkat(dirfd, name, 0) == -1 && errno != ENOENT)
return -1;
+ close(dirfd);
return 0;
}