From e9edbe8bb204b00c7f4b4fda7eeee9d0177934ae Mon Sep 17 00:00:00 2001 From: Matt Johnston Date: Tue, 6 Mar 2018 22:18:20 +0800 Subject: avoid leak of pubkey_options --- svr-authpubkey.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'svr-authpubkey.c') diff --git a/svr-authpubkey.c b/svr-authpubkey.c index 0ca0ea4..e97b158 100644 --- a/svr-authpubkey.c +++ b/svr-authpubkey.c @@ -167,6 +167,10 @@ out: sign_key_free(key); key = NULL; } + /* Retain pubkey options only if auth succeeded */ + if (!ses.authstate.authdone) { + svr_pubkey_options_cleanup(); + } TRACE(("leave pubkeyauth")) } -- cgit v1.2.3 From 27828c742c0c5e024aa5dd1a5333d64cb4a1b16c Mon Sep 17 00:00:00 2001 From: Matt Johnston Date: Wed, 7 Mar 2018 22:16:21 +0800 Subject: don't allow null characters in authorized_keys --- svr-authpubkey.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) (limited to 'svr-authpubkey.c') diff --git a/svr-authpubkey.c b/svr-authpubkey.c index e97b158..ec14ec0 100644 --- a/svr-authpubkey.c +++ b/svr-authpubkey.c @@ -201,7 +201,12 @@ static int checkpubkey_line(buffer* line, int line_num, const char* filename, if (line->len < MIN_AUTHKEYS_LINE || line->len > MAX_AUTHKEYS_LINE) { TRACE(("checkpubkey_line: bad line length %d", line->len)) - return DROPBEAR_FAILURE; + goto out; + } + + if (memchr(line->data, 0x0, line->len) != NULL) { + TRACE(("checkpubkey_line: bad line has null char")) + goto out; } /* compare the algorithm. +3 so we have enough bytes to read a space and some base64 characters too. */ -- cgit v1.2.3 From 52adbb34c32d3e2e1bcdb941e20a6f81138b8248 Mon Sep 17 00:00:00 2001 From: Matt Johnston Date: Thu, 23 Aug 2018 23:43:12 +0800 Subject: Wait to fail invalid usernames --- auth.h | 6 +++--- svr-auth.c | 19 +++++-------------- svr-authpam.c | 26 ++++++++++++++++++++++---- svr-authpasswd.c | 27 ++++++++++++++------------- svr-authpubkey.c | 11 ++++++++++- 5 files changed, 54 insertions(+), 35 deletions(-) (limited to 'svr-authpubkey.c') diff --git a/auth.h b/auth.h index da498f5..98f5468 100644 --- a/auth.h +++ b/auth.h @@ -37,9 +37,9 @@ void recv_msg_userauth_request(void); void send_msg_userauth_failure(int partial, int incrfail); void send_msg_userauth_success(void); void send_msg_userauth_banner(const buffer *msg); -void svr_auth_password(void); -void svr_auth_pubkey(void); -void svr_auth_pam(void); +void svr_auth_password(int valid_user); +void svr_auth_pubkey(int valid_user); +void svr_auth_pam(int valid_user); #if DROPBEAR_SVR_PUBKEY_OPTIONS_BUILT int svr_pubkey_allows_agentfwd(void); diff --git a/svr-auth.c b/svr-auth.c index c19c090..edde86b 100644 --- a/svr-auth.c +++ b/svr-auth.c @@ -149,10 +149,8 @@ void recv_msg_userauth_request() { if (methodlen == AUTH_METHOD_PASSWORD_LEN && strncmp(methodname, AUTH_METHOD_PASSWORD, AUTH_METHOD_PASSWORD_LEN) == 0) { - if (valid_user) { - svr_auth_password(); - goto out; - } + svr_auth_password(valid_user); + goto out; } } #endif @@ -164,10 +162,8 @@ void recv_msg_userauth_request() { if (methodlen == AUTH_METHOD_PASSWORD_LEN && strncmp(methodname, AUTH_METHOD_PASSWORD, AUTH_METHOD_PASSWORD_LEN) == 0) { - if (valid_user) { - svr_auth_pam(); - goto out; - } + svr_auth_pam(valid_user); + goto out; } } #endif @@ -177,12 +173,7 @@ void recv_msg_userauth_request() { if (methodlen == AUTH_METHOD_PUBKEY_LEN && strncmp(methodname, AUTH_METHOD_PUBKEY, AUTH_METHOD_PUBKEY_LEN) == 0) { - if (valid_user) { - svr_auth_pubkey(); - } else { - /* pubkey has no failure delay */ - send_msg_userauth_failure(0, 0); - } + svr_auth_pubkey(valid_user); goto out; } #endif diff --git a/svr-authpam.c b/svr-authpam.c index 05e4f3e..d201bc9 100644 --- a/svr-authpam.c +++ b/svr-authpam.c @@ -178,13 +178,14 @@ pamConvFunc(int num_msg, * 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() { +void svr_auth_pam(int valid_user) { struct UserDataS userData = {NULL, NULL}; struct pam_conv pamConv = { pamConvFunc, &userData /* submitted to pamvConvFunc as appdata_ptr */ }; + const char* printable_user = NULL; pam_handle_t* pamHandlep = NULL; @@ -204,12 +205,23 @@ void svr_auth_pam() { password = buf_getstring(ses.payload, &passwordlen); + /* We run the PAM conversation regardless of whether the username is valid + in case the conversation function has an inherent delay. + Use ses.authstate.username rather than ses.authstate.pw_name. + After PAM succeeds we then check the valid_user flag too */ + /* 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.user = ses.authstate.username; userData.passwd = password; + if (ses.authstate.pw_name) { + printable_user = ses.authstate.pw_name; + } else { + printable_user = ""; + } + /* Init pam */ if ((rc = pam_start("sshd", NULL, &pamConv, &pamHandlep)) != PAM_SUCCESS) { dropbear_log(LOG_WARNING, "pam_start() failed, rc=%d, %s", @@ -242,7 +254,7 @@ void svr_auth_pam() { rc, pam_strerror(pamHandlep, rc)); dropbear_log(LOG_WARNING, "Bad PAM password attempt for '%s' from %s", - ses.authstate.pw_name, + printable_user, svr_ses.addrstring); send_msg_userauth_failure(0, 1); goto cleanup; @@ -253,12 +265,18 @@ void svr_auth_pam() { rc, pam_strerror(pamHandlep, rc)); dropbear_log(LOG_WARNING, "Bad PAM password attempt for '%s' from %s", - ses.authstate.pw_name, + printable_user, svr_ses.addrstring); send_msg_userauth_failure(0, 1); goto cleanup; } + if (!valid_user) { + /* PAM auth succeeded but the username isn't allowed in for another reason + (checkusername() failed) */ + send_msg_userauth_failure(0, 1); + } + /* successful authentication */ dropbear_log(LOG_NOTICE, "PAM password auth succeeded for '%s' from %s", ses.authstate.pw_name, diff --git a/svr-authpasswd.c b/svr-authpasswd.c index bdee2aa..69c7d8a 100644 --- a/svr-authpasswd.c +++ b/svr-authpasswd.c @@ -48,22 +48,14 @@ static int constant_time_strcmp(const char* a, const char* b) { /* Process a password auth request, sending success or failure messages as * appropriate */ -void svr_auth_password() { +void svr_auth_password(int valid_user) { char * passwdcrypt = NULL; /* the crypt from /etc/passwd or /etc/shadow */ char * testcrypt = NULL; /* crypt generated from the user's password sent */ - char * password; + char * password = NULL; unsigned int passwordlen; - unsigned int changepw; - passwdcrypt = ses.authstate.pw_passwd; - -#ifdef DEBUG_HACKCRYPT - /* debugging crypt for non-root testing with shadows */ - passwdcrypt = DEBUG_HACKCRYPT; -#endif - /* check if client wants to change password */ changepw = buf_getbool(ses.payload); if (changepw) { @@ -73,12 +65,21 @@ void svr_auth_password() { } password = buf_getstring(ses.payload, &passwordlen); - - /* the first bytes of passwdcrypt are the salt */ - testcrypt = crypt(password, passwdcrypt); + if (valid_user) { + /* the first bytes of passwdcrypt are the salt */ + passwdcrypt = ses.authstate.pw_passwd; + testcrypt = crypt(password, passwdcrypt); + } m_burn(password, passwordlen); m_free(password); + /* After we have got the payload contents we can exit if the username + is invalid. Invalid users have already been logged. */ + if (!valid_user) { + send_msg_userauth_failure(0, 1); + return; + } + if (testcrypt == NULL) { /* crypt() with an invalid salt like "!!" */ dropbear_log(LOG_WARNING, "User account '%s' is locked", diff --git a/svr-authpubkey.c b/svr-authpubkey.c index aa6087c..ff481c8 100644 --- a/svr-authpubkey.c +++ b/svr-authpubkey.c @@ -79,7 +79,7 @@ static int checkfileperm(char * filename); /* process a pubkey auth request, sending success or failure message as * appropriate */ -void svr_auth_pubkey() { +void svr_auth_pubkey(int valid_user) { unsigned char testkey; /* whether we're just checking if a key is usable */ char* algo = NULL; /* pubkey algo */ @@ -102,6 +102,15 @@ void svr_auth_pubkey() { keybloblen = buf_getint(ses.payload); keyblob = buf_getptr(ses.payload, keybloblen); + if (!valid_user) { + /* Return failure once we have read the contents of the packet + required to validate a public key. + Avoids blind user enumeration though it isn't possible to prevent + testing for user existence if the public key is known */ + send_msg_userauth_failure(0, 0); + goto out; + } + /* check if the key is valid */ if (checkpubkey(algo, algolen, keyblob, keybloblen) == DROPBEAR_FAILURE) { send_msg_userauth_failure(0, 0); -- cgit v1.2.3 From 28b6111db0f4ced931f2ce4b890a8d109904b8e2 Mon Sep 17 00:00:00 2001 From: François Perrad Date: Wed, 20 Mar 2019 15:09:19 +0100 Subject: use strlcpy & strlcat (#74) * refactor checkpubkeyperms() with safe BSD functions fix gcc8 warnings ``` svr-authpubkey.c: In function 'checkpubkeyperms': svr-authpubkey.c:427:2: warning: 'strncat' specified bound 5 equals source length [-Wstringop-overflow=] strncat(filename, "/.ssh", 5); /* strlen("/.ssh") == 5 */ ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~ svr-authpubkey.c:433:2: warning: 'strncat' specified bound 16 equals source length [-Wstringop-overflow=] strncat(filename, "/authorized_keys", 16); ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ``` see https://www.sudo.ws/todd/papers/strlcpy.html * restore strlcpy in xstrdup see original https://cvsweb.openbsd.org/cgi-bin/cvsweb/src/usr.bin/ssh/xmalloc.c?rev=1.16 --- scpmisc.c | 2 +- svr-authpubkey.c | 9 +++++---- 2 files changed, 6 insertions(+), 5 deletions(-) (limited to 'svr-authpubkey.c') diff --git a/scpmisc.c b/scpmisc.c index 33e1891..c2f053e 100644 --- a/scpmisc.c +++ b/scpmisc.c @@ -102,7 +102,7 @@ xstrdup(const char *str) len = strlen(str) + 1; cp = xmalloc(len); - strncpy(cp, str, len); + strlcpy(cp, str, len); return cp; } diff --git a/svr-authpubkey.c b/svr-authpubkey.c index ae1402d..dafa99a 100644 --- a/svr-authpubkey.c +++ b/svr-authpubkey.c @@ -424,8 +424,9 @@ static int checkpubkeyperms() { /* allocate max required pathname storage, * = path + "/.ssh/authorized_keys" + '\0' = pathlen + 22 */ - filename = m_malloc(len + 22); - strncpy(filename, ses.authstate.pw_dir, len+1); + len += 22; + filename = m_malloc(len); + strlcpy(filename, ses.authstate.pw_dir, len); /* check ~ */ if (checkfileperm(filename) != DROPBEAR_SUCCESS) { @@ -433,13 +434,13 @@ static int checkpubkeyperms() { } /* check ~/.ssh */ - strncat(filename, "/.ssh", 5); /* strlen("/.ssh") == 5 */ + strlcat(filename, "/.ssh", len); if (checkfileperm(filename) != DROPBEAR_SUCCESS) { goto out; } /* now check ~/.ssh/authorized_keys */ - strncat(filename, "/authorized_keys", 16); + strlcat(filename, "/authorized_keys", len); if (checkfileperm(filename) != DROPBEAR_SUCCESS) { goto out; } -- cgit v1.2.3 From 0af22aa8e43722082947bb48fa9ec2990f702c49 Mon Sep 17 00:00:00 2001 From: Patrick Stewart Date: Wed, 20 Mar 2019 14:44:49 +0000 Subject: Support servers without multiple user support (#76) --- default_options.h | 3 +++ svr-agentfwd.c | 8 ++++++++ svr-auth.c | 2 +- svr-authpubkey.c | 4 ++++ svr-chansession.c | 2 ++ 5 files changed, 18 insertions(+), 1 deletion(-) (limited to 'svr-authpubkey.c') diff --git a/default_options.h b/default_options.h index 86fb25a..7365d2c 100644 --- a/default_options.h +++ b/default_options.h @@ -196,6 +196,9 @@ group1 in Dropbear server too */ * authorized_keys file into account */ #define DROPBEAR_SVR_PUBKEY_OPTIONS 1 +/* Disable if your kernel does not have multiple user support */ +#define DROPBEAR_SVR_MULTIUSER 1 + /* Client authentication options */ #define DROPBEAR_CLI_PASSWORD_AUTH 1 #define DROPBEAR_CLI_PUBKEY_AUTH 1 diff --git a/svr-agentfwd.c b/svr-agentfwd.c index cff80f7..ac9475f 100644 --- a/svr-agentfwd.c +++ b/svr-agentfwd.c @@ -151,6 +151,7 @@ void svr_agentcleanup(struct ChanSess * chansess) { if (chansess->agentfile != NULL && chansess->agentdir != NULL) { +#if DROPBEAR_SVR_MULTIUSER /* Remove the dir as the user. That way they can't cause problems except * for themselves */ uid = getuid(); @@ -159,6 +160,7 @@ void svr_agentcleanup(struct ChanSess * chansess) { (seteuid(ses.authstate.pw_uid)) < 0) { dropbear_exit("Failed to set euid"); } +#endif /* 2 for "/" and "\0" */ len = strlen(chansess->agentdir) + strlen(chansess->agentfile) + 2; @@ -170,10 +172,12 @@ void svr_agentcleanup(struct ChanSess * chansess) { rmdir(chansess->agentdir); +#if DROPBEAR_SVR_MULTIUSER if ((seteuid(uid)) < 0 || (setegid(gid)) < 0) { dropbear_exit("Failed to revert euid"); } +#endif m_free(chansess->agentfile); m_free(chansess->agentdir); @@ -216,6 +220,7 @@ static int bindagent(int fd, struct ChanSess * chansess) { gid_t gid; int ret = DROPBEAR_FAILURE; +#if DROPBEAR_SVR_MULTIUSER /* drop to user privs to make the dir/file */ uid = getuid(); gid = getgid(); @@ -223,6 +228,7 @@ static int bindagent(int fd, struct ChanSess * chansess) { (seteuid(ses.authstate.pw_uid)) < 0) { dropbear_exit("Failed to set euid"); } +#endif memset((void*)&addr, 0x0, sizeof(addr)); addr.sun_family = AF_UNIX; @@ -262,10 +268,12 @@ bindsocket: out: +#if DROPBEAR_SVR_MULTIUSER if ((seteuid(uid)) < 0 || (setegid(gid)) < 0) { dropbear_exit("Failed to revert euid"); } +#endif return ret; } diff --git a/svr-auth.c b/svr-auth.c index f952b0b..7575f90 100644 --- a/svr-auth.c +++ b/svr-auth.c @@ -276,7 +276,7 @@ static int checkusername(const char *username, unsigned int userlen) { /* check if we are running as non-root, and login user is different from the server */ uid = geteuid(); - if (uid != 0 && uid != ses.authstate.pw_uid) { + if (!(DROPBEAR_SVR_MULTIUSER && uid == 0) && uid != ses.authstate.pw_uid) { TRACE(("running as nonroot, only server uid is allowed")) dropbear_log(LOG_WARNING, "Login attempt with wrong user %s from %s", diff --git a/svr-authpubkey.c b/svr-authpubkey.c index dafa99a..4f27986 100644 --- a/svr-authpubkey.c +++ b/svr-authpubkey.c @@ -347,6 +347,7 @@ static int checkpubkey(const char* algo, unsigned int algolen, snprintf(filename, len + 22, "%s/.ssh/authorized_keys", ses.authstate.pw_dir); +#if DROPBEAR_SVR_MULTIUSER /* open the file as the authenticating user. */ origuid = getuid(); origgid = getgid(); @@ -354,13 +355,16 @@ static int checkpubkey(const char* algo, unsigned int algolen, (seteuid(ses.authstate.pw_uid)) < 0) { dropbear_exit("Failed to set euid"); } +#endif authfile = fopen(filename, "r"); +#if DROPBEAR_SVR_MULTIUSER if ((seteuid(origuid)) < 0 || (setegid(origgid)) < 0) { dropbear_exit("Failed to revert euid"); } +#endif if (authfile == NULL) { goto out; diff --git a/svr-chansession.c b/svr-chansession.c index 038a0f2..5a5a8c8 100644 --- a/svr-chansession.c +++ b/svr-chansession.c @@ -949,6 +949,7 @@ static void execchild(const void *user_data) { #endif /* HAVE_CLEARENV */ #endif /* DEBUG_VALGRIND */ +#if DROPBEAR_SVR_MULTIUSER /* We can only change uid/gid as root ... */ if (getuid() == 0) { @@ -972,6 +973,7 @@ static void execchild(const void *user_data) { dropbear_exit("Couldn't change user as non-root"); } } +#endif /* set env vars */ addnewvar("USER", ses.authstate.pw_name); -- cgit v1.2.3 From 8c6aaf8d361e95ba3d9b371b86abaae1fd0e436f Mon Sep 17 00:00:00 2001 From: fabriziobertocci Date: Wed, 15 May 2019 09:43:57 -0400 Subject: External Public-Key Authentication API (#72) * Implemented dynamic loading of an external plug-in shared library to delegate public key authentication * Moved conditional compilation of the plugin infrastructure into the configure.ac script to be able to add -ldl to dropbear build only when the flag is enabled * Added tags file to the ignore list * Updated API to have the constructor to return function pointers in the pliugin instance. Added support for passing user name to the checkpubkey function. Added options to the session returned by the plugin and have dropbear to parse and process them * Added -rdynamic to the linker flags when EPKA is enabled * Changed the API to pass a previously created session to the checkPubKey function (created during preauth) * Added documentation to the API * Added parameter addrstring to plugin creation function * Modified the API to retrieve the auth options. Instead of having them as field of the EPKASession struct, they are stored internally (plugin-dependent) in the plugin/session and retrieved through a pointer to a function (in the session) * Changed option string to be a simple char * instead of unsigned char * --- .gitignore | 2 + Makefile.in | 11 +++- common-session.c | 4 ++ configure.ac | 15 ++++++ includes.h | 4 ++ pubkeyapi.h | 151 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ runopts.h | 5 ++ session.h | 15 ++++++ svr-authpubkey.c | 48 +++++++++++++++++- svr-runopts.c | 35 +++++++++++-- svr-session.c | 73 +++++++++++++++++++++++++-- sysoptions.h | 3 ++ 12 files changed, 355 insertions(+), 11 deletions(-) create mode 100644 pubkeyapi.h (limited to 'svr-authpubkey.c') 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 #endif +#if DROPBEAR_EPKA +#include +#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 (default %d, larger may be faster, max 1MB)\n" "-K (0 is never, default %d, in seconds)\n" "-I (0 is never, default %d, in seconds)\n" +#if DROPBEAR_EPKA + "-A [,]\n" + " Enable external public key auth through \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) -- cgit v1.2.3 From fa116e983b4931010e1082dd5c8bf38bbc77718c Mon Sep 17 00:00:00 2001 From: Matt Johnston Date: Wed, 15 May 2019 21:59:45 +0800 Subject: Rename EPKA -> Plugin --- Makefile.in | 8 ++++---- common-session.c | 4 ++-- configure.ac | 14 +++++++------- includes.h | 2 +- pubkeyapi.h | 34 +++++++++++++++++----------------- runopts.h | 2 +- session.h | 12 ++++++------ svr-authpubkey.c | 18 +++++++++--------- svr-runopts.c | 10 +++++----- svr-session.c | 54 +++++++++++++++++++++++++++--------------------------- sysoptions.h | 4 ++-- 11 files changed, 81 insertions(+), 81 deletions(-) (limited to 'svr-authpubkey.c') diff --git a/Makefile.in b/Makefile.in index e363fd9..4bac2f7 100644 --- a/Makefile.in +++ b/Makefile.in @@ -80,13 +80,13 @@ else scpobjs=$(SCPOBJS) endif -ifeq (@DROPBEAR_EPKA@, 1) +ifeq (@DROPBEAR_PLUGIN@, 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 + PLUGIN_LIBS=-ldl -rdynamic else - EPKA_LIBS= + PLUGIN_LIBS= endif VPATH=@srcdir@ @@ -198,7 +198,7 @@ dropbearkey: $(dropbearkeyobjs) dropbearconvert: $(dropbearconvertobjs) dropbear: $(HEADERS) $(LIBTOM_DEPS) Makefile - $(CC) $(LDFLAGS) -o $@$(EXEEXT) $($@objs) $(LIBTOM_LIBS) $(LIBS) @CRYPTLIB@ $(EPKA_LIBS) + $(CC) $(LDFLAGS) -o $@$(EXEEXT) $($@objs) $(LIBTOM_LIBS) $(LIBS) @CRYPTLIB@ $(PLUGIN_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 a6449ca..fc0f9a0 100644 --- a/common-session.c +++ b/common-session.c @@ -147,8 +147,8 @@ void common_session_init(int sock_in, int sock_out) { ses.allowprivport = 0; -#if DROPBEAR_EPKA - ses.epka_session = NULL; +#if DROPBEAR_PLUGIN + ses.plugin_session = NULL; #endif TRACE(("leave session_init")) diff --git a/configure.ac b/configure.ac index bbbfd02..9996c34 100644 --- a/configure.ac +++ b/configure.ac @@ -323,20 +323,20 @@ AC_ARG_ENABLE(shadow, ] ) -AC_ARG_ENABLE(epka, - [ --enable-epka Enable support for External Public Key Authentication plug-in], +AC_ARG_ENABLE(plugin, + [ --enable-plugin Enable support for External Public Key Authentication plug-in], [ - AC_DEFINE(DROPBEAR_EPKA, 1, External Public Key Authentication) + AC_DEFINE(DROPBEAR_PLUGIN, 1, External Public Key Authentication) AC_MSG_NOTICE(Enabling support for External Public Key Authentication) - DROPBEAR_EPKA=1 + DROPBEAR_PLUGIN=1 ], [ - AC_DEFINE(DROPBEAR_EPKA, 0, External Public Key Authentication) - DROPBEAR_EPKA=0 + AC_DEFINE(DROPBEAR_PLUGIN, 0, External Public Key Authentication) + DROPBEAR_PLUGIN=0 ] ) -AC_SUBST(DROPBEAR_EPKA) +AC_SUBST(DROPBEAR_PLUGIN) AC_ARG_ENABLE(fuzz, [ --enable-fuzz Build fuzzing. Not recommended for deployment.], diff --git a/includes.h b/includes.h index 2fa26c4..6432d6e 100644 --- a/includes.h +++ b/includes.h @@ -164,7 +164,7 @@ typedef u_int32_t uint32_t; #include #endif -#if DROPBEAR_EPKA +#if DROPBEAR_PLUGIN #include #endif diff --git a/pubkeyapi.h b/pubkeyapi.h index 9ca9551..21b1f24 100644 --- a/pubkeyapi.h +++ b/pubkeyapi.h @@ -33,21 +33,21 @@ * */ -struct EPKAInstance; -struct EPKASession; +struct PluginInstance; +struct PluginSession; /* 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. + * PluginInstance or PluginSession). 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 +#define DROPBEAR_PLUGIN_VERSION_MAJOR 1 +#define DROPBEAR_PLUGIN_VERSION_MINOR 0 /* Creates an instance of the plugin. @@ -60,7 +60,7 @@ struct EPKASession; * 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 + * If plugin_MINOR < dropbear_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. @@ -68,7 +68,7 @@ struct EPKASession; * 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, +typedef struct PluginInstance *(* PubkeyExtPlugin_newFn)(int verbose, const char *options, const char *addrstring); #define DROPBEAR_PUBKEY_PLUGIN_FNNAME_NEW "plugin_new" @@ -83,8 +83,8 @@ typedef struct EPKAInstance *(* PubkeyExtPlugin_newFn)(int verbose, * Returns DROPBEAR_SUCCESS (0) if success or DROPBEAR_FAILURE (-1) if * authentication fails */ -typedef int (* PubkeyExtPlugin_checkPubKeyFn)(struct EPKAInstance *pluginInstance, - struct EPKASession **sessionInOut, +typedef int (* PubkeyExtPlugin_checkPubKeyFn)(struct PluginInstance *PluginInstance, + struct PluginSession **sessionInOut, const char* algo, unsigned int algolen, const unsigned char* keyblob, @@ -93,18 +93,18 @@ typedef int (* PubkeyExtPlugin_checkPubKeyFn)(struct EPKAInstance *pluginInstanc /* Notify the plugin that auth completed (after signature verification) */ -typedef void (* PubkeyExtPlugin_authSuccessFn)(struct EPKASession *session); +typedef void (* PubkeyExtPlugin_authSuccessFn)(struct PluginSession *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); +typedef void (* PubkeyExtPlugin_sessionDeleteFn)(struct PluginSession *session); /* Deletes the plugin instance */ -typedef void (* PubkeyExtPlugin_deleteFn)(struct EPKAInstance *pluginInstance); +typedef void (* PubkeyExtPlugin_deleteFn)(struct PluginInstance *PluginInstance); -/* The EPKAInstance object - A simple container of the pointer to the functions used +/* The PluginInstance object - A simple container of the pointer to the functions used * by Dropbear. * * A plug-in can extend it to add its own properties @@ -113,7 +113,7 @@ typedef void (* PubkeyExtPlugin_deleteFn)(struct EPKAInstance *pluginInstance); * shared library. * The delete_plugin function should delete the object. */ -struct EPKAInstance { +struct PluginInstance { int api_version[2]; /* 0=Major, 1=Minor */ PubkeyExtPlugin_checkPubKeyFn checkpubkey; /* mandatory */ @@ -129,7 +129,7 @@ struct EPKAInstance { * The returned buffer will be destroyed when the session is deleted. * Option buffer string NULL-terminated */ -typedef char * (* PubkeyExtPlugin_getOptionsFn)(struct EPKASession *session); +typedef char * (* PubkeyExtPlugin_getOptionsFn)(struct PluginSession *session); /* An SSH Session. Created during pre-auth and reused during the authentication. @@ -142,8 +142,8 @@ typedef char * (* PubkeyExtPlugin_getOptionsFn)(struct EPKASession *session); * * Store any optional auth options in the auth_options property of the session. */ -struct EPKASession { - struct EPKAInstance * plugin_instance; +struct PluginSession { + struct PluginInstance * plugin_instance; PubkeyExtPlugin_getOptionsFn get_options; }; diff --git a/runopts.h b/runopts.h index f173678..6a4a94c 100644 --- a/runopts.h +++ b/runopts.h @@ -125,7 +125,7 @@ typedef struct svr_runopts { char * forced_command; -#if DROPBEAR_EPKA +#if DROPBEAR_PLUGIN char *pubkey_plugin; char *pubkey_plugin_options; #endif diff --git a/session.h b/session.h index efade6e..e436882 100644 --- a/session.h +++ b/session.h @@ -38,7 +38,7 @@ #include "chansession.h" #include "dbutil.h" #include "netio.h" -#if DROPBEAR_EPKA +#if DROPBEAR_PLUGIN #include "pubkeyapi.h" #endif @@ -220,8 +220,8 @@ struct sshsession { /* 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; +#if DROPBEAR_PLUGIN + struct PluginSession * plugin_session; #endif }; @@ -248,12 +248,12 @@ struct serversession { pid_t server_pid; #endif -#if DROPBEAR_EPKA +#if DROPBEAR_PLUGIN /* The shared library handle */ - void *epka_plugin_handle; + void *plugin_handle; /* The instance created by the plugin_new function */ - struct EPKAInstance *epka_instance; + struct PluginInstance *plugin_instance; #endif }; diff --git a/svr-authpubkey.c b/svr-authpubkey.c index 9d70bfb..5c234b9 100644 --- a/svr-authpubkey.c +++ b/svr-authpubkey.c @@ -111,12 +111,12 @@ void svr_auth_pubkey(int valid_user) { send_msg_userauth_failure(0, 0); goto out; } -#if DROPBEAR_EPKA - if (svr_ses.epka_instance != NULL) { +#if DROPBEAR_PLUGIN + if (svr_ses.plugin_instance != NULL) { char *options_buf; - if (svr_ses.epka_instance->checkpubkey( - svr_ses.epka_instance, - &ses.epka_session, + if (svr_ses.plugin_instance->checkpubkey( + svr_ses.plugin_instance, + &ses.plugin_session, algo, algolen, keyblob, @@ -126,7 +126,7 @@ void svr_auth_pubkey(int valid_user) { auth_failure = 0; /* Options provided? */ - options_buf = ses.epka_session->get_options(ses.epka_session); + options_buf = ses.plugin_session->get_options(ses.plugin_session); if (options_buf) { struct buf temp_buf = { .data = (unsigned char *)options_buf, @@ -193,10 +193,10 @@ 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)) { +#if DROPBEAR_PLUGIN + if ((ses.plugin_session != NULL) && (svr_ses.plugin_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); + svr_ses.plugin_instance->auth_success(ses.plugin_session); } #endif diff --git a/svr-runopts.c b/svr-runopts.c index 19ce14c..d7a0d5a 100644 --- a/svr-runopts.c +++ b/svr-runopts.c @@ -99,7 +99,7 @@ static void printhelp(const char * progname) { "-W (default %d, larger may be faster, max 1MB)\n" "-K (0 is never, default %d, in seconds)\n" "-I (0 is never, default %d, in seconds)\n" -#if DROPBEAR_EPKA +#if DROPBEAR_PLUGIN "-A [,]\n" " Enable external public key auth through \n" #endif @@ -133,7 +133,7 @@ void svr_getopts(int argc, char ** argv) { char* maxauthtries_arg = NULL; char* keyfile = NULL; char c; -#if DROPBEAR_EPKA +#if DROPBEAR_PLUGIN char* pubkey_plugin = NULL; #endif @@ -163,7 +163,7 @@ void svr_getopts(int argc, char ** argv) { #if DROPBEAR_SVR_REMOTETCPFWD svr_opts.noremotetcp = 0; #endif -#if DROPBEAR_EPKA +#if DROPBEAR_PLUGIN svr_opts.pubkey_plugin = NULL; svr_opts.pubkey_plugin_options = NULL; #endif @@ -285,7 +285,7 @@ void svr_getopts(int argc, char ** argv) { case 'u': /* backwards compatibility with old urandom option */ break; -#if DROPBEAR_EPKA +#if DROPBEAR_PLUGIN case 'A': next = &pubkey_plugin; break; @@ -410,7 +410,7 @@ 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 DROPBEAR_PLUGIN if (pubkey_plugin) { char *args = strchr(pubkey_plugin, ','); if (args) { diff --git a/svr-session.c b/svr-session.c index 3ea7589..47f36b5 100644 --- a/svr-session.c +++ b/svr-session.c @@ -89,15 +89,15 @@ svr_session_cleanup(void) { 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; +#if DROPBEAR_PLUGIN + if (svr_ses.plugin_handle != NULL) { + if (svr_ses.plugin_instance) { + svr_ses.plugin_instance->delete_plugin(svr_ses.plugin_instance); + svr_ses.plugin_instance = NULL; } - dlclose(svr_ses.epka_plugin_handle); - svr_ses.epka_plugin_handle = NULL; + dlclose(svr_ses.plugin_handle); + svr_ses.plugin_handle = NULL; } #endif } @@ -122,10 +122,10 @@ 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 DROPBEAR_PLUGIN + /* Initializes the PLUGIN Plugin */ + svr_ses.plugin_handle = NULL; + svr_ses.plugin_instance = NULL; if (svr_opts.pubkey_plugin) { #if DEBUG_TRACE const int verbose = debug_trace; @@ -135,33 +135,33 @@ void svr_session(int sock, int childpipe) { 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) { + svr_ses.plugin_handle = dlopen(svr_opts.pubkey_plugin, RTLD_NOW); + if (svr_ses.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); + pluginConstructor = (PubkeyExtPlugin_newFn)dlsym(svr_ses.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) { + svr_ses.plugin_instance = pluginConstructor(verbose, svr_opts.pubkey_plugin_options, svr_ses.addrstring); + if (svr_ses.plugin_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) ) { + if ( (svr_ses.plugin_instance->api_version[0] != DROPBEAR_PLUGIN_VERSION_MAJOR) || + (svr_ses.plugin_instance->api_version[1] < DROPBEAR_PLUGIN_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]); + DROPBEAR_PLUGIN_VERSION_MAJOR, DROPBEAR_PLUGIN_VERSION_MINOR, + svr_ses.plugin_instance->api_version[0], svr_ses.plugin_instance->api_version[1]); } - if (svr_ses.epka_instance->api_version[1] > DROPBEAR_EPKA_VERSION_MINOR) { + if (svr_ses.plugin_instance->api_version[1] > DROPBEAR_PLUGIN_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_PLUGIN_VERSION_MAJOR, DROPBEAR_PLUGIN_VERSION_MINOR, + svr_ses.plugin_instance->api_version[0], svr_ses.plugin_instance->api_version[1]); } dropbear_log(LOG_INFO, "successfully loaded and initialized pubkey plugin '%s'", svr_opts.pubkey_plugin); } @@ -209,11 +209,11 @@ 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); +#if DROPBEAR_PLUGIN + if ((ses.plugin_session != NULL)) { + svr_ses.plugin_instance->delete_session(ses.plugin_session); } - ses.epka_session = NULL; + ses.plugin_session = NULL; #endif /* Render the formatted exit message */ diff --git a/sysoptions.h b/sysoptions.h index 58604f0..cfd5469 100644 --- a/sysoptions.h +++ b/sysoptions.h @@ -243,8 +243,8 @@ 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)" +#if (DROPBEAR_PLUGIN && !DROPBEAR_SVR_PUBKEY_AUTH) + #error "You must define DROPBEAR_SVR_PUBKEY_AUTH in order to use plugins" #endif #if !(DROPBEAR_AES128 || DROPBEAR_3DES || DROPBEAR_AES256 || DROPBEAR_BLOWFISH \ -- cgit v1.2.3 From 7dc2f36c3e2d21455ae432da4d8f338e7dc0668c Mon Sep 17 00:00:00 2001 From: Matt Johnston Date: Mon, 6 Apr 2020 23:18:26 +0800 Subject: use sigtype where appropriate --- algo.h | 1 - cli-authpubkey.c | 34 ++++++++++++------- cli-kex.c | 11 +++--- common-algo.c | 23 +++---------- common-kex.c | 9 +++-- dss.h | 2 +- ed25519.h | 2 +- fuzzer-pubkey.c | 2 +- keyimport.c | 3 ++ rsa.c | 101 ++++++++++++++++++++++++++++++++++++------------------- rsa.h | 11 +++--- session.h | 3 +- signkey.c | 78 ++++++++++++++++++++++++++++++++---------- signkey.h | 24 ++++++++----- ssh.h | 4 ++- svr-authpubkey.c | 53 ++++++++++++++--------------- svr-kex.c | 2 +- sysoptions.h | 12 +++++-- 18 files changed, 233 insertions(+), 142 deletions(-) (limited to 'svr-authpubkey.c') diff --git a/algo.h b/algo.h index b12fb94..1a6d384 100644 --- a/algo.h +++ b/algo.h @@ -112,7 +112,6 @@ struct dropbear_kex { const struct ltc_hash_descriptor *hash_desc; }; -int have_algo(const char* algo, size_t algolen, const algo_type algos[]); void buf_put_algolist(buffer * buf, const algo_type localalgos[]); enum kexguess2_used { diff --git a/cli-authpubkey.c b/cli-authpubkey.c index 7cee164..d1cc1f6 100644 --- a/cli-authpubkey.c +++ b/cli-authpubkey.c @@ -33,7 +33,7 @@ #include "agentfwd.h" #if DROPBEAR_CLI_PUBKEY_AUTH -static void send_msg_userauth_pubkey(sign_key *key, int type, int realsign); +static void send_msg_userauth_pubkey(sign_key *key, enum signkey_type sigtype, int realsign); /* Called when we receive a SSH_MSG_USERAUTH_FAILURE for a pubkey request. * We use it to remove the key we tried from the list */ @@ -58,14 +58,15 @@ void recv_msg_userauth_pk_ok() { buffer* keybuf = NULL; char* algotype = NULL; unsigned int algolen; - enum signkey_type keytype; + enum signkey_type sigtype, keytype; unsigned int remotelen; TRACE(("enter recv_msg_userauth_pk_ok")) algotype = buf_getstring(ses.payload, &algolen); - keytype = signkey_type_from_name(algotype, algolen); - TRACE(("recv_msg_userauth_pk_ok: type %d", keytype)) + sigtype = signature_type_from_name(algotype, algolen); + keytype = signkey_type_from_signature(sigtype); + TRACE(("recv_msg_userauth_pk_ok: type %d", sigtype)) m_free(algotype); keybuf = buf_new(MAX_PUBKEY_SIZE); @@ -120,9 +121,10 @@ void recv_msg_userauth_pk_ok() { TRACE(("leave recv_msg_userauth_pk_ok")) } -void cli_buf_put_sign(buffer* buf, sign_key *key, int type, +static void cli_buf_put_sign(buffer* buf, sign_key *key, enum signkey_type sigtype, const buffer *data_buf) { #if DROPBEAR_CLI_AGENTFWD + // TODO: rsa-sha256 agent if (key->source == SIGNKEY_SOURCE_AGENT) { /* Format the agent signature ourselves, as buf_put_sign would. */ buffer *sigblob; @@ -133,16 +135,16 @@ void cli_buf_put_sign(buffer* buf, sign_key *key, int type, } else #endif /* DROPBEAR_CLI_AGENTFWD */ { - buf_put_sign(buf, key, type, data_buf); + buf_put_sign(buf, key, sigtype, data_buf); } } -/* TODO: make it take an agent reference to use as well */ -static void send_msg_userauth_pubkey(sign_key *key, int type, int realsign) { +static void send_msg_userauth_pubkey(sign_key *key, enum signkey_type sigtype, int realsign) { const char *algoname = NULL; unsigned int algolen; buffer* sigbuf = NULL; + enum signkey_type keytype = signkey_type_from_signature(sigtype); TRACE(("enter send_msg_userauth_pubkey")) CHECKCLEARTOWRITE(); @@ -160,10 +162,9 @@ static void send_msg_userauth_pubkey(sign_key *key, int type, int realsign) { buf_putbyte(ses.writepayload, realsign); - algoname = signkey_name_from_type(type, &algolen); - + algoname = signature_name_from_type(sigtype, &algolen); buf_putstring(ses.writepayload, algoname, algolen); - buf_put_pub_key(ses.writepayload, key, type); + buf_put_pub_key(ses.writepayload, key, keytype); if (realsign) { TRACE(("realsign")) @@ -172,7 +173,7 @@ static void send_msg_userauth_pubkey(sign_key *key, int type, int realsign) { sigbuf = buf_new(4 + ses.session_id->len + ses.writepayload->len); buf_putbufstring(sigbuf, ses.session_id); buf_putbytes(sigbuf, ses.writepayload->data, ses.writepayload->len); - cli_buf_put_sign(ses.writepayload, key, type, sigbuf); + cli_buf_put_sign(ses.writepayload, key, sigtype, sigbuf); buf_free(sigbuf); /* Nothing confidential in the buffer */ } @@ -195,8 +196,15 @@ int cli_auth_pubkey() { if (cli_opts.privkeys->first) { sign_key * key = (sign_key*)cli_opts.privkeys->first->item; + enum signkey_type sigtype = key->type; /* Send a trial request */ - send_msg_userauth_pubkey(key, key->type, 0); +#if DROPBEAR_RSA && DROPBEAR_RSA_SHA256 + // TODO: use ext-info to choose rsa kind + if (sigtype == DROPBEAR_SIGNKEY_RSA) { + sigtype = DROPBEAR_SIGNKEY_RSA_SHA256; + } +#endif + send_msg_userauth_pubkey(key, sigtype, 0); cli_ses.lastprivkey = key; TRACE(("leave cli_auth_pubkey-success")) return 1; diff --git a/cli-kex.c b/cli-kex.c index 7cefb5f..7337ffd 100644 --- a/cli-kex.c +++ b/cli-kex.c @@ -94,7 +94,7 @@ void send_msg_kexdh_init() { void recv_msg_kexdh_reply() { sign_key *hostkey = NULL; - unsigned int type, keybloblen; + unsigned int keytype, keybloblen; unsigned char* keyblob = NULL; TRACE(("enter recv_msg_kexdh_reply")) @@ -102,8 +102,8 @@ void recv_msg_kexdh_reply() { if (cli_ses.kex_state != KEXDH_INIT_SENT) { dropbear_exit("Received out-of-order kexdhreply"); } - type = ses.newkeys->algo_hostkey; - TRACE(("type is %d", type)) + keytype = ses.newkeys->algo_hostkey; + TRACE(("keytype is %d", keytype)) hostkey = new_sign_key(); keybloblen = buf_getint(ses.payload); @@ -114,7 +114,7 @@ void recv_msg_kexdh_reply() { checkhostkey(keyblob, keybloblen); } - if (buf_get_pub_key(ses.payload, hostkey, &type) != DROPBEAR_SUCCESS) { + if (buf_get_pub_key(ses.payload, hostkey, &keytype) != DROPBEAR_SUCCESS) { TRACE(("failed getting pubkey")) dropbear_exit("Bad KEX packet"); } @@ -173,7 +173,8 @@ void recv_msg_kexdh_reply() { #endif cli_ses.param_kex_algo = NULL; - if (buf_verify(ses.payload, hostkey, ses.hash) != DROPBEAR_SUCCESS) { + if (buf_verify(ses.payload, hostkey, ses.newkeys->algo_signature, + ses.hash) != DROPBEAR_SUCCESS) { dropbear_exit("Bad hostkey signature"); } diff --git a/common-algo.c b/common-algo.c index 558aad2..28f6744 100644 --- a/common-algo.c +++ b/common-algo.c @@ -237,8 +237,13 @@ algo_type sshhostkey[] = { #endif #endif #if DROPBEAR_RSA +#if DROPBEAR_RSA_SHA256 + {"rsa-sha2-256", DROPBEAR_SIGNKEY_RSA_SHA256, NULL, 1, NULL}, +#endif +#if DROPBEAR_RSA_SHA1 {"ssh-rsa", DROPBEAR_SIGNKEY_RSA, NULL, 1, NULL}, #endif +#endif #if DROPBEAR_DSS {"ssh-dss", DROPBEAR_SIGNKEY_DSS, NULL, 1, NULL}, #endif @@ -311,24 +316,6 @@ algo_type sshkex[] = { {NULL, 0, NULL, 0, NULL} }; -/* algolen specifies the length of algo, algos is our local list to match - * against. - * Returns DROPBEAR_SUCCESS if we have a match for algo, DROPBEAR_FAILURE - * otherwise */ -int have_algo(const char* algo, size_t algolen, const algo_type algos[]) { - - int i; - - for (i = 0; algos[i].name != NULL; i++) { - if (strlen(algos[i].name) == algolen - && (strncmp(algos[i].name, algo, algolen) == 0)) { - return DROPBEAR_SUCCESS; - } - } - - return DROPBEAR_FAILURE; -} - /* Output a comma separated list of algorithms to a buffer */ void buf_put_algolist(buffer * buf, const algo_type localalgos[]) { diff --git a/common-kex.c b/common-kex.c index 16b7e27..c1392c1 100644 --- a/common-kex.c +++ b/common-kex.c @@ -111,7 +111,8 @@ void send_msg_kexinit() { if (ses.send_kex_first_guess) { ses.newkeys->algo_kex = sshkex[0].data; - ses.newkeys->algo_hostkey = sshhostkey[0].val; + ses.newkeys->algo_signature = sshhostkey[0].val; + ses.newkeys->algo_hostkey = signkey_type_from_signature(ses.newkeys->algo_signature); ses.send_kex_first_guess(); } @@ -152,6 +153,7 @@ static void switch_keys() { TRACE(("switch_keys done")) ses.keys->algo_kex = ses.newkeys->algo_kex; ses.keys->algo_hostkey = ses.newkeys->algo_hostkey; + ses.keys->algo_signature = ses.newkeys->algo_signature; ses.keys->allow_compress = 0; m_free(ses.newkeys); ses.newkeys = NULL; @@ -847,8 +849,9 @@ static void read_kex_algos() { erralgo = "hostkey"; goto error; } - TRACE(("hostkey algo %s", algo->name)) - ses.newkeys->algo_hostkey = algo->val; + TRACE(("signature algo %s", algo->name)) + ses.newkeys->algo_signature = algo->val; + ses.newkeys->algo_hostkey = signkey_type_from_signature(ses.newkeys->algo_signature); /* encryption_algorithms_client_to_server */ c2s_cipher_algo = buf_match_algo(ses.payload, sshciphers, NULL, NULL); diff --git a/dss.h b/dss.h index 2eaa2f5..40806e5 100644 --- a/dss.h +++ b/dss.h @@ -30,7 +30,7 @@ #if DROPBEAR_DSS -typedef struct { +typedef struct dropbear_DSS_Key { mp_int* p; mp_int* q; diff --git a/ed25519.h b/ed25519.h index 16c0d7b..622111a 100644 --- a/ed25519.h +++ b/ed25519.h @@ -32,7 +32,7 @@ #define CURVE25519_LEN 32 -typedef struct { +typedef struct dropbear_ED25519_Key { unsigned char priv[CURVE25519_LEN]; unsigned char pub[CURVE25519_LEN]; diff --git a/fuzzer-pubkey.c b/fuzzer-pubkey.c index 033f496..7c12cdc 100644 --- a/fuzzer-pubkey.c +++ b/fuzzer-pubkey.c @@ -27,7 +27,7 @@ int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) { unsigned int algolen; char* algoname = buf_getstring(keyblob, &algolen); - if (have_algo(algoname, algolen, sshhostkey) == DROPBEAR_FAILURE) { + if (signature_type_from_name(algoname, algolen) == DROPBEAR_SIGNKEY_NONE) { dropbear_exit("fuzzer imagined a bogus algorithm"); } diff --git a/keyimport.c b/keyimport.c index ad0c530..52ddf6e 100644 --- a/keyimport.c +++ b/keyimport.c @@ -36,6 +36,9 @@ #include "dbutil.h" #include "ecc.h" #include "ssh.h" +#include "rsa.h" +#include "dss.h" +#include "ed25519.h" static const unsigned char OSSH_PKEY_BLOB[] = "openssh-key-v1\0" /* AUTH_MAGIC */ diff --git a/rsa.c b/rsa.c index 67b90f7..ef737c8 100644 --- a/rsa.c +++ b/rsa.c @@ -35,11 +35,16 @@ #include "buffer.h" #include "ssh.h" #include "dbrandom.h" +#include "signkey.h" #if DROPBEAR_RSA +#if !(DROPBEAR_RSA_SHA1 || DROPBEAR_RSA_SHA256) +#error Somehow RSA was enabled with neither DROPBEAR_RSA_SHA1 nor DROPBEAR_RSA_SHA256 +#endif + static void rsa_pad_em(const dropbear_rsa_key * key, - const buffer *data_buf, mp_int * rsa_em); + const buffer *data_buf, mp_int * rsa_em, enum signkey_type sigtype); /* Load a public rsa key from a buffer, initialising the values. * The key will have the same format as buf_put_rsa_key. @@ -191,7 +196,8 @@ void buf_put_rsa_priv_key(buffer* buf, const dropbear_rsa_key *key) { #if DROPBEAR_SIGNKEY_VERIFY /* Verify a signature in buf, made on data by the key given. * Returns DROPBEAR_SUCCESS or DROPBEAR_FAILURE */ -int buf_rsa_verify(buffer * buf, const dropbear_rsa_key *key, const buffer *data_buf) { +int buf_rsa_verify(buffer * buf, const dropbear_rsa_key *key, + enum signkey_type sigtype, const buffer *data_buf) { unsigned int slen; DEF_MP_INT(rsa_s); DEF_MP_INT(rsa_mdash); @@ -223,7 +229,7 @@ int buf_rsa_verify(buffer * buf, const dropbear_rsa_key *key, const buffer *data } /* create the magic PKCS padded value */ - rsa_pad_em(key, data_buf, &rsa_em); + rsa_pad_em(key, data_buf, &rsa_em, sigtype); if (mp_exptmod(&rsa_s, key->e, key->n, &rsa_mdash) != MP_OKAY) { TRACE(("failed exptmod rsa_s")) @@ -246,8 +252,10 @@ out: /* Sign the data presented with key, writing the signature contents * to the buffer */ -void buf_put_rsa_sign(buffer* buf, const dropbear_rsa_key *key, const buffer *data_buf) { - unsigned int nsize, ssize; +void buf_put_rsa_sign(buffer* buf, const dropbear_rsa_key *key, + enum signkey_type sigtype, const buffer *data_buf) { + const char *name = NULL; + unsigned int nsize, ssize, namelen = 0; unsigned int i; DEF_MP_INT(rsa_s); DEF_MP_INT(rsa_tmp1); @@ -259,7 +267,7 @@ void buf_put_rsa_sign(buffer* buf, const dropbear_rsa_key *key, const buffer *da m_mp_init_multi(&rsa_s, &rsa_tmp1, &rsa_tmp2, &rsa_tmp3, NULL); - rsa_pad_em(key, data_buf, &rsa_tmp1); + rsa_pad_em(key, data_buf, &rsa_tmp1, sigtype); /* the actual signing of the padded data */ @@ -311,7 +319,8 @@ void buf_put_rsa_sign(buffer* buf, const dropbear_rsa_key *key, const buffer *da mp_clear_multi(&rsa_tmp1, &rsa_tmp2, &rsa_tmp3, NULL); /* create the signature to return */ - buf_putstring(buf, SSH_SIGNKEY_RSA, SSH_SIGNKEY_RSA_LEN); + name = signature_name_from_type(sigtype, &namelen); + buf_putstring(buf, name, namelen); nsize = mp_unsigned_bin_size(key->n); @@ -340,51 +349,73 @@ void buf_put_rsa_sign(buffer* buf, const dropbear_rsa_key *key, const buffer *da TRACE(("leave buf_put_rsa_sign")) } -/* Creates the message value as expected by PKCS, see rfc2437 etc */ -/* format to be padded to is: - * EM = 01 | FF* | 00 | prefix | hash - * - * where FF is repeated enough times to make EM one byte - * shorter than the size of key->n - * - * prefix is the ASN1 designator prefix, - * hex 30 21 30 09 06 05 2B 0E 03 02 1A 05 00 04 14 - * - * rsa_em must be a pointer to an initialised mp_int. - */ +/* Creates the message value as expected by PKCS, + see rfc8017 section 9.2 */ static void rsa_pad_em(const dropbear_rsa_key * key, - const buffer *data_buf, mp_int * rsa_em) { + const buffer *data_buf, mp_int * rsa_em, enum signkey_type sigtype) { + /* EM = 0x00 || 0x01 || PS || 0x00 || T + PS is padding of 0xff to make EM the size of key->n + + T is the DER encoding of the hash alg (sha1 or sha256) + */ - /* ASN1 designator (including the 0x00 preceding) */ - const unsigned char rsa_asn1_magic[] = - {0x00, 0x30, 0x21, 0x30, 0x09, 0x06, 0x05, 0x2b, + /* From rfc8017 page 46 */ +#if DROPBEAR_RSA_SHA1 + const unsigned char T_sha1[] = + {0x30, 0x21, 0x30, 0x09, 0x06, 0x05, 0x2b, 0x0e, 0x03, 0x02, 0x1a, 0x05, 0x00, 0x04, 0x14}; - const unsigned int RSA_ASN1_MAGIC_LEN = 16; +#endif +#if DROPBEAR_RSA_SHA256 + const unsigned char T_sha256[] = + {0x30, 0x31, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, + 0x65, 0x03, 0x04, 0x02, 0x01, 0x05, 0x00, 0x04, 0x20}; +#endif + int Tlen = 0; + const unsigned char *T = NULL; + const struct ltc_hash_descriptor *hash_desc = NULL; buffer * rsa_EM = NULL; hash_state hs; unsigned int nsize; + + switch (sigtype) { +#if DROPBEAR_RSA_SHA1 + case DROPBEAR_SIGNKEY_RSA: + Tlen = sizeof(T_sha1); + T = T_sha1; + hash_desc = &sha1_desc; + break; +#endif +#if DROPBEAR_RSA_SHA256 + case DROPBEAR_SIGNKEY_RSA_SHA256: + Tlen = sizeof(T_sha256); + T = T_sha256; + hash_desc = &sha256_desc; + break; +#endif + default: + assert(0); + } - dropbear_assert(key != NULL); nsize = mp_unsigned_bin_size(key->n); - rsa_EM = buf_new(nsize-1); + rsa_EM = buf_new(nsize); /* type byte */ + buf_putbyte(rsa_EM, 0x00); buf_putbyte(rsa_EM, 0x01); - /* Padding with 0xFF bytes */ - while(rsa_EM->pos != rsa_EM->size - RSA_ASN1_MAGIC_LEN - SHA1_HASH_SIZE) { + /* Padding with PS 0xFF bytes */ + while(rsa_EM->pos != rsa_EM->size - (1 + Tlen + hash_desc->hashsize)) { buf_putbyte(rsa_EM, 0xff); } + buf_putbyte(rsa_EM, 0x00); /* Magic ASN1 stuff */ - memcpy(buf_getwriteptr(rsa_EM, RSA_ASN1_MAGIC_LEN), - rsa_asn1_magic, RSA_ASN1_MAGIC_LEN); - buf_incrwritepos(rsa_EM, RSA_ASN1_MAGIC_LEN); + buf_putbytes(rsa_EM, T, Tlen); /* The hash of the data */ - sha1_init(&hs); - sha1_process(&hs, data_buf->data, data_buf->len); - sha1_done(&hs, buf_getwriteptr(rsa_EM, SHA1_HASH_SIZE)); - buf_incrwritepos(rsa_EM, SHA1_HASH_SIZE); + hash_desc->init(&hs); + hash_desc->process(&hs, data_buf->data, data_buf->len); + hash_desc->done(&hs, buf_getwriteptr(rsa_EM, hash_desc->hashsize)); + buf_incrwritepos(rsa_EM, hash_desc->hashsize); dropbear_assert(rsa_EM->pos == rsa_EM->size); diff --git a/rsa.h b/rsa.h index 4fee2e6..39e6004 100644 --- a/rsa.h +++ b/rsa.h @@ -26,13 +26,12 @@ #define DROPBEAR_RSA_H_ #include "includes.h" +#include "signkey.h" #include "buffer.h" #if DROPBEAR_RSA -#define RSA_SIGNATURE_SIZE (4+7+4+40) - -typedef struct { +typedef struct dropbear_RSA_Key { mp_int* n; mp_int* e; @@ -43,9 +42,11 @@ typedef struct { } dropbear_rsa_key; -void buf_put_rsa_sign(buffer* buf, const dropbear_rsa_key *key, const buffer *data_buf); +void buf_put_rsa_sign(buffer* buf, const dropbear_rsa_key *key, + enum signkey_type sigtype, const buffer *data_buf); #if DROPBEAR_SIGNKEY_VERIFY -int buf_rsa_verify(buffer * buf, const dropbear_rsa_key *key, const buffer *data_buf); +int buf_rsa_verify(buffer * buf, const dropbear_rsa_key *key, + enum signkey_type sigtype, const buffer *data_buf); #endif int buf_get_rsa_pub_key(buffer* buf, dropbear_rsa_key *key); int buf_get_rsa_priv_key(buffer* buf, dropbear_rsa_key *key); diff --git a/session.h b/session.h index e436882..ebfc915 100644 --- a/session.h +++ b/session.h @@ -92,7 +92,8 @@ struct key_context { struct key_context_directional trans; const struct dropbear_kex *algo_kex; - int algo_hostkey; + int algo_hostkey; /* server key type */ + int algo_signature; /* server signature type */ int allow_compress; /* whether compression has started (useful in zlib@openssh.com delayed compression case) */ diff --git a/signkey.c b/signkey.c index a0af44a..27b09a3 100644 --- a/signkey.c +++ b/signkey.c @@ -28,6 +28,9 @@ #include "buffer.h" #include "ssh.h" #include "ecdsa.h" +#include "rsa.h" +#include "dss.h" +#include "ed25519.h" static const char * const signkey_names[DROPBEAR_SIGNKEY_NUM_NAMED] = { #if DROPBEAR_RSA @@ -44,6 +47,7 @@ static const char * const signkey_names[DROPBEAR_SIGNKEY_NUM_NAMED] = { #if DROPBEAR_ED25519 "ssh-ed25519", #endif /* DROPBEAR_ED25519 */ + /* "rsa-sha2-256" is special-cased below since it is only a signature name, not key type */ }; /* malloc a new sign_key and set the dss and rsa keys to NULL */ @@ -105,6 +109,38 @@ enum signkey_type signkey_type_from_name(const char* name, unsigned int namelen) return DROPBEAR_SIGNKEY_NONE; } +/* Special case for rsa-sha2-256. This could be generalised if more + signature names are added that aren't 1-1 with public key names */ +const char* signature_name_from_type(enum signkey_type type, unsigned int *namelen) { +#if DROPBEAR_RSA_SHA256 + if (type == DROPBEAR_SIGNKEY_RSA_SHA256) { + *namelen = strlen(SSH_SIGNKEY_RSA_SHA256); + return SSH_SIGNKEY_RSA_SHA256; + } +#endif + return signkey_name_from_type(type, namelen); +} + +enum signkey_type signature_type_from_name(const char* name, unsigned int namelen) { +#if DROPBEAR_RSA_SHA256 + if (namelen == strlen(SSH_SIGNKEY_RSA_SHA256) + && memcmp(name, SSH_SIGNKEY_RSA_SHA256, namelen) == 0) { + return DROPBEAR_SIGNKEY_RSA_SHA256; + } +#endif + return signkey_type_from_name(name, namelen); +} + +enum signkey_type signkey_type_from_signature(enum signkey_type sigtype) { +#if DROPBEAR_RSA_SHA256 + if (sigtype == DROPBEAR_SIGNKEY_RSA_SHA256) { + return DROPBEAR_SIGNKEY_RSA; + } +#endif + assert(sigtype < DROPBEAR_SIGNKEY_NUM_NAMED); + return sigtype; +} + /* Returns a pointer to the key part specific to "type". Be sure to check both (ret != NULL) and (*ret != NULL) */ void ** @@ -526,31 +562,31 @@ char * sign_key_fingerprint(const unsigned char* keyblob, unsigned int keybloble #endif } -void buf_put_sign(buffer* buf, sign_key *key, enum signkey_type type, +void buf_put_sign(buffer* buf, sign_key *key, enum signkey_type sigtype, const buffer *data_buf) { - buffer *sigblob; - sigblob = buf_new(MAX_PUBKEY_SIZE); + buffer *sigblob = buf_new(MAX_PUBKEY_SIZE); + enum signkey_type keytype = signkey_type_from_signature(sigtype); #if DROPBEAR_DSS - if (type == DROPBEAR_SIGNKEY_DSS) { + if (keytype == DROPBEAR_SIGNKEY_DSS) { buf_put_dss_sign(sigblob, key->dsskey, data_buf); } #endif #if DROPBEAR_RSA - if (type == DROPBEAR_SIGNKEY_RSA) { - buf_put_rsa_sign(sigblob, key->rsakey, data_buf); + if (keytype == DROPBEAR_SIGNKEY_RSA) { + buf_put_rsa_sign(sigblob, key->rsakey, sigtype, data_buf); } #endif #if DROPBEAR_ECDSA - if (signkey_is_ecdsa(type)) { - ecc_key **eck = (ecc_key**)signkey_key_ptr(key, type); + if (signkey_is_ecdsa(keytype)) { + ecc_key **eck = (ecc_key**)signkey_key_ptr(key, keytype); if (eck && *eck) { buf_put_ecdsa_sign(sigblob, *eck, data_buf); } } #endif #if DROPBEAR_ED25519 - if (type == DROPBEAR_SIGNKEY_ED25519) { + if (keytype == DROPBEAR_SIGNKEY_ED25519) { buf_put_ed25519_sign(sigblob, key->ed25519key, data_buf); } #endif @@ -567,21 +603,27 @@ void buf_put_sign(buffer* buf, sign_key *key, enum signkey_type type, * If FAILURE is returned, the position of * buf is undefined. If SUCCESS is returned, buf will be positioned after the * signature blob */ -int buf_verify(buffer * buf, sign_key *key, const buffer *data_buf) { +int buf_verify(buffer * buf, sign_key *key, enum signkey_type expect_sigtype, const buffer *data_buf) { char *type_name = NULL; unsigned int type_name_len = 0; - enum signkey_type type; + enum signkey_type sigtype, keytype; TRACE(("enter buf_verify")) buf_getint(buf); /* blob length */ type_name = buf_getstring(buf, &type_name_len); - type = signkey_type_from_name(type_name, type_name_len); + sigtype = signature_type_from_name(type_name, type_name_len); m_free(type_name); + if (expect_sigtype != DROPBEAR_SIGNKEY_ANY + && expect_sigtype != sigtype) { + dropbear_exit("Non-matching signing type"); + } + + keytype = signkey_type_from_signature(sigtype); #if DROPBEAR_DSS - if (type == DROPBEAR_SIGNKEY_DSS) { + if (keytype == DROPBEAR_SIGNKEY_DSS) { if (key->dsskey == NULL) { dropbear_exit("No DSS key to verify signature"); } @@ -590,23 +632,23 @@ int buf_verify(buffer * buf, sign_key *key, const buffer *data_buf) { #endif #if DROPBEAR_RSA - if (type == DROPBEAR_SIGNKEY_RSA) { + if (keytype == DROPBEAR_SIGNKEY_RSA) { if (key->rsakey == NULL) { dropbear_exit("No RSA key to verify signature"); } - return buf_rsa_verify(buf, key->rsakey, data_buf); + return buf_rsa_verify(buf, key->rsakey, sigtype, data_buf); } #endif #if DROPBEAR_ECDSA - if (signkey_is_ecdsa(type)) { - ecc_key **eck = (ecc_key**)signkey_key_ptr(key, type); + if (signkey_is_ecdsa(keytype)) { + ecc_key **eck = (ecc_key**)signkey_key_ptr(key, keytype); if (eck && *eck) { return buf_ecdsa_verify(buf, *eck, data_buf); } } #endif #if DROPBEAR_ED25519 - if (type == DROPBEAR_SIGNKEY_ED25519) { + if (keytype == DROPBEAR_SIGNKEY_ED25519) { if (key->ed25519key == NULL) { dropbear_exit("No Ed25519 key to verify signature"); } diff --git a/signkey.h b/signkey.h index fa39a02..78b237b 100644 --- a/signkey.h +++ b/signkey.h @@ -26,9 +26,11 @@ #define DROPBEAR_SIGNKEY_H_ #include "buffer.h" -#include "dss.h" -#include "rsa.h" -#include "ed25519.h" + +/* Forward declarations */ +struct dropbear_DSS_Key; +struct dropbear_RSA_Key; +struct dropbear_ED25519_Key; enum signkey_type { #if DROPBEAR_RSA @@ -47,6 +49,9 @@ enum signkey_type { #endif DROPBEAR_SIGNKEY_NUM_NAMED, DROPBEAR_SIGNKEY_ECDSA_KEYGEN = 70, /* just "ecdsa" for keygen */ +#if DROPBEAR_RSA_SHA256 + DROPBEAR_SIGNKEY_RSA_SHA256, /* rsa-sha2-256 signature. has a ssh-rsa key */ +#endif DROPBEAR_SIGNKEY_ANY = 80, DROPBEAR_SIGNKEY_NONE = 90, }; @@ -66,10 +71,10 @@ struct SIGN_key { char *filename; #if DROPBEAR_DSS - dropbear_dss_key * dsskey; + struct dropbear_DSS_Key * dsskey; #endif #if DROPBEAR_RSA - dropbear_rsa_key * rsakey; + struct dropbear_RSA_Key * rsakey; #endif #if DROPBEAR_ECDSA #if DROPBEAR_ECC_256 @@ -83,7 +88,7 @@ struct SIGN_key { #endif #endif #if DROPBEAR_ED25519 - dropbear_ed25519_key * ed25519key; + struct dropbear_ED25519_Key * ed25519key; #endif }; @@ -92,14 +97,17 @@ typedef struct SIGN_key sign_key; sign_key * new_sign_key(void); const char* signkey_name_from_type(enum signkey_type type, unsigned int *namelen); enum signkey_type signkey_type_from_name(const char* name, unsigned int namelen); +const char* signature_name_from_type(enum signkey_type type, unsigned int *namelen); +enum signkey_type signature_type_from_name(const char* name, unsigned int namelen); +enum signkey_type signkey_type_from_signature(enum signkey_type sigtype); int buf_get_pub_key(buffer *buf, sign_key *key, enum signkey_type *type); int buf_get_priv_key(buffer* buf, sign_key *key, enum signkey_type *type); void buf_put_pub_key(buffer* buf, sign_key *key, enum signkey_type type); void buf_put_priv_key(buffer* buf, sign_key *key, enum signkey_type type); void sign_key_free(sign_key *key); -void buf_put_sign(buffer* buf, sign_key *key, enum signkey_type type, const buffer *data_buf); +void buf_put_sign(buffer* buf, sign_key *key, enum signkey_type sigtype, const buffer *data_buf); #if DROPBEAR_SIGNKEY_VERIFY -int buf_verify(buffer * buf, sign_key *key, const buffer *data_buf); +int buf_verify(buffer * buf, sign_key *key, enum signkey_type type, const buffer *data_buf); char * sign_key_fingerprint(const unsigned char* keyblob, unsigned int keybloblen); #endif int cmp_base64_key(const unsigned char* keyblob, unsigned int keybloblen, diff --git a/ssh.h b/ssh.h index db723b8..fb7f9f2 100644 --- a/ssh.h +++ b/ssh.h @@ -100,13 +100,15 @@ #define SSH_SERVICE_CONNECTION "ssh-connection" #define SSH_SERVICE_CONNECTION_LEN 14 -/* public key types */ +/* public/signature key types */ #define SSH_SIGNKEY_DSS "ssh-dss" #define SSH_SIGNKEY_DSS_LEN 7 #define SSH_SIGNKEY_RSA "ssh-rsa" #define SSH_SIGNKEY_RSA_LEN 7 #define SSH_SIGNKEY_ED25519 "ssh-ed25519" #define SSH_SIGNKEY_ED25519_LEN 11 +/* signature type */ +#define SSH_SIGNKEY_RSA_SHA256 "rsa-sha2-256" /* Agent commands. These aren't part of the spec, and are defined * only on the openssh implementation. */ diff --git a/svr-authpubkey.c b/svr-authpubkey.c index 5c234b9..a52667c 100644 --- a/svr-authpubkey.c +++ b/svr-authpubkey.c @@ -70,10 +70,10 @@ #define MIN_AUTHKEYS_LINE 10 /* "ssh-rsa AB" - short but doesn't matter */ #define MAX_AUTHKEYS_LINE 4200 /* max length of a line in authkeys */ -static int checkpubkey(const char* algo, unsigned int algolen, +static int checkpubkey(const char* keyalgo, unsigned int keyalgolen, const unsigned char* keyblob, unsigned int keybloblen); static int checkpubkeyperms(void); -static void send_msg_userauth_pk_ok(const char* algo, unsigned int algolen, +static void send_msg_userauth_pk_ok(const char* sigalgo, unsigned int sigalgolen, const unsigned char* keyblob, unsigned int keybloblen); static int checkfileperm(char * filename); @@ -82,16 +82,18 @@ static int checkfileperm(char * filename); void svr_auth_pubkey(int valid_user) { unsigned char testkey; /* whether we're just checking if a key is usable */ - char* algo = NULL; /* pubkey algo */ - unsigned int algolen; + char* sigalgo = NULL; + unsigned int sigalgolen; + const char* keyalgo; + unsigned int keyalgolen; unsigned char* keyblob = NULL; unsigned int keybloblen; unsigned int sign_payload_length; buffer * signbuf = NULL; sign_key * key = NULL; char* fp = NULL; - enum signkey_type type = -1; - int auth_failure = 1; + enum signkey_type sigtype, keytype; + int auth_failure = 1; TRACE(("enter pubkeyauth")) @@ -99,7 +101,11 @@ void svr_auth_pubkey(int valid_user) { * actual attempt*/ testkey = (buf_getbool(ses.payload) == 0); - algo = buf_getstring(ses.payload, &algolen); + sigalgo = buf_getstring(ses.payload, &sigalgolen); + sigtype = signature_type_from_name(sigalgo, sigalgolen); + keytype = signkey_type_from_signature(sigtype); + keyalgo = signkey_name_from_type(keytype, &keyalgolen); + keybloblen = buf_getint(ses.payload); keyblob = buf_getptr(ses.payload, keybloblen); @@ -117,8 +123,8 @@ void svr_auth_pubkey(int valid_user) { if (svr_ses.plugin_instance->checkpubkey( svr_ses.plugin_instance, &ses.plugin_session, - algo, - algolen, + keyalgo, + keyalgolen, keyblob, keybloblen, ses.authstate.username) == DROPBEAR_SUCCESS) { @@ -146,7 +152,7 @@ void svr_auth_pubkey(int valid_user) { #endif /* check if the key is valid */ if (auth_failure) { - auth_failure = checkpubkey(algo, algolen, keyblob, keybloblen) == DROPBEAR_FAILURE; + auth_failure = checkpubkey(keyalgo, keyalgolen, keyblob, keybloblen) == DROPBEAR_FAILURE; } if (auth_failure) { @@ -156,7 +162,7 @@ void svr_auth_pubkey(int valid_user) { /* let them know that the key is ok to use */ if (testkey) { - send_msg_userauth_pk_ok(algo, algolen, keyblob, keybloblen); + send_msg_userauth_pk_ok(sigalgo, sigalgolen, keyblob, keybloblen); goto out; } @@ -164,8 +170,7 @@ void svr_auth_pubkey(int valid_user) { /* get the key */ key = new_sign_key(); - type = DROPBEAR_SIGNKEY_ANY; - if (buf_get_pub_key(ses.payload, key, &type) == DROPBEAR_FAILURE) { + if (buf_get_pub_key(ses.payload, key, &keytype) == DROPBEAR_FAILURE) { send_msg_userauth_failure(0, 1); goto out; } @@ -188,7 +193,7 @@ void svr_auth_pubkey(int valid_user) { /* ... and finally verify the signature */ fp = sign_key_fingerprint(keyblob, keybloblen); - if (buf_verify(ses.payload, key, signbuf) == DROPBEAR_SUCCESS) { + if (buf_verify(ses.payload, key, sigtype, signbuf) == DROPBEAR_SUCCESS) { dropbear_log(LOG_NOTICE, "Pubkey auth succeeded for '%s' with key %s from %s", ses.authstate.pw_name, fp, svr_ses.addrstring); @@ -213,8 +218,8 @@ out: if (signbuf) { buf_free(signbuf); } - if (algo) { - m_free(algo); + if (sigalgo) { + m_free(sigalgo); } if (key) { sign_key_free(key); @@ -230,14 +235,14 @@ out: /* Reply that the key is valid for auth, this is sent when the user sends * a straight copy of their pubkey to test, to avoid having to perform * expensive signing operations with a worthless key */ -static void send_msg_userauth_pk_ok(const char* algo, unsigned int algolen, +static void send_msg_userauth_pk_ok(const char* sigalgo, unsigned int sigalgolen, const unsigned char* keyblob, unsigned int keybloblen) { TRACE(("enter send_msg_userauth_pk_ok")) CHECKCLEARTOWRITE(); buf_putbyte(ses.writepayload, SSH_MSG_USERAUTH_PK_OK); - buf_putstring(ses.writepayload, algo, algolen); + buf_putstring(ses.writepayload, sigalgo, sigalgolen); buf_putstring(ses.writepayload, (const char*)keyblob, keybloblen); encrypt_packet(); @@ -354,7 +359,7 @@ out: /* Checks whether a specified publickey (and associated algorithm) is an * acceptable key for authentication */ /* Returns DROPBEAR_SUCCESS if key is ok for auth, DROPBEAR_FAILURE otherwise */ -static int checkpubkey(const char* algo, unsigned int algolen, +static int checkpubkey(const char* keyalgo, unsigned int keyalgolen, const unsigned char* keyblob, unsigned int keybloblen) { FILE * authfile = NULL; @@ -368,14 +373,6 @@ static int checkpubkey(const char* algo, unsigned int algolen, TRACE(("enter checkpubkey")) - /* check that we can use the algo */ - if (have_algo(algo, algolen, sshhostkey) == DROPBEAR_FAILURE) { - dropbear_log(LOG_WARNING, - "Pubkey auth attempt with unknown algo for '%s' from %s", - ses.authstate.pw_name, svr_ses.addrstring); - goto out; - } - /* check file permissions, also whether file exists */ if (checkpubkeyperms() == DROPBEAR_FAILURE) { TRACE(("bad authorized_keys permissions, or file doesn't exist")) @@ -427,7 +424,7 @@ static int checkpubkey(const char* algo, unsigned int algolen, } line_num++; - ret = checkpubkey_line(line, line_num, filename, algo, algolen, keyblob, keybloblen); + ret = checkpubkey_line(line, line_num, filename, keyalgo, keyalgolen, keyblob, keybloblen); if (ret == DROPBEAR_SUCCESS) { break; } diff --git a/svr-kex.c b/svr-kex.c index af16d2f..e3e63d0 100644 --- a/svr-kex.c +++ b/svr-kex.c @@ -234,7 +234,7 @@ static void send_msg_kexdh_reply(mp_int *dh_e, buffer *ecdh_qs) { /* calc the signature */ buf_put_sign(ses.writepayload, svr_opts.hostkey, - ses.newkeys->algo_hostkey, ses.hash); + ses.newkeys->algo_signature, ses.hash); /* the SSH_MSG_KEXDH_REPLY is done */ encrypt_packet(); diff --git a/sysoptions.h b/sysoptions.h index 2c27caf..0f52431 100644 --- a/sysoptions.h +++ b/sysoptions.h @@ -139,9 +139,17 @@ If you test it please contact the Dropbear author */ * signing operations slightly slower. */ #define DROPBEAR_RSA_BLINDING 1 +#ifndef DROPBEAR_RSA_SHA1 +#define DROPBEAR_RSA_SHA1 DROPBEAR_RSA +#endif +#ifndef DROPBEAR_RSA_SHA256 +#define DROPBEAR_RSA_SHA256 DROPBEAR_RSA +#endif + /* hashes which will be linked and registered */ -#define DROPBEAR_SHA256 ((DROPBEAR_SHA2_256_HMAC) || (DROPBEAR_ECC_256) \ - || (DROPBEAR_CURVE25519) || (DROPBEAR_DH_GROUP14_SHA256)) +#define DROPBEAR_SHA256 ((DROPBEAR_SHA2_256_HMAC) || (DROPBEAR_ECC_256) \ + || (DROPBEAR_CURVE25519) || (DROPBEAR_DH_GROUP14_SHA256) \ + || (DROPBEAR_RSA_SHA256)) #define DROPBEAR_SHA384 (DROPBEAR_ECC_384) /* LTC SHA384 depends on SHA512 */ #define DROPBEAR_SHA512 ((DROPBEAR_SHA2_512_HMAC) || (DROPBEAR_ECC_521) \ -- cgit v1.2.3 From 972d723484d89c71e73ed63cc17bb2a6ce8cca5a Mon Sep 17 00:00:00 2001 From: Matt Johnston Date: Sun, 17 May 2020 23:58:31 +0800 Subject: split signkey_type and signature_type for RSA sha1 vs sha256 --- cli-authpubkey.c | 36 ++++++++++++++++++++------------ common-algo.c | 16 +++++++------- fuzzer-verify.c | 18 ++++++++++++---- rsa.c | 12 +++++------ rsa.h | 4 ++-- session.h | 12 +++++++++-- signkey.c | 63 ++++++++++++++++++++++++++++++++++++++++---------------- signkey.h | 36 +++++++++++++++++++++++++------- ssh.h | 2 +- svr-authpubkey.c | 17 ++++++++++----- 10 files changed, 148 insertions(+), 68 deletions(-) (limited to 'svr-authpubkey.c') diff --git a/cli-authpubkey.c b/cli-authpubkey.c index d1cc1f6..0ad80ea 100644 --- a/cli-authpubkey.c +++ b/cli-authpubkey.c @@ -33,7 +33,7 @@ #include "agentfwd.h" #if DROPBEAR_CLI_PUBKEY_AUTH -static void send_msg_userauth_pubkey(sign_key *key, enum signkey_type sigtype, int realsign); +static void send_msg_userauth_pubkey(sign_key *key, enum signature_type sigtype, int realsign); /* Called when we receive a SSH_MSG_USERAUTH_FAILURE for a pubkey request. * We use it to remove the key we tried from the list */ @@ -58,7 +58,8 @@ void recv_msg_userauth_pk_ok() { buffer* keybuf = NULL; char* algotype = NULL; unsigned int algolen; - enum signkey_type sigtype, keytype; + enum signkey_type keytype; + enum signature_type sigtype; unsigned int remotelen; TRACE(("enter recv_msg_userauth_pk_ok")) @@ -113,7 +114,7 @@ void recv_msg_userauth_pk_ok() { TRACE(("matching key")) /* XXX TODO: if it's an encrypted key, here we ask for their * password */ - send_msg_userauth_pubkey((sign_key*)iter->item, keytype, 1); + send_msg_userauth_pubkey((sign_key*)iter->item, sigtype, 1); } else { TRACE(("That was whacky. We got told that a key was valid, but it didn't match our list. Sounds like dodgy code on Dropbear's part")) } @@ -121,7 +122,7 @@ void recv_msg_userauth_pk_ok() { TRACE(("leave recv_msg_userauth_pk_ok")) } -static void cli_buf_put_sign(buffer* buf, sign_key *key, enum signkey_type sigtype, +static void cli_buf_put_sign(buffer* buf, sign_key *key, enum signature_type sigtype, const buffer *data_buf) { #if DROPBEAR_CLI_AGENTFWD // TODO: rsa-sha256 agent @@ -139,14 +140,14 @@ static void cli_buf_put_sign(buffer* buf, sign_key *key, enum signkey_type sigty } } -static void send_msg_userauth_pubkey(sign_key *key, enum signkey_type sigtype, int realsign) { +static void send_msg_userauth_pubkey(sign_key *key, enum signature_type sigtype, int realsign) { const char *algoname = NULL; unsigned int algolen; buffer* sigbuf = NULL; enum signkey_type keytype = signkey_type_from_signature(sigtype); - TRACE(("enter send_msg_userauth_pubkey")) + TRACE(("enter send_msg_userauth_pubkey sigtype %d", sigtype)) CHECKCLEARTOWRITE(); buf_putbyte(ses.writepayload, SSH_MSG_USERAUTH_REQUEST); @@ -183,7 +184,6 @@ static void send_msg_userauth_pubkey(sign_key *key, enum signkey_type sigtype, i /* Returns 1 if a key was tried */ int cli_auth_pubkey() { - TRACE(("enter cli_auth_pubkey")) #if DROPBEAR_CLI_AGENTFWD @@ -194,16 +194,26 @@ int cli_auth_pubkey() { } #endif + /* TODO iterate through privkeys to skip ones not in server-sig-algs */ + + /* TODO: testing */ +#if DROPBEAR_RSA_SHA256 + cli_ses.preferred_rsa_sigtype = DROPBEAR_SIGNATURE_RSA_SHA256; +#elif DROPBEAR_RSA_SHA1 + cli_ses.preferred_rsa_sigtype = DROPBEAR_SIGNATURE_RSA_SHA1; +#endif + if (cli_opts.privkeys->first) { sign_key * key = (sign_key*)cli_opts.privkeys->first->item; - enum signkey_type sigtype = key->type; - /* Send a trial request */ -#if DROPBEAR_RSA && DROPBEAR_RSA_SHA256 - // TODO: use ext-info to choose rsa kind - if (sigtype == DROPBEAR_SIGNKEY_RSA) { - sigtype = DROPBEAR_SIGNKEY_RSA_SHA256; + /* Determine the signature type to use */ + enum signature_type sigtype = (enum signature_type)key->type; +#if DROPBEAR_RSA + if (key->type == DROPBEAR_SIGNKEY_RSA) { + sigtype = cli_ses.preferred_rsa_sigtype; } #endif + + /* Send a trial request */ send_msg_userauth_pubkey(key, sigtype, 0); cli_ses.lastprivkey = key; TRACE(("leave cli_auth_pubkey-success")) diff --git a/common-algo.c b/common-algo.c index 28f6744..2a1047e 100644 --- a/common-algo.c +++ b/common-algo.c @@ -223,29 +223,29 @@ algo_type ssh_nocompress[] = { algo_type sshhostkey[] = { #if DROPBEAR_ED25519 - {"ssh-ed25519", DROPBEAR_SIGNKEY_ED25519, NULL, 1, NULL}, + {"ssh-ed25519", DROPBEAR_SIGNATURE_ED25519, NULL, 1, NULL}, #endif #if DROPBEAR_ECDSA #if DROPBEAR_ECC_256 - {"ecdsa-sha2-nistp256", DROPBEAR_SIGNKEY_ECDSA_NISTP256, NULL, 1, NULL}, + {"ecdsa-sha2-nistp256", DROPBEAR_SIGNATURE_ECDSA_NISTP256, NULL, 1, NULL}, #endif #if DROPBEAR_ECC_384 - {"ecdsa-sha2-nistp384", DROPBEAR_SIGNKEY_ECDSA_NISTP384, NULL, 1, NULL}, + {"ecdsa-sha2-nistp384", DROPBEAR_SIGNATURE_ECDSA_NISTP384, NULL, 1, NULL}, #endif #if DROPBEAR_ECC_521 - {"ecdsa-sha2-nistp521", DROPBEAR_SIGNKEY_ECDSA_NISTP521, NULL, 1, NULL}, + {"ecdsa-sha2-nistp521", DROPBEAR_SIGNATURE_ECDSA_NISTP521, NULL, 1, NULL}, #endif #endif #if DROPBEAR_RSA #if DROPBEAR_RSA_SHA256 - {"rsa-sha2-256", DROPBEAR_SIGNKEY_RSA_SHA256, NULL, 1, NULL}, + {"rsa-sha2-256", DROPBEAR_SIGNATURE_RSA_SHA256, NULL, 1, NULL}, #endif #if DROPBEAR_RSA_SHA1 - {"ssh-rsa", DROPBEAR_SIGNKEY_RSA, NULL, 1, NULL}, + {"ssh-rsa", DROPBEAR_SIGNATURE_RSA_SHA1, NULL, 1, NULL}, #endif #endif #if DROPBEAR_DSS - {"ssh-dss", DROPBEAR_SIGNKEY_DSS, NULL, 1, NULL}, + {"ssh-dss", DROPBEAR_SIGNATURE_DSS, NULL, 1, NULL}, #endif {NULL, 0, NULL, 0, NULL} }; @@ -263,8 +263,6 @@ static const struct dropbear_kex kex_dh_group14_sha256 = {DROPBEAR_KEX_NORMAL_DH static const struct dropbear_kex kex_dh_group16_sha512 = {DROPBEAR_KEX_NORMAL_DH, dh_p_16, DH_P_16_LEN, NULL, &sha512_desc }; #endif -/* These can't be const since dropbear_ecc_fill_dp() fills out - ecc_curve at runtime */ #if DROPBEAR_ECDH #if DROPBEAR_ECC_256 static const struct dropbear_kex kex_ecdh_nistp256 = {DROPBEAR_KEX_ECDH, NULL, 0, &ecc_curve_nistp256, &sha256_desc }; diff --git a/fuzzer-verify.c b/fuzzer-verify.c index 8ecbcdb..cda7723 100644 --- a/fuzzer-verify.c +++ b/fuzzer-verify.c @@ -27,15 +27,25 @@ int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) { if (setjmp(fuzz.jmp) == 0) { sign_key *key = new_sign_key(); - enum signkey_type type = DROPBEAR_SIGNKEY_ANY; - if (buf_get_pub_key(fuzz.input, key, &type) == DROPBEAR_SUCCESS) { - if (buf_verify(fuzz.input, key, verifydata) == DROPBEAR_SUCCESS) { + enum signkey_type keytype = DROPBEAR_SIGNKEY_ANY; + if (buf_get_pub_key(fuzz.input, key, &keytype) == DROPBEAR_SUCCESS) { + enum signature_type sigtype = (enum signature_type)keytype; + if (keytype == DROPBEAR_SIGNKEY_RSA) { + /* Flip a coin to decide rsa signature type */ + int flag = buf_getbyte(fuzz_input); + if (flag & 0x01) { + sigtype = DROPBEAR_SIGNATURE_RSA_SHA256; + } else { + sigtype = DROPBEAR_SIGNATURE_RSA_SHA1; + } + } + if (buf_verify(fuzz.input, key, sigtype, verifydata) == DROPBEAR_SUCCESS) { /* The fuzzer is capable of generating keys with a signature to match. We don't want false positives if the key is bogus, since a client/server wouldn't be trusting a bogus key anyway */ int boguskey = 0; - if (type == DROPBEAR_SIGNKEY_DSS) { + if (keytype == DROPBEAR_SIGNKEY_DSS) { /* So far have seen dss keys with bad p/q/g domain parameters */ int pprime, qprime, trials; trials = mp_prime_rabin_miller_trials(mp_count_bits(key->dsskey->p)); diff --git a/rsa.c b/rsa.c index ef737c8..39e2d46 100644 --- a/rsa.c +++ b/rsa.c @@ -44,7 +44,7 @@ #endif static void rsa_pad_em(const dropbear_rsa_key * key, - const buffer *data_buf, mp_int * rsa_em, enum signkey_type sigtype); + const buffer *data_buf, mp_int * rsa_em, enum signature_type sigtype); /* Load a public rsa key from a buffer, initialising the values. * The key will have the same format as buf_put_rsa_key. @@ -197,7 +197,7 @@ void buf_put_rsa_priv_key(buffer* buf, const dropbear_rsa_key *key) { /* Verify a signature in buf, made on data by the key given. * Returns DROPBEAR_SUCCESS or DROPBEAR_FAILURE */ int buf_rsa_verify(buffer * buf, const dropbear_rsa_key *key, - enum signkey_type sigtype, const buffer *data_buf) { + enum signature_type sigtype, const buffer *data_buf) { unsigned int slen; DEF_MP_INT(rsa_s); DEF_MP_INT(rsa_mdash); @@ -253,7 +253,7 @@ out: /* Sign the data presented with key, writing the signature contents * to the buffer */ void buf_put_rsa_sign(buffer* buf, const dropbear_rsa_key *key, - enum signkey_type sigtype, const buffer *data_buf) { + enum signature_type sigtype, const buffer *data_buf) { const char *name = NULL; unsigned int nsize, ssize, namelen = 0; unsigned int i; @@ -352,7 +352,7 @@ void buf_put_rsa_sign(buffer* buf, const dropbear_rsa_key *key, /* Creates the message value as expected by PKCS, see rfc8017 section 9.2 */ static void rsa_pad_em(const dropbear_rsa_key * key, - const buffer *data_buf, mp_int * rsa_em, enum signkey_type sigtype) { + const buffer *data_buf, mp_int * rsa_em, enum signature_type sigtype) { /* EM = 0x00 || 0x01 || PS || 0x00 || T PS is padding of 0xff to make EM the size of key->n @@ -380,14 +380,14 @@ static void rsa_pad_em(const dropbear_rsa_key * key, switch (sigtype) { #if DROPBEAR_RSA_SHA1 - case DROPBEAR_SIGNKEY_RSA: + case DROPBEAR_SIGNATURE_RSA_SHA1: Tlen = sizeof(T_sha1); T = T_sha1; hash_desc = &sha1_desc; break; #endif #if DROPBEAR_RSA_SHA256 - case DROPBEAR_SIGNKEY_RSA_SHA256: + case DROPBEAR_SIGNATURE_RSA_SHA256: Tlen = sizeof(T_sha256); T = T_sha256; hash_desc = &sha256_desc; diff --git a/rsa.h b/rsa.h index 39e6004..a8bbf41 100644 --- a/rsa.h +++ b/rsa.h @@ -43,10 +43,10 @@ typedef struct dropbear_RSA_Key { } dropbear_rsa_key; void buf_put_rsa_sign(buffer* buf, const dropbear_rsa_key *key, - enum signkey_type sigtype, const buffer *data_buf); + enum signature_type sigtype, const buffer *data_buf); #if DROPBEAR_SIGNKEY_VERIFY int buf_rsa_verify(buffer * buf, const dropbear_rsa_key *key, - enum signkey_type sigtype, const buffer *data_buf); + enum signature_type sigtype, const buffer *data_buf); #endif int buf_get_rsa_pub_key(buffer* buf, dropbear_rsa_key *key); int buf_get_rsa_priv_key(buffer* buf, dropbear_rsa_key *key); diff --git a/session.h b/session.h index ebfc915..cbe9cb5 100644 --- a/session.h +++ b/session.h @@ -92,8 +92,8 @@ struct key_context { struct key_context_directional trans; const struct dropbear_kex *algo_kex; - int algo_hostkey; /* server key type */ - int algo_signature; /* server signature type */ + enum signkey_type algo_hostkey; /* server key type */ + enum signature_type algo_signature; /* server signature type */ int allow_compress; /* whether compression has started (useful in zlib@openssh.com delayed compression case) */ @@ -313,6 +313,14 @@ struct clientsession { #endif sign_key *lastprivkey; + enum signature_type server_sig_algs[DROPBEAR_SIGNKEY_NUM_NAMED+1]; + int server_sig_algs_count; +#if DROPBEAR_RSA + /* Set to DROPBEAR_SIGNATURE_RSA_SHA256 or DROPBEAR_SIGNATURE_RSA_SHA1 + if depending which the server accepts */ + enum signature_type preferred_rsa_sigtype; +#endif + int retval; /* What the command exit status was - we emulate it */ #if 0 TODO diff --git a/signkey.c b/signkey.c index 27b09a3..47c8c8e 100644 --- a/signkey.c +++ b/signkey.c @@ -111,34 +111,52 @@ enum signkey_type signkey_type_from_name(const char* name, unsigned int namelen) /* Special case for rsa-sha2-256. This could be generalised if more signature names are added that aren't 1-1 with public key names */ -const char* signature_name_from_type(enum signkey_type type, unsigned int *namelen) { +const char* signature_name_from_type(enum signature_type type, unsigned int *namelen) { #if DROPBEAR_RSA_SHA256 - if (type == DROPBEAR_SIGNKEY_RSA_SHA256) { - *namelen = strlen(SSH_SIGNKEY_RSA_SHA256); - return SSH_SIGNKEY_RSA_SHA256; + if (type == DROPBEAR_SIGNATURE_RSA_SHA256) { + *namelen = strlen(SSH_SIGNATURE_RSA_SHA256); + return SSH_SIGNATURE_RSA_SHA256; } #endif - return signkey_name_from_type(type, namelen); +#if DROPBEAR_RSA_SHA1 + if (type == DROPBEAR_SIGNATURE_RSA_SHA1) { + *namelen = strlen(SSH_SIGNKEY_RSA); + return SSH_SIGNKEY_RSA; + } +#endif + return signkey_name_from_type((enum signkey_type)type, namelen); } -enum signkey_type signature_type_from_name(const char* name, unsigned int namelen) { +/* Returns DROPBEAR_SIGNATURE_NONE if none match */ +enum signature_type signature_type_from_name(const char* name, unsigned int namelen) { #if DROPBEAR_RSA_SHA256 - if (namelen == strlen(SSH_SIGNKEY_RSA_SHA256) - && memcmp(name, SSH_SIGNKEY_RSA_SHA256, namelen) == 0) { - return DROPBEAR_SIGNKEY_RSA_SHA256; + if (namelen == strlen(SSH_SIGNATURE_RSA_SHA256) + && memcmp(name, SSH_SIGNATURE_RSA_SHA256, namelen) == 0) { + return DROPBEAR_SIGNATURE_RSA_SHA256; } #endif - return signkey_type_from_name(name, namelen); +#if DROPBEAR_RSA_SHA256 + if (namelen == strlen(SSH_SIGNKEY_RSA) + && memcmp(name, SSH_SIGNKEY_RSA, namelen) == 0) { + return DROPBEAR_SIGNATURE_RSA_SHA1; + } +#endif + return (enum signature_type)signkey_type_from_name(name, namelen); } -enum signkey_type signkey_type_from_signature(enum signkey_type sigtype) { +enum signkey_type signkey_type_from_signature(enum signature_type sigtype) { #if DROPBEAR_RSA_SHA256 - if (sigtype == DROPBEAR_SIGNKEY_RSA_SHA256) { + if (sigtype == DROPBEAR_SIGNATURE_RSA_SHA256) { + return DROPBEAR_SIGNKEY_RSA; + } +#endif +#if DROPBEAR_RSA_SHA1 + if (sigtype == DROPBEAR_SIGNATURE_RSA_SHA1) { return DROPBEAR_SIGNKEY_RSA; } #endif assert(sigtype < DROPBEAR_SIGNKEY_NUM_NAMED); - return sigtype; + return (enum signkey_type)sigtype; } /* Returns a pointer to the key part specific to "type". @@ -562,11 +580,20 @@ char * sign_key_fingerprint(const unsigned char* keyblob, unsigned int keybloble #endif } -void buf_put_sign(buffer* buf, sign_key *key, enum signkey_type sigtype, +void buf_put_sign(buffer* buf, sign_key *key, enum signature_type sigtype, const buffer *data_buf) { buffer *sigblob = buf_new(MAX_PUBKEY_SIZE); enum signkey_type keytype = signkey_type_from_signature(sigtype); +#if DEBUG_TRACE + { + int namelen; + const char* signame = signature_name_from_type(sigtype, &namelen); + TRACE(("buf_put_sign type %d %s", sigtype, signame)); + } +#endif + + #if DROPBEAR_DSS if (keytype == DROPBEAR_SIGNKEY_DSS) { buf_put_dss_sign(sigblob, key->dsskey, data_buf); @@ -603,11 +630,12 @@ void buf_put_sign(buffer* buf, sign_key *key, enum signkey_type sigtype, * If FAILURE is returned, the position of * buf is undefined. If SUCCESS is returned, buf will be positioned after the * signature blob */ -int buf_verify(buffer * buf, sign_key *key, enum signkey_type expect_sigtype, const buffer *data_buf) { +int buf_verify(buffer * buf, sign_key *key, enum signature_type expect_sigtype, const buffer *data_buf) { char *type_name = NULL; unsigned int type_name_len = 0; - enum signkey_type sigtype, keytype; + enum signature_type sigtype; + enum signkey_type keytype; TRACE(("enter buf_verify")) @@ -616,8 +644,7 @@ int buf_verify(buffer * buf, sign_key *key, enum signkey_type expect_sigtype, co sigtype = signature_type_from_name(type_name, type_name_len); m_free(type_name); - if (expect_sigtype != DROPBEAR_SIGNKEY_ANY - && expect_sigtype != sigtype) { + if (expect_sigtype != sigtype) { dropbear_exit("Non-matching signing type"); } diff --git a/signkey.h b/signkey.h index 78b237b..9468509 100644 --- a/signkey.h +++ b/signkey.h @@ -32,6 +32,7 @@ struct dropbear_DSS_Key; struct dropbear_RSA_Key; struct dropbear_ED25519_Key; +/* Must match with signature_type below */ enum signkey_type { #if DROPBEAR_RSA DROPBEAR_SIGNKEY_RSA, @@ -49,13 +50,32 @@ enum signkey_type { #endif DROPBEAR_SIGNKEY_NUM_NAMED, DROPBEAR_SIGNKEY_ECDSA_KEYGEN = 70, /* just "ecdsa" for keygen */ -#if DROPBEAR_RSA_SHA256 - DROPBEAR_SIGNKEY_RSA_SHA256, /* rsa-sha2-256 signature. has a ssh-rsa key */ -#endif DROPBEAR_SIGNKEY_ANY = 80, DROPBEAR_SIGNKEY_NONE = 90, }; +/* Must match with signkey_type above, apart from rsa */ +enum signature_type { +#if DROPBEAR_DSS + DROPBEAR_SIGNATURE_DSS = DROPBEAR_SIGNKEY_DSS, +#endif +#if DROPBEAR_ECDSA + DROPBEAR_SIGNATURE_ECDSA_NISTP256 = DROPBEAR_SIGNKEY_ECDSA_NISTP256, + DROPBEAR_SIGNATURE_ECDSA_NISTP384 = DROPBEAR_SIGNKEY_ECDSA_NISTP384, + DROPBEAR_SIGNATURE_ECDSA_NISTP521 = DROPBEAR_SIGNKEY_ECDSA_NISTP521, +#endif /* DROPBEAR_ECDSA */ +#if DROPBEAR_ED25519 + DROPBEAR_SIGNATURE_ED25519 = DROPBEAR_SIGNKEY_ED25519, +#endif +#if DROPBEAR_RSA_SHA1 + DROPBEAR_SIGNATURE_RSA_SHA1 = 100, /* ssh-rsa signature (sha1) */ +#endif +#if DROPBEAR_RSA_SHA256 + DROPBEAR_SIGNATURE_RSA_SHA256 = 101, /* rsa-sha2-256 signature. has a ssh-rsa key */ +#endif + DROPBEAR_SIGNATURE_NONE = DROPBEAR_SIGNKEY_NONE, +}; + /* Sources for signing keys */ typedef enum { @@ -97,17 +117,17 @@ typedef struct SIGN_key sign_key; sign_key * new_sign_key(void); const char* signkey_name_from_type(enum signkey_type type, unsigned int *namelen); enum signkey_type signkey_type_from_name(const char* name, unsigned int namelen); -const char* signature_name_from_type(enum signkey_type type, unsigned int *namelen); -enum signkey_type signature_type_from_name(const char* name, unsigned int namelen); -enum signkey_type signkey_type_from_signature(enum signkey_type sigtype); +const char* signature_name_from_type(enum signature_type type, unsigned int *namelen); +enum signature_type signature_type_from_name(const char* name, unsigned int namelen); +enum signkey_type signkey_type_from_signature(enum signature_type sigtype); int buf_get_pub_key(buffer *buf, sign_key *key, enum signkey_type *type); int buf_get_priv_key(buffer* buf, sign_key *key, enum signkey_type *type); void buf_put_pub_key(buffer* buf, sign_key *key, enum signkey_type type); void buf_put_priv_key(buffer* buf, sign_key *key, enum signkey_type type); void sign_key_free(sign_key *key); -void buf_put_sign(buffer* buf, sign_key *key, enum signkey_type sigtype, const buffer *data_buf); +void buf_put_sign(buffer* buf, sign_key *key, enum signature_type sigtype, const buffer *data_buf); #if DROPBEAR_SIGNKEY_VERIFY -int buf_verify(buffer * buf, sign_key *key, enum signkey_type type, const buffer *data_buf); +int buf_verify(buffer * buf, sign_key *key, enum signature_type expect_sigtype, const buffer *data_buf); char * sign_key_fingerprint(const unsigned char* keyblob, unsigned int keybloblen); #endif int cmp_base64_key(const unsigned char* keyblob, unsigned int keybloblen, diff --git a/ssh.h b/ssh.h index fb7f9f2..5030fe9 100644 --- a/ssh.h +++ b/ssh.h @@ -108,7 +108,7 @@ #define SSH_SIGNKEY_ED25519 "ssh-ed25519" #define SSH_SIGNKEY_ED25519_LEN 11 /* signature type */ -#define SSH_SIGNKEY_RSA_SHA256 "rsa-sha2-256" +#define SSH_SIGNATURE_RSA_SHA256 "rsa-sha2-256" /* Agent commands. These aren't part of the spec, and are defined * only on the openssh implementation. */ diff --git a/svr-authpubkey.c b/svr-authpubkey.c index a52667c..46237b7 100644 --- a/svr-authpubkey.c +++ b/svr-authpubkey.c @@ -92,7 +92,8 @@ void svr_auth_pubkey(int valid_user) { buffer * signbuf = NULL; sign_key * key = NULL; char* fp = NULL; - enum signkey_type sigtype, keytype; + enum signature_type sigtype; + enum signkey_type keytype; int auth_failure = 1; TRACE(("enter pubkeyauth")) @@ -102,10 +103,6 @@ void svr_auth_pubkey(int valid_user) { testkey = (buf_getbool(ses.payload) == 0); sigalgo = buf_getstring(ses.payload, &sigalgolen); - sigtype = signature_type_from_name(sigalgo, sigalgolen); - keytype = signkey_type_from_signature(sigtype); - keyalgo = signkey_name_from_type(keytype, &keyalgolen); - keybloblen = buf_getint(ses.payload); keyblob = buf_getptr(ses.payload, keybloblen); @@ -117,6 +114,16 @@ void svr_auth_pubkey(int valid_user) { send_msg_userauth_failure(0, 0); goto out; } + + sigtype = signature_type_from_name(sigalgo, sigalgolen); + if (sigtype == DROPBEAR_SIGNATURE_NONE) { + send_msg_userauth_failure(0, 0); + goto out; + } + + keytype = signkey_type_from_signature(sigtype); + keyalgo = signkey_name_from_type(keytype, &keyalgolen); + #if DROPBEAR_PLUGIN if (svr_ses.plugin_instance != NULL) { char *options_buf; -- cgit v1.2.3