r15481: Update heimdal/ to match current lorikeet-heimdal.
[ab/samba.git/.git] / source4 / heimdal / kdc / kaserver.c
1 /*
2  * Copyright (c) 1997 - 2005 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 the Institute nor the names of its contributors 
18  *    may be used to endorse or promote products derived from this software 
19  *    without specific prior written permission. 
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND 
22  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 
23  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 
24  * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE 
25  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 
26  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 
27  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 
28  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 
29  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 
30  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 
31  * SUCH DAMAGE. 
32  */
33
34 #include "kdc_locl.h"
35
36 RCSID("$Id: kaserver.c,v 1.35 2006/05/05 10:49:50 lha Exp $");
37
38 #include <krb5-v4compat.h>
39 #include <rx.h>
40
41 #define KA_AUTHENTICATION_SERVICE 731
42 #define KA_TICKET_GRANTING_SERVICE 732
43 #define KA_MAINTENANCE_SERVICE 733
44
45 #define AUTHENTICATE_OLD         1
46 #define CHANGEPASSWORD           2
47 #define GETTICKET_OLD            3
48 #define SETPASSWORD              4
49 #define SETFIELDS                5
50 #define CREATEUSER               6
51 #define DELETEUSER               7
52 #define GETENTRY                 8
53 #define LISTENTRY                9
54 #define GETSTATS                10
55 #define DEBUG                   11
56 #define GETPASSWORD             12
57 #define GETRANDOMKEY            13
58 #define AUTHENTICATE            21
59 #define AUTHENTICATE_V2         22
60 #define GETTICKET               23
61
62 /* XXX - Where do we get these? */
63
64 #define RXGEN_OPCODE (-455)
65
66 #define KADATABASEINCONSISTENT                   (180480L)
67 #define KAEXIST                                  (180481L)
68 #define KAIO                                     (180482L)
69 #define KACREATEFAIL                             (180483L)
70 #define KANOENT                                  (180484L)
71 #define KAEMPTY                                  (180485L)
72 #define KABADNAME                                (180486L)
73 #define KABADINDEX                               (180487L)
74 #define KANOAUTH                                 (180488L)
75 #define KAANSWERTOOLONG                          (180489L)
76 #define KABADREQUEST                             (180490L)
77 #define KAOLDINTERFACE                           (180491L)
78 #define KABADARGUMENT                            (180492L)
79 #define KABADCMD                                 (180493L)
80 #define KANOKEYS                                 (180494L)
81 #define KAREADPW                                 (180495L)
82 #define KABADKEY                                 (180496L)
83 #define KAUBIKINIT                               (180497L)
84 #define KAUBIKCALL                               (180498L)
85 #define KABADPROTOCOL                            (180499L)
86 #define KANOCELLS                                (180500L)
87 #define KANOCELL                                 (180501L)
88 #define KATOOMANYUBIKS                           (180502L)
89 #define KATOOMANYKEYS                            (180503L)
90 #define KABADTICKET                              (180504L)
91 #define KAUNKNOWNKEY                             (180505L)
92 #define KAKEYCACHEINVALID                        (180506L)
93 #define KABADSERVER                              (180507L)
94 #define KABADUSER                                (180508L)
95 #define KABADCPW                                 (180509L)
96 #define KABADCREATE                              (180510L)
97 #define KANOTICKET                               (180511L)
98 #define KAASSOCUSER                              (180512L)
99 #define KANOTSPECIAL                             (180513L)
100 #define KACLOCKSKEW                              (180514L)
101 #define KANORECURSE                              (180515L)
102 #define KARXFAIL                                 (180516L)
103 #define KANULLPASSWORD                           (180517L)
104 #define KAINTERNALERROR                          (180518L)
105 #define KAPWEXPIRED                              (180519L)
106 #define KAREUSED                                 (180520L)
107 #define KATOOSOON                                (180521L)
108 #define KALOCKED                                 (180522L)
109
110
111 static krb5_error_code
112 decode_rx_header (krb5_storage *sp,
113                   struct rx_header *h)
114 {
115     krb5_error_code ret;
116
117     ret = krb5_ret_uint32(sp, &h->epoch);
118     if (ret) return ret;
119     ret = krb5_ret_uint32(sp, &h->connid);
120     if (ret) return ret;
121     ret = krb5_ret_uint32(sp, &h->callid);
122     if (ret) return ret;
123     ret = krb5_ret_uint32(sp, &h->seqno);
124     if (ret) return ret;
125     ret = krb5_ret_uint32(sp, &h->serialno);
126     if (ret) return ret;
127     ret = krb5_ret_uint8(sp,  &h->type);
128     if (ret) return ret;
129     ret = krb5_ret_uint8(sp,  &h->flags);
130     if (ret) return ret;
131     ret = krb5_ret_uint8(sp,  &h->status);
132     if (ret) return ret;
133     ret = krb5_ret_uint8(sp,  &h->secindex);
134     if (ret) return ret;
135     ret = krb5_ret_uint16(sp, &h->reserved);
136     if (ret) return ret;
137     ret = krb5_ret_uint16(sp, &h->serviceid);
138     if (ret) return ret;
139
140     return 0;
141 }
142
143 static krb5_error_code
144 encode_rx_header (struct rx_header *h,
145                   krb5_storage *sp)
146 {
147     krb5_error_code ret;
148
149     ret = krb5_store_uint32(sp, h->epoch);
150     if (ret) return ret;
151     ret = krb5_store_uint32(sp, h->connid);
152     if (ret) return ret;
153     ret = krb5_store_uint32(sp, h->callid);
154     if (ret) return ret;
155     ret = krb5_store_uint32(sp, h->seqno);
156     if (ret) return ret;
157     ret = krb5_store_uint32(sp, h->serialno);
158     if (ret) return ret;
159     ret = krb5_store_uint8(sp,  h->type);
160     if (ret) return ret;
161     ret = krb5_store_uint8(sp,  h->flags);
162     if (ret) return ret;
163     ret = krb5_store_uint8(sp,  h->status);
164     if (ret) return ret;
165     ret = krb5_store_uint8(sp,  h->secindex);
166     if (ret) return ret;
167     ret = krb5_store_uint16(sp, h->reserved);
168     if (ret) return ret;
169     ret = krb5_store_uint16(sp, h->serviceid);
170     if (ret) return ret;
171
172     return 0;
173 }
174
175 static void
176 init_reply_header (struct rx_header *hdr,
177                    struct rx_header *reply_hdr,
178                    u_char type,
179                    u_char flags)
180 {
181     reply_hdr->epoch     = hdr->epoch;
182     reply_hdr->connid    = hdr->connid;
183     reply_hdr->callid    = hdr->callid;
184     reply_hdr->seqno     = 1;
185     reply_hdr->serialno  = 1;
186     reply_hdr->type      = type;
187     reply_hdr->flags     = flags;
188     reply_hdr->status    = 0;
189     reply_hdr->secindex  = 0;
190     reply_hdr->reserved  = 0;
191     reply_hdr->serviceid = hdr->serviceid;
192 }
193
194 static void
195 make_error_reply (struct rx_header *hdr,
196                   uint32_t ret,
197                   krb5_data *reply)
198
199 {
200     krb5_storage *sp;
201     struct rx_header reply_hdr;
202
203     init_reply_header (hdr, &reply_hdr, HT_ABORT, HF_LAST);
204     sp = krb5_storage_emem();
205     ret = encode_rx_header (&reply_hdr, sp);
206     krb5_store_int32(sp, ret);
207     krb5_storage_to_data (sp, reply);
208     krb5_storage_free (sp);
209 }
210
211 static krb5_error_code
212 krb5_ret_xdr_data(krb5_storage *sp,
213                   krb5_data *data)
214 {
215     int ret;
216     int size;
217     ret = krb5_ret_int32(sp, &size);
218     if(ret)
219         return ret;
220     if(size < 0)
221         return ERANGE;
222     data->length = size;
223     if (size) {
224         u_char foo[4];
225         size_t pad = (4 - size % 4) % 4;
226
227         data->data = malloc(size);
228         if (data->data == NULL)
229             return ENOMEM;
230         ret = krb5_storage_read(sp, data->data, size);
231         if(ret != size)
232             return (ret < 0)? errno : KRB5_CC_END;
233         if (pad) {
234             ret = krb5_storage_read(sp, foo, pad);
235             if (ret != pad)
236                 return (ret < 0)? errno : KRB5_CC_END;
237         }
238     } else
239         data->data = NULL;
240     return 0;
241 }
242
243 static krb5_error_code
244 krb5_store_xdr_data(krb5_storage *sp,
245                     krb5_data data)
246 {
247     u_char zero[4] = {0, 0, 0, 0};
248     int ret;
249     size_t pad;
250
251     ret = krb5_store_int32(sp, data.length);
252     if(ret < 0)
253         return ret;
254     ret = krb5_storage_write(sp, data.data, data.length);
255     if(ret != data.length){
256         if(ret < 0)
257             return errno;
258         return KRB5_CC_END;
259     }
260     pad = (4 - data.length % 4) % 4;
261     if (pad) {
262         ret = krb5_storage_write(sp, zero, pad);
263         if (ret != pad) {
264             if (ret < 0)
265                 return errno;
266             return KRB5_CC_END;
267         }
268     }
269     return 0;
270 }
271
272
273 static krb5_error_code
274 create_reply_ticket (krb5_context context, 
275                      struct rx_header *hdr,
276                      Key *skey,
277                      char *name, char *instance, char *realm,
278                      struct sockaddr_in *addr,
279                      int life,
280                      int kvno,
281                      int32_t max_seq_len,
282                      const char *sname, const char *sinstance,
283                      uint32_t challenge,
284                      const char *label,
285                      krb5_keyblock *key,
286                      krb5_data *reply)
287 {
288     krb5_error_code ret;
289     krb5_data ticket;
290     krb5_keyblock session;
291     krb5_storage *sp;
292     krb5_data enc_data;
293     struct rx_header reply_hdr;
294     char zero[8];
295     size_t pad;
296     unsigned fyrtiosjuelva;
297
298     /* create the ticket */
299
300     krb5_generate_random_keyblock(context, ETYPE_DES_PCBC_NONE, &session);
301
302     _krb5_krb_create_ticket(context,
303                             0,
304                             name,
305                             instance,
306                             realm,
307                             addr->sin_addr.s_addr,
308                             &session,
309                             life,
310                             kdc_time,
311                             sname,
312                             sinstance,
313                             &skey->key,
314                             &ticket);
315
316     /* create the encrypted part of the reply */
317     sp = krb5_storage_emem ();
318     krb5_generate_random_block(&fyrtiosjuelva, sizeof(fyrtiosjuelva));
319     fyrtiosjuelva &= 0xffffffff;
320     krb5_store_int32 (sp, fyrtiosjuelva);
321     krb5_store_int32 (sp, challenge);
322     krb5_storage_write  (sp, session.keyvalue.data, 8);
323     krb5_free_keyblock_contents(context, &session);
324     krb5_store_int32 (sp, kdc_time);
325     krb5_store_int32 (sp, kdc_time + _krb5_krb_life_to_time (0, life));
326     krb5_store_int32 (sp, kvno);
327     krb5_store_int32 (sp, ticket.length);
328     krb5_store_stringz (sp, name);
329     krb5_store_stringz (sp, instance);
330 #if 1 /* XXX - Why shouldn't the realm go here? */
331     krb5_store_stringz (sp, "");
332 #else
333     krb5_store_stringz (sp, realm);
334 #endif
335     krb5_store_stringz (sp, sname);
336     krb5_store_stringz (sp, sinstance);
337     krb5_storage_write (sp, ticket.data, ticket.length);
338     krb5_storage_write (sp, label, strlen(label));
339
340     /* pad to DES block */
341     memset (zero, 0, sizeof(zero));
342     pad = (8 - krb5_storage_seek (sp, 0, SEEK_CUR) % 8) % 8;
343     krb5_storage_write (sp, zero, pad);
344
345     krb5_storage_to_data (sp, &enc_data);
346     krb5_storage_free (sp);
347
348     if (enc_data.length > max_seq_len) {
349         krb5_data_free (&enc_data);
350         make_error_reply (hdr, KAANSWERTOOLONG, reply);
351         return 0;
352     }
353
354     /* encrypt it */
355     {
356         DES_key_schedule schedule;
357         DES_cblock deskey;
358         
359         memcpy (&deskey, key->keyvalue.data, sizeof(deskey));
360         DES_set_key (&deskey, &schedule);
361         DES_pcbc_encrypt (enc_data.data,
362                           enc_data.data,
363                           enc_data.length,
364                           &schedule,
365                           &deskey,
366                           DES_ENCRYPT);
367         memset (&schedule, 0, sizeof(schedule));
368         memset (&deskey, 0, sizeof(deskey));
369     }
370
371     /* create the reply packet */
372     init_reply_header (hdr, &reply_hdr, HT_DATA, HF_LAST);
373     sp = krb5_storage_emem ();
374     ret = encode_rx_header (&reply_hdr, sp);
375     krb5_store_int32 (sp, max_seq_len);
376     krb5_store_xdr_data (sp, enc_data);
377     krb5_data_free (&enc_data);
378     krb5_storage_to_data (sp, reply);
379     krb5_storage_free (sp);
380     return 0;
381 }
382
383 static krb5_error_code
384 unparse_auth_args (krb5_storage *sp,
385                    char **name,
386                    char **instance,
387                    time_t *start_time,
388                    time_t *end_time,
389                    krb5_data *request,
390                    int32_t *max_seq_len)
391 {
392     krb5_data data;
393     int32_t tmp;
394
395     krb5_ret_xdr_data (sp, &data);
396     *name = malloc(data.length + 1);
397     if (*name == NULL)
398         return ENOMEM;
399     memcpy (*name, data.data, data.length);
400     (*name)[data.length] = '\0';
401     krb5_data_free (&data);
402
403     krb5_ret_xdr_data (sp, &data);
404     *instance = malloc(data.length + 1);
405     if (*instance == NULL) {
406         free (*name);
407         return ENOMEM;
408     }
409     memcpy (*instance, data.data, data.length);
410     (*instance)[data.length] = '\0';
411     krb5_data_free (&data);
412
413     krb5_ret_int32 (sp, &tmp);
414     *start_time = tmp;
415     krb5_ret_int32 (sp, &tmp);
416     *end_time = tmp;
417     krb5_ret_xdr_data (sp, request);
418     krb5_ret_int32 (sp, max_seq_len);
419     /* ignore the rest */
420     return 0;
421 }
422
423 static void
424 do_authenticate (krb5_context context, 
425                  krb5_kdc_configuration *config,
426                  struct rx_header *hdr,
427                  krb5_storage *sp,
428                  struct sockaddr_in *addr,
429                  const char *from,
430                  krb5_data *reply)
431 {
432     krb5_error_code ret;
433     char *name = NULL;
434     char *instance = NULL;
435     time_t start_time;
436     time_t end_time;
437     krb5_data request;
438     int32_t max_seq_len;
439     hdb_entry_ex *client_entry = NULL;
440     hdb_entry_ex *server_entry = NULL;
441     Key *ckey = NULL;
442     Key *skey = NULL;
443     krb5_storage *reply_sp;
444     time_t max_life;
445     uint8_t life;
446     int32_t chal;
447     char client_name[256];
448     char server_name[256];
449         
450     krb5_data_zero (&request);
451
452     ret = unparse_auth_args (sp, &name, &instance, &start_time, &end_time,
453                              &request, &max_seq_len);
454     if (ret != 0 || request.length < 8) {
455         make_error_reply (hdr, KABADREQUEST, reply);
456         goto out;
457     }
458
459     snprintf (client_name, sizeof(client_name), "%s.%s@%s",
460               name, instance, config->v4_realm);
461     snprintf (server_name, sizeof(server_name), "%s.%s@%s",
462               "krbtgt", config->v4_realm, config->v4_realm);
463
464     kdc_log(context, config, 0, "AS-REQ (kaserver) %s from %s for %s",
465             client_name, from, server_name);
466
467     ret = _kdc_db_fetch4 (context, config, name, instance, 
468                           config->v4_realm, HDB_F_GET_CLIENT, &client_entry);
469     if (ret) {
470         kdc_log(context, config, 0, "Client not found in database: %s: %s",
471                 client_name, krb5_get_err_text(context, ret));
472         make_error_reply (hdr, KANOENT, reply);
473         goto out;
474     }
475
476     ret = _kdc_db_fetch4 (context, config, "krbtgt", 
477                           config->v4_realm, config->v4_realm, 
478                           HDB_F_GET_KRBTGT, &server_entry);
479     if (ret) {
480         kdc_log(context, config, 0, "Server not found in database: %s: %s",
481                 server_name, krb5_get_err_text(context, ret));
482         make_error_reply (hdr, KANOENT, reply);
483         goto out;
484     }
485
486     ret = _kdc_check_flags (context, config,
487                             client_entry, client_name,
488                             server_entry, server_name,
489                             TRUE);
490     if (ret) {
491         make_error_reply (hdr, KAPWEXPIRED, reply);
492         goto out;
493     }
494
495     /* find a DES key */
496     ret = _kdc_get_des_key(context, client_entry, FALSE, TRUE, &ckey);
497     if(ret){
498         kdc_log(context, config, 0, "no suitable DES key for client");
499         make_error_reply (hdr, KANOKEYS, reply);
500         goto out;
501     }
502
503     /* find a DES key */
504     ret = _kdc_get_des_key(context, server_entry, TRUE, TRUE, &skey);
505     if(ret){
506         kdc_log(context, config, 0, "no suitable DES key for server");
507         make_error_reply (hdr, KANOKEYS, reply);
508         goto out;
509     }
510
511     {
512         DES_cblock key;
513         DES_key_schedule schedule;
514         
515         /* try to decode the `request' */
516         memcpy (&key, ckey->key.keyvalue.data, sizeof(key));
517         DES_set_key (&key, &schedule);
518         DES_pcbc_encrypt (request.data,
519                           request.data,
520                           request.length,
521                           &schedule,
522                           &key,
523                           DES_DECRYPT);
524         memset (&schedule, 0, sizeof(schedule));
525         memset (&key, 0, sizeof(key));
526     }
527
528     /* check for the magic label */
529     if (memcmp ((char *)request.data + 4, "gTGS", 4) != 0) {
530         kdc_log(context, config, 0, "preauth failed for %s", client_name);
531         make_error_reply (hdr, KABADREQUEST, reply);
532         goto out;
533     }
534
535     reply_sp = krb5_storage_from_mem (request.data, 4);
536     krb5_ret_int32 (reply_sp, &chal);
537     krb5_storage_free (reply_sp);
538
539     if (abs(chal - kdc_time) > context->max_skew) {
540         make_error_reply (hdr, KACLOCKSKEW, reply);
541         goto out;
542     }
543
544     /* life */
545     max_life = end_time - kdc_time;
546     /* end_time - kdc_time can sometimes be non-positive due to slight
547        time skew between client and server. Let's make sure it is postive */
548     if(max_life < 1)
549         max_life = 1;
550     if (client_entry->entry.max_life)
551         max_life = min(max_life, *client_entry->entry.max_life);
552     if (server_entry->entry.max_life)
553         max_life = min(max_life, *server_entry->entry.max_life);
554
555     life = krb_time_to_life(kdc_time, kdc_time + max_life);
556
557     create_reply_ticket (context, 
558                          hdr, skey,
559                          name, instance, config->v4_realm,
560                          addr, life, server_entry->entry.kvno,
561                          max_seq_len,
562                          "krbtgt", config->v4_realm,
563                          chal + 1, "tgsT",
564                          &ckey->key, reply);
565
566  out:
567     if (request.length) {
568         memset (request.data, 0, request.length);
569         krb5_data_free (&request);
570     }
571     if (name)
572         free (name);
573     if (instance)
574         free (instance);
575     if (client_entry)
576         _kdc_free_ent (context, client_entry);
577     if (server_entry)
578         _kdc_free_ent (context, server_entry);
579 }
580
581 static krb5_error_code
582 unparse_getticket_args (krb5_storage *sp,
583                         int *kvno,
584                         char **auth_domain,
585                         krb5_data *ticket,
586                         char **name,
587                         char **instance,
588                         krb5_data *times,
589                         int32_t *max_seq_len)
590 {
591     krb5_data data;
592     int32_t tmp;
593
594     krb5_ret_int32 (sp, &tmp);
595     *kvno = tmp;
596
597     krb5_ret_xdr_data (sp, &data);
598     *auth_domain = malloc(data.length + 1);
599     if (*auth_domain == NULL)
600         return ENOMEM;
601     memcpy (*auth_domain, data.data, data.length);
602     (*auth_domain)[data.length] = '\0';
603     krb5_data_free (&data);
604
605     krb5_ret_xdr_data (sp, ticket);
606
607     krb5_ret_xdr_data (sp, &data);
608     *name = malloc(data.length + 1);
609     if (*name == NULL) {
610         free (*auth_domain);
611         return ENOMEM;
612     }
613     memcpy (*name, data.data, data.length);
614     (*name)[data.length] = '\0';
615     krb5_data_free (&data);
616
617     krb5_ret_xdr_data (sp, &data);
618     *instance = malloc(data.length + 1);
619     if (*instance == NULL) {
620         free (*auth_domain);
621         free (*name);
622         return ENOMEM;
623     }
624     memcpy (*instance, data.data, data.length);
625     (*instance)[data.length] = '\0';
626     krb5_data_free (&data);
627
628     krb5_ret_xdr_data (sp, times);
629
630     krb5_ret_int32 (sp, max_seq_len);
631     /* ignore the rest */
632     return 0;
633 }
634
635 static void
636 do_getticket (krb5_context context, 
637               krb5_kdc_configuration *config,
638               struct rx_header *hdr,
639               krb5_storage *sp,
640               struct sockaddr_in *addr,
641               const char *from,
642               krb5_data *reply)
643 {
644     krb5_error_code ret;
645     int kvno;
646     char *auth_domain = NULL;
647     krb5_data aticket;
648     char *name = NULL;
649     char *instance = NULL;
650     krb5_data times;
651     int32_t max_seq_len;
652     hdb_entry_ex *server_entry = NULL;
653     hdb_entry_ex *client_entry = NULL;
654     hdb_entry_ex *krbtgt_entry = NULL;
655     Key *kkey = NULL;
656     Key *skey = NULL;
657     DES_cblock key;
658     DES_key_schedule schedule;
659     DES_cblock session;
660     time_t max_life;
661     int8_t life;
662     time_t start_time, end_time;
663     char server_name[256];
664     char client_name[256];
665     struct _krb5_krb_auth_data ad;
666
667     krb5_data_zero (&aticket);
668     krb5_data_zero (&times);
669
670     memset(&ad, 0, sizeof(ad));
671
672     unparse_getticket_args (sp, &kvno, &auth_domain, &aticket,
673                             &name, &instance, &times, &max_seq_len);
674     if (times.length < 8) {
675         make_error_reply (hdr, KABADREQUEST, reply);
676         goto out;
677         
678     }
679
680     snprintf (server_name, sizeof(server_name),
681               "%s.%s@%s", name, instance, config->v4_realm);
682
683     ret = _kdc_db_fetch4 (context, config, name, instance, 
684                           config->v4_realm, HDB_F_GET_SERVER, &server_entry);
685     if (ret) {
686         kdc_log(context, config, 0, "Server not found in database: %s: %s",
687                 server_name, krb5_get_err_text(context, ret));
688         make_error_reply (hdr, KANOENT, reply);
689         goto out;
690     }
691
692     ret = _kdc_db_fetch4 (context, config, "krbtgt", 
693                      config->v4_realm, config->v4_realm, HDB_F_GET_KRBTGT, &krbtgt_entry);
694     if (ret) {
695         kdc_log(context, config, 0,
696                 "Server not found in database: %s.%s@%s: %s",
697                 "krbtgt", config->v4_realm,  config->v4_realm,
698                 krb5_get_err_text(context, ret));
699         make_error_reply (hdr, KANOENT, reply);
700         goto out;
701     }
702
703     /* find a DES key */
704     ret = _kdc_get_des_key(context, krbtgt_entry, TRUE, TRUE, &kkey);
705     if(ret){
706         kdc_log(context, config, 0, "no suitable DES key for krbtgt");
707         make_error_reply (hdr, KANOKEYS, reply);
708         goto out;
709     }
710
711     /* find a DES key */
712     ret = _kdc_get_des_key(context, server_entry, TRUE, TRUE, &skey);
713     if(ret){
714         kdc_log(context, config, 0, "no suitable DES key for server");
715         make_error_reply (hdr, KANOKEYS, reply);
716         goto out;
717     }
718
719     /* decrypt the incoming ticket */
720     memcpy (&key, kkey->key.keyvalue.data, sizeof(key));
721
722     /* unpack the ticket */
723     {
724         char *sname = NULL;
725         char *sinstance = NULL;
726
727         ret = _krb5_krb_decomp_ticket(context, &aticket, &kkey->key, 
728                                       config->v4_realm, &sname,
729                                       &sinstance, &ad);
730         if (ret) {
731             kdc_log(context, config, 0,
732                     "kaserver: decomp failed for %s.%s with %d",
733                     sname, sinstance, ret);
734             make_error_reply (hdr, KABADTICKET, reply);
735             goto out;
736         }
737
738         if (strcmp (sname, "krbtgt") != 0
739             || strcmp (sinstance, config->v4_realm) != 0) {
740             kdc_log(context, config, 0, "no TGT: %s.%s for %s.%s@%s",
741                     sname, sinstance,
742                     ad.pname, ad.pinst, ad.prealm);
743             make_error_reply (hdr, KABADTICKET, reply);
744             free(sname);
745             free(sinstance);
746             goto out;
747         }
748         free(sname);
749         free(sinstance);
750
751         if (kdc_time > _krb5_krb_life_to_time(ad.time_sec, ad.life)) {
752             kdc_log(context, config, 0, "TGT expired: %s.%s@%s",
753                     ad.pname, ad.pinst, ad.prealm);
754             make_error_reply (hdr, KABADTICKET, reply);
755             goto out;
756         }
757     }
758
759     snprintf (client_name, sizeof(client_name),
760               "%s.%s@%s", ad.pname, ad.pinst, ad.prealm);
761
762     kdc_log(context, config, 0, "TGS-REQ (kaserver) %s from %s for %s",
763             client_name, from, server_name);
764
765     ret = _kdc_db_fetch4 (context, config, 
766                           ad.pname, ad.pinst, ad.prealm, HDB_F_GET_CLIENT,
767                           &client_entry);
768     if(ret && ret != HDB_ERR_NOENTRY) {
769         kdc_log(context, config, 0,
770                 "Client not found in database: (krb4) %s: %s",
771                 client_name, krb5_get_err_text(context, ret));
772         make_error_reply (hdr, KANOENT, reply);
773         goto out;
774     }
775     if (client_entry == NULL && strcmp(ad.prealm, config->v4_realm) == 0) {
776         kdc_log(context, config, 0, 
777                 "Local client not found in database: (krb4) "
778                 "%s", client_name);
779         make_error_reply (hdr, KANOENT, reply);
780         goto out;
781     }
782
783     ret = _kdc_check_flags (context, config, 
784                             client_entry, client_name,
785                             server_entry, server_name,
786                             FALSE);
787     if (ret) {
788         make_error_reply (hdr, KAPWEXPIRED, reply);
789         goto out;
790     }
791
792     /* decrypt the times */
793     memcpy(&session, ad.session.keyvalue.data, sizeof(session));
794     DES_set_key (&session, &schedule);
795     DES_ecb_encrypt (times.data,
796                      times.data,
797                      &schedule,
798                      DES_DECRYPT);
799     memset (&schedule, 0, sizeof(schedule));
800     memset (&session, 0, sizeof(session));
801
802     /* and extract them */
803     {
804         krb5_storage *tsp;
805         int32_t tmp;
806
807         tsp = krb5_storage_from_mem (times.data, times.length);
808         krb5_ret_int32 (tsp, &tmp);
809         start_time = tmp;
810         krb5_ret_int32 (tsp, &tmp);
811         end_time = tmp;
812         krb5_storage_free (tsp);
813     }
814
815     /* life */
816     max_life = end_time - kdc_time;
817     /* end_time - kdc_time can sometimes be non-positive due to slight
818        time skew between client and server. Let's make sure it is postive */
819     if(max_life < 1)
820         max_life = 1;
821     if (krbtgt_entry->entry.max_life)
822         max_life = min(max_life, *krbtgt_entry->entry.max_life);
823     if (server_entry->entry.max_life)
824         max_life = min(max_life, *server_entry->entry.max_life);
825     /* if this is a cross realm request, the client_entry will likely
826        be NULL */
827     if (client_entry && client_entry->entry.max_life)
828         max_life = min(max_life, *client_entry->entry.max_life);
829
830     life = _krb5_krb_time_to_life(kdc_time, kdc_time + max_life);
831
832     create_reply_ticket (context, 
833                          hdr, skey,
834                          ad.pname, ad.pinst, ad.prealm,
835                          addr, life, server_entry->entry.kvno,
836                          max_seq_len,
837                          name, instance,
838                          0, "gtkt",
839                          &ad.session, reply);
840     
841  out:
842     _krb5_krb_free_auth_data(context, &ad);
843     if (aticket.length) {
844         memset (aticket.data, 0, aticket.length);
845         krb5_data_free (&aticket);
846     }
847     if (times.length) {
848         memset (times.data, 0, times.length);
849         krb5_data_free (&times);
850     }
851     if (auth_domain)
852         free (auth_domain);
853     if (name)
854         free (name);
855     if (instance)
856         free (instance);
857     if (krbtgt_entry)
858         _kdc_free_ent (context, krbtgt_entry);
859     if (server_entry)
860         _kdc_free_ent (context, server_entry);
861 }
862
863 krb5_error_code
864 _kdc_do_kaserver(krb5_context context, 
865                  krb5_kdc_configuration *config,
866                  unsigned char *buf,
867                  size_t len,
868                  krb5_data *reply,
869                  const char *from,
870                  struct sockaddr_in *addr)
871 {
872     krb5_error_code ret = 0;
873     struct rx_header hdr;
874     uint32_t op;
875     krb5_storage *sp;
876
877     if (len < RX_HEADER_SIZE)
878         return -1;
879     sp = krb5_storage_from_mem (buf, len);
880
881     ret = decode_rx_header (sp, &hdr);
882     if (ret)
883         goto out;
884     buf += RX_HEADER_SIZE;
885     len -= RX_HEADER_SIZE;
886
887     switch (hdr.type) {
888     case HT_DATA :
889         break;
890     case HT_ACK :
891     case HT_BUSY :
892     case HT_ABORT :
893     case HT_ACKALL :
894     case HT_CHAL :
895     case HT_RESP :
896     case HT_DEBUG :
897     default:
898         /* drop */
899         goto out;
900     }
901
902
903     if (hdr.serviceid != KA_AUTHENTICATION_SERVICE
904         && hdr.serviceid != KA_TICKET_GRANTING_SERVICE) {
905         ret = -1;
906         goto out;
907     }
908
909     ret = krb5_ret_uint32(sp, &op);
910     if (ret)
911         goto out;
912     switch (op) {
913     case AUTHENTICATE :
914     case AUTHENTICATE_V2 :
915         do_authenticate (context, config, &hdr, sp, addr, from, reply);
916         break;
917     case GETTICKET :
918         do_getticket (context, config, &hdr, sp, addr, from, reply);
919         break;
920     case AUTHENTICATE_OLD :
921     case CHANGEPASSWORD :
922     case GETTICKET_OLD :
923     case SETPASSWORD :
924     case SETFIELDS :
925     case CREATEUSER :
926     case DELETEUSER :
927     case GETENTRY :
928     case LISTENTRY :
929     case GETSTATS :
930     case DEBUG :
931     case GETPASSWORD :
932     case GETRANDOMKEY :
933     default :
934         make_error_reply (&hdr, RXGEN_OPCODE, reply);
935         break;
936     }
937
938 out:
939     krb5_storage_free (sp);
940     return ret;
941 }