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
36RCSID("$Id: kaserver.c 21654 2007-07-21 17:30:18Z 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
111static krb5_error_code
112decode_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
143static krb5_error_code
144encode_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
175static void
176init_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 */
198static void
199make_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
220static krb5_error_code
221krb5_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
252static krb5_error_code
253krb5_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
282static krb5_error_code
283create_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 (&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
392static krb5_error_code
393unparse_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
432static void
433do_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 (&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
591static krb5_error_code
592unparse_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
645static void
646do_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 (&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
873krb5_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
948out:
949    krb5_storage_free (sp);
950    return ret;
951}
952