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(®ex, 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(®ex, 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(®ex);
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