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
39static kadm5_ret_t check_aliases(kadm5_server_context *,
40                                 kadm5_principal_ent_rec *,
41                                 kadm5_principal_ent_rec *);
42
43static kadm5_ret_t
44kadmind_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;
584fail:
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
598struct iter_aliases_ctx {
599    HDB_Ext_Aliases aliases;
600    krb5_tl_data *tl;
601    int alias_idx;
602    int done;
603};
604
605static kadm5_ret_t
606iter_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
665static kadm5_ret_t
666check_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
712static void
713v5_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
740static krb5_boolean
741match_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
751static void
752handle_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
812krb5_error_code
813kadmind_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