installboot: fix stage2 size check for MBR
[unleashed.git] / usr / src / cmd / modload / plcysubr.c
blob814f5c14ca01aa89f31e9d867a3969b135fda229
1 /*
2 * CDDL HEADER START
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
7 * with the License.
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]
20 * CDDL HEADER END
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.
30 #include <stdio.h>
31 #include <stdlib.h>
32 #include <unistd.h>
33 #include <string.h>
34 #include <ctype.h>
35 #include <priv.h>
36 #include <string.h>
37 #include <libgen.h>
38 #include <libintl.h>
39 #include <errno.h>
40 #include <alloca.h>
41 #include <sys/modctl.h>
42 #include <sys/devpolicy.h>
43 #include <sys/stat.h>
44 #include <sys/sysmacros.h>
46 #include "addrem.h"
47 #include "errmsg.h"
48 #include "plcysubr.h"
50 size_t devplcysys_sz;
51 const priv_impl_info_t *privimplinfo;
54 * New token types should be parsed in parse_plcy_entry.
56 #define PSET 0
58 typedef struct token {
59 const char *token;
60 int type;
61 ptrdiff_t off;
62 } token_t;
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) */ },
69 #define RDPOL 0
70 #define WRPOL 1
72 #define NTOK (sizeof (toktab)/sizeof (token_t))
75 * Compute the size of the datastructures needed.
77 void
78 devplcy_init(void)
80 if ((privimplinfo = getprivimplinfo()) == NULL) {
81 (void) fprintf(stderr, gettext(ERR_PRIVIMPL));
82 exit(1);
85 devplcysys_sz = DEVPLCYSYS_SZ(privimplinfo);
87 toktab[RDPOL].off =
88 (char *)DEVPLCYSYS_RDP((devplcysys_t *)0, privimplinfo) -
89 (char *)0;
90 toktab[WRPOL].off =
91 (char *)DEVPLCYSYS_WRP((devplcysys_t *)0, privimplinfo) -
92 (char *)0;
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
104 fileentry_t *
105 fgetline(FILE *fp)
107 static size_t sz = BUFSIZ;
108 static struct fileentry fe;
109 static int linecnt = 1;
111 char *buf = fe.rawbuf;
112 ptrdiff_t off;
113 char *p;
114 int c, lastc, i;
116 if (buf == NULL) {
117 fe.rawbuf = buf = malloc(sz);
118 if (buf == NULL)
119 return (NULL);
121 if (fe.entry != NULL) {
122 free(fe.entry);
123 fe.orgentry = fe.entry = NULL;
126 i = 0;
127 off = -1;
128 c = '\n';
130 while (lastc = c, (c = getc(fp)) != EOF) {
131 buf[i++] = c;
133 if (i == sz) {
134 sz *= 2;
135 fe.rawbuf = buf = realloc(buf, sz);
136 if (buf == NULL)
137 return (NULL);
140 if (c == '\n') {
141 linecnt++;
142 /* Newline, escaped or not yet processing an entry */
143 if (off == -1 || lastc == '\\')
144 continue;
145 } else if (lastc == '\n' && off == -1) {
146 /* Start of more comments */
147 if (c == '#')
148 continue;
149 /* Found start of entry */
150 off = i - 1;
151 fe.startline = linecnt;
152 continue;
153 } else
154 continue;
156 buf[i] = '\0';
157 fe.orgentry = buf + off;
158 p = fe.entry = strdup(fe.orgentry);
160 if (p == NULL)
161 return (NULL);
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') {
167 off += 2;
168 p++;
171 return (&fe);
173 if (lastc != '\n' || off != -1)
174 return (NULL);
175 buf[i] = '\0';
176 linecnt = 1;
177 return (&fe);
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)
188 unsigned long tmp;
189 char *p;
191 if (*range++ != '(')
192 return (-1);
194 errno = 0;
195 tmp = strtoul(range, &p, 0);
196 if (tmp > L_MAXMIN32 || (tmp == 0 && errno != 0) ||
197 (*p != '-' && *p != ')'))
198 return (-1);
199 *lo = tmp;
200 if (*p == '-') {
201 errno = 0;
202 tmp = strtoul(p + 1, &p, 0);
203 if (tmp > L_MAXMIN32 || (tmp == 0 && errno != 0) || *p != ')')
204 return (-1);
206 *hi = tmp;
207 if (*lo > *hi)
208 return (-1);
210 switch (p[1]) {
211 case '\0':
212 *type = '\0';
213 break;
214 case 'c':
215 case 'C':
216 *type = 'c';
217 break;
218 case 'b':
219 case 'B':
220 *type = 'b';
221 break;
222 default:
223 return (-1);
225 return (0);
228 static void
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);
236 if (type == '\0') {
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);
241 } else {
242 (void) fprintf(fp, "%s:(%d-%d)%c%s", devn, (int)lo, (int)hi,
243 type, tail);
247 static int
248 delete_one_entry(const char *filename, const char *entry)
250 char tfile[MAXPATHLEN];
251 char ofile[MAXPATHLEN];
252 char *nfile;
253 FILE *old, *new;
254 fileentry_t *fep;
255 struct stat buf;
256 int newfd;
257 char *mpart;
258 boolean_t delall;
259 boolean_t delrange;
260 minor_t rlo, rhi;
261 char rtype;
263 mpart = strchr(entry, ':');
264 if (mpart == NULL) {
265 delall = B_TRUE;
266 delrange = B_FALSE;
267 } else {
268 delall = B_FALSE;
269 mpart++;
270 if (*mpart == '(') {
271 if (parse_minor_range(mpart, &rlo, &rhi, &rtype) != 0)
272 return (-1);
273 delrange = B_TRUE;
274 } else {
275 delrange = B_FALSE;
279 if (strlen(filename) + sizeof (XEND) > sizeof (tfile))
280 return (-1);
282 old = fopen(filename, "r");
284 if (old == NULL)
285 return (-1);
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");
293 if (new == NULL) {
294 (void) fclose(old);
295 return (ERROR);
298 newfd = fileno(new);
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);
304 } else {
305 (void) fchown(newfd, 0, 3); /* root:sys */
306 (void) fchmod(newfd, 0644);
309 while ((fep = fgetline(old))) {
310 char *tok;
311 char *min;
312 char *tail;
313 char tc;
314 int len;
316 /* Trailing comments */
317 if (fep->entry == NULL) {
318 (void) fputs(fep->rawbuf, new);
319 break;
322 tok = fep->entry;
323 while (*tok && isspace(*tok))
324 tok++;
326 if (*tok == '\0') {
327 (void) fputs(fep->rawbuf, new);
328 break;
331 /* Make sure we can recover the remainder incl. whitespace */
332 tail = strpbrk(tok, "\t\n ");
333 if (tail == NULL)
334 tail = tok + strlen(tok);
335 tc = *tail;
336 *tail = '\0';
338 min = strchr(tok, ':');
339 if (min && (delall || delrange))
340 *min++ = '\0';
342 len = strlen(tok);
343 if (delrange) {
344 minor_t lo, hi;
345 char type;
348 * Delete or shrink overlapping ranges.
350 if (strncmp(entry, tok, len) == 0 &&
351 entry[len] == ':' &&
352 min != NULL &&
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)
360 continue;
362 /* Partial overlap, shrink range */
363 if (lo < rlo)
364 newhi = rlo - 1;
365 else
366 newhi = hi;
367 if (hi > rhi)
368 newlo = rhi + 1;
369 else
370 newlo = lo;
372 /* restore NULed character */
373 *tail = tc;
375 /* Split range? */
376 if (newlo > newhi) {
378 * We have two ranges:
379 * lo ... newhi (== rlo - 1)
380 * newlo (== rhi + 1) .. hi
382 put_minor_range(new, fep, tok, tail,
383 lo, newhi, type);
384 put_minor_range(new, NULL, tok, tail,
385 newlo, hi, type);
386 } else {
387 put_minor_range(new, fep, tok, tail,
388 newlo, newhi, type);
390 continue;
392 } else if (strcmp(entry, tok) == 0 ||
393 (strncmp(entry, tok, len) == 0 &&
394 entry[len] == ':' &&
395 entry[len+1] == '*' &&
396 entry[len+2] == '\0')) {
398 * Delete exact match.
400 continue;
403 /* Copy unaffected entry. */
404 (void) fputs(fep->rawbuf, new);
406 (void) fclose(old);
407 (void) fflush(new);
408 (void) fsync(newfd);
409 if (ferror(new) == 0 && fclose(new) == 0 && fep != NULL) {
410 if (rename(filename, ofile) != 0) {
411 perror(NULL);
412 (void) fprintf(stderr, gettext(ERR_UPDATE), ofile);
413 (void) unlink(ofile);
414 (void) unlink(nfile);
415 return (ERROR);
416 } else if (rename(nfile, filename) != 0) {
417 perror(NULL);
418 (void) fprintf(stderr, gettext(ERR_UPDATE), ofile);
419 (void) rename(ofile, filename);
420 (void) unlink(nfile);
421 return (ERROR);
423 (void) unlink(ofile);
424 } else
425 (void) unlink(nfile);
426 return (0);
431 delete_plcy_entry(const char *filename, const char *entry)
433 char *p, *single;
434 char *copy;
435 int ret = 0;
437 copy = strdup(entry);
438 if (copy == NULL)
439 return (ERROR);
441 for (single = strtok_r(copy, " \t\n", &p);
442 single != NULL;
443 single = strtok_r(NULL, " \t\n", &p)) {
444 if ((ret = delete_one_entry(filename, single)) != 0) {
445 free(copy);
446 return (ret);
449 free(copy);
450 return (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, '=');
461 const char *perr;
462 int i;
463 priv_set_t *pset;
465 if (val == NULL) {
466 (void) fprintf(stderr, gettext(ERR_NO_EQUALS), token);
467 return (1);
469 *val++ = '\0';
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) {
477 case PSET:
478 pset = priv_str_to_set(val, ",", &perr);
479 if (pset == NULL) {
480 if (perr == NULL)
481 (void) fprintf(stderr,
482 gettext(ERR_NO_MEM));
483 else
484 (void) fprintf(stderr,
485 gettext(ERR_BAD_PRIVS),
486 perr - val, val, perr);
487 return (1);
489 priv_copyset(pset, item);
490 priv_freeset(pset);
491 break;
492 default:
493 (void) fprintf(stderr,
494 "Internal Error: bad token type: %d\n",
495 toktab[i].type);
496 return (1);
498 /* Standard cleanup & return for good tokens */
499 val[-1] = '=';
500 return (0);
503 (void) fprintf(stderr, gettext(ERR_BAD_TOKEN), token);
504 return (1);
507 static int
508 add2str(char **dstp, const char *str, size_t *sz)
510 char *p = *dstp;
511 size_t len = strlen(p) + strlen(str) + 1;
513 if (len > *sz) {
514 *sz *= 2;
515 if (*sz < len)
516 *sz = len;
517 *dstp = p = realloc(p, *sz);
518 if (p == NULL) {
519 (void) fprintf(stderr, gettext(ERR_NO_MEM));
520 return (-1);
523 (void) strcat(p, str);
524 return (0);
528 * Verify that the policy entry is valid and return the canonical entry.
530 char *
531 check_plcy_entry(char *entry, const char *driver, boolean_t todel)
533 char *res;
534 devplcysys_t *ds;
535 char *tok;
536 size_t sz = strlen(entry) * 2 + strlen(driver) + 3;
537 boolean_t tokseen = B_FALSE;
539 devplcy_init();
541 res = malloc(sz);
542 ds = alloca(devplcysys_sz);
544 if (res == NULL || ds == NULL) {
545 (void) fprintf(stderr, gettext(ERR_NO_MEM));
546 return (NULL);
549 *res = '\0';
551 while ((tok = strtok(entry, " \t\n")) != NULL) {
552 entry = 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));
558 free(res);
559 return (NULL);
561 if (*res != '\0' && add2str(&res, "\n", &sz) != 0)
562 return (NULL);
564 if (*tok == '(') {
565 char type;
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));
571 free(res);
572 return (NULL);
574 } else {
575 char *tmp = strchr(tok, '*');
577 if (tmp != NULL &&
578 strchr(tmp + 1, '*') != NULL) {
579 (void) fprintf(stderr,
580 gettext(ERR_BAD_MINOR));
581 free(res);
585 if (add2str(&res, driver, &sz) != 0)
586 return (NULL);
587 if (add2str(&res, ":", &sz) != 0)
588 return (NULL);
589 if (add2str(&res, tok, &sz) != 0)
590 return (NULL);
591 tokseen = B_FALSE;
592 } else {
593 if (*res == '\0') {
594 if (add2str(&res, driver, &sz) != 0)
595 return (NULL);
596 if (add2str(&res, ":*", &sz) != 0)
597 return (NULL);
599 if (parse_plcy_token(tok, ds) != 0) {
600 free(res);
601 return (NULL);
604 if (add2str(&res, "\t", &sz) != 0)
605 return (NULL);
606 if (add2str(&res, tok, &sz) != 0)
607 return (NULL);
608 tokseen = B_TRUE;
611 if (todel && tokseen || *res == '\0' || !todel && !tokseen) {
612 (void) fprintf(stderr, gettext(ERR_INVALID_PLCY));
613 free(res);
614 return (NULL);
616 if (!todel)
617 if (add2str(&res, "\n", &sz) != 0)
618 return (NULL);
619 return (res);
623 update_device_policy(const char *filename, const char *entry, boolean_t repl)
625 FILE *fp;
627 if (repl) {
628 char *dup, *tok, *s1;
630 dup = strdup(entry);
631 if (dup == NULL) {
632 (void) fprintf(stderr, gettext(ERR_NO_MEM));
633 return (ERROR);
637 * Split the entry in lines; then get the first token
638 * of each line.
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) {
646 free(dup);
647 return (ERROR);
651 free(dup);
654 fp = fopen(filename, "a");
655 if (fp == NULL)
656 return (ERROR);
658 (void) fputs(entry, fp);
660 if (fflush(fp) != 0 || fsync(fileno(fp)) != 0 || fclose(fp) != 0)
661 return (ERROR);
663 return (NOERR);
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);
675 char *pr;
677 if (l == NULL) {
678 (void) fprintf(stderr, gettext(ERR_NO_MEM));
679 return (ERROR);
682 while ((pr = strtok_r(l, ",", &l)) != NULL) {
683 /* Privilege already exists */
684 if (priv_getbyname(pr) != -1)
685 continue;
687 if (add && modctl(MODALLOCPRIV, pr) != 0) {
688 (void) fprintf(stderr, gettext(ERR_BAD_PRIV), pr,
689 strerror(errno));
690 return (ERROR);
693 return (NOERR);