version 1.169, 2008/11/01 04:50:08 |
version 1.170, 2008/11/04 08:22:13 |
|
|
/* $OpenBSD$ */ |
/* $OpenBSD$ */ |
/* |
/* |
* Copyright (c) 2000 Markus Friedl. All rights reserved. |
* Copyright (c) 2000 Markus Friedl. All rights reserved. |
|
* Copyright (c) 2008 Damien Miller. All rights reserved. |
* |
* |
* Redistribution and use in source and binary forms, with or without |
* Redistribution and use in source and binary forms, with or without |
* modification, are permitted provided that the following conditions |
* modification, are permitted provided that the following conditions |
|
|
#include "msg.h" |
#include "msg.h" |
#include "pathnames.h" |
#include "pathnames.h" |
#include "uidswap.h" |
#include "uidswap.h" |
|
#include "jpake.h" |
|
|
#ifdef GSSAPI |
#ifdef GSSAPI |
#include "ssh-gss.h" |
#include "ssh-gss.h" |
|
|
struct Authmethod { |
struct Authmethod { |
char *name; /* string to compare against server's list */ |
char *name; /* string to compare against server's list */ |
int (*userauth)(Authctxt *authctxt); |
int (*userauth)(Authctxt *authctxt); |
|
void (*cleanup)(Authctxt *authctxt); |
int *enabled; /* flag in option struct that enables method */ |
int *enabled; /* flag in option struct that enables method */ |
int *batch_flag; /* flag in option struct that disables method */ |
int *batch_flag; /* flag in option struct that disables method */ |
}; |
}; |
|
|
void input_userauth_info_req(int, u_int32_t, void *); |
void input_userauth_info_req(int, u_int32_t, void *); |
void input_userauth_pk_ok(int, u_int32_t, void *); |
void input_userauth_pk_ok(int, u_int32_t, void *); |
void input_userauth_passwd_changereq(int, u_int32_t, void *); |
void input_userauth_passwd_changereq(int, u_int32_t, void *); |
|
void input_userauth_jpake_server_step1(int, u_int32_t, void *); |
|
void input_userauth_jpake_server_step2(int, u_int32_t, void *); |
|
void input_userauth_jpake_server_confirm(int, u_int32_t, void *); |
|
|
int userauth_none(Authctxt *); |
int userauth_none(Authctxt *); |
int userauth_pubkey(Authctxt *); |
int userauth_pubkey(Authctxt *); |
int userauth_passwd(Authctxt *); |
int userauth_passwd(Authctxt *); |
int userauth_kbdint(Authctxt *); |
int userauth_kbdint(Authctxt *); |
int userauth_hostbased(Authctxt *); |
int userauth_hostbased(Authctxt *); |
|
int userauth_jpake(Authctxt *); |
|
|
|
void userauth_jpake_cleanup(Authctxt *); |
|
|
#ifdef GSSAPI |
#ifdef GSSAPI |
int userauth_gssapi(Authctxt *authctxt); |
int userauth_gssapi(Authctxt *authctxt); |
void input_gssapi_response(int type, u_int32_t, void *); |
void input_gssapi_response(int type, u_int32_t, void *); |
|
|
#ifdef GSSAPI |
#ifdef GSSAPI |
{"gssapi-with-mic", |
{"gssapi-with-mic", |
userauth_gssapi, |
userauth_gssapi, |
|
NULL, |
&options.gss_authentication, |
&options.gss_authentication, |
NULL}, |
NULL}, |
#endif |
#endif |
{"hostbased", |
{"hostbased", |
userauth_hostbased, |
userauth_hostbased, |
|
NULL, |
&options.hostbased_authentication, |
&options.hostbased_authentication, |
NULL}, |
NULL}, |
{"publickey", |
{"publickey", |
userauth_pubkey, |
userauth_pubkey, |
|
NULL, |
&options.pubkey_authentication, |
&options.pubkey_authentication, |
NULL}, |
NULL}, |
|
#ifdef JPAKE |
|
{"jpake-01@openssh.com", |
|
userauth_jpake, |
|
userauth_jpake_cleanup, |
|
&options.zero_knowledge_password_authentication, |
|
&options.batch_mode}, |
|
#endif |
{"keyboard-interactive", |
{"keyboard-interactive", |
userauth_kbdint, |
userauth_kbdint, |
|
NULL, |
&options.kbd_interactive_authentication, |
&options.kbd_interactive_authentication, |
&options.batch_mode}, |
&options.batch_mode}, |
{"password", |
{"password", |
userauth_passwd, |
userauth_passwd, |
|
NULL, |
&options.password_authentication, |
&options.password_authentication, |
&options.batch_mode}, |
&options.batch_mode}, |
{"none", |
{"none", |
userauth_none, |
userauth_none, |
NULL, |
NULL, |
|
NULL, |
NULL}, |
NULL}, |
{NULL, NULL, NULL, NULL} |
{NULL, NULL, NULL, NULL, NULL} |
}; |
}; |
|
|
void |
void |
|
|
void |
void |
userauth(Authctxt *authctxt, char *authlist) |
userauth(Authctxt *authctxt, char *authlist) |
{ |
{ |
|
if (authctxt->method != NULL && authctxt->method->cleanup != NULL) |
|
authctxt->method->cleanup(authctxt); |
|
|
if (authctxt->methoddata) { |
if (authctxt->methoddata) { |
xfree(authctxt->methoddata); |
xfree(authctxt->methoddata); |
authctxt->methoddata = NULL; |
authctxt->methoddata = NULL; |
|
|
&input_userauth_passwd_changereq); |
&input_userauth_passwd_changereq); |
} |
} |
|
|
|
#ifdef JPAKE |
|
static char * |
|
pw_encrypt(const char *password, const char *crypt_scheme, const char *salt) |
|
{ |
|
/* OpenBSD crypt(3) handles all of these */ |
|
if (strcmp(crypt_scheme, "crypt") == 0 || |
|
strcmp(crypt_scheme, "bcrypt") == 0 || |
|
strcmp(crypt_scheme, "md5crypt") == 0 || |
|
strcmp(crypt_scheme, "crypt-extended") == 0) |
|
return xstrdup(crypt(password, salt)); |
|
error("%s: unsupported password encryption scheme \"%.100s\"", |
|
__func__, crypt_scheme); |
|
return NULL; |
|
} |
|
|
|
static BIGNUM * |
|
jpake_password_to_secret(Authctxt *authctxt, const char *crypt_scheme, |
|
const char *salt) |
|
{ |
|
char prompt[256], *password, *crypted; |
|
u_char *secret; |
|
u_int secret_len; |
|
BIGNUM *ret; |
|
|
|
snprintf(prompt, sizeof(prompt), "%.30s@%.128s's password (JPAKE): ", |
|
authctxt->server_user, authctxt->host); |
|
password = read_passphrase(prompt, 0); |
|
|
|
if ((crypted = pw_encrypt(password, crypt_scheme, salt)) == NULL) { |
|
logit("Disabling %s authentication", authctxt->method->name); |
|
authctxt->method->enabled = NULL; |
|
/* Continue with an empty password to fail gracefully */ |
|
crypted = xstrdup(""); |
|
} |
|
|
|
#ifdef JPAKE_DEBUG |
|
debug3("%s: salt = %s", __func__, salt); |
|
debug3("%s: scheme = %s", __func__, crypt_scheme); |
|
debug3("%s: crypted = %s", __func__, crypted); |
|
#endif |
|
|
|
if (hash_buffer(crypted, strlen(crypted), EVP_sha256(), |
|
&secret, &secret_len) != 0) |
|
fatal("%s: hash_buffer", __func__); |
|
|
|
bzero(password, strlen(password)); |
|
bzero(crypted, strlen(crypted)); |
|
xfree(password); |
|
xfree(crypted); |
|
|
|
if ((ret = BN_bin2bn(secret, secret_len, NULL)) == NULL) |
|
fatal("%s: BN_bin2bn (secret)", __func__); |
|
bzero(secret, secret_len); |
|
xfree(secret); |
|
|
|
return ret; |
|
} |
|
|
|
/* ARGSUSED */ |
|
void |
|
input_userauth_jpake_server_step1(int type, u_int32_t seq, void *ctxt) |
|
{ |
|
Authctxt *authctxt = ctxt; |
|
struct jpake_ctx *pctx = authctxt->methoddata; |
|
u_char *x3_proof, *x4_proof, *x2_s_proof; |
|
u_int x3_proof_len, x4_proof_len, x2_s_proof_len; |
|
char *crypt_scheme, *salt; |
|
|
|
/* Disable this message */ |
|
dispatch_set(SSH2_MSG_USERAUTH_JPAKE_SERVER_STEP1, NULL); |
|
|
|
if ((pctx->g_x3 = BN_new()) == NULL || |
|
(pctx->g_x4 = BN_new()) == NULL) |
|
fatal("%s: BN_new", __func__); |
|
|
|
/* Fetch step 1 values */ |
|
crypt_scheme = packet_get_string(NULL); |
|
salt = packet_get_string(NULL); |
|
pctx->server_id = packet_get_string(&pctx->server_id_len); |
|
packet_get_bignum2(pctx->g_x3); |
|
packet_get_bignum2(pctx->g_x4); |
|
x3_proof = packet_get_string(&x3_proof_len); |
|
x4_proof = packet_get_string(&x4_proof_len); |
|
packet_check_eom(); |
|
|
|
JPAKE_DEBUG_CTX((pctx, "step 1 received in %s", __func__)); |
|
|
|
/* Obtain password and derive secret */ |
|
pctx->s = jpake_password_to_secret(authctxt, crypt_scheme, salt); |
|
bzero(crypt_scheme, strlen(crypt_scheme)); |
|
bzero(salt, strlen(salt)); |
|
xfree(crypt_scheme); |
|
xfree(salt); |
|
JPAKE_DEBUG_BN((pctx->s, "%s: s = ", __func__)); |
|
|
|
/* Calculate step 2 values */ |
|
jpake_step2(pctx->grp, pctx->s, pctx->g_x1, |
|
pctx->g_x3, pctx->g_x4, pctx->x2, |
|
pctx->server_id, pctx->server_id_len, |
|
pctx->client_id, pctx->client_id_len, |
|
x3_proof, x3_proof_len, |
|
x4_proof, x4_proof_len, |
|
&pctx->a, |
|
&x2_s_proof, &x2_s_proof_len); |
|
|
|
bzero(x3_proof, x3_proof_len); |
|
bzero(x4_proof, x4_proof_len); |
|
xfree(x3_proof); |
|
xfree(x4_proof); |
|
|
|
JPAKE_DEBUG_CTX((pctx, "step 2 sending in %s", __func__)); |
|
|
|
/* Send values for step 2 */ |
|
packet_start(SSH2_MSG_USERAUTH_JPAKE_CLIENT_STEP2); |
|
packet_put_bignum2(pctx->a); |
|
packet_put_string(x2_s_proof, x2_s_proof_len); |
|
packet_send(); |
|
|
|
bzero(x2_s_proof, x2_s_proof_len); |
|
xfree(x2_s_proof); |
|
|
|
/* Expect step 2 packet from peer */ |
|
dispatch_set(SSH2_MSG_USERAUTH_JPAKE_SERVER_STEP2, |
|
input_userauth_jpake_server_step2); |
|
} |
|
|
|
/* ARGSUSED */ |
|
void |
|
input_userauth_jpake_server_step2(int type, u_int32_t seq, void *ctxt) |
|
{ |
|
Authctxt *authctxt = ctxt; |
|
struct jpake_ctx *pctx = authctxt->methoddata; |
|
u_char *x4_s_proof; |
|
u_int x4_s_proof_len; |
|
|
|
/* Disable this message */ |
|
dispatch_set(SSH2_MSG_USERAUTH_JPAKE_SERVER_STEP2, NULL); |
|
|
|
if ((pctx->b = BN_new()) == NULL) |
|
fatal("%s: BN_new", __func__); |
|
|
|
/* Fetch step 2 values */ |
|
packet_get_bignum2(pctx->b); |
|
x4_s_proof = packet_get_string(&x4_s_proof_len); |
|
packet_check_eom(); |
|
|
|
JPAKE_DEBUG_CTX((pctx, "step 2 received in %s", __func__)); |
|
|
|
/* Derive shared key and calculate confirmation hash */ |
|
jpake_key_confirm(pctx->grp, pctx->s, pctx->b, |
|
pctx->x2, pctx->g_x1, pctx->g_x2, pctx->g_x3, pctx->g_x4, |
|
pctx->client_id, pctx->client_id_len, |
|
pctx->server_id, pctx->server_id_len, |
|
session_id2, session_id2_len, |
|
x4_s_proof, x4_s_proof_len, |
|
&pctx->k, |
|
&pctx->h_k_cid_sessid, &pctx->h_k_cid_sessid_len); |
|
|
|
bzero(x4_s_proof, x4_s_proof_len); |
|
xfree(x4_s_proof); |
|
|
|
JPAKE_DEBUG_CTX((pctx, "confirm sending in %s", __func__)); |
|
|
|
/* Send key confirmation proof */ |
|
packet_start(SSH2_MSG_USERAUTH_JPAKE_CLIENT_CONFIRM); |
|
packet_put_string(pctx->h_k_cid_sessid, pctx->h_k_cid_sessid_len); |
|
packet_send(); |
|
|
|
/* Expect confirmation from peer */ |
|
dispatch_set(SSH2_MSG_USERAUTH_JPAKE_SERVER_CONFIRM, |
|
input_userauth_jpake_server_confirm); |
|
} |
|
|
|
/* ARGSUSED */ |
|
void |
|
input_userauth_jpake_server_confirm(int type, u_int32_t seq, void *ctxt) |
|
{ |
|
Authctxt *authctxt = ctxt; |
|
struct jpake_ctx *pctx = authctxt->methoddata; |
|
|
|
/* Disable this message */ |
|
dispatch_set(SSH2_MSG_USERAUTH_JPAKE_SERVER_CONFIRM, NULL); |
|
|
|
pctx->h_k_sid_sessid = packet_get_string(&pctx->h_k_sid_sessid_len); |
|
packet_check_eom(); |
|
|
|
JPAKE_DEBUG_CTX((pctx, "confirm received in %s", __func__)); |
|
|
|
/* Verify expected confirmation hash */ |
|
if (jpake_check_confirm(pctx->k, |
|
pctx->server_id, pctx->server_id_len, |
|
session_id2, session_id2_len, |
|
pctx->h_k_sid_sessid, pctx->h_k_sid_sessid_len) == 1) |
|
debug("%s: %s success", __func__, authctxt->method->name); |
|
else { |
|
debug("%s: confirmation mismatch", __func__); |
|
/* XXX stash this so if auth succeeds then we can warn/kill */ |
|
} |
|
|
|
userauth_jpake_cleanup(authctxt); |
|
} |
|
#endif /* JPAKE */ |
|
|
static int |
static int |
identity_sign(Identity *id, u_char **sigp, u_int *lenp, |
identity_sign(Identity *id, u_char **sigp, u_int *lenp, |
u_char *data, u_int datalen) |
u_char *data, u_int datalen) |
|
|
return 1; |
return 1; |
} |
} |
|
|
|
#ifdef JPAKE |
|
int |
|
userauth_jpake(Authctxt *authctxt) |
|
{ |
|
struct jpake_ctx *pctx; |
|
u_char *x1_proof, *x2_proof; |
|
u_int x1_proof_len, x2_proof_len; |
|
static int attempt = 0; /* XXX share with userauth_password's? */ |
|
|
|
if (attempt++ >= options.number_of_password_prompts) |
|
return 0; |
|
if (attempt != 1) |
|
error("Permission denied, please try again."); |
|
|
|
if (authctxt->methoddata != NULL) |
|
fatal("%s: authctxt->methoddata already set (%p)", |
|
__func__, authctxt->methoddata); |
|
|
|
authctxt->methoddata = pctx = jpake_new(); |
|
|
|
/* |
|
* Send request immediately, to get the protocol going while |
|
* we do the initial computations. |
|
*/ |
|
packet_start(SSH2_MSG_USERAUTH_REQUEST); |
|
packet_put_cstring(authctxt->server_user); |
|
packet_put_cstring(authctxt->service); |
|
packet_put_cstring(authctxt->method->name); |
|
packet_send(); |
|
packet_write_wait(); |
|
|
|
jpake_step1(pctx->grp, |
|
&pctx->client_id, &pctx->client_id_len, |
|
&pctx->x1, &pctx->x2, &pctx->g_x1, &pctx->g_x2, |
|
&x1_proof, &x1_proof_len, |
|
&x2_proof, &x2_proof_len); |
|
|
|
JPAKE_DEBUG_CTX((pctx, "step 1 sending in %s", __func__)); |
|
|
|
packet_start(SSH2_MSG_USERAUTH_JPAKE_CLIENT_STEP1); |
|
packet_put_string(pctx->client_id, pctx->client_id_len); |
|
packet_put_bignum2(pctx->g_x1); |
|
packet_put_bignum2(pctx->g_x2); |
|
packet_put_string(x1_proof, x1_proof_len); |
|
packet_put_string(x2_proof, x2_proof_len); |
|
packet_send(); |
|
|
|
bzero(x1_proof, x1_proof_len); |
|
bzero(x2_proof, x2_proof_len); |
|
xfree(x1_proof); |
|
xfree(x2_proof); |
|
|
|
/* Expect step 1 packet from peer */ |
|
dispatch_set(SSH2_MSG_USERAUTH_JPAKE_SERVER_STEP1, |
|
input_userauth_jpake_server_step1); |
|
|
|
return 1; |
|
} |
|
|
|
void |
|
userauth_jpake_cleanup(Authctxt *authctxt) |
|
{ |
|
debug3("%s: clean up", __func__); |
|
if (authctxt->methoddata != NULL) { |
|
jpake_free(authctxt->methoddata); |
|
authctxt->methoddata = NULL; |
|
} |
|
} |
|
#endif /* JPAKE */ |
|
|
/* find auth method */ |
/* find auth method */ |
|
|
/* |
/* |
|
|
buffer_free(&b); |
buffer_free(&b); |
return list; |
return list; |
} |
} |
|
|