summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMatt Johnston <matt@ucc.asn.au>2015-03-16 21:34:05 +0800
committerMatt Johnston <matt@ucc.asn.au>2015-03-16 21:34:05 +0800
commitb1672d7f2ff204f6428392100e821ea7c49fe5c6 (patch)
tree5ebb1dc268be9ff9564bbe936e3b61ff77a5d2ad
parent95daedaaa0728033fa9ff5f6fadda72c476fe006 (diff)
parent8333636f62e396197b2de12fdaa27ac783fe4f30 (diff)
downloaddropbear-b1672d7f2ff204f6428392100e821ea7c49fe5c6.tar.gz
merge pam branch up to date
-rw-r--r--auth.h10
-rw-r--r--buffer.c5
-rw-r--r--buffer.h1
-rw-r--r--cli-session.c1
-rw-r--r--common-session.c11
-rw-r--r--options.h9
-rw-r--r--process-packet.c8
-rw-r--r--session.h15
-rw-r--r--svr-auth.c25
-rw-r--r--svr-authpam.c293
-rw-r--r--svr-session.c3
-rw-r--r--sysoptions.h4
12 files changed, 205 insertions, 180 deletions
diff --git a/auth.h b/auth.h
index 78397ed..1519567 100644
--- a/auth.h
+++ b/auth.h
@@ -40,6 +40,8 @@ void send_msg_userauth_banner(buffer *msg);
void svr_auth_password();
void svr_auth_pubkey();
void svr_auth_pam();
+/* For PAM/interactive auth */
+void recv_msg_userauth_info_response();
#ifdef ENABLE_SVR_PUBKEY_OPTIONS
int svr_pubkey_allows_agentfwd();
@@ -122,9 +124,14 @@ struct AuthState {
#ifdef ENABLE_SVR_PUBKEY_OPTIONS
struct PubKeyOptions* pubkey_options;
#endif
+
+#ifdef ENABLE_SVR_PAM_AUTH
+ int pam_status;
+ unsigned int pam_num_response;
+ struct pam_response ** pam_response;
+#endif
};
-#ifdef ENABLE_SVR_PUBKEY_OPTIONS
struct PubKeyOptions;
struct PubKeyOptions {
/* Flags */
@@ -135,6 +142,5 @@ struct PubKeyOptions {
/* "command=" option. */
unsigned char * forced_command;
};
-#endif
#endif /* DROPBEAR_AUTH_H_ */
diff --git a/buffer.c b/buffer.c
index d043bdb..7808051 100644
--- a/buffer.c
+++ b/buffer.c
@@ -180,6 +180,11 @@ void buf_putbyte(buffer* buf, unsigned char val) {
buf->pos++;
}
+void buf_putbool(buffer* buf, unsigned int val) {
+ char truth = val ? 1 : 0;
+ buf_putbyte(buf, truth);
+}
+
/* returns an in-place pointer to the buffer, checking that
* the next len bytes from that position can be used */
unsigned char* buf_getptr(buffer* buf, unsigned int len) {
diff --git a/buffer.h b/buffer.h
index 6ab53d7..4e3c327 100644
--- a/buffer.h
+++ b/buffer.h
@@ -54,6 +54,7 @@ void buf_incrwritepos(buffer* buf, unsigned int incr);
unsigned char buf_getbyte(buffer* buf);
unsigned char buf_getbool(buffer* buf);
void buf_putbyte(buffer* buf, unsigned char val);
+void buf_putbool(buffer* buf, unsigned int val);
unsigned char* buf_getptr(buffer* buf, unsigned int len);
unsigned char* buf_getwriteptr(buffer* buf, unsigned int len);
unsigned char* buf_getstring(buffer* buf, unsigned int *retlen);
diff --git a/cli-session.c b/cli-session.c
index 815f5b6..893f459 100644
--- a/cli-session.c
+++ b/cli-session.c
@@ -172,6 +172,7 @@ static void cli_session_init() {
/* For printing "remote host closed" for the user */
ses.remoteclosed = cli_remoteclosed;
+ ses.loop_handler = cli_sessionloop;
ses.extra_session_cleanup = cli_session_cleanup;
/* packet handlers */
diff --git a/common-session.c b/common-session.c
index 164dc85..cf5ff07 100644
--- a/common-session.c
+++ b/common-session.c
@@ -137,14 +137,17 @@ void common_session_init(int sock_in, int sock_out) {
TRACE(("leave session_init"))
}
-void session_loop(void(*loophandler)()) {
+void session_loop() {
fd_set readfd, writefd;
struct timeval timeout;
int val;
+ assert(ses.recursion_count <= 1);
+ ses.recursion_count++;
+
/* main loop, select()s for all sockets in use */
- for(;;) {
+ while (!ses.exit_recursion) {
timeout.tv_sec = select_timeout();
timeout.tv_usec = 0;
@@ -244,8 +247,8 @@ void session_loop(void(*loophandler)()) {
}
} /* for(;;) */
-
- /* Not reached */
+ ses.recursion_count--;
+ ses.exit_recursion = 0;
}
static void cleanup_buf(buffer **buf) {
diff --git a/options.h b/options.h
index e2d69a9..872776f 100644
--- a/options.h
+++ b/options.h
@@ -194,16 +194,9 @@ much traffic. */
/* Authentication Types - at least one required.
RFC Draft requires pubkey auth, and recommends password */
-/* Note: PAM auth is quite simple and only works for PAM modules which just do
- * a simple "Login: " "Password: " (you can edit the strings in svr-authpam.c).
- * It's useful for systems like OS X where standard password crypts don't work
- * but there's an interface via a PAM module. It won't work for more complex
- * PAM challenge/response.
- * You can't enable both PASSWORD and PAM. */
-
#define ENABLE_SVR_PASSWORD_AUTH
/* PAM requires ./configure --enable-pam */
-/*#define ENABLE_SVR_PAM_AUTH */
+#define ENABLE_SVR_PAM_AUTH
#define ENABLE_SVR_PUBKEY_AUTH
/* Whether to take public key options in
diff --git a/process-packet.c b/process-packet.c
index ddeb9ce..39dc496 100644
--- a/process-packet.c
+++ b/process-packet.c
@@ -35,7 +35,7 @@
#include "auth.h"
#include "channel.h"
-#define MAX_UNAUTH_PACKET_TYPE SSH_MSG_USERAUTH_PK_OK
+#define MAX_UNAUTH_PACKET_TYPE 61
static void recv_unimplemented();
@@ -154,8 +154,10 @@ void process_packet() {
recv_unimplemented();
out:
- buf_free(ses.payload);
- ses.payload = NULL;
+ if (ses.payload) {
+ buf_free(ses.payload);
+ ses.payload = NULL;
+ }
TRACE2(("leave process_packet"))
}
diff --git a/session.h b/session.h
index 478de94..1fdd3ff 100644
--- a/session.h
+++ b/session.h
@@ -107,7 +107,7 @@ struct packetlist {
struct sshsession {
/* Is it a client or server? */
- unsigned char isserver;
+ unsigned int isserver;
int sock_in;
int sock_out;
@@ -137,16 +137,20 @@ struct sshsession {
const packettype * packettypes; /* Packet handler mappings for this
session, see process-packet.c */
- unsigned dataallowed : 1; /* whether we can send data packets or we are in
+ unsigned int recursion_count; /* Set when the Dropbear main loop is called
+ recursively for PAM auth */
+ unsigned int exit_recursion;
+
+ unsigned int dataallowed; /* whether we can send data packets or we are in
the middle of a KEX or something */
- unsigned char requirenext; /* byte indicating what packets we require next,
+ unsigned int requirenext; /* byte indicating what packets we require next,
or 0x00 for any. */
- unsigned char ignorenext; /* whether to ignore the next packet,
+ unsigned int ignorenext; /* whether to ignore the next packet,
used for kex_follows stuff */
- unsigned char lastpacket; /* What the last received packet type was */
+ unsigned int lastpacket; /* What the last received packet type was */
int signal_pipe[2]; /* stores endpoints of a self-pipe used for
race-free signal handling */
@@ -185,6 +189,7 @@ struct sshsession {
void(*remoteclosed)(); /* A callback to handle closure of the
remote connection */
+ void(*loop_handler)();
void(*extra_session_cleanup)(); /* client or server specific cleanup */
void(*send_kex_first_guess)();
diff --git a/svr-auth.c b/svr-auth.c
index 89760ef..ebe9602 100644
--- a/svr-auth.c
+++ b/svr-auth.c
@@ -59,11 +59,14 @@ static void authclear() {
#ifdef ENABLE_SVR_PUBKEY_AUTH
ses.authstate.authtypes |= AUTH_TYPE_PUBKEY;
#endif
-#if defined(ENABLE_SVR_PASSWORD_AUTH) || defined(ENABLE_SVR_PAM_AUTH)
+#ifdef ENABLE_SVR_PASSWORD_AUTH
if (!svr_opts.noauthpass) {
ses.authstate.authtypes |= AUTH_TYPE_PASSWORD;
}
#endif
+#ifdef ENABLE_SVR_PAM_AUTH
+ ses.authstate.authtypes |= AUTH_TYPE_INTERACT;
+#endif
if (ses.authstate.pw_name) {
m_free(ses.authstate.pw_name);
}
@@ -185,12 +188,11 @@ void recv_msg_userauth_request() {
#endif
#ifdef ENABLE_SVR_PAM_AUTH
- if (!svr_opts.noauthpass &&
- !(svr_opts.norootpass && ses.authstate.pw_uid == 0) ) {
+ if (!(svr_opts.norootpass && ses.authstate.pw_uid == 0) ) {
/* user wants to try password auth */
- if (methodlen == AUTH_METHOD_PASSWORD_LEN &&
- strncmp(methodname, AUTH_METHOD_PASSWORD,
- AUTH_METHOD_PASSWORD_LEN) == 0) {
+ if (methodlen == AUTH_METHOD_INTERACT_LEN &&
+ strncmp(methodname, AUTH_METHOD_INTERACT,
+ AUTH_METHOD_INTERACT_LEN) == 0) {
if (valid_user) {
svr_auth_pam();
goto out;
@@ -330,7 +332,7 @@ void send_msg_userauth_failure(int partial, int incrfail) {
buf_putbyte(ses.writepayload, SSH_MSG_USERAUTH_FAILURE);
/* put a list of allowed types */
- typebuf = buf_new(30); /* long enough for PUBKEY and PASSWORD */
+ typebuf = buf_new(55);
if (ses.authstate.authtypes & AUTH_TYPE_PUBKEY) {
buf_putbytes(typebuf, AUTH_METHOD_PUBKEY, AUTH_METHOD_PUBKEY_LEN);
@@ -341,6 +343,13 @@ void send_msg_userauth_failure(int partial, int incrfail) {
if (ses.authstate.authtypes & AUTH_TYPE_PASSWORD) {
buf_putbytes(typebuf, AUTH_METHOD_PASSWORD, AUTH_METHOD_PASSWORD_LEN);
+ if (ses.authstate.authtypes & AUTH_TYPE_INTERACT) {
+ buf_putbyte(typebuf, ',');
+ }
+ }
+
+ if (ses.authstate.authtypes & AUTH_TYPE_INTERACT) {
+ buf_putbytes(typebuf, AUTH_METHOD_INTERACT, AUTH_METHOD_INTERACT_LEN);
}
buf_putbufstring(ses.writepayload, typebuf);
@@ -350,7 +359,7 @@ void send_msg_userauth_failure(int partial, int incrfail) {
buf_free(typebuf);
- buf_putbyte(ses.writepayload, partial ? 1 : 0);
+ buf_putbool(ses.writepayload, partial);
encrypt_packet();
if (incrfail) {
diff --git a/svr-authpam.c b/svr-authpam.c
index 0b1d69f..5449463 100644
--- a/svr-authpam.c
+++ b/svr-authpam.c
@@ -30,6 +30,7 @@
#include "buffer.h"
#include "dbutil.h"
#include "auth.h"
+#include "ssh.h"
#ifdef ENABLE_SVR_PAM_AUTH
@@ -39,179 +40,181 @@
#include <pam/pam_appl.h>
#endif
-struct UserDataS {
- char* user;
- char* passwd;
+enum
+{
+ DROPBEAR_PAM_RETCODE_FILL = 100,
+ DROPBEAR_PAM_RETCODE_SKIP = 101,
};
-/* PAM conversation function - for now we only handle one message */
-int
-pamConvFunc(int num_msg,
- const struct pam_message **msg,
- struct pam_response **respp,
- void *appdata_ptr) {
- int rc = PAM_SUCCESS;
- struct pam_response* resp = NULL;
- struct UserDataS* userDatap = (struct UserDataS*) appdata_ptr;
- unsigned int msg_len = 0;
- unsigned int i = 0;
- char * compare_message = NULL;
+void recv_msg_userauth_info_response() {
+ unsigned int i, p;
+ unsigned int num_ssh_resp;
+ if (!ses.authstate.pam_response) {
+ /* A response was sent unprompted */
+ send_msg_userauth_failure(0, 1);
+ return;
+ }
- TRACE(("enter pamConvFunc"))
+ if (ses.recursion_count != 2) {
+ dropbear_exit("PAM failure");
+ }
+
+ num_ssh_resp = buf_getint(ses.payload);
+ ses.authstate.pam_status = DROPBEAR_SUCCESS;
- if (num_msg != 1) {
- /* If you're getting here - Dropbear probably can't support your pam
- * modules. This whole file is a bit of a hack around lack of
- * asynchronocity in PAM anyway. */
- dropbear_log(LOG_INFO, "pamConvFunc() called with >1 messages: not supported.");
- return PAM_CONV_ERR;
+ for (i = 0, p = 0; i < ses.authstate.pam_num_response; i++) {
+ struct pam_response *resp = ses.authstate.pam_response[i];
+ resp->resp = NULL;
+
+ if (resp->resp_retcode == DROPBEAR_PAM_RETCODE_FILL) {
+ if (p >= num_ssh_resp) {
+ TRACE(("Too many PAM responses"))
+ ses.authstate.pam_status = DROPBEAR_FAILURE;
+ } else {
+ /* TODO convert to UTF8? */
+ resp->resp = buf_getstring(ses.payload, NULL);
+ }
+ p++;
+ }
}
- /* make a copy we can strip */
- compare_message = m_strdup((*msg)->msg);
-
- /* Make the string lowercase. */
- msg_len = strlen(compare_message);
- for (i = 0; i < msg_len; i++) {
- compare_message[i] = tolower(compare_message[i]);
+ if (p != num_ssh_resp) {
+ TRACE(("Not enough PAM responses"))
+ ses.authstate.pam_status = DROPBEAR_FAILURE;
}
- /* If the string ends with ": ", remove the space.
- ie "login: " vs "login:" */
- if (msg_len > 2
- && compare_message[msg_len-2] == ':'
- && compare_message[msg_len-1] == ' ') {
- compare_message[msg_len-1] = '\0';
+ ses.exit_recursion = 1;
+}
+
+static void
+send_msg_userauth_info_request(unsigned int num_msg, const struct pam_message **msgs,
+ struct pam_response **respp) {
+ unsigned int i;
+ unsigned int pos, instruction_size, instruction_count;
+ CHECKCLEARTOWRITE();
+
+ buf_putbyte(ses.writepayload, SSH_MSG_USERAUTH_INFO_REQUEST);
+
+ /* name */
+ buf_putstring(ses.writepayload, ses.authstate.pw_name, 0);
+
+ /* any informational messages are send as an instruction */
+ pos = ses.writepayload->pos;
+ /* will be filled out later if required */
+ buf_putint(ses.writepayload, 0);
+ instruction_size = 0;
+ instruction_count = 0;
+ for (i = 0; i < num_msg; i++) {
+ const struct pam_message *msg = msgs[i];
+ if (msg->msg_style == PAM_ERROR_MSG)
+ {
+ buf_putbytes(ses.writepayload, "Error: ", strlen("Error: "));
+ instruction_size += strlen("Error: ");
+ }
+ if (msg->msg_style == PAM_ERROR_MSG || msg->msg_style == PAM_TEXT_INFO)
+ {
+ buf_putbytes(ses.writepayload, msg->msg, strlen(msg->msg));
+ buf_putbyte(ses.writepayload, '\n');
+ instruction_size += strlen(msg->msg)+1;
+ instruction_count++;
+ respp[i]->resp_retcode = DROPBEAR_PAM_RETCODE_SKIP;
+ }
+ else
+ {
+ respp[i]->resp_retcode = DROPBEAR_PAM_RETCODE_FILL;
+ }
}
- switch((*msg)->msg_style) {
+ if (instruction_size > 0)
+ {
+ /* Remove trailing newline */
+ instruction_size--;
+ buf_incrlen(ses.writepayload, -1);
- case PAM_PROMPT_ECHO_OFF:
+ /* Put the instruction string length */
+ buf_setpos(ses.writepayload, pos);
+ buf_putint(ses.writepayload, instruction_size);
+ buf_setpos(ses.writepayload, ses.writepayload->len);
+ }
- if (!(strcmp(compare_message, "password:") == 0)) {
- /* We don't recognise the prompt as asking for a password,
- so can't handle it. Add more above as required for
- different pam modules/implementations. If you need
- to add an entry here please mail the Dropbear developer */
- dropbear_log(LOG_NOTICE, "PAM unknown prompt '%s' (no echo)",
- compare_message);
- rc = PAM_CONV_ERR;
- break;
- }
+ /* language (deprecated) */
+ buf_putstring(ses.writepayload, "", 0);
- /* You have to read the PAM module-writers' docs (do we look like
- * module writers? no.) to find out that the module will
- * free the pam_response and its resp element - ie we _must_ malloc
- * it here */
- resp = (struct pam_response*) m_malloc(sizeof(struct pam_response));
- memset(resp, 0, sizeof(struct pam_response));
-
- resp->resp = m_strdup(userDatap->passwd);
- m_burn(userDatap->passwd, strlen(userDatap->passwd));
- (*respp) = resp;
- break;
-
-
- case PAM_PROMPT_ECHO_ON:
-
- if (!(
- (strcmp(compare_message, "login:" ) == 0)
- || (strcmp(compare_message, "please enter username:") == 0)
- || (strcmp(compare_message, "username:") == 0)
- )) {
- /* We don't recognise the prompt as asking for a username,
- so can't handle it. Add more above as required for
- different pam modules/implementations. If you need
- to add an entry here please mail the Dropbear developer */
- dropbear_log(LOG_NOTICE, "PAM unknown prompt '%s' (with echo)",
- compare_message);
- rc = PAM_CONV_ERR;
- break;
- }
+ /* num-prompts */
+ buf_putint(ses.writepayload, num_msg-instruction_count);
- /* You have to read the PAM module-writers' docs (do we look like
- * module writers? no.) to find out that the module will
- * free the pam_response and its resp element - ie we _must_ malloc
- * it here */
- resp = (struct pam_response*) m_malloc(sizeof(struct pam_response));
- memset(resp, 0, sizeof(struct pam_response));
-
- resp->resp = m_strdup(userDatap->user);
- TRACE(("userDatap->user='%s'", userDatap->user))
- (*respp) = resp;
- break;
-
- case PAM_ERROR_MSG:
- case PAM_TEXT_INFO:
-
- if (msg_len > 0) {
- buffer * pam_err = buf_new(msg_len + 4);
- buf_setpos(pam_err, 0);
- buf_putbytes(pam_err, "\r\n", 2);
- buf_putbytes(pam_err, (*msg)->msg, msg_len);
- buf_putbytes(pam_err, "\r\n", 2);
- buf_setpos(pam_err, 0);
-
- send_msg_userauth_banner(pam_err);
- buf_free(pam_err);
- }
- break;
+ for (i = 0; i < num_msg; i++) {
+ const struct pam_message *msg = msgs[i];
+ if (msg->msg_style != PAM_PROMPT_ECHO_OFF && msg->msg_style != PAM_PROMPT_ECHO_ON) {
+ /* was handled in "instruction" above */
+ continue;
+ }
- default:
- TRACE(("Unknown message type"))
- rc = PAM_CONV_ERR;
- break;
- }
+ /* prompt */
+ buf_putstring(ses.writepayload, msg->msg, strlen(msg->msg));
- m_free(compare_message);
- TRACE(("leave pamConvFunc, rc %d", rc))
+ /* echo */
+ buf_putbool(ses.writepayload, msg->msg_style == PAM_PROMPT_ECHO_ON);
+ }
- return rc;
+ encrypt_packet();
}
-/* Process a password auth request, sending success or failure messages as
- * appropriate. To the client it looks like it's doing normal password auth (as
- * opposed to keyboard-interactive or something), so the pam module has to be
- * fairly standard (ie just "what's your username, what's your password, OK").
- *
- * Keyboard interactive would be a lot nicer, but since PAM is synchronous, it
- * gets very messy trying to send the interactive challenges, and read the
- * interactive responses, over the network. */
-void svr_auth_pam() {
+/* PAM conversation function - for now we only handle one message */
+int
+pamConvFunc(int num_msg,
+ const struct pam_message **msgs,
+ struct pam_response **respp,
+ void *UNUSED(appdata_ptr)) {
- struct UserDataS userData = {NULL, NULL};
- struct pam_conv pamConv = {
- pamConvFunc,
- &userData /* submitted to pamvConvFunc as appdata_ptr */
- };
+ int ret = PAM_SYSTEM_ERR;
- pam_handle_t* pamHandlep = NULL;
+ TRACE(("enter pamConvFunc"))
- unsigned char * password = NULL;
- unsigned int passwordlen;
+ if (ses.recursion_count != 1) {
+ dropbear_exit("PAM failure");
+ }
- int rc = PAM_SUCCESS;
- unsigned char changepw;
+ *respp = m_malloc(sizeof(struct pam_response) * num_msg);
- /* check if client wants to change password */
- changepw = buf_getbool(ses.payload);
- if (changepw) {
- /* not implemented by this server */
- send_msg_userauth_failure(0, 1);
- goto cleanup;
+ send_msg_userauth_info_request(num_msg, msgs, respp);
+
+ ses.authstate.pam_num_response = num_msg;
+ ses.authstate.pam_response = respp;
+ ses.authstate.pam_status = DROPBEAR_FAILURE;
+
+ buf_free(ses.payload);
+ ses.payload = NULL;
+
+ /* Recurse! This will return once a SSH_MSG_USERAUTH_INFO_RESPONSE
+ has been received, with the ses.authstate.pam_* fields populated */
+ session_loop();
+
+ if (ses.authstate.pam_status == DROPBEAR_FAILURE) {
+ ret = PAM_CONV_ERR;
+ m_free(*respp);
+ } else {
+ ses.authstate.pam_response = NULL;
+ ret = PAM_SUCCESS;
}
- password = buf_getstring(ses.payload, &passwordlen);
+ return ret;
+}
- /* used to pass data to the PAM conversation function - don't bother with
- * strdup() etc since these are touched only by our own conversation
- * function (above) which takes care of it */
- userData.user = ses.authstate.pw_name;
- userData.passwd = password;
+void svr_auth_pam() {
+ int rc;
+ struct pam_conv pamConv = {
+ pamConvFunc,
+ NULL
+ };
+
+ pam_handle_t* pamHandlep = NULL;
+
+ /* Ignore the payload, it has "language" and "submethods" */
/* Init pam */
- if ((rc = pam_start("sshd", NULL, &pamConv, &pamHandlep)) != PAM_SUCCESS) {
+ if ((rc = pam_start("sshd", ses.authstate.pw_name, &pamConv, &pamHandlep)) != PAM_SUCCESS) {
dropbear_log(LOG_WARNING, "pam_start() failed, rc=%d, %s",
rc, pam_strerror(pamHandlep, rc));
goto cleanup;
@@ -260,10 +263,6 @@ void svr_auth_pam() {
send_msg_userauth_success();
cleanup:
- if (password != NULL) {
- m_burn(password, passwordlen);
- m_free(password);
- }
if (pamHandlep != NULL) {
TRACE(("pam_end"))
(void) pam_end(pamHandlep, 0 /* pam_status */);
diff --git a/svr-session.c b/svr-session.c
index 8485905..24a5a36 100644
--- a/svr-session.c
+++ b/svr-session.c
@@ -66,6 +66,9 @@ static const packettype svr_packettypes[] = {
{SSH_MSG_CHANNEL_OPEN_CONFIRMATION, recv_msg_channel_open_confirmation},
{SSH_MSG_CHANNEL_OPEN_FAILURE, recv_msg_channel_open_failure},
#endif
+#ifdef ENABLE_SVR_PAM_AUTH
+ {SSH_MSG_USERAUTH_INFO_RESPONSE, recv_msg_userauth_info_response},
+#endif
{0, 0} /* End */
};
diff --git a/sysoptions.h b/sysoptions.h
index 11dc10d..44af267 100644
--- a/sysoptions.h
+++ b/sysoptions.h
@@ -218,9 +218,7 @@
* with flushing compressed data */
#define DROPBEAR_ZLIB_MEM_LEVEL 8
-#if defined(ENABLE_SVR_PASSWORD_AUTH) && defined(ENABLE_SVR_PAM_AUTH)
-#error "You can't turn on PASSWORD and PAM auth both at once. Fix it in options.h"
-#endif
+#define PAM_MAX_INFORMATION_SIZE 2000
/* We use dropbear_client and dropbear_server as shortcuts to avoid redundant
* code, if we're just compiling as client or server */