diff --git a/extensions.py b/extensions.py deleted file mode 100644 index ac505c39..00000000 --- a/extensions.py +++ /dev/null @@ -1,28 +0,0 @@ -""" -build_ext.py - -Created by Kang Zhang on 2009-08-07 -""" - -import sys - -from distutils.core import Extension - - -def get_extensions(): - """Collect the extensions that can be installed. - """ - exts = [] - platform = sys.platform - - if platform in ['darwin', 'mac']: - # Mac OS X, keychain enabled - osx_keychain_module = Extension('osx_keychain', - library_dirs = ['/System/Library/Frameworks/'], - sources = ['keyring/backends/osx_keychain.c'], - extra_link_args = ['-framework', 'Security', - '-framework', 'CoreFoundation', '-framework', - 'CoreServices']) - exts.append(osx_keychain_module) - - return exts diff --git a/keyring/backend.py b/keyring/backend.py index 89af055c..5750e4e0 100644 --- a/keyring/backend.py +++ b/keyring/backend.py @@ -113,7 +113,7 @@ class OSXKeychain(_ExtensionKeyring): def _init_backend(self): """Return the handler: osx_keychain """ - import osx_keychain + from backends import osx_keychain return osx_keychain def _recommend(self): diff --git a/keyring/backends/keyring_util.h b/keyring/backends/keyring_util.h deleted file mode 100644 index 96aacd18..00000000 --- a/keyring/backends/keyring_util.h +++ /dev/null @@ -1,19 +0,0 @@ -/* - * keyring_util.h - * - * Some useful functions for keyring lib - */ -#ifndef KEYRING_UTIL_H -#define KEYRING_UTIL_H -char * -string_dump(const char *s,int n) -{ - char *res; - if (s == NULL) - return NULL; - res = (char*) malloc(n+1); - memcpy(res,s,n); - res[n] = '\0'; - return res; -} -#endif //KEYRING_UTIL_H diff --git a/keyring/backends/osx_keychain.c b/keyring/backends/osx_keychain.c deleted file mode 100644 index f52f997e..00000000 --- a/keyring/backends/osx_keychain.c +++ /dev/null @@ -1,118 +0,0 @@ -#include - -#include "Python.h" -#include "keyring_util.h" - -static PyObject * -keychain_password_set(PyObject *self, PyObject *args) -{ - const char *realmstring; - const char *username; - const char *password; - OSStatus status; - SecKeychainRef keychain; - SecKeychainItemRef item; - - if (!PyArg_ParseTuple(args, "sss", &realmstring, &username, &password)){ - PyErr_Clear(); - PyErr_SetString(PyExc_TypeError, - "password_set() must be called as (servicename,username,password)"); - return NULL; - } - - if (SecKeychainOpen("login.keychain",&keychain) != 0 ){ - PyErr_Clear(); - PyErr_SetString(PyExc_OSError, - "can't access the login.keychain, Authorization failed"); - return NULL; - } - - status = SecKeychainFindGenericPassword(keychain, strlen(realmstring), - realmstring, username == NULL - ? 0 - : strlen(username), - username, 0, NULL, &item); - if (status){ - if (status == errSecItemNotFound) - status = SecKeychainAddGenericPassword(keychain, strlen(realmstring), - realmstring, username == NULL - ? 0 - : strlen(username), - username, strlen(password), - password, NULL); - } - else{ - status = SecKeychainItemModifyAttributesAndData(item, NULL, - strlen(password), - password); - CFRelease(item); - } - - if (status != 0){ // error occurs - PyErr_Clear(); - PyErr_SetString(PyExc_OSError, "Can't store password in Keychain"); - return NULL; - } - - Py_RETURN_NONE; -} - - -static PyObject * -keychain_password_get(PyObject *self, PyObject *args) -{ - const char *realmstring; - const char *username; - char *password; - OSStatus status; - UInt32 length; - SecKeychainRef keychain; - void *data; - - if (!PyArg_ParseTuple(args, "ss", &realmstring, &username)){ - PyErr_Clear(); - PyErr_SetString(PyExc_TypeError, - "password_get() must be called as (servicename,username)"); - return NULL; - } - - if (SecKeychainOpen("login.keychain", &keychain) != 0 ){ - PyErr_Clear(); - PyErr_SetString(PyExc_OSError, - "can't access the login.keychain, Authorization failed"); - return NULL; - } - - status = SecKeychainFindGenericPassword(keychain, strlen(realmstring), - realmstring, username == NULL - ? 0 - : strlen(username), - username, &length, &data, NULL); - - if (status == 0){ - password = string_dump(data, length); - SecKeychainItemFreeContent(NULL, data); - }else if (status == errSecItemNotFound){ - password = NULL; - } - else{ // error occurs - PyErr_Clear(); - PyErr_SetString(PyExc_OSError, "Can't fetch password from system"); - return NULL; - } - - return Py_BuildValue("s",password); -} - -static struct PyMethodDef keychain_methods[] ={ - {"password_set", keychain_password_set, METH_VARARGS}, - {"password_get", keychain_password_get, METH_VARARGS}, - {NULL,NULL} /* Sentinel */ -}; - -PyMODINIT_FUNC -initosx_keychain(void) -{ - Py_InitModule("osx_keychain", keychain_methods); -} - diff --git a/keyring/backends/osx_keychain.py b/keyring/backends/osx_keychain.py new file mode 100644 index 00000000..06d2ef82 --- /dev/null +++ b/keyring/backends/osx_keychain.py @@ -0,0 +1,136 @@ +#!/usr/bin/python + +import sys +if sys.platform != 'darwin': + raise ImportError('Mac OS X only module') + +from ctypes import CDLL, CFUNCTYPE, c_void_p, c_uint32, \ + c_int32, c_char_p, byref, POINTER, memmove, create_string_buffer + +# Types + +SecKeychainRef = c_void_p +SecKeychainItemRef = c_void_p +OSStatus = c_int32 + +# Constants + +errSecSuccess = 0 +errSecUnimplemented = -4 +errSecParam = -50 +errSecAllocate = -108 + +errSecNotAvailable = -25291 +errSecReadOnly = -25292 +errSecAuthFailed = -25293 +errSecNoSuchKeychain = -25294 +errSecInvalidKeychain = -25295 +errSecDuplicateKeychain = -25296 +errSecDuplicateCallback = -25297 +errSecInvalidCallback = -25298 +errSecDuplicateItem = -25299 +errSecItemNotFound = -25300 +errSecBufferTooSmall = -25301 +errSecDataTooLarge = -25302 +errSecNoSuchAttr = -25303 +errSecInvalidItemRef = -25304 +errSecInvalidSearchRef = -25305 +errSecNoSuchClass = -25306 +errSecNoDefaultKeychain = -25307 +errSecInteractionNotAllowed = -25308 +errSecReadOnlyAttr = -25309 +errSecWrongSecVersion = -25310 +errSecKeySizeNotAllowed = -25311 +errSecNoStorageModule = -25312 +errSecNoCertificateModule = -25313 +errSecNoPolicyModule = -25314 +errSecInteractionRequired = -25315 +errSecDataNotAvailable = -25316 +errSecDataNotModifiable = -25317 +errSecCreateChainFailed = -25318 +errSecInvalidPrefsDomain = -25319 + +errSecACLNotSimple = -25240 +errSecPolicyNotFound = -25241 +errSecInvalidTrustSetting = -25242 +errSecNoAccessForItem = -25243 +errSecInvalidOwnerEdit = -25244 +errSecTrustNotAvailable = -25245 +errSecUnsupportedFormat = -25256 +errSecUnknownFormat = -25257 +errSecKeyIsSensitive = -25258 +errSecMultiplePrivKeys = -25259 +errSecPassphraseRequired = -25260 +errSecInvalidPasswordRef = -25261 +errSecInvalidTrustSettings = -25262 +errSecNoTrustSettings = -25263 +errSecPkcs12VerifyFailure = -25264 + +errSecDecode = -26275 + +# Functions + +_dll = CDLL('/System/Library/Frameworks/Security.framework/Versions/A/Security') +_core = CDLL('/System/Library/Frameworks/CoreServices.framework/Versions/A/CoreServices') + +SecKeychainOpen = CFUNCTYPE(OSStatus, c_char_p, POINTER(SecKeychainRef))(('SecKeychainOpen', _dll)) +SecKeychainFindGenericPassword = CFUNCTYPE(OSStatus, SecKeychainRef, c_uint32, + c_char_p, c_uint32, c_char_p, + POINTER(c_uint32), POINTER(c_void_p), + POINTER(SecKeychainItemRef))(('SecKeychainFindGenericPassword', _dll)) +SecKeychainAddGenericPassword = CFUNCTYPE(OSStatus, SecKeychainRef, c_uint32, c_char_p, + c_uint32, c_char_p, c_uint32, + c_char_p, POINTER(SecKeychainItemRef))(('SecKeychainAddGenericPassword', _dll)) +SecKeychainItemModifyAttributesAndData = CFUNCTYPE(OSStatus, SecKeychainItemRef, c_void_p, c_uint32, c_void_p)(('SecKeychainItemModifyAttributesAndData', _dll)) +SecKeychainItemFreeContent = CFUNCTYPE(OSStatus, c_void_p, c_void_p)(('SecKeychainItemFreeContent', _dll)) + +def password_set(realmstring, username, password): + if username is None: + username = '' + + keychain = SecKeychainRef() + if SecKeychainOpen('login.keychain', byref(keychain)): + raise OSError("Can't access the login keychain") + + try: + item = SecKeychainItemRef() + status = SecKeychainFindGenericPassword(keychain, len(realmstring), realmstring, len(username), username, None, None, byref(item)) + if status: + if status == errSecItemNotFound: + status = SecKeychainAddGenericPassword(keychain, len(realmstring), realmstring, len(username), username, len(password), password, None) + else: + status = SecKeychainItemModifyAttributesAndData(item, None, len(password), password) + _core.CFRelease(item) + + if status: + raise OSError("Can't store password in keychain") + finally: + _core.CFRelease(keychain) + +def password_get(realmstring, username): + if username is None: + username = '' + + keychain = SecKeychainRef() + if SecKeychainOpen('login.keychain', byref(keychain)): + raise OSError("Can't access the login keychain") + + try: + length = c_uint32() + data = c_void_p() + status = SecKeychainFindGenericPassword(keychain, len(realmstring), realmstring, + len(username), username, + byref(length), byref(data), None) + if status == 0: + password = create_string_buffer(length.value) + memmove(password, data.value, length.value) + password = password.raw + SecKeychainItemFreeContent(None, data) + elif status == errSecItemNotFound: + password = None + else: + raise OSError("Can't fetch password from system") + return password + finally: + _core.CFRelease(keychain) + diff --git a/setup.py b/setup.py index 1336e5c9..d75df661 100644 --- a/setup.py +++ b/setup.py @@ -10,8 +10,6 @@ from distutils.core import setup, Extension from distutils.version import StrictVersion -from extensions import get_extensions - def runcmd(cmd, env): p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, env=env) @@ -39,6 +37,5 @@ def runcmd(cmd, env): platforms = ["Many"], packages = ['keyring', 'keyring.tests', 'keyring.util', 'keyring.backends'], - ext_modules = get_extensions() )