summaryrefslogtreecommitdiff
path: root/lib/libc/nls
diff options
context:
space:
mode:
authorTobias Stoeckmann <tobias@cvs.openbsd.org>2015-10-23 18:49:08 +0000
committerTobias Stoeckmann <tobias@cvs.openbsd.org>2015-10-23 18:49:08 +0000
commit6ec01d9c6e8b954e943386ce16e87a6de633f6fd (patch)
treed2d63fff534e66fd4e2b0afd2387a7dd8219c46c /lib/libc/nls
parente6e8c193041b99a72967f526ef594479f3df5082 (diff)
Verify that opened message catalog is valid, i.e. avoid integer overflows
and out of boundary accesses. with input by miod, ok stsp
Diffstat (limited to 'lib/libc/nls')
-rw-r--r--lib/libc/nls/catopen.c122
1 files changed, 105 insertions, 17 deletions
diff --git a/lib/libc/nls/catopen.c b/lib/libc/nls/catopen.c
index e3ba4724a5e..3c47d098477 100644
--- a/lib/libc/nls/catopen.c
+++ b/lib/libc/nls/catopen.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: catopen.c,v 1.17 2015/09/05 11:25:30 guenther Exp $ */
+/* $OpenBSD: catopen.c,v 1.18 2015/10/23 18:49:07 tobias Exp $ */
/*-
* Copyright (c) 1996 The NetBSD Foundation, Inc.
* All rights reserved.
@@ -30,20 +30,24 @@
#define _NLS_PRIVATE
-#include <limits.h>
-#include <stdlib.h>
-#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/mman.h>
-#include <unistd.h>
+#include <errno.h>
#include <fcntl.h>
+#include <limits.h>
#include <nl_types.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#define MAXIMUM(a, b) (((a) > (b)) ? (a) : (b))
#define NLS_DEFAULT_PATH "/usr/share/nls/%L/%N.cat:/usr/share/nls/%l.%c/%N.cat:/usr/share/nls/%l/%N.cat"
#define NLS_DEFAULT_LANG "C"
-static nl_catd load_msgcat(const char *);
+static nl_catd load_msgcat(const char *);
+static int verify_msgcat(nl_catd);
nl_catd
catopen(const char *name, int oflag)
@@ -165,6 +169,8 @@ load_msgcat(const char *path)
void *data;
int fd;
+ catd = NULL;
+
if ((fd = open(path, O_RDONLY|O_CLOEXEC)) == -1)
return (nl_catd) -1;
@@ -173,24 +179,106 @@ load_msgcat(const char *path)
return (nl_catd) -1;
}
- data = mmap(0, (size_t) st.st_size, PROT_READ, MAP_SHARED, fd, (off_t)0);
- close (fd);
-
- if (data == MAP_FAILED) {
+ if (st.st_size > INT_MAX || st.st_size < sizeof (struct _nls_cat_hdr)) {
+ errno = EINVAL;
+ close (fd);
return (nl_catd) -1;
}
- if (ntohl(((struct _nls_cat_hdr *) data)->__magic) != _NLS_MAGIC) {
- munmap(data, (size_t) st.st_size);
- return (nl_catd) -1;
- }
+ data = mmap(0, (size_t)st.st_size, PROT_READ, MAP_SHARED, fd, (off_t)0);
+ close (fd);
- if ((catd = malloc(sizeof (*catd))) == 0) {
- munmap(data, (size_t) st.st_size);
+ if (data == MAP_FAILED)
return (nl_catd) -1;
- }
+
+ if (ntohl(((struct _nls_cat_hdr *) data)->__magic) != _NLS_MAGIC)
+ goto invalid;
+
+ if ((catd = malloc(sizeof (*catd))) == 0)
+ goto invalid;
catd->__data = data;
catd->__size = st.st_size;
+
+ if (verify_msgcat(catd))
+ goto invalid;
+
return catd;
+
+invalid:
+ free(catd);
+ munmap(data, (size_t) st.st_size);
+ errno = EINVAL;
+ return (nl_catd) -1;
}
+
+static int
+verify_msgcat(nl_catd catd)
+{
+ struct _nls_cat_hdr *cat;
+ struct _nls_set_hdr *set;
+ struct _nls_msg_hdr *msg;
+ size_t remain;
+ int hdr_offset, i, index, j, msgs, nmsgs, nsets, off, txt_offset;
+
+ remain = catd->__size;
+ cat = (struct _nls_cat_hdr *) catd->__data;
+
+ hdr_offset = ntohl(cat->__msg_hdr_offset);
+ nsets = ntohl(cat->__nsets);
+ txt_offset = ntohl(cat->__msg_txt_offset);
+
+ /* catalog must contain at least one set and no negative offsets */
+ if (nsets < 1 || hdr_offset < 0 || txt_offset < 0)
+ return (1);
+
+ remain -= sizeof (*cat);
+
+ /* check if offsets or set size overflow */
+ if (remain <= hdr_offset || remain <= ntohl(cat->__msg_txt_offset) ||
+ remain / sizeof (*set) < nsets)
+ return (1);
+
+ set = (struct _nls_set_hdr *) ((char *) catd->__data + sizeof (*cat));
+
+ /* make sure that msg has space for at least one index */
+ if (remain - hdr_offset < sizeof(*msg))
+ return (1);
+
+ msg = (struct _nls_msg_hdr *) ((char *) catd->__data + sizeof (*cat)
+ + hdr_offset);
+
+ /* validate and retrieve largest string offset from sets */
+ off = 0;
+ for (i = 0; i < nsets; i++) {
+ index = ntohl(set[i].__index);
+ nmsgs = ntohl(set[i].__nmsgs);
+ /* set must contain at least one message */
+ if (index < 0 || nmsgs < 1)
+ return (1);
+
+ if (INT_MAX - nmsgs < index)
+ return (1);
+ msgs = index + nmsgs;
+
+ /* avoid msg index overflow */
+ if ((remain - hdr_offset) / sizeof(*msg) < msgs)
+ return (1);
+
+ /* retrieve largest string offset */
+ for (j = index; j < nmsgs; j++) {
+ if (ntohl(msg[j].__offset) < 0)
+ return (1);
+ off = MAXIMUM(off, ntohl(msg[j].__offset));
+ }
+ }
+
+ /* check if largest string offset is nul-terminated */
+ if (remain - txt_offset < off ||
+ memchr((char *) catd->__data + sizeof(*cat) + txt_offset + off,
+ '\0', remain - txt_offset - off) == NULL)
+ return (1);
+
+ return (0);
+}
+