Matt Crawford's kadm acl fixes & enhancements
authorKen Raeburn <raeburn@mit.edu>
Thu, 16 Mar 2000 13:05:17 +0000 (13:05 +0000)
committerKen Raeburn <raeburn@mit.edu>
Thu, 16 Mar 2000 13:05:17 +0000 (13:05 +0000)
git-svn-id: svn://anonsvn.mit.edu/krb5/trunk@12121 dc483132-0cff-0310-8789-dd5450dbe970

src/kadmin/server/ChangeLog
src/kadmin/server/server_stubs.c
src/lib/kadm5/srv/ChangeLog
src/lib/kadm5/srv/server_acl.c
src/lib/kadm5/srv/server_acl.h

index f1a14b4e7b38a7519e70a34887c697289afe9aa0..d5f932eb253420cbefdc11a7164cd502c64fe74e 100644 (file)
@@ -1,3 +1,11 @@
+2000-03-16  Ken Raeburn  <raeburn@mit.edu>
+           Matt Crawford  <crawdad@fnal.gov>
+
+       * server_stubs.c: All callers of acl_check updated to add new
+       restriction argument.  Impose any provided restrictions on add- or
+       modify-principal operations; pass NULL pointer for all other
+       operations including rename-principal.
+
 2000-02-27  Tom Yu  <tlyu@mit.edu>
 
        * server_stubs.c (create_principal3_1): Remove keepold argument.
index c90365a50a3b701e0fd8acf2d2b50ea4359ca473..2eef601b22bc5872b04d2e50bf6b3eb0c754d610 100644 (file)
@@ -237,6 +237,7 @@ create_principal_1(cprinc_arg *arg, struct svc_req *rqstp)
     gss_buffer_desc            client_name, service_name;
     OM_uint32                  minor_stat;
     kadm5_server_handle_t      handle;
+    restriction_t              *rp;
 
     xdr_free(xdr_generic_ret, &ret);
 
@@ -256,10 +257,11 @@ create_principal_1(cprinc_arg *arg, struct svc_req *rqstp)
     }
     krb5_unparse_name(handle->context, arg->rec.principal, &prime_arg);
 
-    if (CHANGEPW_SERVICE(rqstp) || !acl_check(handle->context,
-                                             rqstp->rq_clntcred,
-                                             ACL_ADD,
-                                             arg->rec.principal)) {
+    if (CHANGEPW_SERVICE(rqstp)
+       || !acl_check(handle->context, rqstp->rq_clntcred, ACL_ADD,
+                     arg->rec.principal, &rp)
+       || acl_impose_restrictions(handle->context,
+                                  &arg->rec, &arg->mask, rp)) {
         ret.code = KADM5_AUTH_ADD;
         krb5_klog_syslog(LOG_NOTICE, LOG_UNAUTH, "kadm5_create_principal",
                prime_arg, client_name.value, service_name.value,
@@ -289,6 +291,7 @@ create_principal3_1(cprinc3_arg *arg, struct svc_req *rqstp)
     gss_buffer_desc            client_name, service_name;
     OM_uint32                  minor_stat;
     kadm5_server_handle_t      handle;
+    restriction_t              *rp;
 
     xdr_free(xdr_generic_ret, &ret);
 
@@ -308,10 +311,11 @@ create_principal3_1(cprinc3_arg *arg, struct svc_req *rqstp)
     }
     krb5_unparse_name(handle->context, arg->rec.principal, &prime_arg);
 
-    if (CHANGEPW_SERVICE(rqstp) || !acl_check(handle->context,
-                                             rqstp->rq_clntcred,
-                                             ACL_ADD,
-                                             arg->rec.principal)) {
+    if (CHANGEPW_SERVICE(rqstp)
+       || !acl_check(handle->context, rqstp->rq_clntcred, ACL_ADD,
+                     arg->rec.principal, &rp)
+       || acl_impose_restrictions(handle->context,
+                                  &arg->rec, &arg->mask, rp)) {
         ret.code = KADM5_AUTH_ADD;
         krb5_klog_syslog(LOG_NOTICE, LOG_UNAUTH, "kadm5_create_principal",
                prime_arg, client_name.value, service_name.value,
@@ -363,10 +367,9 @@ delete_principal_1(dprinc_arg *arg, struct svc_req *rqstp)
     }
     krb5_unparse_name(handle->context, arg->princ, &prime_arg);
     
-    if (CHANGEPW_SERVICE(rqstp) || !acl_check(handle->context, 
-                                             rqstp->rq_clntcred,
-                                             ACL_DELETE,
-                                             arg->princ)) {
+    if (CHANGEPW_SERVICE(rqstp)
+       || !acl_check(handle->context, rqstp->rq_clntcred, ACL_DELETE,
+                     arg->princ, NULL)) {
         ret.code = KADM5_AUTH_DELETE;
         krb5_klog_syslog(LOG_NOTICE, LOG_UNAUTH, "kadm5_delete_principal",
                prime_arg, client_name.value, service_name.value,
@@ -394,6 +397,7 @@ modify_principal_1(mprinc_arg *arg, struct svc_req *rqstp)
                                    service_name;
     OM_uint32                      minor_stat;
     kadm5_server_handle_t          handle;
+    restriction_t                  *rp;
 
     xdr_free(xdr_generic_ret, &ret);
 
@@ -411,10 +415,11 @@ modify_principal_1(mprinc_arg *arg, struct svc_req *rqstp)
     }
     krb5_unparse_name(handle->context, arg->rec.principal, &prime_arg);
 
-    if (CHANGEPW_SERVICE(rqstp) || !acl_check(handle->context,
-                                             rqstp->rq_clntcred,
-                                             ACL_MODIFY,
-                                             arg->rec.principal)) {
+    if (CHANGEPW_SERVICE(rqstp)
+       || !acl_check(handle->context, rqstp->rq_clntcred, ACL_MODIFY,
+                     arg->rec.principal, &rp)
+       || acl_impose_restrictions(handle->context,
+                                  &arg->rec, &arg->mask, rp)) {
         ret.code = KADM5_AUTH_MODIFY;
         krb5_klog_syslog(LOG_NOTICE, LOG_UNAUTH, "kadm5_modify_principal",
                prime_arg, client_name.value, service_name.value,
@@ -446,6 +451,7 @@ rename_principal_1(rprinc_arg *arg, struct svc_req *rqstp)
                                service_name;
     OM_uint32                  minor_stat;
     kadm5_server_handle_t      handle;
+    restriction_t              *rp;
 
     xdr_free(xdr_generic_ret, &ret);
 
@@ -468,10 +474,11 @@ rename_principal_1(rprinc_arg *arg, struct svc_req *rqstp)
     ret.code = KADM5_OK;
     if (! CHANGEPW_SERVICE(rqstp)) {
         if (!acl_check(handle->context, rqstp->rq_clntcred,
-                       ACL_DELETE, arg->src))
+                       ACL_DELETE, arg->src, NULL))
              ret.code = KADM5_AUTH_DELETE;
+        /* any restrictions at all on the ADD kills the RENAME */
         if (!acl_check(handle->context, rqstp->rq_clntcred,
-                       ACL_ADD, arg->dest)) {
+                       ACL_ADD, arg->dest, &rp) || rp) {
              if (ret.code == KADM5_AUTH_DELETE)
                   ret.code = KADM5_AUTH_INSUFFICIENT;
              else
@@ -536,7 +543,8 @@ get_principal_1(gprinc_arg *arg, struct svc_req *rqstp)
        (CHANGEPW_SERVICE(rqstp) || !acl_check(handle->context,
                                               rqstp->rq_clntcred,
                                               ACL_INQUIRE,
-                                              arg->princ))) {
+                                              arg->princ,
+                                              NULL))) {
         ret.code = KADM5_AUTH_GET;
         krb5_klog_syslog(LOG_NOTICE, LOG_UNAUTH, funcname,
                prime_arg, client_name.value, service_name.value,
@@ -601,6 +609,7 @@ get_princs_1(gprincs_arg *arg, struct svc_req *rqstp)
     if (CHANGEPW_SERVICE(rqstp) || !acl_check(handle->context,
                                              rqstp->rq_clntcred,
                                              ACL_LIST,
+                                             NULL,
                                              NULL)) {
         ret.code = KADM5_AUTH_LIST;
         krb5_klog_syslog(LOG_NOTICE, LOG_UNAUTH, "kadm5_get_principals",
@@ -655,7 +664,7 @@ chpass_principal_1(chpass_arg *arg, struct svc_req *rqstp)
                                             arg->pass);
     } else if (!(CHANGEPW_SERVICE(rqstp)) &&
               acl_check(handle->context, rqstp->rq_clntcred,
-                        ACL_CHANGEPW, arg->princ)) {
+                        ACL_CHANGEPW, arg->princ, NULL)) {
         ret.code = kadm5_chpass_principal((void *)handle, arg->princ,
                                                arg->pass);
     } else {
@@ -713,7 +722,7 @@ chpass_principal3_1(chpass3_arg *arg, struct svc_req *rqstp)
                                             arg->pass);
     } else if (!(CHANGEPW_SERVICE(rqstp)) &&
               acl_check(handle->context, rqstp->rq_clntcred,
-                        ACL_CHANGEPW, arg->princ)) {
+                        ACL_CHANGEPW, arg->princ, NULL)) {
         ret.code = kadm5_chpass_principal_3((void *)handle, arg->princ,
                                             arg->keepold,
                                             arg->n_ks_tuple,
@@ -771,7 +780,7 @@ setv4key_principal_1(setv4key_arg *arg, struct svc_req *rqstp)
 
     if (!(CHANGEPW_SERVICE(rqstp)) &&
               acl_check(handle->context, rqstp->rq_clntcred,
-                        ACL_SETKEY, arg->princ)) {
+                        ACL_SETKEY, arg->princ, NULL)) {
         ret.code = kadm5_setv4key_principal((void *)handle, arg->princ,
                                             arg->keyblock);
     } else {
@@ -826,7 +835,7 @@ setkey_principal_1(setkey_arg *arg, struct svc_req *rqstp)
 
     if (!(CHANGEPW_SERVICE(rqstp)) &&
               acl_check(handle->context, rqstp->rq_clntcred,
-                        ACL_SETKEY, arg->princ)) {
+                        ACL_SETKEY, arg->princ, NULL)) {
         ret.code = kadm5_setkey_principal((void *)handle, arg->princ,
                                           arg->keyblocks, arg->n_keys);
     } else {
@@ -881,7 +890,7 @@ setkey_principal3_1(setkey3_arg *arg, struct svc_req *rqstp)
 
     if (!(CHANGEPW_SERVICE(rqstp)) &&
               acl_check(handle->context, rqstp->rq_clntcred,
-                        ACL_SETKEY, arg->princ)) {
+                        ACL_SETKEY, arg->princ, NULL)) {
         ret.code = kadm5_setkey_principal_3((void *)handle, arg->princ,
                                             arg->keepold,
                                             arg->n_ks_tuple,
@@ -948,7 +957,7 @@ chrand_principal_1(chrand_arg *arg, struct svc_req *rqstp)
                                              arg->princ, &k, &nkeys); 
     } else if (!(CHANGEPW_SERVICE(rqstp)) &&
               acl_check(handle->context, rqstp->rq_clntcred,
-                        ACL_CHANGEPW, arg->princ)) {
+                        ACL_CHANGEPW, arg->princ, NULL)) {
         ret.code = kadm5_randkey_principal((void *)handle, arg->princ,
                                            &k, &nkeys);
     } else {
@@ -1021,7 +1030,7 @@ chrand_principal3_1(chrand3_arg *arg, struct svc_req *rqstp)
                                              arg->princ, &k, &nkeys); 
     } else if (!(CHANGEPW_SERVICE(rqstp)) &&
               acl_check(handle->context, rqstp->rq_clntcred,
-                        ACL_CHANGEPW, arg->princ)) {
+                        ACL_CHANGEPW, arg->princ, NULL)) {
         ret.code = kadm5_randkey_principal_3((void *)handle, arg->princ,
                                              arg->keepold,
                                              arg->n_ks_tuple,
@@ -1088,7 +1097,7 @@ create_policy_1(cpol_arg *arg, struct svc_req *rqstp)
 
     if (CHANGEPW_SERVICE(rqstp) || !acl_check(handle->context,
                                              rqstp->rq_clntcred,
-                                             ACL_ADD, NULL)) {
+                                             ACL_ADD, NULL, NULL)) {
         ret.code = KADM5_AUTH_ADD;
         krb5_klog_syslog(LOG_NOTICE, LOG_UNAUTH, "kadm5_create_policy",
                prime_arg, client_name.value, service_name.value,
@@ -1139,7 +1148,7 @@ delete_policy_1(dpol_arg *arg, struct svc_req *rqstp)
     
     if (CHANGEPW_SERVICE(rqstp) || !acl_check(handle->context,
                                              rqstp->rq_clntcred,
-                                             ACL_DELETE, NULL)) {
+                                             ACL_DELETE, NULL, NULL)) {
         krb5_klog_syslog(LOG_NOTICE, LOG_UNAUTH, "kadm5_delete_policy",
                prime_arg, client_name.value, service_name.value,
                inet_ntoa(rqstp->rq_xprt->xp_raddr.sin_addr));
@@ -1188,7 +1197,7 @@ modify_policy_1(mpol_arg *arg, struct svc_req *rqstp)
 
     if (CHANGEPW_SERVICE(rqstp) || !acl_check(handle->context,
                                              rqstp->rq_clntcred,
-                                             ACL_MODIFY, NULL)) {
+                                             ACL_MODIFY, NULL, NULL)) {
         krb5_klog_syslog(LOG_NOTICE, LOG_UNAUTH, "kadm5_modify_policy",
                prime_arg, client_name.value, service_name.value,
                inet_ntoa(rqstp->rq_xprt->xp_raddr.sin_addr));
@@ -1246,7 +1255,7 @@ get_policy_1(gpol_arg *arg, struct svc_req *rqstp)
     ret.code = KADM5_AUTH_GET;
     if (!CHANGEPW_SERVICE(rqstp) && acl_check(handle->context,
                                              rqstp->rq_clntcred,
-                                             ACL_INQUIRE, NULL))
+                                             ACL_INQUIRE, NULL, NULL))
         ret.code = KADM5_OK;
     else {
         ret.code = kadm5_get_principal(handle->lhandle,
@@ -1325,7 +1334,7 @@ get_pols_1(gpols_arg *arg, struct svc_req *rqstp)
 
     if (CHANGEPW_SERVICE(rqstp) || !acl_check(handle->context,
                                              rqstp->rq_clntcred,
-                                             ACL_LIST, NULL)) {
+                                             ACL_LIST, NULL, NULL)) {
         ret.code = KADM5_AUTH_LIST;
         krb5_klog_syslog(LOG_NOTICE, LOG_UNAUTH, "kadm5_get_policies",
                prime_arg, client_name.value, service_name.value,
index 3ada002ce20132393d1fd450b3b09b78570bfbcf..792936dace0a35e6557ec754722ff0e24b826aac 100644 (file)
@@ -1,3 +1,22 @@
+2000-03-16  Ken Raeburn  <raeburn@mit.edu>
+           Matt Crawford  <crawdad@fnal.gov>
+
+       * server_acl.c: Include kadm5/admin.h.
+       (struct _acl_entry): Add new fields for restrictions.
+       (struct _wildstate): New type.
+       (acl_parse_line): Input is const.  Parse and save restrictions.
+       Allow backslash at end of line to indicate continuation.
+       (acl_parse_restrictions, acl_impose_restrictions): New functions.
+       (acl_free_entries): Free up restriction data if any.
+       (acl_load_acl_file): Don't use tmpbuf for catch-all entry.
+       (acl_match_data): Add new arguments for wildcard state.
+       (acl_find_entry): Support wildcard component matching.
+       Reorganized checks for bad ACL entries.
+       (acl_check): Add restrictions parameter.
+       * server_acl.h (struct _restriction): New type.
+       (acl_check): Update prototype.
+       (acl_impose_restrictions): Declare.
+
 2000-02-26  Tom Yu  <tlyu@mit.edu>
 
        * svr_principal.c (kadm5_create_principal_3): Remove keepold
index 8d330ec7cc9e5665c1cff7195fe96916425c3b8d..04b2ce707ab6337bfc03eaea77555973bc6622ba 100644 (file)
@@ -33,8 +33,9 @@
 #include <sys/param.h>
 #include <gssapi/gssapi_generic.h>
 #include "k5-int.h"
-#include "server_acl.h"
 #include <kadm5/server_internal.h>
+#include <kadm5/admin.h>
+#include "server_acl.h"
 #include <ctype.h>
 
 typedef struct _acl_op_table {
@@ -51,6 +52,10 @@ typedef struct _acl_entry {
     char               *ae_target;
     krb5_boolean       ae_target_bad;
     krb5_principal     ae_target_princ;
+    char               *ae_restriction_string;
+                       /* eg: "-maxlife 3h -service +proxiable" */
+    krb5_boolean       ae_restriction_bad;
+    restriction_t      *ae_restrictions;
 } aent_t;
 
 static const aop_t acl_op_table[] = {
@@ -66,6 +71,11 @@ static const aop_t acl_op_table[] = {
     { '\0',    0 }
 };
 
+typedef struct _wildstate {
+    int                nwild;
+    krb5_data  *backref[9];
+} wildstate_t;
+
 static aent_t  *acl_list_head = (aent_t *) NULL;
 static aent_t  *acl_list_tail = (aent_t *) NULL;
 
@@ -151,11 +161,12 @@ acl_get_line(fp, lnp)
  */
 static aent_t *
 acl_parse_line(lp)
-    char *lp;
+    const char *lp;
 {
     static char acle_principal[BUFSIZ];
     static char acle_ops[BUFSIZ];
     static char acle_object[BUFSIZ];
+    static char acle_restrictions[BUFSIZ];
     aent_t     *acle;
     char       *op;
     int                t, found, opok, nmatch;
@@ -163,13 +174,15 @@ acl_parse_line(lp)
     DPRINT(DEBUG_CALLS, acl_debug_level,
           ("* acl_parse_line(line=%20s)\n", lp));
     /*
-     * Format is very simple:
-     * entry ::= <whitespace> <principal> <whitespace> <opstring> <whitespace>
-     *           [<target> <whitespace>]
+     * Format is still simple:
+     *  entry ::= [<whitespace>] <principal> <whitespace> <opstring>
+     *           [<whitespace> <target> [<whitespace> <restrictions>
+     *                                   [<whitespace>]]]
      */
     acle = (aent_t *) NULL;
     acle_object[0] = '\0';
-    nmatch = sscanf(lp, "%s %s %s", acle_principal, acle_ops, acle_object);
+    nmatch = sscanf(lp, "%s %s %s %[^\n]", acle_principal, acle_ops,
+                   acle_object, acle_restrictions);
     if (nmatch >= 2) {
        acle = (aent_t *) malloc(sizeof(aent_t));
        if (acle) {
@@ -222,6 +235,20 @@ acl_parse_line(lp)
                free(acle);
                acle = (aent_t *) NULL;
            }
+           if ( nmatch >= 4 ) {
+               char    *trailing;
+
+               trailing = &acle_restrictions[strlen(acle_restrictions)-1];
+               while ( isspace(*trailing) )
+                   trailing--;
+               trailing[1] = '\0';
+               acle->ae_restriction_string = strdup(acle_restrictions);
+           }
+           else {
+               acle->ae_restriction_string = (char *) NULL;
+           }
+           acle->ae_restriction_bad = 0;
+           acle->ae_restrictions = (restriction_t *) NULL;
        }
     }
     DPRINT(DEBUG_CALLS, acl_debug_level,
@@ -229,6 +256,183 @@ acl_parse_line(lp)
     return(acle);
 }
 \f
+/*
+ * acl_parse_restrictions()    - Parse optional restrictions field
+ *
+ * Allowed restrictions are:
+ *     [+-]flagname            (recognized by krb5_string_to_flags)
+ *                             flag is forced to indicated value
+ *     -clearpolicy            policy is forced clear
+ *     -policy pol             policy is forced to be "pol"
+ *     -{expire,pwexpire,maxlife,maxrenewlife} deltat
+ *                             associated value will be forced to
+ *                             MIN(deltat, requested value)
+ *
+ * Returns: 0 on success, or system errors
+ */
+static krb5_error_code
+acl_parse_restrictions(s, rpp)
+    char               *s;
+    restriction_t      **rpp;
+{
+    char               *sp, *tp, *ap;
+    static const char  *delims = "\t\n\f\v\r ,";
+    krb5_error_code    ret;
+    krb5_deltat                dt;
+    krb5_flags         flag;
+    krb5_error_code    code;
+
+   DPRINT(DEBUG_CALLS, acl_debug_level,
+          ("* acl_parse_restrictions(s=%20s, rpp=0x%08x)\n", s, (long)rpp));
+
+    *rpp = (restriction_t *) NULL;
+    code = 0;
+    if (s)
+       if (!(sp = strdup(s))   /* Don't munge the original */
+           || !(*rpp = (restriction_t *) malloc(sizeof(restriction_t)))) {
+           code = ENOMEM;
+       } else {
+           memset(*rpp, 0, sizeof(**rpp));
+           for (tp=strtok(sp, delims); tp; tp=strtok((char *)NULL, delims)) {
+               flag = 0;
+               if (!krb5_string_to_flags(tp, "+", "-", &flag)) {
+                   /* OK, but was it in the positive or negative sense? */
+                   if (flag) {
+                       (*rpp)->require_attrs |= flag;
+                   } else {
+                       flag = ~0;
+                       (void) krb5_string_to_flags(tp, "+", "-", &flag);
+                       (*rpp)->forbid_attrs |= ~flag;
+                   }
+                   (*rpp)->mask |= KADM5_ATTRIBUTES;
+               } else if (!strcmp(tp, "-clearpolicy")) {
+                   (*rpp)->mask |= KADM5_POLICY_CLR;
+               } else {
+                   /* everything else needs an argument ... */
+                   if (!(ap = strtok((char *)NULL, delims))) {
+                       code = EINVAL;
+                       break;
+                   }
+                   if (!strcmp(tp, "-policy")) {
+                       if (!((*rpp)->policy = strdup(ap))) {
+                           code = ENOMEM;
+                           break;
+                       }
+                       (*rpp)->mask |= KADM5_POLICY;
+                   } else {
+                       /* all other arguments must be a deltat ... */
+                       if (krb5_string_to_deltat(ap, &dt)) {
+                           code = EINVAL;
+                           break;
+                       }
+                       if (!strcmp(tp, "-expire")) {
+                           (*rpp)->princ_lifetime = dt;
+                           (*rpp)->mask |= KADM5_PRINC_EXPIRE_TIME;
+                       } else if (!strcmp(tp, "-pwexpire")) {
+                           (*rpp)->pw_lifetime = dt;
+                           (*rpp)->mask |= KADM5_PW_EXPIRATION;
+                       } else if (!strcmp(tp, "-maxlife")) {
+                           (*rpp)->max_life = dt;
+                           (*rpp)->mask |= KADM5_MAX_LIFE;
+                       } else if (!strcmp(tp, "-maxrenewlife")) {
+                           (*rpp)->max_renewable_life = dt;
+                           (*rpp)->mask |= KADM5_MAX_RLIFE;
+                       } else {
+                           code = EINVAL;
+                           break;
+                       }
+                   }
+               }
+           }
+       }
+    if (sp)
+       free(sp);
+    if (*rpp && code) {
+       if ((*rpp)->policy)
+           free((*rpp)->policy);
+       free(*rpp);
+       *rpp = (restriction_t *) NULL;
+    }
+    DPRINT(DEBUG_CALLS, acl_debug_level,
+          ("X acl_parse_restrictions() = %d, mask=0x%08x\n",
+           code, (*rpp) ? (*rpp)->mask : 0));
+    return code;
+}
+\f
+/*
+ * acl_impose_restrictions()   - impose restrictions, modifying *recp, *maskp
+ *
+ * Returns: 0 on success;
+ *         malloc or timeofday errors
+ */
+krb5_error_code
+acl_impose_restrictions(kcontext, recp, maskp, rp)
+     krb5_context              kcontext;
+     kadm5_principal_ent_rec   *recp;
+     long                      *maskp;
+     restriction_t             *rp;
+{
+    krb5_error_code    code;
+    krb5_int32         now;
+
+    DPRINT(DEBUG_CALLS, acl_debug_level,
+          ("* acl_impose_restrictions(..., *maskp=0x%08x, rp=0x%08x)\n",
+           *maskp, (long)rp));
+    if (!rp)
+       return 0;
+    if (rp->mask & (KADM5_PRINC_EXPIRE_TIME|KADM5_PW_EXPIRATION))
+       if ((code = krb5_timeofday(kcontext, &now)))
+           return code;
+
+    if (rp->mask & KADM5_ATTRIBUTES) {
+       recp->attributes |= rp->require_attrs;
+       recp->attributes &= ~(rp->forbid_attrs);
+       *maskp |= KADM5_ATTRIBUTES;
+    }
+    if (rp->mask & KADM5_POLICY_CLR) {
+       *maskp &= ~KADM5_POLICY;
+       *maskp |= KADM5_POLICY_CLR;
+    } else if (rp->mask & KADM5_POLICY) {
+       if (recp->policy && strcmp(recp->policy, rp->policy)) {
+               free(recp->policy);
+               recp->policy = (char *) NULL;
+       }
+       if (!recp->policy) {
+           recp->policy = strdup(rp->policy);  /* XDR will free it */
+           if (!recp->policy)
+               return ENOMEM;
+       }
+       *maskp |= KADM5_POLICY;
+    }
+    if (rp->mask & KADM5_PRINC_EXPIRE_TIME) {
+       if (!(*maskp & KADM5_PRINC_EXPIRE_TIME)
+           || (recp->princ_expire_time > (now + rp->princ_lifetime)))
+           recp->princ_expire_time = now + rp->princ_lifetime;
+       *maskp |= KADM5_PRINC_EXPIRE_TIME;
+    }
+    if (rp->mask & KADM5_PW_EXPIRATION) {
+       if (!(*maskp & KADM5_PW_EXPIRATION)
+           || (recp->pw_expiration > (now + rp->pw_lifetime)))
+           recp->pw_expiration = now + rp->pw_lifetime;
+       *maskp |= KADM5_PW_EXPIRATION;
+    }
+    if (rp->mask & KADM5_MAX_LIFE) {
+       if (!(*maskp & KADM5_MAX_LIFE)
+           || (recp->max_life > rp->max_life))
+           recp->max_life = rp->max_life;
+       *maskp |= KADM5_MAX_LIFE;
+    }
+    if (rp->mask & KADM5_MAX_RLIFE) {
+       if (!(*maskp & KADM5_MAX_RLIFE)
+           || (recp->max_renewable_life > rp->max_renewable_life))
+           recp->max_renewable_life = rp->max_renewable_life;
+       *maskp |= KADM5_MAX_RLIFE;
+    }
+    DPRINT(DEBUG_CALLS, acl_debug_level,
+          ("X acl_impose_restrictions() = 0, *maskp=0x%08x\n", *maskp));
+    return 0;
+}
+\f
 /*
  * acl_free_entries()  - Free all ACL entries.
  */
@@ -248,6 +452,13 @@ acl_free_entries()
            free(ap->ae_target);
        if (ap->ae_target_princ)
            krb5_free_principal((krb5_context) NULL, ap->ae_target_princ);
+       if (ap->ae_restriction_string)
+           free(ap->ae_restriction_string);
+       if (ap->ae_restrictions) {
+           if (ap->ae_restrictions->policy)
+               free(ap->ae_restrictions->policy);
+           free(ap->ae_restrictions);
+       }
        np = ap->ae_next;
        free(ap);
     }
@@ -262,7 +473,6 @@ acl_free_entries()
 static int
 acl_load_acl_file()
 {
-char tmpbuf[10];
     FILE       *afp;
     char       *alinep;
     aent_t     **aentpp;
@@ -293,8 +503,7 @@ char tmpbuf[10];
        fclose(afp);
 
        if (acl_catchall_entry) {
-            strcpy(tmpbuf, acl_catchall_entry);
-            if (*aentpp = acl_parse_line(tmpbuf)) {
+            if (*aentpp = acl_parse_line(acl_catchall_entry)) {
                  acl_list_tail = *aentpp;
             }
             else {
@@ -334,17 +543,38 @@ char tmpbuf[10];
  * Wildcarding is only supported for a whole component.
  */
 static krb5_boolean
-acl_match_data(e1, e2)
+acl_match_data(e1, e2, targetflag, ws)
     krb5_data  *e1, *e2;
+    int                targetflag;
+    wildstate_t        *ws;
 {
     krb5_boolean       retval;
 
     DPRINT(DEBUG_CALLS, acl_debug_level, 
           ("* acl_match_entry(%s, %s)\n", e1->data, e2->data));
     retval = 0;
-    if (!strncmp(e1->data, "*", e1->length) ||
-       !strncmp(e2->data, "*", e2->length)) {
+    if (!strncmp(e1->data, "*", e1->length)) {
        retval = 1;
+       if (ws && !targetflag) {
+           if (ws->nwild >= 9) {
+               DPRINT(DEBUG_ACL, acl_debug_level,
+                   ("Too many wildcards in ACL entry %s\n", entry->ae_name));
+           }
+           else
+               ws->backref[ws->nwild++] = e2;
+       }
+    }
+    else if (ws && targetflag && (e1->length == 2) && (e1->data[0] == '*') &&
+            (e1->data[1] >= '1') && (e1->data[1] <= '9')) {
+       int     n = e1->data[1] - '1';
+       if (n >= ws->nwild) {
+           DPRINT(DEBUG_ACL, acl_debug_level,
+                  ("Too many backrefs in ACL entry %s\n", entry->ae_name));
+       }
+       else if ((ws->backref[n]->length == e2->length) &&
+                (!strncmp(ws->backref[n]->data, e2->data, e2->length)))
+           retval = 1;
+       
     }
     else {
        if ((e1->length == e2->length) &&
@@ -368,75 +598,98 @@ acl_find_entry(kcontext, principal, dest_princ)
     krb5_error_code    kret;
     int                        i;
     int                        matchgood;
+    wildstate_t                state;
 
     DPRINT(DEBUG_CALLS, acl_debug_level, ("* acl_find_entry()\n"));
+    memset((char *)&state, 0, sizeof state);
     for (entry=acl_list_head; entry; entry = entry->ae_next) {
+       if (entry->ae_name_bad)
+           continue;
        if (!strcmp(entry->ae_name, "*")) {
            DPRINT(DEBUG_ACL, acl_debug_level, ("A wildcard ACL match\n"));
-           break;
+           matchgood = 1;
        }
-       if (!entry->ae_principal && !entry->ae_name_bad) {
-           kret = krb5_parse_name(kcontext,
-                                  entry->ae_name,
-                                  &entry->ae_principal);
-           if (kret)
-               entry->ae_name_bad = 1;
+       else {
+           if (!entry->ae_principal && !entry->ae_name_bad) {
+               kret = krb5_parse_name(kcontext,
+                                      entry->ae_name,
+                                      &entry->ae_principal);
+               if (kret)
+                   entry->ae_name_bad = 1;
+           }
+           if (entry->ae_name_bad) {
+               DPRINT(DEBUG_ACL, acl_debug_level,
+                      ("Bad ACL entry %s\n", entry->ae_name));
+               continue;
+           }
+           matchgood = 0;
+           if (acl_match_data(&entry->ae_principal->realm,
+                              &principal->realm, 0, (wildstate_t *)0) &&
+               (entry->ae_principal->length == principal->length)) {
+               matchgood = 1;
+               for (i=0; i<principal->length; i++) {
+                   if (!acl_match_data(&entry->ae_principal->data[i],
+                                       &principal->data[i], 0, &state)) {
+                       matchgood = 0;
+                       break;
+                   }
+               }
+           }
        }
-       if (entry->ae_name_bad) {
-           DPRINT(DEBUG_ACL, acl_debug_level,
-                  ("A Bad ACL entry %s\n", entry->ae_name));
+       if (!matchgood)
            continue;
-       }
-       if (entry->ae_target &&
-           !entry->ae_target_princ &&
-           !entry->ae_target_bad) {
-           kret = krb5_parse_name(kcontext,
-                                  entry->ae_target,
-                                  &entry->ae_target_princ);
-           if (kret)
-               entry->ae_target_bad = 1;
+
+       /* We've matched the principal.  If we have a target, then try it */
+       if (entry->ae_target) {
+           if (!strcmp(entry->ae_target, "*"))
+               break;
+           if (!entry->ae_target_princ && !entry->ae_target_bad) {
+               kret = krb5_parse_name(kcontext, entry->ae_target,
+                                      &entry->ae_target_princ);
+               if (kret)
+                   entry->ae_target_bad = 1;
+           }
        }
        if (entry->ae_target_bad) {
            DPRINT(DEBUG_ACL, acl_debug_level,
-                  ("A Bad target in an ACL entry for %s\n", entry->ae_name));
+                  ("Bad target in ACL entry for %s\n", entry->ae_name));
            entry->ae_name_bad = 1;
            continue;
        }
-       matchgood = 0;
-       if (acl_match_data(&entry->ae_principal->realm,
-                          &principal->realm) &&
-           (entry->ae_principal->length == principal->length)) {
-           matchgood = 1;
-           for (i=0; i<principal->length; i++) {
-               if (!acl_match_data(&entry->ae_principal->data[i],
-                                   &principal->data[i])) {
-                   matchgood = 0;
-                   break;
+       if (entry->ae_target && !dest_princ)
+           matchgood = 0;
+       else if (entry->ae_target && entry->ae_target_princ && dest_princ) {
+           if (acl_match_data(&entry->ae_target_princ->realm,
+                              &dest_princ->realm, 1, (wildstate_t *)0) &&
+               (entry->ae_target_princ->length == dest_princ->length)) {
+               for (i=0; i<dest_princ->length; i++) {
+                   if (!acl_match_data(&entry->ae_target_princ->data[i],
+                                       &dest_princ->data[i], 1, &state)) {
+                       matchgood = 0;
+                       break;
+                   }
                }
            }
+           else
+               matchgood = 0;
        }
        if (!matchgood)
            continue;
 
-       /* We've matched the principal.  If we have a target, then try it */
-       if (entry->ae_target && entry->ae_target_princ && dest_princ) {
-           if (acl_match_data(&entry->ae_target_princ->realm,
-                              &dest_princ->realm) &&
-               (entry->ae_target_princ->length == dest_princ->length)) {
-              for (i=0; i<dest_princ->length; i++) {
-                 if (!acl_match_data(&entry->ae_target_princ->data[i],
-                                     &dest_princ->data[i])) {
-                    matchgood = 0;
-                    break;
-                 }
-              }
-           }
-           else
-              matchgood = 0;
+       if (entry->ae_restriction_string
+           && !entry->ae_restriction_bad
+           && !entry->ae_restrictions
+           && acl_parse_restrictions(entry->ae_restriction_string,
+                                     &entry->ae_restrictions)) {
+           DPRINT(DEBUG_ACL, acl_debug_level,
+                  ("Bad restrictions in ACL entry for %s\n", entry->ae_name));
+           entry->ae_restriction_bad = 1;
        }
-
-       if (matchgood)
-           break;
+       if (entry->ae_restriction_bad) {
+           entry->ae_name_bad = 1;
+           continue;
+       }
+       break;
     }
     DPRINT(DEBUG_CALLS, acl_debug_level, ("X acl_find_entry()=%x\n",entry));
     return(entry);
@@ -479,7 +732,7 @@ acl_finish(kcontext, debug_level)
 }
 \f
 /*
- * acl_op_permitted()  - Is this operation permitted for this principal?
+ * acl_check() - Is this operation permitted for this principal?
  *                     this code used not to be based on gssapi.  In order
  *                     to minimize porting hassles, I've put all the
  *                     gssapi hair in this function.  This might not be
@@ -487,11 +740,12 @@ acl_finish(kcontext, debug_level)
  *                     solution is, of course, a real authorization service.)
  */
 krb5_boolean
-acl_check(kcontext, caller, opmask, principal)
+acl_check(kcontext, caller, opmask, principal, restrictions)
     krb5_context       kcontext;
     gss_name_t         caller;
     krb5_int32         opmask;
     krb5_principal     principal;
+    restriction_t      **restrictions;
 {
     krb5_boolean       retval;
     aent_t             *aentry;
@@ -517,8 +771,15 @@ acl_check(kcontext, caller, opmask, principal)
 
     retval = 0;
     if (aentry = acl_find_entry(kcontext, caller_princ, principal)) {
-       if ((aentry->ae_op_allowed & opmask) == opmask)
+       if ((aentry->ae_op_allowed & opmask) == opmask) {
            retval = 1;
+           if (restrictions) {
+               *restrictions =
+                   (aentry->ae_restrictions && aentry->ae_restrictions->mask)
+                   ? aentry->ae_restrictions
+                   : (restriction_t *) NULL;
+           }
+       }
     }
 
     krb5_free_principal(kcontext, caller_princ);
@@ -528,7 +789,8 @@ acl_check(kcontext, caller, opmask, principal)
     return(retval);
 }
 
-kadm5_ret_t kadm5_get_privs(void *server_handle, long *privs)
+kadm5_ret_t
+kadm5_get_privs(void *server_handle, long *privs)
 {
      kadm5_server_handle_t handle = server_handle;
 
index e2aa8bddccd4c4ee44e473ac3dd303c4b56db3a8..226a4d925b5fff80884e4ca9364c960da32d58cf 100644 (file)
                                 ACL_LIST       | \
                                 ACL_SETKEY)
 
+typedef struct _restriction {
+    long               mask;
+    krb5_flags         require_attrs;
+    krb5_flags         forbid_attrs;
+    krb5_deltat                princ_lifetime;
+    krb5_deltat                pw_lifetime;
+    krb5_deltat                max_life;
+    krb5_deltat                max_renewable_life;
+    long               aux_attributes;
+    char               *policy;
+} restriction_t;
+
 krb5_error_code acl_init
        KRB5_PROTOTYPE((krb5_context,
                   int,
@@ -81,6 +93,11 @@ krb5_boolean acl_check
        KRB5_PROTOTYPE((krb5_context,
                   gss_name_t,
                   krb5_int32,
-                  krb5_principal));
-
+                  krb5_principal,
+                  restriction_t **));
+krb5_error_code acl_impose_restrictions
+       KRB5_PROTOTYPE((krb5_context,
+                  kadm5_principal_ent_rec *,
+                  long *,
+                  restriction_t *));
 #endif /* SERVER_ACL_H__ */