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