1
0
mirror of https://git.tartarus.org/simon/putty.git synced 2025-03-22 14:39:24 -05:00

Pageant server: parse requests using BinarySource.

pageant_handle_msg was _particularly_ full of painful manual packet
decoding with error checks at every stage, so it's a great relief to
throw it all away and replace it with short sequences of calls to the
shiny new API!
This commit is contained in:
Simon Tatham 2018-05-27 23:47:40 +01:00
parent e43605ee05
commit 392a8c00f6

335
pageant.c
View File

@ -80,7 +80,7 @@ static int cmpkeys_rsa(void *av, void *bv)
*/ */
static int cmpkeys_ssh2_asymm(void *av, void *bv) static int cmpkeys_ssh2_asymm(void *av, void *bv)
{ {
strbuf *ablob = (strbuf *) av; ptrlen *ablob = (ptrlen *) av;
struct ssh2_userkey *b = (struct ssh2_userkey *) bv; struct ssh2_userkey *b = (struct ssh2_userkey *) bv;
strbuf *bblob; strbuf *bblob;
int i, c; int i, c;
@ -93,10 +93,11 @@ static int cmpkeys_ssh2_asymm(void *av, void *bv)
c = 0; c = 0;
for (i = 0; i < ablob->len && i < bblob->len; i++) { for (i = 0; i < ablob->len && i < bblob->len; i++) {
if (ablob->u[i] < bblob->u[i]) { unsigned char abyte = ((unsigned char *)ablob->ptr)[i];
if (abyte < bblob->u[i]) {
c = -1; c = -1;
break; break;
} else if (ablob->u[i] > bblob->u[i]) { } else if (abyte > bblob->u[i]) {
c = +1; c = +1;
break; break;
} }
@ -118,11 +119,14 @@ static int cmpkeys_ssh2(void *av, void *bv)
{ {
struct ssh2_userkey *a = (struct ssh2_userkey *) av; struct ssh2_userkey *a = (struct ssh2_userkey *) av;
strbuf *ablob; strbuf *ablob;
ptrlen apl;
int toret; int toret;
ablob = strbuf_new(); ablob = strbuf_new();
a->alg->public_blob(a->data, BinarySink_UPCAST(ablob)); a->alg->public_blob(a->data, BinarySink_UPCAST(ablob));
toret = cmpkeys_ssh2_asymm(ablob, bv); apl.ptr = ablob->u;
apl.len = ablob->len;
toret = cmpkeys_ssh2_asymm(&apl, bv);
strbuf_free(ablob); strbuf_free(ablob);
return toret; return toret;
} }
@ -178,24 +182,20 @@ static void plog(void *logctx, pageant_logfn_t logfn, const char *fmt, ...)
} }
void pageant_handle_msg(BinarySink *bs, void pageant_handle_msg(BinarySink *bs,
const void *msg, int msglen, const void *msgdata, int msglen,
void *logctx, pageant_logfn_t logfn) void *logctx, pageant_logfn_t logfn)
{ {
const unsigned char *p = msg; BinarySource msg[1];
const unsigned char *msgend;
int type; int type;
msgend = p + msglen; BinarySource_BARE_INIT(msg, msgdata, msglen);
/* type = get_byte(msg);
* Get the message type. if (get_err(msg)) {
*/
if (msgend < p+1) {
pageant_failure_msg(bs, "message contained no type code", pageant_failure_msg(bs, "message contained no type code",
logctx, logfn); logctx, logfn);
return; return;
} }
type = *p++;
switch (type) { switch (type) {
case SSH1_AGENTC_REQUEST_RSA_IDENTITIES: case SSH1_AGENTC_REQUEST_RSA_IDENTITIES:
@ -253,59 +253,34 @@ void pageant_handle_msg(BinarySink *bs,
{ {
struct RSAKey reqkey, *key; struct RSAKey reqkey, *key;
Bignum challenge, response; Bignum challenge, response;
unsigned char response_source[48], response_md5[16]; ptrlen session_id;
unsigned response_type;
unsigned char response_md5[16];
struct MD5Context md5c; struct MD5Context md5c;
int i; 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; response = NULL;
memset(&reqkey, 0, sizeof(reqkey));
p += 4; get_rsa_ssh1_pub(msg, &reqkey, NULL, RSA_SSH1_EXPONENT_FIRST);
i = ssh1_read_bignum(p, msgend - p, &reqkey.exponent); challenge = get_mp_ssh1(msg);
if (i < 0) { session_id = get_data(msg, 16);
pageant_failure_msg( response_type = get_uint32(msg);
bs, "request truncated before key exponent",
logctx, logfn); if (get_err(msg)) {
pageant_failure_msg(bs, "unable to decode request",
logctx, logfn);
goto challenge1_cleanup; goto challenge1_cleanup;
} }
p += i; if (response_type != 1) {
i = ssh1_read_bignum(p, msgend - p, &reqkey.modulus);
if (i < 0) {
pageant_failure_msg(
bs, "request truncated before key modulus",
logctx, logfn);
goto challenge1_cleanup;
}
p += i;
i = ssh1_read_bignum(p, msgend - p, &challenge);
if (i < 0) {
pageant_failure_msg(
bs, "request truncated before challenge",
logctx, logfn);
goto challenge1_cleanup;
}
p += i;
if (msgend < p+16) {
pageant_failure_msg(
bs, "request truncated before session id",
logctx, logfn);
goto challenge1_cleanup;
}
memcpy(response_source + 32, p, 16);
p += 16;
if (msgend < p+4) {
pageant_failure_msg(
bs, "request truncated before response type",
logctx, logfn);
goto challenge1_cleanup;
}
if (GET_32BIT(p) != 1) {
pageant_failure_msg( pageant_failure_msg(
bs, "response type other than 1 not supported", bs, "response type other than 1 not supported",
logctx, logfn); logctx, logfn);
goto challenge1_cleanup; goto challenge1_cleanup;
} }
if (logfn) { if (logfn) {
char fingerprint[128]; char fingerprint[128];
reqkey.comment = NULL; reqkey.comment = NULL;
@ -317,13 +292,12 @@ void pageant_handle_msg(BinarySink *bs,
goto challenge1_cleanup; goto challenge1_cleanup;
} }
response = rsa_ssh1_decrypt(challenge, key); response = rsa_ssh1_decrypt(challenge, key);
for (i = 0; i < 32; i++)
response_source[i] = bignum_byte(response, 31 - i);
MD5Init(&md5c); MD5Init(&md5c);
put_data(&md5c, response_source, 48); for (i = 0; i < 32; i++)
put_byte(&md5c, bignum_byte(response, 31 - i));
put_data(&md5c, session_id.ptr, session_id.len);
MD5Final(response_md5, &md5c); MD5Final(response_md5, &md5c);
smemclr(response_source, 48); /* burn the evidence */
put_byte(bs, SSH1_AGENT_RSA_RESPONSE); put_byte(bs, SSH1_AGENT_RSA_RESPONSE);
put_data(bs, response_md5, 16); put_data(bs, response_md5, 16);
@ -331,10 +305,11 @@ void pageant_handle_msg(BinarySink *bs,
plog(logctx, logfn, "reply: SSH1_AGENT_RSA_RESPONSE"); plog(logctx, logfn, "reply: SSH1_AGENT_RSA_RESPONSE");
challenge1_cleanup: challenge1_cleanup:
if (response) freebn(response); if (response)
if (challenge) freebn(challenge); freebn(response);
if (reqkey.exponent) freebn(reqkey.exponent); freebn(challenge);
if (reqkey.modulus) freebn(reqkey.modulus); freebn(reqkey.exponent);
freebn(reqkey.modulus);
} }
break; break;
case SSH2_AGENTC_SIGN_REQUEST: case SSH2_AGENTC_SIGN_REQUEST:
@ -345,56 +320,20 @@ void pageant_handle_msg(BinarySink *bs,
*/ */
{ {
struct ssh2_userkey *key; struct ssh2_userkey *key;
const void *blobp; ptrlen keyblob, sigdata;
int bloblen;
const unsigned char *data;
strbuf *signature; strbuf *signature;
int datalen;
plog(logctx, logfn, "request: SSH2_AGENTC_SIGN_REQUEST"); plog(logctx, logfn, "request: SSH2_AGENTC_SIGN_REQUEST");
if (msgend < p+4) { keyblob = get_string(msg);
pageant_failure_msg( sigdata = get_string(msg);
bs, "request truncated before public key",
logctx, logfn);
return;
}
bloblen = toint(GET_32BIT(p));
if (bloblen < 0 || bloblen > msgend - (p+4)) {
pageant_failure_msg(
bs, "request truncated before public key",
logctx, logfn);
return;
}
p += 4;
blobp = p;
p += bloblen;
if (msgend < p+4) {
pageant_failure_msg(
bs, "request truncated before string to sign",
logctx, logfn);
return;
}
datalen = toint(GET_32BIT(p));
p += 4;
if (datalen < 0 || datalen > msgend - p) {
pageant_failure_msg(
bs, "request truncated before string to sign",
logctx, logfn);
return;
}
data = p;
if (logfn) { if (logfn) {
char *fingerprint = ssh2_fingerprint_blob(blobp, bloblen); char *fingerprint = ssh2_fingerprint_blob(
keyblob.ptr, keyblob.len);
plog(logctx, logfn, "requested key: %s", fingerprint); plog(logctx, logfn, "requested key: %s", fingerprint);
sfree(fingerprint); sfree(fingerprint);
} }
{ key = find234(ssh2keys, &keyblob, cmpkeys_ssh2_asymm);
strbuf *blob = strbuf_new();
put_data(blob, blobp, bloblen);
key = find234(ssh2keys, blob, cmpkeys_ssh2_asymm);
strbuf_free(blob);
}
if (!key) { if (!key) {
pageant_failure_msg(bs, "key not found", logctx, logfn); pageant_failure_msg(bs, "key not found", logctx, logfn);
return; return;
@ -403,7 +342,7 @@ void pageant_handle_msg(BinarySink *bs,
put_byte(bs, SSH2_AGENT_SIGN_RESPONSE); put_byte(bs, SSH2_AGENT_SIGN_RESPONSE);
signature = strbuf_new(); signature = strbuf_new();
key->alg->sign(key->data, data, datalen, key->alg->sign(key->data, sigdata.ptr, sigdata.len,
BinarySink_UPCAST(signature)); BinarySink_UPCAST(signature));
put_stringsb(bs, signature); put_stringsb(bs, signature);
@ -417,88 +356,35 @@ void pageant_handle_msg(BinarySink *bs,
*/ */
{ {
struct RSAKey *key; struct RSAKey *key;
char *comment;
int n, commentlen;
plog(logctx, logfn, "request: SSH1_AGENTC_ADD_RSA_IDENTITY"); plog(logctx, logfn, "request: SSH1_AGENTC_ADD_RSA_IDENTITY");
key = snew(struct RSAKey); key = snew(struct RSAKey);
memset(key, 0, sizeof(struct RSAKey)); memset(key, 0, sizeof(struct RSAKey));
n = rsa_ssh1_readpub(p, msgend - p, key, NULL, get_rsa_ssh1_pub(msg, key, NULL, RSA_SSH1_MODULUS_FIRST);
RSA_SSH1_MODULUS_FIRST); get_rsa_ssh1_priv(msg, key);
if (n < 0) {
pageant_failure_msg(
bs, "request truncated before public key",
logctx, logfn);
goto add1_cleanup;
}
p += n;
n = rsa_ssh1_readpriv(p, msgend - p, key);
if (n < 0) {
pageant_failure_msg(
bs, "request truncated before private key",
logctx, logfn);
goto add1_cleanup;
}
p += n;
/* SSH-1 names p and q the other way round, i.e. we have /* SSH-1 names p and q the other way round, i.e. we have
* the inverse of p mod q and not of q mod p. We swap the * the inverse of p mod q and not of q mod p. We swap the
* names, because our internal RSA wants iqmp. */ * names, because our internal RSA wants iqmp. */
key->iqmp = get_mp_ssh1(msg);
key->q = get_mp_ssh1(msg);
key->p = get_mp_ssh1(msg);
n = ssh1_read_bignum(p, msgend - p, &key->iqmp); /* p^-1 mod q */ key->comment = mkstr(get_string(msg));
if (n < 0) {
pageant_failure_msg(bs, "request truncated before iqmp", if (get_err(msg)) {
pageant_failure_msg(bs, "unable to decode request",
logctx, logfn); logctx, logfn);
goto add1_cleanup; goto add1_cleanup;
} }
p += n;
n = ssh1_read_bignum(p, msgend - p, &key->q); /* p */
if (n < 0) {
pageant_failure_msg(bs, "request truncated before p",
logctx, logfn);
goto add1_cleanup;
}
p += n;
n = ssh1_read_bignum(p, msgend - p, &key->p); /* q */
if (n < 0) {
pageant_failure_msg(
bs, "request truncated before q", logctx, logfn);
goto add1_cleanup;
}
p += n;
if (msgend < p+4) {
pageant_failure_msg(
bs, "request truncated before key comment",
logctx, logfn);
goto add1_cleanup;
}
commentlen = toint(GET_32BIT(p));
if (commentlen < 0 || commentlen > msgend - p) {
pageant_failure_msg(
bs, "request truncated before key comment",
logctx, logfn);
goto add1_cleanup;
}
if (!rsa_verify(key)) { if (!rsa_verify(key)) {
pageant_failure_msg(bs, "key is invalid", logctx, logfn); pageant_failure_msg(bs, "key is invalid", logctx, logfn);
goto add1_cleanup; goto add1_cleanup;
} }
comment = snewn(commentlen+1, char);
if (comment) {
memcpy(comment, p + 4, commentlen);
comment[commentlen] = '\0';
key->comment = comment;
}
if (logfn) { if (logfn) {
char fingerprint[128]; char fingerprint[128];
rsa_fingerprint(fingerprint, sizeof(fingerprint), key); rsa_fingerprint(fingerprint, sizeof(fingerprint), key);
@ -529,73 +415,42 @@ void pageant_handle_msg(BinarySink *bs,
*/ */
{ {
struct ssh2_userkey *key = NULL; struct ssh2_userkey *key = NULL;
char *comment; ptrlen alg;
const char *alg;
int alglen, commlen;
int bloblen;
plog(logctx, logfn, "request: SSH2_AGENTC_ADD_IDENTITY"); plog(logctx, logfn, "request: SSH2_AGENTC_ADD_IDENTITY");
if (msgend < p+4) { alg = get_string(msg);
pageant_failure_msg(
bs, "request truncated before key algorithm",
logctx, logfn);
goto add2_cleanup;
}
alglen = toint(GET_32BIT(p));
p += 4;
if (alglen < 0 || alglen > msgend - p) {
pageant_failure_msg(
bs, "request truncated before key algorithm",
logctx, logfn);
goto add2_cleanup;
}
alg = (const char *)p;
p += alglen;
key = snew(struct ssh2_userkey); key = snew(struct ssh2_userkey);
key->data = NULL; key->data = NULL;
key->comment = NULL; key->comment = NULL;
key->alg = find_pubkey_alg_len(make_ptrlen(alg, alglen)); key->alg = find_pubkey_alg_len(alg);
if (!key->alg) { if (!key->alg) {
pageant_failure_msg(bs, "algorithm unknown", logctx, logfn); pageant_failure_msg(bs, "algorithm unknown", logctx, logfn);
goto add2_cleanup; goto add2_cleanup;
} }
bloblen = msgend - p; {
key->data = key->alg->openssh_createkey(key->alg, &p, &bloblen); const unsigned char *p = get_ptr(msg);
int len = get_avail(msg);
key->data = key->alg->openssh_createkey(key->alg, &p, &len);
assert(len >= 0);
assert(len < get_avail(msg));
msg->pos += get_avail(msg) - len;
}
if (!key->data) { if (!key->data) {
pageant_failure_msg(bs, "key setup failed", logctx, logfn); pageant_failure_msg(bs, "key setup failed", logctx, logfn);
goto add2_cleanup; goto add2_cleanup;
} }
/* key->comment = mkstr(get_string(msg));
* p has been advanced by openssh_createkey, but
* certainly not _beyond_ the end of the buffer.
*/
assert(p <= msgend);
if (msgend < p+4) { if (get_err(msg)) {
pageant_failure_msg( pageant_failure_msg(bs, "unable to decode request",
bs, "request truncated before key comment", logctx, logfn);
logctx, logfn); goto add2_cleanup;
goto add2_cleanup; }
}
commlen = toint(GET_32BIT(p));
p += 4;
if (commlen < 0 || commlen > msgend - p) {
pageant_failure_msg(
bs, "request truncated before key comment",
logctx, logfn);
goto add2_cleanup;
}
comment = snewn(commlen + 1, char);
if (comment) {
memcpy(comment, p, commlen);
comment[commlen] = '\0';
}
key->comment = comment;
if (logfn) { if (logfn) {
char *fingerprint = ssh2_fingerprint(key->alg, key->data); char *fingerprint = ssh2_fingerprint(key->alg, key->data);
@ -634,17 +489,17 @@ void pageant_handle_msg(BinarySink *bs,
*/ */
{ {
struct RSAKey reqkey, *key; struct RSAKey reqkey, *key;
int n;
plog(logctx, logfn, "request: SSH1_AGENTC_REMOVE_RSA_IDENTITY"); plog(logctx, logfn, "request: SSH1_AGENTC_REMOVE_RSA_IDENTITY");
n = rsa_ssh1_readpub(p, msgend - p, &reqkey, NULL, get_rsa_ssh1_pub(msg, &reqkey, NULL, RSA_SSH1_EXPONENT_FIRST);
RSA_SSH1_EXPONENT_FIRST);
if (n < 0) { if (get_err(msg)) {
pageant_failure_msg( pageant_failure_msg(bs, "unable to decode request",
bs, "request truncated before public key", logctx, logfn);
logctx, logfn); freebn(reqkey.exponent);
return; freebn(reqkey.modulus);
return;
} }
if (logfn) { if (logfn) {
@ -680,41 +535,25 @@ void pageant_handle_msg(BinarySink *bs,
*/ */
{ {
struct ssh2_userkey *key; struct ssh2_userkey *key;
const void *blobp; ptrlen blob;
int bloblen;
plog(logctx, logfn, "request: SSH2_AGENTC_REMOVE_IDENTITY"); plog(logctx, logfn, "request: SSH2_AGENTC_REMOVE_IDENTITY");
if (msgend < p+4) { blob = get_string(msg);
pageant_failure_msg(
bs, "request truncated before public key",
logctx, logfn);
return;
}
bloblen = toint(GET_32BIT(p));
p += 4;
if (bloblen < 0 || bloblen > msgend - p) { if (get_err(msg)) {
pageant_failure_msg( pageant_failure_msg(bs, "unable to decode request",
bs, "request truncated before public key", logctx, logfn);
logctx, logfn);
return; return;
} }
blobp = p;
p += bloblen;
if (logfn) { if (logfn) {
char *fingerprint = ssh2_fingerprint_blob(blobp, bloblen); char *fingerprint = ssh2_fingerprint_blob(blob.ptr, blob.len);
plog(logctx, logfn, "unwanted key: %s", fingerprint); plog(logctx, logfn, "unwanted key: %s", fingerprint);
sfree(fingerprint); sfree(fingerprint);
} }
{ key = find234(ssh2keys, &blob, cmpkeys_ssh2_asymm);
strbuf *blob = strbuf_new();
put_data(blob, blobp, bloblen);
key = find234(ssh2keys, blob, cmpkeys_ssh2_asymm);
strbuf_free(blob);
}
if (!key) { if (!key) {
pageant_failure_msg(bs, "key not found", logctx, logfn); pageant_failure_msg(bs, "key not found", logctx, logfn);
return; return;