dso_dlfcn.c revision 656d9c7f52f88b3a3daccafa7655dec086c4756e
1/* dso_dlfcn.c -*- mode:C; c-file-style: "eay" -*- */ 2/* Written by Geoff Thorpe (geoff@geoffthorpe.net) for the OpenSSL 3 * project 2000. 4 */ 5/* ==================================================================== 6 * Copyright (c) 2000 The OpenSSL Project. All rights reserved. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 12 * 1. Redistributions of source code must retain the above copyright 13 * notice, this list of conditions and the following disclaimer. 14 * 15 * 2. Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in 17 * the documentation and/or other materials provided with the 18 * distribution. 19 * 20 * 3. All advertising materials mentioning features or use of this 21 * software must display the following acknowledgment: 22 * "This product includes software developed by the OpenSSL Project 23 * for use in the OpenSSL Toolkit. (http://www.OpenSSL.org/)" 24 * 25 * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to 26 * endorse or promote products derived from this software without 27 * prior written permission. For written permission, please contact 28 * licensing@OpenSSL.org. 29 * 30 * 5. Products derived from this software may not be called "OpenSSL" 31 * nor may "OpenSSL" appear in their names without prior written 32 * permission of the OpenSSL Project. 33 * 34 * 6. Redistributions of any form whatsoever must retain the following 35 * acknowledgment: 36 * "This product includes software developed by the OpenSSL Project 37 * for use in the OpenSSL Toolkit (http://www.OpenSSL.org/)" 38 * 39 * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY 40 * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 41 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 42 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE OpenSSL PROJECT OR 43 * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 44 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 45 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 46 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 47 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 48 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 49 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED 50 * OF THE POSSIBILITY OF SUCH DAMAGE. 51 * ==================================================================== 52 * 53 * This product includes cryptographic software written by Eric Young 54 * (eay@cryptsoft.com). This product includes software written by Tim 55 * Hudson (tjh@cryptsoft.com). 56 * 57 */ 58 59#include <stdio.h> 60#include "cryptlib.h" 61#include <openssl/dso.h> 62 63#ifndef DSO_DLFCN 64DSO_METHOD *DSO_METHOD_dlfcn(void) 65 { 66 return NULL; 67 } 68#else 69 70#ifdef HAVE_DLFCN_H 71#include <dlfcn.h> 72#endif 73 74/* Part of the hack in "dlfcn_load" ... */ 75#define DSO_MAX_TRANSLATED_SIZE 256 76 77static int dlfcn_load(DSO *dso); 78static int dlfcn_unload(DSO *dso); 79static void *dlfcn_bind_var(DSO *dso, const char *symname); 80static DSO_FUNC_TYPE dlfcn_bind_func(DSO *dso, const char *symname); 81#if 0 82static int dlfcn_unbind(DSO *dso, char *symname, void *symptr); 83static int dlfcn_init(DSO *dso); 84static int dlfcn_finish(DSO *dso); 85static long dlfcn_ctrl(DSO *dso, int cmd, long larg, void *parg); 86#endif 87static char *dlfcn_name_converter(DSO *dso, const char *filename); 88static char *dlfcn_merger(DSO *dso, const char *filespec1, 89 const char *filespec2); 90 91static DSO_METHOD dso_meth_dlfcn = { 92 "OpenSSL 'dlfcn' shared library method", 93 dlfcn_load, 94 dlfcn_unload, 95 dlfcn_bind_var, 96 dlfcn_bind_func, 97/* For now, "unbind" doesn't exist */ 98#if 0 99 NULL, /* unbind_var */ 100 NULL, /* unbind_func */ 101#endif 102 NULL, /* ctrl */ 103 dlfcn_name_converter, 104 dlfcn_merger, 105 NULL, /* init */ 106 NULL /* finish */ 107 }; 108 109DSO_METHOD *DSO_METHOD_dlfcn(void) 110 { 111 return(&dso_meth_dlfcn); 112 } 113 114/* Prior to using the dlopen() function, we should decide on the flag 115 * we send. There's a few different ways of doing this and it's a 116 * messy venn-diagram to match up which platforms support what. So 117 * as we don't have autoconf yet, I'm implementing a hack that could 118 * be hacked further relatively easily to deal with cases as we find 119 * them. Initially this is to cope with OpenBSD. */ 120#if defined(__OpenBSD__) || defined(__NetBSD__) 121# ifdef DL_LAZY 122# define DLOPEN_FLAG DL_LAZY 123# else 124# ifdef RTLD_NOW 125# define DLOPEN_FLAG RTLD_NOW 126# else 127# define DLOPEN_FLAG 0 128# endif 129# endif 130#else 131# ifdef OPENSSL_SYS_SUNOS 132# define DLOPEN_FLAG 1 133# else 134# define DLOPEN_FLAG RTLD_NOW /* Hope this works everywhere else */ 135# endif 136#endif 137 138/* For this DSO_METHOD, our meth_data STACK will contain; 139 * (i) the handle (void*) returned from dlopen(). 140 */ 141 142static int dlfcn_load(DSO *dso) 143 { 144 void *ptr = NULL; 145 /* See applicable comments in dso_dl.c */ 146 char *filename = DSO_convert_filename(dso, NULL); 147 int flags = DLOPEN_FLAG; 148 149 if(filename == NULL) 150 { 151 DSOerr(DSO_F_DLFCN_LOAD,DSO_R_NO_FILENAME); 152 goto err; 153 } 154 155#ifdef RTLD_GLOBAL 156 if (dso->flags & DSO_FLAG_GLOBAL_SYMBOLS) 157 flags |= RTLD_GLOBAL; 158#endif 159 ptr = dlopen(filename, flags); 160 if(ptr == NULL) 161 { 162 DSOerr(DSO_F_DLFCN_LOAD,DSO_R_LOAD_FAILED); 163 ERR_add_error_data(4, "filename(", filename, "): ", dlerror()); 164 goto err; 165 } 166 if(!sk_push(dso->meth_data, (char *)ptr)) 167 { 168 DSOerr(DSO_F_DLFCN_LOAD,DSO_R_STACK_ERROR); 169 goto err; 170 } 171 /* Success */ 172 dso->loaded_filename = filename; 173 return(1); 174err: 175 /* Cleanup! */ 176 if(filename != NULL) 177 OPENSSL_free(filename); 178 if(ptr != NULL) 179 dlclose(ptr); 180 return(0); 181} 182 183static int dlfcn_unload(DSO *dso) 184 { 185 void *ptr; 186 if(dso == NULL) 187 { 188 DSOerr(DSO_F_DLFCN_UNLOAD,ERR_R_PASSED_NULL_PARAMETER); 189 return(0); 190 } 191 if(sk_num(dso->meth_data) < 1) 192 return(1); 193 ptr = (void *)sk_pop(dso->meth_data); 194 if(ptr == NULL) 195 { 196 DSOerr(DSO_F_DLFCN_UNLOAD,DSO_R_NULL_HANDLE); 197 /* Should push the value back onto the stack in 198 * case of a retry. */ 199 sk_push(dso->meth_data, (char *)ptr); 200 return(0); 201 } 202 /* For now I'm not aware of any errors associated with dlclose() */ 203 dlclose(ptr); 204 return(1); 205 } 206 207static void *dlfcn_bind_var(DSO *dso, const char *symname) 208 { 209 void *ptr, *sym; 210 211 if((dso == NULL) || (symname == NULL)) 212 { 213 DSOerr(DSO_F_DLFCN_BIND_VAR,ERR_R_PASSED_NULL_PARAMETER); 214 return(NULL); 215 } 216 if(sk_num(dso->meth_data) < 1) 217 { 218 DSOerr(DSO_F_DLFCN_BIND_VAR,DSO_R_STACK_ERROR); 219 return(NULL); 220 } 221 ptr = (void *)sk_value(dso->meth_data, sk_num(dso->meth_data) - 1); 222 if(ptr == NULL) 223 { 224 DSOerr(DSO_F_DLFCN_BIND_VAR,DSO_R_NULL_HANDLE); 225 return(NULL); 226 } 227 sym = dlsym(ptr, symname); 228 if(sym == NULL) 229 { 230 DSOerr(DSO_F_DLFCN_BIND_VAR,DSO_R_SYM_FAILURE); 231 ERR_add_error_data(4, "symname(", symname, "): ", dlerror()); 232 return(NULL); 233 } 234 return(sym); 235 } 236 237static DSO_FUNC_TYPE dlfcn_bind_func(DSO *dso, const char *symname) 238 { 239 void *ptr; 240 DSO_FUNC_TYPE sym, *tsym = &sym; 241 242 if((dso == NULL) || (symname == NULL)) 243 { 244 DSOerr(DSO_F_DLFCN_BIND_FUNC,ERR_R_PASSED_NULL_PARAMETER); 245 return(NULL); 246 } 247 if(sk_num(dso->meth_data) < 1) 248 { 249 DSOerr(DSO_F_DLFCN_BIND_FUNC,DSO_R_STACK_ERROR); 250 return(NULL); 251 } 252 ptr = (void *)sk_value(dso->meth_data, sk_num(dso->meth_data) - 1); 253 if(ptr == NULL) 254 { 255 DSOerr(DSO_F_DLFCN_BIND_FUNC,DSO_R_NULL_HANDLE); 256 return(NULL); 257 } 258 *(void **)(tsym) = dlsym(ptr, symname); 259 if(sym == NULL) 260 { 261 DSOerr(DSO_F_DLFCN_BIND_FUNC,DSO_R_SYM_FAILURE); 262 ERR_add_error_data(4, "symname(", symname, "): ", dlerror()); 263 return(NULL); 264 } 265 return(sym); 266 } 267 268static char *dlfcn_merger(DSO *dso, const char *filespec1, 269 const char *filespec2) 270 { 271 char *merged; 272 273 if(!filespec1 && !filespec2) 274 { 275 DSOerr(DSO_F_DLFCN_MERGER, 276 ERR_R_PASSED_NULL_PARAMETER); 277 return(NULL); 278 } 279 /* If the first file specification is a rooted path, it rules. 280 same goes if the second file specification is missing. */ 281 if (!filespec2 || filespec1[0] == '/') 282 { 283 merged = OPENSSL_malloc(strlen(filespec1) + 1); 284 if(!merged) 285 { 286 DSOerr(DSO_F_DLFCN_MERGER, 287 ERR_R_MALLOC_FAILURE); 288 return(NULL); 289 } 290 strcpy(merged, filespec1); 291 } 292 /* If the first file specification is missing, the second one rules. */ 293 else if (!filespec1) 294 { 295 merged = OPENSSL_malloc(strlen(filespec2) + 1); 296 if(!merged) 297 { 298 DSOerr(DSO_F_DLFCN_MERGER, 299 ERR_R_MALLOC_FAILURE); 300 return(NULL); 301 } 302 strcpy(merged, filespec2); 303 } 304 else 305 /* This part isn't as trivial as it looks. It assumes that 306 the second file specification really is a directory, and 307 makes no checks whatsoever. Therefore, the result becomes 308 the concatenation of filespec2 followed by a slash followed 309 by filespec1. */ 310 { 311 int spec2len, len; 312 313 spec2len = (filespec2 ? strlen(filespec2) : 0); 314 len = spec2len + (filespec1 ? strlen(filespec1) : 0); 315 316 if(filespec2 && filespec2[spec2len - 1] == '/') 317 { 318 spec2len--; 319 len--; 320 } 321 merged = OPENSSL_malloc(len + 2); 322 if(!merged) 323 { 324 DSOerr(DSO_F_DLFCN_MERGER, 325 ERR_R_MALLOC_FAILURE); 326 return(NULL); 327 } 328 strcpy(merged, filespec2); 329 merged[spec2len] = '/'; 330 strcpy(&merged[spec2len + 1], filespec1); 331 } 332 return(merged); 333 } 334 335static char *dlfcn_name_converter(DSO *dso, const char *filename) 336 { 337 char *translated; 338 int len, rsize, transform; 339 340 len = strlen(filename); 341 rsize = len + 1; 342 transform = (strstr(filename, "/") == NULL); 343 if(transform) 344 { 345 /* We will convert this to "%s.so" or "lib%s.so" */ 346 rsize += 3; /* The length of ".so" */ 347 if ((DSO_flags(dso) & DSO_FLAG_NAME_TRANSLATION_EXT_ONLY) == 0) 348 rsize += 3; /* The length of "lib" */ 349 } 350 translated = OPENSSL_malloc(rsize); 351 if(translated == NULL) 352 { 353 DSOerr(DSO_F_DLFCN_NAME_CONVERTER, 354 DSO_R_NAME_TRANSLATION_FAILED); 355 return(NULL); 356 } 357 if(transform) 358 { 359 if ((DSO_flags(dso) & DSO_FLAG_NAME_TRANSLATION_EXT_ONLY) == 0) 360 sprintf(translated, "lib%s.so", filename); 361 else 362 sprintf(translated, "%s.so", filename); 363 } 364 else 365 sprintf(translated, "%s", filename); 366 return(translated); 367 } 368 369#endif /* DSO_DLFCN */ 370