1 /* This file is part of the Project Athena Zephyr Notification System.
2 * It contains functions for dealing with Kerberos functions in the server.
4 * Created by: John T Kohl
6 * Copyright (c) 1988 by the Massachusetts Institute of Technology.
7 * For copying and distribution information, see the file
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 $
19 static const char rcsid_kstuff_c[] = "$Id: kstuff.c,v 1.26 2001/04/10 23:28:20 ghudson Exp $";
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. */
30 #define HASHTAB_SIZE 4091
32 typedef struct hash_entry Hash_entry;
34 /* The ticket comes at the end, in a variable-length array. */
38 char srcprincipal[ANAME_SZ+INST_SZ+REALM_SZ+4];
41 unsigned char ticket[1];
44 Hash_entry *hashtab[HASHTAB_SIZE];
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));
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.
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 */
68 KTEXT_ST ticket; /* will get Kerberos ticket from client */
70 char instance[INST_SZ];
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.
77 for (i=0; i<20; i++) {
78 if (read(fd, &p[i], 1) != 1) {
79 syslog(LOG_WARNING,"bad read tkt len");
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);
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");
99 * now have the ticket. use it to get the authenticated
100 * data from Kerberos.
102 (void) strcpy(instance,"*"); /* let Kerberos fill it in */
104 return(krb_rd_req(&ticket, service, instance, haddr.s_addr,
105 kdata, srvtab ? srvtab : ""));
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
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 */
128 rem = krb_mk_req(ticket, service, host, ZGetRealm(), (u_long) 0);
130 return rem + krb_err_base;
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))
138 return (written < 0) ? errno : ZSRV_PKSHORT;
143 #endif /* HAVE_KRB4 */
146 ZCheckRealmAuthentication(notice, from, realm)
148 struct sockaddr_in *from;
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;
157 ZChecksum_t checksum;
164 /* Check for bogus authentication data length. */
165 if (notice->z_authent_len <= 0)
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) {
175 authent.length = notice->z_authent_len;
177 /* Copy the ticket out of the authentication data. */
178 if (krb_find_ticket(&authent, &ticket) != RD_AP_OK)
181 (void) sprintf(rlmprincipal, "%s.%s@%s", SERVER_SERVICE,
182 SERVER_INSTANCE, realm);
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)
188 if (notice->z_time.tv_sec - NOW > CLOCK_SKEW)
190 checksum = compute_rlm_checksum(notice, session_key);
192 /* If checksum matches, packet is authentic. If not, we might
193 * have an outdated session key, so keep going the slow way.
195 if (checksum == notice->z_checksum) {
196 (void) memcpy((char *)__Zephyr_session, (char *)session_key,
197 sizeof(C_Block)); /* For control_dispatch() */
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));
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))
220 return ZAUTH_FAILED; /* didn't decode correctly */
223 /* Check the cryptographic checksum. */
227 checksum = compute_rlm_checksum(notice, dat.session);
229 if (checksum != notice->z_checksum) {
231 checksum = compute_checksum(notice, dat.session);
232 if (checksum != notice->z_checksum)
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));
244 #else /* !HAVE_KRB4 */
245 return (notice->z_auth) ? ZAUTH_YES : ZAUTH_NO;
250 ZCheckAuthentication(notice, from)
252 struct sockaddr_in *from;
256 char srcprincipal[ANAME_SZ+INST_SZ+REALM_SZ+4];
257 KTEXT_ST authent, ticket;
259 ZChecksum_t checksum;
265 /* Check for bogus authentication data length. */
266 if (notice->z_authent_len <= 0)
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) {
276 authent.length = notice->z_authent_len;
278 /* Copy the ticket out of the authentication data. */
279 if (krb_find_ticket(&authent, &ticket) != RD_AP_OK)
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)
286 if (notice->z_time.tv_sec - NOW > CLOCK_SKEW)
288 checksum = compute_checksum(notice, session_key);
290 /* If checksum matches, packet is authentic. If not, we might
291 * have an outdated session key, so keep going the slow way.
293 if (checksum == notice->z_checksum) {
294 memcpy(__Zephyr_session, session_key, sizeof(C_Block));
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))
309 return ZAUTH_FAILED; /* didn't decode correctly */
312 /* Check the cryptographic checksum. */
316 checksum = compute_checksum(notice, dat.session);
318 if (checksum != notice->z_checksum)
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));
328 #else /* !HAVE_KRB4 */
329 return (notice->z_auth) ? ZAUTH_YES : ZAUTH_NO;
335 static int hash_ticket(p, len)
339 unsigned long hashval = 0, g;
341 for (; len > 0; p++, len--) {
342 hashval = (hashval << 4) + *p;
343 g = hashval & 0xf0000000;
349 return hashval % HASHTAB_SIZE;
352 static void add_session_key(ticket, session_key, srcprincipal, expires)
361 /* If we can't allocate memory for the hash table entry, just forget
363 entry = (Hash_entry *) malloc(sizeof(Hash_entry) - 1 + ticket->length);
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));
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;
380 static int find_session_key(ticket, key, srcprincipal)
390 len = ticket->length;
391 hashval = hash_ticket(dat, len);
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);
403 static ZChecksum_t compute_checksum(notice, session_key)
410 ZChecksum_t checksum;
411 char *cstart, *cend, *hstart = notice->z_packet, *hend = notice->z_message;
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,
423 static ZChecksum_t compute_rlm_checksum(notice, session_key)
430 ZChecksum_t checksum;
431 char *cstart, *cend, *hstart = notice->z_packet, *hend = notice->z_message;
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);
440 void sweep_ticket_hash_table(arg)
444 Hash_entry **ptr, *entry;
446 for (i = 0; i < HASHTAB_SIZE; i++) {
450 if (entry->expires < NOW) {
458 timer_set_rel(SWEEP_INTERVAL, sweep_ticket_hash_table, NULL);
461 #endif /* HAVE_KRB4 */