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