diff --git a/lib/krb5/store-int.h b/lib/krb5/store-int.h index 0b7accb86..877ccc008 100644 --- a/lib/krb5/store-int.h +++ b/lib/krb5/store-int.h @@ -43,6 +43,7 @@ struct krb5_storage_data { void (*free)(struct krb5_storage_data*); krb5_flags flags; int eof_code; + size_t max_alloc; }; #endif /* __store_int_h__ */ diff --git a/lib/krb5/store.c b/lib/krb5/store.c index 555496625..ac1fe0ded 100644 --- a/lib/krb5/store.c +++ b/lib/krb5/store.c @@ -119,6 +119,41 @@ krb5_storage_get_byteorder(krb5_storage *sp) return sp->flags & KRB5_STORAGE_BYTEORDER_MASK; } +/** + * Set the max alloc value + * + * @param sp the storage buffer set the max allow for + * @param size maximum size to allocate, use 0 to remove limit + * + * @ingroup krb5_storage + */ + +KRB5_LIB_FUNCTION void KRB5_LIB_CALL +krb5_storage_set_max_alloc(krb5_storage *sp, size_t size) +{ + sp->max_alloc = size; +} + +/* don't allocate unresonable amount of memory */ +static krb5_error_code +size_too_large(krb5_storage *sp, size_t size) +{ + if (sp->max_alloc && sp->max_alloc < size) + return HEIM_ERR_TOO_BIG; + return 0; +} + +static krb5_error_code +size_too_large_num(krb5_storage *sp, size_t count, size_t size) +{ + if (sp->max_alloc == 0 || size == 0) + return 0; + size = sp->max_alloc / size; + if (size < count) + return HEIM_ERR_TOO_BIG; + return 0; +} + /** * Seek to a new offset. * @@ -263,9 +298,9 @@ krb5_storage_to_data(krb5_storage *sp, krb5_data *data) if (pos < 0) return HEIM_ERR_NOT_SEEKABLE; size = sp->seek(sp, 0, SEEK_END); - /* don't allocate unresonable amount of memory */ - if (size > UINT_MAX/8) - return HEIM_ERR_TOO_BIG; + ret = size_too_large(sp, size); + if (ret) + return ret; ret = krb5_data_alloc(data, size); if (ret) { sp->seek(sp, pos, SEEK_SET); @@ -645,6 +680,9 @@ krb5_ret_data(krb5_storage *sp, ret = krb5_ret_int32(sp, &size); if(ret) return ret; + ret = size_too_large(sp, size); + if (ret) + return ret; ret = krb5_data_alloc (data, size); if (ret) return ret; @@ -757,6 +795,9 @@ krb5_ret_stringz(krb5_storage *sp, char *tmp; len++; + ret = size_too_large(sp, len); + if (ret) + break; tmp = realloc (s, len); if (tmp == NULL) { free (s); @@ -823,6 +864,9 @@ krb5_ret_stringnl(krb5_storage *sp, } len++; + ret = size_too_large(sp, len); + if (ret) + break; tmp = realloc (s, len); if (tmp == NULL) { free (s); @@ -923,6 +967,11 @@ krb5_ret_principal(krb5_storage *sp, free(p); return EINVAL; } + ret = size_too_large_num(sp, ncomp, sizeof(p->name.name_string.val[0])); + if (ret) { + free(p); + return ret; + } p->name.name_type = type; p->name.name_string.len = ncomp; ret = krb5_ret_string(sp, &p->realm); @@ -930,7 +979,7 @@ krb5_ret_principal(krb5_storage *sp, free(p); return ret; } - p->name.name_string.val = calloc(ncomp, sizeof(*p->name.name_string.val)); + p->name.name_string.val = calloc(ncomp, sizeof(p->name.name_string.val[0])); if(p->name.name_string.val == NULL && ncomp != 0){ free(p->realm); free(p); @@ -1153,6 +1202,8 @@ krb5_ret_addrs(krb5_storage *sp, krb5_addresses *adr) ret = krb5_ret_int32(sp, &tmp); if(ret) return ret; + ret = size_too_large_num(sp, tmp, sizeof(adr->val[0])); + if (ret) return ret; adr->len = tmp; ALLOC(adr->val, adr->len); if (adr->val == NULL && adr->len != 0) @@ -1211,6 +1262,8 @@ krb5_ret_authdata(krb5_storage *sp, krb5_authdata *auth) int i; ret = krb5_ret_int32(sp, &tmp); if(ret) return ret; + ret = size_too_large_num(sp, tmp, sizeof(auth->val[0])); + if (ret) return ret; ALLOC_SEQ(auth, tmp); if (auth->val == NULL && tmp != 0) return ENOMEM; diff --git a/lib/krb5/store_emem.c b/lib/krb5/store_emem.c index acd61f2e5..7f91b0848 100644 --- a/lib/krb5/store_emem.c +++ b/lib/krb5/store_emem.c @@ -190,5 +190,6 @@ krb5_storage_emem(void) sp->seek = emem_seek; sp->trunc = emem_trunc; sp->free = emem_free; + sp->max_alloc = UINT_MAX/8; return sp; } diff --git a/lib/krb5/store_fd.c b/lib/krb5/store_fd.c index bd357dbe3..dbeec4234 100644 --- a/lib/krb5/store_fd.c +++ b/lib/krb5/store_fd.c @@ -128,5 +128,6 @@ krb5_storage_from_fd(krb5_socket_t fd_in) sp->seek = fd_seek; sp->trunc = fd_trunc; sp->free = fd_free; + sp->max_alloc = UINT_MAX/8; return sp; } diff --git a/lib/krb5/store_mem.c b/lib/krb5/store_mem.c index a6a8b96a5..e674a95db 100644 --- a/lib/krb5/store_mem.c +++ b/lib/krb5/store_mem.c @@ -145,6 +145,7 @@ krb5_storage_from_mem(void *buf, size_t len) sp->seek = mem_seek; sp->trunc = mem_trunc; sp->free = NULL; + sp->max_alloc = UINT_MAX/8; return sp; } @@ -203,5 +204,6 @@ krb5_storage_from_readonly_mem(const void *buf, size_t len) sp->seek = mem_seek; sp->trunc = mem_no_trunc; sp->free = NULL; + sp->max_alloc = UINT_MAX/8; return sp; } diff --git a/lib/krb5/test_store.c b/lib/krb5/test_store.c index 831762b2a..54682ed2f 100644 --- a/lib/krb5/test_store.c +++ b/lib/krb5/test_store.c @@ -193,8 +193,6 @@ test_storage(krb5_context context, krb5_storage *sp) test_uint8(context, sp); test_uint16(context, sp); test_uint32(context, sp); - - krb5_storage_free(sp); } @@ -217,10 +215,25 @@ test_truncate(krb5_context context, krb5_storage *sp, int fd) krb5_err(context, 1, errno, "fstat"); if (sb.st_size != 1024) krb5_errx(context, 1, "length not 2"); - - krb5_storage_free(sp); } +static void +check_too_large(krb5_context context, krb5_storage *sp) +{ + uint32_t too_big_sizes[] = { INT_MAX, INT_MAX / 2, INT_MAX / 4, INT_MAX / 8 + 1}; + krb5_error_code ret; + krb5_data data; + size_t n; + + for (n = 0; n < sizeof(too_big_sizes) / sizeof(too_big_sizes); n++) { + krb5_storage_truncate(sp, 0); + krb5_store_uint32(sp, too_big_sizes[n]); + krb5_storage_seek(sp, 0, SEEK_SET); + ret = krb5_ret_data(sp, &data); + if (ret != HEIM_ERR_TOO_BIG) + errx(1, "not too big: %lu", (unsigned long)n); + } +} /* * @@ -284,10 +297,13 @@ main(int argc, char **argv) krb5_errx(context, 1, "krb5_storage_emem: no mem"); test_storage(context, sp); + check_too_large(context, sp); + krb5_storage_free(sp); + fd = open(fn, O_RDWR|O_CREAT|O_TRUNC, 0600); if (fd < 0) - krb5_err(context, 1, errno, "open(%s", fn); + krb5_err(context, 1, errno, "open(%s)", fn); sp = krb5_storage_from_fd(fd); close(fd); @@ -295,6 +311,7 @@ main(int argc, char **argv) krb5_errx(context, 1, "krb5_storage_from_fd: %s no mem", fn); test_storage(context, sp); + krb5_storage_free(sp); unlink(fn); /* @@ -303,13 +320,14 @@ main(int argc, char **argv) fd = open(fn, O_RDWR|O_CREAT|O_TRUNC, 0600); if (fd < 0) - krb5_err(context, 1, errno, "open(%s", fn); + krb5_err(context, 1, errno, "open(%s)", fn); sp = krb5_storage_from_fd(fd); if (sp == NULL) krb5_errx(context, 1, "krb5_storage_from_fd: %s no mem", fn); test_truncate(context, sp, fd); + krb5_storage_free(sp); close(fd); unlink(fn); diff --git a/lib/krb5/version-script.map b/lib/krb5/version-script.map index a7913110e..a518422d9 100644 --- a/lib/krb5/version-script.map +++ b/lib/krb5/version-script.map @@ -606,6 +606,7 @@ HEIMDAL_KRB5_2.0 { krb5_storage_set_byteorder; krb5_storage_set_eof_code; krb5_storage_set_flags; + krb5_storage_set_max_alloc; krb5_storage_to_data; krb5_storage_truncate; krb5_storage_write;