1/*      $NetBSD: clockctl.c,v 1.39 2022/03/28 12:33:20 riastradh 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.39 2022/03/28 12:33:20 riastradh 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
60kmutex_t clockctl_mtx;
61int clockctl_refcnt;
62
63#include "ioconf.h"
64
65dev_type_ioctl(clockctlioctl);
66
67const 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
82static kauth_listener_t clockctl_listener;
83
84static int
85clockctl_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*/
109void
110clockctlattach(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 */
127int
128clockctlopen(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
138int
139clockctlclose(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
149MODULE(MODULE_CLASS_DRIVER, clockctl, NULL);
150
151int
152clockctl_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		devsw_detach(NULL, &clockctl_cdevsw);
186#endif
187		mutex_exit(&clockctl_mtx);
188
189		kauth_unlisten_scope(clockctl_listener);
190		mutex_destroy(&clockctl_mtx);
191		break;
192
193	default:
194		error = ENOTTY;
195		break;
196	}
197
198	return error;
199}
200
201int
202clockctlioctl(
203    dev_t dev,
204    u_long cmd,
205    void *data,
206    int flags,
207    struct lwp *l)
208{
209	int error = 0;
210
211	switch (cmd) {
212	case CLOCKCTL_SETTIMEOFDAY: {
213		struct clockctl_settimeofday *args = data;
214
215		error = settimeofday1(args->tv, true, args->tzp, l, false);
216		break;
217	}
218	case CLOCKCTL_ADJTIME: {
219		struct timeval atv, oldatv;
220		struct clockctl_adjtime *args = data;
221
222		if (args->delta) {
223			error = copyin(args->delta, &atv, sizeof(atv));
224			if (error)
225				return (error);
226		}
227		adjtime1(args->delta ? &atv : NULL,
228		    args->olddelta ? &oldatv : NULL, l->l_proc);
229		if (args->olddelta)
230			error = copyout(&oldatv, args->olddelta,
231			    sizeof(oldatv));
232		break;
233	}
234	case CLOCKCTL_CLOCK_SETTIME: {
235		struct clockctl_clock_settime *args = data;
236		struct timespec ts;
237
238		error = copyin(args->tp, &ts, sizeof ts);
239		if (error)
240			return (error);
241		error = clock_settime1(l->l_proc, args->clock_id, &ts, false);
242		break;
243	}
244	case CLOCKCTL_NTP_ADJTIME: {
245		struct clockctl_ntp_adjtime *args = data;
246		struct timex ntv;
247
248		if (vec_ntp_timestatus == NULL) {
249			error = ENOTTY;
250			break;
251		}
252		error = copyin(args->tp, &ntv, sizeof(ntv));
253		if (error)
254			return (error);
255
256		(*vec_ntp_adjtime1)(&ntv);
257
258		error = copyout(&ntv, args->tp, sizeof(ntv));
259		if (error == 0)
260			args->retval = (*vec_ntp_timestatus)();
261		break;
262	}
263	default:
264		MODULE_HOOK_CALL(clockctl_ioctl_50_hook,
265		    (dev, cmd, data, flags, l), enosys(), error);
266		if (error == ENOSYS)
267			error = ENOTTY;
268	}
269
270	return (error);
271}
272