xref: /onnv-gate/usr/src/lib/libdevinfo/devinfo_devperm.c (revision 12195:cf3a8ea2dcfd)
10Sstevel@tonic-gate /*
20Sstevel@tonic-gate  * CDDL HEADER START
30Sstevel@tonic-gate  *
40Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
52621Sllai1  * Common Development and Distribution License (the "License").
62621Sllai1  * You may not use this file except in compliance with the License.
70Sstevel@tonic-gate  *
80Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
90Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
100Sstevel@tonic-gate  * See the License for the specific language governing permissions
110Sstevel@tonic-gate  * and limitations under the License.
120Sstevel@tonic-gate  *
130Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
140Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
150Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
160Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
170Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
180Sstevel@tonic-gate  *
190Sstevel@tonic-gate  * CDDL HEADER END
200Sstevel@tonic-gate  */
210Sstevel@tonic-gate /*
22*12195SAaron.Zang@Sun.COM  * Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved.
230Sstevel@tonic-gate  */
240Sstevel@tonic-gate 
25871Scasper #define	_POSIX_PTHREAD_SEMANTICS	/* for getgrnam_r */
260Sstevel@tonic-gate #ifdef lint
270Sstevel@tonic-gate #define	_REENTRANT			/* for strtok_r */
280Sstevel@tonic-gate #endif
290Sstevel@tonic-gate 
300Sstevel@tonic-gate #include <stdio.h>
310Sstevel@tonic-gate #include <stdlib.h>
322805Seota #include <ctype.h>
330Sstevel@tonic-gate #include <string.h>
340Sstevel@tonic-gate #include <unistd.h>
350Sstevel@tonic-gate #include <dirent.h>
360Sstevel@tonic-gate #include <errno.h>
370Sstevel@tonic-gate #include <grp.h>
380Sstevel@tonic-gate #include <pwd.h>
390Sstevel@tonic-gate #include <nss_dbdefs.h>
400Sstevel@tonic-gate #include <stdarg.h>
410Sstevel@tonic-gate #include <syslog.h>
420Sstevel@tonic-gate #include <sys/acl.h>
430Sstevel@tonic-gate #include <sys/types.h>
440Sstevel@tonic-gate #include <sys/stat.h>
450Sstevel@tonic-gate #include <sys/ddi.h>
460Sstevel@tonic-gate #include <sys/sunddi.h>
470Sstevel@tonic-gate #include <sys/devinfo_impl.h>
480Sstevel@tonic-gate #include <sys/hwconf.h>
490Sstevel@tonic-gate #include <sys/modctl.h>
500Sstevel@tonic-gate #include <libnvpair.h>
510Sstevel@tonic-gate #include <device_info.h>
520Sstevel@tonic-gate #include <regex.h>
530Sstevel@tonic-gate #include <strings.h>
540Sstevel@tonic-gate #include <libdevinfo.h>
552642Sjg #include <zone.h>
567688SAaron.Zang@Sun.COM #include <fcntl.h>
577688SAaron.Zang@Sun.COM #include <utmpx.h>
580Sstevel@tonic-gate 
590Sstevel@tonic-gate extern int is_minor_node(const char *, const char **);
600Sstevel@tonic-gate 
617688SAaron.Zang@Sun.COM static int is_login_user(uid_t);
620Sstevel@tonic-gate static int logindevperm(const char *, uid_t, gid_t, void (*)());
630Sstevel@tonic-gate static int dir_dev_acc(char *, char *, uid_t, gid_t, mode_t, char *line,
640Sstevel@tonic-gate 	void (*)());
650Sstevel@tonic-gate static int setdevaccess(char *, uid_t, gid_t, mode_t, void (*)());
660Sstevel@tonic-gate static void logerror(char *);
670Sstevel@tonic-gate 
682805Seota static int is_blank(char *);
692805Seota 
700Sstevel@tonic-gate #define	MAX_LINELEN	256
710Sstevel@tonic-gate #define	LOGINDEVPERM	"/etc/logindevperm"
720Sstevel@tonic-gate #define	DIRWILD		"/*"			/* directory wildcard */
730Sstevel@tonic-gate #define	DIRWLDLEN	2			/* strlen(DIRWILD) */
740Sstevel@tonic-gate 
750Sstevel@tonic-gate /*
760Sstevel@tonic-gate  * Revoke all access to a device node and make sure that there are
770Sstevel@tonic-gate  * no interposed streams devices attached.  Must be called before a
780Sstevel@tonic-gate  * device is actually opened.
790Sstevel@tonic-gate  * When fdetach is called, the underlying device node is revealed; it
800Sstevel@tonic-gate  * will have the previous owner and that owner can re-attach; so we
810Sstevel@tonic-gate  * retry until we win.
820Sstevel@tonic-gate  * Ignore non-existent devices.
830Sstevel@tonic-gate  */
840Sstevel@tonic-gate static int
setdevaccess(char * dev,uid_t uid,gid_t gid,mode_t mode,void (* errmsg)(char *))850Sstevel@tonic-gate setdevaccess(char *dev, uid_t uid, gid_t gid, mode_t mode,
860Sstevel@tonic-gate     void (*errmsg)(char *))
870Sstevel@tonic-gate {
880Sstevel@tonic-gate 	int err = 0, local_errno;
890Sstevel@tonic-gate 	char errstring[MAX_LINELEN];
902912Sartem 	struct stat st;
910Sstevel@tonic-gate 
920Sstevel@tonic-gate 	if (chown(dev, uid, gid) == -1) {
930Sstevel@tonic-gate 		if (errno == ENOENT)	/* no such file */
940Sstevel@tonic-gate 			return (0);
950Sstevel@tonic-gate 		err = -1;
961243Sfrits 		local_errno = errno;
970Sstevel@tonic-gate 	}
980Sstevel@tonic-gate 
992912Sartem 	/*
1002912Sartem 	 * don't fdetach block devices, as it will unmount them
1012912Sartem 	 */
1022912Sartem 	if (!((stat(dev, &st) == 0) && ((st.st_mode & S_IFMT) == S_IFBLK))) {
1032912Sartem 		while (fdetach(dev) == 0) {
1042912Sartem 			if (chown(dev, uid, gid) == -1) {
1052912Sartem 				err = -1;
1062912Sartem 				local_errno = errno;
1072912Sartem 			}
1080Sstevel@tonic-gate 		}
1092912Sartem 		if (err && errmsg) {
1102912Sartem 			(void) snprintf(errstring, MAX_LINELEN,
1112912Sartem 			    "failed to chown device %s: %s\n",
1122912Sartem 			    dev, strerror(local_errno));
1132912Sartem 			(*errmsg)(errstring);
1142912Sartem 		}
1150Sstevel@tonic-gate 	}
1160Sstevel@tonic-gate 
117789Sahrens 	/*
118789Sahrens 	 * strip_acl sets an acl and changes the files owner/group
119789Sahrens 	 */
120789Sahrens 	err = acl_strip(dev, uid, gid, mode);
1210Sstevel@tonic-gate 
122789Sahrens 	if (err != 0) {
1230Sstevel@tonic-gate 		/*
1240Sstevel@tonic-gate 		 * If the file system returned ENOSYS, we know that it
1250Sstevel@tonic-gate 		 * doesn't support ACLs, therefore, we must assume that
1260Sstevel@tonic-gate 		 * there were no ACLs to remove in the first place.
1270Sstevel@tonic-gate 		 */
1282621Sllai1 		err = 0;
1290Sstevel@tonic-gate 		if (errno != ENOSYS) {
1300Sstevel@tonic-gate 			err = -1;
1310Sstevel@tonic-gate 
1320Sstevel@tonic-gate 			if (errmsg) {
1330Sstevel@tonic-gate 				(void) snprintf(errstring, MAX_LINELEN,
1340Sstevel@tonic-gate 				    "failed to set acl on device %s: %s\n",
1350Sstevel@tonic-gate 				    dev, strerror(errno));
1360Sstevel@tonic-gate 				(*errmsg)(errstring);
1370Sstevel@tonic-gate 			}
1380Sstevel@tonic-gate 		}
139789Sahrens 		if (chmod(dev, mode) == -1) {
140789Sahrens 			err = -1;
141789Sahrens 			if (errmsg) {
142789Sahrens 				(void) snprintf(errstring, MAX_LINELEN,
143789Sahrens 				    "failed to chmod device %s: %s\n",
144789Sahrens 				    dev, strerror(errno));
145789Sahrens 				(*errmsg)(errstring);
146789Sahrens 			}
1470Sstevel@tonic-gate 		}
1480Sstevel@tonic-gate 	}
1490Sstevel@tonic-gate 
1500Sstevel@tonic-gate 	return (err);
1510Sstevel@tonic-gate }
1520Sstevel@tonic-gate 
1530Sstevel@tonic-gate /*
1540Sstevel@tonic-gate  * logindevperm - change owner/group/permissions of devices
1550Sstevel@tonic-gate  * list in /etc/logindevperm.
1560Sstevel@tonic-gate  */
1570Sstevel@tonic-gate static int
logindevperm(const char * ttyn,uid_t uid,gid_t gid,void (* errmsg)(char *))1580Sstevel@tonic-gate logindevperm(const char *ttyn, uid_t uid, gid_t gid, void (*errmsg)(char *))
1590Sstevel@tonic-gate {
1600Sstevel@tonic-gate 	int err = 0, lineno = 0;
1610Sstevel@tonic-gate 	const char *field_delims = " \t\n";
1620Sstevel@tonic-gate 	char line[MAX_LINELEN], errstring[MAX_LINELEN];
1630Sstevel@tonic-gate 	char saveline[MAX_LINELEN];
1640Sstevel@tonic-gate 	char *console;
1650Sstevel@tonic-gate 	char *mode_str;
1660Sstevel@tonic-gate 	char *dev_list;
1670Sstevel@tonic-gate 	char *device;
1680Sstevel@tonic-gate 	char *ptr;
1690Sstevel@tonic-gate 	int mode;
1700Sstevel@tonic-gate 	FILE *fp;
171*12195SAaron.Zang@Sun.COM 	char ttyn_path[PATH_MAX + 1];
172*12195SAaron.Zang@Sun.COM 	int n;
1730Sstevel@tonic-gate 
1740Sstevel@tonic-gate 	if ((fp = fopen(LOGINDEVPERM, "r")) == NULL) {
1750Sstevel@tonic-gate 		if (errmsg) {
1760Sstevel@tonic-gate 			(void) snprintf(errstring, MAX_LINELEN,
1770Sstevel@tonic-gate 			    LOGINDEVPERM ": open failed: %s\n",
1780Sstevel@tonic-gate 			    strerror(errno));
1790Sstevel@tonic-gate 			(*errmsg)(errstring);
1800Sstevel@tonic-gate 		}
1810Sstevel@tonic-gate 		return (-1);
1820Sstevel@tonic-gate 	}
1830Sstevel@tonic-gate 
184*12195SAaron.Zang@Sun.COM 	if ((n = resolvepath(ttyn, ttyn_path, PATH_MAX)) == -1)
185*12195SAaron.Zang@Sun.COM 		return (-1);
186*12195SAaron.Zang@Sun.COM 	ttyn_path[n] = '\0';
187*12195SAaron.Zang@Sun.COM 
1880Sstevel@tonic-gate 	while (fgets(line, MAX_LINELEN, fp) != NULL) {
1890Sstevel@tonic-gate 		char *last;
190*12195SAaron.Zang@Sun.COM 		char tmp[PATH_MAX + 1];
191*12195SAaron.Zang@Sun.COM 
1920Sstevel@tonic-gate 		lineno++;
1930Sstevel@tonic-gate 
1940Sstevel@tonic-gate 		if ((ptr = strchr(line, '#')) != NULL)
1950Sstevel@tonic-gate 			*ptr = '\0';	/* handle comments */
1960Sstevel@tonic-gate 
1970Sstevel@tonic-gate 		(void) strcpy(saveline, line);
1980Sstevel@tonic-gate 
1990Sstevel@tonic-gate 		console = strtok_r(line, field_delims, &last);
2000Sstevel@tonic-gate 		if (console == NULL)
2010Sstevel@tonic-gate 			continue;	/* ignore blank lines */
2020Sstevel@tonic-gate 
203*12195SAaron.Zang@Sun.COM 		if ((n = resolvepath(console, tmp, PATH_MAX)) == -1)
204*12195SAaron.Zang@Sun.COM 			continue;
205*12195SAaron.Zang@Sun.COM 		tmp[n] = '\0';
206*12195SAaron.Zang@Sun.COM 
207*12195SAaron.Zang@Sun.COM 		if (strcmp(ttyn_path, tmp) != 0)
208*12195SAaron.Zang@Sun.COM 			continue;
2090Sstevel@tonic-gate 
2100Sstevel@tonic-gate 		mode_str = strtok_r(last, field_delims, &last);
2110Sstevel@tonic-gate 		if (mode_str == NULL) {
2120Sstevel@tonic-gate 			err = -1;	/* invalid entry, skip */
2130Sstevel@tonic-gate 			if (errmsg) {
2140Sstevel@tonic-gate 				(void) snprintf(errstring, MAX_LINELEN,
2150Sstevel@tonic-gate 				    LOGINDEVPERM
2160Sstevel@tonic-gate 				    ": line %d, invalid entry -- %s\n",
2170Sstevel@tonic-gate 				    lineno, line);
2180Sstevel@tonic-gate 				(*errmsg)(errstring);
2190Sstevel@tonic-gate 			}
2200Sstevel@tonic-gate 			continue;
2210Sstevel@tonic-gate 		}
2220Sstevel@tonic-gate 
2230Sstevel@tonic-gate 		/* convert string to octal value */
2240Sstevel@tonic-gate 		mode = strtol(mode_str, &ptr, 8);
2250Sstevel@tonic-gate 		if (mode < 0 || mode > 0777 || *ptr != '\0') {
2260Sstevel@tonic-gate 			err = -1;	/* invalid mode, skip */
2270Sstevel@tonic-gate 			if (errmsg) {
2280Sstevel@tonic-gate 				(void) snprintf(errstring, MAX_LINELEN,
2290Sstevel@tonic-gate 				    LOGINDEVPERM
2300Sstevel@tonic-gate 				    ": line %d, invalid mode -- %s\n",
2310Sstevel@tonic-gate 				    lineno, mode_str);
2320Sstevel@tonic-gate 				(*errmsg)(errstring);
2330Sstevel@tonic-gate 			}
2340Sstevel@tonic-gate 			continue;
2350Sstevel@tonic-gate 		}
2360Sstevel@tonic-gate 
2370Sstevel@tonic-gate 		dev_list = strtok_r(last, field_delims, &last);
2380Sstevel@tonic-gate 		if (dev_list == NULL) {
2390Sstevel@tonic-gate 			err = -1;	/* empty device list, skip */
2400Sstevel@tonic-gate 			if (errmsg) {
2410Sstevel@tonic-gate 				(void) snprintf(errstring, MAX_LINELEN,
2420Sstevel@tonic-gate 				    LOGINDEVPERM
2430Sstevel@tonic-gate 				    ": line %d, empty device list -- %s\n",
2440Sstevel@tonic-gate 				    lineno, line);
2450Sstevel@tonic-gate 				(*errmsg)(errstring);
2460Sstevel@tonic-gate 			}
2470Sstevel@tonic-gate 			continue;
2480Sstevel@tonic-gate 		}
2490Sstevel@tonic-gate 
2500Sstevel@tonic-gate 		device = strtok_r(dev_list, ":", &last);
2510Sstevel@tonic-gate 		while (device != NULL) {
2520Sstevel@tonic-gate 			if ((device[0] != '/') || (strlen(device) <= 1))  {
2530Sstevel@tonic-gate 				err = -1;
2540Sstevel@tonic-gate 			} else if (dir_dev_acc("/", &device[1], uid, gid, mode,
2550Sstevel@tonic-gate 			    saveline, errmsg)) {
2560Sstevel@tonic-gate 				err = -1;
2570Sstevel@tonic-gate 			}
2580Sstevel@tonic-gate 			device = strtok_r(last, ":", &last);
2590Sstevel@tonic-gate 		}
2600Sstevel@tonic-gate 	}
2610Sstevel@tonic-gate 	(void) fclose(fp);
2620Sstevel@tonic-gate 	return (err);
2630Sstevel@tonic-gate }
2640Sstevel@tonic-gate 
2650Sstevel@tonic-gate /*
2660Sstevel@tonic-gate  * returns 0 if resolved, -1 otherwise.
2670Sstevel@tonic-gate  * devpath: Absolute path to /dev link
2680Sstevel@tonic-gate  * devfs_path: Returns malloced string: /devices path w/out "/devices"
2690Sstevel@tonic-gate  */
27011529SJan.Parcel@Sun.COM int
devfs_resolve_link(char * devpath,char ** devfs_path)27111529SJan.Parcel@Sun.COM devfs_resolve_link(char *devpath, char **devfs_path)
2720Sstevel@tonic-gate {
2730Sstevel@tonic-gate 	char contents[PATH_MAX + 1];
2740Sstevel@tonic-gate 	char stage_link[PATH_MAX + 1];
2750Sstevel@tonic-gate 	char *ptr;
2760Sstevel@tonic-gate 	int linksize;
2770Sstevel@tonic-gate 	char *slashdev = "/dev/";
2780Sstevel@tonic-gate 
2790Sstevel@tonic-gate 	if (devfs_path) {
2800Sstevel@tonic-gate 		*devfs_path = NULL;
2810Sstevel@tonic-gate 	}
2820Sstevel@tonic-gate 
2830Sstevel@tonic-gate 	linksize = readlink(devpath, contents, PATH_MAX);
2840Sstevel@tonic-gate 
2850Sstevel@tonic-gate 	if (linksize <= 0) {
2860Sstevel@tonic-gate 		return (-1);
2870Sstevel@tonic-gate 	} else {
2880Sstevel@tonic-gate 		contents[linksize] = '\0';
2890Sstevel@tonic-gate 	}
2900Sstevel@tonic-gate 
2910Sstevel@tonic-gate 	/*
2920Sstevel@tonic-gate 	 * if the link contents is not a minor node assume
2930Sstevel@tonic-gate 	 * that link contents is really a pointer to another
2940Sstevel@tonic-gate 	 * link, and if so recurse and read its link contents.
2950Sstevel@tonic-gate 	 */
2960Sstevel@tonic-gate 	if (is_minor_node((const char *)contents, (const char **)&ptr) !=
2970Sstevel@tonic-gate 	    1) {
2980Sstevel@tonic-gate 		if (strncmp(contents, slashdev, strlen(slashdev)) == 0)  {
2990Sstevel@tonic-gate 			/* absolute path, starting with /dev */
3000Sstevel@tonic-gate 			(void) strcpy(stage_link, contents);
3010Sstevel@tonic-gate 		} else {
3020Sstevel@tonic-gate 			/* relative path, prefix devpath */
3030Sstevel@tonic-gate 			if ((ptr = strrchr(devpath, '/')) == NULL) {
3040Sstevel@tonic-gate 				/* invalid link */
3050Sstevel@tonic-gate 				return (-1);
3060Sstevel@tonic-gate 			}
3070Sstevel@tonic-gate 			*ptr = '\0';
3080Sstevel@tonic-gate 			(void) strcpy(stage_link, devpath);
3090Sstevel@tonic-gate 			*ptr = '/';
3100Sstevel@tonic-gate 			(void) strcat(stage_link, "/");
3110Sstevel@tonic-gate 			(void) strcat(stage_link, contents);
3120Sstevel@tonic-gate 
3130Sstevel@tonic-gate 		}
31411529SJan.Parcel@Sun.COM 		return (devfs_resolve_link(stage_link, devfs_path));
3150Sstevel@tonic-gate 	}
3160Sstevel@tonic-gate 
3170Sstevel@tonic-gate 	if (devfs_path) {
3180Sstevel@tonic-gate 		*devfs_path = strdup(ptr);
3190Sstevel@tonic-gate 		if (*devfs_path == NULL) {
3200Sstevel@tonic-gate 			return (-1);
3210Sstevel@tonic-gate 		}
3220Sstevel@tonic-gate 	}
3230Sstevel@tonic-gate 
3240Sstevel@tonic-gate 	return (0);
3250Sstevel@tonic-gate }
3260Sstevel@tonic-gate 
3270Sstevel@tonic-gate /*
3280Sstevel@tonic-gate  * check a logindevperm line for a driver list and match this against
3290Sstevel@tonic-gate  * the driver of the minor node
3300Sstevel@tonic-gate  * returns 0 if no drivers were specified or a driver match
3310Sstevel@tonic-gate  */
3320Sstevel@tonic-gate static int
check_driver_match(char * path,char * line)3330Sstevel@tonic-gate check_driver_match(char *path, char *line)
3340Sstevel@tonic-gate {
3350Sstevel@tonic-gate 	char *drv, *driver, *lasts;
3360Sstevel@tonic-gate 	char *devfs_path = NULL;
3370Sstevel@tonic-gate 	char saveline[MAX_LINELEN];
3380Sstevel@tonic-gate 	char *p;
3390Sstevel@tonic-gate 
34011529SJan.Parcel@Sun.COM 	if (devfs_resolve_link(path, &devfs_path) == 0) {
3410Sstevel@tonic-gate 		char *p;
3420Sstevel@tonic-gate 		char pwd_buf[PATH_MAX];
3430Sstevel@tonic-gate 		di_node_t node;
3440Sstevel@tonic-gate 
3450Sstevel@tonic-gate 		/* truncate on : so we can take a snapshot */
3460Sstevel@tonic-gate 		(void) strcpy(pwd_buf, devfs_path);
3470Sstevel@tonic-gate 		p = strrchr(pwd_buf, ':');
3480Sstevel@tonic-gate 		*p = '\0';
3490Sstevel@tonic-gate 
3500Sstevel@tonic-gate 		node = di_init(pwd_buf, DINFOMINOR);
3510Sstevel@tonic-gate 		free(devfs_path);
3520Sstevel@tonic-gate 
3530Sstevel@tonic-gate 		if (node) {
3540Sstevel@tonic-gate 			drv = di_driver_name(node);
3550Sstevel@tonic-gate 			di_fini(node);
3560Sstevel@tonic-gate 		} else {
3570Sstevel@tonic-gate 			return (0);
3580Sstevel@tonic-gate 		}
3590Sstevel@tonic-gate 	} else {
3600Sstevel@tonic-gate 		return (0);
3610Sstevel@tonic-gate 	}
3620Sstevel@tonic-gate 
3630Sstevel@tonic-gate 	(void) strcpy(saveline, line);
3640Sstevel@tonic-gate 
3650Sstevel@tonic-gate 	p = strstr(saveline, "driver");
3660Sstevel@tonic-gate 	if (p == NULL) {
3670Sstevel@tonic-gate 		return (0);
3680Sstevel@tonic-gate 	}
3690Sstevel@tonic-gate 
3700Sstevel@tonic-gate 	driver = strtok_r(p, "=", &lasts);
3710Sstevel@tonic-gate 	if (driver) {
3720Sstevel@tonic-gate 		if (strcmp(driver, "driver") == 0) {
3730Sstevel@tonic-gate 			driver = strtok_r(NULL, ", \t\n", &lasts);
3740Sstevel@tonic-gate 			while (driver) {
3750Sstevel@tonic-gate 				if (strcmp(driver, drv) == 0) {
3760Sstevel@tonic-gate 					return (0);
3770Sstevel@tonic-gate 				}
3780Sstevel@tonic-gate 				driver = strtok_r(NULL, ", \t\n", &lasts);
3790Sstevel@tonic-gate 			}
3800Sstevel@tonic-gate 		}
3810Sstevel@tonic-gate 	}
3820Sstevel@tonic-gate 
3830Sstevel@tonic-gate 	return (-1);
3840Sstevel@tonic-gate }
3850Sstevel@tonic-gate 
3860Sstevel@tonic-gate /*
3877688SAaron.Zang@Sun.COM  * Check whether the user has logged onto "/dev/console" or "/dev/vt/#".
3887688SAaron.Zang@Sun.COM  */
3897688SAaron.Zang@Sun.COM static int
is_login_user(uid_t uid)3907688SAaron.Zang@Sun.COM is_login_user(uid_t uid)
3917688SAaron.Zang@Sun.COM {
3927688SAaron.Zang@Sun.COM 	int changed = 0;
3937688SAaron.Zang@Sun.COM 	struct passwd pwd, *ppwd;
3947688SAaron.Zang@Sun.COM 	char pwd_buf[NSS_BUFLEN_PASSWD];
3957688SAaron.Zang@Sun.COM 	struct utmpx *utx;
3967688SAaron.Zang@Sun.COM 
3977688SAaron.Zang@Sun.COM 	if ((getpwuid_r(uid, &pwd, pwd_buf, NSS_BUFLEN_PASSWD, &ppwd))) {
3987688SAaron.Zang@Sun.COM 		return (0);
3997688SAaron.Zang@Sun.COM 	}
4007688SAaron.Zang@Sun.COM 
4017688SAaron.Zang@Sun.COM 	setutxent();
4027688SAaron.Zang@Sun.COM 	while ((utx = getutxent()) != NULL) {
4037688SAaron.Zang@Sun.COM 		if (utx->ut_type == USER_PROCESS &&
4047688SAaron.Zang@Sun.COM 		    strncmp(utx->ut_user, ppwd->pw_name,
4057688SAaron.Zang@Sun.COM 		    strlen(ppwd->pw_name)) == 0 && (strncmp(utx->ut_line,
4067688SAaron.Zang@Sun.COM 		    "console", strlen("console")) == 0 || strncmp(utx->ut_line,
4077688SAaron.Zang@Sun.COM 		    "vt", strlen("vt")) == 0)) {
4087688SAaron.Zang@Sun.COM 
4097688SAaron.Zang@Sun.COM 			changed = 1;
4107688SAaron.Zang@Sun.COM 			break;
4117688SAaron.Zang@Sun.COM 		}
4127688SAaron.Zang@Sun.COM 	}
4137688SAaron.Zang@Sun.COM 	endutxent();
4147688SAaron.Zang@Sun.COM 
4157688SAaron.Zang@Sun.COM 	return (changed);
4167688SAaron.Zang@Sun.COM }
4177688SAaron.Zang@Sun.COM 
4187688SAaron.Zang@Sun.COM /*
4190Sstevel@tonic-gate  * Apply owner/group/perms to all files (except "." and "..")
4200Sstevel@tonic-gate  * in a directory.
4210Sstevel@tonic-gate  * This function is recursive. We start with "/" and the rest of the pathname
4220Sstevel@tonic-gate  * in left_to_do argument, and we walk the entire pathname which may contain
4230Sstevel@tonic-gate  * regular expressions or '*' for each directory name or basename.
4240Sstevel@tonic-gate  */
4250Sstevel@tonic-gate static int
dir_dev_acc(char * path,char * left_to_do,uid_t uid,gid_t gid,mode_t mode,char * line,void (* errmsg)(char *))4260Sstevel@tonic-gate dir_dev_acc(char *path, char *left_to_do, uid_t uid, gid_t gid, mode_t mode,
4270Sstevel@tonic-gate     char *line, void (*errmsg)(char *))
4280Sstevel@tonic-gate {
4290Sstevel@tonic-gate 	struct stat stat_buf;
4300Sstevel@tonic-gate 	int err = 0;
4311243Sfrits 	char errstring[MAX_LINELEN];
4322621Sllai1 	char *p;
4332621Sllai1 	regex_t regex;
4342621Sllai1 	int alwaysmatch = 0;
4352621Sllai1 	char *match;
4362621Sllai1 	char *name, *newpath, *remainder_path;
4372621Sllai1 	finddevhdl_t handle;
4382621Sllai1 
4392621Sllai1 	/*
4402642Sjg 	 * Determine if the search needs to be performed via finddev,
4412642Sjg 	 * which returns only persisted names in the global /dev, or
4422642Sjg 	 * readdir, for paths other than /dev and non-global zones.
4432642Sjg 	 * This use of finddev avoids triggering potential implicit
4442642Sjg 	 * reconfig for names managed by logindevperm but not present
4452642Sjg 	 * on the system.
4462621Sllai1 	 */
4473702Sjg 	if (!device_exists(path)) {
4482621Sllai1 		return (-1);
4492621Sllai1 	}
4500Sstevel@tonic-gate 	if (stat(path, &stat_buf) == -1) {
4511243Sfrits 		/*
4521243Sfrits 		 * ENOENT errors are expected errors when there are
4531243Sfrits 		 * dangling /dev device links. Ignore them silently
4541243Sfrits 		 */
4551243Sfrits 		if (errno == ENOENT) {
4561243Sfrits 			return (0);
4571243Sfrits 		}
4581243Sfrits 		if (errmsg) {
4591243Sfrits 			(void) snprintf(errstring, MAX_LINELEN,
4601243Sfrits 			    "failed to stat %s: %s\n", path,
4611243Sfrits 			    strerror(errno));
4621243Sfrits 			(*errmsg)(errstring);
4631243Sfrits 		}
4640Sstevel@tonic-gate 		return (-1);
4650Sstevel@tonic-gate 	} else {
4660Sstevel@tonic-gate 		if (!S_ISDIR(stat_buf.st_mode)) {
4670Sstevel@tonic-gate 			if (strlen(left_to_do) == 0) {
4680Sstevel@tonic-gate 				/* finally check the driver matches */
4690Sstevel@tonic-gate 				if (check_driver_match(path, line) == 0) {
4707688SAaron.Zang@Sun.COM 					/*
4717688SAaron.Zang@Sun.COM 					 * if the owner of device has been
4727688SAaron.Zang@Sun.COM 					 * login, the ownership and mode
4737688SAaron.Zang@Sun.COM 					 * should be set already. in
4747688SAaron.Zang@Sun.COM 					 * this case, do not set the
4757688SAaron.Zang@Sun.COM 					 * permissions.
4767688SAaron.Zang@Sun.COM 					 */
4777688SAaron.Zang@Sun.COM 					if (is_login_user(stat_buf.st_uid)) {
4787688SAaron.Zang@Sun.COM 
4797688SAaron.Zang@Sun.COM 						return (0);
4807688SAaron.Zang@Sun.COM 					}
4810Sstevel@tonic-gate 					/* we are done, set the permissions */
4820Sstevel@tonic-gate 					if (setdevaccess(path,
4830Sstevel@tonic-gate 					    uid, gid, mode, errmsg)) {
4840Sstevel@tonic-gate 
4850Sstevel@tonic-gate 						return (-1);
4860Sstevel@tonic-gate 					}
4870Sstevel@tonic-gate 				}
4880Sstevel@tonic-gate 			}
4890Sstevel@tonic-gate 			return (0);
4900Sstevel@tonic-gate 		}
4910Sstevel@tonic-gate 	}
4920Sstevel@tonic-gate 
4933702Sjg 	if (finddev_readdir(path, &handle) != 0)
4943702Sjg 		return (0);
4952621Sllai1 
4962621Sllai1 	p = strchr(left_to_do, '/');
4972621Sllai1 	alwaysmatch = 0;
4980Sstevel@tonic-gate 
4992621Sllai1 	newpath = (char *)malloc(MAXPATHLEN);
5002621Sllai1 	if (newpath == NULL) {
5013702Sjg 		finddev_close(handle);
5022621Sllai1 		return (-1);
5032621Sllai1 	}
5045333Srm88369 	match = (char *)calloc(MAXPATHLEN + 2, 1);
5052621Sllai1 	if (match == NULL) {
5063702Sjg 		finddev_close(handle);
5072621Sllai1 		free(newpath);
5082621Sllai1 		return (-1);
5092621Sllai1 	}
5102621Sllai1 
5115333Srm88369 	/* transform pattern into ^pattern$ for exact match */
5125333Srm88369 	if (snprintf(match, MAXPATHLEN + 2, "^%.*s$",
5135333Srm88369 	    p ? (p - left_to_do) : strlen(left_to_do), left_to_do) >=
5145333Srm88369 	    MAXPATHLEN + 2) {
5155333Srm88369 		finddev_close(handle);
5165333Srm88369 		free(newpath);
5175333Srm88369 		free(match);
5185333Srm88369 		return (-1);
5192621Sllai1 	}
5202621Sllai1 
5215333Srm88369 	if (strcmp(match, "^*$") == 0) {
5222621Sllai1 		alwaysmatch = 1;
5232621Sllai1 	} else {
5242621Sllai1 		if (regcomp(&regex, match, REG_EXTENDED) != 0) {
5250Sstevel@tonic-gate 			free(newpath);
5262621Sllai1 			free(match);
5273702Sjg 			finddev_close(handle);
5280Sstevel@tonic-gate 			return (-1);
5290Sstevel@tonic-gate 		}
5302621Sllai1 	}
5310Sstevel@tonic-gate 
5323702Sjg 	while ((name = (char *)finddev_next(handle)) != NULL) {
5332621Sllai1 		if (alwaysmatch ||
5342621Sllai1 		    regexec(&regex, name, 0, NULL, 0) == 0) {
5352621Sllai1 			if (strcmp(path, "/") == 0) {
5362621Sllai1 				(void) snprintf(newpath,
5372621Sllai1 				    MAXPATHLEN, "%s%s", path, name);
5382621Sllai1 			} else {
5392621Sllai1 				(void) snprintf(newpath,
5402621Sllai1 				    MAXPATHLEN, "%s/%s", path, name);
5412621Sllai1 			}
5422621Sllai1 
5432621Sllai1 			/*
5442621Sllai1 			 * recurse but adjust what is still left to do
5452621Sllai1 			 */
5462621Sllai1 			remainder_path = (p ?
5472621Sllai1 			    left_to_do + (p - left_to_do) + 1 :
5482621Sllai1 			    &left_to_do[strlen(left_to_do)]);
5492621Sllai1 			if (dir_dev_acc(newpath, remainder_path,
5502621Sllai1 			    uid, gid, mode, line, errmsg)) {
5512621Sllai1 				err = -1;
5520Sstevel@tonic-gate 			}
5530Sstevel@tonic-gate 		}
5542621Sllai1 	}
5552621Sllai1 
5563702Sjg 	finddev_close(handle);
5572621Sllai1 	free(newpath);
5582621Sllai1 	free(match);
5592621Sllai1 	if (!alwaysmatch) {
5602621Sllai1 		regfree(&regex);
5610Sstevel@tonic-gate 	}
5620Sstevel@tonic-gate 
5630Sstevel@tonic-gate 	return (err);
5640Sstevel@tonic-gate }
5650Sstevel@tonic-gate 
5660Sstevel@tonic-gate /*
5670Sstevel@tonic-gate  * di_devperm_login - modify access of devices in /etc/logindevperm
5680Sstevel@tonic-gate  * by changing owner/group/permissions to that of ttyn.
5690Sstevel@tonic-gate  */
5700Sstevel@tonic-gate int
di_devperm_login(const char * ttyn,uid_t uid,gid_t gid,void (* errmsg)(char *))5710Sstevel@tonic-gate di_devperm_login(const char *ttyn, uid_t uid, gid_t gid,
5720Sstevel@tonic-gate     void (*errmsg)(char *))
5730Sstevel@tonic-gate {
5740Sstevel@tonic-gate 	int err;
5750Sstevel@tonic-gate 	struct group grp, *grpp;
5760Sstevel@tonic-gate 	gid_t tty_gid;
5770Sstevel@tonic-gate 	char grbuf[NSS_BUFLEN_GROUP];
5780Sstevel@tonic-gate 
5790Sstevel@tonic-gate 	if (errmsg == NULL)
5800Sstevel@tonic-gate 		errmsg = logerror;
5810Sstevel@tonic-gate 
5820Sstevel@tonic-gate 	if (ttyn == NULL) {
5830Sstevel@tonic-gate 		(*errmsg)("di_devperm_login: NULL tty device\n");
5840Sstevel@tonic-gate 		return (-1);
5850Sstevel@tonic-gate 	}
5860Sstevel@tonic-gate 
5870Sstevel@tonic-gate 	if (getgrnam_r("tty", &grp, grbuf, NSS_BUFLEN_GROUP, &grpp) != 0) {
5880Sstevel@tonic-gate 		tty_gid = grpp->gr_gid;
5890Sstevel@tonic-gate 	} else {
5900Sstevel@tonic-gate 		/*
5910Sstevel@tonic-gate 		 * this should never happen, but if it does set
5920Sstevel@tonic-gate 		 * group to tty's traditional value.
5930Sstevel@tonic-gate 		 */
5940Sstevel@tonic-gate 		tty_gid = 7;
5950Sstevel@tonic-gate 	}
5960Sstevel@tonic-gate 
5970Sstevel@tonic-gate 	/* set the login console device permission */
5980Sstevel@tonic-gate 	err = setdevaccess((char *)ttyn, uid, tty_gid,
5990Sstevel@tonic-gate 	    S_IRUSR|S_IWUSR|S_IWGRP, errmsg);
6000Sstevel@tonic-gate 	if (err) {
6010Sstevel@tonic-gate 		return (err);
6020Sstevel@tonic-gate 	}
6030Sstevel@tonic-gate 
6040Sstevel@tonic-gate 	/* set the device permissions */
6050Sstevel@tonic-gate 	return (logindevperm(ttyn, uid, gid, errmsg));
6060Sstevel@tonic-gate }
6070Sstevel@tonic-gate 
6080Sstevel@tonic-gate /*
6090Sstevel@tonic-gate  * di_devperm_logout - clean up access of devices in /etc/logindevperm
6100Sstevel@tonic-gate  * by resetting owner/group/permissions.
6110Sstevel@tonic-gate  */
6120Sstevel@tonic-gate int
di_devperm_logout(const char * ttyn)6130Sstevel@tonic-gate di_devperm_logout(const char *ttyn)
6140Sstevel@tonic-gate {
6150Sstevel@tonic-gate 	struct passwd *pwd;
6160Sstevel@tonic-gate 	uid_t root_uid;
6170Sstevel@tonic-gate 	gid_t root_gid;
6180Sstevel@tonic-gate 
6190Sstevel@tonic-gate 	if (ttyn == NULL)
6200Sstevel@tonic-gate 		return (-1);
6210Sstevel@tonic-gate 
6220Sstevel@tonic-gate 	pwd = getpwnam("root");
6230Sstevel@tonic-gate 	if (pwd != NULL) {
6240Sstevel@tonic-gate 		root_uid = pwd->pw_uid;
6250Sstevel@tonic-gate 		root_gid = pwd->pw_gid;
6260Sstevel@tonic-gate 	} else {
6270Sstevel@tonic-gate 		/*
6280Sstevel@tonic-gate 		 * this should never happen, but if it does set user
6290Sstevel@tonic-gate 		 * and group to root's traditional values.
6300Sstevel@tonic-gate 		 */
6310Sstevel@tonic-gate 		root_uid = 0;
6320Sstevel@tonic-gate 		root_gid = 0;
6330Sstevel@tonic-gate 	}
6340Sstevel@tonic-gate 
6350Sstevel@tonic-gate 	return (logindevperm(ttyn, root_uid, root_gid, NULL));
6360Sstevel@tonic-gate }
6370Sstevel@tonic-gate 
6380Sstevel@tonic-gate static void
logerror(char * errstring)6390Sstevel@tonic-gate logerror(char *errstring)
6400Sstevel@tonic-gate {
6410Sstevel@tonic-gate 	syslog(LOG_AUTH | LOG_CRIT, "%s", errstring);
6420Sstevel@tonic-gate }
6430Sstevel@tonic-gate 
6440Sstevel@tonic-gate 
6450Sstevel@tonic-gate /*
6460Sstevel@tonic-gate  * Tokens are separated by ' ', '\t', ':', '=', '&', '|', ';', '\n', or '\0'
6470Sstevel@tonic-gate  */
6480Sstevel@tonic-gate static int
getnexttoken(char * next,char ** nextp,char ** tokenpp,char * tchar)6490Sstevel@tonic-gate getnexttoken(char *next, char **nextp, char **tokenpp, char *tchar)
6500Sstevel@tonic-gate {
6510Sstevel@tonic-gate 	char *cp;
6520Sstevel@tonic-gate 	char *cp1;
6530Sstevel@tonic-gate 	char *tokenp;
6540Sstevel@tonic-gate 
6550Sstevel@tonic-gate 	cp = next;
6560Sstevel@tonic-gate 	while (*cp == ' ' || *cp == '\t') {
6570Sstevel@tonic-gate 		cp++;			/* skip leading spaces */
6580Sstevel@tonic-gate 	}
6590Sstevel@tonic-gate 	tokenp = cp;			/* start of token */
6600Sstevel@tonic-gate 	while (*cp != '\0' && *cp != '\n' && *cp != ' ' && *cp != '\t' &&
6614601Scasper 	    *cp != ':' && *cp != '=' && *cp != '&' &&
6624601Scasper 	    *cp != '|' && *cp != ';') {
6630Sstevel@tonic-gate 		cp++;			/* point to next character */
6640Sstevel@tonic-gate 	}
6650Sstevel@tonic-gate 	/*
6660Sstevel@tonic-gate 	 * If terminating character is a space or tab, look ahead to see if
6670Sstevel@tonic-gate 	 * there's another terminator that's not a space or a tab.
6680Sstevel@tonic-gate 	 * (This code handles trailing spaces.)
6690Sstevel@tonic-gate 	 */
6700Sstevel@tonic-gate 	if (*cp == ' ' || *cp == '\t') {
6710Sstevel@tonic-gate 		cp1 = cp;
6720Sstevel@tonic-gate 		while (*++cp1 == ' ' || *cp1 == '\t')
6730Sstevel@tonic-gate 			;
6740Sstevel@tonic-gate 		if (*cp1 == '=' || *cp1 == ':' || *cp1 == '&' || *cp1 == '|' ||
6754601Scasper 		    *cp1 == ';' || *cp1 == '\n' || *cp1 == '\0') {
6760Sstevel@tonic-gate 			*cp = NULL;	/* terminate token */
6770Sstevel@tonic-gate 			cp = cp1;
6780Sstevel@tonic-gate 		}
6790Sstevel@tonic-gate 	}
6800Sstevel@tonic-gate 	if (tchar != NULL) {
6810Sstevel@tonic-gate 		*tchar = *cp;		/* save terminating character */
6820Sstevel@tonic-gate 		if (*tchar == '\0') {
6830Sstevel@tonic-gate 			*tchar = '\n';
6840Sstevel@tonic-gate 		}
6850Sstevel@tonic-gate 	}
6860Sstevel@tonic-gate 	*cp++ = '\0';			/* terminate token, point to next */
6870Sstevel@tonic-gate 	*nextp = cp;			/* set pointer to next character */
6880Sstevel@tonic-gate 	if (cp - tokenp - 1 == 0) {
6890Sstevel@tonic-gate 		return (0);
6900Sstevel@tonic-gate 	}
6910Sstevel@tonic-gate 	*tokenpp = tokenp;
6920Sstevel@tonic-gate 	return (1);
6930Sstevel@tonic-gate }
6940Sstevel@tonic-gate 
6950Sstevel@tonic-gate /*
6960Sstevel@tonic-gate  * get a decimal octal or hex number. Handle '~' for one's complement.
6970Sstevel@tonic-gate  */
6980Sstevel@tonic-gate static int
getvalue(char * token,int * valuep)6990Sstevel@tonic-gate getvalue(char *token, int *valuep)
7000Sstevel@tonic-gate {
7010Sstevel@tonic-gate 	int radix;
7020Sstevel@tonic-gate 	int retval = 0;
7030Sstevel@tonic-gate 	int onescompl = 0;
7040Sstevel@tonic-gate 	int negate = 0;
7050Sstevel@tonic-gate 	char c;
7060Sstevel@tonic-gate 
7070Sstevel@tonic-gate 	if (*token == '~') {
7080Sstevel@tonic-gate 		onescompl++; /* perform one's complement on result */
7090Sstevel@tonic-gate 		token++;
7100Sstevel@tonic-gate 	} else if (*token == '-') {
7110Sstevel@tonic-gate 		negate++;
7120Sstevel@tonic-gate 		token++;
7130Sstevel@tonic-gate 	}
7140Sstevel@tonic-gate 	if (*token == '0') {
7150Sstevel@tonic-gate 		token++;
7160Sstevel@tonic-gate 		c = *token;
7170Sstevel@tonic-gate 
7180Sstevel@tonic-gate 		if (c == '\0') {
7190Sstevel@tonic-gate 			*valuep = 0;	/* value is 0 */
7200Sstevel@tonic-gate 			return (0);
7210Sstevel@tonic-gate 		}
7220Sstevel@tonic-gate 
7230Sstevel@tonic-gate 		if (c == 'x' || c == 'X') {
7240Sstevel@tonic-gate 			radix = 16;
7250Sstevel@tonic-gate 			token++;
7260Sstevel@tonic-gate 		} else {
7270Sstevel@tonic-gate 			radix = 8;
7280Sstevel@tonic-gate 		}
7290Sstevel@tonic-gate 	} else
7300Sstevel@tonic-gate 		radix = 10;
7310Sstevel@tonic-gate 
7320Sstevel@tonic-gate 	while ((c = *token++)) {
7330Sstevel@tonic-gate 		switch (radix) {
7340Sstevel@tonic-gate 		case 8:
7350Sstevel@tonic-gate 			if (c >= '0' && c <= '7') {
7360Sstevel@tonic-gate 				c -= '0';
7370Sstevel@tonic-gate 			} else {
7380Sstevel@tonic-gate 				/* invalid number */
7390Sstevel@tonic-gate 				return (0);
7400Sstevel@tonic-gate 			}
7410Sstevel@tonic-gate 			retval = (retval << 3) + c;
7420Sstevel@tonic-gate 			break;
7430Sstevel@tonic-gate 		case 10:
7440Sstevel@tonic-gate 			if (c >= '0' && c <= '9') {
7450Sstevel@tonic-gate 				c -= '0';
7460Sstevel@tonic-gate 			} else {
7470Sstevel@tonic-gate 				/* invalid number */
7480Sstevel@tonic-gate 				return (0);
7490Sstevel@tonic-gate 			}
7500Sstevel@tonic-gate 			retval = (retval * 10) + c;
7510Sstevel@tonic-gate 			break;
7520Sstevel@tonic-gate 		case 16:
7530Sstevel@tonic-gate 			if (c >= 'a' && c <= 'f') {
7540Sstevel@tonic-gate 				c = c - 'a' + 10;
7550Sstevel@tonic-gate 			} else if (c >= 'A' && c <= 'F') {
7560Sstevel@tonic-gate 				c = c - 'A' + 10;
7570Sstevel@tonic-gate 			} else if (c >= '0' && c <= '9') {
7580Sstevel@tonic-gate 				c -= '0';
7590Sstevel@tonic-gate 			} else {
7600Sstevel@tonic-gate 				/* invalid number */
7610Sstevel@tonic-gate 				return (0);
7620Sstevel@tonic-gate 			}
7630Sstevel@tonic-gate 			retval = (retval << 4) + c;
7640Sstevel@tonic-gate 			break;
7650Sstevel@tonic-gate 		}
7660Sstevel@tonic-gate 	}
7670Sstevel@tonic-gate 	if (onescompl) {
7680Sstevel@tonic-gate 		retval = ~retval;
7690Sstevel@tonic-gate 	}
7700Sstevel@tonic-gate 	if (negate) {
7710Sstevel@tonic-gate 		retval = -retval;
7720Sstevel@tonic-gate 	}
7730Sstevel@tonic-gate 	*valuep = retval;
7740Sstevel@tonic-gate 	return (1);
7750Sstevel@tonic-gate }
7760Sstevel@tonic-gate 
7770Sstevel@tonic-gate /*
7780Sstevel@tonic-gate  * Read /etc/minor_perm, return mperm list of entries
7790Sstevel@tonic-gate  */
7800Sstevel@tonic-gate struct mperm *
i_devfs_read_minor_perm(char * drvname,void (* errcb)(minorperm_err_t,int))7810Sstevel@tonic-gate i_devfs_read_minor_perm(char *drvname, void (*errcb)(minorperm_err_t, int))
7820Sstevel@tonic-gate {
7830Sstevel@tonic-gate 	FILE *pfd;
7840Sstevel@tonic-gate 	struct mperm *mp;
7850Sstevel@tonic-gate 	char line[MAX_MINOR_PERM_LINE];
7860Sstevel@tonic-gate 	char *cp, *p, t;
7870Sstevel@tonic-gate 	struct mperm *minor_perms = NULL;
7880Sstevel@tonic-gate 	struct mperm *mptail = NULL;
7890Sstevel@tonic-gate 	struct passwd *pw;
7900Sstevel@tonic-gate 	struct group *gp;
7910Sstevel@tonic-gate 	uid_t root_uid;
7920Sstevel@tonic-gate 	gid_t sys_gid;
7930Sstevel@tonic-gate 	int ln = 0;
7940Sstevel@tonic-gate 
7950Sstevel@tonic-gate 	/*
7960Sstevel@tonic-gate 	 * Get root/sys ids, these being the most common
7970Sstevel@tonic-gate 	 */
7980Sstevel@tonic-gate 	if ((pw = getpwnam(DEFAULT_DEV_USER)) != NULL) {
7990Sstevel@tonic-gate 		root_uid = pw->pw_uid;
8000Sstevel@tonic-gate 	} else {
8010Sstevel@tonic-gate 		(*errcb)(MP_CANT_FIND_USER_ERR, 0);
8020Sstevel@tonic-gate 		root_uid = (uid_t)0;	/* assume 0 is root */
8030Sstevel@tonic-gate 	}
8040Sstevel@tonic-gate 	if ((gp = getgrnam(DEFAULT_DEV_GROUP)) != NULL) {
8050Sstevel@tonic-gate 		sys_gid = gp->gr_gid;
8060Sstevel@tonic-gate 	} else {
8070Sstevel@tonic-gate 		(*errcb)(MP_CANT_FIND_GROUP_ERR, 0);
8080Sstevel@tonic-gate 		sys_gid = (gid_t)3;	/* assume 3 is sys */
8090Sstevel@tonic-gate 	}
8100Sstevel@tonic-gate 
8110Sstevel@tonic-gate 	if ((pfd = fopen(MINOR_PERM_FILE, "r")) == NULL) {
8120Sstevel@tonic-gate 		(*errcb)(MP_FOPEN_ERR, errno);
8130Sstevel@tonic-gate 		return (NULL);
8140Sstevel@tonic-gate 	}
8152805Seota 	while (fgets(line, MAX_MINOR_PERM_LINE, pfd) != NULL) {
8160Sstevel@tonic-gate 		ln++;
8172805Seota 		/* cut off comments starting with '#' */
8182805Seota 		if ((cp = strchr(line, '#')) != NULL)
8192805Seota 			*cp = '\0';
8202805Seota 		/* ignore comment or blank lines */
8212805Seota 		if (is_blank(line))
8222805Seota 			continue;
8230Sstevel@tonic-gate 		mp = (struct mperm *)calloc(1, sizeof (struct mperm));
8240Sstevel@tonic-gate 		if (mp == NULL) {
8250Sstevel@tonic-gate 			(*errcb)(MP_ALLOC_ERR, sizeof (struct mperm));
8260Sstevel@tonic-gate 			continue;
8270Sstevel@tonic-gate 		}
8280Sstevel@tonic-gate 		cp = line;
8292805Seota 		/* sanity-check */
8300Sstevel@tonic-gate 		if (getnexttoken(cp, &cp, &p, &t) == 0) {
8310Sstevel@tonic-gate 			(*errcb)(MP_IGNORING_LINE_ERR, ln);
8320Sstevel@tonic-gate 			devfs_free_minor_perm(mp);
8330Sstevel@tonic-gate 			continue;
8340Sstevel@tonic-gate 		}
8350Sstevel@tonic-gate 		mp->mp_drvname = strdup(p);
8360Sstevel@tonic-gate 		if (mp->mp_drvname == NULL) {
8370Sstevel@tonic-gate 			(*errcb)(MP_ALLOC_ERR, strlen(p)+1);
8380Sstevel@tonic-gate 			devfs_free_minor_perm(mp);
8390Sstevel@tonic-gate 			continue;
8400Sstevel@tonic-gate 		} else if (t == '\n' || t == '\0') {
8410Sstevel@tonic-gate 			(*errcb)(MP_IGNORING_LINE_ERR, ln);
8420Sstevel@tonic-gate 			devfs_free_minor_perm(mp);
8430Sstevel@tonic-gate 			continue;
8440Sstevel@tonic-gate 		}
8450Sstevel@tonic-gate 		if (t == ':') {
8460Sstevel@tonic-gate 			if (getnexttoken(cp, &cp, &p, &t) == 0) {
8470Sstevel@tonic-gate 				(*errcb)(MP_IGNORING_LINE_ERR, ln);
8480Sstevel@tonic-gate 				devfs_free_minor_perm(mp);
8490Sstevel@tonic-gate 			}
8500Sstevel@tonic-gate 			mp->mp_minorname = strdup(p);
8510Sstevel@tonic-gate 			if (mp->mp_minorname == NULL) {
8520Sstevel@tonic-gate 				(*errcb)(MP_ALLOC_ERR, strlen(p)+1);
8530Sstevel@tonic-gate 				devfs_free_minor_perm(mp);
8540Sstevel@tonic-gate 				continue;
8550Sstevel@tonic-gate 			}
8560Sstevel@tonic-gate 		} else {
8570Sstevel@tonic-gate 			mp->mp_minorname = NULL;
8580Sstevel@tonic-gate 		}
8590Sstevel@tonic-gate 
8600Sstevel@tonic-gate 		if (t == '\n' || t == '\0') {
8610Sstevel@tonic-gate 			devfs_free_minor_perm(mp);
8620Sstevel@tonic-gate 			(*errcb)(MP_IGNORING_LINE_ERR, ln);
8630Sstevel@tonic-gate 			continue;
8640Sstevel@tonic-gate 		}
8650Sstevel@tonic-gate 		if (getnexttoken(cp, &cp, &p, &t) == 0) {
8660Sstevel@tonic-gate 			goto link;
8670Sstevel@tonic-gate 		}
8680Sstevel@tonic-gate 		if (getvalue(p, (int *)&mp->mp_mode) == 0) {
8690Sstevel@tonic-gate 			goto link;
8700Sstevel@tonic-gate 		}
8710Sstevel@tonic-gate 		if (t == '\n' || t == '\0') {	/* no owner or group */
8720Sstevel@tonic-gate 			goto link;
8730Sstevel@tonic-gate 		}
8740Sstevel@tonic-gate 		if (getnexttoken(cp, &cp, &p, &t) == 0) {
8750Sstevel@tonic-gate 			goto link;
8760Sstevel@tonic-gate 		}
8770Sstevel@tonic-gate 		mp->mp_owner = strdup(p);
8780Sstevel@tonic-gate 		if (mp->mp_owner == NULL) {
8790Sstevel@tonic-gate 			(*errcb)(MP_ALLOC_ERR, strlen(p)+1);
8800Sstevel@tonic-gate 			devfs_free_minor_perm(mp);
8810Sstevel@tonic-gate 			continue;
8820Sstevel@tonic-gate 		} else if (t == '\n' || t == '\0') {	/* no group */
8830Sstevel@tonic-gate 			goto link;
8840Sstevel@tonic-gate 		}
8850Sstevel@tonic-gate 		if (getnexttoken(cp, &cp, &p, 0) == 0) {
8860Sstevel@tonic-gate 			goto link;
8870Sstevel@tonic-gate 		}
8880Sstevel@tonic-gate 		mp->mp_group = strdup(p);
8890Sstevel@tonic-gate 		if (mp->mp_group == NULL) {
8900Sstevel@tonic-gate 			(*errcb)(MP_ALLOC_ERR, strlen(p)+1);
8910Sstevel@tonic-gate 			devfs_free_minor_perm(mp);
8920Sstevel@tonic-gate 			continue;
8930Sstevel@tonic-gate 		}
8940Sstevel@tonic-gate link:
8950Sstevel@tonic-gate 		if (drvname != NULL) {
8960Sstevel@tonic-gate 			/*
8970Sstevel@tonic-gate 			 * We only want the minor perm entry for a
8980Sstevel@tonic-gate 			 * the named driver.  The driver name is the
8990Sstevel@tonic-gate 			 * minor in the clone case.
9000Sstevel@tonic-gate 			 */
9010Sstevel@tonic-gate 			if (strcmp(mp->mp_drvname, "clone") == 0) {
9020Sstevel@tonic-gate 				if (mp->mp_minorname == NULL ||
9030Sstevel@tonic-gate 				    strcmp(drvname, mp->mp_minorname) != 0) {
9040Sstevel@tonic-gate 					devfs_free_minor_perm(mp);
9050Sstevel@tonic-gate 					continue;
9060Sstevel@tonic-gate 				}
9070Sstevel@tonic-gate 			} else {
9080Sstevel@tonic-gate 				if (strcmp(drvname, mp->mp_drvname) != 0) {
9090Sstevel@tonic-gate 					devfs_free_minor_perm(mp);
9100Sstevel@tonic-gate 					continue;
9110Sstevel@tonic-gate 				}
9120Sstevel@tonic-gate 			}
9130Sstevel@tonic-gate 		}
9140Sstevel@tonic-gate 		if (minor_perms == NULL) {
9150Sstevel@tonic-gate 			minor_perms = mp;
9160Sstevel@tonic-gate 		} else {
9170Sstevel@tonic-gate 			mptail->mp_next = mp;
9180Sstevel@tonic-gate 		}
9190Sstevel@tonic-gate 		mptail = mp;
9200Sstevel@tonic-gate 
9210Sstevel@tonic-gate 		/*
9220Sstevel@tonic-gate 		 * Compute the uid's and gid's here - there are
9230Sstevel@tonic-gate 		 * fewer lines in the /etc/minor_perm file than there
9240Sstevel@tonic-gate 		 * are devices to be stat(2)ed.  And almost every
9250Sstevel@tonic-gate 		 * device is 'root sys'.  See 1135520.
9260Sstevel@tonic-gate 		 */
9270Sstevel@tonic-gate 		if (mp->mp_owner == NULL ||
9280Sstevel@tonic-gate 		    strcmp(mp->mp_owner, DEFAULT_DEV_USER) == 0 ||
9290Sstevel@tonic-gate 		    (pw = getpwnam(mp->mp_owner)) == NULL) {
9300Sstevel@tonic-gate 			mp->mp_uid = root_uid;
9310Sstevel@tonic-gate 		} else {
9320Sstevel@tonic-gate 			mp->mp_uid = pw->pw_uid;
9330Sstevel@tonic-gate 		}
9340Sstevel@tonic-gate 
9350Sstevel@tonic-gate 		if (mp->mp_group == NULL ||
9360Sstevel@tonic-gate 		    strcmp(mp->mp_group, DEFAULT_DEV_GROUP) == 0 ||
9370Sstevel@tonic-gate 		    (gp = getgrnam(mp->mp_group)) == NULL) {
9380Sstevel@tonic-gate 			mp->mp_gid = sys_gid;
9390Sstevel@tonic-gate 		} else {
9400Sstevel@tonic-gate 			mp->mp_gid = gp->gr_gid;
9410Sstevel@tonic-gate 		}
9420Sstevel@tonic-gate 	}
9430Sstevel@tonic-gate 
9440Sstevel@tonic-gate 	if (fclose(pfd) == EOF) {
9450Sstevel@tonic-gate 		(*errcb)(MP_FCLOSE_ERR, errno);
9460Sstevel@tonic-gate 	}
9470Sstevel@tonic-gate 
9480Sstevel@tonic-gate 	return (minor_perms);
9490Sstevel@tonic-gate }
9500Sstevel@tonic-gate 
9510Sstevel@tonic-gate struct mperm *
devfs_read_minor_perm(void (* errcb)(minorperm_err_t,int))9520Sstevel@tonic-gate devfs_read_minor_perm(void (*errcb)(minorperm_err_t, int))
9530Sstevel@tonic-gate {
9540Sstevel@tonic-gate 	return (i_devfs_read_minor_perm(NULL, errcb));
9550Sstevel@tonic-gate }
9560Sstevel@tonic-gate 
9570Sstevel@tonic-gate static struct mperm *
i_devfs_read_minor_perm_by_driver(char * drvname,void (* errcb)(minorperm_err_t mp_err,int key))9580Sstevel@tonic-gate i_devfs_read_minor_perm_by_driver(char *drvname,
9590Sstevel@tonic-gate 	void (*errcb)(minorperm_err_t mp_err, int key))
9600Sstevel@tonic-gate {
9610Sstevel@tonic-gate 	return (i_devfs_read_minor_perm(drvname, errcb));
9620Sstevel@tonic-gate }
9630Sstevel@tonic-gate 
9640Sstevel@tonic-gate /*
9650Sstevel@tonic-gate  * Free mperm list of entries
9660Sstevel@tonic-gate  */
9670Sstevel@tonic-gate void
devfs_free_minor_perm(struct mperm * mplist)9680Sstevel@tonic-gate devfs_free_minor_perm(struct mperm *mplist)
9690Sstevel@tonic-gate {
9700Sstevel@tonic-gate 	struct mperm *mp, *next;
9710Sstevel@tonic-gate 
9720Sstevel@tonic-gate 	for (mp = mplist; mp != NULL; mp = next) {
9730Sstevel@tonic-gate 		next = mp->mp_next;
9740Sstevel@tonic-gate 
9750Sstevel@tonic-gate 		if (mp->mp_drvname)
9760Sstevel@tonic-gate 			free(mp->mp_drvname);
9770Sstevel@tonic-gate 		if (mp->mp_minorname)
9780Sstevel@tonic-gate 			free(mp->mp_minorname);
9790Sstevel@tonic-gate 		if (mp->mp_owner)
9800Sstevel@tonic-gate 			free(mp->mp_owner);
9810Sstevel@tonic-gate 		if (mp->mp_group)
9820Sstevel@tonic-gate 			free(mp->mp_group);
9830Sstevel@tonic-gate 		free(mp);
9840Sstevel@tonic-gate 	}
9850Sstevel@tonic-gate }
9860Sstevel@tonic-gate 
9870Sstevel@tonic-gate static int
i_devfs_add_perm_entry(nvlist_t * nvl,struct mperm * mp)9880Sstevel@tonic-gate i_devfs_add_perm_entry(nvlist_t *nvl, struct mperm *mp)
9890Sstevel@tonic-gate {
9900Sstevel@tonic-gate 	int err;
9910Sstevel@tonic-gate 
9920Sstevel@tonic-gate 	err = nvlist_add_string(nvl, mp->mp_drvname, mp->mp_minorname);
9930Sstevel@tonic-gate 	if (err != 0)
9940Sstevel@tonic-gate 		return (err);
9950Sstevel@tonic-gate 
9960Sstevel@tonic-gate 	err = nvlist_add_int32(nvl, "mode", (int32_t)mp->mp_mode);
9970Sstevel@tonic-gate 	if (err != 0)
9980Sstevel@tonic-gate 		return (err);
9990Sstevel@tonic-gate 
10004601Scasper 	err = nvlist_add_uint32(nvl, "uid", mp->mp_uid);
10010Sstevel@tonic-gate 	if (err != 0)
10020Sstevel@tonic-gate 		return (err);
10030Sstevel@tonic-gate 
10044601Scasper 	err = nvlist_add_uint32(nvl, "gid", mp->mp_gid);
10050Sstevel@tonic-gate 	return (err);
10060Sstevel@tonic-gate }
10070Sstevel@tonic-gate 
10080Sstevel@tonic-gate static nvlist_t *
i_devfs_minor_perm_nvlist(struct mperm * mplist,void (* errcb)(minorperm_err_t,int))10090Sstevel@tonic-gate i_devfs_minor_perm_nvlist(struct mperm *mplist,
10100Sstevel@tonic-gate 	void (*errcb)(minorperm_err_t, int))
10110Sstevel@tonic-gate {
10120Sstevel@tonic-gate 	int err;
10130Sstevel@tonic-gate 	struct mperm *mp;
10140Sstevel@tonic-gate 	nvlist_t *nvl = NULL;
10150Sstevel@tonic-gate 
10160Sstevel@tonic-gate 	if ((err = nvlist_alloc(&nvl, 0, 0)) != 0) {
10170Sstevel@tonic-gate 		(*errcb)(MP_NVLIST_ERR, err);
10180Sstevel@tonic-gate 		return (NULL);
10190Sstevel@tonic-gate 	}
10200Sstevel@tonic-gate 
10210Sstevel@tonic-gate 	for (mp = mplist; mp != NULL; mp = mp->mp_next) {
10220Sstevel@tonic-gate 		if ((err = i_devfs_add_perm_entry(nvl, mp)) != 0) {
10230Sstevel@tonic-gate 			(*errcb)(MP_NVLIST_ERR, err);
10240Sstevel@tonic-gate 			nvlist_free(nvl);
10250Sstevel@tonic-gate 			return (NULL);
10260Sstevel@tonic-gate 		}
10270Sstevel@tonic-gate 	}
10280Sstevel@tonic-gate 
10290Sstevel@tonic-gate 	return (nvl);
10300Sstevel@tonic-gate }
10310Sstevel@tonic-gate 
10320Sstevel@tonic-gate /*
10330Sstevel@tonic-gate  * Load all minor perm entries into the kernel
10340Sstevel@tonic-gate  * Done at boot time via devfsadm
10350Sstevel@tonic-gate  */
10360Sstevel@tonic-gate int
devfs_load_minor_perm(struct mperm * mplist,void (* errcb)(minorperm_err_t,int))10370Sstevel@tonic-gate devfs_load_minor_perm(struct mperm *mplist,
10380Sstevel@tonic-gate 	void (*errcb)(minorperm_err_t, int))
10390Sstevel@tonic-gate {
10400Sstevel@tonic-gate 	int err;
10410Sstevel@tonic-gate 	char *buf = NULL;
10420Sstevel@tonic-gate 	size_t buflen;
10430Sstevel@tonic-gate 	nvlist_t *nvl;
10440Sstevel@tonic-gate 
10450Sstevel@tonic-gate 	nvl = i_devfs_minor_perm_nvlist(mplist, errcb);
10460Sstevel@tonic-gate 	if (nvl == NULL)
10470Sstevel@tonic-gate 		return (-1);
10480Sstevel@tonic-gate 
10490Sstevel@tonic-gate 	if (nvlist_pack(nvl, &buf, &buflen, NV_ENCODE_NATIVE, 0) != 0) {
10500Sstevel@tonic-gate 		nvlist_free(nvl);
10510Sstevel@tonic-gate 		return (-1);
10520Sstevel@tonic-gate 	}
10530Sstevel@tonic-gate 
10540Sstevel@tonic-gate 	err = modctl(MODLOADMINORPERM, buf, buflen);
10550Sstevel@tonic-gate 	nvlist_free(nvl);
10560Sstevel@tonic-gate 	free(buf);
10570Sstevel@tonic-gate 
10580Sstevel@tonic-gate 	return (err);
10590Sstevel@tonic-gate }
10600Sstevel@tonic-gate 
10610Sstevel@tonic-gate /*
10620Sstevel@tonic-gate  * Add/remove minor perm entry for a driver
10630Sstevel@tonic-gate  */
10640Sstevel@tonic-gate static int
i_devfs_update_minor_perm(char * drv,int ctl,void (* errcb)(minorperm_err_t,int))10650Sstevel@tonic-gate i_devfs_update_minor_perm(char *drv, int ctl,
10660Sstevel@tonic-gate 	void (*errcb)(minorperm_err_t, int))
10670Sstevel@tonic-gate {
10680Sstevel@tonic-gate 	int err;
10690Sstevel@tonic-gate 	char *buf;
10700Sstevel@tonic-gate 	size_t buflen;
10710Sstevel@tonic-gate 	nvlist_t *nvl;
10720Sstevel@tonic-gate 	struct mperm *mplist;
10730Sstevel@tonic-gate 
10740Sstevel@tonic-gate 	mplist = i_devfs_read_minor_perm_by_driver(drv, errcb);
10750Sstevel@tonic-gate 
10760Sstevel@tonic-gate 	nvl = i_devfs_minor_perm_nvlist(mplist, errcb);
10770Sstevel@tonic-gate 	if (nvl == NULL)
10780Sstevel@tonic-gate 		return (-1);
10790Sstevel@tonic-gate 
10800Sstevel@tonic-gate 	buf = NULL;
10810Sstevel@tonic-gate 	if (nvlist_pack(nvl, &buf, &buflen, NV_ENCODE_NATIVE, 0) != 0) {
10820Sstevel@tonic-gate 		nvlist_free(nvl);
10830Sstevel@tonic-gate 		return (-1);
10840Sstevel@tonic-gate 	}
10850Sstevel@tonic-gate 
10860Sstevel@tonic-gate 	err = modctl(ctl, buf, buflen);
10870Sstevel@tonic-gate 	nvlist_free(nvl);
10880Sstevel@tonic-gate 	devfs_free_minor_perm(mplist);
10890Sstevel@tonic-gate 	free(buf);
10900Sstevel@tonic-gate 
10910Sstevel@tonic-gate 	return (err);
10920Sstevel@tonic-gate }
10930Sstevel@tonic-gate 
10940Sstevel@tonic-gate int
devfs_add_minor_perm(char * drv,void (* errcb)(minorperm_err_t,int))10950Sstevel@tonic-gate devfs_add_minor_perm(char *drv,
10960Sstevel@tonic-gate 	void (*errcb)(minorperm_err_t, int))
10970Sstevel@tonic-gate {
10980Sstevel@tonic-gate 	return (i_devfs_update_minor_perm(drv, MODADDMINORPERM, errcb));
10990Sstevel@tonic-gate }
11000Sstevel@tonic-gate 
11010Sstevel@tonic-gate int
devfs_rm_minor_perm(char * drv,void (* errcb)(minorperm_err_t,int))11020Sstevel@tonic-gate devfs_rm_minor_perm(char *drv,
11030Sstevel@tonic-gate 	void (*errcb)(minorperm_err_t, int))
11040Sstevel@tonic-gate {
11050Sstevel@tonic-gate 	return (i_devfs_update_minor_perm(drv, MODREMMINORPERM, errcb));
11060Sstevel@tonic-gate }
11072805Seota 
11082805Seota /*
11092805Seota  * is_blank() returns 1 (true) if a line specified is composed of
11102805Seota  * whitespace characters only. otherwise, it returns 0 (false).
11112805Seota  *
11122805Seota  * Note. the argument (line) must be null-terminated.
11132805Seota  */
11142805Seota static int
is_blank(char * line)11152805Seota is_blank(char *line)
11162805Seota {
11172805Seota 	for (/* nothing */; *line != '\0'; line++)
11182805Seota 		if (!isspace(*line))
11192805Seota 			return (0);
11202805Seota 	return (1);
11212805Seota }
1122