pdb-module-open-directory [plain text]
Index: samba/source/Makefile.in
===================================================================
--- samba/source/Makefile.in.orig
+++ samba/source/Makefile.in
@@ -453,6 +453,8 @@ AUTH_OBJ = auth/auth.o @AUTH_STATIC@ aut
auth/auth_ntlmssp.o \
$(PLAINTEXT_AUTH_OBJ) $(SLCACHE_OBJ) $(DCUTIL_OBJ)
+ODSAM_PDB_OBJ = lib/opendirectory.o passdb/pdb_odsam.o
+
MANGLE_OBJ = smbd/mangle.o smbd/mangle_hash.o smbd/mangle_map.o smbd/mangle_hash2.o
SMBD_OBJ_MAIN = smbd/server.o
@@ -1334,6 +1336,13 @@ bin/smbpasswd.@SHLIBEXT@: proto_exists p
@echo "Building plugin $@"
@$(SHLD_MODULE) passdb/pdb_smbpasswd.o
+# Note: This module should not be built statically, due to the
+# external dependencies.
+bin/pdb_odsam.@SHLIBEXT@: $(ODSAM_PDB_OBJ)
+ @echo "Building plugin $@"
+ @$(SHLD_MODULE) $(ODSAM_PDB_OBJ) \
+ -framework DirectoryService
+
bin/rid.@SHLIBEXT@: proto_exists nsswitch/idmap_rid.o
@echo "Building plugin $@"
@$(SHLD_MODULE) nsswitch/idmap_rid.o
Index: samba/source/configure.in
===================================================================
--- samba/source/configure.in.orig
+++ samba/source/configure.in
@@ -6141,6 +6141,7 @@ SMB_MODULE(pdb_ldap, passdb/pdb_ldap.o p
[ PASSDB_LIBS="$PASSDB_LIBS $LDAP_LIBS" ] )
SMB_MODULE(pdb_smbpasswd, passdb/pdb_smbpasswd.o, "bin/smbpasswd.$SHLIBEXT", PDB)
SMB_MODULE(pdb_tdbsam, passdb/pdb_tdb.o, "bin/tdbsam.$SHLIBEXT", PDB)
+SMB_MODULE(pdb_odsam, \$(ODSAM_PDB_OBJ), "bin/pdb_odsam.$SHLIBEXT", PDB)
SMB_SUBSYSTEM(PDB,passdb/pdb_interface.o)
Index: samba/source/passdb/pdb_odsam.c
===================================================================
--- /dev/null
+++ samba/source/passdb/pdb_odsam.c
@@ -0,0 +1,2383 @@
+/*
+ Unix SMB/CIFS implementation.
+ passdb opendirectory backend
+
+ Copyright (c) 2003-2007 Apple Inc. All rights reserved.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#define USES_ODGROUPMAPPING 1
+
+#define BOOL_DEFINED
+
+#include "includes.h"
+
+#define USE_SETATTRIBUTEVALUE 1
+#define AUTH_GET_POLICY 0
+#define GLOBAL_DEFAULT 1
+#define WITH_PASSWORD_HASH 1
+
+static int odssam_debug_level = DBGC_ALL;
+static int module_debug;
+static uid_t guest_uid = (uid_t)-1;
+
+#undef DBGC_CLASS
+#define DBGC_CLASS odssam_debug_level
+
+#define MODULE_NAME "odsam"
+
+#include "opendirectory.h"
+
+struct odssam_privates {
+ struct opendirectory_session session;
+
+ /* saved state from last search */
+ CFMutableArrayRef usersArray;
+ CFMutableArrayRef groupsArray;
+
+ int usersIndex;
+ int groupsIndex;
+ tContextData contextData;
+ tContextData localContextData;
+};
+
+static enum ds_trace_level ds_trace = DS_TRACE_ERRORS;
+
+static struct odssam_privates *get_ods_state(struct pdb_methods *methods)
+{
+ struct odssam_privates *ods_state;
+ tDirStatus status;
+ tDirNodeReference dirRef;
+
+
+ ods_state = (struct odssam_privates *)methods->private_data;
+ SMB_ASSERT(ods_state != NULL);
+
+ dirRef = ods_state->session.ref;
+ status = opendirectory_reconnect(&ods_state->session);
+ if (status != eDSNoErr) {
+ /* Unable to restore our connection to the directory. We are
+ * toast for (at least) this call.
+ */
+ LOG_DS_ERROR(ds_trace, status, "opendirectory_reconnect");
+ return NULL;
+ }
+
+ return ods_state;
+}
+
+static uint32_t add_data_buffer_item(tDataBufferPtr dataBuffer,
+ uint32_t len, const void *buffer)
+{
+ uint32_t result = 0;
+
+ memcpy(&(dataBuffer->fBufferData[ dataBuffer->fBufferLength ]), &len, 4);
+ dataBuffer->fBufferLength += 4;
+ if (len != 0) {
+ memcpy(&(dataBuffer->fBufferData[ dataBuffer->fBufferLength ]), buffer, len);
+ dataBuffer->fBufferLength += len;
+ }
+
+ return result;
+}
+
+static BOOL GetCString(CFStringRef cfstr, char *outbuffer, int size)
+{
+ if (cfstr) {
+ return CFStringGetCString(cfstr, outbuffer,
+ size, kCFStringEncodingUTF8);
+ }
+
+ return False;
+}
+static BOOL is_machine_name(const char *name)
+{
+ if (name) {
+ return (name[strlen (name) -1] == '$');
+ }
+
+ return False;
+}
+const const char *get_record_type(const char *name)
+{
+ if (is_machine_name(name)) {
+ return kDSStdRecordTypeComputers;
+ } else {
+ return kDSStdRecordTypeUsers;
+ }
+}
+
+static tDirStatus odssam_open(struct odssam_privates *ods_state)
+{
+ tDirStatus dirStatus;
+
+ dirStatus = opendirectory_reconnect(&ods_state->session);
+ LOG_DS_ERROR_MSG(ds_trace, dirStatus, "dsOpenDirService",
+ ("refnum=%lu\n", (unsigned long)ods_state->session.ref));
+
+ return dirStatus;
+}
+
+static tDirStatus get_password_policy(struct odssam_privates *ods_state,
+ tDirNodeReference pwsNode,
+ const char *userid, char *policy,
+ const char *type)
+{
+ tDirStatus status = eDSNoErr;
+ unsigned long len = 0;
+ tDataBufferPtr authBuff = NULL;
+ tDataBufferPtr stepBuff = NULL;
+ tDataNodePtr authType = NULL;
+ tDataNodePtr recordType = NULL;
+
+ authBuff = dsDataBufferAllocate(ods_state->session.ref,
+ DEFAULT_DS_BUFFER_SIZE);
+ stepBuff = dsDataBufferAllocate(ods_state->session.ref,
+ DEFAULT_DS_BUFFER_SIZE);
+ if (authBuff == NULL || stepBuff == NULL) {
+ goto cleanup;
+ }
+
+ authType = dsDataNodeAllocateString(ods_state->session.ref,
+ /*kDSStdAuthGetEffectivePolicy*/
+ "dsAuthMethodStandard:dsAuthGetEffectivePolicy");
+ recordType = dsDataNodeAllocateString(ods_state->session.ref, type);
+ if (authType == NULL) {
+ goto cleanup;
+ }
+
+ // User Name (target account)
+ add_data_buffer_item(authBuff, strlen(userid), userid);
+
+ status = dsDoDirNodeAuthOnRecordType(pwsNode, authType, True,
+ authBuff, stepBuff, NULL, recordType);
+ LOG_DS_ERROR_MSG(ds_trace, status, "dsDoDirNodeAuthOnRecordType",
+ ("kDSStdAuthGetEffectivePolicy for user \"%s\"\n", userid));
+ if (status == eDSNoErr) {
+ memcpy(&len, stepBuff->fBufferData, 4);
+ stepBuff->fBufferData[len+4] = '\0';
+ safe_strcpy(policy,stepBuff->fBufferData+4, 1024);
+ }
+
+cleanup:
+ opendirectory_free_node(&ods_state->session, authType);
+ opendirectory_free_node(&ods_state->session, recordType);
+ opendirectory_free_buffer(&ods_state->session, authBuff);
+ opendirectory_free_buffer(&ods_state->session, stepBuff);
+
+ return status;
+}
+
+static tDirStatus set_password_policy(struct odssam_privates *ods_state,
+ tDirNodeReference pwsNode,
+ const char *userid,
+ const char *policy,
+ const char *type)
+{
+ tDirStatus status = eDSNoErr;
+ tDataBufferPtr authBuff = NULL;
+ tDataBufferPtr stepBuff = NULL;
+ tDataNodePtr authType = NULL;
+ tDataNodePtr recordType = NULL;
+
+
+ authBuff = dsDataBufferAllocate(ods_state->session.ref,
+ DEFAULT_DS_BUFFER_SIZE);
+ if (authBuff != NULL)
+ {
+ stepBuff = dsDataBufferAllocate(ods_state->session.ref,
+ DEFAULT_DS_BUFFER_SIZE);
+ if (stepBuff != NULL)
+ {
+ authType =
+ dsDataNodeAllocateString(ods_state->session.ref, "dsAuthMethodStandard:dsAuthSetPolicyAsRoot" /*kDSStdAuthSetPolicyAsRoot*/);
+ recordType =
+ dsDataNodeAllocateString(ods_state->session.ref, type);
+ if (authType != NULL)
+ {
+ add_data_buffer_item(authBuff, strlen(userid), userid);
+ add_data_buffer_item(authBuff, strlen(policy), policy);
+
+ status = dsDoDirNodeAuthOnRecordType(pwsNode, authType, True, authBuff, stepBuff, NULL, recordType);
+ if (status == eDSNoErr)
+ {
+ DEBUG(module_debug,("kDSStdAuthSetPolicyAsRoot was successful for user \"%s\" :)\n", userid));
+ }
+ else
+ {
+ DEBUG(module_debug,("kDSStdAuthSetPolicyAsRoot FAILED for user \"%s\" (%d) :(\n", userid, status));
+ }
+ }
+ }
+ else
+ {
+ DEBUG(module_debug,("set_password_policy: *** dsDataBufferAllocate(2) faild with \n"));
+ }
+ }
+ else
+ {
+ DEBUG(module_debug,("set_password_policy: *** dsDataBufferAllocate(1) faild with \n"));
+ }
+
+ opendirectory_free_node(&ods_state->session, authType);
+ opendirectory_free_node(&ods_state->session, recordType);
+ opendirectory_free_buffer(&ods_state->session, authBuff);
+ opendirectory_free_buffer(&ods_state->session, stepBuff);
+
+ return status;
+}
+
+static tDirStatus set_password(struct odssam_privates *ods_state,
+ tDirNodeReference userNode,
+ const char *user,
+ const char *passwordstring,
+ const char *passwordType,
+ const char *type)
+{
+ tDirStatus status = eDSNullParameter;
+ tDataBufferPtr authBuff = NULL;
+ tDataBufferPtr stepBuff = NULL;
+ tDataNodePtr authType = NULL;
+ tDataNodePtr recordType = NULL;
+ const char *password = NULL;
+ unsigned long passwordLen = 0;
+#if defined(kDSStdAuthSetWorkstationPasswd) && defined(kDSStdAuthSetLMHash)
+ uint8 binarypwd[NT_HASH_LEN];
+#endif
+
+ if (strcmp(passwordType, kDSStdAuthSetPasswdAsRoot) == 0) {
+ password = passwordstring;
+ passwordLen = strlen(password);
+#if defined(kDSStdAuthSetWorkstationPasswd) && defined(kDSStdAuthSetLMHash)
+ } else if (strcmp(passwordType, kDSStdAuthSetWorkstationPasswd) == 0 || strcmp(passwordType, kDSStdAuthSetLMHash) == 0) {
+ if (pdb_gethexpwd(passwordstring, binarypwd)) {
+ password = (const char*)binarypwd;
+ passwordLen = NT_HASH_LEN;
+ }
+#endif
+ } else {
+ return status;
+ }
+
+ authBuff = dsDataBufferAllocate(ods_state->session.ref,
+ DEFAULT_DS_BUFFER_SIZE);
+ if (authBuff != NULL && password && passwordLen)
+ {
+ stepBuff = dsDataBufferAllocate(ods_state->session.ref,
+ DEFAULT_DS_BUFFER_SIZE);
+ if (stepBuff != NULL)
+ {
+ authType =
+ dsDataNodeAllocateString(ods_state->session.ref, passwordType);
+ recordType =
+ dsDataNodeAllocateString(ods_state->session.ref, type);
+ if (authType != NULL)
+ {
+ // User Name
+ add_data_buffer_item(authBuff, strlen(user), user);
+ // Password
+ add_data_buffer_item(authBuff, passwordLen, password);
+ DEBUG(module_debug,("set_password len (%ld), password (%s) \n", passwordLen, password));
+
+ status = dsDoDirNodeAuthOnRecordType(userNode, authType, True, authBuff, stepBuff, NULL, recordType);
+ if (status == eDSNoErr)
+ {
+ DEBUG(module_debug,("Set password (%s) was successful for account \"%s\" accountType (%s) :)\n", passwordType, user, type));
+ }
+ else
+ {
+ DEBUG(module_debug,("Set password (%s) FAILED for account \"%s\" accountType (%s) (%d) :(\n", passwordType, user, type, status));
+ }
+ }
+ }
+ else
+ {
+ DEBUG(module_debug,("set_password: *** dsDataBufferAllocate(2) faild with \n"));
+ }
+ }
+ else
+ {
+ DEBUG(module_debug,("set_password: *** dsDataBufferAllocate(1) faild with \n"));
+ }
+
+
+ opendirectory_free_buffer(&ods_state->session, authBuff);
+ opendirectory_free_buffer(&ods_state->session, stepBuff);
+ opendirectory_free_node(&ods_state->session, authType);
+ opendirectory_free_node(&ods_state->session, recordType);
+
+ return status;
+}
+
+static tDirStatus get_record_ref(struct odssam_privates *ods_state,
+ tDirNodeReference nodeReference,
+ tRecordReference *ref,
+ const char *recordType,
+ const char *recordName)
+{
+ tDirStatus status = eDSNoErr;
+ tDataNodePtr recordNameNode = NULL;
+ tDataNodePtr recordTypeNode = NULL;
+
+ recordNameNode = dsDataNodeAllocateString(ods_state->session.ref,
+ recordName);
+ recordTypeNode = dsDataNodeAllocateString(ods_state->session.ref,
+ recordType);
+ status = dsOpenRecord(nodeReference, recordTypeNode,
+ recordNameNode, ref);
+ LOG_DS_ERROR(ds_trace, status, "dsOpenRecord");
+
+ opendirectory_free_node(&ods_state->session, recordNameNode);
+ opendirectory_free_node(&ods_state->session, recordTypeNode);
+
+ return status;
+}
+
+static tDirStatus set_recordname(struct odssam_privates *ods_state,
+ tRecordReference recordReference,
+ const char *value)
+{
+ tDirStatus status = eDSNullParameter;
+ tDataNodePtr attributeValue = NULL;
+
+ if (value && *value) {
+ attributeValue =
+ dsDataNodeAllocateString(ods_state->session.ref, value);
+ status = dsSetRecordName(recordReference, attributeValue);
+ LOG_DS_ERROR(ds_trace, status, "dsSetRecordName");
+ }
+ opendirectory_free_node(&ods_state->session, attributeValue);
+ return status;
+}
+
+static BOOL isPWSAttribute(const char *attribute)
+{
+ BOOL result = false;
+
+ if ((strcmp(attribute, kPWSIsDisabled) == 0) ||
+ (strcmp(attribute, kPWSIsAdmin) == 0) ||
+ (strcmp(attribute, kPWSExpiryDateEnabled) == 0) ||
+ (strcmp(attribute, kPWSNewPasswordRequired) == 0) ||
+ (strcmp(attribute, kPWSIsUsingHistory) == 0) ||
+ (strcmp(attribute, kPWSCanChangePassword) == 0) ||
+ (strcmp(attribute, kPWSExpiryDateEnabled) == 0) ||
+ (strcmp(attribute, kPWSRequiresAlpha) == 0) ||
+ (strcmp(attribute, kPWSExpiryDate) == 0) ||
+ (strcmp(attribute, kPWSHardExpiryDate) == 0) ||
+ (strcmp(attribute, kPWSMaxMinChgPwd) == 0) ||
+ (strcmp(attribute, kPWSMaxMinActive) == 0) ||
+ (strcmp(attribute, kPWSMaxFailedLogins) == 0) ||
+ (strcmp(attribute, kPWSMinChars) == 0) ||
+ (strcmp(attribute, kPWSMaxChars) == 0) ||
+ (strcmp(attribute, kPWSPWDCannotBeName) == 0) ||
+ (strcmp(attribute, kPWSPWDLastSetTime) == 0) ||
+ (strcmp(attribute, kPWSLastLoginTime) == 0) ||
+ (strcmp(attribute, kPWSLogOffTime) == 0) ||
+ (strcmp(attribute, kPWSKickOffTime) == 0))
+ result = true;
+
+ return result;
+}
+
+static void add_password_policy_attribute(char *policy,
+ const char *attribute, const char *value)
+{
+ char *entry = NULL;
+
+ /* XXX: should check for overflow of "policy" */
+ entry = policy + strlen(policy);
+ snprintf(entry, strlen(policy),"%s= %s ", attribute, value);
+}
+
+static tDirStatus add_attribute_with_value(struct odssam_privates *ods_state,
+ tRecordReference recordReference,
+ const char *attribute,
+ const char *value,
+ BOOL addValue)
+{
+ tDirStatus status = eDSNoErr;
+ tDataNodePtr attributeType = NULL;
+ tDataNodePtr attributeValue = NULL;
+ tAttributeValueEntryPtr currentAttributeValueEntry = NULL;
+ tAttributeValueEntryPtr newAttributeValueEntry = NULL;
+
+ attributeType = dsDataNodeAllocateString(ods_state->session.ref,
+ attribute);
+ if (addValue) {
+ attributeValue =
+ dsDataNodeAllocateString(ods_state->session.ref, value);
+ status = dsAddAttributeValue(recordReference, attributeType,
+ attributeValue);
+ LOG_DS_ERROR(ds_trace, status, "dsAddAttributeValue");
+ } else {
+
+#ifdef USE_SETATTRIBUTEVALUE
+ status = dsGetRecordAttributeValueByIndex(recordReference,
+ attributeType, 1,
+ ¤tAttributeValueEntry);
+ LOG_DS_ERROR(ds_trace, status,
+ "dsGetRecordAttributeValueByIndex");
+
+ if (eDSNoErr == status) {
+ newAttributeValueEntry =
+ dsAllocAttributeValueEntry(ods_state->session.ref,
+ currentAttributeValueEntry->fAttributeValueID, (char *)value, strlen(value));
+ status = dsSetAttributeValue(recordReference, attributeType, newAttributeValueEntry);
+ LOG_DS_ERROR(ds_trace, status, "dsSetAttributeValue");
+
+ dsDeallocAttributeValueEntry(ods_state->session.ref, newAttributeValueEntry);
+ }
+
+ if (currentAttributeValueEntry) {
+ dsDeallocAttributeValueEntry(ods_state->session.ref,
+ currentAttributeValueEntry);
+ }
+
+#else /* USE_SETATTRIBUTEVALUE */
+
+ status = dsRemoveAttribute(recordReference, attributeType);
+ LOG_DS_ERROR(ds_trace, status, "dsRemoveAttribute");
+
+ attributeValue =
+ dsDataNodeAllocateString(ods_state->session.ref, value);
+ status = dsAddAttribute(recordReference, attributeType, 0, attributeValue);
+ LOG_DS_ERROR(ds_trace, status, "dsAddAttribute");
+
+#endif /* USE_SETATTRIBUTEVALUE */
+
+ }
+
+ opendirectory_free_node(&ods_state->session, attributeType);
+ opendirectory_free_node(&ods_state->session, attributeValue);
+ return status;
+}
+
+static tDirStatus get_records(struct odssam_privates *ods_state,
+ CFMutableArrayRef recordsArray,
+ tDirNodeReference nodeRef,
+ tDataListPtr recordName,
+ tDataListPtr recordType,
+ tDataListPtr attributes)
+{
+ tDirStatus status = eDSNoErr;
+ tDataBufferPtr dataBuffer = NULL;
+ unsigned long recordCount = 0;
+ tContextData *currentContextData = NULL;
+
+ currentContextData = &ods_state->contextData;
+ dataBuffer = DS_DEFAULT_BUFFER(ods_state->session.ref);
+
+ status = dsGetRecordList(nodeRef, dataBuffer,
+ recordName, eDSiExact, recordType,
+ attributes, False, &recordCount,
+ currentContextData);
+
+ LOG_DS_ERROR(ds_trace, status, "dsGetRecordList");
+ if (status != eDSNoErr) {
+ goto cleanup;
+ }
+
+ DEBUG(module_debug,("get_records recordCount (%lu)\n",recordCount));
+ opendirectory_insert_search_results(nodeRef, recordsArray,
+ recordCount, dataBuffer);
+
+cleanup:
+ opendirectory_free_buffer(&ods_state->session, dataBuffer);
+ return status;
+}
+
+/* Like opendirectory_sam_searchname except that it does a paged search using
+ * the context data in ods_state.
+ */
+static tDirStatus get_sam_record_attributes(
+ struct odssam_privates *ods_state,
+ CFMutableArrayRef recordsArray,
+ const char *type,
+ const char *name)
+{
+ tDirStatus status;
+ tDataListPtr samAttributes = NULL;
+ tDataListPtr recordName = NULL;
+ tDataListPtr recordType = NULL;
+
+ status = opendirectory_reconnect(&ods_state->session);
+ if (status != eDSNoErr) {
+ return status;
+ }
+
+ status = opendirectory_searchnode(&ods_state->session);
+ if (status != eDSNoErr) {
+ return status;
+ }
+
+ recordName = dsBuildListFromStrings(ods_state->session.ref,
+ name ? name : kDSRecordsAll, NULL);
+
+ recordType = dsBuildListFromStrings(ods_state->session.ref,
+ type, NULL);
+ if (recordName == NULL || recordType == NULL) {
+ status = eDSAllocationFailed;
+ goto cleanup;
+ }
+
+ samAttributes = opendirectory_sam_attrlist(&ods_state->session);
+ if (samAttributes == NULL) {
+ status = eDSAllocationFailed;
+ goto cleanup;
+ }
+
+ status = get_records(ods_state, recordsArray,
+ ods_state->session.search,
+ recordName, recordType,
+ samAttributes);
+ LOG_DS_ERROR(ds_trace, status, "get_records");
+
+cleanup:
+ opendirectory_free_list(&ods_state->session, recordName);
+ opendirectory_free_list(&ods_state->session, recordType);
+ opendirectory_free_list(&ods_state->session, samAttributes);
+
+ return status;
+}
+
+/*******************************************************************
+search an attribute and return the first value found.
+******************************************************************/
+static BOOL get_single_attribute (CFDictionaryRef entry,
+ const char *attr, pstring value)
+{
+ CFStringRef attrRef = NULL;
+ const void *opaque_value = NULL;
+ pstring buffer;
+ BOOL result = False;
+
+ DEBUG(module_debug, ("get_single_attribute attr=%s ", attr));
+
+ attrRef = CFStringCreateWithCString(NULL, attr,
+ kCFStringEncodingUTF8);
+ if (!CFDictionaryGetValueIfPresent(entry, attrRef, &opaque_value)) {
+ goto done;
+ }
+
+ if (CFGetTypeID(opaque_value) == CFArrayGetTypeID()) {
+ CFArrayRef valueList = (CFArrayRef)opaque_value;
+ CFStringRef cfstrRef;
+
+ if (CFArrayGetCount(valueList) == 0) {
+ goto done;
+ }
+
+ cfstrRef = (CFStringRef)CFArrayGetValueAtIndex(valueList, 0);
+ if (cfstrRef == NULL) {
+ goto done;
+ }
+
+ if (GetCString(cfstrRef, buffer, sizeof(buffer))) {
+ pstrcpy(value, buffer);
+ result = True;
+ goto done;
+ }
+
+ DEBUGADD(module_debug, ("CFArrayRef[0] <does not exist>]\n"));
+
+ } else if (CFGetTypeID(opaque_value) == CFStringGetTypeID()) {
+ CFStringRef cfstrRef = (CFStringRef)opaque_value;
+
+ if (cfstrRef == NULL) {
+ goto done;
+ }
+
+ if (GetCString(cfstrRef, buffer, PSTRING_LEN)) {
+ pstrcpy(value, buffer);
+ result = True;
+ goto done;
+ }
+
+ DEBUGADD(module_debug, ("CFStringRef <does not exist>\n"));
+ }
+
+done:
+ DEBUGADD(module_debug, ("%s\n", result ? "OK" : "<does not exist>"));
+
+ if (attrRef)
+ CFRelease(attrRef);
+
+#ifdef DEBUG_PASSWORDS
+ if (result == True) {
+ DEBUG(module_debug, ("get_single_attribute: [%s] = [%s]\n",
+ attr, value));
+ }
+#endif
+
+ return result;
+}
+
+static BOOL get_attributevalue_list (CFDictionaryRef entry,
+ const char *attribute, CFArrayRef *valueList)
+{
+ CFStringRef attrRef = NULL;
+ BOOL result = False;
+
+ attrRef = CFStringCreateWithCString(NULL, attribute, kCFStringEncodingUTF8);
+ *valueList = (CFArrayRef)CFDictionaryGetValue(entry, attrRef);
+
+ if (*valueList != NULL && CFArrayGetCount(*valueList)) {
+ result = True;
+ }
+
+ if (attrRef)
+ CFRelease(attrRef);
+
+ return result;
+}
+
+static BOOL parse_password_server(CFArrayRef authAuthorities, char *pwsServerIP, char *userid)
+{
+ BOOL result = False;
+ int arrayCount, arrayIndex;
+ char *tmp = NULL;
+ char *current = NULL;
+ const char *pwdsrvr = "PasswordServer";
+ const char *delimiter = ";";
+ CFStringRef authEntry = NULL;
+ char authStr[512];
+
+ for (arrayIndex = 0, arrayCount = CFArrayGetCount(authAuthorities); arrayIndex < arrayCount; arrayIndex++) {
+ authEntry = CFArrayGetValueAtIndex(authAuthorities, arrayIndex);
+ if (GetCString(authEntry, authStr, 512)) {
+ if (strstr(authStr, pwdsrvr) != NULL) {
+ //;ApplePasswordServer;0x3e5d410b7be9c1bd0000000200000002:17.221.41.95;
+ current = authStr;
+ tmp = strsep(¤t, delimiter); // tmp = ;
+ if (NULL == tmp)
+ break;
+ tmp = strsep(¤t, delimiter); // tmp = ApplePassworServer
+ if (NULL == tmp)
+ break;
+ tmp = strsep(¤t, ":"); // tmp = 0xXXXXX
+ if (NULL == tmp)
+ break;
+ safe_strcpy(userid,tmp, 1024);
+ DEBUG(module_debug, ("parse_password_server: userid(%s)\n",userid));
+ tmp = strsep(¤t, ";"); // tmp = xx.xx.xx
+ if (NULL == tmp)
+ break;
+ safe_strcpy(pwsServerIP,tmp, 1024);
+ DEBUG(module_debug, ("parse_password_server: pwsServerIP(%s)\n",pwsServerIP));
+ result = True;
+ break;
+ }
+ }
+
+ }
+
+ return result;
+}
+static BOOL insert_passwordpolicy_attributes(CFMutableDictionaryRef entry,
+ const char *policy)
+{
+ BOOL result = False;
+ // attr=value attr=value
+
+ char *tmp = NULL;
+ const char *delimiter = "=";
+ char *current = NULL;
+ char *original = NULL;
+ CFStringRef key = NULL;
+ CFStringRef value = NULL;
+ CFMutableArrayRef valueArray = NULL;
+
+ current = SMB_STRDUP(policy);
+ original = current;
+ do {
+ tmp = strsep(¤t, delimiter);
+ DEBUG(module_debug, ("insert_passwordpolicy_attributes: key(%s)\n",tmp));
+ if (tmp != NULL)
+ key = CFStringCreateWithCString(NULL, tmp, kCFStringEncodingUTF8);
+ else
+ key = NULL;
+
+ tmp = strsep(¤t, " ");
+ DEBUG(module_debug, ("insert_passwordpolicy_attributes: value(%s)\n",tmp));
+ if (tmp != NULL)
+ value = CFStringCreateWithCString(NULL, tmp, kCFStringEncodingUTF8);
+ else
+ value = NULL;
+
+ if (key && value) {
+ valueArray = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
+ CFArrayAppendValue(valueArray, value);
+ CFDictionaryAddValue(entry, key, valueArray);
+ CFRelease(valueArray);
+ result = true;
+ }
+ if (key)
+ CFRelease(key);
+ if (value)
+ CFRelease(value);
+
+ } while (current != NULL);
+ free(original);
+ return result;
+}
+
+static tDirStatus get_passwordpolicy_attributes(
+ struct odssam_privates *ods_state,
+ struct samu * sampass,
+ CFDictionaryRef entry,
+ CFMutableDictionaryRef *ppentry)
+{
+ tDirStatus status = eDSNoErr;
+ char pwsServerIP[1024];
+ char userid[1024];
+ CFArrayRef authAuthorities = NULL;
+ const char *recordType = NULL;
+ char dirNode[512] = {0};
+ tDirNodeReference nodeReference = 0;
+ tRecordReference recordReference = 0;
+ char policy[1024];
+ const char * userName;
+
+ userName = pdb_get_username(sampass);
+ SMB_ASSERT(userName != NULL);
+
+ if (!get_attributevalue_list(entry, kDSNAttrAuthenticationAuthority,
+ &authAuthorities)) {
+ goto cleanup;
+ }
+
+ if (!parse_password_server(authAuthorities, pwsServerIP, userid)) {
+ goto cleanup;
+ }
+
+ if (!get_single_attribute(entry, kDSNAttrMetaNodeLocation, dirNode)) {
+ status = eDSInvalidNodeRef;
+ } else {
+ status = opendirectory_open_node(&ods_state->session, dirNode, &nodeReference);
+ if (eDSNoErr != status){
+ goto cleanup;
+ }
+
+#if AUTH_GET_POLICY
+ status = opendirectory_authenticate_node(ods_state->session.ref,
+ nodeReference);
+ if (eDSNoErr != status) {
+ goto cleanup;
+ }
+#endif
+
+ if (eDSNoErr == status) {
+ recordType = get_record_type((const char *)userName);
+ status = get_record_ref(ods_state, nodeReference,
+ &recordReference, recordType, userName);
+ }
+ }
+
+ if (eDSNoErr == status && nodeReference != 0) {
+
+ status = get_password_policy(ods_state, nodeReference,
+ userName, policy, recordType);
+ DEBUG(module_debug, ("%s: [%d]get_password_policy (%s, %s)\n",
+ MODULE_NAME, status, policy, recordType));
+
+ /* If we found some password policy attributes, clone the
+ * record and insert attributes corresponding to the password
+ * policy.
+ */
+ if (eDSNoErr == status) {
+ *ppentry = CFDictionaryCreateMutableCopy(NULL, 0,
+ entry);
+
+ insert_passwordpolicy_attributes(*ppentry, policy);
+ }
+ }
+
+cleanup:
+ DS_CLOSE_NODE(nodeReference);
+
+ return status;
+}
+
+tDirStatus add_record_attributes(struct odssam_privates *ods_state, CFDictionaryRef samAttributes, CFDictionaryRef userCurrent, const char *the_record_type)
+{
+ tDirStatus status = eDSNoErr;
+
+ int attributeIndex;
+ int count = CFDictionaryGetCount(samAttributes);
+ CFStringRef values[count];
+ CFStringRef keys[count];
+ char key[255] = {0};
+ char value[255] = {0};
+ Boolean isKey,isValue;
+ char dirNode[512] = {0};
+ char temp[512] = {0};
+ char userName[128] = {0};
+ char policy[1024] = {0};
+ BOOL addValue;
+
+ tDirNodeReference nodeReference = 0;
+ tRecordReference recordReference = 0;
+ const char *recordType = NULL;
+
+ if (!get_single_attribute(userCurrent, kDSNAttrRecordName, userName)) {
+ status = eDSNullParameter;
+ goto cleanup;
+ }
+ if (!get_single_attribute(userCurrent, kDSNAttrMetaNodeLocation, dirNode)) {
+ status = eDSInvalidNodeRef;
+ } else {
+ status = opendirectory_open_node(&ods_state->session, dirNode, &nodeReference);
+ LOG_DS_ERROR(ds_trace, status, "opendirectory_open_node");
+ if (eDSNoErr == status) {
+ status = opendirectory_authenticate_node(
+ &ods_state->session,
+ nodeReference);
+ if (eDSNoErr == status) {
+ if (the_record_type)
+ recordType = the_record_type;
+ else
+ recordType = get_record_type((const char *)userName);
+ status = get_record_ref(ods_state, nodeReference, &recordReference, recordType, userName);
+ }
+ } else {
+ goto cleanup;
+ }
+ }
+
+ if (eDSNoErr == status) {
+ for (attributeIndex = 0, CFDictionaryGetKeysAndValues(samAttributes, (const void**)keys, (const void**)values); attributeIndex < count; attributeIndex++) {
+ isKey = CFStringGetCString(keys[attributeIndex], key, sizeof(key), kCFStringEncodingUTF8);
+ isValue = CFStringGetCString(values[attributeIndex], value, sizeof(value), kCFStringEncodingUTF8);
+
+ if (get_single_attribute(userCurrent,key, temp))
+ addValue = false;
+ else
+ addValue = true;
+
+ if (isKey && isValue) {
+ if (strcmp(key, kDSStdAuthSetPasswdAsRoot) == 0
+#if defined(kDSStdAuthSetWorkstationPasswd) && defined(kDSStdAuthSetLMHash)
+ || strcmp(key, kDSStdAuthSetWorkstationPasswd) == 0 || strcmp(key, kDSStdAuthSetLMHash) == 0
+#endif
+ ) {
+ status = set_password(ods_state, nodeReference, userName, value, key, recordType);
+ #ifdef DEBUG_PASSWORDS
+ DEBUG (module_debug, ("add_record_attributes: [%d]SetPassword(%s, %s, %s, %s)\n",status, userName, key, value, recordType));
+ else
+ DEBUG (module_debug, ("add_record_attributes: [%d]SetPassword(%s, %s, %s)\n",status, userName, key, recordType));
+ #endif
+ } else if (strcmp(key, kDSNAttrRecordName) == 0) {
+
+ if (strcmp(userName, value) != 0) {
+ status = set_recordname(ods_state, recordReference, value);
+ DEBUG (module_debug, ("add_record_attributes: [%d]set_recordname(%s, %s, %s)\n",status, userName, key, value));
+ }
+ } else if (isPWSAttribute(key)) {
+ add_password_policy_attribute(policy, key, value);
+ } else {
+ status = add_attribute_with_value(ods_state, recordReference, key, value, addValue);
+ DEBUG (module_debug, ("[%d]add_record_attributes: add_attribute_with_value(%s,%s,%s) error\n",status, userName, key, value));
+ }
+ if (status != eDSNoErr)
+ break;
+ }
+ }
+ if (strlen(policy) > 0)
+ status = set_password_policy(ods_state, nodeReference, userName, policy, recordType);
+ } else {
+ DEBUG (module_debug, ("[%d]add_record_attributes: authenticate_node error\n",status));
+ }
+cleanup:
+ if (0 != recordReference)
+ dsCloseRecord(recordReference);
+ DS_CLOSE_NODE(nodeReference);
+ return status;
+}
+
+/**********************************************************************
+Initialize struct samu from an Open Directory query
+(Based on init_sam_from_buffer in pdb_tdb.c)
+*********************************************************************/
+static BOOL init_sam_from_ods (struct odssam_privates *ods_state,
+ struct samu * sampass,
+ CFDictionaryRef entry)
+{
+ CFMutableDictionaryRef ppentry = NULL;
+ time_t logon_time,
+ logoff_time,
+ kickoff_time,
+ pass_last_set_time,
+ pass_can_change_time,
+ pass_must_change_time;
+ pstring username,
+ nt_username,
+ fullname,
+ homedir,
+ dir_drive,
+ logon_script,
+ profile_path,
+ acct_desc,
+ munged_dial,
+ workstations;
+ uint32 is_disabled;
+#if WITH_PASSWORD_HASH
+ uint8 smblmpwd[LM_HASH_LEN],
+ smbntpwd[NT_HASH_LEN];
+#endif
+ uint16 acct_ctrl = 0,
+ logon_divs;
+ uint32 hours_len;
+ uint8 hours[MAX_HOURS_LEN];
+ pstring temp;
+ pstring boolFlag;
+ uid_t uid = guest_uid;
+ gid_t gid = getegid();
+ DOM_SID sid;
+
+ /*
+ * do a little initialization
+ */
+ username[0] = '\0';
+ nt_username[0] = '\0';
+ fullname[0] = '\0';
+ homedir[0] = '\0';
+ dir_drive[0] = '\0';
+ logon_script[0] = '\0';
+ profile_path[0] = '\0';
+ acct_desc[0] = '\0';
+ munged_dial[0] = '\0';
+ workstations[0] = '\0';
+
+
+ SMB_ASSERT(sampass != NULL);
+ SMB_ASSERT(ods_state != NULL);
+ SMB_ASSERT(entry != NULL);
+
+ get_single_attribute(entry, kDSNAttrRecordName, username);
+ DEBUG(module_debug, ("Entry found for user: %s\n", username));
+
+ get_single_attribute(entry, kDS1AttrDistinguishedName, nt_username);
+ pdb_set_username(sampass, username, PDB_SET);
+
+ pdb_set_domain(sampass, lp_workgroup(), PDB_DEFAULT);
+ pdb_set_nt_username(sampass, nt_username, PDB_SET);
+
+ get_passwordpolicy_attributes(ods_state, sampass, entry, &ppentry);
+ if (ppentry) {
+ entry = (CFDictionaryRef)ppentry;
+ }
+
+ if (get_single_attribute(entry, kDS1AttrUniqueID, temp)) {
+ uid = atol(temp);
+ }
+
+ if (opendirectory_find_usersid_from_record(&ods_state->session,
+ entry, &sid)) {
+ pdb_set_user_sid(sampass, &sid, PDB_SET);
+ }
+
+ if (opendirectory_find_groupsid_from_record(&ods_state->session,
+ entry, &sid)) {
+ /* Don't call pdb_set_group_sid here, since it does a SID to
+ * GID resolution that can call back into us and blow the
+ * stack.
+ */
+ if (!sampass->group_sid) {
+ sampass->group_sid = TALLOC_P(sampass, DOM_SID);
+ }
+
+ if (sampass->group_sid) {
+ sid_copy(sampass->group_sid, &sid);
+ pdb_set_init_flags(sampass, PDB_GROUPSID, PDB_SET);
+ }
+ }
+
+ if (get_single_attribute(entry, kDS1AttrPrimaryGroupID, temp)) {
+ gid = atol(temp);
+ }
+
+ if (get_single_attribute(entry, kDS1AttrNFSHomeDirectory, temp)) {
+ pdb_set_homedir(sampass, temp, PDB_SET);
+ }
+
+#if 0
+ if (group_rid == 0) {
+ GROUP_MAP map;
+
+ get_single_attribute(entry, kDS1AttrPrimaryGroupID, temp);
+ gid = atol(temp);
+ /* call the mapping code here */
+ if(pdb_getgrgid(&map, gid)) {
+ pdb_set_group_sid(sampass, &map.sid, PDB_SET);
+ }
+ else {
+ group_rid = pdb_gid_to_group_rid(gid);
+ if(odsam_get_record_sid(ods_state, sampass, entry, group_rid, temp))
+ pdb_set_group_sid_from_string(sampass, temp, PDB_SET);
+ }
+ }
+#endif
+
+ if (get_single_attribute(entry, kPWSPWDLastSetTime, temp)) {
+ pass_last_set_time = (time_t) atol(temp);
+ pdb_set_pass_last_set_time(sampass, pass_last_set_time, PDB_SET);
+ }
+
+ if (get_single_attribute(entry, kPWSLastLoginTime, temp)) {
+ logon_time = (time_t) atol(temp);
+ pdb_set_logon_time(sampass, logon_time, PDB_SET);
+ }
+
+ if (get_single_attribute(entry, kPWSLogOffTime, temp)) {
+ logoff_time = (time_t) atol(temp);
+ pdb_set_logoff_time(sampass, logoff_time, PDB_SET);
+ }
+
+ if (get_single_attribute(entry, kPWSKickOffTime, temp)) {
+ kickoff_time = (time_t) atol(temp);
+ pdb_set_kickoff_time(sampass, kickoff_time, PDB_SET);
+ }
+
+ if (get_single_attribute(entry, kPWSExpiryDate, temp)) {
+ pass_can_change_time = (time_t) atol(temp);
+ pdb_set_pass_can_change_time(sampass, pass_can_change_time, PDB_SET);
+ }
+
+ if (get_single_attribute(entry, kPWSHardExpiryDate, temp)) {
+ if (get_single_attribute(entry, kPWSExpiryDateEnabled, boolFlag) && strcmp(boolFlag,"1") == 0) {
+ if (get_single_attribute(entry, kPWSNewPasswordRequired, boolFlag) && strcmp(boolFlag,"1") == 0) {
+ struct timeval tv;
+ GetTimeOfDay(&tv);
+ pass_must_change_time = (time_t) tv.tv_sec;
+ } else
+ pass_must_change_time = (time_t) atol(temp);
+ pdb_set_pass_must_change_time(sampass, pass_must_change_time, PDB_SET);
+ }
+ }
+
+ if (get_single_attribute(entry, kDS1AttrDistinguishedName, fullname)) {
+ pdb_set_fullname(sampass, fullname, PDB_SET);
+ }
+
+ if (!get_single_attribute(entry, kDS1AttrSMBHomeDrive, dir_drive)) {
+#ifdef GLOBAL_DEFAULT
+ pdb_set_dir_drive(sampass,
+ talloc_sub_specified(ods_state, lp_logon_drive(),
+ username, pdb_get_domain(sampass),
+ uid, gid),
+ PDB_DEFAULT);
+#else
+ pdb_set_dir_drive(sampass, NULL, PDB_SET);
+#endif
+ } else {
+ pdb_set_dir_drive(sampass, dir_drive, PDB_SET);
+ }
+
+ if (!get_single_attribute(entry, kDS1AttrSMBHome, homedir)) {
+#ifdef GLOBAL_DEFAULT
+ pdb_set_homedir(sampass,
+ talloc_sub_specified(ods_state, lp_logon_home(),
+ username, pdb_get_domain(sampass),
+ uid, gid),
+ PDB_DEFAULT);
+#else
+ pdb_set_homedir(sampass, NULL, PDB_SET);
+#endif
+ } else {
+ pdb_set_homedir(sampass, homedir, PDB_SET);
+ }
+
+ if (!get_single_attribute(entry, kDS1AttrSMBScriptPath, logon_script)) {
+#ifdef GLOBAL_DEFAULT
+ pdb_set_logon_script(sampass,
+ talloc_sub_specified(ods_state, lp_logon_script(),
+ username, pdb_get_domain(sampass), uid, gid),
+ PDB_DEFAULT);
+#else
+ pdb_set_logon_script(sampass, NULL, PDB_SET);
+#endif
+ } else {
+ pdb_set_logon_script(sampass, logon_script, PDB_SET);
+ }
+
+ if (!get_single_attribute(entry, kDS1AttrSMBProfilePath, profile_path)) {
+#ifdef GLOBAL_DEFAULT
+ pdb_set_profile_path(sampass,
+ talloc_sub_specified(ods_state, lp_logon_path(),
+ username, pdb_get_domain(sampass), uid, gid),
+ PDB_DEFAULT);
+#else
+ pdb_set_profile_path(sampass, NULL, PDB_SET);
+#endif
+ } else {
+ pdb_set_profile_path(sampass, profile_path, PDB_SET);
+ }
+
+ if (get_single_attribute(entry, kDS1AttrComment, acct_desc)) {
+ pdb_set_acct_desc(sampass, acct_desc, PDB_SET);
+ }
+
+ if (get_single_attribute(entry, kDS1AttrSMBUserWorkstations,
+ workstations)) {
+ pdb_set_workstations(sampass, workstations, PDB_SET);
+ }
+
+ /* FIXME: hours stuff should be cleaner */
+
+ logon_divs = 168;
+ hours_len = 21;
+ memset(hours, 0xff, hours_len);
+#if WITH_PASSWORD_HASH
+ if (get_single_attribute (entry, kDS1AttrSMBLMPassword, temp)) {
+ pdb_gethexpwd(temp, smblmpwd);
+ memset((char *)temp, '\0', strlen(temp)+1);
+ pdb_set_lanman_passwd(sampass, smblmpwd, PDB_SET);
+ ZERO_STRUCT(smblmpwd);
+ }
+
+ if (get_single_attribute (entry, kDS1AttrSMBNTPassword, temp)) {
+ pdb_gethexpwd(temp, smbntpwd);
+ memset((char *)temp, '\0', strlen(temp)+1);
+ pdb_set_nt_passwd(sampass, smbntpwd, PDB_SET);
+ ZERO_STRUCT(smbntpwd);
+ }
+#endif
+ if (!get_single_attribute (entry, kDS1AttrSMBAcctFlags, temp)) {
+ acct_ctrl |= ACB_NORMAL;
+ } else {
+ acct_ctrl = pdb_decode_acct_ctrl(temp);
+
+ if (acct_ctrl == 0)
+ acct_ctrl |= ACB_NORMAL;
+
+ if (get_single_attribute (entry, kPWSIsDisabled, temp)) {
+ is_disabled = atol(temp);
+ if (is_disabled == 1)
+ acct_ctrl |= ACB_DISABLED;
+ }
+
+ pdb_set_acct_ctrl(sampass, acct_ctrl, PDB_SET);
+ }
+
+ pdb_set_hours_len(sampass, hours_len, PDB_SET);
+ pdb_set_logon_divs(sampass, logon_divs, PDB_SET);
+
+ pdb_set_munged_dial(sampass, munged_dial, PDB_SET);
+
+ /* pdb_set_unknown_3(sampass, unknown3, PDB_SET); */
+ /* pdb_set_unknown_5(sampass, unknown5, PDB_SET); */
+ /* pdb_set_unknown_6(sampass, unknown6, PDB_SET); */
+
+ pdb_set_hours(sampass, hours, PDB_SET);
+
+
+ if (ppentry) {
+ CFRelease(ppentry);
+ }
+ return True;
+}
+
+static void update_user_entry(CFMutableDictionaryRef userEntry,
+ const char *attribute,
+ const char *value)
+{
+ static const char const func[] = "update_user_entry";
+ CFStringRef cfKey = NULL;
+ CFStringRef cfValue = NULL;
+
+ if (attribute && *attribute) {
+ cfKey = CFStringCreateWithCString(NULL, attribute, kCFStringEncodingUTF8);
+ } else {
+ DEBUG(module_debug, ("%s: missing attribute!!!\n", func));
+ }
+
+ if (value && *value) {
+ cfValue = CFStringCreateWithCString(NULL, value, kCFStringEncodingUTF8);
+ } else {
+ DEBUG(module_debug, ("%s: missing value!!!\n", func));
+ }
+
+ if (cfKey && cfValue) {
+ CFDictionaryAddValue(userEntry, cfKey, cfValue);
+ }
+
+ if(cfKey) {
+ CFRelease(cfKey);
+ }
+
+ if(cfValue) {
+ CFRelease(cfValue);
+ }
+}
+
+static BOOL need_ods_mod(BOOL pdb_add,
+ const struct samu * sampass,
+ enum pdb_elements element)
+{
+ if (pdb_add) {
+ return (!IS_SAM_DEFAULT(sampass, element));
+ } else {
+ return IS_SAM_CHANGED(sampass, element);
+ }
+}
+
+/**********************************************************************
+Initialize Open Directory account from struct samu
+*********************************************************************/
+
+static BOOL init_ods_from_sam(struct odssam_privates *ods_state,
+ BOOL pdb_add,
+ struct samu * sampass,
+ CFMutableDictionaryRef userEntry)
+{
+ static const char const func[] = "init_ods_from_sam";
+
+ pstring temp;
+ uint32 rid;
+
+ SMB_ASSERT(ods_state != NULL);
+ SMB_ASSERT(sampass != NULL);
+
+ if (need_ods_mod(pdb_add, sampass, PDB_USERNAME)) {
+ update_user_entry(userEntry, kDSNAttrRecordName,
+ pdb_get_username(sampass));
+ DEBUG(module_debug, ("Setting entry for user: %s\n",
+ pdb_get_username(sampass)));
+ }
+
+ if (need_ods_mod(pdb_add, sampass, PDB_USERSID)) {
+ fstring sid_str;
+ fstring dom_sid_str;
+ const DOM_SID *user_sid = pdb_get_user_sid(sampass);
+
+ if (NULL != user_sid) {
+ if (!sid_check_is_in_our_domain(user_sid)) {
+ DEBUG(module_debug, ("%s: User's SID (%s) is not for "
+ "this domain (%s), cannot add to "
+ "Open Directory!\n",
+ func, sid_to_string(sid_str, user_sid),
+ sid_to_string(dom_sid_str, get_global_sam_sid())));
+ return False;
+ }
+ sid_to_string(sid_str, user_sid);
+ DEBUG(module_debug, ("Setting SID entry for user: %s [%s]\n",
+ pdb_get_username(sampass), sid_str));
+ update_user_entry(userEntry, kDS1AttrSMBSID, sid_str);
+ } else if ((rid = pdb_get_user_rid(sampass))!=0) {
+ slprintf(temp, sizeof(temp) - 1, "%i", rid);
+ DEBUG(module_debug, ("Setting RID entry for user: %s [%s]\n",
+ pdb_get_username(sampass), temp));
+ update_user_entry(userEntry, kDS1AttrSMBRID, temp);
+ }
+#ifdef STORE_ALGORITHMIC_RID
+ else if (!IS_SAM_DEFAULT(sampass, PDB_UID)) {
+ rid = algorithmic_pdb_uid_to_user_rid(pdb_get_uid(sampass));
+ slprintf(temp, sizeof(temp) - 1, "%i", rid);
+ update_user_entry(userEntry, kDS1AttrSMBRID, temp);
+ } else {
+ DEBUG(module_debug, ("NO user RID specified on account %s, cannot store!\n", pdb_get_username(sampass)));
+ return False;
+ }
+#endif
+ }
+
+ if (need_ods_mod(pdb_add, sampass, PDB_GROUPSID)) {
+ fstring sid_str;
+ fstring dom_sid_str;
+ const DOM_SID *group_sid = pdb_get_group_sid(sampass);
+
+
+ if (NULL != group_sid) {
+ if (!sid_check_is_in_our_domain(group_sid)) {
+ DEBUG(module_debug, ("init_ods_from_sam: User's Primary Group SID (%s) is not for this domain (%s), cannot add to Open Directory!\n",
+ sid_to_string(sid_str, group_sid),
+ sid_to_string(dom_sid_str, get_global_sam_sid())));
+ return False;
+ }
+ sid_to_string(sid_str, group_sid);
+ DEBUG(module_debug, ("Setting Primary Group SID entry "
+ "for user: %s [%s]\n",
+ pdb_get_username(sampass), sid_str));
+ update_user_entry(userEntry,
+ kDS1AttrSMBPrimaryGroupSID, sid_str);
+ } else if ((rid = pdb_get_group_rid(sampass))!=0) {
+ slprintf(temp, sizeof(temp) - 1, "%i", rid);
+ update_user_entry(userEntry, kDS1AttrSMBGroupRID, temp);
+ }
+#ifdef STORE_ALGORITHMIC_RID
+ else if (!IS_SAM_DEFAULT(sampass, PDB_GID)) {
+ rid = pdb_gid_to_group_rid(pdb_get_gid(sampass));
+ slprintf(temp, sizeof(temp) - 1, "%i", rid);
+ update_user_entry(userEntry, kDS1AttrSMBGroupRID, temp);
+ } else {
+ DEBUG(module_debug, ("NO group RID specified on account %s, cannot store!\n", pdb_get_username(sampass)));
+ return False;
+ }
+#endif
+ }
+
+ /* displayName, cn, and gecos should all be the same
+ * most easily accomplished by giving them the same OID
+ * gecos isn't set here b/c it should be handled by the
+ * add-user script
+ */
+ if (need_ods_mod(pdb_add, sampass, PDB_FULLNAME)) {
+ update_user_entry(userEntry, kDS1AttrDistinguishedName,
+ pdb_get_fullname(sampass));
+ }
+ if (need_ods_mod(pdb_add, sampass, PDB_ACCTDESC)) {
+ update_user_entry(userEntry, kDS1AttrComment,
+ pdb_get_acct_desc(sampass));
+ }
+ if (need_ods_mod(pdb_add, sampass, PDB_WORKSTATIONS)) {
+ update_user_entry(userEntry, kDS1AttrSMBUserWorkstations,
+ pdb_get_workstations(sampass));
+ }
+ /*
+ * Only updates fields which have been set (not defaults from smb.conf)
+ */
+
+ if (need_ods_mod(pdb_add, sampass, PDB_SMBHOME)) {
+ update_user_entry(userEntry, kDS1AttrSMBHome,
+ pdb_get_homedir(sampass));
+ }
+
+ if (need_ods_mod(pdb_add, sampass, PDB_DRIVE)) {
+ update_user_entry(userEntry, kDS1AttrSMBHomeDrive,
+ pdb_get_dir_drive(sampass));
+ }
+
+ if (need_ods_mod(pdb_add, sampass, PDB_LOGONSCRIPT)) {
+ update_user_entry(userEntry, kDS1AttrSMBScriptPath,
+ pdb_get_logon_script(sampass));
+ }
+
+ if (need_ods_mod(pdb_add, sampass, PDB_PROFILE))
+ update_user_entry(userEntry, kDS1AttrSMBProfilePath,
+ pdb_get_profile_path(sampass));
+
+ if (need_ods_mod(pdb_add, sampass, PDB_LOGONTIME)) {
+ slprintf(temp, sizeof(temp) - 1, "%li",
+ pdb_get_logon_time(sampass));
+ update_user_entry(userEntry, kPWSLastLoginTime, temp);
+ }
+
+ if (need_ods_mod(pdb_add, sampass, PDB_LOGOFFTIME)) {
+ slprintf(temp, sizeof(temp) - 1, "%li",
+ pdb_get_logoff_time(sampass));
+ update_user_entry(userEntry, kPWSLogOffTime, temp);
+ }
+
+ if (need_ods_mod(pdb_add, sampass, PDB_KICKOFFTIME)) {
+ slprintf (temp, sizeof (temp) - 1, "%li",
+ pdb_get_kickoff_time(sampass));
+ update_user_entry(userEntry, kPWSKickOffTime, temp);
+ }
+
+
+ if (need_ods_mod(pdb_add, sampass, PDB_CANCHANGETIME)) {
+ slprintf (temp, sizeof (temp) - 1, "%li",
+ pdb_get_pass_can_change_time(sampass));
+ update_user_entry(userEntry, kPWSExpiryDate, temp);
+ }
+
+ if (need_ods_mod(pdb_add, sampass, PDB_MUSTCHANGETIME)) {
+ slprintf (temp, sizeof (temp) - 1, "%li",
+ pdb_get_pass_must_change_time(sampass));
+ update_user_entry(userEntry, kPWSHardExpiryDate, temp);
+ }
+
+ if (pdb_get_acct_ctrl(sampass) &
+ (ACB_WSTRUST|ACB_SVRTRUST|ACB_DOMTRUST|ACB_NORMAL)) {
+
+#if defined(kDSStdAuthSetWorkstationPasswd) && defined(kDSStdAuthSetLMHash)
+ if (need_ods_mod(pdb_add, sampass, PDB_LMPASSWD)) {
+ pdb_sethexpwd (temp, pdb_get_lanman_passwd(sampass),
+ pdb_get_acct_ctrl(sampass));
+ update_user_entry (userEntry, kDSStdAuthSetLMHash,
+ temp);
+ }
+
+ if (need_ods_mod(pdb_add, sampass, PDB_NTPASSWD)) {
+ pdb_sethexpwd(temp, pdb_get_nt_passwd(sampass),
+ pdb_get_acct_ctrl(sampass));
+ update_user_entry(userEntry,
+ kDSStdAuthSetWorkstationPasswd, temp);
+ }
+#endif
+ if (need_ods_mod(pdb_add, sampass, PDB_PLAINTEXT_PW)) {
+ update_user_entry(userEntry, kDSStdAuthSetPasswdAsRoot,
+ pdb_get_plaintext_passwd(sampass));
+ }
+#if USES_PWS
+ if (need_ods_mod(pdb_add, sampass, PDB_PASSLASTSET)) {
+ slprintf (temp, sizeof (temp) - 1, "%li",
+ pdb_get_pass_last_set_time(sampass));
+ update_user_entry(userEntry, kPWSPWDLastSetTime, temp);
+ }
+#endif
+ }
+
+ /* FIXME: Hours stuff goes in directory */
+ if (need_ods_mod(pdb_add, sampass, PDB_ACCTCTRL)) {
+ char * ctrl = pdb_encode_acct_ctrl(pdb_get_acct_ctrl(sampass),
+ NEW_PW_FORMAT_SPACE_PADDED_LEN);
+ update_user_entry (userEntry, kDS1AttrSMBAcctFlags,
+ ctrl);
+ }
+
+ return True;
+}
+
+/* passdb functions */
+
+
+/**********************************************************************
+End enumeration of the Open Directory Service password list
+*********************************************************************/
+static void odssam_endsampwent(struct pdb_methods *methods)
+{
+ struct odssam_privates *ods_state = get_ods_state(methods);
+
+ SMB_ASSERT(ods_state != NULL);
+
+ CFArrayRemoveAllValues(ods_state->usersArray);
+ ods_state->usersIndex = 0;
+}
+
+static NTSTATUS odssam_setsampwent(struct pdb_methods *methods,
+ BOOL update, uint32 acb_mask)
+{
+ struct odssam_privates *ods_state = get_ods_state(methods);
+
+ DEBUG(module_debug,("odssam_setsampwent: update(%d)\n", update));
+
+ SMB_ASSERT(ods_state != NULL);
+ CFArrayRemoveAllValues(ods_state->usersArray);
+ ods_state->usersIndex = 0;
+
+ return NT_STATUS_OK;
+}
+
+/**********************************************************************
+Get the next entry in the Open Directory Service password database
+*********************************************************************/
+static NTSTATUS odssam_getsampwent(struct pdb_methods *methods, struct samu *user)
+{
+ NTSTATUS ret = NT_STATUS_UNSUCCESSFUL;
+ tDirStatus dirStatus = eDSNoErr;
+ int entriesAvailable = 0;
+ CFMutableDictionaryRef entry = NULL;
+
+ struct odssam_privates *ods_state = get_ods_state(methods);
+
+ if (ods_state == NULL) {
+ return NT_STATUS_INVALID_HANDLE;
+ }
+
+ if (ods_state->usersArray != NULL)
+ entriesAvailable = CFArrayGetCount(ods_state->usersArray);
+ else
+ return NT_STATUS_UNSUCCESSFUL; // allocate array???
+
+ if (entriesAvailable == 0 || ods_state->usersIndex >= entriesAvailable) {
+ DEBUG(module_debug,("odssam_getsampwent: entriesAvailable(%d) contextData(%p)\n", entriesAvailable, ods_state->contextData));
+ CFArrayRemoveAllValues(ods_state->usersArray);
+
+ if (entriesAvailable && ods_state->usersIndex >= entriesAvailable && ods_state->contextData == NULL) {
+ odssam_endsampwent(methods);
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ /* NOTE: we can't use opendirectory_sam_searchname here,
+ * because we want to page through the (potentially large
+ * set of) results.
+ */
+
+ if ((dirStatus = get_sam_record_attributes(ods_state, ods_state->usersArray, kDSStdRecordTypeUsers, NULL)) != eDSNoErr) {
+ ret = NT_STATUS_UNSUCCESSFUL;
+ } else {
+ entriesAvailable = CFArrayGetCount(ods_state->usersArray);
+ DEBUG(module_debug,("odssam_getsampwent: entriesAvailable Take 2(%d) contextData(%p)\n", entriesAvailable, ods_state->contextData));
+ ods_state->usersIndex = 0;
+ }
+ }
+
+ if (dirStatus == eDSNoErr && entriesAvailable) {
+ entry = (CFMutableDictionaryRef) CFArrayGetValueAtIndex(ods_state->usersArray, ods_state->usersIndex);
+ ods_state->usersIndex++;
+ if (!init_sam_from_ods(ods_state, user, entry)) {
+ DEBUG(module_debug,("odssam_getsampwent: init_sam_from_ods failed for user index(%d)\n", ods_state->usersIndex));
+// CFRelease(entry);
+ ret = NT_STATUS_UNSUCCESSFUL;
+ }
+// CFRelease(entry);
+ ret = NT_STATUS_OK;
+ }
+
+
+ return ret;
+}
+
+static NTSTATUS odssam_getsampwnam(struct pdb_methods *methods,
+ struct samu *user, const char *sname)
+{
+ NTSTATUS ret = NT_STATUS_UNSUCCESSFUL;
+ tDirStatus dirStatus = eDSNoErr;
+ const char *recordType = NULL;
+ CFMutableDictionaryRef entry = NULL;
+
+ CFMutableArrayRef usersArray = NULL;
+
+ struct odssam_privates *ods_state = get_ods_state(methods);
+
+ if (ods_state == NULL) {
+ return NT_STATUS_INVALID_HANDLE;
+ }
+
+ recordType = get_record_type(sname);
+
+ /* Map the user account "guest" to whatever the configured guest
+ * account is. This makes "guest" an alias for the guest account,
+ * which is generally OK since smbfs uses "guest" as the guest
+ * account name.
+ */
+ if (recordType == kDSStdRecordTypeUsers &&
+ lp_parm_bool(GLOBAL_SECTION_SNUM, MODULE_NAME,
+ "map guest to guest", True) &&
+ strequal(sname, "guest")) {
+ sname = lp_guestaccount();
+ }
+
+ dirStatus = opendirectory_sam_searchname(&ods_state->session,
+ &usersArray, recordType, sname);
+
+ if (dirStatus != eDSNoErr) {
+ LOG_DS_ERROR_MSG(ds_trace, dirStatus,
+ "opendirectory_sam_searchname",
+ ("no %s record for account '%s'\n", recordType, sname));
+ /* FIXME: should map from tDirStatus to NTSTATUS -- jpeach */
+ goto done;
+ }
+
+ /* handle duplicates - currently uses first match in search policy*/
+ entry = (CFMutableDictionaryRef) CFArrayGetValueAtIndex(usersArray, 0);
+ if (!init_sam_from_ods(ods_state, user, entry)) {
+ DEBUG(module_debug,("odssam_getsampwnam: init_sam_from_ods failed for account '%s'!\n", sname));
+ } else {
+ ret = NT_STATUS_OK;
+ }
+
+done:
+ if (usersArray) {
+ CFRelease(usersArray);
+ }
+
+ return ret;
+}
+
+static NTSTATUS odssam_getsampwsid(struct pdb_methods *methods,
+ struct samu * account,
+ const DOM_SID *user_sid)
+{
+
+ NTSTATUS ret = NT_STATUS_UNSUCCESSFUL;
+ struct odssam_privates *ods_state = get_ods_state(methods);
+ CFDictionaryRef entry = NULL;
+ fstring sid_string;
+
+ if (ods_state == NULL) {
+ ret = NT_STATUS_INVALID_HANDLE;
+ goto cleanup;
+ }
+
+ DEBUG(module_debug,("looking up user SID %s\n",
+ sid_to_string(sid_string, user_sid)));
+
+ entry = opendirectory_find_record_from_usersid(&ods_state->session,
+ user_sid);
+ if (entry == NULL) {
+ ret = NT_STATUS_NO_SUCH_USER;
+ goto cleanup;
+ }
+
+ if (!init_sam_from_ods(ods_state, account, entry)) {
+ DEBUG(module_debug,("odssam_getsampwsid: init_sam_from_ods failed!\n"));
+ ret = NT_STATUS_INTERNAL_ERROR;
+ goto cleanup;
+ }
+ ret = NT_STATUS_OK;
+
+cleanup:
+ DEBUG(module_debug,("searching user SID %s, result=%s\n",
+ sid_to_string(sid_string, user_sid), nt_errstr(ret)));
+
+ if (entry) {
+ CFRelease(entry);
+ }
+
+ return ret;
+}
+
+static NTSTATUS odssam_add_sam_account(struct pdb_methods *methods, struct samu * newpwd)
+{
+ NTSTATUS ret = NT_STATUS_UNSUCCESSFUL;
+ tDirStatus dirStatus = eDSNoErr;
+ const char *recordType = NULL;
+ struct odssam_privates *ods_state = get_ods_state(methods);
+ int ops = 0;
+ CFMutableArrayRef usersArray = NULL;
+ CFMutableDictionaryRef userMods = NULL;
+ CFDictionaryRef userCurrent = NULL;
+
+ const char *username = pdb_get_username(newpwd);
+ if (!username || !*username) {
+ DEBUG(module_debug, ("Cannot add user without a username!\n"));
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ if (ods_state == NULL) {
+ return NT_STATUS_INVALID_HANDLE;
+ }
+
+ recordType = get_record_type((const char *)username);
+ if (opendirectory_sam_searchname(&ods_state->session,
+ &usersArray, recordType, username) != eDSNoErr) {
+ ret = NT_STATUS_UNSUCCESSFUL;
+ goto cleanup;
+ }
+
+ /* Check for SMB Attributes and bail if already added. */
+
+#if 0 /* skip duplicates for now */
+ if (CFArrayGetCount(usersArray) > 1) {
+ DEBUG (module_debug, ("More than one user with that uid exists: bailing out!\n"));
+ ret = NT_STATUS_UNSUCCESSFUL;
+ goto cleanup;
+ }
+#endif
+ /* Check if we need to update an existing entry */
+
+ userMods = CFDictionaryCreateMutable(NULL, 0, &kCFCopyStringDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
+ if (!init_ods_from_sam(ods_state, ops, newpwd, userMods)) {
+ DEBUG(module_debug, ("odssam_add_sam_account: init_ods_from_sam failed!\n"));
+ ret = NT_STATUS_UNSUCCESSFUL;
+ goto cleanup;
+ }
+
+ if (CFDictionaryGetCount(userMods) == 0) {
+ DEBUG(module_debug,("mods is empty: nothing to add for user: %s\n",pdb_get_username(newpwd)));
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ userCurrent = (CFDictionaryRef) CFArrayGetValueAtIndex(usersArray, 0);
+ dirStatus = add_record_attributes(ods_state, userMods, userCurrent, NULL);
+ if (eDSNoErr != dirStatus) {
+ ret = NT_STATUS_UNSUCCESSFUL;
+ DEBUG(module_debug, ("odssam_add_sam_account: [%d]add_record_attributes\n", dirStatus));
+ } else {
+ ret = NT_STATUS_OK;
+ }
+
+cleanup:
+ if (usersArray)
+ CFRelease(usersArray);
+ if (userMods)
+ CFRelease(userMods);
+
+ return ret;
+}
+
+static NTSTATUS odssam_update_sam_account(struct pdb_methods *methods,
+ struct samu * newpwd)
+{
+ NTSTATUS ret = NT_STATUS_UNSUCCESSFUL;
+ tDirStatus dirStatus = eDSNoErr;
+ const char *recordType = NULL;
+ struct odssam_privates *ods_state = get_ods_state(methods);
+ int ops = 0;
+ CFMutableArrayRef usersArray = NULL;
+ CFMutableDictionaryRef userMods = NULL;
+ CFDictionaryRef userCurrent = NULL;
+
+ const char *username = pdb_get_username(newpwd);
+ const char *ntusername = pdb_get_nt_username(newpwd);
+ const char *searchname = username;
+
+ if (!username || !*username) {
+ DEBUG(module_debug, ("Cannot update a user without a username!\n"));
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+ DEBUG(module_debug, ("odssam_update_sam_account: username(%s) ntusername(%s)\n", username, ntusername));
+
+ if (ods_state == NULL) {
+ return NT_STATUS_INVALID_HANDLE;
+ }
+
+ if (IS_SAM_CHANGED(newpwd, PDB_USERNAME))
+ searchname = ntusername;
+ else if (IS_SAM_CHANGED(newpwd, PDB_FULLNAME))
+ searchname = username;
+ else
+ searchname = username;
+
+ if (IS_SAM_CHANGED(newpwd, PDB_USERNAME) && IS_SAM_CHANGED(newpwd, PDB_FULLNAME)) {
+ // record name changed - lookup by kDS1AttrSMBSID
+ DEBUG(module_debug, ("odssam_update_sam_account: PDB_USERNAME && PDB_FULLNAME MODIFIED \n"));
+ return ret;
+ } else {
+ recordType = get_record_type((const char *)searchname);
+ if (opendirectory_sam_searchname(&ods_state->session,
+ &usersArray, recordType, searchname) != eDSNoErr) {
+ DEBUG(module_debug, ("odssam_add_sam_account: "
+ "searchname(%s) NOT FOUND\n",
+ searchname));
+ ret = NT_STATUS_UNSUCCESSFUL;
+ goto cleanup;
+ }
+ }
+// check for SMB Attributes and bail if already added
+
+#if 0 /* skip duplicates for now */
+ if (CFArrayGetCount(usersArray) > 1) {
+ DEBUG (module_debug, ("odssam_update_sam_account: More than one user with that uid exists: bailing out!\n"));
+ ret = NT_STATUS_UNSUCCESSFUL;
+ goto cleanup;
+ }
+#endif
+ /* Check if we need to update an existing entry */
+
+ userMods = CFDictionaryCreateMutable(NULL, 0, &kCFCopyStringDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
+ if (!init_ods_from_sam(ods_state, ops, newpwd, userMods)) {
+ DEBUG(module_debug, ("odssam_update_sam_account: init_ods_from_sam failed!\n"));
+ ret = NT_STATUS_UNSUCCESSFUL;
+ goto cleanup;
+ }
+
+ if (CFDictionaryGetCount(userMods) == 0) {
+ DEBUG(module_debug,("odssam_update_sam_account: mods is empty: nothing to add for user: %s\n",pdb_get_username(newpwd)));
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ userCurrent = (CFDictionaryRef) CFArrayGetValueAtIndex(usersArray, 0);
+ dirStatus = add_record_attributes(ods_state, userMods, userCurrent, NULL);
+ if (eDSNoErr != dirStatus) {
+ ret = NT_STATUS_UNSUCCESSFUL;
+ DEBUG(module_debug, ("odssam_update_sam_account: [%d]add_record_attributes\n", dirStatus));
+ } else {
+ ret = NT_STATUS_OK;
+ }
+
+cleanup:
+ if (usersArray)
+ CFRelease(usersArray);
+ if (userMods)
+ CFRelease(userMods);
+
+ return ret;
+}
+
+static BOOL init_group_from_ods(struct odssam_privates *ods_state,
+ GROUP_MAP *map,
+ CFDictionaryRef entry)
+{
+ pstring temp;
+
+ SMB_ASSERT(ods_state != NULL);
+ SMB_ASSERT(map != NULL);
+ SMB_ASSERT(entry != NULL);
+
+ if (!opendirectory_find_groupsid_from_record(&ods_state->session,
+ entry, &map->sid)) {
+ DEBUG(module_debug, ("unable to map group SID\n"));
+ return False;
+ }
+
+ /* We need to set the type to make the idmap pdb calls happy. Since we
+ * know this is a group record, there are only 3 possiblities.
+ */
+ if (sid_check_is_in_builtin(&map->sid) ||
+ sid_check_is_in_wellknown_domain(&map->sid)) {
+ /* well-known group */
+ map->sid_name_use = SID_NAME_WKN_GRP;
+ } else if (sid_check_is_in_our_domain(&map->sid)) {
+ /* local SAM group */
+ map->sid_name_use = SID_NAME_ALIAS;
+ } else {
+ /* domain/network group */
+ map->sid_name_use = SID_NAME_DOM_GRP;
+ }
+
+ if (!get_single_attribute(entry, kDS1AttrPrimaryGroupID, temp)) {
+ DEBUG(module_debug, ("init_group_from_ods: Mandatory attribute %s not found\n", kDS1AttrPrimaryGroupID));
+ return False;
+ }
+
+ map->gid = (gid_t)atol(temp);
+
+
+ if (!get_single_attribute(entry, kDSNAttrMetaNodeLocation, temp)) {
+ DEBUG(module_debug, ("init_group_from_ods: Mandatory attribute %s not found\n", kDS1AttrPrimaryGroupID));
+ return False;
+ }
+
+ if (!get_single_attribute(entry, kDS1AttrDistinguishedName, temp)) {
+ DEBUG(module_debug, ("init_group_from_ods: Attribute %s not found\n", kDS1AttrDistinguishedName));
+ if(!get_single_attribute(entry, kDSNAttrRecordName, temp)) {
+ DEBUG(module_debug, ("init_group_from_ods: Mandatory attribute %s not found\n", kDSNAttrRecordName));
+ return False;
+ }
+ }
+ fstrcpy(map->nt_name, temp);
+
+ if (!get_single_attribute(entry, kDS1AttrComment, temp)) {
+ temp[0] = '\0';
+ }
+ fstrcpy(map->comment, temp);
+
+ return True;
+}
+
+static void odssam_endgrpwent(struct pdb_methods *methods)
+{
+ struct odssam_privates *ods_state = get_ods_state(methods);
+
+ CFArrayRemoveAllValues(ods_state->groupsArray);
+ ods_state->groupsIndex = 0;
+}
+
+static NTSTATUS odssam_setgrpwent(struct pdb_methods *methods, BOOL update)
+{
+ odssam_endgrpwent(methods);
+ DEBUG(module_debug,("odssam_setgrpwent: update(%d)\n", update));
+
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS odssam_getgrpwent(struct pdb_methods *methods, GROUP_MAP *map)
+{
+ NTSTATUS ret = NT_STATUS_UNSUCCESSFUL;
+ tDirStatus dirStatus = eDSNoErr;
+ int entriesAvailable = 0;
+ CFMutableDictionaryRef entry = NULL;
+
+ struct odssam_privates *ods_state = get_ods_state(methods);
+
+ if (ods_state == NULL) {
+ return NT_STATUS_INVALID_HANDLE;
+ }
+
+ if (ods_state->groupsArray != NULL)
+ entriesAvailable = CFArrayGetCount(ods_state->groupsArray);
+ else
+ return NT_STATUS_UNSUCCESSFUL; // allocate array???
+
+ if (entriesAvailable == 0 || ods_state->groupsIndex >= entriesAvailable) {
+ DEBUG(module_debug,("odssam_getgrpwent: entriesAvailable(%d) contextData(%p)\n", entriesAvailable, ods_state->contextData));
+ CFArrayRemoveAllValues(ods_state->groupsArray);
+
+ if (entriesAvailable && ods_state->groupsIndex >= entriesAvailable && ods_state->contextData == NULL) {
+ odssam_endsampwent(methods);
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ if ((dirStatus = get_sam_record_attributes(ods_state, ods_state->groupsArray, kDSStdRecordTypeGroups, NULL)) != eDSNoErr) {
+ ret = NT_STATUS_UNSUCCESSFUL;
+ } else {
+ entriesAvailable = CFArrayGetCount(ods_state->groupsArray);
+ DEBUG(module_debug,("odssam_getgrpwent: entriesAvailable Take 2(%d) contextData(%p)\n", entriesAvailable, ods_state->contextData));
+ ods_state->groupsIndex = 0;
+ }
+ }
+
+ if (dirStatus == eDSNoErr && entriesAvailable) {
+ entry = (CFMutableDictionaryRef) CFArrayGetValueAtIndex(ods_state->groupsArray, ods_state->groupsIndex);
+ ods_state->groupsIndex++;
+ if (!init_group_from_ods(ods_state, map, entry)) {
+ DEBUG(module_debug,("odssam_getgrpwent: init_group_from_ods failed for group index(%d)\n", ods_state->groupsIndex));
+ ret = NT_STATUS_UNSUCCESSFUL;
+ }
+ ret = NT_STATUS_OK;
+ }
+
+
+ return ret;
+}
+
+static NTSTATUS odssam_getgrsid(struct pdb_methods *methods,
+ GROUP_MAP *map, DOM_SID group_sid)
+{
+ NTSTATUS ret = NT_STATUS_UNSUCCESSFUL;
+ struct odssam_privates *ods_state = get_ods_state(methods);
+ CFDictionaryRef entry = NULL;
+ fstring sid_string;
+
+ if (ods_state == NULL) {
+ ret = NT_STATUS_INVALID_HANDLE;
+ goto cleanup;
+ }
+
+ DEBUG(module_debug,("looking up group SID %s\n",
+ sid_to_string(sid_string, &group_sid)));
+
+ entry = opendirectory_find_record_from_groupsid(&ods_state->session,
+ &group_sid);
+ if (!entry) {
+ ret = NT_STATUS_NO_SUCH_USER;
+ goto cleanup;
+ }
+
+ if (!init_group_from_ods(ods_state, map, entry)) {
+ DEBUG(module_debug,("odssam_getgrsid: init_group_from_ods failed!\n"));
+ ret = NT_STATUS_INTERNAL_ERROR;
+ goto cleanup;
+ }
+ ret = NT_STATUS_OK;
+
+cleanup:
+ DEBUG(module_debug,("searching group SID %s, result=%s\n",
+ sid_to_string(sid_string, &group_sid), nt_errstr(ret)));
+
+ if (entry) {
+ CFRelease(entry);
+ }
+
+ return ret;
+}
+
+static NTSTATUS odssam_getgrgid(struct pdb_methods *methods,
+ GROUP_MAP *map, gid_t gid)
+{
+ NTSTATUS ret = NT_STATUS_NO_SUCH_GROUP;
+ struct odssam_privates *ods_state = get_ods_state(methods);
+ CFMutableArrayRef usersArray = NULL;
+ CFMutableDictionaryRef entry = NULL;
+ pstring gid_string;
+
+ if (ods_state == NULL) {
+ return NT_STATUS_INVALID_HANDLE;
+ }
+
+ ZERO_STRUCTP(map);
+
+ snprintf(gid_string, sizeof(gid_string) - 1, "%i", gid);
+ DEBUG(module_debug,("odssam_getgrgid: gid [%s]\n", gid_string));
+
+ if (opendirectory_sam_searchattr(&ods_state->session,
+ &usersArray, kDSStdRecordTypeGroups,
+ kDS1AttrPrimaryGroupID, gid_string) != eDSNoErr) {
+ goto cleanup;
+ }
+
+ entry = (CFMutableDictionaryRef) CFArrayGetValueAtIndex(usersArray, 0);
+ if (!entry) {
+ goto cleanup;
+ }
+
+ if (!init_group_from_ods(ods_state, map, entry)) {
+ DEBUG(module_debug,("odssam_getgrgid: init_group_from_ods failed!\n"));
+ goto cleanup;
+ }
+
+ /* Fail if we were't able to map the SID for this group. */
+ if (!is_null_sid(&map->sid)) {
+ ret = NT_STATUS_OK;
+ }
+
+cleanup:
+ if (usersArray) {
+ CFRelease(usersArray);
+ }
+ return ret;
+}
+
+static NTSTATUS odssam_getgrnam(struct pdb_methods *methods,
+ GROUP_MAP *map,
+ const char *name)
+{
+ tDirStatus dirStatus = eDSNoErr;
+ CFMutableDictionaryRef entry = NULL;
+ CFMutableArrayRef recordsArray;
+
+ struct odssam_privates *ods_state = get_ods_state(methods);
+
+ if (ods_state == NULL) {
+ return NT_STATUS_INVALID_HANDLE;
+ }
+
+ DEBUG(module_debug,("getting kDSStdRecordTypeGroups and "
+ "kDSNAttrRecordName for <%s>\n", name));
+
+ dirStatus = opendirectory_sam_searchattr(&ods_state->session,
+ &recordsArray, kDSStdRecordTypeGroups,
+ kDSNAttrRecordName, name);
+
+ if (dirStatus != eDSNoErr) {
+ DEBUG(module_debug,("getting kDSStdRecordTypeGroups and "
+ "kDS1AttrDistinguishedName for <%s>\n", name));
+
+ dirStatus = opendirectory_sam_searchattr(&ods_state->session,
+ &recordsArray, kDSStdRecordTypeGroups,
+ kDS1AttrDistinguishedName, name);
+ }
+
+ if (dirStatus != eDSNoErr) {
+ LOG_DS_ERROR_MSG(DS_TRACE_ERRORS, dirStatus, "odssam_getgrnam",
+ ("no %s record for '%s'!\n",
+ kDSStdRecordTypeGroups, name));
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ SMB_ASSERT(recordsArray != NULL);
+
+ /* handle duplicates - currently uses first match in search policy */
+ entry = (CFMutableDictionaryRef)CFArrayGetValueAtIndex(recordsArray, 0);
+ if (!init_group_from_ods(ods_state, map, entry)) {
+ CFRelease(recordsArray);
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ CFRelease(recordsArray);
+ return NT_STATUS_OK;
+}
+
+
+static BOOL init_ods_from_group(struct odssam_privates *ods_state,
+ GROUP_MAP *map,
+ CFMutableDictionaryRef groupEntry,
+ CFMutableDictionaryRef mods)
+
+{
+ pstring tmp;
+
+ if (groupEntry == NULL || map == NULL || mods == NULL) {
+ DEBUG(module_debug, ("init_ods_from_group: NULL parameters found!\n"));
+ return False;
+ }
+
+ if (sid_to_string(tmp, &map->sid)) {
+ update_user_entry(mods, kDS1AttrSMBSID, tmp);
+ }
+
+ if (map->nt_name) {
+ update_user_entry(mods, kDS1AttrDistinguishedName,
+ map->nt_name);
+ }
+
+ if (map->comment) {
+ update_user_entry(mods, kDS1AttrComment, map->comment);
+ }
+
+
+ return True;
+}
+
+static NTSTATUS odssam_add_group_mapping_entry(struct pdb_methods *methods,
+ GROUP_MAP *map)
+{
+ NTSTATUS ret = NT_STATUS_UNSUCCESSFUL;
+ struct odssam_privates *ods_state = get_ods_state(methods);
+ tDirStatus dirStatus = eDSNoErr;
+ CFMutableArrayRef recordsArray = NULL;
+ CFMutableDictionaryRef entry = NULL, groupMods = NULL;
+ pstring gid_string;
+
+ if (ods_state == NULL) {
+ return NT_STATUS_INVALID_HANDLE;
+ }
+
+ snprintf(gid_string, sizeof(gid_string) - 1, "%i", map->gid);
+ DEBUG(module_debug,("odssam_add_group_mapping_entry: gid [%s]\n", gid_string));
+ if (opendirectory_sam_searchattr(&ods_state->session,
+ &recordsArray, kDSStdRecordTypeGroups,
+ kDS1AttrPrimaryGroupID, gid_string) != eDSNoErr) {
+ ret = NT_STATUS_UNSUCCESSFUL;
+ goto cleanup;
+ }
+
+ groupMods = CFDictionaryCreateMutable(NULL, 0,
+ &kCFCopyStringDictionaryKeyCallBacks,
+ &kCFTypeDictionaryValueCallBacks);
+ entry = (CFMutableDictionaryRef)CFArrayGetValueAtIndex(recordsArray, 0);
+ if (!entry) {
+ ret = NT_STATUS_UNSUCCESSFUL;
+ goto cleanup;
+ }
+
+ if (!init_ods_from_group(ods_state, map, entry, groupMods)) {
+ DEBUG(module_debug,("odssam_add_group_mapping_entry: "
+ "init_ods_from_group failed!\n"));
+
+ ret = NT_STATUS_UNSUCCESSFUL;
+ goto cleanup;
+ }
+ ret = NT_STATUS_OK;
+
+ if (CFDictionaryGetCount(groupMods) == 0) {
+ DEBUG(module_debug, ("odssam_add_group_mapping_entry: mods is empty\n"));
+ ret = NT_STATUS_UNSUCCESSFUL;
+ goto cleanup;
+ }
+
+ dirStatus = add_record_attributes(ods_state, groupMods,
+ entry, kDSStdRecordTypeGroups);
+ LOG_DS_ERROR(ds_trace, dirStatus, "add_record_attributes");
+ if (eDSNoErr != dirStatus) {
+ ret = NT_STATUS_UNSUCCESSFUL;
+ goto cleanup;
+ }
+
+ ret = NT_STATUS_OK;
+ DEBUG(module_debug, ("odssam_add_group_mapping_entry: successfully "
+ "modified group %ld in Open Directory\n",
+ (long)map->gid));
+
+cleanup:
+ if (groupMods) {
+ CFRelease(groupMods);
+ }
+
+ return ret;
+}
+
+static NTSTATUS odssam_update_group_mapping_entry(struct pdb_methods *methods,
+ GROUP_MAP *map)
+{
+ NTSTATUS ret = NT_STATUS_UNSUCCESSFUL;
+ struct odssam_privates *ods_state = get_ods_state(methods);
+ tDirStatus dirStatus = eDSNoErr;
+ CFMutableArrayRef recordsArray = NULL;
+ CFMutableDictionaryRef entry = NULL;
+ CFMutableDictionaryRef groupMods = NULL;
+ pstring gid_string;
+
+ if (ods_state == NULL) {
+ return NT_STATUS_INVALID_HANDLE;
+ }
+
+ snprintf(gid_string, sizeof(gid_string) - 1, "%i", map->gid);
+ DEBUG(module_debug,("odssam_update_group_mapping_entry: gid [%s]\n", gid_string));
+ if (opendirectory_sam_searchattr(&ods_state->session,
+ &recordsArray, kDSStdRecordTypeGroups,
+ kDS1AttrPrimaryGroupID, gid_string) != eDSNoErr) {
+ ret = NT_STATUS_UNSUCCESSFUL;
+ goto cleanup;
+ }
+
+ groupMods = CFDictionaryCreateMutable(NULL, 0,
+ &kCFCopyStringDictionaryKeyCallBacks,
+ &kCFTypeDictionaryValueCallBacks);
+ entry = (CFMutableDictionaryRef)CFArrayGetValueAtIndex(recordsArray, 0);
+ if (!entry) {
+ ret = NT_STATUS_UNSUCCESSFUL;
+ goto cleanup;
+ }
+
+ if (!init_ods_from_group(ods_state, map, entry, groupMods)) {
+ DEBUG(module_debug,("odssam_update_group_mapping_entry: "
+ "init_ods_from_group failed!\n"));
+ ret = NT_STATUS_UNSUCCESSFUL;
+ goto cleanup;
+ }
+
+ if (CFDictionaryGetCount(groupMods) == 0) {
+ DEBUG(module_debug, ("odssam_update_group_mapping_entry: mods is empty\n"));
+ ret = NT_STATUS_UNSUCCESSFUL;
+ goto cleanup;
+ }
+
+ dirStatus = add_record_attributes(ods_state, groupMods,
+ entry, kDSStdRecordTypeGroups);
+ LOG_DS_ERROR(ds_trace, dirStatus, "add_record_attributes");
+ if (eDSNoErr != dirStatus) {
+ ret = NT_STATUS_UNSUCCESSFUL;
+ goto cleanup;
+ }
+
+ ret = NT_STATUS_OK;
+ DEBUG(module_debug, ("odssam_update_group_mapping_entry: successfully "
+ "modified group %ld in Open Directory\n",
+ (unsigned long)map->gid));
+
+cleanup:
+ if (groupMods) {
+ CFRelease(groupMods);
+ }
+ return ret;
+}
+
+static NTSTATUS odssam_enum_group_mapping(struct pdb_methods *methods,
+ const DOM_SID *sid,
+ enum lsa_SidType sid_name_use,
+ GROUP_MAP **rmap,
+ size_t *num_entries,
+ BOOL unix_only)
+{
+ GROUP_MAP map;
+ int entries = 0;
+
+ *num_entries = 0;
+ *rmap = NULL;
+
+ if (!NT_STATUS_IS_OK(odssam_setgrpwent(methods, False))) {
+ DEBUG(module_debug, ("unable to open passdb\n"));
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+ while (NT_STATUS_IS_OK(odssam_getgrpwent(methods, &map))) {
+ if (sid_name_use != SID_NAME_UNKNOWN &&
+ sid_name_use != map.sid_name_use) {
+ DEBUG(module_debug, ("group %s is not of the requested type\n",
+ map.nt_name));
+ continue;
+ }
+
+ if (unix_only==ENUM_ONLY_MAPPED && map.gid==-1) {
+ DEBUG(module_debug,("group %s is not mapped\n", map.nt_name));
+ continue;
+ }
+
+ *rmap = SMB_REALLOC((*rmap), (entries+1)*sizeof(GROUP_MAP));
+ if (!(*rmap)) {
+ DEBUG(module_debug, ("unable to enlarge group map for %s\n",
+ map.nt_name));
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ (*rmap)[entries] = map;
+ entries++;
+
+ }
+ odssam_endgrpwent(methods);
+
+ *num_entries = entries;
+ return NT_STATUS_OK;
+}
+
+static void odssam_free_private_data(void **data)
+{
+ struct odssam_privates *ods_state = (struct odssam_privates *)(*data);
+
+ DEBUG(module_debug, ("%s: free private data\n", MODULE_NAME));
+ opendirectory_disconnect(&ods_state->session);
+ talloc_free(ods_state);
+ *data = NULL;
+}
+
+static NTSTATUS odssam_init_name(struct pdb_methods ** pdb_methods,
+ const char *location, const char *name)
+{
+ struct odssam_privates *ods_state = NULL;
+ NTSTATUS status;
+
+ if (!NT_STATUS_IS_OK(status = make_pdb_method(pdb_methods))) {
+ return status;
+ }
+
+ (*pdb_methods)->name = name;
+
+ (*pdb_methods)->setsampwent = odssam_setsampwent;
+ (*pdb_methods)->endsampwent = odssam_endsampwent;
+ (*pdb_methods)->getsampwent = odssam_getsampwent;
+ (*pdb_methods)->getsampwnam = odssam_getsampwnam;
+ (*pdb_methods)->getsampwsid = odssam_getsampwsid;
+ (*pdb_methods)->add_sam_account = odssam_add_sam_account;
+ (*pdb_methods)->update_sam_account = odssam_update_sam_account;
+
+#ifdef USES_ODGROUPMAPPING
+ (*pdb_methods)->getgrsid = odssam_getgrsid;
+ (*pdb_methods)->getgrgid = odssam_getgrgid;
+ (*pdb_methods)->getgrnam = odssam_getgrnam;
+ (*pdb_methods)->add_group_mapping_entry = odssam_add_group_mapping_entry;
+ (*pdb_methods)->update_group_mapping_entry = odssam_update_group_mapping_entry;
+// (*pdb_methods)->delete_group_mapping_entry = odssam_delete_group_mapping_entry;
+ (*pdb_methods)->enum_group_mapping = odssam_enum_group_mapping;
+#endif
+
+ ods_state = TALLOC_ZERO_P(NULL, struct odssam_privates);
+
+ if (!ods_state) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ ods_state->usersArray =
+ CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
+ ods_state->groupsArray =
+ CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
+
+ if (!ods_state->usersArray || !ods_state->groupsArray) {
+ talloc_free(ods_state);
+ return NT_STATUS_NO_MEMORY ;
+ }
+
+ (*pdb_methods)->private_data = ods_state;
+ (*pdb_methods)->free_private_data = odssam_free_private_data;
+
+ odssam_debug_level = debug_add_class(MODULE_NAME);
+
+ if (odssam_open(ods_state) != eDSNoErr) {
+ DEBUG(module_debug, ("Unable to access Directory Services\n"));
+ talloc_free(ods_state);
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS odssam_init(struct pdb_methods ** pdb_methods,
+ const char *location)
+{
+ return odssam_init_name(pdb_methods, location, MODULE_NAME);
+}
+
+static NTSTATUS odssam_init_historic(struct pdb_methods ** pdb_methods,
+ const char *location)
+{
+ return odssam_init_name(pdb_methods, location, "opendirectorysam");
+}
+
+NTSTATUS pdb_odsam_init(void)
+{
+ struct passwd *pwd;
+
+ /* Stash the guest user UID for later. */
+ if ((pwd = sys_getpwnam(lp_guestaccount()))) {
+ guest_uid = pwd->pw_uid;
+ } else {
+ DEBUG(0, ("%s: guest account '%s' is not available, using 99\n",
+ MODULE_NAME, lp_guestaccount));
+ guest_uid = 99;
+ }
+
+ /* Use "odsam:traceall = yes" to turn on OD query tracing. */
+ if (lp_parm_bool(GLOBAL_SECTION_SNUM,
+ MODULE_NAME, "traceall", False)) {
+ ds_trace = DS_TRACE_ALL;
+ }
+
+ module_debug = lp_parm_int(GLOBAL_SECTION_SNUM,
+ MODULE_NAME, "msglevel", 100);
+
+ /* Register current module name. */
+ smb_register_passdb(PASSDB_INTERFACE_VERSION,
+ MODULE_NAME, odssam_init);
+
+ /* Register historic name for 10.4 configs. */
+ smb_register_passdb(PASSDB_INTERFACE_VERSION,
+ "opendirectorysam", odssam_init_historic);
+
+ return NT_STATUS_OK;
+}