4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License, Version 1.0 only
6 * (the "License"). You may not use this file except in compliance
9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10 * or http://www.opensolaris.org/os/licensing.
11 * See the License for the specific language governing permissions
12 * and limitations under the License.
14 * When distributing Covered Code, include this CDDL HEADER in each
15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16 * If applicable, add the following below this CDDL HEADER, with the
17 * fields enclosed by brackets "[]" replaced with your own identifying
18 * information: Portions Copyright [yyyy] [name of copyright owner]
23 * Copyright 2003 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
26 * Device policy specific subroutines. We cannot merge them with
27 * drvsubr.c because of static linking requirements.
41 #include <sys/modctl.h>
42 #include <sys/devpolicy.h>
44 #include <sys/sysmacros.h>
51 const priv_impl_info_t
*privimplinfo
;
54 * New token types should be parsed in parse_plcy_entry.
58 typedef struct token
{
64 static token_t toktab
[] = {
65 { DEVPLCY_TKN_RDP
, PSET
/* offsetof(devplcysys_t, dps_rdp) */ },
66 { DEVPLCY_TKN_WRP
, PSET
/* offsetof(devplcysys_t, dps_wrp) */ },
72 #define NTOK (sizeof (toktab)/sizeof (token_t))
75 * Compute the size of the datastructures needed.
80 if ((privimplinfo
= getprivimplinfo()) == NULL
) {
81 (void) fprintf(stderr
, gettext(ERR_PRIVIMPL
));
85 devplcysys_sz
= DEVPLCYSYS_SZ(privimplinfo
);
88 (char *)DEVPLCYSYS_RDP((devplcysys_t
*)0, privimplinfo
) -
91 (char *)DEVPLCYSYS_WRP((devplcysys_t
*)0, privimplinfo
) -
96 * Read a configuration file line and return a static buffer pointing to it.
97 * It returns a static struct fileentry which has several fields:
98 * - rawbuf, which includes the lines including empty lines and comments
99 * leading up to the file and the entry as found in the file
100 * - orgentry, pointer in rawbuf to the start of the entry proper.
101 * - entry, a pre-parsed entry, escaped newlines removed.
102 * - startline, the line number of the first line in the file
107 static size_t sz
= BUFSIZ
;
108 static struct fileentry fe
;
109 static int linecnt
= 1;
111 char *buf
= fe
.rawbuf
;
117 fe
.rawbuf
= buf
= malloc(sz
);
121 if (fe
.entry
!= NULL
) {
123 fe
.orgentry
= fe
.entry
= NULL
;
130 while (lastc
= c
, (c
= getc(fp
)) != EOF
) {
135 fe
.rawbuf
= buf
= realloc(buf
, sz
);
142 /* Newline, escaped or not yet processing an entry */
143 if (off
== -1 || lastc
== '\\')
145 } else if (lastc
== '\n' && off
== -1) {
146 /* Start of more comments */
149 /* Found start of entry */
151 fe
.startline
= linecnt
;
157 fe
.orgentry
= buf
+ off
;
158 p
= fe
.entry
= strdup(fe
.orgentry
);
163 /* Remove <backslash><newline> */
164 if ((p
= strchr(p
, '\\')) != NULL
) {
165 for (off
= 0; (p
[-off
] = p
[0]) != '\0'; p
++)
166 if (p
[0] == '\\' && p
[1] == '\n') {
173 if (lastc
!= '\n' || off
!= -1)
181 * Parse minor number ranges:
182 * (minor) or (lowminor-highminor)
183 * Return 0 for success, -1 for failure.
186 parse_minor_range(const char *range
, minor_t
*lo
, minor_t
*hi
, char *type
)
195 tmp
= strtoul(range
, &p
, 0);
196 if (tmp
> L_MAXMIN32
|| (tmp
== 0 && errno
!= 0) ||
197 (*p
!= '-' && *p
!= ')'))
202 tmp
= strtoul(p
+ 1, &p
, 0);
203 if (tmp
> L_MAXMIN32
|| (tmp
== 0 && errno
!= 0) || *p
!= ')')
229 put_minor_range(FILE *fp
, fileentry_t
*old
, const char *devn
, const char *tail
,
230 minor_t lo
, minor_t hi
, char type
)
232 /* Preserve preceeding comments */
233 if (old
!= NULL
&& old
->rawbuf
!= old
->orgentry
)
234 (void) fwrite(old
->rawbuf
, 1, old
->orgentry
- old
->rawbuf
, fp
);
237 put_minor_range(fp
, NULL
, devn
, tail
, lo
, hi
, 'b');
238 put_minor_range(fp
, NULL
, devn
, tail
, lo
, hi
, 'c');
239 } else if (lo
== hi
) {
240 (void) fprintf(fp
, "%s:(%d)%c%s", devn
, (int)lo
, type
, tail
);
242 (void) fprintf(fp
, "%s:(%d-%d)%c%s", devn
, (int)lo
, (int)hi
,
248 delete_one_entry(const char *filename
, const char *entry
)
250 char tfile
[MAXPATHLEN
];
251 char ofile
[MAXPATHLEN
];
263 mpart
= strchr(entry
, ':');
271 if (parse_minor_range(mpart
, &rlo
, &rhi
, &rtype
) != 0)
279 if (strlen(filename
) + sizeof (XEND
) > sizeof (tfile
))
282 old
= fopen(filename
, "r");
287 (void) snprintf(tfile
, sizeof (tfile
), "%s%s", filename
, XEND
);
288 (void) snprintf(ofile
, sizeof (ofile
), "%s%s", filename
, ".old");
290 nfile
= mktemp(tfile
);
292 new = fopen(nfile
, "w");
300 /* Copy permissions, ownership */
301 if (fstat(fileno(old
), &buf
) == 0) {
302 (void) fchown(newfd
, buf
.st_uid
, buf
.st_gid
);
303 (void) fchmod(newfd
, buf
.st_mode
);
305 (void) fchown(newfd
, 0, 3); /* root:sys */
306 (void) fchmod(newfd
, 0644);
309 while ((fep
= fgetline(old
))) {
316 /* Trailing comments */
317 if (fep
->entry
== NULL
) {
318 (void) fputs(fep
->rawbuf
, new);
323 while (*tok
&& isspace(*tok
))
327 (void) fputs(fep
->rawbuf
, new);
331 /* Make sure we can recover the remainder incl. whitespace */
332 tail
= strpbrk(tok
, "\t\n ");
334 tail
= tok
+ strlen(tok
);
338 min
= strchr(tok
, ':');
339 if (min
&& (delall
|| delrange
))
348 * Delete or shrink overlapping ranges.
350 if (strncmp(entry
, tok
, len
) == 0 &&
353 parse_minor_range(min
, &lo
, &hi
, &type
) == 0 &&
354 (type
== rtype
|| rtype
== '\0') &&
355 lo
<= rhi
&& hi
>= rlo
) {
356 minor_t newlo
, newhi
;
358 /* Complete overlap, then drop it. */
359 if (lo
>= rlo
&& hi
<= rhi
)
362 /* Partial overlap, shrink range */
372 /* restore NULed character */
378 * We have two ranges:
379 * lo ... newhi (== rlo - 1)
380 * newlo (== rhi + 1) .. hi
382 put_minor_range(new, fep
, tok
, tail
,
384 put_minor_range(new, NULL
, tok
, tail
,
387 put_minor_range(new, fep
, tok
, tail
,
392 } else if (strcmp(entry
, tok
) == 0 ||
393 (strncmp(entry
, tok
, len
) == 0 &&
395 entry
[len
+1] == '*' &&
396 entry
[len
+2] == '\0')) {
398 * Delete exact match.
403 /* Copy unaffected entry. */
404 (void) fputs(fep
->rawbuf
, new);
409 if (ferror(new) == 0 && fclose(new) == 0 && fep
!= NULL
) {
410 if (rename(filename
, ofile
) != 0) {
412 (void) fprintf(stderr
, gettext(ERR_UPDATE
), ofile
);
413 (void) unlink(ofile
);
414 (void) unlink(nfile
);
416 } else if (rename(nfile
, filename
) != 0) {
418 (void) fprintf(stderr
, gettext(ERR_UPDATE
), ofile
);
419 (void) rename(ofile
, filename
);
420 (void) unlink(nfile
);
423 (void) unlink(ofile
);
425 (void) unlink(nfile
);
431 delete_plcy_entry(const char *filename
, const char *entry
)
437 copy
= strdup(entry
);
441 for (single
= strtok_r(copy
, " \t\n", &p
);
443 single
= strtok_r(NULL
, " \t\n", &p
)) {
444 if ((ret
= delete_one_entry(filename
, single
)) != 0) {
454 * Analyze the device policy token; new tokens should be added to
455 * toktab; new token types should be coded here.
458 parse_plcy_token(char *token
, devplcysys_t
*dp
)
460 char *val
= strchr(token
, '=');
466 (void) fprintf(stderr
, gettext(ERR_NO_EQUALS
), token
);
471 for (i
= 0; i
< NTOK
; i
++) {
472 if (strcmp(token
, toktab
[i
].token
) == 0) {
473 /* standard pointer computation for tokens */
474 void *item
= (char *)dp
+ toktab
[i
].off
;
476 switch (toktab
[i
].type
) {
478 pset
= priv_str_to_set(val
, ",", &perr
);
481 (void) fprintf(stderr
,
482 gettext(ERR_NO_MEM
));
484 (void) fprintf(stderr
,
485 gettext(ERR_BAD_PRIVS
),
486 perr
- val
, val
, perr
);
489 priv_copyset(pset
, item
);
493 (void) fprintf(stderr
,
494 "Internal Error: bad token type: %d\n",
498 /* Standard cleanup & return for good tokens */
503 (void) fprintf(stderr
, gettext(ERR_BAD_TOKEN
), token
);
508 add2str(char **dstp
, const char *str
, size_t *sz
)
511 size_t len
= strlen(p
) + strlen(str
) + 1;
517 *dstp
= p
= realloc(p
, *sz
);
519 (void) fprintf(stderr
, gettext(ERR_NO_MEM
));
523 (void) strcat(p
, str
);
528 * Verify that the policy entry is valid and return the canonical entry.
531 check_plcy_entry(char *entry
, const char *driver
, boolean_t todel
)
536 size_t sz
= strlen(entry
) * 2 + strlen(driver
) + 3;
537 boolean_t tokseen
= B_FALSE
;
542 ds
= alloca(devplcysys_sz
);
544 if (res
== NULL
|| ds
== NULL
) {
545 (void) fprintf(stderr
, gettext(ERR_NO_MEM
));
551 while ((tok
= strtok(entry
, " \t\n")) != NULL
) {
554 /* It's not a token */
555 if (strchr(tok
, '=') == NULL
) {
556 if (strchr(tok
, ':') != NULL
) {
557 (void) fprintf(stderr
, gettext(ERR_BAD_MINOR
));
561 if (*res
!= '\0' && add2str(&res
, "\n", &sz
) != 0)
566 if (parse_minor_range(tok
, &ds
->dps_lomin
,
567 &ds
->dps_himin
, &type
) != 0 ||
568 (!todel
&& type
== '\0')) {
569 (void) fprintf(stderr
,
570 gettext(ERR_BAD_MINOR
));
575 char *tmp
= strchr(tok
, '*');
578 strchr(tmp
+ 1, '*') != NULL
) {
579 (void) fprintf(stderr
,
580 gettext(ERR_BAD_MINOR
));
585 if (add2str(&res
, driver
, &sz
) != 0)
587 if (add2str(&res
, ":", &sz
) != 0)
589 if (add2str(&res
, tok
, &sz
) != 0)
594 if (add2str(&res
, driver
, &sz
) != 0)
596 if (add2str(&res
, ":*", &sz
) != 0)
599 if (parse_plcy_token(tok
, ds
) != 0) {
604 if (add2str(&res
, "\t", &sz
) != 0)
606 if (add2str(&res
, tok
, &sz
) != 0)
611 if (todel
&& tokseen
|| *res
== '\0' || !todel
&& !tokseen
) {
612 (void) fprintf(stderr
, gettext(ERR_INVALID_PLCY
));
617 if (add2str(&res
, "\n", &sz
) != 0)
623 update_device_policy(const char *filename
, const char *entry
, boolean_t repl
)
628 char *dup
, *tok
, *s1
;
632 (void) fprintf(stderr
, gettext(ERR_NO_MEM
));
637 * Split the entry in lines; then get the first token
640 for (tok
= strtok_r(dup
, "\n", &s1
); tok
!= NULL
;
641 tok
= strtok_r(NULL
, "\n", &s1
)) {
643 tok
= strtok(tok
, " \n\t");
645 if (delete_one_entry(filename
, tok
) != 0) {
654 fp
= fopen(filename
, "a");
658 (void) fputs(entry
, fp
);
660 if (fflush(fp
) != 0 || fsync(fileno(fp
)) != 0 || fclose(fp
) != 0)
668 * We need to allocate the privileges now or the privilege set
669 * parsing code will not allow them.
672 check_priv_entry(const char *privlist
, boolean_t add
)
674 char *l
= strdup(privlist
);
678 (void) fprintf(stderr
, gettext(ERR_NO_MEM
));
682 while ((pr
= strtok_r(l
, ",", &l
)) != NULL
) {
683 /* Privilege already exists */
684 if (priv_getbyname(pr
) != -1)
687 if (add
&& modctl(MODALLOCPRIV
, pr
) != 0) {
688 (void) fprintf(stderr
, gettext(ERR_BAD_PRIV
), pr
,