1/* dso_win32.c -*- mode:C; c-file-style: "eay" -*- */
2/* Written by Geoff Thorpe (geoff@geoffthorpe.net) for the OpenSSL
3 * project 2000.
4 */
5/* ====================================================================
6 * Copyright (c) 2000 The OpenSSL Project.  All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 *
12 * 1. Redistributions of source code must retain the above copyright
13 *    notice, this list of conditions and the following disclaimer.
14 *
15 * 2. Redistributions in binary form must reproduce the above copyright
16 *    notice, this list of conditions and the following disclaimer in
17 *    the documentation and/or other materials provided with the
18 *    distribution.
19 *
20 * 3. All advertising materials mentioning features or use of this
21 *    software must display the following acknowledgment:
22 *    "This product includes software developed by the OpenSSL Project
23 *    for use in the OpenSSL Toolkit. (http://www.OpenSSL.org/)"
24 *
25 * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to
26 *    endorse or promote products derived from this software without
27 *    prior written permission. For written permission, please contact
28 *    licensing@OpenSSL.org.
29 *
30 * 5. Products derived from this software may not be called "OpenSSL"
31 *    nor may "OpenSSL" appear in their names without prior written
32 *    permission of the OpenSSL Project.
33 *
34 * 6. Redistributions of any form whatsoever must retain the following
35 *    acknowledgment:
36 *    "This product includes software developed by the OpenSSL Project
37 *    for use in the OpenSSL Toolkit (http://www.OpenSSL.org/)"
38 *
39 * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY
40 * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
41 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
42 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE OpenSSL PROJECT OR
43 * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
44 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
45 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
46 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
47 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
48 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
49 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
50 * OF THE POSSIBILITY OF SUCH DAMAGE.
51 * ====================================================================
52 *
53 * This product includes cryptographic software written by Eric Young
54 * (eay@cryptsoft.com).  This product includes software written by Tim
55 * Hudson (tjh@cryptsoft.com).
56 *
57 */
58
59#include <stdio.h>
60#include <string.h>
61#include "cryptlib.h"
62#include <openssl/dso.h>
63
64#if !defined(DSO_WIN32)
65DSO_METHOD *DSO_METHOD_win32(void)
66	{
67	return NULL;
68	}
69#else
70
71#ifdef _WIN32_WCE
72# if _WIN32_WCE < 300
73static FARPROC GetProcAddressA(HMODULE hModule,LPCSTR lpProcName)
74	{
75	WCHAR lpProcNameW[64];
76	int i;
77
78	for (i=0;lpProcName[i] && i<64;i++)
79		lpProcNameW[i] = (WCHAR)lpProcName[i];
80	if (i==64) return NULL;
81	lpProcNameW[i] = 0;
82
83	return GetProcAddressW(hModule,lpProcNameW);
84	}
85# endif
86# undef GetProcAddress
87# define GetProcAddress GetProcAddressA
88
89static HINSTANCE LoadLibraryA(LPCSTR lpLibFileName)
90	{
91	WCHAR *fnamw;
92	size_t len_0=strlen(lpLibFileName)+1,i;
93
94#ifdef _MSC_VER
95	fnamw = (WCHAR *)_alloca (len_0*sizeof(WCHAR));
96#else
97	fnamw = (WCHAR *)alloca (len_0*sizeof(WCHAR));
98#endif
99	if (fnamw == NULL) return NULL;
100
101#if defined(_WIN32_WCE) && _WIN32_WCE>=101
102	if (!MultiByteToWideChar(CP_ACP,0,lpLibFileName,len_0,fnamw,len_0))
103#endif
104		for (i=0;i<len_0;i++) fnamw[i]=(WCHAR)lpLibFileName[i];
105
106	return LoadLibraryW(fnamw);
107	}
108#endif
109
110/* Part of the hack in "win32_load" ... */
111#define DSO_MAX_TRANSLATED_SIZE 256
112
113static int win32_load(DSO *dso);
114static int win32_unload(DSO *dso);
115static void *win32_bind_var(DSO *dso, const char *symname);
116static DSO_FUNC_TYPE win32_bind_func(DSO *dso, const char *symname);
117#if 0
118static int win32_unbind_var(DSO *dso, char *symname, void *symptr);
119static int win32_unbind_func(DSO *dso, char *symname, DSO_FUNC_TYPE symptr);
120static int win32_init(DSO *dso);
121static int win32_finish(DSO *dso);
122static long win32_ctrl(DSO *dso, int cmd, long larg, void *parg);
123#endif
124static char *win32_name_converter(DSO *dso, const char *filename);
125static char *win32_merger(DSO *dso, const char *filespec1,
126	const char *filespec2);
127
128static const char *openssl_strnchr(const char *string, int c, size_t len);
129
130static DSO_METHOD dso_meth_win32 = {
131	"OpenSSL 'win32' shared library method",
132	win32_load,
133	win32_unload,
134	win32_bind_var,
135	win32_bind_func,
136/* For now, "unbind" doesn't exist */
137#if 0
138	NULL, /* unbind_var */
139	NULL, /* unbind_func */
140#endif
141	NULL, /* ctrl */
142	win32_name_converter,
143	win32_merger,
144	NULL, /* init */
145	NULL  /* finish */
146	};
147
148DSO_METHOD *DSO_METHOD_win32(void)
149	{
150	return(&dso_meth_win32);
151	}
152
153/* For this DSO_METHOD, our meth_data STACK will contain;
154 * (i) a pointer to the handle (HINSTANCE) returned from
155 *     LoadLibrary(), and copied.
156 */
157
158static int win32_load(DSO *dso)
159	{
160	HINSTANCE h = NULL, *p = NULL;
161	/* See applicable comments from dso_dl.c */
162	char *filename = DSO_convert_filename(dso, NULL);
163
164	if(filename == NULL)
165		{
166		DSOerr(DSO_F_WIN32_LOAD,DSO_R_NO_FILENAME);
167		goto err;
168		}
169	h = LoadLibraryA(filename);
170	if(h == NULL)
171		{
172		DSOerr(DSO_F_WIN32_LOAD,DSO_R_LOAD_FAILED);
173		ERR_add_error_data(3, "filename(", filename, ")");
174		goto err;
175		}
176	p = (HINSTANCE *)OPENSSL_malloc(sizeof(HINSTANCE));
177	if(p == NULL)
178		{
179		DSOerr(DSO_F_WIN32_LOAD,ERR_R_MALLOC_FAILURE);
180		goto err;
181		}
182	*p = h;
183	if(!sk_push(dso->meth_data, (char *)p))
184		{
185		DSOerr(DSO_F_WIN32_LOAD,DSO_R_STACK_ERROR);
186		goto err;
187		}
188	/* Success */
189	dso->loaded_filename = filename;
190	return(1);
191err:
192	/* Cleanup !*/
193	if(filename != NULL)
194		OPENSSL_free(filename);
195	if(p != NULL)
196		OPENSSL_free(p);
197	if(h != NULL)
198		FreeLibrary(h);
199	return(0);
200	}
201
202static int win32_unload(DSO *dso)
203	{
204	HINSTANCE *p;
205	if(dso == NULL)
206		{
207		DSOerr(DSO_F_WIN32_UNLOAD,ERR_R_PASSED_NULL_PARAMETER);
208		return(0);
209		}
210	if(sk_num(dso->meth_data) < 1)
211		return(1);
212	p = (HINSTANCE *)sk_pop(dso->meth_data);
213	if(p == NULL)
214		{
215		DSOerr(DSO_F_WIN32_UNLOAD,DSO_R_NULL_HANDLE);
216		return(0);
217		}
218	if(!FreeLibrary(*p))
219		{
220		DSOerr(DSO_F_WIN32_UNLOAD,DSO_R_UNLOAD_FAILED);
221		/* We should push the value back onto the stack in
222		 * case of a retry. */
223		sk_push(dso->meth_data, (char *)p);
224		return(0);
225		}
226	/* Cleanup */
227	OPENSSL_free(p);
228	return(1);
229	}
230
231/* Using GetProcAddress for variables? TODO: Check this out in
232 * the Win32 API docs, there's probably a variant for variables. */
233static void *win32_bind_var(DSO *dso, const char *symname)
234	{
235	HINSTANCE *ptr;
236	void *sym;
237
238	if((dso == NULL) || (symname == NULL))
239		{
240		DSOerr(DSO_F_WIN32_BIND_VAR,ERR_R_PASSED_NULL_PARAMETER);
241		return(NULL);
242		}
243	if(sk_num(dso->meth_data) < 1)
244		{
245		DSOerr(DSO_F_WIN32_BIND_VAR,DSO_R_STACK_ERROR);
246		return(NULL);
247		}
248	ptr = (HINSTANCE *)sk_value(dso->meth_data, sk_num(dso->meth_data) - 1);
249	if(ptr == NULL)
250		{
251		DSOerr(DSO_F_WIN32_BIND_VAR,DSO_R_NULL_HANDLE);
252		return(NULL);
253		}
254	sym = GetProcAddress(*ptr, symname);
255	if(sym == NULL)
256		{
257		DSOerr(DSO_F_WIN32_BIND_VAR,DSO_R_SYM_FAILURE);
258		ERR_add_error_data(3, "symname(", symname, ")");
259		return(NULL);
260		}
261	return(sym);
262	}
263
264static DSO_FUNC_TYPE win32_bind_func(DSO *dso, const char *symname)
265	{
266	HINSTANCE *ptr;
267	void *sym;
268
269	if((dso == NULL) || (symname == NULL))
270		{
271		DSOerr(DSO_F_WIN32_BIND_FUNC,ERR_R_PASSED_NULL_PARAMETER);
272		return(NULL);
273		}
274	if(sk_num(dso->meth_data) < 1)
275		{
276		DSOerr(DSO_F_WIN32_BIND_FUNC,DSO_R_STACK_ERROR);
277		return(NULL);
278		}
279	ptr = (HINSTANCE *)sk_value(dso->meth_data, sk_num(dso->meth_data) - 1);
280	if(ptr == NULL)
281		{
282		DSOerr(DSO_F_WIN32_BIND_FUNC,DSO_R_NULL_HANDLE);
283		return(NULL);
284		}
285	sym = GetProcAddress(*ptr, symname);
286	if(sym == NULL)
287		{
288		DSOerr(DSO_F_WIN32_BIND_FUNC,DSO_R_SYM_FAILURE);
289		ERR_add_error_data(3, "symname(", symname, ")");
290		return(NULL);
291		}
292	return((DSO_FUNC_TYPE)sym);
293	}
294
295struct file_st
296	{
297	const char *node; int nodelen;
298	const char *device; int devicelen;
299	const char *predir; int predirlen;
300	const char *dir; int dirlen;
301	const char *file; int filelen;
302	};
303
304static struct file_st *win32_splitter(DSO *dso, const char *filename,
305	int assume_last_is_dir)
306	{
307	struct file_st *result = NULL;
308	enum { IN_NODE, IN_DEVICE, IN_FILE } position;
309	const char *start = filename;
310	char last;
311
312	if (!filename)
313		{
314		DSOerr(DSO_F_WIN32_SPLITTER,DSO_R_NO_FILENAME);
315		/*goto err;*/
316		return(NULL);
317		}
318
319	result = OPENSSL_malloc(sizeof(struct file_st));
320	if(result == NULL)
321		{
322		DSOerr(DSO_F_WIN32_SPLITTER,
323			ERR_R_MALLOC_FAILURE);
324		return(NULL);
325		}
326
327	memset(result, 0, sizeof(struct file_st));
328	position = IN_DEVICE;
329
330	if((filename[0] == '\\' && filename[1] == '\\')
331		|| (filename[0] == '/' && filename[1] == '/'))
332		{
333		position = IN_NODE;
334		filename += 2;
335		start = filename;
336		result->node = start;
337		}
338
339	do
340		{
341		last = filename[0];
342		switch(last)
343			{
344		case ':':
345			if(position != IN_DEVICE)
346				{
347				DSOerr(DSO_F_WIN32_SPLITTER,
348					DSO_R_INCORRECT_FILE_SYNTAX);
349				/*goto err;*/
350				OPENSSL_free(result);
351				return(NULL);
352				}
353			result->device = start;
354			result->devicelen = filename - start;
355			position = IN_FILE;
356			start = ++filename;
357			result->dir = start;
358			break;
359		case '\\':
360		case '/':
361			if(position == IN_NODE)
362				{
363				result->nodelen = filename - start;
364				position = IN_FILE;
365				start = ++filename;
366				result->dir = start;
367				}
368			else if(position == IN_DEVICE)
369				{
370				position = IN_FILE;
371				filename++;
372				result->dir = start;
373				result->dirlen = filename - start;
374				start = filename;
375				}
376			else
377				{
378				filename++;
379				result->dirlen += filename - start;
380				start = filename;
381				}
382			break;
383		case '\0':
384			if(position == IN_NODE)
385				{
386				result->nodelen = filename - start;
387				}
388			else
389				{
390				if(filename - start > 0)
391					{
392					if (assume_last_is_dir)
393						{
394						if (position == IN_DEVICE)
395							{
396							result->dir = start;
397							result->dirlen = 0;
398							}
399						result->dirlen +=
400							filename - start;
401						}
402					else
403						{
404						result->file = start;
405						result->filelen =
406							filename - start;
407						}
408					}
409				}
410			break;
411		default:
412			filename++;
413			break;
414			}
415		}
416	while(last);
417
418	if(!result->nodelen) result->node = NULL;
419	if(!result->devicelen) result->device = NULL;
420	if(!result->dirlen) result->dir = NULL;
421	if(!result->filelen) result->file = NULL;
422
423	return(result);
424	}
425
426static char *win32_joiner(DSO *dso, const struct file_st *file_split)
427	{
428	int len = 0, offset = 0;
429	char *result = NULL;
430	const char *start;
431
432	if(!file_split)
433		{
434		DSOerr(DSO_F_WIN32_JOINER,
435				ERR_R_PASSED_NULL_PARAMETER);
436		return(NULL);
437		}
438	if(file_split->node)
439		{
440		len += 2 + file_split->nodelen;	/* 2 for starting \\ */
441		if(file_split->predir || file_split->dir || file_split->file)
442			len++;	/* 1 for ending \ */
443		}
444	else if(file_split->device)
445		{
446		len += file_split->devicelen + 1; /* 1 for ending : */
447		}
448	len += file_split->predirlen;
449	if(file_split->predir && (file_split->dir || file_split->file))
450		{
451		len++;	/* 1 for ending \ */
452		}
453	len += file_split->dirlen;
454	if(file_split->dir && file_split->file)
455		{
456		len++;	/* 1 for ending \ */
457		}
458	len += file_split->filelen;
459
460	if(!len)
461		{
462		DSOerr(DSO_F_WIN32_JOINER, DSO_R_EMPTY_FILE_STRUCTURE);
463		return(NULL);
464		}
465
466	result = OPENSSL_malloc(len + 1);
467	if (!result)
468		{
469		DSOerr(DSO_F_WIN32_JOINER,
470			ERR_R_MALLOC_FAILURE);
471		return(NULL);
472		}
473
474	if(file_split->node)
475		{
476		strcpy(&result[offset], "\\\\"); offset += 2;
477		strncpy(&result[offset], file_split->node,
478			file_split->nodelen); offset += file_split->nodelen;
479		if(file_split->predir || file_split->dir || file_split->file)
480			{
481			result[offset] = '\\'; offset++;
482			}
483		}
484	else if(file_split->device)
485		{
486		strncpy(&result[offset], file_split->device,
487			file_split->devicelen); offset += file_split->devicelen;
488		result[offset] = ':'; offset++;
489		}
490	start = file_split->predir;
491	while(file_split->predirlen > (start - file_split->predir))
492		{
493		const char *end = openssl_strnchr(start, '/',
494			file_split->predirlen - (start - file_split->predir));
495		if(!end)
496			end = start
497				+ file_split->predirlen
498				- (start - file_split->predir);
499		strncpy(&result[offset], start,
500			end - start); offset += end - start;
501		result[offset] = '\\'; offset++;
502		start = end + 1;
503		}
504#if 0 /* Not needed, since the directory converter above already appeneded
505	 a backslash */
506	if(file_split->predir && (file_split->dir || file_split->file))
507		{
508		result[offset] = '\\'; offset++;
509		}
510#endif
511	start = file_split->dir;
512	while(file_split->dirlen > (start - file_split->dir))
513		{
514		const char *end = openssl_strnchr(start, '/',
515			file_split->dirlen - (start - file_split->dir));
516		if(!end)
517			end = start
518				+ file_split->dirlen
519				- (start - file_split->dir);
520		strncpy(&result[offset], start,
521			end - start); offset += end - start;
522		result[offset] = '\\'; offset++;
523		start = end + 1;
524		}
525#if 0 /* Not needed, since the directory converter above already appeneded
526	 a backslash */
527	if(file_split->dir && file_split->file)
528		{
529		result[offset] = '\\'; offset++;
530		}
531#endif
532	strncpy(&result[offset], file_split->file,
533		file_split->filelen); offset += file_split->filelen;
534	result[offset] = '\0';
535	return(result);
536	}
537
538static char *win32_merger(DSO *dso, const char *filespec1, const char *filespec2)
539	{
540	char *merged = NULL;
541	struct file_st *filespec1_split = NULL;
542	struct file_st *filespec2_split = NULL;
543
544	if(!filespec1 && !filespec2)
545		{
546		DSOerr(DSO_F_WIN32_MERGER,
547				ERR_R_PASSED_NULL_PARAMETER);
548		return(NULL);
549		}
550	if (!filespec2)
551		{
552		merged = OPENSSL_malloc(strlen(filespec1) + 1);
553		if(!merged)
554			{
555			DSOerr(DSO_F_WIN32_MERGER,
556				ERR_R_MALLOC_FAILURE);
557			return(NULL);
558			}
559		strcpy(merged, filespec1);
560		}
561	else if (!filespec1)
562		{
563		merged = OPENSSL_malloc(strlen(filespec2) + 1);
564		if(!merged)
565			{
566			DSOerr(DSO_F_WIN32_MERGER,
567				ERR_R_MALLOC_FAILURE);
568			return(NULL);
569			}
570		strcpy(merged, filespec2);
571		}
572	else
573		{
574		filespec1_split = win32_splitter(dso, filespec1, 0);
575		if (!filespec1_split)
576			{
577			DSOerr(DSO_F_WIN32_MERGER,
578				ERR_R_MALLOC_FAILURE);
579			return(NULL);
580			}
581		filespec2_split = win32_splitter(dso, filespec2, 1);
582		if (!filespec2_split)
583			{
584			DSOerr(DSO_F_WIN32_MERGER,
585				ERR_R_MALLOC_FAILURE);
586			OPENSSL_free(filespec1_split);
587			return(NULL);
588			}
589
590		/* Fill in into filespec1_split */
591		if (!filespec1_split->node && !filespec1_split->device)
592			{
593			filespec1_split->node = filespec2_split->node;
594			filespec1_split->nodelen = filespec2_split->nodelen;
595			filespec1_split->device = filespec2_split->device;
596			filespec1_split->devicelen = filespec2_split->devicelen;
597			}
598		if (!filespec1_split->dir)
599			{
600			filespec1_split->dir = filespec2_split->dir;
601			filespec1_split->dirlen = filespec2_split->dirlen;
602			}
603		else if (filespec1_split->dir[0] != '\\'
604			&& filespec1_split->dir[0] != '/')
605			{
606			filespec1_split->predir = filespec2_split->dir;
607			filespec1_split->predirlen = filespec2_split->dirlen;
608			}
609		if (!filespec1_split->file)
610			{
611			filespec1_split->file = filespec2_split->file;
612			filespec1_split->filelen = filespec2_split->filelen;
613			}
614
615		merged = win32_joiner(dso, filespec1_split);
616		}
617	OPENSSL_free(filespec1_split);
618	OPENSSL_free(filespec2_split);
619	return(merged);
620	}
621
622static char *win32_name_converter(DSO *dso, const char *filename)
623	{
624	char *translated;
625	int len, transform;
626
627	len = strlen(filename);
628	transform = ((strstr(filename, "/") == NULL) &&
629			(strstr(filename, "\\") == NULL) &&
630			(strstr(filename, ":") == NULL));
631	if(transform)
632		/* We will convert this to "%s.dll" */
633		translated = OPENSSL_malloc(len + 5);
634	else
635		/* We will simply duplicate filename */
636		translated = OPENSSL_malloc(len + 1);
637	if(translated == NULL)
638		{
639		DSOerr(DSO_F_WIN32_NAME_CONVERTER,
640				DSO_R_NAME_TRANSLATION_FAILED);
641		return(NULL);
642		}
643	if(transform)
644		sprintf(translated, "%s.dll", filename);
645	else
646		sprintf(translated, "%s", filename);
647	return(translated);
648	}
649
650static const char *openssl_strnchr(const char *string, int c, size_t len)
651	{
652	size_t i;
653	const char *p;
654	for (i = 0, p = string; i < len && *p; i++, p++)
655		{
656		if (*p == c)
657			return p;
658		}
659	return NULL;
660	}
661
662
663#endif /* OPENSSL_SYS_WIN32 */
664