summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore2
-rw-r--r--Makefile.in11
-rw-r--r--common-session.c4
-rw-r--r--configure.ac15
-rw-r--r--includes.h4
-rw-r--r--pubkeyapi.h151
-rw-r--r--runopts.h5
-rw-r--r--session.h15
-rw-r--r--svr-authpubkey.c48
-rw-r--r--svr-runopts.c35
-rw-r--r--svr-session.c73
-rw-r--r--sysoptions.h3
12 files changed, 355 insertions, 11 deletions
diff --git a/.gitignore b/.gitignore
index ea58cd8..35565f6 100644
--- a/.gitignore
+++ b/.gitignore
@@ -5,6 +5,7 @@
*.bb
*.bbg
*.prof
+.*.swp
/autom4te.cache
/config.log
/config.status
@@ -20,3 +21,4 @@ config.h
config.h.in
configure
default_options_guard.h
+tags
diff --git a/Makefile.in b/Makefile.in
index be2d39e..e363fd9 100644
--- a/Makefile.in
+++ b/Makefile.in
@@ -80,6 +80,15 @@ else
scpobjs=$(SCPOBJS)
endif
+ifeq (@DROPBEAR_EPKA@, 1)
+ # rdynamic makes all the global symbols of dropbear available to all the loaded shared libraries
+ # this allow a plugin to reuse existing crypto/utilities like base64_decode/base64_encode without
+ # the need to rewrite them.
+ EPKA_LIBS=-ldl -rdynamic
+else
+ EPKA_LIBS=
+endif
+
VPATH=@srcdir@
srcdir=@srcdir@
@@ -189,7 +198,7 @@ dropbearkey: $(dropbearkeyobjs)
dropbearconvert: $(dropbearconvertobjs)
dropbear: $(HEADERS) $(LIBTOM_DEPS) Makefile
- $(CC) $(LDFLAGS) -o $@$(EXEEXT) $($@objs) $(LIBTOM_LIBS) $(LIBS) @CRYPTLIB@
+ $(CC) $(LDFLAGS) -o $@$(EXEEXT) $($@objs) $(LIBTOM_LIBS) $(LIBS) @CRYPTLIB@ $(EPKA_LIBS)
dbclient: $(HEADERS) $(LIBTOM_DEPS) Makefile
$(CC) $(LDFLAGS) -o $@$(EXEEXT) $($@objs) $(LIBTOM_LIBS) $(LIBS)
diff --git a/common-session.c b/common-session.c
index aa31e49..a6449ca 100644
--- a/common-session.c
+++ b/common-session.c
@@ -147,6 +147,10 @@ void common_session_init(int sock_in, int sock_out) {
ses.allowprivport = 0;
+#if DROPBEAR_EPKA
+ ses.epka_session = NULL;
+#endif
+
TRACE(("leave session_init"))
}
diff --git a/configure.ac b/configure.ac
index 7199d7c..bbbfd02 100644
--- a/configure.ac
+++ b/configure.ac
@@ -323,6 +323,21 @@ AC_ARG_ENABLE(shadow,
]
)
+AC_ARG_ENABLE(epka,
+ [ --enable-epka Enable support for External Public Key Authentication plug-in],
+ [
+ AC_DEFINE(DROPBEAR_EPKA, 1, External Public Key Authentication)
+ AC_MSG_NOTICE(Enabling support for External Public Key Authentication)
+ DROPBEAR_EPKA=1
+ ],
+ [
+ AC_DEFINE(DROPBEAR_EPKA, 0, External Public Key Authentication)
+ DROPBEAR_EPKA=0
+ ]
+
+)
+AC_SUBST(DROPBEAR_EPKA)
+
AC_ARG_ENABLE(fuzz,
[ --enable-fuzz Build fuzzing. Not recommended for deployment.],
[
diff --git a/includes.h b/includes.h
index 246882b..2fa26c4 100644
--- a/includes.h
+++ b/includes.h
@@ -164,6 +164,10 @@ typedef u_int32_t uint32_t;
#include <linux/pkt_sched.h>
#endif
+#if DROPBEAR_EPKA
+#include <dlfcn.h>
+#endif
+
#include "fake-rfc2553.h"
#include "fuzz.h"
diff --git a/pubkeyapi.h b/pubkeyapi.h
new file mode 100644
index 0000000..9ca9551
--- /dev/null
+++ b/pubkeyapi.h
@@ -0,0 +1,151 @@
+/*
+ * 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. */
+#ifndef DROPBEAR_PUBKEY_H
+#define DROPBEAR_PUBKEY_H
+
+
+/* External Public Key API (EPKA) Plug-in Interface
+ *
+ * See:
+ * https://github.com/fabriziobertocci/dropbear-epka
+ * for additional information and examples about this API
+ *
+ */
+
+struct EPKAInstance;
+struct EPKASession;
+
+/* API VERSION INFORMATION -
+ * Dropbear will:
+ * - Reject any plugin with a major version mismatch
+ * - Load and print a warning if the plugin's minor version is HIGHER than
+ * dropbear's minor version (assumes properties are added at the end of
+ * EPKAInstance or EPKASession). This is a case of plugin newer than dropbear.
+ * - Reject if the plugin minor version is SMALLER than dropbear one (case
+ * of plugin older than dropbear).
+ * - Load (with no warnings) if version match.
+ */
+#define DROPBEAR_EPKA_VERSION_MAJOR 1
+#define DROPBEAR_EPKA_VERSION_MINOR 0
+
+
+/* Creates an instance of the plugin.
+ *
+ * This is the main entry point of the plug-in and should be IMMUTABLE across
+ * different API versions. Dropbear will check the version number
+ * returned in the api_version to match the version it understands and reject
+ * any plugin for which API major version does not match.
+ *
+ * If the version MINOR is different, dropbear will allow the plugin to run
+ * only if: plugin_MINOR > dropbear_MINOR
+ *
+ * If plugin_MINOR < dropbeart_MINOR or if the MAJOR version is different
+ * dropbear will reject the plugin and terminate the execution.
+ *
+ * addrstring is the IP address of the client.
+ *
+ * Returns NULL in case of failure, otherwise a void * of the instance that need
+ * to be passed to all the subsequent call to the plugin
+ */
+typedef struct EPKAInstance *(* PubkeyExtPlugin_newFn)(int verbose,
+ const char *options,
+ const char *addrstring);
+#define DROPBEAR_PUBKEY_PLUGIN_FNNAME_NEW "plugin_new"
+
+
+/* Validate a client through public key authentication
+ *
+ * If session has not been already created, creates it and store it
+ * in *sessionInOut.
+ * If session is a non-NULL, it will reuse it.
+ *
+ * Returns DROPBEAR_SUCCESS (0) if success or DROPBEAR_FAILURE (-1) if
+ * authentication fails
+ */
+typedef int (* PubkeyExtPlugin_checkPubKeyFn)(struct EPKAInstance *pluginInstance,
+ struct EPKASession **sessionInOut,
+ const char* algo,
+ unsigned int algolen,
+ const unsigned char* keyblob,
+ unsigned int keybloblen,
+ const char *username);
+
+/* Notify the plugin that auth completed (after signature verification)
+ */
+typedef void (* PubkeyExtPlugin_authSuccessFn)(struct EPKASession *session);
+
+/* Deletes a session
+ * TODO: Add a reason why the session is terminated. See svr_dropbear_exit (in svr-session.c)
+ */
+typedef void (* PubkeyExtPlugin_sessionDeleteFn)(struct EPKASession *session);
+
+/* Deletes the plugin instance */
+typedef void (* PubkeyExtPlugin_deleteFn)(struct EPKAInstance *pluginInstance);
+
+
+/* The EPKAInstance object - A simple container of the pointer to the functions used
+ * by Dropbear.
+ *
+ * A plug-in can extend it to add its own properties
+ *
+ * The instance is created from the call to the plugin_new() function of the
+ * shared library.
+ * The delete_plugin function should delete the object.
+ */
+struct EPKAInstance {
+ int api_version[2]; /* 0=Major, 1=Minor */
+
+ PubkeyExtPlugin_checkPubKeyFn checkpubkey; /* mandatory */
+ PubkeyExtPlugin_authSuccessFn auth_success; /* optional */
+ PubkeyExtPlugin_sessionDeleteFn delete_session; /* mandatory */
+ PubkeyExtPlugin_deleteFn delete_plugin; /* mandatory */
+};
+
+/*****************************************************************************
+ * SESSION
+ ****************************************************************************/
+/* Returns the options from the session.
+ * The returned buffer will be destroyed when the session is deleted.
+ * Option buffer string NULL-terminated
+ */
+typedef char * (* PubkeyExtPlugin_getOptionsFn)(struct EPKASession *session);
+
+
+/* An SSH Session. Created during pre-auth and reused during the authentication.
+ * The plug-in should delete this object (or any object extending it) from
+ * the delete_session() function.
+ *
+ * Extend it to cache user and authentication information that can be
+ * reused between pre-auth and auth (and to store whatever session-specific
+ * variable you need to keep).
+ *
+ * Store any optional auth options in the auth_options property of the session.
+ */
+struct EPKASession {
+ struct EPKAInstance * plugin_instance;
+
+ PubkeyExtPlugin_getOptionsFn get_options;
+};
+
+#endif
diff --git a/runopts.h b/runopts.h
index 31eae1f..f173678 100644
--- a/runopts.h
+++ b/runopts.h
@@ -125,6 +125,11 @@ typedef struct svr_runopts {
char * forced_command;
+#if DROPBEAR_EPKA
+ char *pubkey_plugin;
+ char *pubkey_plugin_options;
+#endif
+
} svr_runopts;
extern svr_runopts svr_opts;
diff --git a/session.h b/session.h
index 0f77055..efade6e 100644
--- a/session.h
+++ b/session.h
@@ -38,6 +38,9 @@
#include "chansession.h"
#include "dbutil.h"
#include "netio.h"
+#if DROPBEAR_EPKA
+#include "pubkeyapi.h"
+#endif
void common_session_init(int sock_in, int sock_out);
void session_loop(void(*loophandler)(void)) ATTRIB_NORETURN;
@@ -216,6 +219,10 @@ struct sshsession {
volatile int exitflag;
/* set once the ses structure (and cli_ses/svr_ses) have been populated to their initial state */
int init_done;
+
+#if DROPBEAR_EPKA
+ struct EPKASession * epka_session;
+#endif
};
struct serversession {
@@ -241,6 +248,14 @@ struct serversession {
pid_t server_pid;
#endif
+#if DROPBEAR_EPKA
+ /* The shared library handle */
+ void *epka_plugin_handle;
+
+ /* The instance created by the plugin_new function */
+ struct EPKAInstance *epka_instance;
+#endif
+
};
typedef enum {
diff --git a/svr-authpubkey.c b/svr-authpubkey.c
index 4f27986..9d70bfb 100644
--- a/svr-authpubkey.c
+++ b/svr-authpubkey.c
@@ -91,6 +91,7 @@ void svr_auth_pubkey(int valid_user) {
sign_key * key = NULL;
char* fp = NULL;
enum signkey_type type = -1;
+ int auth_failure = 1;
TRACE(("enter pubkeyauth"))
@@ -110,9 +111,45 @@ void svr_auth_pubkey(int valid_user) {
send_msg_userauth_failure(0, 0);
goto out;
}
-
+#if DROPBEAR_EPKA
+ if (svr_ses.epka_instance != NULL) {
+ char *options_buf;
+ if (svr_ses.epka_instance->checkpubkey(
+ svr_ses.epka_instance,
+ &ses.epka_session,
+ algo,
+ algolen,
+ keyblob,
+ keybloblen,
+ ses.authstate.username) == DROPBEAR_SUCCESS) {
+ /* Success */
+ auth_failure = 0;
+
+ /* Options provided? */
+ options_buf = ses.epka_session->get_options(ses.epka_session);
+ if (options_buf) {
+ struct buf temp_buf = {
+ .data = (unsigned char *)options_buf,
+ .len = strlen(options_buf),
+ .pos = 0,
+ .size = 0
+ };
+ int ret = svr_add_pubkey_options(&temp_buf, 0, "N/A");
+ if (ret == DROPBEAR_FAILURE) {
+ /* Fail immediately as the plugin provided wrong options */
+ send_msg_userauth_failure(0, 0);
+ goto out;
+ }
+ }
+ }
+ }
+#endif
/* check if the key is valid */
- if (checkpubkey(algo, algolen, keyblob, keybloblen) == DROPBEAR_FAILURE) {
+ if (auth_failure) {
+ auth_failure = checkpubkey(algo, algolen, keyblob, keybloblen) == DROPBEAR_FAILURE;
+ }
+
+ if (auth_failure) {
send_msg_userauth_failure(0, 0);
goto out;
}
@@ -156,6 +193,13 @@ void svr_auth_pubkey(int valid_user) {
"Pubkey auth succeeded for '%s' with key %s from %s",
ses.authstate.pw_name, fp, svr_ses.addrstring);
send_msg_userauth_success();
+#if DROPBEAR_EPKA
+ if ((ses.epka_session != NULL) && (svr_ses.epka_instance->auth_success != NULL)) {
+ /* Was authenticated through the external plugin. tell plugin that signature verification was ok */
+ svr_ses.epka_instance->auth_success(ses.epka_session);
+ }
+#endif
+
} else {
dropbear_log(LOG_WARNING,
"Pubkey auth bad signature for '%s' with key %s from %s",
diff --git a/svr-runopts.c b/svr-runopts.c
index d6c78df..19ce14c 100644
--- a/svr-runopts.c
+++ b/svr-runopts.c
@@ -46,16 +46,16 @@ static void printhelp(const char * progname) {
"-b bannerfile Display the contents of bannerfile"
" before user login\n"
" (default: none)\n"
- "-r keyfile Specify hostkeys (repeatable)\n"
+ "-r keyfile Specify hostkeys (repeatable)\n"
" defaults: \n"
#if DROPBEAR_DSS
- " dss %s\n"
+ " - dss %s\n"
#endif
#if DROPBEAR_RSA
- " rsa %s\n"
+ " - rsa %s\n"
#endif
#if DROPBEAR_ECDSA
- " ecdsa %s\n"
+ " - ecdsa %s\n"
#endif
#if DROPBEAR_DELAY_HOSTKEY
"-R Create hostkeys as required\n"
@@ -99,6 +99,10 @@ static void printhelp(const char * progname) {
"-W <receive_window_buffer> (default %d, larger may be faster, max 1MB)\n"
"-K <keepalive> (0 is never, default %d, in seconds)\n"
"-I <idle_timeout> (0 is never, default %d, in seconds)\n"
+#if DROPBEAR_EPKA
+ "-A <authplugin>[,<options>]\n"
+ " Enable external public key auth through <authplugin>\n"
+#endif
"-V Version\n"
#if DEBUG_TRACE
"-v verbose (compiled with DEBUG_TRACE)\n"
@@ -129,6 +133,9 @@ void svr_getopts(int argc, char ** argv) {
char* maxauthtries_arg = NULL;
char* keyfile = NULL;
char c;
+#if DROPBEAR_EPKA
+ char* pubkey_plugin = NULL;
+#endif
/* see printhelp() for options */
@@ -156,6 +163,10 @@ void svr_getopts(int argc, char ** argv) {
#if DROPBEAR_SVR_REMOTETCPFWD
svr_opts.noremotetcp = 0;
#endif
+#if DROPBEAR_EPKA
+ svr_opts.pubkey_plugin = NULL;
+ svr_opts.pubkey_plugin_options = NULL;
+#endif
#ifndef DISABLE_ZLIB
opts.compress_mode = DROPBEAR_COMPRESS_DELAYED;
@@ -274,6 +285,11 @@ void svr_getopts(int argc, char ** argv) {
case 'u':
/* backwards compatibility with old urandom option */
break;
+#if DROPBEAR_EPKA
+ case 'A':
+ next = &pubkey_plugin;
+ break;
+#endif
#if DEBUG_TRACE
case 'v':
debug_trace = 1;
@@ -394,6 +410,17 @@ void svr_getopts(int argc, char ** argv) {
if (svr_opts.forced_command) {
dropbear_log(LOG_INFO, "Forced command set to '%s'", svr_opts.forced_command);
}
+#if DROPBEAR_EPKA
+ if (pubkey_plugin) {
+ char *args = strchr(pubkey_plugin, ',');
+ if (args) {
+ *args='\0';
+ ++args;
+ }
+ svr_opts.pubkey_plugin = pubkey_plugin;
+ svr_opts.pubkey_plugin_options = args;
+ }
+#endif
}
static void addportandaddress(const char* spec) {
diff --git a/svr-session.c b/svr-session.c
index a816398..3ea7589 100644
--- a/svr-session.c
+++ b/svr-session.c
@@ -88,6 +88,18 @@ svr_session_cleanup(void) {
m_free(svr_ses.remotehost);
m_free(svr_ses.childpids);
svr_ses.childpidsize = 0;
+
+#if DROPBEAR_EPKA
+ if (svr_ses.epka_plugin_handle != NULL) {
+ if (svr_ses.epka_instance) {
+ svr_ses.epka_instance->delete_plugin(svr_ses.epka_instance);
+ svr_ses.epka_instance = NULL;
+ }
+
+ dlclose(svr_ses.epka_plugin_handle);
+ svr_ses.epka_plugin_handle = NULL;
+ }
+#endif
}
void svr_session(int sock, int childpipe) {
@@ -101,10 +113,6 @@ void svr_session(int sock, int childpipe) {
#if DROPBEAR_VFORK
svr_ses.server_pid = getpid();
#endif
- svr_authinitialise();
- chaninitialise(svr_chantypes);
- svr_chansessinitialise();
- svr_algos_initialise();
/* for logging the remote address */
get_socket_address(ses.sock_in, NULL, NULL, &host, &port, 0);
@@ -114,6 +122,56 @@ void svr_session(int sock, int childpipe) {
m_free(host);
m_free(port);
+#if DROPBEAR_EPKA
+ /* Initializes the EPKA Plugin */
+ svr_ses.epka_plugin_handle = NULL;
+ svr_ses.epka_instance = NULL;
+ if (svr_opts.pubkey_plugin) {
+#if DEBUG_TRACE
+ const int verbose = debug_trace;
+#else
+ const int verbose = 0;
+#endif
+ PubkeyExtPlugin_newFn pluginConstructor;
+
+ /* RTLD_NOW: fails if not all the symbols are resolved now. Better fail now than at run-time */
+ svr_ses.epka_plugin_handle = dlopen(svr_opts.pubkey_plugin, RTLD_NOW);
+ if (svr_ses.epka_plugin_handle == NULL) {
+ dropbear_exit("failed to load external pubkey plugin '%s': %s", svr_opts.pubkey_plugin, dlerror());
+ }
+ pluginConstructor = (PubkeyExtPlugin_newFn)dlsym(svr_ses.epka_plugin_handle, DROPBEAR_PUBKEY_PLUGIN_FNNAME_NEW);
+ if (!pluginConstructor) {
+ dropbear_exit("plugin constructor method not found in external pubkey plugin");
+ }
+
+ /* Create an instance of the plugin */
+ svr_ses.epka_instance = pluginConstructor(verbose, svr_opts.pubkey_plugin_options, svr_ses.addrstring);
+ if (svr_ses.epka_instance == NULL) {
+ dropbear_exit("external plugin initialization failed");
+ }
+ /* Check if the plugin is compatible */
+ if ( (svr_ses.epka_instance->api_version[0] != DROPBEAR_EPKA_VERSION_MAJOR) ||
+ (svr_ses.epka_instance->api_version[1] < DROPBEAR_EPKA_VERSION_MINOR) ) {
+ dropbear_exit("plugin version check failed: "
+ "Dropbear=%d.%d, plugin=%d.%d",
+ DROPBEAR_EPKA_VERSION_MAJOR, DROPBEAR_EPKA_VERSION_MINOR,
+ svr_ses.epka_instance->api_version[0], svr_ses.epka_instance->api_version[1]);
+ }
+ if (svr_ses.epka_instance->api_version[1] > DROPBEAR_EPKA_VERSION_MINOR) {
+ dropbear_log(LOG_WARNING, "plugin API newer than dropbear API: "
+ "Dropbear=%d.%d, plugin=%d.%d",
+ DROPBEAR_EPKA_VERSION_MAJOR, DROPBEAR_EPKA_VERSION_MINOR,
+ svr_ses.epka_instance->api_version[0], svr_ses.epka_instance->api_version[1]);
+ }
+ dropbear_log(LOG_INFO, "successfully loaded and initialized pubkey plugin '%s'", svr_opts.pubkey_plugin);
+ }
+#endif
+
+ svr_authinitialise();
+ chaninitialise(svr_chantypes);
+ svr_chansessinitialise();
+ svr_algos_initialise();
+
get_socket_address(ses.sock_in, NULL, NULL,
&svr_ses.remotehost, NULL, 1);
@@ -151,6 +209,13 @@ void svr_dropbear_exit(int exitcode, const char* format, va_list param) {
char fullmsg[300];
int i;
+#if DROPBEAR_EPKA
+ if ((ses.epka_session != NULL)) {
+ svr_ses.epka_instance->delete_session(ses.epka_session);
+ }
+ ses.epka_session = NULL;
+#endif
+
/* Render the formatted exit message */
vsnprintf(exitmsg, sizeof(exitmsg), format, param);
diff --git a/sysoptions.h b/sysoptions.h
index efb27d8..58604f0 100644
--- a/sysoptions.h
+++ b/sysoptions.h
@@ -243,6 +243,9 @@ If you test it please contact the Dropbear author */
#error "At least one server authentication type must be enabled. DROPBEAR_SVR_PUBKEY_AUTH and DROPBEAR_SVR_PASSWORD_AUTH are recommended."
#endif
+#if (DROPBEAR_EPKA && !DROPBEAR_SVR_PUBKEY_AUTH)
+ #error "You must define DROPBEAR_SVR_PUBKEY_AUTH in order to use External Public Key Authentication (EPKA)"
+#endif
#if !(DROPBEAR_AES128 || DROPBEAR_3DES || DROPBEAR_AES256 || DROPBEAR_BLOWFISH \
|| DROPBEAR_TWOFISH256 || DROPBEAR_TWOFISH128)