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