]> asedeno.scripts.mit.edu Git - 1ts-debian.git/blob - zephyr/server/kstuff.c
set svn:keywords
[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.27 2004/02/29 06:34:04 zacheiss Exp $
13  */
14
15 #include "zserver.h"
16
17 #ifndef lint
18 #ifndef SABER
19 static const char rcsid_kstuff_c[] = "$Id$";
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     (void) sprintf(rlmprincipal, "%s.%s@%s", SERVER_SERVICE,
178                    SERVER_INSTANCE, realm);
179
180     result = krb_rd_req(&authent, SERVER_SERVICE, SERVER_INSTANCE,
181                         from->sin_addr.s_addr, &dat, srvtab_file);
182     if (result == RD_AP_OK) {
183         sprintf(srcprincipal, "%s%s%s@%s", dat.pname, dat.pinst[0] ? "." : "",
184                 dat.pinst, dat.prealm);
185         if (strcmp(rlmprincipal, srcprincipal))
186             return ZAUTH_FAILED;
187     } else {
188         return ZAUTH_FAILED;    /* didn't decode correctly */
189     }
190
191     /* Check the cryptographic checksum. */
192 #ifdef NOENCRYPTION
193     checksum = 0;
194 #else
195     checksum = compute_rlm_checksum(notice, dat.session);
196 #endif
197     if (checksum != notice->z_checksum) {
198 #ifndef NOENCRYPTION
199       checksum = compute_checksum(notice, dat.session);
200       if (checksum != notice->z_checksum)
201 #endif
202         return ZAUTH_FAILED;
203     }
204
205     return ZAUTH_YES;
206
207 #else /* !HAVE_KRB4 */
208     return (notice->z_auth) ? ZAUTH_YES : ZAUTH_NO;
209 #endif
210 }
211
212 Code_t
213 ZCheckAuthentication(notice, from)
214     ZNotice_t *notice;
215     struct sockaddr_in *from;
216 {       
217 #ifdef HAVE_KRB4
218     int result;
219     char srcprincipal[ANAME_SZ+INST_SZ+REALM_SZ+4];
220     KTEXT_ST authent, ticket;
221     AUTH_DAT dat;
222     ZChecksum_t checksum;
223     C_Block session_key;
224
225     if (!notice->z_auth)
226         return ZAUTH_NO;
227
228     /* Check for bogus authentication data length. */
229     if (notice->z_authent_len <= 0)
230         return ZAUTH_FAILED;
231
232     /* Read in the authentication data. */
233     if (ZReadAscii(notice->z_ascii_authent, 
234                    strlen(notice->z_ascii_authent)+1, 
235                    (unsigned char *)authent.dat, 
236                    notice->z_authent_len) == ZERR_BADFIELD) {
237         return ZAUTH_FAILED;
238     }
239     authent.length = notice->z_authent_len;
240
241     result = krb_rd_req(&authent, SERVER_SERVICE, SERVER_INSTANCE,
242                         from->sin_addr.s_addr, &dat, srvtab_file);
243     if (result == RD_AP_OK) {
244         memcpy(__Zephyr_session, dat.session, sizeof(C_Block));
245         sprintf(srcprincipal, "%s%s%s@%s", dat.pname, dat.pinst[0] ? "." : "",
246                 dat.pinst, dat.prealm);
247         if (strcmp(srcprincipal, notice->z_sender))
248             return ZAUTH_FAILED;
249     } else {
250         return ZAUTH_FAILED;    /* didn't decode correctly */
251     }
252
253     /* Check the cryptographic checksum. */
254 #ifdef NOENCRYPTION
255     checksum = 0;
256 #else
257     checksum = compute_checksum(notice, dat.session);
258 #endif
259     if (checksum != notice->z_checksum)
260         return ZAUTH_FAILED;
261
262     return ZAUTH_YES;
263
264 #else /* !HAVE_KRB4 */
265     return (notice->z_auth) ? ZAUTH_YES : ZAUTH_NO;
266 #endif
267 }
268
269 #ifdef HAVE_KRB4
270
271 static int hash_ticket(p, len)
272     unsigned char *p;
273     int len;
274 {
275     unsigned long hashval = 0, g;
276
277     for (; len > 0; p++, len--) {
278         hashval = (hashval << 4) + *p;
279         g = hashval & 0xf0000000;
280         if (g) {
281             hashval ^= g >> 24;
282             hashval ^= g;
283         }
284     }
285     return hashval % HASHTAB_SIZE;
286 }
287
288 static void add_session_key(ticket, session_key, srcprincipal, expires)
289     KTEXT ticket;
290     C_Block session_key;
291     char *srcprincipal;
292     time_t expires;
293 {
294     Hash_entry *entry;
295     int hashval;
296
297     /* If we can't allocate memory for the hash table entry, just forget
298      * about it. */
299     entry = (Hash_entry *) malloc(sizeof(Hash_entry) - 1 + ticket->length);
300     if (!entry)
301         return;
302
303     /* Initialize the new entry. */
304     memcpy(entry->session_key, session_key, sizeof(entry->session_key));
305     strcpy(entry->srcprincipal, srcprincipal);
306     entry->expires = expires;
307     entry->ticket_len = ticket->length;
308     memcpy(entry->ticket, ticket->dat, ticket->length * sizeof(unsigned char));
309
310     /* Insert the new entry in the hash table. */
311     hashval = hash_ticket(ticket->dat, ticket->length);
312     entry->next = hashtab[hashval];
313     hashtab[hashval] = entry;
314 }
315
316 static int find_session_key(ticket, key, srcprincipal)
317     KTEXT ticket;
318     C_Block key;
319     char *srcprincipal;
320 {
321     unsigned char *dat;
322     int hashval, len;
323     Hash_entry *entry;
324
325     dat = ticket->dat;
326     len = ticket->length;
327     hashval = hash_ticket(dat, len);
328
329     for (entry = hashtab[hashval]; entry; entry = entry->next) {
330         if (entry->ticket_len == len && memcmp(entry->ticket, dat, len) == 0) {
331             memcpy(key, entry->session_key, sizeof(entry->session_key));
332             strcpy(srcprincipal, entry->srcprincipal);
333             return 0;
334         }
335     }
336     return -1;
337 }
338
339 static ZChecksum_t compute_checksum(notice, session_key)
340     ZNotice_t *notice;
341     C_Block session_key;
342 {
343 #ifdef NOENCRYPTION
344     return 0;
345 #else
346     ZChecksum_t checksum;
347     char *cstart, *cend, *hstart = notice->z_packet, *hend = notice->z_message;
348
349     cstart = notice->z_default_format + strlen(notice->z_default_format) + 1;
350     cend = cstart + strlen(cstart) + 1;
351     checksum = des_quad_cksum(hstart, NULL, cstart - hstart, 0, session_key);
352     checksum ^= des_quad_cksum(cend, NULL, hend - cend, 0, session_key);
353     checksum ^= des_quad_cksum(notice->z_message, NULL, notice->z_message_len,
354                                0, session_key);
355     return checksum;
356 #endif
357 }
358
359 static ZChecksum_t compute_rlm_checksum(notice, session_key)
360     ZNotice_t *notice;
361     C_Block session_key;
362 {
363 #ifdef NOENCRYPTION
364     return 0;
365 #else
366     ZChecksum_t checksum;
367     char *cstart, *cend, *hstart = notice->z_packet, *hend = notice->z_message;
368
369     cstart = notice->z_default_format + strlen(notice->z_default_format) + 1;
370     cend = cstart + strlen(cstart) + 1;
371     checksum = des_quad_cksum(hstart, NULL, cstart - hstart, 0, session_key);
372     return checksum;
373 #endif
374 }
375
376 void sweep_ticket_hash_table(arg)
377     void *arg;
378 {
379     int i;
380     Hash_entry **ptr, *entry;
381
382     for (i = 0; i < HASHTAB_SIZE; i++) {
383         ptr = &hashtab[i];
384         while (*ptr) {
385             entry = *ptr;
386             if (entry->expires < NOW) {
387                 *ptr = entry->next;
388                 free(entry);
389             } else {
390                 ptr = &(*ptr)->next;
391             }
392         }
393     }
394     timer_set_rel(SWEEP_INTERVAL, sweep_ticket_hash_table, NULL);
395 }
396
397 #endif /* HAVE_KRB4 */
398