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