version 1.8, 2000/10/12 09:59:19 |
version 1.8.2.3, 2001/03/21 19:46:31 |
|
|
RCSID("$OpenBSD$"); |
RCSID("$OpenBSD$"); |
|
|
#include <openssl/bn.h> |
#include <openssl/bn.h> |
#include <openssl/dsa.h> |
|
#include <openssl/rsa.h> |
|
#include <openssl/evp.h> |
#include <openssl/evp.h> |
|
|
|
#ifdef KRB4 |
|
#include <krb.h> |
|
#endif |
|
#ifdef AFS |
|
#include <kafs.h> |
|
#include "radix.h" |
|
#endif |
|
|
|
#include "ssh.h" |
|
#include "ssh1.h" |
#include "xmalloc.h" |
#include "xmalloc.h" |
#include "rsa.h" |
#include "rsa.h" |
#include "ssh.h" |
|
#include "buffer.h" |
#include "buffer.h" |
#include "packet.h" |
#include "packet.h" |
#include "mpaux.h" |
#include "mpaux.h" |
#include "uidswap.h" |
#include "uidswap.h" |
|
#include "log.h" |
#include "readconf.h" |
#include "readconf.h" |
#include "key.h" |
#include "key.h" |
#include "authfd.h" |
#include "authfd.h" |
#include "sshconnect.h" |
#include "sshconnect.h" |
#include "authfile.h" |
#include "authfile.h" |
|
#include "readpass.h" |
|
#include "cipher.h" |
|
#include "canohost.h" |
|
|
/* Session id for the current session. */ |
/* Session id for the current session. */ |
unsigned char session_id[16]; |
u_char session_id[16]; |
unsigned int supported_authentications = 0; |
u_int supported_authentications = 0; |
|
|
extern Options options; |
extern Options options; |
extern char *__progname; |
extern char *__progname; |
|
|
* authenticate using the agent. |
* authenticate using the agent. |
*/ |
*/ |
int |
int |
try_agent_authentication() |
try_agent_authentication(void) |
{ |
{ |
int type; |
int type; |
char *comment; |
char *comment; |
AuthenticationConnection *auth; |
AuthenticationConnection *auth; |
unsigned char response[16]; |
u_char response[16]; |
unsigned int i; |
u_int i; |
int plen, clen; |
int plen, clen; |
Key *key; |
Key *key; |
BIGNUM *challenge; |
BIGNUM *challenge; |
|
|
return 0; |
return 0; |
|
|
challenge = BN_new(); |
challenge = BN_new(); |
key = key_new(KEY_RSA); |
|
|
|
/* Loop through identities served by the agent. */ |
/* Loop through identities served by the agent. */ |
for (key = ssh_get_first_identity(auth, &comment, 1); |
for (key = ssh_get_first_identity(auth, &comment, 1); |
|
|
|
|
/* The server returns success if it accepted the authentication. */ |
/* The server returns success if it accepted the authentication. */ |
if (type == SSH_SMSG_SUCCESS) { |
if (type == SSH_SMSG_SUCCESS) { |
|
ssh_close_authentication_connection(auth); |
BN_clear_free(challenge); |
BN_clear_free(challenge); |
debug("RSA authentication accepted by server."); |
debug("RSA authentication accepted by server."); |
return 1; |
return 1; |
|
|
packet_disconnect("Protocol error waiting RSA auth response: %d", |
packet_disconnect("Protocol error waiting RSA auth response: %d", |
type); |
type); |
} |
} |
|
ssh_close_authentication_connection(auth); |
BN_clear_free(challenge); |
BN_clear_free(challenge); |
debug("RSA authentication using agent refused."); |
debug("RSA authentication using agent refused."); |
return 0; |
return 0; |
|
|
void |
void |
respond_to_rsa_challenge(BIGNUM * challenge, RSA * prv) |
respond_to_rsa_challenge(BIGNUM * challenge, RSA * prv) |
{ |
{ |
unsigned char buf[32], response[16]; |
u_char buf[32], response[16]; |
MD5_CTX md; |
MD5_CTX md; |
int i, len; |
int i, len; |
|
|
/* Decrypt the challenge using the private key. */ |
/* Decrypt the challenge using the private key. */ |
rsa_private_decrypt(challenge, challenge, prv); |
/* XXX think about Bleichenbacher, too */ |
|
if (rsa_private_decrypt(challenge, challenge, prv) <= 0) |
|
packet_disconnect( |
|
"respond_to_rsa_challenge: rsa_private_decrypt failed"); |
|
|
/* Compute the response. */ |
/* Compute the response. */ |
/* The response is MD5 of decrypted challenge plus session id. */ |
/* The response is MD5 of decrypted challenge plus session id. */ |
len = BN_num_bytes(challenge); |
len = BN_num_bytes(challenge); |
if (len <= 0 || len > sizeof(buf)) |
if (len <= 0 || len > sizeof(buf)) |
packet_disconnect("respond_to_rsa_challenge: bad challenge length %d", |
packet_disconnect( |
len); |
"respond_to_rsa_challenge: bad challenge length %d", len); |
|
|
memset(buf, 0, sizeof(buf)); |
memset(buf, 0, sizeof(buf)); |
BN_bn2bin(challenge, buf + sizeof(buf) - len); |
BN_bn2bin(challenge, buf + sizeof(buf) - len); |
|
|
int plen, clen; |
int plen, clen; |
|
|
/* Try to load identification for the authentication key. */ |
/* Try to load identification for the authentication key. */ |
public = key_new(KEY_RSA); |
public = key_new(KEY_RSA1); |
if (!load_public_key(authfile, public, &comment)) { |
if (!load_public_key(authfile, public, &comment)) { |
key_free(public); |
key_free(public); |
/* Could not load it. Fail. */ |
/* Could not load it. Fail. */ |
|
|
|
|
debug("Received RSA challenge from server."); |
debug("Received RSA challenge from server."); |
|
|
private = key_new(KEY_RSA); |
private = key_new(KEY_RSA1); |
/* |
/* |
* Load the private key. Try first with empty passphrase; if it |
* Load the private key. Try first with empty passphrase; if it |
* fails, ask for a passphrase. |
* fails, ask for a passphrase. |
|
|
/* Expect the server to reject it... */ |
/* Expect the server to reject it... */ |
packet_read_expect(&plen, SSH_SMSG_FAILURE); |
packet_read_expect(&plen, SSH_SMSG_FAILURE); |
xfree(comment); |
xfree(comment); |
|
key_free(private); |
|
BN_clear_free(challenge); |
return 0; |
return 0; |
} |
} |
/* Destroy the passphrase. */ |
/* Destroy the passphrase. */ |
|
|
|
|
#ifdef KRB4 |
#ifdef KRB4 |
int |
int |
try_kerberos_authentication() |
try_kerberos_authentication(void) |
{ |
{ |
KTEXT_ST auth; /* Kerberos data */ |
KTEXT_ST auth; /* Kerberos data */ |
char *reply; |
char *reply; |
|
|
if (stat(tkt_string(), &st) < 0) |
if (stat(tkt_string(), &st) < 0) |
return 0; |
return 0; |
|
|
strncpy(inst, (char *) krb_get_phost(get_canonical_hostname()), INST_SZ); |
strncpy(inst, (char *) krb_get_phost(get_canonical_hostname(1)), INST_SZ); |
|
|
realm = (char *) krb_realmofhost(get_canonical_hostname()); |
realm = (char *) krb_realmofhost(get_canonical_hostname(1)); |
if (!realm) { |
if (!realm) { |
debug("Kerberos V4: no realm for %s", get_canonical_hostname()); |
debug("Kerberos V4: no realm for %s", get_canonical_hostname(1)); |
return 0; |
return 0; |
} |
} |
/* This can really be anything. */ |
/* This can really be anything. */ |
|
|
debug("Kerberos V4 authentication accepted."); |
debug("Kerberos V4 authentication accepted."); |
|
|
/* Get server's response. */ |
/* Get server's response. */ |
reply = packet_get_string((unsigned int *) &auth.length); |
reply = packet_get_string((u_int *) &auth.length); |
memcpy(auth.dat, reply, auth.length); |
memcpy(auth.dat, reply, auth.length); |
xfree(reply); |
xfree(reply); |
|
|
|
|
|
|
#ifdef AFS |
#ifdef AFS |
int |
int |
send_kerberos_tgt() |
send_kerberos_tgt(void) |
{ |
{ |
CREDENTIALS *creds; |
CREDENTIALS *creds; |
char pname[ANAME_SZ], pinst[INST_SZ], prealm[REALM_SZ]; |
char pname[ANAME_SZ], pinst[INST_SZ], prealm[REALM_SZ]; |
|
|
debug("Kerberos V4 ticket expired: %s", TKT_FILE); |
debug("Kerberos V4 ticket expired: %s", TKT_FILE); |
return 0; |
return 0; |
} |
} |
creds_to_radix(creds, (unsigned char *)buffer, sizeof buffer); |
creds_to_radix(creds, (u_char *)buffer, sizeof buffer); |
xfree(creds); |
xfree(creds); |
|
|
packet_start(SSH_CMSG_HAVE_KERBEROS_TGT); |
packet_start(SSH_CMSG_HAVE_KERBEROS_TGT); |
|
|
p = buf; |
p = buf; |
|
|
/* Get secret token. */ |
/* Get secret token. */ |
memcpy(&creds.ticket_st.length, p, sizeof(unsigned int)); |
memcpy(&creds.ticket_st.length, p, sizeof(u_int)); |
if (creds.ticket_st.length > MAX_KTXT_LEN) |
if (creds.ticket_st.length > MAX_KTXT_LEN) |
break; |
break; |
p += sizeof(unsigned int); |
p += sizeof(u_int); |
memcpy(creds.ticket_st.dat, p, creds.ticket_st.length); |
memcpy(creds.ticket_st.dat, p, creds.ticket_st.length); |
p += creds.ticket_st.length; |
p += creds.ticket_st.length; |
|
|
|
|
creds.pinst[0] = '\0'; |
creds.pinst[0] = '\0'; |
|
|
/* Encode token, ship it off. */ |
/* Encode token, ship it off. */ |
if (creds_to_radix(&creds, (unsigned char*) buffer, sizeof buffer) <= 0) |
if (creds_to_radix(&creds, (u_char *) buffer, sizeof buffer) <= 0) |
break; |
break; |
packet_start(SSH_CMSG_HAVE_AFS_TOKEN); |
packet_start(SSH_CMSG_HAVE_AFS_TOKEN); |
packet_put_string(buffer, strlen(buffer)); |
packet_put_string(buffer, strlen(buffer)); |
|
|
* Note that the client code is not tied to s/key or TIS. |
* Note that the client code is not tied to s/key or TIS. |
*/ |
*/ |
int |
int |
try_skey_authentication() |
try_challenge_reponse_authentication(void) |
{ |
{ |
int type, i; |
int type, i; |
int payload_len; |
int payload_len; |
unsigned int clen; |
u_int clen; |
|
char prompt[1024]; |
char *challenge, *response; |
char *challenge, *response; |
|
|
debug("Doing skey authentication."); |
debug("Doing challenge reponse authentication."); |
|
|
/* request a challenge */ |
|
packet_start(SSH_CMSG_AUTH_TIS); |
|
packet_send(); |
|
packet_write_wait(); |
|
|
|
type = packet_read(&payload_len); |
|
if (type != SSH_SMSG_FAILURE && |
|
type != SSH_SMSG_AUTH_TIS_CHALLENGE) { |
|
packet_disconnect("Protocol error: got %d in response " |
|
"to skey-auth", type); |
|
} |
|
if (type != SSH_SMSG_AUTH_TIS_CHALLENGE) { |
|
debug("No challenge for skey authentication."); |
|
return 0; |
|
} |
|
challenge = packet_get_string(&clen); |
|
packet_integrity_check(payload_len, (4 + clen), type); |
|
if (options.cipher == SSH_CIPHER_NONE) |
|
log("WARNING: Encryption is disabled! " |
|
"Reponse will be transmitted in clear text."); |
|
fprintf(stderr, "%s\n", challenge); |
|
xfree(challenge); |
|
fflush(stderr); |
|
for (i = 0; i < options.number_of_password_prompts; i++) { |
for (i = 0; i < options.number_of_password_prompts; i++) { |
|
/* request a challenge */ |
|
packet_start(SSH_CMSG_AUTH_TIS); |
|
packet_send(); |
|
packet_write_wait(); |
|
|
|
type = packet_read(&payload_len); |
|
if (type != SSH_SMSG_FAILURE && |
|
type != SSH_SMSG_AUTH_TIS_CHALLENGE) { |
|
packet_disconnect("Protocol error: got %d in response " |
|
"to SSH_CMSG_AUTH_TIS", type); |
|
} |
|
if (type != SSH_SMSG_AUTH_TIS_CHALLENGE) { |
|
debug("No challenge."); |
|
return 0; |
|
} |
|
challenge = packet_get_string(&clen); |
|
packet_integrity_check(payload_len, (4 + clen), type); |
|
snprintf(prompt, sizeof prompt, "%s%s", challenge, |
|
strchr(challenge, '\n') ? "" : "\nResponse: "); |
|
xfree(challenge); |
if (i != 0) |
if (i != 0) |
error("Permission denied, please try again."); |
error("Permission denied, please try again."); |
response = read_passphrase("Response: ", 0); |
if (options.cipher == SSH_CIPHER_NONE) |
|
log("WARNING: Encryption is disabled! " |
|
"Reponse will be transmitted in clear text."); |
|
response = read_passphrase(prompt, 0); |
|
if (strcmp(response, "") == 0) { |
|
xfree(response); |
|
break; |
|
} |
packet_start(SSH_CMSG_AUTH_TIS_RESPONSE); |
packet_start(SSH_CMSG_AUTH_TIS_RESPONSE); |
packet_put_string(response, strlen(response)); |
ssh_put_password(response); |
memset(response, 0, strlen(response)); |
memset(response, 0, strlen(response)); |
xfree(response); |
xfree(response); |
packet_send(); |
packet_send(); |
|
|
return 1; |
return 1; |
if (type != SSH_SMSG_FAILURE) |
if (type != SSH_SMSG_FAILURE) |
packet_disconnect("Protocol error: got %d in response " |
packet_disconnect("Protocol error: got %d in response " |
"to skey-auth-reponse", type); |
"to SSH_CMSG_AUTH_TIS_RESPONSE", type); |
} |
} |
/* failure */ |
/* failure */ |
return 0; |
return 0; |
|
|
error("Permission denied, please try again."); |
error("Permission denied, please try again."); |
password = read_passphrase(prompt, 0); |
password = read_passphrase(prompt, 0); |
packet_start(SSH_CMSG_AUTH_PASSWORD); |
packet_start(SSH_CMSG_AUTH_PASSWORD); |
packet_put_string(password, strlen(password)); |
ssh_put_password(password); |
memset(password, 0, strlen(password)); |
memset(password, 0, strlen(password)); |
xfree(password); |
xfree(password); |
packet_send(); |
packet_send(); |
|
|
Key k; |
Key k; |
int bits, rbits; |
int bits, rbits; |
int ssh_cipher_default = SSH_CIPHER_3DES; |
int ssh_cipher_default = SSH_CIPHER_3DES; |
unsigned char session_key[SSH_SESSION_KEY_LENGTH]; |
u_char session_key[SSH_SESSION_KEY_LENGTH]; |
unsigned char cookie[8]; |
u_char cookie[8]; |
unsigned int supported_ciphers; |
u_int supported_ciphers; |
unsigned int server_flags, client_flags; |
u_int server_flags, client_flags; |
int payload_len, clen, sum_len = 0; |
int payload_len, clen, sum_len = 0; |
u_int32_t rand = 0; |
u_int32_t rand = 0; |
|
|
|
|
packet_integrity_check(payload_len, |
packet_integrity_check(payload_len, |
8 + 4 + sum_len + 0 + 4 + 0 + 0 + 4 + 4 + 4, |
8 + 4 + sum_len + 0 + 4 + 0 + 0 + 4 + 4 + 4, |
SSH_SMSG_PUBLIC_KEY); |
SSH_SMSG_PUBLIC_KEY); |
k.type = KEY_RSA; |
k.type = KEY_RSA1; |
k.rsa = host_key; |
k.rsa = host_key; |
check_host_key(host, hostaddr, &k, |
check_host_key(host, hostaddr, &k, |
options.user_hostfile, options.system_hostfile); |
options.user_hostfile, options.system_hostfile); |
|
|
RSA_free(public_key); |
RSA_free(public_key); |
RSA_free(host_key); |
RSA_free(host_key); |
|
|
if (options.cipher == SSH_CIPHER_ILLEGAL) { |
if (options.cipher == SSH_CIPHER_NOT_SET) { |
|
if (cipher_mask_ssh1(1) & supported_ciphers & (1 << ssh_cipher_default)) |
|
options.cipher = ssh_cipher_default; |
|
} else if (options.cipher == SSH_CIPHER_ILLEGAL || |
|
!(cipher_mask_ssh1(1) & (1 << options.cipher))) { |
log("No valid SSH1 cipher, using %.100s instead.", |
log("No valid SSH1 cipher, using %.100s instead.", |
cipher_name(ssh_cipher_default)); |
cipher_name(ssh_cipher_default)); |
options.cipher = ssh_cipher_default; |
options.cipher = ssh_cipher_default; |
} else if (options.cipher == SSH_CIPHER_NOT_SET) { |
|
if (cipher_mask_ssh1(1) & supported_ciphers & (1 << ssh_cipher_default)) |
|
options.cipher = ssh_cipher_default; |
|
} |
} |
/* Check that the selected cipher is supported. */ |
/* Check that the selected cipher is supported. */ |
if (!(supported_ciphers & (1 << options.cipher))) |
if (!(supported_ciphers & (1 << options.cipher))) |
|
|
*/ |
*/ |
void |
void |
ssh_userauth( |
ssh_userauth( |
const char* local_user, |
const char *local_user, |
const char* server_user, |
const char *server_user, |
char *host, |
char *host, |
int host_key_valid, RSA *own_host_key) |
int host_key_valid, RSA *own_host_key) |
{ |
{ |
|
|
|
|
/* Try RSA authentication for each identity. */ |
/* Try RSA authentication for each identity. */ |
for (i = 0; i < options.num_identity_files; i++) |
for (i = 0; i < options.num_identity_files; i++) |
if (try_rsa_authentication(options.identity_files[i])) |
if (options.identity_keys[i] != NULL && |
|
options.identity_keys[i]->type == KEY_RSA1 && |
|
try_rsa_authentication(options.identity_files[i])) |
return; |
return; |
} |
} |
/* Try skey authentication if the server supports it. */ |
/* Try challenge response authentication if the server supports it. */ |
if ((supported_authentications & (1 << SSH_AUTH_TIS)) && |
if ((supported_authentications & (1 << SSH_AUTH_TIS)) && |
options.skey_authentication && !options.batch_mode) { |
options.challenge_reponse_authentication && !options.batch_mode) { |
if (try_skey_authentication()) |
if (try_challenge_reponse_authentication()) |
return; |
return; |
} |
} |
/* Try password authentication if the server supports it. */ |
/* Try password authentication if the server supports it. */ |
|
|
options.password_authentication && !options.batch_mode) { |
options.password_authentication && !options.batch_mode) { |
char prompt[80]; |
char prompt[80]; |
|
|
snprintf(prompt, sizeof(prompt), "%.30s@%.40s's password: ", |
snprintf(prompt, sizeof(prompt), "%.30s@%.128s's password: ", |
server_user, host); |
server_user, host); |
if (try_password_authentication(prompt)) |
if (try_password_authentication(prompt)) |
return; |
return; |