idmap-module-open-directory   [plain text]


Index: samba/source/Makefile.in
===================================================================
--- samba/source/Makefile.in.orig
+++ samba/source/Makefile.in
@@ -455,6 +455,7 @@ AUTH_OBJ = auth/auth.o @AUTH_STATIC@ aut
 
 ODSAM_PDB_OBJ = lib/opendirectory.o passdb/pdb_odsam.o
 ODSAM_AUTH_OBJ = lib/opendirectory.o auth/auth_odsam.o
+ODSAM_IDMAP_OBJ = lib/opendirectory.o nsswitch/idmap_odsam.o
 
 MANGLE_OBJ = smbd/mangle.o smbd/mangle_hash.o smbd/mangle_map.o smbd/mangle_hash2.o
 
@@ -1351,6 +1352,11 @@ bin/pdb_odsam.@SHLIBEXT@: $(ODSAM_PDB_OB
 	@$(SHLD_MODULE) $(ODSAM_PDB_OBJ) \
 		-framework DirectoryService
 
+bin/idmap_odsam.@SHLIBEXT@: $(ODSAM_IDMAP_OBJ)
+	@echo "Building plugin $@"
+	@$(SHLD_MODULE) $(ODSAM_IDMAP_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
@@ -6166,6 +6166,7 @@ SMB_MODULE(idmap_passdb, nsswitch/idmap_
 SMB_MODULE(idmap_nss, nsswitch/idmap_nss.o, "bin/nss.$SHLIBEXT", IDMAP)
 SMB_MODULE(idmap_rid, nsswitch/idmap_rid.o, "bin/rid.$SHLIBEXT", IDMAP)
 SMB_MODULE(idmap_ad, nsswitch/idmap_ad.o, "bin/ad.$SHLIBEXT", IDMAP)
+SMB_MODULE(idmap_odsam, \$(ODSAM_IDMAP_OBJ), "bin/idmap_odsam.$SHLIBEXT", IDMAP)
 SMB_SUBSYSTEM(IDMAP, nsswitch/idmap.o)
 
 SMB_MODULE(nss_info_template, nsswitch/nss_info_template.o, "bin/template.$SHLIBEXT", NSS_INFO)
Index: samba/source/nsswitch/idmap_odsam.c
===================================================================
--- /dev/null
+++ samba/source/nsswitch/idmap_odsam.c
@@ -0,0 +1,309 @@
+/*
+   Unix SMB/CIFS implementation.
+   ID mapping Open Directory Server backend
+
+   Copyright (c) 2007 Apple Inc. All rights rserved.
+
+   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.
+*/
+
+
+#include "includes.h"
+#include "winbindd.h"
+#include "opendirectory.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_IDMAP
+
+/* NOTE: The Open Directory passdb and idmap modules are tightly coupled, so we
+ * link them into the same DSO and expect them to be used together.
+ */
+#define MODULE_NAME "odsam"
+
+static enum ds_trace_level ds_trace = DS_TRACE_ERRORS;
+static int module_debug;
+
+static struct opendirectory_session od_idmap_session;
+
+static CFDictionaryRef find_group_by_id(
+				struct opendirectory_session *session,
+				uint32_t groupid)
+{
+	CFMutableArrayRef records = NULL;
+	fstring gid_string;
+	tDirStatus status;
+
+	snprintf(gid_string, sizeof(gid_string) - 1, "%i", groupid);
+	status = opendirectory_sam_searchattr(session,
+			    &records, kDSStdRecordTypeGroups,
+			    kDS1AttrPrimaryGroupID, gid_string);
+	LOG_DS_ERROR_MSG(ds_trace, status,
+		"opendirectory_sam_searchattr[StdRecordTypeGroups]",
+		("GroupID=%u\n", groupid));
+
+	if (records) {
+		CFDictionaryRef first =
+		    (CFDictionaryRef)CFArrayGetValueAtIndex(records, 0);
+
+		CFRetain(first);
+		CFRelease(records);
+		return first;
+	}
+
+	return NULL;
+}
+
+static CFDictionaryRef find_user_by_id(
+				struct opendirectory_session *session,
+				uint32_t userid)
+{
+	CFMutableArrayRef records = NULL;
+	fstring uid_string;
+	tDirStatus status;
+
+	snprintf(uid_string, sizeof(uid_string) - 1, "%i", userid);
+
+	status = opendirectory_sam_searchattr(session,
+			    &records, kDSStdRecordTypeUsers,
+			    kDS1AttrUniqueID, uid_string);
+	LOG_DS_ERROR_MSG(ds_trace, status,
+		"opendirectory_sam_searchattr[StdRecordTypeUsers]",
+		("UniqueID=%u\n", userid));
+
+	/* XXX AFAICT there is no way to look up computer accounts via the
+	 * Unix UID -- jpeach.
+	 */
+
+	if (records) {
+		CFDictionaryRef first =
+		    (CFDictionaryRef)CFArrayGetValueAtIndex(records, 0);
+
+		CFRetain(first);
+		CFRelease(records);
+		return first;
+	}
+
+	return NULL;
+}
+
+/* Map a SID to the corresponding Unix UID or GID. */
+static NTSTATUS ods_map_sid(struct opendirectory_session *session,
+				struct id_map *id_entry)
+{
+	CFDictionaryRef sam_record;
+	char * id_string;
+	fstring sid_string;
+
+	id_entry->status = ID_UNKNOWN;
+
+	if (id_entry->xid.type == ID_TYPE_UID) {
+		sam_record = opendirectory_find_record_from_usersid(session,
+							id_entry->sid);
+		if (sam_record) {
+
+			id_string = opendirectory_get_record_attribute(NULL,
+					    sam_record, kDS1AttrUniqueID);
+
+			goto success;
+		}
+	} else {
+		SMB_ASSERT(id_entry->xid.type == ID_TYPE_GID);
+		sam_record = opendirectory_find_record_from_groupsid(session,
+							id_entry->sid);
+		if (sam_record) {
+			id_entry->xid.type = ID_TYPE_GID;
+			id_string = opendirectory_get_record_attribute(NULL,
+					    sam_record, kDS1AttrPrimaryGroupID);
+			goto success;
+		}
+
+		DEBUG(module_debug, ("%s: no match for %s\n", MODULE_NAME,
+			sid_to_string(sid_string, id_entry->sid)));
+	}
+
+	return NT_STATUS_OK;
+
+success:
+	DEBUG(module_debug, ("%s: mapped %s SID to %s ID %s\n",
+		    MODULE_NAME,
+		    (id_entry->xid.type == ID_TYPE_UID) ? "user" : "group",
+		    sid_to_string(sid_string, id_entry->sid),
+		    id_string));
+
+
+	id_entry->status = ID_MAPPED;
+	id_entry->xid.id = strtoul(id_string, NULL, 10 /* base */);
+	TALLOC_FREE(id_string);
+
+	CFRelease(sam_record);
+	return NT_STATUS_OK;
+}
+
+/* Map a Unix UID or GID to the corresponding SID. */
+static NTSTATUS ods_map_unixid(struct opendirectory_session *session,
+				struct id_map *id_entry)
+{
+	CFDictionaryRef sam_record;
+	fstring sid_string;
+
+	id_entry->status = ID_UNKNOWN;
+
+	if (id_entry->xid.type == ID_TYPE_UID) {
+		sam_record = find_user_by_id(session, id_entry->xid.id);
+	} else {
+		SMB_ASSERT(id_entry->xid.type == ID_TYPE_GID);
+		sam_record = find_group_by_id(session, id_entry->xid.id);
+	}
+
+	if (!sam_record) {
+		DEBUG(module_debug, ("%s: no match for %s ID %li\n",
+		    MODULE_NAME,
+		    (id_entry->xid.type == ID_TYPE_UID) ? "user" : "group",
+		    (long)id_entry->xid.id));
+
+		return NT_STATUS_OK;
+	}
+
+	id_entry->status = ID_UNMAPPED;
+
+	if (id_entry->xid.type == ID_TYPE_UID) {
+		if (opendirectory_find_usersid_from_record(session,
+					    sam_record, id_entry->sid)) {
+			id_entry->status = ID_MAPPED;
+		}
+	} else {
+		SMB_ASSERT(id_entry->xid.type == ID_TYPE_GID);
+		if (opendirectory_find_groupsid_from_record(session,
+					    sam_record, id_entry->sid)) {
+			id_entry->status = ID_MAPPED;
+		}
+	}
+
+	DEBUG(module_debug, ("%s: mapped %s ID %li to SID %s\n",
+		    MODULE_NAME,
+		    (id_entry->xid.type == ID_TYPE_UID) ? "user" : "group",
+		    (long)id_entry->xid.id,
+		    sid_to_string(sid_string, id_entry->sid)));
+
+	CFRelease(sam_record);
+	return NT_STATUS_OK;
+}
+
+static NTSTATUS idmap_ods_initialize(struct idmap_domain *dom)
+{
+	if (opendirectory_connect(&od_idmap_session) != eDSNoErr) {
+		/* XXX should be mapping DS to NT errors */
+		return NT_STATUS_INSUFF_SERVER_RESOURCES;
+	}
+
+	return NT_STATUS_OK;
+}
+
+static NTSTATUS idmap_ods_close(struct idmap_domain *dom)
+{
+	opendirectory_disconnect(&od_idmap_session);
+	return NT_STATUS_OK;
+}
+
+static NTSTATUS idmap_ods_unixids_to_sids(struct idmap_domain *dom,
+				    struct id_map **ids)
+{
+	struct id_map **current;
+
+	if (opendirectory_reconnect(&od_idmap_session) != eDSNoErr) {
+		/* XXX should be mapping DS to NT errors */
+		return NT_STATUS_INSUFF_SERVER_RESOURCES;
+	}
+
+	for (current = ids; current && *current; ++current) {
+		ods_map_unixid(&od_idmap_session, *current);
+	}
+
+	return NT_STATUS_OK;
+}
+
+static NTSTATUS idmap_ods_sids_to_unixids(struct idmap_domain *dom,
+				    struct id_map **ids)
+{
+	struct id_map **current;
+
+	if (opendirectory_reconnect(&od_idmap_session) != eDSNoErr) {
+		/* XXX should be mapping DS to NT errors */
+		return NT_STATUS_INSUFF_SERVER_RESOURCES;
+	}
+
+	for (current = ids; current && *current; ++current) {
+		ods_map_sid(&od_idmap_session, *current);
+	}
+
+	return NT_STATUS_OK;
+}
+
+static NTSTATUS ods_alloc_init(const char * compat)
+{
+	return NT_STATUS_OK;
+}
+
+static NTSTATUS ods_alloc_close(void)
+{
+	return NT_STATUS_OK;
+}
+
+static NTSTATUS ods_alloc_not_supported(struct unixid *id)
+{
+	return NT_STATUS_NOT_SUPPORTED;
+}
+
+static struct idmap_methods ods_idmap_methods = {
+	/* init */		idmap_ods_initialize,
+	/* unixids_to_sids */	idmap_ods_unixids_to_sids,
+	/* sids_to_unixids */	idmap_ods_sids_to_unixids,
+	/* set_mapping */	NULL,
+	/* remove_mapping */	NULL,
+	/* dump_data */		NULL,
+	/* close */		idmap_ods_close
+};
+
+/* Any static mappings are controlled in Open Directory using the native tools.
+ * We provide a module that always fails to make it explicit that we can't
+ * manipulate this from the Samba side.
+ */
+static struct idmap_alloc_methods ods_alloc_methods = {
+
+	/* init */	    ods_alloc_init,
+	/* allocate_id */   ods_alloc_not_supported,
+	/* get_id_hwm */    ods_alloc_not_supported,
+	/* set_id_hwm */    ods_alloc_not_supported,
+	/* close */	    ods_alloc_close
+};
+
+ NTSTATUS idmap_odsam_init(void)
+{
+	/* 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);
+
+	smb_register_idmap(SMB_IDMAP_INTERFACE_VERSION, MODULE_NAME,
+		&ods_idmap_methods);
+	smb_register_idmap_alloc(SMB_IDMAP_INTERFACE_VERSION, MODULE_NAME,
+		&ods_alloc_methods);
+	return NT_STATUS_OK;
+}
+