From 41fcafd20c6dc2e1cc61e0b3d4e77936ab3eec61 Mon Sep 17 00:00:00 2001 From: Nicolas Williams Date: Thu, 11 Jul 2019 21:31:41 -0500 Subject: [PATCH] hx509: add hx509_certs_destroy() --- lib/hx509/file.c | 87 ++++++++++++++++++++++++++++++++++ lib/hx509/hx_locl.h | 8 ++++ lib/hx509/keyset.c | 27 +++++++++++ lib/hx509/ks_dir.c | 1 + lib/hx509/ks_file.c | 18 +++++-- lib/hx509/ks_keychain.c | 1 + lib/hx509/ks_mem.c | 3 +- lib/hx509/ks_null.c | 1 + lib/hx509/ks_p11.c | 1 + lib/hx509/ks_p12.c | 10 +++- lib/hx509/libhx509-exports.def | 1 + lib/hx509/version-script.map | 1 + 12 files changed, 154 insertions(+), 5 deletions(-) diff --git a/lib/hx509/file.c b/lib/hx509/file.c index bc4c58c6f..c2478ff35 100644 --- a/lib/hx509/file.c +++ b/lib/hx509/file.c @@ -300,3 +300,90 @@ hx509_pem_read(hx509_context context, return ret; } + +/* + * On modern systems there's no such thing as scrubbing a file. Not this way + * anyways. However, for now we'll cargo-cult this along just as in lib/krb5. + */ +static int +scrub_file(int fd, ssize_t sz) +{ + char buf[128]; + + memset(buf, 0, sizeof(buf)); + while (sz > 0) { + ssize_t tmp; + size_t wr = sizeof(buf) > sz ? (size_t)sz : sizeof(buf); + + tmp = write(fd, buf, wr); + if (tmp == -1) + return errno; + sz -= tmp; + } +#ifdef _MSC_VER + return _commit(fd); +#else + return fsync(fd); +#endif +} + +int +_hx509_erase_file(hx509_context context, const char *fn) +{ + struct stat sb1, sb2; + int ret; + int fd; + + if (fn == NULL) + return 0; + + /* This is based on _krb5_erase_file(), minus file locking */ + ret = lstat(fn, &sb1); + if (ret == -1 && errno == ENOENT) + return 0; + if (ret == -1) { + hx509_set_error_string(context, 0, ret, "hx509_certs_destroy: " + "stat of \"%s\": %s", fn, strerror(ret)); + return errno; + } + + fd = open(fn, O_RDWR | O_BINARY | O_CLOEXEC | O_NOFOLLOW); + rk_cloexec(fd); + if (ret == -1 && errno == ENOENT) + return 0; + if (ret == -1) + return errno; + + if (unlink(fn) < 0) { + ret = errno; + (void) close(fd); + hx509_set_error_string(context, 0, ret, "hx509_certs_destroy: " + "unlinking \"%s\": %s", fn, strerror(ret)); + return ret; + } + + /* check TOCTOU, symlinks */ + ret = fstat(fd, &sb2); + if (ret < 0) { + ret = errno; + hx509_set_error_string(context, 0, ret, "hx509_certs_destroy: " + "fstat of %d, \"%s\": %s", fd, fn, + strerror(ret)); + (void) close(fd); + return ret; + } + if (sb1.st_dev != sb2.st_dev || sb1.st_ino != sb2.st_ino) { + (void) close(fd); + return EPERM; + } + + /* there are still hard links to this file */ + if (sb2.st_nlink != 0) { + close(fd); + return 0; + } + + ret = scrub_file(fd, sb2.st_size); + (void) close(fd); + return ret; +} diff --git a/lib/hx509/hx_locl.h b/lib/hx509/hx_locl.h index 44d241f35..8603cb3ed 100644 --- a/lib/hx509/hx_locl.h +++ b/lib/hx509/hx_locl.h @@ -70,6 +70,13 @@ #include +#ifndef O_CLOEXEC +#define O_CLOEXEC 0 +#endif +#ifndef O_BINARY +#define O_BINARY 0 +#endif + /* * We use OpenSSL for EC, but to do this we need to disable cross-references * between OpenSSL and hcrypto bn.h and such. Source files that use OpenSSL EC @@ -180,6 +187,7 @@ struct hx509_keyset_ops { void *, int (*)(void *, const char *), void *); int (*getkeys)(hx509_context, hx509_certs, void *, hx509_private_key **); int (*addkey)(hx509_context, hx509_certs, void *, hx509_private_key); + int (*destroy)(hx509_context, hx509_certs, void *); }; struct _hx509_password { diff --git a/lib/hx509/keyset.c b/lib/hx509/keyset.c index 7d35d1c2a..536d0e057 100644 --- a/lib/hx509/keyset.c +++ b/lib/hx509/keyset.c @@ -168,6 +168,33 @@ hx509_certs_init(hx509_context context, return 0; } +/** + * Destroys and frees a hx509 certificate store. + * + * @param context A hx509 context + * @param certs A store to destroy + * + * @return Returns an hx509 error code. + * + * @ingroup hx509_keyset + */ + +HX509_LIB_FUNCTION int HX509_LIB_CALL +hx509_certs_destroy(hx509_context context, + hx509_certs *certs) +{ + int ret = 0; + + if (*certs) { + if ((*certs)->ops->destroy) + ret = ((*certs)->ops->destroy)(context, *certs, (*certs)->ops_data); + else + ret = ENOTSUP; + } + hx509_certs_free(certs); + return ret; +} + /** * Write the certificate store to stable storage. * diff --git a/lib/hx509/ks_dir.c b/lib/hx509/ks_dir.c index 46e91f255..3bc99f2dc 100644 --- a/lib/hx509/ks_dir.c +++ b/lib/hx509/ks_dir.c @@ -220,6 +220,7 @@ static struct hx509_keyset_ops keyset_dir = { dir_iter_end, NULL, NULL, + NULL, NULL }; diff --git a/lib/hx509/ks_file.c b/lib/hx509/ks_file.c index 6e8f5b1aa..eb193a445 100644 --- a/lib/hx509/ks_file.c +++ b/lib/hx509/ks_file.c @@ -664,6 +664,15 @@ file_addkey(hx509_context context, return _hx509_certs_keys_add(context, ksf->certs, key); } +static int +file_destroy(hx509_context context, + hx509_certs certs, + void *data) +{ + struct ks_file *ksf = data; + return _hx509_erase_file(context, ksf->fn); +} + static struct hx509_keyset_ops keyset_file = { "FILE", 0, @@ -677,7 +686,8 @@ static struct hx509_keyset_ops keyset_file = { file_iter_end, NULL, file_getkeys, - file_addkey + file_addkey, + file_destroy }; static struct hx509_keyset_ops keyset_pemfile = { @@ -693,7 +703,8 @@ static struct hx509_keyset_ops keyset_pemfile = { file_iter_end, NULL, file_getkeys, - file_addkey + file_addkey, + file_destroy }; static struct hx509_keyset_ops keyset_derfile = { @@ -709,7 +720,8 @@ static struct hx509_keyset_ops keyset_derfile = { file_iter_end, NULL, file_getkeys, - file_addkey + file_addkey, + file_destroy }; diff --git a/lib/hx509/ks_keychain.c b/lib/hx509/ks_keychain.c index e2eefbf84..44dca03a3 100644 --- a/lib/hx509/ks_keychain.c +++ b/lib/hx509/ks_keychain.c @@ -599,6 +599,7 @@ struct hx509_keyset_ops keyset_keychain = { keychain_iter_end, NULL, NULL, + NULL, NULL }; diff --git a/lib/hx509/ks_mem.c b/lib/hx509/ks_mem.c index 82a8c3cba..f325d12be 100644 --- a/lib/hx509/ks_mem.c +++ b/lib/hx509/ks_mem.c @@ -213,7 +213,8 @@ static struct hx509_keyset_ops keyset_mem = { mem_iter_end, NULL, mem_getkeys, - mem_addkey + mem_addkey, + NULL }; HX509_LIB_FUNCTION void HX509_LIB_CALL diff --git a/lib/hx509/ks_null.c b/lib/hx509/ks_null.c index 69d58e83f..c241d30f3 100644 --- a/lib/hx509/ks_null.c +++ b/lib/hx509/ks_null.c @@ -90,6 +90,7 @@ struct hx509_keyset_ops keyset_null = { null_iter_end, NULL, NULL, + NULL, NULL }; diff --git a/lib/hx509/ks_p11.c b/lib/hx509/ks_p11.c index cfe62e869..1826282b2 100644 --- a/lib/hx509/ks_p11.c +++ b/lib/hx509/ks_p11.c @@ -1208,6 +1208,7 @@ static struct hx509_keyset_ops keyset_pkcs11 = { p11_iter_end, p11_printinfo, NULL, + NULL, NULL }; diff --git a/lib/hx509/ks_p12.c b/lib/hx509/ks_p12.c index b4f66cb13..2838e8129 100644 --- a/lib/hx509/ks_p12.c +++ b/lib/hx509/ks_p12.c @@ -697,6 +697,13 @@ p12_iter_end(hx509_context context, return hx509_certs_end_seq(context, p12->certs, cursor); } +static int +p12_destroy(hx509_context context, hx509_certs certs, void *data) +{ + struct ks_pkcs12 *p12 = data; + return _hx509_erase_file(context, p12->fn); +} + static struct hx509_keyset_ops keyset_pkcs12 = { "PKCS12", 0, @@ -710,7 +717,8 @@ static struct hx509_keyset_ops keyset_pkcs12 = { p12_iter_end, NULL, NULL, - NULL + NULL, + p12_destroy }; HX509_LIB_FUNCTION void HX509_LIB_CALL diff --git a/lib/hx509/libhx509-exports.def b/lib/hx509/libhx509-exports.def index 328936d67..80db8c7e5 100644 --- a/lib/hx509/libhx509-exports.def +++ b/lib/hx509/libhx509-exports.def @@ -92,6 +92,7 @@ EXPORTS hx509_cert_set_friendly_name hx509_certs_add hx509_certs_append + hx509_certs_destroy hx509_certs_end_seq hx509_certs_ref hx509_certs_filter diff --git a/lib/hx509/version-script.map b/lib/hx509/version-script.map index 621a22d60..df04003ec 100644 --- a/lib/hx509/version-script.map +++ b/lib/hx509/version-script.map @@ -88,6 +88,7 @@ HEIMDAL_X509_1.2 { hx509_certs_add; hx509_certs_append; hx509_certs_end_seq; + hx509_certs_destroy; hx509_certs_ref; hx509_certs_filter; hx509_certs_find;