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