xref: /freebsd/sys/kern/kern_environment.c (revision f856af0466c076beef4ea9b15d088e1119a945b8)
1 /*-
2  * Copyright (c) 1998 Michael Smith
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24  * SUCH DAMAGE.
25  */
26 
27 /*
28  * The unified bootloader passes us a pointer to a preserved copy of
29  * bootstrap/kernel environment variables.  We convert them to a
30  * dynamic array of strings later when the VM subsystem is up.
31  *
32  * We make these available through the kenv(2) syscall for userland
33  * and through getenv()/freeenv() setenv() unsetenv() testenv() for
34  * the kernel.
35  */
36 
37 #include <sys/cdefs.h>
38 __FBSDID("$FreeBSD$");
39 
40 #include "opt_mac.h"
41 
42 #include <sys/types.h>
43 #include <sys/param.h>
44 #include <sys/proc.h>
45 #include <sys/queue.h>
46 #include <sys/lock.h>
47 #include <sys/malloc.h>
48 #include <sys/mutex.h>
49 #include <sys/priv.h>
50 #include <sys/kernel.h>
51 #include <sys/systm.h>
52 #include <sys/sysent.h>
53 #include <sys/sysproto.h>
54 #include <sys/libkern.h>
55 #include <sys/kenv.h>
56 
57 #include <security/mac/mac_framework.h>
58 
59 static MALLOC_DEFINE(M_KENV, "kenv", "kernel environment");
60 
61 #define KENV_SIZE	512	/* Maximum number of environment strings */
62 
63 /* pointer to the static environment */
64 char		*kern_envp;
65 static char	*kernenv_next(char *);
66 
67 /* dynamic environment variables */
68 char		**kenvp;
69 struct mtx	kenv_lock;
70 
71 /*
72  * No need to protect this with a mutex
73  * since SYSINITS are single threaded.
74  */
75 int	dynamic_kenv = 0;
76 
77 #define KENV_CHECK	if (!dynamic_kenv) \
78 			    panic("%s: called before SI_SUB_KMEM", __func__)
79 
80 int
81 kenv(td, uap)
82 	struct thread *td;
83 	struct kenv_args /* {
84 		int what;
85 		const char *name;
86 		char *value;
87 		int len;
88 	} */ *uap;
89 {
90 	char *name, *value, *buffer = NULL;
91 	size_t len, done, needed;
92 	int error, i;
93 
94 	KASSERT(dynamic_kenv, ("kenv: dynamic_kenv = 0"));
95 
96 	error = 0;
97 	if (uap->what == KENV_DUMP) {
98 #ifdef MAC
99 		error = mac_check_kenv_dump(td->td_ucred);
100 		if (error)
101 			return (error);
102 #endif
103 		done = needed = 0;
104 		if (uap->len > 0 && uap->value != NULL)
105 			buffer = malloc(uap->len, M_TEMP, M_WAITOK|M_ZERO);
106 		mtx_lock(&kenv_lock);
107 		for (i = 0; kenvp[i] != NULL; i++) {
108 			len = strlen(kenvp[i]) + 1;
109 			needed += len;
110 			len = min(len, uap->len - done);
111 			/*
112 			 * If called with a NULL or insufficiently large
113 			 * buffer, just keep computing the required size.
114 			 */
115 			if (uap->value != NULL && buffer != NULL && len > 0) {
116 				bcopy(kenvp[i], buffer + done, len);
117 				done += len;
118 			}
119 		}
120 		mtx_unlock(&kenv_lock);
121 		if (buffer != NULL) {
122 			error = copyout(buffer, uap->value, done);
123 			free(buffer, M_TEMP);
124 		}
125 		td->td_retval[0] = ((done == needed) ? 0 : needed);
126 		return (error);
127 	}
128 
129 	switch (uap->what) {
130 	case KENV_SET:
131 		error = priv_check(td, PRIV_KENV_SET);
132 		if (error)
133 			return (error);
134 		break;
135 
136 	case KENV_UNSET:
137 		error = priv_check(td, PRIV_KENV_UNSET);
138 		if (error)
139 			return (error);
140 		break;
141 	}
142 
143 	name = malloc(KENV_MNAMELEN, M_TEMP, M_WAITOK);
144 
145 	error = copyinstr(uap->name, name, KENV_MNAMELEN, NULL);
146 	if (error)
147 		goto done;
148 
149 	switch (uap->what) {
150 	case KENV_GET:
151 #ifdef MAC
152 		error = mac_check_kenv_get(td->td_ucred, name);
153 		if (error)
154 			goto done;
155 #endif
156 		value = getenv(name);
157 		if (value == NULL) {
158 			error = ENOENT;
159 			goto done;
160 		}
161 		len = strlen(value) + 1;
162 		if (len > uap->len)
163 			len = uap->len;
164 		error = copyout(value, uap->value, len);
165 		freeenv(value);
166 		if (error)
167 			goto done;
168 		td->td_retval[0] = len;
169 		break;
170 	case KENV_SET:
171 		len = uap->len;
172 		if (len < 1) {
173 			error = EINVAL;
174 			goto done;
175 		}
176 		if (len > KENV_MVALLEN)
177 			len = KENV_MVALLEN;
178 		value = malloc(len, M_TEMP, M_WAITOK);
179 		error = copyinstr(uap->value, value, len, NULL);
180 		if (error) {
181 			free(value, M_TEMP);
182 			goto done;
183 		}
184 #ifdef MAC
185 		error = mac_check_kenv_set(td->td_ucred, name, value);
186 		if (error == 0)
187 #endif
188 			setenv(name, value);
189 		free(value, M_TEMP);
190 		break;
191 	case KENV_UNSET:
192 #ifdef MAC
193 		error = mac_check_kenv_unset(td->td_ucred, name);
194 		if (error)
195 			goto done;
196 #endif
197 		error = unsetenv(name);
198 		if (error)
199 			error = ENOENT;
200 		break;
201 	default:
202 		error = EINVAL;
203 		break;
204 	}
205 done:
206 	free(name, M_TEMP);
207 	return (error);
208 }
209 
210 /*
211  * Setup the dynamic kernel environment.
212  */
213 static void
214 init_dynamic_kenv(void *data __unused)
215 {
216 	char *cp;
217 	int len, i;
218 
219 	kenvp = malloc((KENV_SIZE + 1) * sizeof(char *), M_KENV,
220 		M_WAITOK | M_ZERO);
221 	i = 0;
222 	for (cp = kern_envp; cp != NULL; cp = kernenv_next(cp)) {
223 		len = strlen(cp) + 1;
224 		if (i < KENV_SIZE) {
225 			kenvp[i] = malloc(len, M_KENV, M_WAITOK);
226 			strcpy(kenvp[i++], cp);
227 		} else
228 			printf(
229 			    "WARNING: too many kenv strings, ignoring %s\n",
230 			    cp);
231 	}
232 	kenvp[i] = NULL;
233 
234 	mtx_init(&kenv_lock, "kernel environment", NULL, MTX_DEF);
235 	dynamic_kenv = 1;
236 }
237 SYSINIT(kenv, SI_SUB_KMEM, SI_ORDER_ANY, init_dynamic_kenv, NULL);
238 
239 void
240 freeenv(char *env)
241 {
242 
243 	if (dynamic_kenv)
244 		free(env, M_KENV);
245 }
246 
247 /*
248  * Internal functions for string lookup.
249  */
250 static char *
251 _getenv_dynamic(const char *name, int *idx)
252 {
253 	char *cp;
254 	int len, i;
255 
256 	mtx_assert(&kenv_lock, MA_OWNED);
257 	len = strlen(name);
258 	for (cp = kenvp[0], i = 0; cp != NULL; cp = kenvp[++i]) {
259 		if ((strncmp(cp, name, len) == 0) &&
260 		    (cp[len] == '=')) {
261 			if (idx != NULL)
262 				*idx = i;
263 			return (cp + len + 1);
264 		}
265 	}
266 	return (NULL);
267 }
268 
269 static char *
270 _getenv_static(const char *name)
271 {
272 	char *cp, *ep;
273 	int len;
274 
275 	for (cp = kern_envp; cp != NULL; cp = kernenv_next(cp)) {
276 		for (ep = cp; (*ep != '=') && (*ep != 0); ep++)
277 			;
278 		if (*ep != '=')
279 			continue;
280 		len = ep - cp;
281 		ep++;
282 		if (!strncmp(name, cp, len) && name[len] == 0)
283 			return (ep);
284 	}
285 	return (NULL);
286 }
287 
288 /*
289  * Look up an environment variable by name.
290  * Return a pointer to the string if found.
291  * The pointer has to be freed with freeenv()
292  * after use.
293  */
294 char *
295 getenv(const char *name)
296 {
297 	char buf[KENV_MNAMELEN + 1 + KENV_MVALLEN + 1];
298 	char *ret, *cp;
299 	int len;
300 
301 	if (dynamic_kenv) {
302 		mtx_lock(&kenv_lock);
303 		cp = _getenv_dynamic(name, NULL);
304 		if (cp != NULL) {
305 			strcpy(buf, cp);
306 			mtx_unlock(&kenv_lock);
307 			len = strlen(buf) + 1;
308 			ret = malloc(len, M_KENV, M_WAITOK);
309 			strcpy(ret, buf);
310 		} else {
311 			mtx_unlock(&kenv_lock);
312 			ret = NULL;
313 		}
314 	} else
315 		ret = _getenv_static(name);
316 	return (ret);
317 }
318 
319 /*
320  * Test if an environment variable is defined.
321  */
322 int
323 testenv(const char *name)
324 {
325 	char *cp;
326 
327 	if (dynamic_kenv) {
328 		mtx_lock(&kenv_lock);
329 		cp = _getenv_dynamic(name, NULL);
330 		mtx_unlock(&kenv_lock);
331 	} else
332 		cp = _getenv_static(name);
333 	if (cp != NULL)
334 		return (1);
335 	return (0);
336 }
337 
338 /*
339  * Set an environment variable by name.
340  */
341 int
342 setenv(const char *name, const char *value)
343 {
344 	char *buf, *cp, *oldenv;
345 	int namelen, vallen, i;
346 
347 	KENV_CHECK;
348 
349 	namelen = strlen(name) + 1;
350 	if (namelen > KENV_MNAMELEN)
351 		return (-1);
352 	vallen = strlen(value) + 1;
353 	if (vallen > KENV_MVALLEN)
354 		return (-1);
355 	buf = malloc(namelen + vallen, M_KENV, M_WAITOK);
356 	sprintf(buf, "%s=%s", name, value);
357 
358 	mtx_lock(&kenv_lock);
359 	cp = _getenv_dynamic(name, &i);
360 	if (cp != NULL) {
361 		oldenv = kenvp[i];
362 		kenvp[i] = buf;
363 		mtx_unlock(&kenv_lock);
364 		free(oldenv, M_KENV);
365 	} else {
366 		/* We add the option if it wasn't found */
367 		for (i = 0; (cp = kenvp[i]) != NULL; i++)
368 			;
369 
370 		/* Bounds checking */
371 		if (i < 0 || i >= KENV_SIZE) {
372 			free(buf, M_KENV);
373 			mtx_unlock(&kenv_lock);
374 			return (-1);
375 		}
376 
377 		kenvp[i] = buf;
378 		kenvp[i + 1] = NULL;
379 		mtx_unlock(&kenv_lock);
380 	}
381 	return (0);
382 }
383 
384 /*
385  * Unset an environment variable string.
386  */
387 int
388 unsetenv(const char *name)
389 {
390 	char *cp, *oldenv;
391 	int i, j;
392 
393 	KENV_CHECK;
394 
395 	mtx_lock(&kenv_lock);
396 	cp = _getenv_dynamic(name, &i);
397 	if (cp != NULL) {
398 		oldenv = kenvp[i];
399 		for (j = i + 1; kenvp[j] != NULL; j++)
400 			kenvp[i++] = kenvp[j];
401 		kenvp[i] = NULL;
402 		mtx_unlock(&kenv_lock);
403 		free(oldenv, M_KENV);
404 		return (0);
405 	}
406 	mtx_unlock(&kenv_lock);
407 	return (-1);
408 }
409 
410 /*
411  * Return a string value from an environment variable.
412  */
413 int
414 getenv_string(const char *name, char *data, int size)
415 {
416 	char *tmp;
417 
418 	tmp = getenv(name);
419 	if (tmp != NULL) {
420 		strlcpy(data, tmp, size);
421 		freeenv(tmp);
422 		return (1);
423 	} else
424 		return (0);
425 }
426 
427 /*
428  * Return an integer value from an environment variable.
429  */
430 int
431 getenv_int(const char *name, int *data)
432 {
433 	quad_t tmp;
434 	int rval;
435 
436 	rval = getenv_quad(name, &tmp);
437 	if (rval)
438 		*data = (int) tmp;
439 	return (rval);
440 }
441 
442 /*
443  * Return a long value from an environment variable.
444  */
445 long
446 getenv_long(const char *name, long *data)
447 {
448 	quad_t tmp;
449 	long rval;
450 
451 	rval = getenv_quad(name, &tmp);
452 	if (rval)
453 		*data = (long) tmp;
454 	return (rval);
455 }
456 
457 /*
458  * Return an unsigned long value from an environment variable.
459  */
460 unsigned long
461 getenv_ulong(const char *name, unsigned long *data)
462 {
463 	quad_t tmp;
464 	long rval;
465 
466 	rval = getenv_quad(name, &tmp);
467 	if (rval)
468 		*data = (unsigned long) tmp;
469 	return (rval);
470 }
471 
472 /*
473  * Return a quad_t value from an environment variable.
474  */
475 int
476 getenv_quad(const char *name, quad_t *data)
477 {
478 	char	*value;
479 	char	*vtp;
480 	quad_t	iv;
481 
482 	value = getenv(name);
483 	if (value == NULL)
484 		return (0);
485 	iv = strtoq(value, &vtp, 0);
486 	if (vtp == value || (vtp[0] != '\0' && vtp[1] != '\0')) {
487 		freeenv(value);
488 		return (0);
489 	}
490 	switch (vtp[0]) {
491 	case 't': case 'T':
492 		iv *= 1024;
493 	case 'g': case 'G':
494 		iv *= 1024;
495 	case 'm': case 'M':
496 		iv *= 1024;
497 	case 'k': case 'K':
498 		iv *= 1024;
499 	case '\0':
500 		break;
501 	default:
502 		freeenv(value);
503 		return (0);
504 	}
505 	*data = iv;
506 	freeenv(value);
507 	return (1);
508 }
509 
510 /*
511  * Find the next entry after the one which (cp) falls within, return a
512  * pointer to its start or NULL if there are no more.
513  */
514 static char *
515 kernenv_next(char *cp)
516 {
517 
518 	if (cp != NULL) {
519 		while (*cp != 0)
520 			cp++;
521 		cp++;
522 		if (*cp == 0)
523 			cp = NULL;
524 	}
525 	return (cp);
526 }
527 
528 void
529 tunable_int_init(void *data)
530 {
531 	struct tunable_int *d = (struct tunable_int *)data;
532 
533 	TUNABLE_INT_FETCH(d->path, d->var);
534 }
535 
536 void
537 tunable_long_init(void *data)
538 {
539 	struct tunable_long *d = (struct tunable_long *)data;
540 
541 	TUNABLE_LONG_FETCH(d->path, d->var);
542 }
543 
544 void
545 tunable_ulong_init(void *data)
546 {
547 	struct tunable_ulong *d = (struct tunable_ulong *)data;
548 
549 	TUNABLE_ULONG_FETCH(d->path, d->var);
550 }
551 
552 void
553 tunable_str_init(void *data)
554 {
555 	struct tunable_str *d = (struct tunable_str *)data;
556 
557 	TUNABLE_STR_FETCH(d->path, d->var, d->size);
558 }
559