pmodes.c revision 7c478bd95313f5f23a4c958a745db2134aa03244
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License, Version 1.0 only
* (the "License"). You may not use this file except in compliance
* with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2004 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*
* $Id: pmodes.c,v 1.23 1999/03/22 14:51:16 casper Exp $
*
*
* Program to list files from packages with modes that are to
* permissive. Usage:
*
* pmodes [options] pkgdir ...
*
* Pmodes currently has 4 types of modes that are changed:
*
* except those in the exceptions list.
* w remove user write permission for executables that
* are not root owned.
* o change the owner of files/directories that can be safely
* chowned to root.
*
* Any combination of changes can be switched of by specifying -X
*
* The -n option will create a "FILE.new" file for all changed
* The -D option will limit changes to directories only.
*
* output:
*
* d m oldmode -> newmode pathname
* > type of file.
* d o owner -> newowner pathname [mode]
*
*
* Casper Dik (Casper.Dik@Holland.Sun.COM)
*/
#pragma ident "%Z%%M% %I% %E% SMI"
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <ctype.h>
#include <dirent.h>
#include <stdlib.h>
#include <errno.h>
#include "binsearch.h"
static char *exceptions[] = {
#include "exceptions.h"
};
static char *exempt_pkgs[] = {
"SUNWSMSdf", /* "data files" package for SMS */
"SUNWSMSr", /* "root" package for SMS */
"SUNWSMSsu", /* "user" package for SMS */
};
#define NEXEMPT (sizeof (exempt_pkgs) / sizeof (char *))
#define PROTO "prototype_"
#define DEFAULT_SU 0
#define DEFAULT_OWNER 1
#define DEFAULT_MODES 1
#define DEFAULT_USERWRITE 1
#define DEFAULT_DIRSONLY 0
#define DEFAULT_EDITABLE 1
static int nexceptions = sizeof (exceptions)/sizeof (char *);
static int dosu = DEFAULT_SU;
static int doowner = DEFAULT_OWNER;
static int domodes = DEFAULT_MODES;
static int douserwrite = DEFAULT_USERWRITE;
static int dirsonly = DEFAULT_DIRSONLY;
static int editable = DEFAULT_EDITABLE;
static int makenew = 0;
static int installnew = 0;
static int diffout = 0;
static int proto = 0;
static int verbose = 0;
static int quiet = 0;
static int errors = 0;
static void update_map(char *, char *, int);
static char *program;
static void
usage(void) {
"Usage: %s [-DowsnNmdePvq] [-r file] pkgdir ...\n", program);
exit(1);
}
int
{
char buf[8192];
int c;
opterr = 0;
switch (c) {
case 'r':
if (restrictto == NULL)
restrictto = new_itemlist();
exit(1);
}
break;
default:
case '?': usage(); break;
}
}
if (argc < 1)
usage();
char name[MAXPATHLEN];
int basedir_len;
int isfile = 0;
/*
* If a plain file is passed on the command line, we assume
* it's a prototype or pkgmap file and try to find the matching
* pkginfo file
*/
*lastslash = '\0';
*lastslash = '/';
isfile = 1;
} else
/* if there's no pkginfo file, it could be a prototype area */
if (info == 0) {
if (!quiet)
"Can't open pkginfo file %s\n", name);
continue;
}
int i;
char *str;
for (i = 0; i < NEXEMPT; i++) {
break;
}
}
}
}
/* exempt package */
if (exempt)
continue;
if (basedir_len != 1)
if (isfile)
else {
if (d == NULL) {
"Can't read directory \"%s\"\n", *argv);
continue;
}
/* Skip files with .old or .new suffix */
}
}
(void) closedir(d);
}
}
return (errors != 0);
}
do { \
if (!tmp) {\
if (warnme)\
return (LINE_IGNORE);\
}\
} while (0)
static void
{
}
struct parsed_line {
char *start; /* buffer start */
char *rest; /* buffer after owner */
char *owner; /* same size as ut_user */
char *old_owner; /* same size as ut_user */
int modelen; /* number of mode bytes (3 or 4); */
int mode; /* the complete file mode */
char type; /* */
char realtype; /* */
};
#define LINE_OK 0
#define LINE_IGNORE 1
#define LINE_ERROR 2
static void
{
if (f != NULL)
else
}
/*
* the first field is the path, the second the type, the
* third the class, the fourth the mode, when appropriate.
* We're interested in
* f (file)
* e (edited file)
* v (volatile file)
* d (directory)
* c (character devices)
* b (block devices)
*/
static int
{
char *tmp;
char *p = buf;
char *end, *q;
/* Trim trailing spaces */
end -= 1;
end[0] = '\0';
}
while (*p && isspace(*p))
p++;
if (*p == '#' || *p == ':' || *p == '\0')
return (LINE_IGNORE);
/*
* Special directives; we really should follow the include
* directives but we certainly need to look at default
*/
if (*p == '!') {
p++;
while (*p && isspace(*p))
p++;
if (!*p || *p == '\n')
return (LINE_IGNORE);
tmp = p;
goto domode;
}
return (LINE_IGNORE);
}
/*
* Parse the pkgmap line:
* [<number>] <type> <class> <path> [<major> <minor>]
* [ <mode> <owner> <group> .... ]
*/
/* Skip first column for non-prototype (i.e., pkgmap) files */
if (isdigit(*p))
case 'i': case 's': case 'l':
return (LINE_IGNORE);
}
/* skip class */
/*
* p now points to pathname
* At this point, we could have no mode because we are
* using a default.
*/
tmp = p;
/* end points to space after name */
case 'e':
case 'v':
/* type 'e' and 'v' are files, just like 'f', use 'f' in out */
/* FALLTHROUGH */
case 'f':
case 'd':
case 'p': /* FIFO - assume mode is sensible, don't treat as file */
break;
case 'x': /* Exclusive directory */
break;
/* device files have class major minor, skip */
case 'c':
case 'b':
break;
default:
return (LINE_ERROR);
}
tmp = p;
/*
* set-gid or the mode has a leading 0) or a three digit number
* mode has all the mode bits, mode points to the three least
* significant bit so fthe mode
*/
if (!isdigit(*q) || *q > '7') {
"Warning: Unparseble mode \"%.*s\" at %s:%d\n",
return (LINE_IGNORE);
}
}
tmp[0] = '\0';
*end = '\0';
return (LINE_OK);
}
static void
{
char buf[8192];
int i;
char newname[MAXPATHLEN];
int nchanges = 0;
unsigned int lineno = 0;
struct parsed_line line;
char *fname;
if (map == 0) {
return;
}
if (makenew) {
if (newmap == 0)
name);
} else
newmap = 0;
/* Get last one or two components non-trivial of pathname */
if (verbose) {
if (*tmp == '/') {
if (++cnt == 1)
else {
/* Triviality check */
cnt--;
}
}
}
if (cnt < 2)
}
nchanges = 0;
int root_owner, mode_diff = 0;
int changed = 0;
lineno ++;
case LINE_IGNORE:
continue;
case LINE_ERROR:
errors++;
continue;
}
if (restrictto) {
char nbuf[MAXPATHLEN];
continue;
}
continue;
/*
* The following heuristics are used to determine whether a file
* can be safely chown'ed to root:
* - it's not set-uid.
* and one of the following applies:
* - it's not writable by the current owner and is
* - it's world executable and a file
* - owner, group and world permissions are identical
* - it's a bin owned directory or a "non-volatile"
* file (any owner) for which group and other r-x
* permissions are identical, or it's a bin owned
*/
!root_owner &&
basedir_len < 18 &&
basedir_len) == 0 &&
if (!diffout) {
(void) printf("%c o %s -> root %s%s [%.*o]\n",
}
root_owner = 1;
changed = 1;
}
/*
* Strip user write bit if owner != root and executable by user.
* root can write even if no write bits set
* Could prevent executables from being overwritten.
*/
for (i = 0; i < nexceptions; i++) {
exceptions[i]+basedir_len) == 0)
break;
}
} else {
for (i = 0; i < nexceptions; i++) {
basedir_len) == 0 &&
exceptions[i]+basedir_len) == 0)
break;
}
}
if (i == nexceptions)
}
if (mode_diff) {
if (!diffout) {
printf("%c %c %04o -> %04o %s%s\n",
's' : 'm',
}
changed = 1;
}
}
}
}
return;
}
if (nchanges == 0)
else if (installnew) {
char oldname[MAXPATHLEN];
"Couldn't install %s: %s\n",
}
}
}