Raise an error on syscall failure in tls_retry_write_records
[openssl.git] / crypto / dso / dso_win32.c
1 /*
2  * Copyright 2000-2020 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 "e_os.h"
11 #include "dso_local.h"
12
13 #if defined(DSO_WIN32)
14
15 # ifdef _WIN32_WCE
16 #  if _WIN32_WCE < 300
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
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 DEFINE_STACK_OF(void)
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
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
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         DSOerr(DSO_F_WIN32_LOAD, DSO_R_NO_FILENAME);
106         goto err;
107     }
108     h = LoadLibraryA(filename);
109     if (h == NULL) {
110         DSOerr(DSO_F_WIN32_LOAD, DSO_R_LOAD_FAILED);
111         ERR_add_error_data(3, "filename(", filename, ")");
112         goto err;
113     }
114     p = OPENSSL_malloc(sizeof(*p));
115     if (p == NULL) {
116         DSOerr(DSO_F_WIN32_LOAD, ERR_R_MALLOC_FAILURE);
117         goto err;
118     }
119     *p = h;
120     if (!sk_void_push(dso->meth_data, p)) {
121         DSOerr(DSO_F_WIN32_LOAD, DSO_R_STACK_ERROR);
122         goto err;
123     }
124     /* Success */
125     dso->loaded_filename = filename;
126     return 1;
127  err:
128     /* Cleanup ! */
129     OPENSSL_free(filename);
130     OPENSSL_free(p);
131     if (h != NULL)
132         FreeLibrary(h);
133     return 0;
134 }
135
136 static int win32_unload(DSO *dso)
137 {
138     HINSTANCE *p;
139     if (dso == NULL) {
140         DSOerr(DSO_F_WIN32_UNLOAD, ERR_R_PASSED_NULL_PARAMETER);
141         return 0;
142     }
143     if (sk_void_num(dso->meth_data) < 1)
144         return 1;
145     p = sk_void_pop(dso->meth_data);
146     if (p == NULL) {
147         DSOerr(DSO_F_WIN32_UNLOAD, DSO_R_NULL_HANDLE);
148         return 0;
149     }
150     if (!FreeLibrary(*p)) {
151         DSOerr(DSO_F_WIN32_UNLOAD, DSO_R_UNLOAD_FAILED);
152         /*
153          * We should push the value back onto the stack in case of a retry.
154          */
155         sk_void_push(dso->meth_data, p);
156         return 0;
157     }
158     /* Cleanup */
159     OPENSSL_free(p);
160     return 1;
161 }
162
163 static DSO_FUNC_TYPE win32_bind_func(DSO *dso, const char *symname)
164 {
165     HINSTANCE *ptr;
166     union {
167         void *p;
168         FARPROC f;
169     } sym;
170
171     if ((dso == NULL) || (symname == NULL)) {
172         DSOerr(DSO_F_WIN32_BIND_FUNC, ERR_R_PASSED_NULL_PARAMETER);
173         return NULL;
174     }
175     if (sk_void_num(dso->meth_data) < 1) {
176         DSOerr(DSO_F_WIN32_BIND_FUNC, DSO_R_STACK_ERROR);
177         return NULL;
178     }
179     ptr = sk_void_value(dso->meth_data, sk_void_num(dso->meth_data) - 1);
180     if (ptr == NULL) {
181         DSOerr(DSO_F_WIN32_BIND_FUNC, DSO_R_NULL_HANDLE);
182         return NULL;
183     }
184     sym.f = GetProcAddress(*ptr, symname);
185     if (sym.p == NULL) {
186         DSOerr(DSO_F_WIN32_BIND_FUNC, DSO_R_SYM_FAILURE);
187         ERR_add_error_data(3, "symname(", symname, ")");
188         return NULL;
189     }
190     return (DSO_FUNC_TYPE)sym.f;
191 }
192
193 struct file_st {
194     const char *node;
195     int nodelen;
196     const char *device;
197     int devicelen;
198     const char *predir;
199     int predirlen;
200     const char *dir;
201     int dirlen;
202     const char *file;
203     int filelen;
204 };
205
206 static struct file_st *win32_splitter(DSO *dso, const char *filename,
207                                       int assume_last_is_dir)
208 {
209     struct file_st *result = NULL;
210     enum { IN_NODE, IN_DEVICE, IN_FILE } position;
211     const char *start = filename;
212     char last;
213
214     if (!filename) {
215         DSOerr(DSO_F_WIN32_SPLITTER, DSO_R_NO_FILENAME);
216         return NULL;
217     }
218
219     result = OPENSSL_zalloc(sizeof(*result));
220     if (result == NULL) {
221         DSOerr(DSO_F_WIN32_SPLITTER, ERR_R_MALLOC_FAILURE);
222         return NULL;
223     }
224
225     position = IN_DEVICE;
226
227     if ((filename[0] == '\\' && filename[1] == '\\')
228         || (filename[0] == '/' && filename[1] == '/')) {
229         position = IN_NODE;
230         filename += 2;
231         start = filename;
232         result->node = start;
233     }
234
235     do {
236         last = filename[0];
237         switch (last) {
238         case ':':
239             if (position != IN_DEVICE) {
240                 DSOerr(DSO_F_WIN32_SPLITTER, DSO_R_INCORRECT_FILE_SYNTAX);
241                 OPENSSL_free(result);
242                 return NULL;
243             }
244             result->device = start;
245             result->devicelen = (int)(filename - start);
246             position = IN_FILE;
247             start = ++filename;
248             result->dir = start;
249             break;
250         case '\\':
251         case '/':
252             if (position == IN_NODE) {
253                 result->nodelen = (int)(filename - start);
254                 position = IN_FILE;
255                 start = ++filename;
256                 result->dir = start;
257             } else if (position == IN_DEVICE) {
258                 position = IN_FILE;
259                 filename++;
260                 result->dir = start;
261                 result->dirlen = (int)(filename - start);
262                 start = filename;
263             } else {
264                 filename++;
265                 result->dirlen += (int)(filename - start);
266                 start = filename;
267             }
268             break;
269         case '\0':
270             if (position == IN_NODE) {
271                 result->nodelen = (int)(filename - start);
272             } else {
273                 if (filename - start > 0) {
274                     if (assume_last_is_dir) {
275                         if (position == IN_DEVICE) {
276                             result->dir = start;
277                             result->dirlen = 0;
278                         }
279                         result->dirlen += (int)(filename - start);
280                     } else {
281                         result->file = start;
282                         result->filelen = (int)(filename - start);
283                     }
284                 }
285             }
286             break;
287         default:
288             filename++;
289             break;
290         }
291     }
292     while (last);
293
294     if (!result->nodelen)
295         result->node = NULL;
296     if (!result->devicelen)
297         result->device = NULL;
298     if (!result->dirlen)
299         result->dir = NULL;
300     if (!result->filelen)
301         result->file = NULL;
302
303     return result;
304 }
305
306 static char *win32_joiner(DSO *dso, const struct file_st *file_split)
307 {
308     int len = 0, offset = 0;
309     char *result = NULL;
310     const char *start;
311
312     if (!file_split) {
313         DSOerr(DSO_F_WIN32_JOINER, ERR_R_PASSED_NULL_PARAMETER);
314         return NULL;
315     }
316     if (file_split->node) {
317         len += 2 + file_split->nodelen; /* 2 for starting \\ */
318         if (file_split->predir || file_split->dir || file_split->file)
319             len++;              /* 1 for ending \ */
320     } else if (file_split->device) {
321         len += file_split->devicelen + 1; /* 1 for ending : */
322     }
323     len += file_split->predirlen;
324     if (file_split->predir && (file_split->dir || file_split->file)) {
325         len++;                  /* 1 for ending \ */
326     }
327     len += file_split->dirlen;
328     if (file_split->dir && file_split->file) {
329         len++;                  /* 1 for ending \ */
330     }
331     len += file_split->filelen;
332
333     if (!len) {
334         DSOerr(DSO_F_WIN32_JOINER, DSO_R_EMPTY_FILE_STRUCTURE);
335         return NULL;
336     }
337
338     result = OPENSSL_malloc(len + 1);
339     if (result == NULL) {
340         DSOerr(DSO_F_WIN32_JOINER, ERR_R_MALLOC_FAILURE);
341         return NULL;
342     }
343
344     if (file_split->node) {
345         strcpy(&result[offset], "\\\\");
346         offset += 2;
347         strncpy(&result[offset], file_split->node, file_split->nodelen);
348         offset += file_split->nodelen;
349         if (file_split->predir || file_split->dir || file_split->file) {
350             result[offset] = '\\';
351             offset++;
352         }
353     } else if (file_split->device) {
354         strncpy(&result[offset], file_split->device, file_split->devicelen);
355         offset += file_split->devicelen;
356         result[offset] = ':';
357         offset++;
358     }
359     start = file_split->predir;
360     while (file_split->predirlen > (start - file_split->predir)) {
361         const char *end = openssl_strnchr(start, '/',
362                                           file_split->predirlen - (start -
363                                                                    file_split->predir));
364         if (!end)
365             end = start
366                 + file_split->predirlen - (start - file_split->predir);
367         strncpy(&result[offset], start, end - start);
368         offset += (int)(end - start);
369         result[offset] = '\\';
370         offset++;
371         start = end + 1;
372     }
373     start = file_split->dir;
374     while (file_split->dirlen > (start - file_split->dir)) {
375         const char *end = openssl_strnchr(start, '/',
376                                           file_split->dirlen - (start -
377                                                                 file_split->dir));
378         if (!end)
379             end = start + file_split->dirlen - (start - file_split->dir);
380         strncpy(&result[offset], start, end - start);
381         offset += (int)(end - start);
382         result[offset] = '\\';
383         offset++;
384         start = end + 1;
385     }
386     strncpy(&result[offset], file_split->file, file_split->filelen);
387     offset += file_split->filelen;
388     result[offset] = '\0';
389     return result;
390 }
391
392 static char *win32_merger(DSO *dso, const char *filespec1,
393                           const char *filespec2)
394 {
395     char *merged = NULL;
396     struct file_st *filespec1_split = NULL;
397     struct file_st *filespec2_split = NULL;
398
399     if (!filespec1 && !filespec2) {
400         DSOerr(DSO_F_WIN32_MERGER, ERR_R_PASSED_NULL_PARAMETER);
401         return NULL;
402     }
403     if (!filespec2) {
404         merged = OPENSSL_strdup(filespec1);
405         if (merged == NULL) {
406             DSOerr(DSO_F_WIN32_MERGER, ERR_R_MALLOC_FAILURE);
407             return NULL;
408         }
409     } else if (!filespec1) {
410         merged = OPENSSL_strdup(filespec2);
411         if (merged == NULL) {
412             DSOerr(DSO_F_WIN32_MERGER, ERR_R_MALLOC_FAILURE);
413             return NULL;
414         }
415     } else {
416         filespec1_split = win32_splitter(dso, filespec1, 0);
417         if (!filespec1_split) {
418             DSOerr(DSO_F_WIN32_MERGER, ERR_R_MALLOC_FAILURE);
419             return NULL;
420         }
421         filespec2_split = win32_splitter(dso, filespec2, 1);
422         if (!filespec2_split) {
423             DSOerr(DSO_F_WIN32_MERGER, ERR_R_MALLOC_FAILURE);
424             OPENSSL_free(filespec1_split);
425             return NULL;
426         }
427
428         /* Fill in into filespec1_split */
429         if (!filespec1_split->node && !filespec1_split->device) {
430             filespec1_split->node = filespec2_split->node;
431             filespec1_split->nodelen = filespec2_split->nodelen;
432             filespec1_split->device = filespec2_split->device;
433             filespec1_split->devicelen = filespec2_split->devicelen;
434         }
435         if (!filespec1_split->dir) {
436             filespec1_split->dir = filespec2_split->dir;
437             filespec1_split->dirlen = filespec2_split->dirlen;
438         } else if (filespec1_split->dir[0] != '\\'
439                    && filespec1_split->dir[0] != '/') {
440             filespec1_split->predir = filespec2_split->dir;
441             filespec1_split->predirlen = filespec2_split->dirlen;
442         }
443         if (!filespec1_split->file) {
444             filespec1_split->file = filespec2_split->file;
445             filespec1_split->filelen = filespec2_split->filelen;
446         }
447
448         merged = win32_joiner(dso, filespec1_split);
449     }
450     OPENSSL_free(filespec1_split);
451     OPENSSL_free(filespec2_split);
452     return merged;
453 }
454
455 static char *win32_name_converter(DSO *dso, const char *filename)
456 {
457     char *translated;
458     int len, transform;
459
460     len = strlen(filename);
461     transform = ((strstr(filename, "/") == NULL) &&
462                  (strstr(filename, "\\") == NULL) &&
463                  (strstr(filename, ":") == NULL));
464     if (transform)
465         /* We will convert this to "%s.dll" */
466         translated = OPENSSL_malloc(len + 5);
467     else
468         /* We will simply duplicate filename */
469         translated = OPENSSL_malloc(len + 1);
470     if (translated == NULL) {
471         DSOerr(DSO_F_WIN32_NAME_CONVERTER, DSO_R_NAME_TRANSLATION_FAILED);
472         return NULL;
473     }
474     if (transform)
475         sprintf(translated, "%s.dll", filename);
476     else
477         sprintf(translated, "%s", filename);
478     return translated;
479 }
480
481 static const char *openssl_strnchr(const char *string, int c, size_t len)
482 {
483     size_t i;
484     const char *p;
485     for (i = 0, p = string; i < len && *p; i++, p++) {
486         if (*p == c)
487             return p;
488     }
489     return NULL;
490 }
491
492 # include <tlhelp32.h>
493 # ifdef _WIN32_WCE
494 #  define DLLNAME "TOOLHELP.DLL"
495 # else
496 #  ifdef MODULEENTRY32
497 #   undef MODULEENTRY32         /* unmask the ASCII version! */
498 #  endif
499 #  define DLLNAME "KERNEL32.DLL"
500 # endif
501
502 typedef HANDLE(WINAPI *CREATETOOLHELP32SNAPSHOT) (DWORD, DWORD);
503 typedef BOOL(WINAPI *CLOSETOOLHELP32SNAPSHOT) (HANDLE);
504 typedef BOOL(WINAPI *MODULE32) (HANDLE, MODULEENTRY32 *);
505
506 static int win32_pathbyaddr(void *addr, char *path, int sz)
507 {
508     HMODULE dll;
509     HANDLE hModuleSnap = INVALID_HANDLE_VALUE;
510     MODULEENTRY32 me32;
511     CREATETOOLHELP32SNAPSHOT create_snap;
512     CLOSETOOLHELP32SNAPSHOT close_snap;
513     MODULE32 module_first, module_next;
514
515     if (addr == NULL) {
516         union {
517             int (*f) (void *, char *, int);
518             void *p;
519         } t = {
520             win32_pathbyaddr
521         };
522         addr = t.p;
523     }
524
525     dll = LoadLibrary(TEXT(DLLNAME));
526     if (dll == NULL) {
527         DSOerr(DSO_F_WIN32_PATHBYADDR, DSO_R_UNSUPPORTED);
528         return -1;
529     }
530
531     create_snap = (CREATETOOLHELP32SNAPSHOT)
532         GetProcAddress(dll, "CreateToolhelp32Snapshot");
533     if (create_snap == NULL) {
534         FreeLibrary(dll);
535         DSOerr(DSO_F_WIN32_PATHBYADDR, DSO_R_UNSUPPORTED);
536         return -1;
537     }
538     /* We take the rest for granted... */
539 # ifdef _WIN32_WCE
540     close_snap = (CLOSETOOLHELP32SNAPSHOT)
541         GetProcAddress(dll, "CloseToolhelp32Snapshot");
542 # else
543     close_snap = (CLOSETOOLHELP32SNAPSHOT) CloseHandle;
544 # endif
545     module_first = (MODULE32) GetProcAddress(dll, "Module32First");
546     module_next = (MODULE32) GetProcAddress(dll, "Module32Next");
547
548     /*
549      * Take a snapshot of current process which includes
550      * list of all involved modules.
551      */
552     hModuleSnap = (*create_snap) (TH32CS_SNAPMODULE, 0);
553     if (hModuleSnap == INVALID_HANDLE_VALUE) {
554         FreeLibrary(dll);
555         DSOerr(DSO_F_WIN32_PATHBYADDR, DSO_R_UNSUPPORTED);
556         return -1;
557     }
558
559     me32.dwSize = sizeof(me32);
560
561     if (!(*module_first) (hModuleSnap, &me32)) {
562         (*close_snap) (hModuleSnap);
563         FreeLibrary(dll);
564         DSOerr(DSO_F_WIN32_PATHBYADDR, DSO_R_FAILURE);
565         return -1;
566     }
567
568     /* Enumerate the modules to find one which includes me. */
569     do {
570         if ((uintptr_t) addr >= (uintptr_t) me32.modBaseAddr &&
571             (uintptr_t) addr < (uintptr_t) (me32.modBaseAddr + me32.modBaseSize)) {
572             (*close_snap) (hModuleSnap);
573             FreeLibrary(dll);
574 # ifdef _WIN32_WCE
575 #  if _WIN32_WCE >= 101
576             return WideCharToMultiByte(CP_ACP, 0, me32.szExePath, -1,
577                                        path, sz, NULL, NULL);
578 #  else
579             {
580                 int i, len = (int)wcslen(me32.szExePath);
581                 if (sz <= 0)
582                     return len + 1;
583                 if (len >= sz)
584                     len = sz - 1;
585                 for (i = 0; i < len; i++)
586                     path[i] = (char)me32.szExePath[i];
587                 path[len++] = '\0';
588                 return len;
589             }
590 #  endif
591 # else
592             {
593                 int len = (int)strlen(me32.szExePath);
594                 if (sz <= 0)
595                     return len + 1;
596                 if (len >= sz)
597                     len = sz - 1;
598                 memcpy(path, me32.szExePath, len);
599                 path[len++] = '\0';
600                 return len;
601             }
602 # endif
603         }
604     } while ((*module_next) (hModuleSnap, &me32));
605
606     (*close_snap) (hModuleSnap);
607     FreeLibrary(dll);
608     return 0;
609 }
610
611 static void *win32_globallookup(const char *name)
612 {
613     HMODULE dll;
614     HANDLE hModuleSnap = INVALID_HANDLE_VALUE;
615     MODULEENTRY32 me32;
616     CREATETOOLHELP32SNAPSHOT create_snap;
617     CLOSETOOLHELP32SNAPSHOT close_snap;
618     MODULE32 module_first, module_next;
619     union {
620         void *p;
621         FARPROC f;
622     } ret = { NULL };
623
624     dll = LoadLibrary(TEXT(DLLNAME));
625     if (dll == NULL) {
626         DSOerr(DSO_F_WIN32_GLOBALLOOKUP, DSO_R_UNSUPPORTED);
627         return NULL;
628     }
629
630     create_snap = (CREATETOOLHELP32SNAPSHOT)
631         GetProcAddress(dll, "CreateToolhelp32Snapshot");
632     if (create_snap == NULL) {
633         FreeLibrary(dll);
634         DSOerr(DSO_F_WIN32_GLOBALLOOKUP, DSO_R_UNSUPPORTED);
635         return NULL;
636     }
637     /* We take the rest for granted... */
638 # ifdef _WIN32_WCE
639     close_snap = (CLOSETOOLHELP32SNAPSHOT)
640         GetProcAddress(dll, "CloseToolhelp32Snapshot");
641 # else
642     close_snap = (CLOSETOOLHELP32SNAPSHOT) CloseHandle;
643 # endif
644     module_first = (MODULE32) GetProcAddress(dll, "Module32First");
645     module_next = (MODULE32) GetProcAddress(dll, "Module32Next");
646
647     hModuleSnap = (*create_snap) (TH32CS_SNAPMODULE, 0);
648     if (hModuleSnap == INVALID_HANDLE_VALUE) {
649         FreeLibrary(dll);
650         DSOerr(DSO_F_WIN32_GLOBALLOOKUP, DSO_R_UNSUPPORTED);
651         return NULL;
652     }
653
654     me32.dwSize = sizeof(me32);
655
656     if (!(*module_first) (hModuleSnap, &me32)) {
657         (*close_snap) (hModuleSnap);
658         FreeLibrary(dll);
659         return NULL;
660     }
661
662     do {
663         if ((ret.f = GetProcAddress(me32.hModule, name))) {
664             (*close_snap) (hModuleSnap);
665             FreeLibrary(dll);
666             return ret.p;
667         }
668     } while ((*module_next) (hModuleSnap, &me32));
669
670     (*close_snap) (hModuleSnap);
671     FreeLibrary(dll);
672     return NULL;
673 }
674 #endif                          /* DSO_WIN32 */