2 * Copyright (c) 1997 - 2000 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
40 #include "kerberos4.h"
43 #define KA_AUTHENTICATION_SERVICE 731
44 #define KA_TICKET_GRANTING_SERVICE 732
45 #define KA_MAINTENANCE_SERVICE 733
47 #define AUTHENTICATE_OLD 1
48 #define CHANGEPASSWORD 2
49 #define GETTICKET_OLD 3
58 #define GETPASSWORD 12
59 #define GETRANDOMKEY 13
60 #define AUTHENTICATE 21
61 #define AUTHENTICATE_V2 22
64 /* XXX - Where do we get these? */
66 #define RXGEN_OPCODE (-455)
68 #define KADATABASEINCONSISTENT (180480L)
69 #define KAEXIST (180481L)
70 #define KAIO (180482L)
71 #define KACREATEFAIL (180483L)
72 #define KANOENT (180484L)
73 #define KAEMPTY (180485L)
74 #define KABADNAME (180486L)
75 #define KABADINDEX (180487L)
76 #define KANOAUTH (180488L)
77 #define KAANSWERTOOLONG (180489L)
78 #define KABADREQUEST (180490L)
79 #define KAOLDINTERFACE (180491L)
80 #define KABADARGUMENT (180492L)
81 #define KABADCMD (180493L)
82 #define KANOKEYS (180494L)
83 #define KAREADPW (180495L)
84 #define KABADKEY (180496L)
85 #define KAUBIKINIT (180497L)
86 #define KAUBIKCALL (180498L)
87 #define KABADPROTOCOL (180499L)
88 #define KANOCELLS (180500L)
89 #define KANOCELL (180501L)
90 #define KATOOMANYUBIKS (180502L)
91 #define KATOOMANYKEYS (180503L)
92 #define KABADTICKET (180504L)
93 #define KAUNKNOWNKEY (180505L)
94 #define KAKEYCACHEINVALID (180506L)
95 #define KABADSERVER (180507L)
96 #define KABADUSER (180508L)
97 #define KABADCPW (180509L)
98 #define KABADCREATE (180510L)
99 #define KANOTICKET (180511L)
100 #define KAASSOCUSER (180512L)
101 #define KANOTSPECIAL (180513L)
102 #define KACLOCKSKEW (180514L)
103 #define KANORECURSE (180515L)
104 #define KARXFAIL (180516L)
105 #define KANULLPASSWORD (180517L)
106 #define KAINTERNALERROR (180518L)
107 #define KAPWEXPIRED (180519L)
108 #define KAREUSED (180520L)
109 #define KATOOSOON (180521L)
110 #define KALOCKED (180522L)
113 decode_rx_header (krb5_storage
*sp
,
116 krb5_ret_int32(sp
, &h
->epoch
);
117 krb5_ret_int32(sp
, &h
->connid
);
118 krb5_ret_int32(sp
, &h
->callid
);
119 krb5_ret_int32(sp
, &h
->seqno
);
120 krb5_ret_int32(sp
, &h
->serialno
);
121 krb5_ret_int8(sp
, &h
->type
);
122 krb5_ret_int8(sp
, &h
->flags
);
123 krb5_ret_int8(sp
, &h
->status
);
124 krb5_ret_int8(sp
, &h
->secindex
);
125 krb5_ret_int16(sp
, &h
->reserved
);
126 krb5_ret_int16(sp
, &h
->serviceid
);
130 encode_rx_header (struct rx_header
*h
,
133 krb5_store_int32(sp
, h
->epoch
);
134 krb5_store_int32(sp
, h
->connid
);
135 krb5_store_int32(sp
, h
->callid
);
136 krb5_store_int32(sp
, h
->seqno
);
137 krb5_store_int32(sp
, h
->serialno
);
138 krb5_store_int8(sp
, h
->type
);
139 krb5_store_int8(sp
, h
->flags
);
140 krb5_store_int8(sp
, h
->status
);
141 krb5_store_int8(sp
, h
->secindex
);
142 krb5_store_int16(sp
, h
->reserved
);
143 krb5_store_int16(sp
, h
->serviceid
);
147 init_reply_header (struct rx_header
*hdr
,
148 struct rx_header
*reply_hdr
,
152 reply_hdr
->epoch
= hdr
->epoch
;
153 reply_hdr
->connid
= hdr
->connid
;
154 reply_hdr
->callid
= hdr
->callid
;
155 reply_hdr
->seqno
= 1;
156 reply_hdr
->serialno
= 1;
157 reply_hdr
->type
= type
;
158 reply_hdr
->flags
= flags
;
159 reply_hdr
->status
= 0;
160 reply_hdr
->secindex
= 0;
161 reply_hdr
->reserved
= 0;
162 reply_hdr
->serviceid
= hdr
->serviceid
;
166 make_error_reply (struct rx_header
*hdr
,
172 struct rx_header reply_hdr
;
174 init_reply_header (hdr
, &reply_hdr
, HT_ABORT
, HF_LAST
);
175 sp
= krb5_storage_emem();
176 encode_rx_header (&reply_hdr
, sp
);
177 krb5_store_int32(sp
, ret
);
178 krb5_storage_to_data (sp
, reply
);
179 krb5_storage_free (sp
);
182 static krb5_error_code
183 krb5_ret_xdr_data(krb5_storage
*sp
,
188 ret
= krb5_ret_int32(sp
, &size
);
194 size_t pad
= (4 - size
% 4) % 4;
196 data
->data
= malloc(size
);
197 if (data
->data
== NULL
)
199 ret
= sp
->fetch(sp
, data
->data
, size
);
201 return (ret
< 0)? errno
: KRB5_CC_END
;
203 ret
= sp
->fetch(sp
, foo
, pad
);
205 return (ret
< 0)? errno
: KRB5_CC_END
;
212 static krb5_error_code
213 krb5_store_xdr_data(krb5_storage
*sp
,
216 u_char zero
[4] = {0, 0, 0, 0};
220 ret
= krb5_store_int32(sp
, data
.length
);
223 ret
= sp
->store(sp
, data
.data
, data
.length
);
224 if(ret
!= data
.length
){
229 pad
= (4 - data
.length
% 4) % 4;
231 ret
= sp
->store(sp
, zero
, pad
);
242 static krb5_error_code
243 create_reply_ticket (struct rx_header
*hdr
,
245 char *name
, char *instance
, char *realm
,
246 struct sockaddr_in
*addr
,
250 char *sname
, char *sinstance
,
260 des_key_schedule schedule
;
261 struct rx_header reply_hdr
;
264 unsigned fyrtiosjuelva
;
266 /* create the ticket */
268 des_new_random_key(&session
);
270 krb_create_ticket (&ticket
, 0, name
, instance
, realm
,
271 addr
->sin_addr
.s_addr
,
272 &session
, life
, kdc_time
,
273 sname
, sinstance
, skey
->key
.keyvalue
.data
);
275 /* create the encrypted part of the reply */
276 sp
= krb5_storage_emem ();
277 krb5_generate_random_block(&fyrtiosjuelva
, sizeof(fyrtiosjuelva
));
278 fyrtiosjuelva
&= 0xffffffff;
279 krb5_store_int32 (sp
, fyrtiosjuelva
);
281 krb5_store_int32 (sp
, 4711); /* XXX */
283 krb5_store_int32 (sp
, challenge
);
284 sp
->store (sp
, session
, 8);
285 memset (&session
, 0, sizeof(session
));
286 krb5_store_int32 (sp
, kdc_time
);
287 krb5_store_int32 (sp
, kdc_time
+ krb_life_to_time (0, life
));
288 krb5_store_int32 (sp
, kvno
);
289 krb5_store_int32 (sp
, ticket
.length
);
290 krb5_store_stringz (sp
, name
);
291 krb5_store_stringz (sp
, instance
);
292 #if 1 /* XXX - Why shouldn't the realm go here? */
293 krb5_store_stringz (sp
, "");
295 krb5_store_stringz (sp
, realm
);
297 krb5_store_stringz (sp
, sname
);
298 krb5_store_stringz (sp
, sinstance
);
299 sp
->store (sp
, ticket
.dat
, ticket
.length
);
300 sp
->store (sp
, label
, strlen(label
));
302 /* pad to DES block */
303 memset (zero
, 0, sizeof(zero
));
304 pad
= (8 - sp
->seek (sp
, 0, SEEK_CUR
) % 8) % 8;
305 sp
->store (sp
, zero
, pad
);
307 krb5_storage_to_data (sp
, &enc_data
);
308 krb5_storage_free (sp
);
310 if (enc_data
.length
> max_seq_len
) {
311 krb5_data_free (&enc_data
);
312 make_error_reply (hdr
, KAANSWERTOOLONG
, reply
);
317 des_set_key (key
, schedule
);
318 des_pcbc_encrypt ((des_cblock
*)enc_data
.data
,
319 (des_cblock
*)enc_data
.data
,
324 memset (&schedule
, 0, sizeof(schedule
));
326 /* create the reply packet */
327 init_reply_header (hdr
, &reply_hdr
, HT_DATA
, HF_LAST
);
328 sp
= krb5_storage_emem ();
329 encode_rx_header (&reply_hdr
, sp
);
330 krb5_store_int32 (sp
, max_seq_len
);
331 krb5_store_xdr_data (sp
, enc_data
);
332 krb5_data_free (&enc_data
);
333 krb5_storage_to_data (sp
, reply
);
334 krb5_storage_free (sp
);
338 static krb5_error_code
339 unparse_auth_args (krb5_storage
*sp
,
345 int32_t *max_seq_len
)
350 krb5_ret_xdr_data (sp
, &data
);
351 *name
= malloc(data
.length
+ 1);
354 memcpy (*name
, data
.data
, data
.length
);
355 (*name
)[data
.length
] = '\0';
356 krb5_data_free (&data
);
358 krb5_ret_xdr_data (sp
, &data
);
359 *instance
= malloc(data
.length
+ 1);
360 if (*instance
== NULL
) {
364 memcpy (*instance
, data
.data
, data
.length
);
365 (*instance
)[data
.length
] = '\0';
366 krb5_data_free (&data
);
368 krb5_ret_int32 (sp
, &tmp
);
370 krb5_ret_int32 (sp
, &tmp
);
372 krb5_ret_xdr_data (sp
, request
);
373 krb5_ret_int32 (sp
, max_seq_len
);
374 /* ignore the rest */
379 do_authenticate (struct rx_header
*hdr
,
381 struct sockaddr_in
*addr
,
386 char *instance
= NULL
;
391 hdb_entry
*client_entry
= NULL
;
392 hdb_entry
*server_entry
= NULL
;
396 des_key_schedule schedule
;
397 krb5_storage
*reply_sp
;
402 krb5_data_zero (&request
);
404 unparse_auth_args (sp
, &name
, &instance
, &start_time
, &end_time
,
405 &request
, &max_seq_len
);
407 client_entry
= db_fetch4 (name
, instance
, v4_realm
);
408 if (client_entry
== NULL
) {
409 kdc_log(0, "Client not found in database: %s.%s@%s",
410 name
, instance
, v4_realm
);
411 make_error_reply (hdr
, KANOENT
, reply
);
415 server_entry
= db_fetch4 ("krbtgt", v4_realm
, v4_realm
);
416 if (server_entry
== NULL
) {
417 kdc_log(0, "Server not found in database: %s.%s@%s",
418 "krbtgt", v4_realm
, v4_realm
);
419 make_error_reply (hdr
, KANOENT
, reply
);
424 ret
= get_des_key(client_entry
, &ckey
);
426 kdc_log(0, "%s", krb5_get_err_text(context
, ret
));
427 make_error_reply (hdr
, KANOKEYS
, reply
);
432 ret
= get_des_key(server_entry
, &skey
);
434 kdc_log(0, "%s", krb5_get_err_text(context
, ret
));
435 make_error_reply (hdr
, KANOKEYS
, reply
);
439 /* try to decode the `request' */
440 memcpy (&key
, ckey
->key
.keyvalue
.data
, sizeof(key
));
441 des_set_key (&key
, schedule
);
442 des_pcbc_encrypt ((des_cblock
*)request
.data
,
443 (des_cblock
*)request
.data
,
448 memset (&schedule
, 0, sizeof(schedule
));
450 /* check for the magic label */
451 if (memcmp ((char *)request
.data
+ 4, "gTGS", 4) != 0) {
452 make_error_reply (hdr
, KABADREQUEST
, reply
);
456 reply_sp
= krb5_storage_from_mem (request
.data
, 4);
457 krb5_ret_int32 (reply_sp
, &chal
);
458 krb5_storage_free (reply_sp
);
461 max_life
= end_time
- kdc_time
;
462 if (client_entry
->max_life
)
463 max_life
= min(max_life
, *client_entry
->max_life
);
464 if (server_entry
->max_life
)
465 max_life
= min(max_life
, *server_entry
->max_life
);
467 life
= krb_time_to_life(kdc_time
, kdc_time
+ max_life
);
469 create_reply_ticket (hdr
, skey
,
470 name
, instance
, v4_realm
,
471 addr
, life
, server_entry
->kvno
,
476 memset (&key
, 0, sizeof(key
));
479 if (request
.length
) {
480 memset (request
.data
, 0, request
.length
);
481 krb5_data_free (&request
);
488 hdb_free_entry (context
, client_entry
);
492 hdb_free_entry (context
, server_entry
);
497 static krb5_error_code
498 unparse_getticket_args (krb5_storage
*sp
,
505 int32_t *max_seq_len
)
510 krb5_ret_int32 (sp
, &tmp
);
513 krb5_ret_xdr_data (sp
, &data
);
514 *auth_domain
= malloc(data
.length
+ 1);
515 if (*auth_domain
== NULL
)
517 memcpy (*auth_domain
, data
.data
, data
.length
);
518 (*auth_domain
)[data
.length
] = '\0';
519 krb5_data_free (&data
);
521 krb5_ret_xdr_data (sp
, ticket
);
523 krb5_ret_xdr_data (sp
, &data
);
524 *name
= malloc(data
.length
+ 1);
529 memcpy (*name
, data
.data
, data
.length
);
530 (*name
)[data
.length
] = '\0';
531 krb5_data_free (&data
);
533 krb5_ret_xdr_data (sp
, &data
);
534 *instance
= malloc(data
.length
+ 1);
535 if (*instance
== NULL
) {
540 memcpy (*instance
, data
.data
, data
.length
);
541 (*instance
)[data
.length
] = '\0';
542 krb5_data_free (&data
);
544 krb5_ret_xdr_data (sp
, times
);
546 krb5_ret_int32 (sp
, max_seq_len
);
547 /* ignore the rest */
552 do_getticket (struct rx_header
*hdr
,
554 struct sockaddr_in
*addr
,
559 char *auth_domain
= NULL
;
562 char *instance
= NULL
;
565 hdb_entry
*server_entry
= NULL
;
566 hdb_entry
*krbtgt_entry
= NULL
;
570 des_key_schedule schedule
;
574 time_t start_time
, end_time
;
575 char pname
[ANAME_SZ
];
577 char prealm
[REALM_SZ
];
579 krb5_data_zero (&aticket
);
580 krb5_data_zero (×
);
582 unparse_getticket_args (sp
, &kvno
, &auth_domain
, &aticket
,
583 &name
, &instance
, ×
, &max_seq_len
);
585 server_entry
= db_fetch4 (name
, instance
, v4_realm
);
586 if (server_entry
== NULL
) {
587 kdc_log(0, "Server not found in database: %s.%s@%s",
588 name
, instance
, v4_realm
);
589 make_error_reply (hdr
, KANOENT
, reply
);
593 krbtgt_entry
= db_fetch4 ("krbtgt", v4_realm
, v4_realm
);
594 if (krbtgt_entry
== NULL
) {
595 kdc_log(0, "Server not found in database: %s.%s@%s",
596 "krbtgt", v4_realm
, v4_realm
);
597 make_error_reply (hdr
, KANOENT
, reply
);
602 ret
= get_des_key(krbtgt_entry
, &kkey
);
604 kdc_log(0, "%s", krb5_get_err_text(context
, ret
));
605 make_error_reply (hdr
, KANOKEYS
, reply
);
610 ret
= get_des_key(server_entry
, &skey
);
612 kdc_log(0, "%s", krb5_get_err_text(context
, ret
));
613 make_error_reply (hdr
, KANOKEYS
, reply
);
617 /* decrypt the incoming ticket */
618 memcpy (&key
, kkey
->key
.keyvalue
.data
, sizeof(key
));
620 /* unpack the ticket */
626 char sname
[ANAME_SZ
];
627 char sinstance
[SNAME_SZ
];
630 ticket
.length
= aticket
.length
;
631 memcpy (ticket
.dat
, aticket
.data
, ticket
.length
);
633 des_set_key (&key
, schedule
);
634 decomp_ticket (&ticket
, &flags
, pname
, pinst
, prealm
,
635 &paddress
, session
, &life
, &time_sec
,
639 if (strcmp (sname
, "krbtgt") != 0
640 || strcmp (sinstance
, v4_realm
) != 0) {
641 kdc_log(0, "no TGT: %s.%s for %s.%s@%s",
643 pname
, pinst
, prealm
);
644 make_error_reply (hdr
, KABADTICKET
, reply
);
648 if (kdc_time
> krb_life_to_time(time_sec
, life
)) {
649 kdc_log(0, "TGT expired: %s.%s@%s",
650 pname
, pinst
, prealm
);
651 make_error_reply (hdr
, KABADTICKET
, reply
);
656 /* decrypt the times */
657 des_set_key (&session
, schedule
);
658 des_ecb_encrypt (times
.data
,
662 memset (&schedule
, 0, sizeof(schedule
));
664 /* and extract them */
669 sp
= krb5_storage_from_mem (times
.data
, times
.length
);
670 krb5_ret_int32 (sp
, &tmp
);
672 krb5_ret_int32 (sp
, &tmp
);
674 krb5_storage_free (sp
);
678 max_life
= end_time
- kdc_time
;
679 if (krbtgt_entry
->max_life
)
680 max_life
= min(max_life
, *krbtgt_entry
->max_life
);
681 if (server_entry
->max_life
)
682 max_life
= min(max_life
, *server_entry
->max_life
);
684 life
= krb_time_to_life(kdc_time
, kdc_time
+ max_life
);
686 create_reply_ticket (hdr
, skey
,
687 pname
, pinst
, prealm
,
688 addr
, life
, server_entry
->kvno
,
693 memset (&session
, 0, sizeof(session
));
696 if (aticket
.length
) {
697 memset (aticket
.data
, 0, aticket
.length
);
698 krb5_data_free (&aticket
);
701 memset (times
.data
, 0, times
.length
);
702 krb5_data_free (×
);
711 hdb_free_entry (context
, krbtgt_entry
);
715 hdb_free_entry (context
, server_entry
);
721 do_kaserver(unsigned char *buf
,
725 struct sockaddr_in
*addr
)
727 krb5_error_code ret
= 0;
728 struct rx_header hdr
;
732 if (len
< RX_HEADER_SIZE
)
734 sp
= krb5_storage_from_mem (buf
, len
);
736 decode_rx_header (sp
, &hdr
);
737 buf
+= RX_HEADER_SIZE
;
738 len
-= RX_HEADER_SIZE
;
756 if (hdr
.serviceid
!= KA_AUTHENTICATION_SERVICE
757 && hdr
.serviceid
!= KA_TICKET_GRANTING_SERVICE
) {
762 krb5_ret_int32(sp
, &op
);
765 do_authenticate (&hdr
, sp
, addr
, reply
);
768 do_getticket (&hdr
, sp
, addr
, reply
);
770 case AUTHENTICATE_OLD
:
771 case CHANGEPASSWORD
:
783 case AUTHENTICATE_V2
:
785 make_error_reply (&hdr
, RXGEN_OPCODE
, reply
);
790 krb5_storage_free (sp
);
794 #endif /* KASERVER */