xref: /netbsd-src/crypto/external/bsd/heimdal/dist/kadmin/server.c (revision d3273b5b76f5afaafe308cead5511dbb8df8c5e9)
1 /*	$NetBSD: server.c,v 1.2 2017/01/28 21:31:44 christos Exp $	*/
2 
3 /*
4  * Copyright (c) 1997 - 2005 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 "kadmin_locl.h"
37 #include <krb5/krb5-private.h>
38 
39 static kadm5_ret_t check_aliases(kadm5_server_context *,
40                                  kadm5_principal_ent_rec *,
41                                  kadm5_principal_ent_rec *);
42 
43 static kadm5_ret_t
kadmind_dispatch(void * kadm_handlep,krb5_boolean initial,krb5_data * in,krb5_data * out)44 kadmind_dispatch(void *kadm_handlep, krb5_boolean initial,
45 		 krb5_data *in, krb5_data *out)
46 {
47     kadm5_ret_t ret;
48     int32_t cmd, mask, tmp;
49     kadm5_server_context *contextp = kadm_handlep;
50     char client[128], name[128], name2[128];
51     const char *op = "";
52     krb5_principal princ, princ2;
53     kadm5_principal_ent_rec ent, ent_prev;
54     char *password = NULL, *expression;
55     krb5_keyblock *new_keys;
56     krb5_key_salt_tuple *ks_tuple = NULL;
57     krb5_boolean keepold = FALSE;
58     int n_ks_tuple = 0;
59     int n_keys;
60     char **princs;
61     int n_princs;
62     int keys_ok = 0;
63     krb5_storage *sp;
64     int len;
65 
66     krb5_unparse_name_fixed(contextp->context, contextp->caller,
67 			    client, sizeof(client));
68 
69     sp = krb5_storage_from_data(in);
70     if (sp == NULL)
71 	krb5_errx(contextp->context, 1, "out of memory");
72 
73     krb5_ret_int32(sp, &cmd);
74     switch(cmd){
75     case kadm_get:{
76 	op = "GET";
77 	ret = krb5_ret_principal(sp, &princ);
78 	if(ret)
79 	    goto fail;
80 	ret = krb5_ret_int32(sp, &mask);
81 	if(ret){
82 	    krb5_free_principal(contextp->context, princ);
83 	    goto fail;
84 	}
85 	mask |= KADM5_PRINCIPAL;
86 	krb5_unparse_name_fixed(contextp->context, princ, name, sizeof(name));
87 	krb5_warnx(contextp->context, "%s: %s %s", client, op, name);
88 
89         /* If the caller doesn't have KADM5_PRIV_GET, we're done. */
90 	ret = _kadm5_acl_check_permission(contextp, KADM5_PRIV_GET, princ);
91         if (ret) {
92 	    krb5_free_principal(contextp->context, princ);
93 	    goto fail;
94         }
95 
96         /* Then check to see if it is ok to return keys */
97         if ((mask & KADM5_KEY_DATA) != 0) {
98             ret = _kadm5_acl_check_permission(contextp, KADM5_PRIV_GET_KEYS,
99                                               princ);
100             if (ret == 0) {
101                 keys_ok = 1;
102             } else if ((mask == (KADM5_PRINCIPAL|KADM5_KEY_DATA)) ||
103                        (mask == (KADM5_PRINCIPAL|KADM5_KVNO|KADM5_KEY_DATA))) {
104                 /*
105                  * Requests for keys will get bogus keys, which is useful if
106                  * the client just wants to see what (kvno, enctype)s the
107                  * principal has keys for, but terrible if the client wants to
108                  * write the keys into a keytab or modify the principal and
109                  * write the bogus keys back to the server.
110                  *
111                  * We use a heuristic to detect which case we're handling here.
112                  * If the client only asks for the flags in the above
113                  * condition, then it's very likely a kadmin ext_keytab,
114                  * add_enctype, or other request that should not see bogus
115                  * keys.  We deny them.
116                  *
117                  * The kadmin get command can be coaxed into making a request
118                  * with the same mask.  But the default long and terse output
119                  * modes request other things too, so in all likelihood this
120                  * heuristic will not hurt any kadmin get uses.
121                  */
122                 krb5_free_principal(contextp->context, princ);
123                 goto fail;
124             }
125         }
126 
127 	ret = kadm5_get_principal(kadm_handlep, princ, &ent, mask);
128 	krb5_storage_free(sp);
129 	sp = krb5_storage_emem();
130 	krb5_store_int32(sp, ret);
131 	if (ret == 0){
132 	    if (keys_ok)
133 		kadm5_store_principal_ent(sp, &ent);
134 	    else
135 		kadm5_store_principal_ent_nokeys(sp, &ent);
136 	    kadm5_free_principal_ent(kadm_handlep, &ent);
137 	}
138 	krb5_free_principal(contextp->context, princ);
139 	break;
140     }
141     case kadm_delete:{
142 	op = "DELETE";
143 	ret = krb5_ret_principal(sp, &princ);
144 	if(ret)
145 	    goto fail;
146 	krb5_unparse_name_fixed(contextp->context, princ, name, sizeof(name));
147 	krb5_warnx(contextp->context, "%s: %s %s", client, op, name);
148 	ret = _kadm5_acl_check_permission(contextp, KADM5_PRIV_DELETE, princ);
149 	if(ret){
150 	    krb5_free_principal(contextp->context, princ);
151 	    goto fail;
152 	}
153 
154         /*
155          * There's no need to check that the caller has permission to
156          * delete the victim principal's aliases.
157          */
158 
159 	ret = kadm5_delete_principal(kadm_handlep, princ);
160 	krb5_free_principal(contextp->context, princ);
161 	krb5_storage_free(sp);
162 	sp = krb5_storage_emem();
163 	krb5_store_int32(sp, ret);
164 	break;
165     }
166     case kadm_create:{
167 	op = "CREATE";
168 	ret = kadm5_ret_principal_ent(sp, &ent);
169 	if(ret)
170 	    goto fail;
171 	ret = krb5_ret_int32(sp, &mask);
172 	if(ret){
173 	    kadm5_free_principal_ent(kadm_handlep, &ent);
174 	    goto fail;
175 	}
176 	ret = krb5_ret_string(sp, &password);
177 	if(ret){
178 	    kadm5_free_principal_ent(kadm_handlep, &ent);
179 	    goto fail;
180 	}
181 	krb5_unparse_name_fixed(contextp->context, ent.principal,
182 				name, sizeof(name));
183 	krb5_warnx(contextp->context, "%s: %s %s", client, op, name);
184 	ret = _kadm5_acl_check_permission(contextp, KADM5_PRIV_ADD,
185 					  ent.principal);
186 	if(ret){
187 	    kadm5_free_principal_ent(kadm_handlep, &ent);
188 	    goto fail;
189 	}
190         if ((mask & KADM5_TL_DATA)) {
191             /*
192              * Also check that the caller can create the aliases, if the
193              * new principal has any.
194              */
195             ret = check_aliases(contextp, &ent, NULL);
196             if (ret) {
197                 kadm5_free_principal_ent(kadm_handlep, &ent);
198                 goto fail;
199             }
200         }
201 	ret = kadm5_create_principal(kadm_handlep, &ent,
202 				     mask, password);
203 	kadm5_free_principal_ent(kadm_handlep, &ent);
204 	krb5_storage_free(sp);
205 	sp = krb5_storage_emem();
206 	krb5_store_int32(sp, ret);
207 	break;
208     }
209     case kadm_modify:{
210 	op = "MODIFY";
211 	ret = kadm5_ret_principal_ent(sp, &ent);
212 	if(ret)
213 	    goto fail;
214 	ret = krb5_ret_int32(sp, &mask);
215 	if(ret){
216 	    kadm5_free_principal_ent(contextp, &ent);
217 	    goto fail;
218 	}
219 	krb5_unparse_name_fixed(contextp->context, ent.principal,
220 				name, sizeof(name));
221 	krb5_warnx(contextp->context, "%s: %s %s", client, op, name);
222 	ret = _kadm5_acl_check_permission(contextp, KADM5_PRIV_MODIFY,
223 					  ent.principal);
224 	if(ret){
225 	    kadm5_free_principal_ent(contextp, &ent);
226 	    goto fail;
227 	}
228         if ((mask & KADM5_TL_DATA)) {
229             /*
230              * Also check that the caller can create aliases that are in
231              * the new entry but not the old one.  There's no need to
232              * check that the caller can delete aliases it wants to
233              * drop.  See also handling of rename.
234              */
235             ret = kadm5_get_principal(kadm_handlep, ent.principal, &ent_prev, mask);
236             if (ret) {
237                 kadm5_free_principal_ent(contextp, &ent);
238                 goto fail;
239             }
240             ret = check_aliases(contextp, &ent, &ent_prev);
241             kadm5_free_principal_ent(contextp, &ent_prev);
242             if (ret) {
243                 kadm5_free_principal_ent(contextp, &ent);
244                 goto fail;
245             }
246         }
247 	ret = kadm5_modify_principal(kadm_handlep, &ent, mask);
248 	kadm5_free_principal_ent(kadm_handlep, &ent);
249 	krb5_storage_free(sp);
250 	sp = krb5_storage_emem();
251 	krb5_store_int32(sp, ret);
252 	break;
253     }
254     case kadm_rename:{
255 	op = "RENAME";
256 	ret = krb5_ret_principal(sp, &princ);
257 	if(ret)
258 	    goto fail;
259 	ret = krb5_ret_principal(sp, &princ2);
260 	if(ret){
261 	    krb5_free_principal(contextp->context, princ);
262 	    goto fail;
263 	}
264 	krb5_unparse_name_fixed(contextp->context, princ, name, sizeof(name));
265 	krb5_unparse_name_fixed(contextp->context, princ2,
266                                 name2, sizeof(name2));
267 	krb5_warnx(contextp->context, "%s: %s %s -> %s",
268 		   client, op, name, name2);
269 	ret = _kadm5_acl_check_permission(contextp,
270 					  KADM5_PRIV_ADD,
271 					  princ2);
272         if (ret == 0) {
273             /*
274              * Also require modify for the principal.  For backwards
275              * compatibility, allow delete permission on the old name to
276              * cure lack of modify permission on the old name.
277              */
278             ret = _kadm5_acl_check_permission(contextp,
279                                               KADM5_PRIV_MODIFY,
280                                               princ);
281             if (ret) {
282                 ret = _kadm5_acl_check_permission(contextp,
283                                                   KADM5_PRIV_DELETE,
284                                                   princ);
285             }
286         }
287 	if(ret){
288 	    krb5_free_principal(contextp->context, princ);
289 	    krb5_free_principal(contextp->context, princ2);
290 	    goto fail;
291 	}
292 	ret = kadm5_rename_principal(kadm_handlep, princ, princ2);
293 	krb5_free_principal(contextp->context, princ);
294 	krb5_free_principal(contextp->context, princ2);
295 	krb5_storage_free(sp);
296 	sp = krb5_storage_emem();
297 	krb5_store_int32(sp, ret);
298 	break;
299     }
300     case kadm_chpass:{
301 	op = "CHPASS";
302 	ret = krb5_ret_principal(sp, &princ);
303 	if (ret)
304 	    goto fail;
305 	ret = krb5_ret_string(sp, &password);
306 	if (ret) {
307 	    krb5_free_principal(contextp->context, princ);
308 	    goto fail;
309 	}
310 	ret = krb5_ret_int32(sp, &keepold);
311 	if (ret && ret != HEIM_ERR_EOF) {
312 	    krb5_free_principal(contextp->context, princ);
313 	    goto fail;
314 	}
315 	krb5_unparse_name_fixed(contextp->context, princ, name, sizeof(name));
316 	krb5_warnx(contextp->context, "%s: %s %s", client, op, name);
317 
318 	/*
319 	 * The change is allowed if at least one of:
320 	 *
321 	 * a) allowed by sysadmin
322 	 * b) it's for the principal him/herself and this was an
323 	 *    initial ticket, but then, check with the password quality
324 	 *    function.
325 	 * c) the user is on the CPW ACL.
326 	 */
327 
328 	if (krb5_config_get_bool_default(contextp->context, NULL, TRUE,
329 					 "kadmin", "allow_self_change_password", NULL)
330 	    && initial
331 	    && krb5_principal_compare (contextp->context, contextp->caller,
332 				       princ))
333 	{
334 	    krb5_data pwd_data;
335 	    const char *pwd_reason;
336 
337 	    pwd_data.data = password;
338 	    pwd_data.length = strlen(password);
339 
340 	    pwd_reason = kadm5_check_password_quality (contextp->context,
341 						       princ, &pwd_data);
342 	    if (pwd_reason != NULL)
343 		ret = KADM5_PASS_Q_DICT;
344 	    else
345 		ret = 0;
346 	} else
347 	    ret = _kadm5_acl_check_permission(contextp, KADM5_PRIV_CPW, princ);
348 
349 	if(ret) {
350 	    krb5_free_principal(contextp->context, princ);
351 	    goto fail;
352 	}
353 	ret = kadm5_chpass_principal_3(kadm_handlep, princ, keepold, 0, NULL,
354 				       password);
355 	krb5_free_principal(contextp->context, princ);
356 	krb5_storage_free(sp);
357 	sp = krb5_storage_emem();
358 	krb5_store_int32(sp, ret);
359 	break;
360     }
361     case kadm_chpass_with_key:{
362 	int i;
363 	krb5_key_data *key_data;
364 	int n_key_data;
365 
366 	op = "CHPASS_WITH_KEY";
367 	ret = krb5_ret_principal(sp, &princ);
368 	if(ret)
369 	    goto fail;
370 	ret = krb5_ret_int32(sp, &n_key_data);
371 	if (ret) {
372 	    krb5_free_principal(contextp->context, princ);
373 	    goto fail;
374 	}
375 	ret = krb5_ret_int32(sp, &keepold);
376 	if (ret && ret != HEIM_ERR_EOF) {
377 	    krb5_free_principal(contextp->context, princ);
378 	    goto fail;
379 	}
380 	/* n_key_data will be squeezed into an int16_t below. */
381 	if (n_key_data < 0 || n_key_data >= 1 << 16 ||
382 	    (size_t)n_key_data > UINT_MAX/sizeof(*key_data)) {
383 	    ret = ERANGE;
384 	    krb5_free_principal(contextp->context, princ);
385 	    goto fail;
386 	}
387 
388 	key_data = malloc (n_key_data * sizeof(*key_data));
389 	if (key_data == NULL && n_key_data != 0) {
390 	    ret = ENOMEM;
391 	    krb5_free_principal(contextp->context, princ);
392 	    goto fail;
393 	}
394 
395 	for (i = 0; i < n_key_data; ++i) {
396 	    ret = kadm5_ret_key_data (sp, &key_data[i]);
397 	    if (ret) {
398 		int16_t dummy = i;
399 
400 		kadm5_free_key_data (contextp, &dummy, key_data);
401 		free (key_data);
402 		krb5_free_principal(contextp->context, princ);
403 		goto fail;
404 	    }
405 	}
406 
407 	krb5_unparse_name_fixed(contextp->context, princ, name, sizeof(name));
408 	krb5_warnx(contextp->context, "%s: %s %s", client, op, name);
409 
410 	/*
411 	 * The change is only allowed if the user is on the CPW ACL,
412 	 * this it to force password quality check on the user.
413 	 */
414 
415 	ret = _kadm5_acl_check_permission(contextp, KADM5_PRIV_CPW, princ);
416 	if(ret) {
417 	    int16_t dummy = n_key_data;
418 
419 	    kadm5_free_key_data (contextp, &dummy, key_data);
420 	    free (key_data);
421 	    krb5_free_principal(contextp->context, princ);
422 	    goto fail;
423 	}
424 	ret = kadm5_chpass_principal_with_key_3(kadm_handlep, princ, keepold,
425 					        n_key_data, key_data);
426 	{
427 	    int16_t dummy = n_key_data;
428 	    kadm5_free_key_data (contextp, &dummy, key_data);
429 	}
430 	free (key_data);
431 	krb5_free_principal(contextp->context, princ);
432 	krb5_storage_free(sp);
433 	sp = krb5_storage_emem();
434 	krb5_store_int32(sp, ret);
435 	break;
436     }
437     case kadm_randkey:{
438 	op = "RANDKEY";
439 	ret = krb5_ret_principal(sp, &princ);
440 	if(ret)
441 	    goto fail;
442 	krb5_unparse_name_fixed(contextp->context, princ, name, sizeof(name));
443 	krb5_warnx(contextp->context, "%s: %s %s", client, op, name);
444 	/*
445 	 * The change is allowed if at least one of:
446 	 * a) it's for the principal him/herself and this was an initial ticket
447 	 * b) the user is on the CPW ACL.
448 	 */
449 
450 	if (initial
451 	    && krb5_principal_compare (contextp->context, contextp->caller,
452 				       princ))
453 	    ret = 0;
454 	else
455 	    ret = _kadm5_acl_check_permission(contextp, KADM5_PRIV_CPW, princ);
456 
457 	if(ret) {
458 	    krb5_free_principal(contextp->context, princ);
459 	    goto fail;
460 	}
461 
462 	/*
463 	 * See comments in kadm5_c_randkey_principal() regarding the
464 	 * protocol.
465 	 */
466 	ret = krb5_ret_int32(sp, &keepold);
467 	if (ret != 0 && ret != HEIM_ERR_EOF) {
468 	    krb5_free_principal(contextp->context, princ);
469 	    goto fail;
470 	}
471 
472 	ret = krb5_ret_int32(sp, &n_ks_tuple);
473 	if (ret != 0 && ret != HEIM_ERR_EOF) {
474 	    krb5_free_principal(contextp->context, princ);
475 	    goto fail;
476 	} else if (ret == 0) {
477 	    size_t i;
478 
479 	    if (n_ks_tuple < 0) {
480 		ret = EOVERFLOW;
481 		krb5_free_principal(contextp->context, princ);
482 		goto fail;
483 	    }
484 
485 	    if ((ks_tuple = calloc(n_ks_tuple, sizeof (*ks_tuple))) == NULL) {
486 		ret = errno;
487 		krb5_free_principal(contextp->context, princ);
488 		goto fail;
489 	    }
490 
491 	    for (i = 0; i < n_ks_tuple; i++) {
492 		ret = krb5_ret_int32(sp, &ks_tuple[i].ks_enctype);
493 		if (ret != 0) {
494 		    krb5_free_principal(contextp->context, princ);
495                     free(ks_tuple);
496 		    goto fail;
497 		}
498 		ret = krb5_ret_int32(sp, &ks_tuple[i].ks_salttype);
499 		if (ret != 0) {
500 		    krb5_free_principal(contextp->context, princ);
501                     free(ks_tuple);
502 		    goto fail;
503 		}
504 	    }
505 	}
506 	ret = kadm5_randkey_principal_3(kadm_handlep, princ, keepold,
507 					n_ks_tuple, ks_tuple, &new_keys,
508 					&n_keys);
509 	krb5_free_principal(contextp->context, princ);
510         free(ks_tuple);
511 
512 	krb5_storage_free(sp);
513 	sp = krb5_storage_emem();
514 	krb5_store_int32(sp, ret);
515 	if(ret == 0){
516 	    int i;
517 	    krb5_store_int32(sp, n_keys);
518 	    for(i = 0; i < n_keys; i++){
519                 if (ret == 0)
520                     ret = krb5_store_keyblock(sp, new_keys[i]);
521 		krb5_free_keyblock_contents(contextp->context, &new_keys[i]);
522 	    }
523 	    free(new_keys);
524 	}
525 	break;
526     }
527     case kadm_get_privs:{
528 	uint32_t privs;
529 	ret = kadm5_get_privs(kadm_handlep, &privs);
530 	krb5_storage_free(sp);
531 	sp = krb5_storage_emem();
532 	krb5_store_int32(sp, ret);
533 	if(ret == 0)
534 	    krb5_store_uint32(sp, privs);
535 	break;
536     }
537     case kadm_get_princs:{
538 	op = "LIST";
539 	ret = krb5_ret_int32(sp, &tmp);
540 	if(ret)
541 	    goto fail;
542 	if(tmp){
543 	    ret = krb5_ret_string(sp, &expression);
544 	    if(ret)
545 		goto fail;
546 	}else
547 	    expression = NULL;
548 	krb5_warnx(contextp->context, "%s: %s %s", client, op,
549 		   expression ? expression : "*");
550 	ret = _kadm5_acl_check_permission(contextp, KADM5_PRIV_LIST, NULL);
551 	if(ret){
552 	    free(expression);
553 	    goto fail;
554 	}
555 	ret = kadm5_get_principals(kadm_handlep, expression, &princs, &n_princs);
556 	free(expression);
557 	krb5_storage_free(sp);
558 	sp = krb5_storage_emem();
559 	krb5_store_int32(sp, ret);
560 	if(ret == 0){
561 	    int i;
562 	    krb5_store_int32(sp, n_princs);
563 	    for(i = 0; i < n_princs; i++)
564 		krb5_store_string(sp, princs[i]);
565 	    kadm5_free_name_list(kadm_handlep, princs, &n_princs);
566 	}
567 	break;
568     }
569     default:
570 	krb5_warnx(contextp->context, "%s: UNKNOWN OP %d", client, cmd);
571 	krb5_storage_free(sp);
572 	sp = krb5_storage_emem();
573 	krb5_store_int32(sp, KADM5_FAILURE);
574 	break;
575     }
576     if (password != NULL) {
577 	len = strlen(password);
578 	memset_s(password, len, 0, len);
579 	free(password);
580     }
581     krb5_storage_to_data(sp, out);
582     krb5_storage_free(sp);
583     return 0;
584 fail:
585     if (password != NULL) {
586 	len = strlen(password);
587 	memset_s(password, len, 0, len);
588 	free(password);
589     }
590     krb5_warn(contextp->context, ret, "%s", op);
591     krb5_storage_seek(sp, 0, SEEK_SET);
592     krb5_store_int32(sp, ret);
593     krb5_storage_to_data(sp, out);
594     krb5_storage_free(sp);
595     return 0;
596 }
597 
598 struct iter_aliases_ctx {
599     HDB_Ext_Aliases aliases;
600     krb5_tl_data *tl;
601     int alias_idx;
602     int done;
603 };
604 
605 static kadm5_ret_t
iter_aliases(kadm5_principal_ent_rec * from,struct iter_aliases_ctx * ctx,krb5_principal * out)606 iter_aliases(kadm5_principal_ent_rec *from,
607              struct iter_aliases_ctx *ctx,
608              krb5_principal *out)
609 {
610     HDB_extension ext;
611     kadm5_ret_t ret;
612     size_t size;
613 
614     *out = NULL;
615 
616     if (ctx->done > 0)
617         return 0;
618 
619     if (ctx->done == 0) {
620         if (ctx->alias_idx < ctx->aliases.aliases.len) {
621             *out = &ctx->aliases.aliases.val[ctx->alias_idx++];
622             return 0;
623         }
624         /* Out of aliases in this TL, step to next TL */
625         ctx->tl = ctx->tl->tl_data_next;
626     } else if (ctx->done < 0) {
627         /* Setup iteration context */
628         memset(ctx, 0, sizeof(*ctx));
629         ctx->done = 0;
630         ctx->aliases.aliases.val = NULL;
631         ctx->aliases.aliases.len = 0;
632         ctx->tl = from->tl_data;
633     }
634 
635     free_HDB_Ext_Aliases(&ctx->aliases);
636     ctx->alias_idx = 0;
637 
638     /* Find TL with aliases */
639     for (; ctx->tl != NULL; ctx->tl = ctx->tl->tl_data_next) {
640         if (ctx->tl->tl_data_type != KRB5_TL_EXTENSION)
641             continue;
642 
643         ret = decode_HDB_extension(ctx->tl->tl_data_contents,
644                                    ctx->tl->tl_data_length,
645                                    &ext, &size);
646         if (ret)
647             return ret;
648         if (ext.data.element == choice_HDB_extension_data_aliases &&
649             ext.data.u.aliases.aliases.len > 0) {
650             ctx->aliases = ext.data.u.aliases;
651             break;
652         }
653         free_HDB_extension(&ext);
654     }
655 
656     if (ctx->tl != NULL && ctx->aliases.aliases.len > 0) {
657         *out = &ctx->aliases.aliases.val[ctx->alias_idx++];
658         return 0;
659     }
660 
661     ctx->done = 1;
662     return 0;
663 }
664 
665 static kadm5_ret_t
check_aliases(kadm5_server_context * contextp,kadm5_principal_ent_rec * add_princ,kadm5_principal_ent_rec * del_princ)666 check_aliases(kadm5_server_context *contextp,
667               kadm5_principal_ent_rec *add_princ,
668               kadm5_principal_ent_rec *del_princ)
669 {
670     kadm5_ret_t ret;
671     struct iter_aliases_ctx iter;
672     struct iter_aliases_ctx iter_del;
673     krb5_principal new_name, old_name;
674     int match;
675 
676     /*
677      * Yeah, this is O(N^2).  Gathering and sorting all the aliases
678      * would be a bit of a pain; if we ever have principals with enough
679      * aliases for this to be a problem, we can fix it then.
680      */
681     for (iter.done = -1; iter.done != 1;) {
682         match = 0;
683         ret = iter_aliases(add_princ, &iter, &new_name);
684         if (ret)
685             return ret;
686         if (iter.done == 1)
687             break;
688         for (iter_del.done = -1; iter_del.done != 1;) {
689             ret = iter_aliases(del_princ, &iter_del, &old_name);
690             if (ret)
691                 return ret;
692             if (iter_del.done == 1)
693                 break;
694             if (!krb5_principal_compare(contextp->context, new_name, old_name))
695                 continue;
696             free_HDB_Ext_Aliases(&iter_del.aliases);
697             match = 1;
698             break;
699         }
700         if (match)
701             continue;
702         ret = _kadm5_acl_check_permission(contextp, KADM5_PRIV_ADD, new_name);
703         if (ret) {
704             free_HDB_Ext_Aliases(&iter.aliases);
705             return ret;
706         }
707     }
708 
709     return 0;
710 }
711 
712 static void
v5_loop(krb5_context contextp,krb5_auth_context ac,krb5_boolean initial,void * kadm_handlep,krb5_socket_t fd)713 v5_loop (krb5_context contextp,
714 	 krb5_auth_context ac,
715 	 krb5_boolean initial,
716 	 void *kadm_handlep,
717 	 krb5_socket_t fd)
718 {
719     krb5_error_code ret;
720     krb5_data in, out;
721 
722     for (;;) {
723 	doing_useful_work = 0;
724 	if(term_flag)
725 	    exit(0);
726 	ret = krb5_read_priv_message(contextp, ac, &fd, &in);
727 	if(ret == HEIM_ERR_EOF)
728 	    exit(0);
729 	if(ret)
730 	    krb5_err(contextp, 1, ret, "krb5_read_priv_message");
731 	doing_useful_work = 1;
732 	kadmind_dispatch(kadm_handlep, initial, &in, &out);
733 	krb5_data_free(&in);
734 	ret = krb5_write_priv_message(contextp, ac, &fd, &out);
735 	if(ret)
736 	    krb5_err(contextp, 1, ret, "krb5_write_priv_message");
737     }
738 }
739 
740 static krb5_boolean
match_appl_version(const void * data,const char * appl_version)741 match_appl_version(const void *data, const char *appl_version)
742 {
743     unsigned minor;
744     if(sscanf(appl_version, "KADM0.%u", &minor) != 1)
745 	return 0;
746     /*XXX*/
747     *(unsigned*)(intptr_t)data = minor;
748     return 1;
749 }
750 
751 static void
handle_v5(krb5_context contextp,krb5_keytab keytab,krb5_socket_t fd)752 handle_v5(krb5_context contextp,
753 	  krb5_keytab keytab,
754 	  krb5_socket_t fd)
755 {
756     krb5_error_code ret;
757     krb5_ticket *ticket;
758     char *server_name;
759     char *client;
760     void *kadm_handlep;
761     krb5_boolean initial;
762     krb5_auth_context ac = NULL;
763 
764     unsigned kadm_version = 1;
765     kadm5_config_params realm_params;
766 
767     ret = krb5_recvauth_match_version(contextp, &ac, &fd,
768 				      match_appl_version, &kadm_version,
769 				      NULL, KRB5_RECVAUTH_IGNORE_VERSION,
770 				      keytab, &ticket);
771     if (ret)
772 	krb5_err(contextp, 1, ret, "krb5_recvauth");
773 
774     ret = krb5_unparse_name (contextp, ticket->server, &server_name);
775     if (ret)
776 	krb5_err (contextp, 1, ret, "krb5_unparse_name");
777 
778     if (strncmp (server_name, KADM5_ADMIN_SERVICE,
779 		 strlen(KADM5_ADMIN_SERVICE)) != 0)
780 	krb5_errx (contextp, 1, "ticket for strange principal (%s)",
781 		   server_name);
782 
783     free (server_name);
784 
785     memset(&realm_params, 0, sizeof(realm_params));
786 
787     if(kadm_version == 1) {
788 	krb5_data params;
789 	ret = krb5_read_priv_message(contextp, ac, &fd, &params);
790 	if(ret)
791 	    krb5_err(contextp, 1, ret, "krb5_read_priv_message");
792 	_kadm5_unmarshal_params(contextp, &params, &realm_params);
793     }
794 
795     initial = ticket->ticket.flags.initial;
796     ret = krb5_unparse_name(contextp, ticket->client, &client);
797     if (ret)
798 	krb5_err (contextp, 1, ret, "krb5_unparse_name");
799     krb5_free_ticket (contextp, ticket);
800     ret = kadm5_s_init_with_password_ctx(contextp,
801 					 client,
802 					 NULL,
803 					 KADM5_ADMIN_SERVICE,
804 					 &realm_params,
805 					 0, 0,
806 					 &kadm_handlep);
807     if(ret)
808 	krb5_err (contextp, 1, ret, "kadm5_init_with_password_ctx");
809     v5_loop (contextp, ac, initial, kadm_handlep, fd);
810 }
811 
812 krb5_error_code
kadmind_loop(krb5_context contextp,krb5_keytab keytab,krb5_socket_t sock)813 kadmind_loop(krb5_context contextp,
814 	     krb5_keytab keytab,
815 	     krb5_socket_t sock)
816 {
817     u_char buf[sizeof(KRB5_SENDAUTH_VERSION) + 4];
818     ssize_t n;
819     unsigned long len;
820 
821     n = krb5_net_read(contextp, &sock, buf, 4);
822     if(n == 0)
823 	exit(0);
824     if(n < 0)
825 	krb5_err(contextp, 1, errno, "read");
826     _krb5_get_int(buf, &len, 4);
827 
828     if (len == sizeof(KRB5_SENDAUTH_VERSION)) {
829 
830 	n = krb5_net_read(contextp, &sock, buf + 4, len);
831 	if (n < 0)
832 	    krb5_err (contextp, 1, errno, "reading sendauth version");
833 	if (n == 0)
834 	    krb5_errx (contextp, 1, "EOF reading sendauth version");
835 
836 	if(memcmp(buf + 4, KRB5_SENDAUTH_VERSION, len) == 0) {
837 	    handle_v5(contextp, keytab, sock);
838 	    return 0;
839 	}
840 	len += 4;
841     } else
842 	len = 4;
843 
844     handle_mit(contextp, buf, len, sock);
845 
846     return 0;
847 }
848