]> asedeno.scripts.mit.edu Git - 1ts-debian.git/blob - zephyr/server/kopt.c
eccdc21ba7f071ee22b6ef0ae5e799331ab10eae
[1ts-debian.git] / zephyr / server / kopt.c
1 /*
2  * $Id: kopt.c,v 1.14 1999/01/22 23:19:44 ghudson Exp $
3  *
4  * Copyright 1985, 1986, 1987, 1988, 1990, 1991 by the Massachusetts
5  * Institute of Technology.
6  *
7  * For copying and distribution information, please see the file
8  * <mit-copyright.h>.
9  */
10
11 /*
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
16  */
17
18 #include <zephyr/mit-copyright.h>
19 #include "zserver.h"
20
21 #ifndef lint
22 #ifndef SABER
23 static const char *rcsid_rd_req_c =
24     "$Id: kopt.c,v 1.14 1999/01/22 23:19:44 ghudson Exp $";
25 #endif /* lint */
26 #endif /* SABER */
27
28 #ifdef HAVE_KRB4
29 #ifndef NOENCRYPTION
30
31 /* Byte ordering */
32 #undef HOST_BYTE_ORDER
33 static int krbONE = 1;
34 #define         HOST_BYTE_ORDER (* (char *) &krbONE)
35
36 #define         KRB_PROT_VERSION        4
37
38 /* Message types , always leave lsb for byte order */
39
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
49
50 /* values for kerb error codes */
51
52 #define         KERB_ERR_OK                              0
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
63
64 extern int krb_ap_req_debug;
65
66 extern struct timeval t_local;
67
68 /*
69  * Keep the following information around for subsequent calls
70  * to this routine by the same server using the same key.
71  */
72
73 static Sched serv_ksched;       /* Key sched to decrypt ticket */
74 static des_cblock serv_key;     /* Initialization vector */
75
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 */
80
81 /*
82  * Cache of key schedules
83  */
84 #define HASH_SIZE_1     255     /* not a power of 2 */
85 #define HASH_SIZE_2     3
86 static unsigned long last_use;
87 typedef struct {
88     unsigned long last_time_used;
89     des_cblock key;
90     Sched schedule;
91 } KeySchedRec;
92 static KeySchedRec scheds[HASH_SIZE_1][HASH_SIZE_2];
93
94 Sched *check_key_sched_cache(key)
95     des_cblock key;
96 {
97     unsigned int hash_value = key[0] + key[1] * 256;
98     KeySchedRec *rec = scheds[hash_value % HASH_SIZE_1];
99     int i;
100
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;
106         }
107     }
108     return 0;
109 }
110
111 void add_to_key_sched_cache(key, sched)
112     des_cblock key;
113     Sched *sched;
114 {
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;
118
119     for (i = HASH_SIZE_2 - 1; i >= 0; i--) {
120         if (rec[i].last_time_used == 0) {
121             oldest = i;
122             break;
123         }
124         if (rec[i].last_time_used < rec[oldest].last_time_used)
125             oldest = i;
126     }
127     memcpy (rec[oldest].key, key, sizeof(des_cblock));
128     rec[oldest].schedule = *sched;
129     rec[oldest].last_time_used = last_use++;
130 }
131
132 /*
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
136  * for it.
137  *
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.
141  */
142
143 /*
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
152  * "serv_ksched".
153  *
154  * This routine returns the return value of des_key_sched.
155  *
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
158  * krb_rd_req().
159  */
160
161 int
162 krb_set_key(key,cvt)
163     char *key;
164     int cvt;
165 {
166 #ifdef NOENCRYPTION
167     memset(serv_key, 0, sizeof(serv_key));
168     return KSUCCESS;
169 #else /* Encrypt */
170     Sched *s;
171     int ret;
172
173     if (cvt)
174         string_to_key(key,serv_key);
175     else
176         memcpy((char *)serv_key,key,8);
177
178     s = check_key_sched_cache (serv_key);
179     if (s) {
180         serv_ksched = *s;
181         return 0;
182     }
183     ret = des_key_sched(serv_key, serv_ksched.s);
184     add_to_key_sched_cache(serv_key, &serv_ksched);
185     return ret;
186 #endif /* NOENCRYPTION */
187 }
188
189
190 /*
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
194  * identity.
195  *
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.)
210  *
211  * krb_rd_req() returns RD_AP_OK if the authentication information
212  * was genuine, or one of the following error codes (defined in
213  * "krb.h"):
214  *
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
225  *
226  * For the message format, see krb_mk_req().
227  *
228  * Mutual authentication is not implemented.
229  */
230
231 int
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 */
239 {
240     KTEXT_ST ticket;     /* Temp storage for ticket */
241     KTEXT tkt = &ticket;
242     KTEXT_ST req_id_st;  /* Temp storage for authenticator */
243     KTEXT req_id = &req_id_st;
244
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 */
262     int status;
263
264     if (authent->length <= 0)
265         return(RD_AP_MODIFIED);
266
267     ptr = (char *) authent->dat;
268
269     /* get msg version, type and byte order, and server key version */
270
271     /* check version */
272     if (KRB_PROT_VERSION != (unsigned int) *ptr++)
273         return RD_AP_VERSION;
274
275     /* byte order */
276     swap_bytes = 0;
277     if ((*ptr & 1) != HOST_BYTE_ORDER)
278         swap_bytes++;
279
280     /* check msg type */
281     mutual = 0;
282     switch (*ptr++ & ~1) {
283       case AUTH_MSG_APPL_REQUEST:
284         break;
285       case AUTH_MSG_APPL_REQUEST_MUTUAL:
286         mutual++;
287         break;
288       default:
289         return(RD_AP_MSG_TYPE);
290     }
291
292 #ifdef lint
293     /* XXX mutual is set but not used; why??? */
294     /* this is a crock to get lint to shut up */
295     if (mutual)
296         mutual = 0;
297 #endif /* lint */
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" */
301
302     /*
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.
309      */
310     if (fn && (strcmp(st_nam,service) != 0 || strcmp(st_inst,instance) != 0 ||
311                strcmp(st_rlm,realm) != 0 || (st_kvno != s_kvno))) {
312         if (*fn == 0)
313             fn = KEYFILE;
314         st_kvno = s_kvno;
315 #ifndef NOENCRYPTION
316         if (read_service_key(service,instance,realm, (int) s_kvno,
317                             fn, (char *) skey))
318             return(RD_AP_UNDEC);
319         status = krb_set_key((char *) skey, 0);
320         if (status != 0)
321             return(status);
322 #endif /* !NOENCRYPTION */
323         strcpy(st_rlm,realm);
324         strcpy(st_nam,service);
325         strcpy(st_inst,instance);
326     }
327
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);
333
334     if (krb_ap_req_debug)
335         krb_log("ticket->length: %d", tkt->length);
336
337 #ifndef NOENCRYPTION
338     /* Decrypt and take apart ticket */
339 #endif
340
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))
344         return RD_AP_UNDEC;
345
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);
351     }
352
353     /* Extract the authenticator */
354     req_id->length = (int) *(ptr++);
355     if ((req_id->length + (ptr + tkt->length - (char *) authent->dat)) >
356         authent->length)
357         return RD_AP_MODIFIED;
358     memcpy(req_id->dat, ptr + tkt->length, req_id->length);
359
360 #ifndef NOENCRYPTION
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);
365     if (!sched) {
366         sched = &seskey_sched;
367         key_sched(ad->session, seskey_sched.s);
368         add_to_key_sched_cache(ad->session, &seskey_sched);
369     }
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)
374         krb_log("Done.");
375 #endif /* NOENCRYPTION */
376
377 #define check_ptr() if ((ptr - (char *) req_id->dat) > req_id->length) return(RD_AP_MODIFIED);
378
379     ptr = (char *) req_id->dat;
380     strcpy(r_aname,ptr);        /* Authentication name */
381     ptr += strlen(r_aname) + 1;
382     check_ptr();
383     strcpy(r_inst,ptr);         /* Authentication instance */
384     ptr += strlen(r_inst) + 1;
385     check_ptr();
386     strcpy(r_realm,ptr);        /* Authentication name */
387     ptr += strlen(r_realm) + 1;
388     check_ptr();
389     memcpy(&ad->checksum, ptr, 4);      /* Checksum */
390     ptr += 4;
391     check_ptr();
392     if (swap_bytes)
393         swap_u_long(ad->checksum);
394     r_time_ms = *(ptr++);       /* Time (fine) */
395 #ifdef lint
396     /* XXX r_time_ms is set but not used.  why??? */
397     /* this is a crock to get lint to shut up */
398     if (r_time_ms)
399         r_time_ms = 0;
400 #endif /* lint */
401     check_ptr();
402     /* assume sizeof(r_time_sec) == 4 ?? */
403     memcpy(&r_time_sec,ptr,4); /* Time (coarse) */
404     if (swap_bytes)
405         swap_u_long(r_time_sec);
406
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)
411         return RD_AP_INCON;
412     if (strcmp(ad->pinst,r_inst) != 0)
413         return RD_AP_INCON;
414     if (krb_ap_req_debug)
415         krb_log("Realm:   %s %s", ad->prealm, r_realm);
416     if (strcmp(ad->prealm,r_realm) != 0)
417         return RD_AP_INCON;
418
419     if (krb_ap_req_debug)
420         krb_log("Address: %d %d", ad->address, from_addr);
421     if (from_addr && (ad->address != from_addr))
422         return RD_AP_BADD;
423
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);
432             }
433             return RD_AP_TIME;
434         }
435     }
436
437     /* Now check for expiration of ticket */
438
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);
443     }
444
445     if (t_local.tv_sec < ad->time_sec) {
446         if (ad->time_sec - t_local.tv_sec > CLOCK_SKEW)
447             return RD_AP_NYV;
448     } else if (t_local.tv_sec - ad->time_sec > 5 * 60 * ad->life) {
449         return RD_AP_EXP;
450     }
451
452     /* All seems OK */
453     ad->reply.length = 0;
454
455     return RD_AP_OK;
456 }
457 #endif /* NOENCRYPTION */
458
459 int
460 krb_find_ticket(authent, ticket)
461     KTEXT authent, ticket;
462 {
463     char *ptr;          /* For stepping through */
464
465     /* Check for bogus length. */
466     if (authent->length <= 0)
467         return RD_AP_MODIFIED;
468
469     ptr = (char *) authent->dat;
470
471     /* check version */
472     if (KRB_PROT_VERSION != (unsigned int) *ptr++)
473         return RD_AP_VERSION;
474
475     /* Make sure msg type is ok. */
476     switch (*ptr++ & ~1) {
477     case AUTH_MSG_APPL_REQUEST:
478     case AUTH_MSG_APPL_REQUEST_MUTUAL:
479         break;
480     default:
481         return RD_AP_MSG_TYPE;
482     }
483
484     *ptr++;                     /* skip server key version */
485     ptr += strlen(ptr) + 1;     /* skip the realm "hint" */
486
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);
492
493     return RD_AP_OK;
494 }
495
496 static char local_realm_buffer[REALM_SZ+1];
497
498 int
499 krb_get_lrealm(r,n)
500     char *r;
501     int n;
502 {
503     FILE *cnffile, *fopen();
504
505     if (n > 1)
506         return KFAILURE;  /* Temporary restriction */
507
508     if (my_realm[0]) {
509         strcpy(r, my_realm);
510         return KSUCCESS;
511     }
512
513     if (local_realm_buffer[0]) {
514         strcpy(r, local_realm_buffer);
515         return KSUCCESS;
516     }
517     
518     cnffile = fopen(KRB_CONF, "r");
519     if (cnffile == NULL) {
520         if (n == 1) {
521             strcpy(r, KRB_REALM);
522             return KSUCCESS;
523         } else {
524             return KFAILURE;
525         }
526     }
527
528     if (fscanf(cnffile,"%s",r) != 1) {
529         fclose(cnffile);
530         return KFAILURE;
531     }
532     fclose(cnffile);
533     return KSUCCESS;
534 }
535
536 int
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 */
554 {
555     static int tkt_swap_bytes;
556     unsigned char *uptr;
557     char *ptr = (char *)tkt->dat;
558
559 #ifndef NOENCRYPTION
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 */
564
565     *flags = *ptr;              /* get flags byte */
566     ptr += sizeof(*flags);
567     tkt_swap_bytes = 0;
568     if (HOST_BYTE_ORDER != ((*flags >> K_FLAG_ORDER)& 1))
569         tkt_swap_bytes++;
570
571     if (strlen(ptr) > ANAME_SZ)
572         return(KFAILURE);
573     strcpy(pname,ptr);   /* pname */
574     ptr += strlen(pname) + 1;
575
576     if (strlen(ptr) > INST_SZ)
577         return(KFAILURE);
578     strcpy(pinstance,ptr); /* instance */
579     ptr += strlen(pinstance) + 1;
580
581     if (strlen(ptr) > REALM_SZ)
582         return(KFAILURE);
583     strcpy(prealm,ptr);  /* realm */
584     ptr += strlen(prealm) + 1;
585     /* temporary hack until realms are dealt with properly */
586     if (*prealm == 0)
587         strcpy(prealm, ZGetRealm());
588
589     memcpy((char *)paddress, ptr, 4); /* net address */
590     ptr += 4;
591
592     memcpy((char *)session, ptr, 8); /* session key */
593     ptr+= 8;
594
595     /* get lifetime, being certain we don't get negative lifetimes */
596     uptr = (unsigned char *) ptr++;
597     *life = (int) *uptr;
598
599     memcpy((char *) time_sec, ptr, 4); /* issue time */
600     ptr += 4;
601     if (tkt_swap_bytes)
602         swap_u_long(*time_sec);
603
604     strcpy(sname,ptr);   /* service name */
605     ptr += 1 + strlen(sname);
606
607     strcpy(sinstance,ptr); /* instance */
608     ptr += 1 + strlen(sinstance);
609
610     return(KSUCCESS);
611 }
612 #endif /* HAVE_KRB4 */
613