1 /* 2 * Copyright 2000-2019 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_local.h" 20 #include "e_os.h" 21 22 #ifdef DSO_DLFCN 23 24 # ifdef HAVE_DLFCN_H 25 # ifdef __osf__ 26 # define __EXTENSIONS__ 27 # endif 28 # include <dlfcn.h> 29 # define HAVE_DLINFO 1 30 # if 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 int saveerrno = get_last_sys_error(); 103 104 if (filename == NULL) { 105 DSOerr(DSO_F_DLFCN_LOAD, DSO_R_NO_FILENAME); 106 goto err; 107 } 108 # ifdef RTLD_GLOBAL 109 if (dso->flags & DSO_FLAG_GLOBAL_SYMBOLS) 110 flags |= RTLD_GLOBAL; 111 # endif 112 # ifdef _AIX 113 if (filename[strlen(filename) - 1] == ')') 114 flags |= RTLD_MEMBER; 115 # endif 116 ptr = dlopen(filename, flags); 117 if (ptr == NULL) { 118 DSOerr(DSO_F_DLFCN_LOAD, DSO_R_LOAD_FAILED); 119 ERR_add_error_data(4, "filename(", filename, "): ", dlerror()); 120 goto err; 121 } 122 /* 123 * Some dlopen() implementations (e.g. solaris) do no preserve errno, even 124 * on a successful call. 125 */ 126 set_sys_error(saveerrno); 127 if (!sk_void_push(dso->meth_data, (char *)ptr)) { 128 DSOerr(DSO_F_DLFCN_LOAD, DSO_R_STACK_ERROR); 129 goto err; 130 } 131 /* Success */ 132 dso->loaded_filename = filename; 133 return 1; 134 err: 135 /* Cleanup! */ 136 OPENSSL_free(filename); 137 if (ptr != NULL) 138 dlclose(ptr); 139 return 0; 140 } 141 142 static int dlfcn_unload(DSO *dso) 143 { 144 void *ptr; 145 if (dso == NULL) { 146 DSOerr(DSO_F_DLFCN_UNLOAD, ERR_R_PASSED_NULL_PARAMETER); 147 return 0; 148 } 149 if (sk_void_num(dso->meth_data) < 1) 150 return 1; 151 ptr = sk_void_pop(dso->meth_data); 152 if (ptr == NULL) { 153 DSOerr(DSO_F_DLFCN_UNLOAD, DSO_R_NULL_HANDLE); 154 /* 155 * Should push the value back onto the stack in case of a retry. 156 */ 157 sk_void_push(dso->meth_data, ptr); 158 return 0; 159 } 160 /* For now I'm not aware of any errors associated with dlclose() */ 161 dlclose(ptr); 162 return 1; 163 } 164 165 static DSO_FUNC_TYPE dlfcn_bind_func(DSO *dso, const char *symname) 166 { 167 void *ptr; 168 union { 169 DSO_FUNC_TYPE sym; 170 void *dlret; 171 } u; 172 173 if ((dso == NULL) || (symname == NULL)) { 174 DSOerr(DSO_F_DLFCN_BIND_FUNC, ERR_R_PASSED_NULL_PARAMETER); 175 return NULL; 176 } 177 if (sk_void_num(dso->meth_data) < 1) { 178 DSOerr(DSO_F_DLFCN_BIND_FUNC, DSO_R_STACK_ERROR); 179 return NULL; 180 } 181 ptr = sk_void_value(dso->meth_data, sk_void_num(dso->meth_data) - 1); 182 if (ptr == NULL) { 183 DSOerr(DSO_F_DLFCN_BIND_FUNC, DSO_R_NULL_HANDLE); 184 return NULL; 185 } 186 u.dlret = dlsym(ptr, symname); 187 if (u.dlret == NULL) { 188 DSOerr(DSO_F_DLFCN_BIND_FUNC, DSO_R_SYM_FAILURE); 189 ERR_add_error_data(4, "symname(", symname, "): ", dlerror()); 190 return NULL; 191 } 192 return u.sym; 193 } 194 195 static char *dlfcn_merger(DSO *dso, const char *filespec1, 196 const char *filespec2) 197 { 198 char *merged; 199 200 if (!filespec1 && !filespec2) { 201 DSOerr(DSO_F_DLFCN_MERGER, ERR_R_PASSED_NULL_PARAMETER); 202 return NULL; 203 } 204 /* 205 * If the first file specification is a rooted path, it rules. same goes 206 * if the second file specification is missing. 207 */ 208 if (!filespec2 || (filespec1 != NULL && filespec1[0] == '/')) { 209 merged = OPENSSL_strdup(filespec1); 210 if (merged == NULL) { 211 DSOerr(DSO_F_DLFCN_MERGER, ERR_R_MALLOC_FAILURE); 212 return NULL; 213 } 214 } 215 /* 216 * If the first file specification is missing, the second one rules. 217 */ 218 else if (!filespec1) { 219 merged = OPENSSL_strdup(filespec2); 220 if (merged == NULL) { 221 DSOerr(DSO_F_DLFCN_MERGER, ERR_R_MALLOC_FAILURE); 222 return NULL; 223 } 224 } else { 225 /* 226 * This part isn't as trivial as it looks. It assumes that the 227 * second file specification really is a directory, and makes no 228 * checks whatsoever. Therefore, the result becomes the 229 * concatenation of filespec2 followed by a slash followed by 230 * filespec1. 231 */ 232 int spec2len, len; 233 234 spec2len = strlen(filespec2); 235 len = spec2len + strlen(filespec1); 236 237 if (spec2len && filespec2[spec2len - 1] == '/') { 238 spec2len--; 239 len--; 240 } 241 merged = OPENSSL_malloc(len + 2); 242 if (merged == NULL) { 243 DSOerr(DSO_F_DLFCN_MERGER, ERR_R_MALLOC_FAILURE); 244 return NULL; 245 } 246 strcpy(merged, filespec2); 247 merged[spec2len] = '/'; 248 strcpy(&merged[spec2len + 1], filespec1); 249 } 250 return merged; 251 } 252 253 static char *dlfcn_name_converter(DSO *dso, const char *filename) 254 { 255 char *translated; 256 int len, rsize, transform; 257 258 len = strlen(filename); 259 rsize = len + 1; 260 transform = (strstr(filename, "/") == NULL); 261 if (transform) { 262 /* We will convert this to "%s.so" or "lib%s.so" etc */ 263 rsize += strlen(DSO_EXTENSION); /* The length of ".so" */ 264 if ((DSO_flags(dso) & DSO_FLAG_NAME_TRANSLATION_EXT_ONLY) == 0) 265 rsize += 3; /* The length of "lib" */ 266 } 267 translated = OPENSSL_malloc(rsize); 268 if (translated == NULL) { 269 DSOerr(DSO_F_DLFCN_NAME_CONVERTER, DSO_R_NAME_TRANSLATION_FAILED); 270 return NULL; 271 } 272 if (transform) { 273 if ((DSO_flags(dso) & DSO_FLAG_NAME_TRANSLATION_EXT_ONLY) == 0) 274 sprintf(translated, "lib%s" DSO_EXTENSION, filename); 275 else 276 sprintf(translated, "%s" DSO_EXTENSION, filename); 277 } else 278 sprintf(translated, "%s", filename); 279 return translated; 280 } 281 282 # ifdef __sgi 283 /*- 284 This is a quote from IRIX manual for dladdr(3c): 285 286 <dlfcn.h> does not contain a prototype for dladdr or definition of 287 Dl_info. The #include <dlfcn.h> in the SYNOPSIS line is traditional, 288 but contains no dladdr prototype and no IRIX library contains an 289 implementation. Write your own declaration based on the code below. 290 291 The following code is dependent on internal interfaces that are not 292 part of the IRIX compatibility guarantee; however, there is no future 293 intention to change this interface, so on a practical level, the code 294 below is safe to use on IRIX. 295 */ 296 # include <rld_interface.h> 297 # ifndef _RLD_INTERFACE_DLFCN_H_DLADDR 298 # define _RLD_INTERFACE_DLFCN_H_DLADDR 299 typedef struct Dl_info { 300 const char *dli_fname; 301 void *dli_fbase; 302 const char *dli_sname; 303 void *dli_saddr; 304 int dli_version; 305 int dli_reserved1; 306 long dli_reserved[4]; 307 } Dl_info; 308 # else 309 typedef struct Dl_info Dl_info; 310 # endif 311 # define _RLD_DLADDR 14 312 313 static int dladdr(void *address, Dl_info *dl) 314 { 315 void *v; 316 v = _rld_new_interface(_RLD_DLADDR, address, dl); 317 return (int)v; 318 } 319 # endif /* __sgi */ 320 321 # ifdef _AIX 322 /*- 323 * See IBM's AIX Version 7.2, Technical Reference: 324 * Base Operating System and Extensions, Volume 1 and 2 325 * https://www.ibm.com/support/knowledgecenter/ssw_aix_72/com.ibm.aix.base/technicalreferences.htm 326 */ 327 # include <sys/ldr.h> 328 # include <errno.h> 329 /* ~ 64 * (sizeof(struct ld_info) + _XOPEN_PATH_MAX + _XOPEN_NAME_MAX) */ 330 # define DLFCN_LDINFO_SIZE 86976 331 typedef struct Dl_info { 332 const char *dli_fname; 333 } Dl_info; 334 /* 335 * This dladdr()-implementation will also find the ptrgl (Pointer Glue) virtual 336 * address of a function, which is just located in the DATA segment instead of 337 * the TEXT segment. 338 */ 339 static int dladdr(void *ptr, Dl_info *dl) 340 { 341 uintptr_t addr = (uintptr_t)ptr; 342 unsigned int found = 0; 343 struct ld_info *ldinfos, *next_ldi, *this_ldi; 344 345 if ((ldinfos = OPENSSL_malloc(DLFCN_LDINFO_SIZE)) == NULL) { 346 errno = ENOMEM; 347 dl->dli_fname = NULL; 348 return 0; 349 } 350 351 if ((loadquery(L_GETINFO, (void *)ldinfos, DLFCN_LDINFO_SIZE)) < 0) { 352 /*- 353 * Error handling is done through errno and dlerror() reading errno: 354 * ENOMEM (ldinfos buffer is too small), 355 * EINVAL (invalid flags), 356 * EFAULT (invalid ldinfos ptr) 357 */ 358 OPENSSL_free((void *)ldinfos); 359 dl->dli_fname = NULL; 360 return 0; 361 } 362 next_ldi = ldinfos; 363 364 do { 365 this_ldi = next_ldi; 366 if (((addr >= (uintptr_t)this_ldi->ldinfo_textorg) 367 && (addr < ((uintptr_t)this_ldi->ldinfo_textorg + 368 this_ldi->ldinfo_textsize))) 369 || ((addr >= (uintptr_t)this_ldi->ldinfo_dataorg) 370 && (addr < ((uintptr_t)this_ldi->ldinfo_dataorg + 371 this_ldi->ldinfo_datasize)))) { 372 char *buffer, *member; 373 size_t buffer_sz, member_len; 374 375 buffer_sz = strlen(this_ldi->ldinfo_filename) + 1; 376 member = this_ldi->ldinfo_filename + buffer_sz; 377 if ((member_len = strlen(member)) > 0) 378 buffer_sz += 1 + member_len + 1; 379 found = 1; 380 if ((buffer = OPENSSL_malloc(buffer_sz)) != NULL) { 381 OPENSSL_strlcpy(buffer, this_ldi->ldinfo_filename, buffer_sz); 382 if (member_len > 0) { 383 /* 384 * Need to respect a possible member name and not just 385 * returning the path name in this case. See docs: 386 * sys/ldr.h, loadquery() and dlopen()/RTLD_MEMBER. 387 */ 388 OPENSSL_strlcat(buffer, "(", buffer_sz); 389 OPENSSL_strlcat(buffer, member, buffer_sz); 390 OPENSSL_strlcat(buffer, ")", buffer_sz); 391 } 392 dl->dli_fname = buffer; 393 } else { 394 errno = ENOMEM; 395 } 396 } else { 397 next_ldi = (struct ld_info *)((uintptr_t)this_ldi + 398 this_ldi->ldinfo_next); 399 } 400 } while (this_ldi->ldinfo_next && !found); 401 OPENSSL_free((void *)ldinfos); 402 return (found && dl->dli_fname != NULL); 403 } 404 # endif /* _AIX */ 405 406 static int dlfcn_pathbyaddr(void *addr, char *path, int sz) 407 { 408 # ifdef HAVE_DLINFO 409 Dl_info dli; 410 int len; 411 412 if (addr == NULL) { 413 union { 414 int (*f) (void *, char *, int); 415 void *p; 416 } t = { 417 dlfcn_pathbyaddr 418 }; 419 addr = t.p; 420 } 421 422 if (dladdr(addr, &dli)) { 423 len = (int)strlen(dli.dli_fname); 424 if (sz <= 0) { 425 # ifdef _AIX 426 OPENSSL_free((void *)dli.dli_fname); 427 # endif 428 return len + 1; 429 } 430 if (len >= sz) 431 len = sz - 1; 432 memcpy(path, dli.dli_fname, len); 433 path[len++] = 0; 434 # ifdef _AIX 435 OPENSSL_free((void *)dli.dli_fname); 436 # endif 437 return len; 438 } 439 440 ERR_add_error_data(2, "dlfcn_pathbyaddr(): ", dlerror()); 441 # endif 442 return -1; 443 } 444 445 static void *dlfcn_globallookup(const char *name) 446 { 447 void *ret = NULL, *handle = dlopen(NULL, RTLD_LAZY); 448 449 if (handle) { 450 ret = dlsym(handle, name); 451 dlclose(handle); 452 } 453 454 return ret; 455 } 456 #endif /* DSO_DLFCN */ 457