]> asedeno.scripts.mit.edu Git - 1ts-debian.git/blob - zephyr/server/kopt.c
krb5-interrealm patches
[1ts-debian.git] / zephyr / server / kopt.c
1 /*
2  * $Source: /afs/dev.mit.edu/source/repository/athena/lib/zephyr/server/kopt.c,v $
3  * $Author: zacheiss $
4  *
5  * Copyright 1985, 1986, 1987, 1988, 1990, 1991 by the Massachusetts
6  * Institute of Technology.
7  *
8  * For copying and distribution information, please see the file
9  * <mit-copyright.h>.
10  */
11
12 /*
13  * This includes code taken from:
14  * Kerberos: rd_req.c,v 4.16 89/03/22 14:52:06 jtkohl Exp
15  * Kerberos: prot.h,v 4.13 89/01/24 14:27:22 jtkohl Exp
16  * Kerberos: krb_conf.h,v 4.0 89/01/23 09:59:27 jtkohl Exp
17  */
18
19 #include <zephyr/mit-copyright.h>
20 #include "zserver.h"
21
22 #ifndef lint
23 #ifndef SABER
24 static const char *rcsid_rd_req_c =
25     "$Id: kopt.c,v 1.16 2001/02/27 04:48:01 zacheiss Exp $";
26 #endif /* lint */
27 #endif /* SABER */
28
29 #ifdef HAVE_KRB4
30 #ifndef NOENCRYPTION
31
32 /* Byte ordering */
33 #undef HOST_BYTE_ORDER
34 static int krbONE = 1;
35 #define         HOST_BYTE_ORDER (* (char *) &krbONE)
36
37 #define         KRB_PROT_VERSION        4
38
39 /* Message types , always leave lsb for byte order */
40
41 #define         AUTH_MSG_KDC_REQUEST                     1<<1
42 #define         AUTH_MSG_KDC_REPLY                       2<<1
43 #define         AUTH_MSG_APPL_REQUEST                    3<<1
44 #define         AUTH_MSG_APPL_REQUEST_MUTUAL             4<<1
45 #define         AUTH_MSG_ERR_REPLY                       5<<1
46 #define         AUTH_MSG_PRIVATE                         6<<1
47 #define         AUTH_MSG_SAFE                            7<<1
48 #define         AUTH_MSG_APPL_ERR                        8<<1
49 #define         AUTH_MSG_DIE                            63<<1
50
51 /* values for kerb error codes */
52
53 #define         KERB_ERR_OK                              0
54 #define         KERB_ERR_NAME_EXP                        1
55 #define         KERB_ERR_SERVICE_EXP                     2
56 #define         KERB_ERR_AUTH_EXP                        3
57 #define         KERB_ERR_PKT_VER                         4
58 #define         KERB_ERR_NAME_MAST_KEY_VER               5
59 #define         KERB_ERR_SERV_MAST_KEY_VER               6
60 #define         KERB_ERR_BYTE_ORDER                      7
61 #define         KERB_ERR_PRINCIPAL_UNKNOWN               8
62 #define         KERB_ERR_PRINCIPAL_NOT_UNIQUE            9
63 #define         KERB_ERR_NULL_KEY                       10
64
65 extern int krb_ap_req_debug;
66
67 extern struct timeval t_local;
68
69 /*
70  * Keep the following information around for subsequent calls
71  * to this routine by the same server using the same key.
72  */
73
74 static Sched serv_ksched;       /* Key sched to decrypt ticket */
75 static des_cblock serv_key;     /* Initialization vector */
76
77 static int st_kvno;             /* version number for this key */
78 static char st_rlm[REALM_SZ];   /* server's realm */
79 static char st_nam[ANAME_SZ];   /* service name */
80 static char st_inst[INST_SZ];   /* server's instance */
81
82 /*
83  * Cache of key schedules
84  */
85 #define HASH_SIZE_1     255     /* not a power of 2 */
86 #define HASH_SIZE_2     3
87 static unsigned long last_use;
88 typedef struct {
89     unsigned long last_time_used;
90     des_cblock key;
91     Sched schedule;
92 } KeySchedRec;
93 static KeySchedRec scheds[HASH_SIZE_1][HASH_SIZE_2];
94
95 Sched *check_key_sched_cache(key)
96     des_cblock key;
97 {
98     unsigned int hash_value = key[0] + key[1] * 256;
99     KeySchedRec *rec = scheds[hash_value % HASH_SIZE_1];
100     int i;
101
102     for (i = HASH_SIZE_2 - 1; i >= 0; i--) {
103         if (rec[i].last_time_used && key[0] == rec[i].key[0]
104             && !memcmp(key, rec[i].key, sizeof(des_cblock))) {
105             rec[i].last_time_used = last_use++;
106             return &rec[i].schedule;
107         }
108     }
109     return 0;
110 }
111
112 void add_to_key_sched_cache(key, sched)
113     des_cblock key;
114     Sched *sched;
115 {
116     unsigned int hash_value = key[0] + key[1] * 256;
117     KeySchedRec *rec = scheds[hash_value % HASH_SIZE_1];
118     int i, oldest = HASH_SIZE_2 - 1;
119
120     for (i = HASH_SIZE_2 - 1; i >= 0; i--) {
121         if (rec[i].last_time_used == 0) {
122             oldest = i;
123             break;
124         }
125         if (rec[i].last_time_used < rec[oldest].last_time_used)
126             oldest = i;
127     }
128     memcpy (rec[oldest].key, key, sizeof(des_cblock));
129     rec[oldest].schedule = *sched;
130     rec[oldest].last_time_used = last_use++;
131 }
132
133 /*
134  * This file contains two functions.  krb_set_key() takes a DES
135  * key or password string and returns a DES key (either the original
136  * key, or the password converted into a DES key) and a key schedule
137  * for it.
138  *
139  * krb_rd_req() reads an authentication request and returns information
140  * about the identity of the requestor, or an indication that the
141  * identity information was not authentic.
142  */
143
144 /*
145  * krb_set_key() takes as its first argument either a DES key or a
146  * password string.  The "cvt" argument indicates how the first
147  * argument "key" is to be interpreted: if "cvt" is null, "key" is
148  * taken to be a DES key; if "cvt" is non-null, "key" is taken to
149  * be a password string, and is converted into a DES key using
150  * string_to_key().  In either case, the resulting key is returned
151  * in the external variable "serv_key".  A key schedule is
152  * generated for "serv_key" and returned in the external variable
153  * "serv_ksched".
154  *
155  * This routine returns the return value of des_key_sched.
156  *
157  * krb_set_key() needs to be in the same .o file as krb_rd_req() so that
158  * the key set by krb_set_key() is available in private storage for
159  * krb_rd_req().
160  */
161
162 #if 0
163 int
164 krb_set_key(key,cvt)
165     char *key;
166     int cvt;
167 {
168 #ifdef NOENCRYPTION
169     memset(serv_key, 0, sizeof(serv_key));
170     return KSUCCESS;
171 #else /* Encrypt */
172     Sched *s;
173     int ret;
174
175     if (cvt)
176         string_to_key(key,serv_key);
177     else
178         memcpy((char *)serv_key,key,8);
179
180     s = check_key_sched_cache (serv_key);
181     if (s) {
182         serv_ksched = *s;
183         return 0;
184     }
185     ret = des_key_sched(serv_key, serv_ksched.s);
186     add_to_key_sched_cache(serv_key, &serv_ksched);
187     return ret;
188 #endif /* NOENCRYPTION */
189 }
190 #endif
191
192 /*
193  * krb_rd_req() takes an AUTH_MSG_APPL_REQUEST or
194  * AUTH_MSG_APPL_REQUEST_MUTUAL message created by krb_mk_req(),
195  * checks its integrity and returns a judgement as to the requestor's
196  * identity.
197  *
198  * The "authent" argument is a pointer to the received message.
199  * The "service" and "instance" arguments name the receiving server,
200  * and are used to get the service's ticket to decrypt the ticket
201  * in the message, and to compare against the server name inside the
202  * ticket.  "from_addr" is the network address of the host from which
203  * the message was received; this is checked against the network
204  * address in the ticket.  If "from_addr" is zero, the check is not
205  * performed.  "ad" is an AUTH_DAT structure which is
206  * filled in with information about the sender's identity according
207  * to the authenticator and ticket sent in the message.  Finally,
208  * "fn" contains the name of the file containing the server's key.
209  * (If "fn" is NULL, the server's key is assumed to have been set
210  * by krb_set_key().  If "fn" is the null string ("") the default
211  * file KEYFILE, defined in "krb.h", is used.)
212  *
213  * krb_rd_req() returns RD_AP_OK if the authentication information
214  * was genuine, or one of the following error codes (defined in
215  * "krb.h"):
216  *
217  *      RD_AP_VERSION           - wrong protocol version number
218  *      RD_AP_MSG_TYPE          - wrong message type
219  *      RD_AP_UNDEC             - couldn't decipher the message
220  *      RD_AP_INCON             - inconsistencies found
221  *      RD_AP_BADD              - wrong network address
222  *      RD_AP_TIME              - client time (in authenticator)
223  *                                too far off server time
224  *      RD_AP_NYV               - Kerberos time (in ticket) too
225  *                                far off server time
226  *      RD_AP_EXP               - ticket expired
227  *
228  * For the message format, see krb_mk_req().
229  *
230  * Mutual authentication is not implemented.
231  */
232
233 int
234 krb_rd_req(authent,service,instance,from_addr,ad,fn)
235     KTEXT authent;                      /* The received message */
236     char FAR *service;                  /* Service name */
237     char FAR *instance;                 /* Service instance */
238     unsigned KRB4_32 from_addr; /* Net address of originating host */
239     AUTH_DAT FAR *ad;                   /* Structure to be filled in */
240     char FAR *fn;                               /* Filename to get keys from */
241 {
242     KTEXT_ST ticket;     /* Temp storage for ticket */
243     KTEXT tkt = &ticket;
244     KTEXT_ST req_id_st;  /* Temp storage for authenticator */
245     KTEXT req_id = &req_id_st;
246
247     char realm[REALM_SZ];       /* ZRealm of issuing kerberos */
248     Sched seskey_sched, *sched; /* Key sched for session key */
249     unsigned char skey[KKEY_SZ]; /* Session key from ticket */
250     char sname[SNAME_SZ];       /* Service name from ticket */
251     char iname[INST_SZ];        /* Instance name from ticket */
252     char r_aname[ANAME_SZ];     /* Client name from authenticator */
253     char r_inst[INST_SZ];       /* Client instance from authenticator */
254     char r_realm[REALM_SZ];     /* Client realm from authenticator */
255     unsigned int r_time_ms;     /* Fine time from authenticator */
256     unsigned long r_time_sec;   /* Coarse time from authenticator */
257     char *ptr;                  /* For stepping through */
258     unsigned long delta_t;      /* Time in authenticator - local time */
259     long tkt_age;               /* Age of ticket */
260     int swap_bytes;             /* Need to swap bytes? */
261     int mutual;                 /* Mutual authentication requested? */
262     unsigned char s_kvno;       /* Version number of the server's key
263                                  * Kerberos used to encrypt ticket */
264     int status;
265
266     if (authent->length <= 0)
267         return(RD_AP_MODIFIED);
268
269     ptr = (char *) authent->dat;
270
271     /* get msg version, type and byte order, and server key version */
272
273     /* check version */
274     if (KRB_PROT_VERSION != (unsigned int) *ptr++)
275         return RD_AP_VERSION;
276
277     /* byte order */
278     swap_bytes = 0;
279     if ((*ptr & 1) != HOST_BYTE_ORDER)
280         swap_bytes++;
281
282     /* check msg type */
283     mutual = 0;
284     switch (*ptr++ & ~1) {
285       case AUTH_MSG_APPL_REQUEST:
286         break;
287       case AUTH_MSG_APPL_REQUEST_MUTUAL:
288         mutual++;
289         break;
290       default:
291         return(RD_AP_MSG_TYPE);
292     }
293
294 #ifdef lint
295     /* XXX mutual is set but not used; why??? */
296     /* this is a crock to get lint to shut up */
297     if (mutual)
298         mutual = 0;
299 #endif /* lint */
300     s_kvno = *ptr++;            /* get server key version */
301     strncpy(realm,ptr,REALM_SZ);/* And the realm of the issuing KDC */
302     realm[REALM_SZ-1] = '\0';
303     ptr += strlen(realm) + 1;     /* skip the realm "hint" */
304
305     /*
306      * If "fn" is NULL, key info should already be set; don't
307      * bother with ticket file.  Otherwise, check to see if we
308      * already have key info for the given server and key version
309      * (saved in the static st_* variables).  If not, go get it
310      * from the ticket file.  If "fn" is the null string, use the
311      * default ticket file.
312      */
313     if (fn && (strcmp(st_nam,service) != 0 || strcmp(st_inst,instance) != 0 ||
314                strcmp(st_rlm,realm) != 0 || (st_kvno != s_kvno))) {
315         if (*fn == 0)
316             fn = KEYFILE;
317         st_kvno = s_kvno;
318 #ifndef NOENCRYPTION
319         if (read_service_key(service,instance,realm, (int) s_kvno,
320                             fn, (char *) skey))
321             return(RD_AP_UNDEC);
322         status = krb_set_key((char *) skey, 0);
323         if (status != 0)
324             return(status);
325 #endif /* !NOENCRYPTION */
326         strcpy(st_rlm,realm);
327         strcpy(st_nam,service);
328         strcpy(st_inst,instance);
329     }
330
331     /* Get ticket from authenticator */
332     tkt->length = (int) *ptr++;
333     if ((tkt->length + (ptr+1 - (char *) authent->dat)) > authent->length)
334         return RD_AP_MODIFIED;
335     memcpy(tkt->dat, ptr + 1, tkt->length);
336
337     if (krb_ap_req_debug)
338         krb_log("ticket->length: %d", tkt->length);
339
340 #ifndef NOENCRYPTION
341     /* Decrypt and take apart ticket */
342 #endif
343
344     if (decomp_ticket(tkt, &ad->k_flags, ad->pname, ad->pinst, ad->prealm,
345                       &(ad->address), ad->session, &(ad->life),
346                       &(ad->time_sec), sname, iname, serv_key, serv_ksched.s))
347         return RD_AP_UNDEC;
348
349     if (krb_ap_req_debug) {
350         krb_log("Ticket Contents.");
351         krb_log(" Aname:   %s.%s",ad->pname,
352                 ((int)*(ad->prealm) ? ad->prealm : "Athena"));
353         krb_log(" Service: %s%s%s", sname, ((int)*iname ? "." : ""), iname);
354     }
355
356     /* Extract the authenticator */
357     req_id->length = (int) *(ptr++);
358     if ((req_id->length + (ptr + tkt->length - (char *) authent->dat)) >
359         authent->length)
360         return RD_AP_MODIFIED;
361     memcpy(req_id->dat, ptr + tkt->length, req_id->length);
362
363 #ifndef NOENCRYPTION
364     /* And decrypt it with the session key from the ticket */
365     if (krb_ap_req_debug)
366         krb_log("About to decrypt authenticator");
367     sched = check_key_sched_cache(ad->session);
368     if (!sched) {
369         sched = &seskey_sched;
370         key_sched(ad->session, seskey_sched.s);
371         add_to_key_sched_cache(ad->session, &seskey_sched);
372     }
373     /* can't do much to optimize this... */
374     pcbc_encrypt((C_Block *) req_id->dat, (C_Block *) req_id->dat,
375                  (long) req_id->length, sched->s, ad->session, DES_DECRYPT);
376     if (krb_ap_req_debug)
377         krb_log("Done.");
378 #endif /* NOENCRYPTION */
379
380 #define check_ptr() if ((ptr - (char *) req_id->dat) > req_id->length) return(RD_AP_MODIFIED);
381
382     ptr = (char *) req_id->dat;
383     strncpy(r_aname, ptr, ANAME_SZ);    /* Authentication name */
384     r_aname[ANAME_SZ-1] = '\0';
385     ptr += strlen(r_aname) + 1;
386     check_ptr();
387     strncpy(r_inst, ptr, INST_SZ);      /* Authentication instance */
388     r_inst[INST_SZ-1] = '\0';
389     ptr += strlen(r_inst) + 1;
390     check_ptr();
391     strncpy(r_realm, ptr, REALM_SZ);    /* Authentication name */
392     r_realm[REALM_SZ-1] = '\0';
393     ptr += strlen(r_realm) + 1;
394     check_ptr();
395     memcpy(&ad->checksum, ptr, 4);      /* Checksum */
396     ptr += 4;
397     check_ptr();
398     if (swap_bytes)
399         swap_u_long(ad->checksum);
400     r_time_ms = *(ptr++);       /* Time (fine) */
401 #ifdef lint
402     /* XXX r_time_ms is set but not used.  why??? */
403     /* this is a crock to get lint to shut up */
404     if (r_time_ms)
405         r_time_ms = 0;
406 #endif /* lint */
407     check_ptr();
408     /* assume sizeof(r_time_sec) == 4 ?? */
409     memcpy(&r_time_sec,ptr,4); /* Time (coarse) */
410     if (swap_bytes)
411         swap_u_long(r_time_sec);
412
413     /* Check for authenticity of the request */
414     if (krb_ap_req_debug)
415         krb_log("Pname:   %s %s",ad->pname,r_aname);
416     if (strcmp(ad->pname,r_aname) != 0)
417         return RD_AP_INCON;
418     if (strcmp(ad->pinst,r_inst) != 0)
419         return RD_AP_INCON;
420     if (krb_ap_req_debug)
421         krb_log("ZRealm:   %s %s", ad->prealm, r_realm);
422     if (strcmp(ad->prealm,r_realm) != 0)
423         return RD_AP_INCON;
424
425     if (krb_ap_req_debug)
426         krb_log("Address: %d %d", ad->address, from_addr);
427     if (from_addr && (ad->address != from_addr))
428         return RD_AP_BADD;
429
430     delta_t = abs((int)(t_local.tv_sec - r_time_sec));
431     if (delta_t > CLOCK_SKEW) {
432         gettimeofday(&t_local, NULL);
433         delta_t = abs((int)(t_local.tv_sec - r_time_sec));
434         if (delta_t > CLOCK_SKEW) {
435             if (krb_ap_req_debug) {
436                 krb_log("Time out of range: %d - %d = %d",
437                         t_local.tv_sec, r_time_sec, delta_t);
438             }
439             return RD_AP_TIME;
440         }
441     }
442
443     /* Now check for expiration of ticket */
444
445     tkt_age = t_local.tv_sec - ad->time_sec;
446     if (krb_ap_req_debug) {
447         krb_log("Time: %d Issue Date: %d Diff: %d Life %x",
448                 t_local.tv_sec, ad->time_sec, tkt_age, ad->life);
449     }
450
451     if (t_local.tv_sec < ad->time_sec) {
452         if (ad->time_sec - t_local.tv_sec > CLOCK_SKEW)
453             return RD_AP_NYV;
454     } else if (t_local.tv_sec - ad->time_sec > 5 * 60 * ad->life) {
455         return RD_AP_EXP;
456     }
457
458     /* All seems OK */
459     ad->reply.length = 0;
460
461     return RD_AP_OK;
462 }
463 #endif /* NOENCRYPTION */
464
465 int
466 krb_find_ticket(authent, ticket)
467     KTEXT authent, ticket;
468 {
469     char *ptr;          /* For stepping through */
470
471     /* Check for bogus length. */
472     if (authent->length <= 0)
473         return RD_AP_MODIFIED;
474
475     ptr = (char *) authent->dat;
476
477     /* check version */
478     if (KRB_PROT_VERSION != (unsigned int) *ptr++)
479         return RD_AP_VERSION;
480
481     /* Make sure msg type is ok. */
482     switch (*ptr++ & ~1) {
483     case AUTH_MSG_APPL_REQUEST:
484     case AUTH_MSG_APPL_REQUEST_MUTUAL:
485         break;
486     default:
487         return RD_AP_MSG_TYPE;
488     }
489
490     *ptr++;                     /* skip server key version */
491     ptr += strlen(ptr) + 1;     /* skip the realm "hint" */
492
493     /* Get ticket from authenticator */
494     ticket->length = (int) *ptr++;
495     if ((ticket->length + (ptr + 1 - (char *) authent->dat)) > authent->length)
496         return RD_AP_MODIFIED;
497     memcpy((char *)(ticket->dat),ptr+1,ticket->length);
498
499     return RD_AP_OK;
500 }
501 #endif /* HAVE_KRB4 */