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