From 3f2d010c23f77862bb890b5f88244039bcf8f875 Mon Sep 17 00:00:00 2001 From: simon Date: Sun, 30 Dec 2001 15:58:17 +0000 Subject: [PATCH] Pageant is now able to avoid asking for the passphrase when asked to load a key that is already loaded. This makes command lines such as `pageant mykey -c mycommand' almost infinitely more useful. git-svn-id: svn://svn.tartarus.org/sgt/putty@1522 cda61777-01e9-0310-a592-d414129be87e --- misc.h | 7 ++ pageant.c | 319 ++++++++++++++++++++++++++++++++++++++++++++++++-------------- ssh.h | 3 + sshpubk.c | 54 ++++++++++- sshrsa.c | 55 ++++++++--- x11fwd.c | 7 -- 6 files changed, 350 insertions(+), 95 deletions(-) diff --git a/misc.h b/misc.h index 9ba5a18c..12d0003f 100644 --- a/misc.h +++ b/misc.h @@ -3,6 +3,13 @@ #include "puttymem.h" +#ifndef FALSE +#define FALSE 0 +#endif +#ifndef TRUE +#define TRUE 1 +#endif + char *dupstr(char *s); char *dupcat(char *s1, ...); diff --git a/pageant.c b/pageant.c index 20aaaf48..12e03a8a 100644 --- a/pageant.c +++ b/pageant.c @@ -9,6 +9,7 @@ #include #include #include +#include #include #include "ssh.h" @@ -66,6 +67,14 @@ void agent_query(void *in, int inlen, void **out, int *outlen); int agent_exists(void); /* + * Forward references + */ +static void *make_keylist1(int *length); +static void *make_keylist2(int *length); +static void *get_keylist1(void); +static void *get_keylist2(void); + +/* * We need this to link with the RSA code, because rsaencrypt() * pads its data with random bytes. Since we only use rsadecrypt() * and the signing functions, which are deterministic, this should @@ -357,6 +366,65 @@ static void add_keyfile(char *filename) return; } + /* + * See if the key is already loaded (in the primary Pageant, + * which may or may not be us). + */ + { + void *blob; + unsigned char *keylist, *p; + int i, nkeys, bloblen; + + if (ver == 1) { + if (!rsakey_pubblob(filename, &blob, &bloblen)) { + MessageBox(NULL, "Couldn't load private key.", APPNAME, + MB_OK | MB_ICONERROR); + return; + } + keylist = get_keylist1(); + } else { + unsigned char *blob2; + blob = ssh2_userkey_loadpub(filename, NULL, &bloblen); + if (!blob) { + MessageBox(NULL, "Couldn't load private key.", APPNAME, + MB_OK | MB_ICONERROR); + return; + } + /* For our purposes we want the blob prefixed with its length */ + blob2 = smalloc(bloblen+4); + PUT_32BIT(blob2, bloblen); + memcpy(blob2 + 4, blob, bloblen); + sfree(blob); + blob = blob2; + + keylist = get_keylist2(); + } + if (keylist) { + nkeys = GET_32BIT(keylist); + p = keylist + 4; + + for (i = 0; i < nkeys; i++) { + if (!memcmp(blob, p, bloblen)) { + /* Key is already present; we can now leave. */ + sfree(keylist); + sfree(blob); + return; + } + /* Now skip over public blob */ + if (ver == 1) + p += rsa_public_blob_len(p); + else + p += 4 + GET_32BIT(p); + /* Now skip over comment field */ + p += 4 + GET_32BIT(p); + } + + sfree(keylist); + } + + sfree(blob); + } + if (ver == 1) needs_pass = rsakey_encrypted(filename, &comment); else @@ -461,6 +529,9 @@ static void add_keyfile(char *filename) MessageBox(NULL, "The already running Pageant " "refused to add the key.", APPNAME, MB_OK | MB_ICONERROR); + + sfree(request); + sfree(response); } else { if (add234(rsakeys, rkey) != rkey) sfree(rkey); /* already present, don't waste RAM */ @@ -503,6 +574,9 @@ static void add_keyfile(char *filename) MessageBox(NULL, "The already running Pageant" "refused to add the key.", APPNAME, MB_OK | MB_ICONERROR); + + sfree(request); + sfree(response); } else { if (add234(ssh2keys, skey) != skey) { skey->alg->freekey(skey->data); @@ -513,6 +587,162 @@ static void add_keyfile(char *filename) } /* + * Create an SSH1 key list in a malloc'ed buffer; return its + * length. + */ +static void *make_keylist1(int *length) +{ + int i, nkeys, len; + struct RSAKey *key; + unsigned char *blob, *p, *ret; + int bloblen; + + /* + * Count up the number and length of keys we hold. + */ + len = 4; + nkeys = 0; + for (i = 0; NULL != (key = index234(rsakeys, i)); i++) { + nkeys++; + blob = rsa_public_blob(key, &bloblen); + len += bloblen; + sfree(blob); + len += 4 + strlen(key->comment); + } + + /* Allocate the buffer. */ + p = ret = smalloc(len); + if (length) *length = len; + + PUT_32BIT(p, nkeys); + p += 4; + for (i = 0; NULL != (key = index234(rsakeys, i)); i++) { + blob = rsa_public_blob(key, &bloblen); + memcpy(p, blob, bloblen); + p += bloblen; + sfree(blob); + PUT_32BIT(p, strlen(key->comment)); + memcpy(p + 4, key->comment, strlen(key->comment)); + p += 4 + strlen(key->comment); + } + + assert(p - ret == len); + return ret; +} + +/* + * Create an SSH2 key list in a malloc'ed buffer; return its + * length. + */ +static void *make_keylist2(int *length) +{ + struct ssh2_userkey *key; + int i, len, nkeys; + unsigned char *blob, *p, *ret; + int bloblen; + + /* + * Count up the number and length of keys we hold. + */ + len = 4; + nkeys = 0; + for (i = 0; NULL != (key = index234(ssh2keys, i)); i++) { + nkeys++; + len += 4; /* length field */ + blob = key->alg->public_blob(key->data, &bloblen); + len += bloblen; + sfree(blob); + len += 4 + strlen(key->comment); + } + + /* Allocate the buffer. */ + p = ret = smalloc(len); + if (length) *length = len; + + /* + * Packet header is the obvious five bytes, plus four + * bytes for the key count. + */ + PUT_32BIT(p, nkeys); + p += 4; + for (i = 0; NULL != (key = index234(ssh2keys, i)); i++) { + blob = key->alg->public_blob(key->data, &bloblen); + PUT_32BIT(p, bloblen); + p += 4; + memcpy(p, blob, bloblen); + p += bloblen; + sfree(blob); + PUT_32BIT(p, strlen(key->comment)); + memcpy(p + 4, key->comment, strlen(key->comment)); + p += 4 + strlen(key->comment); + } + + assert(p - ret == len); + return ret; +} + +/* + * Acquire a keylist1 from the primary Pageant; this means either + * calling make_keylist1 (if that's us) or sending a message to the + * primary Pageant (if it's not). + */ +static void *get_keylist1(void) +{ + void *ret; + + if (already_running) { + unsigned char request[5], *response; + void *vresponse; + int resplen; + request[4] = SSH1_AGENTC_REQUEST_RSA_IDENTITIES; + PUT_32BIT(request, 4); + + agent_query(request, 5, &vresponse, &resplen); + response = vresponse; + if (resplen < 5 || response[4] != SSH1_AGENT_RSA_IDENTITIES_ANSWER) + return NULL; + + ret = smalloc(resplen-5); + memcpy(ret, response+5, resplen-5); + sfree(response); + } else { + ret = make_keylist1(NULL); + } + return ret; +} + +/* + * Acquire a keylist2 from the primary Pageant; this means either + * calling make_keylist2 (if that's us) or sending a message to the + * primary Pageant (if it's not). + */ +static void *get_keylist2(void) +{ + void *ret; + + if (already_running) { + unsigned char request[5], *response; + void *vresponse; + int resplen; + + request[4] = SSH2_AGENTC_REQUEST_IDENTITIES; + PUT_32BIT(request, 4); + + agent_query(request, 5, &vresponse, &resplen); + response = vresponse; + if (resplen < 5 || response[4] != SSH2_AGENT_IDENTITIES_ANSWER) + return NULL; + + ret = smalloc(resplen-5); + memcpy(ret, response+5, resplen-5); + sfree(response); + } else { + ret = make_keylist2(NULL); + } + return ret; +} + +/* * This is the main agent function that answers messages. */ static void answer_msg(void *msg) @@ -533,42 +763,18 @@ static void answer_msg(void *msg) * Reply with SSH1_AGENT_RSA_IDENTITIES_ANSWER. */ { - struct RSAKey *key; - int len, nkeys; - int i; - - /* - * Count up the number and length of keys we hold. - */ - len = nkeys = 0; - for (i = 0; NULL != (key = index234(rsakeys, i)); i++) { - nkeys++; - len += 4; /* length field */ - len += ssh1_bignum_length(key->exponent); - len += ssh1_bignum_length(key->modulus); - len += 4 + strlen(key->comment); - } + int len; + void *keylist; - /* - * Packet header is the obvious five bytes, plus four - * bytes for the key count. - */ - len += 5 + 4; - if (len > AGENT_MAX_MSGLEN) - goto failure; /* aaargh! too much stuff! */ - PUT_32BIT(ret, len - 4); ret[4] = SSH1_AGENT_RSA_IDENTITIES_ANSWER; - PUT_32BIT(ret + 5, nkeys); - p = ret + 5 + 4; - for (i = 0; NULL != (key = index234(rsakeys, i)); i++) { - PUT_32BIT(p, bignum_bitcount(key->modulus)); - p += 4; - p += ssh1_write_bignum(p, key->exponent); - p += ssh1_write_bignum(p, key->modulus); - PUT_32BIT(p, strlen(key->comment)); - memcpy(p + 4, key->comment, strlen(key->comment)); - p += 4 + strlen(key->comment); + keylist = make_keylist1(&len); + if (len + 5 > AGENT_MAX_MSGLEN) { + sfree(keylist); + goto failure; } + PUT_32BIT(ret, len + 1); + memcpy(ret + 5, keylist, len); + sfree(keylist); } break; case SSH2_AGENTC_REQUEST_IDENTITIES: @@ -576,47 +782,18 @@ static void answer_msg(void *msg) * Reply with SSH2_AGENT_IDENTITIES_ANSWER. */ { - struct ssh2_userkey *key; - int len, nkeys; - unsigned char *blob; - int bloblen; - int i; - - /* - * Count up the number and length of keys we hold. - */ - len = nkeys = 0; - for (i = 0; NULL != (key = index234(ssh2keys, i)); i++) { - nkeys++; - len += 4; /* length field */ - blob = key->alg->public_blob(key->data, &bloblen); - len += bloblen; - sfree(blob); - len += 4 + strlen(key->comment); - } + int len; + void *keylist; - /* - * Packet header is the obvious five bytes, plus four - * bytes for the key count. - */ - len += 5 + 4; - if (len > AGENT_MAX_MSGLEN) - goto failure; /* aaargh! too much stuff! */ - PUT_32BIT(ret, len - 4); ret[4] = SSH2_AGENT_IDENTITIES_ANSWER; - PUT_32BIT(ret + 5, nkeys); - p = ret + 5 + 4; - for (i = 0; NULL != (key = index234(ssh2keys, i)); i++) { - blob = key->alg->public_blob(key->data, &bloblen); - PUT_32BIT(p, bloblen); - p += 4; - memcpy(p, blob, bloblen); - p += bloblen; - sfree(blob); - PUT_32BIT(p, strlen(key->comment)); - memcpy(p + 4, key->comment, strlen(key->comment)); - p += 4 + strlen(key->comment); + keylist = make_keylist2(&len); + if (len + 5 > AGENT_MAX_MSGLEN) { + sfree(keylist); + goto failure; } + PUT_32BIT(ret, len + 1); + memcpy(ret + 5, keylist, len); + sfree(keylist); } break; case SSH1_AGENTC_RSA_CHALLENGE: diff --git a/ssh.h b/ssh.h index c05c78c8..3981066c 100644 --- a/ssh.h +++ b/ssh.h @@ -64,6 +64,8 @@ int rsastr_len(struct RSAKey *key); void rsastr_fmt(char *str, struct RSAKey *key); void rsa_fingerprint(char *str, int len, struct RSAKey *key); int rsa_verify(struct RSAKey *key); +unsigned char *rsa_public_blob(struct RSAKey *key, int *len); +int rsa_public_blob_len(void *data); void freersakey(struct RSAKey *key); typedef unsigned int word32; @@ -274,6 +276,7 @@ Bignum dh_find_K(Bignum f); int loadrsakey(char *filename, struct RSAKey *key, char *passphrase); int rsakey_encrypted(char *filename, char **comment); +int rsakey_pubblob(char *filename, void **blob, int *bloblen); int saversakey(char *filename, struct RSAKey *key, char *passphrase); diff --git a/sshpubk.c b/sshpubk.c index 86c9fc03..aa65d987 100644 --- a/sshpubk.c +++ b/sshpubk.c @@ -32,7 +32,7 @@ (x)=='+' ? 62 : \ (x)=='/' ? 63 : 0 ) -static int loadrsakey_main(FILE * fp, struct RSAKey *key, +static int loadrsakey_main(FILE * fp, struct RSAKey *key, int pub_only, char **commentptr, char *passphrase) { unsigned char buf[16384]; @@ -76,6 +76,11 @@ static int loadrsakey_main(FILE * fp, struct RSAKey *key, if (len - i < 0) goto end; /* overran */ + if (pub_only) { + ret = 1; + goto end; + } + /* Next, the comment field. */ j = GET_32BIT(buf + i); i += 4; @@ -161,7 +166,7 @@ int loadrsakey(char *filename, struct RSAKey *key, char *passphrase) * key file. */ if (fgets(buf, sizeof(buf), fp) && !strcmp(buf, rsa_signature)) { - return loadrsakey_main(fp, key, NULL, passphrase); + return loadrsakey_main(fp, key, FALSE, NULL, passphrase); } /* @@ -189,13 +194,50 @@ int rsakey_encrypted(char *filename, char **comment) * key file. */ if (fgets(buf, sizeof(buf), fp) && !strcmp(buf, rsa_signature)) { - return loadrsakey_main(fp, NULL, comment, NULL); + return loadrsakey_main(fp, NULL, FALSE, comment, NULL); } fclose(fp); return 0; /* wasn't the right kind of file */ } /* + * Return a malloc'ed chunk of memory containing the public blob of + * an RSA key, as given in the agent protocol (modulus bits, + * exponent, modulus). + */ +int rsakey_pubblob(char *filename, void **blob, int *bloblen) +{ + FILE *fp; + unsigned char buf[64]; + struct RSAKey key; + int ret; + + /* Default return if we fail. */ + *blob = NULL; + *bloblen = 0; + ret = 0; + + fp = fopen(filename, "rb"); + if (!fp) + return 0; /* doesn't even exist */ + + /* + * Read the first line of the file and see if it's a v1 private + * key file. + */ + if (fgets(buf, sizeof(buf), fp) && !strcmp(buf, rsa_signature)) { + memset(&key, 0, sizeof(key)); + if (loadrsakey_main(fp, &key, TRUE, NULL, NULL)) { + *blob = rsa_public_blob(&key, bloblen); + freersakey(&key); + ret = 1; + } + } + fclose(fp); + return ret; +} + +/* * Save an RSA key file. Return nonzero on success. */ int saversakey(char *filename, struct RSAKey *key, char *passphrase) @@ -827,8 +869,10 @@ char *ssh2_userkey_loadpub(char *filename, char **algorithm, goto error; fclose(fp); - *pub_blob_len = public_blob_len; - *algorithm = alg->name; + if (pub_blob_len) + *pub_blob_len = public_blob_len; + if (algorithm) + *algorithm = alg->name; return public_blob; /* diff --git a/sshrsa.c b/sshrsa.c index 74cfac57..92160d46 100644 --- a/sshrsa.c +++ b/sshrsa.c @@ -12,6 +12,17 @@ #include "ssh.h" +#define GET_32BIT(cp) \ + (((unsigned long)(unsigned char)(cp)[0] << 24) | \ + ((unsigned long)(unsigned char)(cp)[1] << 16) | \ + ((unsigned long)(unsigned char)(cp)[2] << 8) | \ + ((unsigned long)(unsigned char)(cp)[3])) + +#define PUT_32BIT(cp, value) { \ + (cp)[0] = (unsigned char)((value) >> 24); \ + (cp)[1] = (unsigned char)((value) >> 16); \ + (cp)[2] = (unsigned char)((value) >> 8); \ + (cp)[3] = (unsigned char)(value); } int makekey(unsigned char *data, struct RSAKey *result, unsigned char **keystr, int order) @@ -217,6 +228,38 @@ int rsa_verify(struct RSAKey *key) return 1; } +/* Public key blob as used by Pageant: exponent before modulus. */ +unsigned char *rsa_public_blob(struct RSAKey *key, int *len) +{ + int length, pos; + unsigned char *ret; + + length = (ssh1_bignum_length(key->modulus) + + ssh1_bignum_length(key->exponent) + 4); + ret = smalloc(length); + + PUT_32BIT(ret, bignum_bitcount(key->modulus)); + pos = 4; + pos += ssh1_write_bignum(ret + pos, key->exponent); + pos += ssh1_write_bignum(ret + pos, key->modulus); + + *len = length; + return ret; +} + +/* Given a public blob, determine its length. */ +int rsa_public_blob_len(void *data) +{ + unsigned char *p = (unsigned char *)data; + int ret; + + p += 4; /* length word */ + p += ssh1_read_bignum(p, NULL); /* exponent */ + p += ssh1_read_bignum(p, NULL); /* modulus */ + + return p - (unsigned char *)data; +} + void freersakey(struct RSAKey *key) { if (key->modulus) @@ -233,18 +276,6 @@ void freersakey(struct RSAKey *key) * Implementation of the ssh-rsa signing key type. */ -#define GET_32BIT(cp) \ - (((unsigned long)(unsigned char)(cp)[0] << 24) | \ - ((unsigned long)(unsigned char)(cp)[1] << 16) | \ - ((unsigned long)(unsigned char)(cp)[2] << 8) | \ - ((unsigned long)(unsigned char)(cp)[3])) - -#define PUT_32BIT(cp, value) { \ - (cp)[0] = (unsigned char)((value) >> 24); \ - (cp)[1] = (unsigned char)((value) >> 16); \ - (cp)[2] = (unsigned char)((value) >> 8); \ - (cp)[3] = (unsigned char)(value); } - static void getstring(char **data, int *datalen, char **p, int *length) { *p = NULL; diff --git a/x11fwd.c b/x11fwd.c index 6fde0d8f..480d8560 100644 --- a/x11fwd.c +++ b/x11fwd.c @@ -5,13 +5,6 @@ #include "putty.h" #include "ssh.h" -#ifndef FALSE -#define FALSE 0 -#endif -#ifndef TRUE -#define TRUE 1 -#endif - #define GET_32BIT_LSB_FIRST(cp) \ (((unsigned long)(unsigned char)(cp)[0]) | \ ((unsigned long)(unsigned char)(cp)[1] << 8) | \ -- 2.11.0