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