mfmediaengine: Remove unnecessary import library.
[wine.git] / dlls / mountmgr.sys / cred.c
blob9fb9e433881776397b542e994f066568c479cd2b
1 /*
2 * Mount manager macOS credentials support
4 * Copyright 2020 Hans Leidekker
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
21 #if 0
22 #pragma makedep unix
23 #endif
25 #include "config.h"
27 #include <stdarg.h>
28 #include <stdlib.h>
30 #ifdef __APPLE__
31 #include <CoreFoundation/CFString.h>
32 #define LoadResource mac_LoadResource
33 #define GetCurrentThread mac_GetCurrentThread
34 #include <CoreServices/CoreServices.h>
35 #undef LoadResource
36 #undef GetCurrentThread
37 #endif
39 #define NONAMELESSUNION
41 #include "mountmgr.h"
42 #include "unixlib.h"
43 #include "wine/debug.h"
45 WINE_DEFAULT_DEBUG_CHANNEL(mountmgr);
47 #ifdef __APPLE__
49 #define TICKSPERSEC 10000000
50 #define SECSPERDAY 86400
51 /* 1601 to 1970 is 369 years plus 89 leap days */
52 #define SECS_1601_TO_1970 ((369 * 365 + 89) * (ULONGLONG)SECSPERDAY)
53 #define TICKS_1601_TO_1970 (SECS_1601_TO_1970 * TICKSPERSEC)
55 /* implementation of Wine extension to use host APIs to find symbol file by GUID */
56 NTSTATUS query_symbol_file( void *args )
58 const struct ioctl_params *params = args;
59 char *result = params->buff;
60 CFStringRef query_cfstring;
61 MDQueryRef mdquery;
62 const GUID *id = params->buff;
63 NTSTATUS status = STATUS_NO_MEMORY;
65 if (!(query_cfstring = CFStringCreateWithFormat(kCFAllocatorDefault, NULL,
66 CFSTR("com_apple_xcode_dsym_uuids == \"%08X-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X\""),
67 id->Data1, id->Data2, id->Data3, id->Data4[0],
68 id->Data4[1], id->Data4[2], id->Data4[3], id->Data4[4],
69 id->Data4[5], id->Data4[6], id->Data4[7] )))
70 return STATUS_NO_MEMORY;
72 mdquery = MDQueryCreate(NULL, query_cfstring, NULL, NULL);
73 CFRelease(query_cfstring);
74 if (!mdquery) return STATUS_NO_MEMORY;
76 MDQuerySetMaxCount(mdquery, 1);
77 if (MDQueryExecute(mdquery, kMDQuerySynchronous))
79 if (MDQueryGetResultCount(mdquery) >= 1)
81 MDItemRef item = (MDItemRef)MDQueryGetResultAtIndex(mdquery, 0);
82 CFStringRef item_path = MDItemCopyAttribute(item, kMDItemPath);
84 if (item_path)
86 if (CFStringGetCString( item_path, result, params->outsize, kCFStringEncodingUTF8 ))
88 *params->info = strlen( result ) + 1;
89 status = STATUS_SUCCESS;
90 TRACE("found %s\n", debugstr_a(result));
92 else status = STATUS_BUFFER_OVERFLOW;
93 CFRelease(item_path);
96 else status = STATUS_NO_MORE_ENTRIES;
98 CFRelease(mdquery);
99 return status;
102 static inline BOOL check_credential_string( const void *buf, ULONG buflen, ULONG size, ULONG offset )
104 const WCHAR *ptr = buf;
105 if (!size || size % sizeof(WCHAR) || offset + size > buflen || ptr[(offset + size) / sizeof(WCHAR) - 1]) return FALSE;
106 return TRUE;
109 static WCHAR *cred_umbstowcs( const char *src, ULONG srclen, ULONG *retlen )
111 WCHAR *ret = malloc( (srclen + 1) * sizeof(WCHAR) );
112 if (ret)
114 *retlen = ntdll_umbstowcs( src, srclen, ret, srclen );
115 ret[*retlen] = 0;
117 return ret;
120 static char *cred_wcstoumbs( const WCHAR *src, ULONG srclen, ULONG *retlen )
122 char *ret = malloc( srclen * 3 + 1 );
123 if (ret)
125 *retlen = ntdll_wcstoumbs( src, srclen, ret, srclen * 3, FALSE );
126 ret[*retlen] = 0;
128 return ret;
131 static SecKeychainItemRef find_credential( const WCHAR *name )
133 int status;
134 SecKeychainSearchRef search;
135 SecKeychainItemRef item;
137 status = SecKeychainSearchCreateFromAttributes( NULL, kSecGenericPasswordItemClass, NULL, &search );
138 if (status != noErr) return NULL;
140 while (SecKeychainSearchCopyNext( search, &item ) == noErr)
142 SecKeychainAttributeInfo info;
143 SecKeychainAttributeList *attr_list;
144 UInt32 info_tags[] = { kSecServiceItemAttr };
145 WCHAR *itemname;
146 ULONG len;
148 info.count = ARRAY_SIZE(info_tags);
149 info.tag = info_tags;
150 info.format = NULL;
151 status = SecKeychainItemCopyAttributesAndData( item, &info, NULL, &attr_list, NULL, NULL );
152 if (status != noErr)
154 WARN( "SecKeychainItemCopyAttributesAndData returned status %d\n", status );
155 continue;
157 if (attr_list->count != 1 || attr_list->attr[0].tag != kSecServiceItemAttr)
159 CFRelease( item );
160 continue;
162 itemname = cred_umbstowcs( attr_list->attr[0].data, attr_list->attr[0].length, &len );
163 if (!itemname)
165 CFRelease( item );
166 CFRelease( search );
167 return NULL;
169 if (wcsicmp( itemname, name ))
171 CFRelease( item );
172 free( itemname );
173 continue;
175 free( itemname );
176 SecKeychainItemFreeAttributesAndData( attr_list, NULL );
177 CFRelease( search );
178 return item;
181 CFRelease( search );
182 return NULL;
185 static NTSTATUS fill_credential( SecKeychainItemRef item, BOOL require_password, void *buf, ULONG data_offset,
186 ULONG buflen, ULONG *retlen )
188 struct mountmgr_credential *cred = buf;
189 int status;
190 ULONG size, len;
191 UInt32 i, cred_blob_len = 0;
192 void *cred_blob;
193 WCHAR *str;
194 BOOL user_name_present = FALSE;
195 SecKeychainAttributeInfo info;
196 SecKeychainAttributeList *attr_list = NULL;
197 UInt32 info_tags[] = { kSecServiceItemAttr, kSecAccountItemAttr, kSecCommentItemAttr, kSecCreationDateItemAttr };
199 info.count = ARRAY_SIZE(info_tags);
200 info.tag = info_tags;
201 info.format = NULL;
202 status = SecKeychainItemCopyAttributesAndData( item, &info, NULL, &attr_list, &cred_blob_len, &cred_blob );
203 if (status == errSecAuthFailed && !require_password)
205 cred_blob_len = 0;
206 cred_blob = NULL;
207 status = SecKeychainItemCopyAttributesAndData( item, &info, NULL, &attr_list, &cred_blob_len, NULL );
209 if (status != noErr)
211 WARN( "SecKeychainItemCopyAttributesAndData returned status %d\n", status );
212 return STATUS_NOT_FOUND;
214 for (i = 0; i < attr_list->count; i++)
216 if (attr_list->attr[i].tag == kSecAccountItemAttr && attr_list->attr[i].data)
218 user_name_present = TRUE;
219 break;
222 if (!user_name_present)
224 WARN( "no kSecAccountItemAttr for item\n" );
225 SecKeychainItemFreeAttributesAndData( attr_list, cred_blob );
226 return STATUS_NOT_FOUND;
229 *retlen = sizeof(*cred);
230 for (i = 0; i < attr_list->count; i++)
232 switch (attr_list->attr[i].tag)
234 case kSecServiceItemAttr:
235 TRACE( "kSecServiceItemAttr: %.*s\n", (int)attr_list->attr[i].length, (char *)attr_list->attr[i].data );
236 if (cred) cred->targetname_offset = cred->targetname_size = 0;
237 if (!attr_list->attr[i].data) continue;
239 if (!(str = cred_umbstowcs( attr_list->attr[i].data, attr_list->attr[i].length, &len )))
240 continue;
241 size = (len + 1) * sizeof(WCHAR);
242 if (cred && *retlen + size <= buflen)
244 cred->targetname_offset = data_offset;
245 cred->targetname_size = size;
246 memcpy( (char *)cred + cred->targetname_offset, str, size );
247 data_offset += size;
249 free( str );
250 *retlen += size;
251 break;
252 case kSecAccountItemAttr:
254 TRACE( "kSecAccountItemAttr: %.*s\n", (int)attr_list->attr[i].length, (char *)attr_list->attr[i].data );
255 if (cred) cred->username_offset = cred->username_size = 0;
256 if (!attr_list->attr[i].data) continue;
258 if (!(str = cred_umbstowcs( attr_list->attr[i].data, attr_list->attr[i].length, &len )))
259 continue;
260 size = (len + 1) * sizeof(WCHAR);
261 if (cred && *retlen + size <= buflen)
263 cred->username_offset = data_offset;
264 cred->username_size = size;
265 memcpy( (char *)cred + cred->username_offset, str, size );
266 data_offset += size;
268 free( str );
269 *retlen += size;
270 break;
272 case kSecCommentItemAttr:
273 TRACE( "kSecCommentItemAttr: %.*s\n", (int)attr_list->attr[i].length, (char *)attr_list->attr[i].data );
274 if (cred) cred->comment_offset = cred->comment_size = 0;
275 if (!attr_list->attr[i].data) continue;
277 if (!(str = cred_umbstowcs( attr_list->attr[i].data, attr_list->attr[i].length, &len )))
278 continue;
279 size = (len + 1) * sizeof(WCHAR);
280 if (cred && *retlen + size <= buflen)
282 cred->comment_offset = data_offset;
283 cred->comment_size = size;
284 memcpy( (char *)cred + cred->comment_offset, str, size );
285 data_offset += size;
287 free( str );
288 *retlen += size;
289 break;
290 case kSecCreationDateItemAttr:
292 ULONGLONG ticks;
293 struct tm tm;
294 time_t time;
296 TRACE( "kSecCreationDateItemAttr: %.*s\n", (int)attr_list->attr[i].length, (char *)attr_list->attr[i].data );
297 if (cred) cred->last_written.dwLowDateTime = cred->last_written.dwHighDateTime = 0;
298 if (!attr_list->attr[i].data) continue;
300 if (cred)
302 memset( &tm, 0, sizeof(tm) );
303 strptime( attr_list->attr[i].data, "%Y%m%d%H%M%SZ", &tm );
304 time = mktime( &tm );
305 ticks = time * (ULONGLONG)TICKSPERSEC + TICKS_1601_TO_1970;
306 cred->last_written.dwLowDateTime = ticks;
307 cred->last_written.dwHighDateTime = ticks >> 32;
309 break;
311 default:
312 FIXME( "unhandled attribute %u\n", (unsigned)attr_list->attr[i].tag );
313 break;
317 if (cred) cred->blob_offset = cred->blob_size = 0;
318 str = cred_umbstowcs( cred_blob, cred_blob_len, &len );
319 size = len * sizeof(WCHAR);
320 if (cred && *retlen + size <= buflen)
322 cred->blob_offset = data_offset;
323 cred->blob_size = size;
324 memcpy( (char *)cred + cred->blob_offset, str, size );
326 free( str );
327 *retlen += size;
329 if (attr_list) SecKeychainItemFreeAttributesAndData( attr_list, cred_blob );
330 return STATUS_SUCCESS;
333 NTSTATUS read_credential( void *args )
335 const struct ioctl_params *params = args;
336 struct mountmgr_credential *cred = params->buff;
337 const WCHAR *targetname;
338 SecKeychainItemRef item;
339 ULONG size;
340 NTSTATUS status;
342 if (!check_credential_string( params->buff, params->insize, cred->targetname_size, cred->targetname_offset ))
343 return STATUS_INVALID_PARAMETER;
344 targetname = (const WCHAR *)((const char *)cred + cred->targetname_offset);
346 if (!(item = find_credential( targetname ))) return STATUS_NOT_FOUND;
348 status = fill_credential( item, TRUE, cred, sizeof(*cred), params->outsize, &size );
349 CFRelease( item );
350 if (status != STATUS_SUCCESS) return status;
352 *params->info = size;
353 return (size > params->outsize) ? STATUS_BUFFER_OVERFLOW : STATUS_SUCCESS;
356 NTSTATUS write_credential( void *args )
358 const struct ioctl_params *params = args;
359 const struct mountmgr_credential *cred = params->buff;
360 int status;
361 ULONG len, len_password = 0;
362 const WCHAR *ptr;
363 SecKeychainItemRef keychain_item;
364 char *targetname, *username = NULL, *password = NULL;
365 SecKeychainAttribute attrs[1];
366 SecKeychainAttributeList attr_list;
367 NTSTATUS ret = STATUS_NO_MEMORY;
369 if (!check_credential_string( params->buff, params->insize, cred->targetname_size, cred->targetname_offset ) ||
370 !check_credential_string( params->buff, params->insize, cred->username_size, cred->username_offset ) ||
371 ((cred->blob_size && cred->blob_size % sizeof(WCHAR)) || cred->blob_offset + cred->blob_size > params->insize) ||
372 (cred->comment_size && !check_credential_string( params->buff, params->insize, cred->comment_size, cred->comment_offset )) ||
373 sizeof(*cred) + cred->targetname_size + cred->username_size + cred->blob_size + cred->comment_size > params->insize)
375 return STATUS_INVALID_PARAMETER;
378 ptr = (const WCHAR *)((const char *)cred + cred->targetname_offset);
379 if (!(targetname = cred_wcstoumbs( ptr, cred->targetname_size / sizeof(WCHAR), &len ))) goto error;
381 ptr = (const WCHAR *)((const char *)cred + cred->username_offset);
382 if (!(username = cred_wcstoumbs( ptr, cred->username_size / sizeof(WCHAR), &len ))) goto error;
384 if (cred->blob_size)
386 ptr = (const WCHAR *)((const char *)cred + cred->blob_offset);
387 if (!(password = cred_wcstoumbs( ptr, cred->blob_size / sizeof(WCHAR), &len_password ))) goto error;
390 TRACE("adding target %s, username %s using Keychain\n", targetname, username );
391 status = SecKeychainAddGenericPassword( NULL, strlen(targetname), targetname, strlen(username), username,
392 len_password, password, &keychain_item );
393 if (status != noErr) ERR( "SecKeychainAddGenericPassword returned %d\n", status );
394 if (status == errSecDuplicateItem)
396 status = SecKeychainFindGenericPassword( NULL, strlen(targetname), targetname, strlen(username), username, NULL,
397 NULL, &keychain_item );
398 if (status != noErr) ERR( "SecKeychainFindGenericPassword returned %d\n", status );
400 free( username );
401 free( targetname );
402 if (status != noErr)
404 free( password );
405 return STATUS_UNSUCCESSFUL;
407 if (cred->comment_size)
409 attr_list.count = 1;
410 attr_list.attr = attrs;
411 attrs[0].tag = kSecCommentItemAttr;
412 ptr = (const WCHAR *)((const char *)cred + cred->comment_offset);
413 if (!(attrs[0].data = cred_wcstoumbs( ptr, cred->comment_size / sizeof(WCHAR), &len ))) goto error;
414 attrs[0].length = len - 1;
416 else
418 attr_list.count = 0;
419 attr_list.attr = NULL;
421 status = SecKeychainItemModifyAttributesAndData( keychain_item, &attr_list, cred->blob_preserve ? 0 : len_password,
422 cred->blob_preserve ? NULL : password );
424 if (cred->comment_size) free( attrs[0].data );
425 free( password );
426 /* FIXME: set TargetAlias attribute */
427 CFRelease( keychain_item );
428 if (status != noErr) return STATUS_UNSUCCESSFUL;
429 return STATUS_SUCCESS;
431 error:
432 free( username );
433 free( targetname );
434 free( password );
435 return ret;
438 NTSTATUS delete_credential( void *args )
440 const struct ioctl_params *params = args;
441 const struct mountmgr_credential *cred = params->buff;
442 const WCHAR *targetname;
443 SecKeychainItemRef item;
445 if (!check_credential_string( params->buff, params->insize, cred->targetname_size, cred->targetname_offset ))
446 return STATUS_INVALID_PARAMETER;
447 targetname = (const WCHAR *)((const char *)cred + cred->targetname_offset);
449 if (!(item = find_credential( targetname ))) return STATUS_NOT_FOUND;
451 SecKeychainItemDelete( item );
452 CFRelease( item );
453 return STATUS_SUCCESS;
456 static BOOL match_credential( void *data, UInt32 data_len, const WCHAR *filter )
458 ULONG len;
459 WCHAR *targetname;
460 const WCHAR *p;
461 BOOL ret;
463 if (!*filter) return TRUE;
464 if (!(targetname = cred_umbstowcs( data, data_len, &len ))) return FALSE;
466 TRACE( "comparing filter %s to target name %s\n", debugstr_w(filter), debugstr_w(targetname) );
468 p = wcschr( filter, '*' );
469 if (*p && !p[1]) ret = !wcsnicmp( filter, targetname, p - filter );
470 else ret = !wcsicmp( filter, targetname );
471 free( targetname );
472 return ret;
475 static NTSTATUS search_credentials( const WCHAR *filter, struct mountmgr_credential_list *list, ULONG *ret_count, ULONG *ret_size )
477 SecKeychainSearchRef search;
478 SecKeychainItemRef item;
479 int status;
480 ULONG i = 0;
481 ULONG data_offset, data_size = 0, size;
482 NTSTATUS ret = STATUS_NOT_FOUND;
484 status = SecKeychainSearchCreateFromAttributes( NULL, kSecGenericPasswordItemClass, NULL, &search );
485 if (status != noErr)
487 ERR( "SecKeychainSearchCreateFromAttributes returned status %d\n", status );
488 return STATUS_INTERNAL_ERROR;
491 while (SecKeychainSearchCopyNext( search, &item ) == noErr)
493 SecKeychainAttributeInfo info;
494 SecKeychainAttributeList *attr_list;
495 UInt32 info_tags[] = { kSecServiceItemAttr };
496 BOOL match;
498 info.count = ARRAY_SIZE(info_tags);
499 info.tag = info_tags;
500 info.format = NULL;
501 status = SecKeychainItemCopyAttributesAndData( item, &info, NULL, &attr_list, NULL, NULL );
502 if (status != noErr)
504 WARN( "SecKeychainItemCopyAttributesAndData returned status %d\n", status );
505 CFRelease( item );
506 continue;
508 if (attr_list->count != 1 || attr_list->attr[0].tag != kSecServiceItemAttr)
510 SecKeychainItemFreeAttributesAndData( attr_list, NULL );
511 CFRelease( item );
512 continue;
514 TRACE( "service item: %.*s\n", (int)attr_list->attr[0].length, (char *)attr_list->attr[0].data );
516 match = match_credential( attr_list->attr[0].data, attr_list->attr[0].length, filter );
517 SecKeychainItemFreeAttributesAndData( attr_list, NULL );
518 if (!match)
520 CFRelease( item );
521 continue;
524 if (!list) ret = fill_credential( item, FALSE, NULL, 0, 0, &size );
525 else
527 data_offset = FIELD_OFFSET( struct mountmgr_credential_list, creds[list->count] ) -
528 FIELD_OFFSET( struct mountmgr_credential_list, creds[i] ) + data_size;
529 ret = fill_credential( item, FALSE, &list->creds[i], data_offset, list->size - data_offset, &size );
532 CFRelease( item );
533 if (ret == STATUS_NOT_FOUND) continue;
534 if (ret != STATUS_SUCCESS) break;
535 data_size += size - sizeof(struct mountmgr_credential);
536 i++;
539 if (ret_count) *ret_count = i;
540 if (ret_size) *ret_size = FIELD_OFFSET( struct mountmgr_credential_list, creds[i] ) + data_size;
542 CFRelease( search );
543 return ret;
546 NTSTATUS enumerate_credentials( void *args )
548 const struct ioctl_params *params = args;
549 struct mountmgr_credential_list *list = params->buff;
550 WCHAR *filter;
551 ULONG size, count;
552 Boolean saved_user_interaction_allowed;
553 NTSTATUS status;
555 if (!check_credential_string( params->buff, params->insize, list->filter_size, list->filter_offset ))
556 return STATUS_INVALID_PARAMETER;
557 if (!(filter = malloc( list->filter_size ))) return STATUS_NO_MEMORY;
558 memcpy( filter, (const char *)list + list->filter_offset, list->filter_size );
560 SecKeychainGetUserInteractionAllowed( &saved_user_interaction_allowed );
561 SecKeychainSetUserInteractionAllowed( false );
563 if ((status = search_credentials( filter, NULL, &count, &size )) == STATUS_SUCCESS)
566 if (size > params->outsize)
568 if (size >= sizeof(list->size)) list->size = size;
569 *params->info = sizeof(list->size);
570 status = STATUS_BUFFER_OVERFLOW;
572 else
574 list->size = size;
575 list->count = count;
576 *params->info = size;
577 status = search_credentials( filter, list, NULL, NULL );
581 SecKeychainSetUserInteractionAllowed( saved_user_interaction_allowed );
582 free( filter );
583 return status;
586 #else /* __APPLE__ */
588 NTSTATUS query_symbol_file( void *args )
590 FIXME( "not supported\n" );
591 return STATUS_NOT_SUPPORTED;
594 NTSTATUS read_credential( void *args )
596 FIXME( "not supported\n" );
597 return STATUS_NOT_SUPPORTED;
600 NTSTATUS write_credential( void *args )
602 FIXME( "not supported\n" );
603 return STATUS_NOT_SUPPORTED;
606 NTSTATUS delete_credential( void *args )
608 FIXME( "not supported\n" );
609 return STATUS_NOT_SUPPORTED;
612 NTSTATUS enumerate_credentials( void *args )
614 FIXME( "not supported\n" );
615 return STATUS_NOT_SUPPORTED;
618 #endif /* __APPLE__ */