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