0
|
1 |
/*
|
|
2 |
* CDDL HEADER START
|
|
3 |
*
|
|
4 |
* The contents of this file are subject to the terms of the
|
|
5 |
* Common Development and Distribution License, Version 1.0 only
|
|
6 |
* (the "License"). You may not use this file except in compliance
|
|
7 |
* with the License.
|
|
8 |
*
|
|
9 |
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
|
|
10 |
* or http://www.opensolaris.org/os/licensing.
|
|
11 |
* See the License for the specific language governing permissions
|
|
12 |
* and limitations under the License.
|
|
13 |
*
|
|
14 |
* When distributing Covered Code, include this CDDL HEADER in each
|
|
15 |
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
|
|
16 |
* If applicable, add the following below this CDDL HEADER, with the
|
|
17 |
* fields enclosed by brackets "[]" replaced with your own identifying
|
|
18 |
* information: Portions Copyright [yyyy] [name of copyright owner]
|
|
19 |
*
|
|
20 |
* CDDL HEADER END
|
|
21 |
*/
|
|
22 |
/*
|
|
23 |
* Copyright 2004 Sun Microsystems, Inc. All rights reserved.
|
|
24 |
* Use is subject to license terms.
|
|
25 |
*/
|
|
26 |
|
|
27 |
#pragma ident "%Z%%M% %I% %E% SMI"
|
|
28 |
|
|
29 |
#include "synonyms.h"
|
|
30 |
#include "mtlib.h"
|
|
31 |
#include <string.h>
|
|
32 |
#include <syslog.h>
|
|
33 |
#include <sys/stat.h>
|
|
34 |
#include <fcntl.h>
|
|
35 |
#include <limits.h>
|
|
36 |
#include <unistd.h>
|
|
37 |
#include <stdlib.h>
|
|
38 |
#include <thread.h>
|
|
39 |
#include <synch.h>
|
|
40 |
#include <ctype.h>
|
|
41 |
#include <errno.h>
|
|
42 |
#include "libc.h"
|
|
43 |
#include "nlspath_checks.h"
|
|
44 |
|
|
45 |
extern const char **environ;
|
|
46 |
|
|
47 |
/*
|
|
48 |
* We want to prevent the use of NLSPATH by setugid applications but
|
|
49 |
* not completely. CDE depends on this very much.
|
|
50 |
* Yes, this is ugly.
|
|
51 |
*/
|
|
52 |
|
|
53 |
struct trusted_systemdirs {
|
|
54 |
const char *dir;
|
|
55 |
size_t dirlen;
|
|
56 |
};
|
|
57 |
|
|
58 |
#define _USRLIB "/usr/lib/"
|
|
59 |
#define _USRDT "/usr/dt/"
|
|
60 |
#define _USROW "/usr/openwin/"
|
|
61 |
|
|
62 |
static const struct trusted_systemdirs prefix[] = {
|
|
63 |
{ _USRLIB, sizeof (_USRLIB) - 1 },
|
|
64 |
{ _USRDT, sizeof (_USRDT) - 1 },
|
|
65 |
{ _USROW, sizeof (_USROW) - 1 },
|
|
66 |
{ NULL, 0 }
|
|
67 |
};
|
|
68 |
|
|
69 |
static int8_t nlspath_safe;
|
|
70 |
|
|
71 |
/*
|
|
72 |
* Routine to check the safety of a messages file.
|
|
73 |
* When the program specifies a pathname and doesn't
|
|
74 |
* use NLSPATH, it should specify the "safe" flag as 1.
|
|
75 |
* Most checks will be disabled then.
|
|
76 |
* fstat64 is done here and the stat structure is returned
|
|
77 |
* to prevent duplication of system calls.
|
|
78 |
*
|
|
79 |
* The trust return value contains an indication of
|
|
80 |
* trustworthiness (i.e., does check_format need to be called or
|
|
81 |
* not)
|
|
82 |
*/
|
|
83 |
|
|
84 |
int
|
|
85 |
nls_safe_open(const char *path, struct stat64 *statbuf, int *trust, int safe)
|
|
86 |
{
|
|
87 |
int fd;
|
|
88 |
int trust_path;
|
|
89 |
int systemdir = 0;
|
|
90 |
int abs_path = 0;
|
|
91 |
int trust_owner = 0;
|
|
92 |
int trust_group = 0;
|
|
93 |
const struct trusted_systemdirs *p;
|
|
94 |
|
|
95 |
/*
|
|
96 |
* If SAFE_F has been specified or NLSPATH is safe (or not set),
|
|
97 |
* set trust_path and trust the file as an initial value.
|
|
98 |
*/
|
|
99 |
trust_path = *trust = safe || nlspath_safe;
|
|
100 |
|
|
101 |
fd = open(path, O_RDONLY);
|
|
102 |
|
|
103 |
if (fd < 0)
|
|
104 |
return (-1);
|
|
105 |
|
|
106 |
if (fstat64(fd, statbuf) == -1) {
|
|
107 |
(void) close(fd);
|
|
108 |
return (-1);
|
|
109 |
}
|
|
110 |
|
|
111 |
/*
|
|
112 |
* Trust only files owned by root or bin (uid 2), except
|
|
113 |
* when specified as full path or when NLSPATH is known to
|
|
114 |
* be safe.
|
|
115 |
* Don't trust files writable by other or writable
|
|
116 |
* by non-bin, non-root system group.
|
|
117 |
* Don't trust these files even if the path is correct.
|
|
118 |
* Since we don't support changing uids/gids on our files,
|
|
119 |
* we hardcode them here for now.
|
|
120 |
*/
|
|
121 |
|
|
122 |
/*
|
|
123 |
* if the path is absolute and does not contain "/../",
|
|
124 |
* set abs_path.
|
|
125 |
*/
|
|
126 |
if (*path == '/' && strstr(path, "/../") == NULL) {
|
|
127 |
abs_path = 1;
|
|
128 |
/*
|
|
129 |
* if the path belongs to the trusted system directory,
|
|
130 |
* set systemdir.
|
|
131 |
*/
|
|
132 |
for (p = prefix; p->dir; p++) {
|
|
133 |
if (strncmp(p->dir, path, p->dirlen) == 0) {
|
|
134 |
systemdir = 1;
|
|
135 |
break;
|
|
136 |
}
|
|
137 |
}
|
|
138 |
}
|
|
139 |
|
|
140 |
/*
|
|
141 |
* If the owner is root or bin, set trust_owner.
|
|
142 |
*/
|
|
143 |
if (statbuf->st_uid == 0 || statbuf->st_uid == 2) {
|
|
144 |
trust_owner = 1;
|
|
145 |
}
|
|
146 |
/*
|
|
147 |
* If the file is neither other-writable nor group-writable by
|
|
148 |
* non-bin and non-root system group, set trust_group.
|
|
149 |
*/
|
|
150 |
if ((statbuf->st_mode & (S_IWOTH)) == 0 &&
|
|
151 |
((statbuf->st_mode & (S_IWGRP)) == 0 ||
|
|
152 |
(statbuf->st_gid < 4 && statbuf->st_gid != 1))) {
|
|
153 |
trust_group = 1;
|
|
154 |
}
|
|
155 |
|
|
156 |
/*
|
|
157 |
* Even if UNSAFE_F has been specified and unsafe-NLSPATH
|
|
158 |
* has been set, trust the file as long as it belongs to
|
|
159 |
* the trusted system directory.
|
|
160 |
*/
|
|
161 |
if (!*trust && systemdir) {
|
|
162 |
*trust = 1;
|
|
163 |
}
|
|
164 |
|
|
165 |
/*
|
|
166 |
* If:
|
|
167 |
* file is not a full pathname,
|
|
168 |
* or
|
|
169 |
* neither trust_owner nor trust_path is set,
|
|
170 |
* or
|
|
171 |
* trust_group is not set,
|
|
172 |
* untrust it.
|
|
173 |
*/
|
|
174 |
if (*trust &&
|
|
175 |
(!abs_path || (!trust_owner && !trust_path) || !trust_group)) {
|
|
176 |
*trust = 0;
|
|
177 |
}
|
|
178 |
|
|
179 |
/*
|
|
180 |
* If set[ug]id process, open for the untrusted file should fail.
|
|
181 |
* Otherwise, the message extracted from the untrusted file
|
|
182 |
* will have to be checked by check_format().
|
|
183 |
*/
|
|
184 |
if (issetugid()) {
|
|
185 |
if (!*trust) {
|
|
186 |
/*
|
|
187 |
* Open should fail
|
|
188 |
*/
|
|
189 |
(void) close(fd);
|
|
190 |
return (-1);
|
|
191 |
}
|
|
192 |
|
|
193 |
/*
|
|
194 |
* if the path does not belong to the trusted system directory
|
|
195 |
* or if the owner is neither root nor bin, untrust it.
|
|
196 |
*/
|
|
197 |
if (!systemdir || !trust_owner) {
|
|
198 |
*trust = 0;
|
|
199 |
}
|
|
200 |
}
|
|
201 |
|
|
202 |
return (fd);
|
|
203 |
}
|
|
204 |
|
|
205 |
/*
|
|
206 |
* Extract a format into a normalized format string.
|
|
207 |
* Returns the number of arguments converted, -1 on error.
|
|
208 |
* The string norm should contain 2N bytes; an upperbound is the
|
|
209 |
* length of the format string.
|
|
210 |
* The canonical format consists of two chars: one is the conversion
|
|
211 |
* character (s, c, d, x, etc), the second one is the option flag.
|
|
212 |
* L, ll, l, w as defined below.
|
|
213 |
* A special conversion character, '*', indicates that the argument
|
|
214 |
* is used as a precision specifier.
|
|
215 |
*/
|
|
216 |
|
|
217 |
#define OPT_L 0x01
|
|
218 |
#define OPT_l 0x02
|
|
219 |
#define OPT_ll 0x04
|
|
220 |
#define OPT_w 0x08
|
|
221 |
#define OPT_h 0x10
|
|
222 |
#define OPT_hh 0x20
|
|
223 |
#define OPT_j 0x40
|
|
224 |
|
|
225 |
/* Number of bytes per canonical format entry */
|
|
226 |
#define FORMAT_SIZE 2
|
|
227 |
|
|
228 |
/*
|
|
229 |
* Check and store the argument; allow each argument to be used only as
|
|
230 |
* one type even though printf allows multiple uses. The specification only
|
|
231 |
* allows one use, but we don't want to break existing functional code,
|
|
232 |
* even if it's buggy.
|
|
233 |
*/
|
|
234 |
#define STORE(buf, size, arg, val) if (arg * FORMAT_SIZE + 1 >= size ||\
|
|
235 |
(strict ? \
|
|
236 |
(buf[arg*FORMAT_SIZE] != '\0' && \
|
|
237 |
buf[arg*FORMAT_SIZE] != val) \
|
|
238 |
: \
|
|
239 |
(buf[arg*FORMAT_SIZE] == 'n'))) \
|
|
240 |
return (-1); \
|
|
241 |
else {\
|
|
242 |
if (arg >= maxarg) \
|
|
243 |
maxarg = arg + 1; \
|
|
244 |
narg++; \
|
|
245 |
buf[arg*FORMAT_SIZE] = val; \
|
|
246 |
}
|
|
247 |
|
|
248 |
/*
|
|
249 |
* This function extracts sprintf format into a canonical
|
|
250 |
* sprintf form. It's not as easy as just removing everything
|
|
251 |
* that isn't a format specifier, because of "%n$" specifiers.
|
|
252 |
* Ideally, this should be compatible with printf and not
|
|
253 |
* fail on bad formats.
|
|
254 |
* However, that makes writing a proper check_format that
|
|
255 |
* doesn't cause crashes a lot harder.
|
|
256 |
*/
|
|
257 |
|
|
258 |
static int
|
|
259 |
extract_format(const char *fmt, char *norm, size_t sz, int strict)
|
|
260 |
{
|
|
261 |
int narg = 0;
|
|
262 |
int t, arg, argp;
|
|
263 |
int dotseen;
|
|
264 |
char flag;
|
|
265 |
char conv;
|
|
266 |
int lastarg = -1;
|
|
267 |
int prevarg;
|
|
268 |
int maxarg = 0; /* Highest index seen + 1 */
|
|
269 |
int lflag;
|
|
270 |
|
|
271 |
(void) memset(norm, '\0', sz);
|
|
272 |
|
|
273 |
#ifdef DEBUG
|
|
274 |
printf("Format \"%s\" canonical form: ", fmt);
|
|
275 |
#endif
|
|
276 |
|
|
277 |
for (; *fmt; fmt++) {
|
|
278 |
if (*fmt == '%') {
|
|
279 |
if (*++fmt == '%')
|
|
280 |
continue;
|
|
281 |
|
|
282 |
if (*fmt == '\0')
|
|
283 |
break;
|
|
284 |
|
|
285 |
prevarg = lastarg;
|
|
286 |
arg = ++lastarg;
|
|
287 |
|
|
288 |
t = 0;
|
|
289 |
while (*fmt && isdigit(*fmt))
|
|
290 |
t = t * 10 + *fmt++ - '0';
|
|
291 |
|
|
292 |
if (*fmt == '$') {
|
|
293 |
lastarg = arg = t - 1;
|
|
294 |
fmt++;
|
|
295 |
}
|
|
296 |
|
|
297 |
if (*fmt == '\0')
|
|
298 |
goto end;
|
|
299 |
|
|
300 |
dotseen = 0;
|
|
301 |
flag = 0;
|
|
302 |
lflag = 0;
|
|
303 |
again:
|
|
304 |
/* Skip flags */
|
|
305 |
while (*fmt) {
|
|
306 |
switch (*fmt) {
|
|
307 |
case '\'':
|
|
308 |
case '+':
|
|
309 |
case '-':
|
|
310 |
case ' ':
|
|
311 |
case '#':
|
|
312 |
case '0':
|
|
313 |
fmt++;
|
|
314 |
continue;
|
|
315 |
}
|
|
316 |
break;
|
|
317 |
}
|
|
318 |
|
|
319 |
while (*fmt && isdigit(*fmt))
|
|
320 |
fmt++;
|
|
321 |
|
|
322 |
if (*fmt == '*') {
|
|
323 |
if (isdigit(fmt[1])) {
|
|
324 |
fmt++;
|
|
325 |
t = 0;
|
|
326 |
while (*fmt && isdigit(*fmt))
|
|
327 |
t = t * 10 + *fmt++ - '0';
|
|
328 |
|
|
329 |
if (*fmt == '$') {
|
|
330 |
argp = t - 1;
|
|
331 |
STORE(norm, sz, argp, '*');
|
|
332 |
}
|
|
333 |
/*
|
|
334 |
* If digits follow a '*', it is
|
|
335 |
* not loaded as an argument, the
|
|
336 |
* digits are used instead.
|
|
337 |
*/
|
|
338 |
} else {
|
|
339 |
/*
|
|
340 |
* Weird as it may seem, if we
|
|
341 |
* use an numbered argument, we
|
|
342 |
* get the next one if we have
|
|
343 |
* an unnumbered '*'
|
|
344 |
*/
|
|
345 |
if (fmt[1] == '$')
|
|
346 |
fmt++;
|
|
347 |
else {
|
|
348 |
argp = arg;
|
|
349 |
prevarg = arg;
|
|
350 |
lastarg = ++arg;
|
|
351 |
STORE(norm, sz, argp, '*');
|
|
352 |
}
|
|
353 |
}
|
|
354 |
fmt++;
|
|
355 |
}
|
|
356 |
|
|
357 |
/* Fail on two or more dots if we do strict checking */
|
|
358 |
if (*fmt == '.' || *fmt == '*') {
|
|
359 |
if (dotseen && strict)
|
|
360 |
return (-1);
|
|
361 |
dotseen = 1;
|
|
362 |
fmt++;
|
|
363 |
goto again;
|
|
364 |
}
|
|
365 |
|
|
366 |
if (*fmt == '\0')
|
|
367 |
goto end;
|
|
368 |
|
|
369 |
while (*fmt) {
|
|
370 |
switch (*fmt) {
|
|
371 |
case 'l':
|
|
372 |
if (!(flag & OPT_ll)) {
|
|
373 |
if (lflag) {
|
|
374 |
flag &= ~OPT_l;
|
|
375 |
flag |= OPT_ll;
|
|
376 |
} else {
|
|
377 |
flag |= OPT_l;
|
|
378 |
}
|
|
379 |
}
|
|
380 |
lflag++;
|
|
381 |
break;
|
|
382 |
case 'L':
|
|
383 |
flag |= OPT_L;
|
|
384 |
break;
|
|
385 |
case 'w':
|
|
386 |
flag |= OPT_w;
|
|
387 |
break;
|
|
388 |
case 'h':
|
|
389 |
if (flag & (OPT_h|OPT_hh))
|
|
390 |
flag |= OPT_hh;
|
|
391 |
else
|
|
392 |
flag |= OPT_h;
|
|
393 |
break;
|
|
394 |
case 'j':
|
|
395 |
flag |= OPT_j;
|
|
396 |
break;
|
|
397 |
case 'z':
|
|
398 |
case 't':
|
|
399 |
if (!(flag & OPT_ll)) {
|
|
400 |
flag |= OPT_l;
|
|
401 |
}
|
|
402 |
break;
|
|
403 |
case '\'':
|
|
404 |
case '+':
|
|
405 |
case '-':
|
|
406 |
case ' ':
|
|
407 |
case '#':
|
|
408 |
case '.':
|
|
409 |
case '*':
|
|
410 |
goto again;
|
|
411 |
default:
|
|
412 |
if (isdigit(*fmt))
|
|
413 |
goto again;
|
|
414 |
else
|
|
415 |
goto done;
|
|
416 |
}
|
|
417 |
fmt++;
|
|
418 |
}
|
|
419 |
done:
|
|
420 |
if (*fmt == '\0')
|
|
421 |
goto end;
|
|
422 |
|
|
423 |
switch (*fmt) {
|
|
424 |
case 'C':
|
|
425 |
flag |= OPT_l;
|
|
426 |
/* FALLTHROUGH */
|
|
427 |
case 'd':
|
|
428 |
case 'i':
|
|
429 |
case 'o':
|
|
430 |
case 'u':
|
|
431 |
case 'c':
|
|
432 |
case 'x':
|
|
433 |
case 'X':
|
|
434 |
conv = 'I';
|
|
435 |
break;
|
|
436 |
case 'e':
|
|
437 |
case 'E':
|
|
438 |
case 'f':
|
|
439 |
case 'F':
|
|
440 |
case 'a':
|
|
441 |
case 'A':
|
|
442 |
case 'g':
|
|
443 |
case 'G':
|
|
444 |
conv = 'D';
|
|
445 |
break;
|
|
446 |
case 'S':
|
|
447 |
flag |= OPT_l;
|
|
448 |
/* FALLTHROUGH */
|
|
449 |
case 's':
|
|
450 |
conv = 's';
|
|
451 |
break;
|
|
452 |
case 'p':
|
|
453 |
case 'n':
|
|
454 |
conv = *fmt;
|
|
455 |
break;
|
|
456 |
default:
|
|
457 |
lastarg = prevarg;
|
|
458 |
continue;
|
|
459 |
}
|
|
460 |
|
|
461 |
STORE(norm, sz, arg, conv);
|
|
462 |
norm[arg*FORMAT_SIZE + 1] = flag;
|
|
463 |
}
|
|
464 |
}
|
|
465 |
#ifdef DEBUG
|
|
466 |
for (t = 0; t < maxarg * FORMAT_SIZE; t += FORMAT_SIZE) {
|
|
467 |
printf("%c(%d)", norm[t], norm[t+1]);
|
|
468 |
}
|
|
469 |
putchar('\n');
|
|
470 |
#endif
|
|
471 |
end:
|
|
472 |
if (strict)
|
|
473 |
for (arg = 0; arg < maxarg; arg++)
|
|
474 |
if (norm[arg*FORMAT_SIZE] == '\0')
|
|
475 |
return (-1);
|
|
476 |
|
|
477 |
return (maxarg);
|
|
478 |
}
|
|
479 |
|
|
480 |
char *
|
|
481 |
check_format(const char *org, const char *new, int strict)
|
|
482 |
{
|
|
483 |
char *ofmt, *nfmt, *torg;
|
|
484 |
size_t osz, nsz;
|
|
485 |
int olen, nlen;
|
|
486 |
|
|
487 |
if (!org) {
|
|
488 |
/*
|
|
489 |
* Default message is NULL.
|
|
490 |
* dtmail uses NULL for default message.
|
|
491 |
*/
|
|
492 |
torg = "(NULL)";
|
|
493 |
} else {
|
|
494 |
torg = (char *)org;
|
|
495 |
}
|
|
496 |
|
|
497 |
/* Short cut */
|
|
498 |
if (org == new || strcmp(torg, new) == 0 ||
|
|
499 |
strchr(new, '%') == NULL)
|
|
500 |
return ((char *)new);
|
|
501 |
|
|
502 |
osz = strlen(torg) * FORMAT_SIZE;
|
|
503 |
ofmt = malloc(osz);
|
|
504 |
if (ofmt == NULL)
|
|
505 |
return ((char *)org);
|
|
506 |
|
|
507 |
olen = extract_format(torg, ofmt, osz, 0);
|
|
508 |
|
|
509 |
if (olen == -1)
|
|
510 |
syslog(LOG_AUTH|LOG_INFO,
|
|
511 |
"invalid format in gettext argument: \"%s\"", torg);
|
|
512 |
|
|
513 |
nsz = strlen(new) * FORMAT_SIZE;
|
|
514 |
nfmt = malloc(nsz);
|
|
515 |
if (nfmt == NULL) {
|
|
516 |
free(ofmt);
|
|
517 |
return ((char *)org);
|
|
518 |
}
|
|
519 |
|
|
520 |
nlen = extract_format(new, nfmt, nsz, strict);
|
|
521 |
|
|
522 |
if (nlen == -1) {
|
|
523 |
free(ofmt);
|
|
524 |
free(nfmt);
|
|
525 |
syslog(LOG_AUTH|LOG_NOTICE,
|
|
526 |
"invalid format in message file \"%.100s\" -> \"%s\"",
|
|
527 |
torg, new);
|
|
528 |
errno = EBADMSG;
|
|
529 |
return ((char *)org);
|
|
530 |
}
|
|
531 |
|
|
532 |
if (strict && (olen != nlen || olen == -1)) {
|
|
533 |
free(ofmt);
|
|
534 |
free(nfmt);
|
|
535 |
syslog(LOG_AUTH|LOG_NOTICE,
|
|
536 |
"incompatible format in message file: \"%.100s\" != \"%s\"",
|
|
537 |
torg, new);
|
|
538 |
errno = EBADMSG;
|
|
539 |
return ((char *)org);
|
|
540 |
}
|
|
541 |
|
|
542 |
if (strict && memcmp(ofmt, nfmt, nlen * FORMAT_SIZE) == 0) {
|
|
543 |
free(ofmt);
|
|
544 |
free(nfmt);
|
|
545 |
return ((char *)new);
|
|
546 |
} else {
|
|
547 |
if (!strict) {
|
|
548 |
char *n;
|
|
549 |
|
|
550 |
nlen *= FORMAT_SIZE;
|
|
551 |
|
|
552 |
for (n = nfmt; n = memchr(n, 'n', nfmt + nlen - n);
|
|
553 |
n++) {
|
|
554 |
int off = (n - nfmt);
|
|
555 |
|
|
556 |
if (off >= olen * FORMAT_SIZE ||
|
|
557 |
ofmt[off] != 'n' ||
|
|
558 |
ofmt[off+1] != nfmt[off+1]) {
|
|
559 |
free(ofmt);
|
|
560 |
free(nfmt);
|
|
561 |
syslog(LOG_AUTH|LOG_NOTICE,
|
|
562 |
"dangerous format in message file: "
|
|
563 |
"\"%.100s\" -> \"%s\"", torg, new);
|
|
564 |
errno = EBADMSG;
|
|
565 |
return ((char *)org);
|
|
566 |
}
|
|
567 |
}
|
|
568 |
free(ofmt);
|
|
569 |
free(nfmt);
|
|
570 |
return ((char *)new);
|
|
571 |
}
|
|
572 |
free(ofmt);
|
|
573 |
free(nfmt);
|
|
574 |
syslog(LOG_AUTH|LOG_NOTICE,
|
|
575 |
"incompatible format in message file \"%.100s\" != \"%s\"",
|
|
576 |
torg, new);
|
|
577 |
errno = EBADMSG;
|
|
578 |
return ((char *)org);
|
|
579 |
}
|
|
580 |
}
|
|
581 |
|
|
582 |
/*
|
|
583 |
* s1 is either name, or name=value
|
|
584 |
* s2 is name=value
|
|
585 |
* if names match, return value of s2, else NULL
|
|
586 |
* used for environment searching: see getenv
|
|
587 |
*/
|
|
588 |
const char *
|
|
589 |
nvmatch(const char *s1, const char *s2)
|
|
590 |
{
|
|
591 |
while (*s1 == *s2++)
|
|
592 |
if (*s1++ == '=')
|
|
593 |
return (s2);
|
|
594 |
if (*s1 == '\0' && *(s2-1) == '=')
|
|
595 |
return (s2);
|
|
596 |
return (NULL);
|
|
597 |
}
|
|
598 |
|
|
599 |
/*
|
|
600 |
* Handle NLSPATH environment variables in the environment.
|
|
601 |
* This routine is hooked into getenv/putenv at first call.
|
|
602 |
*
|
|
603 |
* The intention is to ignore NLSPATH in set-uid applications,
|
|
604 |
* and determine whether the NLSPATH in an application was set
|
|
605 |
* by the applications or derived from the user's environment.
|
|
606 |
*/
|
|
607 |
|
|
608 |
void
|
|
609 |
clean_env(void)
|
|
610 |
{
|
|
611 |
const char **p;
|
|
612 |
|
|
613 |
/* Find the first NLSPATH occurrence */
|
|
614 |
for (p = environ; *p; p++)
|
|
615 |
if (**p == 'N' && nvmatch("NLSPATH", *p) != NULL)
|
|
616 |
break;
|
|
617 |
|
|
618 |
if (!*p) /* None found, we're safe */
|
|
619 |
nlspath_safe = 1;
|
|
620 |
else if (issetugid()) { /* Found and set-uid, clean */
|
|
621 |
int off = 1;
|
|
622 |
|
|
623 |
for (p++; (p[-off] = p[0]) != '\0'; p++)
|
|
624 |
if (**p == 'N' && nvmatch("NLSPATH", *p) != NULL)
|
|
625 |
off++;
|
|
626 |
|
|
627 |
nlspath_safe = 1;
|
|
628 |
}
|
|
629 |
}
|