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
68 static unsigned long
rk_getauxval(unsigned long type)69 rk_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 */
91 ROKEN_LIB_FUNCTION int ROKEN_LIB_CALL
issuid(void)92 issuid(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