mirror of
https://github.com/videolan/vlc.git
synced 2024-12-05 07:46:32 +08:00
Add keychain crypto store
This commit is contained in:
parent
50b19e4b34
commit
bba9bcf046
1
NEWS
1
NEWS
@ -194,6 +194,7 @@ Misc
|
||||
* Add Gnome libsecret-based crypto keystore
|
||||
* Add KDE Kwallet-based crypto keystore
|
||||
* Add a plaintext keystore
|
||||
* Add Keychain based crypto keystore for iOS, Mac OS X and tvOS
|
||||
|
||||
Removed modules
|
||||
* Atmo video filter
|
||||
|
@ -203,6 +203,7 @@ $Id$
|
||||
* kai: OS/2 audio output
|
||||
* karaoke: simple karaoke audio filter
|
||||
* kate: kate text bitstream decoder
|
||||
* keychain: Keystore for iOS, Mac OS X and tvOS
|
||||
* kva: OS/2 video output
|
||||
* kwallet: store secrets via KDE Kwallet
|
||||
* libass: Subtitle renderers using libass
|
||||
|
@ -29,6 +29,14 @@ if HAVE_KWALLET
|
||||
keystore_LTLIBRARIES += libkwallet_plugin.la
|
||||
endif
|
||||
|
||||
libkeychain_plugin_la_SOURCES = keystore/keychain.m
|
||||
libkeychain_plugin_la_OBJCFLAGS = $(AM_CFLAGS) -fobjc-arc
|
||||
libkeychain_plugin_la_LDFLAGS = $(AM_LDFLAGS) -rpath '$(keystoredir)' -Wl,-framework,Foundation -Wl,-framework,Security
|
||||
|
||||
if HAVE_DARWIN
|
||||
keystore_LTLIBRARIES += libkeychain_plugin.la
|
||||
endif
|
||||
|
||||
keystore_LTLIBRARIES += \
|
||||
$(LTLIBsecret)
|
||||
|
||||
|
476
modules/keystore/keychain.m
Normal file
476
modules/keystore/keychain.m
Normal file
@ -0,0 +1,476 @@
|
||||
/*****************************************************************************
|
||||
* keychain.m: Darwin Keychain keystore module
|
||||
*****************************************************************************
|
||||
* Copyright © 2016 VLC authors, VideoLAN and VideoLabs
|
||||
*
|
||||
* Author: Felix Paul Kühne <fkuehne # videolabs.io>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation; either version 2.1 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
|
||||
*****************************************************************************/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
# include "config.h"
|
||||
#endif
|
||||
|
||||
#include <vlc_common.h>
|
||||
#include <vlc_plugin.h>
|
||||
#include <vlc_keystore.h>
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import <Security/Security.h>
|
||||
|
||||
static int Open(vlc_object_t *);
|
||||
|
||||
static const int sync_list[] =
|
||||
{ 0, 1, 2 };
|
||||
static const char *const sync_list_text[] = {
|
||||
N_("Yes"), N_("No"), N_("Any")
|
||||
};
|
||||
|
||||
static const int accessibility_list[] =
|
||||
{ 0, 1, 2, 3, 4, 5, 6, 7 };
|
||||
static const char *const accessibility_list_text[] = {
|
||||
N_("System default"),
|
||||
N_("After first unlock"),
|
||||
N_("After first unlock, on this device only"),
|
||||
N_("Always"),
|
||||
N_("When passcode set, on this device only"),
|
||||
N_("Always, on this device only"),
|
||||
N_("When unlocked"),
|
||||
N_("When unlocked, on this device only")
|
||||
};
|
||||
|
||||
#define SYNC_ITEMS_TEXT N_("Synchronize stored items")
|
||||
#define SYNC_ITEMS_LONGTEXT N_("Synchronizes stored items via iCloud Keychain if enabled in the user domain. Requires iOS 7 / Mac OS X 10.9 / tvOS 9.0 or higher.")
|
||||
|
||||
#define ACCESSIBILITY_TYPE_TEXT N_("Accessibility type for all future passwords saved to the Keychain")
|
||||
|
||||
#define ACCESS_GROUP_TEXT N_("Keychain access group")
|
||||
#define ACCESS_GROUP_LONGTEXT N_("Keychain access group as defined by the app entitlements. Requires iOS 3 / Mac OS X 10.9 / tvOS 9.0 or higher.")
|
||||
|
||||
/* VLC can be compiled against older SDKs (like before OS X 10.10)
|
||||
* but newer features should still be available.
|
||||
* Hence, re-define things as needed */
|
||||
#ifndef kSecAttrSynchronizable
|
||||
#define kSecAttrSynchronizable CFSTR("sync")
|
||||
#endif
|
||||
|
||||
#ifndef kSecAttrSynchronizableAny
|
||||
#define kSecAttrSynchronizableAny CFSTR("syna")
|
||||
#endif
|
||||
|
||||
#ifndef kSecAttrAccessGroup
|
||||
#define kSecAttrAccessGroup CFSTR("agrp")
|
||||
#endif
|
||||
|
||||
#ifndef kSecAttrAccessibleAfterFirstUnlock
|
||||
#define kSecAttrAccessibleAfterFirstUnlock CFSTR("ck")
|
||||
#endif
|
||||
|
||||
#ifndef kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly
|
||||
#define kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly CFSTR("cku")
|
||||
#endif
|
||||
|
||||
#ifndef kSecAttrAccessibleAlways
|
||||
#define kSecAttrAccessibleAlways CFSTR("dk")
|
||||
#endif
|
||||
|
||||
#ifndef kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly
|
||||
#define kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly CFSTR("akpu")
|
||||
#endif
|
||||
|
||||
#ifndef kSecAttrAccessibleAlwaysThisDeviceOnly
|
||||
#define kSecAttrAccessibleAlwaysThisDeviceOnly CFSTR("dku")
|
||||
#endif
|
||||
|
||||
#ifndef kSecAttrAccessibleWhenUnlocked
|
||||
#define kSecAttrAccessibleWhenUnlocked CFSTR("ak")
|
||||
#endif
|
||||
|
||||
#ifndef kSecAttrAccessibleWhenUnlockedThisDeviceOnly
|
||||
#define kSecAttrAccessibleWhenUnlockedThisDeviceOnly CFSTR("aku")
|
||||
#endif
|
||||
|
||||
vlc_module_begin()
|
||||
set_shortname(N_("Keychain keystore"))
|
||||
set_description(N_("Keystore for iOS, Mac OS X and tvOS"))
|
||||
set_category(CAT_ADVANCED)
|
||||
set_subcategory(SUBCAT_ADVANCED_MISC)
|
||||
add_integer("keychain-synchronize", 1, SYNC_ITEMS_TEXT, SYNC_ITEMS_LONGTEXT, true)
|
||||
change_integer_list(sync_list, sync_list_text)
|
||||
add_integer("keychain-accessibility-type", 0, ACCESSIBILITY_TYPE_TEXT, ACCESSIBILITY_TYPE_TEXT, true)
|
||||
change_integer_list(accessibility_list, accessibility_list_text)
|
||||
add_string("keychain-access-group", NULL, ACCESS_GROUP_TEXT, ACCESS_GROUP_LONGTEXT, true)
|
||||
set_capability("keystore", 100)
|
||||
set_callbacks(Open, NULL)
|
||||
vlc_module_end ()
|
||||
|
||||
static NSMutableDictionary * CreateQuery(vlc_keystore *p_keystore)
|
||||
{
|
||||
NSMutableDictionary *dictionary = [NSMutableDictionary dictionaryWithCapacity:3];
|
||||
[dictionary setObject:(__bridge id)kSecClassInternetPassword forKey:(__bridge id)kSecClass];
|
||||
|
||||
[dictionary setObject:@"VLC-Password-Service" forKey:(__bridge id)kSecAttrService];
|
||||
|
||||
const char * psz_access_group = var_InheritString(p_keystore, "keychain-access-group");
|
||||
if (psz_access_group) {
|
||||
[dictionary setObject:[NSString stringWithUTF8String:psz_access_group] forKey:(__bridge id)kSecAttrAccessGroup];
|
||||
}
|
||||
|
||||
id syncValue;
|
||||
int syncMode = var_InheritInteger(p_keystore, "keychain-synchronize");
|
||||
|
||||
if (syncMode == 2) {
|
||||
syncValue = (__bridge id)kSecAttrSynchronizableAny;
|
||||
} else if (syncMode == 0) {
|
||||
syncValue = @(YES);
|
||||
} else {
|
||||
syncValue = @(NO);
|
||||
}
|
||||
|
||||
[dictionary setObject:syncValue forKey:(__bridge id)(kSecAttrSynchronizable)];
|
||||
|
||||
return dictionary;
|
||||
}
|
||||
|
||||
static NSString * ErrorForStatus(OSStatus status)
|
||||
{
|
||||
NSString *message = nil;
|
||||
|
||||
switch (status) {
|
||||
#if TARGET_OS_IPHONE
|
||||
case errSecUnimplemented: {
|
||||
message = @"Query unimplemented";
|
||||
break;
|
||||
}
|
||||
case errSecParam: {
|
||||
message = @"Faulty parameter";
|
||||
break;
|
||||
}
|
||||
case errSecAllocate: {
|
||||
message = @"Allocation failure";
|
||||
break;
|
||||
}
|
||||
case errSecNotAvailable: {
|
||||
message = @"Query not available";
|
||||
break;
|
||||
}
|
||||
case errSecDuplicateItem: {
|
||||
message = @"Duplicated item";
|
||||
break;
|
||||
}
|
||||
case errSecItemNotFound: {
|
||||
message = @"Item not found";
|
||||
break;
|
||||
}
|
||||
case errSecInteractionNotAllowed: {
|
||||
message = @"Interaction not allowed";
|
||||
break;
|
||||
}
|
||||
case errSecDecode: {
|
||||
message = @"Decoding failure";
|
||||
break;
|
||||
}
|
||||
case errSecAuthFailed: {
|
||||
message = @"Authentication failure";
|
||||
break;
|
||||
}
|
||||
case -34018: {
|
||||
message = @"iCloud Keychain failure";
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
message = @"Unknown generic error";
|
||||
}
|
||||
#else
|
||||
default:
|
||||
message = (__bridge_transfer NSString *)SecCopyErrorMessageString(status, NULL);
|
||||
#endif
|
||||
}
|
||||
|
||||
return message;
|
||||
}
|
||||
|
||||
static void SetAccessibilityForQuery(vlc_keystore *p_keystore,
|
||||
NSMutableDictionary *query)
|
||||
{
|
||||
int accessibilityType = var_InheritInteger(p_keystore, "keychain-accessibility-type");
|
||||
switch (accessibilityType) {
|
||||
case 1:
|
||||
[query setObject:(__bridge id)kSecAttrAccessibleAfterFirstUnlock forKey:(__bridge id)kSecAttrAccessible];
|
||||
break;
|
||||
case 2:
|
||||
[query setObject:(__bridge id)kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly forKey:(__bridge id)kSecAttrAccessible];
|
||||
break;
|
||||
case 3:
|
||||
[query setObject:(__bridge id)kSecAttrAccessibleAlways forKey:(__bridge id)kSecAttrAccessible];
|
||||
break;
|
||||
case 4:
|
||||
[query setObject:(__bridge id)kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly forKey:(__bridge id)kSecAttrAccessible];
|
||||
break;
|
||||
case 5:
|
||||
[query setObject:(__bridge id)kSecAttrAccessibleAlwaysThisDeviceOnly forKey:(__bridge id)kSecAttrAccessible];
|
||||
break;
|
||||
case 6:
|
||||
[query setObject:(__bridge id)kSecAttrAccessibleWhenUnlocked forKey:(__bridge id)kSecAttrAccessible];
|
||||
break;
|
||||
case 7:
|
||||
[query setObject:(__bridge id)kSecAttrAccessibleWhenUnlockedThisDeviceOnly forKey:(__bridge id)kSecAttrAccessible];
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void SetAttributesForQuery(const char *const ppsz_values[KEY_MAX], NSMutableDictionary *query, const char *psz_label)
|
||||
{
|
||||
const char *psz_protocol = ppsz_values[KEY_PROTOCOL];
|
||||
const char *psz_user = ppsz_values[KEY_USER];
|
||||
const char *psz_server = ppsz_values[KEY_SERVER];
|
||||
const char *psz_path = ppsz_values[KEY_PATH];
|
||||
const char *psz_port = ppsz_values[KEY_PORT];
|
||||
|
||||
if (psz_label) {
|
||||
[query setObject:[NSString stringWithUTF8String:psz_label] forKey:(__bridge id)kSecAttrLabel];
|
||||
}
|
||||
if (psz_protocol) {
|
||||
[query setObject:[NSString stringWithUTF8String:psz_protocol] forKey:(__bridge id)kSecAttrProtocol];
|
||||
}
|
||||
if (psz_user) {
|
||||
[query setObject:[NSString stringWithUTF8String:psz_user] forKey:(__bridge id)kSecAttrAccount];
|
||||
}
|
||||
if (psz_server) {
|
||||
[query setObject:[NSString stringWithUTF8String:psz_server] forKey:(__bridge id)kSecAttrServer];
|
||||
}
|
||||
if (psz_path) {
|
||||
[query setObject:[NSString stringWithUTF8String:psz_path] forKey:(__bridge id)kSecAttrPath];
|
||||
}
|
||||
if (psz_port) {
|
||||
[query setObject:[NSString stringWithUTF8String:psz_port] forKey:(__bridge id)kSecAttrPort];
|
||||
}
|
||||
}
|
||||
|
||||
static int CopyEntryValues(const char * ppsz_dst[KEY_MAX], const char *const ppsz_src[KEY_MAX])
|
||||
{
|
||||
for (unsigned int i = 0; i < KEY_MAX; ++i)
|
||||
{
|
||||
if (ppsz_src[i])
|
||||
{
|
||||
ppsz_dst[i] = strdup(ppsz_src[i]);
|
||||
if (!ppsz_dst[i])
|
||||
return VLC_EGENERIC;
|
||||
}
|
||||
}
|
||||
return VLC_SUCCESS;
|
||||
}
|
||||
|
||||
static int Store(vlc_keystore *p_keystore,
|
||||
const char *const ppsz_values[KEY_MAX],
|
||||
const uint8_t *p_secret,
|
||||
size_t i_secret_len,
|
||||
const char *psz_label)
|
||||
{
|
||||
OSStatus status;
|
||||
|
||||
if (!ppsz_values[KEY_PROTOCOL] || !p_secret) {
|
||||
return VLC_EGENERIC;
|
||||
}
|
||||
|
||||
NSMutableDictionary *query = nil;
|
||||
NSMutableDictionary *searchQuery = CreateQuery(p_keystore);
|
||||
|
||||
/* set attributes */
|
||||
SetAttributesForQuery(ppsz_values, searchQuery, psz_label);
|
||||
|
||||
/* search */
|
||||
status = SecItemCopyMatching((__bridge CFDictionaryRef)searchQuery, nil);
|
||||
|
||||
/* create storage unit */
|
||||
NSData *secretData = [[NSString stringWithFormat:@"%s", p_secret] dataUsingEncoding:NSUTF8StringEncoding];
|
||||
|
||||
if (status == errSecSuccess) {
|
||||
/* item already existed in keychain, let's update */
|
||||
query = [[NSMutableDictionary alloc] init];
|
||||
|
||||
/* just set the secret data */
|
||||
[query setObject:secretData forKey:(__bridge id)kSecValueData];
|
||||
|
||||
status = SecItemUpdate((__bridge CFDictionaryRef)(searchQuery), (__bridge CFDictionaryRef)(query));
|
||||
} else if (status == errSecItemNotFound) {
|
||||
/* item not found, let's create! */
|
||||
query = CreateQuery(p_keystore);
|
||||
|
||||
/* set attributes */
|
||||
SetAttributesForQuery(ppsz_values, query, psz_label);
|
||||
|
||||
/* set accessibility */
|
||||
SetAccessibilityForQuery(p_keystore, query);
|
||||
|
||||
/* set secret data */
|
||||
[query setObject:secretData forKey:(__bridge id)kSecValueData];
|
||||
|
||||
status = SecItemAdd((__bridge CFDictionaryRef)query, NULL);
|
||||
}
|
||||
if (status != errSecSuccess) {
|
||||
msg_Err(p_keystore, "Storage failed (%i: '%s')", status, [ErrorForStatus(status) UTF8String]);
|
||||
return VLC_EGENERIC;
|
||||
}
|
||||
|
||||
return VLC_SUCCESS;
|
||||
}
|
||||
|
||||
static unsigned int Find(vlc_keystore *p_keystore,
|
||||
const char *const ppsz_values[KEY_MAX],
|
||||
vlc_keystore_entry **pp_entries)
|
||||
{
|
||||
CFTypeRef result = NULL;
|
||||
|
||||
NSMutableDictionary *query = CreateQuery(p_keystore);
|
||||
[query setObject:@(YES) forKey:(__bridge id)kSecReturnRef];
|
||||
[query setObject:(__bridge id)kSecMatchLimitAll forKey:(__bridge id)kSecMatchLimit];
|
||||
|
||||
/* set attributes */
|
||||
SetAttributesForQuery(ppsz_values, query, NULL);
|
||||
|
||||
/* search */
|
||||
OSStatus status = SecItemCopyMatching((__bridge CFDictionaryRef)query, &result);
|
||||
|
||||
if (status != errSecSuccess) {
|
||||
msg_Warn(p_keystore, "lookup failed (%i: '%s')", status, [ErrorForStatus(status) UTF8String]);
|
||||
return 0;
|
||||
}
|
||||
|
||||
NSArray *listOfResults = (__bridge_transfer NSArray *)result;
|
||||
|
||||
NSUInteger count = listOfResults.count;
|
||||
|
||||
vlc_keystore_entry *p_entries = calloc(count,
|
||||
sizeof(vlc_keystore_entry));
|
||||
if (!p_entries)
|
||||
return 0;
|
||||
|
||||
for (NSUInteger i = 0; i < count; i++) {
|
||||
vlc_keystore_entry *p_entry = &p_entries[i];
|
||||
if (CopyEntryValues((const char **)p_entry->ppsz_values, (const char *const*)ppsz_values) != VLC_SUCCESS) {
|
||||
vlc_keystore_release_entries(p_entries, 1);
|
||||
return 0;
|
||||
}
|
||||
|
||||
SecKeychainItemRef itemRef = (__bridge SecKeychainItemRef)(listOfResults[i]);
|
||||
|
||||
SecKeychainAttributeInfo attrInfo;
|
||||
|
||||
#ifndef NDEBUG
|
||||
attrInfo.count = 1;
|
||||
UInt32 tags[1] = {kSecAccountItemAttr}; //, kSecAccountItemAttr, kSecServerItemAttr, kSecPortItemAttr, kSecProtocolItemAttr, kSecPathItemAttr};
|
||||
attrInfo.tag = tags;
|
||||
attrInfo.format = NULL;
|
||||
#endif
|
||||
|
||||
SecKeychainAttributeList *attrList = NULL;
|
||||
|
||||
UInt32 dataLength;
|
||||
void * data;
|
||||
|
||||
status = SecKeychainItemCopyAttributesAndData(itemRef, &attrInfo, NULL, &attrList, &dataLength, &data);
|
||||
|
||||
if (status != noErr) {
|
||||
msg_Err(p_keystore, "Lookup error: %i (%s)", status, [ErrorForStatus(status) UTF8String]);
|
||||
vlc_keystore_release_entries(p_entries, count);
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifndef NDEBUG
|
||||
for (unsigned x = 0; x < attrList->count; x++) {
|
||||
SecKeychainAttribute *attr = &attrList->attr[i];
|
||||
switch (attr->tag) {
|
||||
case kSecLabelItemAttr:
|
||||
NSLog(@"label %@", [[NSString alloc] initWithBytes:attr->data length:attr->length encoding:NSUTF8StringEncoding]);
|
||||
break;
|
||||
case kSecAccountItemAttr:
|
||||
NSLog(@"account %@", [[NSString alloc] initWithBytes:attr->data length:attr->length encoding:NSUTF8StringEncoding]);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/* we need to do some padding here, as string is expected to be 0 terminated */
|
||||
uint8_t *retData = calloc(1, dataLength + 1);
|
||||
memcpy(retData, data, dataLength);
|
||||
|
||||
vlc_keystore_entry_set_secret(p_entry, retData, dataLength + 1);
|
||||
|
||||
free(retData);
|
||||
SecKeychainItemFreeAttributesAndData(attrList, data);
|
||||
}
|
||||
|
||||
*pp_entries = p_entries;
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
static unsigned int Remove(vlc_keystore *p_keystore,
|
||||
const char *const ppsz_values[KEY_MAX])
|
||||
{
|
||||
OSStatus status;
|
||||
|
||||
NSMutableDictionary *query = CreateQuery(p_keystore);
|
||||
|
||||
SetAttributesForQuery(ppsz_values, query, NULL);
|
||||
|
||||
CFTypeRef result = NULL;
|
||||
[query setObject:@(YES) forKey:(__bridge id)kSecReturnRef];
|
||||
[query setObject:(__bridge id)kSecMatchLimitAll forKey:(__bridge id)kSecMatchLimit];
|
||||
|
||||
BOOL failed = NO;
|
||||
status = SecItemCopyMatching((__bridge CFDictionaryRef)query, &result);
|
||||
|
||||
NSUInteger matchCount = 0;
|
||||
|
||||
if (status == errSecSuccess) {
|
||||
NSArray *matches = (__bridge_transfer NSArray *)result;
|
||||
matchCount = matches.count;
|
||||
|
||||
for (NSUInteger x = 0; x < matchCount; x++) {
|
||||
status = SecKeychainItemDelete((__bridge SecKeychainItemRef _Nonnull)(matches[x]));
|
||||
if (status != noErr) {
|
||||
msg_Err(p_keystore, "Deletion error %i (%s)", status , [ErrorForStatus(status) UTF8String]);
|
||||
failed = YES;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
msg_Err(p_keystore, "Lookup error for deletion %i (%s)", status, [ErrorForStatus(status) UTF8String]);
|
||||
return VLC_EGENERIC;
|
||||
}
|
||||
|
||||
if (failed)
|
||||
return VLC_EGENERIC;
|
||||
|
||||
return matchCount;
|
||||
}
|
||||
|
||||
static int Open(vlc_object_t *p_this)
|
||||
{
|
||||
vlc_keystore *p_keystore = (vlc_keystore *)p_this;
|
||||
|
||||
p_keystore->p_sys = NULL;
|
||||
p_keystore->pf_store = Store;
|
||||
p_keystore->pf_find = Find;
|
||||
p_keystore->pf_remove = Remove;
|
||||
|
||||
return VLC_SUCCESS;
|
||||
}
|
@ -947,6 +947,7 @@ modules/hw/vdpau/chroma.c
|
||||
modules/hw/vdpau/deinterlace.c
|
||||
modules/hw/vdpau/display.c
|
||||
modules/hw/vdpau/sharpen.c
|
||||
modules/keystore/keychain.m
|
||||
modules/lua/demux.c
|
||||
modules/lua/intf.c
|
||||
modules/lua/libs/configuration.c
|
||||
|
@ -51,7 +51,8 @@ static const struct
|
||||
/* Following keystores are tested only when asked explicitly by the tester
|
||||
* (with "-a" argv) */
|
||||
{ "secret", false },
|
||||
{ "kwallet", false }
|
||||
{ "kwallet", false },
|
||||
{ "keychain", false }
|
||||
};
|
||||
|
||||
static void
|
||||
|
Loading…
Reference in New Issue
Block a user