1/*	$NetBSD: issuid.c,v 1.2 2017/01/28 21:31:50 christos Exp $	*/
2
3/*
4 * Copyright (c) 1998 - 2001 Kungliga Tekniska H��gskolan
5 * (Royal Institute of Technology, Stockholm, Sweden).
6 * All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 *
12 * 1. Redistributions of source code must retain the above copyright
13 *    notice, this list of conditions and the following disclaimer.
14 *
15 * 2. Redistributions in binary form must reproduce the above copyright
16 *    notice, this list of conditions and the following disclaimer in the
17 *    documentation and/or other materials provided with the distribution.
18 *
19 * 3. Neither the name of the Institute nor the names of its contributors
20 *    may be used to endorse or promote products derived from this software
21 *    without specific prior written permission.
22 *
23 * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
24 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26 * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
27 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33 * SUCH DAMAGE.
34 */
35
36#include <config.h>
37
38#ifdef HAVE_SYS_AUXV_H
39#include <sys/auxv.h>
40#endif
41
42#include <errno.h>
43
44#include <krb5/roken.h>
45
46/* NetBSD calls AT_UID AT_RUID.  Everyone else calls it AT_UID. */
47#if defined(AT_EUID) && defined(AT_RUID) && !defined(AT_UID)
48#define AT_UID AT_RUID
49#endif
50#if defined(AT_EGID) && defined(AT_RGID) && !defined(AT_GID)
51#define AT_GID AT_RGID
52#endif
53
54#ifdef __GLIBC__
55#ifdef __GLIBC_PREREQ
56#define HAVE_GLIBC_API_VERSION_SUPPORT(maj, min) __GLIBC_PREREQ(maj, min)
57#else
58#define HAVE_GLIBC_API_VERSION_SUPPORT(maj, min) \
59    ((__GLIBC << 16) + GLIBC_MINOR >= ((maj) << 16) + (min))
60#endif
61
62#if HAVE_GLIBC_API_VERSION_SUPPORT(2, 19)
63#define GETAUXVAL_SETS_ERRNO
64#endif
65#endif
66
67#ifdef HAVE_GETAUXVAL
68static unsigned long
69rk_getauxval(unsigned long type)
70{
71    errno = 0;
72#ifdef GETAUXVAL_SETS_ERRNO
73    return getauxval(type);
74#else
75    unsigned long ret = getauxval(type);
76
77    if (ret == 0)
78        errno = ENOENT;
79    return ret;
80#endif
81}
82#define USE_RK_GETAUXVAL
83#endif
84
85/**
86 * Returns non-zero if the caller's process started as set-uid or
87 * set-gid (and therefore the environment cannot be trusted).
88 *
89 * @return Non-zero if the environment is not trusted.
90 */
91ROKEN_LIB_FUNCTION int ROKEN_LIB_CALL
92issuid(void)
93{
94    /*
95     * We want to use issetugid(), but issetugid() is not the same on
96     * all OSes.
97     *
98     * On Illumos derivatives, OpenBSD, and Solaris issetugid() returns
99     * true IFF the program exec()ed was set-uid or set-gid.
100     *
101     * On NetBSD and FreeBSD issetugid() returns true if the program
102     * exec()ed was set-uid or set-gid, or if the process has switched
103     * UIDs/GIDs or otherwise changed privileges or is a descendant of
104     * such a process and has not exec()ed since.
105     *
106     * What we want here is to know only if the program exec()ed was
107     * set-uid or set-gid, so we can decide whether to trust the
108     * enviroment variables.  We don't care if this was a process that
109     * started as root and later changed UIDs/privs whatever: since it
110     * started out as privileged, it inherited an environment from a
111     * privileged pre-exec self, and so on, so the environment is
112     * trusted.
113     *
114     * Therefore the FreeBSD/NetBSD issetugid() does us no good.
115     *
116     * Linux, meanwhile, has no issetugid() (at least glibc doesn't
117     * anyways).
118     *
119     * Systems that support ELF put an "auxilliary vector" on the stack
120     * prior to starting the RTLD, and this vector includes (optionally)
121     * information about the process' EUID, RUID, EGID, RGID, and so on
122     * at the time of exec(), which we can use to construct proper
123     * issetugid() functionality.
124     *
125     * Where available, we use the ELF auxilliary vector as a fallback
126     * if issetugid() is not available.
127     *
128     * All of this is as of late March 2015, and might become stale in
129     * the future.
130     */
131
132#ifdef USE_RK_GETAUXVAL
133    /* If we have getauxval(), use that */
134
135#if (defined(AT_EUID) && defined(AT_UID) || (defined(AT_EGID) && defined(AT_GID)))
136    int seen = 0;
137#endif
138
139#if defined(AT_EUID) && defined(AT_UID)
140    {
141        unsigned long euid;
142        unsigned long uid;
143
144        euid = rk_getauxval(AT_EUID);
145        if (errno == 0)
146            seen |= 1;
147        uid = rk_getauxval(AT_UID);
148        if (errno == 0)
149            seen |= 2;
150        if (euid != uid)
151            return 1;
152    }
153#endif
154#if defined(AT_EGID) && defined(AT_GID)
155    {
156        unsigned long egid;
157        unsigned long gid;
158
159        egid = rk_getauxval(AT_EGID);
160        if (errno == 0)
161            seen |= 4;
162        gid = rk_getauxval(AT_GID);
163        if (errno == 0)
164            seen |= 8;
165        if (egid != gid)
166            return 2;
167    }
168#endif
169#ifdef AT_SECURE
170    /* AT_SECURE is set if the program was set-id. */
171    if (rk_getauxval(AT_SECURE) != 0)
172        return 1;
173#endif
174
175#if (defined(AT_EUID) && defined(AT_UID) || (defined(AT_EGID) && defined(AT_GID)))
176    if (seen == 15)
177        return 0;
178#endif
179
180    /* rk_getauxval() does set errno */
181    if (errno == 0)
182        return 0;
183    /*
184     * Fall through if we have getauxval() but we didn't have (or don't
185     * know if we don't have) the aux entries that we needed.
186     */
187#endif /* USE_RK_GETAUXVAL */
188
189#if defined(HAVE_ISSETUGID)
190    /*
191     * If we have issetugid(), use it.
192     *
193     * We may lose on some BSDs.  This manifests as, for example,
194     * gss_store_cred() not honoring KRB5CCNAME.
195     */
196    return issetugid();
197#endif /* USE_RK_GETAUXVAL */
198
199    /*
200     * Paranoia: for extra safety we ought to default to returning 1.
201     * But who knows what that might break where users link statically
202     * and use a.out, say.  Also, on Windows we should always return 0.
203     *
204     * For now we stick to returning zero by default.
205     */
206
207#if defined(HAVE_GETUID) && defined(HAVE_GETEUID)
208    if (getuid() != geteuid())
209	return 1;
210#endif
211#if defined(HAVE_GETGID) && defined(HAVE_GETEGID)
212    if (getgid() != getegid())
213	return 2;
214#endif
215
216    return 0;
217}
218