[CalendarServer-changes] [4827] PyOpenDirectory/trunk
source_changes at macosforge.org
source_changes at macosforge.org
Sat Dec 5 22:18:31 PST 2009
Revision: 4827
http://trac.macosforge.org/projects/calendarserver/changeset/4827
Author: glyph at apple.com
Date: 2009-12-05 22:18:28 -0800 (Sat, 05 Dec 2009)
Log Message:
-----------
Merge of /branches/users/gaya/addigestauth
Modified Paths:
--------------
PyOpenDirectory/trunk/pysrc/opendirectory.py
PyOpenDirectory/trunk/src/CDirectoryServiceAuth.cpp
PyOpenDirectory/trunk/src/CDirectoryServiceAuth.h
PyOpenDirectory/trunk/src/PythonWrapper.cpp
PyOpenDirectory/trunk/support/PyOpenDirectory.xcodeproj/project.pbxproj
PyOpenDirectory/trunk/support/test.cpp
PyOpenDirectory/trunk/test_auth.py
Property Changed:
----------------
PyOpenDirectory/trunk/
Property changes on: PyOpenDirectory/trunk
___________________________________________________________________
Modified: svn:mergeinfo
- /PyOpenDirectory/branches/users/cdaboo/datatypes-3001:3002-3058
+ /PyOpenDirectory/branches/users/cdaboo/datatypes-3001:3002-3058
/PyOpenDirectory/branches/users/gaya/addigestauth:4592-4826
Modified: PyOpenDirectory/trunk/pysrc/opendirectory.py
===================================================================
--- PyOpenDirectory/trunk/pysrc/opendirectory.py 2009-12-04 23:36:35 UTC (rev 4826)
+++ PyOpenDirectory/trunk/pysrc/opendirectory.py 2009-12-06 06:18:28 UTC (rev 4827)
@@ -71,7 +71,7 @@
@param attr: C{str} containing the attribute to search.
@param value: C{str} containing the value to search for.
@param matchType: C{int} DS match type to use when searching.
- @param casei: C{True} to do case-insenstive match, C{False} otherwise.
+ @param casei: C{True} to do case-insensitive match, C{False} otherwise.
@param recordType: C{str}, C{tuple} or C{list} containing the OD record types to lookup.
@param attributes: C{list} or C{tuple} containing the attributes to return for each record.
@param count: C{int} maximum number of records to return (zero returns all).
@@ -87,7 +87,7 @@
@param obj: C{object} the object obtained from an odInit call.
@param compound: C{str} containing the compound search query to use.
- @param casei: C{True} to do case-insenstive match, C{False} otherwise.
+ @param casei: C{True} to do case-insensitive match, C{False} otherwise.
@param recordType: C{str}, C{tuple} or C{list} containing the OD record types to lookup.
@param attributes: C{list} or C{tuple} containing the attributes to return for each record.
@param count: C{int} maximum number of records to return (zero returns all).
@@ -119,7 +119,7 @@
@param attr: C{str} containing the attribute to search.
@param value: C{str} containing the value to search for.
@param matchType: C{int} DS match type to use when searching.
- @param casei: C{True} to do case-insenstive match, C{False} otherwise.
+ @param casei: C{True} to do case-insensitive match, C{False} otherwise.
@param recordType: C{str}, C{tuple} or C{list} containing the OD record types to lookup.
@param attributes: C{list} or C{tuple} containing the attributes to return for each record.
@param count: C{int} maximum number of records to return (zero returns all).
@@ -135,7 +135,7 @@
@param obj: C{object} the object obtained from an odInit call.
@param compound: C{str} containing the compound search query to use.
- @param casei: C{True} to do case-insenstive match, C{False} otherwise.
+ @param casei: C{True} to do case-insensitive match, C{False} otherwise.
@param recordType: C{str}, C{tuple} or C{list} containing the OD record types to lookup.
@param attributes: C{list} or C{tuple} containing the attributes to return for each record.
@param count: C{int} maximum number of records to return (zero returns all).
@@ -166,7 +166,27 @@
@param method: C{str} the HTTP method being used.
@return: C{True} if the user was found, C{False} otherwise.
"""
+def authenticateUserDigestToActiveDirectory(obj, nodename, user, response):
+ """
+ Authenticate using HTTP Digest credentials to an Active Directory node,
+ exported by Open Diretory
+
+ @param obj: C{object} the object obtained from an odInit call.
+ @param nodename: C{str} the directory nodename for the record to check.
+ @param user: C{str} the user identifier/directory record name to check.
+ @param response: C{str} the HTTP response sent from the client.
+ @return: C{True} if the user was found, C{False} otherwise.
+ """
+def getDigestMD5ChallengeFromActiveDirectory(obj, nodename):
+ """
+ Get an DigestMD5 challenge from Active Directory
+
+ @param obj: C{object} the object obtained from an odInit call.
+ @param nodename: C{str} the directory nodename for the record to check.
+ @return: C{string} containing the challenge
+ """
+
class ODError(Exception):
"""
Exceptions from DirectoryServices errors.
Modified: PyOpenDirectory/trunk/src/CDirectoryServiceAuth.cpp
===================================================================
--- PyOpenDirectory/trunk/src/CDirectoryServiceAuth.cpp 2009-12-04 23:36:35 UTC (rev 4826)
+++ PyOpenDirectory/trunk/src/CDirectoryServiceAuth.cpp 2009-12-06 06:18:28 UTC (rev 4827)
@@ -21,6 +21,11 @@
#include "CDirectoryServiceException.h"
+#ifndef kDSStdAuthSASLProxy
+#define kDSStdAuthSASLProxy "dsAuthMethodStandard:dsAuthSASLProxy"
+#endif
+#define kSASLDIGESTMD5 "DIGEST-MD5"
+
#pragma mark -----Public API
CDirectoryServiceAuth::CDirectoryServiceAuth() :
@@ -39,7 +44,7 @@
// Authenticate a user to the directory using plain text credentials.
//
// @param nodename: the directory nodename for the user record.
-// @param user: the uid of the user.
+// @param user: the identifier/directory record name of the user.
// @param pswd: the plain text password to authenticate with.
// @return: true if authentication succeeds, false otherwise.
//
@@ -72,6 +77,7 @@
// Authenticate a user to the directory using HTTP DIGEST credentials.
//
// @param nodename: the directory nodename for the user record.
+// @param user: the identifier/directory record name of the user.
// @param challenge: HTTP challenge sent by server.
// @param response: HTTP response sent by client.
// @return: true if authentication succeeds, false otherwise.
@@ -100,6 +106,72 @@
}
}
+// AuthenticateUserDigestToActiveDirectory
+//
+// Authenticate a user to the directory using SASL Digest credentials.
+//
+// @param nodename: the directory nodename for the user record.
+// @param user: the identifier/directory record name of the user.
+// @param response: HTTP response sent by client.
+// @return: true if authentication succeeds, false otherwise.
+//
+bool CDirectoryServiceAuth::AuthenticateUserDigestToActiveDirectory(const char* nodename, const char* user, const char* response, bool& result, bool using_python)
+{
+ try
+ {
+ StPythonThreadState threading(using_python);
+
+ result = NativeAuthenticationSASLDigestToNode(nodename, user, response);
+ return true;
+ }
+ catch(CDirectoryServiceException& dserror)
+ {
+ if (using_python)
+ dserror.SetPythonException();
+ return false;
+ }
+ catch(...)
+ {
+ CDirectoryServiceException dserror;
+ if (using_python)
+ dserror.SetPythonException();
+ return false;
+ }
+}
+
+
+// GetDigestMD5ChallengeFromActiveDirectory
+//
+// Authenticate a user to the directory using SASL Digest credentials.
+//
+// @param nodename: the directory nodename for the user record.
+// @return: challange as CFStringRef
+//
+CFStringRef CDirectoryServiceAuth::GetDigestMD5ChallengeFromActiveDirectory(const char* nodename, bool using_python)
+{
+ try
+ {
+ StPythonThreadState threading(using_python);
+ CFStringRef challenge = NULL;
+ (void) NativeAuthenticationSASLDigestToNode(nodename, "anonymous", "", &challenge);
+ return challenge;
+ }
+ catch(CDirectoryServiceException& dserror)
+ {
+ if (using_python)
+ dserror.SetPythonException();
+ return NULL;
+ }
+ catch(...)
+ {
+ CDirectoryServiceException dserror;
+ if (using_python)
+ dserror.SetPythonException();
+ return NULL;
+ }
+}
+
+
#pragma mark -----Private API
// NativeAuthenticationBasicToNode
@@ -107,7 +179,7 @@
// Authenticate a user to the directory.
//
// @param nodename: the node to authenticate to.
-// @param user: the uid of the user.
+// @param user: the identifier/directory record name of the user.
// @param pswd: the plain text password to authenticate with.
// @return: true if authentication succeeds, false otherwise.
//
@@ -186,7 +258,7 @@
// Authenticate a user to the directory.
//
// @param nodename: the node to authenticate to.
-// @param user: the uid of the user.
+// @param user: the identifier/directory record name of the user.
// @param challenge: the server challenge.
// @param response: the client response.
// @param method: the HTTP method.
@@ -231,7 +303,7 @@
ThrowIfDSErr(eDSNullDataBuff);
// Fill the buffer
- ::dsFillAuthBuffer(authData, 4,
+ ::dsFillAuthBuffer(authData, ::strlen(method)?4:3,
::strlen(user), user,
::strlen(challenge), challenge,
::strlen(response), response,
@@ -270,6 +342,147 @@
return result;
}
+// NativeAuthenticationSASLDigestToNode
+//
+// Authenticate a user to the directory.
+//
+// @param nodename: the node to authenticate to.
+// @param user: the identifier/directory record name of the user.
+// @param sasldata: the client response.
+// @param saslResult: returned step data.
+// @return: true if authentication succeeds, false otherwise.
+//
+bool CDirectoryServiceAuth::NativeAuthenticationSASLDigestToNode(const char* nodename, const char* user, const char* sasldata, CFStringRef* saslResult)
+{
+ bool result = false;
+ tDirNodeReference node = 0L;
+ tDataNodePtr authType = NULL;
+ tDataBufferPtr authData = NULL;
+ tContextData context = NULL;
+
+ try
+ {
+ // Make sure we have a valid directory service
+ OpenService();
+
+ // Open the node we want to query
+ node = OpenNamedNode(nodename);
+
+ CreateBuffer();
+
+ // First, specify the type of authentication.
+ authType = ::dsDataNodeAllocateString(mDir, kDSStdAuthSASLProxy);
+
+ // Build input data
+ // Native authentication is a one step authentication scheme.
+ // Step 1
+ // Send: <length><user>
+ // <length><saslmechansim>
+ // <length><sasldata>
+ // Receive: success or failure and
+ // step data:
+ // <length><saslresultdata>
+ UInt32 aDataBufSize = sizeof(UInt32) + ::strlen(user) +
+ sizeof(UInt32) + ::strlen(kSASLDIGESTMD5) +
+ sizeof(UInt32) + ::strlen(sasldata);
+ authData = ::dsDataBufferAllocate(mDir, aDataBufSize);
+ if (authData == NULL)
+ ThrowIfDSErr(eDSNullDataBuff);
+
+ // Fill the buffer
+ ::dsFillAuthBuffer(authData, 3,
+ ::strlen(user), user,
+ ::strlen(kSASLDIGESTMD5), kSASLDIGESTMD5,
+ ::strlen(sasldata), sasldata);
+
+ // Do authentication
+ tDirStatus dirStatus = ::dsDoDirNodeAuth(node, authType, true, authData, mData, &context);
+ result = (dirStatus == eDSNoErr);
+
+ if (result && (NULL != saslResult))
+ {
+ // get first step data in string (always a string for kSASLDIGESTMD5)
+ *saslResult = CFStringCreateWithBytes(kCFAllocatorDefault, (UInt8*)&mData->fBufferData[4], *(UInt32*)&mData->fBufferData[0],
+ kCFStringEncodingUTF8, false);
+ }
+
+ // Cleanup
+ ::dsDataBufferDeAllocate(mDir, authData);
+ authData = NULL;
+ ::dsDataNodeDeAllocate(mDir, authType);
+ authType = NULL;
+ RemoveBuffer();
+
+ // If fatal error, force full reset
+ if (not result and (dirStatus != eDSAuthFailed))
+ {
+ CloseService();
+ }
+ }
+ catch(...)
+ {
+ // Cleanup
+ if (authData != NULL)
+ ::dsDataBufferDeAllocate(mDir, authData);
+ if (authType != NULL)
+ ::dsDataNodeDeAllocate(mDir, authType);
+ RemoveBuffer();
+ CloseService();
+
+ throw;
+ }
+
+ return result;
+}
+
+/*
+// NativeGenerateClientResponse
+//
+// Authenticate a user to the directory.
+//
+// @param nodename: the node to authenticate to.
+// @param user: the identifier/directory record name of the user.
+// @param sasldata: the client response.
+// @param saslResult: returned step data.
+// @return: true if authentication succeeds, false otherwise.
+//
+bool CDirectoryServiceAuth::NativeGenerateClientResponse(const char* serverChallenge, CFStringRef* response)
+{
+ bool result = false;
+ tDirNodeReference node = 0L;
+ tDataNodePtr authType = NULL;
+ tDataBufferPtr authData = NULL;
+ tContextData context = NULL;
+
+ try
+ {
+ sasl_conn = ::SASLClientNewContext( callbacks, &sasl_context );
+ // Client hashes the digest and responds
+ result = ::sasl_client_step(sasl_conn, (char *)[serverChal bytes], [serverChal length], NULL, &data, &len);
+ if (result != SASL_CONTINUE) {
+ printf("sasl_client_step = %d\n", result);
+ goto done;
+ }
+
+ }
+ catch(...)
+ {
+ // Cleanup
+ if (authData != NULL)
+ ::dsDataBufferDeAllocate(mDir, authData);
+ if (authType != NULL)
+ ::dsDataNodeDeAllocate(mDir, authType);
+ RemoveBuffer();
+ CloseService();
+
+ throw;
+ }
+
+ return result;
+}
+*/
+
+
// CloseService
//
// Close the directory service if previously open.
Modified: PyOpenDirectory/trunk/src/CDirectoryServiceAuth.h
===================================================================
--- PyOpenDirectory/trunk/src/CDirectoryServiceAuth.h 2009-12-04 23:36:35 UTC (rev 4826)
+++ PyOpenDirectory/trunk/src/CDirectoryServiceAuth.h 2009-12-06 06:18:28 UTC (rev 4827)
@@ -32,6 +32,9 @@
bool AuthenticateUserBasic(const char* nodename, const char* user, const char* pswd, bool& result, bool using_python=true);
bool AuthenticateUserDigest(const char* nodename, const char* user, const char* challenge, const char* response, const char* method, bool& result, bool using_python=true);
+ bool AuthenticateUserDigestToActiveDirectory(const char* nodename, const char* user, const char* response, bool& result, bool using_python=true);
+
+ CFStringRef GetDigestMD5ChallengeFromActiveDirectory(const char* nodename, bool using_python=true);
protected:
@@ -40,6 +43,7 @@
bool NativeAuthenticationBasicToNode(const char* nodename, const char* user, const char* pswd);
bool NativeAuthenticationDigestToNode(const char* nodename, const char* user, const char* challenge, const char* response, const char* method);
+ bool NativeAuthenticationSASLDigestToNode(const char* nodename, const char* user, const char* sasldata, CFStringRef* saslResult = NULL);
virtual void CloseService();
virtual tDirNodeReference OpenNamedNode(const char* nodename);
Modified: PyOpenDirectory/trunk/src/PythonWrapper.cpp
===================================================================
--- PyOpenDirectory/trunk/src/PythonWrapper.cpp 2009-12-04 23:36:35 UTC (rev 4826)
+++ PyOpenDirectory/trunk/src/PythonWrapper.cpp 2009-12-06 06:18:28 UTC (rev 4827)
@@ -909,7 +909,7 @@
}
/*
-def authenticateUserDigest(obj, guid, user, challenge, response, method):
+def authenticateUserDigest(obj, nodename, user, challenge, response, method):
"""
Authenticate using HTTP Digest credentials to Open Directory.
@@ -957,6 +957,92 @@
return NULL;
}
+/*
+def authenticateUserDigestToActiveDirectory(obj, nodename, user, challenge, response):
+ """
+ Authenticate using HTTP Digest credentials to an Active Directory node (exported by Open Directory).
+
+ @param obj: C{object} the object obtained from an odInit call.
+ @param nodename: C{str} the directory nodename for the record to check.
+ @param user: C{str} the user identifier/directory record name to check.
+ @param response: C{str} the HTTP response sent from the client.
+ @return: C{True} if the user was found, C{False} otherwise.
+ """
+ */
+extern "C" PyObject *authenticateUserDigestToActiveDirectory(PyObject *self, PyObject *args)
+{
+ PyObject* pyds;
+ const char* nodename;
+ const char* user;
+ const char* response;
+ if (!PyArg_ParseTuple(args, "Osss", &pyds, &nodename, &user, &response) || !PyCObject_Check(pyds))
+ {
+ PyErr_SetObject(ODException_class, Py_BuildValue("((s:i))", "DirectoryServices authenticateUserDigestToActiveDirectory: could not parse arguments", 0));
+ return NULL;
+ }
+
+ CDirectoryServiceManager* dsmgr = static_cast<CDirectoryServiceManager*>(PyCObject_AsVoidPtr(pyds));
+ if (dsmgr != NULL)
+ {
+ CDirectoryServiceAuth* ds = dsmgr->GetAuthService();
+ bool result = false;
+ bool authresult = false;
+ result = ds->AuthenticateUserDigestToActiveDirectory(nodename, user, response, authresult);
+ if (result)
+ {
+ if (authresult)
+ Py_RETURN_TRUE;
+ else
+ Py_RETURN_FALSE;
+ }
+ }
+ else
+ PyErr_SetObject(ODException_class, Py_BuildValue("((s:i))", "DirectoryServices authenticateUserDigestToActiveDirectory: invalid directory service argument", 0));
+
+ return NULL;
+}
+
+/*
+def getDigestMD5ChallengeFromActiveDirectory(obj, nodename, user ):
+ """
+ Get the Digest MD5 challenge (a.k.a. a nonce) from an Active Directory node (exported by Open Directory).
+
+ @param obj: C{object} the object obtained from an odInit call.
+ @param nodename: C{str} the directory nodename for the record to check.
+ @param user: C{str} the user identifier/directory record name to check.
+ @return: C{string} containing the AD server-generated challange.
+ """
+ */
+extern "C" PyObject *getDigestMD5ChallengeFromActiveDirectory(PyObject *self, PyObject *args)
+{
+ PyObject* pyds;
+ const char* nodename;
+ if (!PyArg_ParseTuple(args, "Os", &pyds, &nodename) || !PyCObject_Check(pyds))
+ {
+ PyErr_SetObject(ODException_class, Py_BuildValue("((s:i))", "DirectoryServices authenticateUserDigestToActiveDirectory: could not parse arguments", 0));
+ return NULL;
+ }
+
+ CDirectoryServiceManager* dsmgr = static_cast<CDirectoryServiceManager*>(PyCObject_AsVoidPtr(pyds));
+ if (dsmgr != NULL)
+ {
+ CDirectoryServiceAuth* ds = dsmgr->GetAuthService();
+ CFStringRef challenge = NULL;
+ challenge = ds->GetDigestMD5ChallengeFromActiveDirectory(nodename);
+ if (challenge != NULL)
+ {
+ PyObject* result = CFStringToPyStr(challenge);
+ CFRelease(challenge);
+
+ return result;
+ }
+ }
+ else
+ PyErr_SetObject(ODException_class, Py_BuildValue("((s:i))", "DirectoryServices authenticateUserDigestToActiveDirectory: invalid directory service argument", 0));
+
+ return NULL;
+}
+
static PyMethodDef ODMethods[] = {
{"odInit", odInit, METH_VARARGS,
"Initialize the Open Directory system."},
@@ -980,6 +1066,10 @@
"Authenticate a user with a password to Open Directory using plain text authentication."},
{"authenticateUserDigest", authenticateUserDigest, METH_VARARGS,
"Authenticate a user with a password to Open Directory using HTTP DIGEST authentication."},
+ {"authenticateUserDigestToActiveDirectory", authenticateUserDigestToActiveDirectory, METH_VARARGS,
+ "Authenticate a user with a password to an Active Directory node (exported by Open Directory) using HTTP DIGEST authentication."},
+ {"getDigestMD5ChallengeFromActiveDirectory", getDigestMD5ChallengeFromActiveDirectory, METH_VARARGS,
+ "Get a Digest MD5 challange (a.k.a. a nonce) from an Active Directory node (exported by Open Directory)."},
{NULL, NULL, 0, NULL} /* Sentinel */
};
Modified: PyOpenDirectory/trunk/support/PyOpenDirectory.xcodeproj/project.pbxproj
===================================================================
--- PyOpenDirectory/trunk/support/PyOpenDirectory.xcodeproj/project.pbxproj 2009-12-04 23:36:35 UTC (rev 4826)
+++ PyOpenDirectory/trunk/support/PyOpenDirectory.xcodeproj/project.pbxproj 2009-12-06 06:18:28 UTC (rev 4827)
@@ -7,6 +7,7 @@
objects = {
/* Begin PBXBuildFile section */
+ 661DFBCA1088E5C900846632 /* libsasl2.2.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 661DFBC91088E5C900846632 /* libsasl2.2.dylib */; };
8DD76F650486A84900D96B5E /* test.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 08FB7796FE84155DC02AAC07 /* test.cpp */; settings = {ATTRIBUTES = (); }; };
AF00155A0B8A21340045DAEE /* Python.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = AF0015590B8A21340045DAEE /* Python.framework */; };
AF00155E0B8A21FD0045DAEE /* PythonWrapper.cpp in Sources */ = {isa = PBXBuildFile; fileRef = AF155A310A501F84007E1E6E /* PythonWrapper.cpp */; };
@@ -38,6 +39,7 @@
/* Begin PBXFileReference section */
08FB7796FE84155DC02AAC07 /* test.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = test.cpp; sourceTree = "<group>"; };
+ 661DFBC91088E5C900846632 /* libsasl2.2.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libsasl2.2.dylib; path = /usr/lib/libsasl2.2.dylib; sourceTree = "<absolute>"; };
8DD76F6C0486A84900D96B5E /* PyOpenDirectory */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = PyOpenDirectory; sourceTree = BUILT_PRODUCTS_DIR; };
AF0015590B8A21340045DAEE /* Python.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Python.framework; path = /System/Library/Frameworks/Python.framework; sourceTree = "<absolute>"; };
AF02AC560CBE690500F478B8 /* CDirectoryServiceException.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = CDirectoryServiceException.cpp; path = ../src/CDirectoryServiceException.cpp; sourceTree = SOURCE_ROOT; };
@@ -65,6 +67,7 @@
AF155A370A501F9D007E1E6E /* CoreFoundation.framework in Frameworks */,
AF155A380A501F9D007E1E6E /* DirectoryService.framework in Frameworks */,
AF00155A0B8A21340045DAEE /* Python.framework in Frameworks */,
+ 661DFBCA1088E5C900846632 /* libsasl2.2.dylib in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -116,6 +119,7 @@
AF0015590B8A21340045DAEE /* Python.framework */,
AF155A350A501F9D007E1E6E /* CoreFoundation.framework */,
AF155A360A501F9D007E1E6E /* DirectoryService.framework */,
+ 661DFBC91088E5C900846632 /* libsasl2.2.dylib */,
);
name = Frameworks;
sourceTree = "<group>";
Modified: PyOpenDirectory/trunk/support/test.cpp
===================================================================
--- PyOpenDirectory/trunk/support/test.cpp 2009-12-04 23:36:35 UTC (rev 4826)
+++ PyOpenDirectory/trunk/support/test.cpp 2009-12-06 06:18:28 UTC (rev 4827)
@@ -32,9 +32,16 @@
void PrintDictionary(const void* key, const void* value, void* ref);
void PrintArrayArray(CFMutableArrayRef list);
void PrintArray(CFArrayRef list);
-void AuthenticateUser(CDirectoryServiceAuth* dir, const char* guid, const char* user, const char* pswd);
-void AuthenticateUserDigest(CDirectoryServiceAuth* dir, const char* guid, const char* user, const char* challenge, const char* response, const char* method);
+void AuthenticateUser(CDirectoryServiceAuth* dir, const char* nodename, const char* user, const char* pswd);
+void AuthenticateUserDigest(CDirectoryServiceAuth* dir, const char* nodename, const char* user, const char* challenge, const char* response, const char* method);
+void AuthenticateUserDigestToActiveDirectory(CDirectoryServiceAuth* dir, const char* nodename, const char* user, const char* response);
+void GetDigestMD5ChallengeFromActiveDirectory(CDirectoryServiceAuth* dir, const char* nodename);
+void AuthenticateUserDigestODAD(CDirectoryServiceAuth* dir, const char* nodename, const char* user, const char* pswd, bool verbose = false);
+
+CFStringRef GetClientResponseFromSASL( const char* username, const char* pswd, const char* serverchallenge );
+CFStringRef GetDigestMD5ChallengeFromSASL( void );
+
#define kDSStdRecordTypeResources "dsRecTypeStandard:Resources"
#define kDSNAttrServicesLocator "dsAttrTypeStandard:ServicesLocator"
#define kDSNAttrJPEGPhoto "dsAttrTypeStandard:JPEGPhoto"
@@ -44,7 +51,7 @@
CFMutableArrayRef data = dir->ListNodes(false);
if (data != NULL)
{
- printf("\n*** Nodes: %d ***\n", CFArrayGetCount(data));
+ printf("\n*** Nodes: %ld ***\n", CFArrayGetCount(data));
for(CFIndex i = 0; i < CFArrayGetCount(data); i++)
{
CFStringRef str = (CFStringRef)CFArrayGetValueAtIndex(data, i);
@@ -55,11 +62,11 @@
char localBuffer[256];
Boolean success;
success = CFStringGetCString(str, localBuffer, 256, kCFStringEncodingUTF8);
- printf("%d: %s\n", i, localBuffer);
+ printf("%ld: %s\n", i, localBuffer);
}
else
{
- printf("%d: %s\n", i, (const char*)bytes);
+ printf("%ld: %s\n", i, (const char*)bytes);
}
}
CFRelease(data);
@@ -80,7 +87,7 @@
CFMutableDictionaryRef nodeData = dir->GetNodeAttributes("/Search", attrsdict, false);
if (nodeData != NULL)
{
- printf("\n*** Node Attributes: %d ***\n", CFDictionaryGetCount(nodeData));
+ printf("\n*** Node Attributes: %ld ***\n", CFDictionaryGetCount(nodeData));
CFDictionaryApplyFunction(nodeData, PrintDictionary, NULL);
CFRelease(nodeData);
}
@@ -107,7 +114,7 @@
CFMutableArrayRef data = dir->ListAllRecordsWithAttributes(recordTypes, attrsdict, 0, false);
if (data != NULL)
{
- printf("\n*** Users: %d ***\n", CFArrayGetCount(data));
+ printf("\n*** Users: %ld ***\n", CFArrayGetCount(data));
for(CFIndex i = 0; i < CFArrayGetCount(data); i++)
{
CFArrayRef tuple = (CFArrayRef)CFArrayGetValueAtIndex(data, i);
@@ -119,11 +126,11 @@
char localBuffer[256];
Boolean success;
success = CFStringGetCString(str, localBuffer, 256, kCFStringEncodingUTF8);
- printf("%d: %s\n", i, localBuffer);
+ printf("%ld: %s\n", i, localBuffer);
}
else
{
- printf("%d: %s\n", i, (const char*)bytes);
+ printf("%ld: %s\n", i, (const char*)bytes);
}
}
CFRelease(data);
@@ -158,11 +165,11 @@
char localBuffer[256];
Boolean success;
success = CFStringGetCString(str, localBuffer, 256, kCFStringEncodingUTF8);
- printf("%d: %s\n", i, localBuffer);
+ printf("%ld: %s\n", i, localBuffer);
}
else
{
- printf("%d: %s\n", i, (const char*)bytes);
+ printf("%ld: %s\n", i, (const char*)bytes);
}
}
CFRelease(data);
@@ -262,17 +269,35 @@
//AuthenticateUser(authdir, "/LDAPv3/127.0.0.1", "oliverdaboo", "oliver");
AuthenticateUser(authdir, "/LDAPv3/127.0.0.1", "eleanordaboo", "eleanor");
- const char* n = "/LDAPv3/127.0.0.1";
- //const char* u = "test";
- //const char* c = "nonce=\"1\", qop=\"auth\", realm=\"Test\", algorithm=\"md5\", opaque=\"1\"";
- //const char* r = "username=\"test\", nonce=\"1\", cnonce=\"1\", nc=\"1\", realm=\"Test\", algorithm=\"md5\", opaque=\"1\", qop=\"auth\", uri=\"/\", response=\"4241f31ffe6f9c99b891f88e9c41caa9\"";
- //const char* c = "WWW-Authenticate: digest nonce=\"1621696297327727918745238639\", opaque=\"121994e78694cbdff74f12cb32ee6f00-MTYyMTY5NjI5NzMyNzcyNzkxODc0NTIzODYzOSwxMjcuMC4wLjEsMTE2ODU2ODg5NQ==\", realm=\"Test Realm\", algorithm=\"md5\", qop=\"auth\"";
- //const char* r = "Authorization: Digest username=\"test\", realm=\"Test Realm\", nonce=\"1621696297327727918745238639\", uri=\"/principals/users/test/\", response=\"e260f13cffcc15572ddeec9c31de437b\", opaque=\"121994e78694cbdff74f12cb32ee6f00-MTYyMTY5NjI5NzMyNzcyNzkxODc0NTIzODYzOSwxMjcuMC4wLjEsMTE2ODU2ODg5NQ==\", algorithm=\"md5\", cnonce=\"70cbd8f04227d8d46c0193b290beaf0d\", nc=00000001, qop=\"auth\"";
- const char* u = "";
- const char* c = "DIGEST-MD5";
- const char* r = "";
- AuthenticateUserDigest(authdir, n, u, c, r, "GET");
+ {
+ const char* n = "/LDAPv3/127.0.0.1";
+ //const char* u = "test";
+ //const char* c = "nonce=\"1\", qop=\"auth\", realm=\"Test\", algorithm=\"md5\", opaque=\"1\"";
+ //const char* r = "username=\"test\", nonce=\"1\", cnonce=\"1\", nc=\"1\", realm=\"Test\", algorithm=\"md5\", opaque=\"1\", qop=\"auth\", uri=\"/\", response=\"4241f31ffe6f9c99b891f88e9c41caa9\"";
+ //const char* c = "WWW-Authenticate: digest nonce=\"1621696297327727918745238639\", opaque=\"121994e78694cbdff74f12cb32ee6f00-MTYyMTY5NjI5NzMyNzcyNzkxODc0NTIzODYzOSwxMjcuMC4wLjEsMTE2ODU2ODg5NQ==\", realm=\"Test Realm\", algorithm=\"md5\", qop=\"auth\"";
+ //const char* r = "Authorization: Digest username=\"test\", realm=\"Test Realm\", nonce=\"1621696297327727918745238639\", uri=\"/principals/users/test/\", response=\"e260f13cffcc15572ddeec9c31de437b\", opaque=\"121994e78694cbdff74f12cb32ee6f00-MTYyMTY5NjI5NzMyNzcyNzkxODc0NTIzODYzOSwxMjcuMC4wLjEsMTE2ODU2ODg5NQ==\", algorithm=\"md5\", cnonce=\"70cbd8f04227d8d46c0193b290beaf0d\", nc=00000001, qop=\"auth\"";
+ const char* u = "";
+ const char* c = "DIGEST-MD5";
+ const char* r = "";
+ AuthenticateUserDigest(authdir, n, u, c, r, "GET");
+ }
+ GetDigestMD5ChallengeFromActiveDirectory(authdir, "/Active Directory/All Domains");
+
+ {
+ const char* n = "/Active Directory/All Domains";
+ const char* u = "";
+ const char* r = "";
+ AuthenticateUserDigestToActiveDirectory(authdir, n, u, r);
+ }
+
+
+ AuthenticateUser(authdir, "/Active Directory/All Domains", "servicetest", "pass");
+ AuthenticateUserDigestODAD(authdir, "/Active Directory/All Domains", "servicetest", "pass");
+
+ //AuthenticateUser(authdir, "/LDAPv3/127.0.0.1", "eleanordaboo", "eleanor");
+ //AuthenticateUserDigestODAD(authdir, "/LDAPv3/127.0.0.1", "eleanordaboo", "eleanor");
+
return 0;
}
@@ -282,12 +307,12 @@
if (dir->AuthenticateUserBasic(nodename, user, pswd, result, false))
{
if (result)
- printf("Authenticated user: %s\n", user);
+ printf("AuthenticateUserBasic() success; nodename:\"%s\", user:\"%s\", pswd:\"%s\"\n", nodename, user, pswd );
else
- printf("Not Authenticated user: %s\n", user);
+ printf("AuthenticateUserBasic() success, but auth failed; nodename:\"%s\", user:\"%s\", pswd:\"%s\"\n", nodename, user, pswd );
}
else
- printf("Failed authentication user: %s\n", user);
+ printf("AuthenticateUserBasic() failed; nodename:\"%s\", user:\"%s\", pswd:\"%s\"\n", nodename, user, pswd );
}
void AuthenticateUserDigest(CDirectoryServiceAuth* dir, const char* nodename, const char* user, const char* challenge, const char* response, const char* method)
@@ -296,14 +321,144 @@
if (dir->AuthenticateUserDigest(nodename, user, challenge, response, method, result, false))
{
if (result)
- printf("Authenticated user: %s\n", user);
+ printf("AuthenticateUserDigest() success; nodename:\"%s\", user:\"%s\", challenge:\"%s\", response:\"%s\", method:\"%s\"\n", nodename, user, challenge, response, method );
else
- printf("Not Authenticated user: %s\n", user);
+ printf("AuthenticateUserDigest() success, but auth failed; user:\"%s\", user:\"%s\", challenge:\"%s\", response:\"%s\", method:\"%s\"\n", nodename, user, challenge, response, method );
}
else
- printf("Failed authentication user: %s\n", user);
+ printf("AuthenticateUserDigest() failed, nodename:\"%s\", user:\"%s\", challenge:\"%s\", response:\"%s\", method:\"%s\"\n", nodename, user, challenge, response, method );
}
+
+void AuthenticateUserDigestToActiveDirectory(CDirectoryServiceAuth* dir, const char* nodename, const char* user, const char* response)
+{
+
+ bool result = false;
+ if (dir->AuthenticateUserDigestToActiveDirectory(nodename, user, response, result, false))
+ {
+ if (result)
+ printf("AuthenticateUserDigestToActiveDirectory() success; nodename:\"%s\", user:\"%s\", response:\"%s\"\n", nodename, user, response );
+ else
+ printf("AuthenticateUserDigestToActiveDirectory() success, but auth failed; nodename:\"%s\", user:\"%s\", response:\"%s\"\n", nodename, user, response );
+ }
+ else
+ printf("AuthenticateUserDigestToActiveDirectory() failed; nodename:\"%s\", user:\"%s\", response:\"%s\"\n", nodename, user, response );
+}
+
+void GetDigestMD5ChallengeFromActiveDirectory(CDirectoryServiceAuth* dir, const char* nodename)
+{
+
+ CFStringRef result = dir->GetDigestMD5ChallengeFromActiveDirectory(nodename, false);
+ if (NULL != result)
+ {
+ CFStringUtil s(result);
+ printf("GetDigestMD5ChallengeFromActiveDirectory() success; nodename:\"%s\", challenge:\"%s\"\n", nodename, s.temp_str());
+
+ CFRelease( result );
+ }
+ else
+ printf("GetDigestMD5ChallengeFromActiveDirectory() failed; nodename:\"%s\"\n", nodename);
+}
+
+
+void AuthenticateUserDigestODAD(CDirectoryServiceAuth* dir, const char* nodename, const char* user, const char* pswd, bool verbose)
+{
+ CFStringRef challengeRef = NULL;
+ CFStringRef responseRef = NULL;
+ bool activeDirectory = (0 == strncmp(nodename, "/Active Directory/", strlen("/Active Directory/")));
+
+ // first get server challange
+ if (activeDirectory)
+ {
+ // get server challenge from AD
+ challengeRef = dir->GetDigestMD5ChallengeFromActiveDirectory(nodename, false);
+ if (NULL != challengeRef)
+ {
+ if (verbose) {
+ CFStringUtil s(challengeRef);
+ printf("GetDigestMD5ChallengeFromActiveDirectory() success; nodename:\"%s\", challenge:\"%s\"\n", nodename, s.temp_str());
+ }
+ }
+ else
+ printf("GetDigestMD5ChallengeFromActiveDirectory() failed; nodename:\"%s\"\n", nodename);
+ }
+ else
+ {
+ // get server challenge from AD
+ challengeRef = GetDigestMD5ChallengeFromSASL();
+ if (NULL != challengeRef)
+ {
+ if (verbose) {
+ CFStringUtil s(challengeRef);
+ printf("GetDigestMD5ChallengeFromSASL() success; nodename:\"%s\", challenge:\"%s\"\n", nodename, s.temp_str());
+ }
+ }
+ else
+ printf("GetDigestMD5ChallengeFromSASL() failed; nodename:\"%s\"\n", nodename);
+ }
+
+ if (NULL != challengeRef)
+ {
+
+ CFStringUtil challengeStrUtil(challengeRef);
+ const char* challenge = challengeStrUtil.temp_str();
+
+ // now create response
+ responseRef = GetClientResponseFromSASL( user, pswd, challenge );
+ CFStringUtil responseStrUtil(responseRef);
+ const char* response = responseStrUtil.temp_str();
+
+ if (NULL != responseRef)
+ {
+ if (verbose) {
+ printf("GetClientResponseFromSASL() success; user:\"%s\", pswd:\"%s\", challenge:\"%s\", response:\"%s\"\n", user, pswd, challenge, response);
+ }
+ }
+ else
+ {
+ printf("GetClientResponseFromSASL() failed; user:\"%s\", pswd:\"%s\", challenge:\"%s\"\n", user, pswd, challenge);
+ goto exit;
+ }
+
+ if (activeDirectory)
+ {
+ // now auth
+ bool result = false;
+ if (dir->AuthenticateUserDigestToActiveDirectory(nodename, user, response, result, false))
+ {
+ if (result)
+ printf("AuthenticateUserDigestToActiveDirectory() success; nodename:\"%s\", user:\"%s\", response:\"%s\"\n", nodename, user, response );
+ else
+ printf("AuthenticateUserDigestToActiveDirectory() success, but auth failed; nodename:\"%s\", user:\"%s\", response:\"%s\"\n", nodename, user, response );
+ }
+ else
+ printf("AuthenticateUserDigestToActiveDirectory() failed; nodename:\"%s\", user:\"%s\", response:\"%s\"\n", nodename, user, response );
+ }
+ else
+ {
+ bool result = false;
+ const char* method = "";
+ if (dir->AuthenticateUserDigest(nodename, user, challenge, response, method, result, false))
+ {
+ if (result)
+ printf("AuthenticateUserDigest() success; nodename:\"%s\", user:\"%s\", challenge:\"%s\", response:\"%s\", method:\"%s\"\n", nodename, user, challenge, response, method );
+ else
+ printf("AuthenticateUserDigest() success, but auth failed; nodename:\"%s\", user:\"%s\", challenge:\"%s\", response:\"%s\", method:\"%s\"\n", nodename, user, challenge, response, method );
+ }
+ else
+ printf("AuthenticateUserDigest() failed, nodename:\"%s\", user:\"%s\", challenge:\"%s\", response:\"%s\", method:\"%s\"\n", nodename, user, challenge, response, method );
+ }
+ }
+
+exit:
+
+ if (NULL != challengeRef)
+ CFRelease( challengeRef );
+ if (NULL != responseRef)
+ CFRelease( responseRef );
+
+}
+
void CFDictionaryIterator(const void* key, const void* value, void* ref)
{
CFStringRef strkey = (CFStringRef)key;
@@ -390,7 +545,7 @@
for(CFIndex i = 0; i < CFArrayGetCount(list); i++)
{
CFMutableArrayRef array = (CFMutableArrayRef)CFArrayGetValueAtIndex(list, i);
- printf("Index: %d\n", i);
+ printf("Index: %ld\n", i);
PrintArray(array);
printf("\n");
}
@@ -409,11 +564,260 @@
char localBuffer[256];
Boolean success;
success = CFStringGetCString(str, localBuffer, 256, kCFStringEncodingUTF8);
- printf("%d: %s\n", i, localBuffer);
+ printf("%ld: %s\n", i, localBuffer);
}
else
{
- printf("%d: %s\n", i, (const char*)bytes);
+ printf("%ld: %s\n", i, (const char*)bytes);
}
}
}
+
+
+#pragma mark ----- SASL calls
+
+#include <sasl/sasl.h>
+
+#define kSASLMinSecurityFactor 0
+#define kSASLMaxSecurityFactor 65535
+#define kSASLMaxBufferSize 65536
+#define kSASLSecurityFlags 0
+#define kSASLPropertyNames (NULL)
+#define kSASLPropertyValues (NULL)
+
+typedef struct saslContext {
+ const char *user;
+ const char *pass;
+} saslContext;
+
+typedef int sasl_cbproc();
+
+
+int getrealm(void *context /*__attribute__((unused))*/,
+ int cb_id,
+ const char **availrealms,
+ const char **result)
+{
+ #pragma unused (context)
+
+ /* paranoia check */
+ if (cb_id != SASL_CB_GETREALM) return SASL_BADPARAM;
+ if (!result) return SASL_BADPARAM;
+
+ if ( availrealms ) {
+ *result = *availrealms;
+ }
+
+ return SASL_OK;
+}
+
+int simple(void *context /*__attribute__((unused))*/,
+ int cb_id,
+ const char **result,
+ unsigned *len)
+{
+ saslContext *text = (saslContext *)context;
+
+ //syslog(LOG_INFO, "in simple\n");
+
+ /* paranoia check */
+ if ( result == NULL )
+ return SASL_BADPARAM;
+
+ *result = NULL;
+
+ switch (cb_id) {
+ case SASL_CB_USER:
+ *result = text->user;
+ break;
+
+ case SASL_CB_AUTHNAME:
+ *result = text->user;
+ break;
+
+ default:
+ return SASL_BADPARAM;
+ }
+
+ if (*result != NULL && len != NULL)
+ *len = strlen(*result);
+
+ return SASL_OK;
+}
+
+
+int
+getsecret(sasl_conn_t *conn,
+ void *context /*__attribute__((unused))*/,
+ int cb_id,
+ sasl_secret_t **psecret)
+{
+ saslContext *text = (saslContext *)context;
+
+ //syslog(LOG_INFO, "in getsecret\n");
+
+ /* paranoia check */
+ if (! conn || ! psecret || cb_id != SASL_CB_PASS)
+ return SASL_BADPARAM;
+
+ size_t pwdLen = strlen(text->pass);
+ *psecret = (sasl_secret_t *) malloc( sizeof(sasl_secret_t) + pwdLen );
+ (*psecret)->len = pwdLen;
+ strcpy((char *)(*psecret)->data, text->pass);
+
+ return SASL_OK;
+}
+
+
+
+
+//----------------------------------------------------------------------------------------
+// SASLClientNewContext
+//
+// Returns: A SASL context, or NULL
+//
+// <callbacks> must be an array with capacity for at least 5 items
+//----------------------------------------------------------------------------------------
+
+sasl_conn_t *SASLClientNewContext( sasl_callback_t *callbacks, saslContext *context )
+{
+ int result = 0;
+ sasl_conn_t *sasl_conn = NULL;
+ sasl_security_properties_t secprops = { kSASLMinSecurityFactor, kSASLMaxSecurityFactor,
+ kSASLMaxBufferSize, kSASLSecurityFlags,
+ kSASLPropertyNames, kSASLPropertyValues };
+
+ result = sasl_client_init( NULL );
+ //printf( "sasl_client_init = %d\n", result );
+ if ( result != SASL_OK )
+ return NULL;
+
+ // callbacks we support
+ callbacks[0].id = SASL_CB_GETREALM;
+ callbacks[0].proc = (sasl_cbproc *)&getrealm;
+ callbacks[0].context = context;
+
+ callbacks[1].id = SASL_CB_USER;
+ callbacks[1].proc = (sasl_cbproc *)&simple;
+ callbacks[1].context = context;
+
+ callbacks[2].id = SASL_CB_AUTHNAME;
+ callbacks[2].proc = (sasl_cbproc *)&simple;
+ callbacks[2].context = context;
+
+ callbacks[3].id = SASL_CB_PASS;
+ callbacks[3].proc = (sasl_cbproc *)&getsecret;
+ callbacks[3].context = context;
+
+ callbacks[4].id = SASL_CB_LIST_END;
+ callbacks[4].proc = NULL;
+ callbacks[4].context = NULL;
+
+ result = sasl_client_new( "http", "servicetest.example.com", NULL, NULL, callbacks, 0, &sasl_conn );
+ //printf( "sasl_client_new = %d\n", result );
+ if ( result != SASL_OK )
+ return NULL;
+
+ result = sasl_setprop( sasl_conn, SASL_SEC_PROPS, &secprops );
+ //printf( "sasl_setprop = %d\n", result );
+ if ( result != SASL_OK ) {
+ sasl_dispose( &sasl_conn );
+ return NULL;
+ }
+
+ return sasl_conn;
+}
+
+
+CFStringRef GetClientResponseFromSASL( const char* username, const char* pswd, const char* serverchallenge )
+{
+ int result = 0;
+ sasl_callback_t callbacks[5] = {{0}};
+ saslContext sasl_context = { NULL, NULL };
+ sasl_conn_t *sasl_conn = NULL;
+ const char *data = NULL;
+ unsigned int len = 0;
+ const char *chosenmech = NULL;
+ CFStringRef response = NULL;
+
+ sasl_context.user = username;
+ sasl_context.pass = pswd;
+
+ // Client's first move
+ sasl_conn = SASLClientNewContext( callbacks, &sasl_context );
+
+ result = sasl_client_start( sasl_conn, "DIGEST-MD5", NULL, &data, &len, &chosenmech );
+ //printf( "sasl_client_start = %d, len = %d\n", result, len );
+ if ( result != SASL_CONTINUE )
+ goto done;
+
+ // Client hashes the digest and responds
+ result = sasl_client_step(sasl_conn, serverchallenge, strlen(serverchallenge), NULL, &data, &len);
+ if (result != SASL_CONTINUE) {
+ printf("sasl_client_step = %d\n", result);
+ goto done;
+ }
+
+ response = CFStringCreateWithBytes(kCFAllocatorDefault, (const UInt8*)data, len, kCFStringEncodingUTF8, false);
+
+
+done:
+ if ( sasl_conn != NULL )
+ sasl_dispose( &sasl_conn );
+
+ return response;
+}
+
+//----------------------------------------------------------------------------------------
+// GetDigestMD5ChallengeFromSASL
+//
+// Returns: A server challenge for DIGEST-MD5 authentication
+//----------------------------------------------------------------------------------------
+
+CFStringRef GetDigestMD5ChallengeFromSASL( void )
+{
+ int result = 0;
+ sasl_conn_t *sasl_server_conn = NULL;
+ const char *serverOut = NULL;
+ unsigned int serverOutLen = 0;
+ CFStringRef challenge = NULL;
+
+ // Passing a minimum security factor of 2 or more triggers the latest digest-md5 specification.
+ // algorithm=md5-sess,qop=auth-conf.
+ sasl_security_properties_t secprops = { 2, kSASLMaxSecurityFactor,
+ kSASLMaxBufferSize, kSASLSecurityFlags,
+ kSASLPropertyNames, kSASLPropertyValues };
+
+ // Get a challenge from SASL
+ result = sasl_server_init_alt( NULL, "AppName" );
+ if ( result != SASL_OK ) {
+ printf( "sasl_server_init_alt = %d\n", result );
+ goto done;
+ }
+
+ //"127.0.0.1;80"
+ result = sasl_server_new( "http", "servicetest.example.com", NULL, NULL, NULL, NULL, 0, &sasl_server_conn );
+ if ( result != SASL_OK ) {
+ printf( "sasl_server_init_alt = %d\n", result );
+ goto done;
+ }
+
+ result = sasl_setprop( sasl_server_conn, SASL_SEC_PROPS, &secprops );
+
+ result = sasl_server_start( sasl_server_conn, "DIGEST-MD5", NULL, 0, &serverOut, &serverOutLen );
+ if ( result != SASL_CONTINUE ) {
+ printf( "sasl_server_start = %d\n", result );
+ goto done;
+ }
+
+ challenge = CFStringCreateWithBytes(kCFAllocatorDefault, (const UInt8*)serverOut, serverOutLen, kCFStringEncodingUTF8, false);
+
+done:
+ if ( sasl_server_conn != NULL )
+ sasl_dispose( &sasl_server_conn );
+
+ return challenge;
+}
+
+
+
Modified: PyOpenDirectory/trunk/test_auth.py
===================================================================
--- PyOpenDirectory/trunk/test_auth.py 2009-12-04 23:36:35 UTC (rev 4826)
+++ PyOpenDirectory/trunk/test_auth.py 2009-12-06 06:18:28 UTC (rev 4827)
@@ -20,6 +20,7 @@
import dsattributes
import md5
import sha
+import shlex
algorithms = {
'md5': md5.new,
@@ -96,7 +97,7 @@
m.update(pszMethod)
m.update(":")
m.update(pszDigestUri)
- if pszQop == "auth-int":
+ if pszQop == "auth-int" or pszQop == "auth-conf":
m.update(":")
m.update(pszHEntity)
HA2 = m.digest().encode('hex')
@@ -120,16 +121,19 @@
attempts = 100
-realm = "/Search"
-nonce = "128446648710842461101646794502"
-nc = "00000001"
-cnonce = "0a4f113b12345"
-uri = "/principals/"
-method = "GET"
-def doAuthDigest(username, password, qop, algorithm):
+def doAuthDigest(username, password, qop, algorithm, cipher):
failures = 0
+ realm = "host.example.com"
+ nonce = "128446648710842461101646794502"
+ nc = "00000001"
+ cnonce = "/rrD6TqPA3lHRmg+fw/vyU6oWoQgzK7h9yWrsCmv/lE="
+ uri = "http://host.example.com"
+ method = "GET"
+ entity = "00000000000000000000000000000000"
+ maxbuf = "65536"
+
result = opendirectory.queryRecordsWithAttribute_list(
od,
dsattributes.kDSNAttrRecordName,
@@ -143,38 +147,104 @@
return
nodename = result[0][1][dsattributes.kDSNAttrMetaNodeLocation]
- expected = calcResponse(
- calcHA1(algorithm, username, realm, password, nonce, cnonce),
- algorithm, nonce, nc, cnonce, qop, method, uri, None
- )
- #print expected
+ print( ' User node= "%s"' % nodename)
+ adUser = nodename.startswith("/Active Directory/")
+
+ for _ignore_x in xrange(attempts):
+ if adUser:
- if qop:
- challenge = 'Digest realm="%s", nonce="%s", algorithm=%s, qop="%s"' % (realm, nonce, algorithm, qop,)
- else:
- challenge = 'Digest realm="%s", nonce="%s", algorithm=%s' % (realm, nonce, algorithm,)
- if qop:
- response = ('Digest username="%s", realm="%s", '
- 'nonce="%s", digest-uri="%s", '
- 'response=%s, algorithm=%s, cnonce="%s", qop=%s, nc=%s' % (username, realm, nonce, uri, expected, algorithm, cnonce, qop, nc, ))
- else:
- response = ('Digest username="%s", realm="%s", '
- 'nonce="%s", digest-uri="%s", '
- 'response=%s, algorithm=%s' % (username, realm, nonce, uri, expected, algorithm, ))
+ challenge = opendirectory.getDigestMD5ChallengeFromActiveDirectory(od, nodename)
+ if not challenge:
+ print "Failed to get Active Directory challenge for user: %s" % (username,)
+ return
+ # parse challenge
+
+ l = shlex.shlex(challenge)
+ l.wordchars = l.wordchars + "_-"
+ l.whitespace = l.whitespace + "=,"
+ auth = {}
+ while 1:
+ k = l.get_token()
+ if not k:
+ break
+ v = l.get_token()
+ if not v:
+ break
+ v = v.strip('"') # this strip is kind of a hack, should remove matched leading and trailing double quotes
+
+ auth[k.strip()] = v.strip()
+
+ # get expected response parameters from challenge
+ nonce = auth["nonce"]
+ #nonce = "+Upgraded+v17fa28b0e0cb4c483144a0d568259ca0102de13e7b48ff9261cfa9748b93f83cc09d8ee50638c6d9794e1b4f8485a7dee"
+
+ if auth.get("digest-uri", False):
+ uri = auth["digest-uri"]
+
+ qopstr = auth.get("qop", False)
+ if qop not in qopstr.split(","):
+ print "WARINING: input qop=%s not in AD challenge qop=\"%s\"" % (qop, qopstr,)
+
+ if auth.get("realm", False):
+ realm = auth["realm"]
+
+ algostr = auth.get("algorithm", "")
+ if algorithm.lower() != algostr.lower():
+ print "WARINING: input algorithm=%s not in AD challenge algorithm=%s" % (algorithm, algostr,)
+
+ cipherstr = auth.get("cipher", "")
+ if cipher.lower() not in cipherstr.lower().split(","):
+ print "WARINING: input cipher=%s not in AD challenge cipher=\"%s\"" % (cipher, cipherstr,)
+
+
+ if auth.get("maxbuf", False):
+ maxbuf = auth["maxbuf"]
+
+ method = "AUTHENTICATE"
+
+ else:
+
+ if qop:
+ challenge = 'realm="%s", nonce="%s", algorithm=%s, qop="%s"' % (realm, nonce, algorithm, qop,)
+ else:
+ challenge = 'realm="%s", nonce="%s", algorithm=%s' % (realm, nonce, algorithm,)
+
+
+ expected = calcResponse(
+ calcHA1(algorithm.lower(), username, realm, password, nonce, cnonce),
+ algorithm.lower(), nonce, nc, cnonce, qop, method, uri, entity
+ )
+
+ if qop:
+ response = ('username="%s",realm="%s",algorithm=%s,'
+ 'nonce="%s",cnonce="%s",nc=%s,qop=%s,'
+ 'cipher=%s,maxbuf=%s,digest-uri="%s",response=%s' % (username, realm, algorithm,
+ nonce, cnonce, nc, qop,
+ cipher, maxbuf, uri, expected ))
+ else:
+ response = ('Digest username="%s", uri="%s", response=%s' % (username, uri, expected, ))
+
+ print " Challenge: %s" % (challenge,)
+ print " Response: %s" % (response, )
+
- print " Challenge: %s" % (challenge,)
- print " Response: %s" % (response, )
-
- for _ignore_x in xrange(attempts):
- success = opendirectory.authenticateUserDigest(
- od,
- nodename,
- username,
- challenge,
- response,
- method
- )
-
+ if adUser:
+ success = opendirectory.authenticateUserDigestToActiveDirectory(
+ od,
+ nodename,
+ username,
+ response,
+ )
+ else:
+ success = opendirectory.authenticateUserDigest(
+ od,
+ nodename,
+ username,
+ challenge,
+ response,
+ method
+ )
+
if not success:
failures += 1
@@ -208,14 +278,40 @@
failures += 1
print "\n%d failures out of %d attempts for Basic.\n\n" % (failures, attempts)
-
+"""
search = raw_input("DS search path: ")
user = raw_input("User: ")
pswd = getpass("Password: ")
attempts = int(raw_input("Number of attempts: "))
+"""
+# to test, bind your client to Active Directory that contains the user specified below
+
+search = "/Search"
+user = "servicetest"
+pswd = "pass"
+attempts = 10
+
od = opendirectory.odInit(search)
-#doAuthBasic(user, pswd)
-doAuthDigest(user, pswd, None, "md5")
+doAuthBasic(user, pswd)
+doAuthDigest(user, pswd, "auth-conf", "md5-sess", "rc4")
+doAuthDigest(user, pswd, "auth-conf", "MD5-sess", "RC4")
+# to test, bind your client to an Open Directory master that contains the user specified below
+
+user = "testuser"
+pswd = "test"
+doAuthBasic(user, pswd)
+doAuthDigest(user, pswd, None, "md5", None)
+#doAuthDigest(user, pswd, None, "md5-sess", "rc4") # fails
+
+doAuthDigest(user, pswd, "auth", "md5", "rc4")
+doAuthDigest(user, pswd, "auth", "md5-sess", "rc4")
+
+doAuthDigest(user, pswd, "auth-int", "md5", "rc4")
+doAuthDigest(user, pswd, "auth-int", "md5-sess", "rc4")
+
+#doAuthDigest(user, pswd, "auth-conf", "md5", "rc4") # fails
+doAuthDigest(user, pswd, "auth-conf", "md5-sess", "rc4")
+
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.macosforge.org/pipermail/calendarserver-changes/attachments/20091205/2e8615d3/attachment-0001.html>
More information about the calendarserver-changes
mailing list