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