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