2 * Copyright (c) 1997 - 2005 Kungliga Tekniska Högskolan
3 * (Royal Institute of Technology, Stockholm, Sweden).
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
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.
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.
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
36 RCSID("$Id: kaserver.c 21654 2007-07-21 17:30:18Z lha $");
38 #include <krb5-v4compat.h>
41 #define KA_AUTHENTICATION_SERVICE 731
42 #define KA_TICKET_GRANTING_SERVICE 732
43 #define KA_MAINTENANCE_SERVICE 733
45 #define AUTHENTICATE_OLD 1
46 #define CHANGEPASSWORD 2
47 #define GETTICKET_OLD 3
56 #define GETPASSWORD 12
57 #define GETRANDOMKEY 13
58 #define AUTHENTICATE 21
59 #define AUTHENTICATE_V2 22
62 /* XXX - Where do we get these? */
64 #define RXGEN_OPCODE (-455)
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)
111 static krb5_error_code
112 decode_rx_header (krb5_storage *sp,
117 ret = krb5_ret_uint32(sp, &h->epoch);
119 ret = krb5_ret_uint32(sp, &h->connid);
121 ret = krb5_ret_uint32(sp, &h->callid);
123 ret = krb5_ret_uint32(sp, &h->seqno);
125 ret = krb5_ret_uint32(sp, &h->serialno);
127 ret = krb5_ret_uint8(sp, &h->type);
129 ret = krb5_ret_uint8(sp, &h->flags);
131 ret = krb5_ret_uint8(sp, &h->status);
133 ret = krb5_ret_uint8(sp, &h->secindex);
135 ret = krb5_ret_uint16(sp, &h->reserved);
137 ret = krb5_ret_uint16(sp, &h->serviceid);
143 static krb5_error_code
144 encode_rx_header (struct rx_header *h,
149 ret = krb5_store_uint32(sp, h->epoch);
151 ret = krb5_store_uint32(sp, h->connid);
153 ret = krb5_store_uint32(sp, h->callid);
155 ret = krb5_store_uint32(sp, h->seqno);
157 ret = krb5_store_uint32(sp, h->serialno);
159 ret = krb5_store_uint8(sp, h->type);
161 ret = krb5_store_uint8(sp, h->flags);
163 ret = krb5_store_uint8(sp, h->status);
165 ret = krb5_store_uint8(sp, h->secindex);
167 ret = krb5_store_uint16(sp, h->reserved);
169 ret = krb5_store_uint16(sp, h->serviceid);
176 init_reply_header (struct rx_header *hdr,
177 struct rx_header *reply_hdr,
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;
195 * Create an error `reply´ using for the packet `hdr' with the error
199 make_error_reply (struct rx_header *hdr,
204 struct rx_header reply_hdr;
208 init_reply_header (hdr, &reply_hdr, HT_ABORT, HF_LAST);
209 sp = krb5_storage_emem();
212 ret = encode_rx_header (&reply_hdr, sp);
215 krb5_store_int32(sp, error);
216 krb5_storage_to_data (sp, reply);
217 krb5_storage_free (sp);
220 static krb5_error_code
221 krb5_ret_xdr_data(krb5_storage *sp,
226 ret = krb5_ret_int32(sp, &size);
234 size_t pad = (4 - size % 4) % 4;
236 data->data = malloc(size);
237 if (data->data == NULL)
239 ret = krb5_storage_read(sp, data->data, size);
241 return (ret < 0)? errno : KRB5_CC_END;
243 ret = krb5_storage_read(sp, foo, pad);
245 return (ret < 0)? errno : KRB5_CC_END;
252 static krb5_error_code
253 krb5_store_xdr_data(krb5_storage *sp,
256 u_char zero[4] = {0, 0, 0, 0};
260 ret = krb5_store_int32(sp, data.length);
263 ret = krb5_storage_write(sp, data.data, data.length);
264 if(ret != data.length){
269 pad = (4 - data.length % 4) % 4;
271 ret = krb5_storage_write(sp, zero, pad);
282 static krb5_error_code
283 create_reply_ticket (krb5_context context,
284 struct rx_header *hdr,
286 char *name, char *instance, char *realm,
287 struct sockaddr_in *addr,
291 const char *sname, const char *sinstance,
299 krb5_keyblock session;
302 struct rx_header reply_hdr;
305 unsigned fyrtiosjuelva;
307 /* create the ticket */
309 krb5_generate_random_keyblock(context, ETYPE_DES_PCBC_NONE, &session);
311 _krb5_krb_create_ticket(context,
316 addr->sin_addr.s_addr,
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, "");
342 krb5_store_stringz (sp, realm);
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));
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);
354 krb5_storage_to_data (sp, &enc_data);
355 krb5_storage_free (sp);
357 if (enc_data.length > max_seq_len) {
358 krb5_data_free (&enc_data);
359 make_error_reply (hdr, KAANSWERTOOLONG, reply);
365 DES_key_schedule schedule;
368 memcpy (&deskey, key->keyvalue.data, sizeof(deskey));
369 DES_set_key (&deskey, &schedule);
370 DES_pcbc_encrypt (enc_data.data,
376 memset (&schedule, 0, sizeof(schedule));
377 memset (&deskey, 0, sizeof(deskey));
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);
392 static krb5_error_code
393 unparse_auth_args (krb5_storage *sp,
399 int32_t *max_seq_len)
404 krb5_ret_xdr_data (sp, &data);
405 *name = malloc(data.length + 1);
408 memcpy (*name, data.data, data.length);
409 (*name)[data.length] = '\0';
410 krb5_data_free (&data);
412 krb5_ret_xdr_data (sp, &data);
413 *instance = malloc(data.length + 1);
414 if (*instance == NULL) {
418 memcpy (*instance, data.data, data.length);
419 (*instance)[data.length] = '\0';
420 krb5_data_free (&data);
422 krb5_ret_int32 (sp, &tmp);
424 krb5_ret_int32 (sp, &tmp);
426 krb5_ret_xdr_data (sp, request);
427 krb5_ret_int32 (sp, max_seq_len);
428 /* ignore the rest */
433 do_authenticate (krb5_context context,
434 krb5_kdc_configuration *config,
435 struct rx_header *hdr,
437 struct sockaddr_in *addr,
443 char *instance = NULL;
448 hdb_entry_ex *client_entry = NULL;
449 hdb_entry_ex *server_entry = NULL;
452 krb5_storage *reply_sp;
456 char client_name[256];
457 char server_name[256];
459 krb5_data_zero (&request);
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);
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);
473 kdc_log(context, config, 0, "AS-REQ (kaserver) %s from %s for %s",
474 client_name, from, server_name);
476 ret = _kdc_db_fetch4 (context, config, name, instance,
477 config->v4_realm, HDB_F_GET_CLIENT,
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);
486 ret = _kdc_db_fetch4 (context, config, "krbtgt",
487 config->v4_realm, config->v4_realm,
488 HDB_F_GET_KRBTGT, &server_entry);
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);
496 ret = _kdc_check_flags (context, config,
497 client_entry, client_name,
498 server_entry, server_name,
501 make_error_reply (hdr, KAPWEXPIRED, reply);
506 ret = _kdc_get_des_key(context, client_entry, FALSE, TRUE, &ckey);
508 kdc_log(context, config, 0, "no suitable DES key for client");
509 make_error_reply (hdr, KANOKEYS, reply);
514 ret = _kdc_get_des_key(context, server_entry, TRUE, TRUE, &skey);
516 kdc_log(context, config, 0, "no suitable DES key for server");
517 make_error_reply (hdr, KANOKEYS, reply);
523 DES_key_schedule schedule;
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,
534 memset (&schedule, 0, sizeof(schedule));
535 memset (&key, 0, sizeof(key));
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);
545 reply_sp = krb5_storage_from_mem (request.data, 4);
546 krb5_ret_int32 (reply_sp, &chal);
547 krb5_storage_free (reply_sp);
549 if (abs(chal - kdc_time) > context->max_skew) {
550 make_error_reply (hdr, KACLOCKSKEW, reply);
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 */
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);
565 life = krb_time_to_life(kdc_time, kdc_time + max_life);
567 create_reply_ticket (context,
569 name, instance, config->v4_realm,
570 addr, life, server_entry->entry.kvno,
572 "krbtgt", config->v4_realm,
577 if (request.length) {
578 memset (request.data, 0, request.length);
579 krb5_data_free (&request);
586 _kdc_free_ent (context, client_entry);
588 _kdc_free_ent (context, server_entry);
591 static krb5_error_code
592 unparse_getticket_args (krb5_storage *sp,
599 int32_t *max_seq_len)
604 krb5_ret_int32 (sp, &tmp);
607 krb5_ret_xdr_data (sp, &data);
608 *auth_domain = malloc(data.length + 1);
609 if (*auth_domain == NULL)
611 memcpy (*auth_domain, data.data, data.length);
612 (*auth_domain)[data.length] = '\0';
613 krb5_data_free (&data);
615 krb5_ret_xdr_data (sp, ticket);
617 krb5_ret_xdr_data (sp, &data);
618 *name = malloc(data.length + 1);
623 memcpy (*name, data.data, data.length);
624 (*name)[data.length] = '\0';
625 krb5_data_free (&data);
627 krb5_ret_xdr_data (sp, &data);
628 *instance = malloc(data.length + 1);
629 if (*instance == NULL) {
634 memcpy (*instance, data.data, data.length);
635 (*instance)[data.length] = '\0';
636 krb5_data_free (&data);
638 krb5_ret_xdr_data (sp, times);
640 krb5_ret_int32 (sp, max_seq_len);
641 /* ignore the rest */
646 do_getticket (krb5_context context,
647 krb5_kdc_configuration *config,
648 struct rx_header *hdr,
650 struct sockaddr_in *addr,
656 char *auth_domain = NULL;
659 char *instance = NULL;
662 hdb_entry_ex *server_entry = NULL;
663 hdb_entry_ex *client_entry = NULL;
664 hdb_entry_ex *krbtgt_entry = NULL;
668 DES_key_schedule schedule;
672 time_t start_time, end_time;
673 char server_name[256];
674 char client_name[256];
675 struct _krb5_krb_auth_data ad;
677 krb5_data_zero (&aticket);
678 krb5_data_zero (×);
680 memset(&ad, 0, sizeof(ad));
682 unparse_getticket_args (sp, &kvno, &auth_domain, &aticket,
683 &name, &instance, ×, &max_seq_len);
684 if (times.length < 8) {
685 make_error_reply (hdr, KABADREQUEST, reply);
690 snprintf (server_name, sizeof(server_name),
691 "%s.%s@%s", name, instance, config->v4_realm);
693 ret = _kdc_db_fetch4 (context, config, name, instance,
694 config->v4_realm, HDB_F_GET_SERVER, &server_entry);
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);
702 ret = _kdc_db_fetch4 (context, config, "krbtgt",
703 config->v4_realm, config->v4_realm, HDB_F_GET_KRBTGT, &krbtgt_entry);
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);
714 ret = _kdc_get_des_key(context, krbtgt_entry, TRUE, TRUE, &kkey);
716 kdc_log(context, config, 0, "no suitable DES key for krbtgt");
717 make_error_reply (hdr, KANOKEYS, reply);
722 ret = _kdc_get_des_key(context, server_entry, TRUE, TRUE, &skey);
724 kdc_log(context, config, 0, "no suitable DES key for server");
725 make_error_reply (hdr, KANOKEYS, reply);
729 /* decrypt the incoming ticket */
730 memcpy (&key, kkey->key.keyvalue.data, sizeof(key));
732 /* unpack the ticket */
735 char *sinstance = NULL;
737 ret = _krb5_krb_decomp_ticket(context, &aticket, &kkey->key,
738 config->v4_realm, &sname,
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);
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",
752 ad.pname, ad.pinst, ad.prealm);
753 make_error_reply (hdr, KABADTICKET, reply);
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);
769 snprintf (client_name, sizeof(client_name),
770 "%s.%s@%s", ad.pname, ad.pinst, ad.prealm);
772 kdc_log(context, config, 0, "TGS-REQ (kaserver) %s from %s for %s",
773 client_name, from, server_name);
775 ret = _kdc_db_fetch4 (context, config,
776 ad.pname, ad.pinst, ad.prealm, HDB_F_GET_CLIENT,
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);
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) "
789 make_error_reply (hdr, KANOENT, reply);
793 ret = _kdc_check_flags (context, config,
794 client_entry, client_name,
795 server_entry, server_name,
798 make_error_reply (hdr, KAPWEXPIRED, reply);
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,
809 memset (&schedule, 0, sizeof(schedule));
810 memset (&session, 0, sizeof(session));
812 /* and extract them */
817 tsp = krb5_storage_from_mem (times.data, times.length);
818 krb5_ret_int32 (tsp, &tmp);
820 krb5_ret_int32 (tsp, &tmp);
822 krb5_storage_free (tsp);
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 */
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
837 if (client_entry && client_entry->entry.max_life)
838 max_life = min(max_life, *client_entry->entry.max_life);
840 life = _krb5_krb_time_to_life(kdc_time, kdc_time + max_life);
842 create_reply_ticket (context,
844 ad.pname, ad.pinst, ad.prealm,
845 addr, life, server_entry->entry.kvno,
852 _krb5_krb_free_auth_data(context, &ad);
853 if (aticket.length) {
854 memset (aticket.data, 0, aticket.length);
855 krb5_data_free (&aticket);
858 memset (times.data, 0, times.length);
859 krb5_data_free (×);
868 _kdc_free_ent (context, krbtgt_entry);
870 _kdc_free_ent (context, server_entry);
874 _kdc_do_kaserver(krb5_context context,
875 krb5_kdc_configuration *config,
880 struct sockaddr_in *addr)
882 krb5_error_code ret = 0;
883 struct rx_header hdr;
887 if (len < RX_HEADER_SIZE)
889 sp = krb5_storage_from_mem (buf, len);
891 ret = decode_rx_header (sp, &hdr);
894 buf += RX_HEADER_SIZE;
895 len -= RX_HEADER_SIZE;
913 if (hdr.serviceid != KA_AUTHENTICATION_SERVICE
914 && hdr.serviceid != KA_TICKET_GRANTING_SERVICE) {
919 ret = krb5_ret_uint32(sp, &op);
924 case AUTHENTICATE_V2 :
925 do_authenticate (context, config, &hdr, sp, addr, from, reply);
928 do_getticket (context, config, &hdr, sp, addr, from, reply);
930 case AUTHENTICATE_OLD :
931 case CHANGEPASSWORD :
944 make_error_reply (&hdr, RXGEN_OPCODE, reply);
949 krb5_storage_free (sp);