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 48static nl_catd load_msgcat(const char *); 49static int verify_msgcat(nl_catd); 50 51nl_catd 52catopen(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} 161DEF_WEAK(catopen); 162 163static nl_catd 164load_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 207invalid: 208 free(catd); 209 munmap(data, st.st_size); 210 errno = EINVAL; 211 return (nl_catd) -1; 212} 213 214static int 215verify_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