xref: /illumos-gate/usr/src/lib/krb5/kadm5/srv/server_acl.c (revision 4de2612967d06c4fdbf524a62556a1e8118a006f)
1 /*
2  * Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
3  * Use is subject to license terms.
4  */
5 
6 #pragma ident	"%Z%%M%	%I%	%E% SMI"
7 
8 /*
9  * WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING
10  *
11  *	Openvision retains the copyright to derivative works of
12  *	this source code.  Do *NOT* create a derivative of this
13  *	source code before consulting with your legal department.
14  *	Do *NOT* integrate *ANY* of this source code into another
15  *	product before consulting with your legal department.
16  *
17  *	For further information, read the top-level Openvision
18  *	copyright which is contained in the top-level MIT Kerberos
19  *	copyright.
20  *
21  * WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING
22  *
23  */
24 
25 
26 /*
27  * kadmin/v5server/srv_acl.c
28  *
29  * Copyright 1995 by the Massachusetts Institute of Technology.
30  * All Rights Reserved.
31  *
32  * Export of this software from the United States of America may
33  *   require a specific license from the United States Government.
34  *   It is the responsibility of any person or organization contemplating
35  *   export to obtain such a license before exporting.
36  *
37  * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
38  * distribute this software and its documentation for any purpose and
39  * without fee is hereby granted, provided that the above copyright
40  * notice appear in all copies and that both that copyright notice and
41  * this permission notice appear in supporting documentation, and that
42  * the name of M.I.T. not be used in advertising or publicity pertaining
43  * to distribution of the software without specific, written prior
44  * permission.  Furthermore if you modify this software you must label
45  * your software as modified software and not distribute it in such a
46  * fashion that it might be confused with the original M.I.T. software.
47  * M.I.T. makes no representations about the suitability of
48  * this software for any purpose.  It is provided "as is" without express
49  * or implied warranty.
50  *
51  */
52 
53 /*
54  * srv_acl.c - Handle Kerberos ACL related functions.
55  */
56 #include <stdio.h>
57 #include <syslog.h>
58 #include <sys/param.h>
59 #include <gssapi_krb5.h>
60 #include "k5-int.h"
61 #include <kadm5/server_internal.h>
62 #include <kadm5/admin.h>
63 #include <adm_proto.h> /* SUNWresync121 XXX */
64 #include "server_acl.h"
65 #include <ctype.h>
66 #include <libintl.h> /* SUNWresync121 XXX */
67 
68 typedef struct _acl_op_table {
69     char	ao_op;
70     krb5_int32	ao_mask;
71 } aop_t;
72 
73 typedef struct _acl_entry {
74     struct _acl_entry	*ae_next;
75     char		*ae_name;
76     krb5_boolean	ae_name_bad;
77     krb5_principal	ae_principal;
78     krb5_int32		ae_op_allowed;
79     char		*ae_target;
80     krb5_boolean	ae_target_bad;
81     krb5_principal	ae_target_princ;
82     char		*ae_restriction_string;
83 			/* eg: "-maxlife 3h -service +proxiable" */
84     krb5_boolean	ae_restriction_bad;
85     restriction_t	*ae_restrictions;
86 } aent_t;
87 
88 static const aop_t acl_op_table[] = {
89     { 'a',	ACL_ADD },
90     { 'd',	ACL_DELETE },
91     { 'm',	ACL_MODIFY },
92     { 'c',	ACL_CHANGEPW },
93     { 'i',	ACL_INQUIRE },
94     { 'l',	ACL_LIST },
95     { 'p',	ACL_IPROP },		/* SUNW IProp */
96     { 's',	ACL_SETKEY },
97     { 'u',	ACL_MIGRATE },		/* pam_krb5_migrate */
98     { 'x',	ACL_ALL_MASK },
99     { '*',	ACL_ALL_MASK },
100     { '\0',	0 }
101 };
102 
103 typedef struct _wildstate {
104     int		nwild;
105     krb5_data	*backref[9];
106 } wildstate_t;
107 
108 static aent_t	*acl_list_head = (aent_t *) NULL;
109 static aent_t	*acl_list_tail = (aent_t *) NULL;
110 
111 static const char *acl_acl_file = (char *) NULL;
112 static int acl_inited = 0;
113 static int acl_debug_level = 0;
114 /*
115  * This is the catchall entry.  If nothing else appropriate is found, or in
116  * the case where the ACL file is not present, this entry controls what can
117  * be done.
118  */
119 static const char *acl_catchall_entry = NULL;
120 
121 #define ACL_LINE2LONG_MSG dgettext(TEXT_DOMAIN, \
122 			"%s: line %d too long, truncated\n")
123 #define ACL_OP_BAD_MSG dgettext(TEXT_DOMAIN, \
124 			"Unrecognized ACL operation '%c' in %s\n")
125 #define ACL_SYN_ERR_MSG dgettext(TEXT_DOMAIN, \
126 			"%s: syntax error at line %d <%10s...>\n")
127 #define ACL_CANTOPEN_MSG dgettext(TEXT_DOMAIN, \
128 			"\007cannot open ACL file")
129 
130 
131 /*
132  * acl_get_line()	- Get a line from the ACL file.
133  *			Lines ending with \ are continued on the next line
134  */
135 static char *
136 acl_get_line(fp, lnp)
137     FILE	*fp;
138     int		*lnp;		/* caller should set to 1 before first call */
139 {
140     int		i, domore;
141     static int	line_incr = 0;
142     static char acl_buf[BUFSIZ];
143 
144     *lnp += line_incr;
145     line_incr = 0;
146     for (domore = 1; domore && !feof(fp); ) {
147 	/* Copy in the line, with continuations */
148 	for (i=0; ((i < sizeof acl_buf) && !feof(fp)); i++ ) {
149 	    acl_buf[i] = fgetc(fp);
150 	    if (acl_buf[i] == (char)EOF) {
151 		if (i > 0 && acl_buf[i-1] == '\\')
152 		    i--;
153 		break;		/* it gets nulled-out below */
154 	    }
155 	    else if (acl_buf[i] == '\n') {
156 		if (i == 0 || acl_buf[i-1] != '\\')
157 		    break;	/* empty line or normal end of line */
158 		else {
159 		    i -= 2;	/* back up over "\\\n" and continue */
160 		    line_incr++;
161 		}
162 	    }
163 	}
164 	/* Check if we exceeded our buffer size */
165 	if (i == sizeof acl_buf && (i--, !feof(fp))) {
166 	    int	c1 = acl_buf[i], c2;
167 
168 	    krb5_klog_syslog(LOG_ERR, ACL_LINE2LONG_MSG, acl_acl_file, *lnp);
169 	    while ((c2 = fgetc(fp)) != EOF) {
170 		if (c2 == '\n') {
171 		    if (c1 != '\\')
172 			break;
173 		    line_incr++;
174 		}
175 		c1 = c2;
176 	    }
177 	}
178 	acl_buf[i] = '\0';
179 	if (acl_buf[0] == (char) EOF)	/* ptooey */
180 	    acl_buf[0] = '\0';
181 	else
182 	    line_incr++;
183 	if ((acl_buf[0] != '#') && (acl_buf[0] != '\0'))
184 	    domore = 0;
185     }
186     if (domore || (strlen(acl_buf) == 0))
187 	return((char *) NULL);
188     else
189 	return(acl_buf);
190 }
191 
192 /*
193  * acl_parse_line()	- Parse the contents of an ACL line.
194  */
195 static aent_t *
196 acl_parse_line(lp)
197     const char *lp;
198 {
199     static char acle_principal[BUFSIZ];
200     static char acle_ops[BUFSIZ];
201     static char acle_object[BUFSIZ];
202     static char acle_restrictions[BUFSIZ];
203     aent_t	*acle;
204     char	*op;
205     int		t, found, opok, nmatch;
206 
207     DPRINT(DEBUG_CALLS, acl_debug_level,
208 	   ("* acl_parse_line(line=%20s)\n", lp));
209     /*
210      * Format is still simple:
211      *  entry ::= [<whitespace>] <principal> <whitespace> <opstring>
212      *		  [<whitespace> <target> [<whitespace> <restrictions>
213      *					  [<whitespace>]]]
214      */
215     acle = (aent_t *) NULL;
216     acle_object[0] = '\0';
217     nmatch = sscanf(lp, "%s %s %s %[^\n]", acle_principal, acle_ops,
218 		    acle_object, acle_restrictions);
219     if (nmatch >= 2) {
220 	acle = (aent_t *) malloc(sizeof(aent_t));
221 	if (acle) {
222 	    acle->ae_next = (aent_t *) NULL;
223 	    acle->ae_op_allowed = (krb5_int32) 0;
224 	    acle->ae_target =
225 		(nmatch >= 3) ? strdup(acle_object) : (char *) NULL;
226 	    acle->ae_target_bad = 0;
227 	    acle->ae_target_princ = (krb5_principal) NULL;
228 	    opok = 1;
229 	    for (op=acle_ops; *op; op++) {
230 		char rop;
231 
232 		rop = (isupper(*op)) ? tolower(*op) : *op;
233 		found = 0;
234 		for (t=0; acl_op_table[t].ao_op; t++) {
235 		    if (rop == acl_op_table[t].ao_op) {
236 			found = 1;
237 			if (rop == *op)
238 			    acle->ae_op_allowed |= acl_op_table[t].ao_mask;
239 			else
240 			    acle->ae_op_allowed &= ~acl_op_table[t].ao_mask;
241 		    }
242 		}
243 		if (!found) {
244 		    krb5_klog_syslog(LOG_ERR, ACL_OP_BAD_MSG, *op, lp);
245 		    opok = 0;
246 		}
247 	    }
248 	    if (opok) {
249 		acle->ae_name = (char *) malloc(strlen(acle_principal)+1);
250 		if (acle->ae_name) {
251 		    strcpy(acle->ae_name, acle_principal);
252 		    acle->ae_principal = (krb5_principal) NULL;
253 		    acle->ae_name_bad = 0;
254 		    DPRINT(DEBUG_ACL, acl_debug_level,
255 			   ("A ACL entry %s -> opmask %x\n",
256 			    acle->ae_name, acle->ae_op_allowed));
257 		}
258 		else {
259 		    if (acle->ae_target)
260 			free(acle->ae_target);
261 		    free(acle);
262 		    acle = (aent_t *) NULL;
263 		}
264 	    }
265 	    else {
266 		if (acle->ae_target)
267 		    free(acle->ae_target);
268 		free(acle);
269 		acle = (aent_t *) NULL;
270 	    }
271 	    if ( nmatch >= 4 ) {
272 		char	*trailing;
273 
274 		trailing = &acle_restrictions[strlen(acle_restrictions)-1];
275 		while ( isspace(*trailing) )
276 		    trailing--;
277 		trailing[1] = '\0';
278 		acle->ae_restriction_string = strdup(acle_restrictions);
279 	    }
280 	    else {
281 		acle->ae_restriction_string = (char *) NULL;
282 	    }
283 	    acle->ae_restriction_bad = 0;
284 	    acle->ae_restrictions = (restriction_t *) NULL;
285 	}
286     }
287     DPRINT(DEBUG_CALLS, acl_debug_level,
288 	   ("X acl_parse_line() = %x\n", (long) acle));
289     return(acle);
290 }
291 
292 /*
293  * acl_parse_restrictions()	- Parse optional restrictions field
294  *
295  * Allowed restrictions are:
296  *	[+-]flagname		(recognized by krb5_string_to_flags)
297  *				flag is forced to indicated value
298  *	-clearpolicy		policy is forced clear
299  *	-policy pol		policy is forced to be "pol"
300  *	-{expire,pwexpire,maxlife,maxrenewlife} deltat
301  *				associated value will be forced to
302  *				MIN(deltat, requested value)
303  *
304  * Returns: 0 on success, or system errors
305  */
306 static krb5_error_code
307 acl_parse_restrictions(s, rpp)
308     char		*s;
309     restriction_t	**rpp;
310 {
311     char		*sp, *tp, *ap;
312     static const char	*delims = "\t\n\f\v\r ,";
313     krb5_error_code	ret;
314     krb5_deltat		dt;
315     krb5_flags		flag;
316     krb5_error_code	code;
317 
318    DPRINT(DEBUG_CALLS, acl_debug_level,
319 	   ("* acl_parse_restrictions(s=%20s, rpp=0x%08x)\n", s, (long)rpp));
320 
321     *rpp = (restriction_t *) NULL;
322     code = 0;
323     if (s)
324 	if (!(sp = strdup(s))	/* Don't munge the original */
325 	    || !(*rpp = (restriction_t *) malloc(sizeof(restriction_t)))) {
326 	    code = ENOMEM;
327 	} else {
328 	    memset(*rpp, 0, sizeof(**rpp));
329 	    for (tp=strtok(sp, delims); tp; tp=strtok((char *)NULL, delims)) {
330 		flag = 0;
331 		if (!krb5_string_to_flags(tp, "+", "-", &flag)) {
332 		    /* OK, but was it in the positive or negative sense? */
333 		    if (flag) {
334 			(*rpp)->require_attrs |= flag;
335 		    } else {
336 			flag = ~0;
337 			(void) krb5_string_to_flags(tp, "+", "-", &flag);
338 			(*rpp)->forbid_attrs |= ~flag;
339 		    }
340 		    (*rpp)->mask |= KADM5_ATTRIBUTES;
341 		} else if (!strcmp(tp, "-clearpolicy")) {
342 		    (*rpp)->mask |= KADM5_POLICY_CLR;
343 		} else {
344 		    /* everything else needs an argument ... */
345 		    if (!(ap = strtok((char *)NULL, delims))) {
346 			code = EINVAL;
347 			break;
348 		    }
349 		    if (!strcmp(tp, "-policy")) {
350 			if (!((*rpp)->policy = strdup(ap))) {
351 			    code = ENOMEM;
352 			    break;
353 			}
354 			(*rpp)->mask |= KADM5_POLICY;
355 		    } else {
356 			/* all other arguments must be a deltat ... */
357 			if (krb5_string_to_deltat(ap, &dt)) {
358 			    code = EINVAL;
359 			    break;
360 			}
361 			if (!strcmp(tp, "-expire")) {
362 			    (*rpp)->princ_lifetime = dt;
363 			    (*rpp)->mask |= KADM5_PRINC_EXPIRE_TIME;
364 			} else if (!strcmp(tp, "-pwexpire")) {
365 			    (*rpp)->pw_lifetime = dt;
366 			    (*rpp)->mask |= KADM5_PW_EXPIRATION;
367 			} else if (!strcmp(tp, "-maxlife")) {
368 			    (*rpp)->max_life = dt;
369 			    (*rpp)->mask |= KADM5_MAX_LIFE;
370 			} else if (!strcmp(tp, "-maxrenewlife")) {
371 			    (*rpp)->max_renewable_life = dt;
372 			    (*rpp)->mask |= KADM5_MAX_RLIFE;
373 			} else {
374 			    code = EINVAL;
375 			    break;
376 			}
377 		    }
378 		}
379 	    }
380 	}
381     if (sp)
382 	free(sp);
383     if (*rpp && code) {
384 	if ((*rpp)->policy)
385 	    free((*rpp)->policy);
386 	free(*rpp);
387 	*rpp = (restriction_t *) NULL;
388     }
389     DPRINT(DEBUG_CALLS, acl_debug_level,
390 	   ("X acl_parse_restrictions() = %d, mask=0x%08x\n",
391 	    code, (*rpp) ? (*rpp)->mask : 0));
392     return code;
393 }
394 
395 /*
396  * acl_impose_restrictions()	- impose restrictions, modifying *recp, *maskp
397  *
398  * Returns: 0 on success;
399  *	    malloc or timeofday errors
400  */
401 krb5_error_code
402 acl_impose_restrictions(kcontext, recp, maskp, rp)
403      krb5_context		kcontext;
404      kadm5_principal_ent_rec	*recp;
405      long			*maskp;
406      restriction_t		*rp;
407 {
408     krb5_error_code	code;
409     krb5_int32		now;
410 
411     DPRINT(DEBUG_CALLS, acl_debug_level,
412 	   ("* acl_impose_restrictions(..., *maskp=0x%08x, rp=0x%08x)\n",
413 	    *maskp, (long)rp));
414     if (!rp)
415 	return 0;
416     if (rp->mask & (KADM5_PRINC_EXPIRE_TIME|KADM5_PW_EXPIRATION))
417 	if ((code = krb5_timeofday(kcontext, &now)))
418 	    return code;
419 
420     if (rp->mask & KADM5_ATTRIBUTES) {
421 	recp->attributes |= rp->require_attrs;
422 	recp->attributes &= ~(rp->forbid_attrs);
423 	*maskp |= KADM5_ATTRIBUTES;
424     }
425     if (rp->mask & KADM5_POLICY_CLR) {
426 	*maskp &= ~KADM5_POLICY;
427 	*maskp |= KADM5_POLICY_CLR;
428     } else if (rp->mask & KADM5_POLICY) {
429 	if (recp->policy && strcmp(recp->policy, rp->policy)) {
430 		free(recp->policy);
431 		recp->policy = (char *) NULL;
432 	}
433 	if (!recp->policy) {
434 	    recp->policy = strdup(rp->policy);  /* XDR will free it */
435 	    if (!recp->policy)
436 		return ENOMEM;
437 	}
438 	*maskp |= KADM5_POLICY;
439     }
440     if (rp->mask & KADM5_PRINC_EXPIRE_TIME) {
441 	if (!(*maskp & KADM5_PRINC_EXPIRE_TIME)
442 	    || (recp->princ_expire_time > (now + rp->princ_lifetime)))
443 	    recp->princ_expire_time = now + rp->princ_lifetime;
444 	*maskp |= KADM5_PRINC_EXPIRE_TIME;
445     }
446     if (rp->mask & KADM5_PW_EXPIRATION) {
447 	if (!(*maskp & KADM5_PW_EXPIRATION)
448 	    || (recp->pw_expiration > (now + rp->pw_lifetime)))
449 	    recp->pw_expiration = now + rp->pw_lifetime;
450 	*maskp |= KADM5_PW_EXPIRATION;
451     }
452     if (rp->mask & KADM5_MAX_LIFE) {
453 	if (!(*maskp & KADM5_MAX_LIFE)
454 	    || (recp->max_life > rp->max_life))
455 	    recp->max_life = rp->max_life;
456 	*maskp |= KADM5_MAX_LIFE;
457     }
458     if (rp->mask & KADM5_MAX_RLIFE) {
459 	if (!(*maskp & KADM5_MAX_RLIFE)
460 	    || (recp->max_renewable_life > rp->max_renewable_life))
461 	    recp->max_renewable_life = rp->max_renewable_life;
462 	*maskp |= KADM5_MAX_RLIFE;
463     }
464     DPRINT(DEBUG_CALLS, acl_debug_level,
465 	   ("X acl_impose_restrictions() = 0, *maskp=0x%08x\n", *maskp));
466     return 0;
467 }
468 
469 /*
470  * acl_free_entries()	- Free all ACL entries.
471  */
472 static void
473 acl_free_entries()
474 {
475     aent_t	*ap;
476     aent_t	*np;
477 
478     DPRINT(DEBUG_CALLS, acl_debug_level, ("* acl_free_entries()\n"));
479     for (ap=acl_list_head; ap; ap = np) {
480 	if (ap->ae_name)
481 	    free(ap->ae_name);
482 	if (ap->ae_principal)
483 	    krb5_free_principal((krb5_context) NULL, ap->ae_principal);
484 	if (ap->ae_target)
485 	    free(ap->ae_target);
486 	if (ap->ae_target_princ)
487 	    krb5_free_principal((krb5_context) NULL, ap->ae_target_princ);
488 	if (ap->ae_restriction_string)
489 	    free(ap->ae_restriction_string);
490 	if (ap->ae_restrictions) {
491 	    if (ap->ae_restrictions->policy)
492 		free(ap->ae_restrictions->policy);
493 	    free(ap->ae_restrictions);
494 	}
495 	np = ap->ae_next;
496 	free(ap);
497     }
498     acl_list_head = acl_list_tail = (aent_t *) NULL;
499     acl_inited = 0;
500     DPRINT(DEBUG_CALLS, acl_debug_level, ("X acl_free_entries()\n"));
501 }
502 
503 /*
504  * acl_load_acl_file()	- Open and parse the ACL file.
505  */
506 static int
507 acl_load_acl_file()
508 {
509     FILE 	*afp;
510     char 	*alinep;
511     aent_t	**aentpp;
512     int		alineno;
513     int		retval = 1;
514 
515     DPRINT(DEBUG_CALLS, acl_debug_level, ("* acl_load_acl_file()\n"));
516     /* Open the ACL file for read */
517     if (afp = fopen(acl_acl_file, "r")) {
518 	alineno = 1;
519 	aentpp = &acl_list_head;
520 
521 	/* Get a non-comment line */
522 	while (alinep = acl_get_line(afp, &alineno)) {
523 	    /* Parse it */
524 	    *aentpp = acl_parse_line(alinep);
525 	    /* If syntax error, then fall out */
526 	    if (!*aentpp) {
527 		krb5_klog_syslog(LOG_ERR, ACL_SYN_ERR_MSG,
528 			acl_acl_file, alineno, alinep);
529 		retval = 0;
530 		break;
531 	    }
532 	    acl_list_tail = *aentpp;
533 	    aentpp = &(*aentpp)->ae_next;
534 	}
535 
536 	fclose(afp);
537 
538 	if (acl_catchall_entry) {
539 	     if (*aentpp = acl_parse_line(acl_catchall_entry)) {
540 		  acl_list_tail = *aentpp;
541 	     }
542 	     else {
543 		  retval = 0;
544 		  DPRINT(DEBUG_OPERATION, acl_debug_level,
545 			 ("> catchall acl entry (%s) load failed\n",
546 			  acl_catchall_entry));
547 	     }
548 	}
549     }
550     else {
551 	krb5_klog_syslog(LOG_ERR,  ACL_CANTOPEN_MSG,
552 			 error_message(errno), acl_acl_file);
553 	if (acl_catchall_entry &&
554 	    (acl_list_head = acl_parse_line((char *)acl_catchall_entry))) {
555 	    acl_list_tail = acl_list_head;
556 	}
557 	else {
558 	    retval = 0;
559 	    DPRINT(DEBUG_OPERATION, acl_debug_level,
560 		   ("> catchall acl entry (%s) load failed\n",
561 		    acl_catchall_entry));
562 	}
563     }
564 
565     if (!retval) {
566 	acl_free_entries();
567     }
568     DPRINT(DEBUG_CALLS, acl_debug_level,
569 	   ("X acl_load_acl_file() = %d\n", retval));
570     return(retval);
571 }
572 
573 /*
574  * acl_match_data()	- See if two data entries match.
575  *
576  * Wildcarding is only supported for a whole component.
577  */
578 static krb5_boolean
579 acl_match_data(e1, e2, targetflag, ws)
580     krb5_data	*e1, *e2;
581     int		targetflag;
582     wildstate_t	*ws;
583 {
584     krb5_boolean	retval;
585 
586     DPRINT(DEBUG_CALLS, acl_debug_level,
587 	   ("* acl_match_entry(%s, %s)\n", e1->data, e2->data));
588     retval = 0;
589     if (!strncmp(e1->data, "*", e1->length)) {
590 	retval = 1;
591 	if (ws && !targetflag) {
592 	    if (ws->nwild >= 9) {
593 		DPRINT(DEBUG_ACL, acl_debug_level,
594 			("Too many wildcards in ACL entry %s\n", e1->data));
595 	    }
596 	    else
597 		ws->backref[ws->nwild++] = e2;
598 	}
599     }
600     else if (ws && targetflag && (e1->length == 2) && (e1->data[0] == '*') &&
601 	     (e1->data[1] >= '1') && (e1->data[1] <= '9')) {
602 	int	n = e1->data[1] - '1';
603 	if (n >= ws->nwild) {
604 	    DPRINT(DEBUG_ACL, acl_debug_level,
605 		    ("Too many backrefs in ACL entry %s\n", e1->data));
606 	}
607 	else if ((ws->backref[n]->length == e2->length) &&
608 		 (!strncmp(ws->backref[n]->data, e2->data, e2->length)))
609 	    retval = 1;
610 
611     }
612     else {
613 	if ((e1->length == e2->length) &&
614 	    (!strncmp(e1->data, e2->data, e1->length)))
615 	    retval = 1;
616     }
617     DPRINT(DEBUG_CALLS, acl_debug_level, ("X acl_match_entry()=%d\n",retval));
618     return(retval);
619 }
620 
621 /*
622  * acl_find_entry()	- Find a matching entry.
623  */
624 static aent_t *
625 acl_find_entry(kcontext, principal, dest_princ)
626     krb5_context	kcontext;
627     krb5_principal	principal;
628     krb5_principal	dest_princ;
629 {
630     aent_t		*entry;
631     krb5_error_code	kret;
632     int			i;
633     int			matchgood;
634     wildstate_t		state;
635 
636     DPRINT(DEBUG_CALLS, acl_debug_level, ("* acl_find_entry()\n"));
637     memset((char *)&state, 0, sizeof state);
638     for (entry=acl_list_head; entry; entry = entry->ae_next) {
639 	if (entry->ae_name_bad)
640 	    continue;
641 	if (!strcmp(entry->ae_name, "*")) {
642 	    DPRINT(DEBUG_ACL, acl_debug_level, ("A wildcard ACL match\n"));
643 	    matchgood = 1;
644 	}
645 	else {
646 	    if (!entry->ae_principal && !entry->ae_name_bad) {
647 		kret = krb5_parse_name(kcontext,
648 				       entry->ae_name,
649 				       &entry->ae_principal);
650 		if (kret)
651 		    entry->ae_name_bad = 1;
652 	    }
653 	    if (entry->ae_name_bad) {
654 		DPRINT(DEBUG_ACL, acl_debug_level,
655 		       ("Bad ACL entry %s\n", entry->ae_name));
656 		continue;
657 	    }
658 	    matchgood = 0;
659 	    if (acl_match_data(&entry->ae_principal->realm,
660 			       &principal->realm, 0, (wildstate_t *)0) &&
661 		(entry->ae_principal->length == principal->length)) {
662 		matchgood = 1;
663 		for (i=0; i<principal->length; i++) {
664 		    if (!acl_match_data(&entry->ae_principal->data[i],
665 					&principal->data[i], 0, &state)) {
666 			matchgood = 0;
667 			break;
668 		    }
669 		}
670 	    }
671 	}
672 	if (!matchgood)
673 	    continue;
674 
675 	/* We've matched the principal.  If we have a target, then try it */
676 	if (entry->ae_target) {
677 	    if (!strcmp(entry->ae_target, "*"))
678 		break;
679 	    if (!entry->ae_target_princ && !entry->ae_target_bad) {
680 		kret = krb5_parse_name(kcontext, entry->ae_target,
681 				       &entry->ae_target_princ);
682 		if (kret)
683 		    entry->ae_target_bad = 1;
684 	    }
685 	}
686 	if (entry->ae_target_bad) {
687 	    DPRINT(DEBUG_ACL, acl_debug_level,
688 		   ("Bad target in ACL entry for %s\n", entry->ae_name));
689 	    entry->ae_name_bad = 1;
690 	    continue;
691 	}
692 	if (entry->ae_target && !dest_princ)
693 	    matchgood = 0;
694 	else if (entry->ae_target && entry->ae_target_princ && dest_princ) {
695 	    if (acl_match_data(&entry->ae_target_princ->realm,
696 			       &dest_princ->realm, 1, (wildstate_t *)0) &&
697 		(entry->ae_target_princ->length == dest_princ->length)) {
698 		for (i=0; i<dest_princ->length; i++) {
699 		    if (!acl_match_data(&entry->ae_target_princ->data[i],
700 					&dest_princ->data[i], 1, &state)) {
701 			matchgood = 0;
702 			break;
703 		    }
704 		}
705 	    }
706 	    else
707 		matchgood = 0;
708 	}
709 	if (!matchgood)
710 	    continue;
711 
712 	if (entry->ae_restriction_string
713 	    && !entry->ae_restriction_bad
714 	    && !entry->ae_restrictions
715 	    && acl_parse_restrictions(entry->ae_restriction_string,
716 				      &entry->ae_restrictions)) {
717 	    DPRINT(DEBUG_ACL, acl_debug_level,
718 		   ("Bad restrictions in ACL entry for %s\n", entry->ae_name));
719 	    entry->ae_restriction_bad = 1;
720 	}
721 	if (entry->ae_restriction_bad) {
722 	    entry->ae_name_bad = 1;
723 	    continue;
724 	}
725 	break;
726     }
727     DPRINT(DEBUG_CALLS, acl_debug_level, ("X acl_find_entry()=%x\n",entry));
728     return(entry);
729 }
730 
731 /*
732  * acl_init()	- Initialize ACL context.
733  */
734 krb5_error_code
735 acl_init(kcontext, debug_level, acl_file)
736     krb5_context	kcontext;
737     int			debug_level;
738     char		*acl_file;
739 {
740     krb5_error_code	kret;
741 
742     kret = 0;
743     acl_debug_level = debug_level;
744     DPRINT(DEBUG_CALLS, acl_debug_level,
745 	   ("* acl_init(afile=%s)\n",
746 	    ((acl_file) ? acl_file : "(null)")));
747     acl_acl_file = (acl_file) ? acl_file : (char *) KRB5_DEFAULT_ADMIN_ACL;
748     acl_inited = acl_load_acl_file();
749 
750     DPRINT(DEBUG_CALLS, acl_debug_level, ("X acl_init() = %d\n", kret));
751     return(kret);
752 }
753 
754 /*
755  * acl_finish	- Terminate ACL context.
756  */
757 void
758 acl_finish(kcontext, debug_level)
759     krb5_context	kcontext;
760     int			debug_level;
761 {
762     DPRINT(DEBUG_CALLS, acl_debug_level, ("* acl_finish()\n"));
763     acl_free_entries();
764     DPRINT(DEBUG_CALLS, acl_debug_level, ("X acl_finish()\n"));
765 }
766 
767 /*
768  * acl_check()	- Is this operation permitted for this principal?
769  *			this code used not to be based on gssapi.  In order
770  *			to minimize porting hassles, I've put all the
771  *			gssapi hair in this function.  This might not be
772  *			the best medium-term solution.  (The best long-term
773  *			solution is, of course, a real authorization service.)
774  */
775 krb5_boolean
776 acl_check(kcontext, caller, opmask, principal, restrictions)
777     krb5_context	kcontext;
778     gss_name_t		caller;
779     krb5_int32		opmask;
780     krb5_principal	principal;
781     restriction_t	**restrictions;
782 {
783     krb5_boolean	retval;
784     aent_t		*aentry;
785     gss_buffer_desc	caller_buf;
786     gss_OID		caller_oid;
787     OM_uint32		emaj, emin;
788     krb5_error_code	code;
789     krb5_principal	caller_princ;
790 
791     DPRINT(DEBUG_CALLS, acl_debug_level, ("* acl_op_permitted()\n"));
792 
793     if (restrictions)
794         *restrictions = NULL;
795 
796     if (GSS_ERROR(emaj = gss_display_name(&emin, caller, &caller_buf,
797 					  &caller_oid)))
798        return(1);
799 
800     code = krb5_parse_name(kcontext, (char *) caller_buf.value,
801 			   &caller_princ);
802 
803     gss_release_buffer(&emin, &caller_buf);
804 
805     if (code)
806        return(code);
807 
808     retval = 0;
809     if (aentry = acl_find_entry(kcontext, caller_princ, principal)) {
810 	if ((aentry->ae_op_allowed & opmask) == opmask) {
811 	    retval = 1;
812 	    if (restrictions) {
813 		*restrictions =
814 		    (aentry->ae_restrictions && aentry->ae_restrictions->mask)
815 		    ? aentry->ae_restrictions
816 		    : (restriction_t *) NULL;
817 	    }
818 	}
819     }
820 
821     krb5_free_principal(kcontext, caller_princ);
822 
823     DPRINT(DEBUG_CALLS, acl_debug_level, ("X acl_op_permitted()=%d\n",
824 					  retval));
825     return(retval);
826 }
827 
828 kadm5_ret_t
829 kadm5_get_privs(void *server_handle, long *privs)
830 {
831      kadm5_server_handle_t handle = server_handle;
832 
833      CHECK_HANDLE(server_handle);
834 
835      /* this is impossible to do with the current interface.  For now,
836 	return all privs, which will confuse some clients, but not
837 	deny any access to users of "smart" clients which try to cache */
838 
839      *privs = ~0;
840 
841      return KADM5_OK;
842 }
843 
844 /* SUNWresync121 (SEAM1.0) XXX */
845 kadm5_ret_t
846 __kadm5_get_priv(void *server_handle, long *privs, gss_name_t client)
847 {
848 
849 	aent_t		*aentry;
850 	gss_buffer_desc	caller_buff;
851 	gss_OID		caller_oid;
852 	krb5_principal	caller_principal;
853 	OM_uint32	minor, major;
854 	krb5_error_code	k_error;
855 	kadm5_ret_t	retval = KADM5_FAILURE;
856 
857      kadm5_server_handle_t handle = server_handle;
858 
859      CHECK_HANDLE(server_handle);
860 
861 	if (GSS_ERROR(major = gss_display_name(&minor, client, &caller_buff,
862 						&caller_oid)))
863 		return(retval);
864 	k_error = krb5_parse_name(handle->context,
865 					(char *) caller_buff.value,
866 					&caller_principal);
867 	gss_release_buffer(&minor, &caller_buff);
868 
869 	if (k_error)
870 		return(retval);
871 
872 	if (aentry = acl_find_entry(handle->context, caller_principal,
873 					(krb5_principal)NULL))
874 		*privs = aentry->ae_op_allowed;
875 	krb5_free_principal(handle->context, caller_principal);
876 
877 	return (KADM5_OK);
878 }
879