2 * $Id: kopt.c,v 1.14 1999/01/22 23:19:44 ghudson Exp $
4 * Copyright 1985, 1986, 1987, 1988, 1990, 1991 by the Massachusetts
5 * Institute of Technology.
7 * For copying and distribution information, please see the file
12 * This includes code taken from:
13 * Kerberos: rd_req.c,v 4.16 89/03/22 14:52:06 jtkohl Exp
14 * Kerberos: prot.h,v 4.13 89/01/24 14:27:22 jtkohl Exp
15 * Kerberos: krb_conf.h,v 4.0 89/01/23 09:59:27 jtkohl Exp
18 #include <zephyr/mit-copyright.h>
23 static const char *rcsid_rd_req_c =
24 "$Id: kopt.c,v 1.14 1999/01/22 23:19:44 ghudson Exp $";
32 #undef HOST_BYTE_ORDER
33 static int krbONE = 1;
34 #define HOST_BYTE_ORDER (* (char *) &krbONE)
36 #define KRB_PROT_VERSION 4
38 /* Message types , always leave lsb for byte order */
40 #define AUTH_MSG_KDC_REQUEST 1<<1
41 #define AUTH_MSG_KDC_REPLY 2<<1
42 #define AUTH_MSG_APPL_REQUEST 3<<1
43 #define AUTH_MSG_APPL_REQUEST_MUTUAL 4<<1
44 #define AUTH_MSG_ERR_REPLY 5<<1
45 #define AUTH_MSG_PRIVATE 6<<1
46 #define AUTH_MSG_SAFE 7<<1
47 #define AUTH_MSG_APPL_ERR 8<<1
48 #define AUTH_MSG_DIE 63<<1
50 /* values for kerb error codes */
53 #define KERB_ERR_NAME_EXP 1
54 #define KERB_ERR_SERVICE_EXP 2
55 #define KERB_ERR_AUTH_EXP 3
56 #define KERB_ERR_PKT_VER 4
57 #define KERB_ERR_NAME_MAST_KEY_VER 5
58 #define KERB_ERR_SERV_MAST_KEY_VER 6
59 #define KERB_ERR_BYTE_ORDER 7
60 #define KERB_ERR_PRINCIPAL_UNKNOWN 8
61 #define KERB_ERR_PRINCIPAL_NOT_UNIQUE 9
62 #define KERB_ERR_NULL_KEY 10
64 extern int krb_ap_req_debug;
66 extern struct timeval t_local;
69 * Keep the following information around for subsequent calls
70 * to this routine by the same server using the same key.
73 static Sched serv_ksched; /* Key sched to decrypt ticket */
74 static des_cblock serv_key; /* Initialization vector */
76 static int st_kvno; /* version number for this key */
77 static char st_rlm[REALM_SZ]; /* server's realm */
78 static char st_nam[ANAME_SZ]; /* service name */
79 static char st_inst[INST_SZ]; /* server's instance */
82 * Cache of key schedules
84 #define HASH_SIZE_1 255 /* not a power of 2 */
86 static unsigned long last_use;
88 unsigned long last_time_used;
92 static KeySchedRec scheds[HASH_SIZE_1][HASH_SIZE_2];
94 Sched *check_key_sched_cache(key)
97 unsigned int hash_value = key[0] + key[1] * 256;
98 KeySchedRec *rec = scheds[hash_value % HASH_SIZE_1];
101 for (i = HASH_SIZE_2 - 1; i >= 0; i--) {
102 if (rec[i].last_time_used && key[0] == rec[i].key[0]
103 && !memcmp(key, rec[i].key, sizeof(des_cblock))) {
104 rec[i].last_time_used = last_use++;
105 return &rec[i].schedule;
111 void add_to_key_sched_cache(key, sched)
115 unsigned int hash_value = key[0] + key[1] * 256;
116 KeySchedRec *rec = scheds[hash_value % HASH_SIZE_1];
117 int i, oldest = HASH_SIZE_2 - 1;
119 for (i = HASH_SIZE_2 - 1; i >= 0; i--) {
120 if (rec[i].last_time_used == 0) {
124 if (rec[i].last_time_used < rec[oldest].last_time_used)
127 memcpy (rec[oldest].key, key, sizeof(des_cblock));
128 rec[oldest].schedule = *sched;
129 rec[oldest].last_time_used = last_use++;
133 * This file contains two functions. krb_set_key() takes a DES
134 * key or password string and returns a DES key (either the original
135 * key, or the password converted into a DES key) and a key schedule
138 * krb_rd_req() reads an authentication request and returns information
139 * about the identity of the requestor, or an indication that the
140 * identity information was not authentic.
144 * krb_set_key() takes as its first argument either a DES key or a
145 * password string. The "cvt" argument indicates how the first
146 * argument "key" is to be interpreted: if "cvt" is null, "key" is
147 * taken to be a DES key; if "cvt" is non-null, "key" is taken to
148 * be a password string, and is converted into a DES key using
149 * string_to_key(). In either case, the resulting key is returned
150 * in the external variable "serv_key". A key schedule is
151 * generated for "serv_key" and returned in the external variable
154 * This routine returns the return value of des_key_sched.
156 * krb_set_key() needs to be in the same .o file as krb_rd_req() so that
157 * the key set by krb_set_key() is available in private storage for
167 memset(serv_key, 0, sizeof(serv_key));
174 string_to_key(key,serv_key);
176 memcpy((char *)serv_key,key,8);
178 s = check_key_sched_cache (serv_key);
183 ret = des_key_sched(serv_key, serv_ksched.s);
184 add_to_key_sched_cache(serv_key, &serv_ksched);
186 #endif /* NOENCRYPTION */
191 * krb_rd_req() takes an AUTH_MSG_APPL_REQUEST or
192 * AUTH_MSG_APPL_REQUEST_MUTUAL message created by krb_mk_req(),
193 * checks its integrity and returns a judgement as to the requestor's
196 * The "authent" argument is a pointer to the received message.
197 * The "service" and "instance" arguments name the receiving server,
198 * and are used to get the service's ticket to decrypt the ticket
199 * in the message, and to compare against the server name inside the
200 * ticket. "from_addr" is the network address of the host from which
201 * the message was received; this is checked against the network
202 * address in the ticket. If "from_addr" is zero, the check is not
203 * performed. "ad" is an AUTH_DAT structure which is
204 * filled in with information about the sender's identity according
205 * to the authenticator and ticket sent in the message. Finally,
206 * "fn" contains the name of the file containing the server's key.
207 * (If "fn" is NULL, the server's key is assumed to have been set
208 * by krb_set_key(). If "fn" is the null string ("") the default
209 * file KEYFILE, defined in "krb.h", is used.)
211 * krb_rd_req() returns RD_AP_OK if the authentication information
212 * was genuine, or one of the following error codes (defined in
215 * RD_AP_VERSION - wrong protocol version number
216 * RD_AP_MSG_TYPE - wrong message type
217 * RD_AP_UNDEC - couldn't decipher the message
218 * RD_AP_INCON - inconsistencies found
219 * RD_AP_BADD - wrong network address
220 * RD_AP_TIME - client time (in authenticator)
221 * too far off server time
222 * RD_AP_NYV - Kerberos time (in ticket) too
223 * far off server time
224 * RD_AP_EXP - ticket expired
226 * For the message format, see krb_mk_req().
228 * Mutual authentication is not implemented.
232 krb_rd_req(authent,service,instance,from_addr,ad,fn)
233 KTEXT authent; /* The received message */
234 char *service; /* Service name */
235 char *instance; /* Service instance */
236 unsigned KRB_INT32 from_addr; /* Net address of originating host */
237 AUTH_DAT *ad; /* Structure to be filled in */
238 char *fn; /* Filename to get keys from */
240 KTEXT_ST ticket; /* Temp storage for ticket */
242 KTEXT_ST req_id_st; /* Temp storage for authenticator */
243 KTEXT req_id = &req_id_st;
245 char realm[REALM_SZ]; /* Realm of issuing kerberos */
246 Sched seskey_sched, *sched; /* Key sched for session key */
247 unsigned char skey[KKEY_SZ]; /* Session key from ticket */
248 char sname[SNAME_SZ]; /* Service name from ticket */
249 char iname[INST_SZ]; /* Instance name from ticket */
250 char r_aname[ANAME_SZ]; /* Client name from authenticator */
251 char r_inst[INST_SZ]; /* Client instance from authenticator */
252 char r_realm[REALM_SZ]; /* Client realm from authenticator */
253 unsigned int r_time_ms; /* Fine time from authenticator */
254 unsigned long r_time_sec; /* Coarse time from authenticator */
255 char *ptr; /* For stepping through */
256 unsigned long delta_t; /* Time in authenticator - local time */
257 long tkt_age; /* Age of ticket */
258 int swap_bytes; /* Need to swap bytes? */
259 int mutual; /* Mutual authentication requested? */
260 unsigned char s_kvno; /* Version number of the server's key
261 * Kerberos used to encrypt ticket */
264 if (authent->length <= 0)
265 return(RD_AP_MODIFIED);
267 ptr = (char *) authent->dat;
269 /* get msg version, type and byte order, and server key version */
272 if (KRB_PROT_VERSION != (unsigned int) *ptr++)
273 return RD_AP_VERSION;
277 if ((*ptr & 1) != HOST_BYTE_ORDER)
282 switch (*ptr++ & ~1) {
283 case AUTH_MSG_APPL_REQUEST:
285 case AUTH_MSG_APPL_REQUEST_MUTUAL:
289 return(RD_AP_MSG_TYPE);
293 /* XXX mutual is set but not used; why??? */
294 /* this is a crock to get lint to shut up */
298 s_kvno = *ptr++; /* get server key version */
299 strcpy(realm,ptr); /* And the realm of the issuing KDC */
300 ptr += strlen(ptr) + 1; /* skip the realm "hint" */
303 * If "fn" is NULL, key info should already be set; don't
304 * bother with ticket file. Otherwise, check to see if we
305 * already have key info for the given server and key version
306 * (saved in the static st_* variables). If not, go get it
307 * from the ticket file. If "fn" is the null string, use the
308 * default ticket file.
310 if (fn && (strcmp(st_nam,service) != 0 || strcmp(st_inst,instance) != 0 ||
311 strcmp(st_rlm,realm) != 0 || (st_kvno != s_kvno))) {
316 if (read_service_key(service,instance,realm, (int) s_kvno,
319 status = krb_set_key((char *) skey, 0);
322 #endif /* !NOENCRYPTION */
323 strcpy(st_rlm,realm);
324 strcpy(st_nam,service);
325 strcpy(st_inst,instance);
328 /* Get ticket from authenticator */
329 tkt->length = (int) *ptr++;
330 if ((tkt->length + (ptr+1 - (char *) authent->dat)) > authent->length)
331 return RD_AP_MODIFIED;
332 memcpy(tkt->dat, ptr + 1, tkt->length);
334 if (krb_ap_req_debug)
335 krb_log("ticket->length: %d", tkt->length);
338 /* Decrypt and take apart ticket */
341 if (decomp_ticket(tkt, &ad->k_flags, ad->pname, ad->pinst, ad->prealm,
342 &(ad->address), ad->session, &(ad->life),
343 &(ad->time_sec), sname, iname, serv_key, serv_ksched.s))
346 if (krb_ap_req_debug) {
347 krb_log("Ticket Contents.");
348 krb_log(" Aname: %s.%s",ad->pname,
349 ((int)*(ad->prealm) ? ad->prealm : "Athena"));
350 krb_log(" Service: %s%s%s", sname, ((int)*iname ? "." : ""), iname);
353 /* Extract the authenticator */
354 req_id->length = (int) *(ptr++);
355 if ((req_id->length + (ptr + tkt->length - (char *) authent->dat)) >
357 return RD_AP_MODIFIED;
358 memcpy(req_id->dat, ptr + tkt->length, req_id->length);
361 /* And decrypt it with the session key from the ticket */
362 if (krb_ap_req_debug)
363 krb_log("About to decrypt authenticator");
364 sched = check_key_sched_cache(ad->session);
366 sched = &seskey_sched;
367 key_sched(ad->session, seskey_sched.s);
368 add_to_key_sched_cache(ad->session, &seskey_sched);
370 /* can't do much to optimize this... */
371 pcbc_encrypt((C_Block *) req_id->dat, (C_Block *) req_id->dat,
372 (long) req_id->length, sched->s, ad->session, DES_DECRYPT);
373 if (krb_ap_req_debug)
375 #endif /* NOENCRYPTION */
377 #define check_ptr() if ((ptr - (char *) req_id->dat) > req_id->length) return(RD_AP_MODIFIED);
379 ptr = (char *) req_id->dat;
380 strcpy(r_aname,ptr); /* Authentication name */
381 ptr += strlen(r_aname) + 1;
383 strcpy(r_inst,ptr); /* Authentication instance */
384 ptr += strlen(r_inst) + 1;
386 strcpy(r_realm,ptr); /* Authentication name */
387 ptr += strlen(r_realm) + 1;
389 memcpy(&ad->checksum, ptr, 4); /* Checksum */
393 swap_u_long(ad->checksum);
394 r_time_ms = *(ptr++); /* Time (fine) */
396 /* XXX r_time_ms is set but not used. why??? */
397 /* this is a crock to get lint to shut up */
402 /* assume sizeof(r_time_sec) == 4 ?? */
403 memcpy(&r_time_sec,ptr,4); /* Time (coarse) */
405 swap_u_long(r_time_sec);
407 /* Check for authenticity of the request */
408 if (krb_ap_req_debug)
409 krb_log("Pname: %s %s",ad->pname,r_aname);
410 if (strcmp(ad->pname,r_aname) != 0)
412 if (strcmp(ad->pinst,r_inst) != 0)
414 if (krb_ap_req_debug)
415 krb_log("Realm: %s %s", ad->prealm, r_realm);
416 if (strcmp(ad->prealm,r_realm) != 0)
419 if (krb_ap_req_debug)
420 krb_log("Address: %d %d", ad->address, from_addr);
421 if (from_addr && (ad->address != from_addr))
424 delta_t = abs((int)(t_local.tv_sec - r_time_sec));
425 if (delta_t > CLOCK_SKEW) {
426 gettimeofday(&t_local, NULL);
427 delta_t = abs((int)(t_local.tv_sec - r_time_sec));
428 if (delta_t > CLOCK_SKEW) {
429 if (krb_ap_req_debug) {
430 krb_log("Time out of range: %d - %d = %d",
431 t_local.tv_sec, r_time_sec, delta_t);
437 /* Now check for expiration of ticket */
439 tkt_age = t_local.tv_sec - ad->time_sec;
440 if (krb_ap_req_debug) {
441 krb_log("Time: %d Issue Date: %d Diff: %d Life %x",
442 t_local.tv_sec, ad->time_sec, tkt_age, ad->life);
445 if (t_local.tv_sec < ad->time_sec) {
446 if (ad->time_sec - t_local.tv_sec > CLOCK_SKEW)
448 } else if (t_local.tv_sec - ad->time_sec > 5 * 60 * ad->life) {
453 ad->reply.length = 0;
457 #endif /* NOENCRYPTION */
460 krb_find_ticket(authent, ticket)
461 KTEXT authent, ticket;
463 char *ptr; /* For stepping through */
465 /* Check for bogus length. */
466 if (authent->length <= 0)
467 return RD_AP_MODIFIED;
469 ptr = (char *) authent->dat;
472 if (KRB_PROT_VERSION != (unsigned int) *ptr++)
473 return RD_AP_VERSION;
475 /* Make sure msg type is ok. */
476 switch (*ptr++ & ~1) {
477 case AUTH_MSG_APPL_REQUEST:
478 case AUTH_MSG_APPL_REQUEST_MUTUAL:
481 return RD_AP_MSG_TYPE;
484 *ptr++; /* skip server key version */
485 ptr += strlen(ptr) + 1; /* skip the realm "hint" */
487 /* Get ticket from authenticator */
488 ticket->length = (int) *ptr++;
489 if ((ticket->length + (ptr + 1 - (char *) authent->dat)) > authent->length)
490 return RD_AP_MODIFIED;
491 memcpy((char *)(ticket->dat),ptr+1,ticket->length);
496 static char local_realm_buffer[REALM_SZ+1];
503 FILE *cnffile, *fopen();
506 return KFAILURE; /* Temporary restriction */
513 if (local_realm_buffer[0]) {
514 strcpy(r, local_realm_buffer);
518 cnffile = fopen(KRB_CONF, "r");
519 if (cnffile == NULL) {
521 strcpy(r, KRB_REALM);
528 if (fscanf(cnffile,"%s",r) != 1) {
537 decomp_ticket(tkt, flags, pname, pinstance, prealm, paddress, session,
538 life, time_sec, sname, sinstance, key, key_s)
539 KTEXT tkt; /* The ticket to be decoded */
540 unsigned char *flags; /* Kerberos ticket flags */
541 char *pname; /* Authentication name */
542 char *pinstance; /* Principal's instance */
543 char *prealm; /* Principal's authentication domain */
544 unsigned long *paddress; /* Net address of entity
545 * requesting ticket */
546 C_Block session; /* Session key inserted in ticket */
547 int *life; /* Lifetime of the ticket */
548 unsigned long *time_sec; /* Issue time and date */
549 char *sname; /* Service name */
550 char *sinstance; /* Service instance */
551 C_Block key; /* Service's secret key
552 * (to decrypt the ticket) */
553 des_key_schedule key_s; /* The precomputed key schedule */
555 static int tkt_swap_bytes;
557 char *ptr = (char *)tkt->dat;
560 /* Do the decryption */
561 pcbc_encrypt((C_Block *)tkt->dat,(C_Block *)tkt->dat,
562 (long) tkt->length,key_s,(C_Block *) key,0);
563 #endif /* ! NOENCRYPTION */
565 *flags = *ptr; /* get flags byte */
566 ptr += sizeof(*flags);
568 if (HOST_BYTE_ORDER != ((*flags >> K_FLAG_ORDER)& 1))
571 if (strlen(ptr) > ANAME_SZ)
573 strcpy(pname,ptr); /* pname */
574 ptr += strlen(pname) + 1;
576 if (strlen(ptr) > INST_SZ)
578 strcpy(pinstance,ptr); /* instance */
579 ptr += strlen(pinstance) + 1;
581 if (strlen(ptr) > REALM_SZ)
583 strcpy(prealm,ptr); /* realm */
584 ptr += strlen(prealm) + 1;
585 /* temporary hack until realms are dealt with properly */
587 strcpy(prealm, ZGetRealm());
589 memcpy((char *)paddress, ptr, 4); /* net address */
592 memcpy((char *)session, ptr, 8); /* session key */
595 /* get lifetime, being certain we don't get negative lifetimes */
596 uptr = (unsigned char *) ptr++;
599 memcpy((char *) time_sec, ptr, 4); /* issue time */
602 swap_u_long(*time_sec);
604 strcpy(sname,ptr); /* service name */
605 ptr += 1 + strlen(sname);
607 strcpy(sinstance,ptr); /* instance */
608 ptr += 1 + strlen(sinstance);
612 #endif /* HAVE_KRB4 */