Ejemplo n.º 1
0
// 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
}
Ejemplo n.º 2
0
// 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
}
Ejemplo n.º 3
0
// 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
}
Ejemplo n.º 4
0
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
}
Ejemplo n.º 5
0
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
}
Ejemplo n.º 6
0
// 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
}