// GetAllAccountNames returns a list of all account names for the // given service name in the default keychain. func GetAllAccountNames(serviceName string) (accountNames []string, err error) { var serviceNameString C.CFStringRef if serviceNameString, err = _UTF8StringToCFString(serviceName); err != nil { return } defer C.CFRelease(C.CFTypeRef(serviceNameString)) query := map[C.CFTypeRef]C.CFTypeRef{ secClass: secClassGenericPassword, secAttrService: C.CFTypeRef(serviceNameString), secMatchLimit: secMatchLimitAll, secReturnAttributes: C.CFTypeRef(C.kCFBooleanTrue), } queryDict := mapToCFDictionary(query) defer C.CFRelease(C.CFTypeRef(queryDict)) var resultsRef C.CFTypeRef errCode := C.SecItemCopyMatching(queryDict, &resultsRef) err = newKeychainError(errCode) if err == ErrItemNotFound { return []string{}, nil } else if err != nil { return nil, err } defer C.CFRelease(resultsRef) // The resultsRef should always be an array (because kSecReturnAttributes is true) // but it's a good sanity check and useful if want to support kSecReturnRef in the future. typeID := C.CFGetTypeID(resultsRef) if typeID != C.CFArrayGetTypeID() { typeDesc := C.CFCopyTypeIDDescription(typeID) defer C.CFRelease(C.CFTypeRef(typeDesc)) err = fmt.Errorf("Invalid result type: %s", _CFStringToUTF8String(typeDesc)) return } results := _CFArrayToArray(C.CFArrayRef(resultsRef)) for _, result := range results { m := _CFDictionaryToMap(C.CFDictionaryRef(result)) resultServiceName := _CFStringToUTF8String(C.CFStringRef(m[secAttrService])) if resultServiceName != serviceName { err = fmt.Errorf("Expected service name %s, got %s", serviceName, resultServiceName) return } accountName := _CFStringToUTF8String(C.CFStringRef(m[secAttrAccount])) accountNames = append(accountNames, accountName) } return }
// QueryItem returns a list of query results. func QueryItem(item Item) ([]QueryResult, error) { cfDict, err := ConvertMapToCFDictionary(item.attr) if err != nil { return nil, err } defer Release(C.CFTypeRef(cfDict)) var resultsRef C.CFTypeRef errCode := C.SecItemCopyMatching(cfDict, &resultsRef) if Error(errCode) == ErrorItemNotFound { return nil, nil } err = checkError(errCode) if err != nil { return nil, err } defer Release(resultsRef) results := make([]QueryResult, 0, 1) typeID := C.CFGetTypeID(resultsRef) if typeID == C.CFArrayGetTypeID() { arr := CFArrayToArray(C.CFArrayRef(resultsRef)) for _, dictRef := range arr { item, err := convertResult(C.CFDictionaryRef(dictRef)) if err != nil { return nil, err } results = append(results, *item) } } else if typeID == C.CFDictionaryGetTypeID() { item, err := convertResult(C.CFDictionaryRef(resultsRef)) if err != nil { return nil, err } results = append(results, *item) } else if typeID == C.CFDataGetTypeID() { b, err := CFDataToBytes(C.CFDataRef(resultsRef)) if err != nil { return nil, err } item := QueryResult{Data: b} results = append(results, item) } else { return nil, fmt.Errorf("Invalid result type: %s", CFTypeDescription(resultsRef)) } return results, nil }
// QueryItemRef returns query result as CFTypeRef. You must release it when you are done. func QueryItemRef(item Item) (C.CFTypeRef, error) { cfDict, err := ConvertMapToCFDictionary(item.attr) if err != nil { return nil, err } defer Release(C.CFTypeRef(cfDict)) var resultsRef C.CFTypeRef errCode := C.SecItemCopyMatching(cfDict, &resultsRef) if Error(errCode) == ErrorItemNotFound { return nil, nil } err = checkError(errCode) if err != nil { return nil, err } return resultsRef, nil }
func (k *keychain) Keys() ([]string, error) { serviceRef, err := _UTF8StringToCFString(k.Service) if err != nil { return nil, err } defer C.CFRelease(C.CFTypeRef(serviceRef)) query := map[C.CFTypeRef]C.CFTypeRef{ C.CFTypeRef(C.kSecClass): C.CFTypeRef(C.kSecClassGenericPassword), C.CFTypeRef(C.kSecAttrService): C.CFTypeRef(serviceRef), C.CFTypeRef(C.kSecMatchLimit): C.CFTypeRef(C.kSecMatchLimitAll), C.CFTypeRef(C.kSecReturnAttributes): C.CFTypeRef(C.kCFBooleanTrue), } kref, err := openKeychain(k.Path) if err != nil { return nil, err } searchArray := arrayToCFArray([]C.CFTypeRef{C.CFTypeRef(kref)}) defer C.CFRelease(C.CFTypeRef(searchArray)) query[C.CFTypeRef(C.kSecMatchSearchList)] = C.CFTypeRef(searchArray) queryDict := mapToCFDictionary(query) defer C.CFRelease(C.CFTypeRef(queryDict)) var resultsRef C.CFTypeRef if err = newKeychainError(C.SecItemCopyMatching(queryDict, &resultsRef)); err == errItemNotFound { return nil, nil } else if err != nil { return nil, err } defer C.CFRelease(resultsRef) var accountNames = []string{} for _, result := range _CFArrayToArray(C.CFArrayRef(resultsRef)) { m := _CFDictionaryToMap(C.CFDictionaryRef(result)) accountName := _CFStringToUTF8String(C.CFStringRef(m[C.CFTypeRef(C.kSecAttrAccount)])) accountNames = append(accountNames, accountName) } return accountNames, nil }
func (k *keychain) Get(key string) (Item, error) { if _, err := os.Stat(k.Path); os.IsNotExist(err) { return Item{}, ErrKeyNotFound } serviceRef, err := _UTF8StringToCFString(k.Service) if err != nil { return Item{}, err } defer C.CFRelease(C.CFTypeRef(serviceRef)) accountRef, err := _UTF8StringToCFString(key) if err != nil { return Item{}, err } defer C.CFRelease(C.CFTypeRef(serviceRef)) query := map[C.CFTypeRef]C.CFTypeRef{ C.CFTypeRef(C.kSecClass): C.CFTypeRef(C.kSecClassGenericPassword), C.CFTypeRef(C.kSecAttrService): C.CFTypeRef(serviceRef), C.CFTypeRef(C.kSecAttrAccount): C.CFTypeRef(accountRef), C.CFTypeRef(C.kSecMatchLimit): C.CFTypeRef(C.kSecMatchLimitOne), C.CFTypeRef(C.kSecReturnAttributes): C.CFTypeRef(C.kCFBooleanTrue), C.CFTypeRef(C.kSecReturnData): C.CFTypeRef(C.kCFBooleanTrue), } kref, err := openKeychain(k.Path) if err != nil { return Item{}, err } searchArray := arrayToCFArray([]C.CFTypeRef{C.CFTypeRef(kref)}) defer C.CFRelease(C.CFTypeRef(searchArray)) query[C.CFTypeRef(C.kSecMatchSearchList)] = C.CFTypeRef(searchArray) queryDict := mapToCFDictionary(query) defer C.CFRelease(C.CFTypeRef(queryDict)) var resultsRef C.CFTypeRef if err = newKeychainError(C.SecItemCopyMatching(queryDict, &resultsRef)); err == errItemNotFound { return Item{}, ErrKeyNotFound } else if err != nil { return Item{}, err } defer C.CFRelease(resultsRef) m := _CFDictionaryToMap(C.CFDictionaryRef(resultsRef)) data := C.CFDataRef(m[C.CFTypeRef(C.kSecValueData)]) dataLen := C.int(C.CFDataGetLength(data)) cdata := C.CFDataGetBytePtr(data) item := Item{ Key: key, Data: C.GoBytes(unsafe.Pointer(cdata), dataLen), } if label, exists := m[C.CFTypeRef(C.kSecAttrLabel)]; exists { item.Label = _CFStringToUTF8String(C.CFStringRef(label)) } if descr, exists := m[C.CFTypeRef(C.kSecAttrDescription)]; exists { item.Description = _CFStringToUTF8String(C.CFStringRef(descr)) } return item, nil }
// FindIdentity ... // IMPORTANT: you have to C.CFRelease the returned items (one-by-one)!! // you can use the ReleaseIdentityWithRefList method to do that func FindIdentity(identityLabel string, isFullLabelMatch bool) ([]IdentityWithRefModel, error) { queryDict := C.CFDictionaryCreateMutable(nil, 0, nil, nil) defer C.CFRelease(C.CFTypeRef(queryDict)) C.CFDictionaryAddValue(queryDict, unsafe.Pointer(C.kSecClass), unsafe.Pointer(C.kSecClassIdentity)) C.CFDictionaryAddValue(queryDict, unsafe.Pointer(C.kSecMatchLimit), unsafe.Pointer(C.kSecMatchLimitAll)) C.CFDictionaryAddValue(queryDict, unsafe.Pointer(C.kSecReturnAttributes), unsafe.Pointer(C.kCFBooleanTrue)) C.CFDictionaryAddValue(queryDict, unsafe.Pointer(C.kSecReturnRef), unsafe.Pointer(C.kCFBooleanTrue)) var resultRefs C.CFTypeRef osStatusCode := C.SecItemCopyMatching(queryDict, &resultRefs) if osStatusCode != C.errSecSuccess { return nil, fmt.Errorf("Failed to call SecItemCopyMatch - OSStatus: %d", osStatusCode) } defer C.CFRelease(C.CFTypeRef(resultRefs)) identitiesArrRef := C.CFArrayRef(resultRefs) identitiesCount := C.CFArrayGetCount(identitiesArrRef) if identitiesCount < 1 { return nil, fmt.Errorf("No Identity (certificate + related private key) found in your Keychain!") } log.Debugf("identitiesCount: %d", identitiesCount) // filter the identities, by label retIdentityRefs := []IdentityWithRefModel{} for i := C.CFIndex(0); i < identitiesCount; i++ { aIdentityRef := C.CFArrayGetValueAtIndex(identitiesArrRef, i) log.Debugf("aIdentityRef: %#v", aIdentityRef) aIdentityDictRef := C.CFDictionaryRef(aIdentityRef) log.Debugf("aIdentityDictRef: %#v", aIdentityDictRef) lablCSting := C.CString("labl") defer C.free(unsafe.Pointer(lablCSting)) vrefCSting := C.CString("v_Ref") defer C.free(unsafe.Pointer(vrefCSting)) labl, err := getCFDictValueUTF8String(aIdentityDictRef, C.CFTypeRef(convertCStringToCFString(lablCSting))) if err != nil { return nil, fmt.Errorf("FindIdentity: failed to get 'labl' property: %s", err) } log.Debugf("labl: %#v", labl) if isFullLabelMatch { if labl != identityLabel { continue } } else { if !strings.Contains(labl, identityLabel) { continue } } log.Debugf("Found identity with label: %s", labl) vrefRef, err := getCFDictValueRef(aIdentityDictRef, C.CFTypeRef(convertCStringToCFString(vrefCSting))) if err != nil { return nil, fmt.Errorf("FindIdentity: failed to get 'v_Ref' property: %s", err) } log.Debugf("vrefRef: %#v", vrefRef) // retain the pointer vrefRef = C.CFRetain(vrefRef) // store it retIdentityRefs = append(retIdentityRefs, IdentityWithRefModel{ KeychainRef: vrefRef, Label: labl, }) } return retIdentityRefs, nil }