/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2007 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #pragma ident "%Z%%M% %I% %E% SMI" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define DA_DEFS "/etc/security/tsol/devalloc_defaults" extern int _readbufline(char *, int, char *, int, int *); extern char *strtok_r(char *, const char *, char **); extern char *_strtok_escape(char *, char *, char **); extern int getdaon(void); extern int da_matchname(devalloc_t *, char *); extern int da_match(devalloc_t *, da_args *); extern int dmap_matchname(devmap_t *, char *); extern int dm_match(devmap_t *, da_args *); /* * The following structure is for recording old entries to be retained. * We read the entries from the database into a linked list in memory, * then turn around and write them out again. */ typedef struct strentry { struct strentry *se_next; char se_str[4096 + 1]; } strentry_t; /* * da_check_longindevperm - * reads /etc/logindevperm and checks if specified device is in the file. * returns 1 if specified device found in /etc/logindevperm, else returns 0 */ int da_check_logindevperm(char *devname) { int ret = 0; int fd = -1; int nlen, plen, slen, lineno, fsize; char line[MAX_CANON]; char *field_delims = " \t\n"; char *fbuf = NULL; char *ptr, *device; char *lasts = NULL; FILE *fp; struct stat f_stat; /* * check if /etc/logindevperm exists and get its size */ if ((fd = open(LOGINDEVPERM, O_RDONLY)) == -1) return (0); if (fstat(fd, &f_stat) != 0) { (void) close(fd); return (0); } fsize = f_stat.st_size; if ((fbuf = (char *)malloc(fsize)) == NULL) { (void) close(fd); return (0); } if ((fp = fdopen(fd, "rF")) == NULL) { free(fbuf); (void) close(fd); return (0); } /* * read and parse /etc/logindevperm */ plen = nlen = lineno = 0; while (fgets(line, MAX_CANON, fp) != NULL) { lineno++; if ((ptr = strchr(line, '#')) != NULL) *ptr = '\0'; /* handle comments */ if (strtok_r(line, field_delims, &lasts) == NULL) continue; /* ignore blank lines */ if (strtok_r(NULL, field_delims, &lasts) == NULL) /* invalid entry */ continue; if ((ptr = strtok_r(NULL, field_delims, &lasts)) == NULL) /* empty device list */ continue; nlen = strlen(ptr) + 1; /* +1 terminator */ nlen += (plen + 1); if (plen == 0) slen = snprintf(fbuf, nlen, "%s", ptr); else slen = snprintf(fbuf + plen, nlen - plen, ":%s", ptr); if (slen >= fsize) { fbuf[0] = '\0'; (void) fclose(fp); return (slen); } plen += slen; } (void) fclose(fp); /* * check if devname exists in /etc/logindevperm */ device = strtok_r(fbuf, ":", &lasts); while (device != NULL) { /* * device and devname may be one of these types - * /dev/xx * /dev/xx* * /dev/dir/xx * /dev/dir/xx* * /dev/dir/"*" */ if (strcmp(device, devname) == 0) { /* /dev/xx, /dev/dir/xx */ free(fbuf); return (1); } if ((ptr = strrchr(device, KV_WILDCHAR)) != NULL) { /* all wildcard types */ *ptr = '\0'; if (strncmp(device, devname, strlen(device)) == 0) { free(fbuf); return (1); } } device = strtok_r(NULL, ":", &lasts); } return (ret); } /* * _da_read_file - * establishes readers/writer lock on fname; reads in the file if its * contents changed since the last time we read it. * returns size of buffer read, or -1 on failure. */ int _da_read_file(char *fname, char **fbuf, time_t *ftime, rwlock_t *flock, int flag) { int fd = -1; int fsize = 0; time_t newtime; struct stat f_stat; if (flag & DA_FORCE) *ftime = 0; /* check the size and the time stamp on the file */ if (rw_rdlock(flock) != 0) return (-1); if (stat(fname, &f_stat) != 0) { (void) rw_unlock(flock); return (-1); } fsize = f_stat.st_size; newtime = f_stat.st_mtime; (void) rw_unlock(flock); while (newtime > *ftime) { /* * file has been modified since we last read it; or this * is a forced read. * read file into the buffer with rw lock. */ if (rw_wrlock(flock) != 0) return (-1); if ((fd = open(fname, O_RDONLY)) == -1) { (void) rw_unlock(flock); return (-1); } if (*fbuf != NULL) { free(*fbuf); *fbuf = NULL; } if ((*fbuf = malloc(fsize)) == NULL) { (void) rw_unlock(flock); (void) close(fd); return (-1); } if (read(fd, *fbuf, fsize) < fsize) { free(*fbuf); (void) rw_unlock(flock); (void) close(fd); return (-1); } (void) rw_unlock(flock); /* * verify that the file did not change just after we read it. */ if (rw_rdlock(flock) != 0) { free(*fbuf); (void) close(fd); return (-1); } if (stat(fname, &f_stat) != 0) { free(*fbuf); (void) rw_unlock(flock); (void) close(fd); return (-1); } fsize = f_stat.st_size; newtime = f_stat.st_mtime; (void) rw_unlock(flock); (void) close(fd); *ftime = newtime; } return (fsize); } /* * _update_zonename - * add/remove current zone's name to the given devalloc_t. */ void _update_zonename(da_args *dargs, devalloc_t *dap) { int i, j; int oldsize, newsize; int has_zonename = 0; char *zonename; kva_t *newkva, *oldkva; kv_t *newdata, *olddata; devinfo_t *devinfo; devinfo = dargs->devinfo; oldkva = dap->da_devopts; if (oldkva == NULL) { if (dargs->optflag & DA_REMOVE_ZONE) return; if (dargs->optflag & DA_ADD_ZONE) { newkva = _str2kva(devinfo->devopts, KV_ASSIGN, KV_TOKEN_DELIMIT); if (newkva != NULL) dap->da_devopts = newkva; return; } } newsize = oldsize = oldkva->length; if (kva_match(oldkva, DAOPT_ZONE)) has_zonename = 1; if (dargs->optflag & DA_ADD_ZONE) { if ((zonename = index(devinfo->devopts, '=')) == NULL) return; zonename++; if (has_zonename) { (void) _insert2kva(oldkva, DAOPT_ZONE, zonename); return; } newsize += 1; } else if (dargs->optflag & DA_REMOVE_ZONE) { if (has_zonename) { newsize -= 1; if (newsize == 0) { /* * If zone name was the only key/value pair, * put 'reserved' in the empty slot. */ _kva_free(oldkva); dap->da_devopts = NULL; return; } } else { return; } } newkva = _new_kva(newsize); newkva->length = 0; newdata = newkva->data; olddata = oldkva->data; for (i = 0, j = 0; i < oldsize; i++) { if ((dargs->optflag & DA_REMOVE_ZONE) && (strcmp(olddata[i].key, DAOPT_ZONE) == 0)) continue; newdata[j].key = strdup(olddata[i].key); newdata[j].value = strdup(olddata[i].value); newkva->length++; j++; } if (dargs->optflag & DA_ADD_ZONE) { newdata[j].key = strdup(DAOPT_ZONE); newdata[j].value = strdup(zonename); newkva->length++; } _kva_free(oldkva); dap->da_devopts = newkva; } /* * _dmap2str - * converts a device_map entry into a printable string * returns 0 on success, -1 on error. */ /*ARGSUSED*/ static int _dmap2str(da_args *dargs, devmap_t *dmp, char *buf, int size, const char *sep) { int length; length = snprintf(buf, size, "%s%s", dmp->dmap_devname, sep); if (length >= size) return (-1); length += snprintf(buf + length, size - length, "%s%s", dmp->dmap_devtype, sep); if (length >= size) return (-1); length += snprintf(buf + length, size - length, "%s\n", dmp->dmap_devlist); if (length >= size) return (-1); return (0); } /* * _dmap2strentry - * calls dmap2str to break given devmap_t into printable entry. * returns pointer to decoded entry, NULL on error. */ static strentry_t * _dmap2strentry(da_args *dargs, devmap_t *devmapp) { strentry_t *sep; if ((sep = (strentry_t *)malloc(sizeof (strentry_t))) == NULL) return (NULL); if (_dmap2str(dargs, devmapp, sep->se_str, sizeof (sep->se_str), KV_TOKEN_DELIMIT"\\\n\t") != 0) { free(sep); return (NULL); } return (sep); } /* * fix_optstr - * removes trailing ':' from buf. */ void fix_optstr(char *buf) { char *p = NULL; if (p = rindex(buf, ':')) *p = ';'; } /* * _da2str - * converts a device_allocate entry into a printable string * returns 0 on success, -1 on error. */ static int _da2str(da_args *dargs, devalloc_t *dap, char *buf, int size, const char *sep, const char *osep) { int length; int matching_entry = 0; char **dnames; if (dargs->optflag & DA_UPDATE && (dargs->optflag & DA_ADD_ZONE || dargs->optflag & DA_REMOVE_ZONE) && dargs->devnames) { for (dnames = dargs->devnames; *dnames != NULL; dnames++) { if (da_matchname(dap, *dnames)) { matching_entry = 1; break; } } } length = snprintf(buf, size, "%s%s", dap->da_devname, sep); if (length >= size) return (-1); length += snprintf(buf + length, size - length, "%s%s", dap->da_devtype, sep); if (length >= size) return (-1); if (matching_entry) _update_zonename(dargs, dap); if ((dap->da_devopts == NULL) || ((dap->da_devopts->length == 1) && (strcmp(dap->da_devopts->data->key, DA_RESERVED) == 0))) { length += snprintf(buf + length, size - length, "%s%s", DA_RESERVED, sep); } else { if (_kva2str(dap->da_devopts, buf + length, size - length, KV_ASSIGN, (char *)osep) != 0) return (-1); length = strlen(buf); } if (dap->da_devopts) fix_optstr(buf); if (length >= size) return (-1); length += snprintf(buf + length, size - length, "%s%s", DA_RESERVED, sep); if (length >= size) return (-1); length += snprintf(buf + length, size - length, "%s%s", dap->da_devauth ? dap->da_devauth : DA_ANYUSER, sep); if (length >= size) return (-1); length += snprintf(buf + length, size - length, "%s\n", dap->da_devexec ? dap->da_devexec : ""); if (length >= size) return (-1); return (0); } /* * _da2strentry - * calls da2str to break given devalloc_t into printable entry. * returns pointer to decoded entry, NULL on error. */ static strentry_t * _da2strentry(da_args *dargs, devalloc_t *dap) { strentry_t *sep; if ((sep = (strentry_t *)malloc(sizeof (strentry_t))) == NULL) return (NULL); if (_da2str(dargs, dap, sep->se_str, sizeof (sep->se_str), KV_DELIMITER "\\\n\t", KV_TOKEN_DELIMIT "\\\n\t") != 0) { free(sep); return (NULL); } return (sep); } /* * _def2str * converts da_defs_t into a printable string. * returns 0 on success, -1 on error. */ static int _def2str(da_defs_t *da_defs, char *buf, int size, const char *sep) { int length; length = snprintf(buf, size, "%s%s", da_defs->devtype, sep); if (length >= size) return (-1); if (da_defs->devopts) { if (_kva2str(da_defs->devopts, buf + length, size - length, KV_ASSIGN, KV_DELIMITER) != 0) return (-1); length = strlen(buf); } if (length >= size) return (-1); return (0); } /* * _def2strentry * calls _def2str to break given da_defs_t into printable entry. * returns pointer decoded entry, NULL on error. */ static strentry_t * _def2strentry(da_defs_t *da_defs) { strentry_t *sep; if ((sep = (strentry_t *)malloc(sizeof (strentry_t))) == NULL) return (NULL); if (_def2str(da_defs, sep->se_str, sizeof (sep->se_str), KV_TOKEN_DELIMIT) != 0) { free(sep); return (NULL); } return (sep); } /* * _build_defattrs * cycles through all defattr entries, stores them in memory. removes * entries with the given search_key (device type). * returns 0 if given entry not found, 1 if given entry removed, 2 on * error. */ static int _build_defattrs(da_args *dargs, strentry_t **head_defent) { int rc = 0; da_defs_t *da_defs; strentry_t *tail_str, *tmp_str; setdadefent(); while ((da_defs = getdadefent()) != NULL) { rc = !(strcmp(da_defs->devtype, dargs->devinfo->devtype)); if (rc && dargs->optflag & DA_ADD && !(dargs->optflag & DA_FORCE)) { /* * During DA_ADD, we keep an existing entry unless * we have DA_FORCE set to override that entry. */ dargs->optflag |= DA_NO_OVERRIDE; rc = 0; } if (rc == 0) { tmp_str = _def2strentry(da_defs); if (tmp_str == NULL) { freedadefent(da_defs); enddadefent(); return (2); } /* retaining defattr entry: tmp_str->se_str */ tmp_str->se_next = NULL; if (*head_defent == NULL) { *head_defent = tail_str = tmp_str; } else { tail_str->se_next = tmp_str; tail_str = tmp_str; } } freedadefent(da_defs); } enddadefent(); return (rc); } /* * _build_lists - * cycles through all the entries, stores them in memory. removes entries * with the given search_key (device name or type). * returns 0 if given entry not found, 1 if given entry removed, 2 on * error. */ static int _build_lists(da_args *dargs, strentry_t **head_devallocp, strentry_t **head_devmapp) { int rc = 0; devalloc_t *devallocp; devmap_t *devmapp; strentry_t *tail_str; strentry_t *tmp_str; if (dargs->optflag & DA_MAPS_ONLY) goto dmap_only; /* build device_allocate */ setdaent(); while ((devallocp = getdaent()) != NULL) { rc = da_match(devallocp, dargs); if (rc && dargs->optflag & DA_ADD && !(dargs->optflag & DA_FORCE)) { /* * During DA_ADD, we keep an existing entry unless * we have DA_FORCE set to override that entry. */ dargs->optflag |= DA_NO_OVERRIDE; rc = 0; } if (rc == 0) { tmp_str = _da2strentry(dargs, devallocp); if (tmp_str == NULL) { freedaent(devallocp); enddaent(); return (2); } /* retaining devalloc entry: tmp_str->se_str */ tmp_str->se_next = NULL; if (*head_devallocp == NULL) { *head_devallocp = tail_str = tmp_str; } else { tail_str->se_next = tmp_str; tail_str = tmp_str; } } freedaent(devallocp); } enddaent(); dmap_only: if (dargs->optflag & DA_ALLOC_ONLY) return (rc); /* build device_maps */ rc = 0; setdmapent(); while ((devmapp = getdmapent()) != NULL) { rc = dm_match(devmapp, dargs); if (rc && dargs->optflag & DA_ADD && !(dargs->optflag & DA_FORCE)) { /* * During DA_ADD, we keep an existing entry unless * we have DA_FORCE set to override that entry. */ dargs->optflag |= DA_NO_OVERRIDE; rc = 0; } if (rc == 0) { tmp_str = _dmap2strentry(dargs, devmapp); if (tmp_str == NULL) { freedmapent(devmapp); enddmapent(); return (2); } /* retaining devmap entry: tmp_str->se_str */ tmp_str->se_next = NULL; if (*head_devmapp == NULL) { *head_devmapp = tail_str = tmp_str; } else { tail_str->se_next = tmp_str; tail_str = tmp_str; } } freedmapent(devmapp); } enddmapent(); return (rc); } /* * _write_defattrs * writes current entries to devalloc_defaults. */ static void _write_defattrs(FILE *fp, strentry_t *head_defent) { strentry_t *tmp_str; for (tmp_str = head_defent; tmp_str != NULL; tmp_str = tmp_str->se_next) { (void) fputs(tmp_str->se_str, fp); (void) fputs("\n", fp); } } /* * _write_device_allocate - * writes current entries in the list to device_allocate. */ static void _write_device_allocate(char *odevalloc, FILE *dafp, strentry_t *head_devallocp) { int is_on = -1; strentry_t *tmp_str; struct stat dastat; (void) fseek(dafp, (off_t)0, SEEK_SET); /* * if the devalloc on/off string existed before, * put it back before anything else. * we need to check for the string only if the file * exists. */ if (stat(odevalloc, &dastat) == 0) { is_on = da_is_on(); if (is_on == 0) (void) fputs(DA_OFF_STR, dafp); else if (is_on == 1) (void) fputs(DA_ON_STR, dafp); } tmp_str = head_devallocp; while (tmp_str) { (void) fputs(tmp_str->se_str, dafp); (void) fputs("\n", dafp); tmp_str = tmp_str->se_next; } } /* * _write_device_maps - * writes current entries in the list to device_maps. */ static void _write_device_maps(FILE *dmfp, strentry_t *head_devmapp) { strentry_t *tmp_str; (void) fseek(dmfp, (off_t)0, SEEK_SET); tmp_str = head_devmapp; while (tmp_str) { (void) fputs(tmp_str->se_str, dmfp); (void) fputs("\n", dmfp); tmp_str = tmp_str->se_next; } } /* * _write_new_defattrs * writes the new entry to devalloc_defaults. * returns 0 on success, -1 on error. */ static int _write_new_defattrs(FILE *fp, da_args *dargs) { int count; char *tok = NULL, *tokp = NULL; char *lasts; devinfo_t *devinfo = dargs->devinfo; if (fseek(fp, (off_t)0, SEEK_END) == (off_t)-1) return (-1); if (!devinfo->devopts) return (0); (void) fprintf(fp, "%s%s", (devinfo->devtype ? devinfo->devtype : ""), KV_TOKEN_DELIMIT); if ((tokp = (char *)malloc(strlen(devinfo->devopts))) != NULL) { (void) strcpy(tokp, devinfo->devopts); if ((tok = strtok_r(tokp, KV_DELIMITER, &lasts)) != NULL) { (void) fprintf(fp, "%s", tok); count = 1; } while ((tok = strtok_r(NULL, KV_DELIMITER, &lasts)) != NULL) { if (count) (void) fprintf(fp, "%s", KV_DELIMITER); (void) fprintf(fp, "%s", tok); count++; } } else { (void) fprintf(fp, "%s", devinfo->devopts); } return (0); } /* * _write_new_entry - * writes the new devalloc_t to device_allocate or the new devmap_t to * device_maps. * returns 0 on success, -1 on error. */ static int _write_new_entry(FILE *fp, da_args *dargs, int flag) { int count; char *tok = NULL, *tokp = NULL; char *lasts; devinfo_t *devinfo = dargs->devinfo; if (flag & DA_MAPS_ONLY) goto dmap_only; if (fseek(fp, (off_t)0, SEEK_END) == (off_t)-1) return (-1); (void) fprintf(fp, "%s%s\\\n\t", (devinfo->devname ? devinfo->devname : ""), KV_DELIMITER); (void) fprintf(fp, "%s%s\\\n\t", (devinfo->devtype ? devinfo->devtype : ""), KV_DELIMITER); if (devinfo->devopts == NULL) { (void) fprintf(fp, "%s%s\\\n\t", DA_RESERVED, KV_DELIMITER); } else { if ((tokp = (char *)malloc(strlen(devinfo->devopts))) != NULL) { (void) strcpy(tokp, devinfo->devopts); if ((tok = strtok_r(tokp, KV_TOKEN_DELIMIT, &lasts)) != NULL) { (void) fprintf(fp, "%s", tok); count = 1; } while ((tok = strtok_r(NULL, KV_TOKEN_DELIMIT, &lasts)) != NULL) { if (count) (void) fprintf(fp, "%s", KV_TOKEN_DELIMIT "\\\n\t"); (void) fprintf(fp, "%s", tok); count++; } if (count) (void) fprintf(fp, "%s", KV_DELIMITER "\\\n\t"); } else { (void) fprintf(fp, "%s%s", devinfo->devopts, KV_DELIMITER "\\\n\t"); } } (void) fprintf(fp, "%s%s\\\n\t", DA_RESERVED, KV_DELIMITER); (void) fprintf(fp, "%s%s\\\n\t", (devinfo->devauths ? devinfo->devauths : DA_ANYUSER), KV_DELIMITER); (void) fprintf(fp, "%s\n", (devinfo->devexec ? devinfo->devexec : KV_DELIMITER)); dmap_only: if (flag & DA_ALLOC_ONLY) return (0); if (fseek(fp, (off_t)0, SEEK_END) == (off_t)-1) return (-1); (void) fprintf(fp, "%s%s\\\n", (devinfo->devname ? devinfo->devname : ""), KV_TOKEN_DELIMIT); (void) fprintf(fp, "\t%s%s\\\n", (devinfo->devtype ? devinfo->devtype : ""), KV_TOKEN_DELIMIT); (void) fprintf(fp, "\t%s\n", (devinfo->devlist ? devinfo->devlist : KV_TOKEN_DELIMIT)); return (0); } /* * _da_lock_devdb - * locks the database files; lock can be either broken explicitly by * closing the fd of the lock file, or it expires automatically at process * termination. * returns fd of the lock file or -1 on error. */ int _da_lock_devdb(char *rootdir) { int lockfd = -1; int ret; int count = 0; int retry = 10; int retry_sleep; uint_t seed; char *lockfile; char path[MAXPATHLEN]; int size = sizeof (path); if (rootdir == NULL) { lockfile = DA_DB_LOCK; } else { path[0] = '\0'; if (snprintf(path, size, "%s%s", rootdir, DA_DB_LOCK) >= size) return (-1); lockfile = path; } if ((lockfd = open(lockfile, O_RDWR | O_CREAT, 0600)) == -1) /* cannot open lock file */ return (-1); (void) fchown(lockfd, DA_UID, DA_GID); if (lseek(lockfd, (off_t)0, SEEK_SET) == -1) { /* cannot position lock file */ (void) close(lockfd); return (-1); } errno = 0; while (retry > 0) { count++; seed = (uint_t)gethrtime(); ret = lockf(lockfd, F_TLOCK, 0); if (ret == 0) { (void) utime(lockfile, NULL); return (lockfd); } if ((errno != EACCES) && (errno != EAGAIN)) { /* cannot set lock */ (void) close(lockfd); return (-1); } retry--; retry_sleep = rand_r(&seed)/((RAND_MAX + 2)/3) + count; (void) sleep(retry_sleep); errno = 0; } return (-1); } /* * da_open_devdb - * opens one or both database files - device_allocate, device_maps - in * the specified mode. * locks the database files; lock is either broken explicitly by the * caller by closing the lock file fd, or it expires automatically at * process termination. * writes the file pointer of opened file in the input args - dafp, dmfp. * returns fd of the lock file on success, -2 if database file does not * exist, -1 on other errors. */ int da_open_devdb(char *rootdir, FILE **dafp, FILE **dmfp, int flag) { int oflag = 0; int fda = -1; int fdm = -1; int lockfd = -1; char *fname; char *fmode; char path[MAXPATHLEN]; FILE *devfile; if ((dafp == NULL) && (dmfp == NULL)) return (-1); if (flag & DA_RDWR) { oflag = DA_RDWR; fmode = "r+F"; } else if (flag & DA_RDONLY) { oflag = DA_RDONLY; fmode = "rF"; } if ((lockfd = _da_lock_devdb(rootdir)) == -1) return (-1); if ((dafp == NULL) || (flag & DA_MAPS_ONLY)) goto dmap_only; path[0] = '\0'; /* * open the device allocation file */ if (rootdir == NULL) { fname = DEVALLOC; } else { if (snprintf(path, sizeof (path), "%s%s", rootdir, DEVALLOC) >= sizeof (path)) { if (lockfd != -1) (void) close(lockfd); return (-1); } fname = path; } if ((fda = open(fname, oflag, DA_DBMODE)) == -1) { if (lockfd != -1) (void) close(lockfd); return ((errno == ENOENT) ? -2 : -1); } if ((devfile = fdopen(fda, fmode)) == NULL) { (void) close(fda); if (lockfd != -1) (void) close(lockfd); return (-1); } *dafp = devfile; (void) fchmod(fda, DA_DBMODE); if ((flag & DA_ALLOC_ONLY)) goto out; dmap_only: path[0] = '\0'; /* * open the device map file */ if (rootdir == NULL) { fname = DEVMAP; } else { if (snprintf(path, sizeof (path), "%s%s", rootdir, DEVMAP) >= sizeof (path)) { (void) close(fda); if (lockfd != -1) (void) close(lockfd); return (-1); } fname = path; } if ((fdm = open(fname, oflag, DA_DBMODE)) == -1) { if (lockfd != -1) (void) close(lockfd); return ((errno == ENOENT) ? -2 : -1); } if ((devfile = fdopen(fdm, fmode)) == NULL) { (void) close(fdm); (void) close(fda); if (lockfd != -1) (void) close(lockfd); return (-1); } *dmfp = devfile; (void) fchmod(fdm, DA_DBMODE); out: return (lockfd); } /* * _record_on_off - * adds either DA_ON_STR or DA_OFF_STR to device_allocate * returns 0 on success, -1 on error. */ static int _record_on_off(da_args *dargs, FILE *tafp, FILE *dafp) { int dafd; int nsize; int nitems = 1; int actionlen; int str_found = 0; int len = 0, nlen = 0, plen = 0; char *ptr = NULL; char *actionstr; char *nbuf = NULL; char line[MAX_CANON]; struct stat dastat; if (dargs->optflag & DA_ON) actionstr = DA_ON_STR; else actionstr = DA_OFF_STR; actionlen = strlen(actionstr); dafd = fileno(dafp); if (fstat(dafd, &dastat) == -1) return (-1); /* check the old device_allocate for on/off string */ ptr = fgets(line, MAX_CANON, dafp); if (ptr != NULL) { if ((strcmp(line, DA_ON_STR) == 0) || (strcmp(line, DA_OFF_STR) == 0)) { str_found = 1; nsize = dastat.st_size; } } if (!ptr || !str_found) { /* * the file never had either the on or the off string; * make room for it. */ str_found = 0; nsize = dastat.st_size + actionlen + 1; } if ((nbuf = (char *)malloc(nsize)) == NULL) return (-1); nbuf[0] = '\0'; /* put the on/off string */ (void) strcpy(nbuf, actionstr); nlen = strlen(nbuf); plen = nlen; if (ptr && !str_found) { /* now put the first line that we read in fgets */ nlen = plen + strlen(line) + 1; len = snprintf(nbuf + plen, nlen - plen, "%s", line); if (len >= nsize) { free(nbuf); return (-1); } plen += len; } /* now get the rest of the old file */ while (fgets(line, MAX_CANON, dafp) != NULL) { nlen = plen + strlen(line) + 1; len = snprintf(nbuf + plen, nlen - plen, "%s", line); if (len >= nsize) { free(nbuf); return (-1); } plen += len; } len = strlen(nbuf) + 1; if (len < nsize) nbuf[len] = '\n'; /* write the on/off str + the old device_allocate to the temp file */ if (fwrite(nbuf, nsize, nitems, tafp) < nitems) { free(nbuf); return (-1); } free(nbuf); return (0); } /* * da_update_defattrs - * writes default attributes to devalloc_defaults * returns 0 on success, -1 on error. */ int da_update_defattrs(da_args *dargs) { int rc = 0, lockfd = 0, tmpfd = 0; char *defpath = DEFATTRS; char *tmpdefpath = TMPATTRS; FILE *tmpfp = NULL; struct stat dstat; strentry_t *head_defent = NULL; if (dargs == NULL) return (0); if ((lockfd = _da_lock_devdb(NULL)) == -1) return (-1); if ((tmpfd = open(tmpdefpath, O_RDWR|O_CREAT, DA_DBMODE)) == -1) { (void) close(lockfd); return (-1); } (void) fchown(tmpfd, DA_UID, DA_GID); if ((tmpfp = fdopen(tmpfd, "r+")) == NULL) { (void) close(tmpfd); (void) unlink(tmpdefpath); (void) close(lockfd); return (-1); } /* * examine all entries, remove an old one if required, check * if a new one needs to be added. */ if (stat(defpath, &dstat) == 0) { if ((rc = _build_defattrs(dargs, &head_defent)) != 0) { if (rc == 1) { (void) close(tmpfd); (void) unlink(tmpdefpath); (void) close(lockfd); return (rc); } } } /* * write back any existing entries. */ _write_defattrs(tmpfp, head_defent); if (dargs->optflag & DA_ADD && !(dargs->optflag & DA_NO_OVERRIDE)) { /* add new entries */ rc = _write_new_defattrs(tmpfp, dargs); (void) fclose(tmpfp); } else { (void) fclose(tmpfp); } if (rename(tmpdefpath, defpath) != 0) { rc = -1; (void) unlink(tmpdefpath); } (void) close(lockfd); return (rc); } /* * da_update_device - * writes devices entries to device_allocate and device_maps. * returns 0 on success, -1 on error. */ int da_update_device(da_args *dargs) { int rc; int tafd = -1, tmfd = -1; int lockfd = -1; char *rootdir = NULL; char *apathp = NULL, *mpathp = NULL; char *dapathp = NULL, *dmpathp = NULL; char apath[MAXPATHLEN], mpath[MAXPATHLEN]; char dapath[MAXPATHLEN], dmpath[MAXPATHLEN]; FILE *tafp = NULL, *tmfp = NULL, *dafp = NULL; struct stat dastat; devinfo_t *devinfo; strentry_t *head_devmapp = NULL; strentry_t *head_devallocp = NULL; if (dargs == NULL) return (0); rootdir = dargs->rootdir; devinfo = dargs->devinfo; /* * adding/removing entries should be done in both * device_allocate and device_maps. updates can be * done in both or either of the files. */ if (dargs->optflag & DA_ADD || dargs->optflag & DA_REMOVE) { if (dargs->optflag & DA_ALLOC_ONLY || dargs->optflag & DA_MAPS_ONLY) return (0); } /* * name, type and list are required fields for adding a new * device. */ if ((dargs->optflag & DA_ADD) && ((devinfo->devname == NULL) || (devinfo->devtype == NULL) || (devinfo->devlist == NULL))) { return (-1); } if (rootdir != NULL) { if (snprintf(apath, sizeof (apath), "%s%s", rootdir, TMPALLOC) >= sizeof (apath)) return (-1); apathp = apath; if (snprintf(dapath, sizeof (dapath), "%s%s", rootdir, DEVALLOC) >= sizeof (dapath)) return (-1); dapathp = dapath; if (!(dargs->optflag & DA_ALLOC_ONLY)) { if (snprintf(mpath, sizeof (mpath), "%s%s", rootdir, TMPMAP) >= sizeof (mpath)) return (-1); mpathp = mpath; if (snprintf(dmpath, sizeof (dmpath), "%s%s", rootdir, DEVMAP) >= sizeof (dmpath)) return (-1); dmpathp = dmpath; } } else { apathp = TMPALLOC; dapathp = DEVALLOC; mpathp = TMPMAP; dmpathp = DEVMAP; } if (dargs->optflag & DA_MAPS_ONLY) goto dmap_only; /* * Check if we are here just to record on/off status of * device_allocation. */ if (dargs->optflag & DA_ON || dargs->optflag & DA_OFF) lockfd = da_open_devdb(dargs->rootdir, &dafp, NULL, DA_RDONLY|DA_ALLOC_ONLY); else lockfd = _da_lock_devdb(rootdir); if (lockfd == -1) return (-1); if ((tafd = open(apathp, O_RDWR|O_CREAT, DA_DBMODE)) == -1) { (void) close(lockfd); (void) fclose(dafp); return (-1); } (void) fchown(tafd, DA_UID, DA_GID); if ((tafp = fdopen(tafd, "r+")) == NULL) { (void) close(tafd); (void) unlink(apathp); (void) fclose(dafp); (void) close(lockfd); return (-1); } /* * We don't need to parse the file if we are here just to record * on/off status of device_allocation. */ if (dargs->optflag & DA_ON || dargs->optflag & DA_OFF) { if (_record_on_off(dargs, tafp, dafp) == -1) { (void) close(tafd); (void) unlink(apathp); (void) fclose(dafp); (void) close(lockfd); return (-1); } (void) fclose(dafp); goto out; } /* * examine all the entries, remove an old one if forced to, * and check that they are suitable for updating. * we need to do this only if the file exists already. */ if (stat(dapathp, &dastat) == 0) { if ((rc = _build_lists(dargs, &head_devallocp, &head_devmapp)) != 0) { if (rc != 1) { (void) close(tafd); (void) unlink(apathp); (void) close(lockfd); return (rc); } } } /* * write back any existing devalloc entries, along with * the devalloc on/off string. */ _write_device_allocate(dapathp, tafp, head_devallocp); if (dargs->optflag & DA_ALLOC_ONLY) goto out; dmap_only: if ((tmfd = open(mpathp, O_RDWR|O_CREAT, DA_DBMODE)) == -1) { (void) close(tafd); (void) unlink(apathp); (void) close(lockfd); return (-1); } (void) fchown(tmfd, DA_UID, DA_GID); if ((tmfp = fdopen(tmfd, "r+")) == NULL) { (void) close(tafd); (void) unlink(apathp); (void) close(tmfd); (void) unlink(mpathp); (void) close(lockfd); return (-1); } /* write back any existing devmap entries */ if (head_devmapp != NULL) _write_device_maps(tmfp, head_devmapp); out: if (dargs->optflag & DA_ADD && !(dargs->optflag & DA_NO_OVERRIDE)) { /* add any new entries */ rc = _write_new_entry(tafp, dargs, DA_ALLOC_ONLY); (void) fclose(tafp); if (rc == 0) rc = _write_new_entry(tmfp, dargs, DA_MAPS_ONLY); (void) fclose(tmfp); } else { if (tafp) (void) fclose(tafp); if (tmfp) (void) fclose(tmfp); } rc = 0; if (!(dargs->optflag & DA_MAPS_ONLY)) { if (rename(apathp, dapathp) != 0) { rc = -1; (void) unlink(apathp); } } if (!(dargs->optflag & DA_ALLOC_ONLY)) { if (rename(mpathp, dmpathp) != 0) { rc = -1; (void) unlink(mpathp); } } (void) close(lockfd); return (rc); } /* * da_add_list - * adds new /dev link name to the linked list of devices. * returns 0 if link added successfully, -1 on error. */ int da_add_list(devlist_t *dlist, char *link, int new_instance, int flag) { int instance; int nlen, plen; int new_entry = 0; char *dtype, *dexec, *tname, *kval; char *minstr = NULL, *maxstr = NULL; char dname[DA_MAXNAME]; kva_t *kva; deventry_t *dentry = NULL, *nentry = NULL, *pentry = NULL; da_defs_t *da_defs; if (dlist == NULL || link == NULL) return (-1); dname[0] = '\0'; if (flag & DA_AUDIO) { dentry = dlist->audio; tname = DA_AUDIO_NAME; dtype = DA_AUDIO_TYPE; dexec = DA_DEFAULT_AUDIO_CLEAN; } else if (flag & DA_CD) { dentry = dlist->cd; tname = DA_CD_NAME; dtype = DA_CD_TYPE; dexec = DA_DEFAULT_DISK_CLEAN; } else if (flag & DA_FLOPPY) { dentry = dlist->floppy; tname = DA_FLOPPY_NAME; dtype = DA_FLOPPY_TYPE; dexec = DA_DEFAULT_DISK_CLEAN; } else if (flag & DA_TAPE) { dentry = dlist->tape; tname = DA_TAPE_NAME; dtype = DA_TAPE_TYPE; dexec = DA_DEFAULT_TAPE_CLEAN; } else if (flag & DA_RMDISK) { dentry = dlist->rmdisk; tname = DA_RMDISK_NAME; dtype = DA_RMDISK_TYPE; dexec = DA_DEFAULT_DISK_CLEAN; } else { return (-1); } for (nentry = dentry; nentry != NULL; nentry = nentry->next) { pentry = nentry; (void) sscanf(nentry->devinfo.devname, "%*[a-z]%d", &instance); if (nentry->devinfo.instance == new_instance) /* * Add the new link name to the list of links * that the device 'dname' has. */ break; } if (nentry == NULL) { /* * Either this is the first entry ever, or no matching entry * was found. Create a new one and add to the list. */ if (dentry == NULL) /* first entry ever */ instance = 0; else /* no matching entry */ instance++; (void) snprintf(dname, sizeof (dname), "%s%d", tname, instance); if ((nentry = (deventry_t *)malloc(sizeof (deventry_t))) == NULL) return (-1); if (pentry != NULL) pentry->next = nentry; new_entry = 1; nentry->devinfo.devname = strdup(dname); nentry->devinfo.devtype = dtype; nentry->devinfo.devauths = DEFAULT_DEV_ALLOC_AUTH; nentry->devinfo.devexec = dexec; nentry->devinfo.instance = new_instance; /* * Look for default label range, authorizations and cleaning * program in devalloc_defaults. If label range is not * specified in devalloc_defaults, assume it to be admin_low * to admin_high. */ minstr = DA_DEFAULT_MIN; maxstr = DA_DEFAULT_MAX; setdadefent(); if (da_defs = getdadeftype(nentry->devinfo.devtype)) { kva = da_defs->devopts; if ((kval = kva_match(kva, DAOPT_MINLABEL)) != NULL) minstr = strdup(kval); if ((kval = kva_match(kva, DAOPT_MAXLABEL)) != NULL) maxstr = strdup(kval); if ((kval = kva_match(kva, DAOPT_AUTHS)) != NULL) nentry->devinfo.devauths = strdup(kval); if ((kval = kva_match(kva, DAOPT_CSCRIPT)) != NULL) nentry->devinfo.devexec = strdup(kval); freedadefent(da_defs); } enddadefent(); kval = NULL; nlen = strlen(DAOPT_MINLABEL) + strlen(KV_ASSIGN) + strlen(minstr) + strlen(KV_TOKEN_DELIMIT) + strlen(DAOPT_MAXLABEL) + strlen(KV_ASSIGN) + strlen(maxstr) + 1; /* +1 for terminator */ if (kval = (char *)malloc(nlen)) (void) snprintf(kval, nlen, "%s%s%s%s%s%s%s", DAOPT_MINLABEL, KV_ASSIGN, minstr, KV_TOKEN_DELIMIT, DAOPT_MAXLABEL, KV_ASSIGN, maxstr); nentry->devinfo.devopts = kval; nentry->devinfo.devlist = NULL; nentry->next = NULL; } nlen = strlen(link) + 1; /* +1 terminator */ if (nentry->devinfo.devlist) { plen = strlen(nentry->devinfo.devlist); nlen = nlen + plen + 1; /* +1 for blank to separate entries */ } else { plen = 0; } if ((nentry->devinfo.devlist = (char *)realloc(nentry->devinfo.devlist, nlen)) == NULL) { if (new_entry) { nentry->devinfo.devname = NULL; free(nentry->devinfo.devname); nentry = NULL; free(nentry); if (pentry != NULL) pentry->next = NULL; } return (-1); } if (plen == 0) (void) snprintf(nentry->devinfo.devlist, nlen, "%s", link); else (void) snprintf(nentry->devinfo.devlist + plen, nlen - plen, " %s", link); if (pentry == NULL) { /* * This is the first entry of this device type. */ if (flag & DA_AUDIO) dlist->audio = nentry; else if (flag & DA_CD) dlist->cd = nentry; else if (flag & DA_FLOPPY) dlist->floppy = nentry; else if (flag & DA_TAPE) dlist->tape = nentry; else if (flag & DA_RMDISK) dlist->rmdisk = nentry; } return (0); } /* * da_remove_list - * removes a /dev link name from the linked list of devices. * returns type of device if link for that device removed * successfully, else returns -1 on error. * if all links for a device are removed, stores that device * name in devname. */ int da_remove_list(devlist_t *dlist, char *link, int type, char *devname, int size) { int flag; int remove_dev = 0; int nlen, plen, slen; char *lasts, *lname, *oldlist; struct stat rmstat; deventry_t *dentry, *current, *prev; if (type != NULL) flag = type; else if (link == NULL) return (-1); else if (strstr(link, DA_AUDIO_NAME) || strstr(link, DA_SOUND_NAME)) flag = DA_AUDIO; else if (strstr(link, "dsk") || strstr(link, "rdsk") || strstr(link, "sr") || strstr(link, "rsr")) flag = DA_CD; else if (strstr(link, "fd") || strstr(link, "rfd") || strstr(link, "diskette") || strstr(link, "rdiskette")) flag = DA_FLOPPY; else if (strstr(link, DA_TAPE_NAME)) flag = DA_TAPE; else flag = DA_RMDISK; switch (type) { case DA_AUDIO: dentry = dlist->audio; break; case DA_CD: dentry = dlist->cd; break; case DA_FLOPPY: dentry = dlist->floppy; break; case DA_TAPE: dentry = dlist->tape; break; case DA_RMDISK: dentry = dlist->rmdisk; break; default: return (-1); } if ((type != NULL) && (link == NULL)) { for (current = dentry, prev = dentry; current != NULL; current = current->next) { oldlist = strdup(current->devinfo.devlist); for (lname = strtok_r(oldlist, " ", &lasts); lname != NULL; lname = strtok_r(NULL, " ", &lasts)) { if (stat(lname, &rmstat) != 0) { remove_dev = 1; goto remove_dev; } } prev = current; } return (-1); } for (current = dentry, prev = dentry; current != NULL; current = current->next) { plen = strlen(current->devinfo.devlist); nlen = strlen(link); if (plen == nlen) { if (strcmp(current->devinfo.devlist, link) == 0) { /* last name in the list */ remove_dev = 1; break; } } if (strstr(current->devinfo.devlist, link)) { nlen = plen - nlen + 1; oldlist = strdup(current->devinfo.devlist); if ((current->devinfo.devlist = (char *)realloc(current->devinfo.devlist, nlen)) == NULL) { free(oldlist); return (-1); } current->devinfo.devlist[0] = '\0'; nlen = plen = slen = 0; for (lname = strtok_r(oldlist, " ", &lasts); lname != NULL; lname = strtok_r(NULL, " ", &lasts)) { if (strcmp(lname, link) == 0) continue; nlen = strlen(lname) + plen + 1; if (plen == 0) { slen = snprintf(current->devinfo.devlist, nlen, "%s", lname); } else { slen = snprintf(current->devinfo.devlist + plen, nlen - plen, " %s", lname); } plen = plen + slen + 1; } free(oldlist); break; } prev = current; } remove_dev: if (remove_dev == 1) { (void) strlcpy(devname, current->devinfo.devname, size); free(current->devinfo.devname); free(current->devinfo.devlist); current->devinfo.devname = current->devinfo.devlist = NULL; prev->next = current->next; free(current); current = NULL; } if ((remove_dev == 1) && (prev->devinfo.devname == NULL)) { if (prev->next) { /* * what we removed above was the first entry * in the list. make the next entry to be the * first. */ current = prev->next; } else { /* * the matching entry was the only entry in the list * for this type. */ current = NULL; } if (flag & DA_AUDIO) dlist->audio = current; else if (flag & DA_CD) dlist->cd = current; else if (flag & DA_FLOPPY) dlist->floppy = current; else if (flag & DA_TAPE) dlist->tape = current; else if (flag & DA_RMDISK) dlist->rmdisk = current; } return (flag); } /* * da_is_on - * checks if device allocation feature is turned on. * returns 1 if on, 0 if off, -1 if status string not * found in device_allocate. */ int da_is_on() { return (getdaon()); } /* * da_print_device - * debug routine to print device entries. */ void da_print_device(int flag, devlist_t *devlist) { deventry_t *entry, *dentry; devinfo_t *devinfo; if (flag & DA_AUDIO) dentry = devlist->audio; else if (flag & DA_CD) dentry = devlist->cd; else if (flag & DA_FLOPPY) dentry = devlist->floppy; else if (flag & DA_TAPE) dentry = devlist->tape; else if (flag & DA_RMDISK) dentry = devlist->rmdisk; else return; for (entry = dentry; entry != NULL; entry = entry->next) { devinfo = &(entry->devinfo); (void) fprintf(stdout, "name: %s\n", devinfo->devname); (void) fprintf(stdout, "type: %s\n", devinfo->devtype); (void) fprintf(stdout, "auth: %s\n", devinfo->devauths); (void) fprintf(stdout, "exec: %s\n", devinfo->devexec); (void) fprintf(stdout, "list: %s\n\n", devinfo->devlist); } }