/* * Copyright 2006 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #pragma ident "%Z%%M% %I% %E% SMI" /* * WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING * * Openvision retains the copyright to derivative works of * this source code. Do *NOT* create a derivative of this * source code before consulting with your legal department. * Do *NOT* integrate *ANY* of this source code into another * product before consulting with your legal department. * * For further information, read the top-level Openvision * copyright which is contained in the top-level MIT Kerberos * copyright. * * WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING * */ /* * kadmin/v5server/srv_acl.c * * Copyright 1995 by the Massachusetts Institute of Technology. * All Rights Reserved. * * Export of this software from the United States of America may * require a specific license from the United States Government. * It is the responsibility of any person or organization contemplating * export to obtain such a license before exporting. * * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and * distribute this software and its documentation for any purpose and * without fee is hereby granted, provided that the above copyright * notice appear in all copies and that both that copyright notice and * this permission notice appear in supporting documentation, and that * the name of M.I.T. not be used in advertising or publicity pertaining * to distribution of the software without specific, written prior * permission. Furthermore if you modify this software you must label * your software as modified software and not distribute it in such a * fashion that it might be confused with the original M.I.T. software. * M.I.T. makes no representations about the suitability of * this software for any purpose. It is provided "as is" without express * or implied warranty. * */ /* * srv_acl.c - Handle Kerberos ACL related functions. */ #include #include #include #include #include "k5-int.h" #include #include #include /* SUNWresync121 XXX */ #include "server_acl.h" #include #include /* SUNWresync121 XXX */ typedef struct _acl_op_table { char ao_op; krb5_int32 ao_mask; } aop_t; typedef struct _acl_entry { struct _acl_entry *ae_next; char *ae_name; krb5_boolean ae_name_bad; krb5_principal ae_principal; krb5_int32 ae_op_allowed; 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[] = { { 'a', ACL_ADD }, { 'd', ACL_DELETE }, { 'm', ACL_MODIFY }, { 'c', ACL_CHANGEPW }, { 'i', ACL_INQUIRE }, { 'l', ACL_LIST }, { 'p', ACL_IPROP }, /* SUNW IProp */ { 's', ACL_SETKEY }, { 'u', ACL_MIGRATE }, /* pam_krb5_migrate */ { 'x', ACL_ALL_MASK }, { '*', ACL_ALL_MASK }, { '\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; static const char *acl_acl_file = (char *) NULL; static int acl_inited = 0; static int acl_debug_level = 0; /* * This is the catchall entry. If nothing else appropriate is found, or in * the case where the ACL file is not present, this entry controls what can * be done. */ static const char *acl_catchall_entry = NULL; #define ACL_LINE2LONG_MSG dgettext(TEXT_DOMAIN, \ "%s: line %d too long, truncated\n") #define ACL_OP_BAD_MSG dgettext(TEXT_DOMAIN, \ "Unrecognized ACL operation '%c' in %s\n") #define ACL_SYN_ERR_MSG dgettext(TEXT_DOMAIN, \ "%s: syntax error at line %d <%10s...>\n") #define ACL_CANTOPEN_MSG dgettext(TEXT_DOMAIN, \ "\007cannot open ACL file") /* * acl_get_line() - Get a line from the ACL file. * Lines ending with \ are continued on the next line */ static char * acl_get_line(fp, lnp) FILE *fp; int *lnp; /* caller should set to 1 before first call */ { int i, domore; static int line_incr = 0; static char acl_buf[BUFSIZ]; *lnp += line_incr; line_incr = 0; for (domore = 1; domore && !feof(fp); ) { /* Copy in the line, with continuations */ for (i=0; ((i < sizeof acl_buf) && !feof(fp)); i++ ) { acl_buf[i] = fgetc(fp); if (acl_buf[i] == (char)EOF) { if (i > 0 && acl_buf[i-1] == '\\') i--; break; /* it gets nulled-out below */ } else if (acl_buf[i] == '\n') { if (i == 0 || acl_buf[i-1] != '\\') break; /* empty line or normal end of line */ else { i -= 2; /* back up over "\\\n" and continue */ line_incr++; } } } /* Check if we exceeded our buffer size */ if (i == sizeof acl_buf && (i--, !feof(fp))) { int c1 = acl_buf[i], c2; krb5_klog_syslog(LOG_ERR, ACL_LINE2LONG_MSG, acl_acl_file, *lnp); while ((c2 = fgetc(fp)) != EOF) { if (c2 == '\n') { if (c1 != '\\') break; line_incr++; } c1 = c2; } } acl_buf[i] = '\0'; if (acl_buf[0] == (char) EOF) /* ptooey */ acl_buf[0] = '\0'; else line_incr++; if ((acl_buf[0] != '#') && (acl_buf[0] != '\0')) domore = 0; } if (domore || (strlen(acl_buf) == 0)) return((char *) NULL); else return(acl_buf); } /* * acl_parse_line() - Parse the contents of an ACL line. */ static aent_t * acl_parse_line(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; DPRINT(DEBUG_CALLS, acl_debug_level, ("* acl_parse_line(line=%20s)\n", lp)); /* * Format is still simple: * entry ::= [] * [ [ * []]] */ acle = (aent_t *) NULL; acle_object[0] = '\0'; 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) { acle->ae_next = (aent_t *) NULL; acle->ae_op_allowed = (krb5_int32) 0; acle->ae_target = (nmatch >= 3) ? strdup(acle_object) : (char *) NULL; acle->ae_target_bad = 0; acle->ae_target_princ = (krb5_principal) NULL; opok = 1; for (op=acle_ops; *op; op++) { char rop; rop = (isupper(*op)) ? tolower(*op) : *op; found = 0; for (t=0; acl_op_table[t].ao_op; t++) { if (rop == acl_op_table[t].ao_op) { found = 1; if (rop == *op) acle->ae_op_allowed |= acl_op_table[t].ao_mask; else acle->ae_op_allowed &= ~acl_op_table[t].ao_mask; } } if (!found) { krb5_klog_syslog(LOG_ERR, ACL_OP_BAD_MSG, *op, lp); opok = 0; } } if (opok) { acle->ae_name = (char *) malloc(strlen(acle_principal)+1); if (acle->ae_name) { strcpy(acle->ae_name, acle_principal); acle->ae_principal = (krb5_principal) NULL; acle->ae_name_bad = 0; DPRINT(DEBUG_ACL, acl_debug_level, ("A ACL entry %s -> opmask %x\n", acle->ae_name, acle->ae_op_allowed)); } else { if (acle->ae_target) free(acle->ae_target); free(acle); acle = (aent_t *) NULL; } } else { if (acle->ae_target) free(acle->ae_target); 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, ("X acl_parse_line() = %x\n", (long) acle)); return(acle); } /* * 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; } /* * 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; } /* * acl_free_entries() - Free all ACL entries. */ static void acl_free_entries() { aent_t *ap; aent_t *np; DPRINT(DEBUG_CALLS, acl_debug_level, ("* acl_free_entries()\n")); for (ap=acl_list_head; ap; ap = np) { if (ap->ae_name) free(ap->ae_name); if (ap->ae_principal) krb5_free_principal((krb5_context) NULL, ap->ae_principal); if (ap->ae_target) 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); } acl_list_head = acl_list_tail = (aent_t *) NULL; acl_inited = 0; DPRINT(DEBUG_CALLS, acl_debug_level, ("X acl_free_entries()\n")); } /* * acl_load_acl_file() - Open and parse the ACL file. */ static int acl_load_acl_file() { FILE *afp; char *alinep; aent_t **aentpp; int alineno; int retval = 1; DPRINT(DEBUG_CALLS, acl_debug_level, ("* acl_load_acl_file()\n")); /* Open the ACL file for read */ if (afp = fopen(acl_acl_file, "rF")) { alineno = 1; aentpp = &acl_list_head; /* Get a non-comment line */ while (alinep = acl_get_line(afp, &alineno)) { /* Parse it */ *aentpp = acl_parse_line(alinep); /* If syntax error, then fall out */ if (!*aentpp) { krb5_klog_syslog(LOG_ERR, ACL_SYN_ERR_MSG, acl_acl_file, alineno, alinep); retval = 0; break; } acl_list_tail = *aentpp; aentpp = &(*aentpp)->ae_next; } fclose(afp); if (acl_catchall_entry) { if (*aentpp = acl_parse_line(acl_catchall_entry)) { acl_list_tail = *aentpp; } else { retval = 0; DPRINT(DEBUG_OPERATION, acl_debug_level, ("> catchall acl entry (%s) load failed\n", acl_catchall_entry)); } } } else { krb5_klog_syslog(LOG_ERR, ACL_CANTOPEN_MSG, error_message(errno), acl_acl_file); if (acl_catchall_entry && (acl_list_head = acl_parse_line((char *)acl_catchall_entry))) { acl_list_tail = acl_list_head; } else { retval = 0; DPRINT(DEBUG_OPERATION, acl_debug_level, ("> catchall acl entry (%s) load failed\n", acl_catchall_entry)); } } if (!retval) { acl_free_entries(); } DPRINT(DEBUG_CALLS, acl_debug_level, ("X acl_load_acl_file() = %d\n", retval)); return(retval); } /* * acl_match_data() - See if two data entries match. * * Wildcarding is only supported for a whole component. */ static krb5_boolean 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)) { retval = 1; if (ws && !targetflag) { if (ws->nwild >= 9) { DPRINT(DEBUG_ACL, acl_debug_level, ("Too many wildcards in ACL entry %s\n", e1->data)); } 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", e1->data)); } 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) && (!strncmp(e1->data, e2->data, e1->length))) retval = 1; } DPRINT(DEBUG_CALLS, acl_debug_level, ("X acl_match_entry()=%d\n",retval)); return(retval); } /* * acl_find_entry() - Find a matching entry. */ static aent_t * acl_find_entry(kcontext, principal, dest_princ) krb5_context kcontext; krb5_principal principal; krb5_principal dest_princ; { aent_t *entry; 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")); matchgood = 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; ilength; i++) { if (!acl_match_data(&entry->ae_principal->data[i], &principal->data[i], 0, &state)) { matchgood = 0; break; } } } } if (!matchgood) continue; /* 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, ("Bad target in ACL entry for %s\n", entry->ae_name)); entry->ae_name_bad = 1; continue; } 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; ilength; 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; 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 (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); } /* * acl_init() - Initialize ACL context. */ krb5_error_code acl_init(kcontext, debug_level, acl_file) krb5_context kcontext; int debug_level; char *acl_file; { krb5_error_code kret; kret = 0; acl_debug_level = debug_level; DPRINT(DEBUG_CALLS, acl_debug_level, ("* acl_init(afile=%s)\n", ((acl_file) ? acl_file : "(null)"))); acl_acl_file = (acl_file) ? acl_file : (char *) KRB5_DEFAULT_ADMIN_ACL; acl_inited = acl_load_acl_file(); DPRINT(DEBUG_CALLS, acl_debug_level, ("X acl_init() = %d\n", kret)); return(kret); } /* * acl_finish - Terminate ACL context. */ void acl_finish(kcontext, debug_level) krb5_context kcontext; int debug_level; { DPRINT(DEBUG_CALLS, acl_debug_level, ("* acl_finish()\n")); acl_free_entries(); DPRINT(DEBUG_CALLS, acl_debug_level, ("X acl_finish()\n")); } /* * 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 * the best medium-term solution. (The best long-term * solution is, of course, a real authorization service.) */ krb5_boolean 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; gss_buffer_desc caller_buf; gss_OID caller_oid; OM_uint32 emaj, emin; krb5_error_code code; krb5_principal caller_princ; DPRINT(DEBUG_CALLS, acl_debug_level, ("* acl_op_permitted()\n")); if (restrictions) *restrictions = NULL; if (GSS_ERROR(emaj = gss_display_name(&emin, caller, &caller_buf, &caller_oid))) return(1); code = krb5_parse_name(kcontext, (char *) caller_buf.value, &caller_princ); gss_release_buffer(&emin, &caller_buf); if (code) return(code); retval = 0; if (aentry = acl_find_entry(kcontext, caller_princ, principal)) { 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); DPRINT(DEBUG_CALLS, acl_debug_level, ("X acl_op_permitted()=%d\n", retval)); return(retval); } kadm5_ret_t kadm5_get_privs(void *server_handle, long *privs) { kadm5_server_handle_t handle = server_handle; CHECK_HANDLE(server_handle); /* this is impossible to do with the current interface. For now, return all privs, which will confuse some clients, but not deny any access to users of "smart" clients which try to cache */ *privs = ~0; return KADM5_OK; } /* SUNWresync121 (SEAM1.0) XXX */ kadm5_ret_t __kadm5_get_priv(void *server_handle, long *privs, gss_name_t client) { aent_t *aentry; gss_buffer_desc caller_buff; gss_OID caller_oid; krb5_principal caller_principal; OM_uint32 minor, major; krb5_error_code k_error; kadm5_ret_t retval = KADM5_FAILURE; kadm5_server_handle_t handle = server_handle; CHECK_HANDLE(server_handle); if (GSS_ERROR(major = gss_display_name(&minor, client, &caller_buff, &caller_oid))) return(retval); k_error = krb5_parse_name(handle->context, (char *) caller_buff.value, &caller_principal); gss_release_buffer(&minor, &caller_buff); if (k_error) return(retval); if (aentry = acl_find_entry(handle->context, caller_principal, (krb5_principal)NULL)) *privs = aentry->ae_op_allowed; krb5_free_principal(handle->context, caller_principal); return (KADM5_OK); }