MacOS "Internet password" keychain items
- Dominant language
- Python
- Stars
- 1.5k
- Forks
- 196
- PR merge metrics
- No merged PRs in 30d
Description
I was hoping to use keyring to retrieve existing internet passwords on the MacOS keychain for things like ssh server passwords, etc. It looks like keyring deals with "application password" items ("kSecClassGenericPassword"), and can't retrieve "Internet password" items ("kSecClassInternetPassword"). I came up with a variation on your routine, and while I was at it, thought I'd try and retrieve the attributes on the item as well, by setting "kSecReturnAttributes" in the query as well.
It seemed to work - I apparently get the promised CFDictionary back, and I cobbled together a CFDictionaryGetValue() routine from other sources. I can get the item data value (the password) from the dictionary entry "kSecValueData", and convert it as you do with cfstr_to_str(). But while I can successfully retrieve other keys like "kSecAttrServer", which should be the name of the server and also a CFString, cfstr_to_str() crashes with "[__NSCFString bytes]: unrecognized selector sent to instance", so it doesn't seem to be a CFString as expected, or something?
I don't really know the MacOS APIs (at least since MacOS 9 or so), and I may be missing something obvious between that and the whole coercing into Python, so I thought I'd check if anyone here might have an idea.
Also, while the keyring API doesn't seem to be set up to handle multiple flavours of passwords like this, would there be any interest in having the macOS.api have superset functions that can handle some of the other types as a convenience for MacOS folks?
```Python
from keyring.backends.macOS import api
def find_internet_password(server, username, protocol="ssh ", not_found_ok=False):
# https://developer.apple.com/documentation/security/keychain_services/keychain_items/searching_for_keychain_items?language=objc
q = api.create_query(
kSecClass=api.k_('kSecClassInternetPassword'),
kSecMatchLimit=api.k_('kSecMatchLimitOne'),
kSecAttrServer=server,
kSecAttrProtocol=protocol,
kSecAttrAccount=username,
kSecReturnAttributes=api.create_cfbool(True),
kSecReturnData=api.create_cfbool(True),
)
data = api.c_void_p()
status = api.SecItemCopyMatching(q, api.byref(data))
if status == api.error.item_not_found and not_found_ok:
return
api.Error.raise_for_status(status)
password = CFDictionaryGetValue(data, api.k_("kSecValueData")) # should be a CFString
password = api.cfstr_to_str(password) # and the conversion works
if True: # now try the attributes
retServer = CFDictionaryGetValue(data, api.k_("kSecAttrServer"))
retServer = api.cfstr_to_str(retServer) # should also be a CFString? but crashes
account = CFDictionaryGetValue(data, api.k_("kSecAttrAccount"))
account = api.cfstr_to_str(account) # should also be a CFString? but crashes
lastmod = CFDictionaryGetValue(data, api.k_("kSecAttrModificationDate"))
#lastmodd = api.ctypes.cast(api.CFDataGetBytePtr(lastmod), ctypes.c_double)
#lastmod = CFDateGetAbsoluteTime(lastmod)
return password
password = find_internet_password("myserver.mydomain.org", "myusername")
```
Contributor guide
No contributing guide indexed for this repository
Research direction
Start by reading keyring.backends.macOS.api and the Apple Security keychain query documentation, focusing on CFDictionary return types for internet-password items. Clarify whether the goal is CF type handling, internet-password retrieval, or broader API support; done requires an agreed API scope and verified behavior for the requested item attributes.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- macos, python
- Domain
- backend-api-design, security
- Issue type
- Feature
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Stale
- Clarity
- Needs clarification
- Newbie friendliness
- 25/100