880 lines
32 KiB
C
880 lines
32 KiB
C
|
/*
|
||
|
* Copyright (c) 1991 by the University of Washington
|
||
|
* Copyright (c) 1993 by the University of Southern California
|
||
|
*
|
||
|
* For copying and distribution information, please see the files
|
||
|
* <uw-copyright.h> and <usc-copyr.h>.
|
||
|
*/
|
||
|
|
||
|
#include <uw-copyright.h>
|
||
|
#include <usc-copyr.h>
|
||
|
|
||
|
#include <string.h>
|
||
|
#include <sys/types.h> /* for decl. of inet_ntoa() */
|
||
|
#include <sys/socket.h> /* for decl. of inet_ntoa() */
|
||
|
#include <netinet/in.h> /* for decl. of inet_ntoa() */
|
||
|
#include <arpa/inet.h> /* for decl. of inet_ntoa() */
|
||
|
|
||
|
|
||
|
#include <ardp.h>
|
||
|
#include <pfs.h>
|
||
|
#include <pserver.h>
|
||
|
#include <pprot.h>
|
||
|
#include <plog.h>
|
||
|
#include <pmachine.h>
|
||
|
#include <psrv.h>
|
||
|
|
||
|
#ifndef IPPORT_RESERVED
|
||
|
#define IPPORT_RESERVED 1024
|
||
|
#endif
|
||
|
|
||
|
|
||
|
/* used to initialize ACLs */
|
||
|
struct aclinit {
|
||
|
int acetype;
|
||
|
char *atype;
|
||
|
char *rights;
|
||
|
char *prin_1; /* principals list */
|
||
|
char *prin_2; /* principals list */
|
||
|
char *prin_3; /* principals list */
|
||
|
char *prin_4; /* principals list */
|
||
|
char *prin_5; /* principals list */
|
||
|
char *prin_6; /* principals list */
|
||
|
char *prin_7; /* principals list */
|
||
|
char *prin_8; /* principals list */
|
||
|
char *prin_9; /* principals list */
|
||
|
char *prin_10; /* principals list */
|
||
|
char *prin_11; /* principals list */
|
||
|
char *prin_12; /* principals list */
|
||
|
char *prin_13; /* principals list */
|
||
|
char *prin_14; /* principals list */
|
||
|
char *prin_15; /* principals list */
|
||
|
char *prin_16; /* principals list */
|
||
|
char *prin_17; /* principals list */
|
||
|
char *prin_18; /* principals list */
|
||
|
char *prin_19; /* principals list */
|
||
|
char *prin_20; /* principals list */
|
||
|
char *prin_21; /* principals list */
|
||
|
char *prin_22; /* principals list */
|
||
|
char *prin_23; /* principals list */
|
||
|
char *prin_24; /* principals list */
|
||
|
char *prin_25; /* principals list */
|
||
|
char *prin_26; /* principals list */
|
||
|
};
|
||
|
|
||
|
static ACL_ST default_iacl = {ACL_DEFAULT, NULL};
|
||
|
static ACL_ST system_iacl = {ACL_SYSTEM, NULL};
|
||
|
static ACL_ST container_iacl = {ACL_CONTAINER, NULL};
|
||
|
static ACL_ST directory_iacl = {ACL_DIRECTORY, NULL};
|
||
|
|
||
|
static struct aclinit default_aclinit[] = DEFAULT_ACL;
|
||
|
static struct aclinit system_aclinit[] = SYSTEM_ACL;
|
||
|
static struct aclinit override_aclinit[] = OVERRIDE_ACL;
|
||
|
static struct aclinit maint_aclinit[] = MAINT_ACL;
|
||
|
|
||
|
/* Only modified in p_srv_check_acl_initialize_defaults(), which is called
|
||
|
before we go multi-threaded. */
|
||
|
ACL default_acl = NULL;
|
||
|
ACL system_acl = NULL;
|
||
|
ACL override_acl = NULL;
|
||
|
ACL nulldir_acl = NULL;
|
||
|
ACL nullobj_acl = NULL;
|
||
|
ACL nullcont_acl = NULL;
|
||
|
ACL nulllink_acl = NULL;
|
||
|
ACL maint_acl = NULL;
|
||
|
|
||
|
static checkl_acl_r();
|
||
|
static check_permissions();
|
||
|
static check_asserted_principal();
|
||
|
ACL get_container_acl();
|
||
|
static ACL aclinit2acl(struct aclinit init[], int nelems);
|
||
|
static int checkl_acl_r(ACL acl, ACL dacl, RREQ req, char *op, int depth, int ld,char *objid,char *itemid);
|
||
|
|
||
|
/* Called before things start to run in psrv. */
|
||
|
void
|
||
|
p_srv_check_acl_initialize_defaults(void)
|
||
|
{
|
||
|
default_acl = aclinit2acl(default_aclinit,
|
||
|
sizeof default_aclinit
|
||
|
/ sizeof default_aclinit[0]);
|
||
|
system_acl = aclinit2acl(system_aclinit,
|
||
|
sizeof system_aclinit
|
||
|
/ sizeof system_aclinit[0]);
|
||
|
override_acl = aclinit2acl(override_aclinit,
|
||
|
sizeof override_aclinit
|
||
|
/ sizeof override_aclinit[0]);
|
||
|
maint_acl = aclinit2acl(maint_aclinit,
|
||
|
sizeof maint_aclinit
|
||
|
/ sizeof maint_aclinit[0]);
|
||
|
|
||
|
default_iacl.next = &system_iacl;
|
||
|
system_iacl.previous = &default_iacl;
|
||
|
nulldir_acl = &default_iacl;
|
||
|
nulllink_acl = &directory_iacl;
|
||
|
nullobj_acl = &container_iacl;
|
||
|
nullcont_acl = nulldir_acl;
|
||
|
|
||
|
}
|
||
|
|
||
|
static ACL aclentryinit2aclentry(struct aclinit *init);
|
||
|
|
||
|
/* constructs a real list of ACL entries out of an array of aclinit
|
||
|
structures. */
|
||
|
static ACL
|
||
|
aclinit2acl(struct aclinit init[], int nelems)
|
||
|
{
|
||
|
|
||
|
ACL rethead = NULL; /* head of list of AC entries being returned */
|
||
|
int i;
|
||
|
|
||
|
for (i = 0; i < nelems; ++i) {
|
||
|
ACL tmp = aclentryinit2aclentry(&init[i]);
|
||
|
APPEND_ITEM(tmp, rethead);
|
||
|
}
|
||
|
return rethead;
|
||
|
}
|
||
|
|
||
|
|
||
|
static ACL
|
||
|
aclentryinit2aclentry(struct aclinit *init)
|
||
|
{
|
||
|
ACL retval;
|
||
|
retval = acalloc();
|
||
|
if (!retval) out_of_memory();
|
||
|
retval->acetype = init->acetype;
|
||
|
retval->atype = init->atype;
|
||
|
retval->rights = init->rights;
|
||
|
retval->principals = NULL;
|
||
|
if (init->prin_1)
|
||
|
retval->principals = tkappend(init->prin_1, retval->principals);
|
||
|
if (init->prin_2)
|
||
|
retval->principals = tkappend(init->prin_2, retval->principals);
|
||
|
if (init->prin_3)
|
||
|
retval->principals = tkappend(init->prin_3, retval->principals);
|
||
|
if (init->prin_4)
|
||
|
retval->principals = tkappend(init->prin_4, retval->principals);
|
||
|
if (init->prin_5)
|
||
|
retval->principals = tkappend(init->prin_5, retval->principals);
|
||
|
if (init->prin_6)
|
||
|
retval->principals = tkappend(init->prin_6, retval->principals);
|
||
|
if (init->prin_7)
|
||
|
retval->principals = tkappend(init->prin_8, retval->principals);
|
||
|
if (init->prin_9)
|
||
|
retval->principals = tkappend(init->prin_9, retval->principals);
|
||
|
if (init->prin_10)
|
||
|
retval->principals = tkappend(init->prin_10, retval->principals);
|
||
|
if (init->prin_11)
|
||
|
retval->principals = tkappend(init->prin_11, retval->principals);
|
||
|
if (init->prin_12)
|
||
|
retval->principals = tkappend(init->prin_12, retval->principals);
|
||
|
if (init->prin_13)
|
||
|
retval->principals = tkappend(init->prin_13, retval->principals);
|
||
|
if (init->prin_14)
|
||
|
retval->principals = tkappend(init->prin_14, retval->principals);
|
||
|
if (init->prin_15)
|
||
|
retval->principals = tkappend(init->prin_15, retval->principals);
|
||
|
if (init->prin_16)
|
||
|
retval->principals = tkappend(init->prin_16, retval->principals);
|
||
|
if (init->prin_17)
|
||
|
retval->principals = tkappend(init->prin_17, retval->principals);
|
||
|
if (init->prin_18)
|
||
|
retval->principals = tkappend(init->prin_18, retval->principals);
|
||
|
if (init->prin_19)
|
||
|
retval->principals = tkappend(init->prin_19, retval->principals);
|
||
|
if (init->prin_20)
|
||
|
retval->principals = tkappend(init->prin_20, retval->principals);
|
||
|
if (init->prin_21)
|
||
|
retval->principals = tkappend(init->prin_21, retval->principals);
|
||
|
if (init->prin_22)
|
||
|
retval->principals = tkappend(init->prin_22, retval->principals);
|
||
|
if (init->prin_23)
|
||
|
retval->principals = tkappend(init->prin_23, retval->principals);
|
||
|
if (init->prin_24)
|
||
|
retval->principals = tkappend(init->prin_24, retval->principals);
|
||
|
if (init->prin_25)
|
||
|
retval->principals = tkappend(init->prin_25, retval->principals);
|
||
|
if (init->prin_26)
|
||
|
retval->principals = tkappend(init->prin_26, retval->principals);
|
||
|
return retval;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* srv_check_acl - check access control list
|
||
|
*
|
||
|
* SRV_CHECK_ACL checks an access control list to see if a particular
|
||
|
* user is authorized to perform a particular operation. It returns
|
||
|
* a positive number if the operation is authorized, and zero if
|
||
|
* not authorized. SRV_CHECK_ACL actually checks up to three access
|
||
|
* control lists. First, the access control list associated with
|
||
|
* a link is checked (if specified). If the operation would not
|
||
|
* be authorized, the secondary ACL may be checked if present, and
|
||
|
* depending on the type of ACL check specified in the flags field,
|
||
|
* to see if it applies to or overrides the individual entry. If sill
|
||
|
* not authorized, an override ACL is checked to see if the operation
|
||
|
* is performed.
|
||
|
*
|
||
|
* NOTE on negative rights: Negative rights apply within
|
||
|
* a particular access control list only. Thus, a negative
|
||
|
* entry in the link ACL can override other entries in the
|
||
|
* link ACL, but it will not prevent access if the user
|
||
|
* is authorized to perform the operation by the directory
|
||
|
* or override ACL's.
|
||
|
*/
|
||
|
int
|
||
|
srv_check_acl(ACL pacl, /* Primary ACL */
|
||
|
ACL sacl, /* Secondary ACL */
|
||
|
RREQ req, /* Request; used for client identification */
|
||
|
char *op, /* Operation */
|
||
|
int flags, /* Type of ACL check */
|
||
|
char *objid, /* hsoname of object to which ACL applies */
|
||
|
char *itemid) /* Name of item to be manipulated */
|
||
|
{
|
||
|
int answer = 0;
|
||
|
|
||
|
/* Now called from dirsrv */
|
||
|
#if 0
|
||
|
if(!initialized) initialize_defaults();
|
||
|
#endif
|
||
|
|
||
|
if(flags == SCA_LINK) {
|
||
|
if(!pacl) pacl = nulllink_acl;
|
||
|
if(!sacl) sacl = nulldir_acl;
|
||
|
answer = checkl_acl_r(pacl,sacl,req,op,ACL_NESTING,
|
||
|
SCA_LINK,objid,itemid);
|
||
|
if(!answer) answer = checkl_acl_r(sacl,sacl,req,op,ACL_NESTING,
|
||
|
SCA_DIRECTORY,objid,itemid);
|
||
|
}
|
||
|
else if(flags == SCA_LINKDIR) {
|
||
|
if(!pacl) pacl = nulllink_acl;
|
||
|
if(!sacl) sacl = nulldir_acl;
|
||
|
answer = checkl_acl_r(pacl,sacl,req,op,ACL_NESTING,
|
||
|
SCA_LINK,objid,itemid);
|
||
|
if(!answer) answer = checkl_acl_r(sacl,sacl,req,op,ACL_NESTING,
|
||
|
SCA_DIRECTORY,objid,itemid);
|
||
|
}
|
||
|
else if(flags == SCA_DIRECTORY) {
|
||
|
if(!pacl) pacl = nulldir_acl;
|
||
|
answer = checkl_acl_r(pacl,pacl,req,op,ACL_NESTING,
|
||
|
SCA_DIRECTORY,objid,itemid);
|
||
|
}
|
||
|
else if (flags == SCA_OBJECT) {
|
||
|
ACL cacl = NULL;
|
||
|
if(!pacl) pacl = nullobj_acl;
|
||
|
if(!sacl) {
|
||
|
if(objid && (cacl = get_container_acl(objid))) {
|
||
|
answer = checkl_acl_r(pacl,cacl,req,op,ACL_NESTING,
|
||
|
SCA_OBJECT,objid,itemid);
|
||
|
aclfree(cacl);
|
||
|
}
|
||
|
else answer = checkl_acl_r(pacl,nullcont_acl,req,op,ACL_NESTING,
|
||
|
SCA_OBJECT,objid,itemid);
|
||
|
}
|
||
|
else answer = checkl_acl_r(pacl,sacl,req,op,ACL_NESTING,SCA_OBJECT,
|
||
|
objid,itemid);
|
||
|
}
|
||
|
else if (flags == SCA_MISC) {
|
||
|
if(!pacl) pacl = maint_acl;
|
||
|
answer = checkl_acl_r(pacl,NULL,req,op,ACL_NESTING,
|
||
|
SCA_MISC,objid,itemid);
|
||
|
}
|
||
|
|
||
|
if(answer) return(answer);
|
||
|
|
||
|
/* Check to see if absolute override applies */
|
||
|
return(checkl_acl_r(override_acl,NULL,req,op,ACL_NESTING,
|
||
|
SCA_MISC, objid,itemid));
|
||
|
|
||
|
}
|
||
|
|
||
|
static int
|
||
|
checkl_acl_r(acl,sacl,req,op,depth,ld,objid,itemid)
|
||
|
ACL acl; /* Access control list */
|
||
|
ACL sacl; /* Secondary access control list */
|
||
|
char *op; /* Operation */
|
||
|
RREQ req; /* Client identification */
|
||
|
int depth; /* Maximum nesting */
|
||
|
int ld; /* 0 = link, 1 = dir, 2 = both, 4=misc */
|
||
|
char *objid; /* Object associated with ACL */
|
||
|
char *itemid; /* Name of item to manipulate */
|
||
|
{
|
||
|
int retval = 0; /* Would this entry authorize op */
|
||
|
int answer = 1; /* How to answer if match */
|
||
|
|
||
|
if(depth == 0) return(NOT_AUTHORIZED);
|
||
|
|
||
|
while(acl) {
|
||
|
retval = check_permissions(op,acl->rights,ld);
|
||
|
if(retval||((acl->rights==NULL)&&((acl->acetype == ACL_DEFAULT)
|
||
|
||(acl->acetype == ACL_SYSTEM)
|
||
|
||(acl->acetype == ACL_DIRECTORY)
|
||
|
||(acl->acetype == ACL_CONTAINER)
|
||
|
||(acl->acetype == ACL_IETF_AAC)))) {
|
||
|
if(retval == NEG_AUTHORIZED) answer = NOT_AUTHORIZED;
|
||
|
else answer = AUTHORIZED;
|
||
|
|
||
|
switch(acl->acetype) {
|
||
|
|
||
|
case ACL_NONE:
|
||
|
break;
|
||
|
case ACL_DEFAULT:
|
||
|
retval = checkl_acl_r(default_acl,sacl,req,op,depth-1,ld,objid,itemid);
|
||
|
if(retval) return(answer);
|
||
|
break;
|
||
|
case ACL_SYSTEM:
|
||
|
retval = checkl_acl_r(system_acl,sacl,req,op,depth-1,ld,objid,itemid);
|
||
|
if(retval) return(answer);
|
||
|
break;
|
||
|
case ACL_OWNER:
|
||
|
/* Check if user is the owner of the dirtectory */
|
||
|
#if 0
|
||
|
/* We need to find the owner of the file/directory */
|
||
|
/* for which this ACL applies and check whether it */
|
||
|
/* is the current principal. For now, we don't */
|
||
|
/* know the name of the file or directory. When */
|
||
|
/* the interface is changed so we do know it, we */
|
||
|
/* will check against the current host address and */
|
||
|
/* the TRSTHOST authentication type. Alternatively */
|
||
|
/* we will use an OBJECT-OWNER attribute from the */
|
||
|
/* object attribute list which will be in the form */
|
||
|
/* of an ACL entry, but without a rights field */
|
||
|
#endif
|
||
|
break;
|
||
|
case ACL_DIRECTORY:
|
||
|
if(ld == SCA_LINK) {
|
||
|
retval = checkl_acl_r(sacl,sacl,req,op,depth-1,
|
||
|
SCA_LINKDIR,objid,itemid);
|
||
|
if(retval) return(answer);
|
||
|
}
|
||
|
break;
|
||
|
case ACL_CONTAINER:
|
||
|
if(ld == SCA_OBJECT) {
|
||
|
retval = checkl_acl_r(sacl,sacl,req,op,depth-1,
|
||
|
SCA_CONTAINER,objid,itemid);
|
||
|
if(retval) return(answer);
|
||
|
}
|
||
|
break;
|
||
|
case ACL_ANY:
|
||
|
return(answer);
|
||
|
case ACL_AUTHENT:
|
||
|
if (acl->atype && strequal(acl->atype, "KERBEROS")) {
|
||
|
PAUTH pap; /* Iteration variable for PAUTH. */
|
||
|
TOKEN prin; /* principals */
|
||
|
/* Loop through all of the Kerberos authenticated
|
||
|
principals. (This is upward-compatible with future
|
||
|
versions of Kerberos that will allow one to be
|
||
|
registered as multiple principals simultaneously.)
|
||
|
*/
|
||
|
for (pap = req->auth_info; pap; pap = pap->next) {
|
||
|
if (pap->ainfo_type == PFSA_KERBEROS) {
|
||
|
for (prin = pap->principals; prin;
|
||
|
prin = prin->next) {
|
||
|
if (member(prin->token, acl->principals))
|
||
|
return answer;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
else if (acl->atype && strequal(acl->atype, "P_PASSWORD")) {
|
||
|
PAUTH pap; /* Iteration variable for PAUTH. */
|
||
|
TOKEN prin; /* principals */
|
||
|
/* Loop through all of the principals. */
|
||
|
for (pap = req->auth_info; pap; pap = pap->next) {
|
||
|
if (pap->ainfo_type == PFSA_P_PASSWORD) {
|
||
|
for (prin = pap->principals; prin;
|
||
|
prin = prin->next) {
|
||
|
if (member(prin->token, acl->principals))
|
||
|
return answer;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
break;
|
||
|
case ACL_LGROUP: /* Not yet implemented */
|
||
|
break;
|
||
|
case ACL_GROUP: /* Not yet implemented */
|
||
|
break;
|
||
|
case ACL_TRSTHOST: /* Check host and userid */
|
||
|
if (!check_prvport(req))
|
||
|
break;
|
||
|
/* DELIBERATE FALLTHROUGH */
|
||
|
case ACL_ASRTHOST: /* Check host and asserted userid */
|
||
|
{
|
||
|
PAUTH pap;
|
||
|
TOKEN prin; /* principals */
|
||
|
/* Loop through all of the asserted principals. */
|
||
|
for (pap = req->auth_info; pap; pap = pap->next) {
|
||
|
if (pap->ainfo_type == PFSA_UNAUTHENTICATED) {
|
||
|
for (prin = pap->principals; prin;
|
||
|
prin = prin->next) {
|
||
|
if (check_asserted_principal(prin->token,
|
||
|
acl->principals,
|
||
|
req->peer_addr))
|
||
|
return answer;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
break;
|
||
|
case ACL_IETF_AAC:
|
||
|
/* This ACL entry type is not yet implemented */
|
||
|
break;
|
||
|
default: /* Not implemented */
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
acl = acl->next;
|
||
|
}
|
||
|
return(NOT_AUTHORIZED);
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* check_permissions - Check if operation is authorized
|
||
|
*
|
||
|
* CHECK_PERMISIONS takes a string with letters representing
|
||
|
* the permissions required for the current opration, and
|
||
|
* a string listing operating authorized by the current ACL
|
||
|
* entry. It returns a 1 if the operation would be
|
||
|
* authorized by the current entry, a 0 if not, and a -1
|
||
|
* if the ACL entry would have authorized the operation,
|
||
|
* but began with a "-" indicating negative authorization.
|
||
|
*
|
||
|
* ARGS: op - String with operations to be performed
|
||
|
* p - Permissions from ACL entry
|
||
|
* ld - Whther ACL entry is for directory or link (or both)
|
||
|
*
|
||
|
* RETURNS: 1 if authorized
|
||
|
* 0 if not authorized
|
||
|
* -1 if negative authorization
|
||
|
*
|
||
|
* Protections
|
||
|
*
|
||
|
* The more common entry appears first if multiple rights allow an
|
||
|
* operation. The operation identifier appears second.
|
||
|
*
|
||
|
* Object File Directory Link* Meaning
|
||
|
* AB AB AB Aa Administer ACL
|
||
|
* VYAB VYAB VYAB VvAa View ACL
|
||
|
* - - L Ll List link
|
||
|
* Rg RG RQ RrQ Read link, get attribute or file
|
||
|
* Wu Ww WM WmM Modify attribute, data, links
|
||
|
* EiWu EeWw EIWM - Insert attributes links, append (extend)
|
||
|
* DzWu - DKWM DdKWMm Delete link or attribute
|
||
|
*
|
||
|
* The following will eventually be replaced by restricted forms of
|
||
|
* Administer (A or B).
|
||
|
*
|
||
|
* > > > ] Add rights
|
||
|
* < < < [ Remove rights
|
||
|
* ) ) ) - Add rights
|
||
|
* ( ( ( - Remove rights
|
||
|
*
|
||
|
* The following only appear on the server maintenance ACL
|
||
|
*
|
||
|
* S Restart server
|
||
|
* T Terminate server
|
||
|
* U Update system information
|
||
|
* P Administer passwords
|
||
|
* p Add new password entry
|
||
|
*
|
||
|
* A - sign in an ACL means that the specified rights are explicitly
|
||
|
* denied. In the table, it means not applicable.
|
||
|
*
|
||
|
* * A capital letter on a link ACL means that this right exists in the
|
||
|
* direcory ACL for the directory containing the link.
|
||
|
*
|
||
|
* ** When restrictions are supported, they can be used to restrict the
|
||
|
* specific attributes to which a right applies, or to restrict the
|
||
|
* interpretation of an ACL entry to only the Object, File, or Directory,
|
||
|
* or link.
|
||
|
*
|
||
|
* When a small letter is associated with a directory, it is the default
|
||
|
* used for those links in the directory which do not otherwise specify
|
||
|
* protections. The large letter for a directory indicates that the
|
||
|
* right applies to ALL entries in the directory, regardless of the ACLs
|
||
|
* associated with the individual link.
|
||
|
*
|
||
|
* These rights apply to the directory and individual links. The ability
|
||
|
* to read a link does not grant any rights to read the file that the
|
||
|
* link points to. Instead, it grants the right to read the binding
|
||
|
* of the link. The protection mechanisms for the objects themselves
|
||
|
* depend on the underlying access mechanism.
|
||
|
*
|
||
|
* The Administer right is required to change the ACL. "A" allows one
|
||
|
* to change the ACLs for the directory as a whole, as well as those
|
||
|
* for individual links. It does not, however, grant any rights to
|
||
|
* the object pointed to by those links (e.g. it doesn't allow one
|
||
|
* to change the permissions on subdirectories.
|
||
|
*
|
||
|
* List allows one to list an entry in a wildcarded search. Read allows
|
||
|
* one to learn the binding of a link. If one can read a link, but
|
||
|
* not list it, then it can only be seen when the user specifies the
|
||
|
* exact name of the link in a query.
|
||
|
*
|
||
|
* Modify allows one to change the binding of a link. It does not
|
||
|
* allow one to create a new link or delete an existing one. Insert
|
||
|
* or delete access are necessary for that.
|
||
|
*
|
||
|
* View allows one to view the contents of the ACL. Administer implies view.
|
||
|
* Add rights and remove rights, ><][)(, allow one to add or remove the
|
||
|
* other rights that are specified as part of the same ACL entry. It is
|
||
|
* a limited form of administer. The other rights included in the
|
||
|
* same ACL entry do not apply, but only restrict which rights may be
|
||
|
* added or removed. The add or remove indicators must appear in the
|
||
|
* first one or two positions in the rights field, and can not themselves
|
||
|
* be added or removed unless they also appear later in the rights field.
|
||
|
*
|
||
|
* If the permission string begins with a "-" and if the specified operation
|
||
|
* would otherwise be authorized, check_permissions returns -1 indicating
|
||
|
* that an applicable negative right has been found, and that the operation
|
||
|
* should be denied even if subsequent ACL entries authorizing it are found.
|
||
|
* If an ACL entry preceding this one has already authorized the operation,
|
||
|
* the operation will be performed.
|
||
|
*
|
||
|
* BUGS: For now, only the first character in ><][])( means add or
|
||
|
* delete the following rights, and all rights included in the
|
||
|
* entry including the first ><][)( may be added or deleted.
|
||
|
* Eventually, we will check the first two positions to see
|
||
|
* if adding and deleting is allowed, and the matching
|
||
|
* characters in those positions will be removed before
|
||
|
* checking subsequent characters.
|
||
|
*/
|
||
|
int
|
||
|
static check_permissions(op,p,ld)
|
||
|
char *op; /* Operation */
|
||
|
char *p; /* Permissions */
|
||
|
int ld; /* 0 =link, 1 =directory, 2=both, 4=misc */
|
||
|
{
|
||
|
char *tp = p;
|
||
|
int polarity = 1; /* -1 = neg authorization */
|
||
|
|
||
|
if(!p || !(*p)) return(NOT_AUTHORIZED);
|
||
|
|
||
|
if(*p == '-') polarity = -1;
|
||
|
|
||
|
/* Reject if ACL entry for insert or delete rights, but not operation */
|
||
|
if(index("><][)(",*p) && !index("><][",*op))
|
||
|
return(NOT_AUTHORIZED);
|
||
|
/* Insert or delete rights must be first in ACL permissions */
|
||
|
while(*(++tp)) if(index("><][)(",*tp)) return(NOT_AUTHORIZED);
|
||
|
|
||
|
while(*op) {
|
||
|
switch(*(op++)) {
|
||
|
|
||
|
case 'a':
|
||
|
if((ld != 1) && index(p,'a')) continue;
|
||
|
if((ld != 0) && index(p,'A')) continue;
|
||
|
else return(NOT_AUTHORIZED);
|
||
|
case 'A':
|
||
|
case 'B':
|
||
|
if((ld != 0) && index(p,'A')) continue;
|
||
|
if((ld != 0) && index(p,'B')) continue;
|
||
|
else return(NOT_AUTHORIZED);
|
||
|
|
||
|
case 'v':
|
||
|
if((ld != 1) && index(p,'v')) continue;
|
||
|
if((ld != 0) && index(p,'V')) continue;
|
||
|
if((ld != 1) && index(p,'a')) continue;
|
||
|
if((ld != 0) && index(p,'A')) continue;
|
||
|
else return(NOT_AUTHORIZED);
|
||
|
case 'V':
|
||
|
case 'Y':
|
||
|
if((ld != 0) && index(p,'V')) continue;
|
||
|
if((ld != 0) && index(p,'Y')) continue;
|
||
|
if((ld != 0) && index(p,'A')) continue;
|
||
|
if((ld != 0) && index(p,'B')) continue;
|
||
|
else return(NOT_AUTHORIZED);
|
||
|
|
||
|
case 'l':
|
||
|
if((ld != 1) && index(p,'l')) continue;
|
||
|
case 'L': /* and fall through */
|
||
|
if((ld != 0) && index(p,'L')) continue;
|
||
|
else return(NOT_AUTHORIZED);
|
||
|
|
||
|
case 'r':
|
||
|
if((ld != 1) && index(p,'r')) continue;
|
||
|
case 'R': /* and fall through */
|
||
|
case 'Q': /* and fall through */
|
||
|
if((ld != 0) && index(p,'R')) continue;
|
||
|
if((ld != 0) && index(p,'Q')) continue;
|
||
|
else return(NOT_AUTHORIZED);
|
||
|
|
||
|
case 'G':
|
||
|
if((ld != 0) && index(p,'G')) continue;
|
||
|
if((ld != 0) && index(p,'R')) continue;
|
||
|
else return(NOT_AUTHORIZED);
|
||
|
|
||
|
case 'g':
|
||
|
if((ld != 0) && index(p,'g')) continue;
|
||
|
if((ld != 0) && index(p,'R')) continue;
|
||
|
else return(NOT_AUTHORIZED);
|
||
|
|
||
|
case 'm':
|
||
|
if((ld != 1) && index(p,'m')) continue;
|
||
|
case 'M': /* and fall through */
|
||
|
if((ld != 0) && index(p,'M')) continue;
|
||
|
if((ld != 0) && index(p,'W')) continue;
|
||
|
else return(NOT_AUTHORIZED);
|
||
|
|
||
|
case 'w': /* and fall through */
|
||
|
if((ld != 0) && index(p,'w')) continue;
|
||
|
if((ld != 0) && index(p,'W')) continue;
|
||
|
else return(NOT_AUTHORIZED);
|
||
|
|
||
|
case 'u': /* and fall through */
|
||
|
if((ld != 0) && index(p,'u')) continue;
|
||
|
if((ld != 0) && index(p,'W')) continue;
|
||
|
else return(NOT_AUTHORIZED);
|
||
|
|
||
|
case 'I':
|
||
|
if((ld != 0) && index(p,'I')) continue;
|
||
|
if((ld != 0) && index(p,'E')) continue;
|
||
|
if((ld != 0) && index(p,'W')) continue;
|
||
|
if((ld != 0) && index(p,'M')) continue;
|
||
|
else return(NOT_AUTHORIZED);
|
||
|
|
||
|
case 'e':
|
||
|
if((ld != 0) && index(p,'e')) continue;
|
||
|
if((ld != 0) && index(p,'E')) continue;
|
||
|
if((ld != 0) && index(p,'W')) continue;
|
||
|
if((ld != 0) && index(p,'w')) continue;
|
||
|
else return(NOT_AUTHORIZED);
|
||
|
|
||
|
case 'i':
|
||
|
if((ld != 0) && index(p,'i')) continue;
|
||
|
if((ld != 0) && index(p,'E')) continue;
|
||
|
if((ld != 0) && index(p,'W')) continue;
|
||
|
if((ld != 0) && index(p,'u')) continue;
|
||
|
else return(NOT_AUTHORIZED);
|
||
|
|
||
|
case 'd':
|
||
|
if((ld != 1) && index(p,'d')) continue;
|
||
|
if((ld != 1) && index(p,'m')) continue;
|
||
|
case 'D': /* and fall through */
|
||
|
if((ld != 0) && index(p,'D')) continue;
|
||
|
if((ld != 0) && index(p,'K')) continue;
|
||
|
if((ld != 0) && index(p,'W')) continue;
|
||
|
if((ld != 0) && index(p,'M')) continue;
|
||
|
else return(NOT_AUTHORIZED);
|
||
|
|
||
|
case 'z': /* and fall through */
|
||
|
if((ld != 0) && index(p,'D')) continue;
|
||
|
if((ld != 0) && index(p,'z')) continue;
|
||
|
if((ld != 0) && index(p,'W')) continue;
|
||
|
if((ld != 0) && index(p,'u')) continue;
|
||
|
else return(NOT_AUTHORIZED);
|
||
|
|
||
|
case ']':
|
||
|
if((ld != 1) && index(p,']')) continue;
|
||
|
if((ld != 0) && index(p,'>')) continue;
|
||
|
else return(NOT_AUTHORIZED);
|
||
|
case '>':
|
||
|
if((ld != 0) && index(p,'>')) continue;
|
||
|
if((ld != 0) && index(p,')')) continue;
|
||
|
else return(NOT_AUTHORIZED);
|
||
|
|
||
|
case '[':
|
||
|
if((ld != 1) && index(p,'[')) continue;
|
||
|
if((ld != 0) && index(p,'<')) continue;
|
||
|
else return(NOT_AUTHORIZED);
|
||
|
case '<':
|
||
|
if((ld != 0) && index(p,'<')) continue;
|
||
|
if((ld != 0) && index(p,'(')) continue;
|
||
|
else return(NOT_AUTHORIZED);
|
||
|
|
||
|
/* Maintenance operations. */
|
||
|
case 'S':
|
||
|
if (index(p, 'S')) continue;
|
||
|
else return NOT_AUTHORIZED;
|
||
|
case 'T':
|
||
|
if (index(p, 'T')) continue;
|
||
|
else return NOT_AUTHORIZED;
|
||
|
case 'U':
|
||
|
if (index(p, 'U')) continue;
|
||
|
else return NOT_AUTHORIZED;
|
||
|
case 'P':
|
||
|
if (index(p, 'P')) continue;
|
||
|
else return NOT_AUTHORIZED;
|
||
|
case 'p':
|
||
|
if (index(p, 'p')) continue;
|
||
|
if (index(p, 'P')) continue;
|
||
|
else return NOT_AUTHORIZED;
|
||
|
default:
|
||
|
return(NOT_AUTHORIZED);
|
||
|
}
|
||
|
}
|
||
|
return(polarity);
|
||
|
}
|
||
|
|
||
|
#ifdef NOTDEF
|
||
|
/* Unused. Make this thread-safe before commenting it out. */
|
||
|
static char *inet_ntoa(a)
|
||
|
struct in_addr a;
|
||
|
{
|
||
|
static char astring[20];
|
||
|
|
||
|
#if BYTE_ORDER == BIG_ENDIAN
|
||
|
sprintf(astring,"%d.%d.%d.%d",(a.s_addr >> 24) & 0xff,
|
||
|
(a.s_addr >> 16) & 0xff,(a.s_addr >> 8) & 0xff,
|
||
|
a.s_addr & 0xff);
|
||
|
#else
|
||
|
sprintf(astring,"%d.%d.%d.%d", a.s_addr & 0xff,
|
||
|
(a.s_addr >> 8) & 0xff,(a.s_addr >> 16) & 0xff,
|
||
|
(a.s_addr >> 24) & 0xff);
|
||
|
#endif
|
||
|
|
||
|
return(astring);
|
||
|
}
|
||
|
#endif NOTDEF
|
||
|
|
||
|
/* Only used locally in a context where it returns static data overwritten by
|
||
|
each call. */
|
||
|
static char *
|
||
|
inet_def_local(char *s)
|
||
|
{
|
||
|
AUTOSTAT_CHARPP(adstringp);
|
||
|
static long myaddr = 0; /* look below to see how this is mutexed */
|
||
|
char intstring[10];
|
||
|
static long o[4]; /* look below to see how this is mutexed */
|
||
|
char *ads;
|
||
|
long *octet;
|
||
|
|
||
|
if (!*adstringp) *adstringp = stalloc(50); /* effectively, an array of size
|
||
|
50. */
|
||
|
if (!myaddr) {
|
||
|
p_th_mutex_lock(p_th_mutexPSRV_CHECK_ACL_INET_DEF_LOCAL);
|
||
|
/* Now that we have a mutex, check adstring again, since it might
|
||
|
have changed since we tested it. This tests whether another
|
||
|
initialization successfully completed since we called this. */
|
||
|
if (!myaddr) {
|
||
|
long local_myaddr = myaddress();
|
||
|
#if BYTE_ORDER == BIG_ENDIAN
|
||
|
o[0] = (local_myaddr >> 24) & 0xff;
|
||
|
o[1] = (local_myaddr >> 16) & 0xff;
|
||
|
o[2] = (local_myaddr >> 8) & 0xff;
|
||
|
o[3] = local_myaddr & 0xff;
|
||
|
#else
|
||
|
o[0] = local_myaddr & 0xff;
|
||
|
o[1] = (local_myaddr >> 8) & 0xff;
|
||
|
o[2] = (local_myaddr >> 16) & 0xff;
|
||
|
o[3] = (local_myaddr >> 24) & 0xff;
|
||
|
#endif
|
||
|
myaddr = local_myaddr; /* change done; commit. */
|
||
|
}
|
||
|
p_th_mutex_unlock(p_th_mutexPSRV_CHECK_ACL_INET_DEF_LOCAL);
|
||
|
}
|
||
|
|
||
|
octet = o;
|
||
|
|
||
|
ads = *adstringp;
|
||
|
while(*s && ((ads - *adstringp) < 40) ) {
|
||
|
switch(*s) {
|
||
|
case '.':
|
||
|
octet++;
|
||
|
*ads++ = *s;
|
||
|
break;
|
||
|
case '%':
|
||
|
qsprintf(intstring,sizeof intstring, "%d",*octet);
|
||
|
bcopy(intstring,ads,strlen(intstring));
|
||
|
ads += strlen(intstring);
|
||
|
break;
|
||
|
default:
|
||
|
*ads++ = *s;
|
||
|
break;
|
||
|
}
|
||
|
s++;
|
||
|
}
|
||
|
*ads++ = '\0';
|
||
|
|
||
|
return(*adstringp);
|
||
|
|
||
|
}
|
||
|
|
||
|
/* Are we logged into a privileged port? 1 if yes, zero if no. */
|
||
|
int
|
||
|
check_prvport(req)
|
||
|
RREQ req;
|
||
|
{
|
||
|
return PEER_PORT(req) <= IPPORT_RESERVED;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* check_asserted_principal - check for membership in list of principals
|
||
|
* and check optional host ID. The principals have the format used by ASRTHOST
|
||
|
* and TRSTHOST.
|
||
|
*/
|
||
|
static int
|
||
|
check_asserted_principal(username,principals, hostaddr)
|
||
|
char username[];
|
||
|
struct in_addr hostaddr;
|
||
|
TOKEN principals;
|
||
|
{
|
||
|
for(; principals; principals = principals->next) {
|
||
|
char *host;
|
||
|
|
||
|
/* principal specified in the ACL entry. We copy it because we can't
|
||
|
modify the principals structure, since the principals structure
|
||
|
might contain strings that are in readonly memory. */
|
||
|
AUTOSTAT_CHARPP(aclprinp);
|
||
|
*aclprinp = stcopyr(principals->token, *aclprinp);
|
||
|
|
||
|
host = strchr(*aclprinp,'@');
|
||
|
if(host == NULL) {
|
||
|
if(wcmatch(username,*aclprinp)) return(TRUE);
|
||
|
else continue;
|
||
|
}
|
||
|
*(host++) = '\0';
|
||
|
|
||
|
if(!wcmatch(username,*aclprinp)) continue;
|
||
|
|
||
|
if(index("%123456789.",*host)) { /* Host address */
|
||
|
if(wcmatch(inet_ntoa(hostaddr),inet_def_local(host)))
|
||
|
return(TRUE);
|
||
|
continue;
|
||
|
}
|
||
|
else if(*host == '0') { /* Host address with leading 0 */
|
||
|
if(wcmatch(inet_ntoa(hostaddr),inet_def_local(host+1)))
|
||
|
return(TRUE);
|
||
|
continue;
|
||
|
}
|
||
|
else { /* Host name - not implemented */
|
||
|
continue;
|
||
|
}
|
||
|
}
|
||
|
return(FALSE);
|
||
|
}
|
||
|
|
||
|
ACL get_container_acl(char *path)
|
||
|
{
|
||
|
PFILE container;
|
||
|
ACL cont_acl = NULL;
|
||
|
ACL tacl = NULL;
|
||
|
ACL tacl2 = NULL;
|
||
|
char cpath[MAXPATHLEN];
|
||
|
char *slash;
|
||
|
|
||
|
strcpy(cpath,path);
|
||
|
slash = strrchr(cpath,'/');
|
||
|
|
||
|
if(slash) {
|
||
|
*slash = '\0';
|
||
|
container = pfalloc();
|
||
|
dsrfinfo(cpath,0,container);
|
||
|
cont_acl = container->oacl; container->oacl = NULL;
|
||
|
pffree(container);
|
||
|
tacl = cont_acl;
|
||
|
while(tacl) {
|
||
|
if(tacl->acetype == ACL_CONTAINER) {
|
||
|
tacl2 = tacl;
|
||
|
tacl = tacl->next;
|
||
|
EXTRACT_ITEM(tacl2,cont_acl);
|
||
|
acfree(tacl2);
|
||
|
}
|
||
|
else tacl = tacl->next;
|
||
|
}
|
||
|
return(cont_acl);
|
||
|
}
|
||
|
return(NULL);
|
||
|
}
|
||
|
|