4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
23 * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
27 #pragma ident "%Z%%M% %I% %E% SMI"
43 #include "nlspath_checks.h"
45 extern const char **_environ
;
48 * We want to prevent the use of NLSPATH by setugid applications but
49 * not completely. CDE depends on this very much.
53 struct trusted_systemdirs
{
58 #define _USRLIB "/usr/lib/"
59 #define _USRDT "/usr/dt/"
60 #define _USROW "/usr/openwin/"
62 static const struct trusted_systemdirs prefix
[] = {
63 { _USRLIB
, sizeof (_USRLIB
) - 1 },
64 { _USRDT
, sizeof (_USRDT
) - 1 },
65 { _USROW
, sizeof (_USROW
) - 1 },
69 static int8_t nlspath_safe
;
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.
79 * The trust return value contains an indication of
80 * trustworthiness (i.e., does check_format need to be called or
85 nls_safe_open(const char *path
, struct stat64
*statbuf
, int *trust
, int safe
)
93 const struct trusted_systemdirs
*p
;
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.
99 trust_path
= *trust
= safe
|| nlspath_safe
;
101 fd
= open(path
, O_RDONLY
);
106 if (fstat64(fd
, statbuf
) == -1) {
112 * Trust only files owned by root or bin (uid 2), except
113 * when specified as full path or when NLSPATH is known to
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.
123 * if the path is absolute and does not contain "/../",
126 if (*path
== '/' && strstr(path
, "/../") == NULL
) {
129 * if the path belongs to the trusted system directory,
132 for (p
= prefix
; p
->dir
; p
++) {
133 if (strncmp(p
->dir
, path
, p
->dirlen
) == 0) {
141 * If the owner is root or bin, set trust_owner.
143 if (statbuf
->st_uid
== 0 || statbuf
->st_uid
== 2) {
147 * If the file is neither other-writable nor group-writable by
148 * non-bin and non-root system group, set trust_group.
150 if ((statbuf
->st_mode
& (S_IWOTH
)) == 0 &&
151 ((statbuf
->st_mode
& (S_IWGRP
)) == 0 ||
152 (statbuf
->st_gid
< 4 && statbuf
->st_gid
!= 1))) {
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.
161 if (!*trust
&& systemdir
) {
167 * file is not a full pathname,
169 * neither trust_owner nor trust_path is set,
171 * trust_group is not set,
175 (!abs_path
|| (!trust_owner
&& !trust_path
) || !trust_group
)) {
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().
194 * if the path does not belong to the trusted system directory
195 * or if the owner is neither root nor bin, untrust it.
197 if (!systemdir
|| !trust_owner
) {
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.
225 /* Number of bytes per canonical format entry */
226 #define FORMAT_SIZE 2
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.
234 #define STORE(buf, size, arg, val) if (arg * FORMAT_SIZE + 1 >= size ||\
236 (buf[arg*FORMAT_SIZE] != '\0' && \
237 buf[arg*FORMAT_SIZE] != val) \
239 (buf[arg*FORMAT_SIZE] == 'n'))) \
245 buf[arg*FORMAT_SIZE] = val; \
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.
259 extract_format(const char *fmt
, char *norm
, size_t sz
, int strict
)
268 int maxarg
= 0; /* Highest index seen + 1 */
271 (void) memset(norm
, '\0', sz
);
274 printf("Format \"%s\" canonical form: ", fmt
);
277 for (; *fmt
; fmt
++) {
289 while (*fmt
&& isdigit(*fmt
))
290 t
= t
* 10 + *fmt
++ - '0';
293 lastarg
= arg
= t
- 1;
319 while (*fmt
&& isdigit(*fmt
))
323 if (isdigit(fmt
[1])) {
326 while (*fmt
&& isdigit(*fmt
))
327 t
= t
* 10 + *fmt
++ - '0';
331 STORE(norm
, sz
, argp
, '*');
334 * If digits follow a '*', it is
335 * not loaded as an argument, the
336 * digits are used instead.
340 * Weird as it may seem, if we
341 * use an numbered argument, we
342 * get the next one if we have
351 STORE(norm
, sz
, argp
, '*');
357 /* Fail on two or more dots if we do strict checking */
358 if (*fmt
== '.' || *fmt
== '*') {
359 if (dotseen
&& strict
)
372 if (!(flag
& OPT_ll
)) {
389 if (flag
& (OPT_h
|OPT_hh
))
399 if (!(flag
& OPT_ll
)) {
461 STORE(norm
, sz
, arg
, conv
);
462 norm
[arg
*FORMAT_SIZE
+ 1] = flag
;
466 for (t
= 0; t
< maxarg
* FORMAT_SIZE
; t
+= FORMAT_SIZE
) {
467 printf("%c(%d)", norm
[t
], norm
[t
+1]);
473 for (arg
= 0; arg
< maxarg
; arg
++)
474 if (norm
[arg
*FORMAT_SIZE
] == '\0')
481 check_format(const char *org
, const char *new, int strict
)
483 char *ofmt
, *nfmt
, *torg
;
489 * Default message is NULL.
490 * dtmail uses NULL for default message.
498 if (org
== new || strcmp(torg
, new) == 0 ||
499 strchr(new, '%') == NULL
)
500 return ((char *)new);
502 osz
= strlen(torg
) * FORMAT_SIZE
;
505 return ((char *)org
);
507 olen
= extract_format(torg
, ofmt
, osz
, 0);
510 syslog(LOG_AUTH
|LOG_INFO
,
511 "invalid format in gettext argument: \"%s\"", torg
);
513 nsz
= strlen(new) * FORMAT_SIZE
;
517 return ((char *)org
);
520 nlen
= extract_format(new, nfmt
, nsz
, strict
);
525 syslog(LOG_AUTH
|LOG_NOTICE
,
526 "invalid format in message file \"%.100s\" -> \"%s\"",
529 return ((char *)org
);
532 if (strict
&& (olen
!= nlen
|| olen
== -1)) {
535 syslog(LOG_AUTH
|LOG_NOTICE
,
536 "incompatible format in message file: \"%.100s\" != \"%s\"",
539 return ((char *)org
);
542 if (strict
&& memcmp(ofmt
, nfmt
, nlen
* FORMAT_SIZE
) == 0) {
545 return ((char *)new);
552 for (n
= nfmt
; n
= memchr(n
, 'n', nfmt
+ nlen
- n
);
554 int off
= (n
- nfmt
);
556 if (off
>= olen
* FORMAT_SIZE
||
558 ofmt
[off
+1] != nfmt
[off
+1]) {
561 syslog(LOG_AUTH
|LOG_NOTICE
,
562 "dangerous format in message file: "
563 "\"%.100s\" -> \"%s\"", torg
, new);
565 return ((char *)org
);
570 return ((char *)new);
574 syslog(LOG_AUTH
|LOG_NOTICE
,
575 "incompatible format in message file \"%.100s\" != \"%s\"",
578 return ((char *)org
);
583 * s1 is either name, or name=value
585 * if names match, return value of s2, else NULL
586 * used for environment searching: see getenv
589 nvmatch(const char *s1
, const char *s2
)
594 if (*s1
== '\0' && *(s2
-1) == '=')
600 * Handle NLSPATH environment variables in the environment.
601 * This routine is hooked into getenv/putenv at first call.
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.
613 if (_environ
== NULL
) {
614 /* can happen when processing a SunOS 4.x AOUT file */
619 /* Find the first NLSPATH occurrence */
620 for (p
= _environ
; *p
; p
++)
621 if (**p
== 'N' && nvmatch("NLSPATH", *p
) != NULL
)
624 if (!*p
) /* None found, we're safe */
626 else if (issetugid()) { /* Found and set-uid, clean */
629 for (p
++; (p
[-off
] = p
[0]) != '\0'; p
++)
630 if (**p
== 'N' && nvmatch("NLSPATH", *p
) != NULL
)