]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - crypto/heimdal/kadmin/version4.c
unfinished sblive driver, playback/mixer only for now - not enabled in
[FreeBSD/FreeBSD.git] / crypto / heimdal / kadmin / version4.c
1 /*
2  * Copyright (c) 1999 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.16 1999/11/25 22:32:47 assar 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_loose_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 sp->fetch(sp, fields, FLDSZ);
65 }
66
67 static int
68 store_fields(krb5_storage *sp, char *fields)
69 {
70     return sp->store(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_PW_EXPIRATION;
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
225     memset(vals, 0, sizeof(*vals));
226     if(mask & KADM5_PRINCIPAL) {
227         ret = krb5_524_conv_principal(context, ent->principal, 
228                                       vals->name, vals->instance, realm);
229         SET_FIELD(KADM_NAME, vals->fields);
230         SET_FIELD(KADM_INST, vals->fields);
231     }
232     if(mask & KADM5_PW_EXPIRATION) {
233         time_t exp = 0;
234         if(ent->princ_expire_time != 0)
235             exp = ent->princ_expire_time;
236         if(ent->pw_expiration != 0 && (exp == 0 || exp > ent->pw_expiration))
237             exp = ent->pw_expiration;
238         if(exp) {
239             vals->exp_date = exp;
240             SET_FIELD(KADM_EXPDATE, vals->fields);
241         }
242     }
243     if(mask & KADM5_MAX_LIFE) {
244         if(ent->max_life == 0)
245             vals->max_life = 255;
246         else
247             vals->max_life = krb_time_to_life(0, ent->max_life);
248         SET_FIELD(KADM_MAXLIFE, vals->fields);
249     }
250     if(mask & KADM5_KEY_DATA) {
251         if(ent->n_key_data > 0) {
252 #ifdef EXTENDED_KADM
253         vals->key_version = ent->key_data[0].key_data_kvno;
254         SET_FIELD(KADM_KVNO, vals->fields);
255 #endif
256         }
257         /* XXX the key itself? */
258     }
259 #ifdef EXTENDED_KADM
260     if(mask & KADM5_MOD_TIME) {
261         vals->mod_date = ent->mod_date;
262         SET_FIELD(KADM_MODDATE, vals->fields);
263     }
264     if(mask & KADM5_MOD_NAME) {
265         krb5_524_conv_principal(context, ent->mod_name, 
266                                 vals->mod_name, vals->mod_instance, realm);
267         SET_FIELD(KADM_MODNAME, vals->fields);
268         SET_FIELD(KADM_MODINST, vals->fields);
269     }
270 #endif
271 }
272
273 /*
274  * convert the kadm4 values in `vals' to `ent' (and `mask')
275  */
276
277 static krb5_error_code
278 values_to_ent(krb5_context context,
279               Kadm_vals *vals, 
280               kadm5_principal_ent_t ent,
281               int32_t *mask)
282 {
283     krb5_error_code ret;
284     *mask = 0;
285     memset(ent, 0, sizeof(*ent));
286     
287     if(IS_FIELD(KADM_NAME, vals->fields)) {
288         char *inst = NULL;
289         if(IS_FIELD(KADM_INST, vals->fields))
290             inst = vals->instance;
291         ret = krb5_425_conv_principal(context, 
292                                       vals->name,
293                                       inst,
294                                       NULL,
295                                       &ent->principal);
296         if(ret)
297             return ret;
298         *mask |= KADM5_PRINCIPAL;
299     }
300     if(IS_FIELD(KADM_EXPDATE, vals->fields)) {
301         ent->pw_expiration = vals->exp_date;
302         *mask |= KADM5_PW_EXPIRATION;
303     }
304     if(IS_FIELD(KADM_MAXLIFE, vals->fields)) {
305         ent->max_life = krb_life_to_time(0, vals->max_life);
306         *mask |= KADM5_MAX_LIFE;
307     }
308     
309     if(IS_FIELD(KADM_DESKEY, vals->fields)) {
310         int i;
311         ent->key_data = calloc(3, sizeof(*ent->key_data));
312         if(ent->key_data == NULL)
313             return ENOMEM;
314         for(i = 0; i < 3; i++) {
315             u_int32_t key_low, key_high;
316
317             ent->key_data[i].key_data_ver = 2;
318 #ifdef EXTENDED_KADM
319             if(IS_FIELD(KADM_KVNO, vals->fields))
320                 ent->key_data[i].key_data_kvno = vals->key_version;
321 #endif
322             ent->key_data[i].key_data_type[0] = ETYPE_DES_CBC_MD5;
323             ent->key_data[i].key_data_length[0] = 8;
324             if((ent->key_data[i].key_data_contents[0] = malloc(8)) == NULL)
325                 return ENOMEM;
326
327             key_low  = ntohl(vals->key_low);
328             key_high = ntohl(vals->key_high);
329             memcpy(ent->key_data[i].key_data_contents[0],
330                    &key_low, 4);
331             memcpy((char*)ent->key_data[i].key_data_contents[0] + 4,
332                    &key_high, 4);
333             ent->key_data[i].key_data_type[1] = KRB5_PW_SALT;
334             ent->key_data[i].key_data_length[1] = 0;
335             ent->key_data[i].key_data_contents[1] = NULL;
336         }
337         ent->key_data[1].key_data_type[0] = ETYPE_DES_CBC_MD4;
338         ent->key_data[2].key_data_type[0] = ETYPE_DES_CBC_CRC;
339         ent->n_key_data = 3;
340         *mask |= KADM5_KEY_DATA;
341     }
342     
343 #ifdef EXTENDED_KADM
344     if(IS_FIELD(KADM_MODDATE, vals->fields)) {
345         ent->mod_date = vals->mod_date;
346         *mask |= KADM5_MOD_TIME;
347     }
348     if(IS_FIELD(KADM_MODNAME, vals->fields)) {
349         char *inst = NULL;
350         if(IS_FIELD(KADM_MODINST, vals->fields))
351             inst = vals->mod_instance;
352         ret = krb5_425_conv_principal(context, 
353                                       vals->mod_name,
354                                       inst,
355                                       NULL,
356                                       &ent->mod_name);
357         if(ret)
358             return ret;
359         *mask |= KADM5_MOD_NAME;
360     }
361 #endif
362     return 0;
363 }
364
365 /*
366  * Try to translate a KADM5 error code into a v4 kadmin one.
367  */
368
369 static int
370 error_code(int ret)
371 {
372     switch (ret) {
373     case 0:
374         return 0;
375     case KADM5_FAILURE :
376     case KADM5_AUTH_GET :
377     case KADM5_AUTH_ADD :
378     case KADM5_AUTH_MODIFY :
379     case KADM5_AUTH_DELETE :
380     case KADM5_AUTH_INSUFFICIENT :
381         return KADM_UNAUTH;
382     case KADM5_BAD_DB :
383         return KADM_UK_RERROR;
384     case KADM5_DUP :
385         return KADM_INUSE;
386     case KADM5_RPC_ERROR :
387     case KADM5_NO_SRV :
388         return KADM_NO_SERV;
389     case KADM5_NOT_INIT :
390         return KADM_NO_CONN;
391     case KADM5_UNK_PRINC :
392         return KADM_NOENTRY;
393     case KADM5_PASS_Q_TOOSHORT :
394 #ifdef KADM_PASS_Q_TOOSHORT
395         return KADM_PASS_Q_TOOSHORT;
396 #else
397         return KADM_INSECURE_PW;
398 #endif
399     case KADM5_PASS_Q_CLASS :
400 #ifdef KADM_PASS_Q_CLASS
401         return KADM_PASS_Q_CLASS;
402 #else
403         return KADM_INSECURE_PW;
404 #endif
405     case KADM5_PASS_Q_DICT :
406 #ifdef KADM_PASS_Q_DICT
407         return KADM_PASS_Q_DICT;
408 #else
409         return KADM_INSECURE_PW;
410 #endif
411     case KADM5_PASS_REUSE :
412     case KADM5_PASS_TOOSOON :
413     case KADM5_BAD_PASSWORD :
414         return KADM_INSECURE_PW;
415     case KADM5_PROTECT_PRINCIPAL :
416         return KADM_IMMUTABLE;
417     case KADM5_POLICY_REF :
418     case KADM5_INIT :
419     case KADM5_BAD_HIST_KEY :
420     case KADM5_UNK_POLICY :
421     case KADM5_BAD_MASK :
422     case KADM5_BAD_CLASS :
423     case KADM5_BAD_LENGTH :
424     case KADM5_BAD_POLICY :
425     case KADM5_BAD_PRINCIPAL :
426     case KADM5_BAD_AUX_ATTR :
427     case KADM5_BAD_HISTORY :
428     case KADM5_BAD_MIN_PASS_LIFE :
429     case KADM5_BAD_SERVER_HANDLE :
430     case KADM5_BAD_STRUCT_VERSION :
431     case KADM5_OLD_STRUCT_VERSION :
432     case KADM5_NEW_STRUCT_VERSION :
433     case KADM5_BAD_API_VERSION :
434     case KADM5_OLD_LIB_API_VERSION :
435     case KADM5_OLD_SERVER_API_VERSION :
436     case KADM5_NEW_LIB_API_VERSION :
437     case KADM5_NEW_SERVER_API_VERSION :
438     case KADM5_SECURE_PRINC_MISSING :
439     case KADM5_NO_RENAME_SALT :
440     case KADM5_BAD_CLIENT_PARAMS :
441     case KADM5_BAD_SERVER_PARAMS :
442     case KADM5_AUTH_LIST :
443     case KADM5_AUTH_CHANGEPW :
444     case KADM5_BAD_TL_TYPE :
445     case KADM5_MISSING_CONF_PARAMS :
446     case KADM5_BAD_SERVER_NAME :
447     default :
448         return KADM_UNAUTH;     /* XXX */
449     }
450 }
451
452 /*
453  * server functions
454  */
455
456 static int
457 kadm_ser_cpw(krb5_context context,
458              void *kadm_handle, 
459              krb5_principal principal, 
460              const char *principal_string,
461              krb5_storage *message,
462              krb5_storage *reply)
463 {
464     char key[8];
465     char *password = NULL;
466     krb5_error_code ret;
467     
468     krb5_warnx(context, "v4-compat %s: cpw %s",
469                principal_string, principal_string); 
470
471     ret = message->fetch(message, key + 4, 4);
472     ret = message->fetch(message, key, 4);
473     ret = krb5_ret_stringz(message, &password);
474     
475     if(password) {
476         krb5_data pwd_data;
477         const char *tmp;
478
479         pwd_data.data   = password;
480         pwd_data.length = strlen(password);
481
482         tmp = kadm5_check_password_quality (context, principal, &pwd_data);
483
484         if (tmp != NULL) {
485             krb5_store_stringz (reply, (char *)tmp);
486             ret = KADM5_PASS_Q_DICT;
487             goto fail;
488         }
489         ret = kadm5_chpass_principal(kadm_handle, principal, password);
490     } else {
491         krb5_key_data key_data[3];
492         int i;
493         for(i = 0; i < 3; i++) {
494             key_data[i].key_data_ver = 2;
495             key_data[i].key_data_kvno = 0;
496             /* key */
497             key_data[i].key_data_type[0] = ETYPE_DES_CBC_CRC;
498             key_data[i].key_data_length[0] = 8;
499             key_data[i].key_data_contents[0] = malloc(8);
500             memcpy(key_data[i].key_data_contents[0], &key, 8);
501             /* salt */
502             key_data[i].key_data_type[1] = KRB5_PW_SALT;
503             key_data[i].key_data_length[1] = 0;
504             key_data[i].key_data_contents[1] = NULL;
505         }
506         key_data[0].key_data_type[0] = ETYPE_DES_CBC_MD5;
507         key_data[1].key_data_type[0] = ETYPE_DES_CBC_MD4;
508         ret = kadm5_s_chpass_principal_with_key(kadm_handle, 
509                                                 principal, 3, key_data);
510     }
511
512     if(ret != 0) {
513         krb5_store_stringz(reply, (char*)krb5_get_err_text(context, ret));
514         goto fail;
515     }
516     return 0;
517 fail:
518     krb5_warn(context, ret, "v4-compat cpw");
519     return error_code(ret);
520 }
521
522 static int
523 kadm_ser_add(krb5_context context,
524              void *kadm_handle, 
525              krb5_principal principal, 
526              const char *principal_string,
527              krb5_storage *message,
528              krb5_storage *reply)
529 {
530     int32_t mask;
531     kadm5_principal_ent_rec ent, out;
532     Kadm_vals values;
533     krb5_error_code ret;
534     char name[128];
535
536     ret_vals(message, &values);
537
538     ret = values_to_ent(context, &values, &ent, &mask);
539     if(ret)
540         goto fail;
541
542     krb5_unparse_name_fixed(context, ent.principal, name, sizeof(name));
543     krb5_warnx(context, "v4-compat %s: add %s",
544                principal_string, name);
545
546     ret = _kadm5_acl_check_permission (kadm_handle, KADM5_PRIV_ADD);
547     if (ret)
548         goto fail;
549
550     ret = kadm5_s_create_principal_with_key(kadm_handle, &ent, mask);
551     if(ret) {
552         kadm5_free_principal_ent(kadm_handle, &ent);
553         goto fail;
554     }
555
556     mask = KADM5_PRINCIPAL | KADM5_PW_EXPIRATION | KADM5_MAX_LIFE |
557         KADM5_KEY_DATA | KADM5_MOD_TIME | KADM5_MOD_NAME;
558     
559     kadm5_get_principal(kadm_handle, ent.principal, &out, mask);
560     ent_to_values(context, &out, mask, &values);
561     kadm5_free_principal_ent(kadm_handle, &ent);
562     kadm5_free_principal_ent(kadm_handle, &out);
563     store_vals(reply, &values);
564     return 0;
565 fail:
566     krb5_warn(context, ret, "v4-compat add");
567     return error_code(ret);
568 }
569
570 static int
571 kadm_ser_get(krb5_context context,
572              void *kadm_handle, 
573              krb5_principal principal, 
574              const char *principal_string,
575              krb5_storage *message,
576              krb5_storage *reply)
577 {
578     krb5_error_code ret;
579     Kadm_vals values;
580     kadm5_principal_ent_rec ent, out;
581     int32_t mask;
582     char flags[FLDSZ];
583     char name[128];
584
585     ret_vals(message, &values);
586     /* XXX BRAIN DAMAGE! these flags are not stored in the same order
587        as in the header */
588     krb5_ret_int8(message, &flags[3]);
589     krb5_ret_int8(message, &flags[2]);
590     krb5_ret_int8(message, &flags[1]);
591     krb5_ret_int8(message, &flags[0]);
592     ret = values_to_ent(context, &values, &ent, &mask);
593     if(ret)
594         goto fail;
595
596     krb5_unparse_name_fixed(context, ent.principal, name, sizeof(name));
597     krb5_warnx(context, "v4-compat %s: get %s",
598                principal_string, name);
599
600     ret = _kadm5_acl_check_permission (kadm_handle, KADM5_PRIV_GET);
601     if (ret)
602         goto fail;
603
604     mask = flags_4_to_5(flags);
605
606     ret = kadm5_get_principal(kadm_handle, ent.principal, &out, mask);
607     kadm5_free_principal_ent(kadm_handle, &ent);
608
609     if (ret)
610         goto fail;
611     
612     ent_to_values(context, &out, mask, &values);
613
614     kadm5_free_principal_ent(kadm_handle, &out);
615
616     store_vals(reply, &values);
617     return 0;
618 fail:
619     krb5_warn(context, ret, "v4-compat get");
620     return error_code(ret);
621 }
622
623 static int
624 kadm_ser_mod(krb5_context context,
625              void *kadm_handle, 
626              krb5_principal principal, 
627              const char *principal_string,
628              krb5_storage *message,
629              krb5_storage *reply)
630 {
631     Kadm_vals values1, values2;
632     kadm5_principal_ent_rec ent, out;
633     int32_t mask;
634     krb5_error_code ret;
635     char name[128];
636     
637     ret_vals(message, &values1);
638     /* why are the old values sent? is the mask the same in the old and
639        the new entry? */
640     ret_vals(message, &values2);
641     
642     ret = values_to_ent(context, &values2, &ent, &mask);
643     if(ret)
644         goto fail;
645
646     krb5_unparse_name_fixed(context, ent.principal, name, sizeof(name));
647     krb5_warnx(context, "v4-compat %s: mod %s",
648                principal_string, name);
649
650     ret = _kadm5_acl_check_permission (kadm_handle, KADM5_PRIV_MODIFY);
651     if (ret)
652         goto fail;
653
654     ret = kadm5_s_modify_principal(kadm_handle, &ent, mask);
655     if(ret) {
656         kadm5_free_principal_ent(kadm_handle, &ent);
657         krb5_warn(context, ret, "kadm5_s_modify_principal");
658         goto fail;
659     }
660
661     ret = kadm5_get_principal(kadm_handle, ent.principal, &out, mask);
662     if(ret) {
663         kadm5_free_principal_ent(kadm_handle, &ent);
664         krb5_warn(context, ret, "kadm5_s_modify_principal");
665         goto fail;
666     }
667
668     ent_to_values(context, &out, mask, &values1);
669
670     kadm5_free_principal_ent(kadm_handle, &ent);
671     kadm5_free_principal_ent(kadm_handle, &out);
672     
673     store_vals(reply, &values1);
674     return 0;
675 fail:
676     krb5_warn(context, ret, "v4-compat mod");
677     return error_code(ret);
678 }
679
680 static int
681 kadm_ser_del(krb5_context context,
682              void *kadm_handle, 
683              krb5_principal principal, 
684              const char *principal_string,
685              krb5_storage *message,
686              krb5_storage *reply)
687 {
688     Kadm_vals values;
689     kadm5_principal_ent_rec ent;
690     int32_t mask;
691     krb5_error_code ret;
692     char name[128];
693
694     ret_vals(message, &values);
695
696     ret = values_to_ent(context, &values, &ent, &mask);
697     if(ret)
698         goto fail;
699     
700     krb5_unparse_name_fixed(context, ent.principal, name, sizeof(name));
701     krb5_warnx(context, "v4-compat %s: del %s",
702                principal_string, name);
703
704     ret = _kadm5_acl_check_permission (kadm_handle, KADM5_PRIV_DELETE);
705     if (ret)
706         goto fail;
707
708     ret = kadm5_delete_principal(kadm_handle, ent.principal);
709
710     kadm5_free_principal_ent(kadm_handle, &ent);
711
712     if (ret)
713         goto fail;
714
715     return 0;
716 fail:
717     krb5_warn(context, ret, "v4-compat add");
718     return error_code(ret);
719 }
720
721 static int
722 dispatch(krb5_context context,
723          void *kadm_handle,
724          krb5_principal principal,
725          const char *principal_string,
726          krb5_data msg, 
727          krb5_data *reply)
728 {
729     int retval;
730     int8_t command;
731     krb5_storage *sp_in, *sp_out;
732     
733     sp_in = krb5_storage_from_data(&msg);
734     krb5_ret_int8(sp_in, &command);
735     
736     sp_out = krb5_storage_emem();
737     sp_out->store(sp_out, KADM_VERSTR, KADM_VERSIZE);
738     krb5_store_int32(sp_out, 0);
739
740     switch(command) {
741     case CHANGE_PW:
742         retval = kadm_ser_cpw(context, kadm_handle, principal,
743                               principal_string,
744                               sp_in, sp_out);
745         break;
746     case ADD_ENT:
747         retval = kadm_ser_add(context, kadm_handle, principal,
748                               principal_string,
749                               sp_in, sp_out);
750         break;
751     case GET_ENT:
752         retval = kadm_ser_get(context, kadm_handle, principal,
753                               principal_string,
754                               sp_in, sp_out);
755         break;
756     case MOD_ENT:
757         retval = kadm_ser_mod(context, kadm_handle, principal,
758                               principal_string,
759                               sp_in, sp_out);
760         break;
761     case DEL_ENT:
762         retval = kadm_ser_del(context, kadm_handle, principal,
763                               principal_string,
764                               sp_in, sp_out);
765         break;
766     default:
767         krb5_warnx(context, "v4-compat %s: unknown opcode: %d",
768                    principal_string, command);
769         retval = KADM_NO_OPCODE;
770         break;
771     }
772     krb5_storage_free(sp_in);
773     if(retval) {
774         sp_out->seek(sp_out, KADM_VERSIZE, SEEK_SET);
775         krb5_store_int32(sp_out, retval);
776     }
777     krb5_storage_to_data(sp_out, reply);
778     krb5_storage_free(sp_out);
779     return retval;
780 }
781
782 /*
783  * Decode a v4 kadmin packet in `message' and create a reply in `reply'
784  */
785
786 static void
787 decode_packet(krb5_context context,
788               struct sockaddr_in *admin_addr,
789               struct sockaddr_in *client_addr,
790               krb5_data message,
791               krb5_data *reply)
792 {
793     int ret;
794     KTEXT_ST authent;
795     AUTH_DAT ad;
796     MSG_DAT msg_dat;
797     off_t off = 0;
798     unsigned long rlen;
799     char sname[] = "changepw", sinst[] = "kerberos";
800     unsigned long checksum;
801     des_key_schedule schedule;
802     char *msg = message.data;
803     void *kadm_handle;
804     krb5_principal client;
805     char *client_str;
806     
807     if(message.length < KADM_VERSIZE
808        || strncmp(msg, KADM_VERSTR, KADM_VERSIZE) != 0) {
809         make_you_loose_packet (KADM_BAD_VER, reply);
810         return;
811     }
812
813     off = KADM_VERSIZE;
814     off += _krb5_get_int(msg + off, &rlen, 4);
815     memset(&authent, 0, sizeof(authent));
816     authent.length = message.length - rlen - KADM_VERSIZE - 4;
817     memcpy(authent.dat, (char*)msg + off, authent.length);
818     off += authent.length;
819     
820     {
821         krb5_principal principal;
822         krb5_keyblock *key;
823
824         ret = krb5_make_principal(context, &principal, NULL, 
825                                   "changepw", "kerberos", NULL);
826         if (ret) {
827             krb5_warn (context, ret, "krb5_make_principal");
828             make_you_loose_packet (KADM_NOMEM, reply);
829             return;
830         }
831         ret = krb5_kt_read_service_key(context, 
832                                        "HDB:",
833                                        principal,
834                                        0,
835 /*                                     ETYPE_DES_CBC_CRC,*/
836                                        ETYPE_DES_CBC_MD5,
837                                        &key);
838         krb5_free_principal(context, principal);
839         if(ret) {
840             if(ret == KRB5_KT_NOTFOUND)
841                 make_you_loose_packet(KADM_NO_AUTH, reply);
842             else
843                 /* XXX */
844                 make_you_loose_packet(KADM_NO_AUTH, reply);
845             krb5_warn(context, ret, "krb5_kt_read_service_key");
846             return;
847         }
848         
849         if(key->keyvalue.length != 8)
850             krb5_abortx(context, "key has wrong length (%lu)", 
851                         (unsigned long)key->keyvalue.length);
852         krb_set_key(key->keyvalue.data, 0);
853         krb5_free_keyblock(context, key);
854     }
855     
856     ret = krb_rd_req(&authent, sname, sinst, 
857                      client_addr->sin_addr.s_addr, &ad, NULL);
858
859     if(ret) {
860         make_you_loose_packet(krb_err_base + ret, reply);
861         krb5_warnx(context, "krb_rd_req: %d", ret);
862         return;
863     }
864
865     krb5_425_conv_principal(context, ad.pname, ad.pinst, ad.prealm,
866                             &client);
867     krb5_unparse_name(context, client, &client_str);
868
869     ret = kadm5_init_with_password_ctx(context, 
870                                        client_str, 
871                                        NULL,
872                                        KADM5_ADMIN_SERVICE,
873                                        NULL, 0, 0, 
874                                        &kadm_handle);
875     if (ret) {
876         krb5_warn (context, ret, "kadm5_init_with_password_ctx");
877         make_you_loose_packet (KADM_NOMEM, reply);
878         goto out;
879     }
880     
881     checksum = des_quad_cksum((des_cblock*)(msg + off), NULL, rlen, 
882                               0, &ad.session);
883     if(checksum != ad.checksum) {
884         krb5_warnx(context, "decode_packet: bad checksum");
885         make_you_loose_packet (KADM_BAD_CHK, reply);
886         goto out;
887     }
888     des_set_key(&ad.session, schedule);
889     ret = krb_rd_priv(msg + off, rlen, schedule, &ad.session, 
890                       client_addr, admin_addr, &msg_dat);
891     if (ret) {
892         make_you_loose_packet (krb_err_base + ret, reply);
893         krb5_warnx(context, "krb_rd_priv: %d", ret);
894         goto out;
895     }
896
897     {
898         krb5_data d, r;
899         int retval;
900
901         d.data   = msg_dat.app_data;
902         d.length = msg_dat.app_length;
903         
904         retval = dispatch(context, kadm_handle,
905                           client, client_str, d, &r);
906         krb5_data_alloc(reply, r.length + 26);
907         reply->length = krb_mk_priv(r.data, reply->data, r.length, 
908                                     schedule, &ad.session, 
909                                     admin_addr, client_addr);
910         if((ssize_t)reply->length < 0) {
911             make_you_loose_packet(KADM_NO_ENCRYPT, reply);
912             goto out;
913         }
914     }
915 out:
916     krb5_free_principal(context, client);
917     free(client_str);
918 }
919
920 void
921 handle_v4(krb5_context context,
922           int len,
923           int fd)
924 {
925     int first = 1;
926     struct sockaddr_in admin_addr, client_addr;
927     int addr_len;
928     krb5_data message, reply;
929     ssize_t n;
930
931     addr_len = sizeof(client_addr);
932     if (getsockname(fd, (struct sockaddr*)&admin_addr, &addr_len) < 0)
933         krb5_errx (context, 1, "getsockname");
934     addr_len = sizeof(client_addr);
935     if (getpeername(fd, (struct sockaddr*)&client_addr, &addr_len) < 0)
936         krb5_errx (context, 1, "getpeername");
937
938     while(1) {
939         if(first) {
940             /* first time around, we have already read len, and two
941                bytes of the version string */
942             krb5_data_alloc(&message, len);
943             memcpy(message.data, "KA", 2);
944             n = krb5_net_read(context, &fd, (char*)message.data + 2,
945                               len - 2);
946             if (n == 0)
947                 exit (0);
948             if (n < 0)
949                 krb5_err (context, 1, errno, "krb5_net_read");
950             first = 0;
951         } else {
952             char buf[2];
953             unsigned long tmp;
954             ssize_t n;
955
956             n = krb5_net_read(context, &fd, buf, sizeof(2));
957             if (n == 0)
958                 exit (0);
959             if (n < 0)
960                 krb5_err (context, 1, errno, "krb5_net_read");
961             _krb5_get_int(buf, &tmp, 2);
962             krb5_data_alloc(&message, tmp);
963             n = krb5_net_read(context, &fd, message.data, message.length);
964             if (n == 0)
965                 krb5_errx (context, 1, "EOF in krb5_net_read");
966             if (n < 0)
967                 krb5_err (context, 1, errno, "krb5_net_read");
968         }
969         decode_packet(context, &admin_addr, &client_addr, 
970                       message, &reply);
971         krb5_data_free(&message);
972         {
973             char buf[2];
974
975             _krb5_put_int(buf, reply.length, sizeof(buf));
976             n = krb5_net_write(context, &fd, buf, sizeof(buf));
977             if (n < 0)
978                 krb5_err (context, 1, errno, "krb5_net_write");
979             n = krb5_net_write(context, &fd, reply.data, reply.length);
980             if (n < 0)
981                 krb5_err (context, 1, errno, "krb5_net_write");
982             krb5_data_free(&reply);
983         }
984     }
985 }