/* * 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 #include "allocate.h" #if !defined(TEXT_DOMAIN) #define TEXT_DOMAIN "SUNW_OST_OSCMD" #endif #define ALLOC "allocate" #define DEALLOC "deallocate" #define LIST "list_devices" extern void audit_allocate_argv(int, int, char *[]); extern int audit_allocate_record(int); int system_labeled = 0; static int windowing = 0; static int wdwmsg(char *name, char *msg); static void usage(int func) { if (system_labeled) { char *use[6]; use[0] = gettext("allocate [-s] [-w] [-U uname] [-z zonename] " "[-F] device|-g dev-type"); use[1] = gettext("deallocate [-s] [-w] [-z zonename] " "[-F] device|-c dev-class|-g dev-type"); use[2] = gettext("deallocate [-s] [-w] [-z zonename] -I"); use[3] = gettext("list_devices [-s] [-U uid] [-z zonename] " "[-a [-w]] -l|-n|-u [device]"); use[4] = gettext("list_devices [-s] [-U uid] [-z zonename] " "[-a [-w]] [-l|-n|-u] -c dev-class"); use[5] = gettext("list_devices [-s] -d [dev-type]"); switch (func) { case 0: (void) fprintf(stderr, "%s\n", use[0]); break; case 1: (void) fprintf(stderr, "%s\n%s\n", use[1], use[2]); break; case 2: (void) fprintf(stderr, "%s\n%s\n%s\n", use[3], use[4], use[5]); break; default: (void) fprintf(stderr, "%s\n%s\n%s\n%s\n%s\n%s\n", use[0], use[1], use[2], use[3], use[4], use[5]); } } else { char *use[5]; use[0] = gettext("allocate " "[-s] [-U uname] [-F] device|-g dev-type"); use[1] = gettext("deallocate [-s] [-F] device|-c dev-class"); use[2] = gettext("deallocate [-s] -I"); use[3] = gettext("list_devices " "[-s] [-U uid] -l|-n|-u [device]"); use[4] = gettext("list_devices " "[-s] [-U uid] [-l|-n|-u] -c dev-class"); switch (func) { case 0: (void) fprintf(stderr, "%s\n", use[0]); break; case 1: (void) fprintf(stderr, "%s\n%s\n", use[1], use[2]); break; case 2: (void) fprintf(stderr, "%s\n%s\n", use[3], use[4]); break; default: (void) fprintf(stderr, "%s\n%s\n%s\n%s\n%s\n", use[0], use[1], use[2], use[3], use[4]); } } exit(1); } void print_error(int error, char *name) { char *msg; char msgbuf[200]; switch (error) { case ALLOCUERR: msg = gettext("Specified device is allocated to another user."); break; case CHOWNERR: msg = gettext("Failed to chown."); break; case CLEANERR: msg = gettext("Unable to clean up device."); break; case CNTDEXECERR: msg = gettext( "Can't exec device-clean program for specified device."); break; case CNTFRCERR: msg = gettext("Can't force deallocate specified device."); break; case DACACCERR: msg = gettext( "Can't access DAC file for the device specified."); break; case DAOFFERR: msg = gettext( "Device allocation feature is not activated " "on this system."); break; case DAUTHERR: msg = gettext("Device not allocatable."); break; case DEFATTRSERR: msg = gettext("No default attributes for specified " "device type."); break; case DEVLKERR: msg = gettext("Concurrent operations for specified device, " "try later."); break; case DEVLONGERR: msg = gettext("Device name is too long."); break; case DEVNALLOCERR: msg = gettext("Device not allocated."); break; case DEVNAMEERR: msg = gettext("Device name error."); break; case DEVSTATEERR: msg = gettext("Device specified is in allocate error state."); break; case DEVZONEERR: msg = gettext("Can't find name of the zone to which " "device is allocated."); break; case DSPMISSERR: msg = gettext( "Device special file(s) missing for specified device."); break; case LABELRNGERR: msg = gettext( "Operation inconsistent with device's label range."); break; case LOGINDEVPERMERR: msg = gettext("Device controlled by logindevperm(4)"); break; case NODAERR: msg = gettext("No entry for specified device."); break; case NODMAPERR: msg = gettext("No entry for specified device."); break; case PREALLOCERR: msg = gettext("Device already allocated."); break; case SETACLERR: msg = gettext("Failed to set ACL."); break; case UAUTHERR: msg = gettext( "User lacks authorization required for this operation."); break; case ZONEERR: msg = gettext("Failed to configure device in zone."); break; default: msg = gettext("Unknown error code."); break; } if (windowing) { (void) snprintf(msgbuf, sizeof (msgbuf), "%s: %s\n", name, msg); (void) wdwmsg(name, msgbuf); } else { (void) fprintf(stderr, "%s: %s\n", name, msg); (void) fflush(stderr); } } char *newenv[] = {"PATH=/usr/bin:/usr/sbin", NULL, /* for LC_ALL */ NULL, /* for LC_COLLATE */ NULL, /* for LC_CTYPE */ NULL, /* for LC_MESSAGES */ NULL, /* for LC_NUMERIC */ NULL, /* for LC_TIME */ NULL, /* for LANG */ NULL }; static char * getenvent(char *name, char *env[]) { for (; *env != NULL; env++) { if (strncmp(*env, name, strlen(name)) == 0) return (*env); } return (NULL); } int main(int argc, char *argv[], char *envp[]) { char *name, *env; int func = -1, optflg = 0, error = 0, c; zoneid_t zoneid; uid_t uid; char *uname = NULL, *device = NULL, *zonename = NULL; char *zname; char pw_buf[NSS_BUFLEN_PASSWD]; struct passwd pw_ent; int env_num = 1; /* PATH= is 0 entry */ #ifdef DEBUG struct stat statbuf; #endif (void) setlocale(LC_ALL, ""); (void) textdomain(TEXT_DOMAIN); system_labeled = is_system_labeled(); /* test hook: see also mkdevalloc.c and devfsadm.c */ if (!system_labeled) { system_labeled = is_system_labeled_debug(&statbuf); if (system_labeled) { fprintf(stderr, "/ALLOCATE_FORCE_LABEL is set,\n" "forcing system label on for testing...\n"); } } /* * get all enviroment variables * which affect on internationalization. */ env = getenvent("LC_ALL=", envp); if (env != NULL) newenv[env_num++] = env; env = getenvent("LC_COLLATE=", envp); if (env != NULL) newenv[env_num++] = env; env = getenvent("LC_CTYPE=", envp); if (env != NULL) newenv[env_num++] = env; env = getenvent("LC_MESSAGES=", envp); if (env != NULL) newenv[env_num++] = env; env = getenvent("LC_NUMERIC=", envp); if (env != NULL) newenv[env_num++] = env; env = getenvent("LC_TIME=", envp); if (env != NULL) newenv[env_num++] = env; env = getenvent("LANG=", envp); if (env != NULL) newenv[env_num] = env; if ((name = strrchr(argv[0], '/')) == NULL) name = argv[0]; else name++; if (strcmp(name, ALLOC) == 0) func = 0; else if (strcmp(name, DEALLOC) == 0) func = 1; else if (strcmp(name, LIST) == 0) func = 2; else usage(-1); audit_allocate_argv(func, argc, argv); if (system_labeled) { /* * allocate, deallocate, list_devices run in * global zone only. */ zoneid = getzoneid(); if (zoneid != GLOBAL_ZONEID) exit(GLOBALERR); zname = GLOBAL_ZONENAME; /* * check if device allocation is activated. */ if (da_is_on() == 0) { (void) fprintf(stderr, "%s%s", gettext("Turn device allocation on"), gettext(" to use this feature.\n")); exit(DAOFFERR); } } if (func == 0) { /* allocate */ while ((c = getopt(argc, argv, "g:swz:FU:")) != -1) { switch (c) { case 'g': optflg |= TYPE; device = optarg; break; case 's': optflg |= SILENT; break; case 'w': if (system_labeled) { optflg |= WINDOWING; windowing = 1; } else { usage(func); } break; case 'z': if (system_labeled) { optflg |= ZONENAME; zonename = optarg; } else { usage(func); } break; case 'F': optflg |= FORCE; break; case 'U': optflg |= USERNAME; uname = optarg; break; case '?': default : usage(func); } } /* * allocate(1) must be supplied with one device argument */ if (device && ((argc - optind) >= 1)) usage(func); if (device == NULL) { if ((argc - optind) != 1) usage(func); device = argv[optind]; } } else if (func == 1) { /* deallocate */ while ((c = getopt(argc, argv, "c:g:swz:FI")) != -1) { switch (c) { case 'c': if (optflg & (TYPE | FORCE_ALL)) usage(func); optflg |= CLASS; device = optarg; break; case 'g': if (system_labeled) { if (optflg & (CLASS | FORCE_ALL)) usage(func); optflg |= TYPE; device = optarg; } else { usage(func); } break; case 's': optflg |= SILENT; break; case 'w': if (system_labeled) { optflg |= WINDOWING; windowing = 1; } else { usage(func); } break; case 'z': if (system_labeled) { optflg |= ZONENAME; zonename = optarg; } else { usage(func); } break; case 'F': if (optflg & FORCE_ALL) usage(func); optflg |= FORCE; break; case 'I': if (optflg & (CLASS | TYPE | FORCE)) usage(func); optflg |= FORCE_ALL; break; case '?': default : usage(func); } } /* * deallocate(1) must be supplied with one device * argument unless the '-I' argument is supplied */ if (device || (optflg & FORCE_ALL)) { if ((argc - optind) >= 1) usage(func); } else if (device == NULL) { if ((argc - optind) != 1) usage(func); device = argv[optind]; } } else if (func == 2) { /* list_devices */ while ((c = getopt(argc, argv, "ac:dlnsuwz:U:")) != -1) { switch (c) { case 'a': if (system_labeled) { /* * list auths, cleaning programs, * labels. */ if (optflg & LISTDEFS) usage(func); optflg |= LISTATTRS; } else { usage(func); } break; case 'c': optflg |= CLASS; device = optarg; break; case 'd': if (system_labeled) { /* * List devalloc_defaults * This cannot used with anything other * than -s. */ if (optflg & (LISTATTRS | CLASS | LISTALL | LISTFREE | LISTALLOC | WINDOWING | ZONENAME | USERID)) usage(func); optflg |= LISTDEFS; } else { usage(func); } break; case 'l': if (optflg & (LISTFREE | LISTALLOC | LISTDEFS)) usage(func); optflg |= LISTALL; break; case 'n': if (optflg & (LISTALL | LISTALLOC | LISTDEFS)) usage(func); optflg |= LISTFREE; break; case 's': optflg |= SILENT; break; case 'u': if (optflg & (LISTALL | LISTFREE | LISTDEFS)) usage(func); optflg |= LISTALLOC; break; case 'w': if (system_labeled) { if (optflg & LISTDEFS) usage(func); optflg |= WINDOWING; } else { usage(func); } break; case 'z': if (system_labeled) { if (optflg & LISTDEFS) usage(func); optflg |= ZONENAME; zonename = optarg; } else { usage(func); } break; case 'U': if (optflg & LISTDEFS) usage(func); optflg |= USERID; uid = atoi(optarg); break; case '?': default : usage(func); } } if (system_labeled) { if (!(optflg & (LISTALL | LISTFREE | LISTALLOC | LISTDEFS | WINDOWING))) { if (!(optflg & CLASS)) usage(func); } } else if (!(optflg & (LISTALL | LISTFREE | LISTALLOC))) { if (!(optflg & CLASS)) usage(func); } /* * list_devices(1) takes an optional device argument. */ if (device && ((argc - optind) >= 1)) usage(func); if (device == NULL) { if ((argc - optind) == 1) device = argv[optind]; else if ((argc - optind) > 1) usage(func); } } if (optflg & USERNAME) { if (getpwnam_r(uname, &pw_ent, pw_buf, sizeof (pw_buf)) == NULL) { (void) fprintf(stderr, gettext("Invalid user name -- %s -- \n"), uname); exit(1); } uid = pw_ent.pw_uid; } else if (optflg & USERID) { if (getpwuid_r(uid, &pw_ent, pw_buf, sizeof (pw_buf)) == NULL) { (void) fprintf(stderr, gettext("Invalid user ID -- %d -- \n"), uid); exit(1); } uid = pw_ent.pw_uid; } else { /* * caller's uid is the default if no user specified. */ uid = getuid(); } /* * global zone is the default if no zonename specified. */ if (zonename == NULL) { zonename = zname; } else { if (zone_get_id(zonename, &zoneid) != 0) { (void) fprintf(stderr, gettext("Invalid zone name -- %s -- \n"), zonename); exit(1); } } if (func == 0) error = allocate(optflg, uid, device, zonename); else if (func == 1) error = deallocate(optflg, uid, device, zonename); else if (func == 2) error = list_devices(optflg, uid, device, zonename); (void) audit_allocate_record(error); if (error) { if (!(optflg & SILENT)) print_error(error, name); exit(error); } return (0); } /* * Display error message via /etc/security/lib/wdwmsg script */ static int wdwmsg(char *name, char *msg) { pid_t child_pid; pid_t wait_pid; int child_status; /* Fork a child */ switch (child_pid = fork()) { case -1: /* FAILURE */ return (-1); break; case 0: /* CHILD */ (void) execl("/etc/security/lib/wdwmsg", "wdwmsg", msg, name, "OK", NULL); /* If exec failed, send message to stderr */ (void) fprintf(stderr, "%s", msg); return (-1); default: /* PARENT */ /* Wait for child to exit */ wait_pid = waitpid(child_pid, &child_status, 0); if ((wait_pid < 0) && (errno == ECHILD)) return (0); if ((wait_pid < 0) || (wait_pid != child_pid)) return (-1); if (WIFEXITED(child_status)) return (WEXITSTATUS(child_status)); if (WIFSIGNALED(child_status)) return (WTERMSIG(child_status)); return (0); } }