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
55extern void audit_allocate_argv(int, int, char *[]);
56extern int audit_allocate_record(int);
57
58int system_labeled = 0;
59static int windowing = 0;
60static int wdwmsg(char *name, char *msg);
61
62static void
63usage(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
129void
130print_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
232char *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
243static char *
244getenvent(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
253int
254main(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 */
629static int
630wdwmsg(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