funk@osiris.cso.uiuc.edu (10/15/89)
You can pull down the source for talk from comp.sources.unix It's v06i010 ------------------------------------------------------------------------------- | Bruce Funk INTERNET: funk@osiris.cso.uiuc.edu | |ACSEH, 21st TAACOM __________________________________________________| |Kaiserslautern, W. Germany | Any resemblance between me and reality | |(guesting on osiris) | is strictly coincidental | -------------------------------------------------------------------------------
egray@fthood.UUCP (10/21/89)
Below is a library of routines for working arround the set-user/group-id problems... Emmet P. Gray US Army, HQ III Corps & Fort Hood ...!uunet!uiucuxc!fthood!egray Attn: AFZF-DE-ENV fthood!egray@uxc.cso.uiuc.edu Directorate of Engineering & Housing Environmental Management Office Fort Hood, TX 76544-5057 ----------------------------------------------------------------------------- /* * This test program demonstrates a problem with v7-derived Unix systems * where you can't flip-flop back and forth between the real and effective * UID/GID in a set-user/group-id program. * * functions such as link() and unlink() are easy to fix with a fork() * because they don't return any resources from the child process. * However for fopen() and open(), this can't be done quite so easily, * you essentially ignore the set-user/group-id priviledges, and open * the file if your real id's would allow it. * * This file contains: * uid_fopen(), uid_open(), uid_link(), and uid_unlink() */ #define SETUID_BROKE #define DENIED 0 #define WRITE_OK 1 #define OK_BUT_EXISTS 2 #include <stdio.h> #include <signal.h> #include <fcntl.h> main() { FILE *fp, *uid_fopen(); char buf[80]; printf("uid=%d, gid=%d, euid=%d, egid=%d\n", getuid(), getgid(), geteuid(), getegid()); if ((fp = uid_fopen("testfile", "r")) == NULL) perror("uid_fopen"); if (fread(buf, sizeof(char), 80, fp) < 0) perror("fread"); printf("buf=%s", buf); if (fclose(fp) < 0) perror("fclose"); printf("uid=%d, gid=%d, euid=%d, egid=%d\n", getuid(), getgid(), geteuid(), getegid()); exit(0); } /* * Check write permission with the real UID and GID. Returns a 0 on * permission denied, 1 on OK, and 2 on OK-but the file already exists. */ int can_write(file) char *file; { char *p, path[256], *strcpy(), *strrchr(); strcpy(path, file); /* dissect the path component */ if (p = strrchr(path, '/')) *p = '\0'; else strcpy(path, "."); /* if it already exists */ if (!access(file, 0)) { if (!access(file, 2)) return(OK_BUT_EXISTS); return(DENIED); } /* if path is writable */ if (!access(path, 2)) return(WRITE_OK); return(DENIED); } /* * Open a file if (and only if) your real UID/GID would allow it. */ FILE * uid_fopen(file, mode) char *file, *mode; { FILE *fp; #ifdef SETUID_BROKE char flag; /* the sum of the two characters */ flag = *mode + *(mode+1); switch (flag) { case 'r': /* read permission */ if (access(file, 4)) fp = (FILE *) NULL; else fp = fopen(file, mode); break; case 'r' + '+': /* read & write */ if (access(file, 4) || access(file, 2)) fp = (FILE *) NULL; else fp = fopen(file, mode); break; case 'w': case 'a': /* write & create */ switch(can_write(file)) { case DENIED: fp = (FILE *) NULL; break; case OK_BUT_EXISTS: fp = fopen(file, mode); break; case WRITE_OK: fp = fopen(file, mode); /* fix the owner */ chown(file, getuid(), getgid()); break; } break; case 'w' + '+': case 'a' + '+': /* read & write & create */ switch(can_write(file)) { case DENIED: fp = (FILE *) NULL; break; case OK_BUT_EXISTS: if (access(file, 4)) fp = (FILE *) NULL; else fp = fopen(file, mode); break; case WRITE_OK: fp = fopen(file, mode); /* fix the owner */ chown(file, getuid(), getgid()); break; } break; } #else /* SETUID_BROKE */ int euid, egid; euid = geteuid(); egid = getegid(); /* abdicate the throne */ setuid(getuid()); setgid(getgid()); fp = fopen(file, mode); /* put things back */ setuid(euid); setgid(egid); #endif /* SETUID_BROKE */ return(fp); } /* * Create a link with real UID/GID. */ int uid_link(path1, path2) char *path1, *path2; { int ret, status, euid, egid; void _exit(); #ifdef SETUID_BROKE switch(fork()) { case 0: setuid(getuid()); setgid(getgid()); _exit(link(path1, path2)); case -1: fprintf(stderr, "uid_link: Can't fork\n"); return(-1); default: if (wait(&status) == -1) { fprintf(stderr, "uid_link: wait failed\n"); return(-1); } ret = status >> 8; } #else /* SETUID_BROKE */ euid = geteuid(); egid = getegid(); setuid(getuid()); setgid(getgid()); ret = link(path1, path2); setuid(euid); setgid(egid); #endif /* SETUID_BROKE */ return(ret); } /* * Unlink a file with real UID/GID. */ int uid_unlink(path) char *path; { int ret, status, euid, egid; void _exit(); #ifdef SETUID_BROKE switch(fork()) { case 0: setuid(getuid()); setgid(getgid()); _exit(unlink(path)); case -1: fprintf(stderr, "uid_unlink: Can't fork\n"); return(-1); default: if (wait(&status) == -1) { fprintf(stderr, "uid_unlink: wait failed\n"); return(-1); } ret = status >> 8; } #else /* SETUID_BROKE */ euid = geteuid(); egid = getegid(); setuid(getuid()); setgid(getgid()); ret = unlink(path); setuid(euid); setgid(egid); #endif /* SETUID_BROKE */ return(ret); }