putty/pageant.c

1507 строки
42 KiB
C
Исходник Обычный вид История

/*
* pageant.c: cross-platform code to implement Pageant.
*/
#include <stddef.h>
#include <stdlib.h>
#include <assert.h>
#include "putty.h"
Complete rewrite of PuTTY's bignum library. The old 'Bignum' data type is gone completely, and so is sshbn.c. In its place is a new thing called 'mp_int', handled by an entirely new library module mpint.c, with API differences both large and small. The main aim of this change is that the new library should be free of timing- and cache-related side channels. I've written the code so that it _should_ - assuming I haven't made any mistakes - do all of its work without either control flow or memory addressing depending on the data words of the input numbers. (Though, being an _arbitrary_ precision library, it does have to at least depend on the sizes of the numbers - but there's a 'formal' size that can vary separately from the actual magnitude of the represented integer, so if you want to keep it secret that your number is actually small, it should work fine to have a very long mp_int and just happen to store 23 in it.) So I've done all my conditionalisation by means of computing both answers and doing bit-masking to swap the right one into place, and all loops over the words of an mp_int go up to the formal size rather than the actual size. I haven't actually tested the constant-time property in any rigorous way yet (I'm still considering the best way to do it). But this code is surely at the very least a big improvement on the old version, even if I later find a few more things to fix. I've also completely rewritten the low-level elliptic curve arithmetic from sshecc.c; the new ecc.c is closer to being an adjunct of mpint.c than it is to the SSH end of the code. The new elliptic curve code keeps all coordinates in Montgomery-multiplication transformed form to speed up all the multiplications mod the same prime, and only converts them back when you ask for the affine coordinates. Also, I adopted extended coordinates for the Edwards curve implementation. sshecc.c has also had a near-total rewrite in the course of switching it over to the new system. While I was there, I've separated ECDSA and EdDSA more completely - they now have separate vtables, instead of a single vtable in which nearly every function had a big if statement in it - and also made the externally exposed types for an ECDSA key and an ECDH context different. A minor new feature: since the new arithmetic code includes a modular square root function, we can now support the compressed point representation for the NIST curves. We seem to have been getting along fine without that so far, but it seemed a shame not to put it in, since it was suddenly easy. In sshrsa.c, one major change is that I've removed the RSA blinding step in rsa_privkey_op, in which we randomise the ciphertext before doing the decryption. The purpose of that was to avoid timing leaks giving away the plaintext - but the new arithmetic code should take that in its stride in the course of also being careful enough to avoid leaking the _private key_, which RSA blinding had no way to do anything about in any case. Apart from those specific points, most of the rest of the changes are more or less mechanical, just changing type names and translating code into the new API.
2018-12-31 16:53:41 +03:00
#include "mpint.h"
#include "ssh.h"
#include "sshcr.h"
#include "pageant.h"
/*
* We need this to link with the RSA code, because rsa_ssh1_encrypt()
* pads its data with random bytes. Since we only use rsa_ssh1_decrypt()
* and the signing functions, which are deterministic, this should
* never be called.
*
* If it _is_ called, there is a _serious_ problem, because it
* won't generate true random numbers. So we must scream, panic,
* and exit immediately if that should happen.
*/
void random_read(void *buf, size_t size)
{
modalfatalbox("Internal error: attempt to use random numbers in Pageant");
}
Convert a lot of 'int' variables to 'bool'. My normal habit these days, in new code, is to treat int and bool as _almost_ completely separate types. I'm still willing to use C's implicit test for zero on an integer (e.g. 'if (!blob.len)' is fine, no need to spell it out as blob.len != 0), but generally, if a variable is going to be conceptually a boolean, I like to declare it bool and assign to it using 'true' or 'false' rather than 0 or 1. PuTTY is an exception, because it predates the C99 bool, and I've stuck to its existing coding style even when adding new code to it. But it's been annoying me more and more, so now that I've decided C99 bool is an acceptable thing to require from our toolchain in the first place, here's a quite thorough trawl through the source doing 'boolification'. Many variables and function parameters are now typed as bool rather than int; many assignments of 0 or 1 to those variables are now spelled 'true' or 'false'. I managed this thorough conversion with the help of a custom clang plugin that I wrote to trawl the AST and apply heuristics to point out where things might want changing. So I've even managed to do a decent job on parts of the code I haven't looked at in years! To make the plugin's work easier, I pushed platform front ends generally in the direction of using standard 'bool' in preference to platform-specific boolean types like Windows BOOL or GTK's gboolean; I've left the platform booleans in places they _have_ to be for the platform APIs to work right, but variables only used by my own code have been converted wherever I found them. In a few places there are int values that look very like booleans in _most_ of the places they're used, but have a rarely-used third value, or a distinction between different nonzero values that most users don't care about. In these cases, I've _removed_ uses of 'true' and 'false' for the return values, to emphasise that there's something more subtle going on than a simple boolean answer: - the 'multisel' field in dialog.h's list box structure, for which the GTK front end in particular recognises a difference between 1 and 2 but nearly everything else treats as boolean - the 'urgent' parameter to plug_receive, where 1 vs 2 tells you something about the specific location of the urgent pointer, but most clients only care about 0 vs 'something nonzero' - the return value of wc_match, where -1 indicates a syntax error in the wildcard. - the return values from SSH-1 RSA-key loading functions, which use -1 for 'wrong passphrase' and 0 for all other failures (so any caller which already knows it's not loading an _encrypted private_ key can treat them as boolean) - term->esc_query, and the 'query' parameter in toggle_mode in terminal.c, which _usually_ hold 0 for ESC[123h or 1 for ESC[?123h, but can also hold -1 for some other intervening character that we don't support. In a few places there's an integer that I haven't turned into a bool even though it really _can_ only take values 0 or 1 (and, as above, tried to make the call sites consistent in not calling those values true and false), on the grounds that I thought it would make it more confusing to imply that the 0 value was in some sense 'negative' or bad and the 1 positive or good: - the return value of plug_accepting uses the POSIXish convention of 0=success and nonzero=error; I think if I made it bool then I'd also want to reverse its sense, and that's a job for a separate piece of work. - the 'screen' parameter to lineptr() in terminal.c, where 0 and 1 represent the default and alternate screens. There's no obvious reason why one of those should be considered 'true' or 'positive' or 'success' - they're just indices - so I've left it as int. ssh_scp_recv had particularly confusing semantics for its previous int return value: its call sites used '<= 0' to check for error, but it never actually returned a negative number, just 0 or 1. Now the function and its call sites agree that it's a bool. In a couple of places I've renamed variables called 'ret', because I don't like that name any more - it's unclear whether it means the return value (in preparation) for the _containing_ function or the return value received from a subroutine call, and occasionally I've accidentally used the same variable for both and introduced a bug. So where one of those got in my way, I've renamed it to 'toret' or 'retd' (the latter short for 'returned') in line with my usual modern practice, but I haven't done a thorough job of finding all of them. Finally, one amusing side effect of doing this is that I've had to separate quite a few chained assignments. It used to be perfectly fine to write 'a = b = c = TRUE' when a,b,c were int and TRUE was just a the 'true' defined by stdbool.h, that idiom provokes a warning from gcc: 'suggest parentheses around assignment used as truth value'!
2018-11-02 22:23:19 +03:00
static bool pageant_local = false;
/*
* rsakeys stores SSH-1 RSA keys. ssh2keys stores all SSH-2 keys.
*/
static tree234 *rsakeys, *ssh2keys;
/*
* Key comparison function for the 2-3-4 tree of RSA keys.
*/
static int cmpkeys_rsa(void *av, void *bv)
{
RSAKey *a = (RSAKey *) av;
RSAKey *b = (RSAKey *) bv;
Complete rewrite of PuTTY's bignum library. The old 'Bignum' data type is gone completely, and so is sshbn.c. In its place is a new thing called 'mp_int', handled by an entirely new library module mpint.c, with API differences both large and small. The main aim of this change is that the new library should be free of timing- and cache-related side channels. I've written the code so that it _should_ - assuming I haven't made any mistakes - do all of its work without either control flow or memory addressing depending on the data words of the input numbers. (Though, being an _arbitrary_ precision library, it does have to at least depend on the sizes of the numbers - but there's a 'formal' size that can vary separately from the actual magnitude of the represented integer, so if you want to keep it secret that your number is actually small, it should work fine to have a very long mp_int and just happen to store 23 in it.) So I've done all my conditionalisation by means of computing both answers and doing bit-masking to swap the right one into place, and all loops over the words of an mp_int go up to the formal size rather than the actual size. I haven't actually tested the constant-time property in any rigorous way yet (I'm still considering the best way to do it). But this code is surely at the very least a big improvement on the old version, even if I later find a few more things to fix. I've also completely rewritten the low-level elliptic curve arithmetic from sshecc.c; the new ecc.c is closer to being an adjunct of mpint.c than it is to the SSH end of the code. The new elliptic curve code keeps all coordinates in Montgomery-multiplication transformed form to speed up all the multiplications mod the same prime, and only converts them back when you ask for the affine coordinates. Also, I adopted extended coordinates for the Edwards curve implementation. sshecc.c has also had a near-total rewrite in the course of switching it over to the new system. While I was there, I've separated ECDSA and EdDSA more completely - they now have separate vtables, instead of a single vtable in which nearly every function had a big if statement in it - and also made the externally exposed types for an ECDSA key and an ECDH context different. A minor new feature: since the new arithmetic code includes a modular square root function, we can now support the compressed point representation for the NIST curves. We seem to have been getting along fine without that so far, but it seemed a shame not to put it in, since it was suddenly easy. In sshrsa.c, one major change is that I've removed the RSA blinding step in rsa_privkey_op, in which we randomise the ciphertext before doing the decryption. The purpose of that was to avoid timing leaks giving away the plaintext - but the new arithmetic code should take that in its stride in the course of also being careful enough to avoid leaking the _private key_, which RSA blinding had no way to do anything about in any case. Apart from those specific points, most of the rest of the changes are more or less mechanical, just changing type names and translating code into the new API.
2018-12-31 16:53:41 +03:00
return ((int)mp_cmp_hs(a->modulus, b->modulus) -
(int)mp_cmp_hs(b->modulus, a->modulus));
}
/*
* Key comparison function for looking up a blob in the 2-3-4 tree
* of SSH-2 keys.
*/
static int cmpkeys_ssh2_asymm(void *av, void *bv)
{
ptrlen *ablob = (ptrlen *) av;
ssh2_userkey *b = (ssh2_userkey *) bv;
strbuf *bblob;
int i, c;
/*
* Compare purely by public blob.
*/
bblob = strbuf_new();
ssh_key_public_blob(b->key, BinarySink_UPCAST(bblob));
c = 0;
for (i = 0; i < ablob->len && i < bblob->len; i++) {
unsigned char abyte = ((unsigned char *)ablob->ptr)[i];
if (abyte < bblob->u[i]) {
c = -1;
break;
} else if (abyte > bblob->u[i]) {
c = +1;
break;
}
}
if (c == 0 && i < ablob->len)
c = +1; /* a is longer */
if (c == 0 && i < bblob->len)
c = -1; /* a is longer */
strbuf_free(bblob);
return c;
}
/*
* Main key comparison function for the 2-3-4 tree of SSH-2 keys.
*/
static int cmpkeys_ssh2(void *av, void *bv)
{
ssh2_userkey *a = (ssh2_userkey *) av;
strbuf *ablob;
ptrlen apl;
int toret;
ablob = strbuf_new();
ssh_key_public_blob(a->key, BinarySink_UPCAST(ablob));
apl.ptr = ablob->u;
apl.len = ablob->len;
toret = cmpkeys_ssh2_asymm(&apl, bv);
strbuf_free(ablob);
return toret;
}
void pageant_make_keylist1(BinarySink *bs)
{
int i;
RSAKey *key;
put_uint32(bs, count234(rsakeys));
for (i = 0; NULL != (key = index234(rsakeys, i)); i++) {
rsa_ssh1_public_blob(bs, key, RSA_SSH1_EXPONENT_FIRST);
put_stringz(bs, key->comment);
}
}
void pageant_make_keylist2(BinarySink *bs)
{
int i;
ssh2_userkey *key;
put_uint32(bs, count234(ssh2keys));
for (i = 0; NULL != (key = index234(ssh2keys, i)); i++) {
strbuf *blob = strbuf_new();
ssh_key_public_blob(key->key, BinarySink_UPCAST(blob));
put_stringsb(bs, blob);
put_stringz(bs, key->comment);
}
}
static void plog(void *logctx, pageant_logfn_t logfn, const char *fmt, ...)
#ifdef __GNUC__
__attribute__ ((format (PUTTY_PRINTF_ARCHETYPE, 3, 4)))
#endif
;
static void plog(void *logctx, pageant_logfn_t logfn, const char *fmt, ...)
{
/*
* This is the wrapper that takes a variadic argument list and
* turns it into the va_list that the log function really expects.
* It's safe to call this with logfn==NULL, because we
* double-check that below; but if you're going to do lots of work
* before getting here (such as looping, or hashing things) then
* you should probably check logfn manually before doing that.
*/
if (logfn) {
va_list ap;
va_start(ap, fmt);
logfn(logctx, fmt, ap);
va_end(ap);
}
}
void pageant_handle_msg(BinarySink *bs,
const void *msgdata, int msglen,
void *logctx, pageant_logfn_t logfn)
{
BinarySource msg[1];
int type;
BinarySource_BARE_INIT(msg, msgdata, msglen);
type = get_byte(msg);
if (get_err(msg)) {
pageant_failure_msg(bs, "message contained no type code",
logctx, logfn);
return;
}
switch (type) {
case SSH1_AGENTC_REQUEST_RSA_IDENTITIES:
/*
* Reply with SSH1_AGENT_RSA_IDENTITIES_ANSWER.
*/
{
plog(logctx, logfn, "request: SSH1_AGENTC_REQUEST_RSA_IDENTITIES");
put_byte(bs, SSH1_AGENT_RSA_IDENTITIES_ANSWER);
pageant_make_keylist1(bs);
plog(logctx, logfn, "reply: SSH1_AGENT_RSA_IDENTITIES_ANSWER");
if (logfn) { /* skip this loop if not logging */
int i;
RSAKey *rkey;
for (i = 0; NULL != (rkey = pageant_nth_ssh1_key(i)); i++) {
char *fingerprint = rsa_ssh1_fingerprint(rkey);
plog(logctx, logfn, "returned key: %s", fingerprint);
sfree(fingerprint);
}
}
}
break;
case SSH2_AGENTC_REQUEST_IDENTITIES:
/*
* Reply with SSH2_AGENT_IDENTITIES_ANSWER.
*/
{
plog(logctx, logfn, "request: SSH2_AGENTC_REQUEST_IDENTITIES");
put_byte(bs, SSH2_AGENT_IDENTITIES_ANSWER);
pageant_make_keylist2(bs);
plog(logctx, logfn, "reply: SSH2_AGENT_IDENTITIES_ANSWER");
if (logfn) { /* skip this loop if not logging */
int i;
ssh2_userkey *skey;
for (i = 0; NULL != (skey = pageant_nth_ssh2_key(i)); i++) {
char *fingerprint = ssh2_fingerprint(skey->key);
plog(logctx, logfn, "returned key: %s %s",
fingerprint, skey->comment);
sfree(fingerprint);
}
}
}
break;
case SSH1_AGENTC_RSA_CHALLENGE:
/*
* Reply with either SSH1_AGENT_RSA_RESPONSE or
* SSH_AGENT_FAILURE, depending on whether we have that key
* or not.
*/
{
RSAKey reqkey, *key;
Complete rewrite of PuTTY's bignum library. The old 'Bignum' data type is gone completely, and so is sshbn.c. In its place is a new thing called 'mp_int', handled by an entirely new library module mpint.c, with API differences both large and small. The main aim of this change is that the new library should be free of timing- and cache-related side channels. I've written the code so that it _should_ - assuming I haven't made any mistakes - do all of its work without either control flow or memory addressing depending on the data words of the input numbers. (Though, being an _arbitrary_ precision library, it does have to at least depend on the sizes of the numbers - but there's a 'formal' size that can vary separately from the actual magnitude of the represented integer, so if you want to keep it secret that your number is actually small, it should work fine to have a very long mp_int and just happen to store 23 in it.) So I've done all my conditionalisation by means of computing both answers and doing bit-masking to swap the right one into place, and all loops over the words of an mp_int go up to the formal size rather than the actual size. I haven't actually tested the constant-time property in any rigorous way yet (I'm still considering the best way to do it). But this code is surely at the very least a big improvement on the old version, even if I later find a few more things to fix. I've also completely rewritten the low-level elliptic curve arithmetic from sshecc.c; the new ecc.c is closer to being an adjunct of mpint.c than it is to the SSH end of the code. The new elliptic curve code keeps all coordinates in Montgomery-multiplication transformed form to speed up all the multiplications mod the same prime, and only converts them back when you ask for the affine coordinates. Also, I adopted extended coordinates for the Edwards curve implementation. sshecc.c has also had a near-total rewrite in the course of switching it over to the new system. While I was there, I've separated ECDSA and EdDSA more completely - they now have separate vtables, instead of a single vtable in which nearly every function had a big if statement in it - and also made the externally exposed types for an ECDSA key and an ECDH context different. A minor new feature: since the new arithmetic code includes a modular square root function, we can now support the compressed point representation for the NIST curves. We seem to have been getting along fine without that so far, but it seemed a shame not to put it in, since it was suddenly easy. In sshrsa.c, one major change is that I've removed the RSA blinding step in rsa_privkey_op, in which we randomise the ciphertext before doing the decryption. The purpose of that was to avoid timing leaks giving away the plaintext - but the new arithmetic code should take that in its stride in the course of also being careful enough to avoid leaking the _private key_, which RSA blinding had no way to do anything about in any case. Apart from those specific points, most of the rest of the changes are more or less mechanical, just changing type names and translating code into the new API.
2018-12-31 16:53:41 +03:00
mp_int *challenge, *response;
ptrlen session_id;
unsigned response_type;
unsigned char response_md5[16];
int i;
plog(logctx, logfn, "request: SSH1_AGENTC_RSA_CHALLENGE");
response = NULL;
memset(&reqkey, 0, sizeof(reqkey));
get_rsa_ssh1_pub(msg, &reqkey, RSA_SSH1_EXPONENT_FIRST);
challenge = get_mp_ssh1(msg);
session_id = get_data(msg, 16);
response_type = get_uint32(msg);
if (get_err(msg)) {
pageant_failure_msg(bs, "unable to decode request",
logctx, logfn);
goto challenge1_cleanup;
}
if (response_type != 1) {
pageant_failure_msg(
bs, "response type other than 1 not supported",
logctx, logfn);
goto challenge1_cleanup;
}
if (logfn) {
char *fingerprint;
reqkey.comment = NULL;
fingerprint = rsa_ssh1_fingerprint(&reqkey);
plog(logctx, logfn, "requested key: %s", fingerprint);
sfree(fingerprint);
}
if ((key = find234(rsakeys, &reqkey, NULL)) == NULL) {
pageant_failure_msg(bs, "key not found", logctx, logfn);
goto challenge1_cleanup;
}
response = rsa_ssh1_decrypt(challenge, key);
{
ssh_hash *h = ssh_hash_new(&ssh_md5);
for (i = 0; i < 32; i++)
put_byte(h, mp_get_byte(response, 31 - i));
put_datapl(h, session_id);
ssh_hash_final(h, response_md5);
}
put_byte(bs, SSH1_AGENT_RSA_RESPONSE);
put_data(bs, response_md5, 16);
plog(logctx, logfn, "reply: SSH1_AGENT_RSA_RESPONSE");
challenge1_cleanup:
if (response)
Complete rewrite of PuTTY's bignum library. The old 'Bignum' data type is gone completely, and so is sshbn.c. In its place is a new thing called 'mp_int', handled by an entirely new library module mpint.c, with API differences both large and small. The main aim of this change is that the new library should be free of timing- and cache-related side channels. I've written the code so that it _should_ - assuming I haven't made any mistakes - do all of its work without either control flow or memory addressing depending on the data words of the input numbers. (Though, being an _arbitrary_ precision library, it does have to at least depend on the sizes of the numbers - but there's a 'formal' size that can vary separately from the actual magnitude of the represented integer, so if you want to keep it secret that your number is actually small, it should work fine to have a very long mp_int and just happen to store 23 in it.) So I've done all my conditionalisation by means of computing both answers and doing bit-masking to swap the right one into place, and all loops over the words of an mp_int go up to the formal size rather than the actual size. I haven't actually tested the constant-time property in any rigorous way yet (I'm still considering the best way to do it). But this code is surely at the very least a big improvement on the old version, even if I later find a few more things to fix. I've also completely rewritten the low-level elliptic curve arithmetic from sshecc.c; the new ecc.c is closer to being an adjunct of mpint.c than it is to the SSH end of the code. The new elliptic curve code keeps all coordinates in Montgomery-multiplication transformed form to speed up all the multiplications mod the same prime, and only converts them back when you ask for the affine coordinates. Also, I adopted extended coordinates for the Edwards curve implementation. sshecc.c has also had a near-total rewrite in the course of switching it over to the new system. While I was there, I've separated ECDSA and EdDSA more completely - they now have separate vtables, instead of a single vtable in which nearly every function had a big if statement in it - and also made the externally exposed types for an ECDSA key and an ECDH context different. A minor new feature: since the new arithmetic code includes a modular square root function, we can now support the compressed point representation for the NIST curves. We seem to have been getting along fine without that so far, but it seemed a shame not to put it in, since it was suddenly easy. In sshrsa.c, one major change is that I've removed the RSA blinding step in rsa_privkey_op, in which we randomise the ciphertext before doing the decryption. The purpose of that was to avoid timing leaks giving away the plaintext - but the new arithmetic code should take that in its stride in the course of also being careful enough to avoid leaking the _private key_, which RSA blinding had no way to do anything about in any case. Apart from those specific points, most of the rest of the changes are more or less mechanical, just changing type names and translating code into the new API.
2018-12-31 16:53:41 +03:00
mp_free(response);
mp_free(challenge);
freersakey(&reqkey);
}
break;
case SSH2_AGENTC_SIGN_REQUEST:
/*
* Reply with either SSH2_AGENT_SIGN_RESPONSE or
* SSH_AGENT_FAILURE, depending on whether we have that key
* or not.
*/
{
ssh2_userkey *key;
ptrlen keyblob, sigdata;
strbuf *signature;
uint32_t flags, supported_flags;
plog(logctx, logfn, "request: SSH2_AGENTC_SIGN_REQUEST");
keyblob = get_string(msg);
sigdata = get_string(msg);
if (get_err(msg)) {
pageant_failure_msg(bs, "unable to decode request",
logctx, logfn);
return;
}
/*
* Later versions of the agent protocol added a flags word
* on the end of the sign request. That hasn't always been
* there, so we don't complain if we don't find it.
*
* get_uint32 will default to returning zero if no data is
* available.
*/
bool have_flags = false;
flags = get_uint32(msg);
if (!get_err(msg))
have_flags = true;
if (logfn) {
char *fingerprint = ssh2_fingerprint_blob(keyblob);
plog(logctx, logfn, "requested key: %s", fingerprint);
sfree(fingerprint);
}
key = find234(ssh2keys, &keyblob, cmpkeys_ssh2_asymm);
if (!key) {
pageant_failure_msg(bs, "key not found", logctx, logfn);
return;
}
if (have_flags)
plog(logctx, logfn, "signature flags = 0x%08"PRIx32, flags);
else
plog(logctx, logfn, "no signature flags");
supported_flags = ssh_key_alg(key->key)->supported_flags;
if (flags & ~supported_flags) {
/*
* We MUST reject any message containing flags we
* don't understand.
*/
char *msg = dupprintf(
"unsupported flag bits 0x%08"PRIx32,
flags & ~supported_flags);
pageant_failure_msg(bs, msg, logctx, logfn);
sfree(msg);
return;
}
char *invalid = ssh_key_invalid(key->key, flags);
if (invalid) {
char *msg = dupprintf("key invalid: %s", invalid);
pageant_failure_msg(bs, msg, logctx, logfn);
sfree(msg);
sfree(invalid);
return;
}
signature = strbuf_new();
ssh_key_sign(key->key, sigdata, flags,
BinarySink_UPCAST(signature));
winpgnt.c: handle arbitrarily large file mappings. I heard recently that at least one third-party client of Pageant exists, and that it's used to generate signatures to use with TLS client certificates. Apparently the signature scheme is compatible, but TLS tends to need signatures over more data than will fit in AGENT_MAX_MSGLEN. Before the BinarySink refactor in commit b6cbad89f, this was OK because the Windows Pageant IPC didn't check the size of the _input_ message against AGENT_MAX_MSGLEN, only the output one. But then we started checking both, so that third-party TLS client started failing. Now we use VirtualQuery to find out the actual size of the file mapping we've been passed, and our only requirement is that the input and output messages should both fit in _that_. So TLS should work again, and also, other clients should be able to retrieve longer lists of public keys if they pass a larger file mapping. One side effect of this change is that Pageant's reply message is now written directly into the shared-memory region. Previously, it was written into a separate buffer and then memcpy()ed over after pageant_handle_msg returned, but now the buffer is variable-size, it seems to me to make more sense to avoid that extra not-entirely controlled malloc. So I've done one very small reordering of statements in the cross-platform pageant_handle_msg(), which fixes the only case I could find where that function started writing its output before it had finished using the contents of the input buffer.
2018-07-08 18:46:32 +03:00
put_byte(bs, SSH2_AGENT_SIGN_RESPONSE);
put_stringsb(bs, signature);
plog(logctx, logfn, "reply: SSH2_AGENT_SIGN_RESPONSE");
}
break;
case SSH1_AGENTC_ADD_RSA_IDENTITY:
/*
* Add to the list and return SSH_AGENT_SUCCESS, or
* SSH_AGENT_FAILURE if the key was malformed.
*/
{
RSAKey *key;
plog(logctx, logfn, "request: SSH1_AGENTC_ADD_RSA_IDENTITY");
key = snew(RSAKey);
memset(key, 0, sizeof(RSAKey));
get_rsa_ssh1_pub(msg, key, RSA_SSH1_MODULUS_FIRST);
get_rsa_ssh1_priv(msg, key);
/* 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
* 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);
key->comment = mkstr(get_string(msg));
if (get_err(msg)) {
pageant_failure_msg(bs, "unable to decode request",
logctx, logfn);
goto add1_cleanup;
}
if (!rsa_verify(key)) {
pageant_failure_msg(bs, "key is invalid", logctx, logfn);
goto add1_cleanup;
}
if (logfn) {
char *fingerprint = rsa_ssh1_fingerprint(key);
plog(logctx, logfn, "submitted key: %s", fingerprint);
sfree(fingerprint);
}
if (add234(rsakeys, key) == key) {
keylist_update();
put_byte(bs, SSH_AGENT_SUCCESS);
plog(logctx, logfn, "reply: SSH_AGENT_SUCCESS");
key = NULL; /* don't free it in cleanup */
} else {
pageant_failure_msg(bs, "key already present",
logctx, logfn);
}
add1_cleanup:
if (key) {
freersakey(key);
sfree(key);
}
}
break;
case SSH2_AGENTC_ADD_IDENTITY:
/*
* Add to the list and return SSH_AGENT_SUCCESS, or
* SSH_AGENT_FAILURE if the key was malformed.
*/
{
ssh2_userkey *key = NULL;
ptrlen algpl;
const ssh_keyalg *alg;
plog(logctx, logfn, "request: SSH2_AGENTC_ADD_IDENTITY");
algpl = get_string(msg);
key = snew(ssh2_userkey);
key->key = NULL;
key->comment = NULL;
alg = find_pubkey_alg_len(algpl);
if (!alg) {
pageant_failure_msg(bs, "algorithm unknown", logctx, logfn);
goto add2_cleanup;
}
key->key = ssh_key_new_priv_openssh(alg, msg);
if (!key->key) {
pageant_failure_msg(bs, "key setup failed", logctx, logfn);
goto add2_cleanup;
}
key->comment = mkstr(get_string(msg));
if (get_err(msg)) {
pageant_failure_msg(bs, "unable to decode request",
logctx, logfn);
goto add2_cleanup;
}
if (logfn) {
char *fingerprint = ssh2_fingerprint(key->key);
plog(logctx, logfn, "submitted key: %s %s",
fingerprint, key->comment);
sfree(fingerprint);
}
if (add234(ssh2keys, key) == key) {
keylist_update();
put_byte(bs, SSH_AGENT_SUCCESS);
plog(logctx, logfn, "reply: SSH_AGENT_SUCCESS");
key = NULL; /* don't clean it up */
} else {
pageant_failure_msg(bs, "key already present",
logctx, logfn);
}
add2_cleanup:
if (key) {
if (key->key)
ssh_key_free(key->key);
if (key->comment)
sfree(key->comment);
sfree(key);
}
}
break;
case SSH1_AGENTC_REMOVE_RSA_IDENTITY:
/*
* Remove from the list and return SSH_AGENT_SUCCESS, or
* perhaps SSH_AGENT_FAILURE if it wasn't in the list to
* start with.
*/
{
RSAKey reqkey, *key;
plog(logctx, logfn, "request: SSH1_AGENTC_REMOVE_RSA_IDENTITY");
memset(&reqkey, 0, sizeof(reqkey));
get_rsa_ssh1_pub(msg, &reqkey, RSA_SSH1_EXPONENT_FIRST);
if (get_err(msg)) {
pageant_failure_msg(bs, "unable to decode request",
logctx, logfn);
freersakey(&reqkey);
return;
}
if (logfn) {
char *fingerprint;
reqkey.comment = NULL;
fingerprint = rsa_ssh1_fingerprint(&reqkey);
plog(logctx, logfn, "unwanted key: %s", fingerprint);
2018-06-03 10:37:17 +03:00
sfree(fingerprint);
}
key = find234(rsakeys, &reqkey, NULL);
freersakey(&reqkey);
if (key) {
plog(logctx, logfn, "found with comment: %s", key->comment);
del234(rsakeys, key);
keylist_update();
freersakey(key);
sfree(key);
put_byte(bs, SSH_AGENT_SUCCESS);
plog(logctx, logfn, "reply: SSH_AGENT_SUCCESS");
} else {
pageant_failure_msg(bs, "key not found", logctx, logfn);
}
}
break;
case SSH2_AGENTC_REMOVE_IDENTITY:
/*
* Remove from the list and return SSH_AGENT_SUCCESS, or
* perhaps SSH_AGENT_FAILURE if it wasn't in the list to
* start with.
*/
{
ssh2_userkey *key;
ptrlen blob;
plog(logctx, logfn, "request: SSH2_AGENTC_REMOVE_IDENTITY");
blob = get_string(msg);
if (get_err(msg)) {
pageant_failure_msg(bs, "unable to decode request",
logctx, logfn);
return;
}
if (logfn) {
char *fingerprint = ssh2_fingerprint_blob(blob);
plog(logctx, logfn, "unwanted key: %s", fingerprint);
sfree(fingerprint);
}
key = find234(ssh2keys, &blob, cmpkeys_ssh2_asymm);
if (!key) {
pageant_failure_msg(bs, "key not found", logctx, logfn);
return;
}
plog(logctx, logfn, "found with comment: %s", key->comment);
del234(ssh2keys, key);
keylist_update();
ssh_key_free(key->key);
sfree(key->comment);
sfree(key);
put_byte(bs, SSH_AGENT_SUCCESS);
plog(logctx, logfn, "reply: SSH_AGENT_SUCCESS");
}
break;
case SSH1_AGENTC_REMOVE_ALL_RSA_IDENTITIES:
/*
* Remove all SSH-1 keys. Always returns success.
*/
{
RSAKey *rkey;
plog(logctx, logfn, "request:"
" SSH1_AGENTC_REMOVE_ALL_RSA_IDENTITIES");
while ((rkey = index234(rsakeys, 0)) != NULL) {
del234(rsakeys, rkey);
freersakey(rkey);
sfree(rkey);
}
keylist_update();
put_byte(bs, SSH_AGENT_SUCCESS);
plog(logctx, logfn, "reply: SSH_AGENT_SUCCESS");
}
break;
case SSH2_AGENTC_REMOVE_ALL_IDENTITIES:
/*
* Remove all SSH-2 keys. Always returns success.
*/
{
ssh2_userkey *skey;
plog(logctx, logfn, "request: SSH2_AGENTC_REMOVE_ALL_IDENTITIES");
while ((skey = index234(ssh2keys, 0)) != NULL) {
del234(ssh2keys, skey);
ssh_key_free(skey->key);
sfree(skey->comment);
sfree(skey);
}
keylist_update();
put_byte(bs, SSH_AGENT_SUCCESS);
plog(logctx, logfn, "reply: SSH_AGENT_SUCCESS");
}
break;
default:
plog(logctx, logfn, "request: unknown message type %d", type);
pageant_failure_msg(bs, "unrecognised message", logctx, logfn);
break;
}
}
void pageant_failure_msg(BinarySink *bs,
const char *log_reason,
void *logctx, pageant_logfn_t logfn)
{
put_byte(bs, SSH_AGENT_FAILURE);
plog(logctx, logfn, "reply: SSH_AGENT_FAILURE (%s)", log_reason);
}
void pageant_init(void)
{
pageant_local = true;
rsakeys = newtree234(cmpkeys_rsa);
ssh2keys = newtree234(cmpkeys_ssh2);
}
RSAKey *pageant_nth_ssh1_key(int i)
{
return index234(rsakeys, i);
}
ssh2_userkey *pageant_nth_ssh2_key(int i)
{
return index234(ssh2keys, i);
}
int pageant_count_ssh1_keys(void)
{
return count234(rsakeys);
}
int pageant_count_ssh2_keys(void)
{
return count234(ssh2keys);
}
bool pageant_add_ssh1_key(RSAKey *rkey)
{
return add234(rsakeys, rkey) == rkey;
}
bool pageant_add_ssh2_key(ssh2_userkey *skey)
{
return add234(ssh2keys, skey) == skey;
}
bool pageant_delete_ssh1_key(RSAKey *rkey)
{
RSAKey *deleted = del234(rsakeys, rkey);
if (!deleted)
return false;
assert(deleted == rkey);
return true;
}
bool pageant_delete_ssh2_key(ssh2_userkey *skey)
{
ssh2_userkey *deleted = del234(ssh2keys, skey);
if (!deleted)
return false;
assert(deleted == skey);
return true;
}
/* ----------------------------------------------------------------------
* The agent plug.
*/
/*
* An extra coroutine macro, specific to this code which is consuming
* 'const char *data'.
*/
#define crGetChar(c) do \
{ \
while (len == 0) { \
*crLine =__LINE__; return; case __LINE__:; \
} \
len--; \
(c) = (unsigned char)*data++; \
} while (0)
struct pageant_conn_state {
Socket *connsock;
void *logctx;
pageant_logfn_t logfn;
unsigned char lenbuf[4], pktbuf[AGENT_MAX_MSGLEN];
unsigned len, got;
Convert a lot of 'int' variables to 'bool'. My normal habit these days, in new code, is to treat int and bool as _almost_ completely separate types. I'm still willing to use C's implicit test for zero on an integer (e.g. 'if (!blob.len)' is fine, no need to spell it out as blob.len != 0), but generally, if a variable is going to be conceptually a boolean, I like to declare it bool and assign to it using 'true' or 'false' rather than 0 or 1. PuTTY is an exception, because it predates the C99 bool, and I've stuck to its existing coding style even when adding new code to it. But it's been annoying me more and more, so now that I've decided C99 bool is an acceptable thing to require from our toolchain in the first place, here's a quite thorough trawl through the source doing 'boolification'. Many variables and function parameters are now typed as bool rather than int; many assignments of 0 or 1 to those variables are now spelled 'true' or 'false'. I managed this thorough conversion with the help of a custom clang plugin that I wrote to trawl the AST and apply heuristics to point out where things might want changing. So I've even managed to do a decent job on parts of the code I haven't looked at in years! To make the plugin's work easier, I pushed platform front ends generally in the direction of using standard 'bool' in preference to platform-specific boolean types like Windows BOOL or GTK's gboolean; I've left the platform booleans in places they _have_ to be for the platform APIs to work right, but variables only used by my own code have been converted wherever I found them. In a few places there are int values that look very like booleans in _most_ of the places they're used, but have a rarely-used third value, or a distinction between different nonzero values that most users don't care about. In these cases, I've _removed_ uses of 'true' and 'false' for the return values, to emphasise that there's something more subtle going on than a simple boolean answer: - the 'multisel' field in dialog.h's list box structure, for which the GTK front end in particular recognises a difference between 1 and 2 but nearly everything else treats as boolean - the 'urgent' parameter to plug_receive, where 1 vs 2 tells you something about the specific location of the urgent pointer, but most clients only care about 0 vs 'something nonzero' - the return value of wc_match, where -1 indicates a syntax error in the wildcard. - the return values from SSH-1 RSA-key loading functions, which use -1 for 'wrong passphrase' and 0 for all other failures (so any caller which already knows it's not loading an _encrypted private_ key can treat them as boolean) - term->esc_query, and the 'query' parameter in toggle_mode in terminal.c, which _usually_ hold 0 for ESC[123h or 1 for ESC[?123h, but can also hold -1 for some other intervening character that we don't support. In a few places there's an integer that I haven't turned into a bool even though it really _can_ only take values 0 or 1 (and, as above, tried to make the call sites consistent in not calling those values true and false), on the grounds that I thought it would make it more confusing to imply that the 0 value was in some sense 'negative' or bad and the 1 positive or good: - the return value of plug_accepting uses the POSIXish convention of 0=success and nonzero=error; I think if I made it bool then I'd also want to reverse its sense, and that's a job for a separate piece of work. - the 'screen' parameter to lineptr() in terminal.c, where 0 and 1 represent the default and alternate screens. There's no obvious reason why one of those should be considered 'true' or 'positive' or 'success' - they're just indices - so I've left it as int. ssh_scp_recv had particularly confusing semantics for its previous int return value: its call sites used '<= 0' to check for error, but it never actually returned a negative number, just 0 or 1. Now the function and its call sites agree that it's a bool. In a couple of places I've renamed variables called 'ret', because I don't like that name any more - it's unclear whether it means the return value (in preparation) for the _containing_ function or the return value received from a subroutine call, and occasionally I've accidentally used the same variable for both and introduced a bug. So where one of those got in my way, I've renamed it to 'toret' or 'retd' (the latter short for 'returned') in line with my usual modern practice, but I haven't done a thorough job of finding all of them. Finally, one amusing side effect of doing this is that I've had to separate quite a few chained assignments. It used to be perfectly fine to write 'a = b = c = TRUE' when a,b,c were int and TRUE was just a the 'true' defined by stdbool.h, that idiom provokes a warning from gcc: 'suggest parentheses around assignment used as truth value'!
2018-11-02 22:23:19 +03:00
bool real_packet;
int crLine; /* for coroutine in pageant_conn_receive */
Plug plug;
};
static void pageant_conn_closing(Plug *plug, const char *error_msg,
Convert a lot of 'int' variables to 'bool'. My normal habit these days, in new code, is to treat int and bool as _almost_ completely separate types. I'm still willing to use C's implicit test for zero on an integer (e.g. 'if (!blob.len)' is fine, no need to spell it out as blob.len != 0), but generally, if a variable is going to be conceptually a boolean, I like to declare it bool and assign to it using 'true' or 'false' rather than 0 or 1. PuTTY is an exception, because it predates the C99 bool, and I've stuck to its existing coding style even when adding new code to it. But it's been annoying me more and more, so now that I've decided C99 bool is an acceptable thing to require from our toolchain in the first place, here's a quite thorough trawl through the source doing 'boolification'. Many variables and function parameters are now typed as bool rather than int; many assignments of 0 or 1 to those variables are now spelled 'true' or 'false'. I managed this thorough conversion with the help of a custom clang plugin that I wrote to trawl the AST and apply heuristics to point out where things might want changing. So I've even managed to do a decent job on parts of the code I haven't looked at in years! To make the plugin's work easier, I pushed platform front ends generally in the direction of using standard 'bool' in preference to platform-specific boolean types like Windows BOOL or GTK's gboolean; I've left the platform booleans in places they _have_ to be for the platform APIs to work right, but variables only used by my own code have been converted wherever I found them. In a few places there are int values that look very like booleans in _most_ of the places they're used, but have a rarely-used third value, or a distinction between different nonzero values that most users don't care about. In these cases, I've _removed_ uses of 'true' and 'false' for the return values, to emphasise that there's something more subtle going on than a simple boolean answer: - the 'multisel' field in dialog.h's list box structure, for which the GTK front end in particular recognises a difference between 1 and 2 but nearly everything else treats as boolean - the 'urgent' parameter to plug_receive, where 1 vs 2 tells you something about the specific location of the urgent pointer, but most clients only care about 0 vs 'something nonzero' - the return value of wc_match, where -1 indicates a syntax error in the wildcard. - the return values from SSH-1 RSA-key loading functions, which use -1 for 'wrong passphrase' and 0 for all other failures (so any caller which already knows it's not loading an _encrypted private_ key can treat them as boolean) - term->esc_query, and the 'query' parameter in toggle_mode in terminal.c, which _usually_ hold 0 for ESC[123h or 1 for ESC[?123h, but can also hold -1 for some other intervening character that we don't support. In a few places there's an integer that I haven't turned into a bool even though it really _can_ only take values 0 or 1 (and, as above, tried to make the call sites consistent in not calling those values true and false), on the grounds that I thought it would make it more confusing to imply that the 0 value was in some sense 'negative' or bad and the 1 positive or good: - the return value of plug_accepting uses the POSIXish convention of 0=success and nonzero=error; I think if I made it bool then I'd also want to reverse its sense, and that's a job for a separate piece of work. - the 'screen' parameter to lineptr() in terminal.c, where 0 and 1 represent the default and alternate screens. There's no obvious reason why one of those should be considered 'true' or 'positive' or 'success' - they're just indices - so I've left it as int. ssh_scp_recv had particularly confusing semantics for its previous int return value: its call sites used '<= 0' to check for error, but it never actually returned a negative number, just 0 or 1. Now the function and its call sites agree that it's a bool. In a couple of places I've renamed variables called 'ret', because I don't like that name any more - it's unclear whether it means the return value (in preparation) for the _containing_ function or the return value received from a subroutine call, and occasionally I've accidentally used the same variable for both and introduced a bug. So where one of those got in my way, I've renamed it to 'toret' or 'retd' (the latter short for 'returned') in line with my usual modern practice, but I haven't done a thorough job of finding all of them. Finally, one amusing side effect of doing this is that I've had to separate quite a few chained assignments. It used to be perfectly fine to write 'a = b = c = TRUE' when a,b,c were int and TRUE was just a the 'true' defined by stdbool.h, that idiom provokes a warning from gcc: 'suggest parentheses around assignment used as truth value'!
2018-11-02 22:23:19 +03:00
int error_code, bool calling_back)
{
struct pageant_conn_state *pc = container_of(
plug, struct pageant_conn_state, plug);
if (error_msg)
plog(pc->logctx, pc->logfn, "%p: error: %s", pc, error_msg);
else
plog(pc->logctx, pc->logfn, "%p: connection closed", pc);
sk_close(pc->connsock);
sfree(pc);
}
static void pageant_conn_sent(Plug *plug, size_t bufsize)
{
/* struct pageant_conn_state *pc = container_of(
plug, struct pageant_conn_state, plug); */
/*
* We do nothing here, because we expect that there won't be a
* need to throttle and unthrottle the connection to an agent -
* clients will typically not send many requests, and will wait
* until they receive each reply before sending a new request.
*/
}
static void pageant_conn_log(void *logctx, const char *fmt, va_list ap)
{
/* Wrapper on pc->logfn that prefixes the connection identifier */
struct pageant_conn_state *pc = (struct pageant_conn_state *)logctx;
char *formatted = dupvprintf(fmt, ap);
plog(pc->logctx, pc->logfn, "%p: %s", pc, formatted);
sfree(formatted);
}
static void pageant_conn_receive(
Plug *plug, int urgent, const char *data, size_t len)
{
struct pageant_conn_state *pc = container_of(
plug, struct pageant_conn_state, plug);
char c;
crBegin(pc->crLine);
while (len > 0) {
pc->got = 0;
while (pc->got < 4) {
crGetChar(c);
pc->lenbuf[pc->got++] = c;
}
pc->len = GET_32BIT_MSB_FIRST(pc->lenbuf);
pc->got = 0;
pc->real_packet = (pc->len < AGENT_MAX_MSGLEN-4);
while (pc->got < pc->len) {
crGetChar(c);
if (pc->real_packet)
pc->pktbuf[pc->got] = c;
pc->got++;
}
{
strbuf *reply = strbuf_new();
put_uint32(reply, 0); /* length field to fill in later */
if (pc->real_packet) {
pageant_handle_msg(BinarySink_UPCAST(reply), pc->pktbuf, pc->len, pc,
pc->logfn ? pageant_conn_log : NULL);
} else {
plog(pc->logctx, pc->logfn, "%p: overlong message (%u)",
pc, pc->len);
pageant_failure_msg(BinarySink_UPCAST(reply), "message too long", pc,
pc->logfn ? pageant_conn_log : NULL);
}
PUT_32BIT_MSB_FIRST(reply->s, reply->len - 4);
sk_write(pc->connsock, reply->s, reply->len);
strbuf_free(reply);
}
}
crFinishV;
}
struct pageant_listen_state {
Socket *listensock;
void *logctx;
pageant_logfn_t logfn;
Plug plug;
};
static void pageant_listen_closing(Plug *plug, const char *error_msg,
Convert a lot of 'int' variables to 'bool'. My normal habit these days, in new code, is to treat int and bool as _almost_ completely separate types. I'm still willing to use C's implicit test for zero on an integer (e.g. 'if (!blob.len)' is fine, no need to spell it out as blob.len != 0), but generally, if a variable is going to be conceptually a boolean, I like to declare it bool and assign to it using 'true' or 'false' rather than 0 or 1. PuTTY is an exception, because it predates the C99 bool, and I've stuck to its existing coding style even when adding new code to it. But it's been annoying me more and more, so now that I've decided C99 bool is an acceptable thing to require from our toolchain in the first place, here's a quite thorough trawl through the source doing 'boolification'. Many variables and function parameters are now typed as bool rather than int; many assignments of 0 or 1 to those variables are now spelled 'true' or 'false'. I managed this thorough conversion with the help of a custom clang plugin that I wrote to trawl the AST and apply heuristics to point out where things might want changing. So I've even managed to do a decent job on parts of the code I haven't looked at in years! To make the plugin's work easier, I pushed platform front ends generally in the direction of using standard 'bool' in preference to platform-specific boolean types like Windows BOOL or GTK's gboolean; I've left the platform booleans in places they _have_ to be for the platform APIs to work right, but variables only used by my own code have been converted wherever I found them. In a few places there are int values that look very like booleans in _most_ of the places they're used, but have a rarely-used third value, or a distinction between different nonzero values that most users don't care about. In these cases, I've _removed_ uses of 'true' and 'false' for the return values, to emphasise that there's something more subtle going on than a simple boolean answer: - the 'multisel' field in dialog.h's list box structure, for which the GTK front end in particular recognises a difference between 1 and 2 but nearly everything else treats as boolean - the 'urgent' parameter to plug_receive, where 1 vs 2 tells you something about the specific location of the urgent pointer, but most clients only care about 0 vs 'something nonzero' - the return value of wc_match, where -1 indicates a syntax error in the wildcard. - the return values from SSH-1 RSA-key loading functions, which use -1 for 'wrong passphrase' and 0 for all other failures (so any caller which already knows it's not loading an _encrypted private_ key can treat them as boolean) - term->esc_query, and the 'query' parameter in toggle_mode in terminal.c, which _usually_ hold 0 for ESC[123h or 1 for ESC[?123h, but can also hold -1 for some other intervening character that we don't support. In a few places there's an integer that I haven't turned into a bool even though it really _can_ only take values 0 or 1 (and, as above, tried to make the call sites consistent in not calling those values true and false), on the grounds that I thought it would make it more confusing to imply that the 0 value was in some sense 'negative' or bad and the 1 positive or good: - the return value of plug_accepting uses the POSIXish convention of 0=success and nonzero=error; I think if I made it bool then I'd also want to reverse its sense, and that's a job for a separate piece of work. - the 'screen' parameter to lineptr() in terminal.c, where 0 and 1 represent the default and alternate screens. There's no obvious reason why one of those should be considered 'true' or 'positive' or 'success' - they're just indices - so I've left it as int. ssh_scp_recv had particularly confusing semantics for its previous int return value: its call sites used '<= 0' to check for error, but it never actually returned a negative number, just 0 or 1. Now the function and its call sites agree that it's a bool. In a couple of places I've renamed variables called 'ret', because I don't like that name any more - it's unclear whether it means the return value (in preparation) for the _containing_ function or the return value received from a subroutine call, and occasionally I've accidentally used the same variable for both and introduced a bug. So where one of those got in my way, I've renamed it to 'toret' or 'retd' (the latter short for 'returned') in line with my usual modern practice, but I haven't done a thorough job of finding all of them. Finally, one amusing side effect of doing this is that I've had to separate quite a few chained assignments. It used to be perfectly fine to write 'a = b = c = TRUE' when a,b,c were int and TRUE was just a the 'true' defined by stdbool.h, that idiom provokes a warning from gcc: 'suggest parentheses around assignment used as truth value'!
2018-11-02 22:23:19 +03:00
int error_code, bool calling_back)
{
struct pageant_listen_state *pl = container_of(
plug, struct pageant_listen_state, plug);
if (error_msg)
plog(pl->logctx, pl->logfn, "listening socket: error: %s", error_msg);
sk_close(pl->listensock);
pl->listensock = NULL;
}
static const PlugVtable pageant_connection_plugvt = {
NULL, /* no log function, because that's for outgoing connections */
pageant_conn_closing,
pageant_conn_receive,
pageant_conn_sent,
NULL /* no accepting function, because we've already done it */
};
static int pageant_listen_accepting(Plug *plug,
accept_fn_t constructor, accept_ctx_t ctx)
{
struct pageant_listen_state *pl = container_of(
plug, struct pageant_listen_state, plug);
struct pageant_conn_state *pc;
const char *err;
SocketPeerInfo *peerinfo;
pc = snew(struct pageant_conn_state);
pc->plug.vt = &pageant_connection_plugvt;
pc->logfn = pl->logfn;
pc->logctx = pl->logctx;
pc->crLine = 0;
pc->connsock = constructor(ctx, &pc->plug);
if ((err = sk_socket_error(pc->connsock)) != NULL) {
sk_close(pc->connsock);
sfree(pc);
Convert a lot of 'int' variables to 'bool'. My normal habit these days, in new code, is to treat int and bool as _almost_ completely separate types. I'm still willing to use C's implicit test for zero on an integer (e.g. 'if (!blob.len)' is fine, no need to spell it out as blob.len != 0), but generally, if a variable is going to be conceptually a boolean, I like to declare it bool and assign to it using 'true' or 'false' rather than 0 or 1. PuTTY is an exception, because it predates the C99 bool, and I've stuck to its existing coding style even when adding new code to it. But it's been annoying me more and more, so now that I've decided C99 bool is an acceptable thing to require from our toolchain in the first place, here's a quite thorough trawl through the source doing 'boolification'. Many variables and function parameters are now typed as bool rather than int; many assignments of 0 or 1 to those variables are now spelled 'true' or 'false'. I managed this thorough conversion with the help of a custom clang plugin that I wrote to trawl the AST and apply heuristics to point out where things might want changing. So I've even managed to do a decent job on parts of the code I haven't looked at in years! To make the plugin's work easier, I pushed platform front ends generally in the direction of using standard 'bool' in preference to platform-specific boolean types like Windows BOOL or GTK's gboolean; I've left the platform booleans in places they _have_ to be for the platform APIs to work right, but variables only used by my own code have been converted wherever I found them. In a few places there are int values that look very like booleans in _most_ of the places they're used, but have a rarely-used third value, or a distinction between different nonzero values that most users don't care about. In these cases, I've _removed_ uses of 'true' and 'false' for the return values, to emphasise that there's something more subtle going on than a simple boolean answer: - the 'multisel' field in dialog.h's list box structure, for which the GTK front end in particular recognises a difference between 1 and 2 but nearly everything else treats as boolean - the 'urgent' parameter to plug_receive, where 1 vs 2 tells you something about the specific location of the urgent pointer, but most clients only care about 0 vs 'something nonzero' - the return value of wc_match, where -1 indicates a syntax error in the wildcard. - the return values from SSH-1 RSA-key loading functions, which use -1 for 'wrong passphrase' and 0 for all other failures (so any caller which already knows it's not loading an _encrypted private_ key can treat them as boolean) - term->esc_query, and the 'query' parameter in toggle_mode in terminal.c, which _usually_ hold 0 for ESC[123h or 1 for ESC[?123h, but can also hold -1 for some other intervening character that we don't support. In a few places there's an integer that I haven't turned into a bool even though it really _can_ only take values 0 or 1 (and, as above, tried to make the call sites consistent in not calling those values true and false), on the grounds that I thought it would make it more confusing to imply that the 0 value was in some sense 'negative' or bad and the 1 positive or good: - the return value of plug_accepting uses the POSIXish convention of 0=success and nonzero=error; I think if I made it bool then I'd also want to reverse its sense, and that's a job for a separate piece of work. - the 'screen' parameter to lineptr() in terminal.c, where 0 and 1 represent the default and alternate screens. There's no obvious reason why one of those should be considered 'true' or 'positive' or 'success' - they're just indices - so I've left it as int. ssh_scp_recv had particularly confusing semantics for its previous int return value: its call sites used '<= 0' to check for error, but it never actually returned a negative number, just 0 or 1. Now the function and its call sites agree that it's a bool. In a couple of places I've renamed variables called 'ret', because I don't like that name any more - it's unclear whether it means the return value (in preparation) for the _containing_ function or the return value received from a subroutine call, and occasionally I've accidentally used the same variable for both and introduced a bug. So where one of those got in my way, I've renamed it to 'toret' or 'retd' (the latter short for 'returned') in line with my usual modern practice, but I haven't done a thorough job of finding all of them. Finally, one amusing side effect of doing this is that I've had to separate quite a few chained assignments. It used to be perfectly fine to write 'a = b = c = TRUE' when a,b,c were int and TRUE was just a the 'true' defined by stdbool.h, that idiom provokes a warning from gcc: 'suggest parentheses around assignment used as truth value'!
2018-11-02 22:23:19 +03:00
return 1;
}
sk_set_frozen(pc->connsock, 0);
peerinfo = sk_peer_info(pc->connsock);
if (peerinfo && peerinfo->log_text) {
plog(pl->logctx, pl->logfn, "%p: new connection from %s",
pc, peerinfo->log_text);
} else {
plog(pl->logctx, pl->logfn, "%p: new connection", pc);
}
sk_free_peer_info(peerinfo);
return 0;
}
static const PlugVtable pageant_listener_plugvt = {
NULL, /* no log function, because that's for outgoing connections */
pageant_listen_closing,
NULL, /* no receive function on a listening socket */
NULL, /* no sent function on a listening socket */
pageant_listen_accepting
};
struct pageant_listen_state *pageant_listener_new(Plug **plug)
{
struct pageant_listen_state *pl = snew(struct pageant_listen_state);
pl->plug.vt = &pageant_listener_plugvt;
pl->logctx = NULL;
pl->logfn = NULL;
pl->listensock = NULL;
*plug = &pl->plug;
return pl;
}
void pageant_listener_got_socket(struct pageant_listen_state *pl, Socket *sock)
{
pl->listensock = sock;
}
void pageant_listener_set_logfn(struct pageant_listen_state *pl,
void *logctx, pageant_logfn_t logfn)
{
pl->logctx = logctx;
pl->logfn = logfn;
}
void pageant_listener_free(struct pageant_listen_state *pl)
{
if (pl->listensock)
sk_close(pl->listensock);
sfree(pl);
}
/* ----------------------------------------------------------------------
* Code to perform agent operations either as a client, or within the
* same process as the running agent.
*/
static tree234 *passphrases = NULL;
/*
* After processing a list of filenames, we want to forget the
* passphrases.
*/
void pageant_forget_passphrases(void)
{
if (!passphrases) /* in case we never set it up at all */
return;
while (count234(passphrases) > 0) {
char *pp = index234(passphrases, 0);
smemclr(pp, strlen(pp));
delpos234(passphrases, 0);
sfree(pp);
}
}
void *pageant_get_keylist1(int *length)
{
void *ret;
if (!pageant_local) {
strbuf *request;
unsigned char *response;
void *vresponse;
int resplen;
request = strbuf_new_for_agent_query();
put_byte(request, SSH1_AGENTC_REQUEST_RSA_IDENTITIES);
agent_query_synchronous(request, &vresponse, &resplen);
strbuf_free(request);
response = vresponse;
if (resplen < 5 || response[4] != SSH1_AGENT_RSA_IDENTITIES_ANSWER) {
sfree(response);
return NULL;
}
ret = snewn(resplen-5, unsigned char);
memcpy(ret, response+5, resplen-5);
sfree(response);
if (length)
*length = resplen-5;
} else {
strbuf *buf = strbuf_new();
pageant_make_keylist1(BinarySink_UPCAST(buf));
*length = buf->len;
ret = strbuf_to_str(buf);
}
return ret;
}
void *pageant_get_keylist2(int *length)
{
void *ret;
if (!pageant_local) {
strbuf *request;
unsigned char *response;
void *vresponse;
int resplen;
request = strbuf_new_for_agent_query();
put_byte(request, SSH2_AGENTC_REQUEST_IDENTITIES);
agent_query_synchronous(request, &vresponse, &resplen);
strbuf_free(request);
response = vresponse;
if (resplen < 5 || response[4] != SSH2_AGENT_IDENTITIES_ANSWER) {
sfree(response);
return NULL;
}
ret = snewn(resplen-5, unsigned char);
memcpy(ret, response+5, resplen-5);
sfree(response);
if (length)
*length = resplen-5;
} else {
strbuf *buf = strbuf_new();
pageant_make_keylist2(BinarySink_UPCAST(buf));
*length = buf->len;
ret = strbuf_to_str(buf);
}
return ret;
}
int pageant_add_keyfile(Filename *filename, const char *passphrase,
char **retstr)
{
RSAKey *rkey = NULL;
ssh2_userkey *skey = NULL;
Convert a lot of 'int' variables to 'bool'. My normal habit these days, in new code, is to treat int and bool as _almost_ completely separate types. I'm still willing to use C's implicit test for zero on an integer (e.g. 'if (!blob.len)' is fine, no need to spell it out as blob.len != 0), but generally, if a variable is going to be conceptually a boolean, I like to declare it bool and assign to it using 'true' or 'false' rather than 0 or 1. PuTTY is an exception, because it predates the C99 bool, and I've stuck to its existing coding style even when adding new code to it. But it's been annoying me more and more, so now that I've decided C99 bool is an acceptable thing to require from our toolchain in the first place, here's a quite thorough trawl through the source doing 'boolification'. Many variables and function parameters are now typed as bool rather than int; many assignments of 0 or 1 to those variables are now spelled 'true' or 'false'. I managed this thorough conversion with the help of a custom clang plugin that I wrote to trawl the AST and apply heuristics to point out where things might want changing. So I've even managed to do a decent job on parts of the code I haven't looked at in years! To make the plugin's work easier, I pushed platform front ends generally in the direction of using standard 'bool' in preference to platform-specific boolean types like Windows BOOL or GTK's gboolean; I've left the platform booleans in places they _have_ to be for the platform APIs to work right, but variables only used by my own code have been converted wherever I found them. In a few places there are int values that look very like booleans in _most_ of the places they're used, but have a rarely-used third value, or a distinction between different nonzero values that most users don't care about. In these cases, I've _removed_ uses of 'true' and 'false' for the return values, to emphasise that there's something more subtle going on than a simple boolean answer: - the 'multisel' field in dialog.h's list box structure, for which the GTK front end in particular recognises a difference between 1 and 2 but nearly everything else treats as boolean - the 'urgent' parameter to plug_receive, where 1 vs 2 tells you something about the specific location of the urgent pointer, but most clients only care about 0 vs 'something nonzero' - the return value of wc_match, where -1 indicates a syntax error in the wildcard. - the return values from SSH-1 RSA-key loading functions, which use -1 for 'wrong passphrase' and 0 for all other failures (so any caller which already knows it's not loading an _encrypted private_ key can treat them as boolean) - term->esc_query, and the 'query' parameter in toggle_mode in terminal.c, which _usually_ hold 0 for ESC[123h or 1 for ESC[?123h, but can also hold -1 for some other intervening character that we don't support. In a few places there's an integer that I haven't turned into a bool even though it really _can_ only take values 0 or 1 (and, as above, tried to make the call sites consistent in not calling those values true and false), on the grounds that I thought it would make it more confusing to imply that the 0 value was in some sense 'negative' or bad and the 1 positive or good: - the return value of plug_accepting uses the POSIXish convention of 0=success and nonzero=error; I think if I made it bool then I'd also want to reverse its sense, and that's a job for a separate piece of work. - the 'screen' parameter to lineptr() in terminal.c, where 0 and 1 represent the default and alternate screens. There's no obvious reason why one of those should be considered 'true' or 'positive' or 'success' - they're just indices - so I've left it as int. ssh_scp_recv had particularly confusing semantics for its previous int return value: its call sites used '<= 0' to check for error, but it never actually returned a negative number, just 0 or 1. Now the function and its call sites agree that it's a bool. In a couple of places I've renamed variables called 'ret', because I don't like that name any more - it's unclear whether it means the return value (in preparation) for the _containing_ function or the return value received from a subroutine call, and occasionally I've accidentally used the same variable for both and introduced a bug. So where one of those got in my way, I've renamed it to 'toret' or 'retd' (the latter short for 'returned') in line with my usual modern practice, but I haven't done a thorough job of finding all of them. Finally, one amusing side effect of doing this is that I've had to separate quite a few chained assignments. It used to be perfectly fine to write 'a = b = c = TRUE' when a,b,c were int and TRUE was just a the 'true' defined by stdbool.h, that idiom provokes a warning from gcc: 'suggest parentheses around assignment used as truth value'!
2018-11-02 22:23:19 +03:00
bool needs_pass;
int ret;
int attempts;
char *comment;
const char *this_passphrase;
const char *error = NULL;
int type;
if (!passphrases) {
passphrases = newtree234(NULL);
}
*retstr = NULL;
type = key_type(filename);
if (type != SSH_KEYTYPE_SSH1 && type != SSH_KEYTYPE_SSH2) {
*retstr = dupprintf("Couldn't load this key (%s)",
key_type_to_str(type));
return PAGEANT_ACTION_FAILURE;
}
/*
* See if the key is already loaded (in the primary Pageant,
* which may or may not be us).
*/
{
strbuf *blob = strbuf_new();
unsigned char *keylist, *p;
int i, nkeys, keylistlen;
if (type == SSH_KEYTYPE_SSH1) {
if (!rsa_ssh1_loadpub(filename, BinarySink_UPCAST(blob), NULL, &error)) {
*retstr = dupprintf("Couldn't load private key (%s)", error);
strbuf_free(blob);
return PAGEANT_ACTION_FAILURE;
}
keylist = pageant_get_keylist1(&keylistlen);
} else {
/* For our purposes we want the blob prefixed with its
* length, so add a placeholder here to fill in
* afterwards */
put_uint32(blob, 0);
if (!ssh2_userkey_loadpub(filename, NULL, BinarySink_UPCAST(blob),
NULL, &error)) {
*retstr = dupprintf("Couldn't load private key (%s)", error);
strbuf_free(blob);
return PAGEANT_ACTION_FAILURE;
}
PUT_32BIT_MSB_FIRST(blob->s, blob->len - 4);
keylist = pageant_get_keylist2(&keylistlen);
}
if (keylist) {
if (keylistlen < 4) {
*retstr = dupstr("Received broken key list from agent");
sfree(keylist);
strbuf_free(blob);
return PAGEANT_ACTION_FAILURE;
}
nkeys = toint(GET_32BIT_MSB_FIRST(keylist));
if (nkeys < 0) {
*retstr = dupstr("Received broken key list from agent");
sfree(keylist);
strbuf_free(blob);
return PAGEANT_ACTION_FAILURE;
}
p = keylist + 4;
keylistlen -= 4;
for (i = 0; i < nkeys; i++) {
if (!memcmp(blob->s, p, blob->len)) {
/* Key is already present; we can now leave. */
sfree(keylist);
strbuf_free(blob);
return PAGEANT_ACTION_OK;
}
/* Now skip over public blob */
if (type == SSH_KEYTYPE_SSH1) {
int n = rsa_ssh1_public_blob_len(
make_ptrlen(p, keylistlen));
if (n < 0) {
*retstr = dupstr("Received broken key list from agent");
sfree(keylist);
strbuf_free(blob);
return PAGEANT_ACTION_FAILURE;
}
p += n;
keylistlen -= n;
} else {
int n;
if (keylistlen < 4) {
*retstr = dupstr("Received broken key list from agent");
sfree(keylist);
strbuf_free(blob);
return PAGEANT_ACTION_FAILURE;
}
n = GET_32BIT_MSB_FIRST(p);
p += 4;
keylistlen -= 4;
if (n < 0 || n > keylistlen) {
*retstr = dupstr("Received broken key list from agent");
sfree(keylist);
strbuf_free(blob);
return PAGEANT_ACTION_FAILURE;
}
p += n;
keylistlen -= n;
}
/* Now skip over comment field */
{
int n;
if (keylistlen < 4) {
*retstr = dupstr("Received broken key list from agent");
sfree(keylist);
strbuf_free(blob);
return PAGEANT_ACTION_FAILURE;
}
n = GET_32BIT_MSB_FIRST(p);
p += 4;
keylistlen -= 4;
if (n < 0 || n > keylistlen) {
*retstr = dupstr("Received broken key list from agent");
sfree(keylist);
strbuf_free(blob);
return PAGEANT_ACTION_FAILURE;
}
p += n;
keylistlen -= n;
}
}
sfree(keylist);
}
strbuf_free(blob);
}
error = NULL;
if (type == SSH_KEYTYPE_SSH1)
needs_pass = rsa_ssh1_encrypted(filename, &comment);
else
needs_pass = ssh2_userkey_encrypted(filename, &comment);
attempts = 0;
if (type == SSH_KEYTYPE_SSH1)
rkey = snew(RSAKey);
/*
* Loop round repeatedly trying to load the key, until we either
* succeed, fail for some serious reason, or run out of
* passphrases to try.
*/
while (1) {
if (needs_pass) {
/*
* If we've been given a passphrase on input, try using
* it. Otherwise, try one from our tree234 of previously
* useful passphrases.
*/
if (passphrase) {
this_passphrase = (attempts == 0 ? passphrase : NULL);
} else {
this_passphrase = (const char *)index234(passphrases, attempts);
}
if (!this_passphrase) {
/*
* Run out of passphrases to try.
*/
*retstr = comment;
sfree(rkey);
return PAGEANT_ACTION_NEED_PP;
}
} else
this_passphrase = "";
if (type == SSH_KEYTYPE_SSH1)
ret = rsa_ssh1_loadkey(filename, rkey, this_passphrase, &error);
else {
skey = ssh2_load_userkey(filename, this_passphrase, &error);
if (skey == SSH2_WRONG_PASSPHRASE)
ret = -1;
else if (!skey)
ret = 0;
else
ret = 1;
}
if (ret == 0) {
/*
* Failed to load the key file, for some reason other than
* a bad passphrase.
*/
*retstr = dupstr(error);
sfree(rkey);
if (comment)
sfree(comment);
return PAGEANT_ACTION_FAILURE;
} else if (ret == 1) {
/*
* Successfully loaded the key file.
*/
break;
} else {
/*
* Passphrase wasn't right; go round again.
*/
attempts++;
}
}
/*
* If we get here, we've successfully loaded the key into
* rkey/skey, but not yet added it to the agent.
*/
/*
* If the key was successfully decrypted, save the passphrase for
* use with other keys we try to load.
*/
{
char *pp_copy = dupstr(this_passphrase);
if (addpos234(passphrases, pp_copy, 0) != pp_copy) {
/* No need; it was already there. */
smemclr(pp_copy, strlen(pp_copy));
sfree(pp_copy);
}
}
if (comment)
sfree(comment);
if (type == SSH_KEYTYPE_SSH1) {
if (!pageant_local) {
strbuf *request;
unsigned char *response;
void *vresponse;
int resplen;
request = strbuf_new_for_agent_query();
put_byte(request, SSH1_AGENTC_ADD_RSA_IDENTITY);
Complete rewrite of PuTTY's bignum library. The old 'Bignum' data type is gone completely, and so is sshbn.c. In its place is a new thing called 'mp_int', handled by an entirely new library module mpint.c, with API differences both large and small. The main aim of this change is that the new library should be free of timing- and cache-related side channels. I've written the code so that it _should_ - assuming I haven't made any mistakes - do all of its work without either control flow or memory addressing depending on the data words of the input numbers. (Though, being an _arbitrary_ precision library, it does have to at least depend on the sizes of the numbers - but there's a 'formal' size that can vary separately from the actual magnitude of the represented integer, so if you want to keep it secret that your number is actually small, it should work fine to have a very long mp_int and just happen to store 23 in it.) So I've done all my conditionalisation by means of computing both answers and doing bit-masking to swap the right one into place, and all loops over the words of an mp_int go up to the formal size rather than the actual size. I haven't actually tested the constant-time property in any rigorous way yet (I'm still considering the best way to do it). But this code is surely at the very least a big improvement on the old version, even if I later find a few more things to fix. I've also completely rewritten the low-level elliptic curve arithmetic from sshecc.c; the new ecc.c is closer to being an adjunct of mpint.c than it is to the SSH end of the code. The new elliptic curve code keeps all coordinates in Montgomery-multiplication transformed form to speed up all the multiplications mod the same prime, and only converts them back when you ask for the affine coordinates. Also, I adopted extended coordinates for the Edwards curve implementation. sshecc.c has also had a near-total rewrite in the course of switching it over to the new system. While I was there, I've separated ECDSA and EdDSA more completely - they now have separate vtables, instead of a single vtable in which nearly every function had a big if statement in it - and also made the externally exposed types for an ECDSA key and an ECDH context different. A minor new feature: since the new arithmetic code includes a modular square root function, we can now support the compressed point representation for the NIST curves. We seem to have been getting along fine without that so far, but it seemed a shame not to put it in, since it was suddenly easy. In sshrsa.c, one major change is that I've removed the RSA blinding step in rsa_privkey_op, in which we randomise the ciphertext before doing the decryption. The purpose of that was to avoid timing leaks giving away the plaintext - but the new arithmetic code should take that in its stride in the course of also being careful enough to avoid leaking the _private key_, which RSA blinding had no way to do anything about in any case. Apart from those specific points, most of the rest of the changes are more or less mechanical, just changing type names and translating code into the new API.
2018-12-31 16:53:41 +03:00
put_uint32(request, mp_get_nbits(rkey->modulus));
put_mp_ssh1(request, rkey->modulus);
put_mp_ssh1(request, rkey->exponent);
put_mp_ssh1(request, rkey->private_exponent);
put_mp_ssh1(request, rkey->iqmp);
put_mp_ssh1(request, rkey->q);
put_mp_ssh1(request, rkey->p);
put_stringz(request, rkey->comment);
agent_query_synchronous(request, &vresponse, &resplen);
strbuf_free(request);
response = vresponse;
if (resplen < 5 || response[4] != SSH_AGENT_SUCCESS) {
*retstr = dupstr("The already running Pageant "
"refused to add the key.");
freersakey(rkey);
sfree(rkey);
sfree(response);
return PAGEANT_ACTION_FAILURE;
}
freersakey(rkey);
sfree(rkey);
sfree(response);
} else {
if (!pageant_add_ssh1_key(rkey)) {
freersakey(rkey);
sfree(rkey); /* already present, don't waste RAM */
}
}
} else {
if (!pageant_local) {
strbuf *request;
unsigned char *response;
void *vresponse;
int resplen;
request = strbuf_new_for_agent_query();
put_byte(request, SSH2_AGENTC_ADD_IDENTITY);
put_stringz(request, ssh_key_ssh_id(skey->key));
ssh_key_openssh_blob(skey->key, BinarySink_UPCAST(request));
put_stringz(request, skey->comment);
agent_query_synchronous(request, &vresponse, &resplen);
strbuf_free(request);
response = vresponse;
if (resplen < 5 || response[4] != SSH_AGENT_SUCCESS) {
*retstr = dupstr("The already running Pageant "
"refused to add the key.");
sfree(response);
return PAGEANT_ACTION_FAILURE;
}
ssh_key_free(skey->key);
sfree(skey);
sfree(response);
} else {
if (!pageant_add_ssh2_key(skey)) {
ssh_key_free(skey->key);
sfree(skey); /* already present, don't waste RAM */
}
}
}
return PAGEANT_ACTION_OK;
}
int pageant_enum_keys(pageant_key_enum_fn_t callback, void *callback_ctx,
char **retstr)
{
unsigned char *keylist;
int i, nkeys, keylistlen;
ptrlen comment;
struct pageant_pubkey cbkey;
BinarySource src[1];
keylist = pageant_get_keylist1(&keylistlen);
if (!keylist) {
*retstr = dupstr("Did not receive an SSH-1 key list from agent");
return PAGEANT_ACTION_FAILURE;
}
BinarySource_BARE_INIT(src, keylist, keylistlen);
nkeys = toint(get_uint32(src));
for (i = 0; i < nkeys; i++) {
RSAKey rkey;
char *fingerprint;
/* public blob and fingerprint */
memset(&rkey, 0, sizeof(rkey));
get_rsa_ssh1_pub(src, &rkey, RSA_SSH1_EXPONENT_FIRST);
comment = get_string(src);
if (get_err(src)) {
*retstr = dupstr("Received broken SSH-1 key list from agent");
freersakey(&rkey);
sfree(keylist);
return PAGEANT_ACTION_FAILURE;
}
fingerprint = rsa_ssh1_fingerprint(&rkey);
cbkey.blob = strbuf_new();
rsa_ssh1_public_blob(BinarySink_UPCAST(cbkey.blob), &rkey,
RSA_SSH1_EXPONENT_FIRST);
cbkey.comment = mkstr(comment);
cbkey.ssh_version = 1;
callback(callback_ctx, fingerprint, cbkey.comment, &cbkey);
strbuf_free(cbkey.blob);
freersakey(&rkey);
sfree(cbkey.comment);
sfree(fingerprint);
}
sfree(keylist);
if (get_err(src) || get_avail(src) != 0) {
*retstr = dupstr("Received broken SSH-1 key list from agent");
return PAGEANT_ACTION_FAILURE;
}
keylist = pageant_get_keylist2(&keylistlen);
if (!keylist) {
*retstr = dupstr("Did not receive an SSH-2 key list from agent");
return PAGEANT_ACTION_FAILURE;
}
BinarySource_BARE_INIT(src, keylist, keylistlen);
nkeys = toint(get_uint32(src));
for (i = 0; i < nkeys; i++) {
ptrlen pubblob;
char *fingerprint;
pubblob = get_string(src);
comment = get_string(src);
if (get_err(src)) {
*retstr = dupstr("Received broken SSH-2 key list from agent");
sfree(keylist);
return PAGEANT_ACTION_FAILURE;
}
fingerprint = ssh2_fingerprint_blob(pubblob);
cbkey.blob = strbuf_new();
put_datapl(cbkey.blob, pubblob);
cbkey.ssh_version = 2;
cbkey.comment = mkstr(comment);
callback(callback_ctx, fingerprint, cbkey.comment, &cbkey);
sfree(fingerprint);
sfree(cbkey.comment);
}
sfree(keylist);
if (get_err(src) || get_avail(src) != 0) {
*retstr = dupstr("Received broken SSH-2 key list from agent");
return PAGEANT_ACTION_FAILURE;
}
return PAGEANT_ACTION_OK;
}
int pageant_delete_key(struct pageant_pubkey *key, char **retstr)
{
strbuf *request;
unsigned char *response;
int resplen, ret;
void *vresponse;
request = strbuf_new_for_agent_query();
if (key->ssh_version == 1) {
put_byte(request, SSH1_AGENTC_REMOVE_RSA_IDENTITY);
put_data(request, key->blob->s, key->blob->len);
} else {
put_byte(request, SSH2_AGENTC_REMOVE_IDENTITY);
put_string(request, key->blob->s, key->blob->len);
}
agent_query_synchronous(request, &vresponse, &resplen);
strbuf_free(request);
response = vresponse;
if (resplen < 5 || response[4] != SSH_AGENT_SUCCESS) {
*retstr = dupstr("Agent failed to delete key");
ret = PAGEANT_ACTION_FAILURE;
} else {
*retstr = NULL;
ret = PAGEANT_ACTION_OK;
}
sfree(response);
return ret;
}
int pageant_delete_all_keys(char **retstr)
{
strbuf *request;
unsigned char *response;
Convert a lot of 'int' variables to 'bool'. My normal habit these days, in new code, is to treat int and bool as _almost_ completely separate types. I'm still willing to use C's implicit test for zero on an integer (e.g. 'if (!blob.len)' is fine, no need to spell it out as blob.len != 0), but generally, if a variable is going to be conceptually a boolean, I like to declare it bool and assign to it using 'true' or 'false' rather than 0 or 1. PuTTY is an exception, because it predates the C99 bool, and I've stuck to its existing coding style even when adding new code to it. But it's been annoying me more and more, so now that I've decided C99 bool is an acceptable thing to require from our toolchain in the first place, here's a quite thorough trawl through the source doing 'boolification'. Many variables and function parameters are now typed as bool rather than int; many assignments of 0 or 1 to those variables are now spelled 'true' or 'false'. I managed this thorough conversion with the help of a custom clang plugin that I wrote to trawl the AST and apply heuristics to point out where things might want changing. So I've even managed to do a decent job on parts of the code I haven't looked at in years! To make the plugin's work easier, I pushed platform front ends generally in the direction of using standard 'bool' in preference to platform-specific boolean types like Windows BOOL or GTK's gboolean; I've left the platform booleans in places they _have_ to be for the platform APIs to work right, but variables only used by my own code have been converted wherever I found them. In a few places there are int values that look very like booleans in _most_ of the places they're used, but have a rarely-used third value, or a distinction between different nonzero values that most users don't care about. In these cases, I've _removed_ uses of 'true' and 'false' for the return values, to emphasise that there's something more subtle going on than a simple boolean answer: - the 'multisel' field in dialog.h's list box structure, for which the GTK front end in particular recognises a difference between 1 and 2 but nearly everything else treats as boolean - the 'urgent' parameter to plug_receive, where 1 vs 2 tells you something about the specific location of the urgent pointer, but most clients only care about 0 vs 'something nonzero' - the return value of wc_match, where -1 indicates a syntax error in the wildcard. - the return values from SSH-1 RSA-key loading functions, which use -1 for 'wrong passphrase' and 0 for all other failures (so any caller which already knows it's not loading an _encrypted private_ key can treat them as boolean) - term->esc_query, and the 'query' parameter in toggle_mode in terminal.c, which _usually_ hold 0 for ESC[123h or 1 for ESC[?123h, but can also hold -1 for some other intervening character that we don't support. In a few places there's an integer that I haven't turned into a bool even though it really _can_ only take values 0 or 1 (and, as above, tried to make the call sites consistent in not calling those values true and false), on the grounds that I thought it would make it more confusing to imply that the 0 value was in some sense 'negative' or bad and the 1 positive or good: - the return value of plug_accepting uses the POSIXish convention of 0=success and nonzero=error; I think if I made it bool then I'd also want to reverse its sense, and that's a job for a separate piece of work. - the 'screen' parameter to lineptr() in terminal.c, where 0 and 1 represent the default and alternate screens. There's no obvious reason why one of those should be considered 'true' or 'positive' or 'success' - they're just indices - so I've left it as int. ssh_scp_recv had particularly confusing semantics for its previous int return value: its call sites used '<= 0' to check for error, but it never actually returned a negative number, just 0 or 1. Now the function and its call sites agree that it's a bool. In a couple of places I've renamed variables called 'ret', because I don't like that name any more - it's unclear whether it means the return value (in preparation) for the _containing_ function or the return value received from a subroutine call, and occasionally I've accidentally used the same variable for both and introduced a bug. So where one of those got in my way, I've renamed it to 'toret' or 'retd' (the latter short for 'returned') in line with my usual modern practice, but I haven't done a thorough job of finding all of them. Finally, one amusing side effect of doing this is that I've had to separate quite a few chained assignments. It used to be perfectly fine to write 'a = b = c = TRUE' when a,b,c were int and TRUE was just a the 'true' defined by stdbool.h, that idiom provokes a warning from gcc: 'suggest parentheses around assignment used as truth value'!
2018-11-02 22:23:19 +03:00
int resplen;
bool success;
void *vresponse;
request = strbuf_new_for_agent_query();
put_byte(request, SSH2_AGENTC_REMOVE_ALL_IDENTITIES);
agent_query_synchronous(request, &vresponse, &resplen);
strbuf_free(request);
response = vresponse;
success = (resplen >= 4 && response[4] == SSH_AGENT_SUCCESS);
sfree(response);
if (!success) {
*retstr = dupstr("Agent failed to delete SSH-2 keys");
return PAGEANT_ACTION_FAILURE;
}
request = strbuf_new_for_agent_query();
put_byte(request, SSH1_AGENTC_REMOVE_ALL_RSA_IDENTITIES);
agent_query_synchronous(request, &vresponse, &resplen);
strbuf_free(request);
response = vresponse;
success = (resplen >= 4 && response[4] == SSH_AGENT_SUCCESS);
sfree(response);
if (!success) {
*retstr = dupstr("Agent failed to delete SSH-1 keys");
return PAGEANT_ACTION_FAILURE;
}
*retstr = NULL;
return PAGEANT_ACTION_OK;
}
struct pageant_pubkey *pageant_pubkey_copy(struct pageant_pubkey *key)
{
struct pageant_pubkey *ret = snew(struct pageant_pubkey);
ret->blob = strbuf_new();
put_data(ret->blob, key->blob->s, key->blob->len);
ret->comment = key->comment ? dupstr(key->comment) : NULL;
ret->ssh_version = key->ssh_version;
return ret;
}
void pageant_pubkey_free(struct pageant_pubkey *key)
{
sfree(key->comment);
strbuf_free(key->blob);
sfree(key);
}