VMS: be less picky when loading DSOs
[openssl.git] / crypto / dso / dso_win32.c
1 /*
2  * Copyright 2000-2016 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 #include "dso_locl.h"
11
12 #if defined(DSO_WIN32)
13
14 # ifdef _WIN32_WCE
15 #  if _WIN32_WCE < 300
16 static FARPROC GetProcAddressA(HMODULE hModule, LPCSTR lpProcName)
17 {
18     WCHAR lpProcNameW[64];
19     int i;
20
21     for (i = 0; lpProcName[i] && i < 64; i++)
22         lpProcNameW[i] = (WCHAR)lpProcName[i];
23     if (i == 64)
24         return NULL;
25     lpProcNameW[i] = 0;
26
27     return GetProcAddressW(hModule, lpProcNameW);
28 }
29 #  endif
30 #  undef GetProcAddress
31 #  define GetProcAddress GetProcAddressA
32
33 static HINSTANCE LoadLibraryA(LPCSTR lpLibFileName)
34 {
35     WCHAR *fnamw;
36     size_t len_0 = strlen(lpLibFileName) + 1, i;
37
38 #  ifdef _MSC_VER
39     fnamw = (WCHAR *)_alloca(len_0 * sizeof(WCHAR));
40 #  else
41     fnamw = (WCHAR *)alloca(len_0 * sizeof(WCHAR));
42 #  endif
43     if (fnamw == NULL) {
44         SetLastError(ERROR_NOT_ENOUGH_MEMORY);
45         return NULL;
46     }
47 #  if defined(_WIN32_WCE) && _WIN32_WCE>=101
48     if (!MultiByteToWideChar(CP_ACP, 0, lpLibFileName, len_0, fnamw, len_0))
49 #  endif
50         for (i = 0; i < len_0; i++)
51             fnamw[i] = (WCHAR)lpLibFileName[i];
52
53     return LoadLibraryW(fnamw);
54 }
55 # endif
56
57 /* Part of the hack in "win32_load" ... */
58 # define DSO_MAX_TRANSLATED_SIZE 256
59
60 static int win32_load(DSO *dso);
61 static int win32_unload(DSO *dso);
62 static DSO_FUNC_TYPE win32_bind_func(DSO *dso, const char *symname);
63 static char *win32_name_converter(DSO *dso, const char *filename);
64 static char *win32_merger(DSO *dso, const char *filespec1,
65                           const char *filespec2);
66 static void *win32_globallookup(const char *name);
67
68 static const char *openssl_strnchr(const char *string, int c, size_t len);
69
70 static DSO_METHOD dso_meth_win32 = {
71     "OpenSSL 'win32' shared library method",
72     win32_load,
73     win32_unload,
74     win32_bind_func,
75     NULL,                       /* ctrl */
76     win32_name_converter,
77     win32_merger,
78     NULL,                       /* init */
79     NULL,                       /* finish */
80     win32_globallookup
81 };
82
83 DSO_METHOD *DSO_METHOD_openssl(void)
84 {
85     return &dso_meth_win32;
86 }
87
88 /*
89  * For this DSO_METHOD, our meth_data STACK will contain; (i) a pointer to
90  * the handle (HINSTANCE) returned from LoadLibrary(), and copied.
91  */
92
93 static int win32_load(DSO *dso)
94 {
95     HINSTANCE h = NULL, *p = NULL;
96     /* See applicable comments from dso_dl.c */
97     char *filename = DSO_convert_filename(dso, NULL);
98
99     if (filename == NULL) {
100         DSOerr(DSO_F_WIN32_LOAD, DSO_R_NO_FILENAME);
101         goto err;
102     }
103     h = LoadLibraryA(filename);
104     if (h == NULL) {
105         DSOerr(DSO_F_WIN32_LOAD, DSO_R_LOAD_FAILED);
106         ERR_add_error_data(3, "filename(", filename, ")");
107         goto err;
108     }
109     p = OPENSSL_malloc(sizeof(*p));
110     if (p == NULL) {
111         DSOerr(DSO_F_WIN32_LOAD, ERR_R_MALLOC_FAILURE);
112         goto err;
113     }
114     *p = h;
115     if (!sk_void_push(dso->meth_data, p)) {
116         DSOerr(DSO_F_WIN32_LOAD, DSO_R_STACK_ERROR);
117         goto err;
118     }
119     /* Success */
120     dso->loaded_filename = filename;
121     return (1);
122  err:
123     /* Cleanup ! */
124     OPENSSL_free(filename);
125     OPENSSL_free(p);
126     if (h != NULL)
127         FreeLibrary(h);
128     return (0);
129 }
130
131 static int win32_unload(DSO *dso)
132 {
133     HINSTANCE *p;
134     if (dso == NULL) {
135         DSOerr(DSO_F_WIN32_UNLOAD, ERR_R_PASSED_NULL_PARAMETER);
136         return (0);
137     }
138     if (sk_void_num(dso->meth_data) < 1)
139         return (1);
140     p = sk_void_pop(dso->meth_data);
141     if (p == NULL) {
142         DSOerr(DSO_F_WIN32_UNLOAD, DSO_R_NULL_HANDLE);
143         return (0);
144     }
145     if (!FreeLibrary(*p)) {
146         DSOerr(DSO_F_WIN32_UNLOAD, DSO_R_UNLOAD_FAILED);
147         /*
148          * We should push the value back onto the stack in case of a retry.
149          */
150         sk_void_push(dso->meth_data, p);
151         return (0);
152     }
153     /* Cleanup */
154     OPENSSL_free(p);
155     return (1);
156 }
157
158 static DSO_FUNC_TYPE win32_bind_func(DSO *dso, const char *symname)
159 {
160     HINSTANCE *ptr;
161     union {
162         void *p;
163         FARPROC f;
164     } sym;
165
166     if ((dso == NULL) || (symname == NULL)) {
167         DSOerr(DSO_F_WIN32_BIND_FUNC, ERR_R_PASSED_NULL_PARAMETER);
168         return (NULL);
169     }
170     if (sk_void_num(dso->meth_data) < 1) {
171         DSOerr(DSO_F_WIN32_BIND_FUNC, DSO_R_STACK_ERROR);
172         return (NULL);
173     }
174     ptr = sk_void_value(dso->meth_data, sk_void_num(dso->meth_data) - 1);
175     if (ptr == NULL) {
176         DSOerr(DSO_F_WIN32_BIND_FUNC, DSO_R_NULL_HANDLE);
177         return (NULL);
178     }
179     sym.f = GetProcAddress(*ptr, symname);
180     if (sym.p == NULL) {
181         DSOerr(DSO_F_WIN32_BIND_FUNC, DSO_R_SYM_FAILURE);
182         ERR_add_error_data(3, "symname(", symname, ")");
183         return (NULL);
184     }
185     return ((DSO_FUNC_TYPE)sym.f);
186 }
187
188 struct file_st {
189     const char *node;
190     int nodelen;
191     const char *device;
192     int devicelen;
193     const char *predir;
194     int predirlen;
195     const char *dir;
196     int dirlen;
197     const char *file;
198     int filelen;
199 };
200
201 static struct file_st *win32_splitter(DSO *dso, const char *filename,
202                                       int assume_last_is_dir)
203 {
204     struct file_st *result = NULL;
205     enum { IN_NODE, IN_DEVICE, IN_FILE } position;
206     const char *start = filename;
207     char last;
208
209     if (!filename) {
210         DSOerr(DSO_F_WIN32_SPLITTER, DSO_R_NO_FILENAME);
211         /*
212          * goto err;
213          */
214         return (NULL);
215     }
216
217     result = OPENSSL_zalloc(sizeof(*result));
218     if (result == NULL) {
219         DSOerr(DSO_F_WIN32_SPLITTER, ERR_R_MALLOC_FAILURE);
220         return (NULL);
221     }
222
223     position = IN_DEVICE;
224
225     if ((filename[0] == '\\' && filename[1] == '\\')
226         || (filename[0] == '/' && filename[1] == '/')) {
227         position = IN_NODE;
228         filename += 2;
229         start = filename;
230         result->node = start;
231     }
232
233     do {
234         last = filename[0];
235         switch (last) {
236         case ':':
237             if (position != IN_DEVICE) {
238                 DSOerr(DSO_F_WIN32_SPLITTER, DSO_R_INCORRECT_FILE_SYNTAX);
239                 /*
240                  * goto err;
241                  */
242                 OPENSSL_free(result);
243                 return (NULL);
244             }
245             result->device = start;
246             result->devicelen = (int)(filename - start);
247             position = IN_FILE;
248             start = ++filename;
249             result->dir = start;
250             break;
251         case '\\':
252         case '/':
253             if (position == IN_NODE) {
254                 result->nodelen = (int)(filename - start);
255                 position = IN_FILE;
256                 start = ++filename;
257                 result->dir = start;
258             } else if (position == IN_DEVICE) {
259                 position = IN_FILE;
260                 filename++;
261                 result->dir = start;
262                 result->dirlen = (int)(filename - start);
263                 start = filename;
264             } else {
265                 filename++;
266                 result->dirlen += (int)(filename - start);
267                 start = filename;
268             }
269             break;
270         case '\0':
271             if (position == IN_NODE) {
272                 result->nodelen = (int)(filename - start);
273             } else {
274                 if (filename - start > 0) {
275                     if (assume_last_is_dir) {
276                         if (position == IN_DEVICE) {
277                             result->dir = start;
278                             result->dirlen = 0;
279                         }
280                         result->dirlen += (int)(filename - start);
281                     } else {
282                         result->file = start;
283                         result->filelen = (int)(filename - start);
284                     }
285                 }
286             }
287             break;
288         default:
289             filename++;
290             break;
291         }
292     }
293     while (last);
294
295     if (!result->nodelen)
296         result->node = NULL;
297     if (!result->devicelen)
298         result->device = NULL;
299     if (!result->dirlen)
300         result->dir = NULL;
301     if (!result->filelen)
302         result->file = NULL;
303
304     return (result);
305 }
306
307 static char *win32_joiner(DSO *dso, const struct file_st *file_split)
308 {
309     int len = 0, offset = 0;
310     char *result = NULL;
311     const char *start;
312
313     if (!file_split) {
314         DSOerr(DSO_F_WIN32_JOINER, ERR_R_PASSED_NULL_PARAMETER);
315         return (NULL);
316     }
317     if (file_split->node) {
318         len += 2 + file_split->nodelen; /* 2 for starting \\ */
319         if (file_split->predir || file_split->dir || file_split->file)
320             len++;              /* 1 for ending \ */
321     } else if (file_split->device) {
322         len += file_split->devicelen + 1; /* 1 for ending : */
323     }
324     len += file_split->predirlen;
325     if (file_split->predir && (file_split->dir || file_split->file)) {
326         len++;                  /* 1 for ending \ */
327     }
328     len += file_split->dirlen;
329     if (file_split->dir && file_split->file) {
330         len++;                  /* 1 for ending \ */
331     }
332     len += file_split->filelen;
333
334     if (!len) {
335         DSOerr(DSO_F_WIN32_JOINER, DSO_R_EMPTY_FILE_STRUCTURE);
336         return (NULL);
337     }
338
339     result = OPENSSL_malloc(len + 1);
340     if (result == NULL) {
341         DSOerr(DSO_F_WIN32_JOINER, ERR_R_MALLOC_FAILURE);
342         return (NULL);
343     }
344
345     if (file_split->node) {
346         strcpy(&result[offset], "\\\\");
347         offset += 2;
348         strncpy(&result[offset], file_split->node, file_split->nodelen);
349         offset += file_split->nodelen;
350         if (file_split->predir || file_split->dir || file_split->file) {
351             result[offset] = '\\';
352             offset++;
353         }
354     } else if (file_split->device) {
355         strncpy(&result[offset], file_split->device, file_split->devicelen);
356         offset += file_split->devicelen;
357         result[offset] = ':';
358         offset++;
359     }
360     start = file_split->predir;
361     while (file_split->predirlen > (start - file_split->predir)) {
362         const char *end = openssl_strnchr(start, '/',
363                                           file_split->predirlen - (start -
364                                                                    file_split->predir));
365         if (!end)
366             end = start
367                 + file_split->predirlen - (start - file_split->predir);
368         strncpy(&result[offset], start, end - start);
369         offset += (int)(end - start);
370         result[offset] = '\\';
371         offset++;
372         start = end + 1;
373     }
374     start = file_split->dir;
375     while (file_split->dirlen > (start - file_split->dir)) {
376         const char *end = openssl_strnchr(start, '/',
377                                           file_split->dirlen - (start -
378                                                                 file_split->dir));
379         if (!end)
380             end = start + file_split->dirlen - (start - file_split->dir);
381         strncpy(&result[offset], start, end - start);
382         offset += (int)(end - start);
383         result[offset] = '\\';
384         offset++;
385         start = end + 1;
386     }
387     strncpy(&result[offset], file_split->file, file_split->filelen);
388     offset += file_split->filelen;
389     result[offset] = '\0';
390     return (result);
391 }
392
393 static char *win32_merger(DSO *dso, const char *filespec1,
394                           const char *filespec2)
395 {
396     char *merged = NULL;
397     struct file_st *filespec1_split = NULL;
398     struct file_st *filespec2_split = NULL;
399
400     if (!filespec1 && !filespec2) {
401         DSOerr(DSO_F_WIN32_MERGER, ERR_R_PASSED_NULL_PARAMETER);
402         return (NULL);
403     }
404     if (!filespec2) {
405         merged = OPENSSL_malloc(strlen(filespec1) + 1);
406         if (merged == NULL) {
407             DSOerr(DSO_F_WIN32_MERGER, ERR_R_MALLOC_FAILURE);
408             return (NULL);
409         }
410         strcpy(merged, filespec1);
411     } else if (!filespec1) {
412         merged = OPENSSL_malloc(strlen(filespec2) + 1);
413         if (merged == NULL) {
414             DSOerr(DSO_F_WIN32_MERGER, ERR_R_MALLOC_FAILURE);
415             return (NULL);
416         }
417         strcpy(merged, filespec2);
418     } else {
419         filespec1_split = win32_splitter(dso, filespec1, 0);
420         if (!filespec1_split) {
421             DSOerr(DSO_F_WIN32_MERGER, ERR_R_MALLOC_FAILURE);
422             return (NULL);
423         }
424         filespec2_split = win32_splitter(dso, filespec2, 1);
425         if (!filespec2_split) {
426             DSOerr(DSO_F_WIN32_MERGER, ERR_R_MALLOC_FAILURE);
427             OPENSSL_free(filespec1_split);
428             return (NULL);
429         }
430
431         /* Fill in into filespec1_split */
432         if (!filespec1_split->node && !filespec1_split->device) {
433             filespec1_split->node = filespec2_split->node;
434             filespec1_split->nodelen = filespec2_split->nodelen;
435             filespec1_split->device = filespec2_split->device;
436             filespec1_split->devicelen = filespec2_split->devicelen;
437         }
438         if (!filespec1_split->dir) {
439             filespec1_split->dir = filespec2_split->dir;
440             filespec1_split->dirlen = filespec2_split->dirlen;
441         } else if (filespec1_split->dir[0] != '\\'
442                    && filespec1_split->dir[0] != '/') {
443             filespec1_split->predir = filespec2_split->dir;
444             filespec1_split->predirlen = filespec2_split->dirlen;
445         }
446         if (!filespec1_split->file) {
447             filespec1_split->file = filespec2_split->file;
448             filespec1_split->filelen = filespec2_split->filelen;
449         }
450
451         merged = win32_joiner(dso, filespec1_split);
452     }
453     OPENSSL_free(filespec1_split);
454     OPENSSL_free(filespec2_split);
455     return (merged);
456 }
457
458 static char *win32_name_converter(DSO *dso, const char *filename)
459 {
460     char *translated;
461     int len, transform;
462
463     len = strlen(filename);
464     transform = ((strstr(filename, "/") == NULL) &&
465                  (strstr(filename, "\\") == NULL) &&
466                  (strstr(filename, ":") == NULL));
467     if (transform)
468         /* We will convert this to "%s.dll" */
469         translated = OPENSSL_malloc(len + 5);
470     else
471         /* We will simply duplicate filename */
472         translated = OPENSSL_malloc(len + 1);
473     if (translated == NULL) {
474         DSOerr(DSO_F_WIN32_NAME_CONVERTER, DSO_R_NAME_TRANSLATION_FAILED);
475         return (NULL);
476     }
477     if (transform)
478         sprintf(translated, "%s.dll", filename);
479     else
480         sprintf(translated, "%s", filename);
481     return (translated);
482 }
483
484 static const char *openssl_strnchr(const char *string, int c, size_t len)
485 {
486     size_t i;
487     const char *p;
488     for (i = 0, p = string; i < len && *p; i++, p++) {
489         if (*p == c)
490             return p;
491     }
492     return NULL;
493 }
494
495 # include <tlhelp32.h>
496 # ifdef _WIN32_WCE
497 #  define DLLNAME "TOOLHELP.DLL"
498 # else
499 #  ifdef MODULEENTRY32
500 #   undef MODULEENTRY32         /* unmask the ASCII version! */
501 #  endif
502 #  define DLLNAME "KERNEL32.DLL"
503 # endif
504
505 typedef HANDLE(WINAPI *CREATETOOLHELP32SNAPSHOT) (DWORD, DWORD);
506 typedef BOOL(WINAPI *CLOSETOOLHELP32SNAPSHOT) (HANDLE);
507 typedef BOOL(WINAPI *MODULE32) (HANDLE, MODULEENTRY32 *);
508
509 static void *win32_globallookup(const char *name)
510 {
511     HMODULE dll;
512     HANDLE hModuleSnap = INVALID_HANDLE_VALUE;
513     MODULEENTRY32 me32;
514     CREATETOOLHELP32SNAPSHOT create_snap;
515     CLOSETOOLHELP32SNAPSHOT close_snap;
516     MODULE32 module_first, module_next;
517     union {
518         void *p;
519         FARPROC f;
520     } ret = { NULL };
521
522     dll = LoadLibrary(TEXT(DLLNAME));
523     if (dll == NULL) {
524         DSOerr(DSO_F_WIN32_GLOBALLOOKUP, DSO_R_UNSUPPORTED);
525         return NULL;
526     }
527
528     create_snap = (CREATETOOLHELP32SNAPSHOT)
529         GetProcAddress(dll, "CreateToolhelp32Snapshot");
530     if (create_snap == NULL) {
531         FreeLibrary(dll);
532         DSOerr(DSO_F_WIN32_GLOBALLOOKUP, DSO_R_UNSUPPORTED);
533         return NULL;
534     }
535     /* We take the rest for granted... */
536 # ifdef _WIN32_WCE
537     close_snap = (CLOSETOOLHELP32SNAPSHOT)
538         GetProcAddress(dll, "CloseToolhelp32Snapshot");
539 # else
540     close_snap = (CLOSETOOLHELP32SNAPSHOT) CloseHandle;
541 # endif
542     module_first = (MODULE32) GetProcAddress(dll, "Module32First");
543     module_next = (MODULE32) GetProcAddress(dll, "Module32Next");
544
545     hModuleSnap = (*create_snap) (TH32CS_SNAPMODULE, 0);
546     if (hModuleSnap == INVALID_HANDLE_VALUE) {
547         FreeLibrary(dll);
548         DSOerr(DSO_F_WIN32_GLOBALLOOKUP, DSO_R_UNSUPPORTED);
549         return NULL;
550     }
551
552     me32.dwSize = sizeof(me32);
553
554     if (!(*module_first) (hModuleSnap, &me32)) {
555         (*close_snap) (hModuleSnap);
556         FreeLibrary(dll);
557         return NULL;
558     }
559
560     do {
561         if ((ret.f = GetProcAddress(me32.hModule, name))) {
562             (*close_snap) (hModuleSnap);
563             FreeLibrary(dll);
564             return ret.p;
565         }
566     } while ((*module_next) (hModuleSnap, &me32));
567
568     (*close_snap) (hModuleSnap);
569     FreeLibrary(dll);
570     return NULL;
571 }
572 #endif                          /* DSO_WIN32 */