/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
#include "lint.h"
#include "mtlib.h"
#include <string.h>
#include <syslog.h>
#include <fcntl.h>
#include <limits.h>
#include <unistd.h>
#include <stdlib.h>
#include <thread.h>
#include <synch.h>
#include <ctype.h>
#include <errno.h>
#include "libc.h"
#include "nlspath_checks.h"
extern const char **_environ;
/*
* We want to prevent the use of NLSPATH by setugid applications but
* not completely. CDE depends on this very much.
* Yes, this is ugly.
*/
struct trusted_systemdirs {
const char *dir;
};
{ NULL, 0 }
};
/*
* Routine to check the safety of a messages file.
* When the program specifies a pathname and doesn't
* use NLSPATH, it should specify the "safe" flag as 1.
* Most checks will be disabled then.
* fstat64 is done here and the stat structure is returned
* to prevent duplication of system calls.
*
* The trust return value contains an indication of
* trustworthiness (i.e., does check_format need to be called or
* not)
*/
int
{
int fd;
int trust_path;
int systemdir = 0;
int abs_path = 0;
int trust_owner = 0;
int trust_group = 0;
const struct trusted_systemdirs *p;
/*
* If SAFE_F has been specified or NLSPATH is safe (or not set),
* set trust_path and trust the file as an initial value.
*/
if (fd < 0)
return (-1);
return (-1);
}
/*
* Trust only files owned by root or bin (uid 2), except
* when specified as full path or when NLSPATH is known to
* be safe.
* Don't trust files writable by other or writable
* by non-bin, non-root system group.
* Don't trust these files even if the path is correct.
* we hardcode them here for now.
*/
/*
* if the path is absolute and does not contain "/../",
* set abs_path.
*/
abs_path = 1;
/*
* if the path belongs to the trusted system directory,
* set systemdir.
*/
systemdir = 1;
break;
}
}
}
/*
* If the owner is root or bin, set trust_owner.
*/
trust_owner = 1;
}
/*
* If the file is neither other-writable nor group-writable by
* non-bin and non-root system group, set trust_group.
*/
trust_group = 1;
}
/*
* Even if UNSAFE_F has been specified and unsafe-NLSPATH
* has been set, trust the file as long as it belongs to
* the trusted system directory.
*/
*trust = 1;
}
/*
* If:
* file is not a full pathname,
* or
* neither trust_owner nor trust_path is set,
* or
* trust_group is not set,
* untrust it.
*/
if (*trust &&
*trust = 0;
}
/*
* If set[ug]id process, open for the untrusted file should fail.
* Otherwise, the message extracted from the untrusted file
* will have to be checked by check_format().
*/
if (issetugid()) {
if (!*trust) {
/*
* Open should fail
*/
return (-1);
}
/*
* if the path does not belong to the trusted system directory
* or if the owner is neither root nor bin, untrust it.
*/
if (!systemdir || !trust_owner) {
*trust = 0;
}
}
return (fd);
}
/*
* Extract a format into a normalized format string.
* Returns the number of arguments converted, -1 on error.
* The string norm should contain 2N bytes; an upperbound is the
* length of the format string.
* The canonical format consists of two chars: one is the conversion
* character (s, c, d, x, etc), the second one is the option flag.
* L, ll, l, w as defined below.
* A special conversion character, '*', indicates that the argument
* is used as a precision specifier.
*/
/* Number of bytes per canonical format entry */
/*
* Check and store the argument; allow each argument to be used only as
* one type even though printf allows multiple uses. The specification only
* allows one use, but we don't want to break existing functional code,
* even if it's buggy.
*/
(strict ? \
: \
return (-1); \
else {\
narg++; \
}
/*
* This function extracts sprintf format into a canonical
* sprintf form. It's not as easy as just removing everything
* that isn't a format specifier, because of "%n$" specifiers.
* Ideally, this should be compatible with printf and not
* fail on bad formats.
* However, that makes writing a proper check_format that
* doesn't cause crashes a lot harder.
*/
static int
{
int narg = 0;
int dotseen;
char flag;
char conv;
int prevarg;
int lflag;
#ifdef DEBUG
#endif
if (*fmt == '%') {
if (*++fmt == '%')
continue;
if (*fmt == '\0')
break;
t = 0;
if (*fmt == '$') {
fmt++;
}
if (*fmt == '\0')
goto end;
dotseen = 0;
flag = 0;
lflag = 0;
/* Skip flags */
while (*fmt) {
switch (*fmt) {
case '\'':
case '+':
case '-':
case ' ':
case '#':
case '0':
fmt++;
continue;
}
break;
}
fmt++;
if (*fmt == '*') {
fmt++;
t = 0;
if (*fmt == '$') {
argp = t - 1;
}
/*
* If digits follow a '*', it is
* not loaded as an argument, the
* digits are used instead.
*/
} else {
/*
* Weird as it may seem, if we
* use an numbered argument, we
* get the next one if we have
* an unnumbered '*'
*/
fmt++;
else {
}
}
fmt++;
}
/* Fail on two or more dots if we do strict checking */
return (-1);
dotseen = 1;
fmt++;
goto again;
}
if (*fmt == '\0')
goto end;
while (*fmt) {
switch (*fmt) {
case 'l':
if (lflag) {
} else {
}
}
lflag++;
break;
case 'L':
break;
case 'w':
break;
case 'h':
else
break;
case 'j':
break;
case 'z':
case 't':
}
break;
case '\'':
case '+':
case '-':
case ' ':
case '#':
case '.':
case '*':
goto again;
default:
goto again;
else
goto done;
}
fmt++;
}
done:
if (*fmt == '\0')
goto end;
switch (*fmt) {
case 'C':
/* FALLTHROUGH */
case 'd':
case 'i':
case 'o':
case 'u':
case 'c':
case 'x':
case 'X':
conv = 'I';
break;
case 'e':
case 'E':
case 'f':
case 'F':
case 'a':
case 'A':
case 'g':
case 'G':
conv = 'D';
break;
case 'S':
/* FALLTHROUGH */
case 's':
conv = 's';
break;
case 'p':
case 'n':
break;
default:
continue;
}
}
}
#ifdef DEBUG
}
putchar('\n');
#endif
end:
if (strict)
return (-1);
return (maxarg);
}
char *
{
if (!org) {
/*
* Default message is NULL.
* dtmail uses NULL for default message.
*/
torg = "(NULL)";
} else {
}
/* Short cut */
return ((char *)new);
return ((char *)org);
if (olen == -1)
"invalid format in gettext argument: \"%s\"", torg);
return ((char *)org);
}
if (nlen == -1) {
"invalid format in message file \"%.100s\" -> \"%s\"",
return ((char *)org);
}
"incompatible format in message file: \"%.100s\" != \"%s\"",
return ((char *)org);
}
return ((char *)new);
} else {
if (!strict) {
char *n;
nlen *= FORMAT_SIZE;
n++) {
"dangerous format in message file: "
return ((char *)org);
}
}
return ((char *)new);
}
"incompatible format in message file \"%.100s\" != \"%s\"",
return ((char *)org);
}
}
/*
* s1 is either name, or name=value
* s2 is name=value
* if names match, return value of s2, else NULL
* used for environment searching: see getenv
*/
const char *
{
if (*s1++ == '=')
return (s2);
return (s2);
return (NULL);
}
/*
* Handle NLSPATH environment variables in the environment.
*
* The intention is to ignore NLSPATH in set-uid applications,
* and determine whether the NLSPATH in an application was set
* by the applications or derived from the user's environment.
*/
void
clean_env(void)
{
const char **p;
/* can happen when processing a SunOS 4.x AOUT file */
nlspath_safe = 1;
return;
}
/* Find the first NLSPATH occurrence */
for (p = _environ; *p; p++)
break;
if (!*p) /* None found, we're safe */
nlspath_safe = 1;
else if (issetugid()) { /* Found and set-uid, clean */
for (p++; (p[-off] = p[0]) != '\0'; p++)
off++;
nlspath_safe = 1;
}
}