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, ¶ms);
790 if(ret)
791 krb5_err(contextp, 1, ret, "krb5_read_priv_message");
792 _kadm5_unmarshal_params(contextp, ¶ms, &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