1 /**
2  * FreeRDP: A Remote Desktop Protocol Implementation
3  * Addin Loader
4  *
5  * Copyright 2012 Marc-Andre Moreau <marcandre.moreau@gmail.com>
6  *
7  * Licensed under the Apache License, Version 2.0 (the "License");
8  * you may not use this file except in compliance with the License.
9  * You may obtain a copy of the License at
10  *
11  *     http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing, software
14  * distributed under the License is distributed on an "AS IS" BASIS,
15  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16  * See the License for the specific language governing permissions and
17  * limitations under the License.
18  */
19 
20 #ifdef HAVE_CONFIG_H
21 #include "config.h"
22 #endif
23 
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <string.h>
27 
28 #include <winpr/crt.h>
29 #include <winpr/path.h>
30 #include <winpr/string.h>
31 #include <winpr/library.h>
32 
33 #include <freerdp/addin.h>
34 #include <freerdp/build-config.h>
35 
36 #include <freerdp/log.h>
37 #define TAG FREERDP_TAG("addin")
38 
is_path_required(LPCSTR path,size_t len)39 static INLINE BOOL is_path_required(LPCSTR path, size_t len)
40 {
41 	if (!path || (len <= 1))
42 		return FALSE;
43 
44 	if (strcmp(path, ".") == 0)
45 		return FALSE;
46 
47 	return TRUE;
48 }
49 
freerdp_get_library_install_path(void)50 LPSTR freerdp_get_library_install_path(void)
51 {
52 	LPSTR pszPath;
53 	size_t cchPath;
54 	size_t cchLibraryPath;
55 	size_t cchInstallPrefix;
56 	BOOL needLibPath, needInstallPath;
57 	LPCSTR pszLibraryPath = FREERDP_LIBRARY_PATH;
58 	LPCSTR pszInstallPrefix = FREERDP_INSTALL_PREFIX;
59 	cchLibraryPath = strlen(pszLibraryPath) + 1;
60 	cchInstallPrefix = strlen(pszInstallPrefix) + 1;
61 	cchPath = cchInstallPrefix + cchLibraryPath;
62 	needInstallPath = is_path_required(pszInstallPrefix, cchInstallPrefix);
63 	needLibPath = is_path_required(pszLibraryPath, cchLibraryPath);
64 
65 	if (!needInstallPath && !needLibPath)
66 		return NULL;
67 
68 	pszPath = (LPSTR)malloc(cchPath + 1);
69 
70 	if (!pszPath)
71 		return NULL;
72 
73 	if (needInstallPath)
74 	{
75 		CopyMemory(pszPath, pszInstallPrefix, cchInstallPrefix);
76 		pszPath[cchInstallPrefix] = '\0';
77 	}
78 
79 	if (needLibPath)
80 	{
81 		if (FAILED(NativePathCchAppendA(pszPath, cchPath + 1, pszLibraryPath)))
82 		{
83 			free(pszPath);
84 			return NULL;
85 		}
86 	}
87 
88 	return pszPath;
89 }
90 
freerdp_get_dynamic_addin_install_path(void)91 LPSTR freerdp_get_dynamic_addin_install_path(void)
92 {
93 	LPSTR pszPath;
94 	size_t cchPath;
95 	size_t cchAddinPath;
96 	size_t cchInstallPrefix;
97 	BOOL needLibPath, needInstallPath;
98 	LPCSTR pszAddinPath = FREERDP_ADDIN_PATH;
99 	LPCSTR pszInstallPrefix = FREERDP_INSTALL_PREFIX;
100 	cchAddinPath = strlen(pszAddinPath) + 1;
101 	cchInstallPrefix = strlen(pszInstallPrefix) + 1;
102 	cchPath = cchInstallPrefix + cchAddinPath;
103 	needInstallPath = is_path_required(pszInstallPrefix, cchInstallPrefix);
104 	needLibPath = is_path_required(pszAddinPath, cchAddinPath);
105 
106 	if (!needInstallPath && !needLibPath)
107 		return NULL;
108 
109 	pszPath = (LPSTR)calloc(cchPath + 1, sizeof(CHAR));
110 
111 	if (!pszPath)
112 		return NULL;
113 
114 	if (needInstallPath)
115 	{
116 		CopyMemory(pszPath, pszInstallPrefix, cchInstallPrefix);
117 		pszPath[cchInstallPrefix] = '\0';
118 	}
119 
120 	if (needLibPath)
121 	{
122 		if (FAILED(NativePathCchAppendA(pszPath, cchPath + 1, pszAddinPath)))
123 		{
124 			free(pszPath);
125 			return NULL;
126 		}
127 	}
128 
129 	return pszPath;
130 }
131 
freerdp_load_dynamic_addin(LPCSTR pszFileName,LPCSTR pszPath,LPCSTR pszEntryName)132 PVIRTUALCHANNELENTRY freerdp_load_dynamic_addin(LPCSTR pszFileName, LPCSTR pszPath,
133                                                 LPCSTR pszEntryName)
134 {
135 	LPSTR pszAddinInstallPath = freerdp_get_dynamic_addin_install_path();
136 	PVIRTUALCHANNELENTRY entry = NULL;
137 	BOOL bHasExt = TRUE;
138 	PCSTR pszExt;
139 	size_t cchExt = 0;
140 	HINSTANCE library = NULL;
141 	size_t cchFileName;
142 	size_t cchFilePath;
143 	LPSTR pszAddinFile = NULL;
144 	LPSTR pszFilePath = NULL;
145 	LPSTR pszRelativeFilePath = NULL;
146 	size_t cchAddinFile;
147 	size_t cchAddinInstallPath;
148 
149 	if (!pszFileName || !pszEntryName)
150 		goto fail;
151 
152 	cchFileName = strlen(pszFileName);
153 
154 	/* Get file name with prefix and extension */
155 	if (FAILED(PathCchFindExtensionA(pszFileName, cchFileName + 1, &pszExt)))
156 	{
157 		pszExt = PathGetSharedLibraryExtensionA(PATH_SHARED_LIB_EXT_WITH_DOT);
158 		cchExt = strlen(pszExt);
159 		bHasExt = FALSE;
160 	}
161 
162 	if (bHasExt)
163 	{
164 		pszAddinFile = _strdup(pszFileName);
165 
166 		if (!pszAddinFile)
167 			goto fail;
168 	}
169 	else
170 	{
171 		cchAddinFile = cchFileName + cchExt + 2 + sizeof(FREERDP_SHARED_LIBRARY_PREFIX);
172 		pszAddinFile = (LPSTR)malloc(cchAddinFile + 1);
173 
174 		if (!pszAddinFile)
175 			goto fail;
176 
177 		sprintf_s(pszAddinFile, cchAddinFile, FREERDP_SHARED_LIBRARY_PREFIX "%s%s", pszFileName,
178 		          pszExt);
179 	}
180 
181 	cchAddinFile = strlen(pszAddinFile);
182 
183 	/* If a path is provided prefix the library name with it. */
184 	if (pszPath)
185 	{
186 		size_t relPathLen = strlen(pszPath) + cchAddinFile + 1;
187 		pszRelativeFilePath = calloc(relPathLen, sizeof(CHAR));
188 
189 		if (!pszRelativeFilePath)
190 			goto fail;
191 
192 		sprintf_s(pszRelativeFilePath, relPathLen, "%s", pszPath);
193 		NativePathCchAppendA(pszRelativeFilePath, relPathLen, pszAddinFile);
194 	}
195 	else
196 		pszRelativeFilePath = _strdup(pszAddinFile);
197 
198 	if (!pszRelativeFilePath)
199 		goto fail;
200 
201 	/* If a system prefix path is provided try these locations too. */
202 	if (pszAddinInstallPath)
203 	{
204 		cchAddinInstallPath = strlen(pszAddinInstallPath);
205 		cchFilePath = cchAddinInstallPath + cchFileName + 32;
206 		pszFilePath = (LPSTR)malloc(cchFilePath + 1);
207 
208 		if (!pszFilePath)
209 			goto fail;
210 
211 		CopyMemory(pszFilePath, pszAddinInstallPath, cchAddinInstallPath);
212 		pszFilePath[cchAddinInstallPath] = '\0';
213 		NativePathCchAppendA((LPSTR)pszFilePath, cchFilePath + 1, pszRelativeFilePath);
214 	}
215 	else
216 		pszFilePath = _strdup(pszRelativeFilePath);
217 
218 	library = LoadLibraryA(pszFilePath);
219 
220 	if (!library)
221 		goto fail;
222 
223 	entry = (PVIRTUALCHANNELENTRY)GetProcAddress(library, pszEntryName);
224 fail:
225 	free(pszRelativeFilePath);
226 	free(pszAddinFile);
227 	free(pszFilePath);
228 	free(pszAddinInstallPath);
229 
230 	if (!entry && library)
231 		FreeLibrary(library);
232 
233 	return entry;
234 }
235 
freerdp_load_dynamic_channel_addin_entry(LPCSTR pszName,LPCSTR pszSubsystem,LPCSTR pszType,DWORD dwFlags)236 PVIRTUALCHANNELENTRY freerdp_load_dynamic_channel_addin_entry(LPCSTR pszName, LPCSTR pszSubsystem,
237                                                               LPCSTR pszType, DWORD dwFlags)
238 {
239 	PVIRTUALCHANNELENTRY entry;
240 	LPSTR pszFileName;
241 	const size_t cchBaseFileName = sizeof(FREERDP_SHARED_LIBRARY_PREFIX) + 32;
242 	LPCSTR pszExtension;
243 	LPCSTR pszPrefix = FREERDP_SHARED_LIBRARY_PREFIX;
244 	size_t nameLen = 0;
245 	size_t subsystemLen = 0;
246 	size_t typeLen = 0;
247 	size_t extensionLen = 0;
248 	pszExtension = PathGetSharedLibraryExtensionA(0);
249 	if (pszName)
250 		nameLen = strnlen(pszName, MAX_PATH);
251 	if (pszSubsystem)
252 		subsystemLen = strnlen(pszSubsystem, MAX_PATH);
253 	if (pszType)
254 		typeLen = strnlen(pszType, MAX_PATH);
255 	if (pszExtension)
256 		extensionLen = strnlen(pszExtension, MAX_PATH);
257 
258 	if (pszName && pszSubsystem && pszType)
259 	{
260 		const size_t cchFileName =
261 		    cchBaseFileName + nameLen + subsystemLen + typeLen + extensionLen;
262 		pszFileName = (LPSTR)malloc(cchFileName);
263 
264 		if (!pszFileName)
265 			return NULL;
266 
267 		sprintf_s(pszFileName, cchFileName, "%s%s-client-%s-%s.%s", pszPrefix, pszName,
268 		          pszSubsystem, pszType, pszExtension);
269 	}
270 	else if (pszName && pszSubsystem)
271 	{
272 		const size_t cchFileName = cchBaseFileName + nameLen + subsystemLen + extensionLen;
273 		pszFileName = (LPSTR)malloc(cchFileName);
274 
275 		if (!pszFileName)
276 			return NULL;
277 
278 		sprintf_s(pszFileName, cchFileName, "%s%s-client-%s.%s", pszPrefix, pszName, pszSubsystem,
279 		          pszExtension);
280 	}
281 	else if (pszName)
282 	{
283 		const size_t cchFileName = cchBaseFileName + nameLen + extensionLen;
284 		pszFileName = (LPSTR)malloc(cchFileName);
285 
286 		if (!pszFileName)
287 			return NULL;
288 
289 		sprintf_s(pszFileName, cchFileName, "%s%s-client.%s", pszPrefix, pszName, pszExtension);
290 	}
291 	else
292 	{
293 		return NULL;
294 	}
295 
296 	if (pszSubsystem)
297 	{
298 		LPSTR pszEntryName;
299 		size_t cchEntryName;
300 		/* subsystem add-in */
301 		cchEntryName = 64 + nameLen;
302 		pszEntryName = (LPSTR)malloc(cchEntryName + 1);
303 
304 		if (!pszEntryName)
305 		{
306 			free(pszFileName);
307 			return NULL;
308 		}
309 
310 		sprintf_s(pszEntryName, cchEntryName + 1, "freerdp_%s_client_subsystem_entry", pszName);
311 		entry = freerdp_load_dynamic_addin(pszFileName, NULL, pszEntryName);
312 		free(pszEntryName);
313 		free(pszFileName);
314 		return entry;
315 	}
316 
317 	/* channel add-in */
318 
319 	if (dwFlags & FREERDP_ADDIN_CHANNEL_STATIC)
320 	{
321 		if (dwFlags & FREERDP_ADDIN_CHANNEL_ENTRYEX)
322 			entry = freerdp_load_dynamic_addin(pszFileName, NULL, "VirtualChannelEntryEx");
323 		else
324 			entry = freerdp_load_dynamic_addin(pszFileName, NULL, "VirtualChannelEntry");
325 	}
326 	else if (dwFlags & FREERDP_ADDIN_CHANNEL_DYNAMIC)
327 		entry = freerdp_load_dynamic_addin(pszFileName, NULL, "DVCPluginEntry");
328 	else if (dwFlags & FREERDP_ADDIN_CHANNEL_DEVICE)
329 		entry = freerdp_load_dynamic_addin(pszFileName, NULL, "DeviceServiceEntry");
330 	else
331 		entry = freerdp_load_dynamic_addin(pszFileName, NULL, pszType);
332 
333 	free(pszFileName);
334 	return entry;
335 }
336 
337 static FREERDP_LOAD_CHANNEL_ADDIN_ENTRY_FN freerdp_load_static_channel_addin_entry = NULL;
338 
freerdp_register_addin_provider(FREERDP_LOAD_CHANNEL_ADDIN_ENTRY_FN provider,DWORD dwFlags)339 int freerdp_register_addin_provider(FREERDP_LOAD_CHANNEL_ADDIN_ENTRY_FN provider, DWORD dwFlags)
340 {
341 	freerdp_load_static_channel_addin_entry = provider;
342 	return 0;
343 }
344 
freerdp_load_channel_addin_entry(LPCSTR pszName,LPCSTR pszSubsystem,LPCSTR pszType,DWORD dwFlags)345 PVIRTUALCHANNELENTRY freerdp_load_channel_addin_entry(LPCSTR pszName, LPCSTR pszSubsystem,
346                                                       LPCSTR pszType, DWORD dwFlags)
347 {
348 	PVIRTUALCHANNELENTRY entry = NULL;
349 
350 	if (freerdp_load_static_channel_addin_entry)
351 		entry = freerdp_load_static_channel_addin_entry(pszName, pszSubsystem, pszType, dwFlags);
352 
353 	if (!entry)
354 		entry = freerdp_load_dynamic_channel_addin_entry(pszName, pszSubsystem, pszType, dwFlags);
355 
356 	if (!entry)
357 		WLog_WARN(TAG, "Failed to load channel %s [%s]", pszName, pszSubsystem);
358 
359 	return entry;
360 }
361