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