r23456: Update Samba4 to 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 17904 2006-08-23 11:45:16Z lha $");
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,
469                           &client_entry);
470     if (ret) {
471         kdc_log(context, config, 0, "Client not found in database: %s: %s",
472                 client_name, krb5_get_err_text(context, ret));
473         make_error_reply (hdr, KANOENT, reply);
474         goto out;
475     }
476
477     ret = _kdc_db_fetch4 (context, config, "krbtgt", 
478                           config->v4_realm, config->v4_realm, 
479                           HDB_F_GET_KRBTGT, &server_entry);
480     if (ret) {
481         kdc_log(context, config, 0, "Server not found in database: %s: %s",
482                 server_name, krb5_get_err_text(context, ret));
483         make_error_reply (hdr, KANOENT, reply);
484         goto out;
485     }
486
487     ret = _kdc_check_flags (context, config,
488                             client_entry, client_name,
489                             server_entry, server_name,
490                             TRUE);
491     if (ret) {
492         make_error_reply (hdr, KAPWEXPIRED, reply);
493         goto out;
494     }
495
496     /* find a DES key */
497     ret = _kdc_get_des_key(context, client_entry, FALSE, TRUE, &ckey);
498     if(ret){
499         kdc_log(context, config, 0, "no suitable DES key for client");
500         make_error_reply (hdr, KANOKEYS, reply);
501         goto out;
502     }
503
504     /* find a DES key */
505     ret = _kdc_get_des_key(context, server_entry, TRUE, TRUE, &skey);
506     if(ret){
507         kdc_log(context, config, 0, "no suitable DES key for server");
508         make_error_reply (hdr, KANOKEYS, reply);
509         goto out;
510     }
511
512     {
513         DES_cblock key;
514         DES_key_schedule schedule;
515         
516         /* try to decode the `request' */
517         memcpy (&key, ckey->key.keyvalue.data, sizeof(key));
518         DES_set_key (&key, &schedule);
519         DES_pcbc_encrypt (request.data,
520                           request.data,
521                           request.length,
522                           &schedule,
523                           &key,
524                           DES_DECRYPT);
525         memset (&schedule, 0, sizeof(schedule));
526         memset (&key, 0, sizeof(key));
527     }
528
529     /* check for the magic label */
530     if (memcmp ((char *)request.data + 4, "gTGS", 4) != 0) {
531         kdc_log(context, config, 0, "preauth failed for %s", client_name);
532         make_error_reply (hdr, KABADREQUEST, reply);
533         goto out;
534     }
535
536     reply_sp = krb5_storage_from_mem (request.data, 4);
537     krb5_ret_int32 (reply_sp, &chal);
538     krb5_storage_free (reply_sp);
539
540     if (abs(chal - kdc_time) > context->max_skew) {
541         make_error_reply (hdr, KACLOCKSKEW, reply);
542         goto out;
543     }
544
545     /* life */
546     max_life = end_time - kdc_time;
547     /* end_time - kdc_time can sometimes be non-positive due to slight
548        time skew between client and server. Let's make sure it is postive */
549     if(max_life < 1)
550         max_life = 1;
551     if (client_entry->entry.max_life)
552         max_life = min(max_life, *client_entry->entry.max_life);
553     if (server_entry->entry.max_life)
554         max_life = min(max_life, *server_entry->entry.max_life);
555
556     life = krb_time_to_life(kdc_time, kdc_time + max_life);
557
558     create_reply_ticket (context, 
559                          hdr, skey,
560                          name, instance, config->v4_realm,
561                          addr, life, server_entry->entry.kvno,
562                          max_seq_len,
563                          "krbtgt", config->v4_realm,
564                          chal + 1, "tgsT",
565                          &ckey->key, reply);
566
567  out:
568     if (request.length) {
569         memset (request.data, 0, request.length);
570         krb5_data_free (&request);
571     }
572     if (name)
573         free (name);
574     if (instance)
575         free (instance);
576     if (client_entry)
577         _kdc_free_ent (context, client_entry);
578     if (server_entry)
579         _kdc_free_ent (context, server_entry);
580 }
581
582 static krb5_error_code
583 unparse_getticket_args (krb5_storage *sp,
584                         int *kvno,
585                         char **auth_domain,
586                         krb5_data *ticket,
587                         char **name,
588                         char **instance,
589                         krb5_data *times,
590                         int32_t *max_seq_len)
591 {
592     krb5_data data;
593     int32_t tmp;
594
595     krb5_ret_int32 (sp, &tmp);
596     *kvno = tmp;
597
598     krb5_ret_xdr_data (sp, &data);
599     *auth_domain = malloc(data.length + 1);
600     if (*auth_domain == NULL)
601         return ENOMEM;
602     memcpy (*auth_domain, data.data, data.length);
603     (*auth_domain)[data.length] = '\0';
604     krb5_data_free (&data);
605
606     krb5_ret_xdr_data (sp, ticket);
607
608     krb5_ret_xdr_data (sp, &data);
609     *name = malloc(data.length + 1);
610     if (*name == NULL) {
611         free (*auth_domain);
612         return ENOMEM;
613     }
614     memcpy (*name, data.data, data.length);
615     (*name)[data.length] = '\0';
616     krb5_data_free (&data);
617
618     krb5_ret_xdr_data (sp, &data);
619     *instance = malloc(data.length + 1);
620     if (*instance == NULL) {
621         free (*auth_domain);
622         free (*name);
623         return ENOMEM;
624     }
625     memcpy (*instance, data.data, data.length);
626     (*instance)[data.length] = '\0';
627     krb5_data_free (&data);
628
629     krb5_ret_xdr_data (sp, times);
630
631     krb5_ret_int32 (sp, max_seq_len);
632     /* ignore the rest */
633     return 0;
634 }
635
636 static void
637 do_getticket (krb5_context context, 
638               krb5_kdc_configuration *config,
639               struct rx_header *hdr,
640               krb5_storage *sp,
641               struct sockaddr_in *addr,
642               const char *from,
643               krb5_data *reply)
644 {
645     krb5_error_code ret;
646     int kvno;
647     char *auth_domain = NULL;
648     krb5_data aticket;
649     char *name = NULL;
650     char *instance = NULL;
651     krb5_data times;
652     int32_t max_seq_len;
653     hdb_entry_ex *server_entry = NULL;
654     hdb_entry_ex *client_entry = NULL;
655     hdb_entry_ex *krbtgt_entry = NULL;
656     Key *kkey = NULL;
657     Key *skey = NULL;
658     DES_cblock key;
659     DES_key_schedule schedule;
660     DES_cblock session;
661     time_t max_life;
662     int8_t life;
663     time_t start_time, end_time;
664     char server_name[256];
665     char client_name[256];
666     struct _krb5_krb_auth_data ad;
667
668     krb5_data_zero (&aticket);
669     krb5_data_zero (&times);
670
671     memset(&ad, 0, sizeof(ad));
672
673     unparse_getticket_args (sp, &kvno, &auth_domain, &aticket,
674                             &name, &instance, &times, &max_seq_len);
675     if (times.length < 8) {
676         make_error_reply (hdr, KABADREQUEST, reply);
677         goto out;
678         
679     }
680
681     snprintf (server_name, sizeof(server_name),
682               "%s.%s@%s", name, instance, config->v4_realm);
683
684     ret = _kdc_db_fetch4 (context, config, name, instance, 
685                           config->v4_realm, HDB_F_GET_SERVER, &server_entry);
686     if (ret) {
687         kdc_log(context, config, 0, "Server not found in database: %s: %s",
688                 server_name, krb5_get_err_text(context, ret));
689         make_error_reply (hdr, KANOENT, reply);
690         goto out;
691     }
692
693     ret = _kdc_db_fetch4 (context, config, "krbtgt", 
694                      config->v4_realm, config->v4_realm, HDB_F_GET_KRBTGT, &krbtgt_entry);
695     if (ret) {
696         kdc_log(context, config, 0,
697                 "Server not found in database: %s.%s@%s: %s",
698                 "krbtgt", config->v4_realm,  config->v4_realm,
699                 krb5_get_err_text(context, ret));
700         make_error_reply (hdr, KANOENT, reply);
701         goto out;
702     }
703
704     /* find a DES key */
705     ret = _kdc_get_des_key(context, krbtgt_entry, TRUE, TRUE, &kkey);
706     if(ret){
707         kdc_log(context, config, 0, "no suitable DES key for krbtgt");
708         make_error_reply (hdr, KANOKEYS, reply);
709         goto out;
710     }
711
712     /* find a DES key */
713     ret = _kdc_get_des_key(context, server_entry, TRUE, TRUE, &skey);
714     if(ret){
715         kdc_log(context, config, 0, "no suitable DES key for server");
716         make_error_reply (hdr, KANOKEYS, reply);
717         goto out;
718     }
719
720     /* decrypt the incoming ticket */
721     memcpy (&key, kkey->key.keyvalue.data, sizeof(key));
722
723     /* unpack the ticket */
724     {
725         char *sname = NULL;
726         char *sinstance = NULL;
727
728         ret = _krb5_krb_decomp_ticket(context, &aticket, &kkey->key, 
729                                       config->v4_realm, &sname,
730                                       &sinstance, &ad);
731         if (ret) {
732             kdc_log(context, config, 0,
733                     "kaserver: decomp failed for %s.%s with %d",
734                     sname, sinstance, ret);
735             make_error_reply (hdr, KABADTICKET, reply);
736             goto out;
737         }
738
739         if (strcmp (sname, "krbtgt") != 0
740             || strcmp (sinstance, config->v4_realm) != 0) {
741             kdc_log(context, config, 0, "no TGT: %s.%s for %s.%s@%s",
742                     sname, sinstance,
743                     ad.pname, ad.pinst, ad.prealm);
744             make_error_reply (hdr, KABADTICKET, reply);
745             free(sname);
746             free(sinstance);
747             goto out;
748         }
749         free(sname);
750         free(sinstance);
751
752         if (kdc_time > _krb5_krb_life_to_time(ad.time_sec, ad.life)) {
753             kdc_log(context, config, 0, "TGT expired: %s.%s@%s",
754                     ad.pname, ad.pinst, ad.prealm);
755             make_error_reply (hdr, KABADTICKET, reply);
756             goto out;
757         }
758     }
759
760     snprintf (client_name, sizeof(client_name),
761               "%s.%s@%s", ad.pname, ad.pinst, ad.prealm);
762
763     kdc_log(context, config, 0, "TGS-REQ (kaserver) %s from %s for %s",
764             client_name, from, server_name);
765
766     ret = _kdc_db_fetch4 (context, config, 
767                           ad.pname, ad.pinst, ad.prealm, HDB_F_GET_CLIENT,
768                           &client_entry);
769     if(ret && ret != HDB_ERR_NOENTRY) {
770         kdc_log(context, config, 0,
771                 "Client not found in database: (krb4) %s: %s",
772                 client_name, krb5_get_err_text(context, ret));
773         make_error_reply (hdr, KANOENT, reply);
774         goto out;
775     }
776     if (client_entry == NULL && strcmp(ad.prealm, config->v4_realm) == 0) {
777         kdc_log(context, config, 0, 
778                 "Local client not found in database: (krb4) "
779                 "%s", client_name);
780         make_error_reply (hdr, KANOENT, reply);
781         goto out;
782     }
783
784     ret = _kdc_check_flags (context, config, 
785                             client_entry, client_name,
786                             server_entry, server_name,
787                             FALSE);
788     if (ret) {
789         make_error_reply (hdr, KAPWEXPIRED, reply);
790         goto out;
791     }
792
793     /* decrypt the times */
794     memcpy(&session, ad.session.keyvalue.data, sizeof(session));
795     DES_set_key (&session, &schedule);
796     DES_ecb_encrypt (times.data,
797                      times.data,
798                      &schedule,
799                      DES_DECRYPT);
800     memset (&schedule, 0, sizeof(schedule));
801     memset (&session, 0, sizeof(session));
802
803     /* and extract them */
804     {
805         krb5_storage *tsp;
806         int32_t tmp;
807
808         tsp = krb5_storage_from_mem (times.data, times.length);
809         krb5_ret_int32 (tsp, &tmp);
810         start_time = tmp;
811         krb5_ret_int32 (tsp, &tmp);
812         end_time = tmp;
813         krb5_storage_free (tsp);
814     }
815
816     /* life */
817     max_life = end_time - kdc_time;
818     /* end_time - kdc_time can sometimes be non-positive due to slight
819        time skew between client and server. Let's make sure it is postive */
820     if(max_life < 1)
821         max_life = 1;
822     if (krbtgt_entry->entry.max_life)
823         max_life = min(max_life, *krbtgt_entry->entry.max_life);
824     if (server_entry->entry.max_life)
825         max_life = min(max_life, *server_entry->entry.max_life);
826     /* if this is a cross realm request, the client_entry will likely
827        be NULL */
828     if (client_entry && client_entry->entry.max_life)
829         max_life = min(max_life, *client_entry->entry.max_life);
830
831     life = _krb5_krb_time_to_life(kdc_time, kdc_time + max_life);
832
833     create_reply_ticket (context, 
834                          hdr, skey,
835                          ad.pname, ad.pinst, ad.prealm,
836                          addr, life, server_entry->entry.kvno,
837                          max_seq_len,
838                          name, instance,
839                          0, "gtkt",
840                          &ad.session, reply);
841     
842  out:
843     _krb5_krb_free_auth_data(context, &ad);
844     if (aticket.length) {
845         memset (aticket.data, 0, aticket.length);
846         krb5_data_free (&aticket);
847     }
848     if (times.length) {
849         memset (times.data, 0, times.length);
850         krb5_data_free (&times);
851     }
852     if (auth_domain)
853         free (auth_domain);
854     if (name)
855         free (name);
856     if (instance)
857         free (instance);
858     if (krbtgt_entry)
859         _kdc_free_ent (context, krbtgt_entry);
860     if (server_entry)
861         _kdc_free_ent (context, server_entry);
862 }
863
864 krb5_error_code
865 _kdc_do_kaserver(krb5_context context, 
866                  krb5_kdc_configuration *config,
867                  unsigned char *buf,
868                  size_t len,
869                  krb5_data *reply,
870                  const char *from,
871                  struct sockaddr_in *addr)
872 {
873     krb5_error_code ret = 0;
874     struct rx_header hdr;
875     uint32_t op;
876     krb5_storage *sp;
877
878     if (len < RX_HEADER_SIZE)
879         return -1;
880     sp = krb5_storage_from_mem (buf, len);
881
882     ret = decode_rx_header (sp, &hdr);
883     if (ret)
884         goto out;
885     buf += RX_HEADER_SIZE;
886     len -= RX_HEADER_SIZE;
887
888     switch (hdr.type) {
889     case HT_DATA :
890         break;
891     case HT_ACK :
892     case HT_BUSY :
893     case HT_ABORT :
894     case HT_ACKALL :
895     case HT_CHAL :
896     case HT_RESP :
897     case HT_DEBUG :
898     default:
899         /* drop */
900         goto out;
901     }
902
903
904     if (hdr.serviceid != KA_AUTHENTICATION_SERVICE
905         && hdr.serviceid != KA_TICKET_GRANTING_SERVICE) {
906         ret = -1;
907         goto out;
908     }
909
910     ret = krb5_ret_uint32(sp, &op);
911     if (ret)
912         goto out;
913     switch (op) {
914     case AUTHENTICATE :
915     case AUTHENTICATE_V2 :
916         do_authenticate (context, config, &hdr, sp, addr, from, reply);
917         break;
918     case GETTICKET :
919         do_getticket (context, config, &hdr, sp, addr, from, reply);
920         break;
921     case AUTHENTICATE_OLD :
922     case CHANGEPASSWORD :
923     case GETTICKET_OLD :
924     case SETPASSWORD :
925     case SETFIELDS :
926     case CREATEUSER :
927     case DELETEUSER :
928     case GETENTRY :
929     case LISTENTRY :
930     case GETSTATS :
931     case DEBUG :
932     case GETPASSWORD :
933     case GETRANDOMKEY :
934     default :
935         make_error_reply (&hdr, RXGEN_OPCODE, reply);
936         break;
937     }
938
939 out:
940     krb5_storage_free (sp);
941     return ret;
942 }