[CalendarServer-changes] [4593] PyOpenDirectory/branches/users/gaya/addigestauth

source_changes at macosforge.org source_changes at macosforge.org
Fri Oct 16 16:31:55 PDT 2009


Revision: 4593
          http://trac.macosforge.org/projects/calendarserver/changeset/4593
Author:   gaya at apple.com
Date:     2009-10-16 16:31:54 -0700 (Fri, 16 Oct 2009)
Log Message:
-----------
First pass changes for ad digest auth

Modified Paths:
--------------
    PyOpenDirectory/branches/users/gaya/addigestauth/pysrc/opendirectory.py
    PyOpenDirectory/branches/users/gaya/addigestauth/src/CDirectoryServiceAuth.cpp
    PyOpenDirectory/branches/users/gaya/addigestauth/src/CDirectoryServiceAuth.h
    PyOpenDirectory/branches/users/gaya/addigestauth/src/PythonWrapper.cpp
    PyOpenDirectory/branches/users/gaya/addigestauth/support/PyOpenDirectory.xcodeproj/project.pbxproj
    PyOpenDirectory/branches/users/gaya/addigestauth/support/test.cpp
    PyOpenDirectory/branches/users/gaya/addigestauth/test_auth.py

Modified: PyOpenDirectory/branches/users/gaya/addigestauth/pysrc/opendirectory.py
===================================================================
--- PyOpenDirectory/branches/users/gaya/addigestauth/pysrc/opendirectory.py	2009-10-16 23:25:33 UTC (rev 4592)
+++ PyOpenDirectory/branches/users/gaya/addigestauth/pysrc/opendirectory.py	2009-10-16 23:31:54 UTC (rev 4593)
@@ -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/branches/users/gaya/addigestauth/src/CDirectoryServiceAuth.cpp
===================================================================
--- PyOpenDirectory/branches/users/gaya/addigestauth/src/CDirectoryServiceAuth.cpp	2009-10-16 23:25:33 UTC (rev 4592)
+++ PyOpenDirectory/branches/users/gaya/addigestauth/src/CDirectoryServiceAuth.cpp	2009-10-16 23:31:54 UTC (rev 4593)
@@ -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/branches/users/gaya/addigestauth/src/CDirectoryServiceAuth.h
===================================================================
--- PyOpenDirectory/branches/users/gaya/addigestauth/src/CDirectoryServiceAuth.h	2009-10-16 23:25:33 UTC (rev 4592)
+++ PyOpenDirectory/branches/users/gaya/addigestauth/src/CDirectoryServiceAuth.h	2009-10-16 23:31:54 UTC (rev 4593)
@@ -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/branches/users/gaya/addigestauth/src/PythonWrapper.cpp
===================================================================
--- PyOpenDirectory/branches/users/gaya/addigestauth/src/PythonWrapper.cpp	2009-10-16 23:25:33 UTC (rev 4592)
+++ PyOpenDirectory/branches/users/gaya/addigestauth/src/PythonWrapper.cpp	2009-10-16 23:31:54 UTC (rev 4593)
@@ -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/branches/users/gaya/addigestauth/support/PyOpenDirectory.xcodeproj/project.pbxproj
===================================================================
--- PyOpenDirectory/branches/users/gaya/addigestauth/support/PyOpenDirectory.xcodeproj/project.pbxproj	2009-10-16 23:25:33 UTC (rev 4592)
+++ PyOpenDirectory/branches/users/gaya/addigestauth/support/PyOpenDirectory.xcodeproj/project.pbxproj	2009-10-16 23:31:54 UTC (rev 4593)
@@ -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/branches/users/gaya/addigestauth/support/test.cpp
===================================================================
--- PyOpenDirectory/branches/users/gaya/addigestauth/support/test.cpp	2009-10-16 23:25:33 UTC (rev 4592)
+++ PyOpenDirectory/branches/users/gaya/addigestauth/support/test.cpp	2009-10-16 23:31:54 UTC (rev 4593)
@@ -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/branches/users/gaya/addigestauth/test_auth.py
===================================================================
--- PyOpenDirectory/branches/users/gaya/addigestauth/test_auth.py	2009-10-16 23:25:33 UTC (rev 4592)
+++ PyOpenDirectory/branches/users/gaya/addigestauth/test_auth.py	2009-10-16 23:31:54 UTC (rev 4593)
@@ -143,41 +143,61 @@
         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( '    User node= "%s"' % nodename)
+    if nodename.startswith("/Active Directory/"):
+
+        challenge = opendirectory.getDigestMD5ChallengeFromActiveDirectory(od, nodename)
+        response = "bogus"
+        print "    Challenge: %s" % (challenge,)
+        print "    Response:  %s" % (response, )
+        
+        for _ignore_x in xrange(attempts):
+            success = opendirectory.authenticateUserDigestToActiveDirectory(
+                od, 
+                nodename,
+                username,
+                response,
             )
-    #print expected
-    
-    if qop:
-        challenge = 'Digest realm="%s", nonce="%s", algorithm=%s, qop="%s"' % (realm, nonce, algorithm, qop,)
+        
+            if not success:
+                failures += 1
     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, ))
     
-    print "    Challenge: %s" % (challenge,)
-    print "    Response:  %s" % (response, )
+        expected = calcResponse(
+                    calcHA1(algorithm, username, realm, password, nonce, cnonce),
+                    algorithm, nonce, nc, cnonce, qop, method, uri, None
+                )
+        #print expected
+        
+        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, ))
+        
+        print "    Challenge: %s" % (challenge,)
+        print "    Response:  %s" % (response, )
+        
+        for _ignore_x in xrange(attempts):
+            success = opendirectory.authenticateUserDigest(
+                od, 
+                nodename,
+                username,
+                challenge,
+                response,
+                method
+            )
+        
+            if not success:
+                failures += 1
     
-    for _ignore_x in xrange(attempts):
-        success = opendirectory.authenticateUserDigest(
-            od, 
-            nodename,
-            username,
-            challenge,
-            response,
-            method
-        )
-    
-        if not success:
-            failures += 1
-    
     print "\n%d failures out of %d attempts for Digest.\n\n" % (failures, attempts)
 
 def doAuthBasic(username, password):
@@ -216,6 +236,6 @@
 
 od = opendirectory.odInit(search)
 
-#doAuthBasic(user, pswd)
+doAuthBasic(user, pswd)
 doAuthDigest(user, pswd, None, "md5")
 
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.macosforge.org/pipermail/calendarserver-changes/attachments/20091016/357bf64f/attachment-0001.html>


More information about the calendarserver-changes mailing list