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 #include <errno.h>
28 #include <locale.h>
29 #include <pwd.h>
30 #include <unistd.h>
31 #include <stdio.h>
32 #include <stdlib.h>
33 #include <string.h>
34 #include <ctype.h>
35 #include <fcntl.h>
36 #include <nss_dbdefs.h>
37 #include <sys/types.h>
38 #include <sys/stat.h>
39 #include <sys/wait.h>
40 #include <tsol/label.h>
41 #include <zone.h>
42 #include <bsm/devalloc.h>
43 #include "allocate.h"
44
45 #if !defined(TEXT_DOMAIN)
46 #define TEXT_DOMAIN "SUNW_OST_OSCMD"
47 #endif
48
49 #define ALLOC "allocate"
50 #define DEALLOC "deallocate"
51 #define LIST "list_devices"
52
53 extern void audit_allocate_argv(int, int, char *[]);
54 extern int audit_allocate_record(int);
55
56 int system_labeled = 0;
57 static int windowing = 0;
58 static int wdwmsg(char *name, char *msg);
59
60 static void
usage(int func)61 usage(int func)
62 {
63 if (system_labeled) {
64 char *use[6];
65
66 use[0] = gettext("allocate [-s] [-w] [-U uname] [-z zonename] "
67 "[-F] device|-g dev-type");
68 use[1] = gettext("deallocate [-s] [-w] [-z zonename] "
69 "[-F] device|-c dev-class|-g dev-type");
70 use[2] = gettext("deallocate [-s] [-w] [-z zonename] -I");
71 use[3] = gettext("list_devices [-s] [-U uid] [-z zonename] "
72 "[-a [-w]] -l|-n|-u [device]");
73 use[4] = gettext("list_devices [-s] [-U uid] [-z zonename] "
74 "[-a [-w]] [-l|-n|-u] -c dev-class");
75 use[5] = gettext("list_devices [-s] -d [dev-type]");
76
77 switch (func) {
78 case 0:
79 (void) fprintf(stderr, "%s\n", use[0]);
80 break;
81 case 1:
82 (void) fprintf(stderr, "%s\n%s\n",
83 use[1], use[2]);
84 break;
85 case 2:
86 (void) fprintf(stderr, "%s\n%s\n%s\n",
87 use[3], use[4], use[5]);
88 break;
89 default:
90 (void) fprintf(stderr,
91 "%s\n%s\n%s\n%s\n%s\n%s\n",
92 use[0], use[1], use[2], use[3], use[4],
93 use[5]);
94 }
95 } else {
96 char *use[5];
97
98 use[0] = gettext("allocate "
99 "[-s] [-U uname] [-F] device|-g dev-type");
100 use[1] = gettext("deallocate [-s] [-F] device|-c dev-class");
101 use[2] = gettext("deallocate [-s] -I");
102 use[3] = gettext("list_devices "
103 "[-s] [-U uid] -l|-n|-u [device]");
104 use[4] = gettext("list_devices "
105 "[-s] [-U uid] [-l|-n|-u] -c dev-class");
106
107 switch (func) {
108 case 0:
109 (void) fprintf(stderr, "%s\n", use[0]);
110 break;
111 case 1:
112 (void) fprintf(stderr, "%s\n%s\n",
113 use[1], use[2]);
114 break;
115 case 2:
116 (void) fprintf(stderr, "%s\n%s\n",
117 use[3], use[4]);
118 break;
119 default:
120 (void) fprintf(stderr, "%s\n%s\n%s\n%s\n%s\n",
121 use[0], use[1], use[2], use[3], use[4]);
122 }
123 }
124 exit(1);
125 }
126
127 void
print_error(int error,char * name)128 print_error(int error, char *name)
129 {
130 char *msg;
131 char msgbuf[200];
132
133 switch (error) {
134 case ALLOCUERR:
135 msg = gettext("Specified device is allocated to another user.");
136 break;
137 case CHOWNERR:
138 msg = gettext("Failed to chown.");
139 break;
140 case CLEANERR:
141 msg = gettext("Unable to clean up device.");
142 break;
143 case CNTDEXECERR:
144 msg = gettext(
145 "Can't exec device-clean program for specified device.");
146 break;
147 case CNTFRCERR:
148 msg = gettext("Can't force deallocate specified device.");
149 break;
150 case DACACCERR:
151 msg = gettext(
152 "Can't access DAC file for the device specified.");
153 break;
154 case DAOFFERR:
155 msg = gettext(
156 "Device allocation feature is not activated "
157 "on this system.");
158 break;
159 case DAUTHERR:
160 msg = gettext("Device not allocatable.");
161 break;
162 case DEFATTRSERR:
163 msg = gettext("No default attributes for specified "
164 "device type.");
165 break;
166 case DEVLKERR:
167 msg = gettext("Concurrent operations for specified device, "
168 "try later.");
169 break;
170 case DEVLONGERR:
171 msg = gettext("Device name is too long.");
172 break;
173 case DEVNALLOCERR:
174 msg = gettext("Device not allocated.");
175 break;
176 case DEVNAMEERR:
177 msg = gettext("Device name error.");
178 break;
179 case DEVSTATEERR:
180 msg = gettext("Device specified is in allocate error state.");
181 break;
182 case DEVZONEERR:
183 msg = gettext("Can't find name of the zone to which "
184 "device is allocated.");
185 break;
186 case DSPMISSERR:
187 msg = gettext(
188 "Device special file(s) missing for specified device.");
189 break;
190 case LABELRNGERR:
191 msg = gettext(
192 "Operation inconsistent with device's label range.");
193 break;
194 case LOGINDEVPERMERR:
195 msg = gettext("Device controlled by logindevperm(5)");
196 break;
197 case NODAERR:
198 msg = gettext("No entry for specified device.");
199 break;
200 case NODMAPERR:
201 msg = gettext("No entry for specified device.");
202 break;
203 case PREALLOCERR:
204 msg = gettext("Device already allocated.");
205 break;
206 case SETACLERR:
207 msg = gettext("Failed to set ACL.");
208 break;
209 case UAUTHERR:
210 msg = gettext(
211 "User lacks authorization required for this operation.");
212 break;
213 case ZONEERR:
214 msg = gettext("Failed to configure device in zone.");
215 break;
216 default:
217 msg = gettext("Unknown error code.");
218 break;
219 }
220
221 if (windowing) {
222 (void) snprintf(msgbuf, sizeof (msgbuf), "%s: %s\n", name, msg);
223 (void) wdwmsg(name, msgbuf);
224 } else {
225 (void) fprintf(stderr, "%s: %s\n", name, msg);
226 (void) fflush(stderr);
227 }
228 }
229
230 char *newenv[] = {"PATH=/usr/bin:/usr/sbin",
231 NULL, /* for LC_ALL */
232 NULL, /* for LC_COLLATE */
233 NULL, /* for LC_CTYPE */
234 NULL, /* for LC_MESSAGES */
235 NULL, /* for LC_NUMERIC */
236 NULL, /* for LC_TIME */
237 NULL, /* for LANG */
238 NULL
239 };
240
241 static char *
getenvent(char * name,char * env[])242 getenvent(char *name, char *env[])
243 {
244 for (; *env != NULL; env++) {
245 if (strncmp(*env, name, strlen(name)) == 0)
246 return (*env);
247 }
248 return (NULL);
249 }
250
251 int
main(int argc,char * argv[],char * envp[])252 main(int argc, char *argv[], char *envp[])
253 {
254 char *name, *env;
255 int func = -1, optflg = 0, error = 0, c;
256 zoneid_t zoneid;
257 uid_t uid;
258 char *uname = NULL, *device = NULL, *zonename = NULL;
259 char *zname;
260 char pw_buf[NSS_BUFLEN_PASSWD];
261 struct passwd pw_ent;
262 int env_num = 1; /* PATH= is 0 entry */
263 #ifdef DEBUG
264 struct stat statbuf;
265 #endif
266
267 (void) setlocale(LC_ALL, "");
268 (void) textdomain(TEXT_DOMAIN);
269
270 system_labeled = is_system_labeled();
271
272 /* test hook: see also mkdevalloc.c and devfsadm.c */
273 if (!system_labeled) {
274 system_labeled = is_system_labeled_debug(&statbuf);
275 if (system_labeled) {
276 fprintf(stderr, "/ALLOCATE_FORCE_LABEL is set,\n"
277 "forcing system label on for testing...\n");
278 }
279 }
280
281 /*
282 * get all enviroment variables
283 * which affect on internationalization.
284 */
285 env = getenvent("LC_ALL=", envp);
286 if (env != NULL)
287 newenv[env_num++] = env;
288 env = getenvent("LC_COLLATE=", envp);
289 if (env != NULL)
290 newenv[env_num++] = env;
291 env = getenvent("LC_CTYPE=", envp);
292 if (env != NULL)
293 newenv[env_num++] = env;
294 env = getenvent("LC_MESSAGES=", envp);
295 if (env != NULL)
296 newenv[env_num++] = env;
297 env = getenvent("LC_NUMERIC=", envp);
298 if (env != NULL)
299 newenv[env_num++] = env;
300 env = getenvent("LC_TIME=", envp);
301 if (env != NULL)
302 newenv[env_num++] = env;
303 env = getenvent("LANG=", envp);
304 if (env != NULL)
305 newenv[env_num] = env;
306
307 if ((name = strrchr(argv[0], '/')) == NULL)
308 name = argv[0];
309 else
310 name++;
311
312 if (strcmp(name, ALLOC) == 0)
313 func = 0;
314 else if (strcmp(name, DEALLOC) == 0)
315 func = 1;
316 else if (strcmp(name, LIST) == 0)
317 func = 2;
318 else
319 usage(-1);
320
321 audit_allocate_argv(func, argc, argv);
322
323 if (system_labeled) {
324 /*
325 * allocate, deallocate, list_devices run in
326 * global zone only.
327 */
328 zoneid = getzoneid();
329 if (zoneid != GLOBAL_ZONEID)
330 exit(GLOBALERR);
331 zname = GLOBAL_ZONENAME;
332 /*
333 * check if device allocation is activated.
334 */
335 if (da_is_on() == 0) {
336 (void) fprintf(stderr, "%s%s",
337 gettext("Turn device allocation on"),
338 gettext(" to use this feature.\n"));
339 exit(DAOFFERR);
340 }
341 }
342
343 if (func == 0) { /* allocate */
344 while ((c = getopt(argc, argv, "g:swz:FU:")) != -1) {
345 switch (c) {
346 case 'g':
347 optflg |= TYPE;
348 device = optarg;
349 break;
350 case 's':
351 optflg |= SILENT;
352 break;
353 case 'w':
354 if (system_labeled) {
355 optflg |= WINDOWING;
356 windowing = 1;
357 } else {
358 usage(func);
359 }
360 break;
361 case 'z':
362 if (system_labeled) {
363 optflg |= ZONENAME;
364 zonename = optarg;
365 } else {
366 usage(func);
367 }
368 break;
369 case 'F':
370 optflg |= FORCE;
371 break;
372 case 'U':
373 optflg |= USERNAME;
374 uname = optarg;
375 break;
376 case '?':
377 default :
378 usage(func);
379 }
380 }
381
382 /*
383 * allocate(1) must be supplied with one device argument
384 */
385 if (device && ((argc - optind) >= 1))
386 usage(func);
387 if (device == NULL) {
388 if ((argc - optind) != 1)
389 usage(func);
390 device = argv[optind];
391 }
392 }
393
394 else if (func == 1) { /* deallocate */
395 while ((c = getopt(argc, argv, "c:g:swz:FI")) != -1) {
396 switch (c) {
397 case 'c':
398 if (optflg & (TYPE | FORCE_ALL))
399 usage(func);
400 optflg |= CLASS;
401 device = optarg;
402 break;
403 case 'g':
404 if (system_labeled) {
405 if (optflg & (CLASS | FORCE_ALL))
406 usage(func);
407 optflg |= TYPE;
408 device = optarg;
409 } else {
410 usage(func);
411 }
412 break;
413 case 's':
414 optflg |= SILENT;
415 break;
416 case 'w':
417 if (system_labeled) {
418 optflg |= WINDOWING;
419 windowing = 1;
420 } else {
421 usage(func);
422 }
423 break;
424 case 'z':
425 if (system_labeled) {
426 optflg |= ZONENAME;
427 zonename = optarg;
428 } else {
429 usage(func);
430 }
431 break;
432 case 'F':
433 if (optflg & FORCE_ALL)
434 usage(func);
435 optflg |= FORCE;
436 break;
437 case 'I':
438 if (optflg & (CLASS | TYPE | FORCE))
439 usage(func);
440 optflg |= FORCE_ALL;
441 break;
442 case '?':
443 default :
444 usage(func);
445 }
446 }
447
448 /*
449 * deallocate(1) must be supplied with one device
450 * argument unless the '-I' argument is supplied
451 */
452 if (device || (optflg & FORCE_ALL)) {
453 if ((argc - optind) >= 1)
454 usage(func);
455 } else if (device == NULL) {
456 if ((argc - optind) != 1)
457 usage(func);
458 device = argv[optind];
459 }
460 }
461
462 else if (func == 2) { /* list_devices */
463 while ((c = getopt(argc, argv, "ac:dlnsuwz:U:")) != -1) {
464 switch (c) {
465 case 'a':
466 if (system_labeled) {
467 /*
468 * list auths, cleaning programs,
469 * labels.
470 */
471 if (optflg & LISTDEFS)
472 usage(func);
473 optflg |= LISTATTRS;
474 } else {
475 usage(func);
476 }
477 break;
478 case 'c':
479 optflg |= CLASS;
480 device = optarg;
481 break;
482 case 'd':
483 if (system_labeled) {
484 /*
485 * List devalloc_defaults
486 * This cannot used with anything other
487 * than -s.
488 */
489 if (optflg & (LISTATTRS | CLASS |
490 LISTALL | LISTFREE | LISTALLOC |
491 WINDOWING | ZONENAME | USERID))
492 usage(func);
493 optflg |= LISTDEFS;
494 } else {
495 usage(func);
496 }
497 break;
498 case 'l':
499 if (optflg & (LISTFREE | LISTALLOC | LISTDEFS))
500 usage(func);
501 optflg |= LISTALL;
502 break;
503 case 'n':
504 if (optflg & (LISTALL | LISTALLOC | LISTDEFS))
505 usage(func);
506 optflg |= LISTFREE;
507 break;
508 case 's':
509 optflg |= SILENT;
510 break;
511 case 'u':
512 if (optflg & (LISTALL | LISTFREE | LISTDEFS))
513 usage(func);
514 optflg |= LISTALLOC;
515 break;
516 case 'w':
517 if (system_labeled) {
518 if (optflg & LISTDEFS)
519 usage(func);
520 optflg |= WINDOWING;
521 } else {
522 usage(func);
523 }
524 break;
525 case 'z':
526 if (system_labeled) {
527 if (optflg & LISTDEFS)
528 usage(func);
529 optflg |= ZONENAME;
530 zonename = optarg;
531 } else {
532 usage(func);
533 }
534 break;
535 case 'U':
536 if (optflg & LISTDEFS)
537 usage(func);
538 optflg |= USERID;
539 uid = atoi(optarg);
540 break;
541 case '?':
542 default :
543 usage(func);
544 }
545 }
546
547 if (system_labeled) {
548 if (!(optflg & (LISTALL | LISTFREE | LISTALLOC |
549 LISTDEFS | WINDOWING))) {
550 if (!(optflg & CLASS))
551 usage(func);
552 }
553 } else if (!(optflg & (LISTALL | LISTFREE | LISTALLOC))) {
554 if (!(optflg & CLASS))
555 usage(func);
556 }
557
558 /*
559 * list_devices(1) takes an optional device argument.
560 */
561 if (device && ((argc - optind) >= 1))
562 usage(func);
563 if (device == NULL) {
564 if ((argc - optind) == 1)
565 device = argv[optind];
566 else if ((argc - optind) > 1)
567 usage(func);
568 }
569 }
570
571 if (optflg & USERNAME) {
572 if (getpwnam_r(uname, &pw_ent, pw_buf, sizeof (pw_buf)) ==
573 NULL) {
574 (void) fprintf(stderr,
575 gettext("Invalid user name -- %s -- \n"), uname);
576 exit(1);
577 }
578 uid = pw_ent.pw_uid;
579 } else if (optflg & USERID) {
580 if (getpwuid_r(uid, &pw_ent, pw_buf, sizeof (pw_buf)) == NULL) {
581 (void) fprintf(stderr,
582 gettext("Invalid user ID -- %d -- \n"), uid);
583 exit(1);
584 }
585 uid = pw_ent.pw_uid;
586 } else {
587 /*
588 * caller's uid is the default if no user specified.
589 */
590 uid = getuid();
591 }
592
593 /*
594 * global zone is the default if no zonename specified.
595 */
596 if (zonename == NULL) {
597 zonename = zname;
598 } else {
599 if (zone_get_id(zonename, &zoneid) != 0) {
600 (void) fprintf(stderr,
601 gettext("Invalid zone name -- %s -- \n"), zonename);
602 exit(1);
603 }
604 }
605
606 if (func == 0)
607 error = allocate(optflg, uid, device, zonename);
608 else if (func == 1)
609 error = deallocate(optflg, uid, device, zonename);
610 else if (func == 2)
611 error = list_devices(optflg, uid, device, zonename);
612
613 (void) audit_allocate_record(error);
614
615 if (error) {
616 if (!(optflg & SILENT))
617 print_error(error, name);
618 exit(error);
619 }
620
621 return (0);
622 }
623
624 /*
625 * Display error message via /etc/security/lib/wdwmsg script
626 */
627 static int
wdwmsg(char * name,char * msg)628 wdwmsg(char *name, char *msg)
629 {
630 pid_t child_pid;
631 pid_t wait_pid;
632 int child_status;
633
634 /* Fork a child */
635 switch (child_pid = fork()) {
636 case -1: /* FAILURE */
637 return (-1);
638 break;
639
640 case 0: /* CHILD */
641 (void) execl("/etc/security/lib/wdwmsg", "wdwmsg", msg,
642 name, "OK", NULL);
643 /* If exec failed, send message to stderr */
644 (void) fprintf(stderr, "%s", msg);
645 return (-1);
646
647 default: /* PARENT */
648 /* Wait for child to exit */
649 wait_pid = waitpid(child_pid, &child_status, 0);
650 if ((wait_pid < 0) && (errno == ECHILD))
651 return (0);
652 if ((wait_pid < 0) || (wait_pid != child_pid))
653 return (-1);
654 if (WIFEXITED(child_status))
655 return (WEXITSTATUS(child_status));
656 if (WIFSIGNALED(child_status))
657 return (WTERMSIG(child_status));
658 return (0);
659 }
660 }
661