xref: /freebsd/crypto/openssl/crypto/dso/dso_win32.c (revision f9bc005b8ef3b507a5ecbc2d7fe4411ef70eff38)
1 /*
2  * Copyright 2000-2026 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 #define GETPROCADDRESS(h, name, type) ((type)(void (*)(void))GetProcAddress((h), (name)))
59 
60 /* Part of the hack in "win32_load" ... */
61 #define DSO_MAX_TRANSLATED_SIZE 256
62 
63 static int win32_load(DSO *dso);
64 static int win32_unload(DSO *dso);
65 static DSO_FUNC_TYPE win32_bind_func(DSO *dso, const char *symname);
66 static char *win32_name_converter(DSO *dso, const char *filename);
67 static char *win32_merger(DSO *dso, const char *filespec1,
68     const char *filespec2);
69 static int win32_pathbyaddr(void *addr, char *path, int sz);
70 static void *win32_globallookup(const char *name);
71 
72 static const char *openssl_strnchr(const char *string, int c, size_t len);
73 
74 static DSO_METHOD dso_meth_win32 = {
75     "OpenSSL 'win32' shared library method",
76     win32_load,
77     win32_unload,
78     win32_bind_func,
79     NULL, /* ctrl */
80     win32_name_converter,
81     win32_merger,
82     NULL, /* init */
83     NULL, /* finish */
84     win32_pathbyaddr, /* pathbyaddr */
85     win32_globallookup
86 };
87 
DSO_METHOD_openssl(void)88 DSO_METHOD *DSO_METHOD_openssl(void)
89 {
90     return &dso_meth_win32;
91 }
92 
93 /*
94  * For this DSO_METHOD, our meth_data STACK will contain; (i) a pointer to
95  * the handle (HINSTANCE) returned from LoadLibrary(), and copied.
96  */
97 
win32_load(DSO * dso)98 static int win32_load(DSO *dso)
99 {
100     HINSTANCE h = NULL, *p = NULL;
101     /* See applicable comments from dso_dl.c */
102     char *filename = DSO_convert_filename(dso, NULL);
103 
104     if (filename == NULL) {
105         ERR_raise(ERR_LIB_DSO, DSO_R_NO_FILENAME);
106         goto err;
107     }
108     h = LoadLibraryA(filename);
109     if (h == NULL) {
110         ERR_raise_data(ERR_LIB_DSO, DSO_R_LOAD_FAILED,
111             "filename(%s)", filename);
112         goto err;
113     }
114     p = OPENSSL_malloc(sizeof(*p));
115     if (p == NULL)
116         goto err;
117     *p = h;
118     if (!sk_void_push(dso->meth_data, p)) {
119         ERR_raise(ERR_LIB_DSO, DSO_R_STACK_ERROR);
120         goto err;
121     }
122     /* Success */
123     dso->loaded_filename = filename;
124     return 1;
125 err:
126     /* Cleanup ! */
127     OPENSSL_free(filename);
128     OPENSSL_free(p);
129     if (h != NULL)
130         FreeLibrary(h);
131     return 0;
132 }
133 
win32_unload(DSO * dso)134 static int win32_unload(DSO *dso)
135 {
136     HINSTANCE *p;
137     if (dso == NULL) {
138         ERR_raise(ERR_LIB_DSO, ERR_R_PASSED_NULL_PARAMETER);
139         return 0;
140     }
141     if (sk_void_num(dso->meth_data) < 1)
142         return 1;
143     p = sk_void_pop(dso->meth_data);
144     if (p == NULL) {
145         ERR_raise(ERR_LIB_DSO, DSO_R_NULL_HANDLE);
146         return 0;
147     }
148     if (!FreeLibrary(*p)) {
149         ERR_raise(ERR_LIB_DSO, DSO_R_UNLOAD_FAILED);
150         /*
151          * We should push the value back onto the stack in case of a retry.
152          */
153         sk_void_push(dso->meth_data, p);
154         return 0;
155     }
156     /* Cleanup */
157     OPENSSL_free(p);
158     return 1;
159 }
160 
win32_bind_func(DSO * dso,const char * symname)161 static DSO_FUNC_TYPE win32_bind_func(DSO *dso, const char *symname)
162 {
163     HINSTANCE *ptr;
164     union {
165         void *p;
166         FARPROC f;
167     } sym;
168 
169     if ((dso == NULL) || (symname == NULL)) {
170         ERR_raise(ERR_LIB_DSO, ERR_R_PASSED_NULL_PARAMETER);
171         return NULL;
172     }
173     if (sk_void_num(dso->meth_data) < 1) {
174         ERR_raise(ERR_LIB_DSO, DSO_R_STACK_ERROR);
175         return NULL;
176     }
177     ptr = sk_void_value(dso->meth_data, sk_void_num(dso->meth_data) - 1);
178     if (ptr == NULL) {
179         ERR_raise(ERR_LIB_DSO, DSO_R_NULL_HANDLE);
180         return NULL;
181     }
182     sym.f = GETPROCADDRESS(*ptr, symname, FARPROC);
183     if (sym.p == NULL) {
184         ERR_raise_data(ERR_LIB_DSO, DSO_R_SYM_FAILURE, "symname(%s)", symname);
185         return NULL;
186     }
187     return (DSO_FUNC_TYPE)sym.f;
188 }
189 
190 struct file_st {
191     const char *node;
192     int nodelen;
193     const char *device;
194     int devicelen;
195     const char *predir;
196     int predirlen;
197     const char *dir;
198     int dirlen;
199     const char *file;
200     int filelen;
201 };
202 
win32_splitter(DSO * dso,const char * filename,int assume_last_is_dir)203 static struct file_st *win32_splitter(DSO *dso, const char *filename,
204     int assume_last_is_dir)
205 {
206     struct file_st *result = NULL;
207     enum { IN_NODE,
208         IN_DEVICE,
209         IN_FILE } position;
210     const char *start = filename;
211     char last;
212 
213     if (!filename) {
214         ERR_raise(ERR_LIB_DSO, DSO_R_NO_FILENAME);
215         return NULL;
216     }
217 
218     result = OPENSSL_zalloc(sizeof(*result));
219     if (result == NULL)
220         return NULL;
221 
222     position = IN_DEVICE;
223 
224     if ((filename[0] == '\\' && filename[1] == '\\')
225         || (filename[0] == '/' && filename[1] == '/')) {
226         position = IN_NODE;
227         filename += 2;
228         start = filename;
229         result->node = start;
230     }
231 
232     do {
233         last = filename[0];
234         switch (last) {
235         case ':':
236             if (position != IN_DEVICE) {
237                 ERR_raise(ERR_LIB_DSO, DSO_R_INCORRECT_FILE_SYNTAX);
238                 OPENSSL_free(result);
239                 return NULL;
240             }
241             result->device = start;
242             result->devicelen = (int)(filename - start);
243             position = IN_FILE;
244             start = ++filename;
245             result->dir = start;
246             break;
247         case '\\':
248         case '/':
249             if (position == IN_NODE) {
250                 result->nodelen = (int)(filename - start);
251                 position = IN_FILE;
252                 start = ++filename;
253                 result->dir = start;
254             } else if (position == IN_DEVICE) {
255                 position = IN_FILE;
256                 filename++;
257                 result->dir = start;
258                 result->dirlen = (int)(filename - start);
259                 start = filename;
260             } else {
261                 filename++;
262                 result->dirlen += (int)(filename - start);
263                 start = filename;
264             }
265             break;
266         case '\0':
267             if (position == IN_NODE) {
268                 result->nodelen = (int)(filename - start);
269             } else {
270                 if (filename - start > 0) {
271                     if (assume_last_is_dir) {
272                         if (position == IN_DEVICE) {
273                             result->dir = start;
274                             result->dirlen = 0;
275                         }
276                         result->dirlen += (int)(filename - start);
277                     } else {
278                         result->file = start;
279                         result->filelen = (int)(filename - start);
280                     }
281                 }
282             }
283             break;
284         default:
285             filename++;
286             break;
287         }
288     } while (last);
289 
290     if (!result->nodelen)
291         result->node = NULL;
292     if (!result->devicelen)
293         result->device = NULL;
294     if (!result->dirlen)
295         result->dir = NULL;
296     if (!result->filelen)
297         result->file = NULL;
298 
299     return result;
300 }
301 
win32_joiner(DSO * dso,const struct file_st * file_split)302 static char *win32_joiner(DSO *dso, const struct file_st *file_split)
303 {
304     int len = 0, offset = 0;
305     char *result = NULL;
306     const char *start;
307 
308     if (!file_split) {
309         ERR_raise(ERR_LIB_DSO, ERR_R_PASSED_NULL_PARAMETER);
310         return NULL;
311     }
312     if (file_split->node) {
313         len += 2 + file_split->nodelen; /* 2 for starting \\ */
314         if (file_split->predir || file_split->dir || file_split->file)
315             len++; /* 1 for ending \ */
316     } else if (file_split->device) {
317         len += file_split->devicelen + 1; /* 1 for ending : */
318     }
319     len += file_split->predirlen;
320     if (file_split->predir && (file_split->dir || file_split->file)) {
321         len++; /* 1 for ending \ */
322     }
323     len += file_split->dirlen;
324     if (file_split->dir) {
325         len++; /* 1 for ending \ */
326     }
327     len += file_split->filelen;
328 
329     if (!len) {
330         ERR_raise(ERR_LIB_DSO, DSO_R_EMPTY_FILE_STRUCTURE);
331         return NULL;
332     }
333 
334     result = OPENSSL_malloc(len + 1);
335     if (result == NULL)
336         return NULL;
337 
338     if (file_split->node) {
339         strcpy(&result[offset], "\\\\");
340         offset += 2;
341         strncpy(&result[offset], file_split->node, file_split->nodelen);
342         offset += file_split->nodelen;
343         if (file_split->predir || file_split->dir || file_split->file) {
344             result[offset] = '\\';
345             offset++;
346         }
347     } else if (file_split->device) {
348         strncpy(&result[offset], file_split->device, file_split->devicelen);
349         offset += file_split->devicelen;
350         result[offset] = ':';
351         offset++;
352     }
353     start = file_split->predir;
354     while (file_split->predirlen > (start - file_split->predir)) {
355         const char *end = openssl_strnchr(start, '/',
356             file_split->predirlen - (start - file_split->predir));
357         if (!end)
358             end = start
359                 + file_split->predirlen - (start - file_split->predir);
360         strncpy(&result[offset], start, end - start);
361         offset += (int)(end - start);
362         result[offset] = '\\';
363         offset++;
364         start = end + 1;
365     }
366     start = file_split->dir;
367     while (file_split->dirlen > (start - file_split->dir)) {
368         const char *end = openssl_strnchr(start, '/',
369             file_split->dirlen - (start - file_split->dir));
370         if (!end)
371             end = start + file_split->dirlen - (start - file_split->dir);
372         strncpy(&result[offset], start, end - start);
373         offset += (int)(end - start);
374         result[offset] = '\\';
375         offset++;
376         start = end + 1;
377     }
378     strncpy(&result[offset], file_split->file, file_split->filelen);
379     offset += file_split->filelen;
380     result[offset] = '\0';
381     return result;
382 }
383 
win32_merger(DSO * dso,const char * filespec1,const char * filespec2)384 static char *win32_merger(DSO *dso, const char *filespec1,
385     const char *filespec2)
386 {
387     char *merged = NULL;
388     struct file_st *filespec1_split = NULL;
389     struct file_st *filespec2_split = NULL;
390 
391     if (!filespec1 && !filespec2) {
392         ERR_raise(ERR_LIB_DSO, ERR_R_PASSED_NULL_PARAMETER);
393         return NULL;
394     }
395     if (!filespec2) {
396         merged = OPENSSL_strdup(filespec1);
397         if (merged == NULL)
398             return NULL;
399     } else if (!filespec1) {
400         merged = OPENSSL_strdup(filespec2);
401         if (merged == NULL)
402             return NULL;
403     } else {
404         filespec1_split = win32_splitter(dso, filespec1, 0);
405         if (!filespec1_split) {
406             ERR_raise(ERR_LIB_DSO, ERR_R_DSO_LIB);
407             return NULL;
408         }
409         filespec2_split = win32_splitter(dso, filespec2, 1);
410         if (!filespec2_split) {
411             ERR_raise(ERR_LIB_DSO, ERR_R_DSO_LIB);
412             OPENSSL_free(filespec1_split);
413             return NULL;
414         }
415 
416         /* Fill in into filespec1_split */
417         if (!filespec1_split->node && !filespec1_split->device) {
418             filespec1_split->node = filespec2_split->node;
419             filespec1_split->nodelen = filespec2_split->nodelen;
420             filespec1_split->device = filespec2_split->device;
421             filespec1_split->devicelen = filespec2_split->devicelen;
422         }
423         if (!filespec1_split->dir) {
424             filespec1_split->dir = filespec2_split->dir;
425             filespec1_split->dirlen = filespec2_split->dirlen;
426         } else if (filespec1_split->dir[0] != '\\'
427             && filespec1_split->dir[0] != '/') {
428             filespec1_split->predir = filespec2_split->dir;
429             filespec1_split->predirlen = filespec2_split->dirlen;
430         }
431         if (!filespec1_split->file) {
432             filespec1_split->file = filespec2_split->file;
433             filespec1_split->filelen = filespec2_split->filelen;
434         }
435 
436         merged = win32_joiner(dso, filespec1_split);
437     }
438     OPENSSL_free(filespec1_split);
439     OPENSSL_free(filespec2_split);
440     return merged;
441 }
442 
win32_name_converter(DSO * dso,const char * filename)443 static char *win32_name_converter(DSO *dso, const char *filename)
444 {
445     char *translated;
446     int len, transform;
447 
448     transform = ((strstr(filename, "/") == NULL) && (strstr(filename, "\\") == NULL) && (strstr(filename, ":") == NULL));
449     /* If transform != 0, then we convert to %s.dll, else just dupe filename */
450 
451     len = strlen(filename) + 1;
452     if (transform)
453         len += strlen(".dll");
454     translated = OPENSSL_malloc(len);
455     if (translated == NULL) {
456         ERR_raise(ERR_LIB_DSO, DSO_R_NAME_TRANSLATION_FAILED);
457         return NULL;
458     }
459     BIO_snprintf(translated, len, "%s%s", filename, transform ? ".dll" : "");
460     return translated;
461 }
462 
openssl_strnchr(const char * string,int c,size_t len)463 static const char *openssl_strnchr(const char *string, int c, size_t len)
464 {
465     size_t i;
466     const char *p;
467     for (i = 0, p = string; i < len && *p; i++, p++) {
468         if (*p == c)
469             return p;
470     }
471     return NULL;
472 }
473 
474 #include <tlhelp32.h>
475 #ifdef _WIN32_WCE
476 #define DLLNAME "TOOLHELP.DLL"
477 #else
478 #ifdef MODULEENTRY32
479 #undef MODULEENTRY32 /* unmask the ASCII version! */
480 #endif
481 #define DLLNAME "KERNEL32.DLL"
482 #endif
483 
484 typedef HANDLE(WINAPI *CREATETOOLHELP32SNAPSHOT)(DWORD, DWORD);
485 typedef BOOL(WINAPI *CLOSETOOLHELP32SNAPSHOT)(HANDLE);
486 typedef BOOL(WINAPI *MODULE32)(HANDLE, MODULEENTRY32 *);
487 
win32_pathbyaddr(void * addr,char * path,int sz)488 static int win32_pathbyaddr(void *addr, char *path, int sz)
489 {
490     HMODULE hModule = NULL;
491     const DWORD wpathSize = 32768; /* 32768 is the maximum possible path length on Windows */
492     WCHAR *wpath = NULL;
493     DWORD wlen, wsz;
494     int utf8len = -1;
495 
496     if (addr == NULL) {
497         union {
498             int (*f)(void *, char *, int);
499             void *p;
500         } t = {
501             win32_pathbyaddr
502         };
503         addr = t.p;
504     }
505 
506     if (!GetModuleHandleExW(
507             GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT,
508             (LPCWSTR)addr, &hModule)) {
509         ERR_raise_data(ERR_LIB_DSO, DSO_R_SYM_FAILURE, "Unable to get module handle (%lu)\n",
510             GetLastError());
511         goto out;
512     }
513     wpath = (WCHAR *)OPENSSL_malloc(wpathSize * sizeof(WCHAR));
514     if (wpath == NULL) {
515         ERR_raise_data(ERR_LIB_DSO, DSO_R_NULL_HANDLE, "Path allocation failure (%lu)\n",
516             GetLastError());
517         goto out;
518     }
519     wlen = GetModuleFileNameW(hModule, wpath, wpathSize);
520     if (wlen == 0 || GetLastError() == ERROR_INSUFFICIENT_BUFFER) {
521         ERR_raise_data(ERR_LIB_DSO, DSO_R_NAME_TRANSLATION_FAILED, "Module name fetch failed (%lu)\n",
522             GetLastError());
523         goto out;
524     }
525 
526     /*
527      * If we pass a size of 0 or less, invoke the size-query pattern,
528      * in which we do not actually copy the name to the path buffer,
529      * but return the size the path buffer needs to be for this object
530      */
531     if (sz <= 0) {
532         utf8len = (int)(wlen + 1);
533         goto out;
534     }
535 
536     /*
537      * Convert the wide path to UTF-8
538      */
539     wsz = (DWORD)sz;
540     utf8len = WideCharToMultiByte(CP_UTF8, 0, wpath, -1, NULL, 0, NULL, NULL);
541     if (utf8len <= 0 || (DWORD)utf8len > wsz) {
542         ERR_raise_data(ERR_LIB_DSO, DSO_R_NAME_TRANSLATION_FAILED, "UTF8 query failed (%lu)\n",
543             GetLastError());
544         goto out;
545     }
546 
547     if (WideCharToMultiByte(CP_UTF8, 0, wpath, -1, path, wsz, NULL, NULL) <= 0) {
548         ERR_raise_data(ERR_LIB_DSO, DSO_R_NAME_TRANSLATION_FAILED, "UTF8 translation failed (%lu)\n",
549             GetLastError());
550         goto out;
551     }
552 out:
553     OPENSSL_free(wpath);
554     return utf8len;
555 }
556 
win32_globallookup(const char * name)557 static void *win32_globallookup(const char *name)
558 {
559     HMODULE dll;
560     HANDLE hModuleSnap = INVALID_HANDLE_VALUE;
561     MODULEENTRY32 me32;
562     CREATETOOLHELP32SNAPSHOT create_snap;
563     CLOSETOOLHELP32SNAPSHOT close_snap;
564     MODULE32 module_first, module_next;
565     union {
566         void *p;
567         FARPROC f;
568     } ret = { NULL };
569 
570     dll = LoadLibrary(TEXT(DLLNAME));
571     if (dll == NULL) {
572         ERR_raise(ERR_LIB_DSO, DSO_R_UNSUPPORTED);
573         return NULL;
574     }
575 
576     create_snap = GETPROCADDRESS(dll, "CreateToolhelp32Snapshot", CREATETOOLHELP32SNAPSHOT);
577     if (create_snap == NULL) {
578         FreeLibrary(dll);
579         ERR_raise(ERR_LIB_DSO, DSO_R_UNSUPPORTED);
580         return NULL;
581     }
582     /* We take the rest for granted... */
583 #ifdef _WIN32_WCE
584     close_snap = GETPROCADDRESS(dll, "CloseToolhelp32Snapshot", CLOSETOOLHELP32SNAPSHOT);
585 #else
586     close_snap = (CLOSETOOLHELP32SNAPSHOT)CloseHandle;
587 #endif
588     module_first = GETPROCADDRESS(dll, "Module32First", MODULE32);
589     module_next = GETPROCADDRESS(dll, "Module32Next", MODULE32);
590 
591     hModuleSnap = (*create_snap)(TH32CS_SNAPMODULE, 0);
592     if (hModuleSnap == INVALID_HANDLE_VALUE) {
593         FreeLibrary(dll);
594         ERR_raise(ERR_LIB_DSO, DSO_R_UNSUPPORTED);
595         return NULL;
596     }
597 
598     me32.dwSize = sizeof(me32);
599 
600     if (!(*module_first)(hModuleSnap, &me32)) {
601         (*close_snap)(hModuleSnap);
602         FreeLibrary(dll);
603         return NULL;
604     }
605 
606     do {
607         if ((ret.f = GETPROCADDRESS(me32.hModule, name, FARPROC))) {
608             (*close_snap)(hModuleSnap);
609             FreeLibrary(dll);
610             return ret.p;
611         }
612     } while ((*module_next)(hModuleSnap, &me32));
613 
614     (*close_snap)(hModuleSnap);
615     FreeLibrary(dll);
616     return NULL;
617 }
618 #endif /* DSO_WIN32 */
619