From 5db2f4ca7eb2fe5927360cb9703a395a488bbd07 Mon Sep 17 00:00:00 2001 From: Simon Tatham Date: Sun, 2 Feb 2020 11:27:03 +0000 Subject: [PATCH] Expose lf_load_keyfile outside sshpubk.c. I'm about to use it in cmdgen for a minor UI improvement. Also, I expect it to be useful in the Pageant client code sooner or later. While I'm here, I've also tweaked its UI a little so that it reports a more precise error, and provided a version that can read from an already open stdio stream. --- defs.h | 1 + ssh.h | 13 +++++ sshpubk.c | 143 +++++++++++++++++++++++++++++++++++------------------- 3 files changed, 106 insertions(+), 51 deletions(-) diff --git a/defs.h b/defs.h index b9577036..f500a04c 100644 --- a/defs.h +++ b/defs.h @@ -64,6 +64,7 @@ typedef struct FontSpec FontSpec; typedef struct bufchain_tag bufchain; typedef struct strbuf strbuf; +typedef struct LoadedFile LoadedFile; typedef struct RSAKey RSAKey; diff --git a/ssh.h b/ssh.h index d0be17e5..13bb75ec 100644 --- a/ssh.h +++ b/ssh.h @@ -1195,6 +1195,19 @@ int rsa1_loadpub_f(const Filename *filename, BinarySink *bs, const ssh_keyalg *find_pubkey_alg(const char *name); const ssh_keyalg *find_pubkey_alg_len(ptrlen name); +/* + * A mechanism for loading a key file from disk into a memory buffer + * where it can be picked apart as a BinarySource. + */ +struct LoadedFile { + char *data; + size_t len, max_size; + BinarySource_IMPLEMENTATION; +}; +LoadedFile *lf_load_keyfile(const Filename *filename, const char **errptr); +LoadedFile *lf_load_keyfile_fp(FILE *fp, const char **errptr); +void lf_free(LoadedFile *lf); + enum { SSH_KEYTYPE_UNOPENABLE, SSH_KEYTYPE_UNKNOWN, diff --git a/sshpubk.c b/sshpubk.c index 87f86de2..4a0e6729 100644 --- a/sshpubk.c +++ b/sshpubk.c @@ -6,6 +6,8 @@ */ #include +#include +#include #include #include @@ -42,36 +44,35 @@ static const ptrlen rsa1_signature = (x)=='+' ? 62 : \ (x)=='/' ? 63 : 0 ) -typedef struct LoadedFile { - char *data; - size_t len; - BinarySource_IMPLEMENTATION; -} LoadedFile; +typedef enum { + LF_OK, /* file loaded successfully */ + LF_TOO_BIG, /* file didn't fit in buffer */ + LF_ERROR, /* error from stdio layer */ +} LoadFileStatus; -static void lf_free(LoadedFile *lf) +static LoadedFile *lf_new(size_t max_size) { + LoadedFile *lf = snew_plus(LoadedFile, max_size); + lf->data = snew_plus_get_aux(lf); + lf->len = 0; + lf->max_size = max_size; + return lf; +} + +void lf_free(LoadedFile *lf) +{ + smemclr(lf->data, lf->max_size); smemclr(lf, sizeof(LoadedFile)); sfree(lf); } -static LoadedFile *lf_load(const Filename *filename, size_t max_size, - bool tolerate_overflow) +static LoadFileStatus lf_load_fp(LoadedFile *lf, FILE *fp) { - FILE *fp = f_open(filename, "rb", false); - if (!fp) - return NULL; - - LoadedFile *lf = snew_plus(LoadedFile, max_size); - lf->data = snew_plus_get_aux(lf); lf->len = 0; - - while (lf->len < max_size) { - size_t retd = fread(lf->data + lf->len, 1, max_size - lf->len, fp); - if (ferror(fp)) { - lf_free(lf); - fclose(fp); - return NULL; - } + while (lf->len < lf->max_size) { + size_t retd = fread(lf->data + lf->len, 1, lf->max_size - lf->len, fp); + if (ferror(fp)) + return LF_ERROR; if (retd == 0) break; @@ -79,24 +80,70 @@ static LoadedFile *lf_load(const Filename *filename, size_t max_size, lf->len += retd; } - if (lf->len == max_size && !tolerate_overflow) { + LoadFileStatus status = LF_OK; + + if (lf->len == lf->max_size) { /* The file might be too long to fit in our fixed-size * structure. Try reading one more byte, to check. */ - if (fgetc(fp) != EOF) { - lf_free(lf); - fclose(fp); - return NULL; - } + if (fgetc(fp) != EOF) + status = LF_TOO_BIG; } - fclose(fp); BinarySource_INIT(lf, lf->data, lf->len); + + return status; +} + +static LoadFileStatus lf_load(LoadedFile *lf, const Filename *filename) +{ + FILE *fp = f_open(filename, "rb", false); + if (!fp) + return LF_ERROR; + + LoadFileStatus status = lf_load_fp(lf, fp); + fclose(fp); + return status; +} + +static inline bool lf_load_keyfile_helper(LoadFileStatus status, + const char **errptr) +{ + const char *error; + switch (status) { + case LF_OK: + return true; + case LF_TOO_BIG: + error = "file is too large to be a key file"; + break; + case LF_ERROR: + error = strerror(errno); + break; + default: + unreachable("bad status value in lf_load_keyfile_helper"); + } + if (errptr) + *errptr = error; + return false; +} + +LoadedFile *lf_load_keyfile(const Filename *filename, const char **errptr) +{ + LoadedFile *lf = lf_new(MAX_KEY_FILE_SIZE); + if (!lf_load_keyfile_helper(lf_load(lf, filename), errptr)) { + lf_free(lf); + return NULL; + } return lf; } -static LoadedFile *lf_load_keyfile(const Filename *filename) +LoadedFile *lf_load_keyfile_fp(FILE *fp, const char **errptr) { - return lf_load(filename, MAX_KEY_FILE_SIZE, false); + LoadedFile *lf = lf_new(MAX_KEY_FILE_SIZE); + if (!lf_load_keyfile_helper(lf_load_fp(lf, fp), errptr)) { + lf_free(lf); + return NULL; + } + return lf; } static int key_type_s(BinarySource *src); @@ -220,11 +267,9 @@ int rsa1_load_s(BinarySource *src, RSAKey *key, int rsa1_load_f(const Filename *filename, RSAKey *key, const char *passphrase, const char **errstr) { - LoadedFile *lf = lf_load_keyfile(filename); - if (!lf) { - *errstr = "can't open file"; + LoadedFile *lf = lf_load_keyfile(filename, errstr); + if (!lf) return false; - } int toret = rsa1_load_s(BinarySource_UPCAST(lf), key, passphrase, errstr); lf_free(lf); @@ -243,7 +288,7 @@ bool rsa1_encrypted_s(BinarySource *src, char **comment) bool rsa1_encrypted_f(const Filename *filename, char **comment) { - LoadedFile *lf = lf_load_keyfile(filename); + LoadedFile *lf = lf_load_keyfile(filename, NULL); if (!lf) return false; /* couldn't even open the file */ @@ -342,11 +387,9 @@ int rsa1_loadpub_s(BinarySource *src, BinarySink *bs, int rsa1_loadpub_f(const Filename *filename, BinarySink *bs, char **commentptr, const char **errorstr) { - LoadedFile *lf = lf_load_keyfile(filename); - if (!lf) { - *errorstr = "can't open file"; + LoadedFile *lf = lf_load_keyfile(filename, errorstr); + if (!lf) return 0; - } int toret = rsa1_loadpub_s(BinarySource_UPCAST(lf), bs, commentptr, errorstr); @@ -883,11 +926,9 @@ ssh2_userkey *ppk_load_s(BinarySource *src, const char *passphrase, ssh2_userkey *ppk_load_f(const Filename *filename, const char *passphrase, const char **errorstr) { - LoadedFile *lf = lf_load_keyfile(filename); - if (!lf) { + LoadedFile *lf = lf_load_keyfile(filename, errorstr); + if (!lf) *errorstr = "can't open file"; - return NULL; - } ssh2_userkey *toret = ppk_load_s(BinarySource_UPCAST(lf), passphrase, errorstr); @@ -1191,11 +1232,9 @@ bool ppk_loadpub_s(BinarySource *src, char **algorithm, BinarySink *bs, bool ppk_loadpub_f(const Filename *filename, char **algorithm, BinarySink *bs, char **commentptr, const char **errorstr) { - LoadedFile *lf = lf_load_keyfile(filename); - if (!lf) { - *errorstr = "can't open file"; + LoadedFile *lf = lf_load_keyfile(filename, errorstr); + if (!lf) return false; - } bool toret = ppk_loadpub_s(BinarySource_UPCAST(lf), algorithm, bs, commentptr, errorstr); @@ -1253,7 +1292,7 @@ bool ppk_encrypted_s(BinarySource *src, char **commentptr) bool ppk_encrypted_f(const Filename *filename, char **commentptr) { - LoadedFile *lf = lf_load_keyfile(filename); + LoadedFile *lf = lf_load_keyfile(filename, NULL); if (!lf) { if (commentptr) *commentptr = NULL; @@ -1648,9 +1687,11 @@ static int key_type_s(BinarySource *src) int key_type(const Filename *filename) { - LoadedFile *lf = lf_load(filename, 1024, true); - if (!lf) + LoadedFile *lf = lf_new(1024); + if (lf_load(lf, filename) == LF_ERROR) { + lf_free(lf); return SSH_KEYTYPE_UNOPENABLE; + } int toret = key_type_s(BinarySource_UPCAST(lf)); lf_free(lf);