xref: /openbsd-src/lib/libc/nls/catopen.c (revision 3123b2cb92f9dffb8297928ef299880de6b0e7d8)
1 /*	$OpenBSD: catopen.c,v 1.21 2017/04/27 23:54:08 millert Exp $ */
2 /*-
3  * Copyright (c) 1996 The NetBSD Foundation, Inc.
4  * All rights reserved.
5  *
6  * This code is derived from software contributed to The NetBSD Foundation
7  * by J.T. Conklin.
8  *
9  * Redistribution and use in source and binary forms, with or without
10  * modification, are permitted provided that the following conditions
11  * are met:
12  * 1. Redistributions of source code must retain the above copyright
13  *    notice, this list of conditions and the following disclaimer.
14  * 2. Redistributions in binary form must reproduce the above copyright
15  *    notice, this list of conditions and the following disclaimer in the
16  *    documentation and/or other materials provided with the distribution.
17  *
18  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
19  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
20  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
21  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE
22  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
23  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
24  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
25  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
26  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
27  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
28  * POSSIBILITY OF SUCH DAMAGE.
29  */
30 
31 #define _NLS_PRIVATE
32 
33 #include <sys/types.h>
34 #include <sys/stat.h>
35 #include <sys/mman.h>
36 #include <errno.h>
37 #include <fcntl.h>
38 #include <limits.h>
39 #include <nl_types.h>
40 #include <stdlib.h>
41 #include <string.h>
42 #include <unistd.h>
43 
44 #define MAXIMUM(a, b)	(((a) > (b)) ? (a) : (b))
45 
46 #define NLS_DEFAULT_LANG "C"
47 
48 static nl_catd	load_msgcat(const char *);
49 static int	verify_msgcat(nl_catd);
50 
51 nl_catd
catopen(const char * name,int oflag)52 catopen(const char *name, int oflag)
53 {
54 	char tmppath[PATH_MAX];
55 	char *nlspath;
56 	char *lang;
57 	char *s, *t, *sep, *dot;
58 	const char *u;
59 	nl_catd catd;
60 
61 	if (name == NULL || *name == '\0')
62 		return (nl_catd) -1;
63 
64 	/* absolute or relative path? */
65 	if (strchr(name, '/'))
66 		return load_msgcat(name);
67 
68 	if (issetugid() != 0 || (nlspath = getenv("NLSPATH")) == NULL)
69 		return (nl_catd) -1;
70 
71 	lang = NULL;
72 	if (oflag & NL_CAT_LOCALE) {
73 		lang = getenv("LC_ALL");
74 		if (lang == NULL)
75 			lang = getenv("LC_MESSAGES");
76 	}
77 	if (lang == NULL)
78 		lang = getenv("LANG");
79 	if (lang == NULL)
80 		lang = NLS_DEFAULT_LANG;
81 	if (strcmp(lang, "POSIX") == 0)
82 		lang = NLS_DEFAULT_LANG;
83 
84 	s = nlspath;
85 	t = tmppath;
86 
87 	/*
88 	 * Locale names are of the form language[_territory][.codeset].
89 	 * See POSIX-1-2008 "8.2 Internationalization Variables"
90 	 */
91 	sep = strchr(lang, '_');
92 	dot = strrchr(lang, '.');
93 	if (dot && sep && dot < sep)
94 		dot = NULL; /* ignore dots preceeding _ */
95 	if (dot == NULL)
96 		lang = NLS_DEFAULT_LANG; /* no codeset specified */
97 	do {
98 		while (*s && *s != ':') {
99 			if (*s == '%') {
100 				switch (*(++s)) {
101 				case 'L':	/* LANG or LC_MESSAGES */
102 					u = lang;
103 					while (*u && t < tmppath + PATH_MAX-1)
104 						*t++ = *u++;
105 					break;
106 				case 'N':	/* value of name parameter */
107 					u = name;
108 					while (*u && t < tmppath + PATH_MAX-1)
109 						*t++ = *u++;
110 					break;
111 				case 'l':	/* language part */
112 					u = lang;
113 					while (*u && t < tmppath + PATH_MAX-1) {
114 						*t++ = *u++;
115 						if (sep && u >= sep)
116 							break;
117 						if (dot && u >= dot)
118 							break;
119 					}
120 					break;
121 				case 't':	/* territory part */
122 					if (sep == NULL)
123 						break;
124 					u = sep + 1;
125 					while (*u && t < tmppath + PATH_MAX-1) {
126 						*t++ = *u++;
127 						if (dot && u >= dot)
128 							break;
129 					}
130 					break;
131 				case 'c':	/* codeset part */
132 					if (dot == NULL)
133 						break;
134 					u = dot + 1;
135 					while (*u && t < tmppath + PATH_MAX-1)
136 						*t++ = *u++;
137 					break;
138 				default:
139 					if (t < tmppath + PATH_MAX-1)
140 						*t++ = *s;
141 				}
142 			} else {
143 				if (t < tmppath + PATH_MAX-1)
144 					*t++ = *s;
145 			}
146 			s++;
147 		}
148 
149 		*t = '\0';
150 		catd = load_msgcat(tmppath);
151 		if (catd != (nl_catd) -1)
152 			return catd;
153 
154 		if (*s)
155 			s++;
156 		t = tmppath;
157 	} while (*s);
158 
159 	return (nl_catd) -1;
160 }
161 DEF_WEAK(catopen);
162 
163 static nl_catd
load_msgcat(const char * path)164 load_msgcat(const char *path)
165 {
166 	struct stat st;
167 	nl_catd catd;
168 	void *data;
169 	int fd;
170 
171 	catd = NULL;
172 
173 	if ((fd = open(path, O_RDONLY|O_CLOEXEC)) == -1)
174 		return (nl_catd) -1;
175 
176 	if (fstat(fd, &st) != 0) {
177 		close (fd);
178 		return (nl_catd) -1;
179 	}
180 
181 	if (st.st_size > INT_MAX || st.st_size < sizeof (struct _nls_cat_hdr)) {
182 		errno = EINVAL;
183 		close (fd);
184 		return (nl_catd) -1;
185 	}
186 
187 	data = mmap(0, st.st_size, PROT_READ, MAP_SHARED, fd, 0);
188 	close (fd);
189 
190 	if (data == MAP_FAILED)
191 		return (nl_catd) -1;
192 
193 	if (ntohl(((struct _nls_cat_hdr *) data)->__magic) != _NLS_MAGIC)
194 		goto invalid;
195 
196 	if ((catd = malloc(sizeof (*catd))) == 0)
197 		goto invalid;
198 
199 	catd->__data = data;
200 	catd->__size = st.st_size;
201 
202 	if (verify_msgcat(catd))
203 		goto invalid;
204 
205 	return catd;
206 
207 invalid:
208 	free(catd);
209 	munmap(data, st.st_size);
210 	errno = EINVAL;
211 	return (nl_catd) -1;
212 }
213 
214 static int
verify_msgcat(nl_catd catd)215 verify_msgcat(nl_catd catd)
216 {
217 	struct _nls_cat_hdr *cat;
218 	struct _nls_set_hdr *set;
219 	struct _nls_msg_hdr *msg;
220 	size_t remain;
221 	int hdr_offset, i, index, j, msgs, nmsgs, nsets, off, txt_offset;
222 
223 	remain = catd->__size;
224 	cat = (struct _nls_cat_hdr *) catd->__data;
225 
226 	hdr_offset = ntohl(cat->__msg_hdr_offset);
227 	nsets = ntohl(cat->__nsets);
228 	txt_offset = ntohl(cat->__msg_txt_offset);
229 
230 	/* catalog must contain at least one set and no negative offsets */
231 	if (nsets < 1 || hdr_offset < 0 || txt_offset < 0)
232 		return (1);
233 
234 	remain -= sizeof (*cat);
235 
236 	/* check if offsets or set size overflow */
237 	if (remain <= hdr_offset || remain <= ntohl(cat->__msg_txt_offset) ||
238 	    remain / sizeof (*set) < nsets)
239 		return (1);
240 
241 	set = (struct _nls_set_hdr *) ((char *) catd->__data + sizeof (*cat));
242 
243 	/* make sure that msg has space for at least one index */
244 	if (remain - hdr_offset < sizeof(*msg))
245 		return (1);
246 
247 	msg = (struct _nls_msg_hdr *) ((char *) catd->__data + sizeof (*cat)
248 	    + hdr_offset);
249 
250 	/* validate and retrieve largest string offset from sets */
251 	off = 0;
252 	for (i = 0; i < nsets; i++) {
253 		index = ntohl(set[i].__index);
254 		nmsgs = ntohl(set[i].__nmsgs);
255 		/* set must contain at least one message */
256 		if (index < 0 || nmsgs < 1)
257 			return (1);
258 
259 		if (INT_MAX - nmsgs < index)
260 			return (1);
261 		msgs = index + nmsgs;
262 
263 		/* avoid msg index overflow */
264 		if ((remain - hdr_offset) / sizeof(*msg) < msgs)
265 			return (1);
266 
267 		/* retrieve largest string offset */
268 		for (j = index; j < nmsgs; j++) {
269 			if (ntohl(msg[j].__offset) > INT_MAX)
270 				return (1);
271 			off = MAXIMUM(off, ntohl(msg[j].__offset));
272 		}
273 	}
274 
275 	/* check if largest string offset is nul-terminated */
276 	if (remain - txt_offset < off ||
277 	    memchr((char *) catd->__data + sizeof(*cat) + txt_offset + off,
278 	    '\0', remain - txt_offset - off) == NULL)
279 		return (1);
280 
281 	return (0);
282 }
283 
284