1 /*
2  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
3  * Use is subject to license terms.
4  */
5 
6 
7 /*
8  * WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING
9  *
10  *	Openvision retains the copyright to derivative works of
11  *	this source code.  Do *NOT* create a derivative of this
12  *	source code before consulting with your legal department.
13  *	Do *NOT* integrate *ANY* of this source code into another
14  *	product before consulting with your legal department.
15  *
16  *	For further information, read the top-level Openvision
17  *	copyright which is contained in the top-level MIT Kerberos
18  *	copyright.
19  *
20  * WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING
21  *
22  */
23 
24 
25 /*
26  * kadmin/v5server/srv_acl.c
27  *
28  * Copyright 1995 by the Massachusetts Institute of Technology.
29  * All Rights Reserved.
30  *
31  * Export of this software from the United States of America may
32  *   require a specific license from the United States Government.
33  *   It is the responsibility of any person or organization contemplating
34  *   export to obtain such a license before exporting.
35  *
36  * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
37  * distribute this software and its documentation for any purpose and
38  * without fee is hereby granted, provided that the above copyright
39  * notice appear in all copies and that both that copyright notice and
40  * this permission notice appear in supporting documentation, and that
41  * the name of M.I.T. not be used in advertising or publicity pertaining
42  * to distribution of the software without specific, written prior
43  * permission.  Furthermore if you modify this software you must label
44  * your software as modified software and not distribute it in such a
45  * fashion that it might be confused with the original M.I.T. software.
46  * M.I.T. makes no representations about the suitability of
47  * this software for any purpose.  It is provided "as is" without express
48  * or implied warranty.
49  *
50  */
51 
52 /*
53  * srv_acl.c - Handle Kerberos ACL related functions.
54  */
55 #include <stdio.h>
56 #include <syslog.h>
57 #include <sys/param.h>
58 #include "k5-int.h"
59 #include <gssapi_krb5.h>
60 #include <kadm5/server_internal.h>
61 #include <kadm5/admin.h>
62 #include <adm_proto.h> /* SUNWresync121 XXX */
63 #include "server_acl.h"
64 #include <ctype.h>
65 #include <libintl.h> /* SUNWresync121 XXX */
66 
67 typedef struct _acl_op_table {
68     char	ao_op;
69     krb5_int32	ao_mask;
70 } aop_t;
71 
72 typedef struct _acl_entry {
73     struct _acl_entry	*ae_next;
74     char		*ae_name;
75     krb5_boolean	ae_name_bad;
76     krb5_principal	ae_principal;
77     krb5_int32		ae_op_allowed;
78     char		*ae_target;
79     krb5_boolean	ae_target_bad;
80     krb5_principal	ae_target_princ;
81     char		*ae_restriction_string;
82 			/* eg: "-maxlife 3h -service +proxiable" */
83     krb5_boolean	ae_restriction_bad;
84     restriction_t	*ae_restrictions;
85 } aent_t;
86 
87 static const aop_t acl_op_table[] = {
88     { 'a',	ACL_ADD },
89     { 'd',	ACL_DELETE },
90     { 'm',	ACL_MODIFY },
91     { 'c',	ACL_CHANGEPW },
92     { 'i',	ACL_INQUIRE },
93     { 'l',	ACL_LIST },
94     { 'p',	ACL_IPROP },		/* SUNW IProp */
95     { 's',	ACL_SETKEY },
96     { 'u',	ACL_MIGRATE },		/* pam_krb5_migrate */
97     { 'x',	ACL_ALL_MASK },
98     { '*',	ACL_ALL_MASK },
99     { '\0',	0 }
100 };
101 
102 typedef struct _wildstate {
103     int		nwild;
104     krb5_data	*backref[9];
105 } wildstate_t;
106 
107 static aent_t	*acl_list_head = (aent_t *) NULL;
108 static aent_t	*acl_list_tail = (aent_t *) NULL;
109 
110 static const char *acl_acl_file = (char *) NULL;
111 static int acl_inited = 0;
112 static int acl_debug_level = 0;
113 /*
114  * This is the catchall entry.  If nothing else appropriate is found, or in
115  * the case where the ACL file is not present, this entry controls what can
116  * be done.
117  */
118 static const char *acl_catchall_entry = NULL;
119 
120 /* Solaris Kerberos */
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 *
kadm5int_acl_get_line(fp,lnp)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 *
kadm5int_acl_parse_line(lp)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((unsigned char) *op)) ? tolower((unsigned char) *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
kadm5int_acl_parse_restrictions(s,rpp)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
kadm5int_acl_impose_restrictions(kcontext,recp,maskp,rp)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
kadm5int_acl_free_entries()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
kadm5int_acl_load_acl_file()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"); /* Solaris Kerberos */
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
kadm5int_acl_match_data(e1,e2,targetflag,ws)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 		/* Solaris Kerberos */
596 		DPRINT(DEBUG_ACL, acl_debug_level,
597 		    ("Too many wildcards in ACL entry %s\n", e1->data));
598 	    }
599 	    else
600 		ws->backref[ws->nwild++] = e2;
601 	}
602     }
603     else if (ws && targetflag && (e1->length == 2) && (e1->data[0] == '*') &&
604 	     (e1->data[1] >= '1') && (e1->data[1] <= '9')) {
605 	int	n = e1->data[1] - '1';
606 	if (n >= ws->nwild) {
607 	    /* Solaris Kerberos */
608 	    DPRINT(DEBUG_ACL, acl_debug_level,
609 		   ("Too many backrefs in ACL entry %s\n", e1->data));
610 	}
611 	else if ((ws->backref[n]->length == e2->length) &&
612 		 (!strncmp(ws->backref[n]->data, e2->data, e2->length)))
613 	    retval = 1;
614 
615     }
616     else {
617 	if ((e1->length == e2->length) &&
618 	    (!strncmp(e1->data, e2->data, e1->length)))
619 	    retval = 1;
620     }
621     DPRINT(DEBUG_CALLS, acl_debug_level, ("X acl_match_entry()=%d\n",retval));
622     return(retval);
623 }
624 
625 /*
626  * kadm5int_acl_find_entry()	- Find a matching entry.
627  */
628 static aent_t *
kadm5int_acl_find_entry(kcontext,principal,dest_princ)629 kadm5int_acl_find_entry(kcontext, principal, dest_princ)
630     krb5_context	kcontext;
631     krb5_principal	principal;
632     krb5_principal	dest_princ;
633 {
634     aent_t		*entry;
635     krb5_error_code	kret;
636     int			i;
637     int			matchgood;
638     wildstate_t		state;
639 
640     DPRINT(DEBUG_CALLS, acl_debug_level, ("* kadm5int_acl_find_entry()\n"));
641     memset((char *)&state, 0, sizeof state);
642     for (entry=acl_list_head; entry; entry = entry->ae_next) {
643 	if (entry->ae_name_bad)
644 	    continue;
645 	if (!strcmp(entry->ae_name, "*")) {
646 	    DPRINT(DEBUG_ACL, acl_debug_level, ("A wildcard ACL match\n"));
647 	    matchgood = 1;
648 	}
649 	else {
650 	    if (!entry->ae_principal && !entry->ae_name_bad) {
651 		kret = krb5_parse_name(kcontext,
652 				       entry->ae_name,
653 				       &entry->ae_principal);
654 		if (kret)
655 		    entry->ae_name_bad = 1;
656 	    }
657 	    if (entry->ae_name_bad) {
658 		DPRINT(DEBUG_ACL, acl_debug_level,
659 		       ("Bad ACL entry %s\n", entry->ae_name));
660 		continue;
661 	    }
662 	    matchgood = 0;
663 	    if (kadm5int_acl_match_data(&entry->ae_principal->realm,
664 			       &principal->realm, 0, (wildstate_t *)0) &&
665 		(entry->ae_principal->length == principal->length)) {
666 		matchgood = 1;
667 		for (i=0; i<principal->length; i++) {
668 		    if (!kadm5int_acl_match_data(&entry->ae_principal->data[i],
669 					&principal->data[i], 0, &state)) {
670 			matchgood = 0;
671 			break;
672 		    }
673 		}
674 	    }
675 	}
676 	if (!matchgood)
677 	    continue;
678 
679 	/* We've matched the principal.  If we have a target, then try it */
680 	if (entry->ae_target && strcmp(entry->ae_target, "*")) {
681 	    if (!entry->ae_target_princ && !entry->ae_target_bad) {
682 		kret = krb5_parse_name(kcontext, entry->ae_target,
683 				       &entry->ae_target_princ);
684 		if (kret)
685 		    entry->ae_target_bad = 1;
686 	    }
687 	    if (entry->ae_target_bad) {
688 	        DPRINT(DEBUG_ACL, acl_debug_level,
689 		       ("Bad target in ACL entry for %s\n", entry->ae_name));
690 	        entry->ae_name_bad = 1;
691 	        continue;
692 	    }
693 	    if (!dest_princ)
694 	        matchgood = 0;
695 	    else if (entry->ae_target_princ && dest_princ) {
696 	        if (kadm5int_acl_match_data(&entry->ae_target_princ->realm,
697 			           &dest_princ->realm, 1, (wildstate_t *)0) &&
698 		    (entry->ae_target_princ->length == dest_princ->length)) {
699 		    for (i=0; i<dest_princ->length; i++) {
700 		        if (!kadm5int_acl_match_data(&entry->ae_target_princ->data[i],
701 			  		    &dest_princ->data[i], 1, &state)) {
702 			    matchgood = 0;
703 			    break;
704 		        }
705 		    }
706 	        }
707 	        else
708 		    matchgood = 0;
709 	    }
710         }
711 	if (!matchgood)
712 	    continue;
713 
714 	if (entry->ae_restriction_string
715 	    && !entry->ae_restriction_bad
716 	    && !entry->ae_restrictions
717 	    && kadm5int_acl_parse_restrictions(entry->ae_restriction_string,
718 				      &entry->ae_restrictions)) {
719 	    DPRINT(DEBUG_ACL, acl_debug_level,
720 		   ("Bad restrictions in ACL entry for %s\n", entry->ae_name));
721 	    entry->ae_restriction_bad = 1;
722 	}
723 	if (entry->ae_restriction_bad) {
724 	    entry->ae_name_bad = 1;
725 	    continue;
726 	}
727 	break;
728     }
729     DPRINT(DEBUG_CALLS, acl_debug_level, ("X kadm5int_acl_find_entry()=%x\n",entry));
730     return(entry);
731 }
732 
733 /*
734  * kadm5int_acl_init()	- Initialize ACL context.
735  */
736 krb5_error_code
kadm5int_acl_init(kcontext,debug_level,acl_file)737 kadm5int_acl_init(kcontext, debug_level, acl_file)
738     krb5_context	kcontext;
739     int			debug_level;
740     char		*acl_file;
741 {
742     krb5_error_code	kret;
743 
744     kret = 0;
745     acl_debug_level = debug_level;
746     DPRINT(DEBUG_CALLS, acl_debug_level,
747 	   ("* kadm5int_acl_init(afile=%s)\n",
748 	    ((acl_file) ? acl_file : "(null)")));
749     acl_acl_file = (acl_file) ? acl_file : (char *) KRB5_DEFAULT_ADMIN_ACL;
750     acl_inited = kadm5int_acl_load_acl_file();
751 
752     DPRINT(DEBUG_CALLS, acl_debug_level, ("X kadm5int_acl_init() = %d\n", kret));
753     return(kret);
754 }
755 
756 /*
757  * kadm5int_acl_finish	- Terminate ACL context.
758  */
759 void
kadm5int_acl_finish(kcontext,debug_level)760 kadm5int_acl_finish(kcontext, debug_level)
761     krb5_context	kcontext;
762     int			debug_level;
763 {
764     DPRINT(DEBUG_CALLS, acl_debug_level, ("* kadm5int_acl_finish()\n"));
765     kadm5int_acl_free_entries();
766     DPRINT(DEBUG_CALLS, acl_debug_level, ("X kadm5int_acl_finish()\n"));
767 }
768 
769 /*
770  * kadm5int_acl_check()	- Is this operation permitted for this principal?
771  *			this code used not to be based on gssapi.  In order
772  *			to minimize porting hassles, I've put all the
773  *			gssapi hair in this function.  This might not be
774  *			the best medium-term solution.  (The best long-term
775  *			solution is, of course, a real authorization service.)
776  */
777 krb5_boolean
kadm5int_acl_check(kcontext,caller,opmask,principal,restrictions)778 kadm5int_acl_check(kcontext, caller, opmask, principal, restrictions)
779     krb5_context	kcontext;
780     gss_name_t		caller;
781     krb5_int32		opmask;
782     krb5_principal	principal;
783     restriction_t	**restrictions;
784 {
785     krb5_boolean	retval;
786     aent_t		*aentry;
787     gss_buffer_desc	caller_buf;
788     gss_OID		caller_oid;
789     OM_uint32		emaj, emin;
790     krb5_error_code	code;
791     krb5_principal	caller_princ;
792 
793     DPRINT(DEBUG_CALLS, acl_debug_level, ("* acl_op_permitted()\n"));
794 
795     /* Solaris Kerberos */
796     if (restrictions)
797         *restrictions = NULL;
798 
799     if (GSS_ERROR(emaj = gss_display_name(&emin, caller, &caller_buf,
800 					  &caller_oid)))
801        return(1);
802 
803     code = krb5_parse_name(kcontext, (char *) caller_buf.value,
804 			   &caller_princ);
805 
806     gss_release_buffer(&emin, &caller_buf);
807 
808     if (code)
809        return(code);
810 
811     retval = 0;
812 
813     aentry = kadm5int_acl_find_entry(kcontext, caller_princ, principal);
814     if (aentry) {
815 	if ((aentry->ae_op_allowed & opmask) == opmask) {
816 	    retval = 1;
817 	    if (restrictions) {
818 		*restrictions =
819 		    (aentry->ae_restrictions && aentry->ae_restrictions->mask)
820 		    ? aentry->ae_restrictions
821 		    : (restriction_t *) NULL;
822 	    }
823 	}
824     }
825 
826     krb5_free_principal(kcontext, caller_princ);
827 
828     DPRINT(DEBUG_CALLS, acl_debug_level, ("X acl_op_permitted()=%d\n",
829 					  retval));
830     return(retval);
831 }
832 
833 kadm5_ret_t
kadm5_get_privs(void * server_handle,long * privs)834 kadm5_get_privs(void *server_handle, long *privs)
835 {
836      CHECK_HANDLE(server_handle);
837 
838      /* this is impossible to do with the current interface.  For now,
839 	return all privs, which will confuse some clients, but not
840 	deny any access to users of "smart" clients which try to cache */
841 
842      *privs = ~0;
843 
844      return KADM5_OK;
845 }
846 
847 /* SUNWresync121 (SEAM1.0) XXX */
848 kadm5_ret_t
__kadm5_get_priv(void * server_handle,long * privs,gss_name_t client)849 __kadm5_get_priv(void *server_handle, long *privs, gss_name_t client)
850 {
851 
852 	aent_t		*aentry;
853 	gss_buffer_desc	caller_buff;
854 	gss_OID		caller_oid;
855 	krb5_principal	caller_principal;
856 	OM_uint32	minor, major;
857 	krb5_error_code	k_error;
858 	kadm5_ret_t	retval = KADM5_FAILURE;
859 
860      kadm5_server_handle_t handle = server_handle;
861 
862      CHECK_HANDLE(server_handle);
863 
864 	if (GSS_ERROR(major = gss_display_name(&minor, client, &caller_buff,
865 						&caller_oid)))
866 		return(retval);
867 	k_error = krb5_parse_name(handle->context,
868 					(char *) caller_buff.value,
869 					&caller_principal);
870 	gss_release_buffer(&minor, &caller_buff);
871 
872 	if (k_error)
873 		return(retval);
874 
875 	if (aentry = kadm5int_acl_find_entry(handle->context, caller_principal,
876 					(krb5_principal)NULL))
877 		*privs = aentry->ae_op_allowed;
878 	krb5_free_principal(handle->context, caller_principal);
879 
880 	return (KADM5_OK);
881 }
882