xref: /illumos-gate/usr/src/lib/krb5/kadm5/srv/server_acl.c (revision ba2be53024c0b999e74ba9adcd7d80fec5df8c57)
1 /*
2  * Copyright 2006 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  * kadm5int_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 kadm5int_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  * kadm5int_acl_parse_line() - Parse the contents of an ACL line.
194  */
195 static aent_t *
196 kadm5int_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 	   ("* kadm5int_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((int) *op)) ? tolower((int) *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((int) *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 kadm5int_acl_parse_line() = %x\n", (long) acle));
289     return(acle);
290 }
291 
292 /*
293  * kadm5int_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 kadm5int_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_deltat		dt;
314     krb5_flags		flag;
315     krb5_error_code	code;
316 
317    DPRINT(DEBUG_CALLS, acl_debug_level,
318 	   ("* kadm5int_acl_parse_restrictions(s=%20s, rpp=0x%08x)\n", s, (long)rpp));
319 
320     *rpp = (restriction_t *) NULL;
321     code = 0;
322     if (s) {
323 	if (!(sp = strdup(s))	/* Don't munge the original */
324 	    || !(*rpp = (restriction_t *) malloc(sizeof(restriction_t)))) {
325 	    code = ENOMEM;
326 	} else {
327 	    memset(*rpp, 0, sizeof(**rpp));
328 	    for (tp=strtok(sp, delims); tp; tp=strtok((char *)NULL, delims)) {
329 		flag = 0;
330 		if (!krb5_string_to_flags(tp, "+", "-", &flag)) {
331 		    /* OK, but was it in the positive or negative sense? */
332 		    if (flag) {
333 			(*rpp)->require_attrs |= flag;
334 		    } else {
335 			flag = ~0;
336 			(void) krb5_string_to_flags(tp, "+", "-", &flag);
337 			(*rpp)->forbid_attrs |= ~flag;
338 		    }
339 		    (*rpp)->mask |= KADM5_ATTRIBUTES;
340 		} else if (!strcmp(tp, "-clearpolicy")) {
341 		    (*rpp)->mask |= KADM5_POLICY_CLR;
342 		} else {
343 		    /* everything else needs an argument ... */
344 		    if (!(ap = strtok((char *)NULL, delims))) {
345 			code = EINVAL;
346 			break;
347 		    }
348 		    if (!strcmp(tp, "-policy")) {
349 			if (!((*rpp)->policy = strdup(ap))) {
350 			    code = ENOMEM;
351 			    break;
352 			}
353 			(*rpp)->mask |= KADM5_POLICY;
354 		    } else {
355 			/* all other arguments must be a deltat ... */
356 			if (krb5_string_to_deltat(ap, &dt)) {
357 			    code = EINVAL;
358 			    break;
359 			}
360 			if (!strcmp(tp, "-expire")) {
361 			    (*rpp)->princ_lifetime = dt;
362 			    (*rpp)->mask |= KADM5_PRINC_EXPIRE_TIME;
363 			} else if (!strcmp(tp, "-pwexpire")) {
364 			    (*rpp)->pw_lifetime = dt;
365 			    (*rpp)->mask |= KADM5_PW_EXPIRATION;
366 			} else if (!strcmp(tp, "-maxlife")) {
367 			    (*rpp)->max_life = dt;
368 			    (*rpp)->mask |= KADM5_MAX_LIFE;
369 			} else if (!strcmp(tp, "-maxrenewlife")) {
370 			    (*rpp)->max_renewable_life = dt;
371 			    (*rpp)->mask |= KADM5_MAX_RLIFE;
372 			} else {
373 			    code = EINVAL;
374 			    break;
375 			}
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 kadm5int_acl_parse_restrictions() = %d, mask=0x%08x\n",
391 	    code, (*rpp) ? (*rpp)->mask : 0));
392     return code;
393 }
394 
395 /*
396  * kadm5int_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 kadm5int_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 	   ("* kadm5int_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 kadm5int_acl_impose_restrictions() = 0, *maskp=0x%08x\n", *maskp));
466     return 0;
467 }
468 
469 /*
470  * kadm5int_acl_free_entries() - Free all ACL entries.
471  */
472 static void
473 kadm5int_acl_free_entries()
474 {
475     aent_t	*ap;
476     aent_t	*np;
477 
478     DPRINT(DEBUG_CALLS, acl_debug_level, ("* kadm5int_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 kadm5int_acl_free_entries()\n"));
501 }
502 
503 /*
504  * kadm5int_acl_load_acl_file()	- Open and parse the ACL file.
505  */
506 static int
507 kadm5int_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, ("* kadm5int_acl_load_acl_file()\n"));
516     /* Open the ACL file for read */
517     afp = fopen(acl_acl_file, "rF");
518     if (afp) {
519 	alineno = 1;
520 	aentpp = &acl_list_head;
521 
522 	/* Get a non-comment line */
523 	while ((alinep = kadm5int_acl_get_line(afp, &alineno))) {
524 	    /* Parse it */
525 	    *aentpp = kadm5int_acl_parse_line(alinep);
526 	    /* If syntax error, then fall out */
527 	    if (!*aentpp) {
528 		krb5_klog_syslog(LOG_ERR, ACL_SYN_ERR_MSG,
529 			acl_acl_file, alineno, alinep);
530 		retval = 0;
531 		break;
532 	    }
533 	    acl_list_tail = *aentpp;
534 	    aentpp = &(*aentpp)->ae_next;
535 	}
536 
537 	fclose(afp);
538 
539 	if (acl_catchall_entry) {
540 	     *aentpp = kadm5int_acl_parse_line(acl_catchall_entry);
541 	     if (*aentpp) {
542 		  acl_list_tail = *aentpp;
543 	     }
544 	     else {
545 		  retval = 0;
546 		  DPRINT(DEBUG_OPERATION, acl_debug_level,
547 			 ("> catchall acl entry (%s) load failed\n",
548 			  acl_catchall_entry));
549 	     }
550 	}
551     }
552     else {
553 	krb5_klog_syslog(LOG_ERR,  ACL_CANTOPEN_MSG,
554 			 error_message(errno), acl_acl_file);
555 	if (acl_catchall_entry &&
556 	    (acl_list_head = kadm5int_acl_parse_line((char *)acl_catchall_entry))) {
557 	    acl_list_tail = acl_list_head;
558 	}
559 	else {
560 	    retval = 0;
561 	    DPRINT(DEBUG_OPERATION, acl_debug_level,
562 		   ("> catchall acl entry (%s) load failed\n",
563 		    acl_catchall_entry));
564 	}
565     }
566 
567     if (!retval) {
568 	kadm5int_acl_free_entries();
569     }
570     DPRINT(DEBUG_CALLS, acl_debug_level,
571 	   ("X kadm5int_acl_load_acl_file() = %d\n", retval));
572     return(retval);
573 }
574 
575 /*
576  * kadm5int_acl_match_data()	- See if two data entries match.
577  *
578  * Wildcarding is only supported for a whole component.
579  */
580 static krb5_boolean
581 kadm5int_acl_match_data(e1, e2, targetflag, ws)
582     krb5_data	*e1, *e2;
583     int		targetflag;
584     wildstate_t	*ws;
585 {
586     krb5_boolean	retval;
587 
588     DPRINT(DEBUG_CALLS, acl_debug_level,
589 	   ("* acl_match_entry(%s, %s)\n", e1->data, e2->data));
590     retval = 0;
591     if (!strncmp(e1->data, "*", e1->length)) {
592 	retval = 1;
593 	if (ws && !targetflag) {
594 	    if (ws->nwild >= 9) {
595 		DPRINT(DEBUG_ACL, acl_debug_level,
596 		    ("Too many wildcards in ACL entry %s\n", e1->data));
597 	    }
598 	    else
599 		ws->backref[ws->nwild++] = e2;
600 	}
601     }
602     else if (ws && targetflag && (e1->length == 2) && (e1->data[0] == '*') &&
603 	     (e1->data[1] >= '1') && (e1->data[1] <= '9')) {
604 	int	n = e1->data[1] - '1';
605 	if (n >= ws->nwild) {
606 	    DPRINT(DEBUG_ACL, acl_debug_level,
607 		   ("Too many backrefs in ACL entry %s\n", e1->data));
608 	}
609 	else if ((ws->backref[n]->length == e2->length) &&
610 		 (!strncmp(ws->backref[n]->data, e2->data, e2->length)))
611 	    retval = 1;
612 
613     }
614     else {
615 	if ((e1->length == e2->length) &&
616 	    (!strncmp(e1->data, e2->data, e1->length)))
617 	    retval = 1;
618     }
619     DPRINT(DEBUG_CALLS, acl_debug_level, ("X acl_match_entry()=%d\n",retval));
620     return(retval);
621 }
622 
623 /*
624  * kadm5int_acl_find_entry()	- Find a matching entry.
625  */
626 static aent_t *
627 kadm5int_acl_find_entry(kcontext, principal, dest_princ)
628     krb5_context	kcontext;
629     krb5_principal	principal;
630     krb5_principal	dest_princ;
631 {
632     aent_t		*entry;
633     krb5_error_code	kret;
634     int			i;
635     int			matchgood;
636     wildstate_t		state;
637 
638     DPRINT(DEBUG_CALLS, acl_debug_level, ("* kadm5int_acl_find_entry()\n"));
639     memset((char *)&state, 0, sizeof state);
640     for (entry=acl_list_head; entry; entry = entry->ae_next) {
641 	if (entry->ae_name_bad)
642 	    continue;
643 	if (!strcmp(entry->ae_name, "*")) {
644 	    DPRINT(DEBUG_ACL, acl_debug_level, ("A wildcard ACL match\n"));
645 	    matchgood = 1;
646 	}
647 	else {
648 	    if (!entry->ae_principal && !entry->ae_name_bad) {
649 		kret = krb5_parse_name(kcontext,
650 				       entry->ae_name,
651 				       &entry->ae_principal);
652 		if (kret)
653 		    entry->ae_name_bad = 1;
654 	    }
655 	    if (entry->ae_name_bad) {
656 		DPRINT(DEBUG_ACL, acl_debug_level,
657 		       ("Bad ACL entry %s\n", entry->ae_name));
658 		continue;
659 	    }
660 	    matchgood = 0;
661 	    if (kadm5int_acl_match_data(&entry->ae_principal->realm,
662 			       &principal->realm, 0, (wildstate_t *)0) &&
663 		(entry->ae_principal->length == principal->length)) {
664 		matchgood = 1;
665 		for (i=0; i<principal->length; i++) {
666 		    if (!kadm5int_acl_match_data(&entry->ae_principal->data[i],
667 					&principal->data[i], 0, &state)) {
668 			matchgood = 0;
669 			break;
670 		    }
671 		}
672 	    }
673 	}
674 	if (!matchgood)
675 	    continue;
676 
677 	/* We've matched the principal.  If we have a target, then try it */
678 	if (entry->ae_target && strcmp(entry->ae_target, "*")) {
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 	    if (entry->ae_target_bad) {
686 	        DPRINT(DEBUG_ACL, acl_debug_level,
687 		       ("Bad target in ACL entry for %s\n", entry->ae_name));
688 	        entry->ae_name_bad = 1;
689 	        continue;
690 	    }
691 	    if (!dest_princ)
692 	        matchgood = 0;
693 	    else if (entry->ae_target_princ && dest_princ) {
694 	        if (kadm5int_acl_match_data(&entry->ae_target_princ->realm,
695 			           &dest_princ->realm, 1, (wildstate_t *)0) &&
696 		    (entry->ae_target_princ->length == dest_princ->length)) {
697 		    for (i=0; i<dest_princ->length; i++) {
698 		        if (!kadm5int_acl_match_data(&entry->ae_target_princ->data[i],
699 			  		    &dest_princ->data[i], 1, &state)) {
700 			    matchgood = 0;
701 			    break;
702 		        }
703 		    }
704 	        }
705 	        else
706 		    matchgood = 0;
707 	    }
708         }
709 	if (!matchgood)
710 	    continue;
711 
712 	if (entry->ae_restriction_string
713 	    && !entry->ae_restriction_bad
714 	    && !entry->ae_restrictions
715 	    && kadm5int_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 kadm5int_acl_find_entry()=%x\n",entry));
728     return(entry);
729 }
730 
731 /*
732  * kadm5int_acl_init()	- Initialize ACL context.
733  */
734 krb5_error_code
735 kadm5int_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 	   ("* kadm5int_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 = kadm5int_acl_load_acl_file();
749 
750     DPRINT(DEBUG_CALLS, acl_debug_level, ("X kadm5int_acl_init() = %d\n", kret));
751     return(kret);
752 }
753 
754 /*
755  * kadm5int_acl_finish	- Terminate ACL context.
756  */
757 void
758 kadm5int_acl_finish(kcontext, debug_level)
759     krb5_context	kcontext;
760     int			debug_level;
761 {
762     DPRINT(DEBUG_CALLS, acl_debug_level, ("* kadm5int_acl_finish()\n"));
763     kadm5int_acl_free_entries();
764     DPRINT(DEBUG_CALLS, acl_debug_level, ("X kadm5int_acl_finish()\n"));
765 }
766 
767 /*
768  * kadm5int_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 kadm5int_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 
810     aentry = kadm5int_acl_find_entry(kcontext, caller_princ, principal);
811     if (aentry) {
812 	if ((aentry->ae_op_allowed & opmask) == opmask) {
813 	    retval = 1;
814 	    if (restrictions) {
815 		*restrictions =
816 		    (aentry->ae_restrictions && aentry->ae_restrictions->mask)
817 		    ? aentry->ae_restrictions
818 		    : (restriction_t *) NULL;
819 	    }
820 	}
821     }
822 
823     krb5_free_principal(kcontext, caller_princ);
824 
825     DPRINT(DEBUG_CALLS, acl_debug_level, ("X acl_op_permitted()=%d\n",
826 					  retval));
827     return(retval);
828 }
829 
830 kadm5_ret_t
831 kadm5_get_privs(void *server_handle, long *privs)
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 = kadm5int_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