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