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