1 /* 2 * Copyright 2000-2024 The OpenSSL Project Authors. All Rights Reserved. 3 * 4 * Licensed under the Apache License 2.0 (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 #include "dso_local.h" 11 12 #ifdef DSO_DL 13 14 #include <dl.h> 15 16 /* Part of the hack in "dl_load" ... */ 17 #define DSO_MAX_TRANSLATED_SIZE 256 18 19 static int dl_load(DSO *dso); 20 static int dl_unload(DSO *dso); 21 static DSO_FUNC_TYPE dl_bind_func(DSO *dso, const char *symname); 22 static char *dl_name_converter(DSO *dso, const char *filename); 23 static char *dl_merger(DSO *dso, const char *filespec1, 24 const char *filespec2); 25 static int dl_pathbyaddr(void *addr, char *path, int sz); 26 static void *dl_globallookup(const char *name); 27 28 static DSO_METHOD dso_meth_dl = { 29 "OpenSSL 'dl' shared library method", 30 dl_load, 31 dl_unload, 32 dl_bind_func, 33 NULL, /* ctrl */ 34 dl_name_converter, 35 dl_merger, 36 NULL, /* init */ 37 NULL, /* finish */ 38 dl_pathbyaddr, 39 dl_globallookup 40 }; 41 42 DSO_METHOD *DSO_METHOD_openssl(void) 43 { 44 return &dso_meth_dl; 45 } 46 47 /* 48 * For this DSO_METHOD, our meth_data STACK will contain; (i) the handle 49 * (shl_t) returned from shl_load(). NB: I checked on HPUX11 and shl_t is 50 * itself a pointer type so the cast is safe. 51 */ 52 53 static int dl_load(DSO *dso) 54 { 55 shl_t ptr = NULL; 56 /* 57 * We don't do any fancy retries or anything, just take the method's (or 58 * DSO's if it has the callback set) best translation of the 59 * platform-independent filename and try once with that. 60 */ 61 char *filename = DSO_convert_filename(dso, NULL); 62 63 if (filename == NULL) { 64 ERR_raise(ERR_LIB_DSO, DSO_R_NO_FILENAME); 65 goto err; 66 } 67 ptr = shl_load(filename, BIND_IMMEDIATE | (dso->flags & DSO_FLAG_NO_NAME_TRANSLATION ? 0 : DYNAMIC_PATH), 0L); 68 if (ptr == NULL) { 69 char errbuf[160]; 70 71 if (openssl_strerror_r(errno, errbuf, sizeof(errbuf))) 72 ERR_raise_data(ERR_LIB_DSO, DSO_R_LOAD_FAILED, 73 "filename(%s): %s", filename, errbuf); 74 else 75 ERR_raise_data(ERR_LIB_DSO, DSO_R_LOAD_FAILED, 76 "filename(%s): errno %d", filename, errno); 77 goto err; 78 } 79 if (!sk_push(dso->meth_data, (char *)ptr)) { 80 ERR_raise(ERR_LIB_DSO, DSO_R_STACK_ERROR); 81 goto err; 82 } 83 /* 84 * Success, stick the converted filename we've loaded under into the DSO 85 * (it also serves as the indicator that we are currently loaded). 86 */ 87 dso->loaded_filename = filename; 88 return 1; 89 err: 90 /* Cleanup! */ 91 OPENSSL_free(filename); 92 if (ptr != NULL) 93 shl_unload(ptr); 94 return 0; 95 } 96 97 static int dl_unload(DSO *dso) 98 { 99 shl_t ptr; 100 if (dso == NULL) { 101 ERR_raise(ERR_LIB_DSO, ERR_R_PASSED_NULL_PARAMETER); 102 return 0; 103 } 104 if (sk_num(dso->meth_data) < 1) 105 return 1; 106 /* Is this statement legal? */ 107 ptr = (shl_t)sk_pop(dso->meth_data); 108 if (ptr == NULL) { 109 ERR_raise(ERR_LIB_DSO, DSO_R_NULL_HANDLE); 110 /* 111 * Should push the value back onto the stack in case of a retry. 112 */ 113 sk_push(dso->meth_data, (char *)ptr); 114 return 0; 115 } 116 shl_unload(ptr); 117 return 1; 118 } 119 120 static DSO_FUNC_TYPE dl_bind_func(DSO *dso, const char *symname) 121 { 122 shl_t ptr; 123 void *sym; 124 125 if ((dso == NULL) || (symname == NULL)) { 126 ERR_raise(ERR_LIB_DSO, ERR_R_PASSED_NULL_PARAMETER); 127 return NULL; 128 } 129 if (sk_num(dso->meth_data) < 1) { 130 ERR_raise(ERR_LIB_DSO, DSO_R_STACK_ERROR); 131 return NULL; 132 } 133 ptr = (shl_t)sk_value(dso->meth_data, sk_num(dso->meth_data) - 1); 134 if (ptr == NULL) { 135 ERR_raise(ERR_LIB_DSO, DSO_R_NULL_HANDLE); 136 return NULL; 137 } 138 if (shl_findsym(&ptr, symname, TYPE_UNDEFINED, &sym) < 0) { 139 char errbuf[160]; 140 141 if (openssl_strerror_r(errno, errbuf, sizeof(errbuf))) 142 ERR_raise_data(ERR_LIB_DSO, DSO_R_SYM_FAILURE, 143 "symname(%s): %s", symname, errbuf); 144 else 145 ERR_raise_data(ERR_LIB_DSO, DSO_R_SYM_FAILURE, 146 "symname(%s): errno %d", symname, errno); 147 return NULL; 148 } 149 return (DSO_FUNC_TYPE)sym; 150 } 151 152 static char *dl_merger(DSO *dso, const char *filespec1, const char *filespec2) 153 { 154 char *merged; 155 156 if (!filespec1 && !filespec2) { 157 ERR_raise(ERR_LIB_DSO, ERR_R_PASSED_NULL_PARAMETER); 158 return NULL; 159 } 160 /* 161 * If the first file specification is a rooted path, it rules. same goes 162 * if the second file specification is missing. 163 */ 164 if (!filespec2 || filespec1[0] == '/') { 165 merged = OPENSSL_strdup(filespec1); 166 if (merged == NULL) 167 return NULL; 168 } 169 /* 170 * If the first file specification is missing, the second one rules. 171 */ 172 else if (!filespec1) { 173 merged = OPENSSL_strdup(filespec2); 174 if (merged == NULL) 175 return NULL; 176 } else 177 /* 178 * This part isn't as trivial as it looks. It assumes that the 179 * second file specification really is a directory, and makes no 180 * checks whatsoever. Therefore, the result becomes the 181 * concatenation of filespec2 followed by a slash followed by 182 * filespec1. 183 */ 184 { 185 int spec2len, len; 186 187 spec2len = (filespec2 ? strlen(filespec2) : 0); 188 len = spec2len + (filespec1 ? strlen(filespec1) : 0); 189 190 if (spec2len && filespec2[spec2len - 1] == '/') { 191 spec2len--; 192 len--; 193 } 194 merged = OPENSSL_malloc(len + 2); 195 if (merged == NULL) 196 return NULL; 197 strcpy(merged, filespec2); 198 merged[spec2len] = '/'; 199 strcpy(&merged[spec2len + 1], filespec1); 200 } 201 return merged; 202 } 203 204 /* 205 * This function is identical to the one in dso_dlfcn.c, but as it is highly 206 * unlikely that both the "dl" *and* "dlfcn" variants are being compiled at 207 * the same time, there's no great duplicating the code. Figuring out an 208 * elegant way to share one copy of the code would be more difficult and 209 * would not leave the implementations independent. 210 */ 211 static char *dl_name_converter(DSO *dso, const char *filename) 212 { 213 char *translated; 214 int len, rsize, transform; 215 216 len = strlen(filename); 217 rsize = len + 1; 218 transform = (strchr(filename, '/') == NULL); 219 if (transform) { 220 /* We will convert this to "%s.s?" or "lib%s.s?" */ 221 rsize += strlen(DSO_EXTENSION); /* The length of ".s?" */ 222 if ((DSO_flags(dso) & DSO_FLAG_NAME_TRANSLATION_EXT_ONLY) == 0) 223 rsize += 3; /* The length of "lib" */ 224 } 225 translated = OPENSSL_malloc(rsize); 226 if (translated == NULL) { 227 ERR_raise(ERR_LIB_DSO, DSO_R_NAME_TRANSLATION_FAILED); 228 return NULL; 229 } 230 if (transform) 231 BIO_snprintf(translated, rsize, 232 (DSO_flags(dso) & DSO_FLAG_NAME_TRANSLATION_EXT_ONLY) == 0 233 ? "lib%s%s" 234 : "%s%s", 235 filename, DSO_EXTENSION); 236 else 237 BIO_snprintf(translated, rsize, "%s", filename); 238 return translated; 239 } 240 241 static int dl_pathbyaddr(void *addr, char *path, int sz) 242 { 243 struct shl_descriptor inf; 244 int i, len; 245 246 if (addr == NULL) { 247 union { 248 int (*f)(void *, char *, int); 249 void *p; 250 } t = { 251 dl_pathbyaddr 252 }; 253 addr = t.p; 254 } 255 256 for (i = -1; shl_get_r(i, &inf) == 0; i++) { 257 if (((size_t)addr >= inf.tstart && (size_t)addr < inf.tend) || ((size_t)addr >= inf.dstart && (size_t)addr < inf.dend)) { 258 len = (int)strlen(inf.filename); 259 if (sz <= 0) 260 return len + 1; 261 if (len >= sz) 262 len = sz - 1; 263 memcpy(path, inf.filename, len); 264 path[len++] = 0; 265 return len; 266 } 267 } 268 269 return -1; 270 } 271 272 static void *dl_globallookup(const char *name) 273 { 274 void *ret; 275 shl_t h = NULL; 276 277 return shl_findsym(&h, name, TYPE_UNDEFINED, &ret) ? NULL : ret; 278 } 279 #endif /* DSO_DL */ 280