lib/krb5: krb5_init_creds_set_service fail if set_realm fails
[heimdal.git] / kadmin / server.c
blobba7774616a5ca62e53c32169bc6ccb74c6f1b8d6
1 /*
2 * Copyright (c) 1997 - 2005 Kungliga Tekniska Högskolan
3 * (Royal Institute of Technology, Stockholm, Sweden).
4 * All rights reserved.
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
17 * 3. Neither the name of the Institute nor the names of its contributors
18 * may be used to endorse or promote products derived from this software
19 * without specific prior written permission.
21 * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24 * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31 * SUCH DAMAGE.
34 #include "kadmin_locl.h"
35 #include <krb5-private.h>
37 static kadm5_ret_t check_aliases(kadm5_server_context *,
38 kadm5_principal_ent_rec *,
39 kadm5_principal_ent_rec *);
41 static kadm5_ret_t
42 kadmind_dispatch(void *kadm_handlep, krb5_boolean initial,
43 krb5_data *in, krb5_data *out, int readonly)
45 kadm5_ret_t ret = 0;
46 kadm5_ret_t ret_sp = 0;
47 int32_t cmd, mask, kvno, tmp;
48 kadm5_server_context *contextp = kadm_handlep;
49 char client[128], name[128], name2[128];
50 const char *op = "";
51 krb5_principal princ = NULL, princ2 = NULL;
52 kadm5_principal_ent_rec ent, ent_prev;
53 char *password = NULL, *expression;
54 krb5_keyblock *new_keys;
55 krb5_key_salt_tuple *ks_tuple = NULL;
56 int keepold = FALSE;
57 int n_ks_tuple = 0;
58 int n_keys;
59 char **princs;
60 int n_princs;
61 int keys_ok = 0;
62 krb5_storage *rsp = NULL; /* response goes here */
63 krb5_storage *sp = NULL;
64 int len;
66 memset(&ent, 0, sizeof(ent));
67 memset(&ent_prev, 0, sizeof(ent_prev));
68 krb5_data_zero(out);
70 ret = krb5_unparse_name_fixed(contextp->context, contextp->caller,
71 client, sizeof(client));
72 if (ret == 0) {
73 rsp = krb5_storage_emem();
74 sp = krb5_storage_from_data(in);
75 if (rsp == NULL || sp == NULL)
76 ret = krb5_enomem(contextp->context);
78 if (ret == 0)
79 ret = krb5_ret_int32(sp, &cmd);
80 if (ret)
81 goto fail;
83 switch(cmd){
84 case kadm_get:{
85 op = "GET";
86 ret = krb5_ret_principal(sp, &princ);
87 if(ret)
88 goto fail;
89 ret = krb5_ret_int32(sp, &mask);
90 if (ret)
91 goto fail;
93 mask |= KADM5_PRINCIPAL;
94 krb5_unparse_name_fixed(contextp->context, princ, name, sizeof(name));
95 krb5_warnx(contextp->context, "%s: %s %s", client, op, name);
97 /* If the caller doesn't have KADM5_PRIV_GET, we're done. */
98 ret = _kadm5_acl_check_permission(contextp, KADM5_PRIV_GET, princ);
99 if (ret)
100 goto fail;
102 /* Then check to see if it is ok to return keys */
103 if ((mask & KADM5_KEY_DATA) != 0) {
104 ret = _kadm5_acl_check_permission(contextp, KADM5_PRIV_GET_KEYS,
105 princ);
106 if (ret == 0) {
107 keys_ok = 1;
108 } else if ((mask == (KADM5_PRINCIPAL|KADM5_KEY_DATA)) ||
109 (mask == (KADM5_PRINCIPAL|KADM5_KVNO|KADM5_KEY_DATA))) {
111 * Requests for keys will get bogus keys, which is useful if
112 * the client just wants to see what (kvno, enctype)s the
113 * principal has keys for, but terrible if the client wants to
114 * write the keys into a keytab or modify the principal and
115 * write the bogus keys back to the server.
117 * We use a heuristic to detect which case we're handling here.
118 * If the client only asks for the flags in the above
119 * condition, then it's very likely a kadmin ext_keytab,
120 * add_enctype, or other request that should not see bogus
121 * keys. We deny them.
123 * The kadmin get command can be coaxed into making a request
124 * with the same mask. But the default long and terse output
125 * modes request other things too, so in all likelihood this
126 * heuristic will not hurt any kadmin get uses.
128 goto fail;
132 ret = kadm5_get_principal(kadm_handlep, princ, &ent, mask);
133 ret_sp = krb5_store_int32(rsp, ret);
134 if (ret == 0) {
135 if (ret_sp == 0 && keys_ok)
136 ret_sp = kadm5_store_principal_ent(rsp, &ent);
137 else if (ret_sp == 0)
138 ret_sp = kadm5_store_principal_ent_nokeys(rsp, &ent);
140 kadm5_free_principal_ent(kadm_handlep, &ent);
141 break;
143 case kadm_delete:{
144 op = "DELETE";
145 if (readonly) {
146 ret = KADM5_READ_ONLY;
147 goto fail;
149 ret = krb5_ret_principal(sp, &princ);
150 if (ret == 0)
151 ret = krb5_unparse_name_fixed(contextp->context, princ, name, sizeof(name));
152 if (ret == 0) {
153 ret = _kadm5_acl_check_permission(contextp, KADM5_PRIV_DELETE, princ);
154 krb5_warnx(contextp->context, "%s: %s %s (%s)", client, op, name,
155 ret == 0 ? "granted" : "denied");
159 * There's no need to check that the caller has permission to
160 * delete the victim principal's aliases.
162 if (ret == 0)
163 ret = kadm5_delete_principal(kadm_handlep, princ);
164 ret_sp = krb5_store_int32(rsp, ret);
165 break;
167 case kadm_create:{
168 op = "CREATE";
169 if (readonly) {
170 ret = KADM5_READ_ONLY;
171 goto fail;
173 ret = kadm5_ret_principal_ent(sp, &ent);
174 if(ret)
175 goto fail;
176 ret = krb5_ret_int32(sp, &mask);
177 if(ret){
178 kadm5_free_principal_ent(kadm_handlep, &ent);
179 goto fail;
181 ret = krb5_ret_string(sp, &password);
182 if(ret){
183 kadm5_free_principal_ent(kadm_handlep, &ent);
184 goto fail;
186 krb5_unparse_name_fixed(contextp->context, ent.principal,
187 name, sizeof(name));
188 krb5_warnx(contextp->context, "%s: %s %s", client, op, name);
189 ret = _kadm5_acl_check_permission(contextp, KADM5_PRIV_ADD,
190 ent.principal);
191 if(ret){
192 kadm5_free_principal_ent(kadm_handlep, &ent);
193 goto fail;
195 if ((mask & KADM5_TL_DATA)) {
197 * Also check that the caller can create the aliases, if the
198 * new principal has any.
200 ret = check_aliases(contextp, &ent, NULL);
201 if (ret) {
202 kadm5_free_principal_ent(kadm_handlep, &ent);
203 goto fail;
206 ret = kadm5_create_principal(kadm_handlep, &ent,
207 mask, password);
208 kadm5_free_principal_ent(kadm_handlep, &ent);
209 ret_sp = krb5_store_int32(rsp, ret);
210 break;
212 case kadm_modify:{
213 op = "MODIFY";
214 if (readonly) {
215 ret = KADM5_READ_ONLY;
216 goto fail;
218 ret = kadm5_ret_principal_ent(sp, &ent);
219 if(ret)
220 goto fail;
221 ret = krb5_ret_int32(sp, &mask);
222 if(ret){
223 kadm5_free_principal_ent(contextp, &ent);
224 goto fail;
226 krb5_unparse_name_fixed(contextp->context, ent.principal,
227 name, sizeof(name));
228 krb5_warnx(contextp->context, "%s: %s %s", client, op, name);
229 ret = _kadm5_acl_check_permission(contextp, KADM5_PRIV_MODIFY,
230 ent.principal);
231 if(ret){
232 kadm5_free_principal_ent(contextp, &ent);
233 goto fail;
235 if ((mask & KADM5_TL_DATA)) {
237 * Also check that the caller can create aliases that are in
238 * the new entry but not the old one. There's no need to
239 * check that the caller can delete aliases it wants to
240 * drop. See also handling of rename.
242 ret = kadm5_get_principal(kadm_handlep, ent.principal, &ent_prev, mask);
243 if (ret) {
244 kadm5_free_principal_ent(contextp, &ent);
245 goto fail;
247 ret = check_aliases(contextp, &ent, &ent_prev);
248 kadm5_free_principal_ent(contextp, &ent_prev);
249 if (ret) {
250 kadm5_free_principal_ent(contextp, &ent);
251 goto fail;
254 ret = kadm5_modify_principal(kadm_handlep, &ent, mask);
255 kadm5_free_principal_ent(kadm_handlep, &ent);
256 ret_sp = krb5_store_int32(rsp, ret);
257 break;
259 case kadm_prune:{
260 op = "PRUNE";
261 if (readonly) {
262 ret = KADM5_READ_ONLY;
263 goto fail;
265 ret = krb5_ret_principal(sp, &princ);
266 if (ret)
267 goto fail;
268 ret = krb5_ret_int32(sp, &kvno);
269 if (ret == HEIM_ERR_EOF) {
270 kvno = 0;
271 } else if (ret) {
272 goto fail;
274 krb5_unparse_name_fixed(contextp->context, princ, name, sizeof(name));
275 krb5_warnx(contextp->context, "%s: %s %s", client, op, name);
276 ret = _kadm5_acl_check_permission(contextp, KADM5_PRIV_CPW, princ);
277 if (ret)
278 goto fail;
280 ret = kadm5_prune_principal(kadm_handlep, princ, kvno);
281 ret_sp = krb5_store_int32(rsp, ret);
282 break;
284 case kadm_rename:{
285 op = "RENAME";
286 if (readonly) {
287 ret = KADM5_READ_ONLY;
288 goto fail;
290 ret = krb5_ret_principal(sp, &princ);
291 if(ret)
292 goto fail;
293 ret = krb5_ret_principal(sp, &princ2);
294 if (ret)
295 goto fail;
297 krb5_unparse_name_fixed(contextp->context, princ, name, sizeof(name));
298 krb5_unparse_name_fixed(contextp->context, princ2,
299 name2, sizeof(name2));
300 krb5_warnx(contextp->context, "%s: %s %s -> %s",
301 client, op, name, name2);
302 ret = _kadm5_acl_check_permission(contextp,
303 KADM5_PRIV_ADD,
304 princ2);
305 if (ret == 0) {
307 * Also require modify for the principal. For backwards
308 * compatibility, allow delete permission on the old name to
309 * cure lack of modify permission on the old name.
311 ret = _kadm5_acl_check_permission(contextp,
312 KADM5_PRIV_MODIFY,
313 princ);
314 if (ret) {
315 ret = _kadm5_acl_check_permission(contextp,
316 KADM5_PRIV_DELETE,
317 princ);
320 if (ret)
321 goto fail;
323 ret = kadm5_rename_principal(kadm_handlep, princ, princ2);
324 ret_sp = krb5_store_int32(sp, ret);
325 break;
327 case kadm_chpass:{
328 krb5_boolean is_self_cpw, allow_self_cpw;
330 op = "CHPASS";
331 if (readonly) {
332 ret = KADM5_READ_ONLY;
333 goto fail;
335 ret = krb5_ret_principal(sp, &princ);
336 if (ret == 0)
337 ret = krb5_ret_string(sp, &password);
338 if (ret == 0)
339 ret = krb5_ret_int32(sp, &keepold);
340 if (ret == HEIM_ERR_EOF)
341 ret = 0;
342 if (ret == 0) {
343 ret = krb5_unparse_name_fixed(contextp->context, princ, name, sizeof(name));
344 if (ret == 0)
345 krb5_warnx(contextp->context, "%s: %s %s", client, op, name);
347 if (ret)
348 goto fail;
351 * Change password requests are subject to ACLs unless the principal is
352 * changing their own password and the initial ticket flag is set, and
353 * the allow_self_change_password configuration option is TRUE.
355 is_self_cpw =
356 krb5_principal_compare(contextp->context, contextp->caller, princ);
357 allow_self_cpw =
358 krb5_config_get_bool_default(contextp->context, NULL, TRUE,
359 "kadmin", "allow_self_change_password", NULL);
360 if (!(is_self_cpw && initial && allow_self_cpw)) {
361 ret = _kadm5_acl_check_permission(contextp, KADM5_PRIV_CPW, princ);
362 if (ret)
363 goto fail;
366 ret = kadm5_chpass_principal_3(kadm_handlep, princ, keepold, 0, NULL,
367 password);
368 ret_sp = krb5_store_int32(rsp, ret);
369 break;
371 case kadm_chpass_with_key:{
372 int i;
373 krb5_key_data *key_data;
374 int n_key_data;
376 op = "CHPASS_WITH_KEY";
377 if (readonly) {
378 ret = KADM5_READ_ONLY;
379 goto fail;
381 ret = krb5_ret_principal(sp, &princ);
382 if (ret == 0)
383 ret = krb5_ret_int32(sp, &n_key_data);
384 if (ret == 0)
385 ret = krb5_ret_int32(sp, &keepold);
386 if (ret == HEIM_ERR_EOF)
387 ret = 0;
388 if (ret)
389 goto fail;
391 /* n_key_data will be squeezed into an int16_t below. */
392 if (n_key_data < 0 || n_key_data >= 1 << 16 ||
393 (size_t)n_key_data > UINT_MAX/sizeof(*key_data)) {
394 ret = ERANGE;
395 goto fail;
398 key_data = malloc (n_key_data * sizeof(*key_data));
399 if (key_data == NULL && n_key_data != 0) {
400 ret = krb5_enomem(contextp->context);
401 goto fail;
404 for (i = 0; i < n_key_data; ++i) {
405 ret = kadm5_ret_key_data (sp, &key_data[i]);
406 if (ret) {
407 int16_t dummy = i;
409 kadm5_free_key_data (contextp, &dummy, key_data);
410 free (key_data);
411 goto fail;
416 * The change is only allowed if the user is on the CPW ACL,
417 * this it to force password quality check on the user.
420 ret = _kadm5_acl_check_permission(contextp, KADM5_PRIV_CPW, princ);
421 ret_sp = krb5_unparse_name_fixed(contextp->context, princ, name, sizeof(name));
422 if (ret_sp == 0)
423 krb5_warnx(contextp->context, "%s: %s %s (%s)", client, op, name,
424 ret ? "denied" : "granted");
425 if(ret) {
426 int16_t dummy = n_key_data;
428 kadm5_free_key_data (contextp, &dummy, key_data);
429 free (key_data);
430 goto fail;
432 ret = kadm5_chpass_principal_with_key_3(kadm_handlep, princ, keepold,
433 n_key_data, key_data);
435 int16_t dummy = n_key_data;
436 kadm5_free_key_data (contextp, &dummy, key_data);
438 free (key_data);
439 ret_sp = krb5_store_int32(rsp, ret);
440 break;
442 case kadm_randkey:{
443 size_t i;
445 op = "RANDKEY";
446 if (readonly) {
447 ret = KADM5_READ_ONLY;
448 goto fail;
450 ret = krb5_ret_principal(sp, &princ);
451 if (ret)
452 goto fail;
453 krb5_unparse_name_fixed(contextp->context, princ, name, sizeof(name));
454 krb5_warnx(contextp->context, "%s: %s %s", client, op, name);
456 * The change is allowed if at least one of:
457 * a) it's for the principal him/herself and this was an initial ticket
458 * b) the user is on the CPW ACL.
461 if (initial
462 && krb5_principal_compare (contextp->context, contextp->caller,
463 princ))
464 ret = 0;
465 else
466 ret = _kadm5_acl_check_permission(contextp, KADM5_PRIV_CPW, princ);
468 if (ret)
469 goto fail;
472 * See comments in kadm5_c_randkey_principal() regarding the
473 * protocol.
475 ret = krb5_ret_int32(sp, &keepold);
476 if (ret != 0 && ret != HEIM_ERR_EOF)
477 goto fail;
479 ret = krb5_ret_int32(sp, &n_ks_tuple);
480 if (ret == HEIM_ERR_EOF) {
481 const char *enctypes;
482 size_t n;
484 enctypes = krb5_config_get_string(contextp->context, NULL,
485 "realms",
486 krb5_principal_get_realm(contextp->context,
487 princ),
488 "supported_enctypes", NULL);
489 if (enctypes == NULL || enctypes[0] == '\0')
490 enctypes = "aes128-cts-hmac-sha1-96";
491 ret = krb5_string_to_keysalts2(contextp->context, enctypes,
492 &n, &ks_tuple);
493 n_ks_tuple = n;
495 if (ret != 0)
496 goto fail;
498 if (n_ks_tuple < 0) {
499 ret = EOVERFLOW;
500 goto fail;
502 free(ks_tuple);
503 if ((ks_tuple = calloc(n_ks_tuple, sizeof (*ks_tuple))) == NULL) {
504 ret = errno;
505 goto fail;
508 for (i = 0; i < n_ks_tuple; i++) {
509 ret = krb5_ret_int32(sp, &ks_tuple[i].ks_enctype);
510 if (ret != 0) {
511 free(ks_tuple);
512 goto fail;
514 ret = krb5_ret_int32(sp, &ks_tuple[i].ks_salttype);
515 if (ret != 0) {
516 free(ks_tuple);
517 goto fail;
520 ret = kadm5_randkey_principal_3(kadm_handlep, princ, keepold,
521 n_ks_tuple, ks_tuple, &new_keys,
522 &n_keys);
523 free(ks_tuple);
525 ret_sp = krb5_store_int32(rsp, ret);
526 if (ret == 0 && ret_sp == 0){
527 ret_sp = krb5_store_int32(rsp, n_keys);
528 for (i = 0; i < n_keys; i++){
529 if (ret_sp == 0)
530 ret_sp = krb5_store_keyblock(rsp, new_keys[i]);
531 krb5_free_keyblock_contents(contextp->context, &new_keys[i]);
533 free(new_keys);
535 break;
537 case kadm_get_privs:{
538 uint32_t privs;
539 ret = kadm5_get_privs(kadm_handlep, &privs);
540 if (ret == 0)
541 ret_sp = krb5_store_uint32(sp, privs);
542 break;
544 case kadm_get_princs:{
545 op = "LIST";
546 ret = krb5_ret_int32(sp, &tmp);
547 if(ret)
548 goto fail;
549 if(tmp){
550 ret = krb5_ret_string(sp, &expression);
551 if(ret)
552 goto fail;
553 }else
554 expression = NULL;
555 krb5_warnx(contextp->context, "%s: %s %s", client, op,
556 expression ? expression : "*");
557 ret = _kadm5_acl_check_permission(contextp, KADM5_PRIV_LIST, NULL);
558 if(ret){
559 free(expression);
560 goto fail;
562 ret = kadm5_get_principals(kadm_handlep, expression, &princs, &n_princs);
563 free(expression);
564 ret_sp = krb5_store_int32(rsp, ret);
565 if (ret == 0) {
566 int i;
568 ret_sp = krb5_store_int32(sp, n_princs);
569 for (i = 0; ret_sp == 0 && i < n_princs; i++)
570 ret_sp = krb5_store_string(sp, princs[i]);
571 kadm5_free_name_list(kadm_handlep, princs, &n_princs);
573 break;
575 default:
576 krb5_warnx(contextp->context, "%s: UNKNOWN OP %d", client, cmd);
577 ret_sp = krb5_store_int32(sp, KADM5_FAILURE);
578 break;
581 fail:
582 if (password != NULL) {
583 len = strlen(password);
584 memset_s(password, len, 0, len);
585 free(password);
587 krb5_storage_to_data(rsp, out);
588 krb5_storage_free(rsp);
589 krb5_storage_free(sp);
590 krb5_free_principal(contextp->context, princ);
591 krb5_free_principal(contextp->context, princ2);
592 if (ret)
593 krb5_warn(contextp->context, ret, "%s", op);
594 if (out->length == 0)
595 krb5_warn(contextp->context, ret, "%s: reply failed", op);
596 else if (ret_sp)
597 krb5_warn(contextp->context, ret, "%s: reply incomplete", op);
598 if (ret_sp)
599 return ret_sp;
600 return 0;
603 struct iter_aliases_ctx {
604 HDB_Ext_Aliases aliases;
605 krb5_tl_data *tl;
606 int alias_idx;
607 int done;
610 static kadm5_ret_t
611 iter_aliases(kadm5_principal_ent_rec *from,
612 struct iter_aliases_ctx *ctx,
613 krb5_principal *out)
615 HDB_extension ext;
616 kadm5_ret_t ret;
617 size_t size;
619 *out = NULL;
621 if (ctx->done > 0)
622 return 0;
623 if (from == NULL) {
624 ctx->done = 1;
625 return 0;
628 if (ctx->done == 0) {
629 if (ctx->alias_idx < ctx->aliases.aliases.len) {
630 *out = &ctx->aliases.aliases.val[ctx->alias_idx++];
631 return 0;
633 /* Out of aliases in this TL, step to next TL */
634 ctx->tl = ctx->tl->tl_data_next;
635 } else if (ctx->done < 0) {
636 /* Setup iteration context */
637 memset(ctx, 0, sizeof(*ctx));
638 ctx->done = 0;
639 ctx->aliases.aliases.val = NULL;
640 ctx->aliases.aliases.len = 0;
641 ctx->tl = from->tl_data;
644 free_HDB_Ext_Aliases(&ctx->aliases);
645 ctx->alias_idx = 0;
647 /* Find TL with aliases */
648 for (; ctx->tl != NULL; ctx->tl = ctx->tl->tl_data_next) {
649 if (ctx->tl->tl_data_type != KRB5_TL_EXTENSION)
650 continue;
652 ret = decode_HDB_extension(ctx->tl->tl_data_contents,
653 ctx->tl->tl_data_length,
654 &ext, &size);
655 if (ret)
656 return ret;
657 if (ext.data.element == choice_HDB_extension_data_aliases &&
658 ext.data.u.aliases.aliases.len > 0) {
659 ctx->aliases = ext.data.u.aliases;
660 break;
662 free_HDB_extension(&ext);
665 if (ctx->tl != NULL && ctx->aliases.aliases.len > 0) {
666 *out = &ctx->aliases.aliases.val[ctx->alias_idx++];
667 return 0;
670 ctx->done = 1;
671 return 0;
674 static kadm5_ret_t
675 check_aliases(kadm5_server_context *contextp,
676 kadm5_principal_ent_rec *add_princ,
677 kadm5_principal_ent_rec *del_princ)
679 kadm5_ret_t ret;
680 struct iter_aliases_ctx iter;
681 struct iter_aliases_ctx iter_del;
682 krb5_principal new_name, old_name;
683 int match;
686 * Yeah, this is O(N^2). Gathering and sorting all the aliases
687 * would be a bit of a pain; if we ever have principals with enough
688 * aliases for this to be a problem, we can fix it then.
690 for (iter.done = -1; iter.done != 1;) {
691 match = 0;
692 ret = iter_aliases(add_princ, &iter, &new_name);
693 if (ret)
694 return ret;
695 if (iter.done == 1)
696 break;
697 for (iter_del.done = -1; iter_del.done != 1;) {
698 ret = iter_aliases(del_princ, &iter_del, &old_name);
699 if (ret)
700 return ret;
701 if (iter_del.done == 1)
702 break;
703 if (!krb5_principal_compare(contextp->context, new_name, old_name))
704 continue;
705 free_HDB_Ext_Aliases(&iter_del.aliases);
706 match = 1;
707 break;
709 if (match)
710 continue;
711 ret = _kadm5_acl_check_permission(contextp, KADM5_PRIV_ADD, new_name);
712 if (ret) {
713 free_HDB_Ext_Aliases(&iter.aliases);
714 return ret;
718 return 0;
721 static void
722 v5_loop (krb5_context contextp,
723 krb5_auth_context ac,
724 krb5_boolean initial,
725 void *kadm_handlep,
726 krb5_socket_t fd,
727 int readonly)
729 krb5_error_code ret;
730 krb5_data in, out;
732 for (;;) {
733 doing_useful_work = 0;
734 if(term_flag)
735 exit(0);
736 ret = krb5_read_priv_message(contextp, ac, &fd, &in);
737 if(ret == HEIM_ERR_EOF)
738 exit(0);
739 if(ret)
740 krb5_err(contextp, 1, ret, "krb5_read_priv_message");
741 doing_useful_work = 1;
742 ret = kadmind_dispatch(kadm_handlep, initial, &in, &out, readonly);
743 if (ret)
744 krb5_err(contextp, 1, ret, "kadmind_dispatch");
745 krb5_data_free(&in);
746 ret = krb5_write_priv_message(contextp, ac, &fd, &out);
747 krb5_data_free(&out);
748 if(ret)
749 krb5_err(contextp, 1, ret, "krb5_write_priv_message");
753 static krb5_boolean
754 match_appl_version(const void *data, const char *appl_version)
756 unsigned minor;
757 if(sscanf(appl_version, "KADM0.%u", &minor) != 1)
758 return 0;
759 /*XXX*/
760 *(unsigned*)(intptr_t)data = minor;
761 return 1;
764 static void
765 handle_v5(krb5_context contextp,
766 krb5_keytab keytab,
767 krb5_socket_t fd,
768 int readonly)
770 krb5_error_code ret;
771 krb5_ticket *ticket;
772 char *server_name;
773 char *client;
774 void *kadm_handlep;
775 krb5_boolean initial;
776 krb5_auth_context ac = NULL;
777 unsigned kadm_version = 1;
778 kadm5_config_params realm_params;
780 ret = krb5_recvauth_match_version(contextp, &ac, &fd,
781 match_appl_version, &kadm_version,
782 NULL, KRB5_RECVAUTH_IGNORE_VERSION,
783 keytab, &ticket);
784 if (ret) {
785 krb5_err(contextp, 1, ret, "krb5_recvauth");
786 return;
788 ret = krb5_unparse_name(contextp, ticket->server, &server_name);
789 if (ret) {
790 krb5_err(contextp, 1, ret, "krb5_unparse_name");
791 krb5_free_ticket(contextp, ticket);
792 return;
794 if (strncmp(server_name, KADM5_ADMIN_SERVICE,
795 strlen(KADM5_ADMIN_SERVICE)) != 0) {
796 krb5_errx(contextp, 1, "ticket for strange principal (%s)", server_name);
797 krb5_free_ticket(contextp, ticket);
798 free(server_name);
799 return;
801 free(server_name);
803 memset(&realm_params, 0, sizeof(realm_params));
805 if(kadm_version == 1) {
806 krb5_data params;
807 ret = krb5_read_priv_message(contextp, ac, &fd, &params);
808 if (ret) {
809 krb5_err(contextp, 1, ret, "krb5_read_priv_message");
810 krb5_free_ticket(contextp, ticket);
811 return;
813 ret = _kadm5_unmarshal_params(contextp, &params, &realm_params);
814 if (ret) {
815 krb5_err(contextp, 1, ret,
816 "Could not read or parse kadm5 parameters");
817 krb5_free_ticket(contextp, ticket);
818 return;
822 initial = ticket->ticket.flags.initial;
823 ret = krb5_unparse_name(contextp, ticket->client, &client);
824 krb5_free_ticket(contextp, ticket);
825 if (ret) {
826 krb5_err(contextp, 1, ret, "krb5_unparse_name");
827 return;
829 ret = kadm5_s_init_with_password_ctx(contextp,
830 client,
831 NULL,
832 KADM5_ADMIN_SERVICE,
833 &realm_params,
834 0, 0,
835 &kadm_handlep);
836 if (ret) {
837 krb5_err(contextp, 1, ret, "kadm5_init_with_password_ctx");
838 return;
840 v5_loop(contextp, ac, initial, kadm_handlep, fd, readonly);
843 krb5_error_code
844 kadmind_loop(krb5_context contextp,
845 krb5_keytab keytab,
846 krb5_socket_t sock,
847 int readonly)
849 u_char buf[sizeof(KRB5_SENDAUTH_VERSION) + 4];
850 ssize_t n;
851 unsigned long len;
853 n = krb5_net_read(contextp, &sock, buf, 4);
854 if(n == 0)
855 exit(0);
856 if(n < 0)
857 krb5_err(contextp, 1, errno, "read");
858 _krb5_get_int(buf, &len, 4);
860 if (len == sizeof(KRB5_SENDAUTH_VERSION)) {
862 n = krb5_net_read(contextp, &sock, buf + 4, len);
863 if (n < 0)
864 krb5_err (contextp, 1, errno, "reading sendauth version");
865 if (n == 0)
866 krb5_errx (contextp, 1, "EOF reading sendauth version");
868 if(memcmp(buf + 4, KRB5_SENDAUTH_VERSION, len) == 0) {
869 handle_v5(contextp, keytab, sock, readonly);
870 return 0;
872 len += 4;
873 } else
874 len = 4;
876 handle_mit(contextp, buf, len, sock, readonly);
878 return 0;