From 518c0f0ea136a43027dacb034efcbb94ff2fbf83 Mon Sep 17 00:00:00 2001 From: Simon Tatham Date: Sun, 9 Feb 2020 21:56:21 +0000 Subject: [PATCH] Unix Pageant: --test-sign client option. This reads data from standard input, turns it into an SSH-2 sign request, and writes the resulting signature blob to standard output. I don't really anticipate many uses for this other than testing. But it _is_ convenient for testing changes to Pageant itself: it lets me ask for a signature without first having to construct a pointless SSH session that will accept the relevant key. --- pageant.c | 32 ++++++++++++++++++++++++++++++++ pageant.h | 2 ++ unix/uxpgnt.c | 51 ++++++++++++++++++++++++++++++++++++++++++++++++++- 3 files changed, 84 insertions(+), 1 deletion(-) diff --git a/pageant.c b/pageant.c index 1c281a40..c14ecc18 100644 --- a/pageant.c +++ b/pageant.c @@ -2117,6 +2117,38 @@ int pageant_delete_all_keys(char **retstr) return PAGEANT_ACTION_OK; } +int pageant_sign(struct pageant_pubkey *key, ptrlen message, strbuf *out, + uint32_t flags, char **retstr) +{ + strbuf *request; + void *response; + int resplen; + BinarySource src[1]; + + request = strbuf_new_for_agent_query(); + put_byte(request, SSH2_AGENTC_SIGN_REQUEST); + put_string(request, key->blob->s, key->blob->len); + put_stringpl(request, message); + put_uint32(request, flags); + pageant_client_query(request, &response, &resplen); + strbuf_free(request); + BinarySource_BARE_INIT(src, response, resplen); + BinarySource_BARE_INIT_PL(src, get_string(src)); + + int type = get_byte(src); + ptrlen signature = get_string(src); + put_datapl(out, signature); + sfree(response); + + if (type == SSH2_AGENT_SIGN_RESPONSE && !get_err(src)) { + *retstr = NULL; + return PAGEANT_ACTION_OK; + } else { + *retstr = dupstr("Agent failed to create signature"); + return PAGEANT_ACTION_FAILURE; + } +} + struct pageant_pubkey *pageant_pubkey_copy(struct pageant_pubkey *key) { struct pageant_pubkey *ret = snew(struct pageant_pubkey); diff --git a/pageant.h b/pageant.h index a34d0094..6f50f9ba 100644 --- a/pageant.h +++ b/pageant.h @@ -242,3 +242,5 @@ int pageant_enum_keys(pageant_key_enum_fn_t callback, void *callback_ctx, char **retstr); int pageant_delete_key(struct pageant_pubkey *key, char **retstr); int pageant_delete_all_keys(char **retstr); +int pageant_sign(struct pageant_pubkey *key, ptrlen message, strbuf *out, + uint32_t flags, char **retstr); diff --git a/unix/uxpgnt.c b/unix/uxpgnt.c index 7376d487..8909ded6 100644 --- a/unix/uxpgnt.c +++ b/unix/uxpgnt.c @@ -395,7 +395,8 @@ typedef enum { KEYACT_CLIENT_DEL_ALL, KEYACT_CLIENT_LIST, KEYACT_CLIENT_PUBLIC_OPENSSH, - KEYACT_CLIENT_PUBLIC + KEYACT_CLIENT_PUBLIC, + KEYACT_CLIENT_SIGN, } keyact; struct cmdline_key_action { struct cmdline_key_action *next; @@ -409,6 +410,7 @@ bool is_agent_action(keyact action) } static struct cmdline_key_action *keyact_head = NULL, *keyact_tail = NULL; +static uint32_t sign_flags = 0; void add_keyact(keyact action, const char *filename) { @@ -762,6 +764,9 @@ void run_client(void) struct pageant_pubkey *key; bool errors = false; char *retstr; + LoadedFile *message = lf_new(AGENT_MAX_MSGLEN); + bool message_loaded = false, message_ok = false; + strbuf *signature = strbuf_new(); if (!agent_exists()) { fprintf(stderr, "pageant: no agent running to talk to\n"); @@ -835,11 +840,49 @@ void run_client(void) errors = true; } break; + case KEYACT_CLIENT_SIGN: + key = NULL; + if (!message_loaded) { + message_loaded = true; + switch(lf_load_fp(message, stdin)) { + case LF_TOO_BIG: + fprintf(stderr, "pageant: message to sign is too big\n"); + errors = true; + break; + case LF_ERROR: + fprintf(stderr, "pageant: reading message to sign: %s\n", + strerror(errno)); + errors = true; + break; + case LF_OK: + message_ok = true; + break; + } + } + if (!message_ok) + break; + strbuf_clear(signature); + if (!(key = find_key(act->filename, &retstr)) || + pageant_sign(key, ptrlen_from_lf(message), signature, + sign_flags, &retstr) == PAGEANT_ACTION_FAILURE) { + fprintf(stderr, "pageant: signing with key '%s': %s\n", + act->filename, retstr); + sfree(retstr); + errors = true; + } else { + fwrite(signature->s, 1, signature->len, stdout); + } + if (key) + pageant_pubkey_free(key); + break; default: unreachable("Invalid client action found"); } } + lf_free(message); + strbuf_free(signature); + if (errors) exit(1); } @@ -1206,6 +1249,12 @@ int main(int argc, char **argv) } } else if (!strcmp(p, "--debug")) { life = LIFE_DEBUG; + } else if (!strcmp(p, "--test-sign")) { + curr_keyact = KEYACT_CLIENT_SIGN; + sign_flags = 0; + } else if (strstartswith(p, "--test-sign-with-flags=")) { + curr_keyact = KEYACT_CLIENT_SIGN; + sign_flags = atoi(p + strlen("--test-sign-with-flags=")); } else if (!strcmp(p, "--permanent")) { life = LIFE_PERM; } else if (!strcmp(p, "--exec")) {