include-afsconfig-before-param-h-20010712
[openafs.git] / src / kauth / user_nt.c
1 /*
2  * Copyright 2000, International Business Machines Corporation and others.
3  * All Rights Reserved.
4  * 
5  * This software has been released under the terms of the IBM Public
6  * License.  For details, see the LICENSE file in the top-level source
7  * directory or online at http://www.openafs.org/dl/license10.html
8  */
9
10 #include <afsconfig.h>
11 #include <afs/param.h>
12
13 RCSID("$Header$");
14
15 #include <afs/stds.h>
16
17 #include <windows.h>
18 #include <rpc.h>
19 #include <errno.h>
20 #include <stdlib.h>
21 #include <stdio.h>              /* sprintf */
22 #include <malloc.h>
23 #include <afs/kautils.h>
24 #include <afs/cm_config.h>
25 #include <afs/krb.h>
26 #include <afs/krb_prot.h>
27 #include <rx/rxkad.h>
28 #include <crypt.h>
29 #include <des.h>
30
31 int krb_add_host (struct sockaddr_in *server_list_p);
32 static void krb_set_port(long port);
33
34 static long ka_AddHostProc(void *rockp, struct sockaddr_in *addrp, char *namep)
35 {
36         return krb_add_host(addrp);
37 }
38
39 static char bogusReason[100];
40
41 static char *ka_MapKerberosError(int code)
42 {
43         switch (code) {
44                 case INTK_BADPW:
45                         return "password was incorrect";
46                 case KERB_ERR_PRINCIPAL_UNKNOWN:
47                         return "user doesn't exist";
48                 case KERB_ERR_SERVICE_EXP:
49                         return "server and client clocks are badly skewed";
50                 case SKDC_RETRY:
51                         return "Authentication Server was unavailable";
52                 default:
53                         sprintf(bogusReason, "unknown authentication error %d",
54                                 code);
55                         return bogusReason;
56         }
57 }
58
59 static int krb_get_in_tkt_ext(
60         char *user,
61         char *instance,
62         char *realm,
63         char *service,
64         char *sinstance,
65         int life,
66         struct ktc_encryptionKey *key1,
67         struct ktc_encryptionKey *key2,
68         char **ticketpp,
69         long *ticketLenp,
70         struct ktc_encryptionKey *outKeyp,
71         long *kvnop,
72         long *expp,
73         char **reasonp);
74
75
76 afs_int32 ka_UserAuthenticateGeneral(
77         afs_int32 flags,
78         char *name,
79         char *instance,
80         char *realm,
81         char *password,
82         Date lifetime,
83         afs_int32 *password_expiresP,
84         afs_int32 spare,
85         char **reasonP)
86 {
87         int code;
88         struct ktc_encryptionKey key1, key2;
89         char *ticket = NULL;
90         int ticketLen;
91         struct ktc_encryptionKey sessionKey;
92         long kvno;
93         long expirationTime;
94         char fullRealm[256];
95         char upperRealm[256];
96         struct servent *sp;
97
98         struct ktc_principal server;
99         struct ktc_principal client;
100         struct ktc_token token;
101
102         if (instance == NULL) instance = "";
103         if (lifetime == 0) lifetime = MAXKTCTICKETLIFETIME;
104
105         code = cm_SearchCellFile(realm, fullRealm, ka_AddHostProc, NULL);
106
107         if (code) {
108                 *reasonP = "specified realm is unknown";
109                 return (code);
110         }
111
112         strcpy(upperRealm, fullRealm);
113         _strupr(upperRealm);
114
115         /* encrypt password, both ways */
116         ka_StringToKey(password, upperRealm, &key1);
117         des_string_to_key(password, &key2);
118
119         /* set port number */
120         sp = getservbyname("kerberos", "udp");
121         if (sp) krb_set_port(ntohs(sp->s_port));
122
123         *reasonP = NULL;
124         code = krb_get_in_tkt_ext(name, instance, upperRealm, "afs", "",
125                 lifetime, &key1, &key2, &ticket, &ticketLen, &sessionKey,
126                 &kvno, &expirationTime, reasonP);
127
128         if (code && *reasonP == NULL)
129                 *reasonP = ka_MapKerberosError(code);
130
131         if (code)
132                 return code;
133
134         strcpy(server.name, "afs");
135         strcpy(server.instance, "");
136         strcpy(server.cell, fullRealm);
137
138         /* Would like to use Vice ID's; using raw names for now. */
139         strcpy(client.name, name);
140         strcpy(client.instance, instance);
141         strcpy(client.cell, upperRealm);
142
143         token.startTime = 0;            /* XXX */
144         token.endTime = expirationTime;
145         token.sessionKey = sessionKey;
146         token.kvno = (short) kvno;
147         token.ticketLen = ticketLen;
148         memcpy(token.ticket, ticket, ticketLen);
149
150         code = ktc_SetToken(&server, &token, &client,
151                 (flags & KA_USERAUTH_AUTHENT_LOGON) ? AFS_SETTOK_LOGON : 0);
152         if (code) {
153                 if (code == KTC_NOCM || code == KTC_NOCMRPC)
154                         *reasonP = "AFS service may not have started";
155                 else if (code == KTC_RPC)
156                         *reasonP = "RPC failure in AFS gateway";
157                 else if (code == KTC_NOCELL)
158                         *reasonP = "unknown cell";
159                 else
160                         *reasonP = "unknown error";
161         }
162
163         return code;
164 }
165
166 /*
167  * krb_get_in_tkt()
168  *
169  * This code is descended from kerberos files krb_get_in_tkt.c and 
170  * send_to_kdc.c, and one.c.
171  */
172
173 /*
174  * definition of variable set to 1.
175  * used in krb_conf.h to determine host byte order.
176  */
177 static int krbONE = 1;
178
179 #define HOST_BYTE_ORDER (* (char *) &krbONE)
180 #define MSB_FIRST               0
181 #define LSB_FIRST               1
182
183 /*
184  * Copyright 1986, 1987, 1988 by the Massachusetts Institute
185  * of Technology.
186  *
187  * For copying and distribution information, please see the file
188  * <mit-cpyright.h>.
189  */
190
191 #include <string.h>
192 #include <time.h>
193
194 #include <des.h>
195 #include "krb.h"
196
197 #include <sys/types.h>
198 #include <winsock2.h>
199
200 static int     swap_bytes;
201
202 /*
203  * The kaserver defines these error codes *privately*. So we redefine them
204  * here, with a slight name change to show that they really are kaserver
205  * errors.
206  */
207 #define KERB_KA_ERR_BAD_MSG_TYPE  99
208 #define KERB_KA_ERR_BAD_LIFETIME  98
209 #define KERB_KA_ERR_NONNULL_REALM 97
210 #define KERB_KA_ERR_PKT_LENGTH    96
211 #define KERB_KA_ERR_TEXT_LENGTH   95
212
213 static void swap_u_int32
214     (afs_uint32 *u)
215 {
216    *u = *u >> 24 | (*u & 0x00ff0000) >> 8 | (*u & 0x0000ff00) << 8 | *u << 24;
217 }
218
219 static void swap_u_int16
220     (afs_uint16 *u) \
221 {
222     *u = *u >> 8 | *u << 8;
223 }
224
225 int pkt_clen(KTEXT pkt);
226 KTEXT pkt_cipher(KTEXT packet);
227
228 /*
229  * The following routine has been hacked to make it work for two different
230  * possible string-to-key algorithms. This is a minimal displacement
231  * of the code.
232  */
233
234 /*
235  * krb_get_in_tkt() gets a ticket for a given principal to use a given
236  * service and stores the returned ticket and session key for future
237  * use.
238  *
239  * The "user", "instance", and "realm" arguments give the identity of
240  * the client who will use the ticket.  The "service" and "sinstance"
241  * arguments give the identity of the server that the client wishes
242  * to use.  (The realm of the server is the same as the Kerberos server
243  * to whom the request is sent.)  The "life" argument indicates the
244  * desired lifetime of the ticket; the "key_proc" argument is a pointer
245  * to the routine used for getting the client's private key to decrypt
246  * the reply from Kerberos.  The "decrypt_proc" argument is a pointer
247  * to the routine used to decrypt the reply from Kerberos; and "arg"
248  * is an argument to be passed on to the "key_proc" routine.
249  *
250  * If all goes well, krb_get_in_tkt() returns INTK_OK, otherwise it
251  * returns an error code:  If an AUTH_MSG_ERR_REPLY packet is returned
252  * by Kerberos, then the error code it contains is returned.  Other
253  * error codes returned by this routine include INTK_PROT to indicate
254  * wrong protocol version, INTK_BADPW to indicate bad password (if
255  * decrypted ticket didn't make sense), INTK_ERR if the ticket was for
256  * the wrong server or the ticket store couldn't be initialized.
257  *
258  * The format of the message sent to Kerberos is as follows:
259  *
260  * Size                 Variable                Field
261  * ----                 --------                -----
262  *
263  * 1 byte               KRB_PROT_VERSION        protocol version number
264  * 1 byte               AUTH_MSG_KDC_REQUEST |  message type
265  *                      HOST_BYTE_ORDER         local byte order in lsb
266  * string               user                    client's name
267  * string               instance                client's instance
268  * string               realm                   client's realm
269  * 4 bytes              tlocal.tv_sec           timestamp in seconds
270  * 1 byte               life                    desired lifetime
271  * string               service                 service's name
272  * string               sinstance               service's instance
273  */
274
275 /*
276  * Check_response is a support routine for krb_get_in_tkt. 
277  *
278  * Check the response with the supplied key. If the key is apparently
279  * wrong, return INTK_BADPW, otherwise zero.
280  */
281 static check_response
282     (KTEXT rpkt,
283      KTEXT cip,
284      char *service,
285      char *instance,
286      char *realm,
287      struct ktc_encryptionKey *key)
288 {
289     Key_schedule key_s;
290     char *ptr;
291     char s_service[SNAME_SZ];
292     char s_instance[INST_SZ];
293     char s_realm[REALM_SZ];
294     int ticket_len;
295
296     if (!key) return -1;
297
298     /* copy information from return packet into "cip" */
299     cip->length = pkt_clen(rpkt);
300     memcpy((char *)(cip->dat), (char *) pkt_cipher(rpkt), cip->length);
301
302     /* decrypt ticket */
303     key_sched((char *) key, key_s);
304     pcbc_encrypt((C_Block *)cip->dat, (C_Block *)cip->dat,
305                  (long) cip->length, key_s, (des_cblock *) key, 0);
306
307     /* Skip session key */
308     ptr = (char *) cip->dat + 8;
309
310     /* Check and extract server's name */
311     if ((strlen(ptr) + (ptr - (char *) cip->dat)) > cip->length)
312         return(INTK_BADPW);
313
314     (void) strncpy(s_service, ptr, sizeof(s_service)-1);
315     s_service[sizeof(s_service)-1] = '\0';
316     ptr += strlen(s_service) + 1;
317
318     /* Check and extract server's instance */
319     if ((strlen(ptr) + (ptr - (char *) cip->dat)) > cip->length)
320         return(INTK_BADPW);
321
322     (void) strncpy(s_instance,ptr, sizeof(s_instance)-1);
323     s_instance[sizeof(s_instance)-1] = '\0';
324     ptr += strlen(s_instance) + 1;
325
326     /* Check and extract server's realm */
327     if ((strlen(ptr) + (ptr - (char *) cip->dat)) > cip->length)
328         return(INTK_BADPW);
329
330     (void) strncpy(s_realm,ptr, sizeof(s_realm));
331     s_realm[sizeof(s_realm)-1] = '\0';
332     ptr += strlen(s_realm) + 1;
333
334     /* Ignore ticket lifetime, server key version */
335     ptr += 2;
336
337     /* Extract and check ticket length */
338     ticket_len = (unsigned char) *ptr++;
339     
340     if ((ticket_len < 0) ||
341         ((ticket_len + (ptr - (char *) cip->dat)) > (int) cip->length))
342         return(INTK_BADPW);
343
344     /* Check returned server name, instance, and realm fields */
345     /*
346      * 7/23/98 - Deleting realm check.  This allows cell name to differ
347      * from realm name.
348      */
349 #ifdef REALMCHECK
350     if (strcmp(s_service, service) || strcmp(s_instance, instance) ||
351         strcmp(s_realm, realm)) {
352 #else
353     if (strcmp(s_service, service) || strcmp(s_instance, instance)) {
354 #endif
355         /* not what we asked for: assume decryption failed */
356         return(INTK_BADPW);
357     }
358
359     return 0;
360 }
361
362 /*
363  * The old kaserver (pre 3.4) returned zero error codes sometimes, leaving
364  * the kaserver error code in a string in the text of the error message.
365  * The new one does the same, but returns KDC_GEN_ERR rather than zero.
366  * We try to extract the actual error code.
367  */
368 static char bogus_kaerror[100];
369 static int kaserver_map_error_code
370     (int code,
371      char *etext,
372      char **reasonP)
373 {
374     if (code == 0 || code == KDC_GEN_ERR) {
375         int mapcode;
376         if (sscanf(etext, "code =%u: ", &mapcode) == 1) {
377             code = mapcode;
378             strcpy(bogus_kaerror, etext);
379             *reasonP = bogus_kaerror;
380         }
381     }
382
383     if (code == 0) {
384         code = KDC_GEN_ERR;
385     }
386
387     return code;
388 }
389
390 static int krb_get_in_tkt_ext(user, instance, realm, service, sinstance, life,
391         key1, key2, ticketpp, ticketLenp, outKeyp, kvnop, expp, reasonp)
392     char *user;
393     char *instance;
394     char *realm;
395     char *service;
396     char *sinstance;
397     int life;
398     struct ktc_encryptionKey *key1, *key2;
399     char **ticketpp;
400     long *ticketLenp;
401     struct ktc_encryptionKey *outKeyp;
402     long *kvnop;
403     long *expp;
404     char **reasonp;
405 {
406     KTEXT_ST pkt_st;
407     KTEXT pkt = &pkt_st;        /* Packet to KDC */
408     KTEXT_ST rpkt_st;
409     KTEXT rpkt = &rpkt_st;      /* Returned packet */
410     KTEXT_ST cip_st;
411     KTEXT cip = &cip_st;        /* Returned Ciphertext */
412     KTEXT_ST tkt_st;
413     KTEXT tkt = &tkt_st;        /* Current ticket */
414     C_Block ses;                /* Session key for tkt */
415     int kvno;                   /* Kvno for session key */
416     unsigned char *v = pkt->dat; /* Prot vers no */
417     unsigned char *t = (pkt->dat+1); /* Prot msg type */
418
419     char s_name[SNAME_SZ];
420     char s_instance[INST_SZ];
421     char rlm[REALM_SZ];
422     int lifetime;
423     char kerberos_life;
424     int msg_byte_order;
425     int kerror;
426     char *ptr;
427
428     unsigned long t_local;
429
430     afs_uint32 rep_err_code;
431     afs_uint32 exp_date;
432     afs_uint32 kdc_time; 
433
434     /* BUILD REQUEST PACKET */
435
436     /* Set up the fixed part of the packet */
437     *v = (unsigned char) KRB_PROT_VERSION;
438     *t = (unsigned char) AUTH_MSG_KDC_REQUEST;
439     *t |= HOST_BYTE_ORDER;
440
441     /* Now for the variable info */
442     (void) strcpy((char *)(pkt->dat+2),user); /* aname */
443     pkt->length = 3 + strlen(user);
444     (void) strcpy((char *)(pkt->dat+pkt->length),
445                   instance);    /* instance */
446     pkt->length += 1 + strlen(instance);
447     (void) strcpy((char *)(pkt->dat+pkt->length),realm); /* realm */
448     pkt->length += 1 + strlen(realm);
449
450 #ifndef WIN32
451     (void) gettimeofday(&t_local,(struct timezone *) 0);
452 #else /* WIN32 */
453     t_local = time((void *) 0);
454 #endif /* WIN32 */
455     /* timestamp */
456     memcpy((char *)(pkt->dat+pkt->length), (char *)&(t_local), 4);
457     pkt->length += 4;
458
459     if (life == 0) {
460         kerberos_life = DEFAULT_TKT_LIFE;
461     } else {
462         kerberos_life = time_to_life(0, life);
463         if (kerberos_life == 0) {
464             kerberos_life = DEFAULT_TKT_LIFE;
465         }
466     }
467
468     *(pkt->dat+(pkt->length)++) = kerberos_life;
469     (void) strcpy((char *)(pkt->dat+pkt->length),service);
470     pkt->length += 1 + strlen(service);
471     (void) strcpy((char *)(pkt->dat+pkt->length),sinstance);
472
473     pkt->length += 1 + strlen(sinstance);
474
475     rpkt->length = 0;
476
477     /* SEND THE REQUEST AND RECEIVE THE RETURN PACKET */
478
479     if (kerror = send_to_kdc(pkt, rpkt)) {
480         return(kerror);
481     }
482
483     /* check packet version of the returned packet */
484     if (pkt_version(rpkt) != KRB_PROT_VERSION)
485         return(INTK_PROT);
486
487     /* Check byte order */
488     msg_byte_order = pkt_msg_type(rpkt) & 1;
489     swap_bytes = 0;
490     if (msg_byte_order != HOST_BYTE_ORDER) {
491         swap_bytes++;
492     }
493
494     switch (pkt_msg_type(rpkt) & ~1) {
495     case AUTH_MSG_KDC_REPLY:
496         break;
497     case AUTH_MSG_ERR_REPLY:
498         memcpy((char *) &rep_err_code, pkt_err_code(rpkt), 4);
499         if (swap_bytes) swap_u_int32(&rep_err_code);
500         /* kaservers return bogus error codes in different ways, so map it
501            from the error text if this is the case */
502         return kaserver_map_error_code(rep_err_code, pkt_err_text(rpkt), reasonp);
503
504     default:
505         return(INTK_PROT);
506     }
507
508     /* get the principal's expiration date */
509     memcpy((char *) &exp_date, pkt_x_date(rpkt), sizeof(exp_date));
510     if (swap_bytes) swap_u_int32(&exp_date);
511
512     /* Extract length. This will be re-extracted in check_response, below */
513     cip->length = pkt_clen(rpkt);
514
515     /* Length of zero seems to correspond to no principal (with kaserver) */
516     if (cip->length== 0) {
517         return (KERB_ERR_PRINCIPAL_UNKNOWN);
518     }
519
520     if ((cip->length < 0) || (cip->length > sizeof(cip->dat))) {
521         return(INTK_ERR);               /* no appropriate error code
522                                          currently defined for INTK_ */
523     }
524
525     /*
526      * Check the response against both possible keys, and use the one
527      * that works.
528      */
529     if (check_response(rpkt, cip, service, sinstance, realm, key1) &&
530         check_response(rpkt, cip, service, sinstance, realm, key2)) {
531         return INTK_BADPW;
532     }
533
534     /*
535      * EXTRACT INFORMATION FROM RETURN PACKET
536      *
537      * Some of the fields, below are already checked for integrity by
538      * check_response.
539      */
540     ptr = (char *) cip->dat;
541
542     /* extract session key */
543     memcpy((char *)ses, ptr, 8);
544     ptr += 8;
545
546     /* extract server's name */
547     (void) strncpy(s_name,ptr, sizeof(s_name)-1);
548     s_name[sizeof(s_name)-1] = '\0';
549     ptr += strlen(s_name) + 1;
550
551     /* extract server's instance */
552     (void) strncpy(s_instance,ptr, sizeof(s_instance)-1);
553     s_instance[sizeof(s_instance)-1] = '\0';
554     ptr += strlen(s_instance) + 1;
555
556     /* extract server's realm */
557     (void) strncpy(rlm,ptr, sizeof(rlm));
558     rlm[sizeof(rlm)-1] = '\0';
559     ptr += strlen(rlm) + 1;
560
561     /* extract ticket lifetime, server key version, ticket length */
562     /* be sure to avoid sign extension on lifetime! */
563     lifetime = (unsigned char) ptr[0];
564     kvno = (unsigned char) ptr[1];
565     tkt->length = (unsigned char) ptr[2];
566     ptr += 3;
567     
568     /* extract ticket itself */
569     memcpy((char *)(tkt->dat), ptr, tkt->length);
570     ptr += tkt->length;
571
572     /* check KDC time stamp */
573     memcpy((char *)&kdc_time, ptr, 4); /* Time (coarse) */
574     if (swap_bytes) swap_u_int32(&kdc_time);
575
576     ptr += 4;
577
578     t_local = time((void *) 0);
579     if (abs((int)(t_local - kdc_time)) > CLOCK_SKEW) {
580         return(RD_AP_TIME);             /* XXX should probably be better
581                                            code */
582     }
583
584     /* copy out results; if *ticketpp is non-null, the caller has already
585      * allocated the buffer for us.
586      */
587     memcpy(outKeyp, ses, sizeof(struct ktc_encryptionKey));
588     if (*ticketpp == NULL) {
589         *ticketpp = malloc(tkt->length);
590     }
591     else if (tkt->length > (unsigned long) *ticketLenp) return -1;
592     *ticketLenp = tkt->length;
593     memcpy(*ticketpp, tkt->dat, tkt->length);
594     *kvnop = kvno;
595     if (expp) *expp = life_to_time(kdc_time, (char) lifetime);
596
597     return(INTK_OK);    /* this is zero */
598 }
599
600 /*
601  *
602  * Copyright 1987, 1988 by the Massachusetts Institute of Technology.
603  *
604  * For copying and distribution information, please see the file
605  * <mit-cpyright.h>.
606  */
607
608 #define S_AD_SZ sizeof(struct sockaddr_in)
609
610 static int krb_debug;
611
612 /* CLIENT_KRB_TIMEOUT indicates the time to wait before
613  * retrying a server.  It's defined in "krb.h".
614  */
615 static struct timeval timeout = { CLIENT_KRB_TIMEOUT, 0};
616 static char *prog = "dm";
617 static send_recv();
618
619 /*
620  * This file contains two routines, send_to_kdc() and send_recv().
621  * send_recv() is a static routine used by send_to_kdc().
622  */
623
624 /*
625  * send_to_kdc() sends a message to the Kerberos authentication
626  * server(s) in the given realm and returns the reply message.
627  * The "pkt" argument points to the message to be sent to Kerberos;
628  * the "rpkt" argument will be filled in with Kerberos' reply.
629  * The "realm" argument indicates the realm of the Kerberos server(s)
630  * to transact with.  If the realm is null, the local realm is used.
631  *
632  * If more than one Kerberos server is known for a given realm,
633  * different servers will be queried until one of them replies.
634  * Several attempts (retries) are made for each server before
635  * giving up entirely.
636  *
637  * If an answer was received from a Kerberos host, KSUCCESS is
638  * returned.  The following errors can be returned:
639  *
640  * SKDC_CANT    - can't get local realm
641  *              - can't find "kerberos" in /etc/services database
642  *              - can't open socket
643  *              - can't bind socket
644  *              - all ports in use
645  *              - couldn't find any Kerberos host
646  *
647  * SKDC_RETRY   - couldn't get an answer from any Kerberos server,
648  *                after several retries
649  */
650
651 typedef struct krb_server {
652         struct krb_server *nextp;
653         struct sockaddr_in addr;
654 } krb_server_t;
655     
656 static long krb_udp_port = KRB_PORT; /* In host byte order */
657 static krb_server_t *krb_hosts_p = NULL;
658 static int krb_nhosts = 0;
659
660 static void krb_set_port
661     (long port)
662 {
663     krb_udp_port = port;
664 }
665
666 int krb_add_host
667     (struct sockaddr_in *server_list_p)
668 {
669     krb_server_t *krb_host_p;
670
671     krb_host_p = malloc(sizeof(krb_server_t));
672     
673     /* add host to list */
674     krb_host_p->nextp = krb_hosts_p;
675     krb_hosts_p = krb_host_p;
676     krb_nhosts++;
677
678     /* copy in the data */
679     memcpy(&krb_host_p->addr, server_list_p, sizeof(struct sockaddr_in));
680
681     return 0;
682 }
683
684 static send_to_kdc(pkt,rpkt)
685     KTEXT pkt;
686     KTEXT rpkt;
687 {
688     SOCKET f;
689     int retry;
690     int retval;
691     krb_server_t *tsp;
692     struct sockaddr_in to;
693     int timeAvail, timePerIter, numIters;
694
695     memset((char *)&to, 0, sizeof(to));
696     if ((f = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
697         if (krb_debug)
698             fprintf(stderr,"%s: Can't open socket\n", prog);
699         return(SKDC_CANT);
700     }
701     /* from now on, exit through rtn label for cleanup */
702
703     /* compute # of retries */
704     /* The SMB client seems to time out after 60 seconds. */
705     timeAvail = 60;
706     /* Leave ourselves some margin for fooling around
707     timeAvail -= 10;
708     /* How long does one iteration take? */
709     timePerIter = krb_nhosts * CLIENT_KRB_TIMEOUT;
710     /* How many iters? */
711     numIters = timeAvail / timePerIter;
712     /* No more than max */
713     if (numIters > CLIENT_KRB_RETRY) numIters = CLIENT_KRB_RETRY;
714     /* At least one */
715     if (numIters < 1) numIters = 1;
716
717     /* retry each host in sequence */
718     for (retry = 0; retry < numIters; ++retry) {
719         for(tsp = krb_hosts_p; tsp; tsp = tsp->nextp) {
720             to = tsp->addr;
721             to.sin_family = AF_INET;
722             to.sin_port = htons(((unsigned short)krb_udp_port));
723             if (send_recv(pkt, rpkt, f, &to)) {
724                 retval = KSUCCESS;
725                 goto rtn;
726             }
727         }
728     }
729
730     retval = SKDC_RETRY;
731
732 rtn:
733     (void) closesocket(f);
734
735     return(retval);
736 }
737
738 /*
739  * try to send out and receive message.
740  * return 1 on success, 0 on failure
741  */
742
743 static send_recv(pkt,rpkt,f,_to)
744     KTEXT pkt;
745     KTEXT rpkt;
746     SOCKET f;
747     struct sockaddr_in *_to;
748 {
749     fd_set readfds;
750     struct sockaddr_in from;
751     int sin_size;
752     int numsent;
753     int code;
754
755     if (krb_debug) {
756         if (_to->sin_family == AF_INET)
757             printf("Sending message to %s...",
758                    inet_ntoa(_to->sin_addr));
759         else
760             printf("Sending message...");
761         (void) fflush(stdout);
762     }
763     if ((numsent = sendto(f,(char *)(pkt->dat), pkt->length, 0, 
764                           (struct sockaddr *)_to,
765                           S_AD_SZ)) != (int) pkt->length) {
766         if (krb_debug)
767             printf("sent only %d/%d\n",numsent, pkt->length);
768         return 0;
769     }
770     if (krb_debug) {
771         printf("Sent\nWaiting for reply...");
772         (void) fflush(stdout);
773     }
774     FD_ZERO(&readfds);
775     FD_SET(f, &readfds);
776     errno = 0;
777     /* select - either recv is ready, or timeout */
778     /* see if timeout or error or wrong descriptor */
779     if (select(f + 1, &readfds, (fd_set *)0, (fd_set *)0, &timeout) < 1
780         || !FD_ISSET(f, &readfds)) {
781         if (krb_debug) {
782             fprintf(stderr, "select failed: readfds=%x",
783                     readfds);
784             perror("");
785         }
786         return 0;
787     }
788     sin_size = sizeof(from);
789     if ((code = recvfrom(f, (char *)(rpkt->dat), sizeof(rpkt->dat), 0,
790                  (struct sockaddr *)&from, &sin_size))
791         < 0) {
792         if (krb_debug)
793             perror("recvfrom");
794         return 0;
795     }
796     if (krb_debug) {
797         printf("received packet from %s\n", inet_ntoa(from.sin_addr));
798         fflush(stdout);
799     }
800     return 1;
801 }
802
803 /*
804  *
805  * Copyright 1985, 1986, 1987, 1988 by the Massachusetts Institute
806  * of Technology.
807  *
808  * For copying and distribution information, please see the file
809  * <mit-copyright.h>.
810  */
811
812 /*
813  * This routine takes a reply packet from the Kerberos ticket-granting
814  * service and returns a pointer to the beginning of the ciphertext in it.
815  *
816  * See "krb_prot.h" for packet format.
817  */
818
819 static KTEXT pkt_cipher
820     (KTEXT packet)
821 {
822     unsigned char *ptr = pkt_a_realm(packet) + 6
823         + strlen((char *)pkt_a_realm(packet));
824     /* Skip a few more fields */
825     ptr += 3 + 4;               /* add 4 for exp_date */
826
827     /* And return the pointer */
828     return((KTEXT) ptr);
829 }
830
831 /*
832  *
833  * Copyright 1985, 1986, 1987, 1988 by the Massachusetts Institute
834  * of Technology.
835  *
836  * For copying and distribution information, please see the file
837  * <mit-copyright.h>.
838  */
839
840 /*
841  * Given a pointer to an AUTH_MSG_KDC_REPLY packet, return the length of
842  * its ciphertext portion.  The external variable "swap_bytes" is assumed
843  * to have been set to indicate whether or not the packet is in local
844  * byte order.  pkt_clen() takes this into account when reading the
845  * ciphertext length out of the packet.
846  */
847
848 static int pkt_clen(KTEXT pkt)
849 {
850     afs_uint16 temp;
851
852     /* Start of ticket list */
853     unsigned char *ptr = pkt_a_realm(pkt) + 10
854         + strlen((char *)pkt_a_realm(pkt));
855
856     /* Finally the length */
857     memcpy((char *)&temp, (char *)(++ptr), 2); /* alignment */
858     if (swap_bytes) {
859         swap_u_int16(&temp);
860     }
861
862     if (krb_debug) {
863         printf("Clen is %d\n", temp);
864     }
865
866     return(temp);
867 }
868
869 /* This defines the Andrew string_to_key function.  It accepts a password
870    string as input and converts its via a one-way encryption algorithm to a DES
871    encryption key.  It is compatible with the original Andrew authentication
872    service password database. */
873
874 static void Andrew_StringToKey (str, cell, key)
875   char          *str;
876   char          *cell;                  /* cell for password */
877   struct ktc_encryptionKey *key;
878 {   char  password[8+1];                /* crypt's limit is 8 chars anyway */
879     int   i;
880     int   passlen;
881
882     memset(key, 0, sizeof(struct ktc_encryptionKey));
883
884     strncpy (password, cell, 8);
885     passlen = strlen (str);
886     if (passlen > 8) passlen = 8;
887
888     for (i=0; i<passlen; i++)
889         password[i] ^= str[i];
890
891     for (i=0;i<8;i++)
892         if (password[i] == '\0') password[i] = 'X';
893
894     /* crypt only considers the first 8 characters of password but for some
895        reason returns eleven characters of result (plus the two salt chars). */
896     strncpy((char *) key, (char *)crypt(password, "p1") + 2, sizeof(struct ktc_encryptionKey));
897
898     /* parity is inserted into the LSB so leftshift each byte up one bit.  This
899        allows ascii characters with a zero MSB to retain as much significance
900        as possible. */
901     {   char *keybytes = (char *)key;
902         unsigned int temp;
903
904         for (i = 0; i < 8; i++) {
905             temp = (unsigned int) keybytes[i];
906             keybytes[i] = (unsigned char) (temp << 1);
907         }
908     }
909     des_fixup_key_parity ((unsigned char *) key);
910 }
911
912
913 static void StringToKey (str, cell, key)
914   char          *str;
915   char          *cell;                  /* cell for password */
916   struct ktc_encryptionKey *key;
917 {   des_key_schedule schedule;
918     char temp_key[8];
919     char ivec[8];
920     char password[BUFSIZ];
921     int  passlen;
922
923     strncpy (password, str, sizeof(password));
924     if ((passlen = strlen (password)) < sizeof(password)-1)
925         strncat (password, cell, sizeof(password)-passlen);
926     if ((passlen = strlen(password)) > sizeof(password))
927         passlen = sizeof(password);
928     
929     memcpy(ivec, "kerberos", 8);
930     memcpy(temp_key, "kerberos", 8);
931     des_fixup_key_parity (temp_key);
932     des_key_sched (temp_key, schedule);
933     des_cbc_cksum (password, ivec, passlen, schedule, ivec);
934
935     memcpy(temp_key, ivec, 8);
936     des_fixup_key_parity (temp_key);
937     des_key_sched (temp_key, schedule);
938     des_cbc_cksum (password, (char *) key, passlen, schedule, ivec);
939     
940     des_fixup_key_parity ((char *) key);
941 }