dmime: Implement band track IDirectMusicTrack_Play.
[wine.git] / dlls / mountmgr.sys / cred.c
blobb9080d162bfb6aaf3286a24051d18d20c6180719
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 #include "mountmgr.h"
40 #include "unixlib.h"
41 #include "wine/debug.h"
43 WINE_DEFAULT_DEBUG_CHANNEL(mountmgr);
45 #ifdef __APPLE__
47 #define TICKSPERSEC 10000000
48 #define SECSPERDAY 86400
49 /* 1601 to 1970 is 369 years plus 89 leap days */
50 #define SECS_1601_TO_1970 ((369 * 365 + 89) * (ULONGLONG)SECSPERDAY)
51 #define TICKS_1601_TO_1970 (SECS_1601_TO_1970 * TICKSPERSEC)
53 /* implementation of Wine extension to use host APIs to find symbol file by GUID */
54 NTSTATUS query_symbol_file( void *args )
56 const struct ioctl_params *params = args;
57 char *result = params->buff;
58 CFStringRef query_cfstring;
59 MDQueryRef mdquery;
60 const GUID *id = params->buff;
61 NTSTATUS status = STATUS_NO_MEMORY;
63 if (!(query_cfstring = CFStringCreateWithFormat(kCFAllocatorDefault, NULL,
64 CFSTR("com_apple_xcode_dsym_uuids == \"%08X-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X\""),
65 (unsigned int)id->Data1, id->Data2, id->Data3, id->Data4[0],
66 id->Data4[1], id->Data4[2], id->Data4[3], id->Data4[4],
67 id->Data4[5], id->Data4[6], id->Data4[7] )))
68 return STATUS_NO_MEMORY;
70 mdquery = MDQueryCreate(NULL, query_cfstring, NULL, NULL);
71 CFRelease(query_cfstring);
72 if (!mdquery) return STATUS_NO_MEMORY;
74 MDQuerySetMaxCount(mdquery, 1);
75 if (MDQueryExecute(mdquery, kMDQuerySynchronous))
77 if (MDQueryGetResultCount(mdquery) >= 1)
79 MDItemRef item = (MDItemRef)MDQueryGetResultAtIndex(mdquery, 0);
80 CFStringRef item_path = MDItemCopyAttribute(item, kMDItemPath);
82 if (item_path)
84 if (CFStringGetCString( item_path, result, params->outsize, kCFStringEncodingUTF8 ))
86 *params->info = strlen( result ) + 1;
87 status = STATUS_SUCCESS;
88 TRACE("found %s\n", debugstr_a(result));
90 else status = STATUS_BUFFER_OVERFLOW;
91 CFRelease(item_path);
94 else status = STATUS_NO_MORE_ENTRIES;
96 CFRelease(mdquery);
97 return status;
100 static inline BOOL check_credential_string( const void *buf, ULONG buflen, ULONG size, ULONG offset )
102 const WCHAR *ptr = buf;
103 if (!size || size % sizeof(WCHAR) || offset + size > buflen || ptr[(offset + size) / sizeof(WCHAR) - 1]) return FALSE;
104 return TRUE;
107 static WCHAR *cred_umbstowcs( const char *src, ULONG srclen, ULONG *retlen )
109 WCHAR *ret = malloc( (srclen + 1) * sizeof(WCHAR) );
110 if (ret)
112 *retlen = ntdll_umbstowcs( src, srclen, ret, srclen );
113 ret[*retlen] = 0;
115 return ret;
118 static char *cred_wcstoumbs( const WCHAR *src, ULONG srclen, ULONG *retlen )
120 char *ret = malloc( srclen * 3 + 1 );
121 if (ret)
123 *retlen = ntdll_wcstoumbs( src, srclen, ret, srclen * 3, FALSE );
124 ret[*retlen] = 0;
126 return ret;
129 static SecKeychainItemRef find_credential( const WCHAR *name )
131 int status;
132 SecKeychainSearchRef search;
133 SecKeychainItemRef item;
135 status = SecKeychainSearchCreateFromAttributes( NULL, kSecGenericPasswordItemClass, NULL, &search );
136 if (status != noErr) return NULL;
138 while (SecKeychainSearchCopyNext( search, &item ) == noErr)
140 SecKeychainAttributeInfo info;
141 SecKeychainAttributeList *attr_list;
142 UInt32 info_tags[] = { kSecServiceItemAttr };
143 WCHAR *itemname;
144 ULONG len;
146 info.count = ARRAY_SIZE(info_tags);
147 info.tag = info_tags;
148 info.format = NULL;
149 status = SecKeychainItemCopyAttributesAndData( item, &info, NULL, &attr_list, NULL, NULL );
150 if (status != noErr)
152 WARN( "SecKeychainItemCopyAttributesAndData returned status %d\n", status );
153 continue;
155 if (attr_list->count != 1 || attr_list->attr[0].tag != kSecServiceItemAttr)
157 CFRelease( item );
158 continue;
160 itemname = cred_umbstowcs( attr_list->attr[0].data, attr_list->attr[0].length, &len );
161 if (!itemname)
163 CFRelease( item );
164 CFRelease( search );
165 return NULL;
167 if (wcsicmp( itemname, name ))
169 CFRelease( item );
170 free( itemname );
171 continue;
173 free( itemname );
174 SecKeychainItemFreeAttributesAndData( attr_list, NULL );
175 CFRelease( search );
176 return item;
179 CFRelease( search );
180 return NULL;
183 static NTSTATUS fill_credential( SecKeychainItemRef item, BOOL require_password, void *buf, ULONG data_offset,
184 ULONG buflen, ULONG *retlen )
186 struct mountmgr_credential *cred = buf;
187 int status;
188 ULONG size, len;
189 UInt32 i, cred_blob_len = 0;
190 void *cred_blob;
191 WCHAR *str;
192 BOOL user_name_present = FALSE;
193 SecKeychainAttributeInfo info;
194 SecKeychainAttributeList *attr_list = NULL;
195 UInt32 info_tags[] = { kSecServiceItemAttr, kSecAccountItemAttr, kSecCommentItemAttr, kSecCreationDateItemAttr };
197 info.count = ARRAY_SIZE(info_tags);
198 info.tag = info_tags;
199 info.format = NULL;
200 status = SecKeychainItemCopyAttributesAndData( item, &info, NULL, &attr_list, &cred_blob_len, &cred_blob );
201 if (status == errSecAuthFailed && !require_password)
203 cred_blob_len = 0;
204 cred_blob = NULL;
205 status = SecKeychainItemCopyAttributesAndData( item, &info, NULL, &attr_list, &cred_blob_len, NULL );
207 if (status != noErr)
209 WARN( "SecKeychainItemCopyAttributesAndData returned status %d\n", status );
210 return STATUS_NOT_FOUND;
212 for (i = 0; i < attr_list->count; i++)
214 if (attr_list->attr[i].tag == kSecAccountItemAttr && attr_list->attr[i].data)
216 user_name_present = TRUE;
217 break;
220 if (!user_name_present)
222 WARN( "no kSecAccountItemAttr for item\n" );
223 SecKeychainItemFreeAttributesAndData( attr_list, cred_blob );
224 return STATUS_NOT_FOUND;
227 *retlen = sizeof(*cred);
228 for (i = 0; i < attr_list->count; i++)
230 switch (attr_list->attr[i].tag)
232 case kSecServiceItemAttr:
233 TRACE( "kSecServiceItemAttr: %.*s\n", (int)attr_list->attr[i].length, (char *)attr_list->attr[i].data );
234 if (cred) cred->targetname_offset = cred->targetname_size = 0;
235 if (!attr_list->attr[i].data) continue;
237 if (!(str = cred_umbstowcs( attr_list->attr[i].data, attr_list->attr[i].length, &len )))
238 continue;
239 size = (len + 1) * sizeof(WCHAR);
240 if (cred && *retlen + size <= buflen)
242 cred->targetname_offset = data_offset;
243 cred->targetname_size = size;
244 memcpy( (char *)cred + cred->targetname_offset, str, size );
245 data_offset += size;
247 free( str );
248 *retlen += size;
249 break;
250 case kSecAccountItemAttr:
252 TRACE( "kSecAccountItemAttr: %.*s\n", (int)attr_list->attr[i].length, (char *)attr_list->attr[i].data );
253 if (cred) cred->username_offset = cred->username_size = 0;
254 if (!attr_list->attr[i].data) continue;
256 if (!(str = cred_umbstowcs( attr_list->attr[i].data, attr_list->attr[i].length, &len )))
257 continue;
258 size = (len + 1) * sizeof(WCHAR);
259 if (cred && *retlen + size <= buflen)
261 cred->username_offset = data_offset;
262 cred->username_size = size;
263 memcpy( (char *)cred + cred->username_offset, str, size );
264 data_offset += size;
266 free( str );
267 *retlen += size;
268 break;
270 case kSecCommentItemAttr:
271 TRACE( "kSecCommentItemAttr: %.*s\n", (int)attr_list->attr[i].length, (char *)attr_list->attr[i].data );
272 if (cred) cred->comment_offset = cred->comment_size = 0;
273 if (!attr_list->attr[i].data) continue;
275 if (!(str = cred_umbstowcs( attr_list->attr[i].data, attr_list->attr[i].length, &len )))
276 continue;
277 size = (len + 1) * sizeof(WCHAR);
278 if (cred && *retlen + size <= buflen)
280 cred->comment_offset = data_offset;
281 cred->comment_size = size;
282 memcpy( (char *)cred + cred->comment_offset, str, size );
283 data_offset += size;
285 free( str );
286 *retlen += size;
287 break;
288 case kSecCreationDateItemAttr:
290 ULONGLONG ticks;
291 struct tm tm;
292 time_t time;
294 TRACE( "kSecCreationDateItemAttr: %.*s\n", (int)attr_list->attr[i].length, (char *)attr_list->attr[i].data );
295 if (cred) cred->last_written.dwLowDateTime = cred->last_written.dwHighDateTime = 0;
296 if (!attr_list->attr[i].data) continue;
298 if (cred)
300 memset( &tm, 0, sizeof(tm) );
301 strptime( attr_list->attr[i].data, "%Y%m%d%H%M%SZ", &tm );
302 time = mktime( &tm );
303 ticks = time * (ULONGLONG)TICKSPERSEC + TICKS_1601_TO_1970;
304 cred->last_written.dwLowDateTime = ticks;
305 cred->last_written.dwHighDateTime = ticks >> 32;
307 break;
309 default:
310 FIXME( "unhandled attribute %u\n", (unsigned)attr_list->attr[i].tag );
311 break;
315 if (cred) cred->blob_offset = cred->blob_size = 0;
316 str = cred_umbstowcs( cred_blob, cred_blob_len, &len );
317 size = len * sizeof(WCHAR);
318 if (cred && *retlen + size <= buflen)
320 cred->blob_offset = data_offset;
321 cred->blob_size = size;
322 memcpy( (char *)cred + cred->blob_offset, str, size );
324 free( str );
325 *retlen += size;
327 if (attr_list) SecKeychainItemFreeAttributesAndData( attr_list, cred_blob );
328 return STATUS_SUCCESS;
331 NTSTATUS read_credential( void *args )
333 const struct ioctl_params *params = args;
334 struct mountmgr_credential *cred = params->buff;
335 const WCHAR *targetname;
336 SecKeychainItemRef item;
337 ULONG size;
338 NTSTATUS status;
340 if (!check_credential_string( params->buff, params->insize, cred->targetname_size, cred->targetname_offset ))
341 return STATUS_INVALID_PARAMETER;
342 targetname = (const WCHAR *)((const char *)cred + cred->targetname_offset);
344 if (!(item = find_credential( targetname ))) return STATUS_NOT_FOUND;
346 status = fill_credential( item, TRUE, cred, sizeof(*cred), params->outsize, &size );
347 CFRelease( item );
348 if (status != STATUS_SUCCESS) return status;
350 *params->info = size;
351 return (size > params->outsize) ? STATUS_BUFFER_OVERFLOW : STATUS_SUCCESS;
354 NTSTATUS write_credential( void *args )
356 const struct ioctl_params *params = args;
357 const struct mountmgr_credential *cred = params->buff;
358 int status;
359 ULONG len, len_password = 0;
360 const WCHAR *ptr;
361 SecKeychainItemRef keychain_item;
362 char *targetname, *username = NULL, *password = NULL;
363 SecKeychainAttribute attrs[1];
364 SecKeychainAttributeList attr_list;
365 NTSTATUS ret = STATUS_NO_MEMORY;
367 if (!check_credential_string( params->buff, params->insize, cred->targetname_size, cred->targetname_offset ) ||
368 !check_credential_string( params->buff, params->insize, cred->username_size, cred->username_offset ) ||
369 ((cred->blob_size && cred->blob_size % sizeof(WCHAR)) || cred->blob_offset + cred->blob_size > params->insize) ||
370 (cred->comment_size && !check_credential_string( params->buff, params->insize, cred->comment_size, cred->comment_offset )) ||
371 sizeof(*cred) + cred->targetname_size + cred->username_size + cred->blob_size + cred->comment_size > params->insize)
373 return STATUS_INVALID_PARAMETER;
376 ptr = (const WCHAR *)((const char *)cred + cred->targetname_offset);
377 if (!(targetname = cred_wcstoumbs( ptr, cred->targetname_size / sizeof(WCHAR), &len ))) goto error;
379 ptr = (const WCHAR *)((const char *)cred + cred->username_offset);
380 if (!(username = cred_wcstoumbs( ptr, cred->username_size / sizeof(WCHAR), &len ))) goto error;
382 if (cred->blob_size)
384 ptr = (const WCHAR *)((const char *)cred + cred->blob_offset);
385 if (!(password = cred_wcstoumbs( ptr, cred->blob_size / sizeof(WCHAR), &len_password ))) goto error;
388 TRACE("adding target %s, username %s using Keychain\n", targetname, username );
389 status = SecKeychainAddGenericPassword( NULL, strlen(targetname), targetname, strlen(username), username,
390 len_password, password, &keychain_item );
391 if (status != noErr) ERR( "SecKeychainAddGenericPassword returned %d\n", status );
392 if (status == errSecDuplicateItem)
394 status = SecKeychainFindGenericPassword( NULL, strlen(targetname), targetname, strlen(username), username, NULL,
395 NULL, &keychain_item );
396 if (status != noErr) ERR( "SecKeychainFindGenericPassword returned %d\n", status );
398 free( username );
399 free( targetname );
400 if (status != noErr)
402 free( password );
403 return STATUS_UNSUCCESSFUL;
405 if (cred->comment_size)
407 attr_list.count = 1;
408 attr_list.attr = attrs;
409 attrs[0].tag = kSecCommentItemAttr;
410 ptr = (const WCHAR *)((const char *)cred + cred->comment_offset);
411 if (!(attrs[0].data = cred_wcstoumbs( ptr, cred->comment_size / sizeof(WCHAR), &len ))) goto error;
412 attrs[0].length = len - 1;
414 else
416 attr_list.count = 0;
417 attr_list.attr = NULL;
419 status = SecKeychainItemModifyAttributesAndData( keychain_item, &attr_list, cred->blob_preserve ? 0 : len_password,
420 cred->blob_preserve ? NULL : password );
422 if (cred->comment_size) free( attrs[0].data );
423 free( password );
424 /* FIXME: set TargetAlias attribute */
425 CFRelease( keychain_item );
426 if (status != noErr) return STATUS_UNSUCCESSFUL;
427 return STATUS_SUCCESS;
429 error:
430 free( username );
431 free( targetname );
432 free( password );
433 return ret;
436 NTSTATUS delete_credential( void *args )
438 const struct ioctl_params *params = args;
439 const struct mountmgr_credential *cred = params->buff;
440 const WCHAR *targetname;
441 SecKeychainItemRef item;
443 if (!check_credential_string( params->buff, params->insize, cred->targetname_size, cred->targetname_offset ))
444 return STATUS_INVALID_PARAMETER;
445 targetname = (const WCHAR *)((const char *)cred + cred->targetname_offset);
447 if (!(item = find_credential( targetname ))) return STATUS_NOT_FOUND;
449 SecKeychainItemDelete( item );
450 CFRelease( item );
451 return STATUS_SUCCESS;
454 static BOOL match_credential( void *data, UInt32 data_len, const WCHAR *filter )
456 ULONG len;
457 WCHAR *targetname;
458 const WCHAR *p;
459 BOOL ret;
461 if (!*filter) return TRUE;
462 if (!(targetname = cred_umbstowcs( data, data_len, &len ))) return FALSE;
464 TRACE( "comparing filter %s to target name %s\n", debugstr_w(filter), debugstr_w(targetname) );
466 p = wcschr( filter, '*' );
467 if (*p && !p[1]) ret = !wcsnicmp( filter, targetname, p - filter );
468 else ret = !wcsicmp( filter, targetname );
469 free( targetname );
470 return ret;
473 static NTSTATUS search_credentials( const WCHAR *filter, struct mountmgr_credential_list *list, ULONG *ret_count, ULONG *ret_size )
475 SecKeychainSearchRef search;
476 SecKeychainItemRef item;
477 int status;
478 ULONG i = 0;
479 ULONG data_offset, data_size = 0, size;
480 NTSTATUS ret = STATUS_NOT_FOUND;
482 status = SecKeychainSearchCreateFromAttributes( NULL, kSecGenericPasswordItemClass, NULL, &search );
483 if (status != noErr)
485 ERR( "SecKeychainSearchCreateFromAttributes returned status %d\n", status );
486 return STATUS_INTERNAL_ERROR;
489 while (SecKeychainSearchCopyNext( search, &item ) == noErr)
491 SecKeychainAttributeInfo info;
492 SecKeychainAttributeList *attr_list;
493 UInt32 info_tags[] = { kSecServiceItemAttr };
494 BOOL match;
496 info.count = ARRAY_SIZE(info_tags);
497 info.tag = info_tags;
498 info.format = NULL;
499 status = SecKeychainItemCopyAttributesAndData( item, &info, NULL, &attr_list, NULL, NULL );
500 if (status != noErr)
502 WARN( "SecKeychainItemCopyAttributesAndData returned status %d\n", status );
503 CFRelease( item );
504 continue;
506 if (attr_list->count != 1 || attr_list->attr[0].tag != kSecServiceItemAttr)
508 SecKeychainItemFreeAttributesAndData( attr_list, NULL );
509 CFRelease( item );
510 continue;
512 TRACE( "service item: %.*s\n", (int)attr_list->attr[0].length, (char *)attr_list->attr[0].data );
514 match = match_credential( attr_list->attr[0].data, attr_list->attr[0].length, filter );
515 SecKeychainItemFreeAttributesAndData( attr_list, NULL );
516 if (!match)
518 CFRelease( item );
519 continue;
522 if (!list) ret = fill_credential( item, FALSE, NULL, 0, 0, &size );
523 else
525 data_offset = FIELD_OFFSET( struct mountmgr_credential_list, creds[list->count] ) -
526 FIELD_OFFSET( struct mountmgr_credential_list, creds[i] ) + data_size;
527 ret = fill_credential( item, FALSE, &list->creds[i], data_offset, list->size - data_offset, &size );
530 CFRelease( item );
531 if (ret == STATUS_NOT_FOUND) continue;
532 if (ret != STATUS_SUCCESS) break;
533 data_size += size - sizeof(struct mountmgr_credential);
534 i++;
537 if (ret_count) *ret_count = i;
538 if (ret_size) *ret_size = FIELD_OFFSET( struct mountmgr_credential_list, creds[i] ) + data_size;
540 CFRelease( search );
541 return ret;
544 NTSTATUS enumerate_credentials( void *args )
546 const struct ioctl_params *params = args;
547 struct mountmgr_credential_list *list = params->buff;
548 WCHAR *filter;
549 ULONG size, count;
550 Boolean saved_user_interaction_allowed;
551 NTSTATUS status;
553 if (!check_credential_string( params->buff, params->insize, list->filter_size, list->filter_offset ))
554 return STATUS_INVALID_PARAMETER;
555 if (!(filter = malloc( list->filter_size ))) return STATUS_NO_MEMORY;
556 memcpy( filter, (const char *)list + list->filter_offset, list->filter_size );
558 SecKeychainGetUserInteractionAllowed( &saved_user_interaction_allowed );
559 SecKeychainSetUserInteractionAllowed( false );
561 if ((status = search_credentials( filter, NULL, &count, &size )) == STATUS_SUCCESS)
564 if (size > params->outsize)
566 if (size >= sizeof(list->size)) list->size = size;
567 *params->info = sizeof(list->size);
568 status = STATUS_BUFFER_OVERFLOW;
570 else
572 list->size = size;
573 list->count = count;
574 *params->info = size;
575 status = search_credentials( filter, list, NULL, NULL );
579 SecKeychainSetUserInteractionAllowed( saved_user_interaction_allowed );
580 free( filter );
581 return status;
584 #else /* __APPLE__ */
586 NTSTATUS query_symbol_file( void *args )
588 FIXME( "not supported\n" );
589 return STATUS_NOT_SUPPORTED;
592 NTSTATUS read_credential( void *args )
594 FIXME( "not supported\n" );
595 return STATUS_NOT_SUPPORTED;
598 NTSTATUS write_credential( void *args )
600 FIXME( "not supported\n" );
601 return STATUS_NOT_SUPPORTED;
604 NTSTATUS delete_credential( void *args )
606 FIXME( "not supported\n" );
607 return STATUS_NOT_SUPPORTED;
610 NTSTATUS enumerate_credentials( void *args )
612 FIXME( "not supported\n" );
613 return STATUS_NOT_SUPPORTED;
616 #endif /* __APPLE__ */