1 /*	$NetBSD: acl.c,v 1.2 2017/01/28 21:31:49 christos Exp $	*/
2 
3 /*
4  * Copyright (c) 1997 - 2001 Kungliga Tekniska Högskolan
5  * (Royal Institute of Technology, Stockholm, Sweden).
6  * All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  *
12  * 1. Redistributions of source code must retain the above copyright
13  *    notice, this list of conditions and the following disclaimer.
14  *
15  * 2. Redistributions in binary form must reproduce the above copyright
16  *    notice, this list of conditions and the following disclaimer in the
17  *    documentation and/or other materials provided with the distribution.
18  *
19  * 3. Neither the name of the Institute nor the names of its contributors
20  *    may be used to endorse or promote products derived from this software
21  *    without specific prior written permission.
22  *
23  * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
24  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26  * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
27  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33  * SUCH DAMAGE.
34  */
35 
36 #include "kadm5_locl.h"
37 
38 __RCSID("$NetBSD: acl.c,v 1.2 2017/01/28 21:31:49 christos Exp $");
39 
40 static struct units acl_units[] = {
41     { "all",		KADM5_PRIV_ALL },
42     { "change-password",KADM5_PRIV_CPW },
43     { "cpw",		KADM5_PRIV_CPW },
44     { "list",		KADM5_PRIV_LIST },
45     { "delete",		KADM5_PRIV_DELETE },
46     { "modify",		KADM5_PRIV_MODIFY },
47     { "add",		KADM5_PRIV_ADD },
48     { "get", 		KADM5_PRIV_GET },
49     { "get-keys",	KADM5_PRIV_GET_KEYS },
50     { NULL,		0 }
51 };
52 
53 kadm5_ret_t
_kadm5_string_to_privs(const char * s,uint32_t * privs)54 _kadm5_string_to_privs(const char *s, uint32_t* privs)
55 {
56     int flags;
57     flags = parse_flags(s, acl_units, 0);
58     if(flags < 0)
59 	return KADM5_FAILURE;
60     *privs = flags;
61     return 0;
62 }
63 
64 kadm5_ret_t
_kadm5_privs_to_string(uint32_t privs,char * string,size_t len)65 _kadm5_privs_to_string(uint32_t privs, char *string, size_t len)
66 {
67     if(privs == 0)
68 	strlcpy(string, "none", len);
69     else
70 	unparse_flags(privs, acl_units + 1, string, len);
71     return 0;
72 }
73 
74 /*
75  * retrieve the right for the current caller on `princ' (NULL means all)
76  * and store them in `ret_flags'
77  * return 0 or an error.
78  */
79 
80 static kadm5_ret_t
fetch_acl(kadm5_server_context * context,krb5_const_principal princ,unsigned * ret_flags)81 fetch_acl (kadm5_server_context *context,
82 	   krb5_const_principal princ,
83 	   unsigned *ret_flags)
84 {
85     FILE *f;
86     krb5_error_code ret = 0;
87     char buf[256];
88 
89     *ret_flags = 0;
90 
91     /* no acl file -> no rights */
92     f = fopen(context->config.acl_file, "r");
93     if (f == NULL)
94 	return 0;
95 
96     while(fgets(buf, sizeof(buf), f) != NULL) {
97 	char *foo = NULL, *p;
98 	krb5_principal this_princ;
99 	unsigned flags = 0;
100 
101 	p = strtok_r(buf, " \t\n", &foo);
102 	if(p == NULL)
103 	    continue;
104 	if (*p == '#')		/* comment */
105 	    continue;
106 	ret = krb5_parse_name(context->context, p, &this_princ);
107 	if(ret)
108 	    break;
109 	if(!krb5_principal_compare(context->context,
110 				   context->caller, this_princ)) {
111 	    krb5_free_principal(context->context, this_princ);
112 	    continue;
113 	}
114 	krb5_free_principal(context->context, this_princ);
115 	p = strtok_r(NULL, " \t\n", &foo);
116 	if(p == NULL)
117 	    continue;
118 	ret = _kadm5_string_to_privs(p, &flags);
119 	if (ret)
120 	    break;
121 	p = strtok_r(NULL, " \t\n", &foo);
122 	if (p == NULL) {
123 	    *ret_flags = flags;
124 	    break;
125 	}
126 	if (princ != NULL) {
127 	    krb5_principal pattern_princ;
128 	    krb5_boolean match;
129 
130 	    ret = krb5_parse_name (context->context, p, &pattern_princ);
131 	    if (ret)
132 		break;
133 	    match = krb5_principal_match (context->context,
134 					  princ, pattern_princ);
135 	    krb5_free_principal (context->context, pattern_princ);
136 	    if (match) {
137 		*ret_flags = flags;
138 		break;
139 	    }
140 	}
141     }
142     fclose(f);
143     return ret;
144 }
145 
146 /*
147  * set global acl flags in `context' for the current caller.
148  * return 0 on success or an error
149  */
150 
151 kadm5_ret_t
_kadm5_acl_init(kadm5_server_context * context)152 _kadm5_acl_init(kadm5_server_context *context)
153 {
154     krb5_principal princ;
155     krb5_error_code ret;
156 
157     ret = krb5_parse_name(context->context, KADM5_ADMIN_SERVICE, &princ);
158     if (ret)
159 	return ret;
160     ret = krb5_principal_compare(context->context, context->caller, princ);
161     krb5_free_principal(context->context, princ);
162     if(ret != 0) {
163 	context->acl_flags = KADM5_PRIV_ALL;
164 	return 0;
165     }
166 
167     return fetch_acl (context, NULL, &context->acl_flags);
168 }
169 
170 /*
171  * check if `flags' allows `op'
172  * return 0 if OK or an error
173  */
174 
175 static kadm5_ret_t
check_flags(unsigned op,unsigned flags)176 check_flags (unsigned op,
177 	     unsigned flags)
178 {
179     unsigned res = ~flags & op;
180 
181     if(res & KADM5_PRIV_GET)
182 	return KADM5_AUTH_GET;
183     if(res & KADM5_PRIV_GET_KEYS)
184 	return KADM5_AUTH_GET_KEYS;
185     if(res & KADM5_PRIV_ADD)
186 	return KADM5_AUTH_ADD;
187     if(res & KADM5_PRIV_MODIFY)
188 	return KADM5_AUTH_MODIFY;
189     if(res & KADM5_PRIV_DELETE)
190 	return KADM5_AUTH_DELETE;
191     if(res & KADM5_PRIV_CPW)
192 	return KADM5_AUTH_CHANGEPW;
193     if(res & KADM5_PRIV_LIST)
194 	return KADM5_AUTH_LIST;
195     if(res)
196 	return KADM5_AUTH_INSUFFICIENT;
197     return 0;
198 }
199 
200 /*
201  * return 0 if the current caller in `context' is allowed to perform
202  * `op' on `princ' and otherwise an error
203  * princ == NULL if it's not relevant.
204  */
205 
206 kadm5_ret_t
_kadm5_acl_check_permission(kadm5_server_context * context,unsigned op,krb5_const_principal princ)207 _kadm5_acl_check_permission(kadm5_server_context *context,
208 			    unsigned op,
209 			    krb5_const_principal princ)
210 {
211     kadm5_ret_t ret;
212     unsigned princ_flags;
213 
214     ret = check_flags (op, context->acl_flags);
215     if (ret == 0)
216 	return ret;
217     ret = fetch_acl (context, princ, &princ_flags);
218     if (ret)
219 	return ret;
220     return check_flags (op, princ_flags);
221 }
222