changeset 1545:0b991dec7ab9 coverity

merge coverity
author Matt Johnston <matt@ucc.asn.au>
date Mon, 26 Feb 2018 22:43:12 +0800
parents 1d163552145f (current diff) d1a8a05216ff (diff)
children 7f2be495dff6
files TODO default_options.h
diffstat 20 files changed, 155 insertions(+), 94 deletions(-) [+]
line wrap: on
line diff
--- a/.gitignore	Mon Feb 19 23:14:49 2018 +0800
+++ b/.gitignore	Mon Feb 26 22:43:12 2018 +0800
@@ -19,3 +19,4 @@
 config.h
 config.h.in
 configure
+default_options_guard.h
--- a/CHANGES	Mon Feb 19 23:14:49 2018 +0800
+++ b/CHANGES	Mon Feb 26 22:43:12 2018 +0800
@@ -2,7 +2,7 @@
 
 - IMPORTANT:
   Custom configuration is now specified in local_options.h rather than options.h
-  Available options and defaults can be seen in default_options.h.in
+  Available options and defaults can be seen in default_options.h
 
   To migrate your configuration, compare your customised options.h against the
   upstream options.h from your relevant version. Any customised options should
--- a/INSTALL	Mon Feb 19 23:14:49 2018 +0800
+++ b/INSTALL	Mon Feb 26 22:43:12 2018 +0800
@@ -1,7 +1,7 @@
 Basic Dropbear build instructions:
 
 - Edit localoptions.h to set which features you want. Available options
-  are described in default_options.h.in, these will be overridden by
+  are described in default_options.h, these will be overridden by
   anything set in localoptions.h
 
 - If using a Mercurial or Git checkout, "autoconf; autoheader"
--- a/TODO	Mon Feb 19 23:14:49 2018 +0800
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,27 +0,0 @@
-Current:
-
-Things which might need doing:
-
-- default private dbclient keys
-
-- Make options.h generated from configure perhaps?
-
-- handle /etc/environment in AIX
-
-- check that there aren't timing issues with valid/invalid user authentication
-  feedback.
-
-- Binding to different interfaces
-
-- CTR mode
-- SSH_MSG_IGNORE sending to improve CBC security
-- DH Group Exchange possibly, or just add group14 (whatever it's called today)
-
-- fix scp.c for IRIX
-
-- Be able to use OpenSSH keys for the client? or at least have some form of 
-  encrypted keys.
-
-- Client agent forwarding
-
-- Handle restrictions in ~/.ssh/authorized_keys ?
--- a/auth.h	Mon Feb 19 23:14:49 2018 +0800
+++ b/auth.h	Mon Feb 26 22:43:12 2018 +0800
@@ -105,12 +105,14 @@
 	unsigned char authtypes; /* Flags indicating which auth types are still 
 								valid */
 	unsigned int failcount; /* Number of (failed) authentication attempts.*/
-	unsigned authdone : 1; /* 0 if we haven't authed, 1 if we have. Applies for
+	unsigned int authdone; /* 0 if we haven't authed, 1 if we have. Applies for
 							  client and server (though has differing 
 							  meanings). */
-	unsigned perm_warn : 1; /* Server only, set if bad permissions on 
+	unsigned int perm_warn; /* Server only, set if bad permissions on 
 							   ~/.ssh/authorized_keys have already been
 							   logged. */
+	unsigned int checkusername_failed;  /* Server only, set if checkusername
+	                                has already failed */
 
 	/* These are only used for the server */
 	uid_t pw_uid;
--- a/cli-session.c	Mon Feb 19 23:14:49 2018 +0800
+++ b/cli-session.c	Mon Feb 26 22:43:12 2018 +0800
@@ -181,7 +181,7 @@
 
 }
 
-static void send_msg_service_request(char* servicename) {
+static void send_msg_service_request(const char* servicename) {
 
 	TRACE(("enter send_msg_service_request: servicename='%s'", servicename))
 
--- a/common-algo.c	Mon Feb 19 23:14:49 2018 +0800
+++ b/common-algo.c	Mon Feb 26 22:43:12 2018 +0800
@@ -276,6 +276,7 @@
 
 algo_type sshkex[] = {
 #if DROPBEAR_CURVE25519
+	{"curve25519-sha256", 0, &kex_curve25519, 1, NULL},
 	{"[email protected]", 0, &kex_curve25519, 1, NULL},
 #endif
 #if DROPBEAR_ECDH
--- a/common-session.c	Mon Feb 19 23:14:49 2018 +0800
+++ b/common-session.c	Mon Feb 26 22:43:12 2018 +0800
@@ -136,7 +136,7 @@
 	TRACE(("leave session_init"))
 }
 
-void session_loop(void(*loophandler)()) {
+void session_loop(void(*loophandler)(void)) {
 
 	fd_set readfd, writefd;
 	struct timeval timeout;
--- a/default_options.h	Mon Feb 19 23:14:49 2018 +0800
+++ b/default_options.h	Mon Feb 26 22:43:12 2018 +0800
@@ -149,12 +149,17 @@
  * Small systems should generally include either curve25519 or ecdh for performance.
  * curve25519 is less widely supported but is faster
  */ 
-#define DROPBEAR_DH_GROUP1 1
 #define DROPBEAR_DH_GROUP14_SHA1 1
 #define DROPBEAR_DH_GROUP14_SHA256 1
 #define DROPBEAR_DH_GROUP16 0
 #define DROPBEAR_CURVE25519 1
 #define DROPBEAR_ECDH 1
+#define DROPBEAR_DH_GROUP1 1
+
+/* When group1 is enabled it will only be allowed by Dropbear client
+not as a server, due to concerns over its strength. Set to 0 to allow
+group1 in Dropbear server too */
+#define DROPBEAR_DH_GROUP1_CLIENTONLY 1
 
 /* Control the memory/performance/compression tradeoff for zlib.
  * Set windowBits=8 for least memory usage, see your system's
--- a/dropbear.8	Mon Feb 19 23:14:49 2018 +0800
+++ b/dropbear.8	Mon Feb 26 22:43:12 2018 +0800
@@ -148,8 +148,10 @@
 Host key files are read at startup from a standard location, by default
 /etc/dropbear/dropbear_dss_host_key, /etc/dropbear/dropbear_rsa_host_key, and 
 /etc/dropbear/dropbear_ecdsa_host_key
-or specified on the commandline with -r. These are of the form generated
-by dropbearkey. The -R option can be used to automatically generate keys
+
+If the -r command line option is specified the default files are not loaded.
+Host key files are of the form generated by dropbearkey. 
+The -R option can be used to automatically generate keys
 in the default location - keys will be generated after startup when the first
 connection is established. This had the benefit that the system /dev/urandom
 random number source has a better chance of being securely seeded.
--- a/ecdsa.h	Mon Feb 19 23:14:49 2018 +0800
+++ b/ecdsa.h	Mon Feb 26 22:43:12 2018 +0800
@@ -7,13 +7,14 @@
 
 #if DROPBEAR_ECDSA
 
-/* Prefer the larger size - it's fast anyway */
-#if DROPBEAR_ECC_521
-#define ECDSA_DEFAULT_SIZE 521
+/* prefer 256 or 384 since those are SHOULD for
+   draft-ietf-curdle-ssh-kex-sha2.txt */
+#if DROPBEAR_ECC_256
+#define ECDSA_DEFAULT_SIZE 256
 #elif DROPBEAR_ECC_384
 #define ECDSA_DEFAULT_SIZE 384
-#elif DROPBEAR_ECC_256
-#define ECDSA_DEFAULT_SIZE 256
+#elif DROPBEAR_ECC_521
+#define ECDSA_DEFAULT_SIZE 521
 #else
 #define ECDSA_DEFAULT_SIZE 0
 #endif
--- a/rsa.c	Mon Feb 19 23:14:49 2018 +0800
+++ b/rsa.c	Mon Feb 26 22:43:12 2018 +0800
@@ -68,6 +68,12 @@
 		goto out;
 	}
 
+	/* 64 bit is limit used by openssl, so we won't block any keys in the wild */
+	if (mp_count_bits(key->e) > 64) {
+		dropbear_log(LOG_WARNING, "RSA key bad e");
+		goto out;
+	}
+
 	TRACE(("leave buf_get_rsa_pub_key: success"))
 	ret = DROPBEAR_SUCCESS;
 out:
--- a/runopts.h	Mon Feb 19 23:14:49 2018 +0800
+++ b/runopts.h	Mon Feb 26 22:43:12 2018 +0800
@@ -92,6 +92,8 @@
 #endif
 
 	int norootlogin;
+	char *restrict_group;
+	gid_t restrict_group_gid;
 
 	int noauthpass;
 	int norootpass;
--- a/session.h	Mon Feb 19 23:14:49 2018 +0800
+++ b/session.h	Mon Feb 26 22:43:12 2018 +0800
@@ -40,7 +40,7 @@
 #include "netio.h"
 
 void common_session_init(int sock_in, int sock_out);
-void session_loop(void(*loophandler)()) ATTRIB_NORETURN;
+void session_loop(void(*loophandler)(void)) ATTRIB_NORETURN;
 void session_cleanup(void);
 void send_session_identification(void);
 void send_msg_ignore(void);
--- a/svr-auth.c	Mon Feb 19 23:14:49 2018 +0800
+++ b/svr-auth.c	Mon Feb 26 22:43:12 2018 +0800
@@ -25,6 +25,8 @@
 /* This file (auth.c) handles authentication requests, passing it to the
  * particular type (auth-passwd, auth-pubkey). */
 
+#include <limits.h>
+
 #include "includes.h"
 #include "dbutil.h"
 #include "session.h"
@@ -35,26 +37,10 @@
 #include "runopts.h"
 #include "dbrandom.h"
 
-static void authclear(void);
-static int checkusername(char *username, unsigned int userlen);
+static int checkusername(const char *username, unsigned int userlen);
 
 /* initialise the first time for a session, resetting all parameters */
 void svr_authinitialise() {
-
-	ses.authstate.failcount = 0;
-	ses.authstate.pw_name = NULL;
-	ses.authstate.pw_dir = NULL;
-	ses.authstate.pw_shell = NULL;
-	ses.authstate.pw_passwd = NULL;
-	authclear();
-	
-}
-
-/* Reset the auth state, but don't reset the failcount. This is for if the
- * user decides to try with a different username etc, and is also invoked
- * on initialisation */
-static void authclear() {
-	
 	memset(&ses.authstate, 0, sizeof(ses.authstate));
 #if DROPBEAR_SVR_PUBKEY_AUTH
 	ses.authstate.authtypes |= AUTH_TYPE_PUBKEY;
@@ -64,19 +50,6 @@
 		ses.authstate.authtypes |= AUTH_TYPE_PASSWORD;
 	}
 #endif
-	if (ses.authstate.pw_name) {
-		m_free(ses.authstate.pw_name);
-	}
-	if (ses.authstate.pw_shell) {
-		m_free(ses.authstate.pw_shell);
-	}
-	if (ses.authstate.pw_dir) {
-		m_free(ses.authstate.pw_dir);
-	}
-	if (ses.authstate.pw_passwd) {
-		m_free(ses.authstate.pw_passwd);
-	}
-	
 }
 
 /* Send a banner message if specified to the client. The client might
@@ -224,31 +197,76 @@
 	m_free(methodname);
 }
 
+/* returns DROPBEAR_SUCCESS or DROPBEAR_FAILURE */
+static int check_group_membership(gid_t check_gid, const char* username, gid_t user_gid) {
+	int ngroups, i, ret;
+	gid_t *grouplist = NULL;
+	int match = DROPBEAR_FAILURE;
+
+	for (ngroups = 32; ngroups <= DROPBEAR_NGROUP_MAX; ngroups *= 2) {
+		grouplist = m_malloc(sizeof(gid_t) * ngroups);
+
+		/* BSD returns ret==0 on success. Linux returns ret==ngroups on success */
+		ret = getgrouplist(username, user_gid, grouplist, &ngroups);
+		if (ret >= 0) {
+			break;
+		}
+		m_free(grouplist);
+		grouplist = NULL;
+	}
+
+	if (!grouplist) {
+		dropbear_log(LOG_ERR, "Too many groups for user '%s'", username);
+		return DROPBEAR_FAILURE;
+	}
+
+	for (i = 0; i < ngroups; i++) {
+		if (grouplist[i] == check_gid) {
+			match = DROPBEAR_SUCCESS;
+			break;
+		}
+	}
+	m_free(grouplist);
+
+	return match;
+}
+
 
 /* Check that the username exists and isn't disallowed (root), and has a valid shell.
  * returns DROPBEAR_SUCCESS on valid username, DROPBEAR_FAILURE on failure */
-static int checkusername(char *username, unsigned int userlen) {
+static int checkusername(const char *username, unsigned int userlen) {
 
 	char* listshell = NULL;
 	char* usershell = NULL;
 	uid_t uid;
+
 	TRACE(("enter checkusername"))
 	if (userlen > MAX_USERNAME_LEN) {
 		return DROPBEAR_FAILURE;
 	}
 
-	/* new user or username has changed */
-	if (ses.authstate.username == NULL ||
-		strcmp(username, ses.authstate.username) != 0) {
-			/* the username needs resetting */
-			if (ses.authstate.username != NULL) {
-				dropbear_log(LOG_WARNING, "Client trying multiple usernames from %s",
-							svr_ses.addrstring);
-				m_free(ses.authstate.username);
-			}
-			authclear();
-			fill_passwd(username);
-			ses.authstate.username = m_strdup(username);
+	if (strlen(username) != userlen) {
+		dropbear_exit("Attempted username with a null byte from %s",
+			svr_ses.addrstring);
+	}
+
+	if (ses.authstate.username == NULL) {
+		/* first request */
+		fill_passwd(username);
+		ses.authstate.username = m_strdup(username);
+	} else {
+		/* check username hasn't changed */
+		if (strcmp(username, ses.authstate.username) != 0) {
+			dropbear_exit("Client trying multiple usernames from %s",
+				svr_ses.addrstring);
+		}
+	}
+
+	/* avoids cluttering logs with repeated failure messages from
+	consecutive authentication requests in a sesssion */
+	if (ses.authstate.checkusername_failed) {
+		TRACE(("checkusername: returning cached failure"))
+		return DROPBEAR_FAILURE;
 	}
 
 	/* check that user exists */
@@ -257,6 +275,7 @@
 		dropbear_log(LOG_WARNING,
 				"Login attempt for nonexistent user from %s",
 				svr_ses.addrstring);
+		ses.authstate.checkusername_failed = 1;
 		return DROPBEAR_FAILURE;
 	}
 
@@ -268,6 +287,7 @@
 				"Login attempt with wrong user %s from %s",
 				ses.authstate.pw_name,
 				svr_ses.addrstring);
+		ses.authstate.checkusername_failed = 1;
 		return DROPBEAR_FAILURE;
 	}
 
@@ -275,9 +295,22 @@
 	if (svr_opts.norootlogin && ses.authstate.pw_uid == 0) {
 		TRACE(("leave checkusername: root login disabled"))
 		dropbear_log(LOG_WARNING, "root login rejected");
+		ses.authstate.checkusername_failed = 1;
 		return DROPBEAR_FAILURE;
 	}
 
+	/* check for login restricted to certain group if desired */
+	if (svr_opts.restrict_group) {
+		if (check_group_membership(svr_opts.restrict_group_gid,
+				ses.authstate.pw_name, ses.authstate.pw_gid) == DROPBEAR_FAILURE) {
+			dropbear_log(LOG_WARNING,
+				"Logins are restricted to the group %s but user '%s' is not a member",
+				svr_opts.restrict_group, ses.authstate.pw_name);
+			ses.authstate.checkusername_failed = 1;
+			return DROPBEAR_FAILURE;
+		}
+	}
+
 	TRACE(("shell is %s", ses.authstate.pw_shell))
 
 	/* check that the shell is set */
@@ -301,6 +334,7 @@
 	/* no matching shell */
 	endusershell();
 	TRACE(("no matching shell"))
+	ses.authstate.checkusername_failed = 1;
 	dropbear_log(LOG_WARNING, "User '%s' has invalid shell, rejected",
 				ses.authstate.pw_name);
 	return DROPBEAR_FAILURE;
--- a/svr-runopts.c	Mon Feb 19 23:14:49 2018 +0800
+++ b/svr-runopts.c	Mon Feb 26 22:43:12 2018 +0800
@@ -30,6 +30,8 @@
 #include "algo.h"
 #include "ecdsa.h"
 
+#include <grp.h>
+
 svr_runopts svr_opts; /* GLOBAL */
 
 static void printhelp(const char * progname);
@@ -68,6 +70,7 @@
 					"-m		Don't display the motd on login\n"
 #endif
 					"-w		Disallow root logins\n"
+					"-G		Restrict logins to members of specified group\n"
 #if DROPBEAR_SVR_PASSWORD_AUTH || DROPBEAR_SVR_PAM_AUTH
 					"-s		Disable password logins\n"
 					"-g		Disable password logins for root\n"
@@ -132,6 +135,8 @@
 	svr_opts.forced_command = NULL;
 	svr_opts.forkbg = 1;
 	svr_opts.norootlogin = 0;
+	svr_opts.restrict_group = NULL;
+	svr_opts.restrict_group_gid = 0;
 	svr_opts.noauthpass = 0;
 	svr_opts.norootpass = 0;
 	svr_opts.allowblankpass = 0;
@@ -230,6 +235,9 @@
 				case 'w':
 					svr_opts.norootlogin = 1;
 					break;
+				case 'G':
+					next = &svr_opts.restrict_group;
+					break;
 				case 'W':
 					next = &recv_window_arg;
 					break;
@@ -331,6 +339,17 @@
 		}
 		buf_setpos(svr_opts.banner, 0);
 	}
+
+	if (svr_opts.restrict_group) {
+		struct group *restrictedgroup = getgrnam(svr_opts.restrict_group);
+
+		if (restrictedgroup){
+			svr_opts.restrict_group_gid = restrictedgroup->gr_gid;
+		} else {
+			dropbear_exit("Cannot restrict logins to group '%s' as the group does not exist", svr_opts.restrict_group);
+		}
+
+	}
 	
 	if (recv_window_arg) {
 		opts.recv_window = atol(recv_window_arg);
@@ -510,17 +529,20 @@
 		m_free(hostkey_file);
 	}
 
+	/* Only load default host keys if a host key is not specified by the user */
+	if (svr_opts.num_hostkey_files == 0) {
 #if DROPBEAR_RSA
-	loadhostkey(RSA_PRIV_FILENAME, 0);
+		loadhostkey(RSA_PRIV_FILENAME, 0);
 #endif
 
 #if DROPBEAR_DSS
-	loadhostkey(DSS_PRIV_FILENAME, 0);
+		loadhostkey(DSS_PRIV_FILENAME, 0);
 #endif
 
 #if DROPBEAR_ECDSA
-	loadhostkey(ECDSA_PRIV_FILENAME, 0);
+		loadhostkey(ECDSA_PRIV_FILENAME, 0);
 #endif
+	}
 
 #if DROPBEAR_DELAY_HOSTKEY
 	if (svr_opts.delay_hostkey) {
--- a/svr-service.c	Mon Feb 19 23:14:49 2018 +0800
+++ b/svr-service.c	Mon Feb 26 22:43:12 2018 +0800
@@ -30,7 +30,7 @@
 #include "ssh.h"
 #include "auth.h"
 
-static void send_msg_service_accept(char *name, int len);
+static void send_msg_service_accept(const char *name, int len);
 
 /* processes a SSH_MSG_SERVICE_REQUEST, returning 0 if finished,
  * 1 if not */
@@ -73,7 +73,7 @@
 
 }
 
-static void send_msg_service_accept(char *name, int len) {
+static void send_msg_service_accept(const char *name, int len) {
 
 	TRACE(("accepting service %s", name))
 
--- a/svr-session.c	Mon Feb 19 23:14:49 2018 +0800
+++ b/svr-session.c	Mon Feb 26 22:43:12 2018 +0800
@@ -42,6 +42,7 @@
 #include "crypto_desc.h"
 
 static void svr_remoteclosed(void);
+static void svr_algos_initialise(void);
 
 struct serversession svr_ses; /* GLOBAL */
 
@@ -102,6 +103,7 @@
 	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);
@@ -243,3 +245,14 @@
 
 }
 
+static void svr_algos_initialise(void) {
+#if DROPBEAR_DH_GROUP1 && DROPBEAR_DH_GROUP1_CLIENTONLY
+	algo_type *algo;
+	for (algo = sshkex; algo->name; algo++) {
+		if (strcmp(algo->name, "diffie-hellman-group1-sha1") == 0) {
+			algo->usable = 0;
+		}
+	}
+#endif
+}
+
--- a/svr-tcpfwd.c	Mon Feb 19 23:14:49 2018 +0800
+++ b/svr-tcpfwd.c	Mon Feb 26 22:43:12 2018 +0800
@@ -94,7 +94,7 @@
 			buf_putbyte(ses.writepayload, SSH_MSG_REQUEST_SUCCESS);
 			buf_putint(ses.writepayload, allocated_listen_port);
 			encrypt_packet();
-			wantreply = 0; //so out does not do so
+			wantreply = 0; /* avoid out: below sending another reply */
 		}
 	} else if (strcmp("cancel-tcpip-forward", reqname) == 0) {
 		ret = svr_cancelremotetcp();
@@ -212,9 +212,6 @@
 	if (DROPBEAR_SUCCESS == ret) {
 		tcpinfo->listenport = get_sock_port(ses.listeners[0]->socks[0]);
 		*allocated_listen_port = tcpinfo->listenport;
-		dropbear_log(LOG_INFO, "tcpip-forward %s:%d '%s'", 
-			((NULL == tcpinfo->listenaddr)?"localhost":tcpinfo->listenaddr), 
-			tcpinfo->listenport, ses.authstate.pw_name);
 	}
 
 out:
--- a/sysoptions.h	Mon Feb 19 23:14:49 2018 +0800
+++ b/sysoptions.h	Mon Feb 26 22:43:12 2018 +0800
@@ -81,6 +81,8 @@
  
 #define DROPBEAR_PASSWORD_ENV "DROPBEAR_PASSWORD"
 
+#define DROPBEAR_NGROUP_MAX 1024
+
 /* Required for pubkey auth */
 #define DROPBEAR_SIGNKEY_VERIFY ((DROPBEAR_SVR_PUBKEY_AUTH) || (DROPBEAR_CLIENT))