xref: /dflybsd-src/sys/kern/kern_varsym.c (revision abf9e20ce1c5ad31f3a7807c4bdb39a8aa2a0aef)
1 /*
2  * Copyright (c) 2003 Matthew Dillon <dillon@backplane.com>
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  * $DragonFly: src/sys/kern/kern_varsym.c,v 1.2 2003/11/09 20:29:59 dillon Exp $
27  */
28 
29 /*
30  * This module implements variable storage and management for variant
31  * symlinks.  These variables may also be used for general purposes.
32  */
33 
34 #include <sys/param.h>
35 #include <sys/systm.h>
36 #include <sys/kernel.h>
37 #include <sys/ucred.h>
38 #include <sys/resourcevar.h>
39 #include <sys/proc.h>
40 #include <sys/queue.h>
41 #include <sys/sysctl.h>
42 #include <sys/malloc.h>
43 #include <sys/varsym.h>
44 #include <sys/sysproto.h>
45 
46 MALLOC_DEFINE(M_VARSYM, "varsym", "variable sets for variant symlinks");
47 
48 struct varsymset	varsymset_sys;
49 
50 /*
51  * Initialize the variant symlink subsystem
52  */
53 static void
54 varsym_sysinit(void *dummy)
55 {
56     varsymset_init(&varsymset_sys, NULL);
57 }
58 SYSINIT(announce, SI_SUB_INTRINSIC, SI_ORDER_FIRST, varsym_sysinit, NULL);
59 
60 /*
61  * varsymreplace() - called from namei
62  *
63  *	Do variant symlink variable substitution
64  */
65 int
66 varsymreplace(char *cp, int linklen, int maxlen)
67 {
68     int rlen;
69     int xlen;
70     int nlen;
71     int i;
72     varsym_t var;
73 
74     rlen = linklen;
75     while (linklen > 1) {
76 	if (cp[0] == '$' && cp[1] == '{') {
77 	    for (i = 2; i < linklen; ++i) {
78 		if (cp[i] == '}')
79 		    break;
80 	    }
81 	    if (i < linklen &&
82 		(var = varsymfind(VARSYM_ALL_MASK, cp + 2, i - 2)) != NULL
83 	    ) {
84 		xlen = i + 1;			/* bytes to strike */
85 		nlen = strlen(var->vs_data);	/* bytes to add */
86 		if (linklen + nlen - xlen >= maxlen) {
87 		    varsymdrop(var);
88 		    return(-1);
89 		}
90 		KKASSERT(linklen >= xlen);
91 		if (linklen != xlen)
92 		    bcopy(cp + xlen, cp + nlen, linklen - xlen);
93 		bcopy(var->vs_data, cp, nlen);
94 		linklen += nlen - xlen;	/* new relative length */
95 		rlen += nlen - xlen;	/* returned total length */
96 		cp += nlen;		/* adjust past replacement */
97 		linklen -= nlen;	/* adjust past replacement */
98 		maxlen -= nlen;		/* adjust past replacement */
99 	    } else {
100 		/*
101 		 * It's ok if i points to the '}', it will simply be
102 		 * skipped.  i could also have hit linklen.
103 		 */
104 		cp += i;
105 		linklen -= i;
106 		maxlen -= i;
107 	    }
108 	} else {
109 	    ++cp;
110 	    --linklen;
111 	    --maxlen;
112 	}
113     }
114     return(rlen);
115 }
116 
117 /*
118  * varsym_set() system call
119  *
120  * (int level, const char *name, const char *data)
121  */
122 int
123 varsym_set(struct varsym_set_args *uap)
124 {
125     char name[MAXVARSYM_NAME];
126     char *buf;
127     int error;
128 
129     if ((error = copyinstr(uap->name, name, sizeof(name), NULL)) != 0)
130 	goto done2;
131     buf = malloc(MAXVARSYM_DATA, M_TEMP, 0);
132     if (uap->data &&
133 	(error = copyinstr(uap->data, buf, MAXVARSYM_DATA, NULL)) != 0)
134     {
135 	goto done1;
136     }
137     switch(uap->level) {
138     case VARSYM_SYS:
139 	if ((error = suser(curthread)) != 0)
140 	    break;
141 	/* XXX implement per-jail sys */
142 	/* fall through */
143     case VARSYM_USER:
144 	/* XXX check jail / implement per-jail user */
145 	/* fall through */
146     case VARSYM_PROC:
147 	if (uap->data) {
148 	    (void)varsymmake(uap->level, name, NULL);
149 	    error = varsymmake(uap->level, name, buf);
150 	} else {
151 	    error = varsymmake(uap->level, name, NULL);
152 	}
153 	break;
154     }
155 done1:
156     free(buf, M_TEMP);
157 done2:
158     return(error);
159 }
160 
161 /*
162  * varsym_get() system call
163  *
164  * (int mask, const char *wild, char *buf, int bufsize)
165  */
166 int
167 varsym_get(struct varsym_get_args *uap)
168 {
169     char wild[MAXVARSYM_NAME];
170     varsym_t sym;
171     int error;
172     int dlen;
173 
174     if ((error = copyinstr(uap->wild, wild, sizeof(wild), NULL)) != 0)
175 	goto done;
176     sym = varsymfind(uap->mask, wild, strlen(wild));
177     if (sym == NULL) {
178 	error = ENOENT;
179 	goto done;
180     }
181     dlen = strlen(sym->vs_data);
182     if (dlen < uap->bufsize) {
183 	copyout(sym->vs_data, uap->buf, dlen + 1);
184     } else if (uap->bufsize) {
185 	copyout("", uap->buf, 1);
186     }
187     uap->sysmsg_result = dlen + 1;
188     varsymdrop(sym);
189 done:
190     return(error);
191 }
192 
193 /*
194  * Lookup a variant symlink.  XXX use a hash table.
195  */
196 static
197 struct varsyment *
198 varsymlookup(struct varsymset *vss, const char *name, int namelen)
199 {
200     struct varsyment *ve;
201 
202     TAILQ_FOREACH(ve, &vss->vx_queue, ve_entry) {
203 	varsym_t var = ve->ve_sym;
204 	if (var->vs_namelen == namelen &&
205 	    bcmp(name, var->vs_name, namelen) == 0
206 	) {
207 	    return(ve);
208 	}
209     }
210     return(NULL);
211 }
212 
213 varsym_t
214 varsymfind(int mask, const char *name, int namelen)
215 {
216     struct proc *p;
217     struct varsyment *ve = NULL;
218     varsym_t sym;
219 
220     if ((mask & (VARSYM_PROC_MASK|VARSYM_USER_MASK)) && (p = curproc) != NULL) {
221 	if (mask & VARSYM_PROC_MASK)
222 	    ve = varsymlookup(&p->p_varsymset, name, namelen);
223 	if (ve == NULL && (mask & VARSYM_USER_MASK))
224 	    ve = varsymlookup(&p->p_ucred->cr_uidinfo->ui_varsymset, name, namelen);
225     }
226     if (ve == NULL && (mask & VARSYM_SYS_MASK))
227 	ve = varsymlookup(&varsymset_sys, name, namelen);
228     if (ve) {
229 	sym = ve->ve_sym;
230 	++sym->vs_refs;
231 	return(sym);
232     } else {
233 	return(NULL);
234     }
235 }
236 
237 int
238 varsymmake(int level, const char *name, const char *data)
239 {
240     struct varsymset *vss = NULL;
241     struct varsyment *ve;
242     struct proc *p = curproc;
243     varsym_t sym;
244     int namelen = strlen(name);
245     int datalen;
246     int error;
247 
248     switch(level) {
249     case VARSYM_PROC:
250 	if (p)
251 	    vss = &p->p_varsymset;
252 	break;
253     case VARSYM_USER:
254 	if (p)
255 	    vss = &p->p_ucred->cr_uidinfo->ui_varsymset;
256 	break;
257     case VARSYM_SYS:
258 	vss = &varsymset_sys;
259 	break;
260     }
261     if (vss == NULL) {
262 	error = EINVAL;
263     } else if (data && vss->vx_setsize >= MAXVARSYM_SET) {
264 	error = E2BIG;
265     } else if (data) {
266 	datalen = strlen(data);
267 	ve = malloc(sizeof(struct varsyment), M_VARSYM, M_ZERO);
268 	sym = malloc(sizeof(struct varsym) + namelen + datalen + 2, M_VARSYM, 0);
269 	ve->ve_sym = sym;
270 	sym->vs_refs = 1;
271 	sym->vs_namelen = namelen;
272 	sym->vs_name = (char *)(sym + 1);
273 	sym->vs_data = sym->vs_name + namelen + 1;
274 	strcpy(sym->vs_name, name);
275 	strcpy(sym->vs_data, data);
276 	TAILQ_INSERT_TAIL(&vss->vx_queue, ve, ve_entry);
277 	vss->vx_setsize += sizeof(struct varsyment) + sizeof(struct varsym) + namelen + datalen + 8;
278 	error = 0;
279     } else {
280 	if ((ve = varsymlookup(vss, name, namelen)) != NULL) {
281 	    TAILQ_REMOVE(&vss->vx_queue, ve, ve_entry);
282 	    vss->vx_setsize -= sizeof(struct varsyment) + sizeof(struct varsym) + namelen + strlen(ve->ve_sym->vs_data) + 8;
283 	    varsymdrop(ve->ve_sym);
284 	    free(ve, M_VARSYM);
285 	    error = 0;
286 	} else {
287 	    error = ENOENT;
288 	}
289     }
290     return(error);
291 }
292 
293 void
294 varsymdrop(varsym_t sym)
295 {
296     KKASSERT(sym->vs_refs > 0);
297     if (--sym->vs_refs == 0) {
298 	free(sym, M_VARSYM);
299     }
300 }
301 
302 static void
303 varsymdup(struct varsymset *vss, struct varsyment *ve)
304 {
305     struct varsyment *nve;
306 
307     nve = malloc(sizeof(struct varsyment), M_VARSYM, M_ZERO);
308     nve->ve_sym = ve->ve_sym;
309     ++nve->ve_sym->vs_refs;
310     TAILQ_INSERT_TAIL(&vss->vx_queue, nve, ve_entry);
311 }
312 
313 void
314 varsymset_init(struct varsymset *vss, struct varsymset *copy)
315 {
316     struct varsyment *ve;
317 
318     TAILQ_INIT(&vss->vx_queue);
319     if (copy) {
320 	TAILQ_FOREACH(ve, &copy->vx_queue, ve_entry) {
321 	    varsymdup(vss, ve);
322 	}
323 	vss->vx_setsize = copy->vx_setsize;
324     }
325 }
326 
327 void
328 varsymset_clean(struct varsymset *vss)
329 {
330     struct varsyment *ve;
331 
332     while ((ve = TAILQ_FIRST(&vss->vx_queue)) != NULL) {
333 	TAILQ_REMOVE(&vss->vx_queue, ve, ve_entry);
334 	varsymdrop(ve->ve_sym);
335 	free(ve, M_VARSYM);
336     }
337     vss->vx_setsize = 0;
338 }
339 
340