jaraco / jaraco/keyring

MacOS "Internet password" keychain items

Open
#624 5 comments 1 reaction 0 assignees View on GitHub
enhancement help wanted macOS
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

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.