Merge from vendor branch OPENSSL:
[dragonfly.git] / crypto / heimdal-0.6.3 / kadmin / version4.c
1 /*
2  * Copyright (c) 1999 - 2002 Kungliga Tekniska Högskolan
3  * (Royal Institute of Technology, Stockholm, Sweden). 
4  * All rights reserved. 
5  *
6  * Redistribution and use in source and binary forms, with or without 
7  * modification, are permitted provided that the following conditions 
8  * are met: 
9  *
10  * 1. Redistributions of source code must retain the above copyright 
11  *    notice, this list of conditions and the following disclaimer. 
12  *
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. 
16  *
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.
20  *
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. */
32
33 #include "kadmin_locl.h"
34 #include <krb5-private.h>
35
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>
43
44 RCSID("$Id: version4.c,v 1.29.2.1 2004/04/29 12:29:23 lha Exp $");
45
46 #define KADM_NO_OPCODE -1
47 #define KADM_NO_ENCRYPT -2
48
49 /*
50  * make an error packet if we fail encrypting
51  */
52
53 static void
54 make_you_lose_packet(int code, krb5_data *reply)
55 {
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);
59 }
60
61 static int
62 ret_fields(krb5_storage *sp, char *fields)
63 {
64     return krb5_storage_read(sp, fields, FLDSZ);
65 }
66
67 static int
68 store_fields(krb5_storage *sp, char *fields)
69 {
70     return krb5_storage_write(sp, fields, FLDSZ);
71 }
72
73 static void
74 ret_vals(krb5_storage *sp, Kadm_vals *vals)
75 {
76     int field;
77     char *tmp_string;
78
79     memset(vals, 0, sizeof(*vals));
80     
81     ret_fields(sp, vals->fields);
82     
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;
132             }
133         }
134     }
135 }
136
137 static void
138 store_vals(krb5_storage *sp, Kadm_vals *vals)
139 {
140     int field;
141     
142     store_fields(sp, vals->fields);
143
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;
182             }
183         }
184     }
185 }
186
187 static int
188 flags_4_to_5(char *flags)
189 {
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
211             }
212     }
213     return mask;
214 }
215
216 static void
217 ent_to_values(krb5_context context,
218               kadm5_principal_ent_t ent,
219               int32_t mask,
220               Kadm_vals *vals)
221 {
222     krb5_error_code ret;
223     char realm[REALM_SZ];
224     time_t exp = 0;
225
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);
232     }
233     if(mask & KADM5_PRINC_EXPIRE_TIME) {
234         if(ent->princ_expire_time != 0)
235             exp = ent->princ_expire_time;
236     }
237     if(mask & KADM5_PW_EXPIRATION) {
238         if(ent->pw_expiration != 0 && (exp == 0 || exp > ent->pw_expiration))
239             exp = ent->pw_expiration;
240     }
241     if(exp) {
242         vals->exp_date = exp;
243         SET_FIELD(KADM_EXPDATE, vals->fields);
244     }
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);
251     }
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
258         }
259         /* XXX the key itself? */
260     }
261 #ifdef EXTENDED_KADM
262     if(mask & KADM5_MOD_TIME) {
263         vals->mod_date = ent->mod_date;
264         SET_FIELD(KADM_MODDATE, vals->fields);
265     }
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);
271     }
272 #endif
273 }
274
275 /*
276  * convert the kadm4 values in `vals' to `ent' (and `mask')
277  */
278
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)
284 {
285     krb5_error_code ret;
286     *mask = 0;
287     memset(ent, 0, sizeof(*ent));
288     
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;
301     }
302     if(IS_FIELD(KADM_EXPDATE, vals->fields)) {
303         ent->princ_expire_time = vals->exp_date;
304         *mask |= KADM5_PRINC_EXPIRE_TIME;
305     }
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;
309     }
310     
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;
318
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;
328
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;
338         }
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;
343     }
344     
345 #ifdef EXTENDED_KADM
346     if(IS_FIELD(KADM_MODDATE, vals->fields)) {
347         ent->mod_date = vals->mod_date;
348         *mask |= KADM5_MOD_TIME;
349     }
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;
362     }
363 #endif
364     return 0;
365 }
366
367 /*
368  * Try to translate a KADM5 error code into a v4 kadmin one.
369  */
370
371 static int
372 error_code(int ret)
373 {
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 */
451     }
452 }
453
454 /*
455  * server functions
456  */
457
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)
465 {
466     char key[8];
467     char *password = NULL;
468     krb5_error_code ret;
469     
470     krb5_warnx(context, "v4-compat %s: CHPASS %s",
471                principal_string, principal_string); 
472
473     ret = krb5_storage_read(message, key + 4, 4);
474     ret = krb5_storage_read(message, key, 4);
475     ret = krb5_ret_stringz(message, &password);
476     
477     if(password) {
478         krb5_data pwd_data;
479         const char *tmp;
480
481         pwd_data.data   = password;
482         pwd_data.length = strlen(password);
483
484         tmp = kadm5_check_password_quality (context, principal, &pwd_data);
485
486         if (tmp != NULL) {
487             krb5_store_stringz (reply, (char *)tmp);
488             ret = KADM5_PASS_Q_DICT;
489             goto fail;
490         }
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;
507         }
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);
512     }
513
514     if(ret != 0) {
515         krb5_store_stringz(reply, (char*)krb5_get_err_text(context, ret));
516         goto fail;
517     }
518     return 0;
519 fail:
520     krb5_warn(context, ret, "v4-compat CHPASS");
521     return error_code(ret);
522 }
523
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)
531 {
532     int32_t mask;
533     kadm5_principal_ent_rec ent, out;
534     Kadm_vals values;
535     krb5_error_code ret;
536     char name[128];
537
538     ret_vals(message, &values);
539
540     ret = values_to_ent(context, &values, &ent, &mask);
541     if(ret)
542         goto fail;
543
544     krb5_unparse_name_fixed(context, ent.principal, name, sizeof(name));
545     krb5_warnx(context, "v4-compat %s: ADD %s",
546                principal_string, name);
547
548     ret = _kadm5_acl_check_permission (kadm_handle, KADM5_PRIV_ADD,
549                                        ent.principal);
550     if (ret)
551         goto fail;
552
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;
557     }
558
559     mask = KADM5_PRINCIPAL | KADM5_PRINC_EXPIRE_TIME | KADM5_MAX_LIFE |
560         KADM5_KEY_DATA | KADM5_MOD_TIME | KADM5_MOD_NAME;
561     
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);
571 }
572
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)
580 {
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];
587
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;
598
599     krb5_unparse_name_fixed(context, ent.principal, name, sizeof(name));
600     krb5_warnx(context, "v4-compat %s: GET %s",
601                principal_string, name);
602
603     ret = _kadm5_acl_check_permission (kadm_handle, KADM5_PRIV_GET,
604                                        ent.principal);
605     if (ret)
606         goto fail;
607
608     mask = flags_4_to_5(flags);
609
610     ret = kadm5_get_principal(kadm_handle, ent.principal, &out, mask);
611     kadm5_free_principal_ent(kadm_handle, &ent);
612
613     if (ret)
614         goto fail;
615     
616     ent_to_values(context, &out, mask, &values);
617
618     kadm5_free_principal_ent(kadm_handle, &out);
619
620     store_vals(reply, &values);
621     return 0;
622 fail:
623     krb5_warn(context, ret, "v4-compat GET");
624     return error_code(ret);
625 }
626
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)
634 {
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];
640     
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);
645     
646     ret = values_to_ent(context, &values2, &ent, &mask);
647     if(ret)
648         goto fail;
649
650     krb5_unparse_name_fixed(context, ent.principal, name, sizeof(name));
651     krb5_warnx(context, "v4-compat %s: MOD %s",
652                principal_string, name);
653
654     ret = _kadm5_acl_check_permission (kadm_handle, KADM5_PRIV_MODIFY,
655                                        ent.principal);
656     if (ret)
657         goto fail;
658
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;
664     }
665
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;
671     }
672
673     ent_to_values(context, &out, mask, &values1);
674
675     kadm5_free_principal_ent(kadm_handle, &ent);
676     kadm5_free_principal_ent(kadm_handle, &out);
677     
678     store_vals(reply, &values1);
679     return 0;
680 fail:
681     krb5_warn(context, ret, "v4-compat MOD");
682     return error_code(ret);
683 }
684
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)
692 {
693     Kadm_vals values;
694     kadm5_principal_ent_rec ent;
695     int32_t mask;
696     krb5_error_code ret;
697     char name[128];
698
699     ret_vals(message, &values);
700
701     ret = values_to_ent(context, &values, &ent, &mask);
702     if(ret)
703         goto fail;
704     
705     krb5_unparse_name_fixed(context, ent.principal, name, sizeof(name));
706     krb5_warnx(context, "v4-compat %s: DEL %s",
707                principal_string, name);
708
709     ret = _kadm5_acl_check_permission (kadm_handle, KADM5_PRIV_DELETE,
710                                        ent.principal);
711     if (ret)
712         goto fail;
713
714     ret = kadm5_delete_principal(kadm_handle, ent.principal);
715
716     kadm5_free_principal_ent(kadm_handle, &ent);
717
718     if (ret)
719         goto fail;
720
721     return 0;
722 fail:
723     krb5_warn(context, ret, "v4-compat ADD");
724     return error_code(ret);
725 }
726
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)
734 {
735     int retval;
736     int8_t command;
737     krb5_storage *sp_in, *sp_out;
738     
739     sp_in = krb5_storage_from_data(&msg);
740     krb5_ret_int8(sp_in, &command);
741     
742     sp_out = krb5_storage_emem();
743     krb5_storage_write(sp_out, KADM_VERSTR, KADM_VERSIZE);
744     krb5_store_int32(sp_out, 0);
745
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;
777     }
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);
782     }
783     krb5_storage_to_data(sp_out, reply);
784     krb5_storage_free(sp_out);
785     return retval;
786 }
787
788 /*
789  * Decode a v4 kadmin packet in `message' and create a reply in `reply'
790  */
791
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)
799 {
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;
814     
815     if(message.length < KADM_VERSIZE + 4
816        || strncmp(msg, KADM_VERSTR, KADM_VERSIZE) != 0) {
817         make_you_lose_packet (KADM_BAD_VER, reply);
818         return;
819     }
820
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;
825
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_lose_packet (KADM_LENGTH_ERROR, reply);
830         return;
831     }
832
833     memcpy(authent.dat, (char*)msg + off, authent.length);
834     off += authent.length;
835     
836     {
837         krb5_principal principal;
838         krb5_keyblock *key;
839
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_lose_packet (KADM_NOMEM, reply);
845             return;
846         }
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_lose_packet (KADM_NO_AUTH, reply);
853             return;
854         }
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_lose_packet(KADM_NO_AUTH, reply);
861             else
862                 /* XXX */
863                 make_you_lose_packet(KADM_NO_AUTH, reply);
864             krb5_warn(context, ret, "krb5_kt_read_service_key");
865             return;
866         }
867         
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);
873     }
874     
875     ret = krb_rd_req(&authent, sname, sinst, 
876                      client_addr->sin_addr.s_addr, &ad, NULL);
877
878     if(ret) {
879         make_you_lose_packet(ERROR_TABLE_BASE_krb + ret, reply);
880         krb5_warnx(context, "krb_rd_req: %d", ret);
881         return;
882     }
883
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_lose_packet (KADM_NOMEM, reply);
889         return;
890     }
891
892     krb5_unparse_name(context, client, &client_str);
893
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_lose_packet (KADM_NOMEM, reply);
903         goto out;
904     }
905     
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_lose_packet (KADM_BAD_CHK, reply);
910         goto out;
911     }
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_lose_packet (ERROR_TABLE_BASE_krb + ret, reply);
917         krb5_warnx(context, "krb_rd_priv: %d", ret);
918         goto out;
919     }
920
921     {
922         krb5_data d, r;
923         int retval;
924
925         d.data   = msg_dat.app_data;
926         d.length = msg_dat.app_length;
927         
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_lose_packet(KADM_NO_ENCRYPT, reply);
936             goto out;
937         }
938     }
939 out:
940     krb5_free_principal(context, client);
941     free(client_str);
942 }
943
944 void
945 handle_v4(krb5_context context,
946           krb5_keytab keytab,
947           int len,
948           int fd)
949 {
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;
955
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");
962
963     while(1) {
964         doing_useful_work = 0;
965         if(term_flag)
966             exit(0);
967         if(first) {
968             if (len < 2)
969                 krb5_errx(context, 1, "received too short len (%d < 2)", len);
970             /* first time around, we have already read len, and two
971                bytes of the version string */
972             krb5_data_alloc(&message, len);
973             memcpy(message.data, "KA", 2);
974             n = krb5_net_read(context, &fd, (char*)message.data + 2,
975                               len - 2);
976             if (n == 0)
977                 exit (0);
978             if (n < 0)
979                 krb5_err (context, 1, errno, "krb5_net_read");
980             first = 0;
981         } else {
982             char buf[2];
983             unsigned long tmp;
984             ssize_t n;
985
986             n = krb5_net_read(context, &fd, buf, sizeof(2));
987             if (n == 0)
988                 exit (0);
989             if (n < 0)
990                 krb5_err (context, 1, errno, "krb5_net_read");
991             _krb5_get_int(buf, &tmp, 2);
992             krb5_data_alloc(&message, tmp);
993             n = krb5_net_read(context, &fd, message.data, message.length);
994             if (n == 0)
995                 krb5_errx (context, 1, "EOF in krb5_net_read");
996             if (n < 0)
997                 krb5_err (context, 1, errno, "krb5_net_read");
998         }
999         doing_useful_work = 1;
1000         decode_packet(context, keytab, &admin_addr, &client_addr, 
1001                       message, &reply);
1002         krb5_data_free(&message);
1003         {
1004             char buf[2];
1005
1006             _krb5_put_int(buf, reply.length, sizeof(buf));
1007             n = krb5_net_write(context, &fd, buf, sizeof(buf));
1008             if (n < 0)
1009                 krb5_err (context, 1, errno, "krb5_net_write");
1010             n = krb5_net_write(context, &fd, reply.data, reply.length);
1011             if (n < 0)
1012                 krb5_err (context, 1, errno, "krb5_net_write");
1013             krb5_data_free(&reply);
1014         }
1015     }
1016 }