home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Usenet 1994 October
/
usenetsourcesnewsgroupsinfomagicoctober1994disk2.iso
/
unix
/
volume2
/
access
< prev
next >
Wrap
Internet Message Format
|
1986-11-30
|
39KB
Date: Wed, 26 Jun 85 11:39:25 edt
From: linus!security!jjg (Jeff Glass)
Subject: source for access control lists
This should be everything you need to install access control lists under 4.2bsd.
create a directory somewhere to hold this stuff. run the rest of this
through sh . then follow the instructions in README.
------------ cut here -------------
#! /bin/sh
echo x - Makefile
cat >Makefile <<'!E!O!F!'
CFLAGS = -O
ALL = edacl lsacl chacl cpacl
BINDIR = /usr/local
# .l (man page) files confuse make, which thinks they are lex programs
.SUFFIXES :
.SUFFIXES : .out .o .c .e .r .f .y .s .p
all : ${ALL}
edacl :
cp edacl.sh edacl
lsacl : lsacl.o
cc lsacl.o -o lsacl
chacl : chacl.o
cc chacl.o -o chacl
cpacl : cpacl.o
cc cpacl.o -o cpacl
clean :
rm -f *.o core a.out ${ALL}
man :
cp chacl.l cpacl.l edacl.l lsacl.l /usr/man/manl
cp getacl.2 setacl.2 /usr/man/man2
install : ${ALL}
for i in ${ALL} ; do install -m 755 $$i ${BINDIR} ; done
!E!O!F!
echo x - README
cat >README <<'!E!O!F!'
This should explain how to install the mods for access control lists.
I am writing this after the fact, though, so if I have forgotten anything,
let me know.
0) after unpacking, you should have the files
README README.kernel
h.pat sys.pat
acl.h acl.c
getacl.2 setacl.2
login.pat
Makefile jjg.h
chacl.c chacl.l
cpacl.c cpacl.l
edacl.sh edacl.l
lsacl.c lsacl.l
1) h.pat and sys.pat are patches to files in the /sys/h and /sys/sys
directories. if you have the patch program, then
( cd /sys/h ; patch ) < h.pat
( cd /sys/sys ; patch ) < sys.pat
otherwise you will have to apply the patches to the files by hand.
2) install the files acl.h and acl.c in /sys/h and /sys/sys , respectively.
3) make and install the new kernel. brief instructions are in README.kernel .
4) edit the Makefile and change BINDIR to whatever. then "make install"
to get the programs chacl cpacl edacl lsacl .
5) login.pat is a patch to /usr/src/bin/login.c , to make login clear
the acl on the user's terminal. patch, make, and install login .
6) install the man pages; "make man" will do it.
7) reboot.
8) read the man pages for the programs, and try them out.
9) please send comments, fixes, improvements, and (mild) flames back
to me - linus!security!jjg .
!E!O!F!
echo x - README.kernel
cat >README.kernel <<'!E!O!F!'
This is a brief set of instructions for installing the system calls getacl()
and setacl(). It is intended for people like me, who have never hacked the
kernel much before.
1) add the routines "getacl" and "setacl" to the standard library.
for the VAX, this requires that you:
a) create the files getacl.c and setacl.c in
/usr/src/lib/libc/vax/sys . copy one of the existing
files there (like read.c) to get the format.
b) update the Makefile in that directory. add "getacl.o"
and "setacl.o" to the definition of the OBJS variable.
c) add the two lines
#define SYS_getacl 151
#define SYS_setacl 152
to the end of the file /usr/include/syscall.h . change "151"
and "152" to whatever are the next two numbers.
d) cd to /usr/src/lib/libc , and "make" . then "make install".
2) add the names of the new system calls ( "getacl" and "setacl" ) to
the end of the array syscallnames in /sys/sys/syscalls.c .
3) in /sys/sys/init_sysent.c , declare getacl() and setacl() as int functions,
and add the two lines
3, getacl, /* 151 = getacl */
3, setacl, /* 152 = setacl */
to the end of the sysent array declaration.
4) cd to /sys/conf , add the one line
sys/acl.c standard
to the file /sys/conf/files , and run "/etc/config MACH",
where MACH is your machine's name.
5) cd to ../MACH and "make depend", and "make".
6) install vmunix in / .
!E!O!F!
echo x - TODO
cat >TODO <<'!E!O!F!'
1) put lsacl(1) into ls(1) . ls needs a "-A" option :-)
2) make cp(1) copy the file's acl to the destination file.
3) when execve(2) is checking for any IEXEC permission
( i.e., "(ip->i_mode & (IEXEC|IEXEC>>3|IEXEC>>6) == 0)" ),
check for an acl entry which has IEXEC permission.
4) come up with a default acl when creating files and directories.
Multics allows defaults at the user/directory level. a compromise
might be to allow a default at the user level, and store it in the
u area, ala umask(2).
5) increase the size of an acl. this requires putting the acl somewhere
other than the inode, or increasing the size of the inode beyond 128 bytes.
!E!O!F!
echo x - acl.c
cat >acl.c <<'!E!O!F!'
#include "../h/param.h"
#include "../h/systm.h"
#include "../h/dir.h"
#include "../h/inode.h"
#include "../h/user.h"
#include "../h/nami.h"
#include "../h/acl.h"
#define FOLLOW_LINK 1
/*
* pack_acle() copies an acle struct into the inode. the acle is packed
* since the inode is rather short of space.
*/
pack_acle( ip, a, n )
struct inode *ip;
struct acle a;
int n;
{
/* clear the type and mode fields */
ip->i_acle[n/2] &= 017 << ((n%2) ? 4 : 0);
/* set the type, mode, and id fields */
ip->i_acle[n/2] |= ( a.a_type != A_GROUP ? A_USER : A_GROUP ) <<
((n%2) ? 3 : 7);
ip->i_acle[n/2] |= ( a.a_mode & 07 ) << ((n%2) ? 0 : 4);
ip->i_aclid[n] = a.a_id;
}
/*
* null_acle() puts an end-of-acl marker in the acl at entry n.
* the end-of-acl marker looks like an acl entry for root
* (a_type == A_USER && a_id == 0).
*/
null_acle( ip, n )
struct inode *ip;
int n;
{
/* clear the type and mode fields */
ip->i_acle[n/2] &= 017 << ((n%2) ? 4 : 0);
/* clear the id field */
ip->i_aclid[n] = 0;
}
/*
* getacl() returns the set of acl entries associated with the file.
* the user must be able to access the file in order to read the acl.
*/
getacl()
{
struct inode *ip;
struct acle ka[MAXACL];
struct a {
char *fname;
int n_acle;
struct acle *acle_p;
} *uap = (struct a *) u.u_ap;
int i;
if ( (ip = namei(uchar,LOOKUP,FOLLOW_LINK)) != NULL ) {
/* unpack the acl entries */
for (i = 0; i < MAXACL && ok_acle(ip,i); i++ ) {
ka[i].a_type = acle_type( ip, i );
ka[i].a_mode = acle_mode( ip, i );
ka[i].a_id = acle_id( ip, i );
}
/* getacl doesn't need the inode any more. */
iput( ip );
if ( uap->n_acle < i ) {
u.u_error = EINVAL;
} else {
/*
* copy the acl entries to the user's buffer,
* and return the number of entries found.
*/
u.u_error = copyout((caddr_t) ka, (caddr_t) uap->acle_p,
i*sizeof(struct acle));
if ( u.u_error == 0 ) u.u_r.r_val1 = i;
}
}
}
/*
* setacl() defines the acl for an inode. to do this, the user must
* be the owner of the inode, or else must be the superuser.
*
* bugs: acl entries should be sorted by a_type , so that access() does
* not have to read the entire acl three times. since the number of acl
* entries is so small, though, this would make setacl() more complex
* (sorting the entries) without really buying that much.
*/
setacl()
{
struct inode *ip;
struct acle ka[MAXACL];
int kn;
struct a {
char *fname;
int n_acle;
struct acle *acle_p;
} *uap = (struct a *) u.u_ap;
int i;
/*
* copy the user's arguments into kernel space; kn will contain
* the number of acl entries, and ka will contain the entries.
*/
if ( (kn = uap->n_acle) > MAXACL ) {
u.u_error = EINVAL;
} else {
u.u_error = copyin( (caddr_t) uap->acle_p, (caddr_t) ka,
kn*sizeof(struct acle) );
if ( u.u_error == 0 ) {
if ( (ip = owner(FOLLOW_LINK)) != NULL ) {
for ( i = 0; i < kn; i++ ) {
pack_acle( ip, ka[i], i );
if (! ok_acle(ip,i) ) break;
}
if ( kn < MAXACL ) null_acle( ip, kn );
/* update ctime for the inode */
ip->i_flag |= ICHG;
iput( ip );
}
}
}
}
!E!O!F!
echo x - acl.h
cat >acl.h <<'!E!O!F!'
#define A_GROUP 1
#define A_USER 0
/*
* macros to disassemble the packed acl in the inode
*/
#ifdef KERNEL
#define acle_type(ip,n) ( ( (ip)->i_acle[(n)/2] >> (((n)%2) ? 3 : 7) ) & 01 )
#define acle_id(ip,n) (ip)->i_aclid[(n)]
#define acle_mode(ip,n) ( ( (ip)->i_acle[(n)/2] >> (((n)%2) ? 0 : 4) ) & 07 )
#define ok_acle(ip,n) ( ( acle_type((ip),(n)) != A_USER ) || ( acle_id((ip),(n)) != 0 ) )
#else
#define ok_uacle(a) ( ( (a).a_type != A_USER ) || ( (a).a_id != 0 ) )
#endif
struct acle {
char a_type; /* A_GROUP or A_USER */
char a_mode; /* 0 (no permission) through 7 (rwx) */
int a_id; /* gid or uid, depending */
};
!E!O!F!
echo x - chacl.c
cat >chacl.c <<'!E!O!F!'
#include <stdio.h>
#include <sys/param.h>
#include <sys/acl.h>
#include <grp.h>
#include <pwd.h>
#include "jjg.h"
#define ERR_FATAL 1 /* for the err() routine */
#define ERR_NONFATAL 0 /* for the err() routine */
/*
* calling sequence: chacl [-u [user mode]] [-g [group mode]] -f [file]
*
* items enclosed in brackets may repeat.
*/
main(argc, argv, environ)
int argc;
char *argv[], *environ[];
{
int i, j, na;
struct acle acl[MAXACL];
int is_u_type;
int filec;
int status;
na = 0;
for ( i = 1; i < argc; i++ ) {
if ( equal(argv[i],"-u") || equal(argv[i],"-g") ) {
is_u_type = equal( argv[i], "-u" );
} else if ( equal(argv[i],"-f") ) {
break;
} else if ( *argv[i] == '-' ) {
err( ERR_FATAL, "unrecognized option - %s", argv[i] );
} else {
struct passwd *up;
struct group *gp;
int valid_name;
if ( is_u_type ) {
up = getpwnam( argv[i] );
valid_name = up != NULL;
} else {
gp = getgrnam( argv[i] );
valid_name = gp != NULL;
}
if ( valid_name ) {
i++;
if ( i < argc ) {
if ( is_u_type && up->pw_uid == 0 ) {
err( ERR_NONFATAL, "warning: cannot deny access to user %s", "root" );
} else {
if ( sscanf( argv[i], "%d", &acl[na].a_mode ) == 1 &&
acl[na].a_mode >= 0 && acl[na].a_mode <= 7 ) {
if ( na < MAXACL ) {
acl[na].a_type = is_u_type ? A_USER : A_GROUP;
acl[na].a_id = is_u_type ? up->pw_uid : gp->gr_gid;
na++;
} else {
err( ERR_NONFATAL, "warning: too many acl entries (must be no more than %d)", MAXACL );
}
} else {
err( ERR_FATAL, "unrecognizable mode - %d (must be 0-7)", acl[na].a_mode );
}
}
} else {
err( ERR_FATAL, "expected mode after %sname", is_u_type ? "user" : "group" );
}
} else {
err( ERR_FATAL, is_u_type ? "no such user - %s" : "no such group - %s", argv[i] );
}
}
}
/*
printf( "number of acle = %d\n", na );
printf( "file list begins at %d; argc = %d\n", i+1, argc );
for ( i = 0; i < na; i++ ) {
printf( "type = %s id = %d mode = %d\n", acl[i].a_type == A_GROUP ? "-g"
: "-u", acl[i].a_id, acl[i].a_mode );
}
*/
status = 0;
for ( filec = i+1; filec < argc; filec++ ) {
if ( setacl( argv[filec], na, acl ) != 0 ) {
perror( argv[filec] );
status++;
}
}
exit( status );
}
err( level, fmt, arg )
int level;
char *fmt, *arg;
{
char buf[240];
sprintf( buf, fmt, arg );
fprintf( stderr, "chacl: %s\n", buf );
if ( level == ERR_FATAL )
exit( 1 );
}
!E!O!F!
echo x - chacl.l
cat >chacl.l <<'!E!O!F!'
''' $Header:$
'''
''' $Log:$
.de Sh
.br
.ne 5
.PP
\fB\\$1\fR
.PP
..
.de Sp
.if t .sp .5v
.if n .sp
..
'''
''' Set up \*(-- to give an unbreakable dash;
''' string Tr holds user defined translation string.
''' Bell System Logo is used as a dummy character.
'''
.ie n \{\
.tr \(bs-\*(Tr
.ds -- \(bs-
.if (\n(.H=4u)&(1m=24u) .ds -- \(bs\h'-12u'\(bs\h'-12u'-\" diablo 10 pitch
.if (\n(.H=4u)&(1m=20u) .ds -- \(bs\h'-12u'\(bs\h'-8u'-\" diablo 12 pitch
.ds L" ""
.ds R" ""
.ds L' '
.ds R' '
'br\}
.el\{\
.ds -- \(em\|
.tr \*(Tr
.ds L" ``
.ds R" ''
.ds L' `
.ds R' '
'br\}
.TH chacl 1 LOCAL
.SH NAME
chacl - change the access control list for files
.SH SYNOPSIS
.B chacl
[-u username mode ...] [-g groupname mode ...] -f file ...
.SH DESCRIPTION
.I chacl
changes the access control lists for one or more files. The
access control list (or acl) and the file's ownership are used to determine
which users can access the file.
.PP
The
.B \-u
option marks the beginning of a list of "username mode" pairs.
Similarly, the
.B \-g
option marks the beginning of a list of "groupname mode" pairs.
A "name mode" pair is referred to as an "acl entry".
The
.B \-f
option separates the acl from the list of files involved.
.PP
The username or groupname is hunted for in the first field of /etc/passwd
or /etc/group , respectively.
The mode is a single digit in the range [0-7] indicating read-write-execute
permission ala
.IR chmod .
.PP
The current implementation allows up to 8 acl entries. This is defined
by the constant MAXACL in <sys/acl.h>. An entry cannot deny access
to the superuser.
Setting a file's acl removes any existing acl entries (i.e., calls to
.I chacl
do not accumulate). Only the owner of a file (or the superuser) can
change its acl.
.PP
Access decisions are made in the following way.
.Sp
If the effective userid (euid) is the superuser, grant access.
.Sp
Else if the euid is the file's owning uid, check the owner permission.
.Sp
Else if the euid matches an acl entry,
check the entry's mode.
.Sp
Else if the effective groupid (egid) matches an acl entry,
check the entry's mode.
.Sp
Else if some member of the group list matches an acl entry,
check the entry's mode.
.Sp
Else if the egid is the file's owning gid, check the group permission.
.Sp
Else if some member of the group list matches the file's owning gid,
check the group permission.
.Sp
Else check the other permission.
.PP
Order within the acl is important. When matching against the acl, entries
are checked in order, stopping at the first match. (Multiple matches could
only occur in group acl entries.)
.SH EXAMPLES
chacl -u jjg 7 -g d75 0 -f gossip
.Sp
grants full access to the file "gossip" to the user "jjg", and denies all
access to this file to the group "d75".
.Sp
chacl -f rumors
.Sp
resets the acl for the file "rumors".
.Sp
chacl -u jjg 6 uucp 4 -f facts
.Sp
grants read-write access to the user "jjg" and read access to the user
"uucp" for the file "facts".
.SH DIAGNOSTICS
Various messages indicate syntactic errors in the command line arguments.
Minor errors are flagged with a "warning:", and indicate that
.I chacl
carried on and set the access control lists for the files.
.PP
.I chacl
returns with exit status 0 if all is well; with exit status 1 if there
are errors in the command line arguments; or with exit status greater
than 0 if the acls for some files could not be set.
.SH SEE ALSO
cpacl(1),
edacl(1),
lsacl(1),
getacl(2),
setacl(2)
.SH CAVEAT
The implementation of access control lists is an experiment. This
implementation stores the access control list in a (formerly) unused field
in the inode.
There is no guarantee that future versions of Unix will not use this field
for something else.
.PP
Therefore, you should only use access control lists with the full
understanding that you are participating in an experiment.
.SH BUGS
Allowing wildcards in the acl entry's name would be useful.
!E!O!F!
echo x - cpacl.c
cat >cpacl.c <<'!E!O!F!'
#include <stdio.h>
#include <sys/param.h>
#include <sys/acl.h>
/*
* cpacl : copy the access control list of one file to other files.
*
* usage : cpacl fromfile tofile [tofile ...]
*/
main(argc, argv, environ)
int argc;
char *argv[], *environ[];
{
int i;
struct acle acl[MAXACL];
int status;
int na;
status = 0;
if ( argc > 1 ) {
if ( (na = getacl(argv[1],MAXACL,acl)) != -1 ) {
for ( i = 2; i < argc; i++ ) {
if ( setacl( argv[i], na, acl ) != 0 ) {
perror( argv[i] );
status++;
}
}
} else {
perror( argv[1] );
status++;
}
} else {
fprintf( stderr, "cpacl: usage: cpacl fromfile tofile [tofile ...]\n" );
}
exit( status );
}
!E!O!F!
echo x - cpacl.l
cat >cpacl.l <<'!E!O!F!'
''' $Header:$
'''
''' $Log:$
.de Sh
.br
.ne 5
.PP
\fB\\$1\fR
.PP
..
.de Sp
.if t .sp .5v
.if n .sp
..
'''
''' Set up \*(-- to give an unbreakable dash;
''' string Tr holds user defined translation string.
''' Bell System Logo is used as a dummy character.
'''
.ie n \{\
.tr \(bs-\*(Tr
.ds -- \(bs-
.if (\n(.H=4u)&(1m=24u) .ds -- \(bs\h'-12u'\(bs\h'-12u'-\" diablo 10 pitch
.if (\n(.H=4u)&(1m=20u) .ds -- \(bs\h'-12u'\(bs\h'-8u'-\" diablo 12 pitch
.ds L" ""
.ds R" ""
.ds L' '
.ds R' '
'br\}
.el\{\
.ds -- \(em\|
.tr \*(Tr
.ds L" ``
.ds R" ''
.ds L' `
.ds R' '
'br\}
.TH CPACL 1 LOCAL
.SH NAME
cpacl - copy the access control list of one file to other files
.SH SYNOPSIS
.B cpacl
fromfile tofile [tofile ...]
.SH DESCRIPTION
.I cpacl
copies the access control list of a file to the access control lists of
one or more other files. The access control list
and the file's ownership are used to determine which users can access
the file.
.PP
.I cpacl
gets the access control list for fromfile, and copies this acl to the acl
for each tofile.
.SH DIAGNOSTICS
.I cpacl
returns with exit status 0 if all is well; with exit status 1 if it
could not get the access control list for fromfile; or with exit status
greater than 0 if the access control lists for some tofiles could not
be set.
.SH SEE ALSO
chacl(1),
edacl(1),
lsacl(1),
getacl(2),
setacl(2)
.SH CAVEAT
The implementation of access control lists is an experiment. This
implementation stores the access control list in a (formerly) unused field
in the inode.
There is no guarantee that future versions of Unix will not use this field
for something else.
.PP
Therefore, you should only use access control lists with the full
understanding that you are participating in an experiment.
.SH BUGS
The entire mechanism hasn't been fully tested.
!E!O!F!
echo x - edacl.l
cat >edacl.l <<'!E!O!F!'
''' $Header:$
'''
''' $Log:$
.de Sh
.br
.ne 5
.PP
\fB\\$1\fR
.PP
..
.de Sp
.if t .sp .5v
.if n .sp
..
'''
''' Set up \*(-- to give an unbreakable dash;
''' string Tr holds user defined translation string.
''' Bell System Logo is used as a dummy character.
'''
.ie n \{\
.tr \(bs-\*(Tr
.ds -- \(bs-
.if (\n(.H=4u)&(1m=24u) .ds -- \(bs\h'-12u'\(bs\h'-12u'-\" diablo 10 pitch
.if (\n(.H=4u)&(1m=20u) .ds -- \(bs\h'-12u'\(bs\h'-8u'-\" diablo 12 pitch
.ds L" ""
.ds R" ""
.ds L' '
.ds R' '
'br\}
.el\{\
.ds -- \(em\|
.tr \*(Tr
.ds L" ``
.ds R" ''
.ds L' `
.ds R' '
'br\}
.TH EDACL 1 LOCAL
.SH NAME
edacl - edit the access control lists for files
.SH SYNOPSIS
.B edacl
file ...
.SH DESCRIPTION
.I edacl
edits the access control lists for one or more files. The access control list
and the file's ownership are used to determine which users can access
the file.
.PP
For each file,
.I edacl
invokes the editor of your choice on the access control list of that file.
The first line has the name of the file whose acl is being edited.
The following lines contain the acl entries.
.PP
Each acl entry has three fields. The first field is either the string "user"
or "group".
The second field is a user or group name; which it is depends on the
value of the first field. The last field is the mode granted to that
user or group. It is a single digit in the range [0-7],
indicating read-write-execute permission ala
.IR chmod .
.PP
You may add, change, or delete entries. Upon exiting the editor,
.I edacl
will ask you if you wish to update the acl for that file.
.SH DIAGNOSTICS
.I edacl
calls
.I lsacl(1)
and
.I chacl(1)
to do the dirty work; see the DIAGNOSTICS sections in their documentation.
.SH ENVIRONMENT VARIABLES
If the EDITOR variable is set,
.I edacl
assumes it contains the name of your desired editor. Otherwise,
.I edacl
will ask you which one to use.
.SH SEE ALSO
chacl(1),
cpacl(1),
lsacl(1),
getacl(2),
setacl(2)
.SH CAVEAT
The implementation of access control lists is an experiment. This
implementation stores the access control list in a (formerly) unused field
in the inode.
There is no guarantee that future versions of Unix will not use this field
for something else.
.PP
Therefore, you should only use access control lists with the full
understanding that you are participating in an experiment.
.SH BUGS
The entire mechanism hasn't been fully tested.
.PP
It might be nice if the mode were given symbolically ("rwx" instead
of "7").
!E!O!F!
echo x - edacl.sh
cat >edacl.sh <<'!E!O!F!'
#! /bin/csh -f
set path = ( /usr/new /usr/local /usr/ucb /usr/bin /bin . )
setenv PATH /usr/new:/usr/local:/usr/ucb:/usr/bin:/bin:.
unset noclobber
set noglob
set DEF_ED = /usr/ucb/vi
# WS == WhiteSpace ; WF == WordForming ; NF == NumberForming .
set WS = "[ ][ ]*"
set WSO = "[ ]*"
set WF = "[a-zA-Z][a-zA-Z0-9]*"
set NF = "[0-9][0-9]*"
foreach i ( $argv[*]:q )
set acl_file = "/tmp/edacl_${i:t}_$$"
lsacl "$i" > "$acl_file"
if ( $status == 0 ) then
set state = edit
while 1
switch ( $state )
case edit :
if ( ! $?EDITOR ) then
echo -n "edacl: editor [$DEF_ED] : "
set EDITOR = $<
if ( "_$EDITOR" == "_" ) set EDITOR = $DEF_ED
endif
/bin/sh -c "$EDITOR '$acl_file'"
if ( $status == 0 ) then
set state = prompt
else
unset EDITOR
unsetenv EDITOR
endif
breaksw
case prompt :
echo -n edacl: update acl for "$i" "? [yne] "
set response = $<
if ( "_$response" == "_" || "_$response" =~ _y* ) then
set state = update
else if ( "_$response" =~ _e* ) then
set state = edit
else if ( "_$response" =~ _n* ) then
set state = noupdate
else
echo "Yes - update acl; No - opposite of Yes; Edit - try again."
endif
breaksw
case update :
chacl `sed -e 1d -e '2,$'"s/^${WSO}user\(${WS}${WF}${WS}${NF}\)/-u \1/" -e '2,$'"s/^${WSO}group\(${WS}${WF}${WS}${NF}\)/-g \1/" "$acl_file"` -f "$i"
break
case noupdate :
break
default :
echo "edacl: internal error (in switch statement). HELP"
breaksw
endsw
end
rm -f "$acl_file"
endif
end
!E!O!F!
echo x - getacl.2
cat >getacl.2 <<'!E!O!F!'
.TH GETACL 2 "15 May 1985"
.UC 4
.SH NAME
getacl \- get the access control list for a file
.SH SYNOPSIS
.nf
.ft B
#include <sys/param.h>
#include <sys/acl.h>
.PP
.ft B
getacl(path, nacle, acl)
char *path;
int nacle;
struct acle *acl;
.fi
.SH DESCRIPTION
.I getacl
fetches the access control list of the file whose name is given by
.IR path .
The list is placed in the array
.IR acl .
The parameter
.I nacle
indicates the number of access control list entries which may be placed in
.IR acl .
No more than MAXACL entries, as defined in
.RI < sys/param.h >,
will be returned.
.PP
An access control list entry is described in
.RI < sys/acl.h >.
.PP
.nf
#define A_GROUP 1
#define A_USER 0
struct acle {
char a_type; /* A_GROUP or A_USER */
char a_mode; /* rwx [0-7] permissions */
int a_id; /* gid or uid according to a_type */
};
.fi
.PP
.I a_type
is a flag indicating whether the entry is for a user or a group.
.I a_id
contains either a uid or a gid, depending on the value of
.IR a_flag .
This user or group will have the permissions described by
.IR a_mode ,
which is an integer in the range 0-7. It indicates read-write-execute
permissions in the manner of
.IR chmod(2) .
.SH "RETURN VALUE
.I getacl
returns the number of entries in the file's access control list,
which is always greater than or equal to zero.
A value of \-1 indicates that an error occurred, and the error
code is stored in the global variable \fIerrno\fP\|.
.SH "ERRORS
.I getacl
will fail if:
.TP 15
[EFAULT]
The argument
.I acl
specifies an invalid address.
.TP 15
[EINVAL]
The argument
.I nacle
is smaller than the number of access control list entries for the file.
.TP 15
[EPERM]
The argument
.I path
contains a byte with the high-order bit set.
.TP 15
[ENOTDIR]
A component of the pathname is not a directory.
.TP 15
[ENOENT]
The pathname was too long, or the named file does not exist.
.TP 15
[EACCES]
Search permission is denied on a component of the pathname.
.TP 15
[ELOOP]
Too many symbolic links were encountered in translating the pathname.
.SH SEE ALSO
chacl(1),
cpacl(1),
edacl(1),
lsacl(1),
setacl(2)
.SH CAVEAT
The implementation of access control lists is an experiment. This
implementation stores the access control list in a (formerly) unused field
in the inode.
There is no guarantee that future versions of Unix will not use this field
for something else.
.PP
Therefore, you should only use access control lists with the full
understanding that you are participating in an experiment.
!E!O!F!
echo x - h.pat
cat >h.pat <<'!E!O!F!'
RCS file: inode.h,v
retrieving revision 1.1
diff -c1 -r1.1 inode.h
*** inode.h.r1_1 Tue Jun 25 18:55:17 1985
--- inode.h Tue Apr 30 16:36:54 1985
***************
*** 48,50
long ic_blocks; /* 104: blocks actually held */
! long ic_spare[5]; /* 108: reserved, currently unused */
} i_ic;
--- 48,51 -----
long ic_blocks; /* 104: blocks actually held */
! char ic_acle[(MAXACL+1)/2];
! short ic_aclid[MAXACL];
} i_ic;
***************
*** 58,59
};
--- 59,63 -----
};
+
+ #define i_acle i_ic.ic_acle
+ #define i_aclid i_ic.ic_aclid
# putting MAXACL in param.h is not a good idea, since changing param.h
# causes the whole kernel to be recompiled. you might try putting MAXACL
# in acl.h , and including acl.h in inode.h .
RCS file: param.h,v
retrieving revision 1.1
diff -c2 -r1.1 param.h
*** param.h.r1_1 Tue Jun 25 18:15:16 1985
--- param.h Tue Apr 30 16:31:54 1985
***************
*** 27,30
/*
* Priorities
*/
--- 27,35 -----
/*
+ * Access Control Lists
+ */
+ #define MAXACL 8
+
+ /*
* Priorities
*/
!E!O!F!
echo x - jjg.h
cat >jjg.h <<'!E!O!F!'
typedef enum { FALSE = 0, TRUE = 1 } bool;
#include <strings.h>
#define equal(x,y) ( strcmp((x),(y)) == 0 )
#define equaln(x,y,n) ( strncmp((x),(y),(n)) == 0 )
#define any(p,c) ( index((p),(c)) != (char *)0 )
#ifdef DEBUG
#ifndef stderr
#include <stdio.h>
#endif
#define dprintf(x) if (debug_control) fprintf( stderr, (x) )
#define d2printf(x,y) if (debug_control) fprintf( stderr, (x), (y) )
#define d3printf(x,y,z) if (debug_control) fprintf( stderr, (x), (y), (z) )
#define DEBUG_FILE "./.debug"
#define debug_test debug_control = (bool) (access(DEBUG_FILE,0) == 0)
#define debug_var bool debug_control = TRUE;
extern bool debug_control;
#else
#define dprintf(x) /* null */
#define d2printf(x,y) /* null */
#define d3printf(x,y,z) /* null */
#define debug_test /* null */
#define debug_var /* null */
#endif
#ifdef BSD41
int BSD41_i;
#define bcopy(x,y,n) for ( BSD41_i = 0; BSD41_i < (n); BSD41_i++ ) \
(y)[BSD41_i] = (x)[BSD41_i]
#define bzero(x,n) for ( BSD41_i = 0; BSD41_i < (n); BSD41_i++ ) \
(x)[BSD41_i] = 0
#endif
!E!O!F!
echo x - login.pat
cat >login.pat <<'!E!O!F!'
RCS file: login.c,v
retrieving revision 1.1
diff -c2 -r1.1 login.c
*** login.c.r1_1 Wed Jun 26 10:19:05 1985
--- login.c Wed Jun 26 10:16:48 1985
***************
*** 14,17
#include <sys/time.h>
#include <sys/resource.h>
#include <sgtty.h>
--- 14,18 -----
#include <sys/time.h>
#include <sys/resource.h>
+ #include <sys/acl.h>
#include <sgtty.h>
***************
*** 93,96
char *rhost;
main(argc, argv)
char *argv[];
--- 94,99 -----
char *rhost;
+ struct acle null_acl[1];
+
main(argc, argv)
char *argv[];
***************
*** 286,289
chown(ttyn, pwd->pw_uid, pwd->pw_gid);
chmod(ttyn, 0622);
setgid(pwd->pw_gid);
strncpy(name, utmp.ut_name, NMAX);
--- 289,296 -----
chown(ttyn, pwd->pw_uid, pwd->pw_gid);
chmod(ttyn, 0622);
+ /*
+ * clear the acl.
+ */
+ setacl(ttyn, 0, null_acl );
setgid(pwd->pw_gid);
strncpy(name, utmp.ut_name, NMAX);
!E!O!F!
echo x - lsacl.c
cat >lsacl.c <<'!E!O!F!'
#include <stdio.h>
#include <sys/param.h>
#include <sys/acl.h>
#include <grp.h>
#include <pwd.h>
main(argc, argv, environ)
int argc;
char *argv[], *environ[];
{
int i, j, na;
struct acle acl[MAXACL];
int status;
status = 0;
for ( i = 1; i < argc; i++ ) {
na = getacl( argv[i], MAXACL, acl );
if ( na < 0 ) {
perror( argv[i] );
status++;
} else {
printf( "%s\n", argv[i] );
for ( j = 0; j < na; j++ ) {
int type = acl[j].a_type;
int id = acl[j].a_id;
int mode = acl[j].a_mode;
if ( type == A_USER ) {
struct passwd *up = getpwuid(id);
if ( up == NULL ) {
printf( "\tuser\t[uid %d]\t%d\n", id, mode );
} else {
printf( "\tuser\t%s\t%d\n", up->pw_name, mode );
}
} else {
struct group *gp = getgrgid(id);
if ( gp == NULL ) {
printf( "\tgroup\t[gid %d]\t%d\n", id, mode );
} else {
printf( "\tgroup\t%s\t%d\n", gp->gr_name, mode );
}
}
}
}
}
exit( status );
}
!E!O!F!
echo x - lsacl.l
cat >lsacl.l <<'!E!O!F!'
''' $Header:$
'''
''' $Log:$
.de Sh
.br
.ne 5
.PP
\fB\\$1\fR
.PP
..
.de Sp
.if t .sp .5v
.if n .sp
..
'''
''' Set up \*(-- to give an unbreakable dash;
''' string Tr holds user defined translation string.
''' Bell System Logo is used as a dummy character.
'''
.ie n \{\
.tr \(bs-\*(Tr
.ds -- \(bs-
.if (\n(.H=4u)&(1m=24u) .ds -- \(bs\h'-12u'\(bs\h'-12u'-\" diablo 10 pitch
.if (\n(.H=4u)&(1m=20u) .ds -- \(bs\h'-12u'\(bs\h'-8u'-\" diablo 12 pitch
.ds L" ""
.ds R" ""
.ds L' '
.ds R' '
'br\}
.el\{\
.ds -- \(em\|
.tr \*(Tr
.ds L" ``
.ds R" ''
.ds L' `
.ds R' '
'br\}
.TH LSACL 1 LOCAL
.SH NAME
lsacl - list the access control lists for files
.SH SYNOPSIS
.B lsacl
file ...
.SH DESCRIPTION
.I lsacl
lists the access control lists for one or more files. The access control list
and the file's ownership are used to determine which users can access
the file.
.PP
For each file,
.I lsacl
prints the file name alone on a line. Following that,
.I lsacl
prints the access control list entries, one per line. The entries are
indented by a tab to make them easily distinguishable from the file name.
Each entry has a user or group name, and the mode granted to
that user or group. The mode is a single digit in the range [0-7],
indicating read-write-execute permission ala
.IR chmod .
.SH DIAGNOSTICS
.I lsacl
returns with exit status 0 if all is well; or with exit status greater
than 0 if it cannot get the access control lists for some files.
.SH SEE ALSO
chacl(1),
cpacl(1),
edacl(1),
getacl(2),
setacl(2)
.SH CAVEAT
The implementation of access control lists is an experiment. This
implementation stores the access control list in a (formerly) unused field
in the inode.
There is no guarantee that future versions of Unix will not use this field
for something else.
.PP
Therefore, you should only use access control lists with the full
understanding that you are participating in an experiment.
.SH BUGS
The entire mechanism hasn't been fully tested.
.PP
It might be nice if the mode were given symbolically ("rwx" instead
of "7").
.PP
.I lsacl
should become yet another option to
.IR ls .
!E!O!F!
echo x - setacl.2
cat >setacl.2 <<'!E!O!F!'
.TH SETACL 2 "15 May 1985"
.UC 4
.SH NAME
setacl \- set the access control list for a file
.SH SYNOPSIS
.nf
.ft B
#include <sys/param.h>
#include <sys/acl.h>
.PP
.ft B
setacl(path, nacle, acl)
char *path;
int nacle;
struct acle *acl;
.fi
.SH DESCRIPTION
.I setacl
sets the access control list of the file whose name is given by
.IR path .
The list is taken from the array
.IR acl .
The parameter
.I nacle
indicates the number of entries in
.IR acl ,
which may not be larger than MAXACL , as defined in
.RI < sys/param.h >.
.PP
An access control list entry is described in
.RI < sys/acl.h >.
.PP
.nf
#define A_GROUP 1
#define A_USER 0
#define ok_uacle(a) ( ( (a).a_type != A_USER ) || ( (a).a_id != 0 ) )
struct acle {
char a_type; /* A_GROUP or A_USER */
char a_mode; /* rwx [0-7] permissions */
int a_id; /* gid or uid according to a_type */
};
.fi
.PP
.I a_type
is a flag indicating whether the entry is for a user or a group.
.I a_id
contains either a uid or a gid, depending on the value of
.IR a_flag .
This user or group will have the permissions described by
.IR a_mode ,
which is an integer in the range 0-7. It indicates read-write-execute
permissions in the manner of
.IR chmod(2) .
.PP
Since the superuser always has access to all files, an entry may not
be specified for the superuser. An entry with a zero
.I a_type
and a zero
.I a_id
is interpreted by
.I setacl
as an end-of-list marker.
.SH "RETURN VALUE
.I setacl
returns the value 0 if all is well.
A value of \-1 indicates that an error occurred, and the error
code is stored in the global variable \fIerrno\fP\|.
.SH "ERRORS
.I setacl
will fail if:
.TP 15
[EFAULT]
The argument
.I acl
specifies an invalid address.
.TP 15
[EINVAL]
The argument
.I nacle
is greater than MAXACL.
.TP 15
[EPERM]
The argument
.I path
contains a byte with the high-order bit set.
.TP 15
[ENOTDIR]
A component of the pathname is not a directory.
.TP 15
[ENOENT]
The pathname was too long, or the named file does not exist.
.TP 15
[EACCES]
Search permission is denied on a component of the pathname.
.TP 15
[EPERM]
The effective userid does not match the owner of the file and the
effective userid is not the superuser.
.TP 15
[EROFS]
The named file resides on a read-only file system.
[ELOOP]
Too many symbolic links were encountered in translating the pathname.
.SH SEE ALSO
chacl(1),
cpacl(1),
edacl(1),
lsacl(1),
setacl(2)
.SH CAVEAT
The implementation of access control lists is an experiment. This
implementation stores the access control list in a (formerly) unused field
in the inode.
There is no guarantee that future versions of Unix will not use this field
for something else.
.PP
Therefore, you should only use access control lists with the full
understanding that you are participating in an experiment.
.SH BUGS
Setting an access control list entry for the file's owner has no
effect, because of the order in which the file access mode and the access
control list are checked. To wit, the owner permission
in the file access mode is checked before the access control
list entry for the file's owner.
This is arguably wrong, since the system reverses this order
when checking for the permission for the file's group (i.e., the
access control list entry for the file's group is checked before the
group permission in the file access mode).
!E!O!F!
echo x - sys.pat
cat >sys.pat <<'!E!O!F!'
RCS file: RCS/ufs_syscalls.c,v
retrieving revision 1.1
diff -c2 -r1.1 ufs_syscalls.c
*** ufs_syscalls.r1_1 Tue Jun 25 18:34:36 1985
--- ufs_syscalls.c Tue Jun 25 18:33:05 1985
***************
*** 1049,1052
mode |= IFREG;
ip->i_mode = mode & ~u.u_cmask;
ip->i_nlink = 1;
ip->i_uid = u.u_uid;
--- 1049,1059 -----
mode |= IFREG;
ip->i_mode = mode & ~u.u_cmask;
+ /*
+ * clear the acl. there should be a default acl (besides a null one).
+ * there is no room to put a default acl in the parent directory's
+ * inode; so the alternative is to put a default acl in the u area
+ * (which is where the umask is).
+ */
+ null_acle( ip, 0 );
ip->i_nlink = 1;
ip->i_uid = u.u_uid;
***************
*** 1126,1129
ip->i_flag |= IACC|IUPD|ICHG;
ip->i_mode = uap->dmode & ~u.u_cmask;
ip->i_nlink = 2;
ip->i_uid = u.u_uid;
--- 1133,1143 -----
ip->i_flag |= IACC|IUPD|ICHG;
ip->i_mode = uap->dmode & ~u.u_cmask;
+ /*
+ * clear the acl. there should be a default acl (besides a null one).
+ * a good default for a directory might be to copy the parent
+ * directory's acl. or put a default directory acl alongside the
+ * default file acl (wherever that ends up).
+ */
+ null_acle( ip, 0 );
ip->i_nlink = 2;
ip->i_uid = u.u_uid;
# there are some changes to the access() routine, just because it made
# it more readable for me, that have nothing to do with acls. like
# changing the "m" variable to "mode_desire".
RCS file: RCS/ufs_fio.c,v
retrieving revision 1.1
diff -c2 -r1.1 ufs_fio.c
*** ufs_fio.c.r1_1 Tue Jun 25 18:47:49 1985
--- ufs_fio.c Tue Jun 25 18:45:33 1985
***************
*** 17,20
#include "../h/proc.h"
#include "../h/nami.h"
/*
--- 17,21 -----
#include "../h/proc.h"
#include "../h/nami.h"
+ #include "../h/acl.h"
/*
***************
*** 31,35
* permissions.
*/
! access(ip, mode)
register struct inode *ip;
int mode;
--- 32,36 -----
* permissions.
*/
! access(ip, mode_desire)
register struct inode *ip;
int mode_desire;
***************
*** 33,37
access(ip, mode)
register struct inode *ip;
! int mode;
{
register m;
--- 34,38 -----
access(ip, mode_desire)
register struct inode *ip;
! int mode_desire;
{
register int i;
***************
*** 35,40
int mode;
{
! register m;
! register int *gp;
m = mode;
--- 36,43 -----
int mode_desire;
{
! register int i;
! int id_match;
! int mode_allow;
! int *gp;
if (mode_desire == IWRITE) {
***************
*** 38,43
register int *gp;
! m = mode;
! if (m == IWRITE) {
/*
* Disallow write attempts on read-only
--- 41,45 -----
int *gp;
! if (mode_desire == IWRITE) {
/*
* Disallow write attempts on read-only
***************
*** 78,92
* check public access.
*/
! if (u.u_uid != ip->i_uid) {
! m >>= 3;
! if (u.u_gid == ip->i_gid)
! goto found;
! gp = u.u_groups;
! for (; gp < &u.u_groups[NGROUPS] && *gp != NOGROUP; gp++)
! if (ip->i_gid == *gp)
! goto found;
! m >>= 3;
! found:
! ;
}
if ((ip->i_mode&m) != 0)
--- 80,100 -----
* check public access.
*/
! id_match = 0;
! /*
! * user access (1) - match effective uid against fileowner uid.
! * user access (2) - match group list against fileowner uid.
! */
! if (u.u_uid == ip->i_uid) {
! id_match++;
! mode_allow = ip->i_mode;
! } else {
! for( i = 0; i < MAXACL && ok_acle(ip,i); i++ ) {
! if ( ( acle_type(ip,i) == A_USER ) &&
! ( u.u_uid == acle_id(ip,i) ) ) {
! id_match++;
! mode_allow = acle_mode(ip,i) << 6;
! break;
! }
! }
}
***************
*** 90,94
;
}
! if ((ip->i_mode&m) != 0)
return (0);
u.u_error = EACCES;
--- 98,158 -----
}
}
!
! /*
! * group access (1) - match effective gid against acl.
! */
! if ( ! id_match ) {
! mode_desire >>= 3;
! for( i = 0; i < MAXACL && ok_acle(ip,i); i++ ) {
! if ( ( acle_type(ip,i) == A_GROUP ) &&
! ( u.u_gid == acle_id(ip,i) ) ) {
! id_match++;
! mode_allow = acle_mode(ip,i) << 3;
! break;
! }
! }
! }
!
! /*
! * group access (2) - match group list against acl.
! */
! if ( ! id_match ) {
! for (gp = u.u_groups; gp < &u.u_groups[NGROUPS] && *gp != NOGROUP; gp++)
! for( i = 0; i < MAXACL && ok_acle(ip,i); i++ ) {
! if ( ( acle_type(ip,i) == A_GROUP ) &&
! ( *gp == acle_id(ip,i) ) ) {
! id_match++;
! mode_allow = acle_mode(ip,i) << 3;
! break;
! }
! }
! }
!
! /*
! * group access (3) - match effective gid against fileowner gid.
! * group access (4) - match group list against fileowner gid.
! */
! if ( ! id_match ) {
! if ( u.u_gid == ip->i_gid ) {
! id_match++;
! mode_allow = ip->i_mode;
! } else {
! for (gp = u.u_groups; gp < &u.u_groups[NGROUPS] && *gp != NOGROUP; gp++)
! if (ip->i_gid == *gp) {
! id_match++;
! mode_allow = ip->i_mode;
! break;
! }
! }
! }
!
! /*
! * other access.
! */
! if ( ! id_match ) {
! mode_desire >>= 3;
! mode_allow = ip->i_mode;
! }
! if ((mode_allow&mode_desire) != 0)
return (0);
u.u_error = EACCES;
!E!O!F!