Progressing client support

--HG--
extra : convert_revision : 48946be1cef774d1c33b0f78689962b18720c627
This commit is contained in:
Matt Johnston 2004-07-27 16:30:46 +00:00
parent 052bf7df93
commit a76b1ba068
28 changed files with 601 additions and 178 deletions

View File

@ -27,7 +27,7 @@ SVROBJS=svr-kex.o svr-algo.o svr-auth.o sshpty.o \
svr-chansession.o svr-runopts.o svr-agentfwd.o svr-main.o svr-x11fwd.o svr-chansession.o svr-runopts.o svr-agentfwd.o svr-main.o svr-x11fwd.o
CLIOBJS=cli-algo.o cli-main.o cli-auth.o cli-authpasswd.o cli-kex.o \ CLIOBJS=cli-algo.o cli-main.o cli-auth.o cli-authpasswd.o cli-kex.o \
cli-session.o cli-service.o cli-session.o cli-service.o cli-runopts.o
CLISVROBJS=common-session.o packet.o common-algo.o common-kex.o \ CLISVROBJS=common-session.o packet.o common-algo.o common-kex.o \
common-channel.o common-chansession.o termcodes.o loginrec.o \ common-channel.o common-chansession.o termcodes.o loginrec.o \
@ -140,11 +140,12 @@ dbclient: $(dbclientobjs)
dropbearkey: $(dropbearkeyobjs) dropbearkey: $(dropbearkeyobjs)
dropbearconvert: $(dropbearconvertobjs) dropbearconvert: $(dropbearconvertobjs)
dropbear dbclient dropbearkey dropbearconvert: $(HEADERS) $(LTC) $(LTM) dropbear dbclient dropbearkey dropbearconvert: $(HEADERS) $(LTC) $(LTM) \
Makefile
$(LD) $(LDFLAGS) -o $(SPREFIX)$@$(EXEEXT) $($@objs) $(LIBS) $(LD) $(LDFLAGS) -o $(SPREFIX)$@$(EXEEXT) $($@objs) $(LIBS)
# scp doesn't use the libs so is special. # scp doesn't use the libs so is special.
scp: $(SCPOBJS) $(HEADERS) scp: $(SCPOBJS) $(HEADERS) Makefile
$(LD) $(LDFLAGS) -o $(SPREFIX)$@$(EXEEXT) $(SCPOBJS) $(LD) $(LDFLAGS) -o $(SPREFIX)$@$(EXEEXT) $(SCPOBJS)
@ -155,16 +156,16 @@ ifeq ($(MULTI),1)
CFLAGS+=$(addprefix -DDBMULTI_, $(PROGRAMS)) -DDROPBEAR_MULTI CFLAGS+=$(addprefix -DDBMULTI_, $(PROGRAMS)) -DDROPBEAR_MULTI
endif endif
dropbearmulti: $(HEADERS) $(MULTIOBJS) $(LTC) $(LTM) dropbearmulti: $(HEADERS) $(MULTIOBJS) $(LTC) $(LTM) Makefile
$(LD) $(LDFLAGS) -o $(SPREFIX)$@$(EXEEXT) $(MULTIOBJS) $(LIBS) $(LD) $(LDFLAGS) -o $(SPREFIX)$@$(EXEEXT) $(MULTIOBJS) $(LIBS)
@echo @echo
@echo "You should now create symlinks to the programs you have included" @echo "You should now create symlinks to the programs you have included"
@echo "ie 'ln -s dropbearmulti dropbear'" @echo "ie 'ln -s dropbearmulti dropbear'"
$(LTC): $(HEADERS) $(LTC): options.h
cd libtomcrypt && $(MAKE) clean && $(MAKE) cd libtomcrypt && $(MAKE) clean && $(MAKE)
$(LTM): $(HEADERS) $(LTM): options.h
cd libtommath && $(MAKE) cd libtommath && $(MAKE)
ltc-clean: ltc-clean:

30
auth.h
View File

@ -27,12 +27,28 @@
#include "includes.h" #include "includes.h"
void authinitialise(); void svr_authinitialise();
void cli_authinitialise();
void svr_auth_password();
void svr_auth_pubkey();
int cli_auth_password();
int cli_auth_pubkey();
/* Server functions */
void recv_msg_userauth_request(); void recv_msg_userauth_request();
void send_msg_userauth_failure(int partial, int incrfail); void send_msg_userauth_failure(int partial, int incrfail);
void send_msg_userauth_success(); void send_msg_userauth_success();
/* Client functions */
void recv_msg_userauth_failure();
void recv_msg_userauth_success();
void cli_get_user();
void cli_auth_getmethods();
void cli_auth_try();
#define MAX_USERNAME_LEN 25 /* arbitrary for the moment */ #define MAX_USERNAME_LEN 25 /* arbitrary for the moment */
#define AUTH_TYPE_PUBKEY 1 << 0 #define AUTH_TYPE_PUBKEY 1 << 0
@ -46,17 +62,23 @@ void send_msg_userauth_success();
#define AUTH_METHOD_PASSWORD "password" #define AUTH_METHOD_PASSWORD "password"
#define AUTH_METHOD_PASSWORD_LEN 8 #define AUTH_METHOD_PASSWORD_LEN 8
/* This structure is shared between server and client - it contains
* relatively little extraneous bits when used for the client rather than the
* server */
struct AuthState { struct AuthState {
char *username; /* This is the username the client presents to check. It char *username; /* This is the username the client presents to check. It
is updated each run through, used for auth checking */ is updated each run through, used for auth checking */
char *printableuser; /* stripped of control chars, used for logs etc */
struct passwd * pw;
unsigned char authtypes; /* Flags indicating which auth types are still unsigned char authtypes; /* Flags indicating which auth types are still
valid */ valid */
unsigned int failcount; /* Number of (failed) authentication attempts.*/ unsigned int failcount; /* Number of (failed) authentication attempts.*/
unsigned authdone : 1; /* 0 if we haven't authed, 1 if we have */ unsigned authdone : 1; /* 0 if we haven't authed, 1 if we have. Applies for
client and server (though has differing [obvious]
meanings). */
/* These are only used for the server */
char *printableuser; /* stripped of control chars, used for logs etc */
struct passwd * pw;
}; };

148
cli-auth.c Normal file
View File

@ -0,0 +1,148 @@
#include "includes.h"
#include "session.h"
#include "auth.h"
#include "dbutil.h"
#include "buffer.h"
#include "ssh.h"
#include "packet.h"
#include "runopts.h"
void cli_authinitialise() {
memset(&ses.authstate, 0, sizeof(ses.authstate));
}
void cli_get_user() {
uid_t uid;
struct passwd *pw;
TRACE(("enter cli_get_user"));
if (cli_opts.username != NULL) {
ses.authstate.username = cli_opts.username;
} else {
uid = getuid();
pw = getpwuid(uid);
if (pw == NULL || pw->pw_name == NULL) {
dropbear_exit("Couldn't find username for current user");
}
ses.authstate.username = m_strdup(pw->pw_name);
}
TRACE(("leave cli_get_user: %s", cli_ses.username));
}
/* Send a "none" auth request to get available methods */
void cli_auth_getmethods() {
TRACE(("enter cli_auth_getmethods"));
CHECKCLEARTOWRITE();
buf_putbyte(ses.writepayload, SSH_MSG_USERAUTH_REQUEST);
buf_putstring(ses.writepayload, ses.authstate.username,
strlen(ses.authstate.username));
buf_putstring(ses.writepayload, SSH_SERVICE_CONNECTION,
SSH_SERVICE_CONNECTION_LEN);
buf_putstring(ses.writepayload, "none", 4); /* 'none' method */
encrypt_packet();
cli_ses.state = USERAUTH_METHODS_SENT;
TRACE(("leave cli_auth_getmethods"));
}
void recv_msg_userauth_failure() {
unsigned char * methods = NULL;
unsigned char * tok = NULL;
unsigned int methlen = 0;
unsigned int partial = 0;
unsigned int i = 0;
TRACE(("<- MSG_USERAUTH_FAILURE"));
TRACE(("enter recv_msg_userauth_failure"));
methods = buf_getstring(ses.payload, &methlen);
partial = buf_getbyte(ses.payload);
if (partial) {
dropbear_log(LOG_INFO, "Authentication partially succeeded, more attempts required");
} else {
ses.authstate.failcount++;
}
TRACE(("Methods (len %d): '%s'", methlen, methods));
ses.authstate.authdone=0;
ses.authstate.authtypes=0;
/* Split with nulls rather than commas */
for (i = 0; i < methlen; i++) {
if (methods[i] == ',') {
methods[i] = '\0';
}
}
tok = methods; /* tok stores the next method we'll compare */
for (i = 0; i <= methlen; i++) {
if (methods[i] == '\0') {
TRACE(("auth method '%s'\n", tok));
#ifdef DROPBEAR_PUBKEY_AUTH
if (strncmp(AUTH_METHOD_PUBKEY, tok,
AUTH_METHOD_PUBKEY_LEN) == 0) {
ses.authstate.authtypes |= AUTH_TYPE_PUBKEY;
}
#endif
#ifdef DROPBEAR_PASSWORD_AUTH
if (strncmp(AUTH_METHOD_PASSWORD, tok,
AUTH_METHOD_PASSWORD_LEN) == 0) {
ses.authstate.authtypes |= AUTH_TYPE_PASSWORD;
}
#endif
tok = &methods[i]; /* Must make sure we don't use it after
the last loop, since it'll point
to something undefined */
}
}
cli_ses.state = USERAUTH_FAIL_RCVD;
TRACE(("leave recv_msg_userauth_failure"));
}
void recv_msg_userauth_success() {
TRACE(("received msg_userauth_success"));
ses.authstate.authdone = 1;
}
void cli_auth_try() {
TRACE(("enter cli_auth_try"));
int finished = 0;
CHECKCLEARTOWRITE();
/* XXX We hardcode that we try a pubkey first */
#ifdef DROPBEAR_PUBKEY_AUTH
if (ses.authstate.authtypes & AUTH_TYPE_PUBKEY) {
finished = cli_auth_pubkey();
}
#endif
#ifdef DROPBEAR_PASSWORD_AUTH
if (!finished && ses.authstate.authtypes & AUTH_TYPE_PASSWORD) {
finished = cli_auth_password();
}
#endif
if (!finished) {
dropbear_exit("No auth methods could be used.");
}
cli_ses.state = USERAUTH_REQ_SENT;
TRACE(("leave cli_auth_try"));
}

36
cli-authpasswd.c Normal file
View File

@ -0,0 +1,36 @@
#include "includes.h"
#include "buffer.h"
#include "dbutil.h"
#include "session.h"
#include "ssh.h"
int cli_auth_password() {
char* password = NULL;
TRACE(("enter cli_auth_password"));
CHECKCLEARTOWRITE();
password = getpass("Password: ");
buf_putbyte(ses.writepayload, SSH_MSG_USERAUTH_REQUEST);
buf_putstring(ses.writepayload, ses.authstate.username,
strlen(ses.authstate.username));
buf_putstring(ses.writepayload, SSH_SERVICE_CONNECTION,
SSH_SERVICE_CONNECTION_LEN);
buf_putstring(ses.writepayload, AUTH_METHOD_PASSWORD,
AUTH_METHOD_PASSWORD_LEN);
buf_putbyte(ses.writepayload, 0); /* FALSE - so says the spec */
buf_putstring(ses.writepayload, password, strlen(password));
encrypt_packet();
m_burn(password, strlen(password));
TRACE(("leave cli_auth_password"));
return 1; /* Password auth can always be tried */
}

View File

@ -34,6 +34,7 @@
#include "bignum.h" #include "bignum.h"
#include "random.h" #include "random.h"
#include "runopts.h" #include "runopts.h"
#include "signkey.h"

View File

@ -1,6 +1,17 @@
#include <includes.h> #include "includes.h"
#include "dbutil.h"
#include "runopts.h"
#include "session.h"
static void cli_dropbear_exit(int exitcode, const char* format, va_list param);
static void cli_dropbear_log(int priority, const char* format, va_list param);
#if defined(DBMULTI_dbclient) || !defined(DROPBEAR_MULTI)
#if defined(DBMULTI_dbclient) && defined(DROPBEAR_MULTI)
int cli_main(int argc, char ** argv) {
#else
int main(int argc, char ** argv) { int main(int argc, char ** argv) {
#endif
int sock; int sock;
char* error = NULL; char* error = NULL;
@ -12,6 +23,9 @@ int main(int argc, char ** argv) {
cli_getopts(argc, argv); cli_getopts(argc, argv);
TRACE(("user='%s' host='%s' port='%s'", cli_opts.username,
cli_opts.remotehost, cli_opts.remoteport));
sock = connect_remote(cli_opts.remotehost, cli_opts.remoteport, sock = connect_remote(cli_opts.remotehost, cli_opts.remoteport,
0, &error); 0, &error);
@ -23,7 +37,7 @@ int main(int argc, char ** argv) {
len = strlen(cli_opts.remotehost); len = strlen(cli_opts.remotehost);
len += 10; /* 16 bit port and leeway*/ len += 10; /* 16 bit port and leeway*/
hostandport = (char*)m_malloc(len); hostandport = (char*)m_malloc(len);
snprintf(hostandport, len, "%s%d", snprintf(hostandport, len, "%s:%s",
cli_opts.remotehost, cli_opts.remoteport); cli_opts.remotehost, cli_opts.remoteport);
cli_session(sock, hostandport); cli_session(sock, hostandport);
@ -31,3 +45,34 @@ int main(int argc, char ** argv) {
/* not reached */ /* not reached */
return -1; return -1;
} }
#endif /* DBMULTI stuff */
static void cli_dropbear_exit(int exitcode, const char* format, va_list param) {
char fmtbuf[300];
if (!sessinitdone) {
snprintf(fmtbuf, sizeof(fmtbuf), "exited: %s",
format);
} else {
snprintf(fmtbuf, sizeof(fmtbuf),
"connection to %s@%s:%s exited: %s",
cli_opts.username, cli_opts.remotehost,
cli_opts.remoteport, format);
}
_dropbear_log(LOG_INFO, fmtbuf, param);
common_session_cleanup();
exit(exitcode);
}
static void cli_dropbear_log(int priority, const char* format, va_list param) {
char printbuf[1024];
vsnprintf(printbuf, sizeof(printbuf), format, param);
fprintf(stderr, "Dropbear: %s\n", printbuf);
}

62
cli-service.c Normal file
View File

@ -0,0 +1,62 @@
#include "includes.h"
#include "service.h"
#include "dbutil.h"
#include "packet.h"
#include "buffer.h"
#include "session.h"
#include "ssh.h"
void send_msg_service_request(char* servicename) {
TRACE(("enter send_msg_service_request: servicename='%s'", servicename));
CHECKCLEARTOWRITE();
buf_putbyte(ses.payload, SSH_MSG_SERVICE_REQUEST);
buf_putstring(ses.payload, servicename, strlen(servicename));
encrypt_packet();
TRACE(("leave send_msg_service_request"));
}
/* This just sets up the state variables right for the main client session loop
* to deal with */
void recv_msg_service_accept() {
unsigned char* servicename;
unsigned int len;
TRACE(("enter recv_msg_service_accept"));
servicename = buf_getstring(ses.payload, &len);
/* ssh-userauth */
if (cli_ses.state = SERVICE_AUTH_REQ_SENT
&& len == SSH_SERVICE_USERAUTH_LEN
&& strncmp(SSH_SERVICE_USERAUTH, servicename, len) == 0) {
cli_ses.state = SERVICE_AUTH_ACCEPT_RCVD;
m_free(servicename);
TRACE(("leave recv_msg_service_accept: done ssh-userauth"));
return;
}
/* ssh-connection */
if (cli_ses.state = SERVICE_CONN_REQ_SENT
&& len == SSH_SERVICE_CONNECTION_LEN
&& strncmp(SSH_SERVICE_CONNECTION, servicename, len) == 0) {
if (ses.authstate.authdone != 1) {
dropbear_exit("request for connection before auth");
}
cli_ses.state = SERVICE_CONN_ACCEPT_RCVD;
m_free(servicename);
TRACE(("leave recv_msg_service_accept: done ssh-connection"));
return;
}
dropbear_exit("unrecognised service accept");
/* m_free(servicename); not reached */
}

View File

@ -8,9 +8,11 @@
#include "tcpfwd-remote.h" #include "tcpfwd-remote.h"
#include "channel.h" #include "channel.h"
#include "random.h" #include "random.h"
#include "service.h"
static void cli_remoteclosed(); static void cli_remoteclosed();
static void cli_sessionloop(); static void cli_sessionloop();
static void cli_session_init();
struct clientsession cli_ses; /* GLOBAL */ struct clientsession cli_ses; /* GLOBAL */
@ -28,6 +30,8 @@ static const packettype cli_packettypes[] = {
{SSH_MSG_CHANNEL_CLOSE, recv_msg_channel_close}, {SSH_MSG_CHANNEL_CLOSE, recv_msg_channel_close},
{SSH_MSG_CHANNEL_OPEN_CONFIRMATION, recv_msg_channel_open_confirmation}, {SSH_MSG_CHANNEL_OPEN_CONFIRMATION, recv_msg_channel_open_confirmation},
{SSH_MSG_CHANNEL_OPEN_FAILURE, recv_msg_channel_open_failure}, {SSH_MSG_CHANNEL_OPEN_FAILURE, recv_msg_channel_open_failure},
{SSH_MSG_USERAUTH_FAILURE, recv_msg_userauth_failure},
{SSH_MSG_USERAUTH_SUCCESS, recv_msg_userauth_success},
{0, 0} /* End */ {0, 0} /* End */
}; };
@ -37,6 +41,7 @@ static const struct ChanType *cli_chantypes[] = {
* that forwarding */ * that forwarding */
NULL /* Null termination */ NULL /* Null termination */
}; };
void cli_session(int sock, char* remotehost) { void cli_session(int sock, char* remotehost) {
crypto_init(); crypto_init();
@ -44,11 +49,9 @@ void cli_session(int sock, char* remotehost) {
chaninitialise(cli_chantypes); chaninitialise(cli_chantypes);
/* For printing "remote host closed" for the user */
session_remoteclosed = cli_remoteclosed;
/* packet handlers */ /* Set up cli_ses vars */
ses.packettypes = cli_packettypes; cli_session_init();
/* Ready to go */ /* Ready to go */
sessinitdone = 1; sessinitdone = 1;
@ -66,27 +69,86 @@ void cli_session(int sock, char* remotehost) {
/* Not reached */ /* Not reached */
} }
static void cli_session_init() {
cli_ses.state = STATE_NOTHING;
cli_ses.kex_state = KEX_NOTHING;
/* For printing "remote host closed" for the user */
ses.remoteclosed = cli_remoteclosed;
ses.buf_match_algo = cli_buf_match_algo;
/* packet handlers */
ses.packettypes = cli_packettypes;
}
/* This function drives the progress of the session - it initiates KEX,
* service, userauth and channel requests */
static void cli_sessionloop() { static void cli_sessionloop() {
TRACE(("enter cli_sessionloop"));
if (cli_ses.kex_state == KEX_NOTHING && ses.kexstate.recvkexinit) {
cli_ses.state = KEXINIT_RCVD;
}
if (cli_ses.state == KEXINIT_RCVD) {
/* We initiate the KEXDH. If DH wasn't the correct type, the KEXINIT
* negotiation would have failed. */
send_msg_kexdh_init();
cli_ses.kex_state = KEXDH_INIT_SENT;
TRACE(("leave cli_sessionloop: done with KEXINIT_RCVD"));
return;
}
/* A KEX has finished, so we should go back to our KEX_NOTHING state */
if (cli_ses.kex_state != KEX_NOTHING && ses.kexstate.recvkexinit == 0
&& ses.kexstate.sentkexinit == 0) {
cli_ses.kex_state = KEX_NOTHING;
}
/* We shouldn't do anything else if a KEX is in progress */
if (cli_ses.kex_state != KEX_NOTHING) {
TRACE(("leave cli_sessionloop: kex_state != KEX_NOTHING"));
return;
}
/* We should exit if we haven't donefirstkex: we shouldn't reach here
* in normal operation */
if (ses.kexstate.donefirstkex == 0) {
TRACE(("XXX XXX might be bad! leave cli_sessionloop: haven't donefirstkex"));
}
switch (cli_ses.state) { switch (cli_ses.state) {
KEXINIT_RCVD: case STATE_NOTHING:
/* We initiate the KEX. If DH wasn't the correct type, the KEXINIT /* We've got the transport layer sorted, we now need to request
* negotiation would have failed. */ * userauth */
send_msg_kexdh_init(); send_msg_service_request(SSH_SERVICE_USERAUTH);
cli_ses.state = KEXDH_INIT_SENT; cli_ses.state = SERVICE_AUTH_REQ_SENT;
break; return;
default: /* userauth code */
break; case SERVICE_AUTH_ACCEPT_RCVD:
cli_get_user();
cli_auth_getmethods();
cli_ses.state = USERAUTH_METHODS_SENT;
return;
case USERAUTH_FAIL_RCVD:
cli_auth_try();
return;
/* XXX more here needed */
default:
break;
} }
if (cli_ses.donefirstkex && !cli_ses.authdone) {
} }
@ -97,5 +159,5 @@ static void cli_remoteclosed() {
* already sent/received disconnect message(s) ??? */ * already sent/received disconnect message(s) ??? */
close(ses.sock); close(ses.sock);
ses.sock = -1; ses.sock = -1;
dropbear_exit("%s closed the connection", ses.remotehost); dropbear_exit("remote closed the connection");
} }

View File

@ -5,7 +5,7 @@
* This code is copied from the larger file "kex.c" * This code is copied from the larger file "kex.c"
* some functions are verbatim, others are generalized --mihnea * some functions are verbatim, others are generalized --mihnea
* *
* Copyright (c) 2002,2003 Matt Johnston * Copyright (c) 2002-2004 Matt Johnston
* Portions Copyright (c) 2004 by Mihnea Stoenescu * Portions Copyright (c) 2004 by Mihnea Stoenescu
* All rights reserved. * All rights reserved.
* *
@ -54,10 +54,12 @@ const unsigned char dh_p_val[] = {
const int DH_G_VAL = 2; const int DH_G_VAL = 2;
static void kexinitialise();
static void gen_new_keys(); static void gen_new_keys();
#ifndef DISABLE_ZLIB #ifndef DISABLE_ZLIB
static void gen_new_zstreams(); static void gen_new_zstreams();
#endif #endif
static void read_kex_algos();
/* helper function for gen_new_keys */ /* helper function for gen_new_keys */
static void hashkeys(unsigned char *out, int outlen, static void hashkeys(unsigned char *out, int outlen,
const hash_state * hs, unsigned const char X); const hash_state * hs, unsigned const char X);
@ -145,6 +147,7 @@ void send_msg_newkeys() {
kexinitialise(); /* we've finished with this kex */ kexinitialise(); /* we've finished with this kex */
TRACE((" -> DATAALLOWED=1")); TRACE((" -> DATAALLOWED=1"));
ses.dataallowed = 1; /* we can send other packets again now */ ses.dataallowed = 1; /* we can send other packets again now */
ses.kexstate.donefirstkex = 1;
} else { } else {
ses.kexstate.sentnewkeys = 1; ses.kexstate.sentnewkeys = 1;
TRACE(("SENTNEWKEYS=1")); TRACE(("SENTNEWKEYS=1"));
@ -168,6 +171,7 @@ void recv_msg_newkeys() {
kexinitialise(); /* we've finished with this kex */ kexinitialise(); /* we've finished with this kex */
TRACE((" -> DATAALLOWED=1")); TRACE((" -> DATAALLOWED=1"));
ses.dataallowed = 1; /* we can send other packets again now */ ses.dataallowed = 1; /* we can send other packets again now */
ses.kexstate.donefirstkex = 1;
} else { } else {
TRACE(("RECVNEWKEYS=1")); TRACE(("RECVNEWKEYS=1"));
ses.kexstate.recvnewkeys = 1; ses.kexstate.recvnewkeys = 1;
@ -177,8 +181,15 @@ void recv_msg_newkeys() {
} }
/* Duplicated verbatim from kex.c --mihnea */ /* Set up the kex for the first time */
void kexinitialise() { void kexfirstinitialise() {
ses.kexstate.donefirstkex = 0;
kexinitialise();
}
/* Reset the kex state, ready for a new negotiation */
static void kexinitialise() {
struct timeval tv; struct timeval tv;
@ -404,7 +415,7 @@ void recv_msg_kexinit() {
#ifdef DROPBEAR_CLIENT #ifdef DROPBEAR_CLIENT
/* read the peer's choice of algos */ /* read the peer's choice of algos */
read_kex_algos(cli_buf_match_algo); read_kex_algos();
/* V_C, the client's version string (CR and NL excluded) */ /* V_C, the client's version string (CR and NL excluded) */
buf_putstring(ses.kexhashbuf, buf_putstring(ses.kexhashbuf,
@ -423,14 +434,13 @@ void recv_msg_kexinit() {
buf_getptr(ses.payload, ses.payload->len), buf_getptr(ses.payload, ses.payload->len),
ses.payload->len); ses.payload->len);
cli_ses.state = KEXINIT_RCVD;
#endif #endif
} else { } else {
/* SERVER */ /* SERVER */
#ifdef DROPBEAR_SERVER #ifdef DROPBEAR_SERVER
/* read the peer's choice of algos */ /* read the peer's choice of algos */
read_kex_algos(svr_buf_match_algo); read_kex_algos();
/* V_C, the client's version string (CR and NL excluded) */ /* V_C, the client's version string (CR and NL excluded) */
buf_putstring(ses.kexhashbuf, buf_putstring(ses.kexhashbuf,
ses.remoteident, strlen((char*)ses.remoteident)); ses.remoteident, strlen((char*)ses.remoteident));
@ -583,9 +593,7 @@ void kexdh_comb_key(mp_int *dh_pub_us, mp_int *dh_priv, mp_int *dh_pub_them,
/* read the other side's algo list. buf_match_algo is a callback to match /* read the other side's algo list. buf_match_algo is a callback to match
* algos for the client or server. */ * algos for the client or server. */
void read_kex_algos( static void read_kex_algos() {
algo_type*(buf_match_algo)(buffer*buf, algo_type localalgos[],
int *goodguess)) {
algo_type * algo; algo_type * algo;
char * erralgo = NULL; char * erralgo = NULL;
@ -599,7 +607,7 @@ void read_kex_algos(
ses.newkeys = (struct key_context*)m_malloc(sizeof(struct key_context)); ses.newkeys = (struct key_context*)m_malloc(sizeof(struct key_context));
/* kex_algorithms */ /* kex_algorithms */
algo = buf_match_algo(ses.payload, sshkex, &goodguess); algo = ses.buf_match_algo(ses.payload, sshkex, &goodguess);
allgood &= goodguess; allgood &= goodguess;
if (algo == NULL) { if (algo == NULL) {
erralgo = "kex"; erralgo = "kex";
@ -608,7 +616,7 @@ void read_kex_algos(
ses.newkeys->algo_kex = algo->val; ses.newkeys->algo_kex = algo->val;
/* server_host_key_algorithms */ /* server_host_key_algorithms */
algo = buf_match_algo(ses.payload, sshhostkey, &goodguess); algo = ses.buf_match_algo(ses.payload, sshhostkey, &goodguess);
allgood &= goodguess; allgood &= goodguess;
if (algo == NULL) { if (algo == NULL) {
erralgo = "hostkey"; erralgo = "hostkey";
@ -617,7 +625,7 @@ void read_kex_algos(
ses.newkeys->algo_hostkey = algo->val; ses.newkeys->algo_hostkey = algo->val;
/* encryption_algorithms_client_to_server */ /* encryption_algorithms_client_to_server */
algo = buf_match_algo(ses.payload, sshciphers, &goodguess); algo = ses.buf_match_algo(ses.payload, sshciphers, &goodguess);
if (algo == NULL) { if (algo == NULL) {
erralgo = "enc c->s"; erralgo = "enc c->s";
goto error; goto error;
@ -625,7 +633,7 @@ void read_kex_algos(
ses.newkeys->recv_algo_crypt = (struct dropbear_cipher*)algo->data; ses.newkeys->recv_algo_crypt = (struct dropbear_cipher*)algo->data;
/* encryption_algorithms_server_to_client */ /* encryption_algorithms_server_to_client */
algo = buf_match_algo(ses.payload, sshciphers, &goodguess); algo = ses.buf_match_algo(ses.payload, sshciphers, &goodguess);
if (algo == NULL) { if (algo == NULL) {
erralgo = "enc s->c"; erralgo = "enc s->c";
goto error; goto error;
@ -633,7 +641,7 @@ void read_kex_algos(
ses.newkeys->trans_algo_crypt = (struct dropbear_cipher*)algo->data; ses.newkeys->trans_algo_crypt = (struct dropbear_cipher*)algo->data;
/* mac_algorithms_client_to_server */ /* mac_algorithms_client_to_server */
algo = buf_match_algo(ses.payload, sshhashes, &goodguess); algo = ses.buf_match_algo(ses.payload, sshhashes, &goodguess);
if (algo == NULL) { if (algo == NULL) {
erralgo = "mac c->s"; erralgo = "mac c->s";
goto error; goto error;
@ -641,7 +649,7 @@ void read_kex_algos(
ses.newkeys->recv_algo_mac = (struct dropbear_hash*)algo->data; ses.newkeys->recv_algo_mac = (struct dropbear_hash*)algo->data;
/* mac_algorithms_server_to_client */ /* mac_algorithms_server_to_client */
algo = buf_match_algo(ses.payload, sshhashes, &goodguess); algo = ses.buf_match_algo(ses.payload, sshhashes, &goodguess);
if (algo == NULL) { if (algo == NULL) {
erralgo = "mac s->c"; erralgo = "mac s->c";
goto error; goto error;
@ -649,7 +657,7 @@ void read_kex_algos(
ses.newkeys->trans_algo_mac = (struct dropbear_hash*)algo->data; ses.newkeys->trans_algo_mac = (struct dropbear_hash*)algo->data;
/* compression_algorithms_client_to_server */ /* compression_algorithms_client_to_server */
algo = buf_match_algo(ses.payload, sshcompress, &goodguess); algo = ses.buf_match_algo(ses.payload, sshcompress, &goodguess);
if (algo == NULL) { if (algo == NULL) {
erralgo = "comp c->s"; erralgo = "comp c->s";
goto error; goto error;
@ -657,7 +665,7 @@ void read_kex_algos(
ses.newkeys->recv_algo_comp = algo->val; ses.newkeys->recv_algo_comp = algo->val;
/* compression_algorithms_server_to_client */ /* compression_algorithms_server_to_client */
algo = buf_match_algo(ses.payload, sshcompress, &goodguess); algo = ses.buf_match_algo(ses.payload, sshcompress, &goodguess);
if (algo == NULL) { if (algo == NULL) {
erralgo = "comp s->c"; erralgo = "comp s->c";
goto error; goto error;

28
common-runopts.c Normal file
View File

@ -0,0 +1,28 @@
/*
* Dropbear - a SSH2 server
*
* Copyright (c) 2002,2003 Matt Johnston
* All rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE. */
#include "includes.h"
#include "runopts.h"
runopts opts; /* GLOBAL */

View File

@ -45,8 +45,6 @@ int sessinitdone = 0; /* GLOBAL */
/* this is set when we get SIGINT or SIGTERM, the handler is in main.c */ /* this is set when we get SIGINT or SIGTERM, the handler is in main.c */
int exitflag = 0; /* GLOBAL */ int exitflag = 0; /* GLOBAL */
void(*session_remoteclosed)() = NULL;
static void checktimeouts(); static void checktimeouts();
static int ident_readln(int fd, char* buf, int count); static int ident_readln(int fd, char* buf, int count);
@ -63,7 +61,7 @@ void common_session_init(int sock, char* remotehost) {
ses.connecttimeout = 0; ses.connecttimeout = 0;
kexinitialise(); /* initialise the kex state */ kexfirstinitialise(); /* initialise the kex state */
chaninitialise(); /* initialise the channel state */ chaninitialise(); /* initialise the channel state */
ses.writepayload = buf_new(MAX_TRANS_PAYLOAD_LEN); ses.writepayload = buf_new(MAX_TRANS_PAYLOAD_LEN);
@ -104,8 +102,6 @@ void common_session_init(int sock, char* remotehost) {
ses.dh_K = NULL; ses.dh_K = NULL;
ses.remoteident = NULL; ses.remoteident = NULL;
ses.authdone = 0;
ses.chantypes = NULL; ses.chantypes = NULL;
ses.allowprivport = 0; ses.allowprivport = 0;

View File

@ -19,6 +19,11 @@ int main(int argc, char ** argv) {
return dropbear_main(argc, argv); return dropbear_main(argc, argv);
} }
#endif #endif
#ifdef DBMULTI_dbclient
if (strcmp(progname, "dbclient") == 0) {
return cli_main(argc, argv);
}
#endif
#ifdef DBMULTI_dropbearkey #ifdef DBMULTI_dropbearkey
if (strcmp(progname, "dropbearkey") == 0) { if (strcmp(progname, "dropbearkey") == 0) {
return dropbearkey_main(argc, argv); return dropbearkey_main(argc, argv);
@ -41,6 +46,9 @@ int main(int argc, char ** argv) {
#ifdef DBMULTI_dropbear #ifdef DBMULTI_dropbear
"'dropbear' - the Dropbear server\n" "'dropbear' - the Dropbear server\n"
#endif #endif
#ifdef DBMULTI_dbclient
"'dbclient' - the Dropbear client\n"
#endif
#ifdef DBMULTI_dropbearkey #ifdef DBMULTI_dropbearkey
"'dropbearkey' - the key generator\n" "'dropbearkey' - the key generator\n"
#endif #endif

View File

@ -36,7 +36,7 @@
/* Define this to print trace statements - very verbose */ /* Define this to print trace statements - very verbose */
/* Caution: Don't use this in an unfriendly environment (ie unfirewalled), /* Caution: Don't use this in an unfriendly environment (ie unfirewalled),
* since the printing does not sanitise strings etc */ * since the printing does not sanitise strings etc */
//#define DEBUG_TRACE #define DEBUG_TRACE
/* All functions writing to the cleartext payload buffer call /* All functions writing to the cleartext payload buffer call
* CHECKCLEARTOWRITE() before writing. This is only really useful if you're * CHECKCLEARTOWRITE() before writing. This is only really useful if you're

9
kex.h
View File

@ -32,15 +32,11 @@ void send_msg_kexinit();
void recv_msg_kexinit(); void recv_msg_kexinit();
void send_msg_newkeys(); void send_msg_newkeys();
void recv_msg_newkeys(); void recv_msg_newkeys();
void kexinitialise(); void kexfirstinitialise();
void gen_kexdh_vals(mp_int *dh_pub, mp_int *dh_priv); void gen_kexdh_vals(mp_int *dh_pub, mp_int *dh_priv);
void kexdh_comb_key(mp_int *dh_pub_us, mp_int *dh_priv, mp_int *dh_pub_them, void kexdh_comb_key(mp_int *dh_pub_us, mp_int *dh_priv, mp_int *dh_pub_them,
sign_key *hostkey); sign_key *hostkey);
void read_kex_algos(
algo_type*(buf_match_algo)(buffer*buf, algo_type localalgos[],
int *goodguess));
void recv_msg_kexdh_init(); // server void recv_msg_kexdh_init(); // server
void send_msg_kexdh_init(); // client void send_msg_kexdh_init(); // client
@ -59,6 +55,9 @@ struct KEXState {
unsigned sentnewkeys : 1; /* set once we've send/recv'ed MSG_NEWKEYS*/ unsigned sentnewkeys : 1; /* set once we've send/recv'ed MSG_NEWKEYS*/
unsigned recvnewkeys : 1; unsigned recvnewkeys : 1;
unsigned donefirstkex : 1; /* Set to 1 after the first kex has completed,
ie the transport layer has been set up */
long lastkextime; /* time of the last kex */ long lastkextime; /* time of the last kex */
unsigned int datatrans; /* data transmitted since last kex */ unsigned int datatrans; /* data transmitted since last kex */
unsigned int datarecv; /* data received since last kex */ unsigned int datarecv; /* data received since last kex */

View File

@ -29,8 +29,6 @@
* Define compile-time options below - the "#ifndef DROPBEAR_XXX .... #endif" * Define compile-time options below - the "#ifndef DROPBEAR_XXX .... #endif"
* parts are to allow for commandline -DDROPBEAR_XXX options etc. * parts are to allow for commandline -DDROPBEAR_XXX options etc.
******************************************************************/ ******************************************************************/
#define DROPBEAR_SERVER
//#define DROPBEAR_CLIENT
#ifndef DROPBEAR_PORT #ifndef DROPBEAR_PORT
#define DROPBEAR_PORT 22 #define DROPBEAR_PORT 22
@ -48,7 +46,6 @@
* perhaps 20% slower for pubkey operations (it is probably worth experimenting * perhaps 20% slower for pubkey operations (it is probably worth experimenting
* if you want to use this) */ * if you want to use this) */
/*#define NO_FAST_EXPTMOD*/ /*#define NO_FAST_EXPTMOD*/
#define DROPBEAR_SMALL_CODE
/* Enable X11 Forwarding */ /* Enable X11 Forwarding */
#define ENABLE_X11FWD #define ENABLE_X11FWD
@ -114,7 +111,7 @@
/* Authentication types to enable, at least one required. /* Authentication types to enable, at least one required.
RFC Draft requires pubkey auth, and recommends password */ RFC Draft requires pubkey auth, and recommends password */
#define DROPBEAR_PASSWORD_AUTH #define DROPBEAR_PASSWORD_AUTH
#define DROPBEAR_PUBKEY_AUTH //#define DROPBEAR_PUBKEY_AUTH
/* Random device to use - you must specify _one only_. /* Random device to use - you must specify _one only_.
* DEV_RANDOM is recommended on hosts with a good /dev/urandom, otherwise use * DEV_RANDOM is recommended on hosts with a good /dev/urandom, otherwise use
@ -162,20 +159,8 @@
/* This is used by the scp binary when used as a client binary */ /* This is used by the scp binary when used as a client binary */
#define _PATH_SSH_PROGRAM "/usr/bin/ssh" #define _PATH_SSH_PROGRAM "/usr/bin/ssh"
/* Multi-purpose binary configuration - if you want to make the combined /* Multi-purpose binary configuration has now moved. Look at the top
* binary, first define DROPBEAR_MULTI, and then define which of the three * of the Makefile for instructions, or INSTALL */
* components you want. You should then compile Dropbear with
* "make clean; make dropbearmulti". You'll need to install the binary
* manually, see MULTI for details */
/* #define DROPBEAR_MULTI */
/* The three multi binaries: dropbear, dropbearkey, dropbearconvert
* Comment out these if you don't want some of them */
#define DBMULTI_DROPBEAR
#define DBMULTI_KEY
#define DBMULTI_CONVERT
/******************************************************************* /*******************************************************************
* You shouldn't edit below here unless you know you need to. * You shouldn't edit below here unless you know you need to.
@ -246,7 +231,7 @@
#define DROPBEAR_COMP_ZLIB 1 #define DROPBEAR_COMP_ZLIB 1
/* Required for pubkey auth */ /* Required for pubkey auth */
#ifdef DROPBEAR_PUBKEY_AUTH #if defined(DROPBEAR_PUBKEY_AUTH) || defined(DROPBEAR_CLIENT)
#define DROPBEAR_SIGNKEY_VERIFY #define DROPBEAR_SIGNKEY_VERIFY
#endif #endif

View File

@ -73,7 +73,7 @@ void write_packet() {
} }
if (written == 0) { if (written == 0) {
session_remoteclosed(); ses.remoteclosed();
} }
if (written == len) { if (written == len) {
@ -122,7 +122,7 @@ void read_packet() {
len = read(ses.sock, buf_getptr(ses.readbuf, maxlen), maxlen); len = read(ses.sock, buf_getptr(ses.readbuf, maxlen), maxlen);
if (len == 0) { if (len == 0) {
session_remoteclosed(); ses.remoteclosed();
} }
if (len < 0) { if (len < 0) {
@ -171,7 +171,7 @@ static void read_packet_init() {
len = read(ses.sock, buf_getwriteptr(ses.readbuf, maxlen), len = read(ses.sock, buf_getwriteptr(ses.readbuf, maxlen),
maxlen); maxlen);
if (len == 0) { if (len == 0) {
session_remoteclosed(); ses.remoteclosed();
} }
if (len < 0) { if (len < 0) {
if (errno == EINTR) { if (errno == EINTR) {

View File

@ -79,7 +79,11 @@ void svr_getopts(int argc, char ** argv);
/* Uncompleted XXX matt */ /* Uncompleted XXX matt */
typedef struct cli_runopts { typedef struct cli_runopts {
int todo; char *remotehost;
char *remoteport;
char *username;
/* XXX TODO */
} cli_runopts; } cli_runopts;

7
scp.c
View File

@ -229,8 +229,12 @@ void tolocal(int, char *[]);
void toremote(char *, int, char *[]); void toremote(char *, int, char *[]);
void usage(void); void usage(void);
int #if defined(DBMULTI_scp) || !defined(DROPBEAR_MULTI)
#if defined(DBMULTI_scp) && defined(DROPBEAR_MULTI)
int scp_main(int argc, char **argv)
#else
main(int argc, char **argv) main(int argc, char **argv)
#endif
{ {
int ch, fflag, tflag, status; int ch, fflag, tflag, status;
double speed; double speed;
@ -379,6 +383,7 @@ main(int argc, char **argv)
} }
exit(errs != 0); exit(errs != 0);
} }
#endif /* DBMULTI stuff */
void void
toremote(char *targ, int argc, char **argv) toremote(char *targ, int argc, char **argv)

View File

@ -25,6 +25,7 @@
#ifndef _SERVICE_H_ #ifndef _SERVICE_H_
#define _SERVICE_H_ #define _SERVICE_H_
void recv_msg_service_request(); void recv_msg_service_request(); /* Server */
void send_msg_service_request(); /* Client */
#endif /* _SERVICE_H_ */ #endif /* _SERVICE_H_ */

View File

@ -45,7 +45,6 @@ void common_session_cleanup();
void checktimeouts(); void checktimeouts();
void session_identification(); void session_identification();
extern void(*session_remoteclosed)();
/* Server */ /* Server */
void svr_session(int sock, int childpipe, char *remotehost); void svr_session(int sock, int childpipe, char *remotehost);
@ -135,13 +134,18 @@ struct sshsession {
buffer* transkexinit; /* the kexinit packet we send should be kept so we buffer* transkexinit; /* the kexinit packet we send should be kept so we
can add it to the hash when generating keys */ can add it to the hash when generating keys */
algo_type*(*buf_match_algo)(buffer*buf, algo_type localalgos[],
int *goodguess); /* The function to use to choose which algorithm
to use from the ones presented by the remote
side. Is specific to the client/server mode,
hence the function-pointer callback.*/
unsigned char authdone; /* Indicates when authentication has been void(*remoteclosed)(); /* A callback to handle closure of the
completed. This applies to both client and remote connection */
server - in the server it gets set to 1 when
authentication is successful, in the client it
is set when the server has told us that auth struct AuthState authstate; /* Common amongst client and server, since most
succeeded */ struct elements are common */
/* Channel related */ /* Channel related */
struct Channel ** channels; /* these pointers may be null */ struct Channel ** channels; /* these pointers may be null */
@ -165,7 +169,6 @@ struct serversession {
/* Server specific options */ /* Server specific options */
int childpipe; /* kept open until we successfully authenticate */ int childpipe; /* kept open until we successfully authenticate */
/* userauth */ /* userauth */
struct AuthState authstate;
struct ChildPid * childpids; /* array of mappings childpid<->channel */ struct ChildPid * childpids; /* array of mappings childpid<->channel */
unsigned int childpidsize; unsigned int childpidsize;
@ -173,17 +176,30 @@ struct serversession {
}; };
typedef enum { typedef enum {
NOTHING, KEX_NOTHING,
KEXINIT_RCVD, KEXINIT_RCVD,
KEXDH_INIT_SENT, KEXDH_INIT_SENT,
KEXDH_REPLY_RCVD, KEXDONE,
} cli_kex_state;
typedef enum {
STATE_NOTHING,
SERVICE_AUTH_REQ_SENT,
SERVICE_AUTH_ACCEPT_RCVD,
SERVICE_CONN_REQ_SENT,
SERVICE_CONN_ACCEPT_RCVD,
USERAUTH_METHODS_SENT,
USERAUTH_REQ_SENT,
USERAUTH_FAIL_RCVD,
} cli_state; } cli_state;
struct clientsession { struct clientsession {
mp_int *dh_e, *dh_x; /* Used during KEX */ mp_int *dh_e, *dh_x; /* Used during KEX */
cli_state state; /* Used to progress the KEX/auth/channelsession etc */ cli_kex_state kex_state; /* Used for progressing KEX */
cli_state state; /* Used to progress auth/channelsession etc */
int something; /* XXX */ int something; /* XXX */
unsigned donefirstkex : 1; /* Set when we set sentnewkeys, never reset */ unsigned donefirstkex : 1; /* Set when we set sentnewkeys, never reset */

View File

@ -152,8 +152,8 @@ void agentcleanup(struct ChanSess * chansess) {
* for themselves */ * for themselves */
uid = getuid(); uid = getuid();
gid = getgid(); gid = getgid();
if ((setegid(svr_ses.authstate.pw->pw_gid)) < 0 || if ((setegid(ses.authstate.pw->pw_gid)) < 0 ||
(seteuid(svr_ses.authstate.pw->pw_uid)) < 0) { (seteuid(ses.authstate.pw->pw_uid)) < 0) {
dropbear_exit("failed to set euid"); dropbear_exit("failed to set euid");
} }
@ -215,8 +215,8 @@ static int bindagent(int fd, struct ChanSess * chansess) {
/* drop to user privs to make the dir/file */ /* drop to user privs to make the dir/file */
uid = getuid(); uid = getuid();
gid = getgid(); gid = getgid();
if ((setegid(svr_ses.authstate.pw->pw_gid)) < 0 || if ((setegid(ses.authstate.pw->pw_gid)) < 0 ||
(seteuid(svr_ses.authstate.pw->pw_uid)) < 0) { (seteuid(ses.authstate.pw->pw_uid)) < 0) {
dropbear_exit("failed to set euid"); dropbear_exit("failed to set euid");
} }

View File

@ -41,9 +41,9 @@ static int checkusername(unsigned char *username, unsigned int userlen);
static void send_msg_userauth_banner(); static void send_msg_userauth_banner();
/* initialise the first time for a session, resetting all parameters */ /* initialise the first time for a session, resetting all parameters */
void authinitialise() { void svr_authinitialise() {
svr_ses.authstate.failcount = 0; ses.authstate.failcount = 0;
authclear(); authclear();
} }
@ -53,17 +53,13 @@ void authinitialise() {
* on initialisation */ * on initialisation */
static void authclear() { static void authclear() {
ses.authdone = 0; memset(&ses.authstate, 0, sizeof(ses.authstate));
svr_ses.authstate.pw = NULL;
svr_ses.authstate.username = NULL;
svr_ses.authstate.printableuser = NULL;
svr_ses.authstate.authtypes = 0;
#ifdef DROPBEAR_PUBKEY_AUTH #ifdef DROPBEAR_PUBKEY_AUTH
svr_ses.authstate.authtypes |= AUTH_TYPE_PUBKEY; ses.authstate.authtypes |= AUTH_TYPE_PUBKEY;
#endif #endif
#ifdef DROPBEAR_PASSWORD_AUTH #ifdef DROPBEAR_PASSWORD_AUTH
if (svr_opts.noauthpass) { if (svr_opts.noauthpass) {
svr_ses.authstate.authtypes |= AUTH_TYPE_PASSWORD; ses.authstate.authtypes |= AUTH_TYPE_PASSWORD;
} }
#endif #endif
@ -103,7 +99,7 @@ void recv_msg_userauth_request() {
TRACE(("enter recv_msg_userauth_request")); TRACE(("enter recv_msg_userauth_request"));
/* ignore packets if auth is already done */ /* ignore packets if auth is already done */
if (ses.authdone == 1) { if (ses.authstate.authdone == 1) {
return; return;
} }
@ -147,12 +143,12 @@ void recv_msg_userauth_request() {
#ifdef DROPBEAR_PASSWORD_AUTH #ifdef DROPBEAR_PASSWORD_AUTH
if (!svr_opts.noauthpass && if (!svr_opts.noauthpass &&
!(svr_opts.norootpass && svr_ses.authstate.pw->pw_uid == 0) ) { !(svr_opts.norootpass && ses.authstate.pw->pw_uid == 0) ) {
/* user wants to try password auth */ /* user wants to try password auth */
if (methodlen == AUTH_METHOD_PASSWORD_LEN && if (methodlen == AUTH_METHOD_PASSWORD_LEN &&
strncmp(methodname, AUTH_METHOD_PASSWORD, strncmp(methodname, AUTH_METHOD_PASSWORD,
AUTH_METHOD_PASSWORD_LEN) == 0) { AUTH_METHOD_PASSWORD_LEN) == 0) {
passwordauth(); svr_auth_password();
goto out; goto out;
} }
} }
@ -163,7 +159,7 @@ void recv_msg_userauth_request() {
if (methodlen == AUTH_METHOD_PUBKEY_LEN && if (methodlen == AUTH_METHOD_PUBKEY_LEN &&
strncmp(methodname, AUTH_METHOD_PUBKEY, strncmp(methodname, AUTH_METHOD_PUBKEY,
AUTH_METHOD_PUBKEY_LEN) == 0) { AUTH_METHOD_PUBKEY_LEN) == 0) {
pubkeyauth(); svr_auth_pubkey();
goto out; goto out;
} }
#endif #endif
@ -192,21 +188,21 @@ static int checkusername(unsigned char *username, unsigned int userlen) {
} }
/* new user or username has changed */ /* new user or username has changed */
if (svr_ses.authstate.username == NULL || if (ses.authstate.username == NULL ||
strcmp(username, svr_ses.authstate.username) != 0) { strcmp(username, ses.authstate.username) != 0) {
/* the username needs resetting */ /* the username needs resetting */
if (svr_ses.authstate.username != NULL) { if (ses.authstate.username != NULL) {
dropbear_log(LOG_WARNING, "client trying multiple usernames"); dropbear_log(LOG_WARNING, "client trying multiple usernames");
m_free(svr_ses.authstate.username); m_free(ses.authstate.username);
} }
authclear(); authclear();
svr_ses.authstate.pw = getpwnam((char*)username); ses.authstate.pw = getpwnam((char*)username);
svr_ses.authstate.username = m_strdup(username); ses.authstate.username = m_strdup(username);
m_free(svr_ses.authstate.printableuser); m_free(ses.authstate.printableuser);
} }
/* check that user exists */ /* check that user exists */
if (svr_ses.authstate.pw == NULL) { if (ses.authstate.pw == NULL) {
TRACE(("leave checkusername: user '%s' doesn't exist", username)); TRACE(("leave checkusername: user '%s' doesn't exist", username));
dropbear_log(LOG_WARNING, dropbear_log(LOG_WARNING,
"login attempt for nonexistent user"); "login attempt for nonexistent user");
@ -215,10 +211,10 @@ static int checkusername(unsigned char *username, unsigned int userlen) {
} }
/* We can set it once we know its a real user */ /* We can set it once we know its a real user */
svr_ses.authstate.printableuser = m_strdup(svr_ses.authstate.pw->pw_name); ses.authstate.printableuser = m_strdup(ses.authstate.pw->pw_name);
/* check for non-root if desired */ /* check for non-root if desired */
if (svr_opts.norootlogin && svr_ses.authstate.pw->pw_uid == 0) { if (svr_opts.norootlogin && ses.authstate.pw->pw_uid == 0) {
TRACE(("leave checkusername: root login disabled")); TRACE(("leave checkusername: root login disabled"));
dropbear_log(LOG_WARNING, "root login rejected"); dropbear_log(LOG_WARNING, "root login rejected");
send_msg_userauth_failure(0, 1); send_msg_userauth_failure(0, 1);
@ -226,18 +222,18 @@ static int checkusername(unsigned char *username, unsigned int userlen) {
} }
/* check for an empty password */ /* check for an empty password */
if (svr_ses.authstate.pw->pw_passwd[0] == '\0') { if (ses.authstate.pw->pw_passwd[0] == '\0') {
TRACE(("leave checkusername: empty pword")); TRACE(("leave checkusername: empty pword"));
dropbear_log(LOG_WARNING, "user '%s' has blank password, rejected", dropbear_log(LOG_WARNING, "user '%s' has blank password, rejected",
svr_ses.authstate.printableuser); ses.authstate.printableuser);
send_msg_userauth_failure(0, 1); send_msg_userauth_failure(0, 1);
return DROPBEAR_FAILURE; return DROPBEAR_FAILURE;
} }
TRACE(("shell is %s", svr_ses.authstate.pw->pw_shell)); TRACE(("shell is %s", ses.authstate.pw->pw_shell));
/* check that the shell is set */ /* check that the shell is set */
usershell = svr_ses.authstate.pw->pw_shell; usershell = ses.authstate.pw->pw_shell;
if (usershell[0] == '\0') { if (usershell[0] == '\0') {
/* empty shell in /etc/passwd means /bin/sh according to passwd(5) */ /* empty shell in /etc/passwd means /bin/sh according to passwd(5) */
usershell = "/bin/sh"; usershell = "/bin/sh";
@ -258,7 +254,7 @@ static int checkusername(unsigned char *username, unsigned int userlen) {
endusershell(); endusershell();
TRACE(("no matching shell")); TRACE(("no matching shell"));
dropbear_log(LOG_WARNING, "user '%s' has invalid shell, rejected", dropbear_log(LOG_WARNING, "user '%s' has invalid shell, rejected",
svr_ses.authstate.printableuser); ses.authstate.printableuser);
send_msg_userauth_failure(0, 1); send_msg_userauth_failure(0, 1);
return DROPBEAR_FAILURE; return DROPBEAR_FAILURE;
@ -266,7 +262,7 @@ goodshell:
endusershell(); endusershell();
TRACE(("matching shell")); TRACE(("matching shell"));
TRACE(("uid = %d", svr_ses.authstate.pw->pw_uid)); TRACE(("uid = %d", ses.authstate.pw->pw_uid));
TRACE(("leave checkusername")); TRACE(("leave checkusername"));
return DROPBEAR_SUCCESS; return DROPBEAR_SUCCESS;
@ -290,14 +286,14 @@ void send_msg_userauth_failure(int partial, int incrfail) {
/* put a list of allowed types */ /* put a list of allowed types */
typebuf = buf_new(30); /* long enough for PUBKEY and PASSWORD */ typebuf = buf_new(30); /* long enough for PUBKEY and PASSWORD */
if (svr_ses.authstate.authtypes & AUTH_TYPE_PUBKEY) { if (ses.authstate.authtypes & AUTH_TYPE_PUBKEY) {
buf_putbytes(typebuf, AUTH_METHOD_PUBKEY, AUTH_METHOD_PUBKEY_LEN); buf_putbytes(typebuf, AUTH_METHOD_PUBKEY, AUTH_METHOD_PUBKEY_LEN);
if (svr_ses.authstate.authtypes & AUTH_TYPE_PASSWORD) { if (ses.authstate.authtypes & AUTH_TYPE_PASSWORD) {
buf_putbyte(typebuf, ','); buf_putbyte(typebuf, ',');
} }
} }
if (svr_ses.authstate.authtypes & AUTH_TYPE_PASSWORD) { if (ses.authstate.authtypes & AUTH_TYPE_PASSWORD) {
buf_putbytes(typebuf, AUTH_METHOD_PASSWORD, AUTH_METHOD_PASSWORD_LEN); buf_putbytes(typebuf, AUTH_METHOD_PASSWORD, AUTH_METHOD_PASSWORD_LEN);
} }
@ -311,18 +307,18 @@ void send_msg_userauth_failure(int partial, int incrfail) {
if (incrfail) { if (incrfail) {
usleep(300000); /* XXX improve this */ usleep(300000); /* XXX improve this */
svr_ses.authstate.failcount++; ses.authstate.failcount++;
} }
if (svr_ses.authstate.failcount >= MAX_AUTH_TRIES) { if (ses.authstate.failcount >= MAX_AUTH_TRIES) {
char * userstr; char * userstr;
/* XXX - send disconnect ? */ /* XXX - send disconnect ? */
TRACE(("Max auth tries reached, exiting")); TRACE(("Max auth tries reached, exiting"));
if (svr_ses.authstate.printableuser == NULL) { if (ses.authstate.printableuser == NULL) {
userstr = "is invalid"; userstr = "is invalid";
} else { } else {
userstr = svr_ses.authstate.printableuser; userstr = ses.authstate.printableuser;
} }
dropbear_exit("Max auth tries reached - user %s", userstr); dropbear_exit("Max auth tries reached - user %s", userstr);
} }
@ -340,9 +336,9 @@ void send_msg_userauth_success() {
buf_putbyte(ses.writepayload, SSH_MSG_USERAUTH_SUCCESS); buf_putbyte(ses.writepayload, SSH_MSG_USERAUTH_SUCCESS);
encrypt_packet(); encrypt_packet();
ses.authdone = 1; ses.authstate.authdone = 1;
if (svr_ses.authstate.pw->pw_uid == 0) { if (ses.authstate.pw->pw_uid == 0) {
ses.allowprivport = 1; ses.allowprivport = 1;
} }

View File

@ -35,7 +35,7 @@
/* Process a password auth request, sending success or failure messages as /* Process a password auth request, sending success or failure messages as
* appropriate */ * appropriate */
void passwordauth() { void svr_auth_password() {
#ifdef HAVE_SHADOW_H #ifdef HAVE_SHADOW_H
struct spwd *spasswd; struct spwd *spasswd;
@ -47,10 +47,10 @@ void passwordauth() {
unsigned char changepw; unsigned char changepw;
passwdcrypt = svr_ses.authstate.pw->pw_passwd; passwdcrypt = ses.authstate.pw->pw_passwd;
#ifdef HAVE_SHADOW_H #ifdef HAVE_SHADOW_H
/* get the shadow password if possible */ /* get the shadow password if possible */
spasswd = getspnam(svr_ses.authstate.pw->pw_name); spasswd = getspnam(ses.authstate.pw->pw_name);
if (spasswd != NULL && spasswd->sp_pwdp != NULL) { if (spasswd != NULL && spasswd->sp_pwdp != NULL) {
passwdcrypt = spasswd->sp_pwdp; passwdcrypt = spasswd->sp_pwdp;
} }
@ -66,7 +66,7 @@ void passwordauth() {
* in auth.c */ * in auth.c */
if (passwdcrypt[0] == '\0') { if (passwdcrypt[0] == '\0') {
dropbear_log(LOG_WARNING, "user '%s' has blank password, rejected", dropbear_log(LOG_WARNING, "user '%s' has blank password, rejected",
svr_ses.authstate.printableuser); ses.authstate.printableuser);
send_msg_userauth_failure(0, 1); send_msg_userauth_failure(0, 1);
return; return;
} }
@ -92,12 +92,12 @@ void passwordauth() {
/* successful authentication */ /* successful authentication */
dropbear_log(LOG_NOTICE, dropbear_log(LOG_NOTICE,
"password auth succeeded for '%s'", "password auth succeeded for '%s'",
svr_ses.authstate.printableuser); ses.authstate.printableuser);
send_msg_userauth_success(); send_msg_userauth_success();
} else { } else {
dropbear_log(LOG_WARNING, dropbear_log(LOG_WARNING,
"bad password attempt for '%s'", "bad password attempt for '%s'",
svr_ses.authstate.printableuser); ses.authstate.printableuser);
send_msg_userauth_failure(0, 1); send_msg_userauth_failure(0, 1);
} }

View File

@ -50,7 +50,7 @@ static int getauthline(buffer * line, FILE * authfile);
/* process a pubkey auth request, sending success or failure message as /* process a pubkey auth request, sending success or failure message as
* appropriate */ * appropriate */
void pubkeyauth() { void svr_auth_pubkey() {
unsigned char testkey; /* whether we're just checking if a key is usable */ unsigned char testkey; /* whether we're just checking if a key is usable */
unsigned char* algo = NULL; /* pubkey algo */ unsigned char* algo = NULL; /* pubkey algo */
@ -113,12 +113,12 @@ void pubkeyauth() {
signbuf->len) == DROPBEAR_SUCCESS) { signbuf->len) == DROPBEAR_SUCCESS) {
dropbear_log(LOG_NOTICE, dropbear_log(LOG_NOTICE,
"pubkey auth succeeded for '%s' with key %s", "pubkey auth succeeded for '%s' with key %s",
svr_ses.authstate.printableuser, fp); ses.authstate.printableuser, fp);
send_msg_userauth_success(); send_msg_userauth_success();
} else { } else {
dropbear_log(LOG_WARNING, dropbear_log(LOG_WARNING,
"pubkey auth bad signature for '%s' with key %s", "pubkey auth bad signature for '%s' with key %s",
svr_ses.authstate.printableuser, fp); ses.authstate.printableuser, fp);
send_msg_userauth_failure(0, 1); send_msg_userauth_failure(0, 1);
} }
m_free(fp); m_free(fp);
@ -178,7 +178,7 @@ static int checkpubkey(unsigned char* algo, unsigned int algolen,
if (have_algo(algo, algolen, sshhostkey) == DROPBEAR_FAILURE) { if (have_algo(algo, algolen, sshhostkey) == DROPBEAR_FAILURE) {
dropbear_log(LOG_WARNING, dropbear_log(LOG_WARNING,
"pubkey auth attempt with unknown algo for '%s'", "pubkey auth attempt with unknown algo for '%s'",
svr_ses.authstate.printableuser); ses.authstate.printableuser);
goto out; goto out;
} }
@ -190,12 +190,12 @@ static int checkpubkey(unsigned char* algo, unsigned int algolen,
/* we don't need to check pw and pw_dir for validity, since /* we don't need to check pw and pw_dir for validity, since
* its been done in checkpubkeyperms. */ * its been done in checkpubkeyperms. */
len = strlen(svr_ses.authstate.pw->pw_dir); len = strlen(ses.authstate.pw->pw_dir);
/* allocate max required pathname storage, /* allocate max required pathname storage,
* = path + "/.ssh/authorized_keys" + '\0' = pathlen + 22 */ * = path + "/.ssh/authorized_keys" + '\0' = pathlen + 22 */
filename = m_malloc(len + 22); filename = m_malloc(len + 22);
snprintf(filename, len + 22, "%s/.ssh/authorized_keys", snprintf(filename, len + 22, "%s/.ssh/authorized_keys",
svr_ses.authstate.pw->pw_dir); ses.authstate.pw->pw_dir);
/* open the file */ /* open the file */
authfile = fopen(filename, "r"); authfile = fopen(filename, "r");
@ -352,19 +352,19 @@ static int checkpubkeyperms() {
TRACE(("enter checkpubkeyperms")); TRACE(("enter checkpubkeyperms"));
assert(svr_ses.authstate.pw); assert(ses.authstate.pw);
if (svr_ses.authstate.pw->pw_dir == NULL) { if (ses.authstate.pw->pw_dir == NULL) {
goto out; goto out;
} }
if ((len = strlen(svr_ses.authstate.pw->pw_dir)) == 0) { if ((len = strlen(ses.authstate.pw->pw_dir)) == 0) {
goto out; goto out;
} }
/* allocate max required pathname storage, /* allocate max required pathname storage,
* = path + "/.ssh/authorized_keys" + '\0' = pathlen + 22 */ * = path + "/.ssh/authorized_keys" + '\0' = pathlen + 22 */
filename = m_malloc(len + 22); filename = m_malloc(len + 22);
strncpy(filename, svr_ses.authstate.pw->pw_dir, len+1); strncpy(filename, ses.authstate.pw->pw_dir, len+1);
/* check ~ */ /* check ~ */
if (checkfileperm(filename) != DROPBEAR_SUCCESS) { if (checkfileperm(filename) != DROPBEAR_SUCCESS) {
@ -406,7 +406,7 @@ static int checkfileperm(char * filename) {
return DROPBEAR_FAILURE; return DROPBEAR_FAILURE;
} }
/* check ownership - user or root only*/ /* check ownership - user or root only*/
if (filestat.st_uid != svr_ses.authstate.pw->pw_uid if (filestat.st_uid != ses.authstate.pw->pw_uid
&& filestat.st_uid != 0) { && filestat.st_uid != 0) {
TRACE(("leave checkfileperm: wrong ownership")); TRACE(("leave checkfileperm: wrong ownership"));
return DROPBEAR_FAILURE; return DROPBEAR_FAILURE;

View File

@ -239,7 +239,7 @@ static void closechansess(struct Channel *channel) {
if (chansess->tty) { if (chansess->tty) {
/* write the utmp/wtmp login record */ /* write the utmp/wtmp login record */
li = login_alloc_entry(chansess->pid, svr_ses.authstate.username, li = login_alloc_entry(chansess->pid, ses.authstate.username,
ses.remotehost, chansess->tty); ses.remotehost, chansess->tty);
login_logout(li); login_logout(li);
login_free_entry(li); login_free_entry(li);
@ -425,7 +425,7 @@ static int sessionpty(struct ChanSess * chansess) {
dropbear_exit("out of memory"); /* TODO disconnect */ dropbear_exit("out of memory"); /* TODO disconnect */
} }
pty_setowner(svr_ses.authstate.pw, chansess->tty); pty_setowner(ses.authstate.pw, chansess->tty);
pty_change_window_size(chansess->master, chansess->termr, chansess->termc, pty_change_window_size(chansess->master, chansess->termr, chansess->termc,
chansess->termw, chansess->termh); chansess->termw, chansess->termh);
@ -683,7 +683,7 @@ static int ptycommand(struct Channel *channel, struct ChanSess *chansess) {
/* write the utmp/wtmp login record - must be after changing the /* write the utmp/wtmp login record - must be after changing the
* terminal used for stdout with the dup2 above */ * terminal used for stdout with the dup2 above */
li= login_alloc_entry(getpid(), svr_ses.authstate.username, li= login_alloc_entry(getpid(), ses.authstate.username,
ses.remotehost, chansess->tty); ses.remotehost, chansess->tty);
login_login(li); login_login(li);
login_free_entry(li); login_free_entry(li);
@ -695,10 +695,10 @@ static int ptycommand(struct Channel *channel, struct ChanSess *chansess) {
/* don't show the motd if ~/.hushlogin exists */ /* don't show the motd if ~/.hushlogin exists */
/* 11 == strlen("/hushlogin\0") */ /* 11 == strlen("/hushlogin\0") */
len = strlen(svr_ses.authstate.pw->pw_dir) + 11; len = strlen(ses.authstate.pw->pw_dir) + 11;
hushpath = m_malloc(len); hushpath = m_malloc(len);
snprintf(hushpath, len, "%s/hushlogin", svr_ses.authstate.pw->pw_dir); snprintf(hushpath, len, "%s/hushlogin", ses.authstate.pw->pw_dir);
if (stat(hushpath, &sb) < 0) { if (stat(hushpath, &sb) < 0) {
/* more than a screenful is stupid IMHO */ /* more than a screenful is stupid IMHO */
@ -808,10 +808,10 @@ static void execchild(struct ChanSess *chansess) {
/* We can only change uid/gid as root ... */ /* We can only change uid/gid as root ... */
if (getuid() == 0) { if (getuid() == 0) {
if ((setgid(svr_ses.authstate.pw->pw_gid) < 0) || if ((setgid(ses.authstate.pw->pw_gid) < 0) ||
(initgroups(svr_ses.authstate.pw->pw_name, (initgroups(ses.authstate.pw->pw_name,
svr_ses.authstate.pw->pw_gid) < 0) || ses.authstate.pw->pw_gid) < 0) ||
(setuid(svr_ses.authstate.pw->pw_uid) < 0)) { (setuid(ses.authstate.pw->pw_uid) < 0)) {
dropbear_exit("error changing user"); dropbear_exit("error changing user");
} }
} else { } else {
@ -822,29 +822,29 @@ static void execchild(struct ChanSess *chansess) {
* usernames with the same uid, but differing groups, then the * usernames with the same uid, but differing groups, then the
* differing groups won't be set (as with initgroups()). The solution * differing groups won't be set (as with initgroups()). The solution
* is for the sysadmin not to give out the UID twice */ * is for the sysadmin not to give out the UID twice */
if (getuid() != svr_ses.authstate.pw->pw_uid) { if (getuid() != ses.authstate.pw->pw_uid) {
dropbear_exit("couldn't change user as non-root"); dropbear_exit("couldn't change user as non-root");
} }
} }
/* an empty shell should be interpreted as "/bin/sh" */ /* an empty shell should be interpreted as "/bin/sh" */
if (svr_ses.authstate.pw->pw_shell[0] == '\0') { if (ses.authstate.pw->pw_shell[0] == '\0') {
usershell = "/bin/sh"; usershell = "/bin/sh";
} else { } else {
usershell = svr_ses.authstate.pw->pw_shell; usershell = ses.authstate.pw->pw_shell;
} }
/* set env vars */ /* set env vars */
addnewvar("USER", svr_ses.authstate.pw->pw_name); addnewvar("USER", ses.authstate.pw->pw_name);
addnewvar("LOGNAME", svr_ses.authstate.pw->pw_name); addnewvar("LOGNAME", ses.authstate.pw->pw_name);
addnewvar("HOME", svr_ses.authstate.pw->pw_dir); addnewvar("HOME", ses.authstate.pw->pw_dir);
addnewvar("SHELL", usershell); addnewvar("SHELL", usershell);
if (chansess->term != NULL) { if (chansess->term != NULL) {
addnewvar("TERM", chansess->term); addnewvar("TERM", chansess->term);
} }
/* change directory */ /* change directory */
if (chdir(svr_ses.authstate.pw->pw_dir) < 0) { if (chdir(ses.authstate.pw->pw_dir) < 0) {
dropbear_exit("error changing directory"); dropbear_exit("error changing directory");
} }

View File

@ -106,8 +106,8 @@ void svr_getopts(int argc, char ** argv) {
opts.nolocaltcp = 0; opts.nolocaltcp = 0;
opts.noremotetcp = 0; opts.noremotetcp = 0;
/* not yet /* not yet
svr_opts.ipv4 = 1; opts.ipv4 = 1;
svr_opts.ipv6 = 1; opts.ipv6 = 1;
*/ */
#ifdef DO_MOTD #ifdef DO_MOTD
svr_opts.domotd = 1; svr_opts.domotd = 1;

View File

@ -56,7 +56,7 @@ void recv_msg_service_request() {
/* ssh-connection */ /* ssh-connection */
if (len == SSH_SERVICE_CONNECTION_LEN && if (len == SSH_SERVICE_CONNECTION_LEN &&
(strncmp(SSH_SERVICE_CONNECTION, name, len) == 0)) { (strncmp(SSH_SERVICE_CONNECTION, name, len) == 0)) {
if (ses.authdone != 1) { if (ses.authstate.authdone != 1) {
dropbear_exit("request for connection before auth"); dropbear_exit("request for connection before auth");
} }
@ -70,7 +70,6 @@ void recv_msg_service_request() {
/* TODO this should be a MSG_DISCONNECT */ /* TODO this should be a MSG_DISCONNECT */
dropbear_exit("unrecognised SSH_MSG_SERVICE_REQUEST"); dropbear_exit("unrecognised SSH_MSG_SERVICE_REQUEST");
TRACE(("leave recv_msg_service_request"));
} }

View File

@ -79,7 +79,7 @@ void svr_session(int sock, int childpipe, char* remotehost) {
/* Initialise server specific parts of the session */ /* Initialise server specific parts of the session */
svr_ses.childpipe = childpipe; svr_ses.childpipe = childpipe;
authinitialise(); svr_authinitialise();
chaninitialise(svr_chantypes); chaninitialise(svr_chantypes);
svr_chansessinitialise(); svr_chansessinitialise();
@ -90,10 +90,11 @@ void svr_session(int sock, int childpipe, char* remotehost) {
ses.connecttimeout = timeout.tv_sec + AUTH_TIMEOUT; ses.connecttimeout = timeout.tv_sec + AUTH_TIMEOUT;
/* set up messages etc */ /* set up messages etc */
session_remoteclosed = svr_remoteclosed; ses.remoteclosed = svr_remoteclosed;
/* packet handlers */ /* packet handlers */
ses.packettypes = svr_packettypes; ses.packettypes = svr_packettypes;
ses.buf_match_algo = svr_buf_match_algo;
/* We're ready to go now */ /* We're ready to go now */
sessinitdone = 1; sessinitdone = 1;
@ -123,16 +124,16 @@ void svr_dropbear_exit(int exitcode, const char* format, va_list param) {
/* before session init */ /* before session init */
snprintf(fmtbuf, sizeof(fmtbuf), snprintf(fmtbuf, sizeof(fmtbuf),
"premature exit: %s", format); "premature exit: %s", format);
} else if (svr_ses.authstate.authdone) { } else if (ses.authstate.authdone) {
/* user has authenticated */ /* user has authenticated */
snprintf(fmtbuf, sizeof(fmtbuf), snprintf(fmtbuf, sizeof(fmtbuf),
"exit after auth (%s): %s", "exit after auth (%s): %s",
svr_ses.authstate.printableuser, format); ses.authstate.printableuser, format);
} else if (svr_ses.authstate.printableuser) { } else if (ses.authstate.printableuser) {
/* we have a potential user */ /* we have a potential user */
snprintf(fmtbuf, sizeof(fmtbuf), snprintf(fmtbuf, sizeof(fmtbuf),
"exit before auth (user '%s', %d fails): %s", "exit before auth (user '%s', %d fails): %s",
svr_ses.authstate.printableuser, svr_ses.authstate.failcount, format); ses.authstate.printableuser, ses.authstate.failcount, format);
} else { } else {
/* before userauth */ /* before userauth */
snprintf(fmtbuf, sizeof(fmtbuf), snprintf(fmtbuf, sizeof(fmtbuf),