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