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 (c) 1996, 2010, Oracle and/or its affiliates. All rights reserved.
23 *
24 */
25
26 #include <string.h>
27 #include <strings.h>
28 #include <stdlib.h>
29 #include <unistd.h>
30 #include <limits.h>
31 #include <bsm/devices.h>
32 #include <bsm/devalloc.h>
33
34 char *strtok_r(char *, const char *, char **);
35
36 /* externs from getdaent.c */
37 extern char *trim_white(char *);
38 extern int pack_white(char *);
39 extern char *getdadmfield(char *, char *);
40 extern int getdadmline(char *, int, FILE *);
41
42 static struct _dmapbuff {
43 FILE *_dmapf; /* for /etc/security/device_maps */
44 devmap_t _interpdevmap;
45 char _interpdmline[DA_BUFSIZE + 1];
46 char *_DEVMAP;
47 } *__dmapbuff;
48
49 #define dmapf (_dmap->_dmapf)
50 #define interpdevmap (_dmap->_interpdevmap)
51 #define interpdmline (_dmap->_interpdmline)
52 #define DEVMAPS_FILE (_dmap->_DEVMAP)
53
54 devmap_t *dmap_interpret(char *, devmap_t *);
55 static devmap_t *dmap_interpretf(char *, devmap_t *);
56 static devmap_t *dmap_dlexpand(devmap_t *);
57 static int dmap_resolve_link(char *devpath, char **devfs_path);
58
59 int dmap_matchdev(devmap_t *, char *);
60 int dmap_matchname(devmap_t *, char *);
61
62
63 /*
64 * _dmapalloc -
65 * allocates common buffers and structures.
66 * returns pointer to the new structure, else returns NULL on error.
67 */
68 static struct _dmapbuff *
_dmapalloc(void)69 _dmapalloc(void)
70 {
71 struct _dmapbuff *_dmap = __dmapbuff;
72
73 if (_dmap == NULL) {
74 _dmap = (struct _dmapbuff *)calloc((unsigned)1,
75 (unsigned)sizeof (*__dmapbuff));
76 if (_dmap == NULL)
77 return (NULL);
78 DEVMAPS_FILE = "/etc/security/device_maps";
79 dmapf = NULL;
80 __dmapbuff = _dmap;
81 }
82
83 return (_dmap);
84 }
85
86 /*
87 * setdmapent -
88 * rewinds the device_maps file to the beginning.
89 */
90 void
setdmapent(void)91 setdmapent(void)
92 {
93 struct _dmapbuff *_dmap = _dmapalloc();
94
95 if (_dmap == NULL)
96 return;
97 if (dmapf == NULL)
98 dmapf = fopen(DEVMAPS_FILE, "rF");
99 else
100 rewind(dmapf);
101 }
102
103 /*
104 * enddmapent -
105 * closes device_maps file.
106 */
107 void
enddmapent(void)108 enddmapent(void)
109 {
110 struct _dmapbuff *_dmap = _dmapalloc();
111
112 if (_dmap == NULL)
113 return;
114 if (dmapf != NULL) {
115 (void) fclose(dmapf);
116 dmapf = NULL;
117 }
118 }
119
120 void
freedmapent(devmap_t * dmap)121 freedmapent(devmap_t *dmap)
122 {
123 char **darp;
124
125 if ((darp = dmap->dmap_devarray) != NULL) {
126 while (*darp != NULL)
127 free(*darp++);
128 free(dmap->dmap_devarray);
129 dmap->dmap_devarray = NULL;
130 }
131 }
132
133 /*
134 * setdmapfile -
135 * changes the default device_maps file to the one specified.
136 * It does not close the previous file. If this is desired, enddmapent
137 * should be called prior to setdampfile.
138 */
139 void
setdmapfile(char * file)140 setdmapfile(char *file)
141 {
142 struct _dmapbuff *_dmap = _dmapalloc();
143
144 if (_dmap == NULL)
145 return;
146 if (dmapf != NULL) {
147 (void) fclose(dmapf);
148 dmapf = NULL;
149 }
150 DEVMAPS_FILE = file;
151 }
152
153 /*
154 * getdmapent -
155 * When first called, returns a pointer to the first devmap_t structure
156 * in device_maps; thereafter, it returns a pointer to the next devmap_t
157 * structure in the file. Thus successive calls can be used to read the
158 * entire file.
159 * call to getdmapent should be bracketed by setdmapent and enddmapent.
160 * returns pointer to devmap_t found, else returns NULL if no entry found
161 * or on error.
162 */
163 devmap_t *
getdmapent(void)164 getdmapent(void)
165 {
166 devmap_t *dmap;
167 struct _dmapbuff *_dmap = _dmapalloc();
168
169 if ((_dmap == 0) || (dmapf == NULL))
170 return (NULL);
171
172 while (getdadmline(interpdmline, (int)sizeof (interpdmline),
173 dmapf) != 0) {
174 if ((dmap = dmap_interpret(interpdmline,
175 &interpdevmap)) == NULL)
176 continue;
177 return (dmap);
178 }
179
180 return (NULL);
181 }
182
183 /*
184 * getdmapnam -
185 * searches from the beginning of device_maps for the device specified by
186 * its name.
187 * call to getdmapnam should be bracketed by setdmapent and enddmapent.
188 * returns pointer to devmapt_t for the device if it is found, else
189 * returns NULL if device not found or in case of error.
190 */
191 devmap_t *
getdmapnam(char * name)192 getdmapnam(char *name)
193 {
194 devmap_t *dmap;
195 struct _dmapbuff *_dmap = _dmapalloc();
196
197 if ((name == NULL) || (_dmap == 0) || (dmapf == NULL))
198 return (NULL);
199
200 while (getdadmline(interpdmline, (int)sizeof (interpdmline),
201 dmapf) != 0) {
202 if (strstr(interpdmline, name) == NULL)
203 continue;
204 if ((dmap = dmap_interpretf(interpdmline,
205 &interpdevmap)) == NULL)
206 continue;
207 if (dmap_matchname(dmap, name)) {
208 if ((dmap = dmap_dlexpand(dmap)) == NULL)
209 continue;
210 enddmapent();
211 return (dmap);
212 }
213 freedmapent(dmap);
214 }
215
216 return (NULL);
217 }
218
219 /*
220 * getdmapdev -
221 * searches from the beginning of device_maps for the device specified by
222 * its logical name.
223 * call to getdmapdev should be bracketed by setdmapent and enddmapent.
224 * returns pointer to the devmap_t for the device if device is found,
225 * else returns NULL if device not found or on error.
226 */
227 devmap_t *
getdmapdev(char * dev)228 getdmapdev(char *dev)
229 {
230 devmap_t *dmap;
231 struct _dmapbuff *_dmap = _dmapalloc();
232
233 if ((dev == NULL) || (_dmap == 0) || (dmapf == NULL))
234 return (NULL);
235
236 while (getdadmline(interpdmline, (int)sizeof (interpdmline),
237 dmapf) != 0) {
238 if ((dmap = dmap_interpret(interpdmline,
239 &interpdevmap)) == NULL)
240 continue;
241 if (dmap_matchdev(dmap, dev)) {
242 enddmapent();
243 return (dmap);
244 }
245 freedmapent(dmap);
246 }
247
248 return (NULL);
249 }
250
251 /*
252 * getdmaptype -
253 * searches from the beginning of device_maps for the device specified by
254 * its type.
255 * call to getdmaptype should be bracketed by setdmapent and enddmapent.
256 * returns pointer to devmap_t found, else returns NULL if no entry found
257 * or on error.
258 */
259 devmap_t *
getdmaptype(char * type)260 getdmaptype(char *type)
261 {
262 devmap_t *dmap;
263 struct _dmapbuff *_dmap = _dmapalloc();
264
265 if ((type == NULL) || (_dmap == 0) || (dmapf == NULL))
266 return (NULL);
267
268 while (getdadmline(interpdmline, (int)sizeof (interpdmline),
269 dmapf) != 0) {
270 if ((dmap = dmap_interpretf(interpdmline,
271 &interpdevmap)) == NULL)
272 continue;
273 if (dmap->dmap_devtype != NULL &&
274 strcmp(type, dmap->dmap_devtype) == 0) {
275 if ((dmap = dmap_dlexpand(dmap)) == NULL)
276 continue;
277 return (dmap);
278 }
279 freedmapent(dmap);
280 }
281
282 return (NULL);
283 }
284
285 /*
286 * dmap_match_one_dev -
287 * Checks if the specified devmap_t contains strings
288 * for the same link as the device specified.
289 *
290 * Returns 1 for a match, else returns 0.
291 */
292 static int
dmap_match_one_dev(devmap_t * dmap,char * dev)293 dmap_match_one_dev(devmap_t *dmap, char *dev)
294 {
295 char **dva;
296 char *dv;
297 char *dmap_link;
298 char *dev_link;
299 char stage_link[PATH_MAX + 1];
300
301 if (dmap->dmap_devarray == NULL)
302 return (0);
303
304 for (dva = dmap->dmap_devarray; (dv = *dva) != NULL; dva++) {
305 if (strstr(dev, dv) != NULL)
306 return (1);
307 }
308 /* check if both refer to same physical device */
309 (void) strncpy(stage_link, dmap->dmap_devarray[0], sizeof (stage_link));
310 if (dmap_resolve_link(stage_link, &dmap_link) != 0)
311 return (0);
312 (void) strncpy(stage_link, dev, sizeof (stage_link));
313 if (dmap_resolve_link(stage_link, &dev_link) != 0) {
314 free(dmap_link);
315 return (0);
316 }
317 if (strcmp(dev_link, dmap_link) == 0) {
318 free(dmap_link);
319 free(dev_link);
320 return (1);
321 }
322 free(dmap_link);
323 free(dev_link);
324 return (0);
325 }
326
327 /*
328 * dmap_matchdev -
329 * checks if the specified devmap_t is for the device specified.
330 * returns 1 if it is, else returns 0.
331 */
332 int
dmap_matchdev(devmap_t * dmap,char * dev)333 dmap_matchdev(devmap_t *dmap, char *dev)
334 {
335 char **dva;
336 char *dv;
337
338 if (dmap->dmap_devarray == NULL)
339 return (0);
340 for (dva = dmap->dmap_devarray; (dv = *dva) != NULL; dva ++) {
341 if (strcmp(dv, dev) == 0)
342 return (1);
343 }
344
345 return (0);
346 }
347
348 /*
349 * Requires a match of the /dev/? links, not just the logical devname
350 * Returns 1 for match found, 0 for match not found, 2 for invalid arguments.
351 *
352 * Also looks for an instance number at the end of the logical name, and
353 * puts instance or -1 into *num.
354 */
355 int
dmap_exact_dev(devmap_t * dmap,char * dev,int * num)356 dmap_exact_dev(devmap_t *dmap, char *dev, int *num)
357 {
358 char *dv;
359
360 if ((dev == NULL) || (dmap->dmap_devname == NULL))
361 return (2);
362 dv = dmap->dmap_devname;
363 dv += strcspn(dmap->dmap_devname, "0123456789");
364 if (sscanf(dv, "%d", num) != 1)
365 *num = -1;
366 /* during some add processes, dev can be shorter than dmap */
367 return (dmap_match_one_dev(dmap, dev));
368 }
369
370 /*
371 * dmap_matchtype -
372 * checks if the specified devmap_t is for the device specified.
373 * returns 1 if it is, else returns 0.
374 */
375 int
dmap_matchtype(devmap_t * dmap,char * type)376 dmap_matchtype(devmap_t *dmap, char *type)
377 {
378 if ((dmap->dmap_devtype == NULL) || (type == NULL))
379 return (0);
380
381 return ((strcmp(dmap->dmap_devtype, type) == 0));
382 }
383
384 /*
385 * dmap_matchname -
386 * checks if the specified devmap_t is for the device specified.
387 * returns 1 if it is, else returns 0.
388 */
389 int
dmap_matchname(devmap_t * dmap,char * name)390 dmap_matchname(devmap_t *dmap, char *name)
391 {
392 if (dmap->dmap_devname == NULL)
393 return (0);
394
395 return ((strcmp(dmap->dmap_devname, name) == 0));
396 }
397
398 /*
399 * Temporarily duplicated directly from libdevinfo's is_minor_node(),
400 * and renamed, because we cannot link this from libdevinfo.
401 *
402 * To be resolved in a couple of builds.
403 *
404 * The real fix is to move device allocation out of libbsm.
405 *
406 * returns 1 if contents is a minor node in /devices.
407 * If mn_root is not NULL, mn_root is set to:
408 * if contents is a /dev node, mn_root = contents
409 * OR
410 * if contents is a /devices node, mn_root set to the '/'
411 * following /devices.
412 */
413 static int
dmap_minor_root(const char * contents,const char ** mn_root)414 dmap_minor_root(const char *contents, const char **mn_root)
415 {
416 char *ptr, *prefix;
417
418 prefix = "../devices/";
419
420 if ((ptr = strstr(contents, prefix)) != NULL) {
421
422 /* mn_root should point to the / following /devices */
423 if (mn_root != NULL) {
424 *mn_root = ptr += strlen(prefix) - 1;
425 }
426 return (1);
427 }
428
429 prefix = "/devices/";
430
431 if (strncmp(contents, prefix, strlen(prefix)) == 0) {
432
433 /* mn_root should point to the / following /devices/ */
434 if (mn_root != NULL) {
435 *mn_root = contents + strlen(prefix) - 1;
436 }
437 return (1);
438 }
439
440 if (mn_root != NULL) {
441 *mn_root = contents;
442 }
443 return (0);
444 }
445
446 /*
447 * Temporarily duplicated directly from libdevinfo's devfs_resolve_link(),
448 * and renamed, because we cannot link this from libdevinfo now, and will
449 * create a sensible solution in the near future.
450 *
451 * returns 0 if resolved, -1 otherwise.
452 * devpath: Absolute path to /dev link
453 * devfs_path: Returns malloced string: /devices path w/out "/devices"
454 */
455 static int
dmap_resolve_link(char * devpath,char ** devfs_path)456 dmap_resolve_link(char *devpath, char **devfs_path)
457 {
458 char contents[PATH_MAX + 1];
459 char stage_link[PATH_MAX + 1];
460 char *ptr;
461 int linksize;
462 char *slashdev = "/dev/";
463
464 if (devfs_path) {
465 *devfs_path = NULL;
466 }
467
468 linksize = readlink(devpath, contents, PATH_MAX);
469
470 if (linksize <= 0) {
471 return (-1);
472 } else {
473 contents[linksize] = '\0';
474 }
475
476 /*
477 * if the link contents is not a minor node assume
478 * that link contents is really a pointer to another
479 * link, and if so recurse and read its link contents.
480 */
481 if (dmap_minor_root((const char *)contents, (const char **)&ptr) !=
482 1) {
483 if (strncmp(contents, slashdev, strlen(slashdev)) == 0) {
484 /* absolute path, starting with /dev */
485 (void) strcpy(stage_link, contents);
486 } else {
487 /* relative path, prefix devpath */
488 if ((ptr = strrchr(devpath, '/')) == NULL) {
489 /* invalid link */
490 return (-1);
491 }
492 *ptr = '\0';
493 (void) strcpy(stage_link, devpath);
494 *ptr = '/';
495 (void) strcat(stage_link, "/");
496 (void) strcat(stage_link, contents);
497
498 }
499 return (dmap_resolve_link(stage_link, devfs_path));
500 }
501
502 if (devfs_path) {
503 *devfs_path = strdup(ptr);
504 if (*devfs_path == NULL) {
505 return (-1);
506 }
507 }
508
509 return (0);
510 }
511
512 /*
513 * dmap_physname: path to /devices device
514 * Returns:
515 * strdup'd (i.e. malloc'd) real device file if successful
516 * NULL on error
517 */
518 char *
dmap_physname(devmap_t * dmap)519 dmap_physname(devmap_t *dmap)
520 {
521 char *oldlink;
522 char stage_link[PATH_MAX + 1];
523
524 if ((dmap == NULL) || (dmap->dmap_devarray == NULL) ||
525 (dmap->dmap_devarray[0] == NULL))
526 return (NULL);
527
528 (void) strncpy(stage_link, dmap->dmap_devarray[0], sizeof (stage_link));
529
530 if (dmap_resolve_link(stage_link, &oldlink) == 0)
531 return (oldlink);
532 return (NULL);
533 }
534
535 /*
536 * dm_match -
537 * calls dmap_matchname or dmap_matchtype as appropriate.
538 */
539 int
dm_match(devmap_t * dmap,da_args * dargs)540 dm_match(devmap_t *dmap, da_args *dargs)
541 {
542 if (dargs->devinfo->devname)
543 return (dmap_matchname(dmap, dargs->devinfo->devname));
544 else if (dargs->devinfo->devtype)
545 return (dmap_matchtype(dmap, dargs->devinfo->devtype));
546
547 return (0);
548 }
549
550 /*
551 * dmap_interpret -
552 * calls dmap_interpretf and dmap_dlexpand to parse devmap_t line.
553 * returns pointer to parsed devmapt_t entry, else returns NULL on error.
554 */
555 devmap_t *
dmap_interpret(char * val,devmap_t * dm)556 dmap_interpret(char *val, devmap_t *dm)
557 {
558 if (dmap_interpretf(val, dm) == NULL)
559 return (NULL);
560
561 return (dmap_dlexpand(dm));
562 }
563
564 /*
565 * dmap_interpretf -
566 * parses string "val" and initializes pointers in the given devmap_t to
567 * fields in "val".
568 * returns pointer to updated devmap_t.
569 */
570 static devmap_t *
dmap_interpretf(char * val,devmap_t * dm)571 dmap_interpretf(char *val, devmap_t *dm)
572 {
573 dm->dmap_devname = getdadmfield(val, KV_TOKEN_DELIMIT);
574 dm->dmap_devtype = getdadmfield(NULL, KV_TOKEN_DELIMIT);
575 dm->dmap_devlist = getdadmfield(NULL, KV_TOKEN_DELIMIT);
576 dm->dmap_devarray = NULL;
577 if (dm->dmap_devname == NULL ||
578 dm->dmap_devtype == NULL ||
579 dm->dmap_devlist == NULL)
580 return (NULL);
581
582 return (dm);
583 }
584
585 /*
586 * dmap_dlexpand -
587 * expands dmap_devlist of the form `devlist_generate`
588 * returns unexpanded form if there is no '\`' or in case of error.
589 */
590 static devmap_t *
dmap_dlexpand(devmap_t * dmp)591 dmap_dlexpand(devmap_t *dmp)
592 {
593 char tmplist[DA_BUFSIZE + 1];
594 char *cp, *cpl, **darp;
595 int count;
596 FILE *expansion;
597
598 dmp->dmap_devarray = NULL;
599 if (dmp->dmap_devlist == NULL)
600 return (NULL);
601 if (*(dmp->dmap_devlist) != '`') {
602 (void) strcpy(tmplist, dmp->dmap_devlist);
603 } else {
604 (void) strcpy(tmplist, dmp->dmap_devlist + 1);
605 if ((cp = strchr(tmplist, '`')) != NULL)
606 *cp = '\0';
607 if ((expansion = popen(tmplist, "rF")) == NULL)
608 return (NULL);
609 count = fread(tmplist, 1, sizeof (tmplist) - 1, expansion);
610 (void) pclose(expansion);
611 tmplist[count] = '\0';
612 }
613
614 /* cleanup the list */
615 count = pack_white(tmplist);
616 dmp->dmap_devarray = darp =
617 (char **)malloc((count + 2) * sizeof (char *));
618 if (darp == NULL)
619 return (NULL);
620 cp = tmplist;
621 while ((cp = strtok_r(cp, " ", &cpl)) != NULL) {
622 *darp = strdup(cp);
623 if (*darp == NULL) {
624 freedmapent(dmp);
625 return (NULL);
626 }
627 darp++;
628 cp = NULL;
629 }
630 *darp = NULL;
631
632 return (dmp);
633 }
634
635 /*
636 * dmapskip -
637 * scans input string to find next colon or end of line.
638 * returns pointer to next char.
639 */
640 static char *
dmapskip(char * p)641 dmapskip(char *p)
642 {
643 while (*p && *p != ':' && *p != '\n')
644 ++p;
645 if (*p == '\n')
646 *p = '\0';
647 else if (*p != '\0')
648 *p++ = '\0';
649
650 return (p);
651 }
652
653 /*
654 * dmapdskip -
655 * scans input string to find next space or end of line.
656 * returns pointer to next char.
657 */
658 static char *
dmapdskip(p)659 dmapdskip(p)
660 register char *p;
661 {
662 while (*p && *p != ' ' && *p != '\n')
663 ++p;
664 if (*p != '\0')
665 *p++ = '\0';
666
667 return (p);
668 }
669
670 char *
getdmapfield(char * ptr)671 getdmapfield(char *ptr)
672 {
673 static char *tptr;
674
675 if (ptr == NULL)
676 ptr = tptr;
677 if (ptr == NULL)
678 return (NULL);
679 tptr = dmapskip(ptr);
680 ptr = trim_white(ptr);
681 if (ptr == NULL)
682 return (NULL);
683 if (*ptr == NULL)
684 return (NULL);
685
686 return (ptr);
687 }
688
689 char *
getdmapdfield(char * ptr)690 getdmapdfield(char *ptr)
691 {
692 static char *tptr;
693 if (ptr != NULL) {
694 ptr = trim_white(ptr);
695 } else {
696 ptr = tptr;
697 }
698 if (ptr == NULL)
699 return (NULL);
700 tptr = dmapdskip(ptr);
701 if (ptr == NULL)
702 return (NULL);
703 if (*ptr == NULL)
704 return (NULL);
705
706 return (ptr);
707 }
708