home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
The World of Computer Software
/
World_Of_Computer_Software-02-385-Vol-1of3.iso
/
c
/
cops_104.zip
/
cops_104
/
src
/
is_able.c
< prev
next >
Wrap
C/C++ Source or Header
|
1992-03-10
|
6KB
|
222 lines
/*
Usage:
is_able filename {w|g|s|S} {r|w|B|b|s}
(world/group/SUID/SGID read/write/{read&write}/{suid&write}/s[ug]id)
The second arg of {r|w} determines whether a file is (group or world
depending on the first arg of {w|g}) writable/readable, or if it is
SUID/SGID (first arg, either s or S, respectively), and prints out a
short message to that effect.
So:
is_able w w # checks if world writable
is_able g r # checks if group readable
is_able s s # checks if SUID
is_able S b # checks if world writable and SGID
Permissions bits: vvv--- Permission bits
1 = execute 00000
2 = writable ^
4 = readable + Setuid bits
Setuid bits:
1 = sticky
2 = set group id
4 = set user od
Pete Shipley (shipley@mica.berkeley.edu) gutted my original code,
made in cleaner and smarter, and combined everything into one compact
file. What a deal, huh? Then I came along and beat up his code and
made it look ugly again (I changed the is_writeable option to return
true if _any_ parent directories are writable, not just the target. So
you can blame me if you want. Better yet, just send me a patch if I
blew it.)
*/
#include <sys/types.h>
#include <sys/stat.h>
#include <ctype.h>
#include <stdio.h>
#define G_READ_TEST 00044 /* group (or world) readable */
#define W_READ_TEST 00004 /* world readable */
#define G_READ_STRING "Warning! %s is group readable!\n"
#define W_READ_STRING "Warning! %s is _World_ readable!\n"
#define G_WRITE_TEST 00022 /* group (or world) writable */
#define W_WRITE_TEST 00002 /* world writable */
#define G_WRITE_STRING "Warning! %s is group writable!\n"
#define G_WRITE_TREE "Warning! %s is group writable! (*)\n"
#define W_WRITE_STRING "Warning! %s is _World_ writable!\n"
#define W_WRITE_TREE "Warning! %s is _World_ writable! (*)\n"
#define SGID_TEST 02000 /* set group id */
#define SUID_TEST 04000 /* set user id */
#define SUID_STRING "Warning! %s is SUID!\n"
#define SGID_STRING "Warning! %s is SGID!\n"
char usage[]="Usage: is_able file {w|g|S|s} {r|w|B|b|}\n";
main(argc,argv)
int argc;
char **argv;
{
char file[256],wg,rwb,gstring[35],wstring[35],wstring_tree[50],
gstring_tree[50], suidstring[35];
register int group, read, write, both, suid, sgid, verbose, xmode;
static struct stat statb;
group=read=write=suid=sgid=both=verbose=xmode=0;
/* check out arguments */
if (argc != 4) {
fprintf(stderr, usage);
exit(1);
}
/* parse arguments */
strcpy(file, argv[1]);
/* get stats on file in question -- if doesn't exist, exit */
if (stat(file,&statb) < 0) {
fprintf(stderr, file);
exit(2);
}
wg = argv[2][0]; /* world or group */
rwb = argv[3][0]; /* read/write/both */
/* set the report string and some flags */
if (wg == 'g') group = 1;
else if (wg == 's') suid = 1;
else if (wg == 'S') sgid = 1;
if (rwb == 'r') {
if (group) strcpy(gstring, G_READ_STRING);
else strcpy(wstring, W_READ_STRING);
read = 1;
}
else if (rwb == 's')
(suid?strcpy(suidstring,SUID_STRING):strcpy(suidstring,SGID_STRING));
else if (rwb == 'w') {
if (group) {
strcpy(gstring, G_WRITE_STRING);
strcpy(gstring_tree, G_WRITE_TREE);
}
else {
strcpy(wstring, W_WRITE_STRING);
strcpy(wstring_tree, W_WRITE_TREE);
}
write = 1;
}
else if (rwb == 'b') {
/* do the write first, then read check */
if (group) strcpy(gstring, G_WRITE_STRING);
else strcpy(wstring, W_WRITE_STRING);
if (suid) strcpy(suidstring,SUID_STRING);
both = read = write = 1;
}
else if (rwb == 'B') {
/* do the write first, then s[ug]id check */
if (suid) strcpy(suidstring, SUID_STRING);
else if (sgid) strcpy(suidstring, SGID_STRING);
else {
fprintf(stderr, usage);
exit(1);
}
both = write = 1;
}
else {
fprintf(stderr, usage);
exit(1);
}
/*
* the write stuff, so to speak...
* What I'm doing in this mess is to parse the file in question, check out
* whole path; 'cause if anything is world writable, you can compromise.
* For instance, if /usr is world writable, then /usr/spool/mail is
* compromisable, no matter what its permissions are.
*
*/
if (write) {
/* 256 levels of dirs, max len each 256 chars */
char foo_dirs[256][256];
char *foo_file;
int i = 0, j;
foo_file = file;
strcpy(foo_dirs[i++], foo_file);
j=strlen(foo_file) - 1;
do {
if (foo_file[j] == '/')
strncpy(foo_dirs[i++], foo_file, j);
} while (--j > 0);
for (j = 0; j < i; j++) {
if (stat(foo_dirs[j],&statb) < 0)
continue;
xmode=statb.st_mode;
if (!group) {
if (xmode & W_WRITE_TEST) {
/* want to distinguish between file being
writable and directory tree being
writable; j==0 means actual file */
if (!j) printf( wstring, file);
else printf( wstring_tree, file);
if (both) goto bboth;
exit(!xmode);
}
}
else if (xmode & G_WRITE_TEST) {
if (!j) printf( gstring, file);
else printf( gstring_tree, file);
if (both) goto bboth;
exit(!xmode);
}
}
if (!both) exit(!xmode);
}
bboth:
if (both) if (stat(file,&statb) < 0) {
fprintf(stderr, file);
exit(2);
}
/* find premissions on file in question */
if (group)
xmode = statb.st_mode & G_READ_TEST;
else
xmode = statb.st_mode & W_READ_TEST;
if (wg == 's') {
/* check SUID */
xmode = statb.st_mode & SUID_TEST;
if (xmode) printf( suidstring, file);
exit (!xmode);
}
if (wg == 'S') {
/* check SGID */
xmode = statb.st_mode & SGID_TEST;
if (xmode) printf( suidstring, file);
exit (!xmode);
}
if (rwb == 'b') {
/* do the read now */
if (group) strcpy(gstring, G_READ_STRING);
else strcpy(wstring, W_READ_STRING);
}
/* report finding */
if (xmode) printf( (group ? gstring : wstring), file);
exit(!xmode);
}