Orphaned Response

egray at fthood.UUCP egray at fthood.UUCP
Sat Oct 21 06:52:00 AEST 1989


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 at 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);
}



More information about the Comp.unix.questions mailing list