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