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				return(NULL);
351				}
352			result->device = start;
353			result->devicelen = filename - start;
354			position = IN_FILE;
355			start = ++filename;
356			result->dir = start;
357			break;
358		case '\\':
359		case '/':
360			if(position == IN_NODE)
361				{
362				result->nodelen = filename - start;
363				position = IN_FILE;
364				start = ++filename;
365				result->dir = start;
366				}
367			else if(position == IN_DEVICE)
368				{
369				position = IN_FILE;
370				filename++;
371				result->dir = start;
372				result->dirlen = filename - start;
373				start = filename;
374				}
375			else
376				{
377				filename++;
378				result->dirlen += filename - start;
379				start = filename;
380				}
381			break;
382		case '\0':
383			if(position == IN_NODE)
384				{
385				result->nodelen = filename - start;
386				}
387			else
388				{
389				if(filename - start > 0)
390					{
391					if (assume_last_is_dir)
392						{
393						if (position == IN_DEVICE)
394							{
395							result->dir = start;
396							result->dirlen = 0;
397							}
398						result->dirlen +=
399							filename - start;
400						}
401					else
402						{
403						result->file = start;
404						result->filelen =
405							filename - start;
406						}
407					}
408				}
409			break;
410		default:
411			filename++;
412			break;
413			}
414		}
415	while(last);
416
417	if(!result->nodelen) result->node = NULL;
418	if(!result->devicelen) result->device = NULL;
419	if(!result->dirlen) result->dir = NULL;
420	if(!result->filelen) result->file = NULL;
421
422	return(result);
423	}
424
425static char *win32_joiner(DSO *dso, const struct file_st *file_split)
426	{
427	int len = 0, offset = 0;
428	char *result = NULL;
429	const char *start;
430
431	if(!file_split)
432		{
433		DSOerr(DSO_F_WIN32_JOINER,
434				ERR_R_PASSED_NULL_PARAMETER);
435		return(NULL);
436		}
437	if(file_split->node)
438		{
439		len += 2 + file_split->nodelen;	/* 2 for starting \\ */
440		if(file_split->predir || file_split->dir || file_split->file)
441			len++;	/* 1 for ending \ */
442		}
443	else if(file_split->device)
444		{
445		len += file_split->devicelen + 1; /* 1 for ending : */
446		}
447	len += file_split->predirlen;
448	if(file_split->predir && (file_split->dir || file_split->file))
449		{
450		len++;	/* 1 for ending \ */
451		}
452	len += file_split->dirlen;
453	if(file_split->dir && file_split->file)
454		{
455		len++;	/* 1 for ending \ */
456		}
457	len += file_split->filelen;
458
459	if(!len)
460		{
461		DSOerr(DSO_F_WIN32_JOINER, DSO_R_EMPTY_FILE_STRUCTURE);
462		return(NULL);
463		}
464
465	result = OPENSSL_malloc(len + 1);
466	if (!result)
467		{
468		DSOerr(DSO_F_WIN32_JOINER,
469			ERR_R_MALLOC_FAILURE);
470		return(NULL);
471		}
472
473	if(file_split->node)
474		{
475		strcpy(&result[offset], "\\\\"); offset += 2;
476		strncpy(&result[offset], file_split->node,
477			file_split->nodelen); offset += file_split->nodelen;
478		if(file_split->predir || file_split->dir || file_split->file)
479			{
480			result[offset] = '\\'; offset++;
481			}
482		}
483	else if(file_split->device)
484		{
485		strncpy(&result[offset], file_split->device,
486			file_split->devicelen); offset += file_split->devicelen;
487		result[offset] = ':'; offset++;
488		}
489	start = file_split->predir;
490	while(file_split->predirlen > (start - file_split->predir))
491		{
492		const char *end = openssl_strnchr(start, '/',
493			file_split->predirlen - (start - file_split->predir));
494		if(!end)
495			end = start
496				+ file_split->predirlen
497				- (start - file_split->predir);
498		strncpy(&result[offset], start,
499			end - start); offset += end - start;
500		result[offset] = '\\'; offset++;
501		start = end + 1;
502		}
503#if 0 /* Not needed, since the directory converter above already appeneded
504	 a backslash */
505	if(file_split->predir && (file_split->dir || file_split->file))
506		{
507		result[offset] = '\\'; offset++;
508		}
509#endif
510	start = file_split->dir;
511	while(file_split->dirlen > (start - file_split->dir))
512		{
513		const char *end = openssl_strnchr(start, '/',
514			file_split->dirlen - (start - file_split->dir));
515		if(!end)
516			end = start
517				+ file_split->dirlen
518				- (start - file_split->dir);
519		strncpy(&result[offset], start,
520			end - start); offset += end - start;
521		result[offset] = '\\'; offset++;
522		start = end + 1;
523		}
524#if 0 /* Not needed, since the directory converter above already appeneded
525	 a backslash */
526	if(file_split->dir && file_split->file)
527		{
528		result[offset] = '\\'; offset++;
529		}
530#endif
531	strncpy(&result[offset], file_split->file,
532		file_split->filelen); offset += file_split->filelen;
533	result[offset] = '\0';
534	return(result);
535	}
536
537static char *win32_merger(DSO *dso, const char *filespec1, const char *filespec2)
538	{
539	char *merged = NULL;
540	struct file_st *filespec1_split = NULL;
541	struct file_st *filespec2_split = NULL;
542
543	if(!filespec1 && !filespec2)
544		{
545		DSOerr(DSO_F_WIN32_MERGER,
546				ERR_R_PASSED_NULL_PARAMETER);
547		return(NULL);
548		}
549	if (!filespec2)
550		{
551		merged = OPENSSL_malloc(strlen(filespec1) + 1);
552		if(!merged)
553			{
554			DSOerr(DSO_F_WIN32_MERGER,
555				ERR_R_MALLOC_FAILURE);
556			return(NULL);
557			}
558		strcpy(merged, filespec1);
559		}
560	else if (!filespec1)
561		{
562		merged = OPENSSL_malloc(strlen(filespec2) + 1);
563		if(!merged)
564			{
565			DSOerr(DSO_F_WIN32_MERGER,
566				ERR_R_MALLOC_FAILURE);
567			return(NULL);
568			}
569		strcpy(merged, filespec2);
570		}
571	else
572		{
573		filespec1_split = win32_splitter(dso, filespec1, 0);
574		if (!filespec1_split)
575			{
576			DSOerr(DSO_F_WIN32_MERGER,
577				ERR_R_MALLOC_FAILURE);
578			return(NULL);
579			}
580		filespec2_split = win32_splitter(dso, filespec2, 1);
581		if (!filespec2_split)
582			{
583			DSOerr(DSO_F_WIN32_MERGER,
584				ERR_R_MALLOC_FAILURE);
585			OPENSSL_free(filespec1_split);
586			return(NULL);
587			}
588
589		/* Fill in into filespec1_split */
590		if (!filespec1_split->node && !filespec1_split->device)
591			{
592			filespec1_split->node = filespec2_split->node;
593			filespec1_split->nodelen = filespec2_split->nodelen;
594			filespec1_split->device = filespec2_split->device;
595			filespec1_split->devicelen = filespec2_split->devicelen;
596			}
597		if (!filespec1_split->dir)
598			{
599			filespec1_split->dir = filespec2_split->dir;
600			filespec1_split->dirlen = filespec2_split->dirlen;
601			}
602		else if (filespec1_split->dir[0] != '\\'
603			&& filespec1_split->dir[0] != '/')
604			{
605			filespec1_split->predir = filespec2_split->dir;
606			filespec1_split->predirlen = filespec2_split->dirlen;
607			}
608		if (!filespec1_split->file)
609			{
610			filespec1_split->file = filespec2_split->file;
611			filespec1_split->filelen = filespec2_split->filelen;
612			}
613
614		merged = win32_joiner(dso, filespec1_split);
615		}
616	return(merged);
617	}
618
619static char *win32_name_converter(DSO *dso, const char *filename)
620	{
621	char *translated;
622	int len, transform;
623
624	len = strlen(filename);
625	transform = ((strstr(filename, "/") == NULL) &&
626			(strstr(filename, "\\") == NULL) &&
627			(strstr(filename, ":") == NULL));
628	if(transform)
629		/* We will convert this to "%s.dll" */
630		translated = OPENSSL_malloc(len + 5);
631	else
632		/* We will simply duplicate filename */
633		translated = OPENSSL_malloc(len + 1);
634	if(translated == NULL)
635		{
636		DSOerr(DSO_F_WIN32_NAME_CONVERTER,
637				DSO_R_NAME_TRANSLATION_FAILED);
638		return(NULL);
639		}
640	if(transform)
641		sprintf(translated, "%s.dll", filename);
642	else
643		sprintf(translated, "%s", filename);
644	return(translated);
645	}
646
647static const char *openssl_strnchr(const char *string, int c, size_t len)
648	{
649	size_t i;
650	const char *p;
651	for (i = 0, p = string; i < len && *p; i++, p++)
652		{
653		if (*p == c)
654			return p;
655		}
656	return NULL;
657	}
658
659
660#endif /* OPENSSL_SYS_WIN32 */
661