1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 /*
22  * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 #include <ctype.h>
27 #include <string.h>
28 #include <stdlib.h>
29 #include <tsol/label.h>
30 #include <bsm/devices.h>
31 #include <bsm/devalloc.h>
32 
33 extern char *_strdup_null(char *);
34 
35 static struct _dabuff {
36 	FILE		*_daf;	/* pointer into /etc/security/device_allocate */
37 	devalloc_t	_interpdevalloc;
38 	char		_interpdaline[DA_BUFSIZE + 1];
39 	char		 *_DEVALLOC;
40 } *__dabuff;
41 
42 #define	daf	(_da->_daf)
43 #define	interpdevalloc	(_da->_interpdevalloc)
44 #define	interpdaline	(_da->_interpdaline)
45 #define	DEVALLOC_FILE	(_da->_DEVALLOC)
46 static devalloc_t	*da_interpret(char *);
47 
48 int da_matchname(devalloc_t *, char *);
49 int da_matchtype(devalloc_t *, char *);
50 
51 static int system_labeled = 0;
52 
53 /*
54  * trim_white -
55  *	trims off leading and trailing white space from input string.
56  * 	The leading white space is skipped by moving the pointer forward.
57  * 	The trailing white space is removed by nulling the white space
58  *	characters.
59  *	returns pointer to non-white string, else returns NULL if input string
60  *	is null or if the resulting string has zero length.
61  */
62 char *
trim_white(char * ptr)63 trim_white(char *ptr)
64 {
65 	char	*tptr;
66 
67 	if (ptr == NULL)
68 		return (NULL);
69 	while (isspace(*ptr))
70 		ptr++;
71 	tptr = ptr + strlen(ptr);
72 	while (tptr != ptr && isspace(tptr[-1]))
73 		--tptr;
74 	*tptr = '\0';
75 	if (*ptr == '\0')
76 		return (NULL);
77 
78 	return (ptr);
79 }
80 
81 /*
82  * pack_white -
83  *	trims off multiple occurrences of white space from input string.
84  * 	returns the number of spaces retained
85  */
86 int
pack_white(char * ptr)87 pack_white(char *ptr)
88 {
89 	int	cnt = 0;
90 	char	*tptr, ch;
91 
92 	if (ptr == NULL)
93 		return (0);
94 	tptr = ptr;
95 	while (isspace(*tptr))
96 		tptr++;
97 	for (;;) {
98 		while ((ch = *tptr) != '\0' && !isspace(ch)) {
99 			*ptr++ = ch;
100 			tptr++;
101 		}
102 		while (isspace(*tptr))
103 			tptr++;
104 		if (*tptr == '\0')
105 			break;
106 		*ptr++ = ' ';
107 		cnt++;
108 	}
109 	*ptr = '\0';
110 
111 	return (cnt);
112 }
113 
114 /*
115  * getdadmline -
116  *	reads one device_alloc/device_maps line from stream into buff of len
117  *	bytes. Continued lines from stream are concatenated into one line in
118  *	buff. Comments are removed from buff.
119  *	returns the number of characters in buff, else returns 0 if no
120  * 	characters are read or an error occurred.
121  */
122 int
getdadmline(char * buff,int len,FILE * stream)123 getdadmline(char *buff, int len, FILE *stream)
124 {
125 	int 	tmpcnt;
126 	int 	charcnt = 0;
127 	int 	fileerr = 0;
128 	int 	contline = 0;
129 	char 	*cp;
130 	char 	*ccp;
131 
132 	do {
133 		cp = buff;
134 		*cp = '\0';
135 		do {
136 			contline = 0;
137 			if (fgets(cp, len - charcnt, stream) == NULL) {
138 				fileerr = 1;
139 				break;
140 			}
141 			ccp = strchr(cp, '\n');
142 			if (ccp != NULL) {
143 				if (ccp != cp && ccp[-1] == '\\') {
144 					ccp--;
145 					contline = 1;
146 				}
147 				else
148 					contline = 0;
149 				*ccp = '\0';
150 			}
151 			tmpcnt = strlen(cp);
152 			cp += tmpcnt;
153 			charcnt += tmpcnt;
154 		} while ((contline) || (charcnt == 0));
155 		ccp = strpbrk(buff, "#");
156 		if (ccp != NULL)
157 			*ccp = '\0';
158 		charcnt = strlen(buff);
159 	} while ((fileerr == 0) && (charcnt == 0));
160 
161 	if (fileerr && !charcnt)
162 		return (0);
163 	else
164 		return (charcnt);
165 }
166 
167 /*
168  * _daalloc -
169  *	allocates common buffers and structures.
170  * 	returns pointer to the new structure, else returns NULL on error.
171  */
172 static struct _dabuff *
_daalloc(void)173 _daalloc(void)
174 {
175 	struct _dabuff	*_da = __dabuff;
176 
177 	if (_da == NULL) {
178 		_da = (struct _dabuff *)calloc((unsigned)1,
179 		    (unsigned)sizeof (*__dabuff));
180 		if (_da == NULL)
181 			return (NULL);
182 		DEVALLOC_FILE = "/etc/security/device_allocate";
183 		daf = NULL;
184 		__dabuff = _da;
185 		system_labeled = is_system_labeled();
186 	}
187 
188 	return (__dabuff);
189 }
190 
191 /*
192  * getdadmfield -
193  *	gets individual fields separated by skip in ptr.
194  */
195 char *
getdadmfield(char * ptr,char * skip)196 getdadmfield(char *ptr, char *skip)
197 {
198 	static char	*tptr = NULL;
199 	char		*pend;
200 
201 	/* check for a continuing search */
202 	if (ptr == NULL)
203 		ptr = tptr;
204 	/* check for source end */
205 	if (ptr == NULL || *ptr == '\0')
206 		return (NULL);
207 	/* find terminator */
208 	pend = strpbrk(ptr, skip);
209 	/* terminate and set continuation pointer */
210 	if (pend != NULL) {
211 		*pend++ = '\0';
212 		tptr = pend;
213 	} else
214 		tptr = NULL;
215 	/*
216 	 * trim off any surrounding white space, return what's left
217 	 */
218 
219 	return (trim_white(ptr));
220 }
221 
222 /*
223  * setdaent -
224  *	rewinds the device_allocate file to the begining.
225  */
226 
227 void
setdaent(void)228 setdaent(void)
229 {
230 	struct _dabuff	*_da = _daalloc();
231 
232 	if (_da == NULL)
233 		return;
234 	if (daf == NULL)
235 		daf = fopen(DEVALLOC_FILE, "rF");
236 	else
237 		rewind(daf);
238 }
239 
240 /*
241  * enddaent -
242  *	closes device_allocate file.
243  */
244 
245 void
enddaent(void)246 enddaent(void)
247 {
248 	struct _dabuff	*_da = _daalloc();
249 
250 	if (_da == NULL)
251 		return;
252 	if (daf != NULL) {
253 		(void) fclose(daf);
254 		daf = NULL;
255 	}
256 }
257 
258 /*
259  * setdafile -
260  *	changes the default device_allocate file to the one specified.
261  * 	It does not close the previous file. If this is desired, enddaent
262  *	should be called prior to setdafile.
263  */
264 void
setdafile(char * file)265 setdafile(char *file)
266 {
267 	struct _dabuff	*_da = _daalloc();
268 
269 	if (_da == NULL)
270 		return;
271 	if (daf != NULL) {
272 		(void) fclose(daf);
273 		daf = NULL;
274 	}
275 	DEVALLOC_FILE = file;
276 }
277 
278 void
freedaent(devalloc_t * dap)279 freedaent(devalloc_t *dap)
280 {
281 	if (dap == NULL)
282 		return;
283 	_kva_free(dap->da_devopts);
284 	dap->da_devopts = NULL;
285 }
286 
287 /*
288  * getdaon -
289  *	checks if device_allocate has string DEVICE_ALLOCATION=ON or
290  *	DEVICE_ALLOCATION=OFF string in it.
291  *	returns 1 if the string is DEVICE_ALLOCATION=ON, 0 if it is
292  *	DEVICE_ALLOCATION=OFF, -1 if neither string present.
293  */
294 int
getdaon()295 getdaon()
296 {
297 	int		is_on = -1;
298 	char		line1[DA_BUFSIZE + 1];
299 	struct _dabuff *_da = _daalloc();
300 
301 	setdaent();
302 	if ((_da == NULL) || (daf == NULL)) {
303 		enddaent();
304 		return (is_on);
305 	}
306 	while (getdadmline(line1, (int)sizeof (line1), daf) != 0) {
307 		if (strncmp(line1, DA_ON_STR, (strlen(DA_ON_STR) - 1)) == 0) {
308 			is_on = 1;
309 			break;
310 		} else if (strncmp(line1, DA_OFF_STR,
311 		    (strlen(DA_OFF_STR) - 1)) == 0) {
312 			is_on = 0;
313 			break;
314 		}
315 	}
316 	enddaent();
317 
318 	return (is_on);
319 }
320 
321 /*
322  * getdaent -
323  *	When first called, returns a pointer to the first devalloc_t
324  * 	structure in device_allocate; thereafter, it returns a pointer to the
325  *	next devalloc_t structure in the file. Thus, successive calls can be
326  *	used to search the entire file.
327  *	call to getdaent should be bracketed by setdaent and enddaent.
328  *	returns NULL on error.
329  */
330 devalloc_t *
getdaent(void)331 getdaent(void)
332 {
333 	char		line1[DA_BUFSIZE + 1];
334 	devalloc_t	*da;
335 	struct _dabuff	*_da = _daalloc();
336 
337 	if ((_da == 0) || (daf == NULL))
338 		return (NULL);
339 
340 	while (getdadmline(line1, (int)sizeof (line1), daf) != 0) {
341 		if ((strncmp(line1, DA_ON_STR, (strlen(DA_ON_STR) - 1)) == 0) ||
342 		    (strncmp(line1, DA_OFF_STR, (strlen(DA_OFF_STR) - 1)) == 0))
343 			continue;
344 		if ((da = da_interpret(line1)) == NULL)
345 			continue;
346 		return (da);
347 	}
348 
349 	return (NULL);
350 }
351 
352 /*
353  * getdanam
354  * 	searches from the beginning of device_allocate for the device specified
355  * 	by its name.
356  *	call to getdanam should be bracketed by setdaent and enddaent.
357  * 	returns pointer to devalloc_t for the device if it is found, else
358  *	returns NULL if device not found or in case of error.
359  */
360 devalloc_t *
getdanam(char * name)361 getdanam(char *name)
362 {
363 	char		line[DA_BUFSIZE + 1];
364 	devalloc_t	*da;
365 	struct _dabuff	*_da = _daalloc();
366 
367 	if ((name == NULL) || (_da == 0) || (daf == NULL))
368 		return (NULL);
369 
370 	while (getdadmline(line, (int)sizeof (line), daf) != 0) {
371 		if (strstr(line, name) == NULL)
372 			continue;
373 		if ((da = da_interpret(line)) == NULL)
374 			continue;
375 		if (da_matchname(da, name)) {
376 			enddaent();
377 			return (da);
378 		}
379 		freedaent(da);
380 	}
381 
382 	return (NULL);
383 }
384 
385 /*
386  * getdatype -
387  * 	searches from the beginning of device_allocate for the device specified
388  * 	by its type.
389  *	call to getdatype should be bracketed by setdaent and enddaent.
390  * 	returns pointer to devalloc_t for the device if it is found, else
391  *	returns NULL if device not found or in case of error.
392  */
393 devalloc_t *
getdatype(char * type)394 getdatype(char *type)
395 {
396 	char		line1[DA_BUFSIZE + 1];
397 	devalloc_t	*da;
398 	struct _dabuff	*_da = _daalloc();
399 
400 	if ((type == NULL) || (_da == NULL) || (daf == NULL))
401 		return (NULL);
402 
403 	while (getdadmline(line1, (int)sizeof (line1), daf) != 0) {
404 		if (strstr(line1, type) == NULL)
405 			continue;
406 		if ((da = da_interpret(line1)) == NULL)
407 			continue;
408 		if (da_matchtype(da, type))
409 			return (da);
410 		freedaent(da);
411 	}
412 
413 	return (NULL);
414 }
415 
416 /*
417  * da_matchname -
418  *	checks if the specified devalloc_t is for the device specified.
419  * 	returns 1 if it is, else returns 0.
420  */
421 int
da_matchname(devalloc_t * dap,char * name)422 da_matchname(devalloc_t *dap, char *name)
423 {
424 	if (dap->da_devname == NULL)
425 		return (0);
426 
427 	return ((strcmp(dap->da_devname, name) == 0));
428 }
429 
430 /*
431  * da_matchtype -
432  *	checks if the specified devalloc_t is for the device type specified.
433  *	returns 1 if match found, else, returns 0.
434  */
435 int
da_matchtype(devalloc_t * da,char * type)436 da_matchtype(devalloc_t *da, char *type)
437 {
438 	if (da->da_devtype == NULL)
439 		return (0);
440 
441 	return ((strcmp(da->da_devtype, type) == 0));
442 }
443 
444 /*
445  * da_match -
446  * 	calls da_matchname or da_matchdev as appropriate.
447  */
448 int
da_match(devalloc_t * dap,da_args * dargs)449 da_match(devalloc_t *dap, da_args *dargs)
450 {
451 	if (dargs->devinfo->devname)
452 		return (da_matchname(dap, dargs->devinfo->devname));
453 	else if (dargs->devinfo->devtype)
454 		return (da_matchtype(dap, dargs->devinfo->devtype));
455 
456 	return (0);
457 }
458 
459 /*
460  * da_interpret -
461  *	parses val and initializes pointers in devalloc_t.
462  * 	returns pointer to parsed devalloc_t entry, else returns NULL on error.
463  */
464 static devalloc_t  *
da_interpret(char * val)465 da_interpret(char *val)
466 {
467 	struct _dabuff	*_da = _daalloc();
468 	char	*opts;
469 	int	i;
470 	kva_t	*kvap;
471 	kv_t	*kvp;
472 
473 	if (_da == NULL)
474 		return (NULL);
475 
476 	(void) strcpy(interpdaline, val);
477 	interpdevalloc.da_devname = getdadmfield(interpdaline, KV_DELIMITER);
478 	interpdevalloc.da_devtype = getdadmfield(NULL, KV_DELIMITER);
479 	opts = getdadmfield(NULL, KV_DELIMITER);
480 	(void) getdadmfield(NULL, KV_DELIMITER);	/* reserved field */
481 	interpdevalloc.da_devauth = getdadmfield(NULL, KV_DELIMITER);
482 	interpdevalloc.da_devexec = getdadmfield(NULL, KV_DELIMITER);
483 	interpdevalloc.da_devopts = NULL;
484 	if (interpdevalloc.da_devname == NULL ||
485 	    interpdevalloc.da_devtype == NULL)
486 		return (NULL);
487 	if ((opts != NULL) &&
488 	    (strncmp(opts, DA_RESERVED, strlen(DA_RESERVED)) != 0)) {
489 		interpdevalloc.da_devopts =
490 		    _str2kva(opts, KV_ASSIGN, KV_TOKEN_DELIMIT);
491 	}
492 	/* remove any extraneous whitespace in the options */
493 	if ((kvap = interpdevalloc.da_devopts) != NULL) {
494 		for (i = 0, kvp = kvap->data; i < kvap->length; i++, kvp++) {
495 			(void) pack_white(kvp->key);
496 			(void) pack_white(kvp->value);
497 		}
498 	}
499 
500 	if (system_labeled) {
501 		/* if label range is not defined, use the default range. */
502 		int		i = 0, nlen = 0;
503 		char		*minstr = NULL, *maxstr = NULL;
504 		kva_t		*nkvap = NULL;
505 		kv_t		*ndata = NULL, *odata = NULL;
506 
507 		if (kvap == NULL) {
508 			nlen = 2;	/* minlabel, maxlabel */
509 		} else {
510 			nlen += kvap->length;
511 			if ((minstr = kva_match(kvap, DAOPT_MINLABEL)) == NULL)
512 				nlen++;
513 			if ((maxstr = kva_match(kvap, DAOPT_MAXLABEL)) == NULL)
514 				nlen++;
515 		}
516 		if ((minstr != NULL) && (maxstr != NULL))
517 			/*
518 			 * label range provided; we don't need to construct
519 			 * default range.
520 			 */
521 			goto out;
522 		nkvap = _new_kva(nlen);
523 		ndata = nkvap->data;
524 		if (kvap != NULL) {
525 			for (i = 0; i < kvap->length; i++) {
526 				odata = kvap->data;
527 				ndata[i].key = _strdup_null(odata[i].key);
528 				ndata[i].value = _strdup_null(odata[i].value);
529 				nkvap->length++;
530 			}
531 		}
532 		if (minstr == NULL) {
533 			ndata[i].key = strdup(DAOPT_MINLABEL);
534 			ndata[i].value = strdup(DA_DEFAULT_MIN);
535 			nkvap->length++;
536 			i++;
537 		}
538 		if (maxstr == NULL) {
539 			ndata[i].key = strdup(DAOPT_MAXLABEL);
540 			ndata[i].value = strdup(DA_DEFAULT_MAX);
541 			nkvap->length++;
542 		}
543 		interpdevalloc.da_devopts = nkvap;
544 	}
545 
546 out:
547 	return (&interpdevalloc);
548 }
549