xref: /dragonfly/sys/kern/kern_varsym.c (revision 0bb9290e)
1 /*
2  * Copyright (c) 2003,2004 The DragonFly Project.  All rights reserved.
3  *
4  * This code is derived from software contributed to The DragonFly Project
5  * by Matthew Dillon <dillon@backplane.com>
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  *
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in
15  *    the documentation and/or other materials provided with the
16  *    distribution.
17  * 3. Neither the name of The DragonFly Project nor the names of its
18  *    contributors may be used to endorse or promote products derived
19  *    from this software without specific, prior written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
24  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE
25  * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
26  * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
27  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
28  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
29  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
30  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
31  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32  * SUCH DAMAGE.
33  *
34  * $DragonFly: src/sys/kern/kern_varsym.c,v 1.7 2006/06/05 07:26:10 dillon Exp $
35  */
36 
37 /*
38  * This module implements variable storage and management for variant
39  * symlinks.  These variables may also be used for general purposes.
40  */
41 
42 #include <sys/param.h>
43 #include <sys/systm.h>
44 #include <sys/kernel.h>
45 #include <sys/ucred.h>
46 #include <sys/resourcevar.h>
47 #include <sys/proc.h>
48 #include <sys/jail.h>
49 #include <sys/queue.h>
50 #include <sys/sysctl.h>
51 #include <sys/malloc.h>
52 #include <sys/varsym.h>
53 #include <sys/sysproto.h>
54 
55 MALLOC_DEFINE(M_VARSYM, "varsym", "variable sets for variant symlinks");
56 
57 struct varsymset	varsymset_sys;
58 
59 /*
60  * Initialize the variant symlink subsystem
61  */
62 static void
63 varsym_sysinit(void *dummy)
64 {
65     varsymset_init(&varsymset_sys, NULL);
66 }
67 SYSINIT(announce, SI_SUB_INTRINSIC, SI_ORDER_FIRST, varsym_sysinit, NULL);
68 
69 /*
70  * varsymreplace() - called from namei
71  *
72  *	Do variant symlink variable substitution
73  */
74 int
75 varsymreplace(char *cp, int linklen, int maxlen)
76 {
77     int rlen;
78     int xlen;
79     int nlen;
80     int i;
81     varsym_t var;
82 
83     rlen = linklen;
84     while (linklen > 1) {
85 	if (cp[0] == '$' && cp[1] == '{') {
86 	    for (i = 2; i < linklen; ++i) {
87 		if (cp[i] == '}')
88 		    break;
89 	    }
90 	    if (i < linklen &&
91 		(var = varsymfind(VARSYM_ALL_MASK, cp + 2, i - 2)) != NULL
92 	    ) {
93 		xlen = i + 1;			/* bytes to strike */
94 		nlen = strlen(var->vs_data);	/* bytes to add */
95 		if (linklen + nlen - xlen >= maxlen) {
96 		    varsymdrop(var);
97 		    return(-1);
98 		}
99 		KKASSERT(linklen >= xlen);
100 		if (linklen != xlen)
101 		    bcopy(cp + xlen, cp + nlen, linklen - xlen);
102 		bcopy(var->vs_data, cp, nlen);
103 		linklen += nlen - xlen;	/* new relative length */
104 		rlen += nlen - xlen;	/* returned total length */
105 		cp += nlen;		/* adjust past replacement */
106 		linklen -= nlen;	/* adjust past replacement */
107 		maxlen -= nlen;		/* adjust past replacement */
108 	    } else {
109 		/*
110 		 * It's ok if i points to the '}', it will simply be
111 		 * skipped.  i could also have hit linklen.
112 		 */
113 		cp += i;
114 		linklen -= i;
115 		maxlen -= i;
116 	    }
117 	} else {
118 	    ++cp;
119 	    --linklen;
120 	    --maxlen;
121 	}
122     }
123     return(rlen);
124 }
125 
126 /*
127  * varsym_set() system call
128  *
129  * (int level, const char *name, const char *data)
130  */
131 int
132 sys_varsym_set(struct varsym_set_args *uap)
133 {
134     char name[MAXVARSYM_NAME];
135     char *buf;
136     int error;
137 
138     if ((error = copyinstr(uap->name, name, sizeof(name), NULL)) != 0)
139 	goto done2;
140     buf = malloc(MAXVARSYM_DATA, M_TEMP, M_WAITOK);
141     if (uap->data &&
142 	(error = copyinstr(uap->data, buf, MAXVARSYM_DATA, NULL)) != 0)
143     {
144 	goto done1;
145     }
146     switch(uap->level) {
147     case VARSYM_SYS:
148 	if (curthread->td_proc != NULL && curthread->td_proc->p_ucred->cr_prison != NULL)
149 	    uap->level = VARSYM_PRISON;
150     case VARSYM_PRISON:
151 	if (curthread->td_proc != NULL &&
152 	    (error = suser_cred(curthread->td_proc->p_ucred, PRISON_ROOT)) != 0)
153 	    break;
154 	/* fall through */
155     case VARSYM_USER:
156 	/* XXX check jail / implement per-jail user */
157 	/* fall through */
158     case VARSYM_PROC:
159 	if (uap->data) {
160 	    (void)varsymmake(uap->level, name, NULL);
161 	    error = varsymmake(uap->level, name, buf);
162 	} else {
163 	    error = varsymmake(uap->level, name, NULL);
164 	}
165 	break;
166     }
167 done1:
168     free(buf, M_TEMP);
169 done2:
170     return(error);
171 }
172 
173 /*
174  * varsym_get() system call
175  *
176  * (int mask, const char *wild, char *buf, int bufsize)
177  */
178 int
179 sys_varsym_get(struct varsym_get_args *uap)
180 {
181     char wild[MAXVARSYM_NAME];
182     varsym_t sym;
183     int error;
184     int dlen;
185 
186     if ((error = copyinstr(uap->wild, wild, sizeof(wild), NULL)) != 0)
187 	goto done;
188     sym = varsymfind(uap->mask, wild, strlen(wild));
189     if (sym == NULL) {
190 	error = ENOENT;
191 	goto done;
192     }
193     dlen = strlen(sym->vs_data);
194     if (dlen < uap->bufsize) {
195 	copyout(sym->vs_data, uap->buf, dlen + 1);
196     } else if (uap->bufsize) {
197 	copyout("", uap->buf, 1);
198     }
199     uap->sysmsg_result = dlen + 1;
200     varsymdrop(sym);
201 done:
202     return(error);
203 }
204 
205 /*
206  * varsym_list() system call
207  *
208  * (int level, char *buf, int maxsize, int *marker)
209  */
210 int
211 sys_varsym_list(struct varsym_list_args *uap)
212 {
213 	struct varsymset *vss;
214 	struct varsyment *ve;
215 	struct proc *p;
216 	int i;
217 	int error;
218 	int bytes;
219 	int earlyterm;
220 	int marker;
221 
222 	/*
223 	 * Get the marker from userspace.
224 	 */
225 	if ((error = copyin(uap->marker, &marker, sizeof(marker))) != 0)
226 		goto done;
227 
228 	/*
229 	 * Figure out the varsym set.
230 	 */
231 	p = curproc;
232 	vss = NULL;
233 
234 	switch (uap->level) {
235 	case VARSYM_PROC:
236 		if (p)
237 			vss = &p->p_varsymset;
238 		break;
239 	case VARSYM_USER:
240 		if (p)
241 			vss = &p->p_ucred->cr_uidinfo->ui_varsymset;
242 		break;
243 	case VARSYM_SYS:
244 		vss = &varsymset_sys;
245 		break;
246 	case VARSYM_PRISON:
247 		if (p != NULL && p->p_ucred->cr_prison != NULL)
248 			vss = &p->p_ucred->cr_prison->pr_varsymset;
249 		break;
250 	}
251 	if (vss == NULL) {
252 		error = EINVAL;
253 		goto done;
254 	}
255 
256 	/*
257 	 * Loop through the variables and dump them to uap->buf
258 	 */
259 	i = 0;
260 	bytes = 0;
261 	earlyterm = 0;
262 
263 	TAILQ_FOREACH(ve, &vss->vx_queue, ve_entry) {
264 		varsym_t sym = ve->ve_sym;
265 		int namelen = strlen(sym->vs_name);
266 		int datalen = strlen(sym->vs_data);
267 		int totlen = namelen + datalen + 2;
268 
269 		/*
270 		 * Skip to our index point
271 		 */
272 		if (i < marker) {
273 			++i;
274 			continue;
275 		}
276 
277 		/*
278 		 * Stop if there is insufficient space in the user buffer.
279 		 * If we haven't stored anything yet return EOVERFLOW.
280 		 * Note that the marker index (i) does not change.
281 		 */
282 		if (bytes + totlen > uap->maxsize) {
283 			if (bytes == 0)
284 				error = EOVERFLOW;
285 			earlyterm = 1;
286 			break;
287 		}
288 
289 		error = copyout(sym->vs_name, uap->buf + bytes, namelen + 1);
290 		if (error == 0) {
291 			bytes += namelen + 1;
292 			error = copyout(sym->vs_data, uap->buf + bytes, datalen + 1);
293 			if (error == 0)
294 				bytes += datalen + 1;
295 			else
296 				bytes -= namelen + 1;	/* revert if error */
297 		}
298 		if (error) {
299 			earlyterm = 1;
300 			break;
301 		}
302 		++i;
303 	}
304 
305 	/*
306 	 * Save the marker back.  If no error occured and earlyterm is clear
307 	 * the marker is set to -1 indicating that the variable list has been
308 	 * exhausted.  If no error occured the number of bytes loaded into
309 	 * the buffer will be returned, otherwise the syscall code returns -1.
310 	 */
311 	if (error == 0 && earlyterm == 0)
312 		marker = -1;
313 	else
314 		marker = i;
315 	if (error == 0)
316 		error = copyout(&marker, uap->marker, sizeof(marker));
317 	uap->sysmsg_result = bytes;
318 done:
319 	return(error);
320 }
321 
322 /*
323  * Lookup a variant symlink.  XXX use a hash table.
324  */
325 static
326 struct varsyment *
327 varsymlookup(struct varsymset *vss, const char *name, int namelen)
328 {
329     struct varsyment *ve;
330 
331     TAILQ_FOREACH(ve, &vss->vx_queue, ve_entry) {
332 	varsym_t var = ve->ve_sym;
333 	if (var->vs_namelen == namelen &&
334 	    bcmp(name, var->vs_name, namelen) == 0
335 	) {
336 	    return(ve);
337 	}
338     }
339     return(NULL);
340 }
341 
342 varsym_t
343 varsymfind(int mask, const char *name, int namelen)
344 {
345     struct proc *p = curproc;
346     struct varsyment *ve = NULL;
347     varsym_t sym;
348 
349     if ((mask & (VARSYM_PROC_MASK|VARSYM_USER_MASK)) && p != NULL) {
350 	if (mask & VARSYM_PROC_MASK)
351 	    ve = varsymlookup(&p->p_varsymset, name, namelen);
352 	if (ve == NULL && (mask & VARSYM_USER_MASK))
353 	    ve = varsymlookup(&p->p_ucred->cr_uidinfo->ui_varsymset, name, namelen);
354     }
355     if (ve == NULL && (mask & VARSYM_SYS_MASK)) {
356 	if (p != NULL && p->p_ucred->cr_prison)
357 	    ve = varsymlookup(&p->p_ucred->cr_prison->pr_varsymset, name, namelen);
358 	else
359 	    ve = varsymlookup(&varsymset_sys, name, namelen);
360     }
361     if (ve) {
362 	sym = ve->ve_sym;
363 	++sym->vs_refs;
364 	return(sym);
365     } else {
366 	return(NULL);
367     }
368 }
369 
370 int
371 varsymmake(int level, const char *name, const char *data)
372 {
373     struct varsymset *vss = NULL;
374     struct varsyment *ve;
375     struct proc *p = curproc;
376     varsym_t sym;
377     int namelen = strlen(name);
378     int datalen;
379     int error;
380 
381     switch(level) {
382     case VARSYM_PROC:
383 	if (p)
384 	    vss = &p->p_varsymset;
385 	break;
386     case VARSYM_USER:
387 	if (p)
388 	    vss = &p->p_ucred->cr_uidinfo->ui_varsymset;
389 	break;
390     case VARSYM_SYS:
391 	vss = &varsymset_sys;
392 	break;
393     case VARSYM_PRISON:
394 	if (p != NULL && p->p_ucred->cr_prison != NULL)
395 	    vss = &p->p_ucred->cr_prison->pr_varsymset;
396 	break;
397     }
398     if (vss == NULL) {
399 	error = EINVAL;
400     } else if (data && vss->vx_setsize >= MAXVARSYM_SET) {
401 	error = E2BIG;
402     } else if (data) {
403 	datalen = strlen(data);
404 	ve = malloc(sizeof(struct varsyment), M_VARSYM, M_WAITOK|M_ZERO);
405 	sym = malloc(sizeof(struct varsym) + namelen + datalen + 2, M_VARSYM, M_WAITOK);
406 	ve->ve_sym = sym;
407 	sym->vs_refs = 1;
408 	sym->vs_namelen = namelen;
409 	sym->vs_name = (char *)(sym + 1);
410 	sym->vs_data = sym->vs_name + namelen + 1;
411 	strcpy(sym->vs_name, name);
412 	strcpy(sym->vs_data, data);
413 	TAILQ_INSERT_TAIL(&vss->vx_queue, ve, ve_entry);
414 	vss->vx_setsize += sizeof(struct varsyment) + sizeof(struct varsym) + namelen + datalen + 8;
415 	error = 0;
416     } else {
417 	if ((ve = varsymlookup(vss, name, namelen)) != NULL) {
418 	    TAILQ_REMOVE(&vss->vx_queue, ve, ve_entry);
419 	    vss->vx_setsize -= sizeof(struct varsyment) + sizeof(struct varsym) + namelen + strlen(ve->ve_sym->vs_data) + 8;
420 	    varsymdrop(ve->ve_sym);
421 	    free(ve, M_VARSYM);
422 	    error = 0;
423 	} else {
424 	    error = ENOENT;
425 	}
426     }
427     return(error);
428 }
429 
430 void
431 varsymdrop(varsym_t sym)
432 {
433     KKASSERT(sym->vs_refs > 0);
434     if (--sym->vs_refs == 0) {
435 	free(sym, M_VARSYM);
436     }
437 }
438 
439 static void
440 varsymdup(struct varsymset *vss, struct varsyment *ve)
441 {
442     struct varsyment *nve;
443 
444     nve = malloc(sizeof(struct varsyment), M_VARSYM, M_WAITOK|M_ZERO);
445     nve->ve_sym = ve->ve_sym;
446     ++nve->ve_sym->vs_refs;
447     TAILQ_INSERT_TAIL(&vss->vx_queue, nve, ve_entry);
448 }
449 
450 void
451 varsymset_init(struct varsymset *vss, struct varsymset *copy)
452 {
453     struct varsyment *ve;
454 
455     TAILQ_INIT(&vss->vx_queue);
456     if (copy) {
457 	TAILQ_FOREACH(ve, &copy->vx_queue, ve_entry) {
458 	    varsymdup(vss, ve);
459 	}
460 	vss->vx_setsize = copy->vx_setsize;
461     }
462 }
463 
464 void
465 varsymset_clean(struct varsymset *vss)
466 {
467     struct varsyment *ve;
468 
469     while ((ve = TAILQ_FIRST(&vss->vx_queue)) != NULL) {
470 	TAILQ_REMOVE(&vss->vx_queue, ve, ve_entry);
471 	varsymdrop(ve->ve_sym);
472 	free(ve, M_VARSYM);
473     }
474     vss->vx_setsize = 0;
475 }
476 
477