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
33extern char *_strdup_null(char *);
34
35static 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)
46static devalloc_t	*da_interpret(char *);
47
48int da_matchname(devalloc_t *, char *);
49int da_matchtype(devalloc_t *, char *);
50
51static 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 */
62char *
63trim_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 */
86int
87pack_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 */
122int
123getdadmline(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 */
172static struct _dabuff *
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 */
195char *
196getdadmfield(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
227void
228setdaent(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
245void
246enddaent(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 */
264void
265setdafile(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
278void
279freedaent(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 */
294int
295getdaon()
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 */
330devalloc_t *
331getdaent(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 */
360devalloc_t *
361getdanam(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 */
393devalloc_t *
394getdatype(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 */
421int
422da_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 */
435int
436da_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 */
448int
449da_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 */
464static devalloc_t  *
465da_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
546out:
547	return (&interpdevalloc);
548}
549