Home | History | Annotate | Download | only in allocate
      1 /*
      2  * CDDL HEADER START
      3  *
      4  * The contents of this file are subject to the terms of the
      5  * Common Development and Distribution License (the "License").
      6  * You may not use this file except in compliance with the License.
      7  *
      8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
      9  * or http://www.opensolaris.org/os/licensing.
     10  * See the License for the specific language governing permissions
     11  * and limitations under the License.
     12  *
     13  * When distributing Covered Code, include this CDDL HEADER in each
     14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
     15  * If applicable, add the following below this CDDL HEADER, with the
     16  * fields enclosed by brackets "[]" replaced with your own identifying
     17  * information: Portions Copyright [yyyy] [name of copyright owner]
     18  *
     19  * CDDL HEADER END
     20  */
     21 
     22 /*
     23  * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
     24  * Use is subject to license terms.
     25  */
     26 
     27 #pragma ident	"%Z%%M%	%I%	%E% SMI"
     28 
     29 #include <errno.h>
     30 #include <locale.h>
     31 #include <pwd.h>
     32 #include <unistd.h>
     33 #include <stdio.h>
     34 #include <stdlib.h>
     35 #include <string.h>
     36 #include <ctype.h>
     37 #include <fcntl.h>
     38 #include <nss_dbdefs.h>
     39 #include <sys/types.h>
     40 #include <sys/stat.h>
     41 #include <sys/wait.h>
     42 #include <tsol/label.h>
     43 #include <zone.h>
     44 #include <bsm/devalloc.h>
     45 #include "allocate.h"
     46 
     47 #if !defined(TEXT_DOMAIN)
     48 #define	TEXT_DOMAIN "SUNW_OST_OSCMD"
     49 #endif
     50 
     51 #define	ALLOC	"allocate"
     52 #define	DEALLOC	"deallocate"
     53 #define	LIST	"list_devices"
     54 
     55 extern void audit_allocate_argv(int, int, char *[]);
     56 extern int audit_allocate_record(int);
     57 
     58 int system_labeled = 0;
     59 static int windowing = 0;
     60 static int wdwmsg(char *name, char *msg);
     61 
     62 static void
     63 usage(int func)
     64 {
     65 	if (system_labeled) {
     66 		char *use[6];
     67 
     68 		use[0] = gettext("allocate [-s] [-w] [-U uname] [-z zonename] "
     69 		    "[-F] device|-g dev-type");
     70 		use[1] = gettext("deallocate [-s] [-w] [-z zonename] "
     71 		    "[-F] device|-c dev-class|-g dev-type");
     72 		use[2] = gettext("deallocate [-s] [-w] [-z zonename] -I");
     73 		use[3] = gettext("list_devices [-s] [-U uid] [-z zonename] "
     74 		    "[-a [-w]] -l|-n|-u [device]");
     75 		use[4] = gettext("list_devices [-s] [-U uid] [-z zonename] "
     76 		    "[-a [-w]] [-l|-n|-u] -c dev-class");
     77 		use[5] = gettext("list_devices [-s] -d [dev-type]");
     78 
     79 		switch (func) {
     80 			case 0:
     81 				(void) fprintf(stderr, "%s\n", use[0]);
     82 				break;
     83 			case 1:
     84 				(void) fprintf(stderr, "%s\n%s\n",
     85 				    use[1], use[2]);
     86 				break;
     87 			case 2:
     88 				(void) fprintf(stderr, "%s\n%s\n%s\n",
     89 				    use[3], use[4], use[5]);
     90 				break;
     91 			default:
     92 				(void) fprintf(stderr,
     93 				    "%s\n%s\n%s\n%s\n%s\n%s\n",
     94 				    use[0], use[1], use[2], use[3], use[4],
     95 				    use[5]);
     96 		}
     97 	} else {
     98 		char *use[5];
     99 
    100 		use[0] = gettext("allocate "
    101 		    "[-s] [-U uname] [-F] device|-g dev-type");
    102 		use[1] = gettext("deallocate [-s] [-F] device|-c dev-class");
    103 		use[2] = gettext("deallocate [-s] -I");
    104 		use[3] = gettext("list_devices "
    105 		    "[-s] [-U uid] -l|-n|-u [device]");
    106 		use[4] = gettext("list_devices "
    107 		    "[-s] [-U uid] [-l|-n|-u] -c dev-class");
    108 
    109 		switch (func) {
    110 			case 0:
    111 				(void) fprintf(stderr, "%s\n", use[0]);
    112 				break;
    113 			case 1:
    114 				(void) fprintf(stderr, "%s\n%s\n",
    115 				    use[1], use[2]);
    116 				break;
    117 			case 2:
    118 				(void) fprintf(stderr, "%s\n%s\n",
    119 				    use[3], use[4]);
    120 				break;
    121 			default:
    122 				(void) fprintf(stderr, "%s\n%s\n%s\n%s\n%s\n",
    123 				    use[0], use[1], use[2], use[3], use[4]);
    124 		}
    125 	}
    126 	exit(1);
    127 }
    128 
    129 void
    130 print_error(int error, char *name)
    131 {
    132 	char	*msg;
    133 	char	msgbuf[200];
    134 
    135 	switch (error) {
    136 	case ALLOCUERR:
    137 		msg = gettext("Specified device is allocated to another user.");
    138 		break;
    139 	case CHOWNERR:
    140 		msg = gettext("Failed to chown.");
    141 		break;
    142 	case CLEANERR:
    143 		msg = gettext("Unable to clean up device.");
    144 		break;
    145 	case CNTDEXECERR:
    146 		msg = gettext(
    147 		    "Can't exec device-clean program for specified device.");
    148 		break;
    149 	case CNTFRCERR:
    150 		msg = gettext("Can't force deallocate specified device.");
    151 		break;
    152 	case DACACCERR:
    153 		msg = gettext(
    154 		    "Can't access DAC file for the device specified.");
    155 		break;
    156 	case DAOFFERR:
    157 		msg = gettext(
    158 		    "Device allocation feature is not activated "
    159 		    "on this system.");
    160 		break;
    161 	case DAUTHERR:
    162 		msg = gettext("Device not allocatable.");
    163 		break;
    164 	case DEFATTRSERR:
    165 		msg = gettext("No default attributes for specified "
    166 		    "device type.");
    167 		break;
    168 	case DEVLKERR:
    169 		msg = gettext("Concurrent operations for specified device, "
    170 		    "try later.");
    171 		break;
    172 	case DEVLONGERR:
    173 		msg = gettext("Device name is too long.");
    174 		break;
    175 	case DEVNALLOCERR:
    176 		msg = gettext("Device not allocated.");
    177 		break;
    178 	case DEVNAMEERR:
    179 		msg = gettext("Device name error.");
    180 		break;
    181 	case DEVSTATEERR:
    182 		msg = gettext("Device specified is in allocate error state.");
    183 		break;
    184 	case DEVZONEERR:
    185 		msg = gettext("Can't find name of the zone to which "
    186 		    "device is allocated.");
    187 		break;
    188 	case DSPMISSERR:
    189 		msg = gettext(
    190 		    "Device special file(s) missing for specified device.");
    191 		break;
    192 	case LABELRNGERR:
    193 		msg = gettext(
    194 		    "Operation inconsistent with device's label range.");
    195 		break;
    196 	case LOGINDEVPERMERR:
    197 		msg = gettext("Device controlled by logindevperm(4)");
    198 		break;
    199 	case NODAERR:
    200 		msg = gettext("No entry for specified device.");
    201 		break;
    202 	case NODMAPERR:
    203 		msg = gettext("No entry for specified device.");
    204 		break;
    205 	case PREALLOCERR:
    206 		msg = gettext("Device already allocated.");
    207 		break;
    208 	case SETACLERR:
    209 		msg = gettext("Failed to set ACL.");
    210 		break;
    211 	case UAUTHERR:
    212 		msg = gettext(
    213 		    "User lacks authorization required for this operation.");
    214 		break;
    215 	case ZONEERR:
    216 		msg = gettext("Failed to configure device in zone.");
    217 		break;
    218 	default:
    219 		msg = gettext("Unknown error code.");
    220 		break;
    221 	}
    222 
    223 	if (windowing) {
    224 		(void) snprintf(msgbuf, sizeof (msgbuf), "%s: %s\n", name, msg);
    225 		(void) wdwmsg(name, msgbuf);
    226 	} else {
    227 		(void) fprintf(stderr, "%s: %s\n", name, msg);
    228 		(void) fflush(stderr);
    229 	}
    230 }
    231 
    232 char *newenv[] = {"PATH=/usr/bin:/usr/sbin",
    233 			NULL,			/* for LC_ALL		*/
    234 			NULL,			/* for LC_COLLATE	*/
    235 			NULL,			/* for LC_CTYPE		*/
    236 			NULL,			/* for LC_MESSAGES	*/
    237 			NULL,			/* for LC_NUMERIC	*/
    238 			NULL,			/* for LC_TIME		*/
    239 			NULL,			/* for LANG		*/
    240 			NULL
    241 };
    242 
    243 static char *
    244 getenvent(char *name, char *env[])
    245 {
    246 	for (; *env != NULL; env++) {
    247 		if (strncmp(*env, name, strlen(name)) == 0)
    248 			return (*env);
    249 	}
    250 	return (NULL);
    251 }
    252 
    253 int
    254 main(int argc, char *argv[], char *envp[])
    255 {
    256 	char		*name, *env;
    257 	int		func = -1, optflg = 0, error = 0, c;
    258 	zoneid_t	zoneid;
    259 	uid_t		uid;
    260 	char		*uname = NULL, *device = NULL, *zonename = NULL;
    261 	char		*zname;
    262 	char		pw_buf[NSS_BUFLEN_PASSWD];
    263 	struct passwd	pw_ent;
    264 	int 		env_num = 1;	/* PATH= is 0 entry */
    265 #ifdef DEBUG
    266 	struct stat	statbuf;
    267 #endif
    268 
    269 	(void) setlocale(LC_ALL, "");
    270 	(void) textdomain(TEXT_DOMAIN);
    271 
    272 	system_labeled = is_system_labeled();
    273 
    274 	/* test hook: see also mkdevalloc.c and devfsadm.c */
    275 	if (!system_labeled) {
    276 		system_labeled = is_system_labeled_debug(&statbuf);
    277 		if (system_labeled) {
    278 			fprintf(stderr, "/ALLOCATE_FORCE_LABEL is set,\n"
    279 			    "forcing system label on for testing...\n");
    280 		}
    281 	}
    282 
    283 	/*
    284 	 * get all enviroment variables
    285 	 * which affect on internationalization.
    286 	 */
    287 	env = getenvent("LC_ALL=", envp);
    288 	if (env != NULL)
    289 		newenv[env_num++] = env;
    290 	env = getenvent("LC_COLLATE=", envp);
    291 	if (env != NULL)
    292 		newenv[env_num++] = env;
    293 	env = getenvent("LC_CTYPE=", envp);
    294 	if (env != NULL)
    295 		newenv[env_num++] = env;
    296 	env = getenvent("LC_MESSAGES=", envp);
    297 	if (env != NULL)
    298 		newenv[env_num++] = env;
    299 	env = getenvent("LC_NUMERIC=", envp);
    300 	if (env != NULL)
    301 		newenv[env_num++] = env;
    302 	env = getenvent("LC_TIME=", envp);
    303 	if (env != NULL)
    304 		newenv[env_num++] = env;
    305 	env = getenvent("LANG=", envp);
    306 	if (env != NULL)
    307 		newenv[env_num] = env;
    308 
    309 	if ((name = strrchr(argv[0], '/')) == NULL)
    310 		name = argv[0];
    311 	else
    312 		name++;
    313 
    314 	if (strcmp(name, ALLOC) == 0)
    315 		func = 0;
    316 	else if (strcmp(name, DEALLOC) == 0)
    317 		func = 1;
    318 	else if (strcmp(name, LIST) == 0)
    319 		func = 2;
    320 	else
    321 		usage(-1);
    322 
    323 	audit_allocate_argv(func, argc, argv);
    324 
    325 	if (system_labeled) {
    326 		/*
    327 		 * allocate, deallocate, list_devices run in
    328 		 * global zone only.
    329 		 */
    330 		zoneid = getzoneid();
    331 		if (zoneid != GLOBAL_ZONEID)
    332 			exit(GLOBALERR);
    333 		zname = GLOBAL_ZONENAME;
    334 		/*
    335 		 * check if device allocation is activated.
    336 		 */
    337 		if (da_is_on() == 0) {
    338 			(void) fprintf(stderr, "%s%s",
    339 			    gettext("Turn device allocation on"),
    340 			    gettext(" to use this feature.\n"));
    341 			exit(DAOFFERR);
    342 		}
    343 	}
    344 
    345 	if (func == 0) {	/* allocate */
    346 		while ((c = getopt(argc, argv, "g:swz:FU:")) != -1) {
    347 			switch (c) {
    348 			case 'g':
    349 				optflg |= TYPE;
    350 				device = optarg;
    351 				break;
    352 			case 's':
    353 				optflg |= SILENT;
    354 				break;
    355 			case 'w':
    356 				if (system_labeled) {
    357 					optflg |= WINDOWING;
    358 					windowing = 1;
    359 				} else {
    360 					usage(func);
    361 				}
    362 				break;
    363 			case 'z':
    364 				if (system_labeled) {
    365 					optflg |= ZONENAME;
    366 					zonename = optarg;
    367 				} else {
    368 					usage(func);
    369 				}
    370 				break;
    371 			case 'F':
    372 				optflg |= FORCE;
    373 				break;
    374 			case 'U':
    375 				optflg |= USERNAME;
    376 				uname = optarg;
    377 				break;
    378 			case '?':
    379 			default :
    380 				usage(func);
    381 			}
    382 		}
    383 
    384 		/*
    385 		 * allocate(1) must be supplied with one device argument
    386 		 */
    387 		if (device && ((argc - optind) >= 1))
    388 			usage(func);
    389 		if (device == NULL) {
    390 			if ((argc - optind) != 1)
    391 				usage(func);
    392 			device = argv[optind];
    393 		}
    394 	}
    395 
    396 	else if (func == 1) {	/* deallocate */
    397 		while ((c = getopt(argc, argv, "c:g:swz:FI")) != -1) {
    398 			switch (c) {
    399 			case 'c':
    400 				if (optflg & (TYPE | FORCE_ALL))
    401 					usage(func);
    402 				optflg |= CLASS;
    403 				device = optarg;
    404 				break;
    405 			case 'g':
    406 				if (system_labeled) {
    407 					if (optflg & (CLASS | FORCE_ALL))
    408 						usage(func);
    409 					optflg |= TYPE;
    410 					device = optarg;
    411 				} else {
    412 					usage(func);
    413 				}
    414 				break;
    415 			case 's':
    416 				optflg |= SILENT;
    417 				break;
    418 			case 'w':
    419 				if (system_labeled) {
    420 					optflg |= WINDOWING;
    421 					windowing = 1;
    422 				} else {
    423 					usage(func);
    424 				}
    425 				break;
    426 			case 'z':
    427 				if (system_labeled) {
    428 					optflg |= ZONENAME;
    429 					zonename = optarg;
    430 				} else {
    431 					usage(func);
    432 				}
    433 				break;
    434 			case 'F':
    435 				if (optflg & FORCE_ALL)
    436 					usage(func);
    437 				optflg |= FORCE;
    438 				break;
    439 			case 'I':
    440 				if (optflg & (CLASS | TYPE | FORCE))
    441 					usage(func);
    442 				optflg |= FORCE_ALL;
    443 				break;
    444 			case '?':
    445 			default :
    446 				usage(func);
    447 			}
    448 		}
    449 
    450 		/*
    451 		 * deallocate(1) must be supplied with one device
    452 		 * argument unless the '-I' argument is supplied
    453 		 */
    454 		if (device || (optflg & FORCE_ALL)) {
    455 			if ((argc - optind) >= 1)
    456 				usage(func);
    457 		} else if (device == NULL) {
    458 			if ((argc - optind) != 1)
    459 				usage(func);
    460 			device = argv[optind];
    461 		}
    462 	}
    463 
    464 	else if (func == 2) {	/* list_devices */
    465 		while ((c = getopt(argc, argv, "ac:dlnsuwz:U:")) != -1) {
    466 			switch (c) {
    467 			case 'a':
    468 				if (system_labeled) {
    469 					/*
    470 					 * list auths, cleaning programs,
    471 					 * labels.
    472 					 */
    473 					if (optflg & LISTDEFS)
    474 						usage(func);
    475 					optflg |= LISTATTRS;
    476 				} else {
    477 					usage(func);
    478 				}
    479 				break;
    480 			case 'c':
    481 				optflg |= CLASS;
    482 				device = optarg;
    483 				break;
    484 			case 'd':
    485 				if (system_labeled) {
    486 					/*
    487 					 * List devalloc_defaults
    488 					 * This cannot used with anything other
    489 					 * than -s.
    490 					 */
    491 					if (optflg & (LISTATTRS | CLASS |
    492 					    LISTALL | LISTFREE | LISTALLOC |
    493 					    WINDOWING | ZONENAME | USERID))
    494 						usage(func);
    495 					optflg |= LISTDEFS;
    496 				} else {
    497 					usage(func);
    498 				}
    499 				break;
    500 			case 'l':
    501 				if (optflg & (LISTFREE | LISTALLOC | LISTDEFS))
    502 					usage(func);
    503 				optflg |= LISTALL;
    504 				break;
    505 			case 'n':
    506 				if (optflg & (LISTALL | LISTALLOC | LISTDEFS))
    507 					usage(func);
    508 				optflg |= LISTFREE;
    509 				break;
    510 			case 's':
    511 				optflg |= SILENT;
    512 				break;
    513 			case 'u':
    514 				if (optflg & (LISTALL | LISTFREE | LISTDEFS))
    515 					usage(func);
    516 				optflg |= LISTALLOC;
    517 				break;
    518 			case 'w':
    519 				if (system_labeled) {
    520 					if (optflg & LISTDEFS)
    521 						usage(func);
    522 					optflg |= WINDOWING;
    523 				} else {
    524 					usage(func);
    525 				}
    526 				break;
    527 			case 'z':
    528 				if (system_labeled) {
    529 					if (optflg & LISTDEFS)
    530 						usage(func);
    531 					optflg |= ZONENAME;
    532 					zonename = optarg;
    533 				} else {
    534 					usage(func);
    535 				}
    536 				break;
    537 			case 'U':
    538 				if (optflg & LISTDEFS)
    539 					usage(func);
    540 				optflg |= USERID;
    541 				uid = atoi(optarg);
    542 				break;
    543 			case '?':
    544 			default :
    545 				usage(func);
    546 			}
    547 		}
    548 
    549 		if (system_labeled) {
    550 			if (!(optflg & (LISTALL | LISTFREE | LISTALLOC |
    551 			    LISTDEFS | WINDOWING))) {
    552 				if (!(optflg & CLASS))
    553 					usage(func);
    554 			}
    555 		} else if (!(optflg & (LISTALL | LISTFREE | LISTALLOC))) {
    556 			if (!(optflg & CLASS))
    557 				usage(func);
    558 		}
    559 
    560 		/*
    561 		 * list_devices(1) takes an optional device argument.
    562 		 */
    563 		if (device && ((argc - optind) >= 1))
    564 			usage(func);
    565 		if (device == NULL) {
    566 			if ((argc - optind) == 1)
    567 				device = argv[optind];
    568 			else if ((argc - optind) > 1)
    569 				usage(func);
    570 		}
    571 	}
    572 
    573 	if (optflg & USERNAME) {
    574 		if (getpwnam_r(uname, &pw_ent, pw_buf, sizeof (pw_buf)) ==
    575 		    NULL) {
    576 			(void) fprintf(stderr,
    577 			    gettext("Invalid user name -- %s -- \n"), uname);
    578 			exit(1);
    579 		}
    580 		uid = pw_ent.pw_uid;
    581 	} else if (optflg & USERID) {
    582 		if (getpwuid_r(uid, &pw_ent, pw_buf, sizeof (pw_buf)) == NULL) {
    583 			(void) fprintf(stderr,
    584 			    gettext("Invalid user ID -- %d -- \n"), uid);
    585 			exit(1);
    586 		}
    587 		uid = pw_ent.pw_uid;
    588 	} else {
    589 		/*
    590 		 * caller's uid is the default if no user specified.
    591 		 */
    592 		uid = getuid();
    593 	}
    594 
    595 	/*
    596 	 * global zone is the default if no zonename specified.
    597 	 */
    598 	if (zonename == NULL) {
    599 		zonename = zname;
    600 	} else {
    601 		if (zone_get_id(zonename, &zoneid) != 0) {
    602 			(void) fprintf(stderr,
    603 			    gettext("Invalid zone name -- %s -- \n"), zonename);
    604 			exit(1);
    605 		}
    606 	}
    607 
    608 	if (func == 0)
    609 		error = allocate(optflg, uid, device, zonename);
    610 	else if (func == 1)
    611 		error = deallocate(optflg, uid, device, zonename);
    612 	else if (func == 2)
    613 		error = list_devices(optflg, uid, device, zonename);
    614 
    615 	(void) audit_allocate_record(error);
    616 
    617 	if (error) {
    618 		if (!(optflg & SILENT))
    619 			print_error(error, name);
    620 		exit(error);
    621 	}
    622 
    623 	return (0);
    624 }
    625 
    626 /*
    627  * Display error message via /etc/security/lib/wdwmsg script
    628  */
    629 static int
    630 wdwmsg(char *name, char *msg)
    631 {
    632 	pid_t child_pid;
    633 	pid_t wait_pid;
    634 	int child_status;
    635 
    636 	/* Fork a child */
    637 	switch (child_pid = fork()) {
    638 	case -1:	/* FAILURE */
    639 		return (-1);
    640 		break;
    641 
    642 	case 0:		/* CHILD */
    643 		(void) execl("/etc/security/lib/wdwmsg", "wdwmsg", msg,
    644 		    name, "OK", NULL);
    645 		/* If exec failed, send message to stderr */
    646 		(void) fprintf(stderr, "%s", msg);
    647 		return (-1);
    648 
    649 	default:	/* PARENT */
    650 		/* Wait for child to exit */
    651 		wait_pid = waitpid(child_pid, &child_status, 0);
    652 		if ((wait_pid < 0) && (errno == ECHILD))
    653 			return (0);
    654 		if ((wait_pid < 0) || (wait_pid != child_pid))
    655 			return (-1);
    656 		if (WIFEXITED(child_status))
    657 			return (WEXITSTATUS(child_status));
    658 		if (WIFSIGNALED(child_status))
    659 			return (WTERMSIG(child_status));
    660 		return (0);
    661 	}
    662 }
    663