1 /* 2 * Copyright 2000-2021 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 OPENSSL_SYS_VMS 13 14 # pragma message disable DOLLARID 15 # include <errno.h> 16 # include <rms.h> 17 # include <lib$routines.h> 18 # include <libfisdef.h> 19 # include <stsdef.h> 20 # include <descrip.h> 21 # include <starlet.h> 22 # include "../vms_rms.h" 23 24 /* Some compiler options may mask the declaration of "_malloc32". */ 25 # if __INITIAL_POINTER_SIZE && defined _ANSI_C_SOURCE 26 # if __INITIAL_POINTER_SIZE == 64 27 # pragma pointer_size save 28 # pragma pointer_size 32 29 void *_malloc32(__size_t); 30 # pragma pointer_size restore 31 # endif /* __INITIAL_POINTER_SIZE == 64 */ 32 # endif /* __INITIAL_POINTER_SIZE && defined 33 * _ANSI_C_SOURCE */ 34 35 # pragma message disable DOLLARID 36 37 static int vms_load(DSO *dso); 38 static int vms_unload(DSO *dso); 39 static DSO_FUNC_TYPE vms_bind_func(DSO *dso, const char *symname); 40 static char *vms_name_converter(DSO *dso, const char *filename); 41 static char *vms_merger(DSO *dso, const char *filespec1, 42 const char *filespec2); 43 44 static DSO_METHOD dso_meth_vms = { 45 "OpenSSL 'VMS' shared library method", 46 vms_load, 47 NULL, /* unload */ 48 vms_bind_func, 49 NULL, /* ctrl */ 50 vms_name_converter, 51 vms_merger, 52 NULL, /* init */ 53 NULL, /* finish */ 54 NULL, /* pathbyaddr */ 55 NULL /* globallookup */ 56 }; 57 58 /* 59 * On VMS, the only "handle" is the file name. LIB$FIND_IMAGE_SYMBOL depends 60 * on the reference to the file name being the same for all calls regarding 61 * one shared image, so we'll just store it in an instance of the following 62 * structure and put a pointer to that instance in the meth_data stack. 63 */ 64 typedef struct dso_internal_st { 65 /* 66 * This should contain the name only, no directory, no extension, nothing 67 * but a name. 68 */ 69 struct dsc$descriptor_s filename_dsc; 70 char filename[NAMX_MAXRSS + 1]; 71 /* 72 * This contains whatever is not in filename, if needed. Normally not 73 * defined. 74 */ 75 struct dsc$descriptor_s imagename_dsc; 76 char imagename[NAMX_MAXRSS + 1]; 77 } DSO_VMS_INTERNAL; 78 79 DSO_METHOD *DSO_METHOD_openssl(void) 80 { 81 return &dso_meth_vms; 82 } 83 84 static int vms_load(DSO *dso) 85 { 86 void *ptr = NULL; 87 /* See applicable comments in dso_dl.c */ 88 char *filename = DSO_convert_filename(dso, NULL); 89 90 /* Ensure 32-bit pointer for "p", and appropriate malloc() function. */ 91 # if __INITIAL_POINTER_SIZE == 64 92 # define DSO_MALLOC _malloc32 93 # pragma pointer_size save 94 # pragma pointer_size 32 95 # else /* __INITIAL_POINTER_SIZE == 64 */ 96 # define DSO_MALLOC OPENSSL_malloc 97 # endif /* __INITIAL_POINTER_SIZE == 64 [else] */ 98 99 DSO_VMS_INTERNAL *p = NULL; 100 101 # if __INITIAL_POINTER_SIZE == 64 102 # pragma pointer_size restore 103 # endif /* __INITIAL_POINTER_SIZE == 64 */ 104 105 const char *sp1, *sp2; /* Search result */ 106 const char *ext = NULL; /* possible extension to add */ 107 108 if (filename == NULL) { 109 ERR_raise(ERR_LIB_DSO, DSO_R_NO_FILENAME); 110 goto err; 111 } 112 113 /*- 114 * A file specification may look like this: 115 * 116 * node::dev:[dir-spec]name.type;ver 117 * 118 * or (for compatibility with TOPS-20): 119 * 120 * node::dev:<dir-spec>name.type;ver 121 * 122 * and the dir-spec uses '.' as separator. Also, a dir-spec 123 * may consist of several parts, with mixed use of [] and <>: 124 * 125 * [dir1.]<dir2> 126 * 127 * We need to split the file specification into the name and 128 * the rest (both before and after the name itself). 129 */ 130 /* 131 * Start with trying to find the end of a dir-spec, and save the position 132 * of the byte after in sp1 133 */ 134 sp1 = strrchr(filename, ']'); 135 sp2 = strrchr(filename, '>'); 136 if (sp1 == NULL) 137 sp1 = sp2; 138 if (sp2 != NULL && sp2 > sp1) 139 sp1 = sp2; 140 if (sp1 == NULL) 141 sp1 = strrchr(filename, ':'); 142 if (sp1 == NULL) 143 sp1 = filename; 144 else 145 sp1++; /* The byte after the found character */ 146 /* Now, let's see if there's a type, and save the position in sp2 */ 147 sp2 = strchr(sp1, '.'); 148 /* 149 * If there is a period and the next character is a semi-colon, 150 * we need to add an extension 151 */ 152 if (sp2 != NULL && sp2[1] == ';') 153 ext = ".EXE"; 154 /* 155 * If we found it, that's where we'll cut. Otherwise, look for a version 156 * number and save the position in sp2 157 */ 158 if (sp2 == NULL) { 159 sp2 = strchr(sp1, ';'); 160 ext = ".EXE"; 161 } 162 /* 163 * If there was still nothing to find, set sp2 to point at the end of the 164 * string 165 */ 166 if (sp2 == NULL) 167 sp2 = sp1 + strlen(sp1); 168 169 /* Check that we won't get buffer overflows */ 170 if (sp2 - sp1 > FILENAME_MAX 171 || (sp1 - filename) + strlen(sp2) > FILENAME_MAX) { 172 ERR_raise(ERR_LIB_DSO, DSO_R_FILENAME_TOO_BIG); 173 goto err; 174 } 175 176 p = DSO_MALLOC(sizeof(*p)); 177 if (p == NULL) { 178 ERR_raise(ERR_LIB_DSO, ERR_R_MALLOC_FAILURE); 179 goto err; 180 } 181 182 strncpy(p->filename, sp1, sp2 - sp1); 183 p->filename[sp2 - sp1] = '\0'; 184 185 strncpy(p->imagename, filename, sp1 - filename); 186 p->imagename[sp1 - filename] = '\0'; 187 if (ext) { 188 strcat(p->imagename, ext); 189 if (*sp2 == '.') 190 sp2++; 191 } 192 strcat(p->imagename, sp2); 193 194 p->filename_dsc.dsc$w_length = strlen(p->filename); 195 p->filename_dsc.dsc$b_dtype = DSC$K_DTYPE_T; 196 p->filename_dsc.dsc$b_class = DSC$K_CLASS_S; 197 p->filename_dsc.dsc$a_pointer = p->filename; 198 p->imagename_dsc.dsc$w_length = strlen(p->imagename); 199 p->imagename_dsc.dsc$b_dtype = DSC$K_DTYPE_T; 200 p->imagename_dsc.dsc$b_class = DSC$K_CLASS_S; 201 p->imagename_dsc.dsc$a_pointer = p->imagename; 202 203 if (!sk_void_push(dso->meth_data, (char *)p)) { 204 ERR_raise(ERR_LIB_DSO, DSO_R_STACK_ERROR); 205 goto err; 206 } 207 208 /* Success (for now, we lie. We actually do not know...) */ 209 dso->loaded_filename = filename; 210 return 1; 211 err: 212 /* Cleanup! */ 213 OPENSSL_free(p); 214 OPENSSL_free(filename); 215 return 0; 216 } 217 218 /* 219 * Note that this doesn't actually unload the shared image, as there is no 220 * such thing in VMS. Next time it get loaded again, a new copy will 221 * actually be loaded. 222 */ 223 static int vms_unload(DSO *dso) 224 { 225 DSO_VMS_INTERNAL *p; 226 if (dso == NULL) { 227 ERR_raise(ERR_LIB_DSO, ERR_R_PASSED_NULL_PARAMETER); 228 return 0; 229 } 230 if (sk_void_num(dso->meth_data) < 1) 231 return 1; 232 p = (DSO_VMS_INTERNAL *)sk_void_pop(dso->meth_data); 233 if (p == NULL) { 234 ERR_raise(ERR_LIB_DSO, DSO_R_NULL_HANDLE); 235 return 0; 236 } 237 /* Cleanup */ 238 OPENSSL_free(p); 239 return 1; 240 } 241 242 /* 243 * We must do this in a separate function because of the way the exception 244 * handler works (it makes this function return 245 */ 246 static int do_find_symbol(DSO_VMS_INTERNAL *ptr, 247 struct dsc$descriptor_s *symname_dsc, void **sym, 248 unsigned long flags) 249 { 250 /* 251 * Make sure that signals are caught and returned instead of aborting the 252 * program. The exception handler gets unestablished automatically on 253 * return from this function. 254 */ 255 lib$establish(lib$sig_to_ret); 256 257 if (ptr->imagename_dsc.dsc$w_length) 258 return lib$find_image_symbol(&ptr->filename_dsc, 259 symname_dsc, sym, 260 &ptr->imagename_dsc, flags); 261 else 262 return lib$find_image_symbol(&ptr->filename_dsc, 263 symname_dsc, sym, 0, flags); 264 } 265 266 # ifndef LIB$M_FIS_MIXEDCASE 267 # define LIB$M_FIS_MIXEDCASE (1 << 4); 268 # endif 269 void vms_bind_sym(DSO *dso, const char *symname, void **sym) 270 { 271 DSO_VMS_INTERNAL *ptr; 272 int status = 0; 273 struct dsc$descriptor_s symname_dsc; 274 275 /* Arrange 32-bit pointer to (copied) string storage, if needed. */ 276 # if __INITIAL_POINTER_SIZE == 64 277 # define SYMNAME symname_32p 278 # pragma pointer_size save 279 # pragma pointer_size 32 280 char *symname_32p; 281 # pragma pointer_size restore 282 char symname_32[NAMX_MAXRSS + 1]; 283 # else /* __INITIAL_POINTER_SIZE == 64 */ 284 # define SYMNAME ((char *) symname) 285 # endif /* __INITIAL_POINTER_SIZE == 64 [else] */ 286 287 *sym = NULL; 288 289 if ((dso == NULL) || (symname == NULL)) { 290 ERR_raise(ERR_LIB_DSO, ERR_R_PASSED_NULL_PARAMETER); 291 return; 292 } 293 # if __INITIAL_POINTER_SIZE == 64 294 /* Copy the symbol name to storage with a 32-bit pointer. */ 295 symname_32p = symname_32; 296 strcpy(symname_32p, symname); 297 # endif /* __INITIAL_POINTER_SIZE == 64 [else] */ 298 299 symname_dsc.dsc$w_length = strlen(SYMNAME); 300 symname_dsc.dsc$b_dtype = DSC$K_DTYPE_T; 301 symname_dsc.dsc$b_class = DSC$K_CLASS_S; 302 symname_dsc.dsc$a_pointer = SYMNAME; 303 304 if (sk_void_num(dso->meth_data) < 1) { 305 ERR_raise(ERR_LIB_DSO, DSO_R_STACK_ERROR); 306 return; 307 } 308 ptr = (DSO_VMS_INTERNAL *)sk_void_value(dso->meth_data, 309 sk_void_num(dso->meth_data) - 1); 310 if (ptr == NULL) { 311 ERR_raise(ERR_LIB_DSO, DSO_R_NULL_HANDLE); 312 return; 313 } 314 315 status = do_find_symbol(ptr, &symname_dsc, sym, LIB$M_FIS_MIXEDCASE); 316 317 if (!$VMS_STATUS_SUCCESS(status)) 318 status = do_find_symbol(ptr, &symname_dsc, sym, 0); 319 320 if (!$VMS_STATUS_SUCCESS(status)) { 321 unsigned short length; 322 char errstring[257]; 323 struct dsc$descriptor_s errstring_dsc; 324 325 errstring_dsc.dsc$w_length = sizeof(errstring); 326 errstring_dsc.dsc$b_dtype = DSC$K_DTYPE_T; 327 errstring_dsc.dsc$b_class = DSC$K_CLASS_S; 328 errstring_dsc.dsc$a_pointer = errstring; 329 330 *sym = NULL; 331 332 status = sys$getmsg(status, &length, &errstring_dsc, 1, 0); 333 334 if (!$VMS_STATUS_SUCCESS(status)) 335 lib$signal(status); /* This is really bad. Abort! */ 336 else { 337 errstring[length] = '\0'; 338 339 if (ptr->imagename_dsc.dsc$w_length) 340 ERR_raise_data(ERR_LIB_DSO, DSO_R_SYM_FAILURE, 341 "Symbol %s in %s (%s): %s", 342 symname, ptr->filename, ptr->imagename, 343 errstring); 344 else 345 ERR_raise_data(ERR_LIB_DSO, DSO_R_SYM_FAILURE, 346 "Symbol %s in %s: %s", 347 symname, ptr->filename, errstring); 348 } 349 return; 350 } 351 return; 352 } 353 354 static DSO_FUNC_TYPE vms_bind_func(DSO *dso, const char *symname) 355 { 356 DSO_FUNC_TYPE sym = 0; 357 vms_bind_sym(dso, symname, (void **)&sym); 358 return sym; 359 } 360 361 static char *vms_merger(DSO *dso, const char *filespec1, 362 const char *filespec2) 363 { 364 int status; 365 int filespec1len, filespec2len; 366 struct FAB fab; 367 struct NAMX_STRUCT nam; 368 char esa[NAMX_MAXRSS + 1]; 369 char *merged; 370 371 /* Arrange 32-bit pointer to (copied) string storage, if needed. */ 372 # if __INITIAL_POINTER_SIZE == 64 373 # define FILESPEC1 filespec1_32p; 374 # define FILESPEC2 filespec2_32p; 375 # pragma pointer_size save 376 # pragma pointer_size 32 377 char *filespec1_32p; 378 char *filespec2_32p; 379 # pragma pointer_size restore 380 char filespec1_32[NAMX_MAXRSS + 1]; 381 char filespec2_32[NAMX_MAXRSS + 1]; 382 # else /* __INITIAL_POINTER_SIZE == 64 */ 383 # define FILESPEC1 ((char *) filespec1) 384 # define FILESPEC2 ((char *) filespec2) 385 # endif /* __INITIAL_POINTER_SIZE == 64 [else] */ 386 387 if (!filespec1) 388 filespec1 = ""; 389 if (!filespec2) 390 filespec2 = ""; 391 filespec1len = strlen(filespec1); 392 filespec2len = strlen(filespec2); 393 394 # if __INITIAL_POINTER_SIZE == 64 395 /* Copy the file names to storage with a 32-bit pointer. */ 396 filespec1_32p = filespec1_32; 397 filespec2_32p = filespec2_32; 398 strcpy(filespec1_32p, filespec1); 399 strcpy(filespec2_32p, filespec2); 400 # endif /* __INITIAL_POINTER_SIZE == 64 [else] */ 401 402 fab = cc$rms_fab; 403 nam = CC_RMS_NAMX; 404 405 FAB_OR_NAML(fab, nam).FAB_OR_NAML_FNA = FILESPEC1; 406 FAB_OR_NAML(fab, nam).FAB_OR_NAML_FNS = filespec1len; 407 FAB_OR_NAML(fab, nam).FAB_OR_NAML_DNA = FILESPEC2; 408 FAB_OR_NAML(fab, nam).FAB_OR_NAML_DNS = filespec2len; 409 NAMX_DNA_FNA_SET(fab) 410 411 nam.NAMX_ESA = esa; 412 nam.NAMX_ESS = NAMX_MAXRSS; 413 nam.NAMX_NOP = NAM$M_SYNCHK | NAM$M_PWD; 414 SET_NAMX_NO_SHORT_UPCASE(nam); 415 416 fab.FAB_NAMX = &nam; 417 418 status = sys$parse(&fab, 0, 0); 419 420 if (!$VMS_STATUS_SUCCESS(status)) { 421 unsigned short length; 422 char errstring[257]; 423 struct dsc$descriptor_s errstring_dsc; 424 425 errstring_dsc.dsc$w_length = sizeof(errstring); 426 errstring_dsc.dsc$b_dtype = DSC$K_DTYPE_T; 427 errstring_dsc.dsc$b_class = DSC$K_CLASS_S; 428 errstring_dsc.dsc$a_pointer = errstring; 429 430 status = sys$getmsg(status, &length, &errstring_dsc, 1, 0); 431 432 if (!$VMS_STATUS_SUCCESS(status)) 433 lib$signal(status); /* This is really bad. Abort! */ 434 else { 435 errstring[length] = '\0'; 436 437 ERR_raise_data(ERR_LIB_DSO, DSO_R_FAILURE, 438 "filespec \"%s\", default \"%s\": %s", 439 filespec1, filespec2, errstring); 440 } 441 return NULL; 442 } 443 444 merged = OPENSSL_malloc(nam.NAMX_ESL + 1); 445 if (merged == NULL) 446 goto malloc_err; 447 strncpy(merged, nam.NAMX_ESA, nam.NAMX_ESL); 448 merged[nam.NAMX_ESL] = '\0'; 449 return merged; 450 malloc_err: 451 ERR_raise(ERR_LIB_DSO, ERR_R_MALLOC_FAILURE); 452 } 453 454 static char *vms_name_converter(DSO *dso, const char *filename) 455 { 456 char *translated; 457 int len, transform; 458 const char *p; 459 460 len = strlen(filename); 461 462 p = strchr(filename, ':'); 463 if (p != NULL) { 464 transform = 0; 465 } else { 466 p = filename; 467 transform = (strrchr(p, '>') == NULL && strrchr(p, ']') == NULL); 468 } 469 470 if (transform) { 471 int rsize = len + sizeof(DSO_EXTENSION); 472 473 if ((translated = OPENSSL_malloc(rsize)) != NULL) { 474 p = strrchr(filename, ';'); 475 if (p != NULL) 476 len = p - filename; 477 strncpy(translated, filename, len); 478 translated[len] = '\0'; 479 strcat(translated, DSO_EXTENSION); 480 if (p != NULL) 481 strcat(translated, p); 482 } 483 } else { 484 translated = OPENSSL_strdup(filename); 485 } 486 return translated; 487 } 488 489 #endif /* OPENSSL_SYS_VMS */ 490