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