1 /* $NetBSD: clockctl.c,v 1.38 2020/02/21 00:26:22 joerg Exp $ */ 2 3 /*- 4 * Copyright (c) 2001 The NetBSD Foundation, Inc. 5 * All rights reserved. 6 * 7 * This code is derived from software contributed to The NetBSD Foundation 8 * by Emmanuel Dreyfus. 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted provided that the following conditions 12 * are met: 13 * 1. Redistributions of source code must retain the above copyright 14 * notice, this list of conditions and the following disclaimer. 15 * 2. Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in the 17 * documentation and/or other materials provided with the distribution. 18 * 3. The name of the author may not be used to endorse or promote products 19 * derived from this software without specific prior written permission. 20 * 21 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 22 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 23 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 24 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 25 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 26 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 27 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 28 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 29 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 30 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 31 */ 32 33 #include <sys/cdefs.h> 34 __KERNEL_RCSID(0, "$NetBSD: clockctl.c,v 1.38 2020/02/21 00:26:22 joerg Exp $"); 35 36 #ifdef _KERNEL_OPT 37 #include "opt_ntp.h" 38 #include "opt_compat_netbsd.h" 39 #endif 40 41 #include <sys/param.h> 42 #include <sys/systm.h> 43 #include <sys/proc.h> 44 #include <sys/errno.h> 45 #include <sys/ioctl.h> 46 #include <sys/device.h> 47 #include <sys/time.h> 48 #include <sys/conf.h> 49 #include <sys/timex.h> 50 #include <sys/kauth.h> 51 #include <sys/module.h> 52 #include <sys/mutex.h> 53 #include <sys/compat_stub.h> 54 55 #include <sys/clockctl.h> 56 #include <compat/sys/clockctl.h> 57 #include <compat/sys/time_types.h> 58 59 60 kmutex_t clockctl_mtx; 61 int clockctl_refcnt; 62 63 #include "ioconf.h" 64 65 dev_type_ioctl(clockctlioctl); 66 67 const struct cdevsw clockctl_cdevsw = { 68 .d_open = clockctlopen, 69 .d_close = clockctlclose, 70 .d_read = noread, 71 .d_write = nowrite, 72 .d_ioctl = clockctlioctl, 73 .d_stop = nostop, 74 .d_tty = notty, 75 .d_poll = nopoll, 76 .d_mmap = nommap, 77 .d_kqfilter = nokqfilter, 78 .d_discard = nodiscard, 79 .d_flag = D_OTHER, 80 }; 81 82 static kauth_listener_t clockctl_listener; 83 84 static int 85 clockctl_listener_cb(kauth_cred_t cred, kauth_action_t action, void *cookie, 86 void *arg0, void *arg1, void *arg2, void *arg3) 87 { 88 int result; 89 enum kauth_system_req req; 90 bool device_context; 91 92 result = KAUTH_RESULT_DEFER; 93 req = (enum kauth_system_req)(uintptr_t)arg0; 94 95 if ((action != KAUTH_SYSTEM_TIME) || 96 (req != KAUTH_REQ_SYSTEM_TIME_SYSTEM)) 97 return result; 98 99 device_context = arg3 != NULL; 100 101 /* Device is controlled by permissions, so allow. */ 102 if (device_context) 103 result = KAUTH_RESULT_ALLOW; 104 105 return result; 106 } 107 108 /*ARGSUSED*/ 109 void 110 clockctlattach(int num) 111 { 112 113 /* 114 * Don't initialize the listener here - it will get handled as part 115 * of module initialization. 116 */ 117 #if 0 118 clockctl_listener = kauth_listen_scope(KAUTH_SCOPE_SYSTEM, 119 clockctl_listener_cb, NULL); 120 #endif 121 } 122 123 /* 124 * Maintain a refcount for each open/close, so we know when it is 125 * safe to call devsw_detach() 126 */ 127 int 128 clockctlopen(dev_t dev, int flag, int mode, struct lwp *l) 129 { 130 131 mutex_enter(&clockctl_mtx); 132 clockctl_refcnt++; 133 mutex_exit(&clockctl_mtx); 134 135 return 0; 136 } 137 138 int 139 clockctlclose(dev_t dev, int flag, int mode, struct lwp *l) 140 { 141 142 mutex_enter(&clockctl_mtx); 143 clockctl_refcnt--; 144 mutex_exit(&clockctl_mtx); 145 146 return 0; 147 } 148 149 MODULE(MODULE_CLASS_DRIVER, clockctl, NULL); 150 151 int 152 clockctl_modcmd(modcmd_t cmd, void *data) 153 { 154 int error; 155 #ifdef _MODULE 156 int bmajor, cmajor; 157 #endif 158 159 error = 0; 160 161 switch (cmd) { 162 case MODULE_CMD_INIT: 163 mutex_init(&clockctl_mtx, MUTEX_DEFAULT, IPL_NONE); 164 165 clockctl_listener = kauth_listen_scope(KAUTH_SCOPE_SYSTEM, 166 clockctl_listener_cb, NULL); 167 168 #ifdef _MODULE 169 bmajor = cmajor = -1; 170 error = devsw_attach("clockctl", NULL, &bmajor, 171 &clockctl_cdevsw, &cmajor); 172 if (error != 0) 173 kauth_unlisten_scope(clockctl_listener); 174 #endif 175 176 break; 177 178 case MODULE_CMD_FINI: 179 mutex_enter(&clockctl_mtx); 180 if (clockctl_refcnt != 0) { 181 mutex_exit(&clockctl_mtx); 182 return EBUSY; 183 } 184 #ifdef _MODULE 185 error = devsw_detach(NULL, &clockctl_cdevsw); 186 #endif 187 mutex_exit(&clockctl_mtx); 188 189 if (error == 0) { 190 kauth_unlisten_scope(clockctl_listener); 191 mutex_destroy(&clockctl_mtx); 192 } 193 break; 194 195 default: 196 error = ENOTTY; 197 break; 198 } 199 200 return error; 201 } 202 203 int 204 clockctlioctl( 205 dev_t dev, 206 u_long cmd, 207 void *data, 208 int flags, 209 struct lwp *l) 210 { 211 int error = 0; 212 213 switch (cmd) { 214 case CLOCKCTL_SETTIMEOFDAY: { 215 struct clockctl_settimeofday *args = data; 216 217 error = settimeofday1(args->tv, true, args->tzp, l, false); 218 break; 219 } 220 case CLOCKCTL_ADJTIME: { 221 struct timeval atv, oldatv; 222 struct clockctl_adjtime *args = data; 223 224 if (args->delta) { 225 error = copyin(args->delta, &atv, sizeof(atv)); 226 if (error) 227 return (error); 228 } 229 adjtime1(args->delta ? &atv : NULL, 230 args->olddelta ? &oldatv : NULL, l->l_proc); 231 if (args->olddelta) 232 error = copyout(&oldatv, args->olddelta, 233 sizeof(oldatv)); 234 break; 235 } 236 case CLOCKCTL_CLOCK_SETTIME: { 237 struct clockctl_clock_settime *args = data; 238 struct timespec ts; 239 240 error = copyin(args->tp, &ts, sizeof ts); 241 if (error) 242 return (error); 243 error = clock_settime1(l->l_proc, args->clock_id, &ts, false); 244 break; 245 } 246 case CLOCKCTL_NTP_ADJTIME: { 247 struct clockctl_ntp_adjtime *args = data; 248 struct timex ntv; 249 250 if (vec_ntp_timestatus == NULL) { 251 error = ENOTTY; 252 break; 253 } 254 error = copyin(args->tp, &ntv, sizeof(ntv)); 255 if (error) 256 return (error); 257 258 (*vec_ntp_adjtime1)(&ntv); 259 260 error = copyout(&ntv, args->tp, sizeof(ntv)); 261 if (error == 0) 262 args->retval = (*vec_ntp_timestatus)(); 263 break; 264 } 265 default: 266 MODULE_HOOK_CALL(clockctl_ioctl_50_hook, 267 (dev, cmd, data, flags, l), enosys(), error); 268 if (error == ENOSYS) 269 error = ENOTTY; 270 } 271 272 return (error); 273 } 274