r8302: import mini HEIMDAL into the tree
[samba.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.30 2005/06/30 01:49:39 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 static void
111 decode_rx_header (krb5_storage *sp,
112                   struct rx_header *h)
113 {
114     krb5_ret_int32(sp, &h->epoch);
115     krb5_ret_int32(sp, &h->connid);
116     krb5_ret_int32(sp, &h->callid);
117     krb5_ret_int32(sp, &h->seqno);
118     krb5_ret_int32(sp, &h->serialno);
119     krb5_ret_int8(sp,  &h->type);
120     krb5_ret_int8(sp,  &h->flags);
121     krb5_ret_int8(sp,  &h->status);
122     krb5_ret_int8(sp,  &h->secindex);
123     krb5_ret_int16(sp, &h->reserved);
124     krb5_ret_int16(sp, &h->serviceid);
125 }
126
127 static void
128 encode_rx_header (struct rx_header *h,
129                   krb5_storage *sp)
130 {
131     krb5_store_int32(sp, h->epoch);
132     krb5_store_int32(sp, h->connid);
133     krb5_store_int32(sp, h->callid);
134     krb5_store_int32(sp, h->seqno);
135     krb5_store_int32(sp, h->serialno);
136     krb5_store_int8(sp,  h->type);
137     krb5_store_int8(sp,  h->flags);
138     krb5_store_int8(sp,  h->status);
139     krb5_store_int8(sp,  h->secindex);
140     krb5_store_int16(sp, h->reserved);
141     krb5_store_int16(sp, h->serviceid);
142 }
143
144 static void
145 init_reply_header (struct rx_header *hdr,
146                    struct rx_header *reply_hdr,
147                    u_char type,
148                    u_char flags)
149 {
150     reply_hdr->epoch     = hdr->epoch;
151     reply_hdr->connid    = hdr->connid;
152     reply_hdr->callid    = hdr->callid;
153     reply_hdr->seqno     = 1;
154     reply_hdr->serialno  = 1;
155     reply_hdr->type      = type;
156     reply_hdr->flags     = flags;
157     reply_hdr->status    = 0;
158     reply_hdr->secindex  = 0;
159     reply_hdr->reserved  = 0;
160     reply_hdr->serviceid = hdr->serviceid;
161 }
162
163 static void
164 make_error_reply (struct rx_header *hdr,
165                   u_int32_t ret,
166                   krb5_data *reply)
167
168 {
169     krb5_storage *sp;
170     struct rx_header reply_hdr;
171
172     init_reply_header (hdr, &reply_hdr, HT_ABORT, HF_LAST);
173     sp = krb5_storage_emem();
174     encode_rx_header (&reply_hdr, sp);
175     krb5_store_int32(sp, ret);
176     krb5_storage_to_data (sp, reply);
177     krb5_storage_free (sp);
178 }
179
180 static krb5_error_code
181 krb5_ret_xdr_data(krb5_storage *sp,
182                   krb5_data *data)
183 {
184     int ret;
185     int size;
186     ret = krb5_ret_int32(sp, &size);
187     if(ret)
188         return ret;
189     if(size < 0)
190         return ERANGE;
191     data->length = size;
192     if (size) {
193         u_char foo[4];
194         size_t pad = (4 - size % 4) % 4;
195
196         data->data = malloc(size);
197         if (data->data == NULL)
198             return ENOMEM;
199         ret = krb5_storage_read(sp, data->data, size);
200         if(ret != size)
201             return (ret < 0)? errno : KRB5_CC_END;
202         if (pad) {
203             ret = krb5_storage_read(sp, foo, pad);
204             if (ret != pad)
205                 return (ret < 0)? errno : KRB5_CC_END;
206         }
207     } else
208         data->data = NULL;
209     return 0;
210 }
211
212 static krb5_error_code
213 krb5_store_xdr_data(krb5_storage *sp,
214                     krb5_data data)
215 {
216     u_char zero[4] = {0, 0, 0, 0};
217     int ret;
218     size_t pad;
219
220     ret = krb5_store_int32(sp, data.length);
221     if(ret < 0)
222         return ret;
223     ret = krb5_storage_write(sp, data.data, data.length);
224     if(ret != data.length){
225         if(ret < 0)
226             return errno;
227         return KRB5_CC_END;
228     }
229     pad = (4 - data.length % 4) % 4;
230     if (pad) {
231         ret = krb5_storage_write(sp, zero, pad);
232         if (ret != pad) {
233             if (ret < 0)
234                 return errno;
235             return KRB5_CC_END;
236         }
237     }
238     return 0;
239 }
240
241
242 static krb5_error_code
243 create_reply_ticket (krb5_context context, 
244                      struct rx_header *hdr,
245                      Key *skey,
246                      char *name, char *instance, char *realm,
247                      struct sockaddr_in *addr,
248                      int life,
249                      int kvno,
250                      int32_t max_seq_len,
251                      const char *sname, const char *sinstance,
252                      u_int32_t challenge,
253                      const char *label,
254                      krb5_keyblock *key,
255                      krb5_data *reply)
256 {
257     krb5_data ticket;
258     krb5_keyblock session;
259     krb5_storage *sp;
260     krb5_data enc_data;
261     struct rx_header reply_hdr;
262     char zero[8];
263     size_t pad;
264     unsigned fyrtiosjuelva;
265
266     /* create the ticket */
267
268     krb5_generate_random_keyblock(context, ETYPE_DES_PCBC_NONE, &session);
269
270     _krb5_krb_create_ticket(context,
271                             0,
272                             name,
273                             instance,
274                             realm,
275                             addr->sin_addr.s_addr,
276                             &session,
277                             life,
278                             kdc_time,
279                             sname,
280                             sinstance,
281                             &skey->key,
282                             &ticket);
283
284     /* create the encrypted part of the reply */
285     sp = krb5_storage_emem ();
286     krb5_generate_random_block(&fyrtiosjuelva, sizeof(fyrtiosjuelva));
287     fyrtiosjuelva &= 0xffffffff;
288     krb5_store_int32 (sp, fyrtiosjuelva);
289     krb5_store_int32 (sp, challenge);
290     krb5_storage_write  (sp, session.keyvalue.data, 8);
291     krb5_free_keyblock_contents(context, &session);
292     krb5_store_int32 (sp, kdc_time);
293     krb5_store_int32 (sp, kdc_time + _krb5_krb_life_to_time (0, life));
294     krb5_store_int32 (sp, kvno);
295     krb5_store_int32 (sp, ticket.length);
296     krb5_store_stringz (sp, name);
297     krb5_store_stringz (sp, instance);
298 #if 1 /* XXX - Why shouldn't the realm go here? */
299     krb5_store_stringz (sp, "");
300 #else
301     krb5_store_stringz (sp, realm);
302 #endif
303     krb5_store_stringz (sp, sname);
304     krb5_store_stringz (sp, sinstance);
305     krb5_storage_write (sp, ticket.data, ticket.length);
306     krb5_storage_write (sp, label, strlen(label));
307
308     /* pad to DES block */
309     memset (zero, 0, sizeof(zero));
310     pad = (8 - krb5_storage_seek (sp, 0, SEEK_CUR) % 8) % 8;
311     krb5_storage_write (sp, zero, pad);
312
313     krb5_storage_to_data (sp, &enc_data);
314     krb5_storage_free (sp);
315
316     if (enc_data.length > max_seq_len) {
317         krb5_data_free (&enc_data);
318         make_error_reply (hdr, KAANSWERTOOLONG, reply);
319         return 0;
320     }
321
322     /* encrypt it */
323     {
324         DES_key_schedule schedule;
325         DES_cblock deskey;
326         
327         memcpy (&deskey, key->keyvalue.data, sizeof(deskey));
328         DES_set_key (&deskey, &schedule);
329         DES_pcbc_encrypt (enc_data.data,
330                           enc_data.data,
331                           enc_data.length,
332                           &schedule,
333                           &deskey,
334                           DES_ENCRYPT);
335         memset (&schedule, 0, sizeof(schedule));
336         memset (&deskey, 0, sizeof(deskey));
337     }
338
339     /* create the reply packet */
340     init_reply_header (hdr, &reply_hdr, HT_DATA, HF_LAST);
341     sp = krb5_storage_emem ();
342     encode_rx_header (&reply_hdr, sp);
343     krb5_store_int32 (sp, max_seq_len);
344     krb5_store_xdr_data (sp, enc_data);
345     krb5_data_free (&enc_data);
346     krb5_storage_to_data (sp, reply);
347     krb5_storage_free (sp);
348     return 0;
349 }
350
351 static krb5_error_code
352 unparse_auth_args (krb5_storage *sp,
353                    char **name,
354                    char **instance,
355                    time_t *start_time,
356                    time_t *end_time,
357                    krb5_data *request,
358                    int32_t *max_seq_len)
359 {
360     krb5_data data;
361     int32_t tmp;
362
363     krb5_ret_xdr_data (sp, &data);
364     *name = malloc(data.length + 1);
365     if (*name == NULL)
366         return ENOMEM;
367     memcpy (*name, data.data, data.length);
368     (*name)[data.length] = '\0';
369     krb5_data_free (&data);
370
371     krb5_ret_xdr_data (sp, &data);
372     *instance = malloc(data.length + 1);
373     if (*instance == NULL) {
374         free (*name);
375         return ENOMEM;
376     }
377     memcpy (*instance, data.data, data.length);
378     (*instance)[data.length] = '\0';
379     krb5_data_free (&data);
380
381     krb5_ret_int32 (sp, &tmp);
382     *start_time = tmp;
383     krb5_ret_int32 (sp, &tmp);
384     *end_time = tmp;
385     krb5_ret_xdr_data (sp, request);
386     krb5_ret_int32 (sp, max_seq_len);
387     /* ignore the rest */
388     return 0;
389 }
390
391 static void
392 do_authenticate (krb5_context context, 
393                  krb5_kdc_configuration *config,
394                  struct rx_header *hdr,
395                  krb5_storage *sp,
396                  struct sockaddr_in *addr,
397                  const char *from,
398                  krb5_data *reply)
399 {
400     krb5_error_code ret;
401     char *name = NULL;
402     char *instance = NULL;
403     time_t start_time;
404     time_t end_time;
405     krb5_data request;
406     int32_t max_seq_len;
407     hdb_entry *client_entry = NULL;
408     hdb_entry *server_entry = NULL;
409     Key *ckey = NULL;
410     Key *skey = NULL;
411     krb5_storage *reply_sp;
412     time_t max_life;
413     u_int8_t life;
414     int32_t chal;
415     char client_name[256];
416     char server_name[256];
417         
418     krb5_data_zero (&request);
419
420     ret = unparse_auth_args (sp, &name, &instance, &start_time, &end_time,
421                              &request, &max_seq_len);
422     if (ret != 0 || request.length < 8) {
423         make_error_reply (hdr, KABADREQUEST, reply);
424         goto out;
425     }
426
427     snprintf (client_name, sizeof(client_name), "%s.%s@%s",
428               name, instance, config->v4_realm);
429     snprintf (server_name, sizeof(server_name), "%s.%s@%s",
430               "krbtgt", config->v4_realm, config->v4_realm);
431
432     kdc_log(context, config, 0, "AS-REQ (kaserver) %s from %s for %s",
433             client_name, from, server_name);
434
435     ret = _kdc_db_fetch4 (context, config, name, instance, 
436                           config->v4_realm, HDB_ENT_TYPE_CLIENT, 
437                           &client_entry);
438     if (ret) {
439         kdc_log(context, config, 0, "Client not found in database: %s: %s",
440                 client_name, krb5_get_err_text(context, ret));
441         make_error_reply (hdr, KANOENT, reply);
442         goto out;
443     }
444
445     ret = _kdc_db_fetch4 (context, config, "krbtgt", 
446                           config->v4_realm, config->v4_realm, 
447                           HDB_ENT_TYPE_SERVER, &server_entry);
448     if (ret) {
449         kdc_log(context, config, 0, "Server not found in database: %s: %s",
450                 server_name, krb5_get_err_text(context, ret));
451         make_error_reply (hdr, KANOENT, reply);
452         goto out;
453     }
454
455     ret = _kdc_check_flags (context, config,
456                             client_entry, client_name,
457                             server_entry, server_name,
458                             TRUE);
459     if (ret) {
460         make_error_reply (hdr, KAPWEXPIRED, reply);
461         goto out;
462     }
463
464     /* find a DES key */
465     ret = _kdc_get_des_key(context, client_entry, FALSE, TRUE, &ckey);
466     if(ret){
467         kdc_log(context, config, 0, "no suitable DES key for client");
468         make_error_reply (hdr, KANOKEYS, reply);
469         goto out;
470     }
471
472     /* find a DES key */
473     ret = _kdc_get_des_key(context, server_entry, TRUE, TRUE, &skey);
474     if(ret){
475         kdc_log(context, config, 0, "no suitable DES key for server");
476         make_error_reply (hdr, KANOKEYS, reply);
477         goto out;
478     }
479
480     {
481         DES_cblock key;
482         DES_key_schedule schedule;
483         
484         /* try to decode the `request' */
485         memcpy (&key, ckey->key.keyvalue.data, sizeof(key));
486         DES_set_key (&key, &schedule);
487         DES_pcbc_encrypt (request.data,
488                           request.data,
489                           request.length,
490                           &schedule,
491                           &key,
492                           DES_DECRYPT);
493         memset (&schedule, 0, sizeof(schedule));
494         memset (&key, 0, sizeof(key));
495     }
496
497     /* check for the magic label */
498     if (memcmp ((char *)request.data + 4, "gTGS", 4) != 0) {
499         kdc_log(context, config, 0, "preauth failed for %s", client_name);
500         make_error_reply (hdr, KABADREQUEST, reply);
501         goto out;
502     }
503
504     reply_sp = krb5_storage_from_mem (request.data, 4);
505     krb5_ret_int32 (reply_sp, &chal);
506     krb5_storage_free (reply_sp);
507
508     if (abs(chal - kdc_time) > context->max_skew) {
509         make_error_reply (hdr, KACLOCKSKEW, reply);
510         goto out;
511     }
512
513     /* life */
514     max_life = end_time - kdc_time;
515     /* end_time - kdc_time can sometimes be non-positive due to slight
516        time skew between client and server. Let's make sure it is postive */
517     if(max_life < 1)
518         max_life = 1;
519     if (client_entry->max_life)
520         max_life = min(max_life, *client_entry->max_life);
521     if (server_entry->max_life)
522         max_life = min(max_life, *server_entry->max_life);
523
524     life = krb_time_to_life(kdc_time, kdc_time + max_life);
525
526     create_reply_ticket (context, 
527                          hdr, skey,
528                          name, instance, config->v4_realm,
529                          addr, life, server_entry->kvno,
530                          max_seq_len,
531                          "krbtgt", config->v4_realm,
532                          chal + 1, "tgsT",
533                          &ckey->key, reply);
534
535  out:
536     if (request.length) {
537         memset (request.data, 0, request.length);
538         krb5_data_free (&request);
539     }
540     if (name)
541         free (name);
542     if (instance)
543         free (instance);
544     if (client_entry)
545         _kdc_free_ent (context, client_entry);
546     if (server_entry)
547         _kdc_free_ent (context, server_entry);
548 }
549
550 static krb5_error_code
551 unparse_getticket_args (krb5_storage *sp,
552                         int *kvno,
553                         char **auth_domain,
554                         krb5_data *ticket,
555                         char **name,
556                         char **instance,
557                         krb5_data *times,
558                         int32_t *max_seq_len)
559 {
560     krb5_data data;
561     int32_t tmp;
562
563     krb5_ret_int32 (sp, &tmp);
564     *kvno = tmp;
565
566     krb5_ret_xdr_data (sp, &data);
567     *auth_domain = malloc(data.length + 1);
568     if (*auth_domain == NULL)
569         return ENOMEM;
570     memcpy (*auth_domain, data.data, data.length);
571     (*auth_domain)[data.length] = '\0';
572     krb5_data_free (&data);
573
574     krb5_ret_xdr_data (sp, ticket);
575
576     krb5_ret_xdr_data (sp, &data);
577     *name = malloc(data.length + 1);
578     if (*name == NULL) {
579         free (*auth_domain);
580         return ENOMEM;
581     }
582     memcpy (*name, data.data, data.length);
583     (*name)[data.length] = '\0';
584     krb5_data_free (&data);
585
586     krb5_ret_xdr_data (sp, &data);
587     *instance = malloc(data.length + 1);
588     if (*instance == NULL) {
589         free (*auth_domain);
590         free (*name);
591         return ENOMEM;
592     }
593     memcpy (*instance, data.data, data.length);
594     (*instance)[data.length] = '\0';
595     krb5_data_free (&data);
596
597     krb5_ret_xdr_data (sp, times);
598
599     krb5_ret_int32 (sp, max_seq_len);
600     /* ignore the rest */
601     return 0;
602 }
603
604 static void
605 do_getticket (krb5_context context, 
606               krb5_kdc_configuration *config,
607               struct rx_header *hdr,
608               krb5_storage *sp,
609               struct sockaddr_in *addr,
610               const char *from,
611               krb5_data *reply)
612 {
613     krb5_error_code ret;
614     int kvno;
615     char *auth_domain = NULL;
616     krb5_data aticket;
617     char *name = NULL;
618     char *instance = NULL;
619     krb5_data times;
620     int32_t max_seq_len;
621     hdb_entry *server_entry = NULL;
622     hdb_entry *client_entry = NULL;
623     hdb_entry *krbtgt_entry = NULL;
624     Key *kkey = NULL;
625     Key *skey = NULL;
626     DES_cblock key;
627     DES_key_schedule schedule;
628     DES_cblock session;
629     time_t max_life;
630     int8_t life;
631     time_t start_time, end_time;
632     char server_name[256];
633     char client_name[256];
634     struct _krb5_krb_auth_data ad;
635
636     krb5_data_zero (&aticket);
637     krb5_data_zero (&times);
638
639     memset(&ad, 0, sizeof(ad));
640
641     unparse_getticket_args (sp, &kvno, &auth_domain, &aticket,
642                             &name, &instance, &times, &max_seq_len);
643     if (times.length < 8) {
644         make_error_reply (hdr, KABADREQUEST, reply);
645         goto out;
646         
647     }
648
649     snprintf (server_name, sizeof(server_name),
650               "%s.%s@%s", name, instance, config->v4_realm);
651
652     ret = _kdc_db_fetch4 (context, config, name, instance, 
653                           config->v4_realm, HDB_ENT_TYPE_SERVER, 
654                           &server_entry);
655     if (ret) {
656         kdc_log(context, config, 0, "Server not found in database: %s: %s",
657                 server_name, krb5_get_err_text(context, ret));
658         make_error_reply (hdr, KANOENT, reply);
659         goto out;
660     }
661
662     ret = _kdc_db_fetch4 (context, config, "krbtgt", 
663                           config->v4_realm, config->v4_realm, 
664                           HDB_ENT_TYPE_CLIENT, &krbtgt_entry);
665     if (ret) {
666         kdc_log(context, config, 0,
667                 "Server not found in database: %s.%s@%s: %s",
668                 "krbtgt", config->v4_realm,  config->v4_realm,
669                 krb5_get_err_text(context, ret));
670         make_error_reply (hdr, KANOENT, reply);
671         goto out;
672     }
673
674     /* find a DES key */
675     ret = _kdc_get_des_key(context, krbtgt_entry, TRUE, TRUE, &kkey);
676     if(ret){
677         kdc_log(context, config, 0, "no suitable DES key for krbtgt");
678         make_error_reply (hdr, KANOKEYS, reply);
679         goto out;
680     }
681
682     /* find a DES key */
683     ret = _kdc_get_des_key(context, server_entry, TRUE, TRUE, &skey);
684     if(ret){
685         kdc_log(context, config, 0, "no suitable DES key for server");
686         make_error_reply (hdr, KANOKEYS, reply);
687         goto out;
688     }
689
690     /* decrypt the incoming ticket */
691     memcpy (&key, kkey->key.keyvalue.data, sizeof(key));
692
693     /* unpack the ticket */
694     {
695         char *sname = NULL;
696         char *sinstance = NULL;
697
698         ret = _krb5_krb_decomp_ticket(context, &aticket, &kkey->key, 
699                                       config->v4_realm, &sname,
700                                       &sinstance, &ad);
701         if (ret) {
702             kdc_log(context, config, 0,
703                     "kaserver: decomp failed for %s.%s with %d",
704                     sname, sinstance, ret);
705             make_error_reply (hdr, KABADTICKET, reply);
706             goto out;
707         }
708
709         if (strcmp (sname, "krbtgt") != 0
710             || strcmp (sinstance, config->v4_realm) != 0) {
711             kdc_log(context, config, 0, "no TGT: %s.%s for %s.%s@%s",
712                     sname, sinstance,
713                     ad.pname, ad.pinst, ad.prealm);
714             make_error_reply (hdr, KABADTICKET, reply);
715             free(sname);
716             free(sinstance);
717             goto out;
718         }
719         free(sname);
720         free(sinstance);
721
722         if (kdc_time > _krb5_krb_life_to_time(ad.time_sec, ad.life)) {
723             kdc_log(context, config, 0, "TGT expired: %s.%s@%s",
724                     ad.pname, ad.pinst, ad.prealm);
725             make_error_reply (hdr, KABADTICKET, reply);
726             goto out;
727         }
728     }
729
730     snprintf (client_name, sizeof(client_name),
731               "%s.%s@%s", ad.pname, ad.pinst, ad.prealm);
732
733     kdc_log(context, config, 0, "TGS-REQ (kaserver) %s from %s for %s",
734             client_name, from, server_name);
735
736     ret = _kdc_db_fetch4 (context, config, 
737                           ad.pname, ad.pinst, ad.prealm, 
738                           HDB_ENT_TYPE_CLIENT, &client_entry);
739     if(ret && ret != HDB_ERR_NOENTRY) {
740         kdc_log(context, config, 0,
741                 "Client not found in database: (krb4) %s: %s",
742                 client_name, krb5_get_err_text(context, ret));
743         make_error_reply (hdr, KANOENT, reply);
744         goto out;
745     }
746     if (client_entry == NULL && strcmp(ad.prealm, config->v4_realm) == 0) {
747         kdc_log(context, config, 0, 
748                 "Local client not found in database: (krb4) "
749                 "%s", client_name);
750         make_error_reply (hdr, KANOENT, reply);
751         goto out;
752     }
753
754     ret = _kdc_check_flags (context, config, 
755                             client_entry, client_name,
756                             server_entry, server_name,
757                             FALSE);
758     if (ret) {
759         make_error_reply (hdr, KAPWEXPIRED, reply);
760         goto out;
761     }
762
763     /* decrypt the times */
764     memcpy(&session, ad.session.keyvalue.data, sizeof(session));
765     DES_set_key (&session, &schedule);
766     DES_ecb_encrypt (times.data,
767                      times.data,
768                      &schedule,
769                      DES_DECRYPT);
770     memset (&schedule, 0, sizeof(schedule));
771     memset (&session, 0, sizeof(session));
772
773     /* and extract them */
774     {
775         krb5_storage *tsp;
776         int32_t tmp;
777
778         tsp = krb5_storage_from_mem (times.data, times.length);
779         krb5_ret_int32 (tsp, &tmp);
780         start_time = tmp;
781         krb5_ret_int32 (tsp, &tmp);
782         end_time = tmp;
783         krb5_storage_free (tsp);
784     }
785
786     /* life */
787     max_life = end_time - kdc_time;
788     /* end_time - kdc_time can sometimes be non-positive due to slight
789        time skew between client and server. Let's make sure it is postive */
790     if(max_life < 1)
791         max_life = 1;
792     if (krbtgt_entry->max_life)
793         max_life = min(max_life, *krbtgt_entry->max_life);
794     if (server_entry->max_life)
795         max_life = min(max_life, *server_entry->max_life);
796     /* if this is a cross realm request, the client_entry will likely
797        be NULL */
798     if (client_entry && client_entry->max_life)
799         max_life = min(max_life, *client_entry->max_life);
800
801     life = _krb5_krb_time_to_life(kdc_time, kdc_time + max_life);
802
803     create_reply_ticket (context, 
804                          hdr, skey,
805                          ad.pname, ad.pinst, ad.prealm,
806                          addr, life, server_entry->kvno,
807                          max_seq_len,
808                          name, instance,
809                          0, "gtkt",
810                          &ad.session, reply);
811     
812  out:
813     _krb5_krb_free_auth_data(context, &ad);
814     if (aticket.length) {
815         memset (aticket.data, 0, aticket.length);
816         krb5_data_free (&aticket);
817     }
818     if (times.length) {
819         memset (times.data, 0, times.length);
820         krb5_data_free (&times);
821     }
822     if (auth_domain)
823         free (auth_domain);
824     if (name)
825         free (name);
826     if (instance)
827         free (instance);
828     if (krbtgt_entry)
829         _kdc_free_ent (context, krbtgt_entry);
830     if (server_entry)
831         _kdc_free_ent (context, server_entry);
832 }
833
834 krb5_error_code
835 _kdc_do_kaserver(krb5_context context, 
836                  krb5_kdc_configuration *config,
837                  unsigned char *buf,
838                  size_t len,
839                  krb5_data *reply,
840                  const char *from,
841                  struct sockaddr_in *addr)
842 {
843     krb5_error_code ret = 0;
844     struct rx_header hdr;
845     u_int32_t op;
846     krb5_storage *sp;
847
848     if (len < RX_HEADER_SIZE)
849         return -1;
850     sp = krb5_storage_from_mem (buf, len);
851
852     decode_rx_header (sp, &hdr);
853     buf += RX_HEADER_SIZE;
854     len -= RX_HEADER_SIZE;
855
856     switch (hdr.type) {
857     case HT_DATA :
858         break;
859     case HT_ACK :
860     case HT_BUSY :
861     case HT_ABORT :
862     case HT_ACKALL :
863     case HT_CHAL :
864     case HT_RESP :
865     case HT_DEBUG :
866     default:
867         /* drop */
868         goto out;
869     }
870
871
872     if (hdr.serviceid != KA_AUTHENTICATION_SERVICE
873         && hdr.serviceid != KA_TICKET_GRANTING_SERVICE) {
874         ret = -1;
875         goto out;
876     }
877
878     krb5_ret_int32(sp, &op);
879     switch (op) {
880     case AUTHENTICATE :
881     case AUTHENTICATE_V2 :
882         do_authenticate (context, config, &hdr, sp, addr, from, reply);
883         break;
884     case GETTICKET :
885         do_getticket (context, config, &hdr, sp, addr, from, reply);
886         break;
887     case AUTHENTICATE_OLD :
888     case CHANGEPASSWORD :
889     case GETTICKET_OLD :
890     case SETPASSWORD :
891     case SETFIELDS :
892     case CREATEUSER :
893     case DELETEUSER :
894     case GETENTRY :
895     case LISTENTRY :
896     case GETSTATS :
897     case DEBUG :
898     case GETPASSWORD :
899     case GETRANDOMKEY :
900     default :
901         make_error_reply (&hdr, RXGEN_OPCODE, reply);
902         break;
903     }
904
905 out:
906     krb5_storage_free (sp);
907     return ret;
908 }