1 /*
2 * SPDX-License-Identifier: ISC
3 *
4 * Copyright (c) 2010, 2012-2014 Todd C. Miller <Todd.Miller@sudo.ws>
5 *
6 * Permission to use, copy, modify, and distribute this software for any
7 * purpose with or without fee is hereby granted, provided that the above
8 * copyright notice and this permission notice appear in all copies.
9 *
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 */
18
19 /*
20 * This is an open source non-commercial project. Dear PVS-Studio, please check it.
21 * PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com
22 */
23
24 #include <config.h>
25
26 #include <stdio.h>
27 #include <stdlib.h>
28 #include <string.h>
29 #if defined(HAVE_SHL_LOAD)
30 # include <dl.h>
31 #elif defined(HAVE_DLOPEN)
32 # include <dlfcn.h>
33 #endif
34 #include <errno.h>
35
36 #include "sudo_compat.h"
37 #include "sudo_dso.h"
38
39 /*
40 * Pointer for statically compiled symbols.
41 */
42 static struct sudo_preload_table *preload_table;
43
44 void
sudo_dso_preload_table_v1(struct sudo_preload_table * table)45 sudo_dso_preload_table_v1(struct sudo_preload_table *table)
46 {
47 preload_table = table;
48 }
49
50 #if defined(HAVE_SHL_LOAD)
51
52 # ifndef DYNAMIC_PATH
53 # define DYNAMIC_PATH 0
54 # endif
55
56 void *
sudo_dso_load_v1(const char * path,int mode)57 sudo_dso_load_v1(const char *path, int mode)
58 {
59 struct sudo_preload_table *pt;
60 int flags = DYNAMIC_PATH | BIND_VERBOSE;
61
62 if (mode == 0)
63 mode = SUDO_DSO_LAZY; /* default behavior */
64
65 /* Check prelinked symbols first. */
66 if (preload_table != NULL) {
67 for (pt = preload_table; pt->handle != NULL; pt++) {
68 if (pt->path != NULL && strcmp(path, pt->path) == 0)
69 return pt->handle;
70 }
71 }
72
73 /* We don't support SUDO_DSO_GLOBAL or SUDO_DSO_LOCAL yet. */
74 if (ISSET(mode, SUDO_DSO_LAZY))
75 flags |= BIND_DEFERRED;
76 if (ISSET(mode, SUDO_DSO_NOW))
77 flags |= BIND_IMMEDIATE;
78
79 return (void *)shl_load(path, flags, 0L);
80 }
81
82 int
sudo_dso_unload_v1(void * handle)83 sudo_dso_unload_v1(void *handle)
84 {
85 struct sudo_preload_table *pt;
86
87 /* Check prelinked symbols first. */
88 if (preload_table != NULL) {
89 for (pt = preload_table; pt->handle != NULL; pt++) {
90 if (pt->handle == handle)
91 return 0;
92 }
93 }
94
95 return shl_unload((shl_t)handle);
96 }
97
98 void *
sudo_dso_findsym_v1(void * vhandle,const char * symbol)99 sudo_dso_findsym_v1(void *vhandle, const char *symbol)
100 {
101 struct sudo_preload_table *pt;
102 shl_t handle = vhandle;
103 void *value = NULL;
104
105 /* Check prelinked symbols first. */
106 if (preload_table != NULL) {
107 for (pt = preload_table; pt->handle != NULL; pt++) {
108 if (pt->handle == handle) {
109 struct sudo_preload_symbol *sym;
110 for (sym = pt->symbols; sym->name != NULL; sym++) {
111 if (strcmp(sym->name, symbol) == 0)
112 return sym->addr;
113 }
114 errno = ENOENT;
115 return NULL;
116 }
117 }
118 }
119
120 /*
121 * Note that the behavior of of SUDO_DSO_NEXT and SUDO_DSO_SELF
122 * differs from most implementations when called from
123 * a shared library.
124 */
125 if (vhandle == SUDO_DSO_NEXT) {
126 /* Iterate over all shared libs looking for symbol. */
127 shl_t myhandle = PROG_HANDLE;
128 struct shl_descriptor *desc;
129 int idx = 0;
130
131 /* Find program's real handle. */
132 if (shl_gethandle(PROG_HANDLE, &desc) == 0)
133 myhandle = desc->handle;
134 while (shl_get(idx++, &desc) == 0) {
135 if (desc->handle == myhandle)
136 continue;
137 if (shl_findsym(&desc->handle, symbol, TYPE_UNDEFINED, &value) == 0)
138 break;
139 }
140 } else {
141 if (vhandle == SUDO_DSO_DEFAULT)
142 handle = NULL;
143 else if (vhandle == SUDO_DSO_SELF)
144 handle = PROG_HANDLE;
145 (void)shl_findsym(&handle, symbol, TYPE_UNDEFINED, &value);
146 }
147
148 return value;
149 }
150
151 char *
sudo_dso_strerror_v1(void)152 sudo_dso_strerror_v1(void)
153 {
154 return strerror(errno);
155 }
156
157 #elif defined(HAVE_DLOPEN)
158
159 # ifndef RTLD_GLOBAL
160 # define RTLD_GLOBAL 0
161 # endif
162
163 /* Default member names for AIX when dlopen()ing an ar (.a) file. */
164 # ifdef RTLD_MEMBER
165 # ifdef __LP64__
166 # define SUDO_DSO_MEMBER "shr_64.o"
167 # else
168 # define SUDO_DSO_MEMBER "shr.o"
169 # endif
170 # endif
171
172 void *
sudo_dso_load_v1(const char * path,int mode)173 sudo_dso_load_v1(const char *path, int mode)
174 {
175 struct sudo_preload_table *pt;
176 int flags = 0;
177 void *ret;
178 #ifdef RTLD_MEMBER
179 char *cp;
180 #endif
181
182 /* Check prelinked symbols first. */
183 if (preload_table != NULL) {
184 for (pt = preload_table; pt->handle != NULL; pt++) {
185 if (pt->path != NULL && strcmp(path, pt->path) == 0)
186 return pt->handle;
187 }
188 }
189
190 /* Map SUDO_DSO_* -> RTLD_* */
191 if (ISSET(mode, SUDO_DSO_LAZY))
192 SET(flags, RTLD_LAZY);
193 if (ISSET(mode, SUDO_DSO_NOW))
194 SET(flags, RTLD_NOW);
195 if (ISSET(mode, SUDO_DSO_GLOBAL))
196 SET(flags, RTLD_GLOBAL);
197 if (ISSET(mode, SUDO_DSO_LOCAL))
198 SET(flags, RTLD_LOCAL);
199
200 #ifdef RTLD_MEMBER
201 /* Check for AIX path(module) syntax and add RTLD_MEMBER for a module. */
202 cp = strrchr(path, '(');
203 if (cp != NULL) {
204 size_t len = strlen(cp);
205 if (len > 2 && cp[len - 1] == '\0')
206 SET(flags, RTLD_MEMBER);
207 }
208 #endif /* RTLD_MEMBER */
209 ret = dlopen(path, flags);
210 #ifdef RTLD_MEMBER
211 /*
212 * If we try to dlopen() an AIX .a file without an explicit member
213 * it will fail with ENOEXEC. Try again using the default member.
214 */
215 if (ret == NULL && !ISSET(flags, RTLD_MEMBER) && errno == ENOEXEC) {
216 if (asprintf(&cp, "%s(%s)", path, SUDO_DSO_MEMBER) != -1) {
217 ret = dlopen(cp, flags|RTLD_MEMBER);
218 free(cp);
219 }
220 }
221 #endif /* RTLD_MEMBER */
222
223 return ret;
224 }
225
226 int
sudo_dso_unload_v1(void * handle)227 sudo_dso_unload_v1(void *handle)
228 {
229 struct sudo_preload_table *pt;
230
231 /* Check prelinked symbols first. */
232 if (preload_table != NULL) {
233 for (pt = preload_table; pt->handle != NULL; pt++) {
234 if (pt->handle == handle)
235 return 0;
236 }
237 }
238
239 return dlclose(handle);
240 }
241
242 void *
sudo_dso_findsym_v1(void * handle,const char * symbol)243 sudo_dso_findsym_v1(void *handle, const char *symbol)
244 {
245 struct sudo_preload_table *pt;
246
247 /* Check prelinked symbols first. */
248 if (preload_table != NULL) {
249 for (pt = preload_table; pt->handle != NULL; pt++) {
250 if (pt->handle == handle) {
251 struct sudo_preload_symbol *sym;
252 for (sym = pt->symbols; sym->name != NULL; sym++) {
253 if (strcmp(sym->name, symbol) == 0)
254 return sym->addr;
255 }
256 errno = ENOENT;
257 return NULL;
258 }
259 }
260 }
261
262 /*
263 * Not all implementations support the special handles.
264 */
265 if (handle == SUDO_DSO_NEXT) {
266 # ifdef RTLD_NEXT
267 handle = RTLD_NEXT;
268 # else
269 errno = ENOENT;
270 return NULL;
271 # endif
272 } else if (handle == SUDO_DSO_DEFAULT) {
273 # ifdef RTLD_DEFAULT
274 handle = RTLD_DEFAULT;
275 # else
276 errno = ENOENT;
277 return NULL;
278 # endif
279 } else if (handle == SUDO_DSO_SELF) {
280 # ifdef RTLD_SELF
281 handle = RTLD_SELF;
282 # else
283 errno = ENOENT;
284 return NULL;
285 # endif
286 }
287
288 return dlsym(handle, symbol);
289 }
290
291 char *
sudo_dso_strerror_v1(void)292 sudo_dso_strerror_v1(void)
293 {
294 return dlerror();
295 }
296
297 #else /* !HAVE_SHL_LOAD && !HAVE_DLOPEN */
298
299 /*
300 * Emulate dlopen() using a static list of symbols compiled into sudo.
301 */
302 void *
sudo_dso_load_v1(const char * path,int mode)303 sudo_dso_load_v1(const char *path, int mode)
304 {
305 struct sudo_preload_table *pt;
306
307 /* Check prelinked symbols first. */
308 if (preload_table != NULL) {
309 for (pt = preload_table; pt->handle != NULL; pt++) {
310 if (pt->path != NULL && strcmp(path, pt->path) == 0)
311 return pt->handle;
312 }
313 }
314 return NULL;
315 }
316
317 int
sudo_dso_unload_v1(void * handle)318 sudo_dso_unload_v1(void *handle)
319 {
320 struct sudo_preload_table *pt;
321
322 if (preload_table != NULL) {
323 for (pt = preload_table; pt->handle != NULL; pt++) {
324 if (pt->handle == handle)
325 return 0;
326 }
327 }
328 return -1;
329 }
330
331 void *
sudo_dso_findsym_v1(void * handle,const char * symbol)332 sudo_dso_findsym_v1(void *handle, const char *symbol)
333 {
334 struct sudo_preload_table *pt;
335
336 if (preload_table != NULL) {
337 for (pt = preload_table; pt->handle != NULL; pt++) {
338 if (pt->handle == handle) {
339 struct sudo_preload_symbol *sym;
340 for (sym = pt->symbols; sym->name != NULL; sym++) {
341 if (strcmp(sym->name, symbol) == 0)
342 return sym->addr;
343 }
344 }
345 }
346 }
347 errno = ENOENT;
348 return NULL;
349 }
350
351 char *
sudo_dso_strerror_v1(void)352 sudo_dso_strerror_v1(void)
353 {
354 return strerror(errno);
355 }
356 #endif /* !HAVE_SHL_LOAD && !HAVE_DLOPEN */
357