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 *
trim_white(char * ptr)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
pack_white(char * ptr)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
getdadmline(char * buff,int len,FILE * stream)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 *
_daalloc(void)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 *
getdadmfield(char * ptr,char * skip)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
setdaent(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
enddaent(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
setdafile(char * file)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
freedaent(devalloc_t * dap)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
getdaon()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 *
getdaent(void)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 *
getdanam(char * name)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 *
getdatype(char * type)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
da_matchname(devalloc_t * dap,char * name)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
da_matchtype(devalloc_t * da,char * type)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
da_match(devalloc_t * dap,da_args * dargs)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 *
da_interpret(char * val)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