changeset 47:4b53a43f0082

- client pubkey auth works - rearrange the runopts code for client and server (hostkey reading is needed by both (if the client is doing pubkey auth. otherwise....))
author Matt Johnston <matt@ucc.asn.au>
date Fri, 06 Aug 2004 16:18:01 +0000
parents 3bea78e1b175
children aecba0e16e9c cc59bfcdee17
files auth.h cli-authpubkey.c cli-runopts.c cli-session.c common-runopts.c debug.h options.h runopts.h session.h svr-runopts.c
diffstat 10 files changed, 206 insertions(+), 100 deletions(-) [+]
line wrap: on
line diff
--- a/auth.h	Fri Aug 06 02:51:17 2004 +0000
+++ b/auth.h	Fri Aug 06 16:18:01 2004 +0000
@@ -63,9 +63,6 @@
 #define AUTH_METHOD_PASSWORD "password"
 #define AUTH_METHOD_PASSWORD_LEN 8
 
-/* For a 4096 bit DSS key, empirically determined to be 1590 bytes */
-#define MAX_PUBKEY_SIZE 1600
-
 /* This structure is shared between server and client - it contains
  * relatively little extraneous bits when used for the client rather than the
  * server */
--- a/cli-authpubkey.c	Fri Aug 06 02:51:17 2004 +0000
+++ b/cli-authpubkey.c	Fri Aug 06 16:18:01 2004 +0000
@@ -16,7 +16,7 @@
 
 	TRACE(("enter cli_pubkeyfail"));
 	/* Find the key we failed with, and remove it */
-	for (keyitem = cli_ses.pubkeys; keyitem != NULL; keyitem = keyitem->next) {
+	for (keyitem = cli_opts.pubkeys; keyitem != NULL; keyitem = keyitem->next) {
 		if (keyitem->next == cli_ses.lastpubkey) {
 			keyitem->next = cli_ses.lastpubkey->next;
 		}
@@ -40,6 +40,7 @@
 
 	algotype = buf_getstring(ses.payload, &algolen);
 	keytype = signkey_type_from_name(algotype, algolen);
+	TRACE(("recv_msg_userauth_pk_ok: type %d", keytype));
 	m_free(algotype);
 
 	keybuf = buf_new(MAX_PUBKEY_SIZE);
@@ -48,25 +49,32 @@
 
 	/* Iterate through our keys, find which one it was that matched, and
 	 * send a real request with that key */
-	for (keyitem = cli_ses.pubkeys; keyitem != NULL; keyitem = keyitem->next) {
+	for (keyitem = cli_opts.pubkeys; keyitem != NULL; keyitem = keyitem->next) {
 
 		if (keyitem->type != keytype) {
 			/* Types differed */
+			TRACE(("types differed"));
 			continue;
 		}
 
 		/* Now we compare the contents of the key */
 		keybuf->pos = keybuf->len = 0;
 		buf_put_pub_key(keybuf, keyitem->key, keytype);
+		buf_setpos(keybuf, 0);
+		buf_incrpos(keybuf, 4); /* first int is the length of the remainder (ie
+								   remotelen) which has already been taken from
+								   the remote buffer */
 
-		if (keybuf->len != remotelen) {
+
+		if (keybuf->len-4 != remotelen) {
+			TRACE(("lengths differed: localh %d remote %d", keybuf->len, remotelen));
 			/* Lengths differed */
 			continue;
 		}
-
-		if (memcmp(keybuf->data, 
+		if (memcmp(buf_getptr(keybuf, remotelen),
 					buf_getptr(ses.payload, remotelen), remotelen) != 0) {
 			/* Data didn't match this key */
+			TRACE(("data differed"));
 			continue;
 		}
 
@@ -133,10 +141,10 @@
 
 	TRACE(("enter cli_auth_pubkey"));
 
-	if (cli_ses.pubkeys != NULL) {
+	if (cli_opts.pubkeys != NULL) {
 		/* Send a trial request */
-		send_msg_userauth_pubkey(cli_ses.pubkeys->key,
-				cli_ses.pubkeys->type, 0);
+		send_msg_userauth_pubkey(cli_opts.pubkeys->key,
+				cli_opts.pubkeys->type, 0);
 		TRACE(("leave cli_auth_pubkey-success"));
 		return 1;
 	} else {
--- a/cli-runopts.c	Fri Aug 06 02:51:17 2004 +0000
+++ b/cli-runopts.c	Fri Aug 06 16:18:01 2004 +0000
@@ -31,17 +31,24 @@
 
 cli_runopts cli_opts; /* GLOBAL */
 
-static void printhelp(const char * progname);
+static void printhelp();
+static void parsehostname(char* userhostarg);
+#ifdef DROPBEAR_PUBKEY_AUTH
+static void loadidentityfile(const char* filename);
+#endif
 
-static void printhelp(const char * progname) {
+static void printhelp() {
 
 	fprintf(stderr, "Dropbear client v%s\n"
-					"Usage: %s [options] user@host[:port]\n"
+					"Usage: %s [options] user@host\n"
 					"Options are:\n"
-					"user		Remote username\n"
-					"host		Remote host\n"
-					"port		Remote port\n"
-					,DROPBEAR_VERSION, progname);
+					"-p <remoteport>\n"
+					"-t    Allocate a pty"
+					"-T    Don't allocate a pty"
+#ifdef DROPBEAR_PUBKEY_AUTH
+					"-i <identityfile>   (multiple allowed)"
+#endif
+					,DROPBEAR_VERSION, cli_opts.progname);
 }
 
 void cli_getopts(int argc, char ** argv) {
@@ -49,12 +56,11 @@
 	unsigned int i, j;
 	char ** next = 0;
 	unsigned int cmdlen;
+#ifdef DROPBEAR_PUBKEY_AUTH
 	int nextiskey = 0; /* A flag if the next argument is a keyfile */
+#endif
 
-	uid_t uid;
-	struct passwd *pw = NULL; 
 
-	char* userhostarg = NULL;
 
 	/* see printhelp() for options */
 	cli_opts.progname = argv[0];
@@ -62,7 +68,10 @@
 	cli_opts.remoteport = NULL;
 	cli_opts.username = NULL;
 	cli_opts.cmd = NULL;
-	cli_opts.wantpty = 1;
+	cli_opts.wantpty = 9; /* 9 means "it hasn't been touched", gets set later */
+#ifdef DROPBEAR_PUBKEY_AUTH
+	cli_opts.pubkeys = NULL;
+#endif
 	opts.nolocaltcp = 0;
 	opts.noremotetcp = 0;
 	/* not yet
@@ -70,17 +79,18 @@
 	opts.ipv6 = 1;
 	*/
 
-	if (argc != 2) {
-		printhelp(argv[0]);
-		exit(EXIT_FAILURE);
-	}
-
+	/* Iterate all the arguments */
 	for (i = 1; i < (unsigned int)argc; i++) {
+#ifdef DROPBEAR_PUBKEY_AUTH
 		if (nextiskey) {
-			/* XXX do stuff */
-			break;
+			/* Load a hostkey since the previous argument was "-i" */
+			loadidentityfile(argv[i]);
+			nextiskey = 0;
+			continue;
 		}
+#endif
 		if (next) {
+			/* The previous flag set a value to assign */
 			*next = argv[i];
 			if (*next == NULL) {
 				dropbear_exit("Invalid null argument");
@@ -90,59 +100,43 @@
 		}
 
 		if (argv[i][0] == '-') {
+			/* A flag *waves* */
 
-			/* A flag *waves* */
 			switch (argv[i][1]) {
-				case 'p':
+				case 'p': /* remoteport */
 					next = &cli_opts.remoteport;
 					break;
 #ifdef DROPBEAR_PUBKEY_AUTH
-				case 'i':
+				case 'i': /* an identityfile */
 					nextiskey = 1;
 					break;
 #endif
+				case 't': /* we want a pty */
+					cli_opts.wantpty = 1;
+					break;
+				case 'T': /* don't want a pty */
+					cli_opts.wantpty = 0;
+					break;
 				default:
-					fprintf(stderr, "Unknown argument %s\n", argv[i]);
-					printhelp(argv[0]);
+					fprintf(stderr, "Unknown argument '%s'\n", argv[i]);
+					printhelp();
 					exit(EXIT_FAILURE);
 					break;
 			} /* Switch */
 
+			continue; /* next argument */
+
 		} else {
+			TRACE(("non-flag arg"));
 
 			/* Either the hostname or commands */
-			/* Hostname is first up, must be set before we get the cmds */
 
 			if (cli_opts.remotehost == NULL) {
-				/* We'll be editing it, should probably make a copy */
-				userhostarg = m_strdup(argv[1]);
 
-				cli_opts.remotehost = strchr(userhostarg, '@');
-				if (cli_opts.remotehost == NULL) {
-					/* no username portion, the cli-auth.c code can figure the
-					 * local user's name */
-					cli_opts.remotehost = userhostarg;
-				} else {
-					cli_opts.remotehost[0] = '\0'; /* Split the user/host */
-					cli_opts.remotehost++;
-					cli_opts.username = userhostarg;
-				}
+				parsehostname(argv[i]);
 
-				if (cli_opts.username == NULL) {
-					uid = getuid();
-					
-					pw = getpwuid(uid);
-					if (pw == NULL || pw->pw_name == NULL) {
-						dropbear_exit("I don't know my own [user]name");
-					}
+			} else {
 
-					cli_opts.username = m_strdup(pw->pw_name);
-				}
-
-				if (cli_opts.remotehost[0] == '\0') {
-					dropbear_exit("Bad hostname");
-				}
-			} else {
 				/* this is part of the commands to send - after this we
 				 * don't parse any more options, and flags are sent as the
 				 * command */
@@ -166,4 +160,82 @@
 			}
 		}
 	}
+
+	if (cli_opts.remotehost == NULL) {
+		dropbear_exit("Bad syntax");
+	}
+
+	if (cli_opts.remoteport == NULL) {
+		cli_opts.remoteport = "22";
+	}
+
+	/* If not explicitly specified with -t or -T, we don't want a pty if
+	 * there's a command, but we do otherwise */
+	if (cli_opts.wantpty == 9) {
+		if (cli_opts.cmd == NULL) {
+			cli_opts.wantpty = 1;
+		} else {
+			cli_opts.wantpty = 0;
+		}
+	}
 }
+
+#ifdef DROPBEAR_PUBKEY_AUTH
+static void loadidentityfile(const char* filename) {
+
+	struct PubkeyList * nextkey;
+	sign_key *key;
+	int keytype;
+
+	key = new_sign_key();
+	keytype = DROPBEAR_SIGNKEY_ANY;
+	if ( readhostkey(filename, key, &keytype) != DROPBEAR_SUCCESS ) {
+
+		fprintf(stderr, "Failed loading keyfile '%s'\n", filename);
+		sign_key_free(key);
+
+	} else {
+
+		nextkey = (struct PubkeyList*)m_malloc(sizeof(struct PubkeyList));
+		nextkey->key = key;
+		nextkey->next = cli_opts.pubkeys;
+		nextkey->type = keytype;
+		cli_opts.pubkeys = nextkey;
+	}
+}
+#endif
+
+
+/* Parses a [user@]hostname argument. userhostarg is the argv[i] corresponding
+ * - note that it will be modified */
+static void parsehostname(char* userhostarg) {
+
+	uid_t uid;
+	struct passwd *pw = NULL; 
+
+	cli_opts.remotehost = strchr(userhostarg, '@');
+	if (cli_opts.remotehost == NULL) {
+		/* no username portion, the cli-auth.c code can figure the
+		 * local user's name */
+		cli_opts.remotehost = userhostarg;
+	} else {
+		cli_opts.remotehost[0] = '\0'; /* Split the user/host */
+		cli_opts.remotehost++;
+		cli_opts.username = userhostarg;
+	}
+
+	if (cli_opts.username == NULL) {
+		uid = getuid();
+		
+		pw = getpwuid(uid);
+		if (pw == NULL || pw->pw_name == NULL) {
+			dropbear_exit("Unknown own user");
+		}
+
+		cli_opts.username = m_strdup(pw->pw_name);
+	}
+
+	if (cli_opts.remotehost[0] == '\0') {
+		dropbear_exit("Bad hostname");
+	}
+}
--- a/cli-session.c	Fri Aug 06 02:51:17 2004 +0000
+++ b/cli-session.c	Fri Aug 06 16:18:01 2004 +0000
@@ -88,6 +88,10 @@
 	cli_ses.tty_raw_mode = 0;
 	cli_ses.winchange = 0;
 
+	/* Auth */
+	cli_ses.lastpubkey = NULL;
+	cli_ses.lastauthtype = NULL;
+
 	/* For printing "remote host closed" for the user */
 	ses.remoteclosed = cli_remoteclosed;
 	ses.buf_match_algo = cli_buf_match_algo;
@@ -160,6 +164,7 @@
 			TRACE(("leave cli_sessionloop: cli_auth_try"));
 			return;
 
+			/*
 		case USERAUTH_SUCCESS_RCVD:
 			send_msg_service_request(SSH_SERVICE_CONNECTION);
 			cli_ses.state = SERVICE_CONN_REQ_SENT;
@@ -171,14 +176,13 @@
 			TRACE(("leave cli_sessionloop: cli_send_chansess_request"));
 			cli_ses.state = SESSION_RUNNING;
 			return;
+			*/
 
-			/*
 		case USERAUTH_SUCCESS_RCVD:
 			cli_send_chansess_request();
 			TRACE(("leave cli_sessionloop: cli_send_chansess_request"));
 			cli_ses.state = SESSION_RUNNING;
 			return;
-			*/
 
 		case SESSION_RUNNING:
 			if (ses.chancount < 1) {
@@ -236,7 +240,7 @@
 void cleantext(unsigned char* dirtytext) {
 
 	unsigned int i, j;
-	unsigned char c, lastchar;
+	unsigned char c;
 
 	j = 0;
 	for (i = 0; dirtytext[i] != '\0'; i++) {
--- a/common-runopts.c	Fri Aug 06 02:51:17 2004 +0000
+++ b/common-runopts.c	Fri Aug 06 16:18:01 2004 +0000
@@ -24,5 +24,34 @@
 
 #include "includes.h"
 #include "runopts.h"
+#include "signkey.h"
+#include "buffer.h"
+#include "dbutil.h"
+#include "auth.h"
 
 runopts opts; /* GLOBAL */
+
+/* returns success or failure, and the keytype in *type. If we want
+ * to restrict the type, type can contain a type to return */
+int readhostkey(const char * filename, sign_key * hostkey, int *type) {
+
+	int ret = DROPBEAR_FAILURE;
+	buffer *buf;
+
+	buf = buf_new(MAX_PRIVKEY_SIZE);
+
+	if (buf_readfile(buf, filename) == DROPBEAR_FAILURE) {
+		goto out;
+	}
+	buf_setpos(buf, 0);
+	if (buf_get_priv_key(buf, hostkey, type) == DROPBEAR_FAILURE) {
+		goto out;
+	}
+
+	ret = DROPBEAR_SUCCESS;
+out:
+
+	buf_burn(buf);
+	buf_free(buf);
+	return ret;
+}
--- a/debug.h	Fri Aug 06 02:51:17 2004 +0000
+++ b/debug.h	Fri Aug 06 16:18:01 2004 +0000
@@ -36,7 +36,7 @@
 /* Define this to print trace statements - very verbose */
 /* Caution: Don't use this in an unfriendly environment (ie unfirewalled),
  * since the printing does not sanitise strings etc */
-//#define DEBUG_TRACE
+#define DEBUG_TRACE
 
 /* All functions writing to the cleartext payload buffer call
  * CHECKCLEARTOWRITE() before writing. This is only really useful if you're
--- a/options.h	Fri Aug 06 02:51:17 2004 +0000
+++ b/options.h	Fri Aug 06 16:18:01 2004 +0000
@@ -275,6 +275,11 @@
 #define MAX_STRING_LEN 1400 /* ~= MAX_PROPOSED_ALGO * MAX_NAME_LEN, also
 							   is the max length for a password etc */
 
+/* For a 4096 bit DSS key, empirically determined to be 1590 bytes */
+#define MAX_PUBKEY_SIZE 1600
+/* For a 4096 bit DSS key, empirically determined to be 1590 bytes */
+#define MAX_PRIVKEY_SIZE 1600
+
 #ifndef ENABLE_X11FWD
 #define DISABLE_X11FWD
 #endif
--- a/runopts.h	Fri Aug 06 02:51:17 2004 +0000
+++ b/runopts.h	Fri Aug 06 16:18:01 2004 +0000
@@ -28,6 +28,7 @@
 #include "includes.h"
 #include "signkey.h"
 #include "buffer.h"
+#include "auth.h"
 
 typedef struct runopts {
 
@@ -38,6 +39,8 @@
 
 extern runopts opts;
 
+int readhostkey(const char * filename, sign_key * hostkey, int *type);
+
 typedef struct svr_runopts {
 
 	char * rsakeyfile;
@@ -87,6 +90,9 @@
 
 	char *cmd;
 	int wantpty;
+	struct PubkeyList *pubkeys; /* Keys to use for public-key auth */
+#ifdef DROPBEAR_PUBKEY_AUTH
+#endif
 	/* XXX TODO */
 
 } cli_runopts;
--- a/session.h	Fri Aug 06 02:51:17 2004 +0000
+++ b/session.h	Fri Aug 06 16:18:01 2004 +0000
@@ -214,7 +214,6 @@
 
 	int winchange; /* Set to 1 when a windowchange signal happens */
 
-	struct PubkeyList *pubkeys; /* Keys to use for public-key auth */
 	int lastauthtype; /* either AUTH_TYPE_PUBKEY or AUTH_TYPE_PASSWORD,
 						 for the last type of auth we tried */
 	struct PubkeyList *lastpubkey;
--- a/svr-runopts.c	Fri Aug 06 02:51:17 2004 +0000
+++ b/svr-runopts.c	Fri Aug 06 16:18:01 2004 +0000
@@ -33,7 +33,6 @@
 
 static sign_key * loadhostkeys(const char * dsskeyfile,
 		const char * rsakeyfile);
-static int readhostkey(const char * filename, sign_key * hostkey, int type);
 static void printhelp(const char * progname);
 
 static void printhelp(const char * progname) {
@@ -263,57 +262,44 @@
 
 }
 
-
-/* returns success or failure */
-static int readhostkey(const char * filename, sign_key * hostkey, int type) {
+static void disablekey(int type, const char* filename) {
 
-	int ret = DROPBEAR_FAILURE;
 	int i;
-	buffer *buf;
 
-	buf = buf_new(2000);
-
-	if (buf_readfile(buf, filename) == DROPBEAR_FAILURE) {
-		goto out;
-	}
-	buf_setpos(buf, 0);
-	if (buf_get_priv_key(buf, hostkey, &type) == DROPBEAR_FAILURE) {
-		goto out;
+	for (i = 0; sshhostkey[i].name != NULL; i++) {
+		if (sshhostkey[i].val == type) {
+			sshhostkey[i].usable = 0;
+			break;
+		}
 	}
-
-	ret = DROPBEAR_SUCCESS;
-out:
-	if (ret == DROPBEAR_FAILURE) {
-		for (i = 0; sshhostkey[i].name != NULL; i++) {
-			if (sshhostkey[i].val == type) {
-				sshhostkey[i].usable = 0;
-				break;
-			}
-		}
-		fprintf(stderr, "Failed reading '%s', disabling %s\n", filename,
-				type == DROPBEAR_SIGNKEY_DSS ? "DSS" : "RSA");
-	}
-
-	buf_burn(buf);
-	buf_free(buf);
-	return ret;
+	fprintf(stderr, "Failed reading '%s', disabling %s\n", filename,
+			type == DROPBEAR_SIGNKEY_DSS ? "DSS" : "RSA");
 }
 
 static sign_key * loadhostkeys(const char * dsskeyfile, 
 		const char * rsakeyfile) {
 
 	sign_key * hostkey;
+	int ret;
+	int type;
 
 	TRACE(("enter loadhostkeys"));
 
 	hostkey = new_sign_key();
 
 #ifdef DROPBEAR_RSA
-	(void)readhostkey(rsakeyfile, hostkey, DROPBEAR_SIGNKEY_RSA);
+	type = DROPBEAR_SIGNKEY_RSA;
+	ret = readhostkey(rsakeyfile, hostkey, &type);
+	if (ret == DROPBEAR_FAILURE) {
+		disablekey(DROPBEAR_SIGNKEY_RSA, rsakeyfile);
+	}
 #endif
-
 #ifdef DROPBEAR_DSS
-	(void)readhostkey(dsskeyfile, hostkey, DROPBEAR_SIGNKEY_DSS);
+	type = DROPBEAR_SIGNKEY_RSA;
+	ret = readhostkey(dsskeyfile, hostkey, &type);
+	if (ret == DROPBEAR_FAILURE) {
+		disablekey(DROPBEAR_SIGNKEY_DSS, dsskeyfile);
+	}
 #endif
 
 	if ( 1