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