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
34char *strtok_r(char *, const char *, char **);
35
36/* externs from getdaent.c */
37extern char *trim_white(char *);
38extern int pack_white(char *);
39extern char *getdadmfield(char *, char *);
40extern int getdadmline(char *, int, FILE *);
41
42static 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
54devmap_t	*dmap_interpret(char *, devmap_t *);
55static devmap_t	*dmap_interpretf(char *, devmap_t *);
56static devmap_t *dmap_dlexpand(devmap_t *);
57static int dmap_resolve_link(char *devpath, char **devfs_path);
58
59int	dmap_matchdev(devmap_t *, char *);
60int	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 */
68static 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 */
90void
91setdmapent(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 */
107void
108enddmapent(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
120void
121freedmapent(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 */
139void
140setdmapfile(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 */
163devmap_t *
164getdmapent(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 */
191devmap_t *
192getdmapnam(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 */
227devmap_t *
228getdmapdev(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 */
259devmap_t *
260getdmaptype(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 */
292static int
293dmap_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 */
332int
333dmap_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 */
355int
356dmap_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 */
375int
376dmap_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 */
389int
390dmap_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 */
413static int
414dmap_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 */
455static int
456dmap_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 */
518char *
519dmap_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 */
539int
540dm_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 */
555devmap_t  *
556dmap_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 */
570static devmap_t  *
571dmap_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 */
590static devmap_t *
591dmap_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 */
640static char *
641dmapskip(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 */
658static char *
659dmapdskip(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
670char *
671getdmapfield(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
689char *
690getdmapdfield(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