This commit was manufactured by cvs2svn to create tag
[heimdal.git] / kadmin / version4.c
blobdeadedccee93fd3cda0ca93a39b03e682c1b7aa2
1 /*
2 * Copyright (c) 1999 - 2002 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 KTH nor the names of its contributors may be
18 * used to endorse or promote products derived from this software without
19 * specific prior written permission.
21 * THIS SOFTWARE IS PROVIDED BY KTH AND ITS CONTRIBUTORS ``AS IS'' AND ANY
22 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
24 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL KTH OR ITS CONTRIBUTORS BE
25 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
26 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
27 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
28 * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
29 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
30 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
31 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */
33 #include "kadmin_locl.h"
34 #include <krb5-private.h>
36 #define Principal krb4_Principal
37 #define kadm_get krb4_kadm_get
38 #undef ALLOC
39 #include <krb.h>
40 #include <kadm.h>
41 #include <krb_err.h>
42 #include <kadm_err.h>
44 RCSID("$Id$");
46 #define KADM_NO_OPCODE -1
47 #define KADM_NO_ENCRYPT -2
50 * make an error packet if we fail encrypting
53 static void
54 make_you_loose_packet(int code, krb5_data *reply)
56 krb5_data_alloc(reply, KADM_VERSIZE + 4);
57 memcpy(reply->data, KADM_ULOSE, KADM_VERSIZE);
58 _krb5_put_int((char*)reply->data + KADM_VERSIZE, code, 4);
61 static int
62 ret_fields(krb5_storage *sp, char *fields)
64 return krb5_storage_read(sp, fields, FLDSZ);
67 static int
68 store_fields(krb5_storage *sp, char *fields)
70 return krb5_storage_write(sp, fields, FLDSZ);
73 static void
74 ret_vals(krb5_storage *sp, Kadm_vals *vals)
76 int field;
77 char *tmp_string;
79 memset(vals, 0, sizeof(*vals));
81 ret_fields(sp, vals->fields);
83 for(field = 31; field >= 0; field--) {
84 if(IS_FIELD(field, vals->fields)) {
85 switch(field) {
86 case KADM_NAME:
87 krb5_ret_stringz(sp, &tmp_string);
88 strlcpy(vals->name, tmp_string, sizeof(vals->name));
89 free(tmp_string);
90 break;
91 case KADM_INST:
92 krb5_ret_stringz(sp, &tmp_string);
93 strlcpy(vals->instance, tmp_string,
94 sizeof(vals->instance));
95 free(tmp_string);
96 break;
97 case KADM_EXPDATE:
98 krb5_ret_int32(sp, &vals->exp_date);
99 break;
100 case KADM_ATTR:
101 krb5_ret_int16(sp, &vals->attributes);
102 break;
103 case KADM_MAXLIFE:
104 krb5_ret_int8(sp, &vals->max_life);
105 break;
106 case KADM_DESKEY:
107 krb5_ret_int32(sp, &vals->key_high);
108 krb5_ret_int32(sp, &vals->key_low);
109 break;
110 #ifdef EXTENDED_KADM
111 case KADM_MODDATE:
112 krb5_ret_int32(sp, &vals->mod_date);
113 break;
114 case KADM_MODNAME:
115 krb5_ret_stringz(sp, &tmp_string);
116 strlcpy(vals->mod_name, tmp_string,
117 sizeof(vals->mod_name));
118 free(tmp_string);
119 break;
120 case KADM_MODINST:
121 krb5_ret_stringz(sp, &tmp_string);
122 strlcpy(vals->mod_instance, tmp_string,
123 sizeof(vals->mod_instance));
124 free(tmp_string);
125 break;
126 case KADM_KVNO:
127 krb5_ret_int8(sp, &vals->key_version);
128 break;
129 #endif
130 default:
131 break;
137 static void
138 store_vals(krb5_storage *sp, Kadm_vals *vals)
140 int field;
142 store_fields(sp, vals->fields);
144 for(field = 31; field >= 0; field--) {
145 if(IS_FIELD(field, vals->fields)) {
146 switch(field) {
147 case KADM_NAME:
148 krb5_store_stringz(sp, vals->name);
149 break;
150 case KADM_INST:
151 krb5_store_stringz(sp, vals->instance);
152 break;
153 case KADM_EXPDATE:
154 krb5_store_int32(sp, vals->exp_date);
155 break;
156 case KADM_ATTR:
157 krb5_store_int16(sp, vals->attributes);
158 break;
159 case KADM_MAXLIFE:
160 krb5_store_int8(sp, vals->max_life);
161 break;
162 case KADM_DESKEY:
163 krb5_store_int32(sp, vals->key_high);
164 krb5_store_int32(sp, vals->key_low);
165 break;
166 #ifdef EXTENDED_KADM
167 case KADM_MODDATE:
168 krb5_store_int32(sp, vals->mod_date);
169 break;
170 case KADM_MODNAME:
171 krb5_store_stringz(sp, vals->mod_name);
172 break;
173 case KADM_MODINST:
174 krb5_store_stringz(sp, vals->mod_instance);
175 break;
176 case KADM_KVNO:
177 krb5_store_int8(sp, vals->key_version);
178 break;
179 #endif
180 default:
181 break;
187 static int
188 flags_4_to_5(char *flags)
190 int i;
191 int32_t mask = 0;
192 for(i = 31; i >= 0; i--) {
193 if(IS_FIELD(i, flags))
194 switch(i) {
195 case KADM_NAME:
196 case KADM_INST:
197 mask |= KADM5_PRINCIPAL;
198 case KADM_EXPDATE:
199 mask |= KADM5_PRINC_EXPIRE_TIME;
200 case KADM_MAXLIFE:
201 mask |= KADM5_MAX_LIFE;
202 #ifdef EXTENDED_KADM
203 case KADM_KVNO:
204 mask |= KADM5_KEY_DATA;
205 case KADM_MODDATE:
206 mask |= KADM5_MOD_TIME;
207 case KADM_MODNAME:
208 case KADM_MODINST:
209 mask |= KADM5_MOD_NAME;
210 #endif
213 return mask;
216 static void
217 ent_to_values(krb5_context context,
218 kadm5_principal_ent_t ent,
219 int32_t mask,
220 Kadm_vals *vals)
222 krb5_error_code ret;
223 char realm[REALM_SZ];
224 time_t exp = 0;
226 memset(vals, 0, sizeof(*vals));
227 if(mask & KADM5_PRINCIPAL) {
228 ret = krb5_524_conv_principal(context, ent->principal,
229 vals->name, vals->instance, realm);
230 SET_FIELD(KADM_NAME, vals->fields);
231 SET_FIELD(KADM_INST, vals->fields);
233 if(mask & KADM5_PRINC_EXPIRE_TIME) {
234 if(ent->princ_expire_time != 0)
235 exp = ent->princ_expire_time;
237 if(mask & KADM5_PW_EXPIRATION) {
238 if(ent->pw_expiration != 0 && (exp == 0 || exp > ent->pw_expiration))
239 exp = ent->pw_expiration;
241 if(exp) {
242 vals->exp_date = exp;
243 SET_FIELD(KADM_EXPDATE, vals->fields);
245 if(mask & KADM5_MAX_LIFE) {
246 if(ent->max_life == 0)
247 vals->max_life = 255;
248 else
249 vals->max_life = krb_time_to_life(0, ent->max_life);
250 SET_FIELD(KADM_MAXLIFE, vals->fields);
252 if(mask & KADM5_KEY_DATA) {
253 if(ent->n_key_data > 0) {
254 #ifdef EXTENDED_KADM
255 vals->key_version = ent->key_data[0].key_data_kvno;
256 SET_FIELD(KADM_KVNO, vals->fields);
257 #endif
259 /* XXX the key itself? */
261 #ifdef EXTENDED_KADM
262 if(mask & KADM5_MOD_TIME) {
263 vals->mod_date = ent->mod_date;
264 SET_FIELD(KADM_MODDATE, vals->fields);
266 if(mask & KADM5_MOD_NAME) {
267 krb5_524_conv_principal(context, ent->mod_name,
268 vals->mod_name, vals->mod_instance, realm);
269 SET_FIELD(KADM_MODNAME, vals->fields);
270 SET_FIELD(KADM_MODINST, vals->fields);
272 #endif
276 * convert the kadm4 values in `vals' to `ent' (and `mask')
279 static krb5_error_code
280 values_to_ent(krb5_context context,
281 Kadm_vals *vals,
282 kadm5_principal_ent_t ent,
283 int32_t *mask)
285 krb5_error_code ret;
286 *mask = 0;
287 memset(ent, 0, sizeof(*ent));
289 if(IS_FIELD(KADM_NAME, vals->fields)) {
290 char *inst = NULL;
291 if(IS_FIELD(KADM_INST, vals->fields))
292 inst = vals->instance;
293 ret = krb5_425_conv_principal(context,
294 vals->name,
295 inst,
296 NULL,
297 &ent->principal);
298 if(ret)
299 return ret;
300 *mask |= KADM5_PRINCIPAL;
302 if(IS_FIELD(KADM_EXPDATE, vals->fields)) {
303 ent->princ_expire_time = vals->exp_date;
304 *mask |= KADM5_PRINC_EXPIRE_TIME;
306 if(IS_FIELD(KADM_MAXLIFE, vals->fields)) {
307 ent->max_life = krb_life_to_time(0, vals->max_life);
308 *mask |= KADM5_MAX_LIFE;
311 if(IS_FIELD(KADM_DESKEY, vals->fields)) {
312 int i;
313 ent->key_data = calloc(3, sizeof(*ent->key_data));
314 if(ent->key_data == NULL)
315 return ENOMEM;
316 for(i = 0; i < 3; i++) {
317 u_int32_t key_low, key_high;
319 ent->key_data[i].key_data_ver = 2;
320 #ifdef EXTENDED_KADM
321 if(IS_FIELD(KADM_KVNO, vals->fields))
322 ent->key_data[i].key_data_kvno = vals->key_version;
323 #endif
324 ent->key_data[i].key_data_type[0] = ETYPE_DES_CBC_MD5;
325 ent->key_data[i].key_data_length[0] = 8;
326 if((ent->key_data[i].key_data_contents[0] = malloc(8)) == NULL)
327 return ENOMEM;
329 key_low = ntohl(vals->key_low);
330 key_high = ntohl(vals->key_high);
331 memcpy(ent->key_data[i].key_data_contents[0],
332 &key_low, 4);
333 memcpy((char*)ent->key_data[i].key_data_contents[0] + 4,
334 &key_high, 4);
335 ent->key_data[i].key_data_type[1] = KRB5_PW_SALT;
336 ent->key_data[i].key_data_length[1] = 0;
337 ent->key_data[i].key_data_contents[1] = NULL;
339 ent->key_data[1].key_data_type[0] = ETYPE_DES_CBC_MD4;
340 ent->key_data[2].key_data_type[0] = ETYPE_DES_CBC_CRC;
341 ent->n_key_data = 3;
342 *mask |= KADM5_KEY_DATA;
345 #ifdef EXTENDED_KADM
346 if(IS_FIELD(KADM_MODDATE, vals->fields)) {
347 ent->mod_date = vals->mod_date;
348 *mask |= KADM5_MOD_TIME;
350 if(IS_FIELD(KADM_MODNAME, vals->fields)) {
351 char *inst = NULL;
352 if(IS_FIELD(KADM_MODINST, vals->fields))
353 inst = vals->mod_instance;
354 ret = krb5_425_conv_principal(context,
355 vals->mod_name,
356 inst,
357 NULL,
358 &ent->mod_name);
359 if(ret)
360 return ret;
361 *mask |= KADM5_MOD_NAME;
363 #endif
364 return 0;
368 * Try to translate a KADM5 error code into a v4 kadmin one.
371 static int
372 error_code(int ret)
374 switch (ret) {
375 case 0:
376 return 0;
377 case KADM5_FAILURE :
378 case KADM5_AUTH_GET :
379 case KADM5_AUTH_ADD :
380 case KADM5_AUTH_MODIFY :
381 case KADM5_AUTH_DELETE :
382 case KADM5_AUTH_INSUFFICIENT :
383 return KADM_UNAUTH;
384 case KADM5_BAD_DB :
385 return KADM_UK_RERROR;
386 case KADM5_DUP :
387 return KADM_INUSE;
388 case KADM5_RPC_ERROR :
389 case KADM5_NO_SRV :
390 return KADM_NO_SERV;
391 case KADM5_NOT_INIT :
392 return KADM_NO_CONN;
393 case KADM5_UNK_PRINC :
394 return KADM_NOENTRY;
395 case KADM5_PASS_Q_TOOSHORT :
396 #ifdef KADM_PASS_Q_TOOSHORT
397 return KADM_PASS_Q_TOOSHORT;
398 #else
399 return KADM_INSECURE_PW;
400 #endif
401 case KADM5_PASS_Q_CLASS :
402 #ifdef KADM_PASS_Q_CLASS
403 return KADM_PASS_Q_CLASS;
404 #else
405 return KADM_INSECURE_PW;
406 #endif
407 case KADM5_PASS_Q_DICT :
408 #ifdef KADM_PASS_Q_DICT
409 return KADM_PASS_Q_DICT;
410 #else
411 return KADM_INSECURE_PW;
412 #endif
413 case KADM5_PASS_REUSE :
414 case KADM5_PASS_TOOSOON :
415 case KADM5_BAD_PASSWORD :
416 return KADM_INSECURE_PW;
417 case KADM5_PROTECT_PRINCIPAL :
418 return KADM_IMMUTABLE;
419 case KADM5_POLICY_REF :
420 case KADM5_INIT :
421 case KADM5_BAD_HIST_KEY :
422 case KADM5_UNK_POLICY :
423 case KADM5_BAD_MASK :
424 case KADM5_BAD_CLASS :
425 case KADM5_BAD_LENGTH :
426 case KADM5_BAD_POLICY :
427 case KADM5_BAD_PRINCIPAL :
428 case KADM5_BAD_AUX_ATTR :
429 case KADM5_BAD_HISTORY :
430 case KADM5_BAD_MIN_PASS_LIFE :
431 case KADM5_BAD_SERVER_HANDLE :
432 case KADM5_BAD_STRUCT_VERSION :
433 case KADM5_OLD_STRUCT_VERSION :
434 case KADM5_NEW_STRUCT_VERSION :
435 case KADM5_BAD_API_VERSION :
436 case KADM5_OLD_LIB_API_VERSION :
437 case KADM5_OLD_SERVER_API_VERSION :
438 case KADM5_NEW_LIB_API_VERSION :
439 case KADM5_NEW_SERVER_API_VERSION :
440 case KADM5_SECURE_PRINC_MISSING :
441 case KADM5_NO_RENAME_SALT :
442 case KADM5_BAD_CLIENT_PARAMS :
443 case KADM5_BAD_SERVER_PARAMS :
444 case KADM5_AUTH_LIST :
445 case KADM5_AUTH_CHANGEPW :
446 case KADM5_BAD_TL_TYPE :
447 case KADM5_MISSING_CONF_PARAMS :
448 case KADM5_BAD_SERVER_NAME :
449 default :
450 return KADM_UNAUTH; /* XXX */
455 * server functions
458 static int
459 kadm_ser_cpw(krb5_context context,
460 void *kadm_handle,
461 krb5_principal principal,
462 const char *principal_string,
463 krb5_storage *message,
464 krb5_storage *reply)
466 char key[8];
467 char *password = NULL;
468 krb5_error_code ret;
470 krb5_warnx(context, "v4-compat %s: CHPASS %s",
471 principal_string, principal_string);
473 ret = krb5_storage_read(message, key + 4, 4);
474 ret = krb5_storage_read(message, key, 4);
475 ret = krb5_ret_stringz(message, &password);
477 if(password) {
478 krb5_data pwd_data;
479 const char *tmp;
481 pwd_data.data = password;
482 pwd_data.length = strlen(password);
484 tmp = kadm5_check_password_quality (context, principal, &pwd_data);
486 if (tmp != NULL) {
487 krb5_store_stringz (reply, (char *)tmp);
488 ret = KADM5_PASS_Q_DICT;
489 goto fail;
491 ret = kadm5_chpass_principal(kadm_handle, principal, password);
492 } else {
493 krb5_key_data key_data[3];
494 int i;
495 for(i = 0; i < 3; i++) {
496 key_data[i].key_data_ver = 2;
497 key_data[i].key_data_kvno = 0;
498 /* key */
499 key_data[i].key_data_type[0] = ETYPE_DES_CBC_CRC;
500 key_data[i].key_data_length[0] = 8;
501 key_data[i].key_data_contents[0] = malloc(8);
502 memcpy(key_data[i].key_data_contents[0], &key, 8);
503 /* salt */
504 key_data[i].key_data_type[1] = KRB5_PW_SALT;
505 key_data[i].key_data_length[1] = 0;
506 key_data[i].key_data_contents[1] = NULL;
508 key_data[0].key_data_type[0] = ETYPE_DES_CBC_MD5;
509 key_data[1].key_data_type[0] = ETYPE_DES_CBC_MD4;
510 ret = kadm5_s_chpass_principal_with_key(kadm_handle,
511 principal, 3, key_data);
514 if(ret != 0) {
515 krb5_store_stringz(reply, (char*)krb5_get_err_text(context, ret));
516 goto fail;
518 return 0;
519 fail:
520 krb5_warn(context, ret, "v4-compat CHPASS");
521 return error_code(ret);
524 static int
525 kadm_ser_add(krb5_context context,
526 void *kadm_handle,
527 krb5_principal principal,
528 const char *principal_string,
529 krb5_storage *message,
530 krb5_storage *reply)
532 int32_t mask;
533 kadm5_principal_ent_rec ent, out;
534 Kadm_vals values;
535 krb5_error_code ret;
536 char name[128];
538 ret_vals(message, &values);
540 ret = values_to_ent(context, &values, &ent, &mask);
541 if(ret)
542 goto fail;
544 krb5_unparse_name_fixed(context, ent.principal, name, sizeof(name));
545 krb5_warnx(context, "v4-compat %s: ADD %s",
546 principal_string, name);
548 ret = _kadm5_acl_check_permission (kadm_handle, KADM5_PRIV_ADD,
549 ent.principal);
550 if (ret)
551 goto fail;
553 ret = kadm5_s_create_principal_with_key(kadm_handle, &ent, mask);
554 if(ret) {
555 kadm5_free_principal_ent(kadm_handle, &ent);
556 goto fail;
559 mask = KADM5_PRINCIPAL | KADM5_PRINC_EXPIRE_TIME | KADM5_MAX_LIFE |
560 KADM5_KEY_DATA | KADM5_MOD_TIME | KADM5_MOD_NAME;
562 kadm5_get_principal(kadm_handle, ent.principal, &out, mask);
563 ent_to_values(context, &out, mask, &values);
564 kadm5_free_principal_ent(kadm_handle, &ent);
565 kadm5_free_principal_ent(kadm_handle, &out);
566 store_vals(reply, &values);
567 return 0;
568 fail:
569 krb5_warn(context, ret, "v4-compat ADD");
570 return error_code(ret);
573 static int
574 kadm_ser_get(krb5_context context,
575 void *kadm_handle,
576 krb5_principal principal,
577 const char *principal_string,
578 krb5_storage *message,
579 krb5_storage *reply)
581 krb5_error_code ret;
582 Kadm_vals values;
583 kadm5_principal_ent_rec ent, out;
584 int32_t mask;
585 char flags[FLDSZ];
586 char name[128];
588 ret_vals(message, &values);
589 /* XXX BRAIN DAMAGE! these flags are not stored in the same order
590 as in the header */
591 krb5_ret_int8(message, &flags[3]);
592 krb5_ret_int8(message, &flags[2]);
593 krb5_ret_int8(message, &flags[1]);
594 krb5_ret_int8(message, &flags[0]);
595 ret = values_to_ent(context, &values, &ent, &mask);
596 if(ret)
597 goto fail;
599 krb5_unparse_name_fixed(context, ent.principal, name, sizeof(name));
600 krb5_warnx(context, "v4-compat %s: GET %s",
601 principal_string, name);
603 ret = _kadm5_acl_check_permission (kadm_handle, KADM5_PRIV_GET,
604 ent.principal);
605 if (ret)
606 goto fail;
608 mask = flags_4_to_5(flags);
610 ret = kadm5_get_principal(kadm_handle, ent.principal, &out, mask);
611 kadm5_free_principal_ent(kadm_handle, &ent);
613 if (ret)
614 goto fail;
616 ent_to_values(context, &out, mask, &values);
618 kadm5_free_principal_ent(kadm_handle, &out);
620 store_vals(reply, &values);
621 return 0;
622 fail:
623 krb5_warn(context, ret, "v4-compat GET");
624 return error_code(ret);
627 static int
628 kadm_ser_mod(krb5_context context,
629 void *kadm_handle,
630 krb5_principal principal,
631 const char *principal_string,
632 krb5_storage *message,
633 krb5_storage *reply)
635 Kadm_vals values1, values2;
636 kadm5_principal_ent_rec ent, out;
637 int32_t mask;
638 krb5_error_code ret;
639 char name[128];
641 ret_vals(message, &values1);
642 /* why are the old values sent? is the mask the same in the old and
643 the new entry? */
644 ret_vals(message, &values2);
646 ret = values_to_ent(context, &values2, &ent, &mask);
647 if(ret)
648 goto fail;
650 krb5_unparse_name_fixed(context, ent.principal, name, sizeof(name));
651 krb5_warnx(context, "v4-compat %s: MOD %s",
652 principal_string, name);
654 ret = _kadm5_acl_check_permission (kadm_handle, KADM5_PRIV_MODIFY,
655 ent.principal);
656 if (ret)
657 goto fail;
659 ret = kadm5_s_modify_principal(kadm_handle, &ent, mask);
660 if(ret) {
661 kadm5_free_principal_ent(kadm_handle, &ent);
662 krb5_warn(context, ret, "kadm5_s_modify_principal");
663 goto fail;
666 ret = kadm5_get_principal(kadm_handle, ent.principal, &out, mask);
667 if(ret) {
668 kadm5_free_principal_ent(kadm_handle, &ent);
669 krb5_warn(context, ret, "kadm5_s_modify_principal");
670 goto fail;
673 ent_to_values(context, &out, mask, &values1);
675 kadm5_free_principal_ent(kadm_handle, &ent);
676 kadm5_free_principal_ent(kadm_handle, &out);
678 store_vals(reply, &values1);
679 return 0;
680 fail:
681 krb5_warn(context, ret, "v4-compat MOD");
682 return error_code(ret);
685 static int
686 kadm_ser_del(krb5_context context,
687 void *kadm_handle,
688 krb5_principal principal,
689 const char *principal_string,
690 krb5_storage *message,
691 krb5_storage *reply)
693 Kadm_vals values;
694 kadm5_principal_ent_rec ent;
695 int32_t mask;
696 krb5_error_code ret;
697 char name[128];
699 ret_vals(message, &values);
701 ret = values_to_ent(context, &values, &ent, &mask);
702 if(ret)
703 goto fail;
705 krb5_unparse_name_fixed(context, ent.principal, name, sizeof(name));
706 krb5_warnx(context, "v4-compat %s: DEL %s",
707 principal_string, name);
709 ret = _kadm5_acl_check_permission (kadm_handle, KADM5_PRIV_DELETE,
710 ent.principal);
711 if (ret)
712 goto fail;
714 ret = kadm5_delete_principal(kadm_handle, ent.principal);
716 kadm5_free_principal_ent(kadm_handle, &ent);
718 if (ret)
719 goto fail;
721 return 0;
722 fail:
723 krb5_warn(context, ret, "v4-compat ADD");
724 return error_code(ret);
727 static int
728 dispatch(krb5_context context,
729 void *kadm_handle,
730 krb5_principal principal,
731 const char *principal_string,
732 krb5_data msg,
733 krb5_data *reply)
735 int retval;
736 int8_t command;
737 krb5_storage *sp_in, *sp_out;
739 sp_in = krb5_storage_from_data(&msg);
740 krb5_ret_int8(sp_in, &command);
742 sp_out = krb5_storage_emem();
743 krb5_storage_write(sp_out, KADM_VERSTR, KADM_VERSIZE);
744 krb5_store_int32(sp_out, 0);
746 switch(command) {
747 case CHANGE_PW:
748 retval = kadm_ser_cpw(context, kadm_handle, principal,
749 principal_string,
750 sp_in, sp_out);
751 break;
752 case ADD_ENT:
753 retval = kadm_ser_add(context, kadm_handle, principal,
754 principal_string,
755 sp_in, sp_out);
756 break;
757 case GET_ENT:
758 retval = kadm_ser_get(context, kadm_handle, principal,
759 principal_string,
760 sp_in, sp_out);
761 break;
762 case MOD_ENT:
763 retval = kadm_ser_mod(context, kadm_handle, principal,
764 principal_string,
765 sp_in, sp_out);
766 break;
767 case DEL_ENT:
768 retval = kadm_ser_del(context, kadm_handle, principal,
769 principal_string,
770 sp_in, sp_out);
771 break;
772 default:
773 krb5_warnx(context, "v4-compat %s: unknown opcode: %d",
774 principal_string, command);
775 retval = KADM_NO_OPCODE;
776 break;
778 krb5_storage_free(sp_in);
779 if(retval) {
780 krb5_storage_seek(sp_out, KADM_VERSIZE, SEEK_SET);
781 krb5_store_int32(sp_out, retval);
783 krb5_storage_to_data(sp_out, reply);
784 krb5_storage_free(sp_out);
785 return retval;
789 * Decode a v4 kadmin packet in `message' and create a reply in `reply'
792 static void
793 decode_packet(krb5_context context,
794 krb5_keytab keytab,
795 struct sockaddr_in *admin_addr,
796 struct sockaddr_in *client_addr,
797 krb5_data message,
798 krb5_data *reply)
800 int ret;
801 KTEXT_ST authent;
802 AUTH_DAT ad;
803 MSG_DAT msg_dat;
804 off_t off = 0;
805 unsigned long rlen;
806 char sname[] = "changepw", sinst[] = "kerberos";
807 unsigned long checksum;
808 des_key_schedule schedule;
809 char *msg = message.data;
810 void *kadm_handle;
811 krb5_principal client;
812 char *client_str;
813 krb5_keytab_entry entry;
815 if(message.length < KADM_VERSIZE + 4
816 || strncmp(msg, KADM_VERSTR, KADM_VERSIZE) != 0) {
817 make_you_loose_packet (KADM_BAD_VER, reply);
818 return;
821 off = KADM_VERSIZE;
822 off += _krb5_get_int(msg + off, &rlen, 4);
823 memset(&authent, 0, sizeof(authent));
824 authent.length = message.length - rlen - KADM_VERSIZE - 4;
826 if(rlen > message.length - KADM_VERSIZE - 4
827 || authent.length > MAX_KTXT_LEN) {
828 krb5_warnx(context, "received bad rlen (%lu)", (unsigned long)rlen);
829 make_you_loose_packet (KADM_LENGTH_ERROR, reply);
830 return;
833 memcpy(authent.dat, (char*)msg + off, authent.length);
834 off += authent.length;
837 krb5_principal principal;
838 krb5_keyblock *key;
840 ret = krb5_make_principal(context, &principal, NULL,
841 "changepw", "kerberos", NULL);
842 if (ret) {
843 krb5_warn (context, ret, "krb5_make_principal");
844 make_you_loose_packet (KADM_NOMEM, reply);
845 return;
847 ret = krb5_kt_get_entry (context, keytab, principal, 0,
848 ETYPE_DES_CBC_MD5, &entry);
849 krb5_kt_close (context, keytab);
850 if (ret) {
851 krb5_free_principal(context, principal);
852 make_you_loose_packet (KADM_NO_AUTH, reply);
853 return;
855 ret = krb5_copy_keyblock (context, &entry.keyblock,& key);
856 krb5_kt_free_entry(context, &entry);
857 krb5_free_principal(context, principal);
858 if(ret) {
859 if(ret == KRB5_KT_NOTFOUND)
860 make_you_loose_packet(KADM_NO_AUTH, reply);
861 else
862 /* XXX */
863 make_you_loose_packet(KADM_NO_AUTH, reply);
864 krb5_warn(context, ret, "krb5_kt_read_service_key");
865 return;
868 if(key->keyvalue.length != 8)
869 krb5_abortx(context, "key has wrong length (%lu)",
870 (unsigned long)key->keyvalue.length);
871 krb_set_key(key->keyvalue.data, 0);
872 krb5_free_keyblock(context, key);
875 ret = krb_rd_req(&authent, sname, sinst,
876 client_addr->sin_addr.s_addr, &ad, NULL);
878 if(ret) {
879 make_you_loose_packet(ERROR_TABLE_BASE_krb + ret, reply);
880 krb5_warnx(context, "krb_rd_req: %d", ret);
881 return;
884 ret = krb5_425_conv_principal(context, ad.pname, ad.pinst, ad.prealm,
885 &client);
886 if (ret) {
887 krb5_warnx (context, "krb5_425_conv_principal: %d", ret);
888 make_you_loose_packet (KADM_NOMEM, reply);
889 return;
892 krb5_unparse_name(context, client, &client_str);
894 ret = kadm5_init_with_password_ctx(context,
895 client_str,
896 NULL,
897 KADM5_ADMIN_SERVICE,
898 NULL, 0, 0,
899 &kadm_handle);
900 if (ret) {
901 krb5_warn (context, ret, "kadm5_init_with_password_ctx");
902 make_you_loose_packet (KADM_NOMEM, reply);
903 goto out;
906 checksum = des_quad_cksum((void *)(msg + off), NULL, rlen, 0, &ad.session);
907 if(checksum != ad.checksum) {
908 krb5_warnx(context, "decode_packet: bad checksum");
909 make_you_loose_packet (KADM_BAD_CHK, reply);
910 goto out;
912 des_set_key(&ad.session, schedule);
913 ret = krb_rd_priv(msg + off, rlen, schedule, &ad.session,
914 client_addr, admin_addr, &msg_dat);
915 if (ret) {
916 make_you_loose_packet (ERROR_TABLE_BASE_krb + ret, reply);
917 krb5_warnx(context, "krb_rd_priv: %d", ret);
918 goto out;
922 krb5_data d, r;
923 int retval;
925 d.data = msg_dat.app_data;
926 d.length = msg_dat.app_length;
928 retval = dispatch(context, kadm_handle,
929 client, client_str, d, &r);
930 krb5_data_alloc(reply, r.length + 26);
931 reply->length = krb_mk_priv(r.data, reply->data, r.length,
932 schedule, &ad.session,
933 admin_addr, client_addr);
934 if((ssize_t)reply->length < 0) {
935 make_you_loose_packet(KADM_NO_ENCRYPT, reply);
936 goto out;
939 out:
940 krb5_free_principal(context, client);
941 free(client_str);
944 void
945 handle_v4(krb5_context context,
946 krb5_keytab keytab,
947 int len,
948 int fd)
950 int first = 1;
951 struct sockaddr_in admin_addr, client_addr;
952 socklen_t addr_len;
953 krb5_data message, reply;
954 ssize_t n;
956 addr_len = sizeof(client_addr);
957 if (getsockname(fd, (struct sockaddr*)&admin_addr, &addr_len) < 0)
958 krb5_errx (context, 1, "getsockname");
959 addr_len = sizeof(client_addr);
960 if (getpeername(fd, (struct sockaddr*)&client_addr, &addr_len) < 0)
961 krb5_errx (context, 1, "getpeername");
963 while(1) {
964 doing_useful_work = 0;
965 if(term_flag)
966 exit(0);
967 if(first) {
968 /* first time around, we have already read len, and two
969 bytes of the version string */
970 krb5_data_alloc(&message, len);
971 memcpy(message.data, "KA", 2);
972 n = krb5_net_read(context, &fd, (char*)message.data + 2,
973 len - 2);
974 if (n == 0)
975 exit (0);
976 if (n < 0)
977 krb5_err (context, 1, errno, "krb5_net_read");
978 first = 0;
979 } else {
980 char buf[2];
981 unsigned long tmp;
982 ssize_t n;
984 n = krb5_net_read(context, &fd, buf, sizeof(2));
985 if (n == 0)
986 exit (0);
987 if (n < 0)
988 krb5_err (context, 1, errno, "krb5_net_read");
989 _krb5_get_int(buf, &tmp, 2);
990 krb5_data_alloc(&message, tmp);
991 n = krb5_net_read(context, &fd, message.data, message.length);
992 if (n == 0)
993 krb5_errx (context, 1, "EOF in krb5_net_read");
994 if (n < 0)
995 krb5_err (context, 1, errno, "krb5_net_read");
997 doing_useful_work = 1;
998 decode_packet(context, keytab, &admin_addr, &client_addr,
999 message, &reply);
1000 krb5_data_free(&message);
1002 char buf[2];
1004 _krb5_put_int(buf, reply.length, sizeof(buf));
1005 n = krb5_net_write(context, &fd, buf, sizeof(buf));
1006 if (n < 0)
1007 krb5_err (context, 1, errno, "krb5_net_write");
1008 n = krb5_net_write(context, &fd, reply.data, reply.length);
1009 if (n < 0)
1010 krb5_err (context, 1, errno, "krb5_net_write");
1011 krb5_data_free(&reply);