GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: lib/libc/nls/catopen.c Lines: 0 116 0.0 %
Date: 2017-11-07 Branches: 0 118 0.0 %

Line Branch Exec Source
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
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
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
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