Added implementation of security.dll.
[wine] / dlls / crypt32 / oid.c
1 /*
2  * Copyright 2002 Mike McCormack for CodeWeavers
3  * Copyright 2005 Juan Lang
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Lesser General Public
7  * License as published by the Free Software Foundation; either
8  * version 2.1 of the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * Lesser General Public License for more details.
14  *
15  * You should have received a copy of the GNU Lesser General Public
16  * License along with this library; if not, write to the Free Software
17  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
18  */
19 #include <stdio.h>
20 #include <stdarg.h>
21 #include "windef.h"
22 #include "winbase.h"
23 #include "wincrypt.h"
24 #include "winreg.h"
25 #include "wine/debug.h"
26 #include "wine/list.h"
27 #include "crypt32_private.h"
28
29 WINE_DEFAULT_DEBUG_CHANNEL(crypt);
30
31 static const WCHAR DllW[] = { 'D','l','l',0 };
32 CRITICAL_SECTION funcSetCS;
33 struct list funcSets;
34
35 struct OIDFunctionSet
36 {
37     LPSTR name;
38     CRITICAL_SECTION cs; /* protects functions */
39     struct list functions;
40     struct list next;
41 };
42
43 struct OIDFunction
44 {
45     DWORD encoding;
46     CRYPT_OID_FUNC_ENTRY entry;
47     struct list next;
48 };
49
50 void CRYPT_InitFunctionSets(void)
51 {
52     InitializeCriticalSection(&funcSetCS);
53     list_init(&funcSets);
54 }
55
56 void CRYPT_FreeFunctionSets(void)
57 {
58     struct OIDFunctionSet *setCursor, *setNext;
59
60     LIST_FOR_EACH_ENTRY_SAFE(setCursor, setNext, &funcSets,
61      struct OIDFunctionSet, next)
62     {
63         struct OIDFunction *functionCursor, *funcNext;
64
65         list_remove(&setCursor->next);
66         CryptMemFree(setCursor->name);
67         CryptMemFree(setCursor);
68         LIST_FOR_EACH_ENTRY_SAFE(functionCursor, funcNext,
69          &setCursor->functions, struct OIDFunction, next)
70         {
71             list_remove(&functionCursor->next);
72             CryptMemFree(functionCursor);
73         }
74         DeleteCriticalSection(&setCursor->cs);
75     }
76     DeleteCriticalSection(&funcSetCS);
77 }
78
79 /* There is no free function associated with this; therefore, the sets are
80  * freed when crypt32.dll is unloaded.
81  */
82 HCRYPTOIDFUNCSET WINAPI CryptInitOIDFunctionSet(LPCSTR pszFuncName,
83  DWORD dwFlags)
84 {
85     struct OIDFunctionSet *cursor, *ret = NULL;
86
87     TRACE("(%s, %lx)\n", debugstr_a(pszFuncName), dwFlags);
88
89     EnterCriticalSection(&funcSetCS);
90     LIST_FOR_EACH_ENTRY(cursor, &funcSets, struct OIDFunctionSet, next)
91     {
92         if (!strcasecmp(pszFuncName, cursor->name))
93         {
94             ret = (HCRYPTOIDFUNCSET)cursor;
95             break;
96         }
97     }
98     if (!ret)
99     {
100         ret = CryptMemAlloc(sizeof(struct OIDFunctionSet));
101         if (ret)
102         {
103             memset(ret, 0, sizeof(*ret));
104             ret->name = CryptMemAlloc(strlen(pszFuncName) + 1);
105             if (ret->name)
106             {
107                 InitializeCriticalSection(&ret->cs);
108                 list_init(&ret->functions);
109                 strcpy(ret->name, pszFuncName);
110                 list_add_tail(&funcSets, &ret->next);
111             }
112             else
113             {
114                 CryptMemFree(ret);
115                 ret = NULL;
116             }
117         }
118     }
119     LeaveCriticalSection(&funcSetCS);
120
121     return (HCRYPTOIDFUNCSET)ret;
122 }
123
124 static char *CRYPT_GetKeyName(DWORD dwEncodingType, LPCSTR pszFuncName,
125  LPCSTR pszOID)
126 {
127     static const char szEncodingTypeFmt[] =
128      "Software\\Microsoft\\Cryptography\\OID\\EncodingType %ld\\%s\\%s";
129     UINT len;
130     char numericOID[7]; /* enough for "#65535" */
131     const char *oid;
132     LPSTR szKey;
133
134     /* MSDN says the encoding type is a mask, but it isn't treated that way.
135      * (E.g., if dwEncodingType were 3, the key names "EncodingType 1" and
136      * "EncodingType 2" would be expected if it were a mask.  Instead native
137      * stores values in "EncodingType 3".
138      */
139     if (!HIWORD(pszOID))
140     {
141         snprintf(numericOID, sizeof(numericOID), "#%d", LOWORD(pszOID));
142         oid = numericOID;
143     }
144     else
145         oid = pszOID;
146
147     /* This is enough: the lengths of the two string parameters are explicitly
148      * counted, and we need up to five additional characters for the encoding
149      * type.  These are covered by the "%d", "%s", and "%s" characters in the
150      * format specifier that are removed by sprintf.
151      */
152     len = sizeof(szEncodingTypeFmt) + lstrlenA(pszFuncName) + lstrlenA(oid);
153     szKey = CryptMemAlloc(len);
154     if (szKey)
155         sprintf(szKey, szEncodingTypeFmt, dwEncodingType, pszFuncName, oid);
156     return szKey;
157 }
158
159 BOOL WINAPI CryptGetDefaultOIDDllList(HCRYPTOIDFUNCSET hFuncSet,
160  DWORD dwEncodingType, LPWSTR pwszDllList, DWORD *pcchDllList)
161 {
162     BOOL ret = TRUE;
163     struct OIDFunctionSet *set = (struct OIDFunctionSet *)hFuncSet;
164     char *keyName;
165     HKEY key;
166     long rc;
167
168     TRACE("(%p, %ld, %p, %p)\n", hFuncSet, dwEncodingType, pwszDllList,
169      pcchDllList);
170
171     keyName = CRYPT_GetKeyName(dwEncodingType, set->name, "DEFAULT");
172     rc = RegCreateKeyExA(HKEY_LOCAL_MACHINE, keyName, 0, NULL, 0,
173      KEY_READ, NULL, &key, NULL);
174     if (!rc)
175     {
176         DWORD size = *pcchDllList * sizeof(WCHAR);
177
178         rc = RegQueryValueExW(key, DllW, NULL, NULL, (LPBYTE)pwszDllList,
179          &size);
180         if (!rc)
181             *pcchDllList = size / sizeof(WCHAR);
182         else
183         {
184             /* No value, return an empty list */
185             if (*pcchDllList)
186                 *pwszDllList = '\0';
187             *pcchDllList = 1;
188         }
189         RegCloseKey(key);
190     }
191     else
192     {
193         SetLastError(rc);
194         ret = FALSE;
195     }
196     CryptMemFree(keyName);
197
198     return ret;
199 }
200
201 BOOL WINAPI CryptInstallOIDFunctionAddress(HMODULE hModule,
202  DWORD dwEncodingType, LPCSTR pszFuncName, DWORD cFuncEntry,
203  const CRYPT_OID_FUNC_ENTRY rgFuncEntry[], DWORD dwFlags)
204 {
205     BOOL ret = TRUE;
206     struct OIDFunctionSet *set;
207
208     TRACE("(%p, %ld, %s, %ld, %p, %08lx)\n", hModule, dwEncodingType,
209      debugstr_a(pszFuncName), cFuncEntry, rgFuncEntry, dwFlags);
210
211     set = (struct OIDFunctionSet *)CryptInitOIDFunctionSet(pszFuncName, 0);
212     if (set)
213     {
214         DWORD i;
215
216         EnterCriticalSection(&set->cs);
217         for (i = 0; ret && i < cFuncEntry; i++)
218         {
219             struct OIDFunction *func;
220
221             if (HIWORD(rgFuncEntry[i].pszOID))
222                 func = CryptMemAlloc(sizeof(struct OIDFunction)
223                  + strlen(rgFuncEntry[i].pszOID) + 1);
224             else
225                 func = CryptMemAlloc(sizeof(struct OIDFunction));
226             if (func)
227             {
228                 func->encoding = dwEncodingType;
229                 if (HIWORD(rgFuncEntry[i].pszOID))
230                 {
231                     func->entry.pszOID = (LPSTR)((LPBYTE)func + sizeof(*func));
232                     strcpy((LPSTR)func->entry.pszOID, rgFuncEntry[i].pszOID);
233                 }
234                 else
235                     func->entry.pszOID = rgFuncEntry[i].pszOID;
236                 func->entry.pvFuncAddr = rgFuncEntry[i].pvFuncAddr;
237                 list_add_tail(&set->functions, &func->next);
238             }
239             else
240                 ret = FALSE;
241         }
242         LeaveCriticalSection(&set->cs);
243     }
244     else
245         ret = FALSE;
246     return ret;
247 }
248
249 static BOOL CRYPT_GetFuncFromReg(DWORD dwEncodingType, LPCSTR pszOID,
250  LPCSTR szFuncName, LPVOID *ppvFuncAddr, HCRYPTOIDFUNCADDR *phFuncAddr)
251 {
252     BOOL ret = FALSE;
253     char *keyName;
254     const char *funcName;
255     HKEY key;
256     long rc;
257
258     keyName = CRYPT_GetKeyName(dwEncodingType, szFuncName, pszOID);
259     rc = RegOpenKeyExA(HKEY_LOCAL_MACHINE, keyName, 0, KEY_READ, &key);
260     if (!rc)
261     {
262         DWORD type, size = 0;
263
264         rc = RegQueryValueExA(key, "FuncName", NULL, &type, NULL, &size);
265         if (rc == ERROR_MORE_DATA && type == REG_SZ)
266         {
267             funcName = CryptMemAlloc(size);
268             rc = RegQueryValueExA(key, "FuncName", NULL, &type,
269              (LPBYTE)funcName, &size);
270         }
271         else
272             funcName = szFuncName;
273         rc = RegQueryValueExW(key, DllW, NULL, &type, NULL, &size);
274         if (rc == ERROR_MORE_DATA && type == REG_SZ)
275         {
276             LPWSTR dllName = CryptMemAlloc(size);
277
278             if (dllName)
279             {
280                 rc = RegQueryValueExW(key, DllW, NULL, NULL,
281                  (LPBYTE)dllName, &size);
282                 if (!rc)
283                 {
284                     HMODULE lib;
285
286                     /* This is a bit of a hack; MSDN describes a more
287                      * complicated unload routine than this will allow.
288                      * Still, this seems to suffice for now.
289                      */
290                     lib = LoadLibraryW(dllName);
291                     if (lib)
292                     {
293                         *ppvFuncAddr = GetProcAddress(lib, szFuncName);
294                         if (*ppvFuncAddr)
295                         {
296                             *phFuncAddr = (HCRYPTOIDFUNCADDR)lib;
297                             ret = TRUE;
298                         }
299                         else
300                         {
301                             /* Unload the library, the caller doesn't want
302                              * to unload it when the return value is NULL.
303                              */
304                             FreeLibrary(lib);
305                         }
306                     }
307                 }
308                 else
309                     SetLastError(rc);
310                 CryptMemFree(dllName);
311             }
312         }
313         else
314             SetLastError(rc);
315         if (funcName != szFuncName)
316             CryptMemFree((char *)funcName);
317         RegCloseKey(key);
318     }
319     else
320         SetLastError(rc);
321     CryptMemFree(keyName);
322     return ret;
323 }
324
325 BOOL WINAPI CryptGetOIDFunctionAddress(HCRYPTOIDFUNCSET hFuncSet,
326  DWORD dwEncodingType, LPCSTR pszOID, DWORD dwFlags, void **ppvFuncAddr,
327  HCRYPTOIDFUNCADDR *phFuncAddr)
328 {
329     BOOL ret = FALSE;
330     struct OIDFunctionSet *set = (struct OIDFunctionSet *)hFuncSet;
331
332     TRACE("(%p, %ld, %s, %08lx, %p, %p)\n", hFuncSet, dwEncodingType,
333      debugstr_a(pszOID), dwFlags, ppvFuncAddr, phFuncAddr);
334
335     *ppvFuncAddr = NULL;
336     if (!(dwFlags & CRYPT_GET_INSTALLED_OID_FUNC_FLAG))
337     {
338         struct OIDFunction *function;
339
340         EnterCriticalSection(&set->cs);
341         LIST_FOR_EACH_ENTRY(function, &set->functions, struct OIDFunction, next)
342         {
343             if (function->encoding == dwEncodingType)
344             {
345                 if (HIWORD(pszOID))
346                 {
347                     if (HIWORD(function->entry.pszOID &&
348                      !strcasecmp(function->entry.pszOID, pszOID)))
349                     {
350                         *ppvFuncAddr = function->entry.pvFuncAddr;
351                         *phFuncAddr = NULL; /* FIXME: what should it be? */
352                         ret = TRUE;
353                         break;
354                     }
355                 }
356                 else if (function->entry.pszOID == pszOID)
357                 {
358                     *ppvFuncAddr = function->entry.pvFuncAddr;
359                     *phFuncAddr = NULL; /* FIXME: what should it be? */
360                     ret = TRUE;
361                     break;
362                 }
363             }
364         }
365         LeaveCriticalSection(&set->cs);
366     }
367     if (!*ppvFuncAddr)
368         ret = CRYPT_GetFuncFromReg(dwEncodingType, pszOID, set->name,
369          ppvFuncAddr, phFuncAddr);
370     return ret;
371 }
372
373 BOOL WINAPI CryptFreeOIDFunctionAddress(HCRYPTOIDFUNCADDR hFuncAddr,
374  DWORD dwFlags)
375 {
376     TRACE("(%p, %08lx)\n", hFuncAddr, dwFlags);
377
378     /* FIXME: as MSDN states, need to check for DllCanUnloadNow in the DLL,
379      * and only unload it if it can be unloaded.  Also need to implement ref
380      * counting on the functions.
381      */
382     FreeLibrary((HMODULE)hFuncAddr);
383     return TRUE;
384 }
385
386 BOOL WINAPI CryptRegisterDefaultOIDFunction(DWORD dwEncodingType,
387  LPCSTR pszFuncName, DWORD dwIndex, LPCWSTR pwszDll)
388 {
389     FIXME("(%lx,%s,%lx,%s) stub!\n", dwEncodingType, pszFuncName, dwIndex,
390           debugstr_w(pwszDll));
391     return FALSE;
392 }
393
394 BOOL WINAPI CryptUnregisterDefaultOIDFunction(DWORD dwEncodingType,
395  LPCSTR pszFuncName, LPCWSTR pwszDll)
396 {
397     FIXME("(%lx %s %s): stub\n", dwEncodingType, debugstr_a(pszFuncName),
398      debugstr_w(pwszDll));
399     return FALSE;
400 }
401
402 BOOL WINAPI CryptGetDefaultOIDFunctionAddress(HCRYPTOIDFUNCSET hFuncSet,
403  DWORD dwEncodingType, LPCWSTR pwszDll, DWORD dwFlags, void *ppvFuncAddr,
404  HCRYPTOIDFUNCADDR *phFuncAddr)
405 {
406     FIXME("(%p, %ld, %s, %08lx, %p, %p): stub\n", hFuncSet, dwEncodingType,
407      debugstr_w(pwszDll), dwFlags, ppvFuncAddr, phFuncAddr);
408     return FALSE;
409 }
410
411 BOOL WINAPI CryptRegisterOIDFunction(DWORD dwEncodingType, LPCSTR pszFuncName,
412                   LPCSTR pszOID, LPCWSTR pwszDll, LPCSTR pszOverrideFuncName)
413 {
414     LONG r;
415     HKEY hKey;
416     LPSTR szKey;
417
418     TRACE("(%lx, %s, %s, %s, %s)\n", dwEncodingType, pszFuncName, pszOID,
419      debugstr_w(pwszDll), pszOverrideFuncName);
420
421     /* This only registers functions for encoding certs, not messages */
422     if (!GET_CERT_ENCODING_TYPE(dwEncodingType))
423         return TRUE;
424
425     /* Native does nothing pwszDll is NULL */
426     if (!pwszDll)
427         return TRUE;
428
429     /* I'm not matching MS bug for bug here, because I doubt any app depends on
430      * it:  native "succeeds" if pszFuncName is NULL, but the nonsensical entry
431      * it creates would never be used.
432      */
433     if (!pszFuncName || !pszOID)
434     {
435         SetLastError(HRESULT_FROM_WIN32(ERROR_INVALID_PARAMETER));
436         return FALSE;
437     }
438
439     szKey = CRYPT_GetKeyName(dwEncodingType, pszFuncName, pszOID);
440     TRACE("Key name is %s\n", debugstr_a(szKey));
441
442     if (!szKey)
443         return FALSE;
444
445     r = RegCreateKeyA(HKEY_LOCAL_MACHINE, szKey, &hKey);
446     CryptMemFree(szKey);
447     if(r != ERROR_SUCCESS)
448         return FALSE;
449
450     /* write the values */
451     if (pszOverrideFuncName)
452         RegSetValueExA(hKey, "FuncName", 0, REG_SZ,
453          (const BYTE*)pszOverrideFuncName, lstrlenA(pszOverrideFuncName) + 1);
454     RegSetValueExW(hKey, DllW, 0, REG_SZ, (const BYTE*) pwszDll,
455      (lstrlenW(pwszDll) + 1) * sizeof (WCHAR));
456
457     RegCloseKey(hKey);
458     return TRUE;
459 }
460
461 BOOL WINAPI CryptUnregisterOIDFunction(DWORD dwEncodingType, LPCSTR pszFuncName,
462  LPCSTR pszOID)
463 {
464     LPSTR szKey;
465     LONG rc;
466
467     TRACE("%lx %s %s\n", dwEncodingType, pszFuncName, pszOID);
468
469     if (!GET_CERT_ENCODING_TYPE(dwEncodingType))
470         return TRUE;
471
472     if (!pszFuncName || !pszOID)
473     {
474         SetLastError(ERROR_INVALID_PARAMETER);
475         return FALSE;
476     }
477
478     szKey = CRYPT_GetKeyName(dwEncodingType, pszFuncName, pszOID);
479     rc = RegDeleteKeyA(HKEY_LOCAL_MACHINE, szKey);
480     CryptMemFree(szKey);
481     if (rc)
482         SetLastError(rc);
483     return rc ? FALSE : TRUE;
484 }
485
486 BOOL WINAPI CryptGetOIDFunctionValue(DWORD dwEncodingType, LPCSTR pszFuncName,
487  LPCSTR pszOID, LPCWSTR pwszValueName, DWORD *pdwValueType, BYTE *pbValueData,
488  DWORD *pcbValueData)
489 {
490     LPSTR szKey;
491     LONG rc;
492     HKEY hKey;
493
494     TRACE("%lx %s %s %s %p %p %p\n", dwEncodingType, debugstr_a(pszFuncName),
495      debugstr_a(pszOID), debugstr_w(pwszValueName), pdwValueType, pbValueData,
496      pcbValueData);
497
498     if (!GET_CERT_ENCODING_TYPE(dwEncodingType))
499         return TRUE;
500
501     if (!pszFuncName || !pszOID || !pwszValueName)
502     {
503         SetLastError(ERROR_INVALID_PARAMETER);
504         return FALSE;
505     }
506
507     szKey = CRYPT_GetKeyName(dwEncodingType, pszFuncName, pszOID);
508     rc = RegOpenKeyA(HKEY_LOCAL_MACHINE, szKey, &hKey);
509     CryptMemFree(szKey);
510     if (rc)
511         SetLastError(rc);
512     else
513     {
514         rc = RegQueryValueExW(hKey, pwszValueName, NULL, pdwValueType,
515          pbValueData, pcbValueData);
516         if (rc)
517             SetLastError(rc);
518         RegCloseKey(hKey);
519     }
520     return rc ? FALSE : TRUE;
521 }
522
523 BOOL WINAPI CryptSetOIDFunctionValue(DWORD dwEncodingType, LPCSTR pszFuncName,
524  LPCSTR pszOID, LPCWSTR pwszValueName, DWORD dwValueType,
525  const BYTE *pbValueData, DWORD cbValueData)
526 {
527     LPSTR szKey;
528     LONG rc;
529     HKEY hKey;
530
531     TRACE("%lx %s %s %s %ld %p %ld\n", dwEncodingType, debugstr_a(pszFuncName),
532      debugstr_a(pszOID), debugstr_w(pwszValueName), dwValueType, pbValueData,
533      cbValueData);
534
535     if (!GET_CERT_ENCODING_TYPE(dwEncodingType))
536         return TRUE;
537
538     if (!pszFuncName || !pszOID || !pwszValueName)
539     {
540         SetLastError(ERROR_INVALID_PARAMETER);
541         return FALSE;
542     }
543
544     szKey = CRYPT_GetKeyName(dwEncodingType, pszFuncName, pszOID);
545     rc = RegOpenKeyA(HKEY_LOCAL_MACHINE, szKey, &hKey);
546     CryptMemFree(szKey);
547     if (rc)
548         SetLastError(rc);
549     else
550     {
551         rc = RegSetValueExW(hKey, pwszValueName, 0, dwValueType, pbValueData,
552          cbValueData);
553         if (rc)
554             SetLastError(rc);
555         RegCloseKey(hKey);
556     }
557     return rc ? FALSE : TRUE;
558 }