1
0
mirror of https://git.tartarus.org/simon/putty.git synced 2025-01-10 01:48:00 +00:00

Build SSH agent reply messages in a BinarySink.

This gets rid of yet another huge pile of beating around the bush with
length-counting. Also, this time, the BinarySink in question is a
little more interesting than just being a strbuf every time: on
Windows, where the shared-memory Pageant IPC system imposes a hard
limit on the size of message we can return, I've written a custom
BinarySink implementation that collects up to that much data and then
gives up and sets an overflow flag rather than continue to allocate
memory.

So the main Pageant code no longer has to worry about checking
AGENT_MAX_MSGLEN all the time - and better still, the Unix version of
Pageant is no longer _limited_ by AGENT_MAX_MSGLEN in its outgoing
messages, i.e. it could store a really extra large number of keys if
it needed to. That limitation is now a local feature of Windows
Pageant rather than intrinsic to the whole code base.

(AGENT_MAX_MSGLEN is still used to check incoming agent messages for
sanity, however. Mostly that's because I feel I ought to check them
against _some_ limit, and this one seems sensible enough. Incoming
agent messages are more bounded anyway - they generally don't hold
more than _one_ private key.)
This commit is contained in:
Simon Tatham 2018-05-24 13:23:17 +01:00
parent 0c44fa85df
commit b6cbad89fc
3 changed files with 234 additions and 237 deletions

409
pageant.c
View File

@ -127,14 +127,8 @@ static int cmpkeys_ssh2(void *av, void *bv)
return toret; return toret;
} }
/* void pageant_make_keylist1(BinarySink *bs)
* Create an SSH-1 key list in a malloc'ed buffer; return its
* length.
*/
void *pageant_make_keylist1(int *length)
{ {
strbuf *buf = strbuf_new();
BinarySink *bs = BinarySink_UPCAST(buf);
int i; int i;
struct RSAKey *key; struct RSAKey *key;
@ -143,18 +137,10 @@ void *pageant_make_keylist1(int *length)
rsa_ssh1_public_blob(bs, key, RSA_SSH1_EXPONENT_FIRST); rsa_ssh1_public_blob(bs, key, RSA_SSH1_EXPONENT_FIRST);
put_stringz(bs, key->comment); put_stringz(bs, key->comment);
} }
return strbuf_to_str(buf);
} }
/* void pageant_make_keylist2(BinarySink *bs)
* Create an SSH-2 key list in a malloc'ed buffer; return its
* length.
*/
void *pageant_make_keylist2(int *length)
{ {
strbuf *buf = strbuf_new();
BinarySink *bs = BinarySink_UPCAST(buf);
int i; int i;
struct ssh2_userkey *key; struct ssh2_userkey *key;
@ -165,8 +151,6 @@ void *pageant_make_keylist2(int *length)
put_stringsb(bs, blob); put_stringsb(bs, blob);
put_stringz(bs, key->comment); put_stringz(bs, key->comment);
} }
return strbuf_to_str(buf);
} }
static void plog(void *logctx, pageant_logfn_t logfn, const char *fmt, ...) static void plog(void *logctx, pageant_logfn_t logfn, const char *fmt, ...)
@ -193,14 +177,13 @@ static void plog(void *logctx, pageant_logfn_t logfn, const char *fmt, ...)
} }
} }
void *pageant_handle_msg(const void *msg, int msglen, int *outlen, void pageant_handle_msg(BinarySink *bs,
void *logctx, pageant_logfn_t logfn) const void *msg, int msglen,
void *logctx, pageant_logfn_t logfn)
{ {
const unsigned char *p = msg; const unsigned char *p = msg;
const unsigned char *msgend; const unsigned char *msgend;
unsigned char *ret = snewn(AGENT_MAX_MSGLEN, unsigned char);
int type; int type;
const char *fail_reason;
msgend = p + msglen; msgend = p + msglen;
@ -208,8 +191,9 @@ void *pageant_handle_msg(const void *msg, int msglen, int *outlen,
* Get the message type. * Get the message type.
*/ */
if (msgend < p+1) { if (msgend < p+1) {
fail_reason = "message contained no type code"; pageant_failure_msg(bs, "message contained no type code",
goto failure; logctx, logfn);
return;
} }
type = *p++; type = *p++;
@ -219,20 +203,10 @@ void *pageant_handle_msg(const void *msg, int msglen, int *outlen,
* Reply with SSH1_AGENT_RSA_IDENTITIES_ANSWER. * Reply with SSH1_AGENT_RSA_IDENTITIES_ANSWER.
*/ */
{ {
int len;
void *keylist;
plog(logctx, logfn, "request: SSH1_AGENTC_REQUEST_RSA_IDENTITIES"); plog(logctx, logfn, "request: SSH1_AGENTC_REQUEST_RSA_IDENTITIES");
ret[4] = SSH1_AGENT_RSA_IDENTITIES_ANSWER; put_byte(bs, SSH1_AGENT_RSA_IDENTITIES_ANSWER);
keylist = pageant_make_keylist1(&len); pageant_make_keylist1(bs);
if (len + 5 > AGENT_MAX_MSGLEN) {
sfree(keylist);
fail_reason = "output would exceed max msglen";
goto failure;
}
PUT_32BIT(ret, len + 1);
memcpy(ret + 5, keylist, len);
plog(logctx, logfn, "reply: SSH1_AGENT_RSA_IDENTITIES_ANSWER"); plog(logctx, logfn, "reply: SSH1_AGENT_RSA_IDENTITIES_ANSWER");
if (logfn) { /* skip this loop if not logging */ if (logfn) { /* skip this loop if not logging */
@ -244,7 +218,6 @@ void *pageant_handle_msg(const void *msg, int msglen, int *outlen,
plog(logctx, logfn, "returned key: %s", fingerprint); plog(logctx, logfn, "returned key: %s", fingerprint);
} }
} }
sfree(keylist);
} }
break; break;
case SSH2_AGENTC_REQUEST_IDENTITIES: case SSH2_AGENTC_REQUEST_IDENTITIES:
@ -252,20 +225,10 @@ void *pageant_handle_msg(const void *msg, int msglen, int *outlen,
* Reply with SSH2_AGENT_IDENTITIES_ANSWER. * Reply with SSH2_AGENT_IDENTITIES_ANSWER.
*/ */
{ {
int len;
void *keylist;
plog(logctx, logfn, "request: SSH2_AGENTC_REQUEST_IDENTITIES"); plog(logctx, logfn, "request: SSH2_AGENTC_REQUEST_IDENTITIES");
ret[4] = SSH2_AGENT_IDENTITIES_ANSWER; put_byte(bs, SSH2_AGENT_IDENTITIES_ANSWER);
keylist = pageant_make_keylist2(&len); pageant_make_keylist2(bs);
if (len + 5 > AGENT_MAX_MSGLEN) {
sfree(keylist);
fail_reason = "output would exceed max msglen";
goto failure;
}
PUT_32BIT(ret, len + 1);
memcpy(ret + 5, keylist, len);
plog(logctx, logfn, "reply: SSH2_AGENT_IDENTITIES_ANSWER"); plog(logctx, logfn, "reply: SSH2_AGENT_IDENTITIES_ANSWER");
if (logfn) { /* skip this loop if not logging */ if (logfn) { /* skip this loop if not logging */
@ -279,8 +242,6 @@ void *pageant_handle_msg(const void *msg, int msglen, int *outlen,
sfree(fingerprint); sfree(fingerprint);
} }
} }
sfree(keylist);
} }
break; break;
case SSH1_AGENTC_RSA_CHALLENGE: case SSH1_AGENTC_RSA_CHALLENGE:
@ -294,55 +255,56 @@ void *pageant_handle_msg(const void *msg, int msglen, int *outlen,
Bignum challenge, response; Bignum challenge, response;
unsigned char response_source[48], response_md5[16]; unsigned char response_source[48], response_md5[16];
struct MD5Context md5c; struct MD5Context md5c;
int i, len; int i;
plog(logctx, logfn, "request: SSH1_AGENTC_RSA_CHALLENGE"); plog(logctx, logfn, "request: SSH1_AGENTC_RSA_CHALLENGE");
reqkey.exponent = reqkey.modulus = challenge = response = NULL;
p += 4; p += 4;
i = ssh1_read_bignum(p, msgend - p, &reqkey.exponent); i = ssh1_read_bignum(p, msgend - p, &reqkey.exponent);
if (i < 0) { if (i < 0) {
fail_reason = "request truncated before key exponent"; pageant_failure_msg(
goto failure; bs, "request truncated before key exponent",
logctx, logfn);
goto challenge1_cleanup;
} }
p += i; p += i;
i = ssh1_read_bignum(p, msgend - p, &reqkey.modulus); i = ssh1_read_bignum(p, msgend - p, &reqkey.modulus);
if (i < 0) { if (i < 0) {
freebn(reqkey.exponent); pageant_failure_msg(
fail_reason = "request truncated before key modulus"; bs, "request truncated before key modulus",
goto failure; logctx, logfn);
goto challenge1_cleanup;
} }
p += i; p += i;
i = ssh1_read_bignum(p, msgend - p, &challenge); i = ssh1_read_bignum(p, msgend - p, &challenge);
if (i < 0) { if (i < 0) {
freebn(reqkey.exponent); pageant_failure_msg(
freebn(reqkey.modulus); bs, "request truncated before challenge",
freebn(challenge); logctx, logfn);
fail_reason = "request truncated before challenge"; goto challenge1_cleanup;
goto failure;
} }
p += i; p += i;
if (msgend < p+16) { if (msgend < p+16) {
freebn(reqkey.exponent); pageant_failure_msg(
freebn(reqkey.modulus); bs, "request truncated before session id",
freebn(challenge); logctx, logfn);
fail_reason = "request truncated before session id"; goto challenge1_cleanup;
goto failure;
} }
memcpy(response_source + 32, p, 16); memcpy(response_source + 32, p, 16);
p += 16; p += 16;
if (msgend < p+4) { if (msgend < p+4) {
freebn(reqkey.exponent); pageant_failure_msg(
freebn(reqkey.modulus); bs, "request truncated before response type",
freebn(challenge); logctx, logfn);
fail_reason = "request truncated before response type"; goto challenge1_cleanup;
goto failure;
} }
if (GET_32BIT(p) != 1) { if (GET_32BIT(p) != 1) {
freebn(reqkey.exponent); pageant_failure_msg(
freebn(reqkey.modulus); bs, "response type other than 1 not supported",
freebn(challenge); logctx, logfn);
fail_reason = "response type other than 1 not supported"; goto challenge1_cleanup;
goto failure;
} }
if (logfn) { if (logfn) {
char fingerprint[128]; char fingerprint[128];
@ -351,11 +313,8 @@ void *pageant_handle_msg(const void *msg, int msglen, int *outlen,
plog(logctx, logfn, "requested key: %s", fingerprint); plog(logctx, logfn, "requested key: %s", fingerprint);
} }
if ((key = find234(rsakeys, &reqkey, NULL)) == NULL) { if ((key = find234(rsakeys, &reqkey, NULL)) == NULL) {
freebn(reqkey.exponent); pageant_failure_msg(bs, "key not found", logctx, logfn);
freebn(reqkey.modulus); goto challenge1_cleanup;
freebn(challenge);
fail_reason = "key not found";
goto failure;
} }
response = rsa_ssh1_decrypt(challenge, key); response = rsa_ssh1_decrypt(challenge, key);
for (i = 0; i < 32; i++) for (i = 0; i < 32; i++)
@ -365,21 +324,17 @@ void *pageant_handle_msg(const void *msg, int msglen, int *outlen,
put_data(&md5c, response_source, 48); put_data(&md5c, response_source, 48);
MD5Final(response_md5, &md5c); MD5Final(response_md5, &md5c);
smemclr(response_source, 48); /* burn the evidence */ smemclr(response_source, 48); /* burn the evidence */
freebn(response); /* and that evidence */
freebn(challenge); /* yes, and that evidence */
freebn(reqkey.exponent); /* and free some memory ... */
freebn(reqkey.modulus); /* ... while we're at it. */
/* put_byte(bs, SSH1_AGENT_RSA_RESPONSE);
* Packet is the obvious five byte header, plus sixteen put_data(bs, response_md5, 16);
* bytes of MD5.
*/
len = 5 + 16;
PUT_32BIT(ret, len - 4);
ret[4] = SSH1_AGENT_RSA_RESPONSE;
memcpy(ret + 5, response_md5, 16);
plog(logctx, logfn, "reply: SSH1_AGENT_RSA_RESPONSE"); plog(logctx, logfn, "reply: SSH1_AGENT_RSA_RESPONSE");
challenge1_cleanup:
if (response) freebn(response);
if (challenge) freebn(challenge);
if (reqkey.exponent) freebn(reqkey.exponent);
if (reqkey.modulus) freebn(reqkey.modulus);
} }
break; break;
case SSH2_AGENTC_SIGN_REQUEST: case SSH2_AGENTC_SIGN_REQUEST:
@ -394,31 +349,39 @@ void *pageant_handle_msg(const void *msg, int msglen, int *outlen,
int bloblen; int bloblen;
const unsigned char *data; const unsigned char *data;
strbuf *signature; strbuf *signature;
int datalen, len; int datalen;
plog(logctx, logfn, "request: SSH2_AGENTC_SIGN_REQUEST"); plog(logctx, logfn, "request: SSH2_AGENTC_SIGN_REQUEST");
if (msgend < p+4) { if (msgend < p+4) {
fail_reason = "request truncated before public key"; pageant_failure_msg(
goto failure; bs, "request truncated before public key",
logctx, logfn);
return;
} }
bloblen = toint(GET_32BIT(p)); bloblen = toint(GET_32BIT(p));
if (bloblen < 0 || bloblen > msgend - (p+4)) { if (bloblen < 0 || bloblen > msgend - (p+4)) {
fail_reason = "request truncated before public key"; pageant_failure_msg(
goto failure; bs, "request truncated before public key",
logctx, logfn);
return;
} }
p += 4; p += 4;
blobp = p; blobp = p;
p += bloblen; p += bloblen;
if (msgend < p+4) { if (msgend < p+4) {
fail_reason = "request truncated before string to sign"; pageant_failure_msg(
goto failure; bs, "request truncated before string to sign",
logctx, logfn);
return;
} }
datalen = toint(GET_32BIT(p)); datalen = toint(GET_32BIT(p));
p += 4; p += 4;
if (datalen < 0 || datalen > msgend - p) { if (datalen < 0 || datalen > msgend - p) {
fail_reason = "request truncated before string to sign"; pageant_failure_msg(
goto failure; bs, "request truncated before string to sign",
logctx, logfn);
return;
} }
data = p; data = p;
if (logfn) { if (logfn) {
@ -433,18 +396,16 @@ void *pageant_handle_msg(const void *msg, int msglen, int *outlen,
strbuf_free(blob); strbuf_free(blob);
} }
if (!key) { if (!key) {
fail_reason = "key not found"; pageant_failure_msg(bs, "key not found", logctx, logfn);
goto failure; return;
} }
put_byte(bs, SSH2_AGENT_SIGN_RESPONSE);
signature = strbuf_new(); signature = strbuf_new();
key->alg->sign(key->data, (const char *)data, datalen, key->alg->sign(key->data, (const char *)data, datalen,
BinarySink_UPCAST(signature)); BinarySink_UPCAST(signature));
len = 5 + 4 + signature->len; put_stringsb(bs, signature);
PUT_32BIT(ret, len - 4);
ret[4] = SSH2_AGENT_SIGN_RESPONSE;
PUT_32BIT(ret + 5, signature->len);
memcpy(ret + 5 + 4, signature->s, signature->len);
strbuf_free(signature);
plog(logctx, logfn, "reply: SSH2_AGENT_SIGN_RESPONSE"); plog(logctx, logfn, "reply: SSH2_AGENT_SIGN_RESPONSE");
} }
@ -467,19 +428,19 @@ void *pageant_handle_msg(const void *msg, int msglen, int *outlen,
n = rsa_ssh1_readpub(p, msgend - p, key, NULL, n = rsa_ssh1_readpub(p, msgend - p, key, NULL,
RSA_SSH1_MODULUS_FIRST); RSA_SSH1_MODULUS_FIRST);
if (n < 0) { if (n < 0) {
freersakey(key); pageant_failure_msg(
sfree(key); bs, "request truncated before public key",
fail_reason = "request truncated before public key"; logctx, logfn);
goto failure; goto add1_cleanup;
} }
p += n; p += n;
n = rsa_ssh1_readpriv(p, msgend - p, key); n = rsa_ssh1_readpriv(p, msgend - p, key);
if (n < 0) { if (n < 0) {
freersakey(key); pageant_failure_msg(
sfree(key); bs, "request truncated before private key",
fail_reason = "request truncated before private key"; logctx, logfn);
goto failure; goto add1_cleanup;
} }
p += n; p += n;
@ -489,44 +450,41 @@ void *pageant_handle_msg(const void *msg, int msglen, int *outlen,
n = ssh1_read_bignum(p, msgend - p, &key->iqmp); /* p^-1 mod q */ n = ssh1_read_bignum(p, msgend - p, &key->iqmp); /* p^-1 mod q */
if (n < 0) { if (n < 0) {
freersakey(key); pageant_failure_msg(bs, "request truncated before iqmp",
sfree(key); logctx, logfn);
fail_reason = "request truncated before iqmp"; goto add1_cleanup;
goto failure;
} }
p += n; p += n;
n = ssh1_read_bignum(p, msgend - p, &key->q); /* p */ n = ssh1_read_bignum(p, msgend - p, &key->q); /* p */
if (n < 0) { if (n < 0) {
freersakey(key); pageant_failure_msg(bs, "request truncated before p",
sfree(key); logctx, logfn);
fail_reason = "request truncated before p"; goto add1_cleanup;
goto failure;
} }
p += n; p += n;
n = ssh1_read_bignum(p, msgend - p, &key->p); /* q */ n = ssh1_read_bignum(p, msgend - p, &key->p); /* q */
if (n < 0) { if (n < 0) {
freersakey(key); pageant_failure_msg(
sfree(key); bs, "request truncated before q", logctx, logfn);
fail_reason = "request truncated before q"; goto add1_cleanup;
goto failure;
} }
p += n; p += n;
if (msgend < p+4) { if (msgend < p+4) {
freersakey(key); pageant_failure_msg(
sfree(key); bs, "request truncated before key comment",
fail_reason = "request truncated before key comment"; logctx, logfn);
goto failure; goto add1_cleanup;
} }
commentlen = toint(GET_32BIT(p)); commentlen = toint(GET_32BIT(p));
if (commentlen < 0 || commentlen > msgend - p) { if (commentlen < 0 || commentlen > msgend - p) {
freersakey(key); pageant_failure_msg(
sfree(key); bs, "request truncated before key comment",
fail_reason = "request truncated before key comment"; logctx, logfn);
goto failure; goto add1_cleanup;
} }
comment = snewn(commentlen+1, char); comment = snewn(commentlen+1, char);
@ -544,17 +502,19 @@ void *pageant_handle_msg(const void *msg, int msglen, int *outlen,
if (add234(rsakeys, key) == key) { if (add234(rsakeys, key) == key) {
keylist_update(); keylist_update();
PUT_32BIT(ret, 1); put_byte(bs, SSH_AGENT_SUCCESS);
ret[4] = SSH_AGENT_SUCCESS;
plog(logctx, logfn, "reply: SSH_AGENT_SUCCESS"); plog(logctx, logfn, "reply: SSH_AGENT_SUCCESS");
key = NULL; /* don't free it in cleanup */
} else { } else {
pageant_failure_msg(bs, "key already present",
logctx, logfn);
}
add1_cleanup:
if (key) {
freersakey(key); freersakey(key);
sfree(key); sfree(key);
}
fail_reason = "key already present";
goto failure;
}
} }
break; break;
case SSH2_AGENTC_ADD_IDENTITY: case SSH2_AGENTC_ADD_IDENTITY:
@ -563,7 +523,7 @@ void *pageant_handle_msg(const void *msg, int msglen, int *outlen,
* SSH_AGENT_FAILURE if the key was malformed. * SSH_AGENT_FAILURE if the key was malformed.
*/ */
{ {
struct ssh2_userkey *key; struct ssh2_userkey *key = NULL;
char *comment; char *comment;
const char *alg; const char *alg;
int alglen, commlen; int alglen, commlen;
@ -572,32 +532,36 @@ void *pageant_handle_msg(const void *msg, int msglen, int *outlen,
plog(logctx, logfn, "request: SSH2_AGENTC_ADD_IDENTITY"); plog(logctx, logfn, "request: SSH2_AGENTC_ADD_IDENTITY");
if (msgend < p+4) { if (msgend < p+4) {
fail_reason = "request truncated before key algorithm"; pageant_failure_msg(
goto failure; bs, "request truncated before key algorithm",
logctx, logfn);
goto add2_cleanup;
} }
alglen = toint(GET_32BIT(p)); alglen = toint(GET_32BIT(p));
p += 4; p += 4;
if (alglen < 0 || alglen > msgend - p) { if (alglen < 0 || alglen > msgend - p) {
fail_reason = "request truncated before key algorithm"; pageant_failure_msg(
goto failure; bs, "request truncated before key algorithm",
logctx, logfn);
goto add2_cleanup;
} }
alg = (const char *)p; alg = (const char *)p;
p += alglen; p += alglen;
key = snew(struct ssh2_userkey); key = snew(struct ssh2_userkey);
key->data = NULL;
key->comment = NULL;
key->alg = find_pubkey_alg_len(alglen, alg); key->alg = find_pubkey_alg_len(alglen, alg);
if (!key->alg) { if (!key->alg) {
sfree(key); pageant_failure_msg(bs, "algorithm unknown", logctx, logfn);
fail_reason = "algorithm unknown"; goto add2_cleanup;
goto failure;
} }
bloblen = msgend - p; bloblen = msgend - p;
key->data = key->alg->openssh_createkey(key->alg, &p, &bloblen); key->data = key->alg->openssh_createkey(key->alg, &p, &bloblen);
if (!key->data) { if (!key->data) {
sfree(key); pageant_failure_msg(bs, "key setup failed", logctx, logfn);
fail_reason = "key setup failed"; goto add2_cleanup;
goto failure;
} }
/* /*
@ -607,19 +571,19 @@ void *pageant_handle_msg(const void *msg, int msglen, int *outlen,
assert(p <= msgend); assert(p <= msgend);
if (msgend < p+4) { if (msgend < p+4) {
key->alg->freekey(key->data); pageant_failure_msg(
sfree(key); bs, "request truncated before key comment",
fail_reason = "request truncated before key comment"; logctx, logfn);
goto failure; goto add2_cleanup;
} }
commlen = toint(GET_32BIT(p)); commlen = toint(GET_32BIT(p));
p += 4; p += 4;
if (commlen < 0 || commlen > msgend - p) { if (commlen < 0 || commlen > msgend - p) {
key->alg->freekey(key->data); pageant_failure_msg(
sfree(key); bs, "request truncated before key comment",
fail_reason = "request truncated before key comment"; logctx, logfn);
goto failure; goto add2_cleanup;
} }
comment = snewn(commlen + 1, char); comment = snewn(commlen + 1, char);
if (comment) { if (comment) {
@ -637,18 +601,24 @@ void *pageant_handle_msg(const void *msg, int msglen, int *outlen,
if (add234(ssh2keys, key) == key) { if (add234(ssh2keys, key) == key) {
keylist_update(); keylist_update();
PUT_32BIT(ret, 1); put_byte(bs, SSH_AGENT_SUCCESS);
ret[4] = SSH_AGENT_SUCCESS;
plog(logctx, logfn, "reply: SSH_AGENT_SUCCESS"); plog(logctx, logfn, "reply: SSH_AGENT_SUCCESS");
} else {
key->alg->freekey(key->data);
sfree(key->comment);
sfree(key);
fail_reason = "key already present"; key = NULL; /* don't clean it up */
goto failure; } else {
pageant_failure_msg(bs, "key already present",
logctx, logfn);
} }
add2_cleanup:
if (key) {
if (key->data)
key->alg->freekey(key->data);
if (key->comment)
sfree(key->comment);
sfree(key);
}
} }
break; break;
case SSH1_AGENTC_REMOVE_RSA_IDENTITY: case SSH1_AGENTC_REMOVE_RSA_IDENTITY:
@ -666,8 +636,10 @@ void *pageant_handle_msg(const void *msg, int msglen, int *outlen,
n = rsa_ssh1_readpub(p, msgend - p, &reqkey, NULL, n = rsa_ssh1_readpub(p, msgend - p, &reqkey, NULL,
RSA_SSH1_EXPONENT_FIRST); RSA_SSH1_EXPONENT_FIRST);
if (n < 0) { if (n < 0) {
fail_reason = "request truncated before public key"; pageant_failure_msg(
goto failure; bs, "request truncated before public key",
logctx, logfn);
return;
} }
if (logfn) { if (logfn) {
@ -680,7 +652,6 @@ void *pageant_handle_msg(const void *msg, int msglen, int *outlen,
key = find234(rsakeys, &reqkey, NULL); key = find234(rsakeys, &reqkey, NULL);
freebn(reqkey.exponent); freebn(reqkey.exponent);
freebn(reqkey.modulus); freebn(reqkey.modulus);
PUT_32BIT(ret, 1);
if (key) { if (key) {
plog(logctx, logfn, "found with comment: %s", key->comment); plog(logctx, logfn, "found with comment: %s", key->comment);
@ -688,12 +659,11 @@ void *pageant_handle_msg(const void *msg, int msglen, int *outlen,
keylist_update(); keylist_update();
freersakey(key); freersakey(key);
sfree(key); sfree(key);
ret[4] = SSH_AGENT_SUCCESS; put_byte(bs, SSH_AGENT_SUCCESS);
plog(logctx, logfn, "reply: SSH_AGENT_SUCCESS"); plog(logctx, logfn, "reply: SSH_AGENT_SUCCESS");
} else { } else {
fail_reason = "key not found"; pageant_failure_msg(bs, "key not found", logctx, logfn);
goto failure;
} }
} }
break; break;
@ -711,15 +681,19 @@ void *pageant_handle_msg(const void *msg, int msglen, int *outlen,
plog(logctx, logfn, "request: SSH2_AGENTC_REMOVE_IDENTITY"); plog(logctx, logfn, "request: SSH2_AGENTC_REMOVE_IDENTITY");
if (msgend < p+4) { if (msgend < p+4) {
fail_reason = "request truncated before public key"; pageant_failure_msg(
goto failure; bs, "request truncated before public key",
logctx, logfn);
return;
} }
bloblen = toint(GET_32BIT(p)); bloblen = toint(GET_32BIT(p));
p += 4; p += 4;
if (bloblen < 0 || bloblen > msgend - p) { if (bloblen < 0 || bloblen > msgend - p) {
fail_reason = "request truncated before public key"; pageant_failure_msg(
goto failure; bs, "request truncated before public key",
logctx, logfn);
return;
} }
blobp = p; blobp = p;
p += bloblen; p += bloblen;
@ -737,8 +711,8 @@ void *pageant_handle_msg(const void *msg, int msglen, int *outlen,
strbuf_free(blob); strbuf_free(blob);
} }
if (!key) { if (!key) {
fail_reason = "key not found"; pageant_failure_msg(bs, "key not found", logctx, logfn);
goto failure; return;
} }
plog(logctx, logfn, "found with comment: %s", key->comment); plog(logctx, logfn, "found with comment: %s", key->comment);
@ -747,8 +721,7 @@ void *pageant_handle_msg(const void *msg, int msglen, int *outlen,
keylist_update(); keylist_update();
key->alg->freekey(key->data); key->alg->freekey(key->data);
sfree(key); sfree(key);
PUT_32BIT(ret, 1); put_byte(bs, SSH_AGENT_SUCCESS);
ret[4] = SSH_AGENT_SUCCESS;
plog(logctx, logfn, "reply: SSH_AGENT_SUCCESS"); plog(logctx, logfn, "reply: SSH_AGENT_SUCCESS");
} }
@ -770,8 +743,7 @@ void *pageant_handle_msg(const void *msg, int msglen, int *outlen,
} }
keylist_update(); keylist_update();
PUT_32BIT(ret, 1); put_byte(bs, SSH_AGENT_SUCCESS);
ret[4] = SSH_AGENT_SUCCESS;
plog(logctx, logfn, "reply: SSH_AGENT_SUCCESS"); plog(logctx, logfn, "reply: SSH_AGENT_SUCCESS");
} }
@ -792,38 +764,24 @@ void *pageant_handle_msg(const void *msg, int msglen, int *outlen,
} }
keylist_update(); keylist_update();
PUT_32BIT(ret, 1); put_byte(bs, SSH_AGENT_SUCCESS);
ret[4] = SSH_AGENT_SUCCESS;
plog(logctx, logfn, "reply: SSH_AGENT_SUCCESS"); plog(logctx, logfn, "reply: SSH_AGENT_SUCCESS");
} }
break; break;
default: default:
plog(logctx, logfn, "request: unknown message type %d", type); plog(logctx, logfn, "request: unknown message type %d", type);
pageant_failure_msg(bs, "unrecognised message", logctx, logfn);
fail_reason = "unrecognised message";
/* fall through */
failure:
/*
* Unrecognised message. Return SSH_AGENT_FAILURE.
*/
PUT_32BIT(ret, 1);
ret[4] = SSH_AGENT_FAILURE;
plog(logctx, logfn, "reply: SSH_AGENT_FAILURE (%s)", fail_reason);
break; break;
} }
*outlen = 4 + GET_32BIT(ret);
return ret;
} }
void *pageant_failure_msg(int *outlen) void pageant_failure_msg(BinarySink *bs,
const char *log_reason,
void *logctx, pageant_logfn_t logfn)
{ {
unsigned char *ret = snewn(5, unsigned char); put_byte(bs, SSH_AGENT_FAILURE);
PUT_32BIT(ret, 1); plog(logctx, logfn, "reply: SSH_AGENT_FAILURE (%s)", log_reason);
ret[4] = SSH_AGENT_FAILURE;
*outlen = 5;
return ret;
} }
void pageant_init(void) void pageant_init(void)
@ -971,21 +929,24 @@ static void pageant_conn_receive(Plug plug, int urgent, char *data, int len)
} }
{ {
void *reply; strbuf *reply = strbuf_new();
int replylen;
put_uint32(reply, 0); /* length field to fill in later */
if (pc->real_packet) { if (pc->real_packet) {
reply = pageant_handle_msg(pc->pktbuf, pc->len, &replylen, pc, pageant_handle_msg(BinarySink_UPCAST(reply), pc->pktbuf, pc->len, pc,
pc->logfn?pageant_conn_log:NULL); pc->logfn ? pageant_conn_log : NULL);
} else { } else {
plog(pc->logctx, pc->logfn, "%p: overlong message (%u)", plog(pc->logctx, pc->logfn, "%p: overlong message (%u)",
pc, pc->len); pc, pc->len);
plog(pc->logctx, pc->logfn, "%p: reply: SSH_AGENT_FAILURE " pageant_failure_msg(BinarySink_UPCAST(reply), "message too long", pc,
"(message too long)", pc); pc->logfn ? pageant_conn_log : NULL);
reply = pageant_failure_msg(&replylen);
} }
sk_write(pc->connsock, reply, replylen);
smemclr(reply, replylen); PUT_32BIT(reply->s, reply->len - 4);
sk_write(pc->connsock, reply->s, reply->len);
strbuf_free(reply);
} }
} }
@ -1141,7 +1102,10 @@ void *pageant_get_keylist1(int *length)
if (length) if (length)
*length = resplen-5; *length = resplen-5;
} else { } else {
ret = pageant_make_keylist1(length); strbuf *buf = strbuf_new();
pageant_make_keylist1(BinarySink_UPCAST(buf));
*length = buf->len;
ret = strbuf_to_str(buf);
} }
return ret; return ret;
} }
@ -1174,7 +1138,10 @@ void *pageant_get_keylist2(int *length)
if (length) if (length)
*length = resplen-5; *length = resplen-5;
} else { } else {
ret = pageant_make_keylist2(length); strbuf *buf = strbuf_new();
pageant_make_keylist2(BinarySink_UPCAST(buf));
*length = buf->len;
ret = strbuf_to_str(buf);
} }
return ret; return ret;
} }

View File

@ -28,21 +28,26 @@ void pageant_init(void);
* Returns a fully formatted message as output, *with* its initial * Returns a fully formatted message as output, *with* its initial
* length field, and sets *outlen to the full size of that message. * length field, and sets *outlen to the full size of that message.
*/ */
void *pageant_handle_msg(const void *msg, int msglen, int *outlen, void pageant_handle_msg(BinarySink *bs,
void *logctx, pageant_logfn_t logfn); const void *msg, int msglen,
void *logctx, pageant_logfn_t logfn);
/* /*
* Construct a failure response. Useful for agent front ends which * Construct a failure response. Useful for agent front ends which
* suffer a problem before they even get to pageant_handle_msg. * suffer a problem before they even get to pageant_handle_msg.
*
* 'log_reason' is only used if logfn is not NULL.
*/ */
void *pageant_failure_msg(int *outlen); void pageant_failure_msg(BinarySink *bs,
const char *log_reason,
void *logctx, pageant_logfn_t logfn);
/* /*
* Construct a list of public keys, just as the two LIST_IDENTITIES * Construct a list of public keys, just as the two LIST_IDENTITIES
* requests would have returned them. * requests would have returned them.
*/ */
void *pageant_make_keylist1(int *length); void pageant_make_keylist1(BinarySink *);
void *pageant_make_keylist2(int *length); void pageant_make_keylist2(BinarySink *);
/* /*
* Accessor functions for Pageant's internal key lists. Fetch the nth * Accessor functions for Pageant's internal key lists. Fetch the nth

View File

@ -376,22 +376,46 @@ void keylist_update(void)
} }
} }
struct PageantReply {
char buf[AGENT_MAX_MSGLEN - 4];
int len, overflowed;
BinarySink_IMPLEMENTATION;
};
static void pageant_reply_BinarySink_write(
BinarySink *bs, const void *data, size_t len)
{
struct PageantReply *rep = BinarySink_DOWNCAST(bs, struct PageantReply);
if (!rep->overflowed && len <= sizeof(rep->buf) - rep->len) {
memcpy(rep->buf + rep->len, data, len);
rep->len += len;
} else {
rep->overflowed = TRUE;
}
}
static void answer_msg(void *msgv) static void answer_msg(void *msgv)
{ {
unsigned char *msg = (unsigned char *)msgv; unsigned char *msg = (unsigned char *)msgv;
unsigned msglen; unsigned msglen;
void *reply; struct PageantReply reply;
int replylen;
reply.len = 0;
reply.overflowed = FALSE;
BinarySink_INIT(&reply, pageant_reply_BinarySink_write);
msglen = GET_32BIT(msg); msglen = GET_32BIT(msg);
if (msglen > AGENT_MAX_MSGLEN) { if (msglen > AGENT_MAX_MSGLEN) {
reply = pageant_failure_msg(&replylen); pageant_failure_msg(BinarySink_UPCAST(&reply),
"incoming length field too large", NULL, NULL);
} else { } else {
reply = pageant_handle_msg(msg + 4, msglen, &replylen, NULL, NULL); pageant_handle_msg(BinarySink_UPCAST(&reply),
if (replylen > AGENT_MAX_MSGLEN) { msg + 4, msglen, NULL, NULL);
smemclr(reply, replylen); if (reply.len > AGENT_MAX_MSGLEN) {
sfree(reply); reply.len = 0;
reply = pageant_failure_msg(&replylen); reply.overflowed = FALSE;
pageant_failure_msg(BinarySink_UPCAST(&reply),
"output would exceed max msglen", NULL, NULL);
} }
} }
@ -399,9 +423,10 @@ static void answer_msg(void *msgv)
* Windows Pageant answers messages in place, by overwriting the * Windows Pageant answers messages in place, by overwriting the
* input message buffer. * input message buffer.
*/ */
memcpy(msg, reply, replylen); assert(4 + reply.len <= AGENT_MAX_MSGLEN);
smemclr(reply, replylen); PUT_32BIT(msg, reply.len);
sfree(reply); memcpy(msg + 4, reply.buf, reply.len);
smemclr(reply.buf, sizeof(reply.buf));
} }
static void win_add_keyfile(Filename *filename) static void win_add_keyfile(Filename *filename)