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