1 /* 2 * Copyright 2000-2018 The OpenSSL Project Authors. All Rights Reserved. 3 * 4 * Licensed under the OpenSSL license (the "License"). You may not use 5 * this file except in compliance with the License. You can obtain a copy 6 * in the file LICENSE in the source distribution or at 7 * https://www.openssl.org/source/license.html 8 */ 9 10 /* 11 * We need to do this early, because stdio.h includes the header files that 12 * handle _GNU_SOURCE and other similar macros. Defining it later is simply 13 * too late, because those headers are protected from re- inclusion. 14 */ 15 #ifndef _GNU_SOURCE 16 # define _GNU_SOURCE /* make sure dladdr is declared */ 17 #endif 18 19 #include "dso_locl.h" 20 21 #ifdef DSO_DLFCN 22 23 # ifdef HAVE_DLFCN_H 24 # ifdef __osf__ 25 # define __EXTENSIONS__ 26 # endif 27 # include <dlfcn.h> 28 # define HAVE_DLINFO 1 29 # if defined(__CYGWIN__) || \ 30 defined(__SCO_VERSION__) || defined(_SCO_ELF) || \ 31 (defined(__osf__) && !defined(RTLD_NEXT)) || \ 32 (defined(__OpenBSD__) && !defined(RTLD_SELF)) || \ 33 defined(__ANDROID__) 34 # undef HAVE_DLINFO 35 # endif 36 # endif 37 38 /* Part of the hack in "dlfcn_load" ... */ 39 # define DSO_MAX_TRANSLATED_SIZE 256 40 41 static int dlfcn_load(DSO *dso); 42 static int dlfcn_unload(DSO *dso); 43 static DSO_FUNC_TYPE dlfcn_bind_func(DSO *dso, const char *symname); 44 static char *dlfcn_name_converter(DSO *dso, const char *filename); 45 static char *dlfcn_merger(DSO *dso, const char *filespec1, 46 const char *filespec2); 47 static int dlfcn_pathbyaddr(void *addr, char *path, int sz); 48 static void *dlfcn_globallookup(const char *name); 49 50 static DSO_METHOD dso_meth_dlfcn = { 51 "OpenSSL 'dlfcn' shared library method", 52 dlfcn_load, 53 dlfcn_unload, 54 dlfcn_bind_func, 55 NULL, /* ctrl */ 56 dlfcn_name_converter, 57 dlfcn_merger, 58 NULL, /* init */ 59 NULL, /* finish */ 60 dlfcn_pathbyaddr, 61 dlfcn_globallookup 62 }; 63 64 DSO_METHOD *DSO_METHOD_openssl(void) 65 { 66 return &dso_meth_dlfcn; 67 } 68 69 /* 70 * Prior to using the dlopen() function, we should decide on the flag we 71 * send. There's a few different ways of doing this and it's a messy 72 * venn-diagram to match up which platforms support what. So as we don't have 73 * autoconf yet, I'm implementing a hack that could be hacked further 74 * relatively easily to deal with cases as we find them. Initially this is to 75 * cope with OpenBSD. 76 */ 77 # if defined(__OpenBSD__) || defined(__NetBSD__) 78 # ifdef DL_LAZY 79 # define DLOPEN_FLAG DL_LAZY 80 # else 81 # ifdef RTLD_NOW 82 # define DLOPEN_FLAG RTLD_NOW 83 # else 84 # define DLOPEN_FLAG 0 85 # endif 86 # endif 87 # else 88 # define DLOPEN_FLAG RTLD_NOW /* Hope this works everywhere else */ 89 # endif 90 91 /* 92 * For this DSO_METHOD, our meth_data STACK will contain; (i) the handle 93 * (void*) returned from dlopen(). 94 */ 95 96 static int dlfcn_load(DSO *dso) 97 { 98 void *ptr = NULL; 99 /* See applicable comments in dso_dl.c */ 100 char *filename = DSO_convert_filename(dso, NULL); 101 int flags = DLOPEN_FLAG; 102 103 if (filename == NULL) { 104 DSOerr(DSO_F_DLFCN_LOAD, DSO_R_NO_FILENAME); 105 goto err; 106 } 107 # ifdef RTLD_GLOBAL 108 if (dso->flags & DSO_FLAG_GLOBAL_SYMBOLS) 109 flags |= RTLD_GLOBAL; 110 # endif 111 # ifdef _AIX 112 if (filename[strlen(filename) - 1] == ')') 113 flags |= RTLD_MEMBER; 114 # endif 115 ptr = dlopen(filename, flags); 116 if (ptr == NULL) { 117 DSOerr(DSO_F_DLFCN_LOAD, DSO_R_LOAD_FAILED); 118 ERR_add_error_data(4, "filename(", filename, "): ", dlerror()); 119 goto err; 120 } 121 if (!sk_void_push(dso->meth_data, (char *)ptr)) { 122 DSOerr(DSO_F_DLFCN_LOAD, DSO_R_STACK_ERROR); 123 goto err; 124 } 125 /* Success */ 126 dso->loaded_filename = filename; 127 return 1; 128 err: 129 /* Cleanup! */ 130 OPENSSL_free(filename); 131 if (ptr != NULL) 132 dlclose(ptr); 133 return 0; 134 } 135 136 static int dlfcn_unload(DSO *dso) 137 { 138 void *ptr; 139 if (dso == NULL) { 140 DSOerr(DSO_F_DLFCN_UNLOAD, ERR_R_PASSED_NULL_PARAMETER); 141 return 0; 142 } 143 if (sk_void_num(dso->meth_data) < 1) 144 return 1; 145 ptr = sk_void_pop(dso->meth_data); 146 if (ptr == NULL) { 147 DSOerr(DSO_F_DLFCN_UNLOAD, DSO_R_NULL_HANDLE); 148 /* 149 * Should push the value back onto the stack in case of a retry. 150 */ 151 sk_void_push(dso->meth_data, ptr); 152 return 0; 153 } 154 /* For now I'm not aware of any errors associated with dlclose() */ 155 dlclose(ptr); 156 return 1; 157 } 158 159 static DSO_FUNC_TYPE dlfcn_bind_func(DSO *dso, const char *symname) 160 { 161 void *ptr; 162 union { 163 DSO_FUNC_TYPE sym; 164 void *dlret; 165 } u; 166 167 if ((dso == NULL) || (symname == NULL)) { 168 DSOerr(DSO_F_DLFCN_BIND_FUNC, ERR_R_PASSED_NULL_PARAMETER); 169 return NULL; 170 } 171 if (sk_void_num(dso->meth_data) < 1) { 172 DSOerr(DSO_F_DLFCN_BIND_FUNC, DSO_R_STACK_ERROR); 173 return NULL; 174 } 175 ptr = sk_void_value(dso->meth_data, sk_void_num(dso->meth_data) - 1); 176 if (ptr == NULL) { 177 DSOerr(DSO_F_DLFCN_BIND_FUNC, DSO_R_NULL_HANDLE); 178 return NULL; 179 } 180 u.dlret = dlsym(ptr, symname); 181 if (u.dlret == NULL) { 182 DSOerr(DSO_F_DLFCN_BIND_FUNC, DSO_R_SYM_FAILURE); 183 ERR_add_error_data(4, "symname(", symname, "): ", dlerror()); 184 return NULL; 185 } 186 return u.sym; 187 } 188 189 static char *dlfcn_merger(DSO *dso, const char *filespec1, 190 const char *filespec2) 191 { 192 char *merged; 193 194 if (!filespec1 && !filespec2) { 195 DSOerr(DSO_F_DLFCN_MERGER, ERR_R_PASSED_NULL_PARAMETER); 196 return NULL; 197 } 198 /* 199 * If the first file specification is a rooted path, it rules. same goes 200 * if the second file specification is missing. 201 */ 202 if (!filespec2 || (filespec1 != NULL && filespec1[0] == '/')) { 203 merged = OPENSSL_strdup(filespec1); 204 if (merged == NULL) { 205 DSOerr(DSO_F_DLFCN_MERGER, ERR_R_MALLOC_FAILURE); 206 return NULL; 207 } 208 } 209 /* 210 * If the first file specification is missing, the second one rules. 211 */ 212 else if (!filespec1) { 213 merged = OPENSSL_strdup(filespec2); 214 if (merged == NULL) { 215 DSOerr(DSO_F_DLFCN_MERGER, ERR_R_MALLOC_FAILURE); 216 return NULL; 217 } 218 } else { 219 /* 220 * This part isn't as trivial as it looks. It assumes that the 221 * second file specification really is a directory, and makes no 222 * checks whatsoever. Therefore, the result becomes the 223 * concatenation of filespec2 followed by a slash followed by 224 * filespec1. 225 */ 226 int spec2len, len; 227 228 spec2len = strlen(filespec2); 229 len = spec2len + strlen(filespec1); 230 231 if (spec2len && filespec2[spec2len - 1] == '/') { 232 spec2len--; 233 len--; 234 } 235 merged = OPENSSL_malloc(len + 2); 236 if (merged == NULL) { 237 DSOerr(DSO_F_DLFCN_MERGER, ERR_R_MALLOC_FAILURE); 238 return NULL; 239 } 240 strcpy(merged, filespec2); 241 merged[spec2len] = '/'; 242 strcpy(&merged[spec2len + 1], filespec1); 243 } 244 return merged; 245 } 246 247 static char *dlfcn_name_converter(DSO *dso, const char *filename) 248 { 249 char *translated; 250 int len, rsize, transform; 251 252 len = strlen(filename); 253 rsize = len + 1; 254 transform = (strstr(filename, "/") == NULL); 255 if (transform) { 256 /* We will convert this to "%s.so" or "lib%s.so" etc */ 257 rsize += strlen(DSO_EXTENSION); /* The length of ".so" */ 258 if ((DSO_flags(dso) & DSO_FLAG_NAME_TRANSLATION_EXT_ONLY) == 0) 259 rsize += 3; /* The length of "lib" */ 260 } 261 translated = OPENSSL_malloc(rsize); 262 if (translated == NULL) { 263 DSOerr(DSO_F_DLFCN_NAME_CONVERTER, DSO_R_NAME_TRANSLATION_FAILED); 264 return NULL; 265 } 266 if (transform) { 267 if ((DSO_flags(dso) & DSO_FLAG_NAME_TRANSLATION_EXT_ONLY) == 0) 268 sprintf(translated, "lib%s" DSO_EXTENSION, filename); 269 else 270 sprintf(translated, "%s" DSO_EXTENSION, filename); 271 } else 272 sprintf(translated, "%s", filename); 273 return translated; 274 } 275 276 # ifdef __sgi 277 /*- 278 This is a quote from IRIX manual for dladdr(3c): 279 280 <dlfcn.h> does not contain a prototype for dladdr or definition of 281 Dl_info. The #include <dlfcn.h> in the SYNOPSIS line is traditional, 282 but contains no dladdr prototype and no IRIX library contains an 283 implementation. Write your own declaration based on the code below. 284 285 The following code is dependent on internal interfaces that are not 286 part of the IRIX compatibility guarantee; however, there is no future 287 intention to change this interface, so on a practical level, the code 288 below is safe to use on IRIX. 289 */ 290 # include <rld_interface.h> 291 # ifndef _RLD_INTERFACE_DLFCN_H_DLADDR 292 # define _RLD_INTERFACE_DLFCN_H_DLADDR 293 typedef struct Dl_info { 294 const char *dli_fname; 295 void *dli_fbase; 296 const char *dli_sname; 297 void *dli_saddr; 298 int dli_version; 299 int dli_reserved1; 300 long dli_reserved[4]; 301 } Dl_info; 302 # else 303 typedef struct Dl_info Dl_info; 304 # endif 305 # define _RLD_DLADDR 14 306 307 static int dladdr(void *address, Dl_info *dl) 308 { 309 void *v; 310 v = _rld_new_interface(_RLD_DLADDR, address, dl); 311 return (int)v; 312 } 313 # endif /* __sgi */ 314 315 # ifdef _AIX 316 /*- 317 * See IBM's AIX Version 7.2, Technical Reference: 318 * Base Operating System and Extensions, Volume 1 and 2 319 * https://www.ibm.com/support/knowledgecenter/ssw_aix_72/com.ibm.aix.base/technicalreferences.htm 320 */ 321 # include <sys/ldr.h> 322 # include <errno.h> 323 /* ~ 64 * (sizeof(struct ld_info) + _XOPEN_PATH_MAX + _XOPEN_NAME_MAX) */ 324 # define DLFCN_LDINFO_SIZE 86976 325 typedef struct Dl_info { 326 const char *dli_fname; 327 } Dl_info; 328 /* 329 * This dladdr()-implementation will also find the ptrgl (Pointer Glue) virtual 330 * address of a function, which is just located in the DATA segment instead of 331 * the TEXT segment. 332 */ 333 static int dladdr(void *ptr, Dl_info *dl) 334 { 335 uintptr_t addr = (uintptr_t)ptr; 336 unsigned int found = 0; 337 struct ld_info *ldinfos, *next_ldi, *this_ldi; 338 339 if ((ldinfos = OPENSSL_malloc(DLFCN_LDINFO_SIZE)) == NULL) { 340 errno = ENOMEM; 341 dl->dli_fname = NULL; 342 return 0; 343 } 344 345 if ((loadquery(L_GETINFO, (void *)ldinfos, DLFCN_LDINFO_SIZE)) < 0) { 346 /*- 347 * Error handling is done through errno and dlerror() reading errno: 348 * ENOMEM (ldinfos buffer is too small), 349 * EINVAL (invalid flags), 350 * EFAULT (invalid ldinfos ptr) 351 */ 352 OPENSSL_free((void *)ldinfos); 353 dl->dli_fname = NULL; 354 return 0; 355 } 356 next_ldi = ldinfos; 357 358 do { 359 this_ldi = next_ldi; 360 if (((addr >= (uintptr_t)this_ldi->ldinfo_textorg) 361 && (addr < ((uintptr_t)this_ldi->ldinfo_textorg + 362 this_ldi->ldinfo_textsize))) 363 || ((addr >= (uintptr_t)this_ldi->ldinfo_dataorg) 364 && (addr < ((uintptr_t)this_ldi->ldinfo_dataorg + 365 this_ldi->ldinfo_datasize)))) { 366 char *buffer, *member; 367 size_t buffer_sz, member_len; 368 369 buffer_sz = strlen(this_ldi->ldinfo_filename) + 1; 370 member = this_ldi->ldinfo_filename + buffer_sz; 371 if ((member_len = strlen(member)) > 0) 372 buffer_sz += 1 + member_len + 1; 373 found = 1; 374 if ((buffer = OPENSSL_malloc(buffer_sz)) != NULL) { 375 OPENSSL_strlcpy(buffer, this_ldi->ldinfo_filename, buffer_sz); 376 if (member_len > 0) { 377 /* 378 * Need to respect a possible member name and not just 379 * returning the path name in this case. See docs: 380 * sys/ldr.h, loadquery() and dlopen()/RTLD_MEMBER. 381 */ 382 OPENSSL_strlcat(buffer, "(", buffer_sz); 383 OPENSSL_strlcat(buffer, member, buffer_sz); 384 OPENSSL_strlcat(buffer, ")", buffer_sz); 385 } 386 dl->dli_fname = buffer; 387 } else { 388 errno = ENOMEM; 389 } 390 } else { 391 next_ldi = (struct ld_info *)((uintptr_t)this_ldi + 392 this_ldi->ldinfo_next); 393 } 394 } while (this_ldi->ldinfo_next && !found); 395 OPENSSL_free((void *)ldinfos); 396 return (found && dl->dli_fname != NULL); 397 } 398 # endif /* _AIX */ 399 400 static int dlfcn_pathbyaddr(void *addr, char *path, int sz) 401 { 402 # ifdef HAVE_DLINFO 403 Dl_info dli; 404 int len; 405 406 if (addr == NULL) { 407 union { 408 int (*f) (void *, char *, int); 409 void *p; 410 } t = { 411 dlfcn_pathbyaddr 412 }; 413 addr = t.p; 414 } 415 416 if (dladdr(addr, &dli)) { 417 len = (int)strlen(dli.dli_fname); 418 if (sz <= 0) { 419 # ifdef _AIX 420 OPENSSL_free((void *)dli.dli_fname); 421 # endif 422 return len + 1; 423 } 424 if (len >= sz) 425 len = sz - 1; 426 memcpy(path, dli.dli_fname, len); 427 path[len++] = 0; 428 # ifdef _AIX 429 OPENSSL_free((void *)dli.dli_fname); 430 # endif 431 return len; 432 } 433 434 ERR_add_error_data(2, "dlfcn_pathbyaddr(): ", dlerror()); 435 # endif 436 return -1; 437 } 438 439 static void *dlfcn_globallookup(const char *name) 440 { 441 void *ret = NULL, *handle = dlopen(NULL, RTLD_LAZY); 442 443 if (handle) { 444 ret = dlsym(handle, name); 445 dlclose(handle); 446 } 447 448 return ret; 449 } 450 #endif /* DSO_DLFCN */ 451