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