]> asedeno.scripts.mit.edu Git - 1ts-debian.git/blob - zephyr/server/kstuff.c
5fd31c9f2a0e57b54938a9c012c3028314e457d3
[1ts-debian.git] / zephyr / server / kstuff.c
1 /* This file is part of the Project Athena Zephyr Notification System.
2  * It contains functions for dealing with Kerberos functions in the server.
3  *
4  *      Created by:     John T Kohl
5  *
6  *      Copyright (c) 1988 by the Massachusetts Institute of Technology.
7  *      For copying and distribution information, see the file
8  *      "mit-copyright.h". 
9  */
10 /*
11  *      $Source: /afs/dev.mit.edu/source/repository/athena/lib/zephyr/server/kstuff.c,v $
12  *      $Header: /afs/dev.mit.edu/source/repository/athena/lib/zephyr/server/kstuff.c,v 1.26 2001/04/10 23:28:20 ghudson Exp $
13  */
14
15 #include "zserver.h"
16
17 #ifndef lint
18 #ifndef SABER
19 static const char rcsid_kstuff_c[] = "$Id: kstuff.c,v 1.26 2001/04/10 23:28:20 ghudson Exp $";
20 #endif
21 #endif
22
23 #ifdef HAVE_KRB4
24
25 /* Keep a hash table mapping tickets to session keys, so we can do a fast
26  * check of the cryptographic checksum without doing and DES decryptions.
27  * Also remember the expiry time of the ticket, so that we can sweep the
28  * table periodically. */
29
30 #define HASHTAB_SIZE 4091
31
32 typedef struct hash_entry Hash_entry;
33
34 /* The ticket comes at the end, in a variable-length array. */
35 struct hash_entry {
36     C_Block session_key;
37     time_t expires;
38     char srcprincipal[ANAME_SZ+INST_SZ+REALM_SZ+4];
39     Hash_entry *next;
40     int ticket_len;
41     unsigned char ticket[1];
42 };
43
44 Hash_entry *hashtab[HASHTAB_SIZE];
45
46 static int hash_ticket __P((unsigned char *, int));
47 static void add_session_key __P((KTEXT, C_Block, char *, time_t));
48 static int find_session_key __P((KTEXT, C_Block, char *));
49 static ZChecksum_t compute_checksum __P((ZNotice_t *, C_Block));
50 static ZChecksum_t compute_rlm_checksum __P((ZNotice_t *, C_Block));
51
52 /*
53  * GetKerberosData
54  *
55  * get ticket from file descriptor and decode it.
56  * Return KFAILURE if we barf on reading the ticket, else return
57  * the value of rd_ap_req() applied to the ticket.
58  */
59 int
60 GetKerberosData(fd, haddr, kdata, service, srvtab)
61      int fd; /* file descr. to read from */
62      struct in_addr haddr; /* address of foreign host on fd */
63      AUTH_DAT *kdata;   /* kerberos data (returned) */
64      char *service; /* service principal desired */
65      char *srvtab; /* file to get keys from */
66 {
67     char p[20];
68     KTEXT_ST ticket;            /* will get Kerberos ticket from client */
69     int i;
70     char instance[INST_SZ];
71
72     /*
73      * Get the Kerberos ticket.  The first few characters, terminated
74      * by a blank, should give us a length; then get than many chars
75      * which will be the ticket proper.
76      */
77     for (i=0; i<20; i++) {
78         if (read(fd, &p[i], 1) != 1) {
79             syslog(LOG_WARNING,"bad read tkt len");
80             return(KFAILURE);
81         }
82         if (p[i] == ' ') {
83             p[i] = '\0';
84             break;
85         }
86     }
87     ticket.length = atoi(p);
88     if ((i==20) || (ticket.length<=0) || (ticket.length>MAX_KTXT_LEN)) {
89         syslog(LOG_WARNING,"bad tkt len %d",ticket.length);
90         return(KFAILURE);
91     }
92     for (i=0; i<ticket.length; i++) {
93         if (read(fd, (caddr_t) &(ticket.dat[i]), 1) != 1) {
94             syslog(LOG_WARNING,"bad tkt read");
95             return(KFAILURE);
96         }
97     }
98     /*
99      * now have the ticket.  use it to get the authenticated
100      * data from Kerberos.
101      */
102     (void) strcpy(instance,"*");                /* let Kerberos fill it in */
103
104     return(krb_rd_req(&ticket, service, instance, haddr.s_addr,
105                       kdata, srvtab ? srvtab : ""));
106 }
107
108 /*
109  * SendKerberosData
110  * 
111  * create and transmit a ticket over the file descriptor for service.host
112  * return failure codes if appropriate, or 0 if we
113  * get the ticket and write it to the file descriptor
114  */
115
116 Code_t
117 SendKerberosData(fd, ticket, service, host)
118      int fd;            /* file descriptor to write onto */
119      KTEXT ticket;      /* where to put ticket (return) */
120      char *service;     /* service name, foreign host */
121      char *host;
122 {
123     int rem;
124     char p[32];
125     int written;
126     int size_to_write;
127
128     rem = krb_mk_req(ticket, service, host, ZGetRealm(), (u_long) 0);
129     if (rem != KSUCCESS)
130         return rem + krb_err_base;
131
132     (void) sprintf(p,"%d ",ticket->length);
133     size_to_write = strlen (p);
134     if ((written = write(fd, p, size_to_write)) != size_to_write)
135         return (written < 0) ? errno : ZSRV_PKSHORT;
136     if ((written = write(fd, (caddr_t) (ticket->dat), ticket->length))
137         != ticket->length)
138         return (written < 0) ? errno : ZSRV_PKSHORT;
139
140     return 0;
141 }
142
143 #endif /* HAVE_KRB4 */
144
145 Code_t
146 ZCheckRealmAuthentication(notice, from, realm)
147     ZNotice_t *notice;
148     struct sockaddr_in *from;
149     char *realm;
150 {       
151 #ifdef HAVE_KRB4
152     int result;
153     char rlmprincipal[ANAME_SZ+INST_SZ+REALM_SZ+4];
154     char srcprincipal[ANAME_SZ+INST_SZ+REALM_SZ+4];
155     KTEXT_ST authent, ticket;
156     AUTH_DAT dat;
157     ZChecksum_t checksum;
158     CREDENTIALS cred;
159     C_Block session_key;
160
161     if (!notice->z_auth)
162         return ZAUTH_NO;
163
164     /* Check for bogus authentication data length. */
165     if (notice->z_authent_len <= 0)
166         return ZAUTH_FAILED;
167
168     /* Read in the authentication data. */
169     if (ZReadAscii(notice->z_ascii_authent, 
170                    strlen(notice->z_ascii_authent)+1, 
171                    (unsigned char *)authent.dat, 
172                    notice->z_authent_len) == ZERR_BADFIELD) {
173         return ZAUTH_FAILED;
174     }
175     authent.length = notice->z_authent_len;
176
177     /* Copy the ticket out of the authentication data. */
178     if (krb_find_ticket(&authent, &ticket) != RD_AP_OK)
179         return ZAUTH_FAILED;
180
181     (void) sprintf(rlmprincipal, "%s.%s@%s", SERVER_SERVICE,
182                    SERVER_INSTANCE, realm);
183
184     /* Try to do a fast check against the cryptographic checksum. */
185     if (find_session_key(&ticket, session_key, srcprincipal) >= 0) {
186         if (strcmp(srcprincipal, rlmprincipal) != 0)
187             return ZAUTH_FAILED;
188         if (notice->z_time.tv_sec - NOW > CLOCK_SKEW)
189             return ZAUTH_FAILED;
190         checksum = compute_rlm_checksum(notice, session_key);
191
192         /* If checksum matches, packet is authentic.  If not, we might
193          * have an outdated session key, so keep going the slow way.
194          */
195         if (checksum == notice->z_checksum) {
196           (void) memcpy((char *)__Zephyr_session, (char *)session_key, 
197                         sizeof(C_Block)); /* For control_dispatch() */
198           return ZAUTH_YES;
199         }
200
201         /* Try again. This way we can switch to the same checksums
202          * that the rest of Zephyr uses at a future date, but for now 
203          * we need to be compatible */
204         checksum = compute_checksum(notice, session_key);
205         if (checksum == notice->z_checksum) {
206             memcpy(__Zephyr_session, session_key, sizeof(C_Block));
207             return ZAUTH_YES;
208         }
209     }
210
211     /* We don't have the session key cached; do it the long way. */
212     result = krb_rd_req(&authent, SERVER_SERVICE, SERVER_INSTANCE,
213                         from->sin_addr.s_addr, &dat, srvtab_file);
214     if (result == RD_AP_OK) {
215         sprintf(srcprincipal, "%s%s%s@%s", dat.pname, dat.pinst[0] ? "." : "",
216                 dat.pinst, dat.prealm);
217         if (strcmp(rlmprincipal, srcprincipal))
218             return ZAUTH_FAILED;
219     } else {
220         return ZAUTH_FAILED;    /* didn't decode correctly */
221     }
222
223     /* Check the cryptographic checksum. */
224 #ifdef NOENCRYPTION
225     checksum = 0;
226 #else
227     checksum = compute_rlm_checksum(notice, dat.session);
228 #endif
229     if (checksum != notice->z_checksum) {
230 #ifndef NOENCRYPTION
231       checksum = compute_checksum(notice, dat.session);
232       if (checksum != notice->z_checksum)
233 #endif
234         return ZAUTH_FAILED;
235     }
236
237     /* Record the session key, expiry time, and source principal in the
238      * hash table, so we can do a fast check next time. */
239     add_session_key(&ticket, dat.session, srcprincipal,
240                     (time_t)(dat.time_sec + dat.life * 5 * 60));
241
242     return ZAUTH_YES;
243
244 #else /* !HAVE_KRB4 */
245     return (notice->z_auth) ? ZAUTH_YES : ZAUTH_NO;
246 #endif
247 }
248
249 Code_t
250 ZCheckAuthentication(notice, from)
251     ZNotice_t *notice;
252     struct sockaddr_in *from;
253 {       
254 #ifdef HAVE_KRB4
255     int result;
256     char srcprincipal[ANAME_SZ+INST_SZ+REALM_SZ+4];
257     KTEXT_ST authent, ticket;
258     AUTH_DAT dat;
259     ZChecksum_t checksum;
260     C_Block session_key;
261
262     if (!notice->z_auth)
263         return ZAUTH_NO;
264
265     /* Check for bogus authentication data length. */
266     if (notice->z_authent_len <= 0)
267         return ZAUTH_FAILED;
268
269     /* Read in the authentication data. */
270     if (ZReadAscii(notice->z_ascii_authent, 
271                    strlen(notice->z_ascii_authent)+1, 
272                    (unsigned char *)authent.dat, 
273                    notice->z_authent_len) == ZERR_BADFIELD) {
274         return ZAUTH_FAILED;
275     }
276     authent.length = notice->z_authent_len;
277
278     /* Copy the ticket out of the authentication data. */
279     if (krb_find_ticket(&authent, &ticket) != RD_AP_OK)
280         return ZAUTH_FAILED;
281
282     /* Try to do a fast check against the cryptographic checksum. */
283     if (find_session_key(&ticket, session_key, srcprincipal) >= 0) {
284         if (strcmp(srcprincipal, notice->z_sender) != 0)
285             return ZAUTH_FAILED;
286         if (notice->z_time.tv_sec - NOW > CLOCK_SKEW)
287             return ZAUTH_FAILED;
288         checksum = compute_checksum(notice, session_key);
289
290         /* If checksum matches, packet is authentic.  If not, we might
291          * have an outdated session key, so keep going the slow way.
292          */
293         if (checksum == notice->z_checksum) {
294             memcpy(__Zephyr_session, session_key, sizeof(C_Block));
295             return ZAUTH_YES;
296         }
297     }
298
299     /* We don't have the session key cached; do it the long way. */
300     result = krb_rd_req(&authent, SERVER_SERVICE, SERVER_INSTANCE,
301                         from->sin_addr.s_addr, &dat, srvtab_file);
302     if (result == RD_AP_OK) {
303         memcpy(__Zephyr_session, dat.session, sizeof(C_Block));
304         sprintf(srcprincipal, "%s%s%s@%s", dat.pname, dat.pinst[0] ? "." : "",
305                 dat.pinst, dat.prealm);
306         if (strcmp(srcprincipal, notice->z_sender))
307             return ZAUTH_FAILED;
308     } else {
309         return ZAUTH_FAILED;    /* didn't decode correctly */
310     }
311
312     /* Check the cryptographic checksum. */
313 #ifdef NOENCRYPTION
314     checksum = 0;
315 #else
316     checksum = compute_checksum(notice, dat.session);
317 #endif
318     if (checksum != notice->z_checksum)
319         return ZAUTH_FAILED;
320
321     /* Record the session key, expiry time, and source principal in the
322      * hash table, so we can do a fast check next time. */
323     add_session_key(&ticket, dat.session, srcprincipal,
324                     (time_t)(dat.time_sec + dat.life * 5 * 60));
325
326     return ZAUTH_YES;
327
328 #else /* !HAVE_KRB4 */
329     return (notice->z_auth) ? ZAUTH_YES : ZAUTH_NO;
330 #endif
331 }
332
333 #ifdef HAVE_KRB4
334
335 static int hash_ticket(p, len)
336     unsigned char *p;
337     int len;
338 {
339     unsigned long hashval = 0, g;
340
341     for (; len > 0; p++, len--) {
342         hashval = (hashval << 4) + *p;
343         g = hashval & 0xf0000000;
344         if (g) {
345             hashval ^= g >> 24;
346             hashval ^= g;
347         }
348     }
349     return hashval % HASHTAB_SIZE;
350 }
351
352 static void add_session_key(ticket, session_key, srcprincipal, expires)
353     KTEXT ticket;
354     C_Block session_key;
355     char *srcprincipal;
356     time_t expires;
357 {
358     Hash_entry *entry;
359     int hashval;
360
361     /* If we can't allocate memory for the hash table entry, just forget
362      * about it. */
363     entry = (Hash_entry *) malloc(sizeof(Hash_entry) - 1 + ticket->length);
364     if (!entry)
365         return;
366
367     /* Initialize the new entry. */
368     memcpy(entry->session_key, session_key, sizeof(entry->session_key));
369     strcpy(entry->srcprincipal, srcprincipal);
370     entry->expires = expires;
371     entry->ticket_len = ticket->length;
372     memcpy(entry->ticket, ticket->dat, ticket->length * sizeof(unsigned char));
373
374     /* Insert the new entry in the hash table. */
375     hashval = hash_ticket(ticket->dat, ticket->length);
376     entry->next = hashtab[hashval];
377     hashtab[hashval] = entry;
378 }
379
380 static int find_session_key(ticket, key, srcprincipal)
381     KTEXT ticket;
382     C_Block key;
383     char *srcprincipal;
384 {
385     unsigned char *dat;
386     int hashval, len;
387     Hash_entry *entry;
388
389     dat = ticket->dat;
390     len = ticket->length;
391     hashval = hash_ticket(dat, len);
392
393     for (entry = hashtab[hashval]; entry; entry = entry->next) {
394         if (entry->ticket_len == len && memcmp(entry->ticket, dat, len) == 0) {
395             memcpy(key, entry->session_key, sizeof(entry->session_key));
396             strcpy(srcprincipal, entry->srcprincipal);
397             return 0;
398         }
399     }
400     return -1;
401 }
402
403 static ZChecksum_t compute_checksum(notice, session_key)
404     ZNotice_t *notice;
405     C_Block session_key;
406 {
407 #ifdef NOENCRYPTION
408     return 0;
409 #else
410     ZChecksum_t checksum;
411     char *cstart, *cend, *hstart = notice->z_packet, *hend = notice->z_message;
412
413     cstart = notice->z_default_format + strlen(notice->z_default_format) + 1;
414     cend = cstart + strlen(cstart) + 1;
415     checksum = des_quad_cksum(hstart, NULL, cstart - hstart, 0, session_key);
416     checksum ^= des_quad_cksum(cend, NULL, hend - cend, 0, session_key);
417     checksum ^= des_quad_cksum(notice->z_message, NULL, notice->z_message_len,
418                                0, session_key);
419     return checksum;
420 #endif
421 }
422
423 static ZChecksum_t compute_rlm_checksum(notice, session_key)
424     ZNotice_t *notice;
425     C_Block session_key;
426 {
427 #ifdef NOENCRYPTION
428     return 0;
429 #else
430     ZChecksum_t checksum;
431     char *cstart, *cend, *hstart = notice->z_packet, *hend = notice->z_message;
432
433     cstart = notice->z_default_format + strlen(notice->z_default_format) + 1;
434     cend = cstart + strlen(cstart) + 1;
435     checksum = des_quad_cksum(hstart, NULL, cstart - hstart, 0, session_key);
436     return checksum;
437 #endif
438 }
439
440 void sweep_ticket_hash_table(arg)
441     void *arg;
442 {
443     int i;
444     Hash_entry **ptr, *entry;
445
446     for (i = 0; i < HASHTAB_SIZE; i++) {
447         ptr = &hashtab[i];
448         while (*ptr) {
449             entry = *ptr;
450             if (entry->expires < NOW) {
451                 *ptr = entry->next;
452                 free(entry);
453             } else {
454                 ptr = &(*ptr)->next;
455             }
456         }
457     }
458     timer_set_rel(SWEEP_INTERVAL, sweep_ticket_hash_table, NULL);
459 }
460
461 #endif /* HAVE_KRB4 */
462