2 * Copyright 2000, International Business Machines Corporation and others.
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
10 #include <afsconfig.h>
11 #include <afs/param.h>
21 #include <stdio.h> /* sprintf */
23 #include <afs/kautils.h>
24 #include <afs/cm_config.h>
26 #include <afs/krb_prot.h>
31 int krb_add_host (struct sockaddr_in *server_list_p);
32 static void krb_set_port(long port);
34 static long ka_AddHostProc(void *rockp, struct sockaddr_in *addrp, char *namep)
36 return krb_add_host(addrp);
39 static char bogusReason[100];
41 static char *ka_MapKerberosError(int code)
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";
51 return "Authentication Server was unavailable";
53 return "server and client clocks are badly skewed";
55 sprintf(bogusReason, "unknown authentication error %d",
61 static int krb_get_in_tkt_ext(
68 struct ktc_encryptionKey *key1,
69 struct ktc_encryptionKey *key2,
72 struct ktc_encryptionKey *outKeyp,
78 afs_int32 ka_UserAuthenticateGeneral(
85 afs_int32 *password_expiresP,
89 return ka_UserAuthenticateGeneral2(flags, name, instance, realm, password, NULL,
90 lifetime, password_expiresP, spare, reasonP);
93 afs_int32 ka_UserAuthenticateGeneral2(
101 afs_int32 *password_expiresP,
106 struct ktc_encryptionKey key1, key2;
109 struct ktc_encryptionKey sessionKey;
113 char upperRealm[256];
117 struct ktc_principal server;
118 struct ktc_principal client;
119 struct ktc_token token;
121 if (instance == NULL) instance = "";
122 if (lifetime == 0) lifetime = MAXKTCTICKETLIFETIME;
124 code = cm_SearchCellFile(realm, fullRealm, ka_AddHostProc, NULL);
128 code = cm_SearchCellByDNS(realm, fullRealm, &ttl, ka_AddHostProc, NULL);
132 *reasonP = "specified realm is unknown";
136 strcpy(upperRealm, fullRealm);
139 /* encrypt password, both ways */
140 ka_StringToKey(password, upperRealm, &key1);
141 des_string_to_key(password, &key2);
143 /* set port number */
144 sp = getservbyname("kerberos", "udp");
145 if (sp) krb_set_port(ntohs(sp->s_port));
148 code = krb_get_in_tkt_ext(name, instance, upperRealm, "afs", "",
149 lifetime, &key1, &key2, &ticket, &ticketLen, &sessionKey,
150 &kvno, &expirationTime, reasonP);
152 if (code && *reasonP == NULL)
153 *reasonP = ka_MapKerberosError(code);
158 strcpy(server.name, "afs");
159 strcpy(server.instance, "");
160 strcpy(server.cell, fullRealm);
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);
167 strcpy(client.smbname, smbname);
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);
176 code = ktc_SetToken(&server, &token, &client,
177 (flags & KA_USERAUTH_AUTHENT_LOGON) ? AFS_SETTOK_LOGON : 0);
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";
186 *reasonP = "unknown error";
195 * This code is descended from kerberos files krb_get_in_tkt.c and
196 * send_to_kdc.c, and one.c.
200 * definition of variable set to 1.
201 * used in krb_conf.h to determine host byte order.
203 static int krbONE = 1;
205 #define HOST_BYTE_ORDER (* (char *) &krbONE)
210 * Copyright 1986, 1987, 1988 by the Massachusetts Institute
213 * For copying and distribution information, please see the file
223 #include <sys/types.h>
224 #include <winsock2.h>
226 static int swap_bytes;
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
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
239 static void swap_u_int32
242 *u = *u >> 24 | (*u & 0x00ff0000) >> 8 | (*u & 0x0000ff00) << 8 | *u << 24;
245 static void swap_u_int16
248 *u = *u >> 8 | *u << 8;
251 int pkt_clen(KTEXT pkt);
252 KTEXT pkt_cipher(KTEXT packet);
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
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
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.
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.
284 * The format of the message sent to Kerberos is as follows:
286 * Size Variable Field
287 * ---- -------- -----
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
302 * Check_response is a support routine for krb_get_in_tkt.
304 * Check the response with the supplied key. If the key is apparently
305 * wrong, return INTK_BADPW, otherwise zero.
307 static check_response
313 struct ktc_encryptionKey *key)
317 char s_service[SNAME_SZ];
318 char s_instance[INST_SZ];
319 char s_realm[REALM_SZ];
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);
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);
333 /* Skip session key */
334 ptr = (char *) cip->dat + 8;
336 /* Check and extract server's name */
337 if ((strlen(ptr) + (ptr - (char *) cip->dat)) > cip->length) {
341 (void) strncpy(s_service, ptr, sizeof(s_service)-1);
342 s_service[sizeof(s_service)-1] = '\0';
343 ptr += strlen(s_service) + 1;
345 /* Check and extract server's instance */
346 if ((strlen(ptr) + (ptr - (char *) cip->dat)) > cip->length) {
350 (void) strncpy(s_instance,ptr, sizeof(s_instance)-1);
351 s_instance[sizeof(s_instance)-1] = '\0';
352 ptr += strlen(s_instance) + 1;
354 /* Check and extract server's realm */
355 if ((strlen(ptr) + (ptr - (char *) cip->dat)) > cip->length) {
359 (void) strncpy(s_realm,ptr, sizeof(s_realm));
360 s_realm[sizeof(s_realm)-1] = '\0';
361 ptr += strlen(s_realm) + 1;
363 /* Ignore ticket lifetime, server key version */
366 /* Extract and check ticket length */
367 ticket_len = (unsigned char) *ptr++;
369 if ((ticket_len < 0) ||
370 ((ticket_len + (ptr - (char *) cip->dat)) > (int) cip->length)) {
374 /* Check returned server name, instance, and realm fields */
376 * 7/23/98 - Deleting realm check. This allows cell name to differ
380 if (strcmp(s_service, service) || strcmp(s_instance, instance) ||
381 strcmp(s_realm, realm)) {
383 if (strcmp(s_service, service) || strcmp(s_instance, instance)) {
385 /* not what we asked for: assume decryption failed */
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.
398 static char bogus_kaerror[100];
399 static int kaserver_map_error_code
404 if (code == 0 || code == KDC_GEN_ERR) {
406 if (sscanf(etext, "code =%u: ", &mapcode) == 1) {
408 strcpy(bogus_kaerror, etext);
409 *reasonP = bogus_kaerror;
420 static int krb_get_in_tkt_ext(user, instance, realm, service, sinstance, life,
421 key1, key2, ticketpp, ticketLenp, outKeyp, kvnop, expp, reasonp)
428 struct ktc_encryptionKey *key1, *key2;
431 struct ktc_encryptionKey *outKeyp;
437 KTEXT pkt = &pkt_st; /* Packet to KDC */
439 KTEXT rpkt = &rpkt_st; /* Returned packet */
441 KTEXT cip = &cip_st; /* Returned Ciphertext */
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 */
449 char s_name[SNAME_SZ];
450 char s_instance[INST_SZ];
458 unsigned long t_local;
460 afs_uint32 rep_err_code;
464 /* BUILD REQUEST PACKET */
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;
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);
481 (void) gettimeofday(&t_local,NULL);
483 t_local = time((void *) 0);
486 memcpy((char *)(pkt->dat+pkt->length), (char *)&(t_local), 4);
490 kerberos_life = DEFAULT_TKT_LIFE;
492 kerberos_life = time_to_life(0, life);
493 if (kerberos_life == 0) {
494 kerberos_life = DEFAULT_TKT_LIFE;
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);
503 pkt->length += 1 + strlen(sinstance);
507 /* SEND THE REQUEST AND RECEIVE THE RETURN PACKET */
509 if (kerror = send_to_kdc(pkt, rpkt)) {
513 /* check packet version of the returned packet */
514 if (pkt_version(rpkt) != KRB_PROT_VERSION)
517 /* Check byte order */
518 msg_byte_order = pkt_msg_type(rpkt) & 1;
520 if (msg_byte_order != HOST_BYTE_ORDER) {
524 switch (pkt_msg_type(rpkt) & ~1) {
525 case AUTH_MSG_KDC_REPLY:
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);
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);
542 /* Extract length. This will be re-extracted in check_response, below */
543 cip->length = pkt_clen(rpkt);
545 /* Length of zero seems to correspond to no principal (with kaserver) */
546 if (cip->length== 0) {
547 return (KERB_ERR_PRINCIPAL_UNKNOWN);
550 if ((cip->length < 0) || (cip->length > sizeof(cip->dat))) {
551 return(INTK_ERR); /* no appropriate error code
552 currently defined for INTK_ */
556 * Check the response against both possible keys, and use the one
559 if (check_response(rpkt, cip, service, sinstance, realm, key1) &&
560 check_response(rpkt, cip, service, sinstance, realm, key2)) {
565 * EXTRACT INFORMATION FROM RETURN PACKET
567 * Some of the fields, below are already checked for integrity by
570 ptr = (char *) cip->dat;
572 /* extract session key */
573 memcpy((char *)ses, ptr, 8);
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;
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;
586 /* extract server's realm */
587 (void) strncpy(rlm,ptr, sizeof(rlm));
588 rlm[sizeof(rlm)-1] = '\0';
589 ptr += strlen(rlm) + 1;
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];
598 /* extract ticket itself */
599 memcpy((char *)(tkt->dat), ptr, tkt->length);
602 /* check KDC time stamp */
603 memcpy((char *)&kdc_time, ptr, 4); /* Time (coarse) */
604 if (swap_bytes) swap_u_int32(&kdc_time);
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
614 /* copy out results; if *ticketpp is non-null, the caller has already
615 * allocated the buffer for us.
617 memcpy(outKeyp, ses, sizeof(struct ktc_encryptionKey));
618 if (*ticketpp == NULL) {
619 *ticketpp = malloc(tkt->length);
621 else if (tkt->length > (unsigned long) *ticketLenp) return -1;
622 *ticketLenp = tkt->length;
623 memcpy(*ticketpp, tkt->dat, tkt->length);
625 if (expp) *expp = life_to_time(kdc_time, (char) lifetime);
627 return(INTK_OK); /* this is zero */
632 * Copyright 1987, 1988 by the Massachusetts Institute of Technology.
634 * For copying and distribution information, please see the file
638 #define S_AD_SZ sizeof(struct sockaddr_in)
640 static int krb_debug;
642 /* CLIENT_KRB_TIMEOUT indicates the time to wait before
643 * retrying a server. It's defined in "krb.h".
645 static struct timeval timeout = { CLIENT_KRB_TIMEOUT, 0};
646 static char *prog = "dm";
650 * This file contains two routines, send_to_kdc() and send_recv().
651 * send_recv() is a static routine used by send_to_kdc().
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.
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.
667 * If an answer was received from a Kerberos host, KSUCCESS is
668 * returned. The following errors can be returned:
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
675 * - couldn't find any Kerberos host
677 * SKDC_RETRY - couldn't get an answer from any Kerberos server,
678 * after several retries
681 typedef struct krb_server {
682 struct krb_server *nextp;
683 struct sockaddr_in addr;
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;
690 static void krb_set_port
697 (struct sockaddr_in *server_list_p)
699 krb_server_t *krb_host_p;
701 krb_host_p = malloc(sizeof(krb_server_t));
703 /* add host to list */
704 krb_host_p->nextp = krb_hosts_p;
705 krb_hosts_p = krb_host_p;
708 /* copy in the data */
709 memcpy(&krb_host_p->addr, server_list_p, sizeof(struct sockaddr_in));
714 static send_to_kdc(pkt,rpkt)
722 struct sockaddr_in to;
723 int timeAvail, timePerIter, numIters;
725 memset((char *)&to, 0, sizeof(to));
726 if ((f = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
728 fprintf(stderr,"%s: Can't open socket\n", prog);
731 /* from now on, exit through rtn label for cleanup */
733 /* compute # of retries */
734 /* The SMB client seems to time out after 60 seconds. */
736 /* Leave ourselves some margin for fooling around
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;
745 if (numIters < 1) numIters = 1;
747 /* retry each host in sequence */
748 for (retry = 0; retry < numIters; ++retry) {
749 for(tsp = krb_hosts_p; tsp; tsp = tsp->nextp) {
751 to.sin_family = AF_INET;
752 to.sin_port = htons(((unsigned short)krb_udp_port));
753 if (send_recv(pkt, rpkt, f, &to)) {
763 (void) closesocket(f);
769 * try to send out and receive message.
770 * return 1 on success, 0 on failure
773 static send_recv(pkt,rpkt,f,_to)
777 struct sockaddr_in *_to;
780 struct sockaddr_in from;
786 if (_to->sin_family == AF_INET)
787 printf("Sending message to %s...",
788 inet_ntoa(_to->sin_addr));
790 printf("Sending message...");
791 (void) fflush(stdout);
793 if ((numsent = sendto(f,(char *)(pkt->dat), pkt->length, 0,
794 (struct sockaddr *)_to,
795 S_AD_SZ)) != (int) pkt->length) {
797 printf("sent only %d/%d\n",numsent, pkt->length);
801 printf("Sent\nWaiting for reply...");
802 (void) fflush(stdout);
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)) {
812 fprintf(stderr, "select failed: readfds=%x",
818 sin_size = sizeof(from);
819 if ((code = recvfrom(f, (char *)(rpkt->dat), sizeof(rpkt->dat), 0,
820 (struct sockaddr *)&from, &sin_size))
827 printf("received packet from %s\n", inet_ntoa(from.sin_addr));
835 * Copyright 1985, 1986, 1987, 1988 by the Massachusetts Institute
838 * For copying and distribution information, please see the file
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.
846 * See "krb_prot.h" for packet format.
849 static KTEXT pkt_cipher
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 */
857 /* And return the pointer */
863 * Copyright 1985, 1986, 1987, 1988 by the Massachusetts Institute
866 * For copying and distribution information, please see the file
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.
878 static int pkt_clen(KTEXT pkt)
882 /* Start of ticket list */
883 unsigned char *ptr = pkt_a_realm(pkt) + 10
884 + strlen((char *)pkt_a_realm(pkt));
886 /* Finally the length */
887 memcpy((char *)&temp, (char *)(++ptr), 2); /* alignment */
893 printf("Clen is %d\n", temp);
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. */
904 static void Andrew_StringToKey (str, cell, key)
906 char *cell; /* cell for password */
907 struct ktc_encryptionKey *key;
908 { char password[8+1]; /* crypt's limit is 8 chars anyway */
912 memset(key, 0, sizeof(struct ktc_encryptionKey));
914 strncpy (password, cell, 8);
915 passlen = strlen (str);
916 if (passlen > 8) passlen = 8;
918 for (i=0; i<passlen; i++)
919 password[i] ^= str[i];
922 if (password[i] == '\0') password[i] = 'X';
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));
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
931 { char *keybytes = (char *)key;
934 for (i = 0; i < 8; i++) {
935 temp = (unsigned int) keybytes[i];
936 keybytes[i] = (unsigned char) (temp << 1);
939 des_fixup_key_parity ((unsigned char *) key);
943 static void StringToKey (str, cell, key)
945 char *cell; /* cell for password */
946 struct ktc_encryptionKey *key;
947 { des_key_schedule schedule;
950 char password[BUFSIZ];
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);
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);
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);
970 des_fixup_key_parity ((char *) key);