| /* |
| * 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 2006 Sun Microsystems, Inc. All rights reserved. |
| * Use is subject to license terms. |
| */ |
| |
| #pragma ident "%Z%%M% %I% %E% SMI" |
| |
| #include <errno.h> |
| #include <locale.h> |
| #include <pwd.h> |
| #include <unistd.h> |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <string.h> |
| #include <ctype.h> |
| #include <nss_dbdefs.h> |
| #include <sys/types.h> |
| #include <sys/wait.h> |
| #include <tsol/label.h> |
| #include <zone.h> |
| #include <bsm/devalloc.h> |
| #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[9]; |
| |
| use[0] = gettext("allocate [-s] [-w] [-U uname] [-z zonename] " |
| "[-F] device"); |
| use[1] = gettext("allocate [-s] [-w] [-U uname] [-z zonename] " |
| "[-F] -g dev_type"); |
| use[2] = gettext("deallocate [-s] [-w] [-z zonename] " |
| "[-F] device"); |
| use[3] = gettext("deallocate [-s] [-w] [-z zonename] " |
| "[-F] -g dev_type"); |
| use[4] = gettext("deallocate [-s] [-w] [-z zonename] -I"); |
| use[5] = gettext("list_devices [-s] [-U uid] [-z zonename] " |
| "[-a] -l [device]"); |
| use[6] = gettext("list_devices [-s] [-U uid] [-z zonename] " |
| "[-a] -n [device]"); |
| use[7] = gettext("list_devices [-s] [-U uid] [-z zonename] " |
| "[-a] -u [device]"); |
| use[8] = gettext("list_devices [-s] -d dev_type"); |
| |
| switch (func) { |
| case 0: |
| (void) fprintf(stderr, "%s\n%s\n", |
| use[0], use[1]); |
| break; |
| case 1: |
| (void) fprintf(stderr, "%s\n%s\n%s\n", |
| use[2], use[3], use[4]); |
| break; |
| case 2: |
| (void) fprintf(stderr, "%s\n%s\n%s\n%s\n", |
| use[5], use[6], use[7], use[8]); |
| break; |
| default: |
| (void) fprintf(stderr, |
| "%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n", |
| use[0], use[1], use[2], use[3], use[4], |
| use[5], use[6], use[7], use[8]); |
| } |
| } else { |
| char *use[7]; |
| |
| use[0] = gettext("allocate [-s] [-U uname] [-F] device"); |
| use[1] = gettext("allocate [-s] [-U uname] -g dev_type"); |
| use[2] = gettext("deallocate [-s] [-F] device"); |
| use[3] = gettext("deallocate [-s] -I"); |
| use[4] = gettext("list_devices [-s] [-U uid] -l [device]"); |
| use[5] = gettext("list_devices [-s] [-U uid] -n [device]"); |
| use[6] = gettext("list_devices [-s] [-U uid] -u [device]"); |
| |
| switch (func) { |
| case 0: |
| (void) fprintf(stderr, "%s\n%s\n", |
| use[0], use[1]); |
| break; |
| case 1: |
| (void) fprintf(stderr, "%s\n%s\n", |
| use[2], use[3]); |
| break; |
| case 2: |
| (void) fprintf(stderr, "%s\n%s\n%s\n", |
| use[4], use[5], use[6]); |
| break; |
| default: |
| (void) fprintf(stderr, |
| "%s\n%s\n%s\n%s\n%s\n%s\n%s\n", |
| use[0], use[1], use[2], use[3], use[4], |
| use[5], use[6]); |
| } |
| } |
| 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 */ |
| |
| (void) setlocale(LC_ALL, ""); |
| (void) textdomain(TEXT_DOMAIN); |
| |
| system_labeled = is_system_labeled(); |
| |
| /* |
| * 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, "gswz:FU:")) != -1) { |
| switch (c) { |
| case 'g': |
| optflg |= TYPE; |
| 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 ((argc - optind) != 1) { |
| usage(func); |
| } else { |
| device = argv[optind]; |
| } |
| } |
| |
| else if (func == 1) { /* deallocate */ |
| while ((c = getopt(argc, argv, "gswz:FI")) != -1) { |
| switch (c) { |
| case 'g': |
| if (system_labeled) |
| optflg |= TYPE; |
| 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': |
| optflg |= FORCE; |
| break; |
| case 'I': |
| optflg |= FORCE_ALL; |
| break; |
| case '?': |
| default : |
| usage(func); |
| } |
| } |
| |
| if ((optflg & FORCE) && (optflg & FORCE_ALL)) |
| usage(func); |
| |
| if (system_labeled && ((optflg & FORCE_ALL) && (optflg & TYPE))) |
| usage(func); |
| |
| /* |
| * deallocate(1) must be supplied with one device |
| * argument unless the '-I' argument is supplied |
| */ |
| if (!(optflg & FORCE_ALL)) { |
| if ((argc - optind) != 1) { |
| usage(func); |
| } else { |
| device = argv[optind]; |
| } |
| } else { |
| if ((argc - optind) >= 1) { |
| usage(func); |
| } |
| } |
| } |
| |
| else if (func == 2) { /* list_devices */ |
| while ((c = getopt(argc, argv, "adlnsuwz:U:")) != -1) { |
| switch (c) { |
| case 'a': |
| if (system_labeled) { |
| /* |
| * list auths, cleaning programs, |
| * labels. |
| */ |
| optflg |= LISTATTRS; |
| } else { |
| usage(func); |
| } |
| break; |
| case 'd': |
| if (system_labeled) { |
| /* |
| * list devalloc_defaults |
| */ |
| optflg |= LISTDEFS; |
| } else { |
| usage(func); |
| } |
| break; |
| case 'l': |
| optflg |= LISTALL; |
| break; |
| case 'n': |
| optflg |= LISTFREE; |
| break; |
| case 's': |
| optflg |= SILENT; |
| break; |
| case 'u': |
| optflg |= LISTALLOC; |
| break; |
| case 'w': |
| if (system_labeled) { |
| /* |
| * Private interface for use by |
| * list_devices GUI |
| */ |
| optflg |= WINDOWING; |
| } else { |
| usage(func); |
| } |
| break; |
| case 'z': |
| if (system_labeled) { |
| optflg |= ZONENAME; |
| zonename = optarg; |
| } else { |
| usage(func); |
| } |
| break; |
| case 'U': |
| optflg |= USERID; |
| uid = atoi(optarg); |
| break; |
| case '?': |
| default : |
| usage(func); |
| } |
| } |
| |
| if (system_labeled) { |
| if (((optflg & LISTALL) && (optflg & LISTFREE)) || |
| ((optflg & LISTALL) && (optflg & LISTALLOC)) || |
| ((optflg & LISTFREE) && (optflg & LISTALLOC)) || |
| ((optflg & LISTDEFS) && |
| (optflg & (LISTATTRS | LISTALL | LISTFREE | |
| LISTALLOC | USERID | WINDOWING | ZONENAME))) || |
| (!(optflg & (LISTALL | LISTFREE | LISTALLOC | |
| LISTDEFS | WINDOWING)))) |
| usage(func); |
| } else if (((optflg & LISTALL) && (optflg & LISTFREE)) || |
| ((optflg & LISTALL) && (optflg & LISTALLOC)) || |
| ((optflg & LISTFREE) && (optflg & LISTALLOC)) || |
| (!(optflg & (LISTALL | LISTFREE | LISTALLOC)))) { |
| usage(func); |
| } |
| |
| /* |
| * list_devices(1) takes an optional device argument |
| */ |
| 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); |
| } |
| } |