From f358b4d7c65b1f9cc3e88544c1bdce5af97f501f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alejandro=20L=C3=B3pez?= Date: Wed, 29 Nov 2023 15:41:37 +0100 Subject: [PATCH] KCM: Securely erase memory used for secrets Make sure all the memory blocks allocated dynamically or statically by KCM to store credentials and messages (which might include credentials) are erased by calling sss_erase_mem_securely() or sss_erase_talloc_mem_securely() before being freed. --- Makefile.am | 1 + contrib/sssd.spec.in | 1 + src/responder/kcm/kcm_renew.c | 3 + src/responder/kcm/kcmsrv_ccache.c | 18 ++ src/responder/kcm/kcmsrv_ccache_binary.c | 2 +- src/responder/kcm/kcmsrv_ccache_secdb.c | 5 +- src/responder/kcm/kcmsrv_ops.c | 12 +- src/responder/kcm/secrets/secrets.c | 75 +++++++- src/tests/cmocka/test_iobuf.c | 225 ++++++++++++++++++++++- src/util/sss_iobuf.c | 33 +++- src/util/sss_iobuf.h | 21 ++- 11 files changed, 363 insertions(+), 33 deletions(-) diff --git a/Makefile.am b/Makefile.am index 1846ec79e93..af5f5eaf254 100644 --- a/Makefile.am +++ b/Makefile.am @@ -3726,6 +3726,7 @@ test_iobuf_CFLAGS = \ test_iobuf_LDADD = \ $(CMOCKA_LIBS) \ $(SSSD_LIBS) \ + $(SSSD_INTERNAL_LTLIBS) \ $(NULL) test_confdb_SOURCES = \ diff --git a/contrib/sssd.spec.in b/contrib/sssd.spec.in index f04469f2475..a1b2cf8250b 100644 --- a/contrib/sssd.spec.in +++ b/contrib/sssd.spec.in @@ -167,6 +167,7 @@ BuildRequires: systemd-devel BuildRequires: systemtap-sdt-devel BuildRequires: uid_wrapper BuildRequires: po4a +BuildRequires: valgrind-devel %if %{build_subid} BuildRequires: shadow-utils-subid-devel %endif diff --git a/src/responder/kcm/kcm_renew.c b/src/responder/kcm/kcm_renew.c index 39e9470fa22..6aea89f614e 100644 --- a/src/responder/kcm/kcm_renew.c +++ b/src/responder/kcm/kcm_renew.c @@ -666,6 +666,9 @@ errno_t kcm_renew_all_tgts(TALLOC_CTX *mem_ctx, kcm_creds_check_times(tmp_ctx, renew_tgt_ctx, extracted_creds[j], cc, client_name); + + /* Once used, clean the credentials from memory. */ + sss_erase_talloc_mem_securely(extracted_creds[j]); } } diff --git a/src/responder/kcm/kcmsrv_ccache.c b/src/responder/kcm/kcmsrv_ccache.c index 6e4ea64e013..c2659851e00 100644 --- a/src/responder/kcm/kcmsrv_ccache.c +++ b/src/responder/kcm/kcmsrv_ccache.c @@ -273,6 +273,21 @@ static krb5_creds *kcm_cred_to_krb5(krb5_context kctx, struct kcm_cred *kcm_crd) } #endif +static void securely_erase_krb5_data(krb5_data *data) +{ + if (data != NULL) { + sss_erase_mem_securely(data->data, data->length); + } +} + +static void securely_erase_krb5_creds(krb5_creds *cred) +{ + if (cred != NULL) { + securely_erase_krb5_data(&cred->ticket); + securely_erase_krb5_data(&cred->second_ticket); + } +} + static errno_t kcm_cc_remove_duplicates(struct kcm_ccache *cc, struct kcm_cred *kcm_crd) @@ -308,6 +323,7 @@ kcm_cc_remove_duplicates(struct kcm_ccache *cc, } bret = sss_krb5_creds_compare(kctx, kcrd, kcrd_cc); + securely_erase_krb5_creds(kcrd_cc); krb5_free_creds(kctx, kcrd_cc); if (!bret) { continue; @@ -320,6 +336,7 @@ kcm_cc_remove_duplicates(struct kcm_ccache *cc, ret = EOK; done: + securely_erase_krb5_creds(kcrd); krb5_free_creds(kctx, kcrd); krb5_free_context(kctx); @@ -380,6 +397,7 @@ static int kcm_cc_unmarshal_destructor(krb5_creds **creds) } for (i = 0; creds[i] != NULL; i++) { + securely_erase_krb5_creds(creds[i]); krb5_free_creds(krb_ctx, creds[i]); } diff --git a/src/responder/kcm/kcmsrv_ccache_binary.c b/src/responder/kcm/kcmsrv_ccache_binary.c index cc4191a1161..7efe937447d 100644 --- a/src/responder/kcm/kcmsrv_ccache_binary.c +++ b/src/responder/kcm/kcmsrv_ccache_binary.c @@ -109,7 +109,7 @@ errno_t kcm_ccache_to_sec_input_binary(TALLOC_CTX *mem_ctx, struct sss_iobuf *buf; errno_t ret; - buf = sss_iobuf_init_empty(mem_ctx, sizeof(krb5_principal_data), 0); + buf = sss_iobuf_init_empty(mem_ctx, sizeof(krb5_principal_data), 0, true); if (buf == NULL) { return ENOMEM; } diff --git a/src/responder/kcm/kcmsrv_ccache_secdb.c b/src/responder/kcm/kcmsrv_ccache_secdb.c index ff5e6e24c38..a5d462db095 100644 --- a/src/responder/kcm/kcmsrv_ccache_secdb.c +++ b/src/responder/kcm/kcmsrv_ccache_secdb.c @@ -61,7 +61,7 @@ static errno_t sec_get(TALLOC_CTX *mem_ctx, goto done; } - buf = sss_iobuf_init_steal(tmp_ctx, data, len); + buf = sss_iobuf_init_steal(tmp_ctx, data, len, true); if (buf == NULL) { DEBUG(SSSDBG_CRIT_FAILURE, "Cannot init the iobuf\n"); ret = EIO; @@ -683,7 +683,8 @@ static struct tevent_req *ccdb_secdb_set_default_send(TALLOC_CTX *mem_ctx, iobuf = sss_iobuf_init_readonly(state, (const uint8_t *) uuid_str, - UUID_STR_SIZE); + UUID_STR_SIZE, + false); if (iobuf == NULL) { ret = ENOMEM; goto immediate; diff --git a/src/responder/kcm/kcmsrv_ops.c b/src/responder/kcm/kcmsrv_ops.c index 8935a7932b0..00a056473b7 100644 --- a/src/responder/kcm/kcmsrv_ops.c +++ b/src/responder/kcm/kcmsrv_ops.c @@ -110,7 +110,8 @@ struct tevent_req *kcm_cmd_send(TALLOC_CTX *mem_ctx, state->reply = sss_iobuf_init_empty(state, KCM_PACKET_INITIAL_SIZE, - KCM_PACKET_MAX_SIZE); + KCM_PACKET_MAX_SIZE, + true); if (state->reply == NULL) { ret = ENOMEM; goto immediate; @@ -138,7 +139,8 @@ struct tevent_req *kcm_cmd_send(TALLOC_CTX *mem_ctx, state->op_ctx->input = sss_iobuf_init_readonly(state->op_ctx, input->data, - input->length); + input->length, + true); if (state->op_ctx->input == NULL) { ret = ENOMEM; goto immediate; @@ -155,7 +157,8 @@ struct tevent_req *kcm_cmd_send(TALLOC_CTX *mem_ctx, state->op_ctx->reply = sss_iobuf_init_empty( state, KCM_PACKET_INITIAL_SIZE, - KCM_PACKET_MAX_SIZE - 2*sizeof(uint32_t)); + KCM_PACKET_MAX_SIZE - 2*sizeof(uint32_t), + true); if (state->op_ctx->reply == NULL) { ret = ENOMEM; goto immediate; @@ -882,7 +885,8 @@ static struct tevent_req *kcm_op_store_send(TALLOC_CTX *mem_ctx, state->cred_blob = sss_iobuf_init_empty(state, creds_len, - creds_len); + creds_len, + true); if (state->cred_blob == NULL) { ret = ENOMEM; goto immediate; diff --git a/src/responder/kcm/secrets/secrets.c b/src/responder/kcm/secrets/secrets.c index a37edcc2a51..b785b4216b2 100644 --- a/src/responder/kcm/secrets/secrets.c +++ b/src/responder/kcm/secrets/secrets.c @@ -57,6 +57,33 @@ static char *local_dn_to_path(TALLOC_CTX *mem_ctx, struct ldb_dn *basedn, struct ldb_dn *dn); +static void db_result_erase_message_securely(struct ldb_message *msg, const char *attr) +{ + int i; + struct ldb_message_element *element; + struct ldb_val *value; + + element = ldb_msg_find_element(msg, attr); + if (element != NULL) { + /* If the element exists, overwrite every single value */ + for (i = 0; i < element->num_values; i++) { + value = &element->values[i]; + + sss_erase_mem_securely(value->data, value->length); + value->length = 0; + } + } +} + +static void db_result_erase_securely(struct ldb_result *res, const char *attr) +{ + int i; + + for (i = 0; i < res->count; i++) { + db_result_erase_message_securely(res->msgs[i], attr); + } +} + static int local_db_check_containers(TALLOC_CTX *mem_ctx, struct sss_sec_ctx *sec_ctx, struct ldb_dn *leaf_dn) @@ -198,7 +225,8 @@ static errno_t get_secret_expiration_time(uint8_t *key, size_t key_length, struct cli_creds client = {}; struct kcm_ccache *cc; struct sss_iobuf *iobuf; - krb5_creds **cred_list, **cred; + krb5_creds **cred_list = NULL; + krb5_creds **cred; const char *key_str; if (_expiration == NULL) { @@ -216,7 +244,7 @@ static errno_t get_secret_expiration_time(uint8_t *key, size_t key_length, goto done; } - iobuf = sss_iobuf_init_readonly(tmp_ctx, sec, sec_length); + iobuf = sss_iobuf_init_readonly(tmp_ctx, sec, sec_length, true); if (iobuf == NULL) { ret = ENOMEM; goto done; @@ -233,11 +261,14 @@ static errno_t get_secret_expiration_time(uint8_t *key, size_t key_length, goto done; } + /* Look for the (first) expiration time while securely deleting the + * unmarshalled credentials. */ for (cred = cred_list; *cred != NULL; cred++) { - if ((*cred)->times.endtime != 0) { + if (expiration == 0 && (*cred)->times.endtime != 0) { expiration = (time_t) (*cred)->times.endtime; - break; } + sss_erase_talloc_mem_securely(*cred); + *cred = NULL; } *_expiration = expiration; @@ -395,6 +426,9 @@ static int local_db_check_peruid_number_of_secrets(TALLOC_CTX *mem_ctx, ret = EOK; done: + if (res != NULL) { + db_result_erase_securely(res, SEC_ATTR_SECRET); + } talloc_free(tmp_ctx); return ret; } @@ -820,7 +854,7 @@ errno_t sss_sec_list(TALLOC_CTX *mem_ctx, size_t *_num_keys) { TALLOC_CTX *tmp_ctx; - static const char *attrs[] = { SEC_ATTR_SECRET, NULL }; + static const char *attrs[] = { NULL }; struct ldb_result *res; char **keys; int ret; @@ -884,8 +918,8 @@ errno_t sss_sec_get(TALLOC_CTX *mem_ctx, { TALLOC_CTX *tmp_ctx; static const char *attrs[] = { SEC_ATTR_SECRET, NULL }; - struct ldb_result *res; - const struct ldb_val *attr_secret; + struct ldb_result *res = NULL; + const struct ldb_val *attr_secret = NULL; int ret; if (req == NULL || _secret == NULL) { @@ -936,6 +970,7 @@ errno_t sss_sec_get(TALLOC_CTX *mem_ctx, ret = ENOMEM; goto done; } + talloc_set_destructor((void *) *_secret, sss_erase_talloc_mem_securely); if (_secret_len) { *_secret_len = attr_secret->length; @@ -944,6 +979,12 @@ errno_t sss_sec_get(TALLOC_CTX *mem_ctx, ret = EOK; done: + if (attr_secret != NULL) { + sss_erase_mem_securely(attr_secret->data, attr_secret->length); + } + if (res != NULL) { + db_result_erase_securely(res, SEC_ATTR_SECRET); + } talloc_free(tmp_ctx); return ret; } @@ -953,7 +994,8 @@ errno_t sss_sec_put(struct sss_sec_req *req, size_t secret_len) { struct ldb_message *msg; - struct ldb_val secret_val; + struct ldb_val secret_val = { .data = NULL }; + bool erase_msg = false; int ret; if (req == NULL || secret == NULL) { @@ -1016,6 +1058,7 @@ errno_t sss_sec_put(struct sss_sec_req *req, ret, sss_strerror(ret)); goto done; } + erase_msg = true; ret = ldb_msg_add_fmt(msg, SEC_ATTR_CTIME, "%lu", time(NULL)); if (ret != EOK) { @@ -1041,6 +1084,12 @@ errno_t sss_sec_put(struct sss_sec_req *req, ret = EOK; done: + if (secret_val.data != NULL) { + sss_erase_mem_securely(secret_val.data, secret_val.length); + } + if (erase_msg) { + db_result_erase_message_securely(msg, SEC_ATTR_SECRET); + } talloc_free(msg); return ret; } @@ -1050,7 +1099,8 @@ errno_t sss_sec_update(struct sss_sec_req *req, size_t secret_len) { struct ldb_message *msg; - struct ldb_val secret_val; + struct ldb_val secret_val = { .data = NULL }; + bool erase_msg = false; int ret; if (req == NULL || secret == NULL) { @@ -1122,6 +1172,7 @@ errno_t sss_sec_update(struct sss_sec_req *req, ret = EIO; goto done; } + erase_msg = true; ret = ldb_modify(req->sctx->ldb, msg); if (ret == LDB_ERR_NO_SUCH_OBJECT) { @@ -1138,6 +1189,12 @@ errno_t sss_sec_update(struct sss_sec_req *req, ret = EOK; done: + if (secret_val.data != NULL) { + sss_erase_mem_securely(secret_val.data, secret_val.length); + } + if (erase_msg) { + db_result_erase_message_securely(msg, SEC_ATTR_SECRET); + } talloc_free(msg); return ret; } diff --git a/src/tests/cmocka/test_iobuf.c b/src/tests/cmocka/test_iobuf.c index 796db514dac..ecc36473980 100644 --- a/src/tests/cmocka/test_iobuf.c +++ b/src/tests/cmocka/test_iobuf.c @@ -18,6 +18,8 @@ You should have received a copy of the GNU General Public License along with this program. If not, see . */ +#define _GNU_SOURCE /* For memmem() */ + #include #include #include @@ -26,10 +28,211 @@ #include #include #include +#include #include +#include +#include #include "util/sss_iobuf.h" #include "util/util.h" +#include "tests/common.h" + + +static void copy_heap_to_file(int out, int in, off_t start, off_t end) +{ + static char buffer[5*1024*1024]; + off_t pos; + ssize_t bytes_to_write; + ssize_t bytes_written; + size_t size = end - start; + + pos = lseek(in, start, SEEK_SET); + assert_int_equal(pos, start); + + while (size > 0) { + bytes_to_write = read(in, buffer, MIN(size, sizeof(buffer))); + assert_int_not_equal(bytes_to_write, -1); + + while (bytes_to_write > 0) { + bytes_written = write(out, buffer, bytes_to_write); + assert_int_not_equal(bytes_written, -1); + + bytes_to_write -= bytes_written; + size -= bytes_written; + } + } +} + +static void read_heap_to_file(const char *output) +{ +# define LINE_SIZE 1000 +# define FIELD_SIZE 20 + FILE *maps; + int mem; + int out; + char line[LINE_SIZE]; + char type[FIELD_SIZE + 1]; + char start_str[FIELD_SIZE + 1]; + char end_str[FIELD_SIZE + 1]; + char *end_ptr; + off_t start = 0; + off_t end = 0; + int items; + + + maps = fopen("/proc/self/maps", "r"); + assert_non_null(maps); + + mem = open("/proc/self/mem", O_RDONLY); + assert_int_not_equal(mem, -1); + + out = open(output, O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR); + assert_int_not_equal(out, -1); + + while (fgets(line, LINE_SIZE, maps) != NULL) { + errno = 0; + items = sscanf(line, + "%" AS_STR(FIELD_SIZE) "[0-9a-fA-F]-%" AS_STR(FIELD_SIZE) + "[0-9a-fA-F] %*[rwxp-] %*[0-9a-fA-F] %*d:%*d %*d %" + AS_STR(FIELD_SIZE) "s", + start_str, end_str, type); + if (errno == 0 && items == 3 && strcmp(type, "[heap]") == 0) { + start = strtoul(start_str, &end_ptr, 16); + assert_int_equal(*end_ptr, '\0'); + end = strtoul(end_str, &end_ptr, 16); + assert_int_equal(*end_ptr, '\0'); + + copy_heap_to_file(out, mem, start, end); + } + } + close(out); + close(mem); + fclose(maps); +} + + +static void map_file(const char *filename, void **_map, size_t *_size) +{ + int fd; + int res; + void *map; + struct stat stat; + + + fd = open(filename, O_RDONLY); + assert_int_not_equal(fd, -1); + + res = fstat(fd, &stat); + assert_int_equal(res, 0); + + map = mmap(NULL, stat.st_size, PROT_READ, MAP_PRIVATE, fd, 0); + assert_int_not_equal(map, MAP_FAILED); + + close(fd); + + *_map = map; + *_size = stat.st_size; +} + +static void check_presence(const char *filename, + const uint8_t *data1, size_t size1, bool present1, + const uint8_t *data2, size_t size2, bool present2) +{ + void *pos; + void *map; + size_t map_size; + + map_file(filename, &map, &map_size); + + pos = memmem(map, map_size, data1, size1); + if (present1) { + assert_non_null(pos); + } else { + assert_null(pos); + } + + if (data2 != NULL) { + pos = memmem(map, map_size, data2, size2); + if (present2) { + assert_non_null(pos); + } else { + assert_null(pos); + } + } + + munmap(map, map_size); +} + + +static int test_sss_iobuf_secure_teardown(void **state) +{ + unlink("./heap.bin.1"); + unlink("./heap.bin.2"); + unlink("./heap.bin.3"); + unlink("./heap.bin.4"); + unlink("./heap.bin.5"); + + return 0; +} + +static void test_sss_iobuf_secure(void **state) +{ + static const uint8_t secret[] = "=== This is the secret to hide ==="; + static const uint8_t no_secret[] = "=== This is no secret ==="; + TALLOC_CTX *mem_ctx; + struct sss_iobuf *iobuf_secret; + struct sss_iobuf *iobuf_secret_2; + struct sss_iobuf *iobuf_nosecret; + + /* Valgrind interferes with this test by somehow making disappear the heap. + * So don't run it on Valgrind. */ + if (RUNNING_ON_VALGRIND) { + return; + } + + + mem_ctx = talloc_new(NULL); + assert_non_null(mem_ctx); + + iobuf_secret = sss_iobuf_init_readonly(mem_ctx, secret, sizeof(secret), true); + assert_non_null(iobuf_secret); + iobuf_nosecret = sss_iobuf_init_readonly(mem_ctx, no_secret, sizeof(no_secret), false); + assert_non_null(iobuf_nosecret); + read_heap_to_file("./heap.bin.1"); + + talloc_free(iobuf_secret); + read_heap_to_file("./heap.bin.2"); + + iobuf_secret = sss_iobuf_init_readonly(mem_ctx, secret, sizeof(secret), true); + assert_non_null(iobuf_secret); + read_heap_to_file("./heap.bin.3"); + + iobuf_secret_2 = sss_iobuf_init_steal(mem_ctx, sss_iobuf_get_data(iobuf_secret), + sss_iobuf_get_size(iobuf_secret), true); + assert_non_null(iobuf_secret_2); + talloc_free(iobuf_secret); + read_heap_to_file("./heap.bin.4"); + + talloc_free(mem_ctx); + read_heap_to_file("./heap.bin.5"); + + check_presence("./heap.bin.1", + secret, sizeof(secret), true, + no_secret, sizeof(no_secret), true); + check_presence("./heap.bin.2", + secret, sizeof(secret), false, + no_secret, sizeof(no_secret), true); + check_presence("./heap.bin.3", + secret, sizeof(secret), true, + no_secret, sizeof(no_secret), true); + check_presence("./heap.bin.4", + secret, sizeof(secret), true, + no_secret, sizeof(no_secret), true); + check_presence("./heap.bin.5", + secret, sizeof(secret), false, + NULL, 0, false); +} + static void test_sss_iobuf_read(void **state) { @@ -39,7 +242,7 @@ static void test_sss_iobuf_read(void **state) size_t nread; struct sss_iobuf *rb; - rb = sss_iobuf_init_readonly(NULL, buffer, sizeof(buffer)); + rb = sss_iobuf_init_readonly(NULL, buffer, sizeof(buffer), false); assert_non_null(rb); ret = sss_iobuf_read(rb, 5, readbuf, &nread); @@ -91,7 +294,7 @@ static void test_sss_iobuf_write(void **state) errno_t ret; /* Exactly fill the capacity */ - wb = sss_iobuf_init_empty(NULL, hwlen, hwlen); + wb = sss_iobuf_init_empty(NULL, hwlen, hwlen, false); assert_non_null(wb); ret = sss_iobuf_write_len(wb, (uint8_t *) discard_const("Hello world"), @@ -100,7 +303,8 @@ static void test_sss_iobuf_write(void **state) rb = sss_iobuf_init_readonly(NULL, sss_iobuf_get_data(wb), - sss_iobuf_get_len(wb)); + sss_iobuf_get_len(wb), + false); talloc_free(wb); assert_non_null(rb); @@ -111,7 +315,7 @@ static void test_sss_iobuf_write(void **state) talloc_zfree(rb); /* Overflow the capacity by one */ - wb = sss_iobuf_init_empty(NULL, hwlen, hwlen); + wb = sss_iobuf_init_empty(NULL, hwlen, hwlen, false); assert_non_null(wb); ret = sss_iobuf_write_len(wb, (uint8_t *) discard_const("Hello world!"), @@ -120,7 +324,7 @@ static void test_sss_iobuf_write(void **state) talloc_zfree(wb); /* Test resizing exactly up to capacity in several writes */ - wb = sss_iobuf_init_empty(NULL, 2, hwlen); + wb = sss_iobuf_init_empty(NULL, 2, hwlen, false); assert_non_null(wb); ret = sss_iobuf_write_len(wb, @@ -134,7 +338,8 @@ static void test_sss_iobuf_write(void **state) rb = sss_iobuf_init_readonly(NULL, sss_iobuf_get_data(wb), - sss_iobuf_get_len(wb)); + sss_iobuf_get_len(wb), + false); talloc_free(wb); assert_non_null(rb); @@ -145,7 +350,7 @@ static void test_sss_iobuf_write(void **state) talloc_zfree(rb); /* Overflow the capacity during a resize by one */ - wb = sss_iobuf_init_empty(NULL, 2, hwlen); + wb = sss_iobuf_init_empty(NULL, 2, hwlen, false); assert_non_null(wb); ret = sss_iobuf_write_len(wb, @@ -159,7 +364,7 @@ static void test_sss_iobuf_write(void **state) talloc_zfree(wb); /* Test allocating an unlimited buffer */ - wb = sss_iobuf_init_empty(NULL, 2, 0); + wb = sss_iobuf_init_empty(NULL, 2, 0, false); assert_non_null(wb); ret = sss_iobuf_write_len(wb, @@ -173,7 +378,8 @@ static void test_sss_iobuf_write(void **state) rb = sss_iobuf_init_readonly(NULL, sss_iobuf_get_data(wb), - sss_iobuf_get_len(wb)); + sss_iobuf_get_len(wb), + false); talloc_free(wb); assert_non_null(rb); @@ -189,6 +395,7 @@ int main(void) const struct CMUnitTest tests[] = { cmocka_unit_test(test_sss_iobuf_read), cmocka_unit_test(test_sss_iobuf_write), + cmocka_unit_test_setup_teardown(test_sss_iobuf_secure, NULL, test_sss_iobuf_secure_teardown), }; return cmocka_run_group_tests(tests, NULL, NULL); diff --git a/src/util/sss_iobuf.c b/src/util/sss_iobuf.c index b4dbb7cd90d..f64ce8dfb7a 100644 --- a/src/util/sss_iobuf.c +++ b/src/util/sss_iobuf.c @@ -16,11 +16,14 @@ struct sss_iobuf { size_t dp; /* Data pointer */ size_t size; /* Current data buffer size */ size_t capacity; /* Maximum capacity */ + + bool secure; /* Securely erased before freeing it */ }; struct sss_iobuf *sss_iobuf_init_empty(TALLOC_CTX *mem_ctx, size_t size, - size_t capacity) + size_t capacity, + bool secure) { struct sss_iobuf *iobuf; uint8_t *buf; @@ -36,6 +39,10 @@ struct sss_iobuf *sss_iobuf_init_empty(TALLOC_CTX *mem_ctx, return NULL; } + if (secure) { + talloc_set_destructor((void *) buf, sss_erase_talloc_mem_securely); + } + if (capacity == 0) { capacity = SIZE_MAX / 2; } @@ -43,17 +50,19 @@ struct sss_iobuf *sss_iobuf_init_empty(TALLOC_CTX *mem_ctx, iobuf->data = buf; iobuf->size = size; iobuf->capacity = capacity; + iobuf->secure = secure; return iobuf; } struct sss_iobuf *sss_iobuf_init_readonly(TALLOC_CTX *mem_ctx, const uint8_t *data, - size_t size) + size_t size, + bool secure) { struct sss_iobuf *iobuf; - iobuf = sss_iobuf_init_empty(mem_ctx, size, size); + iobuf = sss_iobuf_init_empty(mem_ctx, size, size, secure); if (iobuf == NULL) { return NULL; } @@ -67,7 +76,8 @@ struct sss_iobuf *sss_iobuf_init_readonly(TALLOC_CTX *mem_ctx, struct sss_iobuf *sss_iobuf_init_steal(TALLOC_CTX *mem_ctx, uint8_t *data, - size_t size) + size_t size, + bool secure) { struct sss_iobuf *iobuf; @@ -79,6 +89,7 @@ struct sss_iobuf *sss_iobuf_init_steal(TALLOC_CTX *mem_ctx, iobuf->data = talloc_steal(iobuf, data); iobuf->size = size; iobuf->capacity = size; + iobuf->secure = secure; return iobuf; } @@ -124,6 +135,15 @@ uint8_t *sss_iobuf_get_data(const struct sss_iobuf *iobuf) return iobuf->data; } +bool sss_iobuf_get_secure(const struct sss_iobuf *iobuf) +{ + if (iobuf == NULL) { + return false; + } + + return iobuf->secure; +} + static size_t iobuf_get_len(const struct sss_iobuf *iobuf) { if (iobuf == NULL) { @@ -274,6 +294,9 @@ errno_t sss_iobuf_read_varlen(TALLOC_CTX *mem_ctx, if (out == NULL) { return ENOMEM; } + if (iobuf->secure) { + talloc_set_destructor((void *) out, sss_erase_talloc_mem_securely); + } slen = len; ret = sss_iobuf_read_len(iobuf, slen, out); @@ -324,7 +347,7 @@ errno_t sss_iobuf_read_iobuf(TALLOC_CTX *mem_ctx, return ret; } - out = sss_iobuf_init_steal(mem_ctx, data, len); + out = sss_iobuf_init_steal(mem_ctx, data, len, iobuf->secure); if (out == NULL) { return ENOMEM; } diff --git a/src/util/sss_iobuf.h b/src/util/sss_iobuf.h index a1abc5a5e66..4a618e94798 100644 --- a/src/util/sss_iobuf.h +++ b/src/util/sss_iobuf.h @@ -2,6 +2,7 @@ #define __SSS_IOBUF_H_ #include +#include #include #include @@ -22,6 +23,8 @@ struct sss_iobuf; * @param[in] capacity The maximum capacity the buffer can grow into. * Use 0 for an 'unlimited' buffer that will grow * until SIZE_MAX/2. + * @param[in] secure Secure the data by erasing it before the memory is + * released. * * @warning The allocated data storage will not be zeroed. * @@ -30,7 +33,8 @@ struct sss_iobuf; */ struct sss_iobuf *sss_iobuf_init_empty(TALLOC_CTX *mem_ctx, size_t size, - size_t capacity); + size_t capacity, + bool secure); /* * @brief Allocate an IO buffer with a fixed size @@ -45,6 +49,8 @@ struct sss_iobuf *sss_iobuf_init_empty(TALLOC_CTX *mem_ctx, * @param[in] data The data to initialize the IO buffer with. This * data is copied into the iobuf-owned buffer. * @param[in] size The size of the data buffer + * @param[in] secure Secure the data by erasing it before the memory is + * released. * * @warning The allocated data storage will not be zeroed. * @@ -52,7 +58,8 @@ struct sss_iobuf *sss_iobuf_init_empty(TALLOC_CTX *mem_ctx, */ struct sss_iobuf *sss_iobuf_init_readonly(TALLOC_CTX *mem_ctx, const uint8_t *data, - size_t size); + size_t size, + bool secure); /* * @brief Allocate an IO buffer with a fixed size, stealing input data. @@ -65,12 +72,15 @@ struct sss_iobuf *sss_iobuf_init_readonly(TALLOC_CTX *mem_ctx, * @param[in] mem_ctx The talloc context that owns the iobuf * @param[in] data The data to initialize the IO buffer with. * @param[in] size The size of the data buffer + * @param[in] secure Secure the data by erasing it before the memory is + * released. * * @return The newly created buffer on success or NULL on an error. */ struct sss_iobuf *sss_iobuf_init_steal(TALLOC_CTX *mem_ctx, uint8_t *data, - size_t size); + size_t size, + bool secure); /* * @brief Reset internal cursor of the IO buffer (seek to the start) @@ -102,6 +112,11 @@ size_t sss_iobuf_get_size(const struct sss_iobuf *iobuf); */ uint8_t *sss_iobuf_get_data(const struct sss_iobuf *iobuf); +/* + * @brief Returns whether the data is secured + */ +bool sss_iobuf_get_secure(const struct sss_iobuf *iobuf); + /* * @brief Read from an IO buffer *