/*
* 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 <errno.h>
#include <locale.h>
#include <pwd.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <fcntl.h>
#include <nss_dbdefs.h>
#include <sys/types.h>
#include <sys/stat.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[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);
}
}