$NetBSD: patch-bk,v 1.2 2000/03/20 02:25:57 itojun Exp $ --- sshconnect.c- Wed May 12 20:19:29 1999 +++ sshconnect.c Mon Mar 20 09:55:37 2000 @@ -215,7 +215,6 @@ #include "userfile.h" #include "emulate.h" -#ifdef KERBEROS #ifdef KRB5 #include @@ -223,7 +222,19 @@ krb5_context ssh_context = 0; krb5_auth_context auth_context = 0; #endif /* KRB5 */ -#endif /* KERBEROS */ + +#ifdef KRB4 +#include +#ifdef AFS +#if defined(HAVE_SYS_IOCTL_H) && SunOS != 4 +#include +#endif +#ifdef HAVE_SYS_FILIO_H +#include +#endif +#include +#endif /* AFS */ +#endif /* KRB4 */ /* Session id for the current session. */ unsigned char session_id[16]; @@ -932,10 +943,9 @@ return 0; } -#ifdef KERBEROS +#ifdef KRB5 int try_kerberos_authentication(void) { -#ifdef KRB5 char *remotehost; krb5_data auth; krb5_error_code r; @@ -1084,15 +1094,118 @@ krb5_free_ap_rep_enc_part(ssh_context, repl); return(ret_stat); +} #endif /* KRB5 */ + +#ifdef KRB4 +int try_kerberos_authentication() +{ + KTEXT_ST auth; /* Kerberos data */ + char *reply; + char inst[INST_SZ]; + char *realm; + char *service; + CREDENTIALS cred; + int r, type; + Key_schedule schedule; + u_long checksum, cksum; + MSG_DAT msg_data; + struct sockaddr_in local, foreign; + struct stat st; + + /* Don't do anything if we don't have any tickets. */ + if (stat(tkt_string(), &st) < 0) return 0; + + strncpy(inst, (char *) krb_get_phost(get_canonical_hostname()), INST_SZ); + + realm = (char *)krb_realmofhost(get_canonical_hostname()); + if (!realm) { + debug("Kerberos V4: no realm for %s", get_canonical_hostname()); + return 0; + } + /* This can really be anything. */ + checksum = (u_long) getpid(); + + if (r = krb_mk_req(&auth, KRB4_SERVICE_NAME, inst, realm, checksum)) { + debug("Kerberos V4 krb_mk_req failed: %s", krb_err_txt[r]); + return 0; + } + /* Get session key to decrypt the server's reply with. */ + if (r = krb_get_cred(KRB4_SERVICE_NAME, inst, realm, &cred)) { + debug("get_cred failed: %s", krb_err_txt[r]); + return 0; + } + des_key_sched((des_cblock *)cred.session, schedule); + + /* Send authentication info to server. */ + packet_start(SSH_CMSG_AUTH_KERBEROS); + packet_put_string((char *)auth.dat, auth.length); + packet_send(); + packet_write_wait(); + + /* zero the buffer */ + (void) memset(auth.dat, 0, MAX_KTXT_LEN); + + r = sizeof(local); + memset(&local, 0, sizeof(local)); + if (getsockname(packet_get_connection_in(), + (struct sockaddr *) &local, &r) < 0) + debug("getsockname failed: %.100s", strerror(errno)); + + r = sizeof(foreign); + memset(&foreign, 0, sizeof(foreign)); + if (getpeername(packet_get_connection_in(), + (struct sockaddr *)&foreign, &r) < 0) + debug("getpeername failed: %.100s", strerror(errno)); + + /* Get server reply. */ + type = packet_read(); + switch(type) { + + case SSH_SMSG_FAILURE: /* Should really be SSH_SMSG_AUTH_KERBEROS_FAILURE */ + debug("Kerberos V4 authentication failed."); + return 0; + break; + + case SSH_SMSG_AUTH_KERBEROS_RESPONSE: /* SSH_SMSG_AUTH_KERBEROS_SUCCESS */ + debug("Kerberos V4 authentication accepted."); + + /* Get server's response. */ + reply = packet_get_string((unsigned int *)&auth.length); + memcpy(auth.dat, reply, auth.length); + xfree(reply); + + /* If his response isn't properly encrypted with the session key, + and the decrypted checksum fails to match, he's bogus. Bail out. */ + if (r = krb_rd_priv(auth.dat, auth.length, schedule, &cred.session, + &foreign, &local, &msg_data)) { + debug("Kerberos V4 krb_rd_priv failed: %s", krb_err_txt[r]); + packet_disconnect("Kerberos V4 challenge failed!"); + } + /* fetch the (incremented) checksum that we supplied in the request */ + (void)memcpy((char *)&cksum, (char *)msg_data.app_data, sizeof(cksum)); + cksum = ntohl(cksum); + + /* If it matches, we're golden. */ + if (cksum == checksum + 1) { + debug("Kerberos V4 challenge successful."); + return 1; + } + else + packet_disconnect("Kerberos V4 challenge failed!"); + break; + + default: + packet_disconnect("Protocol error on Kerberos V4 response: %d", type); + } } -#endif /* KERBEROS */ +#endif /* KRB4 */ + -#ifdef KERBEROS_TGT_PASSING /* Forward our local Kerberos tgt to the server. */ +#ifdef KRB5 int send_kerberos_tgt(void) { -#ifdef KRB5 char *remotehost; krb5_principal client; krb5_principal server; @@ -1172,22 +1285,117 @@ krb5_free_principal(ssh_context, client); krb5_free_principal(ssh_context, server); - type = packet_read(); - if (type == SSH_SMSG_SUCCESS) - { - debug("Kerberos V5 TGT passing was successful."); - return 1; - } - else - if (type != SSH_SMSG_FAILURE) - packet_disconnect("Protocol error on Kerberos tgt response: %d", type); - else - debug("Kerberos V5 TGT passing failed."); - - return 0; + return 1; +} #endif /* KRB5 */ + +#ifdef AFS +int send_kerberos_tgt() +{ + CREDENTIALS *creds; + char pname[ANAME_SZ], pinst[INST_SZ], prealm[REALM_SZ]; + int r, type; + unsigned char buffer[8192]; + struct stat st; + + /* Don't do anything if we don't have any tickets. */ + if (stat(tkt_string(), &st) < 0) return 0; + + creds = xmalloc(sizeof(CREDENTIALS)); + + if ((r=krb_get_tf_fullname(TKT_FILE,pname,pinst,prealm)) != KSUCCESS) { + debug("Kerberos V4 tf_fullname failed: %s",krb_err_txt[r]); + return 0; + } + if ((r=krb_get_cred("krbtgt", prealm, prealm, creds)) != GC_OK) { + debug("Kerberos V4 get_cred failed: %s", krb_err_txt[r]); + return 0; + } + if (time(0) > +#ifdef HAVE_KRB_LIFE_TO_TIME + (unsigned long)krb_life_to_time(creds->issue_date, creds->lifetime)) { +#else + (creds->issue_date + ((unsigned char)creds->lifetime * 5 * 60))) { +#endif /* HAVE_KRB_LIFE_TO_TIME */ + debug("Kerberos V4 ticket expired: %s", TKT_FILE); + return 0; + } + + creds_to_radix(creds, buffer); + xfree(creds); + + packet_start(SSH_CMSG_HAVE_KERBEROS_TGT); + packet_put_string((char *)buffer, strlen(buffer)); + packet_send(); + packet_write_wait(); + + return 1; } -#endif /* KERBEROS_TGT_PASSING */ + +/* Forwards our AFS tokens to the server. */ +void send_afs_tokens(void) +{ + CREDENTIALS creds; + struct ViceIoctl parms; + struct ClearToken ct; + int i, type; + int len; + char buf[2048], *p, *server_cell; + unsigned char buffer[8192]; + + /* Move over ktc_GetToken, here's something leaner. */ + for (i = 0; i < 100; i++) { /* just in case */ + parms.in = (char *)&i; + parms.in_size = sizeof(i); + parms.out = buf; + parms.out_size = sizeof(buf); + if (k_pioctl(0, VIOCGETTOK, &parms, 0) != 0) break; + p = buf; + + /* Get secret token. */ + memcpy(&creds.ticket_st.length, p, sizeof(unsigned int)); + if (creds.ticket_st.length > MAX_KTXT_LEN) break; + p += sizeof(unsigned int); + memcpy(creds.ticket_st.dat, p, creds.ticket_st.length); + p += creds.ticket_st.length; + + /* Get clear token. */ + memcpy(&len, p, sizeof(len)); + if (len != sizeof(struct ClearToken)) break; + p += sizeof(len); + memcpy(&ct, p, len); + p += len; + p += sizeof(len); /* primary flag */ + server_cell = p; + + /* Flesh out our credentials. */ + strcpy(creds.service, "afs"); + creds.instance[0] = '\0'; + strncpy(creds.realm, server_cell, REALM_SZ); + memcpy(creds.session, ct.HandShakeKey, DES_KEY_SZ); + creds.issue_date = ct.BeginTimestamp; + creds.lifetime = krb_time_to_life(creds.issue_date, ct.EndTimestamp); + creds.kvno = ct.AuthHandle; + snprintf(creds.pname, sizeof(creds.pname), "AFS ID %d", ct.ViceId); + creds.pinst[0] = '\0'; + + /* Encode token, ship it off. */ + if (!creds_to_radix(&creds, buffer)) break; + packet_start(SSH_CMSG_HAVE_AFS_TOKEN); + packet_put_string((char *)buffer, strlen(buffer)); + packet_send(); + packet_write_wait(); + + /* Roger, Roger. Clearance, Clarence. What's your vector, Victor? */ + type = packet_read(); + + if (type == SSH_SMSG_FAILURE) + debug("AFS token for cell %s rejected.", server_cell); + else if (type != SSH_SMSG_SUCCESS) + packet_disconnect("Protocol error on AFS token response: %d", type); + } +} +#endif /* AFS */ /* Waits for the server identification string, and sends our own identification string. */ @@ -1285,14 +1493,12 @@ unsigned char check_bytes[8]; unsigned int supported_ciphers, supported_authentications, protocol_flags; HostStatus host_status; -#ifdef KERBEROS #ifdef KRB5 char *kuser; krb5_ccache ccache; krb5_error_code problem; krb5_principal client; -#endif -#endif +#endif /* KRB5 */ /* Convert the user-supplied hostname into all lowercase. */ host = xstrdup(orighost); @@ -1595,7 +1801,6 @@ debug("Received encrypted confirmation."); -#ifdef KERBEROS #ifdef KRB5 if (!ssh_context) { @@ -1629,7 +1834,6 @@ debug("Kerberos V5: could not get default ccache."); } #endif /* KRB5 */ -#endif /* KERBEROS */ /* Send the name of the user to log in as on the server. */ packet_start(SSH_CMSG_USER); @@ -1647,24 +1851,39 @@ packet_disconnect("Protocol error: got %d in response to SSH_CMSG_USER", type); -#ifdef KERBEROS_TGT_PASSING +#if defined(KRB5) || defined(AFS) /* Try Kerberos tgt passing if the server supports it. */ if ((supported_authentications & (1 << SSH_PASS_KERBEROS_TGT)) && options->kerberos_tgt_passing) { if (options->cipher == SSH_CIPHER_NONE) log_msg("WARNING: Encryption is disabled! Ticket will be transmitted in the clear!"); - (void)send_kerberos_tgt(); + if (send_kerberos_tgt()) + { + type = packet_read(); + if (type == SSH_SMSG_FAILURE) + debug("Kerberos TGT passing failed."); + else if (type != SSH_SMSG_SUCCESS) + packet_disconnect("Protocol error on Kerberos tgt response: %d", type); + } } -#endif /* KERBEROS_TGT_PASSING */ +#endif /* KRB5 || AFS */ + +#ifdef AFS + /* Try AFS token passing if the server supports it. */ + if ((supported_authentications & (1 << SSH_PASS_AFS_TOKEN)) && + options->afs_token_passing && k_hasafs()) { + if (options->cipher == SSH_CIPHER_NONE) + log_msg("WARNING: Encryption is disabled! Token will be transmitted in the clear!"); + send_afs_tokens(); + } +#endif /* AFS */ -#ifdef KERBEROS -#ifdef KRB5 +#if defined(KRB4) || defined(KRB5) if ((supported_authentications & (1 << SSH_AUTH_KERBEROS)) && options->kerberos_authentication) { - debug("Trying Kerberos V5 authentication."); -#endif + debug("Trying Kerberos authentication."); if (try_kerberos_authentication()) { /* The server should respond with success or failure. */ type = packet_read(); @@ -1673,10 +1892,8 @@ if (type != SSH_SMSG_FAILURE) packet_disconnect("Protocol error: got %d in response to Kerberos auth", type); } -#ifdef KRB5 } -#endif -#endif /* KERBEROS */ +#endif /* KRB4 || KRB5 */ /* Use rhosts authentication if running in privileged socket and we do not wish to remain anonymous. */