xref: /illumos-gate/usr/src/lib/libbsm/common/getdment.c (revision d8109ce4330e1b8ad6c29f9fccacec969066bb9d)
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 *
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
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
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
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
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 *
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 *
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 *
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 *
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
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
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
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
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
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
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
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 *
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
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  *
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  *
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 *
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 *
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 *
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 *
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 == '\0')
684 		return (NULL);
685 
686 	return (ptr);
687 }
688 
689 char *
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 == '\0')
704 		return (NULL);
705 
706 	return (ptr);
707 }
708