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