]> git.ipfire.org Git - thirdparty/freeradius-server.git/commitdiff
Switch to OpenDirectory & NetLogon frameworks. v2.1.x-apple 1388/head
authorBryan Duncan <bduncan@apple.com>
Tue, 21 Jun 2011 21:11:11 +0000 (14:11 -0700)
committerAlan T. DeKok <aland@freeradius.org>
Thu, 18 Aug 2011 01:29:06 +0000 (21:29 -0400)
src/modules/rlm_mschap/Makefile
src/modules/rlm_mschap/opendir.c
src/modules/rlm_mschap/rlm_mschap.c
src/modules/rlm_opendirectory/configure
src/modules/rlm_opendirectory/configure.in
src/modules/rlm_opendirectory/rlm_opendirectory.c

index d2d5813bee5614cdbad8da1940318ae52577980f..63148a7afc32a569379c0a7e3ff1c42651d4451d 100644 (file)
@@ -7,9 +7,10 @@
 TARGET         = rlm_mschap
 SRCS           = rlm_mschap.c mschap.c smbdes.c opendir.c
 HEADERS                = mschap.h smbdes.h
-RLM_CFLAGS     =
+RLM_CFLAGS     = -F /System/Library/PrivateFrameworks
 RLM_LDFLAGS    =
-RLM_LIBS       =
+# libtool doesn't seem to pass the -F option to the linker so provide the full path to nt framework
+RLM_LIBS       = -framework OpenDirectory /System/Library/PrivateFrameworks/nt.framework/nt
 RLM_UTILS      = smbencrypt
 RLM_INSTALL    = smbencrypt-install
 
index f9481b113eda7f53086fe7a6c0cf01cca3d71e44..34628f7df85a6a7929c6c2588e5451351ad246fc 100644 (file)
  *   along with this program; if not, write to the Free Software
  *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  *
- * Copyright 2007 Apple Inc.
+ * Copyright 2007-2010 Apple Inc.  All rights reserved.
  */
 
-#include       <freeradius-devel/ident.h>
+#include        <freeradius-devel/ident.h>
 RCSID("$Id$")
 
-#include       <freeradius-devel/radiusd.h>
-#include       <freeradius-devel/modules.h>
-#include       <freeradius-devel/rad_assert.h>
+#include        <freeradius-devel/radiusd.h>
+#include        <freeradius-devel/modules.h>
+#include        <freeradius-devel/rad_assert.h>
 #include        <freeradius-devel/md5.h>
 
-#include       <ctype.h>
+#include        <ctype.h>
 
-#include       "smbdes.h"
+#include        "mschap.h"
 
-#include <DirectoryService/DirectoryService.h>
+#include        <OpenDirectory/OpenDirectory.h>
+#include        <nt/ntlm.h>
 
-#define kActiveDirLoc "/Active Directory/"
 
-static int getUserNodeRef(char* inUserName, char **outUserName,
-                         tDirNodeReference* userNodeRef, tDirReference dsRef)
+extern void mschap_add_reply(REQUEST *request, VALUE_PAIR** vp, unsigned char ident,
+                             const char* name, const char* value, int len);
+
+/*
+ * Finds the record in given node.
+ *
+ * Can return NULL.  If non-NULL is returned, caller must CFRelease() the
+ * returned value.
+ */
+static ODRecordRef od_find_rec(REQUEST* request, ODNodeRef node, CFStringRef recName, const char* recNameStr)
 {
-       tDataBuffer             *tDataBuff      = NULL;
-       tDirNodeReference       nodeRef         = 0;
-       long                    status          = eDSNoErr;
-       tContextData            context         = 0;
-       unsigned long           nodeCount       = 0;
-       uint32_t                attrIndex       = 0;
-       tDataList               *nodeName       = NULL;
-       tAttributeEntryPtr      pAttrEntry      = NULL;
-       tDataList               *pRecName       = NULL;
-       tDataList               *pRecType       = NULL;
-       tDataList               *pAttrType      = NULL;
-       unsigned long           recCount        = 0;
-       tRecordEntry            *pRecEntry      = NULL;
-       tAttributeListRef       attrListRef     = 0;
-       char                    *pUserLocation  = NULL;
-       tAttributeValueListRef  valueRef        = 0;
-       tAttributeValueEntry    *pValueEntry    = NULL;
-       tDataList               *pUserNode      = NULL;
-       int                     result          = RLM_MODULE_FAIL;
-       
-       if (inUserName == NULL) {
-               radlog(L_ERR, "rlm_mschap: getUserNodeRef(): no username");
-               return RLM_MODULE_FAIL;
-       }
-    
-       tDataBuff = dsDataBufferAllocate(dsRef, 4096);
-       if (tDataBuff == NULL) {
-               radlog(L_ERR, "rlm_mschap: getUserNodeRef(): dsDataBufferAllocate() status = %ld", status);  
-               return RLM_MODULE_FAIL;
-       }
-       
-       do {
-               /* find on search node */
-               status = dsFindDirNodes(dsRef, tDataBuff, NULL,
-                                       eDSAuthenticationSearchNodeName,
-                                       &nodeCount, &context);
-               if (status != eDSNoErr) {
-                       radlog(L_ERR,"rlm_mschap: getUserNodeRef(): no node found? status = %ld", status);  
-                       result = RLM_MODULE_FAIL;
-                       break;
-               }
-               if (nodeCount < 1) {
-                       radlog(L_ERR,"rlm_mschap: getUserNodeRef(): nodeCount < 1, status = %ld", status);  
-                       result = RLM_MODULE_FAIL;
-                       break;
-               }
-               
-               status = dsGetDirNodeName(dsRef, tDataBuff, 1, &nodeName);
-               if (status != eDSNoErr) {
-                       radlog(L_ERR,"rlm_mschap: getUserNodeRef(): dsGetDirNodeName() status = %ld", status);  
-                       result = RLM_MODULE_FAIL;
-                       break;
-               }
-               
-               status = dsOpenDirNode(dsRef, nodeName, &nodeRef);
-               dsDataListDeallocate(dsRef, nodeName);
-               free(nodeName);
-               nodeName = NULL;
-               
-               if (status != eDSNoErr) {
-                       radlog(L_ERR,"rlm_mschap: getUserNodeRef(): dsOpenDirNode() status = %ld", status);  
-                       result = RLM_MODULE_FAIL;
-                       break;
-               }
-               
-               pRecName = dsBuildListFromStrings(dsRef, inUserName, NULL);
-               pRecType = dsBuildListFromStrings(dsRef, kDSStdRecordTypeUsers,
-                                                 NULL);
-               pAttrType = dsBuildListFromStrings(dsRef,
-                                                  kDSNAttrMetaNodeLocation,
-                                                  kDSNAttrRecordName, NULL);
-               
-               recCount = 1;
-               status = dsGetRecordList(nodeRef, tDataBuff, pRecName,
-                                        eDSExact, pRecType, pAttrType, 0,
-                                        &recCount, &context);
-               if (status != eDSNoErr || recCount == 0) {
-                       radlog(L_ERR,"rlm_mschap: getUserNodeRef(): dsGetRecordList() status = %ld, recCount=%lu", status, recCount);  
-                       result = RLM_MODULE_FAIL;
-                       break;
-               }
-               
-               status = dsGetRecordEntry(nodeRef, tDataBuff, 1,
-                                         &attrListRef, &pRecEntry);
-               if (status != eDSNoErr) {
-                       radlog(L_ERR,"rlm_mschap: getUserNodeRef(): dsGetRecordEntry() status = %ld", status);  
-                       result = RLM_MODULE_FAIL;
-                       break;  
-               }
-               
-               for (attrIndex = 1; (attrIndex <= pRecEntry->fRecordAttributeCount) && (status == eDSNoErr); attrIndex++) {
-                       status = dsGetAttributeEntry(nodeRef, tDataBuff, attrListRef, attrIndex, &valueRef, &pAttrEntry);
-                       if (status == eDSNoErr && pAttrEntry != NULL) {
-                               if (strcmp(pAttrEntry->fAttributeSignature.fBufferData, kDSNAttrMetaNodeLocation) == 0) {
-                                       status = dsGetAttributeValue(nodeRef, tDataBuff, 1, valueRef, &pValueEntry);
-                                       if (status == eDSNoErr && pValueEntry != NULL) {
-                                               pUserLocation = (char *) calloc(pValueEntry->fAttributeValueData.fBufferLength + 1, sizeof(char));
-                                               memcpy(pUserLocation, pValueEntry->fAttributeValueData.fBufferData, pValueEntry->fAttributeValueData.fBufferLength);
-                                       }
-                               } else if (strcmp(pAttrEntry->fAttributeSignature.fBufferData, kDSNAttrRecordName) == 0) {
-                                       status = dsGetAttributeValue(nodeRef, tDataBuff, 1, valueRef, &pValueEntry);
-                                       if (status == eDSNoErr && pValueEntry != NULL) {
-                                               *outUserName = (char *) malloc(pValueEntry->fAttributeValueData.fBufferLength + 1);
-                                               bzero(*outUserName,pValueEntry->fAttributeValueData.fBufferLength + 1);
-                                               memcpy(*outUserName, pValueEntry->fAttributeValueData.fBufferData, pValueEntry->fAttributeValueData.fBufferLength);
-                                       }
-                               }
-                               
-                               if (pValueEntry != NULL) {
-                                       dsDeallocAttributeValueEntry(dsRef, pValueEntry);
-                                       pValueEntry = NULL;
-                               }
-                               
-                               dsDeallocAttributeEntry(dsRef, pAttrEntry);
-                               pAttrEntry = NULL;
-                               dsCloseAttributeValueList(valueRef);
-                               valueRef = 0;
-                       }
-               }
-               
-               /* OpenDirectory doesn't support mschapv2 authentication against
-                * Active Directory.  AD users need to be authenticated using the
-                * normal freeradius AD path (i.e. ntlm_auth).
-                */
-               if (strncmp(pUserLocation, kActiveDirLoc, strlen(kActiveDirLoc)) == 0) {
-                       DEBUG2("[mschap] OpenDirectory authentication returning noop.  OD doesn't support MSCHAPv2 for ActiveDirectory users.");
-                       result = RLM_MODULE_NOOP;
-                       break;
-               }
-        
-               pUserNode = dsBuildFromPath(dsRef, pUserLocation, "/");
-               if (pUserNode == NULL) {
-                       radlog(L_ERR,"rlm_mschap: getUserNodeRef(): dsBuildFromPath() returned NULL");  
-                       result = RLM_MODULE_FAIL;
-                       break;
-               }
-               
-               status = dsOpenDirNode(dsRef, pUserNode, userNodeRef);
-               dsDataListDeallocate(dsRef, pUserNode);
-               free(pUserNode);
-
-               if (status != eDSNoErr) {
-                       radlog(L_ERR,"rlm_mschap: getUserNodeRef(): dsOpenDirNode() status = %ld", status);  
-                       result = RLM_MODULE_FAIL;
-                       break;
-               }
-               
-               result = RLM_MODULE_OK;
-       }
-       while (0);
-       
-       if (pRecEntry != NULL)
-               dsDeallocRecordEntry(dsRef, pRecEntry);
-
-       if (tDataBuff != NULL)
-               dsDataBufferDeAllocate(dsRef, tDataBuff);
-               
-       if (pUserLocation != NULL)
-               free(pUserLocation);
-               
-       if (pRecName != NULL) {
-               dsDataListDeallocate(dsRef, pRecName);
-               free(pRecName);
-       }
-       if (pRecType != NULL) {
-               dsDataListDeallocate(dsRef, pRecType);
-               free(pRecType);
-       }
-       if (pAttrType != NULL) {
-               dsDataListDeallocate(dsRef, pAttrType);
-               free(pAttrType);
-       }
-       if (nodeRef != 0)
-               dsCloseDirNode(nodeRef);
-       
-       return  result;
+    if (!node || !recName) return NULL;
+
+    ODRecordRef rec = NULL;
+
+    ODQueryRef query = ODQueryCreateWithNode(kCFAllocatorDefault,
+                                             node,
+                                             kODRecordTypeUsers,
+                                             kODAttributeTypeRecordName,
+                                             kODMatchEqualTo,
+                                             recName,
+                                             NULL,
+                                             0,
+                                             NULL);
+
+    if (!query) {
+        RDEBUG2("Unable to create OD query for %s", recNameStr);
+    } else {
+        CFArrayRef queryResults = ODQueryCopyResults(query, false, NULL);
+        if (queryResults == NULL || CFArrayGetCount(queryResults) == 0) {
+            RDEBUG2("Unable to find record %s in OD", recNameStr);
+        } else {
+            rec = (ODRecordRef)CFArrayGetValueAtIndex(queryResults, 0);
+            CFRetain(rec);
+            CFRelease(queryResults);
+        }
+
+        CFRelease(query);
+    }
+
+    return rec;
 }
 
 
-int od_mschap_auth(REQUEST *request, VALUE_PAIR *challenge,
-                  VALUE_PAIR * usernamepair)
+static CFErrorRef create_nt_error(uint32_t nt_status)
 {
-       tDirStatus              status           = eDSNoErr;
-       tDirReference           dsRef            = 0;
-       tDirNodeReference       userNodeRef      = 0;
-       tDataBuffer             *tDataBuff       = NULL;
-       tDataBuffer             *pStepBuff       = NULL;
-       tDataNode               *pAuthType       = NULL;
-       uint32_t                uiCurr           = 0;
-       uint32_t                uiLen            = 0;
-       char                    *username_string = NULL;
-       char                    *shortUserName   = NULL;
-       VALUE_PAIR              *response        = pairfind(request->packet->vps, PW_MSCHAP2_RESPONSE);
-#ifndef NDEBUG
-       int t;
-#endif
-       
-       username_string = (char *) malloc(usernamepair->length + 1);
-       if (username_string == NULL)
-               return RLM_MODULE_FAIL;
-       
-       strlcpy(username_string, (char *)usernamepair->vp_strvalue,
-               usernamepair->length + 1);
-       
-       status = dsOpenDirService(&dsRef);
-       if (status != eDSNoErr) {
-               free(username_string);
-               radlog(L_ERR,"rlm_mschap: od_mschap_auth(): dsOpenDirService = %d", status);
-               return RLM_MODULE_FAIL;
-       }
+    CFStringRef desc = NULL;
+    switch (nt_status) {
+        case 0xC000005E: // STATUS_NO_LOGON_SERVERS
+            desc = CFSTR("no logon servers");
+            break;
+        case 0xC0000064: // STATUS_NO_SUCH_USER
+            desc = CFSTR("no such user");
+            break;
+        case 0xC000006A: // STATUS_WRONG_PASSWORD
+            desc = CFSTR("no wrong password");
+            break;
+        case 0xC000006D: // STATUS_LOGON_FAILURE
+            desc = CFSTR("logon failure");
+            desc = CFStringCreateCopy(kCFAllocatorDefault, desc);
+            break;
+        case 0xC000006F: // STATUS_INVALID_LOGON_HOURS
+            desc = CFSTR("invalid logon hours");
+            break;
+        case 0xC0000070: // STATUS_INVALID_WORKSTATION
+            desc = CFSTR("invalid workstation");
+            break;
+        case 0xC0000071: // STATUS_PASSWORD_EXPIRED
+            desc = CFSTR("password expired");
+            break;
+        case 0xC0000072: // STATUS_ACCOUNT_DISABLED
+            desc = CFSTR("account disabled");
+            break;
+        case 0xC000000D: // STATUS_INVALID_PARAMETER
+            desc = CFSTR("invalid parameter");
+            break;
+        default:
+            desc = CFSTR("unknown error");
+            break;
+    }
+
+    return CFErrorCreateWithUserInfoKeysAndValues(kCFAllocatorDefault,
+                                                  CFSTR("com.apple.netlogon.freeradius"),
+                                                  nt_status,
+                                                  (const void* const*)&kCFErrorDescriptionKey,
+                                                  (const void* const*)&desc,
+                                                  1);
+}
+
+/*
+ * Handles NT auth for AD users.
+ */
+static int od_nt_auth(REQUEST*    request,
+                      VALUE_PAIR* response,
+                      VALUE_PAIR* challenge,
+                      ODNodeRef   node,
+                      ODRecordRef rec,
+                      CFStringRef recName,
+                      const char* username_string,
+                      CFErrorRef* error)
+{
+    int status = RLM_MODULE_REJECT;
+
+    NTLM_LOGON_REQ logonReq = {
+        .Version = NTLM_LOGON_REQ_VERSION,
+        .LogonDomainName = NULL,
+        .UserName = username_string,
+        .Workstation = NULL,
+        .LmChallenge = { 0 },
+        .LmChallengeResponseLength = 0,
+        .LmChallengeResponse = NULL,
+        .NtChallengeResponseLength = 24,
+        .NtChallengeResponse = response->vp_octets + 26
+    };
+
+    char *accountName = NULL;
+    char *accountDomain = NULL;
+    uint32_t userFlags = 0;
+    uint8_t sessionKey[16] = { 0 };
     
-       status = getUserNodeRef(username_string, &shortUserName, &userNodeRef, dsRef);
-       if(status != RLM_MODULE_OK) {
-               if (status != RLM_MODULE_NOOP) {
-                       RDEBUG2("od_mschap_auth: getUserNodeRef() failed");
-               }
-               if (username_string != NULL)
-                       free(username_string);
-               if (dsRef != 0)
-                       dsCloseDirService(dsRef);
-               return status;
-       }
-       
-       /* We got a node; fill the stepBuffer 
-          kDSStdAuthMSCHAP2
-          MS-CHAPv2 authentication method. The Open Directory plug-in generates the reply data for the client. 
-          The input buffer format consists of 
-          a four byte length specifying the length of the user name that follows, the user name, 
-          a four byte value specifying the length of the server challenge that follows, the server challenge, 
-          a four byte value specifying the length of the peer challenge that follows, the peer challenge, 
-          a four byte value specifying the length of the client's digest that follows, and the client's digest. 
-          The output buffer consists of a four byte value specifying the length of the return digest for the client's challenge.
-          r = FillAuthBuff(pAuthBuff, 5,
-          strlen(inName), inName,                                              // Directory Services long or short name
-          strlen(schal), schal,                                                // server challenge
-          strlen(peerchal), peerchal,                                  // client challenge
-          strlen(p24), p24,                                                    // P24 NT-Response
-          4, "User");                                                                  // must match the username that was used for the hash
-               
-          inName               =       username_string
-          schal                =   challenge->vp_strvalue
-          peerchal     =   response->vp_strvalue + 2 (16 octets)
-          p24                  =   response->vp_strvalue + 26 (24 octets)
-       */
-
-       pStepBuff = dsDataBufferAllocate(dsRef, 4096);
-       tDataBuff = dsDataBufferAllocate(dsRef, 4096);
-       pAuthType = dsDataNodeAllocateString(dsRef, kDSStdAuthMSCHAP2);
-       uiCurr = 0;
-       
-       RDEBUG2("OD username_string = %s, OD shortUserName=%s (length = %lu)\n", username_string, shortUserName, strlen(shortUserName));
-       
-       /* User name length + username */
-       uiLen = (uint32_t)strlen(shortUserName);
-       memcpy(&(tDataBuff->fBufferData[uiCurr]), &uiLen, sizeof(uiLen));
-       uiCurr += sizeof(uiLen);
-       memcpy(&(tDataBuff->fBufferData[uiCurr]), shortUserName, uiLen);
-       uiCurr += uiLen;
-#ifndef NDEBUG
-       RDEBUG2("       stepbuf server challenge:\t");
-       for (t = 0; t < challenge->length; t++) {
-               fprintf(stderr, "%02x", challenge->vp_strvalue[t]);
-       }
-       fprintf(stderr, "\n");
-#endif
-       
-       /* server challenge (ie. my (freeRADIUS) challenge) */
-       uiLen = 16;
-       memcpy(&(tDataBuff->fBufferData[uiCurr]), &uiLen, sizeof(uiLen));
-       uiCurr += sizeof(uiLen);
-       memcpy(&(tDataBuff->fBufferData[uiCurr]), &(challenge->vp_strvalue[0]),
-              uiLen);
-       uiCurr += uiLen;
-       
-#ifndef NDEBUG
-       RDEBUG2("       stepbuf peer challenge:\t\t");
-       for (t = 2; t < 18; t++) {
-               fprintf(stderr, "%02x", response->vp_strvalue[t]);
-       }
-       fprintf(stderr, "\n");
-#endif
-       
-       /* peer challenge (ie. the client-generated response) */
-       uiLen = 16;
-       memcpy(&(tDataBuff->fBufferData[uiCurr]), &uiLen, sizeof(uiLen));
-       uiCurr += sizeof(uiLen);
-       memcpy(&(tDataBuff->fBufferData[uiCurr]), &(response->vp_strvalue[2]),
-              uiLen);
-       uiCurr += uiLen;        
-       
-#ifndef NDEBUG
-       RDEBUG2("       stepbuf p24:\t\t");
-       for (t = 26; t < 50; t++) {
-               fprintf(stderr, "%02x", response->vp_strvalue[t]);
-       }
-       fprintf(stderr, "\n");
-#endif
-       
-       /* p24 (ie. second part of client-generated response) */
-       uiLen =  24; /* strlen(&(response->vp_strvalue[26])); may contain NULL byte in the middle. */
-       memcpy(&(tDataBuff->fBufferData[uiCurr]), &uiLen, sizeof(uiLen));
-       uiCurr += sizeof(uiLen);
-       memcpy(&(tDataBuff->fBufferData[uiCurr]), &(response->vp_strvalue[26]),
-              uiLen);
-       uiCurr += uiLen;
-       
-       /* Client generated use name (short name?) */
-       uiLen =  (uint32_t)strlen(username_string);
-       memcpy(&(tDataBuff->fBufferData[uiCurr]), &uiLen, sizeof(uiLen));
-       uiCurr += sizeof(uiLen);
-       memcpy(&(tDataBuff->fBufferData[uiCurr]), username_string, uiLen);
-       uiCurr += uiLen;
-
-       tDataBuff->fBufferLength = uiCurr;
-       
-       status = dsDoDirNodeAuth(userNodeRef, pAuthType, 1, tDataBuff,
-                                pStepBuff, NULL);
-       if (status == eDSNoErr) {
-               if (pStepBuff->fBufferLength > 4) {
-                       uint32_t len;
-                       
-                       memcpy(&len, pStepBuff->fBufferData, sizeof(len));
-                       if (len == 40) {
-                               char mschap_reply[42] = { '\0' };
-                               pStepBuff->fBufferData[len+4] = '\0';
-                               mschap_reply[0] = 'S';
-                               mschap_reply[1] = '=';
-                               memcpy(&(mschap_reply[2]), &(pStepBuff->fBufferData[4]), len);
-                               mschap_add_reply(request, &request->reply->vps,
-                                                *response->vp_strvalue,
-                                                "MS-CHAP2-Success",
-                                                mschap_reply, len+2);
-                               RDEBUG2("dsDoDirNodeAuth returns stepbuff: %s (len=%ld)\n", mschap_reply, len);
-                       }
-               }
-       }
-
-       /* clean up */
-       if (username_string != NULL)
-               free(username_string);
-       if (shortUserName != NULL)
-               free(shortUserName);
-
-       if (tDataBuff != NULL)
-               dsDataBufferDeAllocate(dsRef, tDataBuff);
-       if (pStepBuff != NULL)
-               dsDataBufferDeAllocate(dsRef, pStepBuff);
-       if (pAuthType != NULL)
-               dsDataNodeDeAllocate(dsRef, pAuthType);
-       if (userNodeRef != 0)
-               dsCloseDirNode(userNodeRef);
-       if (dsRef != 0)
-               dsCloseDirService(dsRef);
-       
-       if (status != eDSNoErr) {
-               errno = EACCES;
-               radlog(L_ERR, "rlm_mschap: authentication failed %d", status); /* <-- returns -14091 (eDSAuthMethodNotSupported) -14090 */
-               return RLM_MODULE_REJECT;
-       }
-       
-       return RLM_MODULE_OK;
+    CFDataRef serverChallenge = NULL;
+    if (challenge->length == 8) {
+        serverChallenge = CFDataCreate(kCFAllocatorDefault, (UInt8*)challenge->vp_strvalue, 8);
+    } else if (challenge->length == 16) {
+        uint8_t buffer[32];
+        mschap_challenge_hash(response->vp_octets + 2,
+                              challenge->vp_octets,
+                              username_string,
+                              buffer);
+
+        serverChallenge = CFDataCreate(kCFAllocatorDefault, (UInt8*)buffer, 8);
+    }
+
+    if (serverChallenge) {
+        memcpy(logonReq.LmChallenge, CFDataGetBytePtr(serverChallenge), 8);
+    }
+
+    uint32_t auth_status = NTLMLogon(&logonReq, NULL, NULL, &accountName, &accountDomain, sessionKey, &userFlags);
+    if (auth_status != 0) {
+        *error = create_nt_error(auth_status);
+    } else {
+        char mschap_reply[42];
+        memset(mschap_reply, 0, sizeof(mschap_reply));
+
+        mschap_auth_response(username_string, /* without the domain */
+                             sessionKey, /* nt-hash-hash */
+                             response->vp_octets + 26, /* peer response */
+                             response->vp_octets + 2, /* peer challenge */
+                             challenge->vp_octets, /* our challenge */
+                             mschap_reply); /* calculated MPPE key */
+        mschap_add_reply(request, &request->reply->vps, *response->vp_octets,
+                         "MS-CHAP2-Success", mschap_reply, 42);
+
+        status = RLM_MODULE_OK;
+    }
+
+    CFRelease(serverChallenge);
+    return status;
+}
+
+/*
+ * Handles MSCHAPv2 auths for OD users.
+ */
+static int od_mschap_auth(REQUEST*    request,
+                          VALUE_PAIR* response,
+                          VALUE_PAIR* challenge,
+                          ODNodeRef   node,
+                          ODRecordRef rec,
+                          CFStringRef recName,
+                          CFErrorRef* error)
+{
+    int status = RLM_MODULE_REJECT;
+
+    /* Create the array of auth-specific data to pass to OD and do the auth. */
+    CFMutableArrayRef authItems = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks);
+    if (authItems) {
+        CFArrayInsertValueAtIndex(authItems, 0, recName);
+
+        CFDataRef serverChallenge = CFDataCreate(kCFAllocatorDefault, (UInt8*)challenge->vp_strvalue, 16);
+        CFArrayInsertValueAtIndex(authItems, 1, serverChallenge);
+        CFRelease(serverChallenge);
+
+        CFDataRef peerChallenge = CFDataCreate(kCFAllocatorDefault, (UInt8*)&response->vp_strvalue[2], 16);
+        CFArrayInsertValueAtIndex(authItems, 2, peerChallenge);
+        CFRelease(peerChallenge);
+
+        CFDataRef p24 = CFDataCreate(kCFAllocatorDefault, (UInt8*)&response->vp_strvalue[26], 24);
+        CFArrayInsertValueAtIndex(authItems, 3, p24);
+        CFRelease(p24);
+
+        CFArrayInsertValueAtIndex(authItems, 4, recName);
+        CFArrayRef returnedItems = NULL;
+        if (ODRecordVerifyPasswordExtended(rec, kODAuthenticationTypeMSCHAP2, authItems, &returnedItems, NULL, error) &&
+            returnedItems && CFArrayGetCount(returnedItems) == 1)
+        {
+            /* Extract the data from OD and create the reply. */
+            unsigned char* respData = NULL;
+            size_t respDataLen = 0;
+            CFTypeRef cfRespData = CFArrayGetValueAtIndex(returnedItems, 0);
+            if (CFGetTypeID(cfRespData) == CFStringGetTypeID()) {
+                respDataLen = CFStringGetLength(cfRespData);
+                respData = malloc(respDataLen + 1);
+                CFStringGetCString(cfRespData, (char*)respData, respDataLen+1, kCFStringEncodingUTF8);
+            } else if (CFGetTypeID(cfRespData) == CFDataGetTypeID()) {
+                respDataLen = CFDataGetLength(cfRespData);
+                respData = malloc(respDataLen);
+                CFDataGetBytes(cfRespData, CFRangeMake(0, respDataLen), respData);
+            }
+
+            if (respData) {
+                if (respDataLen == 40) {
+                    char mschap_reply[42];
+                    memset(mschap_reply, 0, sizeof(mschap_reply));
+                    mschap_reply[0] = 'S';
+                    mschap_reply[1] = '=';
+                    memcpy(&mschap_reply[2], respData, respDataLen);
+                    mschap_add_reply(request,
+                                     &request->reply->vps,
+                                     *response->vp_strvalue,
+                                     "MS-CHAP2-Success",
+                                     mschap_reply,
+                                     42);
+                    status = RLM_MODULE_OK;
+                }
+
+                free(respData);
+            }
+        }
+
+        if (returnedItems) CFRelease(returnedItems);
+        CFRelease(authItems);
+    }
+
+    return status;
+}
+
+
+/*
+ * Handles auths for both AD & OD users.
+ */
+int do_od_mschap(REQUEST*    request,
+                 VALUE_PAIR* response,
+                 VALUE_PAIR* challenge,
+                 const char* username_string)
+{
+    RDEBUG2("Using OpenDirectory to authenticate");
+
+    /* Open Search node for querying. */
+    ODNodeRef searchNode = ODNodeCreateWithName(kCFAllocatorDefault, kODSessionDefault, CFSTR("/Search"), NULL);
+    if (!searchNode) {
+        RDEBUG2("Unable to open OD search node");
+        return RLM_MODULE_FAIL;
+    }
+
+    /* Find the record to be used for the auth attempt. */
+    int         status  = RLM_MODULE_FAIL;
+    CFErrorRef  error   = NULL;
+    CFStringRef recName = CFStringCreateWithCString(kCFAllocatorDefault, username_string, kCFStringEncodingUTF8);
+    ODRecordRef rec     = od_find_rec(request, searchNode, recName, username_string);
+    if (rec) {
+        CFArrayRef vals = ODRecordCopyValues(rec, kODAttributeTypeMetaNodeLocation, NULL);
+        if (vals && CFArrayGetCount(vals) != 0) {
+            /* opendirectoryd supports MSCHAPv2 for OD users but not for AD
+             * users.  Use netlogon for AD users.
+             */
+            CFStringRef metaNodeLoc = CFArrayGetValueAtIndex(vals, 0);
+            if (CFStringFind(metaNodeLoc, CFSTR("/Active Directory/"), 0).location == kCFNotFound) {
+                RDEBUG2("Doing OD MSCHAPv2 auth");
+                status = od_mschap_auth(request, response, challenge, searchNode, rec, recName, &error);
+            } else {
+                RDEBUG2("Doing AD netlogon auth");
+                status = od_nt_auth(request, response, challenge, searchNode, rec, recName, username_string, &error);
+            }
+        }
+        CFRelease(rec);
+    }
+
+    if (recName) CFRelease(recName);
+    CFRelease(searchNode);
+
+    /* On success the auth functions have already created the response
+     * data since the work differs for AD & OD. Handle the error response
+     * here since it's common.
+     */
+    if (status == RLM_MODULE_OK) {
+        RDEBUG2("Successful authentication for %s", username_string);
+    } else {
+        mschap_add_reply(request, &request->reply->vps,
+                         *response->vp_octets,
+                         "MS-CHAP-Error", "E=691 R=1", 9);
+        if (error == NULL) {
+            RDEBUG2("Authentication failed for %s", username_string);
+        } else {
+            char* desc_str = NULL;
+            CFDictionaryRef userInfo = CFErrorCopyUserInfo(error);
+            if (userInfo) {
+                CFStringRef desc = CFDictionaryGetValue(userInfo, kCFErrorDescriptionKey);
+                if (desc) {
+                    size_t desc_str_size = CFStringGetLength(desc) + 1;
+                    desc_str = malloc(desc_str_size);
+                    if (desc_str) {
+                        if (!CFStringGetCString(desc, desc_str, desc_str_size, kCFStringEncodingUTF8)) {
+                            free(desc_str);
+                            desc_str = NULL;
+                        }
+                    }
+                }
+                CFRelease(userInfo);
+            }
+            RDEBUG2("Authentication failed for %s: error %d (0x%x): %s",
+                    username_string,
+                    CFErrorGetCode(error),
+                    CFErrorGetCode(error),
+                    desc_str ? desc_str : "unknown error");
+
+            if (desc_str) free(desc_str);
+            CFRelease(error);
+        }
+    }
+
+    return status;
 }
 
 #endif /* __APPLE__ */
index ea700047113389ee81fb7c5f351e8f4c1e13d79b..f099572f17f00da706629e1110805a0f4b5247cf 100644 (file)
@@ -37,7 +37,7 @@ RCSID("$Id$")
 #include       "smbdes.h"
 
 #ifdef __APPLE__
-extern int od_mschap_auth(REQUEST *request, VALUE_PAIR *challenge, VALUE_PAIR * usernamepair);
+extern int do_od_mschap(REQUEST* request, VALUE_PAIR* response, VALUE_PAIR* challenge, const char* username_string);
 #endif
 
 /* Allowable account control bits */
@@ -1203,23 +1203,6 @@ static int mschap_authenticate(void * instance, REQUEST *request)
                        return RLM_MODULE_REJECT;
                }
 
-#ifdef __APPLE__
-               /*
-                *  No "known good" NT-Password attribute.  Try to do
-                *  OpenDirectory authentication.
-                *
-                *  If OD determines the user is an AD user it will return noop, which
-                *  indicates the auth process should continue directly to AD.
-                *  Otherwise OD will determine auth success/fail.
-                */
-               if (!nt_password && inst->open_directory) {
-                       RDEBUG2("No NT-Password configured. Trying OpenDirectory Authentication.");
-                       int odStatus = od_mschap_auth(request, challenge, username);
-                       if (odStatus != RLM_MODULE_NOOP) {
-                               return odStatus;
-                       }
-               }
-#endif
                /*
                 *      The old "mschapv2" function has been moved to
                 *      here.
@@ -1237,6 +1220,12 @@ static int mschap_authenticate(void * instance, REQUEST *request)
                RDEBUG2("Told to do MS-CHAPv2 for %s with NT-Password",
                       username_string);
 
+#ifdef __APPLE__
+               if (inst->open_directory) {
+                       return do_od_mschap(request, response, challenge, username_string);
+               }
+#endif
+
                if (do_mschap(inst, request, nt_password, mschapv1_challenge,
                              response->vp_octets + 26, nthashhash,
                              do_ntlm_auth) < 0) {
index 25a9bd9fec5806c0dc56c5f65d26edc7e4a30985..542fb7a2535c7fbae124e75c518d4337a8ace2d9 100755 (executable)
@@ -2790,7 +2790,7 @@ ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $
 ac_compiler_gnu=$ac_cv_c_compiler_gnu
 
 
-       od_ldflags="${od_ldflags} -framework DirectoryService"
+       od_ldflags="${od_ldflags} -framework OpenDirectory"
 
 
 
index b315f5e83f63aeb0ed0ae2893fb418f6f4c27b85..1b623c931668ef78a9107502b510910212526a22 100644 (file)
@@ -7,7 +7,7 @@ if test x$with_[]modname != xno; then
        AC_PROG_CC
        AC_PROG_CPP
 
-       od_ldflags="${od_ldflags} -framework DirectoryService" 
+       od_ldflags="${od_ldflags} -framework OpenDirectory"
        
        FR_SMART_CHECK_INCLUDE(membership.h)
        if test "$ac_cv_header_membership_h" != "yes"; then
index a160b810301652d3b4cd2dd787b2cf5efec307fc..156909686b363e950b153215f3b45c3e0e22dffb 100644 (file)
  *   along with this program; if not, write to the Free Software
  *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  *
- * Copyright 2007 Apple Inc.
+ * Copyright 2007-2010 Apple Inc.  All rights reserved.
  */
 
 /*
  *     For a typical Makefile, add linker flag like this:
- *     LDFLAGS = -framework DirectoryService
+ *     LDFLAGS = -framework OpenDirectory
  */
 
 #include <freeradius-devel/radiusd.h>
@@ -37,8 +37,9 @@
 #include <pwd.h>
 #include <sys/types.h>
 #include <sys/stat.h>
+#include <uuid/uuid.h>
 
-#include <DirectoryService/DirectoryService.h>
+#include <OpenDirectory/OpenDirectory.h>
 #include <membership.h>
 
 #if HAVE_APPLE_SPI
@@ -52,235 +53,59 @@ int mbr_check_membership_refresh(const uuid_t user, uuid_t group, int *ismember)
 #define kRadiusSACLName                "com.apple.access_radius"
 #define kRadiusServiceName     "radius"
 
-#define kAuthType           "opendirectory"
+#define kAuthType               "opendirectory"
 
 /*
- *     od_check_passwd
+ * Finds the record in given node.
  *
- *  Returns: ds err
+ * Can return NULL.  If non-NULL is returned, caller must CFRelease() the
+ * returned value.
  */
-
-static long od_check_passwd(const char *uname, const char *password)
+static ODRecordRef od_find_rec(REQUEST* request, ODNodeRef node, CFStringRef recName, const char* recNameStr)
 {
-       long                                            result                          = eDSAuthFailed;
-       tDirReference                           dsRef                           = 0;
-    tDataBuffer                                   *tDataBuff                   = NULL;
-    tDirNodeReference                  nodeRef                         = 0;
-    long                                               status                          = eDSNoErr;
-    tContextData                               context                         = 0;
-       unsigned long                           nodeCount                       = 0;
-       uint32_t                                attrIndex                       = 0;
-       tDataList                                  *nodeName                    = NULL;
-    tAttributeEntryPtr                 pAttrEntry                      = NULL;
-       tDataList                                  *pRecName                    = NULL;
-       tDataList                                  *pRecType                    = NULL;
-       tDataList                                  *pAttrType                   = NULL;
-       unsigned long                           recCount                        = 0;
-       tRecordEntry                            *pRecEntry                      = NULL;
-       tAttributeListRef                       attrListRef                     = 0;
-       char                                       *pUserLocation               = NULL;
-       char                                       *pUserName                   = NULL;
-       tAttributeValueListRef          valueRef                        = 0;
-       tAttributeValueEntry            *pValueEntry            = NULL;
-       tDataList                                  *pUserNode                   = NULL;
-       tDirNodeReference                       userNodeRef                     = 0;
-       tDataBuffer                                     *pStepBuff                      = NULL;
-       tDataNode                                  *pAuthType                   = NULL;
-       tAttributeValueEntry       *pRecordType                 = NULL;
-       uint32_t                                uiCurr                          = 0;
-       uint32_t                                uiLen                           = 0;
-       uint32_t                                pwLen                           = 0;
-       
-       if (uname == NULL || password == NULL)
-               return result;
-       
-       do
-       {               
-               status = dsOpenDirService( &dsRef );
-               if ( status != eDSNoErr )
-                       return result;
-               
-               tDataBuff = dsDataBufferAllocate( dsRef, 4096 );
-               if (tDataBuff == NULL)
-                       break;
-               
-               /* find user on search node */
-               status = dsFindDirNodes( dsRef, tDataBuff, NULL, eDSSearchNodeName, &nodeCount, &context );
-               if (status != eDSNoErr || nodeCount < 1)
-                       break;
-               
-               status = dsGetDirNodeName( dsRef, tDataBuff, 1, &nodeName );
-               if (status != eDSNoErr)
-                       break;
-               
-               status = dsOpenDirNode( dsRef, nodeName, &nodeRef );
-               dsDataListDeallocate( dsRef, nodeName );
-               free( nodeName );
-               nodeName = NULL;
-               if (status != eDSNoErr)
-                       break;
-
-               pRecName = dsBuildListFromStrings( dsRef, uname, NULL );
-               pRecType = dsBuildListFromStrings( dsRef, kDSStdRecordTypeUsers, kDSStdRecordTypeComputers, kDSStdRecordTypeMachines, NULL );
-               pAttrType = dsBuildListFromStrings( dsRef, kDSNAttrMetaNodeLocation, kDSNAttrRecordName, kDSNAttrRecordType, NULL );
-               
-               recCount = 1;
-               status = dsGetRecordList( nodeRef, tDataBuff, pRecName, eDSExact, pRecType,
-                                                                                                       pAttrType, 0, &recCount, &context );
-               if ( status != eDSNoErr || recCount == 0 )
-                       break;
-                               
-               status = dsGetRecordEntry( nodeRef, tDataBuff, 1, &attrListRef, &pRecEntry );
-               if ( status != eDSNoErr )
-                       break;
-               
-               for ( attrIndex = 1; (attrIndex <= pRecEntry->fRecordAttributeCount) && (status == eDSNoErr); attrIndex++ )
-               {
-                       status = dsGetAttributeEntry( nodeRef, tDataBuff, attrListRef, attrIndex, &valueRef, &pAttrEntry );
-                       if ( status == eDSNoErr && pAttrEntry != NULL )
-                       {
-                               if ( strcmp( pAttrEntry->fAttributeSignature.fBufferData, kDSNAttrMetaNodeLocation ) == 0 )
-                               {
-                                       status = dsGetAttributeValue( nodeRef, tDataBuff, 1, valueRef, &pValueEntry );
-                                       if ( status == eDSNoErr && pValueEntry != NULL )
-                                       {
-                                               pUserLocation = (char *) calloc( pValueEntry->fAttributeValueData.fBufferLength + 1, sizeof(char) );
-                                               memcpy( pUserLocation, pValueEntry->fAttributeValueData.fBufferData, pValueEntry->fAttributeValueData.fBufferLength );
-                                       }
-                               }
-                               else
-                               if ( strcmp( pAttrEntry->fAttributeSignature.fBufferData, kDSNAttrRecordName ) == 0 )
-                               {
-                                       status = dsGetAttributeValue( nodeRef, tDataBuff, 1, valueRef, &pValueEntry );
-                                       if ( status == eDSNoErr && pValueEntry != NULL )
-                                       {
-                                               pUserName = (char *) calloc( pValueEntry->fAttributeValueData.fBufferLength + 1, sizeof(char) );
-                                               memcpy( pUserName, pValueEntry->fAttributeValueData.fBufferData, pValueEntry->fAttributeValueData.fBufferLength );
-                                       }
-                               }
-                               else
-                               if ( strcmp( pAttrEntry->fAttributeSignature.fBufferData, kDSNAttrRecordType ) == 0 )
-                               {
-                                       status = dsGetAttributeValue( nodeRef, tDataBuff, 1, valueRef, &pValueEntry );
-                                       if ( status == eDSNoErr && pValueEntry != NULL )
-                                       {
-                                               pRecordType = pValueEntry;
-                                               pValueEntry = NULL;
-                                       }
-                               }
-                               
-                               if ( pValueEntry != NULL ) {
-                                       dsDeallocAttributeValueEntry( dsRef, pValueEntry );
-                                       pValueEntry = NULL;
-                               }
-                               if ( pAttrEntry != NULL ) {
-                                       dsDeallocAttributeEntry( dsRef, pAttrEntry );
-                                       pAttrEntry = NULL;
-                               }
-                               dsCloseAttributeValueList( valueRef );
-                               valueRef = 0;
-                       }
-               }
-               
-               pUserNode = dsBuildFromPath( dsRef, pUserLocation, "/" );
-               status = dsOpenDirNode( dsRef, pUserNode, &userNodeRef );
-               dsDataListDeallocate( dsRef, pUserNode );
-               free( pUserNode );
-               pUserNode = NULL;
-               if ( status != eDSNoErr )
-                       break;
-               
-               pStepBuff = dsDataBufferAllocate( dsRef, 128 );
-               
-               pAuthType = dsDataNodeAllocateString( dsRef, kDSStdAuthNodeNativeClearTextOK );
-               uiCurr = 0;
-               
-               /* User name */
-               uiLen = (uint32_t)strlen( pUserName );
-               memcpy( &(tDataBuff->fBufferData[ uiCurr ]), &uiLen, sizeof(uiLen) );
-               uiCurr += (uint32_t)sizeof( uiLen );
-               memcpy( &(tDataBuff->fBufferData[ uiCurr ]), pUserName, uiLen );
-               uiCurr += uiLen;
-               
-               /* pw */
-               pwLen = (uint32_t)strlen( password );
-               memcpy( &(tDataBuff->fBufferData[ uiCurr ]), &pwLen, sizeof(pwLen) );
-               uiCurr += (uint32_t)sizeof( pwLen );
-               memcpy( &(tDataBuff->fBufferData[ uiCurr ]), password, pwLen );
-               uiCurr += pwLen;
-               
-               tDataBuff->fBufferLength = uiCurr;
-               
-               result = dsDoDirNodeAuthOnRecordType( userNodeRef, pAuthType, 1, tDataBuff, pStepBuff, NULL, &pRecordType->fAttributeValueData );
-       }
-       while ( 0 );
-       
-       /* clean up */
-       if (pAuthType != NULL) {
-               dsDataNodeDeAllocate( dsRef, pAuthType );
-               pAuthType = NULL;
-       }
-       if (pRecordType != NULL) {
-               dsDeallocAttributeValueEntry( dsRef, pRecordType );
-               pRecordType = NULL;
-       }
-       if (tDataBuff != NULL) {
-               bzero( tDataBuff, tDataBuff->fBufferSize );
-               dsDataBufferDeAllocate( dsRef, tDataBuff );
-               tDataBuff = NULL;
-       }
-       if (pStepBuff != NULL) {
-               dsDataBufferDeAllocate( dsRef, pStepBuff );
-               pStepBuff = NULL;
-       }
-       if (pUserLocation != NULL) {
-               free(pUserLocation);
-               pUserLocation = NULL;
-       }
-       if (pRecName != NULL) {
-               dsDataListDeallocate( dsRef, pRecName );
-               free( pRecName );
-               pRecName = NULL;
-       }
-       if (pRecType != NULL) {
-               dsDataListDeallocate( dsRef, pRecType );
-               free( pRecType );
-               pRecType = NULL;
-       }
-       if (pAttrType != NULL) {
-               dsDataListDeallocate( dsRef, pAttrType );
-               free( pAttrType );
-               pAttrType = NULL;
-       }
-       if (nodeRef != 0) {
-               dsCloseDirNode(nodeRef);
-               nodeRef = 0;
-       }
-       if (dsRef != 0) {
-               dsCloseDirService(dsRef);
-               dsRef = 0;
-       }
-       
-       return result;
-}
+    if (!node || !recName) return NULL;
+
+    ODRecordRef rec = NULL;
+
+    ODQueryRef query = ODQueryCreateWithNode(kCFAllocatorDefault,
+                                             node,
+                                             kODRecordTypeUsers,
+                                             kODAttributeTypeRecordName,
+                                             kODMatchEqualTo,
+                                             recName,
+                                             NULL,
+                                             0,
+                                             NULL);
 
+    if (!query) {
+        RDEBUG2("Unable to create OD query for %s", recNameStr);
+    } else {
+        CFArrayRef queryResults = ODQueryCopyResults(query, false, NULL);
+        if (queryResults == NULL || CFArrayGetCount(queryResults) == 0) {
+            RDEBUG2("Unable to find record '%s' in OD", recNameStr);
+        } else {
+            rec = (ODRecordRef)CFArrayGetValueAtIndex(queryResults, 0);
+            CFRetain(rec);
+            CFRelease(queryResults);
+        }
+
+        CFRelease(query);
+    }
+
+    return rec;
+}
 
 /*
- *     Check the users password against the standard UNIX
- *     password table.
+ *     Check the users password against OD.
  */
 static int od_authenticate(UNUSED void *instance, REQUEST *request)
 {
-       char *name, *passwd;
-       int             ret;
-       long odResult = eDSAuthFailed;
-       
        /*
         *      We can only authenticate user requests which HAVE
         *      a User-Name attribute.
         */
        if (!request->username) {
-               RDEBUG("ERROR: You set 'Auth-Type = OpenDirectory' for a request that does not contain a User-Name attribute!");
+               RDEBUG("ERROR: Request does not contain a User-Name attribute!");
                return RLM_MODULE_INVALID;
        }
 
@@ -289,43 +114,64 @@ static int od_authenticate(UNUSED void *instance, REQUEST *request)
         */
        if (!request->password ||
            (request->password->attribute != PW_PASSWORD)) {
-               RDEBUG("ERROR: You set 'Auth-Type = OpenDirectory' for a request that does not contain a User-Password attribute!");
+               RDEBUG("ERROR: Request does not contain a User-Password attribute!");
                return RLM_MODULE_INVALID;
        }
        
-       name = (char *)request->username->vp_strvalue;
-       passwd = (char *)request->password->vp_strvalue;
-       
-       odResult = od_check_passwd(name, passwd);
-       switch(odResult)
-       {
-               case eDSNoErr:
-                       ret = RLM_MODULE_OK;
-                       break;
-                       
-               case eDSAuthUnknownUser:
-               case eDSAuthInvalidUserName:
-               case eDSAuthNewPasswordRequired:
-               case eDSAuthPasswordExpired:
-               case eDSAuthAccountDisabled:
-               case eDSAuthAccountExpired:
-               case eDSAuthAccountInactive:
-               case eDSAuthInvalidLogonHours:
-               case eDSAuthInvalidComputer:
-                       ret = RLM_MODULE_USERLOCK;
-                       break;
-               
-               default:
-                       ret = RLM_MODULE_REJECT;
-                       break;
+       /* Open Search node for querying. */
+       ODNodeRef searchNode = ODNodeCreateWithName(kCFAllocatorDefault, kODSessionDefault, CFSTR("/Search"), NULL);
+       if (!searchNode) {
+               RDEBUG2("Unable to open OD search node");
+               return RLM_MODULE_FAIL;
        }
-       
-       if (ret != RLM_MODULE_OK) {
-               RDEBUG("[%s]: Invalid password", name);
-               return ret;
+
+       CFStringRef username = CFStringCreateWithCString(kCFAllocatorDefault,
+                                                        request->username->vp_strvalue,
+                                                        kCFStringEncodingUTF8);
+
+       CFStringRef password = CFStringCreateWithCString(kCFAllocatorDefault,
+                                                        request->password->vp_strvalue,
+                                                        kCFStringEncodingUTF8);
+
+       int status = RLM_MODULE_REJECT;
+       CFErrorRef error = NULL;
+       ODRecordRef rec = od_find_rec(request, searchNode, username, request->username->vp_strvalue);
+       if (rec) {
+               if (ODRecordVerifyPassword(rec, password, &error)) {
+                       status = RLM_MODULE_OK;
+               } else {
+                       if (error == NULL) {
+                               RDEBUG2("Authentication failed for %s", request->username->vp_strvalue);
+                       } else {
+                               char* desc_str = NULL;
+                               CFStringRef desc = CFErrorCopyDescription(error);
+                               if (desc) {
+                                       size_t desc_str_size = CFStringGetLength(desc) + 1;
+                                       desc_str = malloc(desc_str_size);
+                                       if (desc_str) {
+                                               CFStringGetCString(desc,
+                                                                  desc_str,
+                                                                  desc_str_size,
+                                                                  kCFStringEncodingUTF8);
+                                       }
+                               }
+
+                               RDEBUG2("Authentication failed for %s: error %d: %s",
+                                       request->username->vp_strvalue, CFErrorGetCode(error),
+                                       desc_str ? desc_str : "unknown error");
+
+                               CFRelease(error);
+                       }
+               }
+
+               CFRelease(rec);
        }
-               
-       return RLM_MODULE_OK;
+
+       if (username) CFRelease(username);
+       if (password) CFRelease(password);
+       CFRelease(searchNode);
+
+       return status;
 }
 
 
@@ -334,8 +180,6 @@ static int od_authenticate(UNUSED void *instance, REQUEST *request)
  */
 static int od_authorize(UNUSED void *instance, REQUEST *request)
 {
-       char *name = NULL;
-       struct passwd *userdata = NULL;
        struct group *groupdata = NULL;
        int ismember = 0;
        RADCLIENT *rad_client = NULL;
@@ -404,27 +248,48 @@ static int od_authorize(UNUSED void *instance, REQUEST *request)
                }
        }
        
-       if (uuid_is_null(guid_sacl) && uuid_is_null(guid_nasgroup)) {
-               RDEBUG("no access control groups, all users allowed.");
-       if (pairfind(request->config_items, PW_AUTH_TYPE) == NULL) {
-               pairadd(&request->config_items, pairmake("Auth-Type", kAuthType, T_OP_EQ));
-               RDEBUG("Setting Auth-Type = %s", kAuthType);
-               }
-               return RLM_MODULE_OK;
-       }
-
        /* resolve user */
        uuid_clear(uuid);
-       name = (char *)request->username->vp_strvalue;
-       rad_assert(name != NULL);
-
-       userdata = getpwnam(name);
-       if (userdata != NULL) {
-               err = mbr_uid_to_uuid(userdata->pw_uid, uuid);
-               if (err != 0)
-                       uuid_clear(uuid);
+
+       ODNodeRef searchNode = ODNodeCreateWithName(kCFAllocatorDefault, kODSessionDefault, CFSTR("/Search"), NULL);
+       if (!searchNode) {
+               RDEBUG2("Unable to open OD search node");
+               return RLM_MODULE_FAIL;
        }
-       
+
+       CFStringRef username = CFStringCreateWithCString(kCFAllocatorDefault,
+                                                        request->username->vp_strvalue,
+                                                        kCFStringEncodingUTF8);
+
+       ODRecordRef rec = od_find_rec(request, searchNode, username, request->username->vp_strvalue);
+       if (!rec) {
+               RDEBUG("User %s does not exist in OD", request->username->vp_strvalue);
+       } else {
+               RDEBUG("User %s exists in OD", request->username->vp_strvalue);
+               CFArrayRef vals = ODRecordCopyValues(rec, kODAttributeTypeGUID, NULL);
+               if (!vals || CFArrayGetCount(vals) == 0) {
+                       RDEBUG("Could not find GUID for user %s", request->username->vp_strvalue);
+               } else {
+                   CFTypeRef user_guid = CFArrayGetValueAtIndex(vals, 0);
+                       if (CFGetTypeID(user_guid) == CFStringGetTypeID()) {
+                               size_t len = CFStringGetLength(user_guid) + 1;
+                               char* user_guid_str = malloc(len);
+                               if (user_guid_str) {
+                                       CFStringGetCString(user_guid, user_guid_str, len, kCFStringEncodingUTF8);
+                                       uuid_parse(user_guid_str, uuid);
+                               }
+                       }
+                       CFRelease(vals);
+                   }
+               CFRelease(rec);
+       }
+       if (username) CFRelease(username);
+       CFRelease(searchNode);
+
+       /*
+        * Check the user membership in the access groups (if they exist).
+        */
+
        if (uuid_is_null(uuid)) {
                radius_pairmake(request, &request->packet->vps,
                                "Module-Failure-Message", "Could not get the user's uuid", T_OP_EQ);
@@ -440,10 +305,13 @@ static int od_authorize(UNUSED void *instance, REQUEST *request)
                }
                
                if (ismember == 0) {
+                       RDEBUG("User %s is not a member of the RADUIS SACL", request->username->vp_strvalue);
                        radius_pairmake(request, &request->packet->vps,
                                        "Module-Failure-Message", "User is not authorized", T_OP_EQ);
-                       return RLM_MODULE_USERLOCK;
+                       return RLM_MODULE_REJECT;
                }
+
+               RDEBUG("User %s is a member of the RADUIS SACL", request->username->vp_strvalue);
        }
        
        if (!uuid_is_null(guid_nasgroup)) {
@@ -455,12 +323,19 @@ static int od_authorize(UNUSED void *instance, REQUEST *request)
                }
                
                if (ismember == 0) {
+                       RDEBUG("User %s is not a member of the host access group", request->username->vp_strvalue);
                        radius_pairmake(request, &request->packet->vps,
                                        "Module-Failure-Message", "User is not authorized", T_OP_EQ);
-                       return RLM_MODULE_USERLOCK;
+                       return RLM_MODULE_REJECT;
                }
+
+               RDEBUG("User %s is a member of the hostaccess group", request->username->vp_strvalue);
        }
        
+       if (uuid_is_null(guid_sacl) && uuid_is_null(guid_nasgroup)) {
+               RDEBUG("no access control groups, all OD users allowed.");
+       }
+
        if (pairfind(request->config_items, PW_AUTH_TYPE) == NULL) {
                pairadd(&request->config_items, pairmake("Auth-Type", kAuthType, T_OP_EQ));
                RDEBUG("Setting Auth-Type = %s", kAuthType);