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/*
30 * add_allocatable -
31 *	a command-line interface to add device to device_allocate and
32 *	device_maps.
33 */
34
35#ifndef	__EXTENSIONS__
36#define	__EXTENSIONS__		/* needed for _strtok_r */
37#endif
38
39#include <sys/types.h>
40#include <unistd.h>
41#include <stdlib.h>
42#include <strings.h>
43#include <string.h>
44#include <locale.h>
45#include <libintl.h>
46#include <pwd.h>
47#include <nss_dbdefs.h>
48#include <auth_attr.h>
49#include <auth_list.h>
50#include <zone.h>
51#include <tsol/label.h>
52#include <bsm/devices.h>
53#include <bsm/devalloc.h>
54
55#define	NO_OVERRIDE	-1
56
57int check_args(da_args *);
58int process_args(int, char **, da_args *, char *);
59int scan_label(char *, char *);
60void usage(da_args *, char *);
61
62int system_labeled = 0;
63
64int
65main(int argc, char *argv[])
66{
67	int		rc;
68	uid_t		uid;
69	char		*progname;
70	char		pwbuf[NSS_LINELEN_PASSWD];
71	struct passwd	pwd;
72	da_args		dargs;
73	devinfo_t	devinfo;
74
75	(void) setlocale(LC_ALL, "");
76#if !defined(TEXT_DOMAIN)
77#define	TEXT_DOMAIN	"SYS_TEST"
78#endif
79	(void) textdomain(TEXT_DOMAIN);
80	if ((progname = strrchr(argv[0], '/')) == NULL)
81		progname = argv[0];
82	else
83		progname++;
84
85	system_labeled = is_system_labeled();
86	if (system_labeled) {
87		/*
88		 * this command can be run only in the global zone.
89		 */
90		if (getzoneid() != GLOBAL_ZONEID) {
91			(void) fprintf(stderr, "%s%s", progname,
92			    gettext(" : must be run in global zone\n"));
93			exit(1);
94		}
95	} else {
96		/*
97		 * this command works in Trusted Extensions only.
98		 */
99		(void) fprintf(stderr, "%s%s", progname,
100		    gettext(" : need to install Trusted Extensions\n"));
101		exit(1);
102	}
103
104	dargs.optflag = 0;
105	dargs.rootdir = NULL;
106	dargs.devnames = NULL;
107	dargs.devinfo = &devinfo;
108
109	if (strcmp(progname, "add_allocatable") == 0) {
110		dargs.optflag |= DA_ADD;
111	} else if (strcmp(progname, "remove_allocatable") == 0) {
112		dargs.optflag |= DA_REMOVE;
113	} else {
114		usage(&dargs, progname);
115		exit(1);
116	}
117
118	uid = getuid();
119	if ((getpwuid_r(uid, &pwd, pwbuf, sizeof (pwbuf))) == NULL) {
120		(void) fprintf(stderr, "%s%s", progname,
121		    gettext(" : getpwuid_r failed: "));
122		(void) fprintf(stderr, "%s\n", strerror(errno));
123		exit(2);
124	}
125
126	if (chkauthattr(DEVICE_CONFIG_AUTH, pwd.pw_name) != 1) {
127		(void) fprintf(stderr, "%s%s%s", progname,
128		    gettext(" : user lacks authorization:  \n"),
129		    DEVICE_CONFIG_AUTH);
130		exit(4);
131	}
132
133	if (process_args(argc, argv, &dargs, progname) != 0) {
134		usage(&dargs, progname);
135		exit(1);
136	}
137
138	if (dargs.optflag & DA_ADD) {
139		if (check_args(&dargs) == NO_OVERRIDE) {
140			(void) fprintf(stderr, "%s%s%s%s", progname,
141			    gettext(" : entry exists for "),
142			    dargs.devinfo->devname, gettext("\n"));
143			usage(&dargs, progname);
144			exit(3);
145		}
146	}
147
148	if (dargs.optflag & DA_DEFATTRS)
149		rc = da_update_defattrs(&dargs);
150	else
151		rc = da_update_device(&dargs);
152
153	if ((rc != 0) && (!(dargs.optflag & DA_SILENT))) {
154		if (rc == -2)
155			(void) fprintf(stderr, "%s%s", progname,
156			    gettext(" : device name/type/list missing\n"));
157		else if (dargs.optflag & DA_ADD)
158			(void) fprintf(stderr, "%s%s", progname,
159			    gettext(" : error adding/updating device\n"));
160		else if (dargs.optflag & DA_REMOVE)
161			(void) fprintf(stderr, "%s%s", progname,
162			    gettext(" : error removing device\n"));
163		rc = 2;	/* exit code for 'Unknown system error' in man page */
164	}
165
166	return (rc);
167}
168
169int
170process_args(int argc, char **argv, da_args *dargs, char *progname)
171{
172	int 		c;
173	int		aflag, cflag, dflag, fflag, lflag, nflag, oflag, tflag;
174	extern char	*optarg;
175	devinfo_t	*devinfo;
176
177	devinfo = dargs->devinfo;
178	aflag = cflag = dflag = fflag = lflag = nflag = oflag = tflag = 0;
179	devinfo->devname = devinfo->devtype = devinfo->devauths =
180	    devinfo->devexec = devinfo->devopts = devinfo->devlist = NULL;
181	devinfo->instance = 0;
182
183	while ((c = getopt(argc, argv, "a:c:dfl:n:o:st:")) != EOF) {
184		switch (c) {
185		case 'a':
186			devinfo->devauths = optarg;
187			aflag++;
188			break;
189		case 'c':
190			devinfo->devexec = optarg;
191			if (strlen(devinfo->devexec) == 0) {
192				if (!(dargs->optflag & DA_SILENT))
193					(void) fprintf(stderr, "%s%s", progname,
194					    gettext(" : device clean program"
195					    " name not found\n"));
196				return (1);
197			}
198			cflag++;
199			break;
200		case 'd':
201			dargs->optflag |= DA_DEFATTRS;
202			dflag++;
203			break;
204		case 'l':
205			devinfo->devlist = optarg;
206			if (strlen(devinfo->devlist) == 0) {
207				if (!(dargs->optflag & DA_SILENT))
208					(void) fprintf(stderr, "%s%s", progname,
209					    gettext(" : device file list"
210					    " not found\n"));
211				return (1);
212			}
213			lflag++;
214			break;
215		case 'f':
216			dargs->optflag |= DA_FORCE;
217			fflag++;
218			break;
219		case 'n':
220			devinfo->devname = optarg;
221			if (strlen(devinfo->devname) == 0) {
222				if (!(dargs->optflag & DA_SILENT))
223					(void) fprintf(stderr, "%s%s", progname,
224					    gettext(" : device name "
225					    "not found\n"));
226				return (1);
227			}
228			nflag++;
229			break;
230		case 'o':
231			/* check for field delimiters in the option */
232			if (strpbrk(optarg, ":;=") == NULL) {
233				if (!(dargs->optflag & DA_SILENT)) {
234					(void) fprintf(stderr, "%s%s%s",
235					    progname,
236					    gettext(" : invalid "
237					    "key=val string: "),
238					    optarg);
239					(void) fprintf(stderr, "%s",
240					    gettext("\n"));
241				}
242				return (1);
243			}
244			devinfo->devopts = optarg;
245			if (dargs->optflag & DA_ADD) {
246				if (scan_label(devinfo->devopts, progname) != 0)
247					return (1);
248			}
249			oflag++;
250			break;
251		case 's':
252			dargs->optflag |= DA_SILENT;
253			break;
254		case 't':
255			devinfo->devtype = optarg;
256			if (strlen(devinfo->devtype) == 0) {
257				if (!(dargs->optflag & DA_SILENT))
258					(void) fprintf(stderr, "%s%s", progname,
259					    gettext(" : device type "
260					    "not found\n"));
261				return (1);
262			}
263			tflag++;
264			break;
265		default	:
266			return (1);
267		}
268	}
269
270
271	if (dargs->optflag & DA_ADD) {
272		if (dflag) {
273			/* -d requires -t, but does not like -n */
274			if (nflag || tflag == 0)
275				return (1);
276		} else if (nflag == 0 && tflag == 0 && lflag == 0) {
277			/* require at least -n or -t or -l to be specified */
278			if (!(dargs->optflag & DA_SILENT))
279				(void) fprintf(stderr, "%s%s", progname,
280				    gettext(" : required options missing\n"));
281			return (1);
282		}
283	} else if (dargs->optflag & DA_REMOVE) {
284		if (dflag) {
285			/* -d requires -t, but does not like -n */
286			if (nflag || tflag == 0)
287				return (1);
288		} else if (nflag == 0 && tflag == 0) {
289			/* require at least -n or -t to be specified */
290			if (!(dargs->optflag & DA_SILENT))
291				(void) fprintf(stderr, "%s%s", progname,
292				    gettext(" : required options missing\n"));
293			return (1);
294		}
295		/* there's a bunch not accepted by remove_allocatable */
296		if (aflag || cflag || lflag || oflag)
297			return (1);
298	} else {
299		return (1);
300	}
301
302	/* check for option specified more than once */
303	if (aflag > 1 || cflag > 1 || lflag > 1 || fflag > 1 ||
304	    nflag > 1 || tflag > 1) {
305		if (!(dargs->optflag & DA_SILENT))
306			(void) fprintf(stderr, "%s%s", progname,
307			    gettext(" : multiple-defined options\n"));
308		return (1);
309	}
310
311	return (0);
312}
313
314int
315verify_label(char *token, char *progname)
316{
317	int		error = 0;
318	char		*p, *val, *str;
319
320	if ((strstr(token, DAOPT_MINLABEL) == NULL) &&
321	    (strstr(token, DAOPT_MAXLABEL) == NULL)) {
322		/* no label specified */
323		return (0);
324	}
325	if ((val = strchr(token, '=')) == NULL)
326		return (1);
327	val++;
328	/*
329	 * if non-default labels are specified, check if they are correct
330	 */
331	if ((strcmp(val, DA_DEFAULT_MIN) != 0) &&
332	    (strcmp(val, DA_DEFAULT_MAX) != 0)) {
333		m_label_t	*slabel = NULL;
334
335		str = strdup(val);
336		/* get rid of double quotes if they exist */
337		while (*str == '"')
338			str++;
339		if ((p = strchr(str, '"')) != NULL)
340			*p = '\0';
341		if (str_to_label(str, &slabel, MAC_LABEL, L_NO_CORRECTION,
342		    &error) == -1) {
343			(void) fprintf(stderr, "%s%s%s", progname,
344			    gettext(" : bad label input: "),
345			    val);
346			(void) fprintf(stderr, "%s", gettext("\n"));
347			free(str);
348			m_label_free(slabel);
349			return (1);
350		}
351		free(str);
352		m_label_free(slabel);
353	}
354
355	return (0);
356}
357
358int
359scan_label(char *devopts, char *progname)
360{
361	char		*tok = NULL;
362	char		*lasts, *optsarg;
363
364	if (devopts == NULL)
365		return (0);
366
367	if ((optsarg = strdup(devopts)) == NULL)
368		return (1);
369
370	if ((tok = strtok_r(optsarg, KV_TOKEN_DELIMIT, &lasts)) == NULL)
371		return (1);
372
373	if (verify_label(tok, progname) != 0) {
374		free(optsarg);
375		return (1);
376	}
377
378	while ((tok = strtok_r(NULL, KV_TOKEN_DELIMIT, &lasts)) != NULL) {
379		if (verify_label(tok, progname) != 0) {
380			free(optsarg);
381			return (1);
382		}
383	}
384
385	return (0);
386}
387
388int
389check_args(da_args *dargs)
390{
391	int		nlen;
392	char		*kval, *nopts, *ntok, *nstr,
393	    *defmin, *defmax, *defauths, *defexec;
394	kva_t		*kva;
395	devinfo_t	*devinfo;
396	devalloc_t	*da = NULL;
397	da_defs_t	*da_defs = NULL;
398
399	devinfo = dargs->devinfo;
400	/*
401	 * check if we're updating an existing entry without -f
402	 */
403	setdaent();
404	da = getdanam(devinfo->devname);
405	enddaent();
406	if (da && !(dargs->optflag & DA_FORCE)) {
407		freedaent(da);
408		return (NO_OVERRIDE);
409	}
410	if ((devinfo->devopts == NULL) ||
411	    (strstr(devinfo->devopts, DAOPT_MINLABEL) == NULL) ||
412	    (strstr(devinfo->devopts, DAOPT_MAXLABEL) == NULL) ||
413	    (devinfo->devauths == NULL) ||
414	    (devinfo->devexec == NULL)) {
415		/* fill in defaults as required */
416		defmin = DA_DEFAULT_MIN;
417		defmax = DA_DEFAULT_MAX;
418		defauths = DEFAULT_DEV_ALLOC_AUTH;
419		defexec = DA_DEFAULT_CLEAN;
420		setdadefent();
421		if (da_defs = getdadeftype(devinfo->devtype)) {
422			kva = da_defs->devopts;
423			if ((kval = kva_match(kva, DAOPT_MINLABEL)) != NULL)
424				defmin = strdup(kval);
425			if ((kval = kva_match(kva, DAOPT_MAXLABEL)) != NULL)
426				defmax = strdup(kval);
427			if ((kval = kva_match(kva, DAOPT_AUTHS)) != NULL)
428				defauths = strdup(kval);
429			if ((kval = kva_match(kva, DAOPT_CSCRIPT)) != NULL)
430				defexec = strdup(kval);
431			freedadefent(da_defs);
432		}
433		enddadefent();
434		if (devinfo->devauths == NULL)
435			devinfo->devauths = defauths;
436		if (devinfo->devexec == NULL)
437			devinfo->devexec = defexec;
438		if (devinfo->devopts == NULL) {
439			/* add default minlabel and maxlabel */
440			nlen = strlen(DAOPT_MINLABEL) + strlen(KV_ASSIGN) +
441			    strlen(defmin) + strlen(KV_TOKEN_DELIMIT) +
442			    strlen(DAOPT_MAXLABEL) + strlen(KV_ASSIGN) +
443			    strlen(defmax) + 1;		/* +1 for terminator */
444			if (nopts = (char *)malloc(nlen)) {
445				(void) snprintf(nopts, nlen, "%s%s%s%s%s%s%s",
446				    DAOPT_MINLABEL, KV_ASSIGN, defmin,
447				    KV_TOKEN_DELIMIT,
448				    DAOPT_MAXLABEL, KV_ASSIGN, defmax);
449				devinfo->devopts = nopts;
450			}
451		} else {
452			if (strstr(devinfo->devopts, DAOPT_MINLABEL) == NULL) {
453				/* add default minlabel */
454				ntok = DAOPT_MINLABEL;
455				nstr = defmin;
456				nlen = strlen(devinfo->devopts) +
457				    strlen(KV_TOKEN_DELIMIT) +
458				    strlen(ntok) + strlen(KV_ASSIGN) +
459				    strlen(nstr) + 1;
460				if (nopts = (char *)malloc(nlen)) {
461					(void) snprintf(nopts, nlen,
462					    "%s%s%s%s%s",
463					    devinfo->devopts, KV_TOKEN_DELIMIT,
464					    ntok, KV_ASSIGN, nstr);
465					devinfo->devopts = nopts;
466				}
467			}
468			if (strstr(devinfo->devopts, DAOPT_MAXLABEL) == NULL) {
469				/* add default maxlabel */
470				ntok = DAOPT_MAXLABEL;
471				nstr = defmax;
472				nlen = strlen(devinfo->devopts) +
473				    strlen(KV_TOKEN_DELIMIT) +
474				    strlen(ntok) + strlen(KV_ASSIGN) +
475				    strlen(nstr) + 1;
476				if (nopts = (char *)malloc(nlen)) {
477					(void) snprintf(nopts, nlen,
478					    "%s%s%s%s%s",
479					    devinfo->devopts, KV_TOKEN_DELIMIT,
480					    ntok, KV_ASSIGN, nstr);
481					devinfo->devopts = nopts;
482				}
483			}
484		}
485	}
486
487	return (0);
488}
489
490void
491usage(da_args *dargs, char *progname)
492{
493	if (dargs->optflag & DA_SILENT)
494		return;
495	if (dargs->optflag & DA_ADD)
496		(void) fprintf(stderr, "%s%s%s", gettext("Usage: "), progname,
497		    gettext(" [-f][-s][-d] -n name -t type -l device-list"
498		    "\n\t[-a authorization] [-c cleaning program] "
499		    "[-o key=value]\n"));
500	else if (dargs->optflag & DA_REMOVE)
501		(void) fprintf(stderr, "%s%s%s", gettext("Usage: "), progname,
502		    gettext(" [-f][-s][-d] [-n name|-t type]\n"));
503	else
504		(void) fprintf(stderr, gettext("Invalid usage\n"), progname);
505}
506