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