xref: /illumos-gate/usr/src/cmd/allocate/allocate.c (revision d583b39b)
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