sshconnect2.c revision 7c478bd95313f5f23a4c958a745db2134aa03244
/*
* Copyright (c) 2000 Markus Friedl. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/*
* Copyright 2004 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#include "includes.h"
RCSID("$OpenBSD: sshconnect2.c,v 1.107 2002/07/01 19:48:46 markus Exp $");
#pragma ident "%Z%%M% %I% %E% SMI"
#include "ssh.h"
#include "ssh2.h"
#include "xmalloc.h"
#include "buffer.h"
#include "packet.h"
#include "compat.h"
#include "bufaux.h"
#include "cipher.h"
#include "kex.h"
#include "myproposal.h"
#include "sshconnect.h"
#include "authfile.h"
#include "dh.h"
#include "authfd.h"
#include "log.h"
#include "readconf.h"
#include "readpass.h"
#include "match.h"
#include "dispatch.h"
#include "canohost.h"
#include "msg.h"
#include "pathnames.h"
#include "g11n.h"
#ifdef GSSAPI
#include "ssh-gss.h"
extern Gssctxt *xxx_gssctxt;
#endif /* GSSAPI */
/* import */
extern char *client_version_string;
extern char *server_version_string;
/*
* SSH2 key exchange
*/
int session_id2_len = 0;
char *xxx_host;
struct sockaddr *xxx_hostaddr;
static int
{
fatal("Host key verification failed.");
return 0;
}
static int
{
log("GSS-API authenticated host key addition to "
"known_hosts file failed");
return 0;
}
void
{
#ifdef GSSAPI
/* Add the GSSAPI mechanisms currently supported on this client to
* the key exchange algorithm proposal */
#endif /* GSSAPI */
log("No valid ciphers for protocol version 2 given, using defaults.");
}
}
if (options.compression) {
} else {
}
}
if (datafellows & SSH_BUG_LOCALES_NOT_LANGTAGS) {
/* Solaris 9 SSHD expects a locale, not a langtag list */
} else {
}
/* Same languages proposal for both directions */
} else {
}
/* start key exchange */
#ifdef GSSAPI
#endif /* GSSAPI */
#ifdef DEBUG_KEXDH
/* send 1st encrypted/maced/compressed message */
packet_put_cstring("markus");
packet_send();
#endif
debug("done: ssh_kex2.");
}
/*
* Authenticate user
*/
typedef struct Authmethod Authmethod;
typedef int sign_cb_fn(
struct Authctxt {
const char *server_user;
const char *local_user;
const char *host;
const char *service;
int success;
char *authlist;
/* pubkey */
int last_key_hint;
/* hostbased */
/* kbd-interactive */
int info_req_seen;
/* generic */
void *methoddata;
};
struct Authmethod {
char *name; /* string to compare against server's list */
int *enabled; /* flag in option struct that enables method */
int *batch_flag; /* flag in option struct that disables method */
};
void input_userauth_success(int, u_int32_t, void *);
void input_userauth_failure(int, u_int32_t, void *);
void input_userauth_banner(int, u_int32_t, void *);
void input_userauth_error(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_passwd_changereq(int, u_int32_t, void *);
int userauth_none(Authctxt *);
int userauth_pubkey(Authctxt *);
int userauth_passwd(Authctxt *);
int userauth_kbdint(Authctxt *);
int userauth_hostbased(Authctxt *);
#ifdef GSSAPI
static void input_gssapi_error(int, u_int32_t, void *);
static void input_gssapi_errtok(int, u_int32_t, void *);
#endif /* GSSAPI */
static void clear_auth_state(Authctxt *);
static char *authmethods_get(void);
Authmethod authmethods[] = {
#ifdef GSSAPI
{"gssapi-keyex",
NULL},
{"gssapi-with-mic",
NULL},
#endif /* GSSAPI */
{"hostbased",
NULL,
NULL},
{"publickey",
NULL,
NULL},
{"keyboard-interactive",
NULL,
{"password",
NULL,
{"none",
NULL,
NULL,
NULL},
};
void
{
int type;
packet_put_cstring("ssh-userauth");
packet_send();
debug("send SSH2_MSG_SERVICE_REQUEST");
type = packet_read();
if (type != SSH2_MSG_SERVICE_ACCEPT)
if (packet_remaining() > 0) {
} else {
debug2("buggy server: service_accept w/o service");
}
debug("got SSH2_MSG_SERVICE_ACCEPT");
/* setup authentication context */
authctxt.info_req_seen = 0;
fatal("ssh_userauth2: internal error: cannot send userauth none request");
/* initial userauth request */
}
void
{
} else {
}
for (;;) {
break;
} else {
debug2("we did not send a packet, disable method");
}
}
}
void
{
fatal("input_userauth_error: bad message during authentication: "
"type %d", type);
}
void
{
debug3("input_userauth_banner");
}
void
{
fatal("input_userauth_success: no authentication context");
}
void
{
int partial;
fatal("input_userauth_failure: no authentication context");
partial = packet_get_char();
if (partial != 0)
log("Authenticated with partial success.");
}
void
{
Buffer b;
fatal("input_userauth_pk_ok: no authentication context");
if (datafellows & SSH_BUG_PKOK) {
/* this is similar to SSH_BUG_PKAUTH */
debug2("input_userauth_pk_ok: SSH_BUG_PKOK");
buffer_init(&b);
buffer_free(&b);
} else {
}
debug("Server accepts key: pkalg %s blen %u lastkey %p hint %d",
do {
debug("no last key or no sign cb");
break;
}
break;
}
break;
}
error("input_userauth_pk_ok: type mismatch "
"for decoded key (received %d, expected %d)",
break;
}
debug("key != last_key");
break;
}
} while (0);
/* unregister */
/* try another method if we did not send a packet */
if (sent == 0)
}
#ifdef GSSAPI
int
{
static int initialized = 0;
static int mech_idx = 0;
/* Things work better if we send one mechanism at a time, rather
* than them all at once. This means that if we fail at some point
* in the middle of a negotiation, we can come back and try something
* different. */
if (datafellows & SSH_OLD_GSSAPI) return 0;
/* Before we offer a mechanism, check that we can support it. Don't
* bother trying to get credentials - as the standard fallback will
* deal with that kind of failure.
*/
if (!initialized) {
initialized = 1;
return (0);
} else if (supported != GSS_C_NULL_OID_SET) {
/* Try next mech, if any */
mech_idx++;
return (0);
} else {
return (0);
}
packet_put_int(1);
/* The newest gsskeyex draft stipulates that OIDs should
* be DER encoded, so we need to add the object type and
* length information back on */
if (datafellows & SSH_BUG_GSSAPI_BER) {
} else {
packet_put_char(0x06);
}
packet_send();
return 1;
}
void
{
char *oidv;
fatal("input_gssapi_response: no authentication context");
/* Setup our OID */
if (datafellows & SSH_BUG_GSSAPI_BER) {
debug("Server returned different OID (%s) than expected (%s)",
return;
}
} else {
debug("Badly encoded mechanism OID received");
return;
}
debug("Server returned different OID (%s) than expected (%s)",
return;
}
}
packet_send();
}
/* Start again with next method on list */
debug("Trying to start again");
return;
}
/* We must have data to send */
packet_send();
}
void
{
fatal("input_gssapi_response: no authentication context");
packet_send();
}
/* Start again with the next method in the list */
return;
}
packet_send();
}
if (status != GSS_S_COMPLETE)
return;
/* Make data buffer to MIC */
/* Make MIC */
/*
* Oops, now what? There's no error token...
* Next userauth
*/
debug("GSS_GetMIC() failed! - "
"Abandoning GSSAPI userauth");
return;
}
packet_send();
}
void
{
fatal("input_gssapi_response: no authentication context");
/* Stick it into GSSAPI and see what it says */
debug("Server sent a GSS-API error token during GSS userauth -- %s",
/* We can't send a packet to the server */
/* The draft says that we should wait for the server to fail
* before starting the next authentication. So, we clear the
* state, but don't do anything else
*/
return;
}
void
{
char *msg;
char *lang;
maj = packet_get_int();
min = packet_get_int();
}
int
{
static int attempt = 0;
fatal("input_gssapi_response: no authentication context");
return 0;
if (attempt++ >= 1)
return 0;
debug2("Authenticating with GSS-API context from key exchange (w/ MIC)");
/* Make data buffer to MIC */
/* Make MIC */
/*
* Oops, now what? There's no error token...
* Next userauth
*/
debug("GSS_GetMIC() failed! - "
"Abandoning GSSAPI userauth");
return 0;
}
packet_send();
debug2("Authentication with deprecated \"external-keyx\""
" method not supported");
return 0;
}
return 1;
}
static
void
{
return;
}
}
#endif /* GSSAPI */
int
{
/* initial userauth request */
packet_send();
return 1;
}
int
{
static int attempt = 0;
char prompt[150];
char *password;
return 0;
if (attempt != 1)
error("Permission denied, please try again.");
packet_put_char(0);
packet_add_padding(64);
packet_send();
return 1;
}
/*
* parse PASSWD_CHANGEREQ, prompt user and send SSH2_MSG_USERAUTH_REQUEST
*/
void
{
char prompt[150];
debug2("input_userauth_passwd_changereq");
fatal("input_userauth_passwd_changereq: "
"no authentication context");
gettext("Enter %.30s@%.128s's old password: "),
gettext("Enter %.30s@%.128s's new password: "),
/* bail out */
return;
}
gettext("Retype %.30s@%.128s's new password: "),
log("Mismatch; try again, EOF to quit.");
}
}
packet_add_padding(64);
packet_send();
}
static void
{
/* XXX clear authentication state */
#ifdef GSSAPI
#endif /* GSSAPI */
}
}
static int
{
Buffer b;
int skip = 0;
int ret = -1;
int have_sig = 1;
debug3("sign_and_send_pubkey");
/* we cannot handle this key */
debug3("sign_and_send_pubkey: cannot handle key");
return 0;
}
/* data to be signed */
buffer_init(&b);
if (datafellows & SSH_OLD_SESSIONID) {
} else {
skip = buffer_len(&b);
}
"ssh-userauth" :
if (datafellows & SSH_BUG_PKAUTH) {
buffer_put_char(&b, have_sig);
} else {
buffer_put_char(&b, have_sig);
buffer_put_cstring(&b, key_ssh_name(k));
}
/* generate signature */
buffer_ptr(&b), buffer_len(&b));
if (ret == -1) {
buffer_free(&b);
return 0;
}
#ifdef DEBUG_PK
buffer_dump(&b);
#endif
if (datafellows & SSH_BUG_PKSERVICE) {
buffer_clear(&b);
buffer_put_char(&b, have_sig);
if (!(datafellows & SSH_BUG_PKAUTH))
buffer_put_cstring(&b, key_ssh_name(k));
}
/* append signature */
/* skip session id and packet type */
fatal("userauth_pubkey: internal error");
/* put remaining data from buffer into packet */
buffer_free(&b);
packet_send();
return 1;
}
static int
int hint)
{
debug3("send_pubkey_test");
/* we cannot handle this key */
debug3("send_pubkey_test: cannot handle key");
return 0;
}
/* register callback for USERAUTH_PK_OK message */
if (!(datafellows & SSH_BUG_PKAUTH))
packet_send();
return 1;
}
static Key *
load_identity_file(char *filename)
{
int quit, i;
return NULL;
}
if (options.batch_mode)
return NULL;
for (i = 0; i < options.number_of_password_prompts; i++) {
passphrase, NULL);
quit = 0;
} else {
debug2("no passphrase given, try next key");
quit = 1;
}
break;
debug2("bad passphrase given, try again...");
}
}
return private;
}
static int
{
if (idx < 0)
return -1;
/* private key is stored in external hardware */
return -1;
return ret;
}
static int
{
}
static int
{
}
static int
{
static int called = 0;
int ret = 0;
char *comment;
Key *k;
if (called == 0) {
debug2("userauth_pubkey_agent: no keys at all");
called = 1;
}
if (k == NULL) {
debug2("userauth_pubkey_agent: no more keys");
} else {
if (ret == 0)
key_free(k);
}
if (ret == 0)
debug2("userauth_pubkey_agent: no message sent");
return ret;
}
int
{
static int idx = 0;
int sent = 0;
char *filename;
do {
}
}
}
idx++;
}
return sent;
}
/*
* Send userauth request message specifying keyboard-interactive method.
*/
int
{
static int attempt = 0;
return 0;
/* disable if no SSH2_MSG_USERAUTH_INFO_REQUEST has been seen */
debug3("userauth_kbdint: disable: no info_req_seen");
return 0;
}
debug2("userauth_kbdint");
packet_send();
return 1;
}
/*
* parse INFO_REQUEST, prompt user and send INFO_RESPONSE
*/
void
{
u_int num_prompts, i;
int echo = 0;
debug2("input_userauth_info_req");
fatal("input_userauth_info_req: no authentication context");
/*
* Begin to build info response packet based on prompts requested.
* We commit to providing the correct number of responses, so if
* further on we run into a problem that prevents this, we have to
* be sure and clean this up and send a correct error response.
*/
for (i = 0; i < num_prompts; i++) {
echo = packet_get_char();
}
packet_check_eom(); /* done with parsing incoming message. */
packet_add_padding(64);
packet_send();
}
static int
{
Buffer b;
debug2("ssh_keysign called");
return -1;
}
return -1;
}
return -1;
}
return -1;
}
if (pid == 0) {
}
buffer_init(&b);
if (ssh_msg_recv(from[0], &b) < 0) {
error("ssh_keysign: no reply");
buffer_clear(&b);
return -1;
}
break;
if (buffer_get_char(&b) != version) {
error("ssh_keysign: bad version");
buffer_clear(&b);
return -1;
}
buffer_clear(&b);
return 0;
}
int
{
Buffer b;
const char *service;
static int last_hostkey = -1;
/* check for a useful key */
found = 1;
last_hostkey = i;
/* we take and free the key */
break;
}
}
if (!found) {
debug("No more client hostkeys for hostbased authentication");
return 0;
}
return 0;
}
/* figure out a name for the client host */
if (p == NULL) {
return 0;
}
xfree(p);
buffer_init(&b);
/* construct data */
buffer_put_cstring(&b, service);
buffer_put_cstring(&b, pkalg);
buffer_put_cstring(&b, chost);
#ifdef DEBUG_PK
buffer_dump(&b);
#endif
if (sensitive->external_keysign)
buffer_ptr(&b), buffer_len(&b));
else
buffer_ptr(&b), buffer_len(&b));
buffer_free(&b);
if (ok != 0) {
error("key_sign failed");
return 0;
}
packet_send();
return 1;
}
/* find auth method */
/*
* given auth method name, if configurable options permit this method fill
* in auth_ident field and return true, otherwise return false.
*/
static int
{
return 0;
/* return false if options indicate this method is disabled */
return 0;
/* return false if batch mode is enabled but method needs interactive mode */
return 0;
return 1;
}
static Authmethod *
authmethod_lookup(const char *name)
{
return method;
}
}
return NULL;
}
/* XXX internal state */
/*
* Given the authentication method list sent by the server, return the
* next method we should try. If the server initially sends a nil list,
* use a built-in default list.
*/
static Authmethod *
authmethod_get(char *authlist)
{
/* Use a suitable default if we're passed a nil list. */
return current;
for (;;) {
debug("No more authentication methods to try.");
return NULL;
}
return current;
}
}
}
static char *
authmethods_get(void)
{
Buffer b;
char *list;
buffer_init(&b);
if (authmethod_is_enabled(method)) {
if (buffer_len(&b) > 0)
}
}
buffer_free(&b);
return list;
}