[net.sources] Access -- a program to give permissions across logins

bet@ecsvax.UUCP (Bennett E. Todd III) (10/17/86)

#!/bin/sh
# Cut above the preceeding line, or cut here if you must.
# This is a shar archive.  Extract with sh, not csh.
# The rest of this file will extract:
# README Makefile access.c regerror.c
sed 's/^X//' > README << '/*EOF'
XAccess is a program I wrote to handle a not-so-infrequent situation that
Xstandard UNIX file permissions didn't seem to address, quite. Given a
Xset of users (e.g. students in a class) which are to be protected from
Xeach other (can't read or destroy each other's homework, for example)
Xand one or more "senior" logins (e.g. the teacher of the class, TAs for
Xthe class) arrange for these (individually protected from each other)
Xlogins to all be accessible to the senior, administrative logins. The
Xsenior logins aren't to be given root permissions. I couldn't figure a
Xway to do this with the standard UNIX permissions system, so I wrote the
Xfollowing. It should be installed somewhere suid root (I'd make it owner
Xroot, in a directory that isn't world writeable, mode 6711. The setgid
Xprobably isn't necessary). It checks a file "/etc/accesstab" (shouldn't
Xbe world writeable, right) for lines of the form
X
X	regexp1:regexp2
X
XIf the requesting user's username matches regexp1, then a regsub is done
Xfrom that match onto regexp2, and the resulting pattern is matched
Xagainst the requested destination. If it matches, the program succeeds.
XOtherwise, it fails. In behavior, it is about the same as su(1) with the
Xfollowing differences:
X
X	1) Doesn't require any password
X	2) Requires a match in the accesstab file
X	3) Changes to the login home directory of the requested user.
X
X(3) above is possibly not appropriate; it turned out to be proper for
Xthe use I had for it, and didn't seem to me to be a bad idea. You can
Xrip it out if you don't like it.
X
XCaveats:  I am not sure that this isn't a serious breach of security;
XI cannot think of a way to break it, but that doesn't mean it's
Xsecure. Use at your own risk, and check it before you use it. This
Xcritter explicitly kicks holes in the UNIX security system; don't use
Xit to give any logins access to privileged logins such as root, and
Xit probably shouldn't be used for any permanent project, only for
Xtemporary undertakings. DON'T TRY TO USE IT IF YOU AREN'T COMPLETELY
XCOMFORTABLE WITH REGULAR EXPRESSIONS!!! Use completely explicit
Xregular expressions, to ensure no accidental matches; regular
Xexpression metacharacters should only be used whete the logins have a
Xsystematic format (such as class logins with a common format). A
Xreasonable use would be for a class with a teacher "deboss" with
Xclass logins "cs00" - "cs49":
X
X    deboss:cs[0-4][0-9]
X
XMake sure that not only are all logins that should be permitted
Xmatchable by the target, but that all patterns matchable by the
Xtarget are ones that are supposed to be reachable.
X
XThis program is built with Henry Spencer's V8-compatible regexp
Xlibrary, from mod.sources. Get it. It's excellent.
X
XThis program was written and tested under System V (AT&T System V
XRelease 2.0.1 3B5 Version 2); as far as I know it should be portable to
Xmost reasonably modern UNIXs.
X
X-Bennett
/*EOF
ls -l README
sed 's/^X//' > Makefile << '/*EOF'
X# MYINCL is where the include file "regexp.h" can be found, if it isn't
X# in your /usr/include
XMYINCL=$(HOME)/include
X
X# Likewise for regexp.a, if the regexp routines aren't in your libc.a
XMYLIBS=$(HOME)/lib
X
XCFLAGS=-O -I$(MYINCL)
X
Xaccess : access.o regerror.o
X	cc -O -o access access.o regerror.o $(MYLIBS)/regexp.a
/*EOF
ls -l Makefile
sed 's/^X//' > access.c << '/*EOF'
X#include <stdio.h>
X#include "regexp.h"
X/*
X * access (userid) [command]
X *
X * Scans /etc/accesstab. Accesstab format
X *
X *	regex1:regex2
X *
X * If the login name of the invoker matches regex1, then a regsub is
X * performed on regex2, and if the requested "(userid)" matches the
X * resulting string, then it does a setgid to the gid of the requested
X * "(userid)", a setuid to the appropriate userid, then exec's /bin/sh.
X * It also changes directory to the home directory of the requested userid.
X * [command], if specified, is passed to sh(1).
X */
X
Xchar *progname;
Xstatic char **shargv;
Xstatic char *nope = "permission denied";
Xstatic char *tablename = "/etc/accesstab";
X
X#ifndef ERR
X#define ERR (-1)
X#endif
X
Xmain(argc, argv)
Xint argc;
Xchar **argv;
X{
X	char *source,
X		 *dest,
X		 *getlogin();
X
X	progname = argv[0];
X
X	if (argc < 2)
X		syntax();
X
X	dest = argv[1];
X	shargv = argv+1;
X
X	if ((source = getlogin()) == NULL)
X		error("Who are you?!");
X
X	if (check(source, dest))
X		doit(dest);
X	else
X		error(nope);
X}
X
Xsyntax()
X{
X	fprintf(stderr, "syntax: %s username [command]\n", progname);
X	exit(1);
X}
X
Xerror(s)
Xchar *s;
X{
X	fprintf(stderr, "%s: %s\n", progname, s);
X	exit(1);
X}
X
XFILE *efopen(name, mode)
Xchar *name, *mode;
X{
X	FILE *fp, *fopen();
X	if (fp = fopen(name, mode))
X		return(fp);
X
X	fprintf(stderr, "%s: cannot open %s\n", progname, name);
X	exit(1);
X}
X
Xcheck(s, d)
Xchar *s, *d;
X{
X	FILE *fp, *efopen();
X	char buffer[256], *ptr, destbuff[256], *strchr();
X	regexp *r_src, *r_dest, *regcomp();
X
X	fp = efopen(tablename, "r");
X	while (fgets(buffer, 256, fp)) {
X		if (strlen(buffer) < 3 || *buffer == '#' || !strchr(buffer, ':'))
X			continue;
X		while ((ptr=strchr(buffer, '\n')) || (ptr=strchr(buffer, '\r')))
X			*ptr = '\0';
X		if (!(ptr=strchr(buffer, ':')))
X			error("parse error in accesstab");
X		*ptr++ = '\0';
X		r_src = regcomp(buffer);
X		if (!regexec(r_src, s))
X			continue;
X		if (!*ptr)
X			error("bad dest expression in accesstab");
X		regsub(r_src, ptr, destbuff);
X		r_dest = regcomp(destbuff);
X		if (regexec(r_dest, d)) {
X			fclose(fp);
X			return(1);
X		}
X	}
X	fclose(fp);
X	return(0);
X}
X
X#include <pwd.h>
X
Xdoit(name)
Xchar *name;
X{
X	struct passwd *ptr, *getpwnam();
X
X	if (!(ptr=getpwnam(name)))
X		error("no such userid");
X	endpwent();
X
X	if (strcmp(name, ptr->pw_name) != 0)
X		error("wrong passwd entry returned");
X
X	if (chdir(ptr->pw_dir) == ERR)
X		error("cannot chdir");
X
X	if (setgid(ptr->pw_gid) == ERR)
X		error("cannot setgid");
X
X	if (setuid(ptr->pw_uid) == ERR)
X		error("cannot setuid");
X
X	shargv[0] = "sh";
X
X	execv("/bin/sh", shargv);
X	error("exec failed");	
X}
/*EOF
ls -l access.c
sed 's/^X//' > regerror.c << '/*EOF'
X#include <stdio.h>
X
Xextern char *progname;
X
Xvoid
Xregerror(s)
Xchar *s;
X{
X	fprintf(stderr, "%s: regexp(3): %s", progname, s);
X	exit(1);
X}
/*EOF
ls -l regerror.c
exit
-- 

Bennett Todd -- Duke Computation Center, Durham, NC 27706-7756; (919) 684-3695
UUCP: ...{decvax,seismo,philabs,ihnp4,akgua}!mcnc!ecsvax!duccpc!bet
BITNET: DBTODD@TUCC.BITNET -or- DBTODD@TUCCVM.BITNET -or- bet@ECSVAX.BITNET

liam@cs.qmc.ac.uk (William Roberts) (10/24/86)

>Access is a program I wrote to handle a not-so-infrequent situation that
>standard UNIX file permissions didn't seem to address, quite. Given a
>set of users (e.g. students in a class) which are to be protected from
>each other (can't read or destroy each other's homework, for example)
>and one or more "senior" logins (e.g. the teacher of the class, TAs for
>the class) arrange for these (individually protected from each other)
>logins to all be accessible to the senior, administrative logins. The
>senior logins aren't to be given root permissions. I couldn't figure a
>way to do this with the standard UNIX permissions system.

The way to do this is to use UNIX group permissions. In 4.2 BSD
(can't speak for any other flavour or version), the /etc/passwd
file contains both the UID and GID of each user. Each file has
both a user (UID) and a group (GID) that "own" it, and the file
permissions distinguish between permissions for the UID that owns
the file, permissions for people who belong to the group that
owns the file, and all the rest of the universe (known
collectively as "others").

It is not necessary for the user who owns the file to be in the
group that owns it. The problem you describe is covered by the
case:

% ls -lg ~astudent
total 5
-rw-r----- 1 astudent seniors     341 Fri Oct 24 12:13:14  asgnmt.c
drwxr-x--- 2 astudent seniors     512 Fri Oct 20 10:01:43  private

etc etc.

If the teacher and other suitable people have GID "seniors",
then they can read the files in this directory, as can user
"astudent" who is in group "students". Her classmates, also in
group student, are neither owner nor group owner of these
files, so they have to put up with the "others" permissions
i.e. none whatsoever.  This achieves the desired effect.

In order to make this state of affairs automatic, two other
measures must be taken:

1) The student home directories must have group ownership "seniors"
2) The umask for each student must be set to 027.

Files inherit the group ownership of the directory in which
they are made (I believe - references in the manual anyone?),
so measure (1) causes student files to belong to group "seniors"
by default. The second measure is definitely a 4.2ism: when a
file is created, the file permissions are ANDed with NOT(umask),
so 027 (octal) means "remove group write permission and all
permissions for other". The system default umask is set by the
login program to 022, but the command "umask 027" in start-up
files like .login, .cshrc and .profile will have the necessary
effect.

The advantages of doing it like this are many and various:
there is no need for dodgy "su" programs, the students can
selectively make some of their files more public (or less
public) and the whole thing is fairly easy to understand.

If you need to have different groups for different courses (e.g
when 3rd year students act as supervisors on 1st year courses),
then you can use the 4.2 BSD facilities for putting individuals
in more than one group (see group(5) in the manual).


This is the system we use in the Computer Science Department
here at QMC and (touch wood) it does seem to be working nicely.