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 
57 int check_args(da_args *);
58 int process_args(int, char **, da_args *, char *);
59 int scan_label(char *, char *);
60 void usage(da_args *, char *);
61 
62 int system_labeled = 0;
63 
64 int
65 main(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 
169 int
170 process_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 
314 int
315 verify_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 
358 int
359 scan_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 
388 int
389 check_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 
490 void
491 usage(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