While looking whether it's possible to change ntpd to work when
chrooted to a file system mounted with the nodev flag, I noticed
that /dev/clockctl is open with O_CLOEXEC and its file descriptor
is kept in a static variable.  I'm not sure it will work after a
fork correctly. ntpd doesn't open /dev/clockctl before forking but
things may change in a future. Any opinions?

PS I attach my proof-of-concept patch that attempts to configure
/dev/clockctl before chrooting ntpd but it doesn't work because
clockctl's file descriptor is closed by the fork.

-- 
Alex
Index: src/external/bsd/ntp/dist/ntpd/ntpd.c
===================================================================
RCS file: /cvsroot/src/external/bsd/ntp/dist/ntpd/ntpd.c,v
retrieving revision 1.15
diff -p -u -u -r1.15 ntpd.c
--- src/external/bsd/ntp/dist/ntpd/ntpd.c       7 Apr 2018 00:19:53 -0000       
1.15
+++ src/external/bsd/ntp/dist/ntpd/ntpd.c       29 Apr 2018 16:06:36 -0000
@@ -214,6 +214,10 @@ extern int syscall (int, ...);
 static volatile int signalled  = 0;
 static volatile int signo      = 0;
 
+#if 1 /* XXX HAVE_CLOCKCTL_CONFIGURE */
+extern int clockctl_configure(void);
+#endif
+
 /* In an ideal world, 'finish_safe()' would declared as noreturn... */
 static void            finish_safe     (int);
 static RETSIGTYPE      finish          (int);
@@ -1027,6 +1031,11 @@ getgroup:
                                msyslog(LOG_ERR, "Cannot chdir() to `%s': %m", 
chrootdir);
                                exit (-1);
                        }
+#if 1                  /* XXX HAVE_CLOCKCTL_CONFIGURE */
+                       /* open /dev/clockctl while it's still accessible */
+                       if (user != NULL)
+                               clockctl_configure();
+#endif
                        if (chroot(chrootdir)) {
                                msyslog(LOG_ERR, "Cannot chroot() to `%s': %m", 
chrootdir);
                                exit (-1);
Index: src/lib/libc/sys/clock_settime.c
===================================================================
RCS file: /cvsroot/src/lib/libc/sys/clock_settime.c,v
retrieving revision 1.12
diff -p -u -u -r1.12 clock_settime.c
--- src/lib/libc/sys/clock_settime.c    15 Oct 2011 23:00:02 -0000      1.12
+++ src/lib/libc/sys/clock_settime.c    29 Apr 2018 16:06:36 -0000
@@ -51,23 +51,23 @@ __RCSID("$NetBSD: clock_settime.c,v 1.12
 
 #include <sys/clockctl.h>
  
-extern int __clockctl_fd;
-
+int clockctl_configure(void);
+int clockctl_is_configured(void);
 int ____clock_settime50(clockid_t, const struct timespec *);
 
 int
 clock_settime(clockid_t clock_id, const struct timespec *tp)
 {
        struct clockctl_clock_settime args;
-       int rv;
+       int rv, clockctl = -1;
 
        /*
         * always try the syscall first and attempt to switch to
         * clockctl if that fails.
         */
-       if (__clockctl_fd == -1) {
+       if (!clockctl_is_configured()) {
                rv = ____clock_settime50(clock_id, tp);
-       
+
                /*
                 * return unless we failed with EPERM
                 */
@@ -78,19 +78,18 @@ clock_settime(clockid_t clock_id, const 
                 * If this fails, it means that we are not root
                 * and we cannot open clockctl. This is a failure.
                 */
-               __clockctl_fd = open(_PATH_CLOCKCTL, O_WRONLY | O_CLOEXEC, 0);
-               if (__clockctl_fd == -1) {
+               if ((clockctl = clockctl_configure()) == -1) {
                        /* original error was EPERM - don't leak open errors */
                        errno = EPERM;
                        return -1;
                }
        }
 
-       /* 
-        * If __clockctl_fd >=0, clockctl has already been open
+       /*
+        * If we're here, clockctl has already been open
         * and used, so we carry on using it.
         */
        args.clock_id = clock_id;
        args.tp = tp;
-       return ioctl(__clockctl_fd, CLOCKCTL_CLOCK_SETTIME, &args);
+       return ioctl(clockctl, CLOCKCTL_CLOCK_SETTIME, &args);
 }
Index: src/lib/libc/sys/adjtime.c
===================================================================
RCS file: /cvsroot/src/lib/libc/sys/adjtime.c,v
retrieving revision 1.12
diff -p -u -u -r1.12 adjtime.c
--- src/lib/libc/sys/adjtime.c  15 Oct 2011 23:00:02 -0000      1.12
+++ src/lib/libc/sys/adjtime.c  29 Apr 2018 16:06:36 -0000
@@ -48,24 +48,24 @@ __RCSID("$NetBSD: adjtime.c,v 1.12 2011/
 #include <sys/syscall.h>
 
 #include <sys/clockctl.h>
- 
-extern int __clockctl_fd;
 
+int clockctl_configure(void);
+int clockctl_is_configured(void);
 int ____adjtime50(const struct timeval *, struct timeval *);
 
 int
 adjtime(const struct timeval *delta, struct timeval *olddelta)
 {
        struct clockctl_adjtime args;
-       int rv;
+       int rv, clockctl = -1;
 
        /*
-        * we always attempt the syscall first and switch to 
+        * we always attempt the syscall first and switch to
         * clockctl if that fails with EPERM
         */
-       if (__clockctl_fd == -1) {
+       if (!clockctl_is_configured()) {
                rv = ____adjtime50(delta, olddelta);
-       
+
                /*
                 * try via clockctl if the call fails with EPERM
                 */
@@ -76,19 +76,18 @@ adjtime(const struct timeval *delta, str
                 * If this fails, it means that we are not root
                 * and we cannot open clockctl. This is a failure.
                 */
-               __clockctl_fd = open(_PATH_CLOCKCTL, O_WRONLY | O_CLOEXEC, 0);
-               if (__clockctl_fd == -1) {
+               if ((clockctl = clockctl_configure()) == -1) {
                        /* original error was EPERM - don't leak open errors */
                        errno = EPERM;
                        return -1;
                }
        }
 
-       /* 
-        * If __clockctl_fd >=0, clockctl has already been open
+       /*
+        * If we're, clockctl has already been open
         * and used, so we carry on using it.
         */
        args.delta = delta;
        args.olddelta = olddelta;
-       return ioctl(__clockctl_fd, CLOCKCTL_ADJTIME, &args);
+       return ioctl(clockctl, CLOCKCTL_ADJTIME, &args);
 }
Index: src/lib/libc/sys/ntp_adjtime.c
===================================================================
RCS file: /cvsroot/src/lib/libc/sys/ntp_adjtime.c,v
retrieving revision 1.13
diff -p -u -u -r1.13 ntp_adjtime.c
--- src/lib/libc/sys/ntp_adjtime.c      20 Mar 2012 16:26:12 -0000      1.13
+++ src/lib/libc/sys/ntp_adjtime.c      29 Apr 2018 16:06:36 -0000
@@ -55,8 +55,8 @@ __RCSID("$NetBSD: ntp_adjtime.c,v 1.13 2
 __weak_alias(ntp_adjtime,_ntp_adjtime)
 #endif
 
-extern int __clockctl_fd;
-
+int clockctl_configure(void);
+int clockctl_is_configured(void);
 int __ntp_adjtime(struct timex *);
 
 int
@@ -64,7 +64,7 @@ ntp_adjtime(struct timex *tp)
 {
        struct clockctl_ntp_adjtime args;
        int error;
-       int rv;
+       int rv, clockctl = -1;
 
        /*
         * we always attempt to use the syscall unless we had to
@@ -72,9 +72,9 @@ ntp_adjtime(struct timex *tp)
         *
         * ntp_adjtime() is callable for mortals if tp->modes == 0 !
         */
-       if (__clockctl_fd == -1) {
+       if (!clockctl_is_configured()) {
                rv = __ntp_adjtime(tp);
-       
+
                /*
                 * if we fail with EPERM we try the clockctl device
                 */
@@ -86,8 +86,7 @@ ntp_adjtime(struct timex *tp)
                 * and we cannot open clockctl. This is a true
                 * failure.
                 */
-               __clockctl_fd = open(_PATH_CLOCKCTL, O_WRONLY | O_CLOEXEC, 0);
-               if (__clockctl_fd == -1) {
+               if ((clockctl = clockctl_configure()) == -1) {
                        /* original error was EPERM - don't leak open errors */
                        errno = EPERM;
                        return -1;
@@ -95,11 +94,11 @@ ntp_adjtime(struct timex *tp)
        }
 
        /*
-        * If __clockctl_fd >=0, clockctl has already been open
+        * If we're, clockctl has already been open
         * and used, so we carry on using it.
         */
        args.tp = tp;
-       error = ioctl(__clockctl_fd, CLOCKCTL_NTP_ADJTIME, &args);
+       error = ioctl(clockctl, CLOCKCTL_NTP_ADJTIME, &args);
 
        /*
         * There is no way to get retval set through ioctl(), hence we
Index: src/lib/libc/sys/settimeofday.c
===================================================================
RCS file: /cvsroot/src/lib/libc/sys/settimeofday.c,v
retrieving revision 1.14
diff -p -u -u -r1.14 settimeofday.c
--- src/lib/libc/sys/settimeofday.c     15 Oct 2011 23:00:02 -0000      1.14
+++ src/lib/libc/sys/settimeofday.c     29 Apr 2018 16:06:36 -0000
@@ -50,24 +50,45 @@ __RCSID("$NetBSD: settimeofday.c,v 1.14 
 #include <string.h>
 #include <time.h>
 #include <unistd.h>
- 
-int __clockctl_fd = -1;
 
+static int __clockctl_fd = -1;
+
+int clockctl_configure(void);
+int clockctl_is_configured(void);
 int ____settimeofday50(const struct timeval *, const void *);
 
 int
+clockctl_configure(void)
+{
+
+       if (__clockctl_fd == -1) {
+               __clockctl_fd = open(_PATH_CLOCKCTL, O_WRONLY | O_CLOEXEC, 0);
+               /* XXX Check that __clockctl_fd is usable. */
+       }
+
+       return __clockctl_fd;
+}
+
+int
+clockctl_is_configured(void)
+{
+
+       return __clockctl_fd != -1;
+}
+
+int
 settimeofday(const struct timeval *tv, const void *tzp)
 {
        struct clockctl_settimeofday args;
-       int rv;
+       int rv, clockctl = -1;
 
        /*
         * try syscal first and attempt to switch to clockctl
         * if that fails with EPERM
         */
-       if (__clockctl_fd == -1) {
+       if (!clockctl_is_configured()) {
                rv = ____settimeofday50(tv, tzp);
-       
+
                /*
                 * switch to clockctl if we fail with EPERM, this
                 * may be cause by an attempt to set the time backwards
@@ -77,19 +98,18 @@ settimeofday(const struct timeval *tv, c
                if (rv != -1 || errno != EPERM)
                        return rv;
 
-               __clockctl_fd = open(_PATH_CLOCKCTL, O_WRONLY | O_CLOEXEC, 0);
-               if (__clockctl_fd == -1) {
+               if ((clockctl = clockctl_configure()) == -1) {
                        /* original error was EPERM - don't leak open errors */
                        errno = EPERM;
                        return -1;
                }
        }
 
-       /* 
-        * If __clockctl_fd >=0, clockctl has already been open
+       /*
+        * If we're, clockctl has already been open
         * and used, so we carry on using it.
         */
        args.tv = tv;
        args.tzp = tzp;
-       return ioctl(__clockctl_fd, CLOCKCTL_SETTIMEOFDAY, &args);
+       return ioctl(clockctl, CLOCKCTL_SETTIMEOFDAY, &args);
 }

Reply via email to