10Sstevel@tonic-gate /*
20Sstevel@tonic-gate * CDDL HEADER START
30Sstevel@tonic-gate *
40Sstevel@tonic-gate * The contents of this file are subject to the terms of the
56372Sdduvall * Common Development and Distribution License (the "License").
66372Sdduvall * You may not use this file except in compliance with the License.
70Sstevel@tonic-gate *
80Sstevel@tonic-gate * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
90Sstevel@tonic-gate * or http://www.opensolaris.org/os/licensing.
100Sstevel@tonic-gate * See the License for the specific language governing permissions
110Sstevel@tonic-gate * and limitations under the License.
120Sstevel@tonic-gate *
130Sstevel@tonic-gate * When distributing Covered Code, include this CDDL HEADER in each
140Sstevel@tonic-gate * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
150Sstevel@tonic-gate * If applicable, add the following below this CDDL HEADER, with the
160Sstevel@tonic-gate * fields enclosed by brackets "[]" replaced with your own identifying
170Sstevel@tonic-gate * information: Portions Copyright [yyyy] [name of copyright owner]
180Sstevel@tonic-gate *
190Sstevel@tonic-gate * CDDL HEADER END
200Sstevel@tonic-gate */
21*11960SMark.J.Nelson@Oracle.COM
220Sstevel@tonic-gate /*
23*11960SMark.J.Nelson@Oracle.COM * Copyright 2010 Sun Microsystems, Inc. All rights reserved.
240Sstevel@tonic-gate * Use is subject to license terms.
250Sstevel@tonic-gate *
260Sstevel@tonic-gate * $Id: pmodes.c,v 1.23 1999/03/22 14:51:16 casper Exp $
270Sstevel@tonic-gate *
280Sstevel@tonic-gate *
290Sstevel@tonic-gate * Program to list files from packages with modes that are to
300Sstevel@tonic-gate * permissive. Usage:
310Sstevel@tonic-gate *
320Sstevel@tonic-gate * pmodes [options] pkgdir ...
330Sstevel@tonic-gate *
340Sstevel@tonic-gate * Pmodes currently has 4 types of modes that are changed:
350Sstevel@tonic-gate *
360Sstevel@tonic-gate * m remove group/other write permissions of all files,
370Sstevel@tonic-gate * except those in the exceptions list.
380Sstevel@tonic-gate * w remove user write permission for executables that
390Sstevel@tonic-gate * are not root owned.
400Sstevel@tonic-gate * s remove g/o read permission for set-uid/set-gid executables
410Sstevel@tonic-gate * o change the owner of files/directories that can be safely
420Sstevel@tonic-gate * chowned to root.
430Sstevel@tonic-gate *
440Sstevel@tonic-gate * Any combination of changes can be switched of by specifying -X
450Sstevel@tonic-gate *
460Sstevel@tonic-gate * The -n option will create a "FILE.new" file for all changed
470Sstevel@tonic-gate * pkgmap/prototype files.
480Sstevel@tonic-gate * The -D option will limit changes to directories only.
490Sstevel@tonic-gate *
500Sstevel@tonic-gate * output:
510Sstevel@tonic-gate *
520Sstevel@tonic-gate * d m oldmode -> newmode pathname
530Sstevel@tonic-gate * | ^ whether the file/dir is group writable or even world writable
540Sstevel@tonic-gate * > type of file.
550Sstevel@tonic-gate * d o owner -> newowner pathname [mode]
560Sstevel@tonic-gate *
570Sstevel@tonic-gate *
580Sstevel@tonic-gate * Casper Dik (Casper.Dik@Holland.Sun.COM)
590Sstevel@tonic-gate */
600Sstevel@tonic-gate
610Sstevel@tonic-gate #include <stdio.h>
620Sstevel@tonic-gate #include <unistd.h>
630Sstevel@tonic-gate #include <string.h>
640Sstevel@tonic-gate #include <ctype.h>
650Sstevel@tonic-gate #include <dirent.h>
660Sstevel@tonic-gate #include <stdlib.h>
670Sstevel@tonic-gate #include <errno.h>
680Sstevel@tonic-gate #include <sys/param.h>
690Sstevel@tonic-gate #include <sys/stat.h>
700Sstevel@tonic-gate #include "binsearch.h"
710Sstevel@tonic-gate
720Sstevel@tonic-gate static char *exceptions[] = {
73*11960SMark.J.Nelson@Oracle.COM "/etc/lp",
74*11960SMark.J.Nelson@Oracle.COM "/var/cache/cups",
750Sstevel@tonic-gate };
760Sstevel@tonic-gate
770Sstevel@tonic-gate static char *exempt_pkgs[] = {
780Sstevel@tonic-gate "SUNWSMSdf", /* "data files" package for SMS */
790Sstevel@tonic-gate "SUNWSMSr", /* "root" package for SMS */
800Sstevel@tonic-gate "SUNWSMSsu", /* "user" package for SMS */
816372Sdduvall "SUNWnethackr", /* "root" package for nethack */
820Sstevel@tonic-gate };
830Sstevel@tonic-gate
840Sstevel@tonic-gate #define NEXEMPT (sizeof (exempt_pkgs) / sizeof (char *))
850Sstevel@tonic-gate
860Sstevel@tonic-gate #define PROTO "prototype_"
870Sstevel@tonic-gate
880Sstevel@tonic-gate #define DEFAULT_SU 0
890Sstevel@tonic-gate #define DEFAULT_OWNER 1
900Sstevel@tonic-gate #define DEFAULT_MODES 1
910Sstevel@tonic-gate #define DEFAULT_USERWRITE 1
920Sstevel@tonic-gate #define DEFAULT_DIRSONLY 0
930Sstevel@tonic-gate #define DEFAULT_EDITABLE 1
940Sstevel@tonic-gate
950Sstevel@tonic-gate static int nexceptions = sizeof (exceptions)/sizeof (char *);
960Sstevel@tonic-gate static int dosu = DEFAULT_SU;
970Sstevel@tonic-gate static int doowner = DEFAULT_OWNER;
980Sstevel@tonic-gate static int domodes = DEFAULT_MODES;
990Sstevel@tonic-gate static int douserwrite = DEFAULT_USERWRITE;
1000Sstevel@tonic-gate static int dirsonly = DEFAULT_DIRSONLY;
1010Sstevel@tonic-gate static int editable = DEFAULT_EDITABLE;
1020Sstevel@tonic-gate static int makenew = 0;
1030Sstevel@tonic-gate static int installnew = 0;
1040Sstevel@tonic-gate static int diffout = 0;
1050Sstevel@tonic-gate static int proto = 0;
1060Sstevel@tonic-gate static int verbose = 0;
1070Sstevel@tonic-gate static int quiet = 0;
1080Sstevel@tonic-gate static int errors = 0;
1090Sstevel@tonic-gate
1100Sstevel@tonic-gate static void update_map(char *, char *, int);
1110Sstevel@tonic-gate
1120Sstevel@tonic-gate static char *program;
1130Sstevel@tonic-gate
1140Sstevel@tonic-gate itemlist restrictto = NULL;
1150Sstevel@tonic-gate
1160Sstevel@tonic-gate static void
usage(void)1170Sstevel@tonic-gate usage(void) {
1180Sstevel@tonic-gate (void) fprintf(stderr,
1190Sstevel@tonic-gate "Usage: %s [-DowsnNmdePvq] [-r file] pkgdir ...\n", program);
1200Sstevel@tonic-gate exit(1);
1210Sstevel@tonic-gate }
1220Sstevel@tonic-gate
1230Sstevel@tonic-gate int
main(int argc,char ** argv)1240Sstevel@tonic-gate main(int argc, char **argv)
1250Sstevel@tonic-gate {
1260Sstevel@tonic-gate char buf[8192];
1270Sstevel@tonic-gate int c;
1280Sstevel@tonic-gate extern int optind, opterr;
1290Sstevel@tonic-gate
1300Sstevel@tonic-gate opterr = 0;
1310Sstevel@tonic-gate
1320Sstevel@tonic-gate program = argv[0];
1330Sstevel@tonic-gate
1340Sstevel@tonic-gate while ((c = getopt(argc, argv, "eDowsnNmdPvqr:")) != EOF) {
1350Sstevel@tonic-gate switch (c) {
1360Sstevel@tonic-gate case 's': dosu = !DEFAULT_SU; break;
1370Sstevel@tonic-gate case 'o': doowner = !DEFAULT_OWNER; break;
1380Sstevel@tonic-gate case 'm': domodes = !DEFAULT_MODES; break;
1390Sstevel@tonic-gate case 'w': douserwrite = !DEFAULT_USERWRITE; break;
1400Sstevel@tonic-gate case 'D': dirsonly = !DEFAULT_DIRSONLY; break;
1410Sstevel@tonic-gate case 'e': editable = !DEFAULT_EDITABLE; break;
1420Sstevel@tonic-gate case 'N': installnew = 1; /* FALLTHROUGH */
1430Sstevel@tonic-gate case 'n': makenew = 1; break;
1440Sstevel@tonic-gate case 'd': diffout = 1; break;
1450Sstevel@tonic-gate case 'P': proto = 1; break;
1460Sstevel@tonic-gate case 'v': verbose = 1; break;
1470Sstevel@tonic-gate case 'q': quiet = 1; break;
1480Sstevel@tonic-gate case 'r':
1490Sstevel@tonic-gate if (restrictto == NULL)
1500Sstevel@tonic-gate restrictto = new_itemlist();
1510Sstevel@tonic-gate if (item_addfile(restrictto, optarg) != 0) {
1520Sstevel@tonic-gate perror(optarg);
1530Sstevel@tonic-gate exit(1);
1540Sstevel@tonic-gate }
1550Sstevel@tonic-gate break;
1560Sstevel@tonic-gate default:
1570Sstevel@tonic-gate case '?': usage(); break;
1580Sstevel@tonic-gate }
1590Sstevel@tonic-gate }
1600Sstevel@tonic-gate argc -= optind;
1610Sstevel@tonic-gate argv += optind;
1620Sstevel@tonic-gate
1630Sstevel@tonic-gate if (argc < 1)
1640Sstevel@tonic-gate usage();
1650Sstevel@tonic-gate
1660Sstevel@tonic-gate for (; *argv; argv++) {
1670Sstevel@tonic-gate FILE *info;
1680Sstevel@tonic-gate char name[MAXPATHLEN];
1690Sstevel@tonic-gate char basedir[MAXPATHLEN] = "/";
1700Sstevel@tonic-gate int basedir_len;
1710Sstevel@tonic-gate struct stat stb;
1720Sstevel@tonic-gate int isfile = 0;
1730Sstevel@tonic-gate boolean_t exempt = B_FALSE;
1740Sstevel@tonic-gate
1750Sstevel@tonic-gate /*
1760Sstevel@tonic-gate * If a plain file is passed on the command line, we assume
1770Sstevel@tonic-gate * it's a prototype or pkgmap file and try to find the matching
1780Sstevel@tonic-gate * pkginfo file
1790Sstevel@tonic-gate */
1800Sstevel@tonic-gate if (lstat(*argv, &stb) == 0 && S_ISREG(stb.st_mode)) {
1810Sstevel@tonic-gate char *lastslash = strrchr(*argv, '/');
1820Sstevel@tonic-gate
1830Sstevel@tonic-gate if (lastslash != NULL)
1840Sstevel@tonic-gate *lastslash = '\0';
1850Sstevel@tonic-gate (void) sprintf(name, "%s/pkginfo", *argv);
1860Sstevel@tonic-gate if (lastslash != NULL)
1870Sstevel@tonic-gate *lastslash = '/';
1880Sstevel@tonic-gate isfile = 1;
1890Sstevel@tonic-gate } else
1900Sstevel@tonic-gate (void) sprintf(name, "%s/pkginfo", *argv);
1910Sstevel@tonic-gate
1920Sstevel@tonic-gate /* if there's no pkginfo file, it could be a prototype area */
1930Sstevel@tonic-gate
1940Sstevel@tonic-gate if (access(name, R_OK) != 0)
1950Sstevel@tonic-gate (void) strcat(name, ".tmpl");
1960Sstevel@tonic-gate
1970Sstevel@tonic-gate info = fopen(name, "r");
1980Sstevel@tonic-gate if (info == 0) {
1990Sstevel@tonic-gate if (!quiet)
2000Sstevel@tonic-gate (void) fprintf(stderr,
2010Sstevel@tonic-gate "Can't open pkginfo file %s\n", name);
2020Sstevel@tonic-gate continue;
2030Sstevel@tonic-gate }
2040Sstevel@tonic-gate
2050Sstevel@tonic-gate while (fgets(buf, sizeof (buf), info) != NULL && !exempt) {
2060Sstevel@tonic-gate if (strncmp(buf, "BASEDIR=", 8) == 0) {
2070Sstevel@tonic-gate (void) strcpy(basedir, buf+8);
2080Sstevel@tonic-gate basedir[strlen(basedir)-1] = '\0';
2090Sstevel@tonic-gate } else if (strncmp(buf, "PKG=", 4) == 0) {
2100Sstevel@tonic-gate int i;
2110Sstevel@tonic-gate char *str;
2120Sstevel@tonic-gate
2130Sstevel@tonic-gate str = buf + sizeof ("PKG=") - 1;
2140Sstevel@tonic-gate str[strlen(str)-1] = '\0';
2156372Sdduvall if (str[0] == '"')
2166372Sdduvall str++;
2176372Sdduvall if (str[strlen(str)-1] == '"')
2186372Sdduvall str[strlen(str)-1] = '\0';
2190Sstevel@tonic-gate for (i = 0; i < NEXEMPT; i++) {
2200Sstevel@tonic-gate if (strcmp(exempt_pkgs[i], str) == 0) {
2210Sstevel@tonic-gate exempt = B_TRUE;
2220Sstevel@tonic-gate break;
2230Sstevel@tonic-gate }
2240Sstevel@tonic-gate }
2250Sstevel@tonic-gate }
2260Sstevel@tonic-gate }
2270Sstevel@tonic-gate
2280Sstevel@tonic-gate (void) fclose(info);
2290Sstevel@tonic-gate
2300Sstevel@tonic-gate /* exempt package */
2310Sstevel@tonic-gate if (exempt)
2320Sstevel@tonic-gate continue;
2330Sstevel@tonic-gate
2340Sstevel@tonic-gate basedir_len = strlen(basedir);
2350Sstevel@tonic-gate if (basedir_len != 1)
2360Sstevel@tonic-gate basedir[basedir_len++] = '/';
2370Sstevel@tonic-gate
2380Sstevel@tonic-gate (void) sprintf(name, "%s/pkgmap", *argv);
2390Sstevel@tonic-gate if (isfile)
2400Sstevel@tonic-gate update_map(*argv, basedir, basedir_len);
2410Sstevel@tonic-gate else if (!proto && access(name, R_OK) == 0)
2420Sstevel@tonic-gate update_map(name, basedir, basedir_len);
2430Sstevel@tonic-gate else {
2440Sstevel@tonic-gate DIR *d = opendir(*argv);
2450Sstevel@tonic-gate struct dirent *de;
2460Sstevel@tonic-gate
2470Sstevel@tonic-gate if (d == NULL) {
2480Sstevel@tonic-gate (void) fprintf(stderr,
2490Sstevel@tonic-gate "Can't read directory \"%s\"\n", *argv);
2500Sstevel@tonic-gate continue;
2510Sstevel@tonic-gate }
2520Sstevel@tonic-gate while (de = readdir(d)) {
2530Sstevel@tonic-gate /* Skip files with .old or .new suffix */
2540Sstevel@tonic-gate if (strstr(de->d_name, PROTO) != NULL &&
2550Sstevel@tonic-gate strncmp(de->d_name, ".del-", 5) != 0 &&
2560Sstevel@tonic-gate strstr(de->d_name, ".old") == NULL &&
2570Sstevel@tonic-gate strstr(de->d_name, ".new") == NULL) {
2580Sstevel@tonic-gate (void) sprintf(name, "%s/%s", *argv,
2590Sstevel@tonic-gate de->d_name);
2600Sstevel@tonic-gate update_map(name, basedir, basedir_len);
2610Sstevel@tonic-gate }
2620Sstevel@tonic-gate }
2630Sstevel@tonic-gate (void) closedir(d);
2640Sstevel@tonic-gate }
2650Sstevel@tonic-gate }
2660Sstevel@tonic-gate return (errors != 0);
2670Sstevel@tonic-gate }
2680Sstevel@tonic-gate
2690Sstevel@tonic-gate #define NEXTWORD(tmp, end, warnme) \
2700Sstevel@tonic-gate do { \
2710Sstevel@tonic-gate tmp = strpbrk(tmp, "\t ");\
2720Sstevel@tonic-gate if (!tmp) {\
2730Sstevel@tonic-gate if (warnme)\
2740Sstevel@tonic-gate warn(name, lineno);\
2750Sstevel@tonic-gate return (LINE_IGNORE);\
2760Sstevel@tonic-gate }\
2770Sstevel@tonic-gate end = tmp++;\
2780Sstevel@tonic-gate while (*tmp && isspace(*tmp)) tmp++;\
2790Sstevel@tonic-gate } while (0)
2800Sstevel@tonic-gate
2810Sstevel@tonic-gate static void
warn(const char * file,int line)2820Sstevel@tonic-gate warn(const char *file, int line)
2830Sstevel@tonic-gate {
2840Sstevel@tonic-gate (void) fprintf(stderr, "pmodes: %s, line %d: unexpected format\n",
2850Sstevel@tonic-gate file, line);
2860Sstevel@tonic-gate }
2870Sstevel@tonic-gate
2880Sstevel@tonic-gate struct parsed_line {
2890Sstevel@tonic-gate char *start; /* buffer start */
2900Sstevel@tonic-gate char *rest; /* buffer after owner */
2910Sstevel@tonic-gate char *owner; /* same size as ut_user */
2920Sstevel@tonic-gate char *old_owner; /* same size as ut_user */
2930Sstevel@tonic-gate char group[16]; /* whatever */
2940Sstevel@tonic-gate int modelen; /* number of mode bytes (3 or 4); */
2950Sstevel@tonic-gate int mode; /* the complete file mode */
2960Sstevel@tonic-gate char path[MAXPATHLEN]; /* NUL terminated pathname */
2970Sstevel@tonic-gate char type; /* */
2980Sstevel@tonic-gate char realtype; /* */
2990Sstevel@tonic-gate };
3000Sstevel@tonic-gate
3010Sstevel@tonic-gate #define LINE_OK 0
3020Sstevel@tonic-gate #define LINE_IGNORE 1
3030Sstevel@tonic-gate #define LINE_ERROR 2
3040Sstevel@tonic-gate
3050Sstevel@tonic-gate static void
put_line(FILE * f,struct parsed_line * line)3060Sstevel@tonic-gate put_line(FILE *f, struct parsed_line *line)
3070Sstevel@tonic-gate {
3080Sstevel@tonic-gate if (f != NULL)
3090Sstevel@tonic-gate if (line->rest)
3100Sstevel@tonic-gate (void) fprintf(f, "%s%.*o %s %s", line->start,
3110Sstevel@tonic-gate line->modelen, line->mode, line->owner, line->rest);
3120Sstevel@tonic-gate else
3130Sstevel@tonic-gate (void) fputs(line->start, f);
3140Sstevel@tonic-gate }
3150Sstevel@tonic-gate
3160Sstevel@tonic-gate /*
3170Sstevel@tonic-gate * the first field is the path, the second the type, the
3180Sstevel@tonic-gate * third the class, the fourth the mode, when appropriate.
3190Sstevel@tonic-gate * We're interested in
3200Sstevel@tonic-gate * f (file)
3210Sstevel@tonic-gate * e (edited file)
3220Sstevel@tonic-gate * v (volatile file)
3230Sstevel@tonic-gate * d (directory)
3240Sstevel@tonic-gate * c (character devices)
3250Sstevel@tonic-gate * b (block devices)
3260Sstevel@tonic-gate */
3270Sstevel@tonic-gate
3280Sstevel@tonic-gate static int
parse_line(struct parsed_line * parse,char * buf,const char * name,int lineno)3290Sstevel@tonic-gate parse_line(struct parsed_line *parse, char *buf, const char *name, int lineno)
3300Sstevel@tonic-gate {
3310Sstevel@tonic-gate char *tmp;
3320Sstevel@tonic-gate char *p = buf;
3330Sstevel@tonic-gate char *end, *q;
3340Sstevel@tonic-gate
3350Sstevel@tonic-gate parse->start = buf;
3360Sstevel@tonic-gate parse->rest = 0; /* makes put_line work */
3370Sstevel@tonic-gate
3380Sstevel@tonic-gate /* Trim trailing spaces */
3390Sstevel@tonic-gate end = buf + strlen(buf);
3400Sstevel@tonic-gate while (end > buf+1 && isspace(end[-2])) {
3410Sstevel@tonic-gate end -= 1;
3420Sstevel@tonic-gate end[-1] = end[0];
3430Sstevel@tonic-gate end[0] = '\0';
3440Sstevel@tonic-gate }
3450Sstevel@tonic-gate
3460Sstevel@tonic-gate while (*p && isspace(*p))
3470Sstevel@tonic-gate p++;
3480Sstevel@tonic-gate
3490Sstevel@tonic-gate if (*p == '#' || *p == ':' || *p == '\0')
3500Sstevel@tonic-gate return (LINE_IGNORE);
3510Sstevel@tonic-gate
3520Sstevel@tonic-gate /*
3530Sstevel@tonic-gate * Special directives; we really should follow the include
3540Sstevel@tonic-gate * directives but we certainly need to look at default
3550Sstevel@tonic-gate */
3560Sstevel@tonic-gate if (*p == '!') {
3570Sstevel@tonic-gate p++;
3580Sstevel@tonic-gate while (*p && isspace(*p))
3590Sstevel@tonic-gate p++;
3600Sstevel@tonic-gate
3610Sstevel@tonic-gate if (!*p || *p == '\n')
3620Sstevel@tonic-gate return (LINE_IGNORE);
3630Sstevel@tonic-gate
3640Sstevel@tonic-gate if (strncmp(p, "default", 7) == 0) {
3650Sstevel@tonic-gate NEXTWORD(p, end, 1);
3660Sstevel@tonic-gate parse->type = 'f';
3670Sstevel@tonic-gate parse->realtype = 'D';
3680Sstevel@tonic-gate strcpy(parse->path, "(default)");
3690Sstevel@tonic-gate tmp = p;
3700Sstevel@tonic-gate NEXTWORD(p, end, 1);
3710Sstevel@tonic-gate goto domode;
3720Sstevel@tonic-gate } else if (strncmp(p, "include", 7) == 0) {
3730Sstevel@tonic-gate NEXTWORD(p, end, 1);
3740Sstevel@tonic-gate if (strstr(p, PROTO) == NULL)
3750Sstevel@tonic-gate fprintf(stderr, "including file %s", p);
3760Sstevel@tonic-gate }
3770Sstevel@tonic-gate return (LINE_IGNORE);
3780Sstevel@tonic-gate }
3790Sstevel@tonic-gate
3800Sstevel@tonic-gate /*
3810Sstevel@tonic-gate * Parse the pkgmap line:
3820Sstevel@tonic-gate * [<number>] <type> <class> <path> [<major> <minor>]
3830Sstevel@tonic-gate * [ <mode> <owner> <group> .... ]
3840Sstevel@tonic-gate */
3850Sstevel@tonic-gate
3860Sstevel@tonic-gate /* Skip first column for non-prototype (i.e., pkgmap) files */
3870Sstevel@tonic-gate if (isdigit(*p))
3880Sstevel@tonic-gate NEXTWORD(p, end, 1);
3890Sstevel@tonic-gate
3900Sstevel@tonic-gate parse->realtype = parse->type = *p;
3910Sstevel@tonic-gate
3920Sstevel@tonic-gate switch (parse->type) {
3930Sstevel@tonic-gate case 'i': case 's': case 'l':
3940Sstevel@tonic-gate return (LINE_IGNORE);
3950Sstevel@tonic-gate }
3960Sstevel@tonic-gate
3970Sstevel@tonic-gate NEXTWORD(p, end, 1);
3980Sstevel@tonic-gate
3990Sstevel@tonic-gate /* skip class */
4000Sstevel@tonic-gate NEXTWORD(p, end, 1);
4010Sstevel@tonic-gate
4020Sstevel@tonic-gate /*
4030Sstevel@tonic-gate * p now points to pathname
4040Sstevel@tonic-gate * At this point, we could have no mode because we are
4050Sstevel@tonic-gate * using a default.
4060Sstevel@tonic-gate */
4070Sstevel@tonic-gate tmp = p;
4080Sstevel@tonic-gate NEXTWORD(p, end, 0);
4090Sstevel@tonic-gate
4100Sstevel@tonic-gate /* end points to space after name */
4110Sstevel@tonic-gate (void) strncpy(parse->path, tmp, end - tmp);
4120Sstevel@tonic-gate parse->path[end - tmp] = '\0';
4130Sstevel@tonic-gate
4140Sstevel@tonic-gate switch (parse->type) {
4150Sstevel@tonic-gate case 'e':
4160Sstevel@tonic-gate case 'v':
4170Sstevel@tonic-gate /* type 'e' and 'v' are files, just like 'f', use 'f' in out */
4180Sstevel@tonic-gate parse->type = 'f';
4190Sstevel@tonic-gate /* FALLTHROUGH */
4200Sstevel@tonic-gate case 'f':
4210Sstevel@tonic-gate case 'd':
4220Sstevel@tonic-gate case 'p': /* FIFO - assume mode is sensible, don't treat as file */
4230Sstevel@tonic-gate break;
4240Sstevel@tonic-gate
4250Sstevel@tonic-gate case 'x': /* Exclusive directory */
4260Sstevel@tonic-gate parse->type = 'd';
4270Sstevel@tonic-gate break;
4280Sstevel@tonic-gate
4290Sstevel@tonic-gate /* device files have class major minor, skip */
4300Sstevel@tonic-gate case 'c':
4310Sstevel@tonic-gate case 'b':
4320Sstevel@tonic-gate NEXTWORD(p, end, 1); NEXTWORD(p, end, 1);
4330Sstevel@tonic-gate break;
4340Sstevel@tonic-gate
4350Sstevel@tonic-gate default:
4360Sstevel@tonic-gate (void) fprintf(stderr, "Unknown type '%c', %s:%d\n",
4370Sstevel@tonic-gate parse->type, name, lineno);
4380Sstevel@tonic-gate return (LINE_ERROR);
4390Sstevel@tonic-gate }
4400Sstevel@tonic-gate tmp = p;
4410Sstevel@tonic-gate NEXTWORD(p, end, 1);
4420Sstevel@tonic-gate
4430Sstevel@tonic-gate domode:
4440Sstevel@tonic-gate /*
4450Sstevel@tonic-gate * the mode is either a 4 digit number (file is sticky/set-uid or
4460Sstevel@tonic-gate * set-gid or the mode has a leading 0) or a three digit number
4470Sstevel@tonic-gate * mode has all the mode bits, mode points to the three least
4480Sstevel@tonic-gate * significant bit so fthe mode
4490Sstevel@tonic-gate */
4500Sstevel@tonic-gate parse->mode = 0;
4510Sstevel@tonic-gate for (q = tmp; q < end; q++) {
4520Sstevel@tonic-gate if (!isdigit(*q) || *q > '7') {
4530Sstevel@tonic-gate (void) fprintf(stderr,
4540Sstevel@tonic-gate "Warning: Unparseble mode \"%.*s\" at %s:%d\n",
4550Sstevel@tonic-gate end-tmp, tmp, name, lineno);
4560Sstevel@tonic-gate return (LINE_IGNORE);
4570Sstevel@tonic-gate }
4580Sstevel@tonic-gate parse->mode <<= 3;
4590Sstevel@tonic-gate parse->mode += *q - '0';
4600Sstevel@tonic-gate }
4610Sstevel@tonic-gate parse->modelen = end - tmp;
4620Sstevel@tonic-gate tmp[0] = '\0';
4630Sstevel@tonic-gate
4640Sstevel@tonic-gate parse->old_owner = parse->owner = p;
4650Sstevel@tonic-gate
4660Sstevel@tonic-gate NEXTWORD(p, end, 1);
4670Sstevel@tonic-gate
4680Sstevel@tonic-gate parse->rest = end+1;
4690Sstevel@tonic-gate *end = '\0';
4700Sstevel@tonic-gate
4710Sstevel@tonic-gate (void) memset(parse->group, 0, sizeof (parse->group));
4720Sstevel@tonic-gate (void) strncpy(parse->group, end+1, strcspn(end+1, " \t\n"));
4730Sstevel@tonic-gate
4740Sstevel@tonic-gate return (LINE_OK);
4750Sstevel@tonic-gate }
4760Sstevel@tonic-gate
4770Sstevel@tonic-gate static void
update_map(char * name,char * basedir,int basedir_len)4780Sstevel@tonic-gate update_map(char *name, char *basedir, int basedir_len)
4790Sstevel@tonic-gate {
4800Sstevel@tonic-gate char buf[8192];
4810Sstevel@tonic-gate int i;
4820Sstevel@tonic-gate FILE *map, *newmap;
4830Sstevel@tonic-gate char newname[MAXPATHLEN];
4840Sstevel@tonic-gate int nchanges = 0;
4850Sstevel@tonic-gate unsigned int lineno = 0;
4860Sstevel@tonic-gate struct parsed_line line;
4870Sstevel@tonic-gate char *fname;
4880Sstevel@tonic-gate
4890Sstevel@tonic-gate map = fopen(name, "r");
4900Sstevel@tonic-gate if (map == 0) {
4910Sstevel@tonic-gate (void) fprintf(stderr, "Can't open \"%s\"\n", name);
4920Sstevel@tonic-gate return;
4930Sstevel@tonic-gate }
4940Sstevel@tonic-gate (void) strcpy(newname, name);
4950Sstevel@tonic-gate (void) strcat(newname, ".new");
4960Sstevel@tonic-gate if (makenew) {
4970Sstevel@tonic-gate newmap = fopen(newname, "w");
4980Sstevel@tonic-gate if (newmap == 0)
4990Sstevel@tonic-gate (void) fprintf(stderr, "Can't open %s for writing\n",
5000Sstevel@tonic-gate name);
5010Sstevel@tonic-gate } else
5020Sstevel@tonic-gate newmap = 0;
5030Sstevel@tonic-gate
5040Sstevel@tonic-gate /* Get last one or two components non-trivial of pathname */
5050Sstevel@tonic-gate if (verbose) {
5060Sstevel@tonic-gate char *tmp = name + strlen(name);
5070Sstevel@tonic-gate int cnt = 0, first = 0;
5080Sstevel@tonic-gate
5090Sstevel@tonic-gate while (--tmp > name && cnt < 2) {
5100Sstevel@tonic-gate if (*tmp == '/') {
5110Sstevel@tonic-gate if (++cnt == 1)
5120Sstevel@tonic-gate first = tmp - name;
5130Sstevel@tonic-gate else {
5140Sstevel@tonic-gate fname = tmp + 1;
5150Sstevel@tonic-gate /* Triviality check */
5160Sstevel@tonic-gate if (tmp - name > first - 4)
5170Sstevel@tonic-gate cnt--;
5180Sstevel@tonic-gate }
5190Sstevel@tonic-gate }
5200Sstevel@tonic-gate }
5210Sstevel@tonic-gate if (cnt < 2)
5220Sstevel@tonic-gate fname = name;
5230Sstevel@tonic-gate }
5240Sstevel@tonic-gate
5250Sstevel@tonic-gate nchanges = 0;
5260Sstevel@tonic-gate
5270Sstevel@tonic-gate for (; fgets(buf, sizeof (buf), map) != 0; put_line(newmap, &line)) {
5280Sstevel@tonic-gate
5290Sstevel@tonic-gate int root_owner, mode_diff = 0;
5300Sstevel@tonic-gate int changed = 0;
5310Sstevel@tonic-gate
5320Sstevel@tonic-gate lineno ++;
5330Sstevel@tonic-gate
5340Sstevel@tonic-gate switch (parse_line(&line, buf, name, lineno)) {
5350Sstevel@tonic-gate case LINE_IGNORE:
5360Sstevel@tonic-gate continue;
5370Sstevel@tonic-gate case LINE_ERROR:
5380Sstevel@tonic-gate errors++;
5390Sstevel@tonic-gate continue;
5400Sstevel@tonic-gate }
5410Sstevel@tonic-gate
5420Sstevel@tonic-gate if (restrictto) {
5430Sstevel@tonic-gate char nbuf[MAXPATHLEN];
5440Sstevel@tonic-gate snprintf(nbuf, sizeof (nbuf), "%.*s%s", basedir_len,
5450Sstevel@tonic-gate basedir, line.path);
5460Sstevel@tonic-gate
5470Sstevel@tonic-gate if (item_search(restrictto, nbuf) == -1)
5480Sstevel@tonic-gate continue;
5490Sstevel@tonic-gate }
5500Sstevel@tonic-gate
5510Sstevel@tonic-gate if (dirsonly && line.type != 'd')
5520Sstevel@tonic-gate continue;
5530Sstevel@tonic-gate
5540Sstevel@tonic-gate root_owner = strcmp(line.owner, "root") == 0;
5550Sstevel@tonic-gate if (dosu && line.type == 'f' && (line.mode & (S_ISUID|S_ISGID)))
5560Sstevel@tonic-gate mode_diff = line.mode & (S_IRGRP|S_IROTH);
5570Sstevel@tonic-gate
5580Sstevel@tonic-gate /*
5590Sstevel@tonic-gate * The following heuristics are used to determine whether a file
5600Sstevel@tonic-gate * can be safely chown'ed to root:
5610Sstevel@tonic-gate * - it's not set-uid.
5620Sstevel@tonic-gate * and one of the following applies:
5630Sstevel@tonic-gate * - it's not writable by the current owner and is
5640Sstevel@tonic-gate * group/world readable
5650Sstevel@tonic-gate * - it's world executable and a file
5660Sstevel@tonic-gate * - owner, group and world permissions are identical
5670Sstevel@tonic-gate * - it's a bin owned directory or a "non-volatile"
5680Sstevel@tonic-gate * file (any owner) for which group and other r-x
5690Sstevel@tonic-gate * permissions are identical, or it's a bin owned
5700Sstevel@tonic-gate * executable or it's a /etc/security/dev/ device
5710Sstevel@tonic-gate */
5720Sstevel@tonic-gate
5730Sstevel@tonic-gate if (doowner && !(line.mode & S_ISUID) &&
5740Sstevel@tonic-gate !root_owner &&
5750Sstevel@tonic-gate ((!(line.mode & S_IWUSR) &&
5760Sstevel@tonic-gate (line.mode&(S_IRGRP|S_IROTH)) == (S_IRGRP|S_IROTH)) ||
5770Sstevel@tonic-gate (line.type == 'f' && (line.mode & S_IXOTH)) ||
5780Sstevel@tonic-gate ((line.mode & 07) == ((line.mode>>3) & 07) &&
5790Sstevel@tonic-gate (line.mode & 07) == ((line.mode>>6) & 07) &&
5800Sstevel@tonic-gate strcmp(line.owner, "uucp") != 0) ||
5810Sstevel@tonic-gate ((line.type == 'd' && strcmp(line.owner, "bin") == 0 ||
5820Sstevel@tonic-gate (editable && strcmp(line.owner, "bin") == 0 ?
5830Sstevel@tonic-gate line.type : line.realtype) == 'f') &&
5840Sstevel@tonic-gate ((line.mode & 05) == ((line.mode>>3) & 05) ||
5850Sstevel@tonic-gate (line.mode & 0100) &&
5860Sstevel@tonic-gate strcmp(line.owner, "bin") == 0) &&
5870Sstevel@tonic-gate ((line.mode & 0105) != 0 ||
5880Sstevel@tonic-gate basedir_len < 18 &&
5890Sstevel@tonic-gate strncmp(basedir, "/etc/security/dev/",
5900Sstevel@tonic-gate basedir_len) == 0 &&
5910Sstevel@tonic-gate strncmp(line.path, "/etc/security/dev/"
5920Sstevel@tonic-gate + basedir_len, 18 - basedir_len) == 0)))) {
5930Sstevel@tonic-gate if (!diffout) {
5940Sstevel@tonic-gate if (!changed && verbose && !nchanges)
5950Sstevel@tonic-gate (void) printf("%s:\n", fname);
5960Sstevel@tonic-gate (void) printf("%c o %s -> root %s%s [%.*o]\n",
5970Sstevel@tonic-gate line.realtype, line.owner, basedir,
5980Sstevel@tonic-gate line.path, line.modelen, line.mode);
5990Sstevel@tonic-gate }
6000Sstevel@tonic-gate line.owner = "root";
6010Sstevel@tonic-gate root_owner = 1;
6020Sstevel@tonic-gate changed = 1;
6030Sstevel@tonic-gate }
6040Sstevel@tonic-gate /*
6050Sstevel@tonic-gate * Strip user write bit if owner != root and executable by user.
6060Sstevel@tonic-gate * root can write even if no write bits set
6070Sstevel@tonic-gate * Could prevent executables from being overwritten.
6080Sstevel@tonic-gate */
6090Sstevel@tonic-gate if (douserwrite && line.type == 'f' && !root_owner &&
6100Sstevel@tonic-gate (line.mode & (S_IWUSR|S_IXUSR)) == (S_IWUSR|S_IXUSR))
6110Sstevel@tonic-gate mode_diff |= S_IWUSR;
6120Sstevel@tonic-gate
6130Sstevel@tonic-gate
6140Sstevel@tonic-gate if (domodes && (line.mode & (S_IWGRP|S_IWOTH)) != 0 &&
6150Sstevel@tonic-gate (line.mode & S_ISVTX) == 0) {
6160Sstevel@tonic-gate if (basedir_len <= 1) { /* root dir */
6170Sstevel@tonic-gate for (i = 0; i < nexceptions; i++) {
6180Sstevel@tonic-gate if (strcmp(line.path,
6190Sstevel@tonic-gate exceptions[i]+basedir_len) == 0)
6200Sstevel@tonic-gate break;
6210Sstevel@tonic-gate }
6220Sstevel@tonic-gate } else {
6230Sstevel@tonic-gate for (i = 0; i < nexceptions; i++) {
6240Sstevel@tonic-gate if (strncmp(basedir, exceptions[i],
6250Sstevel@tonic-gate basedir_len) == 0 &&
6260Sstevel@tonic-gate strcmp(line.path,
6270Sstevel@tonic-gate exceptions[i]+basedir_len) == 0)
6280Sstevel@tonic-gate break;
6290Sstevel@tonic-gate }
6300Sstevel@tonic-gate }
6310Sstevel@tonic-gate if (i == nexceptions)
6320Sstevel@tonic-gate mode_diff |= line.mode & (S_IWGRP|S_IWOTH);
6330Sstevel@tonic-gate }
6340Sstevel@tonic-gate
6350Sstevel@tonic-gate if (mode_diff) {
6360Sstevel@tonic-gate int oldmode = line.mode;
6370Sstevel@tonic-gate
6380Sstevel@tonic-gate line.mode &= ~mode_diff;
6390Sstevel@tonic-gate
6400Sstevel@tonic-gate if (line.mode != oldmode) {
6410Sstevel@tonic-gate if (!diffout) {
6420Sstevel@tonic-gate if (!changed && verbose && !nchanges)
6430Sstevel@tonic-gate (void) printf("%s:\n", fname);
6440Sstevel@tonic-gate printf("%c %c %04o -> %04o %s%s\n",
6450Sstevel@tonic-gate line.realtype,
6460Sstevel@tonic-gate (mode_diff & (S_IRGRP|S_IROTH)) ?
6470Sstevel@tonic-gate 's' : 'm',
6480Sstevel@tonic-gate oldmode, line.mode, basedir,
6490Sstevel@tonic-gate line.path);
6500Sstevel@tonic-gate }
6510Sstevel@tonic-gate changed = 1;
6520Sstevel@tonic-gate }
6530Sstevel@tonic-gate }
6540Sstevel@tonic-gate nchanges += changed;
6550Sstevel@tonic-gate if (diffout && changed) {
6560Sstevel@tonic-gate if (nchanges == 1 && verbose)
6570Sstevel@tonic-gate (void) printf("%s:\n", fname);
6580Sstevel@tonic-gate
6590Sstevel@tonic-gate (void) printf("< %c %04o %s %s %s%s\n", line.realtype,
6600Sstevel@tonic-gate line.mode | mode_diff, line.old_owner, line.group,
6610Sstevel@tonic-gate basedir, line.path);
6620Sstevel@tonic-gate (void) printf("> %c %04o %s %s %s%s\n", line.realtype,
6630Sstevel@tonic-gate line.mode, line.owner, line.group, basedir,
6640Sstevel@tonic-gate line.path);
6650Sstevel@tonic-gate }
6660Sstevel@tonic-gate }
6670Sstevel@tonic-gate (void) fclose(map);
6680Sstevel@tonic-gate
6690Sstevel@tonic-gate if (newmap != NULL) {
6700Sstevel@tonic-gate (void) fflush(newmap);
6710Sstevel@tonic-gate if (ferror(newmap)) {
6720Sstevel@tonic-gate (void) fprintf(stderr, "Error writing %s\n", name);
6730Sstevel@tonic-gate return;
6740Sstevel@tonic-gate }
6750Sstevel@tonic-gate (void) fclose(newmap);
6760Sstevel@tonic-gate if (nchanges == 0)
6770Sstevel@tonic-gate (void) unlink(newname);
6780Sstevel@tonic-gate else if (installnew) {
6790Sstevel@tonic-gate char oldname[MAXPATHLEN];
6800Sstevel@tonic-gate
6810Sstevel@tonic-gate (void) strcpy(oldname, name);
6820Sstevel@tonic-gate (void) strcat(oldname, ".old");
6830Sstevel@tonic-gate if (rename(name, oldname) == -1 ||
6840Sstevel@tonic-gate rename(newname, name) == -1)
6850Sstevel@tonic-gate (void) fprintf(stderr,
6860Sstevel@tonic-gate "Couldn't install %s: %s\n",
6870Sstevel@tonic-gate newname, strerror(errno));
6880Sstevel@tonic-gate }
6890Sstevel@tonic-gate }
6900Sstevel@tonic-gate }
691