From 28aa4bff8d6be031c6089fe5c7ab010f1cc48340 Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Fri, 14 Sep 2007 12:03:58 +0000 Subject: r25154: move winbindd code into winbindd/ metze (This used to be commit 3ac7566ae14c48ff9b0f6b232e0ec4b2f73df558) --- source3/Makefile.in | 68 +- source3/configure.in | 20 +- source3/nsswitch/idmap.c | 1562 ------------ source3/nsswitch/idmap_ad.c | 870 ------- source3/nsswitch/idmap_cache.c | 531 ---- source3/nsswitch/idmap_ldap.c | 1505 ----------- source3/nsswitch/idmap_nss.c | 223 -- source3/nsswitch/idmap_passdb.c | 139 -- source3/nsswitch/idmap_rid.c | 267 -- source3/nsswitch/idmap_tdb.c | 1232 --------- source3/nsswitch/idmap_util.c | 171 -- source3/nsswitch/nss_info.c | 301 --- source3/nsswitch/nss_info_template.c | 83 - source3/nsswitch/winbindd.c | 1254 ---------- source3/nsswitch/winbindd.h | 365 --- source3/nsswitch/winbindd_ads.c | 1312 ---------- source3/nsswitch/winbindd_async.c | 1695 ------------- source3/nsswitch/winbindd_cache.c | 3892 ----------------------------- source3/nsswitch/winbindd_ccache_access.c | 283 --- source3/nsswitch/winbindd_cm.c | 2271 ----------------- source3/nsswitch/winbindd_cred_cache.c | 802 ------ source3/nsswitch/winbindd_creds.c | 162 -- source3/nsswitch/winbindd_dual.c | 1130 --------- source3/nsswitch/winbindd_group.c | 1746 ------------- source3/nsswitch/winbindd_misc.c | 634 ----- source3/nsswitch/winbindd_pam.c | 2382 ------------------ source3/nsswitch/winbindd_passdb.c | 467 ---- source3/nsswitch/winbindd_reconnect.c | 316 --- source3/nsswitch/winbindd_rpc.c | 1111 -------- source3/nsswitch/winbindd_sid.c | 560 ----- source3/nsswitch/winbindd_sockinit.c | 126 - source3/nsswitch/winbindd_user.c | 875 ------- source3/nsswitch/winbindd_util.c | 1460 ----------- source3/nsswitch/winbindd_wins.c | 234 -- source3/winbindd/idmap.c | 1562 ++++++++++++ source3/winbindd/idmap_ad.c | 870 +++++++ source3/winbindd/idmap_cache.c | 531 ++++ source3/winbindd/idmap_ldap.c | 1505 +++++++++++ source3/winbindd/idmap_nss.c | 223 ++ source3/winbindd/idmap_passdb.c | 139 ++ source3/winbindd/idmap_rid.c | 267 ++ source3/winbindd/idmap_tdb.c | 1232 +++++++++ source3/winbindd/idmap_util.c | 171 ++ source3/winbindd/nss_info.c | 301 +++ source3/winbindd/nss_info_template.c | 83 + source3/winbindd/winbindd.c | 1254 ++++++++++ source3/winbindd/winbindd.h | 365 +++ source3/winbindd/winbindd_ads.c | 1312 ++++++++++ source3/winbindd/winbindd_async.c | 1695 +++++++++++++ source3/winbindd/winbindd_cache.c | 3892 +++++++++++++++++++++++++++++ source3/winbindd/winbindd_ccache_access.c | 283 +++ source3/winbindd/winbindd_cm.c | 2271 +++++++++++++++++ source3/winbindd/winbindd_cred_cache.c | 802 ++++++ source3/winbindd/winbindd_creds.c | 162 ++ source3/winbindd/winbindd_dual.c | 1130 +++++++++ source3/winbindd/winbindd_group.c | 1746 +++++++++++++ source3/winbindd/winbindd_misc.c | 634 +++++ source3/winbindd/winbindd_pam.c | 2382 ++++++++++++++++++ source3/winbindd/winbindd_passdb.c | 467 ++++ source3/winbindd/winbindd_reconnect.c | 316 +++ source3/winbindd/winbindd_rpc.c | 1111 ++++++++ source3/winbindd/winbindd_sid.c | 560 +++++ source3/winbindd/winbindd_sockinit.c | 126 + source3/winbindd/winbindd_user.c | 875 +++++++ source3/winbindd/winbindd_util.c | 1460 +++++++++++ source3/winbindd/winbindd_wins.c | 234 ++ 66 files changed, 30004 insertions(+), 30006 deletions(-) delete mode 100644 source3/nsswitch/idmap.c delete mode 100644 source3/nsswitch/idmap_ad.c delete mode 100644 source3/nsswitch/idmap_cache.c delete mode 100644 source3/nsswitch/idmap_ldap.c delete mode 100644 source3/nsswitch/idmap_nss.c delete mode 100644 source3/nsswitch/idmap_passdb.c delete mode 100644 source3/nsswitch/idmap_rid.c delete mode 100644 source3/nsswitch/idmap_tdb.c delete mode 100644 source3/nsswitch/idmap_util.c delete mode 100644 source3/nsswitch/nss_info.c delete mode 100644 source3/nsswitch/nss_info_template.c delete mode 100644 source3/nsswitch/winbindd.c delete mode 100644 source3/nsswitch/winbindd.h delete mode 100644 source3/nsswitch/winbindd_ads.c delete mode 100644 source3/nsswitch/winbindd_async.c delete mode 100644 source3/nsswitch/winbindd_cache.c delete mode 100644 source3/nsswitch/winbindd_ccache_access.c delete mode 100644 source3/nsswitch/winbindd_cm.c delete mode 100644 source3/nsswitch/winbindd_cred_cache.c delete mode 100644 source3/nsswitch/winbindd_creds.c delete mode 100644 source3/nsswitch/winbindd_dual.c delete mode 100644 source3/nsswitch/winbindd_group.c delete mode 100644 source3/nsswitch/winbindd_misc.c delete mode 100644 source3/nsswitch/winbindd_pam.c delete mode 100644 source3/nsswitch/winbindd_passdb.c delete mode 100644 source3/nsswitch/winbindd_reconnect.c delete mode 100644 source3/nsswitch/winbindd_rpc.c delete mode 100644 source3/nsswitch/winbindd_sid.c delete mode 100644 source3/nsswitch/winbindd_sockinit.c delete mode 100644 source3/nsswitch/winbindd_user.c delete mode 100644 source3/nsswitch/winbindd_util.c delete mode 100644 source3/nsswitch/winbindd_wins.c create mode 100644 source3/winbindd/idmap.c create mode 100644 source3/winbindd/idmap_ad.c create mode 100644 source3/winbindd/idmap_cache.c create mode 100644 source3/winbindd/idmap_ldap.c create mode 100644 source3/winbindd/idmap_nss.c create mode 100644 source3/winbindd/idmap_passdb.c create mode 100644 source3/winbindd/idmap_rid.c create mode 100644 source3/winbindd/idmap_tdb.c create mode 100644 source3/winbindd/idmap_util.c create mode 100644 source3/winbindd/nss_info.c create mode 100644 source3/winbindd/nss_info_template.c create mode 100644 source3/winbindd/winbindd.c create mode 100644 source3/winbindd/winbindd.h create mode 100644 source3/winbindd/winbindd_ads.c create mode 100644 source3/winbindd/winbindd_async.c create mode 100644 source3/winbindd/winbindd_cache.c create mode 100644 source3/winbindd/winbindd_ccache_access.c create mode 100644 source3/winbindd/winbindd_cm.c create mode 100644 source3/winbindd/winbindd_cred_cache.c create mode 100644 source3/winbindd/winbindd_creds.c create mode 100644 source3/winbindd/winbindd_dual.c create mode 100644 source3/winbindd/winbindd_group.c create mode 100644 source3/winbindd/winbindd_misc.c create mode 100644 source3/winbindd/winbindd_pam.c create mode 100644 source3/winbindd/winbindd_passdb.c create mode 100644 source3/winbindd/winbindd_reconnect.c create mode 100644 source3/winbindd/winbindd_rpc.c create mode 100644 source3/winbindd/winbindd_sid.c create mode 100644 source3/winbindd/winbindd_sockinit.c create mode 100644 source3/winbindd/winbindd_user.c create mode 100644 source3/winbindd/winbindd_util.c create mode 100644 source3/winbindd/winbindd_wins.c diff --git a/source3/Makefile.in b/source3/Makefile.in index f2f6282509..ffb5d93d50 100644 --- a/source3/Makefile.in +++ b/source3/Makefile.in @@ -852,33 +852,31 @@ PAM_SMBPASS_OBJ = $(PAM_SMBPASS_OBJ_0) $(PARAM_OBJ) $(LIB_NONSMBD_OBJ) $(PASSDB_ $(SECRETS_OBJ) $(SMBLDAP_OBJ) $(LIBSAMBA_OBJ) \ $(RPC_PARSE_OBJ1) $(DOSERR_OBJ) $(LDB_OBJ) $(ERRORMAP_OBJ) -IDMAP_OBJ = nsswitch/idmap.o nsswitch/idmap_cache.o nsswitch/idmap_util.o @IDMAP_STATIC@ +IDMAP_OBJ = winbindd/idmap.o winbindd/idmap_cache.o winbindd/idmap_util.o @IDMAP_STATIC@ -NSS_INFO_OBJ = nsswitch/nss_info.o @NSS_INFO_STATIC@ - -IDMAP_NSS_OBJ = sam/idmap_nss.o @IDMAP_NSS_STATIC@ +NSS_INFO_OBJ = winbindd/nss_info.o @NSS_INFO_STATIC@ WINBINDD_OBJ1 = \ - nsswitch/winbindd.o \ - nsswitch/winbindd_sockinit.o \ - nsswitch/winbindd_user.o \ - nsswitch/winbindd_group.o \ - nsswitch/winbindd_util.o \ - nsswitch/winbindd_cache.o \ - nsswitch/winbindd_pam.o \ - nsswitch/winbindd_sid.o \ - nsswitch/winbindd_misc.o \ - nsswitch/winbindd_cm.o \ - nsswitch/winbindd_wins.o \ - nsswitch/winbindd_rpc.o \ - nsswitch/winbindd_reconnect.o \ - nsswitch/winbindd_ads.o \ - nsswitch/winbindd_passdb.o \ - nsswitch/winbindd_dual.o \ - nsswitch/winbindd_async.o \ - nsswitch/winbindd_creds.o \ - nsswitch/winbindd_cred_cache.o \ - nsswitch/winbindd_ccache_access.o \ + winbindd/winbindd.o \ + winbindd/winbindd_sockinit.o \ + winbindd/winbindd_user.o \ + winbindd/winbindd_group.o \ + winbindd/winbindd_util.o \ + winbindd/winbindd_cache.o \ + winbindd/winbindd_pam.o \ + winbindd/winbindd_sid.o \ + winbindd/winbindd_misc.o \ + winbindd/winbindd_cm.o \ + winbindd/winbindd_wins.o \ + winbindd/winbindd_rpc.o \ + winbindd/winbindd_reconnect.o \ + winbindd/winbindd_ads.o \ + winbindd/winbindd_passdb.o \ + winbindd/winbindd_dual.o \ + winbindd/winbindd_async.o \ + winbindd/winbindd_creds.o \ + winbindd/winbindd_cred_cache.o \ + winbindd/winbindd_ccache_access.o \ auth/token_util.o WINBINDD_OBJ = \ @@ -1549,17 +1547,17 @@ bin/smbpasswd.@SHLIBEXT@: $(BINARY_PREREQS) passdb/pdb_smbpasswd.o @echo "Building plugin $@" @$(SHLD_MODULE) passdb/pdb_smbpasswd.o -bin/rid.@SHLIBEXT@: $(BINARY_PREREQS) nsswitch/idmap_rid.o +bin/rid.@SHLIBEXT@: $(BINARY_PREREQS) winbindd/idmap_rid.o @echo "Building plugin $@" - @$(SHLD_MODULE) nsswitch/idmap_rid.o + @$(SHLD_MODULE) winbindd/idmap_rid.o -bin/ad.@SHLIBEXT@: $(BINARY_PREREQS) nsswitch/idmap_ad.o +bin/ad.@SHLIBEXT@: $(BINARY_PREREQS) winbindd/idmap_ad.o @echo "Building plugin $@" - @$(SHLD_MODULE) nsswitch/idmap_ad.o + @$(SHLD_MODULE) winbindd/idmap_ad.o -bin/ldap.@SHLIBEXT@: $(BINARY_PREREQS) nsswitch/idmap_ldap.o +bin/ldap.@SHLIBEXT@: $(BINARY_PREREQS) winbindd/idmap_ldap.o @echo "Building plugin $@" - @$(SHLD_MODULE) nsswitch/idmap_ldap.o + @$(SHLD_MODULE) winbindd/idmap_ldap.o bin/weird.@SHLIBEXT@: $(BINARY_PREREQS) $(DEVEL_HELP_WEIRD_OBJ) @echo "Building plugin $@" @@ -1962,14 +1960,14 @@ clean: delheaders python_clean # removed by "make clean" this will always be run when you do anything # afterwards. proto_exists: include/proto.h include/build_env.h \ - nsswitch/winbindd_proto.h web/swat_proto.h \ + winbindd/winbindd_proto.h web/swat_proto.h \ client/client_proto.h utils/net_proto.h utils/ntlm_auth_proto.h smbd/build_options.c @touch proto_exists delheaders: @echo Removing prototype headers @rm -f include/proto.h include/build_env.h \ - nsswitch/winbindd_proto.h web/swat_proto.h \ + winbindd/winbindd_proto.h web/swat_proto.h \ client/client_proto.h utils/net_proto.h \ smbd/build_options.c utils/ntlm_auth_proto.h \ utils/passwd_proto.h @@ -1987,9 +1985,9 @@ include/build_env.h: script/build_env.sh @$(SHELL) $(srcdir)/script/build_env.sh $(srcdir) $(builddir) $(CC) \ > $(builddir)/include/build_env.h -nsswitch/winbindd_proto.h: +winbindd/winbindd_proto.h: @cd $(srcdir) && $(SHELL) $(MKPROTO_SH) $(AWK) \ - -h _WINBINDD_PROTO_H_ $(builddir)/nsswitch/winbindd_proto.h \ + -h _WINBINDD_PROTO_H_ $(builddir)/winbindd/winbindd_proto.h \ $(WINBINDD_OBJ1) web/swat_proto.h: @@ -2025,7 +2023,7 @@ headers: $(MAKE) smbd/build_options.c; \ $(MAKE) include/proto.h; \ $(MAKE) include/build_env.h; \ - $(MAKE) nsswitch/winbindd_proto.h; \ + $(MAKE) winbindd/winbindd_proto.h; \ $(MAKE) web/swat_proto.h; \ $(MAKE) client/client_proto.h; \ $(MAKE) utils/ntlm_auth_proto.h; \ diff --git a/source3/configure.in b/source3/configure.in index 031d530f65..67cec38109 100644 --- a/source3/configure.in +++ b/source3/configure.in @@ -6333,16 +6333,16 @@ SMB_MODULE(rpc_unixinfo, \$(RPC_UNIXINFO_OBJ), "bin/librpc_unixinfo.$SHLIBEXT", SMB_MODULE(rpc_epmapper, \$(RPC_EPMAPPER_OBJ), "bin/librpc_epmapper.$SHLIBEXT", RPC) SMB_SUBSYSTEM(RPC,smbd/server.o) -SMB_MODULE(idmap_ldap, nsswitch/idmap_ldap.o, "bin/ldap.$SHLIBEXT", IDMAP) -SMB_MODULE(idmap_tdb, nsswitch/idmap_tdb.o, "bin/tdb.$SHLIBEXT", IDMAP) -SMB_MODULE(idmap_passdb, nsswitch/idmap_passdb.o, "bin/passdb.$SHLIBEXT", 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_SUBSYSTEM(IDMAP, nsswitch/idmap.o) - -SMB_MODULE(nss_info_template, nsswitch/nss_info_template.o, "bin/template.$SHLIBEXT", NSS_INFO) -SMB_SUBSYSTEM(NSS_INFO, nsswitch/nss_info.o) +SMB_MODULE(idmap_ldap, winbindd/idmap_ldap.o, "bin/ldap.$SHLIBEXT", IDMAP) +SMB_MODULE(idmap_tdb, winbindd/idmap_tdb.o, "bin/tdb.$SHLIBEXT", IDMAP) +SMB_MODULE(idmap_passdb, winbindd/idmap_passdb.o, "bin/passdb.$SHLIBEXT", IDMAP) +SMB_MODULE(idmap_nss, winbindd/idmap_nss.o, "bin/nss.$SHLIBEXT", IDMAP) +SMB_MODULE(idmap_rid, winbindd/idmap_rid.o, "bin/rid.$SHLIBEXT", IDMAP) +SMB_MODULE(idmap_ad, winbindd/idmap_ad.o, "bin/ad.$SHLIBEXT", IDMAP) +SMB_SUBSYSTEM(IDMAP, winbindd/idmap.o) + +SMB_MODULE(nss_info_template, winbindd/nss_info_template.o, "bin/template.$SHLIBEXT", NSS_INFO) +SMB_SUBSYSTEM(NSS_INFO, winbindd/nss_info.o) SMB_MODULE(charset_weird, modules/weird.o, "bin/weird.$SHLIBEXT", CHARSET) SMB_MODULE(charset_CP850, modules/CP850.o, "bin/CP850.$SHLIBEXT", CHARSET) diff --git a/source3/nsswitch/idmap.c b/source3/nsswitch/idmap.c deleted file mode 100644 index 2c7acc185c..0000000000 --- a/source3/nsswitch/idmap.c +++ /dev/null @@ -1,1562 +0,0 @@ -/* - Unix SMB/CIFS implementation. - ID Mapping - Copyright (C) Tim Potter 2000 - Copyright (C) Jim McDonough 2003 - Copyright (C) Simo Sorce 2003-2007 - Copyright (C) Jeremy Allison 2006 - - 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 3 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, see . -*/ - -#include "includes.h" -#include "winbindd.h" - -#undef DBGC_CLASS -#define DBGC_CLASS DBGC_IDMAP - -static_decl_idmap; - -struct idmap_backend { - const char *name; - struct idmap_methods *methods; - struct idmap_backend *prev, *next; -}; - -struct idmap_alloc_backend { - const char *name; - struct idmap_alloc_methods *methods; - struct idmap_alloc_backend *prev, *next; -}; - -struct idmap_cache_ctx; - -struct idmap_alloc_context { - const char *params; - struct idmap_alloc_methods *methods; - BOOL initialized; -}; - -static TALLOC_CTX *idmap_ctx = NULL; -static struct idmap_cache_ctx *idmap_cache; - -static struct idmap_backend *backends = NULL; -static struct idmap_domain **idmap_domains = NULL; -static int num_domains = 0; -static int pdb_dom_num = -1; -static int def_dom_num = -1; - -static struct idmap_alloc_backend *alloc_backends = NULL; -static struct idmap_alloc_context *idmap_alloc_ctx = NULL; - -#define IDMAP_CHECK_RET(ret) do { \ - if ( ! NT_STATUS_IS_OK(ret)) { \ - DEBUG(2, ("ERROR: NTSTATUS = 0x%08x\n", NT_STATUS_V(ret))); \ - goto done; \ - } } while(0) -#define IDMAP_REPORT_RET(ret) do { \ - if ( ! NT_STATUS_IS_OK(ret)) { \ - DEBUG(2, ("ERROR: NTSTATUS = 0x%08x\n", NT_STATUS_V(ret))); \ - } } while(0) -#define IDMAP_CHECK_ALLOC(mem) do { \ - if (!mem) { \ - DEBUG(0, ("Out of memory!\n")); ret = NT_STATUS_NO_MEMORY; \ - goto done; \ - } } while(0) - -static struct idmap_methods *get_methods(struct idmap_backend *be, - const char *name) -{ - struct idmap_backend *b; - - for (b = be; b; b = b->next) { - if (strequal(b->name, name)) { - return b->methods; - } - } - - return NULL; -} - -static struct idmap_alloc_methods *get_alloc_methods( - struct idmap_alloc_backend *be, - const char *name) -{ - struct idmap_alloc_backend *b; - - for (b = be; b; b = b->next) { - if (strequal(b->name, name)) { - return b->methods; - } - } - - return NULL; -} - -BOOL idmap_is_offline(void) -{ - return ( lp_winbind_offline_logon() && - get_global_winbindd_state_offline() ); -} - -/********************************************************************** - Allow a module to register itself as a method. -**********************************************************************/ - -NTSTATUS smb_register_idmap(int version, const char *name, - struct idmap_methods *methods) -{ - struct idmap_methods *test; - struct idmap_backend *entry; - - if (!idmap_ctx) { - return NT_STATUS_INTERNAL_DB_ERROR; - } - - if ((version != SMB_IDMAP_INTERFACE_VERSION)) { - DEBUG(0, ("Failed to register idmap module.\n" - "The module was compiled against " - "SMB_IDMAP_INTERFACE_VERSION %d,\n" - "current SMB_IDMAP_INTERFACE_VERSION is %d.\n" - "Please recompile against the current version " - "of samba!\n", - version, SMB_IDMAP_INTERFACE_VERSION)); - return NT_STATUS_OBJECT_TYPE_MISMATCH; - } - - if (!name || !name[0] || !methods) { - DEBUG(0,("Called with NULL pointer or empty name!\n")); - return NT_STATUS_INVALID_PARAMETER; - } - - test = get_methods(backends, name); - if (test) { - DEBUG(0,("Idmap module %s already registered!\n", name)); - return NT_STATUS_OBJECT_NAME_COLLISION; - } - - entry = talloc(idmap_ctx, struct idmap_backend); - if ( ! entry) { - DEBUG(0,("Out of memory!\n")); - return NT_STATUS_NO_MEMORY; - } - entry->name = talloc_strdup(idmap_ctx, name); - if ( ! entry->name) { - DEBUG(0,("Out of memory!\n")); - return NT_STATUS_NO_MEMORY; - } - entry->methods = methods; - - DLIST_ADD(backends, entry); - DEBUG(5, ("Successfully added idmap backend '%s'\n", name)); - return NT_STATUS_OK; -} - -/********************************************************************** - Allow a module to register itself as a method. -**********************************************************************/ - -NTSTATUS smb_register_idmap_alloc(int version, const char *name, - struct idmap_alloc_methods *methods) -{ - struct idmap_alloc_methods *test; - struct idmap_alloc_backend *entry; - - if (!idmap_ctx) { - return NT_STATUS_INTERNAL_DB_ERROR; - } - - if ((version != SMB_IDMAP_INTERFACE_VERSION)) { - DEBUG(0, ("Failed to register idmap alloc module.\n" - "The module was compiled against " - "SMB_IDMAP_INTERFACE_VERSION %d,\n" - "current SMB_IDMAP_INTERFACE_VERSION is %d.\n" - "Please recompile against the current version " - "of samba!\n", - version, SMB_IDMAP_INTERFACE_VERSION)); - return NT_STATUS_OBJECT_TYPE_MISMATCH; - } - - if (!name || !name[0] || !methods) { - DEBUG(0,("Called with NULL pointer or empty name!\n")); - return NT_STATUS_INVALID_PARAMETER; - } - - test = get_alloc_methods(alloc_backends, name); - if (test) { - DEBUG(0,("idmap_alloc module %s already registered!\n", name)); - return NT_STATUS_OBJECT_NAME_COLLISION; - } - - entry = talloc(idmap_ctx, struct idmap_alloc_backend); - if ( ! entry) { - DEBUG(0,("Out of memory!\n")); - return NT_STATUS_NO_MEMORY; - } - entry->name = talloc_strdup(idmap_ctx, name); - if ( ! entry->name) { - DEBUG(0,("Out of memory!\n")); - return NT_STATUS_NO_MEMORY; - } - entry->methods = methods; - - DLIST_ADD(alloc_backends, entry); - DEBUG(5, ("Successfully added idmap alloc backend '%s'\n", name)); - return NT_STATUS_OK; -} - -static int close_domain_destructor(struct idmap_domain *dom) -{ - NTSTATUS ret; - - ret = dom->methods->close_fn(dom); - if (!NT_STATUS_IS_OK(ret)) { - DEBUG(3, ("Failed to close idmap domain [%s]!\n", dom->name)); - } - - return 0; -} - -/************************************************************************** - Shutdown. -**************************************************************************/ - -NTSTATUS idmap_close(void) -{ - /* close the alloc backend first before freeing idmap_ctx */ - if (idmap_alloc_ctx) { - idmap_alloc_ctx->methods->close_fn(); - idmap_alloc_ctx->methods = NULL; - } - alloc_backends = NULL; - - /* this talloc_free call will fire the talloc destructors - * that will free all active backends resources */ - TALLOC_FREE(idmap_ctx); - idmap_cache = NULL; - idmap_domains = NULL; - backends = NULL; - - return NT_STATUS_OK; -} - -/********************************************************************** - Initialise idmap cache and a remote backend (if configured). -**********************************************************************/ - -static const char *idmap_default_domain[] = { "default domain", NULL }; - -/**************************************************************************** - ****************************************************************************/ - -NTSTATUS idmap_init_cache(void) -{ - /* Always initialize the cache. We'll have to delay initialization - of backends if we are offline */ - - if ( idmap_ctx ) { - return NT_STATUS_OK; - } - - if ( (idmap_ctx = talloc_named_const(NULL, 0, "idmap_ctx")) == NULL ) { - return NT_STATUS_NO_MEMORY; - } - - if ( (idmap_cache = idmap_cache_init(idmap_ctx)) == NULL ) { - return NT_STATUS_UNSUCCESSFUL; - } - - return NT_STATUS_OK; -} - -/**************************************************************************** - ****************************************************************************/ - -NTSTATUS idmap_init(void) -{ - NTSTATUS ret; - static NTSTATUS idmap_init_status = NT_STATUS_UNSUCCESSFUL; - struct idmap_domain *dom; - char *compat_backend = NULL; - char *compat_params = NULL; - const char **dom_list = NULL; - char *alloc_backend = NULL; - BOOL default_already_defined = False; - BOOL pri_dom_is_in_list = False; - int compat = 0; - int i; - - ret = idmap_init_cache(); - if (!NT_STATUS_IS_OK(ret)) - return ret; - - if (NT_STATUS_IS_OK(idmap_init_status)) { - return NT_STATUS_OK; - } - - /* We can't reliably call intialization code here unless - we are online. But return NT_STATUS_OK so the upper - level code doesn't abort idmap lookups. */ - - if ( get_global_winbindd_state_offline() ) { - idmap_init_status = NT_STATUS_FILE_IS_OFFLINE; - return NT_STATUS_OK; - } - - static_init_idmap; - - dom_list = lp_idmap_domains(); - - if ( lp_idmap_backend() ) { - const char **compat_list = lp_idmap_backend(); - char *p = NULL; - const char *q = NULL; - - if ( dom_list ) { - DEBUG(0, ("WARNING: idmap backend and idmap domains are" - " mutually exclusive!\n")); - DEBUGADD(0,("idmap backend option will be IGNORED!\n")); - } else { - compat = 1; - - compat_backend = talloc_strdup(idmap_ctx, *compat_list); - if (compat_backend == NULL ) { - ret = NT_STATUS_NO_MEMORY; - goto done; - } - - /* strip any leading idmap_ prefix of */ - if (strncmp(*compat_list, "idmap_", 6) == 0 ) { - q = *compat_list += 6; - DEBUG(0, ("WARNING: idmap backend uses obsolete" - " and deprecated 'idmap_' prefix.\n" - "Please replace 'idmap_%s' by '%s' in" - " %s\n", q, q, dyn_CONFIGFILE)); - compat_backend = talloc_strdup(idmap_ctx, q); - } else { - compat_backend = talloc_strdup(idmap_ctx, - *compat_list); - } - - /* separate the backend and module arguements */ - if ((p = strchr(compat_backend, ':')) != NULL) { - *p = '\0'; - compat_params = p + 1; - } - } - } else if ( !dom_list ) { - /* Back compatible: without idmap domains and explicit - idmap backend. Taking default idmap backend: tdb */ - - compat = 1; - compat_backend = talloc_strdup( idmap_ctx, "tdb"); - compat_params = compat_backend; - } - - if ( ! dom_list) { - dom_list = idmap_default_domain; - } - - /*************************** - * initialize idmap domains - */ - DEBUG(1, ("Initializing idmap domains\n")); - - for (i = 0; dom_list[i]; i++) { - const char *parm_backend; - char *config_option; - - /* ignore BUILTIN and local MACHINE domains */ - if (strequal(dom_list[i], "BUILTIN") - || strequal(dom_list[i], get_global_sam_name())) - { - DEBUG(0,("idmap_init: Ignoring invalid domain %s\n", - dom_list[i])); - continue; - } - - if (strequal(dom_list[i], lp_workgroup())) { - pri_dom_is_in_list = True; - } - /* init domain */ - - dom = TALLOC_ZERO_P(idmap_ctx, struct idmap_domain); - IDMAP_CHECK_ALLOC(dom); - - dom->name = talloc_strdup(dom, dom_list[i]); - IDMAP_CHECK_ALLOC(dom->name); - - config_option = talloc_asprintf(dom, "idmap config %s", - dom->name); - IDMAP_CHECK_ALLOC(config_option); - - /* default or specific ? */ - - dom->default_domain = lp_parm_bool(-1, config_option, - "default", False); - - if (dom->default_domain || - strequal(dom_list[i], idmap_default_domain[0])) { - - /* make sure this is set even when we match - * idmap_default_domain[0] */ - dom->default_domain = True; - - if (default_already_defined) { - DEBUG(1, ("ERROR: Multiple domains defined as" - " default!\n")); - ret = NT_STATUS_INVALID_PARAMETER; - goto done; - } - - default_already_defined = True; - - } - - dom->readonly = lp_parm_bool(-1, config_option, - "readonly", False); - - /* find associated backend (default: tdb) */ - if (compat) { - parm_backend = talloc_strdup(idmap_ctx, compat_backend); - } else { - parm_backend = talloc_strdup(idmap_ctx, - lp_parm_const_string( - -1, config_option, - "backend", "tdb")); - } - IDMAP_CHECK_ALLOC(parm_backend); - - /* get the backend methods for this domain */ - dom->methods = get_methods(backends, parm_backend); - - if ( ! dom->methods) { - ret = smb_probe_module("idmap", parm_backend); - if (NT_STATUS_IS_OK(ret)) { - dom->methods = get_methods(backends, - parm_backend); - } - } - if ( ! dom->methods) { - DEBUG(0, ("ERROR: Could not get methods for " - "backend %s\n", parm_backend)); - ret = NT_STATUS_UNSUCCESSFUL; - goto done; - } - - /* check the set_mapping function exists otherwise mark the - * module as readonly */ - if ( ! dom->methods->set_mapping) { - DEBUG(5, ("Forcing to readonly, as this module can't" - " store arbitrary mappings.\n")); - dom->readonly = True; - } - - /* now that we have methods, - * set the destructor for this domain */ - talloc_set_destructor(dom, close_domain_destructor); - - if (compat_params) { - dom->params = talloc_strdup(dom, compat_params); - IDMAP_CHECK_ALLOC(dom->params); - } else { - dom->params = NULL; - } - - /* Finally instance a backend copy for this domain */ - ret = dom->methods->init(dom); - if ( ! NT_STATUS_IS_OK(ret)) { - DEBUG(0, ("ERROR: Initialization failed for backend " - "%s (domain %s), deferred!\n", - parm_backend, dom->name)); - } - idmap_domains = talloc_realloc(idmap_ctx, idmap_domains, - struct idmap_domain *, i+1); - if ( ! idmap_domains) { - DEBUG(0, ("Out of memory!\n")); - ret = NT_STATUS_NO_MEMORY; - goto done; - } - idmap_domains[i] = dom; - - /* save default domain position for future uses */ - if (dom->default_domain) { - def_dom_num = i; - } - - DEBUG(10, ("Domain %s - Backend %s - %sdefault - %sreadonly\n", - dom->name, parm_backend, - dom->default_domain?"":"not ", - dom->readonly?"":"not ")); - - talloc_free(config_option); - } - - /* save the number of domains we have */ - num_domains = i; - - /* automatically add idmap_nss backend if needed */ - if ((lp_server_role() == ROLE_DOMAIN_MEMBER) && - ( ! pri_dom_is_in_list) && - lp_winbind_trusted_domains_only()) { - - dom = TALLOC_ZERO_P(idmap_ctx, struct idmap_domain); - IDMAP_CHECK_ALLOC(dom); - - dom->name = talloc_strdup(dom, lp_workgroup()); - IDMAP_CHECK_ALLOC(dom->name); - - dom->default_domain = False; - dom->readonly = True; - - /* get the backend methods for passdb */ - dom->methods = get_methods(backends, "nss"); - - /* (the nss module is always statically linked) */ - if ( ! dom->methods) { - DEBUG(0, ("ERROR: No methods for idmap_nss ?!\n")); - ret = NT_STATUS_UNSUCCESSFUL; - goto done; - } - - /* now that we have methods, - * set the destructor for this domain */ - talloc_set_destructor(dom, close_domain_destructor); - - if (compat_params) { - dom->params = talloc_strdup(dom, compat_params); - IDMAP_CHECK_ALLOC(dom->params); - } else { - dom->params = NULL; - } - - /* Finally instance a backend copy for this domain */ - ret = dom->methods->init(dom); - if ( ! NT_STATUS_IS_OK(ret)) { - DEBUG(0, ("ERROR: Init. failed for idmap_nss ?!\n")); - ret = NT_STATUS_UNSUCCESSFUL; - goto done; - } - - idmap_domains = talloc_realloc(idmap_ctx, - idmap_domains, - struct idmap_domain *, - num_domains+1); - if ( ! idmap_domains) { - DEBUG(0, ("Out of memory!\n")); - ret = NT_STATUS_NO_MEMORY; - goto done; - } - idmap_domains[num_domains] = dom; - - DEBUG(10, ("Domain %s - Backend nss - not default - readonly\n", - dom->name )); - - num_domains++; - } - - /**** automatically add idmap_passdb backend ****/ - dom = TALLOC_ZERO_P(idmap_ctx, struct idmap_domain); - IDMAP_CHECK_ALLOC(dom); - - dom->name = talloc_strdup(dom, get_global_sam_name()); - IDMAP_CHECK_ALLOC(dom->name); - - dom->default_domain = False; - dom->readonly = True; - - /* get the backend methods for passdb */ - dom->methods = get_methods(backends, "passdb"); - - /* (the passdb module is always statically linked) */ - if ( ! dom->methods) { - DEBUG(0, ("ERROR: No methods for idmap_passdb ?!\n")); - ret = NT_STATUS_UNSUCCESSFUL; - goto done; - } - - /* now that we have methods, set the destructor for this domain */ - talloc_set_destructor(dom, close_domain_destructor); - - if (compat_params) { - dom->params = talloc_strdup(dom, compat_params); - IDMAP_CHECK_ALLOC(dom->params); - } else { - dom->params = NULL; - } - - /* Finally instance a backend copy for this domain */ - ret = dom->methods->init(dom); - if ( ! NT_STATUS_IS_OK(ret)) { - DEBUG(0, ("ERROR: Init. failed for idmap_passdb ?!\n")); - ret = NT_STATUS_UNSUCCESSFUL; - goto done; - } - - idmap_domains = talloc_realloc(idmap_ctx, - idmap_domains, - struct idmap_domain *, - num_domains+1); - if ( ! idmap_domains) { - DEBUG(0, ("Out of memory!\n")); - ret = NT_STATUS_NO_MEMORY; - goto done; - } - idmap_domains[num_domains] = dom; - - /* needed to handle special BUILTIN and wellknown SIDs cases */ - pdb_dom_num = num_domains; - - DEBUG(10, ("Domain %s - Backend passdb - not default - readonly\n", - dom->name)); - - num_domains++; - /**** finished adding idmap_passdb backend ****/ - - /* sort domains so that the default is the last one */ - /* don't sort if no default domain defined */ - if (def_dom_num != -1 && def_dom_num != num_domains-1) { - /* default is not last, move it */ - struct idmap_domain *tmp; - - if (pdb_dom_num > def_dom_num) { - pdb_dom_num --; - - } else if (pdb_dom_num == def_dom_num) { /* ?? */ - pdb_dom_num = num_domains - 1; - } - - tmp = idmap_domains[def_dom_num]; - - for (i = def_dom_num; i < num_domains-1; i++) { - idmap_domains[i] = idmap_domains[i+1]; - } - idmap_domains[i] = tmp; - def_dom_num = i; - } - - - /* Initialize alloc module */ - - DEBUG(3, ("Initializing idmap alloc module\n")); - - alloc_backend = NULL; - if (compat) { - alloc_backend = talloc_strdup(idmap_ctx, compat_backend); - } else { - char *ab = lp_idmap_alloc_backend(); - - if (ab && (ab[0] != '\0')) { - alloc_backend = talloc_strdup(idmap_ctx, - lp_idmap_alloc_backend()); - } - } - - if ( alloc_backend ) { - - idmap_alloc_ctx = TALLOC_ZERO_P(idmap_ctx, - struct idmap_alloc_context); - IDMAP_CHECK_ALLOC(idmap_alloc_ctx); - - idmap_alloc_ctx->methods = get_alloc_methods(alloc_backends, - alloc_backend); - if ( ! idmap_alloc_ctx->methods) { - ret = smb_probe_module("idmap", alloc_backend); - if (NT_STATUS_IS_OK(ret)) { - idmap_alloc_ctx->methods = - get_alloc_methods(alloc_backends, - alloc_backend); - } - } - if (idmap_alloc_ctx->methods) { - - if (compat_params) { - idmap_alloc_ctx->params = - talloc_strdup(idmap_alloc_ctx, - compat_params); - IDMAP_CHECK_ALLOC(idmap_alloc_ctx->params); - } else { - idmap_alloc_ctx->params = NULL; - } - - ret = idmap_alloc_ctx->methods->init(idmap_alloc_ctx->params); - if ( ! NT_STATUS_IS_OK(ret)) { - DEBUG(0, ("ERROR: Initialization failed for " - "alloc backend %s, deferred!\n", - alloc_backend)); - } else { - idmap_alloc_ctx->initialized = True; - } - } else { - DEBUG(2, ("idmap_init: Unable to get methods for " - "alloc backend %s\n", - alloc_backend)); - /* certain compat backends are just readonly */ - if ( compat ) { - TALLOC_FREE(idmap_alloc_ctx); - ret = NT_STATUS_OK; - } else { - ret = NT_STATUS_UNSUCCESSFUL; - } - } - } - - /* cleanpu temporary strings */ - TALLOC_FREE( compat_backend ); - - idmap_init_status = NT_STATUS_OK; - - return ret; - -done: - DEBUG(0, ("Aborting IDMAP Initialization ...\n")); - idmap_close(); - - return ret; -} - -static NTSTATUS idmap_alloc_init(void) -{ - NTSTATUS ret; - - if (! NT_STATUS_IS_OK(ret = idmap_init())) { - return ret; - } - - if ( ! idmap_alloc_ctx) { - return NT_STATUS_NOT_SUPPORTED; - } - - if ( ! idmap_alloc_ctx->initialized) { - ret = idmap_alloc_ctx->methods->init(idmap_alloc_ctx->params); - if ( ! NT_STATUS_IS_OK(ret)) { - DEBUG(0, ("ERROR: Initialization failed for alloc " - "backend, deferred!\n")); - return ret; - } else { - idmap_alloc_ctx->initialized = True; - } - } - - return NT_STATUS_OK; -} - -/************************************************************************** - idmap allocator interface functions -**************************************************************************/ - -NTSTATUS idmap_allocate_uid(struct unixid *id) -{ - NTSTATUS ret; - - if (! NT_STATUS_IS_OK(ret = idmap_alloc_init())) { - return ret; - } - - id->type = ID_TYPE_UID; - return idmap_alloc_ctx->methods->allocate_id(id); -} - -NTSTATUS idmap_allocate_gid(struct unixid *id) -{ - NTSTATUS ret; - - if (! NT_STATUS_IS_OK(ret = idmap_alloc_init())) { - return ret; - } - - id->type = ID_TYPE_GID; - return idmap_alloc_ctx->methods->allocate_id(id); -} - -NTSTATUS idmap_set_uid_hwm(struct unixid *id) -{ - NTSTATUS ret; - - if (! NT_STATUS_IS_OK(ret = idmap_alloc_init())) { - return ret; - } - - id->type = ID_TYPE_UID; - return idmap_alloc_ctx->methods->set_id_hwm(id); -} - -NTSTATUS idmap_set_gid_hwm(struct unixid *id) -{ - NTSTATUS ret; - - if (! NT_STATUS_IS_OK(ret = idmap_alloc_init())) { - return ret; - } - - id->type = ID_TYPE_GID; - return idmap_alloc_ctx->methods->set_id_hwm(id); -} - -/****************************************************************************** - Lookup an idmap_domain give a full user or group SID - ******************************************************************************/ - -static struct idmap_domain* find_idmap_domain_from_sid( DOM_SID *account_sid ) -{ - DOM_SID domain_sid; - uint32 rid; - struct winbindd_domain *domain = NULL; - int i; - - /* 1. Handle BUILTIN or Special SIDs and prevent them from - falling into the default domain space (if we have a - configured passdb backend. */ - - if ( (pdb_dom_num != -1) && - (sid_check_is_in_builtin(account_sid) || - sid_check_is_in_wellknown_domain(account_sid) || - sid_check_is_in_unix_groups(account_sid) || - sid_check_is_in_unix_users(account_sid)) ) - { - return idmap_domains[pdb_dom_num]; - } - - /* 2. Lookup the winbindd_domain from the account_sid */ - - sid_copy( &domain_sid, account_sid ); - sid_split_rid( &domain_sid, &rid ); - domain = find_domain_from_sid_noinit( &domain_sid ); - - for (i = 0; domain && i < num_domains; i++) { - if ( strequal( idmap_domains[i]->name, domain->name ) ) { - return idmap_domains[i]; - } - } - - /* 3. Fall back to the default domain */ - - if ( def_dom_num != -1 ) { - return idmap_domains[def_dom_num]; - } - - return NULL; -} - -/****************************************************************************** - Lookup an index given an idmap_domain pointer - ******************************************************************************/ - -static uint32 find_idmap_domain_index( struct idmap_domain *id_domain) -{ - int i; - - for (i = 0; i < num_domains; i++) { - if ( idmap_domains[i] == id_domain ) - return i; - } - - return -1; -} - - -/********************************************************* - Check if creating a mapping is permitted for the domain -*********************************************************/ - -static NTSTATUS idmap_can_map(const struct id_map *map, - struct idmap_domain **ret_dom) -{ - struct idmap_domain *dom; - - /* Check we do not create mappings for our own local domain, - * or BUILTIN or special SIDs */ - if ((sid_compare_domain(map->sid, get_global_sam_sid()) == 0) || - sid_check_is_in_builtin(map->sid) || - sid_check_is_in_wellknown_domain(map->sid) || - sid_check_is_in_unix_users(map->sid) || - sid_check_is_in_unix_groups(map->sid) ) - { - DEBUG(10, ("We are not supposed to create mappings for our own " - "domains (local, builtin, specials)\n")); - return NT_STATUS_UNSUCCESSFUL; - } - - /* Special check for trusted domain only = Yes */ - if (lp_winbind_trusted_domains_only()) { - struct winbindd_domain *wdom = find_our_domain(); - if (wdom && (sid_compare_domain(map->sid, &wdom->sid) == 0)) { - DEBUG(10, ("We are not supposed to create mappings for " - "our primary domain when is True\n")); - DEBUGADD(10, ("Leave [%s] unmapped\n", - sid_string_static(map->sid))); - return NT_STATUS_UNSUCCESSFUL; - } - } - - if ( (dom = find_idmap_domain_from_sid( map->sid )) == NULL ) { - /* huh, couldn't find a suitable domain, - * let's just leave it unmapped */ - DEBUG(10, ("Could not find idmap backend for SID %s", - sid_string_static(map->sid))); - return NT_STATUS_NO_SUCH_DOMAIN; - } - - if (dom->readonly) { - /* ouch the domain is read only, - * let's just leave it unmapped */ - DEBUG(10, ("idmap backend for SID %s is READONLY!\n", - sid_string_static(map->sid))); - return NT_STATUS_UNSUCCESSFUL; - } - - *ret_dom = dom; - return NT_STATUS_OK; -} - -static NTSTATUS idmap_new_mapping(TALLOC_CTX *ctx, struct id_map *map) -{ - NTSTATUS ret; - struct idmap_domain *dom; - - /* If we are offline we cannot lookup SIDs, deny mapping */ - if (idmap_is_offline()) { - return NT_STATUS_FILE_IS_OFFLINE; - } - - ret = idmap_can_map(map, &dom); - if ( ! NT_STATUS_IS_OK(ret)) { - return NT_STATUS_NONE_MAPPED; - } - - /* check if this is a valid SID and then map it */ - switch (map->xid.type) { - case ID_TYPE_UID: - ret = idmap_allocate_uid(&map->xid); - if ( ! NT_STATUS_IS_OK(ret)) { - /* can't allocate id, let's just leave it unmapped */ - DEBUG(2, ("uid allocation failed! " - "Can't create mapping\n")); - return NT_STATUS_NONE_MAPPED; - } - break; - case ID_TYPE_GID: - ret = idmap_allocate_gid(&map->xid); - if ( ! NT_STATUS_IS_OK(ret)) { - /* can't allocate id, let's just leave it unmapped */ - DEBUG(2, ("gid allocation failed! " - "Can't create mapping\n")); - return NT_STATUS_NONE_MAPPED; - } - break; - default: - /* invalid sid, let's just leave it unmapped */ - DEBUG(3,("idmap_new_mapping: Refusing to create a " - "mapping for an unspecified ID type.\n")); - return NT_STATUS_NONE_MAPPED; - } - - /* ok, got a new id, let's set a mapping */ - map->status = ID_MAPPED; - - DEBUG(10, ("Setting mapping: %s <-> %s %lu\n", - sid_string_static(map->sid), - (map->xid.type == ID_TYPE_UID) ? "UID" : "GID", - (unsigned long)map->xid.id)); - ret = dom->methods->set_mapping(dom, map); - - if ( ! NT_STATUS_IS_OK(ret)) { - /* something wrong here :-( */ - DEBUG(2, ("Failed to commit mapping\n!")); - - /* TODO: would it make sense to have an "unalloc_id function?" */ - - return NT_STATUS_NONE_MAPPED; - } - - return NT_STATUS_OK; -} - -static NTSTATUS idmap_backends_set_mapping(const struct id_map *map) -{ - struct idmap_domain *dom; - NTSTATUS ret; - - DEBUG(10, ("Setting mapping %s <-> %s %lu\n", - sid_string_static(map->sid), - (map->xid.type == ID_TYPE_UID) ? "UID" : "GID", - (unsigned long)map->xid.id)); - - ret = idmap_can_map(map, &dom); - if ( ! NT_STATUS_IS_OK(ret)) { - return ret; - } - - DEBUG(10,("set_mapping for domain %s\n", dom->name )); - - return dom->methods->set_mapping(dom, map); -} - -static NTSTATUS idmap_backends_unixids_to_sids(struct id_map **ids) -{ - struct idmap_domain *dom; - struct id_map **unmapped; - struct id_map **_ids; - TALLOC_CTX *ctx; - NTSTATUS ret; - int i, u, n; - - if (!ids || !*ids) { - DEBUG(1, ("Invalid list of maps\n")); - return NT_STATUS_INVALID_PARAMETER; - } - - ctx = talloc_named_const(NULL, 0, "idmap_backends_unixids_to_sids ctx"); - if ( ! ctx) { - DEBUG(0, ("Out of memory!\n")); - return NT_STATUS_NO_MEMORY; - } - - DEBUG(10, ("Query backends to map ids->sids\n")); - - /* start from the default (the last one) and then if there are still - * unmapped entries cycle through the others */ - - _ids = ids; - - unmapped = NULL; - for (n = num_domains-1; n >= 0; n--) { /* cycle backwards */ - - dom = idmap_domains[n]; - - DEBUG(10, ("Query sids from domain %s\n", dom->name)); - - ret = dom->methods->unixids_to_sids(dom, _ids); - IDMAP_REPORT_RET(ret); - - unmapped = NULL; - - for (i = 0, u = 0; _ids[i]; i++) { - if (_ids[i]->status != ID_MAPPED) { - unmapped = talloc_realloc(ctx, unmapped, - struct id_map *, u + 2); - IDMAP_CHECK_ALLOC(unmapped); - unmapped[u] = _ids[i]; - u++; - } - } - if (unmapped) { - /* terminate the unmapped list */ - unmapped[u] = NULL; - } else { /* no more entries, get out */ - break; - } - - _ids = unmapped; - - } - - if (unmapped) { - /* there are still unmapped ids, - * map them to the unix users/groups domains */ - /* except for expired entries, - * these will be returned as valid (offline mode) */ - for (i = 0; unmapped[i]; i++) { - if (unmapped[i]->status == ID_EXPIRED) continue; - switch (unmapped[i]->xid.type) { - case ID_TYPE_UID: - uid_to_unix_users_sid( - (uid_t)unmapped[i]->xid.id, - unmapped[i]->sid); - unmapped[i]->status = ID_MAPPED; - break; - case ID_TYPE_GID: - gid_to_unix_groups_sid( - (gid_t)unmapped[i]->xid.id, - unmapped[i]->sid); - unmapped[i]->status = ID_MAPPED; - break; - default: /* what?! */ - unmapped[i]->status = ID_UNKNOWN; - break; - } - } - } - - ret = NT_STATUS_OK; - -done: - talloc_free(ctx); - return ret; -} - -static NTSTATUS idmap_backends_sids_to_unixids(struct id_map **ids) -{ - struct id_map ***dom_ids; - struct idmap_domain *dom; - TALLOC_CTX *ctx; - NTSTATUS ret; - int i, *counters; - - if ( (ctx = talloc_named_const(NULL, 0, "be_sids_to_ids")) == NULL ) { - DEBUG(1, ("failed to allocate talloc context, OOM?\n")); - return NT_STATUS_NO_MEMORY; - } - - DEBUG(10, ("Query backends to map sids->ids\n")); - - /* split list per domain */ - if (num_domains == 0) { - DEBUG(1, ("No domains available?\n")); - return NT_STATUS_UNSUCCESSFUL; - } - - dom_ids = TALLOC_ZERO_ARRAY(ctx, struct id_map **, num_domains); - IDMAP_CHECK_ALLOC(dom_ids); - counters = TALLOC_ZERO_ARRAY(ctx, int, num_domains); - IDMAP_CHECK_ALLOC(counters); - - /* partition the requests by domain */ - - for (i = 0; ids[i]; i++) { - uint32 idx; - - if ((dom = find_idmap_domain_from_sid(ids[i]->sid)) == NULL) { - /* no available idmap_domain. Move on */ - continue; - } - - DEBUG(10,("SID %s is being handled by %s\n", - sid_string_static(ids[i]->sid), - dom ? dom->name : "none" )); - - idx = find_idmap_domain_index( dom ); - SMB_ASSERT( idx != -1 ); - - dom_ids[idx] = talloc_realloc(ctx, dom_ids[idx], - struct id_map *, - counters[idx] + 2); - IDMAP_CHECK_ALLOC(dom_ids[idx]); - - dom_ids[idx][counters[idx]] = ids[i]; - counters[idx]++; - dom_ids[idx][counters[idx]] = NULL; - } - - /* All the ids have been dispatched in the right queues. - Let's cycle through the filled ones */ - - for (i = 0; i < num_domains; i++) { - if (dom_ids[i]) { - dom = idmap_domains[i]; - DEBUG(10, ("Query ids from domain %s\n", dom->name)); - ret = dom->methods->sids_to_unixids(dom, dom_ids[i]); - IDMAP_REPORT_RET(ret); - } - } - - /* ok all the backends have been contacted at this point */ - /* let's see if we have any unmapped SID left and act accordingly */ - - for (i = 0; ids[i]; i++) { - /* NOTE: this will NOT touch ID_EXPIRED entries that the backend - * was not able to confirm/deny (offline mode) */ - if (ids[i]->status == ID_UNKNOWN || - ids[i]->status == ID_UNMAPPED) { - /* ok this is an unmapped one, see if we can map it */ - ret = idmap_new_mapping(ctx, ids[i]); - if (NT_STATUS_IS_OK(ret)) { - /* successfully mapped */ - ids[i]->status = ID_MAPPED; - } else - if (NT_STATUS_EQUAL(ret, NT_STATUS_NONE_MAPPED)) { - /* could not map it */ - ids[i]->status = ID_UNMAPPED; - } else { - /* Something very bad happened down there - * OR we are offline */ - ids[i]->status = ID_UNKNOWN; - } - } - } - - ret = NT_STATUS_OK; - -done: - talloc_free(ctx); - return ret; -} - -/************************************************************************** - idmap interface functions -**************************************************************************/ - -NTSTATUS idmap_unixids_to_sids(struct id_map **ids) -{ - TALLOC_CTX *ctx; - NTSTATUS ret; - struct id_map **bids; - int i, bi; - int bn = 0; - struct winbindd_domain *our_domain = find_our_domain(); - - if (! NT_STATUS_IS_OK(ret = idmap_init())) { - return ret; - } - - if (!ids || !*ids) { - DEBUG(1, ("Invalid list of maps\n")); - return NT_STATUS_INVALID_PARAMETER; - } - - ctx = talloc_named_const(NULL, 0, "idmap_unixids_to_sids ctx"); - if ( ! ctx) { - DEBUG(1, ("failed to allocate talloc context, OOM?\n")); - return NT_STATUS_NO_MEMORY; - } - - /* no ids to be asked to the backends by default */ - bids = NULL; - bi = 0; - - for (i = 0; ids[i]; i++) { - - if ( ! ids[i]->sid) { - DEBUG(1, ("invalid null SID in id_map array")); - talloc_free(ctx); - return NT_STATUS_INVALID_PARAMETER; - } - - ret = idmap_cache_map_id(idmap_cache, ids[i]); - - if ( ! NT_STATUS_IS_OK(ret)) { - - if ( ! bids) { - /* alloc space for ids to be resolved by - * backends (realloc ten by ten) */ - bids = TALLOC_ARRAY(ctx, struct id_map *, 10); - if ( ! bids) { - DEBUG(1, ("Out of memory!\n")); - talloc_free(ctx); - return NT_STATUS_NO_MEMORY; - } - bn = 10; - } - - /* add this id to the ones to be retrieved - * from the backends */ - bids[bi] = ids[i]; - bi++; - - /* check if we need to allocate new space - * on the rids array */ - if (bi == bn) { - bn += 10; - bids = talloc_realloc(ctx, bids, - struct id_map *, bn); - if ( ! bids) { - DEBUG(1, ("Out of memory!\n")); - talloc_free(ctx); - return NT_STATUS_NO_MEMORY; - } - } - - /* make sure the last element is NULL */ - bids[bi] = NULL; - } - } - - /* let's see if there is any id mapping to be retieved - * from the backends */ - if (bi) { - /* Only do query if we are online */ - if ( IS_DOMAIN_OFFLINE(our_domain) ) { - ret = NT_STATUS_FILE_IS_OFFLINE; - goto done; - } - - ret = idmap_backends_unixids_to_sids(bids); - IDMAP_CHECK_RET(ret); - - /* update the cache */ - for (i = 0; i < bi; i++) { - if (bids[i]->status == ID_MAPPED) { - ret = idmap_cache_set(idmap_cache, bids[i]); - } else if (bids[i]->status == ID_EXPIRED) { - /* the cache returned an expired entry and the - * backend was not able to clear the situation - * (offline). This handles a previous - * NT_STATUS_SYNCHRONIZATION_REQUIRED - * for disconnected mode, */ - bids[i]->status = ID_MAPPED; - } else if (bids[i]->status == ID_UNKNOWN) { - /* something bad here. We were not able to - * handle this for some reason, mark it as - * unmapped and hope next time things will - * settle down. */ - bids[i]->status = ID_UNMAPPED; - } else { /* unmapped */ - ret = idmap_cache_set_negative_id(idmap_cache, - bids[i]); - } - IDMAP_CHECK_RET(ret); - } - } - - ret = NT_STATUS_OK; -done: - talloc_free(ctx); - return ret; -} - -NTSTATUS idmap_sids_to_unixids(struct id_map **ids) -{ - TALLOC_CTX *ctx; - NTSTATUS ret; - struct id_map **bids; - int i, bi; - int bn = 0; - struct winbindd_domain *our_domain = find_our_domain(); - - if (! NT_STATUS_IS_OK(ret = idmap_init())) { - return ret; - } - - if (!ids || !*ids) { - DEBUG(1, ("Invalid list of maps\n")); - return NT_STATUS_INVALID_PARAMETER; - } - - ctx = talloc_named_const(NULL, 0, "idmap_sids_to_unixids ctx"); - if ( ! ctx) { - DEBUG(1, ("failed to allocate talloc context, OOM?\n")); - return NT_STATUS_NO_MEMORY; - } - - /* no ids to be asked to the backends by default */ - bids = NULL; - bi = 0; - - for (i = 0; ids[i]; i++) { - - if ( ! ids[i]->sid) { - DEBUG(1, ("invalid null SID in id_map array\n")); - talloc_free(ctx); - return NT_STATUS_INVALID_PARAMETER; - } - - ret = idmap_cache_map_sid(idmap_cache, ids[i]); - - if ( ! NT_STATUS_IS_OK(ret)) { - - if ( ! bids) { - /* alloc space for ids to be resolved - by backends (realloc ten by ten) */ - bids = TALLOC_ARRAY(ctx, struct id_map *, 10); - if ( ! bids) { - DEBUG(1, ("Out of memory!\n")); - talloc_free(ctx); - return NT_STATUS_NO_MEMORY; - } - bn = 10; - } - - /* add this id to the ones to be retrieved - * from the backends */ - bids[bi] = ids[i]; - bi++; - - /* check if we need to allocate new space - * on the ids array */ - if (bi == bn) { - bn += 10; - bids = talloc_realloc(ctx, bids, - struct id_map *, bn); - if ( ! bids) { - DEBUG(1, ("Out of memory!\n")); - talloc_free(ctx); - return NT_STATUS_NO_MEMORY; - } - } - - /* make sure the last element is NULL */ - bids[bi] = NULL; - } - } - - /* let's see if there is any id mapping to be retieved - * from the backends */ - if (bids) { - /* Only do query if we are online */ - if ( IS_DOMAIN_OFFLINE(our_domain) ) { - ret = NT_STATUS_FILE_IS_OFFLINE; - goto done; - } - - ret = idmap_backends_sids_to_unixids(bids); - IDMAP_CHECK_RET(ret); - - /* update the cache */ - for (i = 0; bids[i]; i++) { - if (bids[i]->status == ID_MAPPED) { - ret = idmap_cache_set(idmap_cache, bids[i]); - } else if (bids[i]->status == ID_EXPIRED) { - /* the cache returned an expired entry and the - * backend was not able to clear the situation - * (offline). This handles a previous - * NT_STATUS_SYNCHRONIZATION_REQUIRED - * for disconnected mode, */ - bids[i]->status = ID_MAPPED; - } else if (bids[i]->status == ID_UNKNOWN) { - /* something bad here. We were not able to - * handle this for some reason, mark it as - * unmapped and hope next time things will - * settle down. */ - bids[i]->status = ID_UNMAPPED; - } else { /* unmapped */ - ret = idmap_cache_set_negative_sid(idmap_cache, - bids[i]); - } - IDMAP_CHECK_RET(ret); - } - } - - ret = NT_STATUS_OK; -done: - talloc_free(ctx); - return ret; -} - -NTSTATUS idmap_set_mapping(const struct id_map *id) -{ - TALLOC_CTX *ctx; - NTSTATUS ret; - - if (! NT_STATUS_IS_OK(ret = idmap_init())) { - return ret; - } - - /* sanity checks */ - if ((id->sid == NULL) || (id->status != ID_MAPPED)) { - DEBUG(1, ("NULL SID or unmapped entry\n")); - return NT_STATUS_INVALID_PARAMETER; - } - - /* TODO: check uid/gid range ? */ - - ctx = talloc_named_const(NULL, 0, "idmap_set_mapping ctx"); - if ( ! ctx) { - DEBUG(1, ("failed to allocate talloc context, OOM?\n")); - return NT_STATUS_NO_MEMORY; - } - - /* set the new mapping */ - ret = idmap_backends_set_mapping(id); - IDMAP_CHECK_RET(ret); - - /* set the mapping in the cache */ - ret = idmap_cache_set(idmap_cache, id); - IDMAP_CHECK_RET(ret); - -done: - talloc_free(ctx); - return ret; -} - -/************************************************************************** - Dump backend status. -**************************************************************************/ - -void idmap_dump_maps(char *logfile) -{ - NTSTATUS ret; - struct unixid allid; - struct id_map *maps; - int num_maps; - FILE *dump; - int i; - - if (! NT_STATUS_IS_OK(ret = idmap_init())) { - return; - } - - dump = fopen(logfile, "w"); - if ( ! dump) { - DEBUG(0, ("Unable to open open stream for file [%s], " - "errno: %d\n", logfile, errno)); - return; - } - - if (NT_STATUS_IS_OK(ret = idmap_alloc_init())) { - allid.type = ID_TYPE_UID; - allid.id = 0; - idmap_alloc_ctx->methods->get_id_hwm(&allid); - fprintf(dump, "USER HWM %lu\n", (unsigned long)allid.id); - - allid.type = ID_TYPE_GID; - allid.id = 0; - idmap_alloc_ctx->methods->get_id_hwm(&allid); - fprintf(dump, "GROUP HWM %lu\n", (unsigned long)allid.id); - } - - maps = talloc(idmap_ctx, struct id_map); - num_maps = 0; - - for (i = 0; i < num_domains; i++) { - if (idmap_domains[i]->methods->dump_data) { - idmap_domains[i]->methods->dump_data(idmap_domains[i], - &maps, &num_maps); - } - } - - for (i = 0; i < num_maps; i++) { - switch (maps[i].xid.type) { - case ID_TYPE_UID: - fprintf(dump, "UID %lu %s\n", - (unsigned long)maps[i].xid.id, - sid_string_static(maps[i].sid)); - break; - case ID_TYPE_GID: - fprintf(dump, "GID %lu %s\n", - (unsigned long)maps[i].xid.id, - sid_string_static(maps[i].sid)); - break; - case ID_TYPE_NOT_SPECIFIED: - break; - } - } - - fflush(dump); - fclose(dump); -} - -char *idmap_fetch_secret(const char *backend, bool alloc, - const char *domain, const char *identity) -{ - char *tmp, *ret; - int r; - - if (alloc) { - r = asprintf(&tmp, "IDMAP_ALLOC_%s", backend); - } else { - r = asprintf(&tmp, "IDMAP_%s_%s", backend, domain); - } - - if (r < 0) - return NULL; - - strupper_m(tmp); /* make sure the key is case insensitive */ - ret = secrets_fetch_generic(tmp, identity); - - SAFE_FREE(tmp); - - return ret; -} - diff --git a/source3/nsswitch/idmap_ad.c b/source3/nsswitch/idmap_ad.c deleted file mode 100644 index 41dbb471f9..0000000000 --- a/source3/nsswitch/idmap_ad.c +++ /dev/null @@ -1,870 +0,0 @@ -/* - * idmap_ad: map between Active Directory and RFC 2307 or "Services for Unix" (SFU) Accounts - * - * Unix SMB/CIFS implementation. - * - * Winbind ADS backend functions - * - * Copyright (C) Andrew Tridgell 2001 - * Copyright (C) Andrew Bartlett 2003 - * Copyright (C) Gerald (Jerry) Carter 2004-2007 - * Copyright (C) Luke Howard 2001-2004 - * - * 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 3 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, see . - */ - -#include "includes.h" - -#undef DBGC_CLASS -#define DBGC_CLASS DBGC_IDMAP - -#define WINBIND_CCACHE_NAME "MEMORY:winbind_ccache" - -#define IDMAP_AD_MAX_IDS 30 -#define CHECK_ALLOC_DONE(mem) do { \ - if (!mem) { \ - DEBUG(0, ("Out of memory!\n")); \ - ret = NT_STATUS_NO_MEMORY; \ - goto done; \ - } \ -} while (0) - -struct idmap_ad_context { - uint32_t filter_low_id; - uint32_t filter_high_id; -}; - -NTSTATUS init_module(void); - -static ADS_STRUCT *ad_idmap_ads = NULL; -static struct posix_schema *ad_schema = NULL; -static enum wb_posix_mapping ad_map_type = WB_POSIX_MAP_UNKNOWN; - -/************************************************************************ - ***********************************************************************/ - -static ADS_STRUCT *ad_idmap_cached_connection_internal(void) -{ - ADS_STRUCT *ads; - ADS_STATUS status; - BOOL local = False; - fstring dc_name; - struct in_addr dc_ip; - - if (ad_idmap_ads != NULL) { - - time_t expire; - time_t now = time(NULL); - - ads = ad_idmap_ads; - - expire = MIN(ads->auth.tgt_expire, ads->auth.tgs_expire); - - /* check for a valid structure */ - DEBUG(7, ("Current tickets expire in %d seconds (at %d, time is now %d)\n", - (uint32)expire-(uint32)now, (uint32) expire, (uint32) now)); - - if ( ads->config.realm && (expire > time(NULL))) { - return ads; - } else { - /* we own this ADS_STRUCT so make sure it goes away */ - DEBUG(7,("Deleting expired krb5 credential cache\n")); - ads->is_mine = True; - ads_destroy( &ads ); - ads_kdestroy(WINBIND_CCACHE_NAME); - ad_idmap_ads = NULL; - TALLOC_FREE( ad_schema ); - } - } - - if (!local) { - /* we don't want this to affect the users ccache */ - setenv("KRB5CCNAME", WINBIND_CCACHE_NAME, 1); - } - - if ( (ads = ads_init(lp_realm(), lp_workgroup(), NULL)) == NULL ) { - DEBUG(1,("ads_init failed\n")); - return NULL; - } - - /* the machine acct password might have change - fetch it every time */ - SAFE_FREE(ads->auth.password); - ads->auth.password = secrets_fetch_machine_password(lp_workgroup(), NULL, NULL); - - SAFE_FREE(ads->auth.realm); - ads->auth.realm = SMB_STRDUP(lp_realm()); - - /* setup server affinity */ - - get_dc_name( NULL, ads->auth.realm, dc_name, &dc_ip ); - - status = ads_connect(ads); - if (!ADS_ERR_OK(status)) { - DEBUG(1, ("ad_idmap_init: failed to connect to AD\n")); - ads_destroy(&ads); - return NULL; - } - - ads->is_mine = False; - - ad_idmap_ads = ads; - - return ads; -} - -/************************************************************************ - ***********************************************************************/ - -static ADS_STRUCT *ad_idmap_cached_connection(void) -{ - ADS_STRUCT *ads = ad_idmap_cached_connection_internal(); - - if ( !ads ) - return NULL; - - /* if we have a valid ADS_STRUCT and the schema model is - defined, then we can return here. */ - - if ( ad_schema ) - return ads; - - /* Otherwise, set the schema model */ - - if ( (ad_map_type == WB_POSIX_MAP_SFU) || - (ad_map_type == WB_POSIX_MAP_SFU20) || - (ad_map_type == WB_POSIX_MAP_RFC2307) ) - { - ADS_STATUS schema_status; - - schema_status = ads_check_posix_schema_mapping( NULL, ads, ad_map_type, &ad_schema); - if ( !ADS_ERR_OK(schema_status) ) { - DEBUG(2,("ad_idmap_cached_connection: Failed to obtain schema details!\n")); - return NULL; - } - } - - return ads; -} - -/************************************************************************ - ***********************************************************************/ - -static NTSTATUS idmap_ad_initialize(struct idmap_domain *dom) -{ - struct idmap_ad_context *ctx; - char *config_option; - const char *range = NULL; - const char *schema_mode = NULL; - - if ( (ctx = TALLOC_ZERO_P(dom, struct idmap_ad_context)) == NULL ) { - DEBUG(0, ("Out of memory!\n")); - return NT_STATUS_NO_MEMORY; - } - - if ( (config_option = talloc_asprintf(ctx, "idmap config %s", dom->name)) == NULL ) { - DEBUG(0, ("Out of memory!\n")); - talloc_free(ctx); - return NT_STATUS_NO_MEMORY; - } - - /* load ranges */ - range = lp_parm_const_string(-1, config_option, "range", NULL); - if (range && range[0]) { - if ((sscanf(range, "%u - %u", &ctx->filter_low_id, &ctx->filter_high_id) != 2) || - (ctx->filter_low_id > ctx->filter_high_id)) { - DEBUG(1, ("ERROR: invalid filter range [%s]", range)); - ctx->filter_low_id = 0; - ctx->filter_high_id = 0; - } - } - - /* schema mode */ - if ( ad_map_type == WB_POSIX_MAP_UNKNOWN ) - ad_map_type = WB_POSIX_MAP_RFC2307; - schema_mode = lp_parm_const_string(-1, config_option, "schema_mode", NULL); - if ( schema_mode && schema_mode[0] ) { - if ( strequal(schema_mode, "sfu") ) - ad_map_type = WB_POSIX_MAP_SFU; - else if ( strequal(schema_mode, "sfu20" ) ) - ad_map_type = WB_POSIX_MAP_SFU20; - else if ( strequal(schema_mode, "rfc2307" ) ) - ad_map_type = WB_POSIX_MAP_RFC2307; - else - DEBUG(0,("idmap_ad_initialize: Unknown schema_mode (%s)\n", - schema_mode)); - } - - dom->private_data = ctx; - dom->initialized = True; - - talloc_free(config_option); - - return NT_STATUS_OK; -} - -/************************************************************************ - Search up to IDMAP_AD_MAX_IDS entries in maps for a match. - ***********************************************************************/ - -static struct id_map *find_map_by_id(struct id_map **maps, enum id_type type, uint32_t id) -{ - int i; - - for (i = 0; maps[i] && ixid.type == type) && (maps[i]->xid.id == id)) { - return maps[i]; - } - } - - return NULL; -} - -/************************************************************************ - Search up to IDMAP_AD_MAX_IDS entries in maps for a match - ***********************************************************************/ - -static struct id_map *find_map_by_sid(struct id_map **maps, DOM_SID *sid) -{ - int i; - - for (i = 0; maps[i] && isid, sid)) { - return maps[i]; - } - } - - return NULL; -} - -/************************************************************************ - ***********************************************************************/ - -static NTSTATUS idmap_ad_unixids_to_sids(struct idmap_domain *dom, struct id_map **ids) -{ - NTSTATUS ret; - TALLOC_CTX *memctx; - struct idmap_ad_context *ctx; - ADS_STATUS rc; - ADS_STRUCT *ads; - const char *attrs[] = { "sAMAccountType", - "objectSid", - NULL, /* uidnumber */ - NULL, /* gidnumber */ - NULL }; - LDAPMessage *res = NULL; - LDAPMessage *entry = NULL; - char *filter = NULL; - int idx = 0; - int bidx = 0; - int count; - int i; - char *u_filter = NULL; - char *g_filter = NULL; - - /* Only do query if we are online */ - if (idmap_is_offline()) { - return NT_STATUS_FILE_IS_OFFLINE; - } - - /* Initilization my have been deferred because we were offline */ - if ( ! dom->initialized) { - ret = idmap_ad_initialize(dom); - if ( ! NT_STATUS_IS_OK(ret)) { - return ret; - } - } - - ctx = talloc_get_type(dom->private_data, struct idmap_ad_context); - - if ( (memctx = talloc_new(ctx)) == NULL ) { - DEBUG(0, ("Out of memory!\n")); - return NT_STATUS_NO_MEMORY; - } - - if ( (ads = ad_idmap_cached_connection()) == NULL ) { - DEBUG(1, ("ADS uninitialized\n")); - ret = NT_STATUS_UNSUCCESSFUL; - goto done; - } - - attrs[2] = ad_schema->posix_uidnumber_attr; - attrs[3] = ad_schema->posix_gidnumber_attr; - -again: - bidx = idx; - for (i = 0; (i < IDMAP_AD_MAX_IDS) && ids[idx]; i++, idx++) { - switch (ids[idx]->xid.type) { - case ID_TYPE_UID: - if ( ! u_filter) { - u_filter = talloc_asprintf(memctx, "(&(|" - "(sAMAccountType=%d)" - "(sAMAccountType=%d)" - "(sAMAccountType=%d))(|", - ATYPE_NORMAL_ACCOUNT, - ATYPE_WORKSTATION_TRUST, - ATYPE_INTERDOMAIN_TRUST); - } - u_filter = talloc_asprintf_append(u_filter, "(%s=%lu)", - ad_schema->posix_uidnumber_attr, - (unsigned long)ids[idx]->xid.id); - CHECK_ALLOC_DONE(u_filter); - break; - - case ID_TYPE_GID: - if ( ! g_filter) { - g_filter = talloc_asprintf(memctx, "(&(|" - "(sAMAccountType=%d)" - "(sAMAccountType=%d))(|", - ATYPE_SECURITY_GLOBAL_GROUP, - ATYPE_SECURITY_LOCAL_GROUP); - } - g_filter = talloc_asprintf_append(g_filter, "(%s=%lu)", - ad_schema->posix_gidnumber_attr, - (unsigned long)ids[idx]->xid.id); - CHECK_ALLOC_DONE(g_filter); - break; - - default: - DEBUG(3, ("Error: mapping requested but Unknown ID type\n")); - ids[idx]->status = ID_UNKNOWN; - continue; - } - } - filter = talloc_asprintf(memctx, "(|"); - CHECK_ALLOC_DONE(filter); - if ( u_filter) { - filter = talloc_asprintf_append(filter, "%s))", u_filter); - CHECK_ALLOC_DONE(filter); - TALLOC_FREE(u_filter); - } - if ( g_filter) { - filter = talloc_asprintf_append(filter, "%s))", g_filter); - CHECK_ALLOC_DONE(filter); - TALLOC_FREE(g_filter); - } - filter = talloc_asprintf_append(filter, ")"); - CHECK_ALLOC_DONE(filter); - - rc = ads_search_retry(ads, &res, filter, attrs); - if (!ADS_ERR_OK(rc)) { - DEBUG(1, ("ERROR: ads search returned: %s\n", ads_errstr(rc))); - ret = NT_STATUS_UNSUCCESSFUL; - goto done; - } - - if ( (count = ads_count_replies(ads, res)) == 0 ) { - DEBUG(10, ("No IDs found\n")); - } - - entry = res; - for (i = 0; (i < count) && entry; i++) { - DOM_SID sid; - enum id_type type; - struct id_map *map; - uint32_t id; - uint32_t atype; - - if (i == 0) { /* first entry */ - entry = ads_first_entry(ads, entry); - } else { /* following ones */ - entry = ads_next_entry(ads, entry); - } - - if ( !entry ) { - DEBUG(2, ("ERROR: Unable to fetch ldap entries from results\n")); - break; - } - - /* first check if the SID is present */ - if (!ads_pull_sid(ads, entry, "objectSid", &sid)) { - DEBUG(2, ("Could not retrieve SID from entry\n")); - continue; - } - - /* get type */ - if (!ads_pull_uint32(ads, entry, "sAMAccountType", &atype)) { - DEBUG(1, ("could not get SAM account type\n")); - continue; - } - - switch (atype & 0xF0000000) { - case ATYPE_SECURITY_GLOBAL_GROUP: - case ATYPE_SECURITY_LOCAL_GROUP: - type = ID_TYPE_GID; - break; - case ATYPE_NORMAL_ACCOUNT: - case ATYPE_WORKSTATION_TRUST: - case ATYPE_INTERDOMAIN_TRUST: - type = ID_TYPE_UID; - break; - default: - DEBUG(1, ("unrecognized SAM account type %08x\n", atype)); - continue; - } - - if (!ads_pull_uint32(ads, entry, (type==ID_TYPE_UID) ? - ad_schema->posix_uidnumber_attr : - ad_schema->posix_gidnumber_attr, - &id)) - { - DEBUG(1, ("Could not get unix ID\n")); - continue; - } - - if ((id == 0) || - (ctx->filter_low_id && (id < ctx->filter_low_id)) || - (ctx->filter_high_id && (id > ctx->filter_high_id))) { - DEBUG(5, ("Requested id (%u) out of range (%u - %u). Filtered!\n", - id, ctx->filter_low_id, ctx->filter_high_id)); - continue; - } - - map = find_map_by_id(&ids[bidx], type, id); - if (!map) { - DEBUG(2, ("WARNING: couldn't match result with requested ID\n")); - continue; - } - - sid_copy(map->sid, &sid); - - /* mapped */ - map->status = ID_MAPPED; - - DEBUG(10, ("Mapped %s -> %lu (%d)\n", - sid_string_static(map->sid), - (unsigned long)map->xid.id, - map->xid.type)); - } - - if (res) { - ads_msgfree(ads, res); - } - - if (ids[idx]) { /* still some values to map */ - goto again; - } - - ret = NT_STATUS_OK; - - /* mark all unknown/expired ones as unmapped */ - for (i = 0; ids[i]; i++) { - if (ids[i]->status != ID_MAPPED) - ids[i]->status = ID_UNMAPPED; - } - -done: - talloc_free(memctx); - return ret; -} - -/************************************************************************ - ***********************************************************************/ - -static NTSTATUS idmap_ad_sids_to_unixids(struct idmap_domain *dom, struct id_map **ids) -{ - NTSTATUS ret; - TALLOC_CTX *memctx; - struct idmap_ad_context *ctx; - ADS_STATUS rc; - ADS_STRUCT *ads; - const char *attrs[] = { "sAMAccountType", - "objectSid", - NULL, /* attr_uidnumber */ - NULL, /* attr_gidnumber */ - NULL }; - LDAPMessage *res = NULL; - LDAPMessage *entry = NULL; - char *filter = NULL; - int idx = 0; - int bidx = 0; - int count; - int i; - char *sidstr; - - /* Only do query if we are online */ - if (idmap_is_offline()) { - return NT_STATUS_FILE_IS_OFFLINE; - } - - /* Initilization my have been deferred because we were offline */ - if ( ! dom->initialized) { - ret = idmap_ad_initialize(dom); - if ( ! NT_STATUS_IS_OK(ret)) { - return ret; - } - } - - ctx = talloc_get_type(dom->private_data, struct idmap_ad_context); - - if ( (memctx = talloc_new(ctx)) == NULL ) { - DEBUG(0, ("Out of memory!\n")); - return NT_STATUS_NO_MEMORY; - } - - if ( (ads = ad_idmap_cached_connection()) == NULL ) { - DEBUG(1, ("ADS uninitialized\n")); - ret = NT_STATUS_UNSUCCESSFUL; - goto done; - } - - attrs[2] = ad_schema->posix_uidnumber_attr; - attrs[3] = ad_schema->posix_gidnumber_attr; - -again: - filter = talloc_asprintf(memctx, "(&(|" - "(sAMAccountType=%d)(sAMAccountType=%d)(sAMAccountType=%d)" /* user account types */ - "(sAMAccountType=%d)(sAMAccountType=%d)" /* group account types */ - ")(|", - ATYPE_NORMAL_ACCOUNT, ATYPE_WORKSTATION_TRUST, ATYPE_INTERDOMAIN_TRUST, - ATYPE_SECURITY_GLOBAL_GROUP, ATYPE_SECURITY_LOCAL_GROUP); - - CHECK_ALLOC_DONE(filter); - - bidx = idx; - for (i = 0; (i < IDMAP_AD_MAX_IDS) && ids[idx]; i++, idx++) { - - sidstr = sid_binstring(ids[idx]->sid); - filter = talloc_asprintf_append(filter, "(objectSid=%s)", sidstr); - - free(sidstr); - CHECK_ALLOC_DONE(filter); - } - filter = talloc_asprintf_append(filter, "))"); - CHECK_ALLOC_DONE(filter); - DEBUG(10, ("Filter: [%s]\n", filter)); - - rc = ads_search_retry(ads, &res, filter, attrs); - if (!ADS_ERR_OK(rc)) { - DEBUG(1, ("ERROR: ads search returned: %s\n", ads_errstr(rc))); - ret = NT_STATUS_UNSUCCESSFUL; - goto done; - } - - if ( (count = ads_count_replies(ads, res)) == 0 ) { - DEBUG(10, ("No IDs found\n")); - } - - entry = res; - for (i = 0; (i < count) && entry; i++) { - DOM_SID sid; - enum id_type type; - struct id_map *map; - uint32_t id; - uint32_t atype; - - if (i == 0) { /* first entry */ - entry = ads_first_entry(ads, entry); - } else { /* following ones */ - entry = ads_next_entry(ads, entry); - } - - if ( !entry ) { - DEBUG(2, ("ERROR: Unable to fetch ldap entries from results\n")); - break; - } - - /* first check if the SID is present */ - if (!ads_pull_sid(ads, entry, "objectSid", &sid)) { - DEBUG(2, ("Could not retrieve SID from entry\n")); - continue; - } - - map = find_map_by_sid(&ids[bidx], &sid); - if (!map) { - DEBUG(2, ("WARNING: couldn't match result with requested SID\n")); - continue; - } - - /* get type */ - if (!ads_pull_uint32(ads, entry, "sAMAccountType", &atype)) { - DEBUG(1, ("could not get SAM account type\n")); - continue; - } - - switch (atype & 0xF0000000) { - case ATYPE_SECURITY_GLOBAL_GROUP: - case ATYPE_SECURITY_LOCAL_GROUP: - type = ID_TYPE_GID; - break; - case ATYPE_NORMAL_ACCOUNT: - case ATYPE_WORKSTATION_TRUST: - case ATYPE_INTERDOMAIN_TRUST: - type = ID_TYPE_UID; - break; - default: - DEBUG(1, ("unrecognized SAM account type %08x\n", atype)); - continue; - } - - if (!ads_pull_uint32(ads, entry, (type==ID_TYPE_UID) ? - ad_schema->posix_uidnumber_attr : - ad_schema->posix_gidnumber_attr, - &id)) - { - DEBUG(1, ("Could not get unix ID\n")); - continue; - } - if ((id == 0) || - (ctx->filter_low_id && (id < ctx->filter_low_id)) || - (ctx->filter_high_id && (id > ctx->filter_high_id))) { - DEBUG(5, ("Requested id (%u) out of range (%u - %u). Filtered!\n", - id, ctx->filter_low_id, ctx->filter_high_id)); - continue; - } - - /* mapped */ - map->xid.type = type; - map->xid.id = id; - map->status = ID_MAPPED; - - DEBUG(10, ("Mapped %s -> %lu (%d)\n", - sid_string_static(map->sid), - (unsigned long)map->xid.id, - map->xid.type)); - } - - if (res) { - ads_msgfree(ads, res); - } - - if (ids[idx]) { /* still some values to map */ - goto again; - } - - ret = NT_STATUS_OK; - - /* mark all unknwoni/expired ones as unmapped */ - for (i = 0; ids[i]; i++) { - if (ids[i]->status != ID_MAPPED) - ids[i]->status = ID_UNMAPPED; - } - -done: - talloc_free(memctx); - return ret; -} - -/************************************************************************ - ***********************************************************************/ - -static NTSTATUS idmap_ad_close(struct idmap_domain *dom) -{ - ADS_STRUCT *ads = ad_idmap_ads; - - if (ads != NULL) { - /* we own this ADS_STRUCT so make sure it goes away */ - ads->is_mine = True; - ads_destroy( &ads ); - ad_idmap_ads = NULL; - } - - TALLOC_FREE( ad_schema ); - - return NT_STATUS_OK; -} - -/* - * nss_info_{sfu,sfu20,rfc2307} - */ - -/************************************************************************ - Initialize the {sfu,sfu20,rfc2307} state - ***********************************************************************/ - -static NTSTATUS nss_sfu_init( struct nss_domain_entry *e ) -{ - /* Sanity check if we have previously been called with a - different schema model */ - - if ( (ad_map_type != WB_POSIX_MAP_UNKNOWN) && - (ad_map_type != WB_POSIX_MAP_SFU) ) - { - DEBUG(0,("nss_sfu_init: Posix Map type has already been set. " - "Mixed schema models not supported!\n")); - return NT_STATUS_NOT_SUPPORTED; - } - - ad_map_type = WB_POSIX_MAP_SFU; - - return NT_STATUS_OK; -} - -static NTSTATUS nss_sfu20_init( struct nss_domain_entry *e ) -{ - /* Sanity check if we have previously been called with a - different schema model */ - - if ( (ad_map_type != WB_POSIX_MAP_UNKNOWN) && - (ad_map_type != WB_POSIX_MAP_SFU20) ) - { - DEBUG(0,("nss_sfu20_init: Posix Map type has already been set. " - "Mixed schema models not supported!\n")); - return NT_STATUS_NOT_SUPPORTED; - } - - ad_map_type = WB_POSIX_MAP_SFU20; - - return NT_STATUS_OK; -} - -static NTSTATUS nss_rfc2307_init( struct nss_domain_entry *e ) -{ - /* Sanity check if we have previously been called with a - different schema model */ - - if ( (ad_map_type != WB_POSIX_MAP_UNKNOWN) && - (ad_map_type != WB_POSIX_MAP_RFC2307) ) - { - DEBUG(0,("nss_rfc2307_init: Posix Map type has already been set. " - "Mixed schema models not supported!\n")); - return NT_STATUS_NOT_SUPPORTED; - } - - ad_map_type = WB_POSIX_MAP_RFC2307; - - return NT_STATUS_OK; -} - - -/************************************************************************ - ***********************************************************************/ -static NTSTATUS nss_ad_get_info( struct nss_domain_entry *e, - const DOM_SID *sid, - TALLOC_CTX *ctx, - ADS_STRUCT *ads, - LDAPMessage *msg, - char **homedir, - char **shell, - char **gecos, - uint32 *gid ) -{ - ADS_STRUCT *ads_internal = NULL; - - /* Only do query if we are online */ - if (idmap_is_offline()) { - return NT_STATUS_FILE_IS_OFFLINE; - } - - /* We are assuming that the internal ADS_STRUCT is for the - same forest as the incoming *ads pointer */ - - ads_internal = ad_idmap_cached_connection(); - - if ( !ads_internal || !ad_schema ) - return NT_STATUS_OBJECT_NAME_NOT_FOUND; - - if ( !homedir || !shell || !gecos ) - return NT_STATUS_INVALID_PARAMETER; - - *homedir = ads_pull_string( ads, ctx, msg, ad_schema->posix_homedir_attr ); - *shell = ads_pull_string( ads, ctx, msg, ad_schema->posix_shell_attr ); - *gecos = ads_pull_string( ads, ctx, msg, ad_schema->posix_gecos_attr ); - - if ( gid ) { - if ( !ads_pull_uint32(ads, msg, ad_schema->posix_gidnumber_attr, gid ) ) - *gid = (uint32)-1; - } - - return NT_STATUS_OK; -} - -/************************************************************************ - ***********************************************************************/ - -static NTSTATUS nss_ad_close( void ) -{ - /* nothing to do. All memory is free()'d by the idmap close_fn() */ - - return NT_STATUS_OK; -} - -/************************************************************************ - Function dispatch tables for the idmap and nss plugins - ***********************************************************************/ - -static struct idmap_methods ad_methods = { - .init = idmap_ad_initialize, - .unixids_to_sids = idmap_ad_unixids_to_sids, - .sids_to_unixids = idmap_ad_sids_to_unixids, - .close_fn = idmap_ad_close -}; - -/* The SFU and RFC2307 NSS plugins share everything but the init - function which sets the intended schema model to use */ - -static struct nss_info_methods nss_rfc2307_methods = { - .init = nss_rfc2307_init, - .get_nss_info = nss_ad_get_info, - .close_fn = nss_ad_close -}; - -static struct nss_info_methods nss_sfu_methods = { - .init = nss_sfu_init, - .get_nss_info = nss_ad_get_info, - .close_fn = nss_ad_close -}; - -static struct nss_info_methods nss_sfu20_methods = { - .init = nss_sfu20_init, - .get_nss_info = nss_ad_get_info, - .close_fn = nss_ad_close -}; - - - -/************************************************************************ - Initialize the plugins - ***********************************************************************/ - -NTSTATUS idmap_ad_init(void) -{ - static NTSTATUS status_idmap_ad = NT_STATUS_UNSUCCESSFUL; - static NTSTATUS status_nss_rfc2307 = NT_STATUS_UNSUCCESSFUL; - static NTSTATUS status_nss_sfu = NT_STATUS_UNSUCCESSFUL; - static NTSTATUS status_nss_sfu20 = NT_STATUS_UNSUCCESSFUL; - - /* Always register the AD method first in order to get the - idmap_domain interface called */ - - if ( !NT_STATUS_IS_OK(status_idmap_ad) ) { - status_idmap_ad = smb_register_idmap(SMB_IDMAP_INTERFACE_VERSION, - "ad", &ad_methods); - if ( !NT_STATUS_IS_OK(status_idmap_ad) ) - return status_idmap_ad; - } - - if ( !NT_STATUS_IS_OK( status_nss_rfc2307 ) ) { - status_nss_rfc2307 = smb_register_idmap_nss(SMB_NSS_INFO_INTERFACE_VERSION, - "rfc2307", &nss_rfc2307_methods ); - if ( !NT_STATUS_IS_OK(status_nss_rfc2307) ) - return status_nss_rfc2307; - } - - if ( !NT_STATUS_IS_OK( status_nss_sfu ) ) { - status_nss_sfu = smb_register_idmap_nss(SMB_NSS_INFO_INTERFACE_VERSION, - "sfu", &nss_sfu_methods ); - if ( !NT_STATUS_IS_OK(status_nss_sfu) ) - return status_nss_sfu; - } - - if ( !NT_STATUS_IS_OK( status_nss_sfu20 ) ) { - status_nss_sfu20 = smb_register_idmap_nss(SMB_NSS_INFO_INTERFACE_VERSION, - "sfu20", &nss_sfu20_methods ); - if ( !NT_STATUS_IS_OK(status_nss_sfu20) ) - return status_nss_sfu20; - } - - return NT_STATUS_OK; -} - diff --git a/source3/nsswitch/idmap_cache.c b/source3/nsswitch/idmap_cache.c deleted file mode 100644 index 4f01cb1392..0000000000 --- a/source3/nsswitch/idmap_cache.c +++ /dev/null @@ -1,531 +0,0 @@ -/* - Unix SMB/CIFS implementation. - ID Mapping Cache - - based on gencache - - Copyright (C) Simo Sorce 2006 - Copyright (C) Rafal Szczesniak 2002 - - 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 3 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, see .*/ - -#include "includes.h" -#include "winbindd.h" - -#define TIMEOUT_LEN 12 -#define IDMAP_CACHE_DATA_FMT "%12u/%s" -#define IDMAP_READ_CACHE_DATA_FMT_TEMPLATE "%%12u/%%%us" - -struct idmap_cache_ctx { - TDB_CONTEXT *tdb; -}; - -static int idmap_cache_destructor(struct idmap_cache_ctx *cache) -{ - int ret = 0; - - if (cache && cache->tdb) { - ret = tdb_close(cache->tdb); - cache->tdb = NULL; - } - - return ret; -} - -struct idmap_cache_ctx *idmap_cache_init(TALLOC_CTX *memctx) -{ - struct idmap_cache_ctx *cache; - char* cache_fname = NULL; - - cache = talloc(memctx, struct idmap_cache_ctx); - if ( ! cache) { - DEBUG(0, ("Out of memory!\n")); - return NULL; - } - - cache_fname = lock_path("idmap_cache.tdb"); - - DEBUG(10, ("Opening cache file at %s\n", cache_fname)); - - cache->tdb = tdb_open_log(cache_fname, 0, TDB_DEFAULT, O_RDWR|O_CREAT, 0600); - - if (!cache->tdb) { - DEBUG(5, ("Attempt to open %s has failed.\n", cache_fname)); - return NULL; - } - - talloc_set_destructor(cache, idmap_cache_destructor); - - return cache; -} - -void idmap_cache_shutdown(struct idmap_cache_ctx *cache) -{ - talloc_free(cache); -} - -NTSTATUS idmap_cache_build_sidkey(TALLOC_CTX *ctx, char **sidkey, const struct id_map *id) -{ - *sidkey = talloc_asprintf(ctx, "IDMAP/SID/%s", sid_string_static(id->sid)); - if ( ! *sidkey) { - DEBUG(1, ("failed to build sidkey, OOM?\n")); - return NT_STATUS_NO_MEMORY; - } - - return NT_STATUS_OK; -} - -NTSTATUS idmap_cache_build_idkey(TALLOC_CTX *ctx, char **idkey, const struct id_map *id) -{ - *idkey = talloc_asprintf(ctx, "IDMAP/%s/%lu", - (id->xid.type==ID_TYPE_UID)?"UID":"GID", - (unsigned long)id->xid.id); - if ( ! *idkey) { - DEBUG(1, ("failed to build idkey, OOM?\n")); - return NT_STATUS_NO_MEMORY; - } - - return NT_STATUS_OK; -} - -NTSTATUS idmap_cache_set(struct idmap_cache_ctx *cache, const struct id_map *id) -{ - NTSTATUS ret; - time_t timeout = time(NULL) + lp_idmap_cache_time(); - TDB_DATA databuf; - char *sidkey; - char *idkey; - char *valstr; - - /* Don't cache lookups in the S-1-22-{1,2} domain */ - if ( (id->xid.type == ID_TYPE_UID) && - sid_check_is_in_unix_users(id->sid) ) - { - return NT_STATUS_OK; - } - if ( (id->xid.type == ID_TYPE_GID) && - sid_check_is_in_unix_groups(id->sid) ) - { - return NT_STATUS_OK; - } - - - ret = idmap_cache_build_sidkey(cache, &sidkey, id); - if (!NT_STATUS_IS_OK(ret)) return ret; - - /* use sidkey as the local memory ctx */ - ret = idmap_cache_build_idkey(sidkey, &idkey, id); - if (!NT_STATUS_IS_OK(ret)) { - goto done; - } - - /* save SID -> ID */ - - /* use sidkey as the local memory ctx */ - valstr = talloc_asprintf(sidkey, IDMAP_CACHE_DATA_FMT, (int)timeout, idkey); - if (!valstr) { - DEBUG(0, ("Out of memory!\n")); - ret = NT_STATUS_NO_MEMORY; - goto done; - } - - databuf = string_term_tdb_data(valstr); - DEBUG(10, ("Adding cache entry with key = %s; value = %s and timeout =" - " %s (%d seconds %s)\n", sidkey, valstr , ctime(&timeout), - (int)(timeout - time(NULL)), - timeout > time(NULL) ? "ahead" : "in the past")); - - if (tdb_store_bystring(cache->tdb, sidkey, databuf, TDB_REPLACE) != 0) { - DEBUG(3, ("Failed to store cache entry!\n")); - ret = NT_STATUS_UNSUCCESSFUL; - goto done; - } - - /* save ID -> SID */ - - /* use sidkey as the local memory ctx */ - valstr = talloc_asprintf(sidkey, IDMAP_CACHE_DATA_FMT, (int)timeout, sidkey); - if (!valstr) { - DEBUG(0, ("Out of memory!\n")); - ret = NT_STATUS_NO_MEMORY; - goto done; - } - - databuf = string_term_tdb_data(valstr); - DEBUG(10, ("Adding cache entry with key = %s; value = %s and timeout =" - " %s (%d seconds %s)\n", idkey, valstr, ctime(&timeout), - (int)(timeout - time(NULL)), - timeout > time(NULL) ? "ahead" : "in the past")); - - if (tdb_store_bystring(cache->tdb, idkey, databuf, TDB_REPLACE) != 0) { - DEBUG(3, ("Failed to store cache entry!\n")); - ret = NT_STATUS_UNSUCCESSFUL; - goto done; - } - - ret = NT_STATUS_OK; - -done: - talloc_free(sidkey); - return ret; -} - -NTSTATUS idmap_cache_set_negative_sid(struct idmap_cache_ctx *cache, const struct id_map *id) -{ - NTSTATUS ret; - time_t timeout = time(NULL) + lp_idmap_negative_cache_time(); - TDB_DATA databuf; - char *sidkey; - char *valstr; - - ret = idmap_cache_build_sidkey(cache, &sidkey, id); - if (!NT_STATUS_IS_OK(ret)) return ret; - - /* use sidkey as the local memory ctx */ - valstr = talloc_asprintf(sidkey, IDMAP_CACHE_DATA_FMT, (int)timeout, "IDMAP/NEGATIVE"); - if (!valstr) { - DEBUG(0, ("Out of memory!\n")); - ret = NT_STATUS_NO_MEMORY; - goto done; - } - - databuf = string_term_tdb_data(valstr); - DEBUG(10, ("Adding cache entry with key = %s; value = %s and timeout =" - " %s (%d seconds %s)\n", sidkey, valstr, ctime(&timeout), - (int)(timeout - time(NULL)), - timeout > time(NULL) ? "ahead" : "in the past")); - - if (tdb_store_bystring(cache->tdb, sidkey, databuf, TDB_REPLACE) != 0) { - DEBUG(3, ("Failed to store cache entry!\n")); - ret = NT_STATUS_UNSUCCESSFUL; - goto done; - } - -done: - talloc_free(sidkey); - return ret; -} - -NTSTATUS idmap_cache_set_negative_id(struct idmap_cache_ctx *cache, const struct id_map *id) -{ - NTSTATUS ret; - time_t timeout = time(NULL) + lp_idmap_negative_cache_time(); - TDB_DATA databuf; - char *idkey; - char *valstr; - - ret = idmap_cache_build_idkey(cache, &idkey, id); - if (!NT_STATUS_IS_OK(ret)) return ret; - - /* use idkey as the local memory ctx */ - valstr = talloc_asprintf(idkey, IDMAP_CACHE_DATA_FMT, (int)timeout, "IDMAP/NEGATIVE"); - if (!valstr) { - DEBUG(0, ("Out of memory!\n")); - ret = NT_STATUS_NO_MEMORY; - goto done; - } - - databuf = string_term_tdb_data(valstr); - DEBUG(10, ("Adding cache entry with key = %s; value = %s and timeout =" - " %s (%d seconds %s)\n", idkey, valstr, ctime(&timeout), - (int)(timeout - time(NULL)), - timeout > time(NULL) ? "ahead" : "in the past")); - - if (tdb_store_bystring(cache->tdb, idkey, databuf, TDB_REPLACE) != 0) { - DEBUG(3, ("Failed to store cache entry!\n")); - ret = NT_STATUS_UNSUCCESSFUL; - goto done; - } - -done: - talloc_free(idkey); - return ret; -} - -NTSTATUS idmap_cache_fill_map(struct id_map *id, const char *value) -{ - char *rem; - - /* see if it is a sid */ - if ( ! strncmp("IDMAP/SID/", value, 10)) { - - if ( ! string_to_sid(id->sid, &value[10])) { - goto failed; - } - - id->status = ID_MAPPED; - - return NT_STATUS_OK; - } - - /* not a SID see if it is an UID or a GID */ - if ( ! strncmp("IDMAP/UID/", value, 10)) { - - /* a uid */ - id->xid.type = ID_TYPE_UID; - - } else if ( ! strncmp("IDMAP/GID/", value, 10)) { - - /* a gid */ - id->xid.type = ID_TYPE_GID; - - } else { - - /* a completely bogus value bail out */ - goto failed; - } - - id->xid.id = strtol(&value[10], &rem, 0); - if (*rem != '\0') { - goto failed; - } - - id->status = ID_MAPPED; - - return NT_STATUS_OK; - -failed: - DEBUG(1, ("invalid value: %s\n", value)); - id->status = ID_UNKNOWN; - return NT_STATUS_INTERNAL_DB_CORRUPTION; -} - -BOOL idmap_cache_is_negative(const char *val) -{ - if ( ! strcmp("IDMAP/NEGATIVE", val)) { - return True; - } - return False; -} - -/* search the cahce for the SID an return a mapping if found * - * - * 4 cases are possible - * - * 1 map found - * in this case id->status = ID_MAPPED and NT_STATUS_OK is returned - * 2 map not found - * in this case id->status = ID_UNKNOWN and NT_STATUS_NONE_MAPPED is returned - * 3 negative cache found - * in this case id->status = ID_UNMAPPED and NT_STATUS_OK is returned - * 4 map found but timer expired - * in this case id->status = ID_EXPIRED and NT_STATUS_SYNCHRONIZATION_REQUIRED - * is returned. In this case revalidation of the cache is needed. - */ - -NTSTATUS idmap_cache_map_sid(struct idmap_cache_ctx *cache, struct id_map *id) -{ - NTSTATUS ret; - TDB_DATA databuf; - time_t t; - char *sidkey; - char *endptr; - struct winbindd_domain *our_domain = find_our_domain(); - time_t now = time(NULL); - - /* make sure it is marked as not mapped by default */ - id->status = ID_UNKNOWN; - - ret = idmap_cache_build_sidkey(cache, &sidkey, id); - if (!NT_STATUS_IS_OK(ret)) return ret; - - databuf = tdb_fetch_bystring(cache->tdb, sidkey); - - if (databuf.dptr == NULL) { - DEBUG(10, ("Cache entry with key = %s couldn't be found\n", sidkey)); - ret = NT_STATUS_NONE_MAPPED; - goto done; - } - - t = strtol((const char *)databuf.dptr, &endptr, 10); - - if ((endptr == NULL) || (*endptr != '/')) { - DEBUG(2, ("Invalid gencache data format: %s\n", (const char *)databuf.dptr)); - /* remove the entry */ - tdb_delete_bystring(cache->tdb, sidkey); - ret = NT_STATUS_NONE_MAPPED; - goto done; - } - - /* check it is not negative */ - if (strcmp("IDMAP/NEGATIVE", endptr+1) != 0) { - - DEBUG(10, ("Returning %s cache entry: key = %s, value = %s, " - "timeout = %s", t > now ? "valid" : - "expired", sidkey, endptr+1, ctime(&t))); - - /* this call if successful will also mark the entry as mapped */ - ret = idmap_cache_fill_map(id, endptr+1); - if ( ! NT_STATUS_IS_OK(ret)) { - /* if not valid form delete the entry */ - tdb_delete_bystring(cache->tdb, sidkey); - ret = NT_STATUS_NONE_MAPPED; - goto done; - } - - /* here ret == NT_STATUS_OK and id->status = ID_MAPPED */ - - if (t <= now) { - /* If we've been told to be offline - stay in - that state... */ - if ( IS_DOMAIN_OFFLINE(our_domain) ) { - DEBUG(10,("idmap_cache_map_sid: idmap is offline\n")); - goto done; - } - - /* We're expired, set an error code - for upper layer */ - ret = NT_STATUS_SYNCHRONIZATION_REQUIRED; - } - - goto done; - } - - /* Was a negative cache hit */ - - /* Ignore the negative cache when offline */ - - if ( IS_DOMAIN_OFFLINE(our_domain) ) { - DEBUG(10,("idmap_cache_map_sid: idmap is offline\n")); - goto done; - } - - - /* Check for valid or expired cache hits */ - if (t <= now) { - /* We're expired. Return not mapped */ - ret = NT_STATUS_NONE_MAPPED; - } else { - /* this is not mapped as it was a negative cache hit */ - id->status = ID_UNMAPPED; - ret = NT_STATUS_OK; - } - -done: - SAFE_FREE(databuf.dptr); - talloc_free(sidkey); - return ret; -} - -/* search the cahce for the ID an return a mapping if found * - * - * 4 cases are possible - * - * 1 map found - * in this case id->status = ID_MAPPED and NT_STATUS_OK is returned - * 2 map not found - * in this case id->status = ID_UNKNOWN and NT_STATUS_NONE_MAPPED is returned - * 3 negative cache found - * in this case id->status = ID_UNMAPPED and NT_STATUS_OK is returned - * 4 map found but timer expired - * in this case id->status = ID_EXPIRED and NT_STATUS_SYNCHRONIZATION_REQUIRED - * is returned. In this case revalidation of the cache is needed. - */ - -NTSTATUS idmap_cache_map_id(struct idmap_cache_ctx *cache, struct id_map *id) -{ - NTSTATUS ret; - TDB_DATA databuf; - time_t t; - char *idkey; - char *endptr; - struct winbindd_domain *our_domain = find_our_domain(); - time_t now = time(NULL); - - /* make sure it is marked as unknown by default */ - id->status = ID_UNKNOWN; - - ret = idmap_cache_build_idkey(cache, &idkey, id); - if (!NT_STATUS_IS_OK(ret)) return ret; - - databuf = tdb_fetch_bystring(cache->tdb, idkey); - - if (databuf.dptr == NULL) { - DEBUG(10, ("Cache entry with key = %s couldn't be found\n", idkey)); - ret = NT_STATUS_NONE_MAPPED; - goto done; - } - - t = strtol((const char *)databuf.dptr, &endptr, 10); - - if ((endptr == NULL) || (*endptr != '/')) { - DEBUG(2, ("Invalid gencache data format: %s\n", (const char *)databuf.dptr)); - /* remove the entry */ - tdb_delete_bystring(cache->tdb, idkey); - ret = NT_STATUS_NONE_MAPPED; - goto done; - } - - /* check it is not negative */ - if (strcmp("IDMAP/NEGATIVE", endptr+1) != 0) { - - DEBUG(10, ("Returning %s cache entry: key = %s, value = %s, " - "timeout = %s", t > now ? "valid" : - "expired", idkey, endptr+1, ctime(&t))); - - /* this call if successful will also mark the entry as mapped */ - ret = idmap_cache_fill_map(id, endptr+1); - if ( ! NT_STATUS_IS_OK(ret)) { - /* if not valid form delete the entry */ - tdb_delete_bystring(cache->tdb, idkey); - ret = NT_STATUS_NONE_MAPPED; - goto done; - } - - /* here ret == NT_STATUS_OK and id->mapped = ID_MAPPED */ - - if (t <= now) { - /* If we've been told to be offline - stay in - that state... */ - if ( IS_DOMAIN_OFFLINE(our_domain) ) { - DEBUG(10,("idmap_cache_map_sid: idmap is offline\n")); - goto done; - } - - /* We're expired, set an error code - for upper layer */ - ret = NT_STATUS_SYNCHRONIZATION_REQUIRED; - } - - goto done; - } - - /* Was a negative cache hit */ - - /* Ignore the negative cache when offline */ - - if ( IS_DOMAIN_OFFLINE(our_domain) ) { - DEBUG(10,("idmap_cache_map_sid: idmap is offline\n")); - ret = NT_STATUS_NONE_MAPPED; - - goto done; - } - - /* Process the negative cache hit */ - - if (t <= now) { - /* We're expired. Return not mapped */ - ret = NT_STATUS_NONE_MAPPED; - } else { - /* this is not mapped is it was a negative cache hit */ - id->status = ID_UNMAPPED; - ret = NT_STATUS_OK; - } - -done: - SAFE_FREE(databuf.dptr); - talloc_free(idkey); - return ret; -} - diff --git a/source3/nsswitch/idmap_ldap.c b/source3/nsswitch/idmap_ldap.c deleted file mode 100644 index 3b63915b05..0000000000 --- a/source3/nsswitch/idmap_ldap.c +++ /dev/null @@ -1,1505 +0,0 @@ -/* - Unix SMB/CIFS implementation. - - idmap LDAP backend - - Copyright (C) Tim Potter 2000 - Copyright (C) Jim McDonough 2003 - Copyright (C) Gerald Carter 2003 - Copyright (C) Simo Sorce 2003-2007 - - 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 3 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, see . -*/ - -#include "includes.h" -#include "winbindd.h" - -#undef DBGC_CLASS -#define DBGC_CLASS DBGC_IDMAP - -#include -#include - -#include "smbldap.h" - -struct idmap_ldap_context { - struct smbldap_state *smbldap_state; - char *url; - char *suffix; - char *user_dn; - uint32_t filter_low_id, filter_high_id; /* Filter range */ - BOOL anon; -}; - -struct idmap_ldap_alloc_context { - struct smbldap_state *smbldap_state; - char *url; - char *suffix; - char *user_dn; - uid_t low_uid, high_uid; /* Range of uids */ - gid_t low_gid, high_gid; /* Range of gids */ - -}; - -#define CHECK_ALLOC_DONE(mem) do { \ - if (!mem) { \ - DEBUG(0, ("Out of memory!\n")); \ - ret = NT_STATUS_NO_MEMORY; \ - goto done; \ - } } while (0) - -/********************************************************************** - IDMAP ALLOC TDB BACKEND -**********************************************************************/ - -static struct idmap_ldap_alloc_context *idmap_alloc_ldap; - -/********************************************************************* - ********************************************************************/ - -static NTSTATUS get_credentials( TALLOC_CTX *mem_ctx, - struct smbldap_state *ldap_state, - const char *config_option, - struct idmap_domain *dom, - char **dn ) -{ - NTSTATUS ret = NT_STATUS_UNSUCCESSFUL; - char *secret = NULL; - const char *tmp = NULL; - char *user_dn = NULL; - BOOL anon = False; - - /* assume anonymous if we don't have a specified user */ - - tmp = lp_parm_const_string(-1, config_option, "ldap_user_dn", NULL); - - if ( tmp ) { - if (!dom) { - /* only the alloc backend can pass in a NULL dom */ - secret = idmap_fetch_secret("ldap", True, - NULL, tmp); - } else { - secret = idmap_fetch_secret("ldap", False, - dom->name, tmp); - } - - if (!secret) { - DEBUG(0, ("get_credentials: Unable to fetch " - "auth credentials for %s in %s\n", - tmp, (dom==NULL)?"ALLOC":dom->name)); - ret = NT_STATUS_ACCESS_DENIED; - goto done; - } - *dn = talloc_strdup(mem_ctx, tmp); - CHECK_ALLOC_DONE(*dn); - } else { - if (!fetch_ldap_pw(&user_dn, &secret)) { - DEBUG(2, ("get_credentials: Failed to lookup ldap " - "bind creds. Using anonymous connection.\n")); - anon = True; - } else { - *dn = talloc_strdup(mem_ctx, user_dn); - SAFE_FREE( user_dn ); - CHECK_ALLOC_DONE(*dn); - } - } - - smbldap_set_creds(ldap_state, anon, *dn, secret); - ret = NT_STATUS_OK; - -done: - SAFE_FREE(secret); - - return ret; -} - - -/********************************************************************** - Verify the sambaUnixIdPool entry in the directory. -**********************************************************************/ - -static NTSTATUS verify_idpool(void) -{ - NTSTATUS ret; - TALLOC_CTX *ctx; - LDAPMessage *result = NULL; - LDAPMod **mods = NULL; - const char **attr_list; - char *filter; - int count; - int rc; - - if ( ! idmap_alloc_ldap) { - return NT_STATUS_UNSUCCESSFUL; - } - - ctx = talloc_new(idmap_alloc_ldap); - if ( ! ctx) { - DEBUG(0, ("Out of memory!\n")); - return NT_STATUS_NO_MEMORY; - } - - filter = talloc_asprintf(ctx, "(objectclass=%s)", LDAP_OBJ_IDPOOL); - CHECK_ALLOC_DONE(filter); - - attr_list = get_attr_list(ctx, idpool_attr_list); - CHECK_ALLOC_DONE(attr_list); - - rc = smbldap_search(idmap_alloc_ldap->smbldap_state, - idmap_alloc_ldap->suffix, - LDAP_SCOPE_SUBTREE, - filter, - attr_list, - 0, - &result); - - if (rc != LDAP_SUCCESS) { - DEBUG(1, ("Unable to verify the idpool, " - "cannot continue initialization!\n")); - return NT_STATUS_UNSUCCESSFUL; - } - - count = ldap_count_entries(idmap_alloc_ldap->smbldap_state->ldap_struct, - result); - - ldap_msgfree(result); - - if ( count > 1 ) { - DEBUG(0,("Multiple entries returned from %s (base == %s)\n", - filter, idmap_alloc_ldap->suffix)); - ret = NT_STATUS_UNSUCCESSFUL; - goto done; - } - else if (count == 0) { - char *uid_str, *gid_str; - - uid_str = talloc_asprintf(ctx, "%lu", - (unsigned long)idmap_alloc_ldap->low_uid); - gid_str = talloc_asprintf(ctx, "%lu", - (unsigned long)idmap_alloc_ldap->low_gid); - - smbldap_set_mod(&mods, LDAP_MOD_ADD, - "objectClass", LDAP_OBJ_IDPOOL); - smbldap_set_mod(&mods, LDAP_MOD_ADD, - get_attr_key2string(idpool_attr_list, - LDAP_ATTR_UIDNUMBER), - uid_str); - smbldap_set_mod(&mods, LDAP_MOD_ADD, - get_attr_key2string(idpool_attr_list, - LDAP_ATTR_GIDNUMBER), - gid_str); - if (mods) { - rc = smbldap_modify(idmap_alloc_ldap->smbldap_state, - idmap_alloc_ldap->suffix, - mods); - ldap_mods_free(mods, True); - } else { - ret = NT_STATUS_UNSUCCESSFUL; - goto done; - } - } - - ret = (rc == LDAP_SUCCESS)?NT_STATUS_OK:NT_STATUS_UNSUCCESSFUL; -done: - talloc_free(ctx); - return ret; -} - -/***************************************************************************** - Initialise idmap database. -*****************************************************************************/ - -static NTSTATUS idmap_ldap_alloc_init(const char *params) -{ - NTSTATUS ret = NT_STATUS_UNSUCCESSFUL; - const char *range; - const char *tmp; - uid_t low_uid = 0; - uid_t high_uid = 0; - gid_t low_gid = 0; - gid_t high_gid = 0; - - /* Only do init if we are online */ - if (idmap_is_offline()) { - return NT_STATUS_FILE_IS_OFFLINE; - } - - idmap_alloc_ldap = TALLOC_ZERO_P(NULL, struct idmap_ldap_alloc_context); - CHECK_ALLOC_DONE( idmap_alloc_ldap ); - - /* load ranges */ - - idmap_alloc_ldap->low_uid = 0; - idmap_alloc_ldap->high_uid = 0; - idmap_alloc_ldap->low_gid = 0; - idmap_alloc_ldap->high_gid = 0; - - range = lp_parm_const_string(-1, "idmap alloc config", "range", NULL); - if (range && range[0]) { - unsigned low_id, high_id; - - if (sscanf(range, "%u - %u", &low_id, &high_id) == 2) { - if (low_id < high_id) { - idmap_alloc_ldap->low_gid = low_id; - idmap_alloc_ldap->low_uid = low_id; - idmap_alloc_ldap->high_gid = high_id; - idmap_alloc_ldap->high_uid = high_id; - } else { - DEBUG(1, ("ERROR: invalid idmap alloc range " - "[%s]", range)); - } - } else { - DEBUG(1, ("ERROR: invalid syntax for idmap alloc " - "config:range [%s]", range)); - } - } - - if (lp_idmap_uid(&low_uid, &high_uid)) { - idmap_alloc_ldap->low_uid = low_uid; - idmap_alloc_ldap->high_uid = high_uid; - } - - if (lp_idmap_gid(&low_gid, &high_gid)) { - idmap_alloc_ldap->low_gid = low_gid; - idmap_alloc_ldap->high_gid= high_gid; - } - - if (idmap_alloc_ldap->high_uid <= idmap_alloc_ldap->low_uid) { - DEBUG(1, ("idmap uid range missing or invalid\n")); - DEBUGADD(1, ("idmap will be unable to map foreign SIDs\n")); - ret = NT_STATUS_UNSUCCESSFUL; - goto done; - } - - if (idmap_alloc_ldap->high_gid <= idmap_alloc_ldap->low_gid) { - DEBUG(1, ("idmap gid range missing or invalid\n")); - DEBUGADD(1, ("idmap will be unable to map foreign SIDs\n")); - ret = NT_STATUS_UNSUCCESSFUL; - goto done; - } - - if (params && *params) { - /* assume location is the only parameter */ - idmap_alloc_ldap->url = talloc_strdup(idmap_alloc_ldap, params); - } else { - tmp = lp_parm_const_string(-1, "idmap alloc config", - "ldap_url", NULL); - - if ( ! tmp) { - DEBUG(1, ("ERROR: missing idmap ldap url\n")); - ret = NT_STATUS_UNSUCCESSFUL; - goto done; - } - - idmap_alloc_ldap->url = talloc_strdup(idmap_alloc_ldap, tmp); - } - CHECK_ALLOC_DONE( idmap_alloc_ldap->url ); - - tmp = lp_parm_const_string(-1, "idmap alloc config", - "ldap_base_dn", NULL); - if ( ! tmp || ! *tmp) { - tmp = lp_ldap_idmap_suffix(); - if ( ! tmp) { - DEBUG(1, ("ERROR: missing idmap ldap suffix\n")); - ret = NT_STATUS_UNSUCCESSFUL; - goto done; - } - } - - idmap_alloc_ldap->suffix = talloc_strdup(idmap_alloc_ldap, tmp); - CHECK_ALLOC_DONE( idmap_alloc_ldap->suffix ); - - ret = smbldap_init(idmap_alloc_ldap, winbind_event_context(), - idmap_alloc_ldap->url, - &idmap_alloc_ldap->smbldap_state); - if (!NT_STATUS_IS_OK(ret)) { - DEBUG(1, ("ERROR: smbldap_init (%s) failed!\n", - idmap_alloc_ldap->url)); - goto done; - } - - ret = get_credentials( idmap_alloc_ldap, - idmap_alloc_ldap->smbldap_state, - "idmap alloc config", NULL, - &idmap_alloc_ldap->user_dn ); - if ( !NT_STATUS_IS_OK(ret) ) { - DEBUG(1,("idmap_ldap_alloc_init: Failed to get connection " - "credentials (%s)\n", nt_errstr(ret))); - goto done; - } - - /* see if the idmap suffix and sub entries exists */ - - ret = verify_idpool(); - - done: - if ( !NT_STATUS_IS_OK( ret ) ) - TALLOC_FREE( idmap_alloc_ldap ); - - return ret; -} - -/******************************** - Allocate a new uid or gid -********************************/ - -static NTSTATUS idmap_ldap_allocate_id(struct unixid *xid) -{ - TALLOC_CTX *ctx; - NTSTATUS ret = NT_STATUS_UNSUCCESSFUL; - int rc = LDAP_SERVER_DOWN; - int count = 0; - LDAPMessage *result = NULL; - LDAPMessage *entry = NULL; - LDAPMod **mods = NULL; - char *id_str; - char *new_id_str; - char *filter = NULL; - const char *dn = NULL; - const char **attr_list; - const char *type; - - /* Only do query if we are online */ - if (idmap_is_offline()) { - return NT_STATUS_FILE_IS_OFFLINE; - } - - if ( ! idmap_alloc_ldap) { - return NT_STATUS_UNSUCCESSFUL; - } - - ctx = talloc_new(idmap_alloc_ldap); - if ( ! ctx) { - DEBUG(0, ("Out of memory!\n")); - return NT_STATUS_NO_MEMORY; - } - - /* get type */ - switch (xid->type) { - - case ID_TYPE_UID: - type = get_attr_key2string(idpool_attr_list, - LDAP_ATTR_UIDNUMBER); - break; - - case ID_TYPE_GID: - type = get_attr_key2string(idpool_attr_list, - LDAP_ATTR_GIDNUMBER); - break; - - default: - DEBUG(2, ("Invalid ID type (0x%x)\n", xid->type)); - return NT_STATUS_INVALID_PARAMETER; - } - - filter = talloc_asprintf(ctx, "(objectClass=%s)", LDAP_OBJ_IDPOOL); - CHECK_ALLOC_DONE(filter); - - attr_list = get_attr_list(ctx, idpool_attr_list); - CHECK_ALLOC_DONE(attr_list); - - DEBUG(10, ("Search of the id pool (filter: %s)\n", filter)); - - rc = smbldap_search(idmap_alloc_ldap->smbldap_state, - idmap_alloc_ldap->suffix, - LDAP_SCOPE_SUBTREE, filter, - attr_list, 0, &result); - - if (rc != LDAP_SUCCESS) { - DEBUG(0,("%s object not found\n", LDAP_OBJ_IDPOOL)); - goto done; - } - - talloc_autofree_ldapmsg(ctx, result); - - count = ldap_count_entries(idmap_alloc_ldap->smbldap_state->ldap_struct, - result); - if (count != 1) { - DEBUG(0,("Single %s object not found\n", LDAP_OBJ_IDPOOL)); - goto done; - } - - entry = ldap_first_entry(idmap_alloc_ldap->smbldap_state->ldap_struct, - result); - - dn = smbldap_talloc_dn(ctx, - idmap_alloc_ldap->smbldap_state->ldap_struct, - entry); - if ( ! dn) { - goto done; - } - - if ( ! (id_str = smbldap_talloc_single_attribute(idmap_alloc_ldap->smbldap_state->ldap_struct, - entry, type, ctx))) { - DEBUG(0,("%s attribute not found\n", type)); - goto done; - } - if ( ! id_str) { - DEBUG(0,("Out of memory\n")); - ret = NT_STATUS_NO_MEMORY; - goto done; - } - - xid->id = strtoul(id_str, NULL, 10); - - /* make sure we still have room to grow */ - - switch (xid->type) { - case ID_TYPE_UID: - if (xid->id > idmap_alloc_ldap->high_uid) { - DEBUG(0,("Cannot allocate uid above %lu!\n", - (unsigned long)idmap_alloc_ldap->high_uid)); - goto done; - } - break; - - case ID_TYPE_GID: - if (xid->id > idmap_alloc_ldap->high_gid) { - DEBUG(0,("Cannot allocate gid above %lu!\n", - (unsigned long)idmap_alloc_ldap->high_uid)); - goto done; - } - break; - - default: - /* impossible */ - goto done; - } - - new_id_str = talloc_asprintf(ctx, "%lu", (unsigned long)xid->id + 1); - if ( ! new_id_str) { - DEBUG(0,("Out of memory\n")); - ret = NT_STATUS_NO_MEMORY; - goto done; - } - - smbldap_set_mod(&mods, LDAP_MOD_DELETE, type, id_str); - smbldap_set_mod(&mods, LDAP_MOD_ADD, type, new_id_str); - - if (mods == NULL) { - DEBUG(0,("smbldap_set_mod() failed.\n")); - goto done; - } - - DEBUG(10, ("Try to atomically increment the id (%s -> %s)\n", - id_str, new_id_str)); - - rc = smbldap_modify(idmap_alloc_ldap->smbldap_state, dn, mods); - - ldap_mods_free(mods, True); - - if (rc != LDAP_SUCCESS) { - DEBUG(1,("Failed to allocate new %s. " - "smbldap_modify() failed.\n", type)); - goto done; - } - - ret = NT_STATUS_OK; - -done: - talloc_free(ctx); - return ret; -} - -/********************************** - Get current highest id. -**********************************/ - -static NTSTATUS idmap_ldap_get_hwm(struct unixid *xid) -{ - TALLOC_CTX *memctx; - NTSTATUS ret = NT_STATUS_UNSUCCESSFUL; - int rc = LDAP_SERVER_DOWN; - int count = 0; - LDAPMessage *result = NULL; - LDAPMessage *entry = NULL; - char *id_str; - char *filter = NULL; - const char **attr_list; - const char *type; - - /* Only do query if we are online */ - if (idmap_is_offline()) { - return NT_STATUS_FILE_IS_OFFLINE; - } - - if ( ! idmap_alloc_ldap) { - return NT_STATUS_UNSUCCESSFUL; - } - - memctx = talloc_new(idmap_alloc_ldap); - if ( ! memctx) { - DEBUG(0, ("Out of memory!\n")); - return NT_STATUS_NO_MEMORY; - } - - /* get type */ - switch (xid->type) { - - case ID_TYPE_UID: - type = get_attr_key2string(idpool_attr_list, - LDAP_ATTR_UIDNUMBER); - break; - - case ID_TYPE_GID: - type = get_attr_key2string(idpool_attr_list, - LDAP_ATTR_GIDNUMBER); - break; - - default: - DEBUG(2, ("Invalid ID type (0x%x)\n", xid->type)); - return NT_STATUS_INVALID_PARAMETER; - } - - filter = talloc_asprintf(memctx, "(objectClass=%s)", LDAP_OBJ_IDPOOL); - CHECK_ALLOC_DONE(filter); - - attr_list = get_attr_list(memctx, idpool_attr_list); - CHECK_ALLOC_DONE(attr_list); - - rc = smbldap_search(idmap_alloc_ldap->smbldap_state, - idmap_alloc_ldap->suffix, - LDAP_SCOPE_SUBTREE, filter, - attr_list, 0, &result); - - if (rc != LDAP_SUCCESS) { - DEBUG(0,("%s object not found\n", LDAP_OBJ_IDPOOL)); - goto done; - } - - talloc_autofree_ldapmsg(memctx, result); - - count = ldap_count_entries(idmap_alloc_ldap->smbldap_state->ldap_struct, - result); - if (count != 1) { - DEBUG(0,("Single %s object not found\n", LDAP_OBJ_IDPOOL)); - goto done; - } - - entry = ldap_first_entry(idmap_alloc_ldap->smbldap_state->ldap_struct, - result); - - id_str = smbldap_talloc_single_attribute(idmap_alloc_ldap->smbldap_state->ldap_struct, - entry, type, memctx); - if ( ! id_str) { - DEBUG(0,("%s attribute not found\n", type)); - goto done; - } - if ( ! id_str) { - DEBUG(0,("Out of memory\n")); - ret = NT_STATUS_NO_MEMORY; - goto done; - } - - xid->id = strtoul(id_str, NULL, 10); - - ret = NT_STATUS_OK; -done: - talloc_free(memctx); - return ret; -} -/********************************** - Set highest id. -**********************************/ - -static NTSTATUS idmap_ldap_set_hwm(struct unixid *xid) -{ - TALLOC_CTX *ctx; - NTSTATUS ret = NT_STATUS_UNSUCCESSFUL; - int rc = LDAP_SERVER_DOWN; - int count = 0; - LDAPMessage *result = NULL; - LDAPMessage *entry = NULL; - LDAPMod **mods = NULL; - char *new_id_str; - char *filter = NULL; - const char *dn = NULL; - const char **attr_list; - const char *type; - - /* Only do query if we are online */ - if (idmap_is_offline()) { - return NT_STATUS_FILE_IS_OFFLINE; - } - - if ( ! idmap_alloc_ldap) { - return NT_STATUS_UNSUCCESSFUL; - } - - ctx = talloc_new(idmap_alloc_ldap); - if ( ! ctx) { - DEBUG(0, ("Out of memory!\n")); - return NT_STATUS_NO_MEMORY; - } - - /* get type */ - switch (xid->type) { - - case ID_TYPE_UID: - type = get_attr_key2string(idpool_attr_list, - LDAP_ATTR_UIDNUMBER); - break; - - case ID_TYPE_GID: - type = get_attr_key2string(idpool_attr_list, - LDAP_ATTR_GIDNUMBER); - break; - - default: - DEBUG(2, ("Invalid ID type (0x%x)\n", xid->type)); - return NT_STATUS_INVALID_PARAMETER; - } - - filter = talloc_asprintf(ctx, "(objectClass=%s)", LDAP_OBJ_IDPOOL); - CHECK_ALLOC_DONE(filter); - - attr_list = get_attr_list(ctx, idpool_attr_list); - CHECK_ALLOC_DONE(attr_list); - - rc = smbldap_search(idmap_alloc_ldap->smbldap_state, - idmap_alloc_ldap->suffix, - LDAP_SCOPE_SUBTREE, filter, - attr_list, 0, &result); - - if (rc != LDAP_SUCCESS) { - DEBUG(0,("%s object not found\n", LDAP_OBJ_IDPOOL)); - goto done; - } - - talloc_autofree_ldapmsg(ctx, result); - - count = ldap_count_entries(idmap_alloc_ldap->smbldap_state->ldap_struct, - result); - if (count != 1) { - DEBUG(0,("Single %s object not found\n", LDAP_OBJ_IDPOOL)); - goto done; - } - - entry = ldap_first_entry(idmap_alloc_ldap->smbldap_state->ldap_struct, - result); - - dn = smbldap_talloc_dn(ctx, - idmap_alloc_ldap->smbldap_state->ldap_struct, - entry); - if ( ! dn) { - goto done; - } - - new_id_str = talloc_asprintf(ctx, "%lu", (unsigned long)xid->id); - if ( ! new_id_str) { - DEBUG(0,("Out of memory\n")); - ret = NT_STATUS_NO_MEMORY; - goto done; - } - - smbldap_set_mod(&mods, LDAP_MOD_REPLACE, type, new_id_str); - - if (mods == NULL) { - DEBUG(0,("smbldap_set_mod() failed.\n")); - goto done; - } - - rc = smbldap_modify(idmap_alloc_ldap->smbldap_state, dn, mods); - - ldap_mods_free(mods, True); - - if (rc != LDAP_SUCCESS) { - DEBUG(1,("Failed to allocate new %s. " - "smbldap_modify() failed.\n", type)); - goto done; - } - - ret = NT_STATUS_OK; - -done: - talloc_free(ctx); - return ret; -} - -/********************************** - Close idmap ldap alloc -**********************************/ - -static NTSTATUS idmap_ldap_alloc_close(void) -{ - if (idmap_alloc_ldap) { - smbldap_free_struct(&idmap_alloc_ldap->smbldap_state); - DEBUG(5,("The connection to the LDAP server was closed\n")); - /* maybe free the results here --metze */ - TALLOC_FREE(idmap_alloc_ldap); - } - return NT_STATUS_OK; -} - - -/********************************************************************** - IDMAP MAPPING LDAP BACKEND -**********************************************************************/ - -static int idmap_ldap_close_destructor(struct idmap_ldap_context *ctx) -{ - smbldap_free_struct(&ctx->smbldap_state); - DEBUG(5,("The connection to the LDAP server was closed\n")); - /* maybe free the results here --metze */ - - return 0; -} - -/******************************** - Initialise idmap database. -********************************/ - -static NTSTATUS idmap_ldap_db_init(struct idmap_domain *dom) -{ - NTSTATUS ret; - struct idmap_ldap_context *ctx = NULL; - char *config_option = NULL; - const char *range = NULL; - const char *tmp = NULL; - - /* Only do init if we are online */ - if (idmap_is_offline()) { - return NT_STATUS_FILE_IS_OFFLINE; - } - - ctx = TALLOC_ZERO_P(dom, struct idmap_ldap_context); - if ( ! ctx) { - DEBUG(0, ("Out of memory!\n")); - return NT_STATUS_NO_MEMORY; - } - - config_option = talloc_asprintf(ctx, "idmap config %s", dom->name); - if ( ! config_option) { - DEBUG(0, ("Out of memory!\n")); - ret = NT_STATUS_NO_MEMORY; - goto done; - } - - /* load ranges */ - range = lp_parm_const_string(-1, config_option, "range", NULL); - if (range && range[0]) { - if ((sscanf(range, "%u - %u", &ctx->filter_low_id, - &ctx->filter_high_id) != 2) || - (ctx->filter_low_id > ctx->filter_high_id)) { - DEBUG(1, ("ERROR: invalid filter range [%s]", range)); - ctx->filter_low_id = 0; - ctx->filter_high_id = 0; - } - } - - if (dom->params && *(dom->params)) { - /* assume location is the only parameter */ - ctx->url = talloc_strdup(ctx, dom->params); - } else { - tmp = lp_parm_const_string(-1, config_option, "ldap_url", NULL); - - if ( ! tmp) { - DEBUG(1, ("ERROR: missing idmap ldap url\n")); - ret = NT_STATUS_UNSUCCESSFUL; - goto done; - } - - ctx->url = talloc_strdup(ctx, tmp); - } - CHECK_ALLOC_DONE(ctx->url); - - tmp = lp_parm_const_string(-1, config_option, "ldap_base_dn", NULL); - if ( ! tmp || ! *tmp) { - tmp = lp_ldap_idmap_suffix(); - if ( ! tmp) { - DEBUG(1, ("ERROR: missing idmap ldap suffix\n")); - ret = NT_STATUS_UNSUCCESSFUL; - goto done; - } - } - - ctx->suffix = talloc_strdup(ctx, tmp); - CHECK_ALLOC_DONE(ctx->suffix); - - ret = smbldap_init(ctx, winbind_event_context(), ctx->url, - &ctx->smbldap_state); - if (!NT_STATUS_IS_OK(ret)) { - DEBUG(1, ("ERROR: smbldap_init (%s) failed!\n", ctx->url)); - goto done; - } - - ret = get_credentials( ctx, ctx->smbldap_state, config_option, - dom, &ctx->user_dn ); - if ( !NT_STATUS_IS_OK(ret) ) { - DEBUG(1,("idmap_ldap_db_init: Failed to get connection " - "credentials (%s)\n", nt_errstr(ret))); - goto done; - } - - /* set the destructor on the context, so that resource are properly - freed if the contexts is released */ - - talloc_set_destructor(ctx, idmap_ldap_close_destructor); - - dom->private_data = ctx; - dom->initialized = True; - - talloc_free(config_option); - return NT_STATUS_OK; - -/*failed */ -done: - talloc_free(ctx); - return ret; -} - -/* max number of ids requested per batch query */ -#define IDMAP_LDAP_MAX_IDS 30 - -/********************************** - lookup a set of unix ids. -**********************************/ - -/* this function searches up to IDMAP_LDAP_MAX_IDS entries - * in maps for a match */ -static struct id_map *find_map_by_id(struct id_map **maps, - enum id_type type, - uint32_t id) -{ - int i; - - for (i = 0; i < IDMAP_LDAP_MAX_IDS; i++) { - if (maps[i] == NULL) { /* end of the run */ - return NULL; - } - if ((maps[i]->xid.type == type) && (maps[i]->xid.id == id)) { - return maps[i]; - } - } - - return NULL; -} - -static NTSTATUS idmap_ldap_unixids_to_sids(struct idmap_domain *dom, - struct id_map **ids) -{ - NTSTATUS ret; - TALLOC_CTX *memctx; - struct idmap_ldap_context *ctx; - LDAPMessage *result = NULL; - const char *uidNumber; - const char *gidNumber; - const char **attr_list; - char *filter = NULL; - BOOL multi = False; - int idx = 0; - int bidx = 0; - int count; - int rc; - int i; - - /* Only do query if we are online */ - if (idmap_is_offline()) { - return NT_STATUS_FILE_IS_OFFLINE; - } - - /* Initilization my have been deferred because we were offline */ - if ( ! dom->initialized) { - ret = idmap_ldap_db_init(dom); - if ( ! NT_STATUS_IS_OK(ret)) { - return ret; - } - } - - ctx = talloc_get_type(dom->private_data, struct idmap_ldap_context); - - memctx = talloc_new(ctx); - if ( ! memctx) { - DEBUG(0, ("Out of memory!\n")); - return NT_STATUS_NO_MEMORY; - } - - uidNumber = get_attr_key2string(idpool_attr_list, LDAP_ATTR_UIDNUMBER); - gidNumber = get_attr_key2string(idpool_attr_list, LDAP_ATTR_GIDNUMBER); - - attr_list = get_attr_list(memctx, sidmap_attr_list); - - if ( ! ids[1]) { - /* if we are requested just one mapping use the simple filter */ - - filter = talloc_asprintf(memctx, "(&(objectClass=%s)(%s=%lu))", - LDAP_OBJ_IDMAP_ENTRY, - (ids[0]->xid.type==ID_TYPE_UID)?uidNumber:gidNumber, - (unsigned long)ids[0]->xid.id); - CHECK_ALLOC_DONE(filter); - DEBUG(10, ("Filter: [%s]\n", filter)); - } else { - /* multiple mappings */ - multi = True; - } - -again: - if (multi) { - - talloc_free(filter); - filter = talloc_asprintf(memctx, - "(&(objectClass=%s)(|", - LDAP_OBJ_IDMAP_ENTRY); - CHECK_ALLOC_DONE(filter); - - bidx = idx; - for (i = 0; (i < IDMAP_LDAP_MAX_IDS) && ids[idx]; i++, idx++) { - filter = talloc_asprintf_append(filter, "(%s=%lu)", - (ids[idx]->xid.type==ID_TYPE_UID)?uidNumber:gidNumber, - (unsigned long)ids[idx]->xid.id); - CHECK_ALLOC_DONE(filter); - } - filter = talloc_asprintf_append(filter, "))"); - CHECK_ALLOC_DONE(filter); - DEBUG(10, ("Filter: [%s]\n", filter)); - } else { - bidx = 0; - idx = 1; - } - - rc = smbldap_search(ctx->smbldap_state, ctx->suffix, LDAP_SCOPE_SUBTREE, - filter, attr_list, 0, &result); - - if (rc != LDAP_SUCCESS) { - DEBUG(3,("Failure looking up ids (%s)\n", ldap_err2string(rc))); - ret = NT_STATUS_UNSUCCESSFUL; - goto done; - } - - count = ldap_count_entries(ctx->smbldap_state->ldap_struct, result); - - if (count == 0) { - DEBUG(10, ("NO SIDs found\n")); - } - - for (i = 0; i < count; i++) { - LDAPMessage *entry = NULL; - char *sidstr = NULL; - char *tmp = NULL; - enum id_type type; - struct id_map *map; - uint32_t id; - - if (i == 0) { /* first entry */ - entry = ldap_first_entry(ctx->smbldap_state->ldap_struct, - result); - } else { /* following ones */ - entry = ldap_next_entry(ctx->smbldap_state->ldap_struct, - entry); - } - if ( ! entry) { - DEBUG(2, ("ERROR: Unable to fetch ldap entries " - "from results\n")); - break; - } - - /* first check if the SID is present */ - sidstr = smbldap_talloc_single_attribute( - ctx->smbldap_state->ldap_struct, - entry, LDAP_ATTRIBUTE_SID, memctx); - if ( ! sidstr) { /* no sid, skip entry */ - DEBUG(2, ("WARNING SID not found on entry\n")); - continue; - } - - /* now try to see if it is a uid, if not try with a gid - * (gid is more common, but in case both uidNumber and - * gidNumber are returned the SID is mapped to the uid - *not the gid) */ - type = ID_TYPE_UID; - tmp = smbldap_talloc_single_attribute( - ctx->smbldap_state->ldap_struct, - entry, uidNumber, memctx); - if ( ! tmp) { - type = ID_TYPE_GID; - tmp = smbldap_talloc_single_attribute( - ctx->smbldap_state->ldap_struct, - entry, gidNumber, memctx); - } - if ( ! tmp) { /* wow very strange entry, how did it match ? */ - DEBUG(5, ("Unprobable match on (%s), no uidNumber, " - "nor gidNumber returned\n", sidstr)); - TALLOC_FREE(sidstr); - continue; - } - - id = strtoul(tmp, NULL, 10); - if ((id == 0) || - (ctx->filter_low_id && (id < ctx->filter_low_id)) || - (ctx->filter_high_id && (id > ctx->filter_high_id))) { - DEBUG(5, ("Requested id (%u) out of range (%u - %u). " - "Filtered!\n", id, - ctx->filter_low_id, ctx->filter_high_id)); - TALLOC_FREE(sidstr); - TALLOC_FREE(tmp); - continue; - } - TALLOC_FREE(tmp); - - map = find_map_by_id(&ids[bidx], type, id); - if (!map) { - DEBUG(2, ("WARNING: couldn't match sid (%s) " - "with requested ids\n", sidstr)); - TALLOC_FREE(sidstr); - continue; - } - - if ( ! string_to_sid(map->sid, sidstr)) { - DEBUG(2, ("ERROR: Invalid SID on entry\n")); - TALLOC_FREE(sidstr); - continue; - } - TALLOC_FREE(sidstr); - - /* mapped */ - map->status = ID_MAPPED; - - DEBUG(10, ("Mapped %s -> %lu (%d)\n", - sid_string_static(map->sid), - (unsigned long)map->xid.id, map->xid.type)); - } - - /* free the ldap results */ - if (result) { - ldap_msgfree(result); - result = NULL; - } - - if (multi && ids[idx]) { /* still some values to map */ - goto again; - } - - ret = NT_STATUS_OK; - - /* mark all unknwon/expired ones as unmapped */ - for (i = 0; ids[i]; i++) { - if (ids[i]->status != ID_MAPPED) - ids[i]->status = ID_UNMAPPED; - } - -done: - talloc_free(memctx); - return ret; -} - -/********************************** - lookup a set of sids. -**********************************/ - -/* this function searches up to IDMAP_LDAP_MAX_IDS entries - * in maps for a match */ -static struct id_map *find_map_by_sid(struct id_map **maps, DOM_SID *sid) -{ - int i; - - for (i = 0; i < IDMAP_LDAP_MAX_IDS; i++) { - if (maps[i] == NULL) { /* end of the run */ - return NULL; - } - if (sid_equal(maps[i]->sid, sid)) { - return maps[i]; - } - } - - return NULL; -} - -static NTSTATUS idmap_ldap_sids_to_unixids(struct idmap_domain *dom, - struct id_map **ids) -{ - LDAPMessage *entry = NULL; - NTSTATUS ret; - TALLOC_CTX *memctx; - struct idmap_ldap_context *ctx; - LDAPMessage *result = NULL; - const char *uidNumber; - const char *gidNumber; - const char **attr_list; - char *filter = NULL; - BOOL multi = False; - int idx = 0; - int bidx = 0; - int count; - int rc; - int i; - - /* Only do query if we are online */ - if (idmap_is_offline()) { - return NT_STATUS_FILE_IS_OFFLINE; - } - - /* Initilization my have been deferred because we were offline */ - if ( ! dom->initialized) { - ret = idmap_ldap_db_init(dom); - if ( ! NT_STATUS_IS_OK(ret)) { - return ret; - } - } - - ctx = talloc_get_type(dom->private_data, struct idmap_ldap_context); - - memctx = talloc_new(ctx); - if ( ! memctx) { - DEBUG(0, ("Out of memory!\n")); - return NT_STATUS_NO_MEMORY; - } - - uidNumber = get_attr_key2string(idpool_attr_list, LDAP_ATTR_UIDNUMBER); - gidNumber = get_attr_key2string(idpool_attr_list, LDAP_ATTR_GIDNUMBER); - - attr_list = get_attr_list(memctx, sidmap_attr_list); - - if ( ! ids[1]) { - /* if we are requested just one mapping use the simple filter */ - - filter = talloc_asprintf(memctx, "(&(objectClass=%s)(%s=%s))", - LDAP_OBJ_IDMAP_ENTRY, - LDAP_ATTRIBUTE_SID, - sid_string_static(ids[0]->sid)); - CHECK_ALLOC_DONE(filter); - DEBUG(10, ("Filter: [%s]\n", filter)); - } else { - /* multiple mappings */ - multi = True; - } - -again: - if (multi) { - - TALLOC_FREE(filter); - filter = talloc_asprintf(memctx, - "(&(objectClass=%s)(|", - LDAP_OBJ_IDMAP_ENTRY); - CHECK_ALLOC_DONE(filter); - - bidx = idx; - for (i = 0; (i < IDMAP_LDAP_MAX_IDS) && ids[idx]; i++, idx++) { - filter = talloc_asprintf_append(filter, "(%s=%s)", - LDAP_ATTRIBUTE_SID, - sid_string_static(ids[idx]->sid)); - CHECK_ALLOC_DONE(filter); - } - filter = talloc_asprintf_append(filter, "))"); - CHECK_ALLOC_DONE(filter); - DEBUG(10, ("Filter: [%s]", filter)); - } else { - bidx = 0; - idx = 1; - } - - rc = smbldap_search(ctx->smbldap_state, ctx->suffix, LDAP_SCOPE_SUBTREE, - filter, attr_list, 0, &result); - - if (rc != LDAP_SUCCESS) { - DEBUG(3,("Failure looking up sids (%s)\n", - ldap_err2string(rc))); - ret = NT_STATUS_UNSUCCESSFUL; - goto done; - } - - count = ldap_count_entries(ctx->smbldap_state->ldap_struct, result); - - if (count == 0) { - DEBUG(10, ("NO SIDs found\n")); - } - - for (i = 0; i < count; i++) { - char *sidstr = NULL; - char *tmp = NULL; - enum id_type type; - struct id_map *map; - DOM_SID sid; - uint32_t id; - - if (i == 0) { /* first entry */ - entry = ldap_first_entry(ctx->smbldap_state->ldap_struct, - result); - } else { /* following ones */ - entry = ldap_next_entry(ctx->smbldap_state->ldap_struct, - entry); - } - if ( ! entry) { - DEBUG(2, ("ERROR: Unable to fetch ldap entries " - "from results\n")); - break; - } - - /* first check if the SID is present */ - sidstr = smbldap_talloc_single_attribute( - ctx->smbldap_state->ldap_struct, - entry, LDAP_ATTRIBUTE_SID, memctx); - if ( ! sidstr) { /* no sid ??, skip entry */ - DEBUG(2, ("WARNING SID not found on entry\n")); - continue; - } - - if ( ! string_to_sid(&sid, sidstr)) { - DEBUG(2, ("ERROR: Invalid SID on entry\n")); - TALLOC_FREE(sidstr); - continue; - } - - map = find_map_by_sid(&ids[bidx], &sid); - if (!map) { - DEBUG(2, ("WARNING: couldn't find entry sid (%s) " - "in ids", sidstr)); - TALLOC_FREE(sidstr); - continue; - } - - TALLOC_FREE(sidstr); - - /* now try to see if it is a uid, if not try with a gid - * (gid is more common, but in case both uidNumber and - * gidNumber are returned the SID is mapped to the uid - * not the gid) */ - type = ID_TYPE_UID; - tmp = smbldap_talloc_single_attribute( - ctx->smbldap_state->ldap_struct, - entry, uidNumber, memctx); - if ( ! tmp) { - type = ID_TYPE_GID; - tmp = smbldap_talloc_single_attribute( - ctx->smbldap_state->ldap_struct, - entry, gidNumber, memctx); - } - if ( ! tmp) { /* no ids ?? */ - DEBUG(5, ("no uidNumber, " - "nor gidNumber attributes found\n")); - continue; - } - - id = strtoul(tmp, NULL, 10); - if ((id == 0) || - (ctx->filter_low_id && (id < ctx->filter_low_id)) || - (ctx->filter_high_id && (id > ctx->filter_high_id))) { - DEBUG(5, ("Requested id (%u) out of range (%u - %u). " - "Filtered!\n", id, - ctx->filter_low_id, ctx->filter_high_id)); - TALLOC_FREE(tmp); - continue; - } - TALLOC_FREE(tmp); - - /* mapped */ - map->xid.type = type; - map->xid.id = id; - map->status = ID_MAPPED; - - DEBUG(10, ("Mapped %s -> %lu (%d)\n", - sid_string_static(map->sid), - (unsigned long)map->xid.id, map->xid.type)); - } - - /* free the ldap results */ - if (result) { - ldap_msgfree(result); - result = NULL; - } - - if (multi && ids[idx]) { /* still some values to map */ - goto again; - } - - ret = NT_STATUS_OK; - - /* mark all unknwon/expired ones as unmapped */ - for (i = 0; ids[i]; i++) { - if (ids[i]->status != ID_MAPPED) - ids[i]->status = ID_UNMAPPED; - } - -done: - talloc_free(memctx); - return ret; -} - -/********************************** - set a mapping. -**********************************/ - -/* TODO: change this: This function cannot be called to modify a mapping, - * only set a new one */ - -static NTSTATUS idmap_ldap_set_mapping(struct idmap_domain *dom, - const struct id_map *map) -{ - NTSTATUS ret; - TALLOC_CTX *memctx; - struct idmap_ldap_context *ctx; - LDAPMessage *entry = NULL; - LDAPMod **mods = NULL; - const char *type; - char *id_str; - char *sid; - char *dn; - int rc = -1; - - /* Only do query if we are online */ - if (idmap_is_offline()) { - return NT_STATUS_FILE_IS_OFFLINE; - } - - /* Initilization my have been deferred because we were offline */ - if ( ! dom->initialized) { - ret = idmap_ldap_db_init(dom); - if ( ! NT_STATUS_IS_OK(ret)) { - return ret; - } - } - - ctx = talloc_get_type(dom->private_data, struct idmap_ldap_context); - - switch(map->xid.type) { - case ID_TYPE_UID: - type = get_attr_key2string(sidmap_attr_list, - LDAP_ATTR_UIDNUMBER); - break; - - case ID_TYPE_GID: - type = get_attr_key2string(sidmap_attr_list, - LDAP_ATTR_GIDNUMBER); - break; - - default: - return NT_STATUS_INVALID_PARAMETER; - } - - memctx = talloc_new(ctx); - if ( ! memctx) { - DEBUG(0, ("Out of memory!\n")); - return NT_STATUS_NO_MEMORY; - } - - id_str = talloc_asprintf(memctx, "%lu", (unsigned long)map->xid.id); - CHECK_ALLOC_DONE(id_str); - - sid = talloc_strdup(memctx, sid_string_static(map->sid)); - CHECK_ALLOC_DONE(sid); - - dn = talloc_asprintf(memctx, "%s=%s,%s", - get_attr_key2string(sidmap_attr_list, LDAP_ATTR_SID), - sid, - ctx->suffix); - CHECK_ALLOC_DONE(dn); - - smbldap_set_mod(&mods, LDAP_MOD_ADD, - "objectClass", LDAP_OBJ_IDMAP_ENTRY); - - smbldap_make_mod(ctx->smbldap_state->ldap_struct, - entry, &mods, type, id_str); - - smbldap_make_mod(ctx->smbldap_state->ldap_struct, entry, &mods, - get_attr_key2string(sidmap_attr_list, LDAP_ATTR_SID), - sid); - - if ( ! mods) { - DEBUG(2, ("ERROR: No mods?\n")); - ret = NT_STATUS_UNSUCCESSFUL; - goto done; - } - - /* TODO: remove conflicting mappings! */ - - smbldap_set_mod(&mods, LDAP_MOD_ADD, "objectClass", LDAP_OBJ_SID_ENTRY); - - DEBUG(10, ("Set DN %s (%s -> %s)\n", dn, sid, id_str)); - - rc = smbldap_add(ctx->smbldap_state, dn, mods); - ldap_mods_free(mods, True); - - if (rc != LDAP_SUCCESS) { - char *ld_error = NULL; - ldap_get_option(ctx->smbldap_state->ldap_struct, - LDAP_OPT_ERROR_STRING, &ld_error); - DEBUG(0,("ldap_set_mapping_internals: Failed to add %s to %lu " - "mapping [%s]\n", sid, - (unsigned long)map->xid.id, type)); - DEBUG(0, ("ldap_set_mapping_internals: Error was: %s (%s)\n", - ld_error ? ld_error : "(NULL)", ldap_err2string (rc))); - if (ld_error) { - ldap_memfree(ld_error); - } - ret = NT_STATUS_UNSUCCESSFUL; - goto done; - } - - DEBUG(10,("ldap_set_mapping: Successfully created mapping from %s to " - "%lu [%s]\n", sid, (unsigned long)map->xid.id, type)); - - ret = NT_STATUS_OK; - -done: - talloc_free(memctx); - return ret; -} - -/********************************** - Close the idmap ldap instance -**********************************/ - -static NTSTATUS idmap_ldap_close(struct idmap_domain *dom) -{ - struct idmap_ldap_context *ctx; - - if (dom->private_data) { - ctx = talloc_get_type(dom->private_data, - struct idmap_ldap_context); - - talloc_free(ctx); - dom->private_data = NULL; - } - - return NT_STATUS_OK; -} - -static struct idmap_methods idmap_ldap_methods = { - - .init = idmap_ldap_db_init, - .unixids_to_sids = idmap_ldap_unixids_to_sids, - .sids_to_unixids = idmap_ldap_sids_to_unixids, - .set_mapping = idmap_ldap_set_mapping, - .close_fn = idmap_ldap_close -}; - -static struct idmap_alloc_methods idmap_ldap_alloc_methods = { - - .init = idmap_ldap_alloc_init, - .allocate_id = idmap_ldap_allocate_id, - .get_id_hwm = idmap_ldap_get_hwm, - .set_id_hwm = idmap_ldap_set_hwm, - .close_fn = idmap_ldap_alloc_close, - /* .dump_data = TODO */ -}; - -NTSTATUS idmap_alloc_ldap_init(void) -{ - return smb_register_idmap_alloc(SMB_IDMAP_INTERFACE_VERSION, "ldap", - &idmap_ldap_alloc_methods); -} - -NTSTATUS idmap_ldap_init(void) -{ - NTSTATUS ret; - - /* FIXME: bad hack to actually register also the alloc_ldap module - * without changining configure.in */ - ret = idmap_alloc_ldap_init(); - if (! NT_STATUS_IS_OK(ret)) { - return ret; - } - return smb_register_idmap(SMB_IDMAP_INTERFACE_VERSION, "ldap", - &idmap_ldap_methods); -} - diff --git a/source3/nsswitch/idmap_nss.c b/source3/nsswitch/idmap_nss.c deleted file mode 100644 index 5bb2389c93..0000000000 --- a/source3/nsswitch/idmap_nss.c +++ /dev/null @@ -1,223 +0,0 @@ -/* - Unix SMB/CIFS implementation. - - idmap PASSDB backend - - Copyright (C) Simo Sorce 2006 - - 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 3 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, see . -*/ - -#include "includes.h" -#include "winbindd.h" - -#undef DBGC_CLASS -#define DBGC_CLASS DBGC_IDMAP - -/***************************** - Initialise idmap database. -*****************************/ - -static NTSTATUS idmap_nss_int_init(struct idmap_domain *dom) -{ - dom->initialized = True; - return NT_STATUS_OK; -} - -/********************************** - lookup a set of unix ids. -**********************************/ - -static NTSTATUS idmap_nss_unixids_to_sids(struct idmap_domain *dom, struct id_map **ids) -{ - TALLOC_CTX *ctx; - int i; - - if (! dom->initialized) { - return NT_STATUS_UNSUCCESSFUL; - } - - ctx = talloc_new(dom); - if ( ! ctx) { - DEBUG(0, ("Out of memory!\n")); - return NT_STATUS_NO_MEMORY; - } - - for (i = 0; ids[i]; i++) { - struct passwd *pw; - struct group *gr; - const char *name; - enum lsa_SidType type; - BOOL ret; - - switch (ids[i]->xid.type) { - case ID_TYPE_UID: - pw = getpwuid((uid_t)ids[i]->xid.id); - - if (!pw) { - ids[i]->status = ID_UNMAPPED; - continue; - } - name = pw->pw_name; - break; - case ID_TYPE_GID: - gr = getgrgid((gid_t)ids[i]->xid.id); - - if (!gr) { - ids[i]->status = ID_UNMAPPED; - continue; - } - name = gr->gr_name; - break; - default: /* ?? */ - ids[i]->status = ID_UNKNOWN; - continue; - } - - /* by default calls to winbindd are disabled - the following call will not recurse so this is safe */ - winbind_on(); - /* Lookup name from PDC using lsa_lookup_names() */ - ret = winbind_lookup_name(dom->name, name, ids[i]->sid, &type); - winbind_off(); - - if (!ret) { - /* TODO: how do we know if the name is really not mapped, - * or something just failed ? */ - ids[i]->status = ID_UNMAPPED; - continue; - } - - switch (type) { - case SID_NAME_USER: - if (ids[i]->xid.type == ID_TYPE_UID) { - ids[i]->status = ID_MAPPED; - } - break; - - case SID_NAME_DOM_GRP: - case SID_NAME_ALIAS: - case SID_NAME_WKN_GRP: - if (ids[i]->xid.type == ID_TYPE_GID) { - ids[i]->status = ID_MAPPED; - } - break; - - default: - ids[i]->status = ID_UNKNOWN; - break; - } - } - - - talloc_free(ctx); - return NT_STATUS_OK; -} - -/********************************** - lookup a set of sids. -**********************************/ - -static NTSTATUS idmap_nss_sids_to_unixids(struct idmap_domain *dom, struct id_map **ids) -{ - TALLOC_CTX *ctx; - int i; - - if (! dom->initialized) { - return NT_STATUS_UNSUCCESSFUL; - } - - ctx = talloc_new(dom); - if ( ! ctx) { - DEBUG(0, ("Out of memory!\n")); - return NT_STATUS_NO_MEMORY; - } - - for (i = 0; ids[i]; i++) { - struct passwd *pw; - struct group *gr; - enum lsa_SidType type; - const char *dom_name = NULL; - const char *name = NULL; - BOOL ret; - - /* by default calls to winbindd are disabled - the following call will not recurse so this is safe */ - winbind_on(); - ret = winbind_lookup_sid(ctx, ids[i]->sid, &dom_name, &name, &type); - winbind_off(); - - if (!ret) { - /* TODO: how do we know if the name is really not mapped, - * or something just failed ? */ - ids[i]->status = ID_UNMAPPED; - continue; - } - - switch (type) { - case SID_NAME_USER: - - /* this will find also all lower case name and use username level */ - - pw = Get_Pwnam(name); - if (pw) { - ids[i]->xid.id = pw->pw_uid; - ids[i]->xid.type = ID_TYPE_UID; - ids[i]->status = ID_MAPPED; - } - break; - - case SID_NAME_DOM_GRP: - case SID_NAME_ALIAS: - case SID_NAME_WKN_GRP: - - gr = getgrnam(name); - if (gr) { - ids[i]->xid.id = gr->gr_gid; - ids[i]->xid.type = ID_TYPE_GID; - ids[i]->status = ID_MAPPED; - } - break; - - default: - ids[i]->status = ID_UNKNOWN; - break; - } - } - - talloc_free(ctx); - return NT_STATUS_OK; -} - -/********************************** - Close the idmap tdb instance -**********************************/ - -static NTSTATUS idmap_nss_close(struct idmap_domain *dom) -{ - return NT_STATUS_OK; -} - -static struct idmap_methods nss_methods = { - - .init = idmap_nss_int_init, - .unixids_to_sids = idmap_nss_unixids_to_sids, - .sids_to_unixids = idmap_nss_sids_to_unixids, - .close_fn = idmap_nss_close -}; - -NTSTATUS idmap_nss_init(void) -{ - return smb_register_idmap(SMB_IDMAP_INTERFACE_VERSION, "nss", &nss_methods); -} diff --git a/source3/nsswitch/idmap_passdb.c b/source3/nsswitch/idmap_passdb.c deleted file mode 100644 index 17afd71ab8..0000000000 --- a/source3/nsswitch/idmap_passdb.c +++ /dev/null @@ -1,139 +0,0 @@ -/* - Unix SMB/CIFS implementation. - - idmap PASSDB backend - - Copyright (C) Simo Sorce 2006 - - 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 3 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, see . -*/ - -#include "includes.h" - -#undef DBGC_CLASS -#define DBGC_CLASS DBGC_IDMAP - -/***************************** - Initialise idmap database. -*****************************/ - -static NTSTATUS idmap_pdb_init(struct idmap_domain *dom) -{ - dom->initialized = True; - return NT_STATUS_OK; -} - -/********************************** - lookup a set of unix ids. -**********************************/ - -static NTSTATUS idmap_pdb_unixids_to_sids(struct idmap_domain *dom, struct id_map **ids) -{ - int i; - - if (! dom->initialized) { - return NT_STATUS_UNSUCCESSFUL; - } - - for (i = 0; ids[i]; i++) { - - /* unmapped by default */ - ids[i]->status = ID_UNMAPPED; - - switch (ids[i]->xid.type) { - case ID_TYPE_UID: - if (pdb_uid_to_sid((uid_t)ids[i]->xid.id, ids[i]->sid)) { - ids[i]->status = ID_MAPPED; - } - break; - case ID_TYPE_GID: - if (pdb_gid_to_sid((gid_t)ids[i]->xid.id, ids[i]->sid)) { - ids[i]->status = ID_MAPPED; - } - break; - default: /* ?? */ - ids[i]->status = ID_UNKNOWN; - } - } - - return NT_STATUS_OK; -} - -/********************************** - lookup a set of sids. -**********************************/ - -static NTSTATUS idmap_pdb_sids_to_unixids(struct idmap_domain *dom, struct id_map **ids) -{ - int i; - - if (! dom->initialized) { - return NT_STATUS_UNSUCCESSFUL; - } - - for (i = 0; ids[i]; i++) { - enum lsa_SidType type; - union unid_t id; - - if (pdb_sid_to_id(ids[i]->sid, &id, &type)) { - switch (type) { - case SID_NAME_USER: - ids[i]->xid.id = id.uid; - ids[i]->xid.type = ID_TYPE_UID; - ids[i]->status = ID_MAPPED; - break; - - case SID_NAME_DOM_GRP: - case SID_NAME_ALIAS: - case SID_NAME_WKN_GRP: - ids[i]->xid.id = id.gid; - ids[i]->xid.type = ID_TYPE_GID; - ids[i]->status = ID_MAPPED; - break; - - default: /* ?? */ - /* make sure it is marked as unmapped */ - ids[i]->status = ID_UNKNOWN; - break; - } - } else { - /* Query Failed */ - ids[i]->status = ID_UNMAPPED; - } - } - - return NT_STATUS_OK; -} - -/********************************** - Close the idmap tdb instance -**********************************/ - -static NTSTATUS idmap_pdb_close(struct idmap_domain *dom) -{ - return NT_STATUS_OK; -} - -static struct idmap_methods passdb_methods = { - - .init = idmap_pdb_init, - .unixids_to_sids = idmap_pdb_unixids_to_sids, - .sids_to_unixids = idmap_pdb_sids_to_unixids, - .close_fn =idmap_pdb_close -}; - -NTSTATUS idmap_passdb_init(void) -{ - return smb_register_idmap(SMB_IDMAP_INTERFACE_VERSION, "passdb", &passdb_methods); -} diff --git a/source3/nsswitch/idmap_rid.c b/source3/nsswitch/idmap_rid.c deleted file mode 100644 index 8e5f1302f7..0000000000 --- a/source3/nsswitch/idmap_rid.c +++ /dev/null @@ -1,267 +0,0 @@ -/* - * idmap_rid: static map between Active Directory/NT RIDs and RFC 2307 accounts - * Copyright (C) Guenther Deschner, 2004 - * Copyright (C) Sumit Bose, 2004 - * - * 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 3 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, see . - * - */ - -#include "includes.h" -#include "winbindd.h" - -#undef DBGC_CLASS -#define DBGC_CLASS DBGC_IDMAP - -struct idmap_rid_context { - const char *domain_name; - uint32_t low_id; - uint32_t high_id; - uint32_t base_rid; -}; - -/****************************************************************************** - compat params can't be used because of the completely different way - we support multiple domains in the new idmap - *****************************************************************************/ - -static NTSTATUS idmap_rid_initialize(struct idmap_domain *dom) -{ - NTSTATUS ret; - struct idmap_rid_context *ctx; - char *config_option = NULL; - const char *range; - uid_t low_uid = 0; - uid_t high_uid = 0; - gid_t low_gid = 0; - gid_t high_gid = 0; - - if ( (ctx = TALLOC_ZERO_P(dom, struct idmap_rid_context)) == NULL ) { - DEBUG(0, ("Out of memory!\n")); - return NT_STATUS_NO_MEMORY; - } - - config_option = talloc_asprintf(ctx, "idmap config %s", dom->name); - if ( ! config_option) { - DEBUG(0, ("Out of memory!\n")); - ret = NT_STATUS_NO_MEMORY; - goto failed; - } - - range = lp_parm_const_string(-1, config_option, "range", NULL); - if ( !range || - (sscanf(range, "%u - %u", &ctx->low_id, &ctx->high_id) != 2) || - (ctx->low_id > ctx->high_id)) - { - ctx->low_id = 0; - ctx->high_id = 0; - } - - /* lets see if the range is defined by the old idmap uid/idmap gid */ - if (!ctx->low_id && !ctx->high_id) { - if (lp_idmap_uid(&low_uid, &high_uid)) { - ctx->low_id = low_uid; - ctx->high_id = high_uid; - } - - if (lp_idmap_gid(&low_gid, &high_gid)) { - if ((ctx->low_id != low_gid) || - (ctx->high_id != high_uid)) { - DEBUG(1, ("ERROR: idmap uid range must match idmap gid range\n")); - ret = NT_STATUS_UNSUCCESSFUL; - goto failed; - } - } - } - - if (!ctx->low_id || !ctx->high_id) { - DEBUG(1, ("ERROR: Invalid configuration, ID range missing or invalid\n")); - ret = NT_STATUS_UNSUCCESSFUL; - goto failed; - } - - ctx->base_rid = lp_parm_int(-1, config_option, "base_rid", 0); - ctx->domain_name = talloc_strdup( ctx, dom->name ); - - dom->private_data = ctx; - dom->initialized = True; - - talloc_free(config_option); - return NT_STATUS_OK; - -failed: - talloc_free(ctx); - return ret; -} - -static NTSTATUS idmap_rid_id_to_sid(TALLOC_CTX *memctx, struct idmap_rid_context *ctx, struct id_map *map) -{ - struct winbindd_domain *domain; - - /* apply filters before checking */ - if ((map->xid.id < ctx->low_id) || (map->xid.id > ctx->high_id)) { - DEBUG(5, ("Requested id (%u) out of range (%u - %u). Filtered!\n", - map->xid.id, ctx->low_id, ctx->high_id)); - return NT_STATUS_NONE_MAPPED; - } - - if ( (domain = find_domain_from_name_noinit(ctx->domain_name)) == NULL ) { - return NT_STATUS_NO_SUCH_DOMAIN; - } - - sid_compose(map->sid, &domain->sid, map->xid.id - ctx->low_id + ctx->base_rid); - - /* We **really** should have some way of validating - the SID exists and is the correct type here. But - that is a deficiency in the idmap_rid design. */ - - map->status = ID_MAPPED; - - return NT_STATUS_OK; -} - -/********************************** - Single sid to id lookup function. -**********************************/ - -static NTSTATUS idmap_rid_sid_to_id(TALLOC_CTX *memctx, struct idmap_rid_context *ctx, struct id_map *map) -{ - uint32_t rid; - - sid_peek_rid(map->sid, &rid); - map->xid.id = rid - ctx->base_rid + ctx->low_id; - - /* apply filters before returning result */ - - if ((map->xid.id < ctx->low_id) || (map->xid.id > ctx->high_id)) { - DEBUG(5, ("Requested id (%u) out of range (%u - %u). Filtered!\n", - map->xid.id, ctx->low_id, ctx->high_id)); - map->status = ID_UNMAPPED; - return NT_STATUS_NONE_MAPPED; - } - - /* We **really** should have some way of validating - the SID exists and is the correct type here. But - that is a deficiency in the idmap_rid design. */ - - map->status = ID_MAPPED; - - return NT_STATUS_OK; -} - -/********************************** - lookup a set of unix ids. -**********************************/ - -static NTSTATUS idmap_rid_unixids_to_sids(struct idmap_domain *dom, struct id_map **ids) -{ - struct idmap_rid_context *ridctx; - TALLOC_CTX *ctx; - NTSTATUS ret; - int i; - - /* Initilization my have been deferred because of an error, retry or fail */ - if ( ! dom->initialized) { - ret = idmap_rid_initialize(dom); - if ( ! NT_STATUS_IS_OK(ret)) { - return ret; - } - } - - ridctx = talloc_get_type(dom->private_data, struct idmap_rid_context); - - ctx = talloc_new(dom); - if ( ! ctx) { - DEBUG(0, ("Out of memory!\n")); - return NT_STATUS_NO_MEMORY; - } - - for (i = 0; ids[i]; i++) { - - ret = idmap_rid_id_to_sid(ctx, ridctx, ids[i]); - - if (( ! NT_STATUS_IS_OK(ret)) && - ( ! NT_STATUS_EQUAL(ret, NT_STATUS_NONE_MAPPED))) { - /* some fatal error occurred, log it */ - DEBUG(3, ("Unexpected error resolving an ID (%d)\n", ids[i]->xid.id)); - } - } - - talloc_free(ctx); - return NT_STATUS_OK; -} - -/********************************** - lookup a set of sids. -**********************************/ - -static NTSTATUS idmap_rid_sids_to_unixids(struct idmap_domain *dom, struct id_map **ids) -{ - struct idmap_rid_context *ridctx; - TALLOC_CTX *ctx; - NTSTATUS ret; - int i; - - /* Initilization my have been deferred because of an error, retry or fail */ - if ( ! dom->initialized) { - ret = idmap_rid_initialize(dom); - if ( ! NT_STATUS_IS_OK(ret)) { - return ret; - } - } - - ridctx = talloc_get_type(dom->private_data, struct idmap_rid_context); - - ctx = talloc_new(dom); - if ( ! ctx) { - DEBUG(0, ("Out of memory!\n")); - return NT_STATUS_NO_MEMORY; - } - - for (i = 0; ids[i]; i++) { - - ret = idmap_rid_sid_to_id(ctx, ridctx, ids[i]); - - if (( ! NT_STATUS_IS_OK(ret)) && - ( ! NT_STATUS_EQUAL(ret, NT_STATUS_NONE_MAPPED))) { - /* some fatal error occurred, log it */ - DEBUG(3, ("Unexpected error resolving a SID (%s)\n", - sid_string_static(ids[i]->sid))); - } - } - - talloc_free(ctx); - return NT_STATUS_OK; -} - -static NTSTATUS idmap_rid_close(struct idmap_domain *dom) -{ - if (dom->private_data) { - TALLOC_FREE(dom->private_data); - } - return NT_STATUS_OK; -} - -static struct idmap_methods rid_methods = { - .init = idmap_rid_initialize, - .unixids_to_sids = idmap_rid_unixids_to_sids, - .sids_to_unixids = idmap_rid_sids_to_unixids, - .close_fn = idmap_rid_close -}; - -NTSTATUS idmap_rid_init(void) -{ - return smb_register_idmap(SMB_IDMAP_INTERFACE_VERSION, "rid", &rid_methods); -} - diff --git a/source3/nsswitch/idmap_tdb.c b/source3/nsswitch/idmap_tdb.c deleted file mode 100644 index 97000689fa..0000000000 --- a/source3/nsswitch/idmap_tdb.c +++ /dev/null @@ -1,1232 +0,0 @@ -/* - Unix SMB/CIFS implementation. - - idmap TDB backend - - Copyright (C) Tim Potter 2000 - Copyright (C) Jim McDonough 2003 - Copyright (C) Jeremy Allison 2006 - Copyright (C) Simo Sorce 2003-2006 - - 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 3 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, see . -*/ - -#include "includes.h" -#include "winbindd.h" - -#undef DBGC_CLASS -#define DBGC_CLASS DBGC_IDMAP - -/* High water mark keys */ -#define HWM_GROUP "GROUP HWM" -#define HWM_USER "USER HWM" - -static struct idmap_tdb_state { - - /* User and group id pool */ - uid_t low_uid, high_uid; /* Range of uids to allocate */ - gid_t low_gid, high_gid; /* Range of gids to allocate */ - -} idmap_tdb_state; - -/***************************************************************************** - For idmap conversion: convert one record to new format - Ancient versions (eg 2.2.3a) of winbindd_idmap.tdb mapped DOMAINNAME/rid - instead of the SID. -*****************************************************************************/ -static int convert_fn(TDB_CONTEXT *tdb, TDB_DATA key, TDB_DATA data, void *state) -{ - struct winbindd_domain *domain; - char *p; - DOM_SID sid; - uint32 rid; - fstring keystr; - fstring dom_name; - TDB_DATA key2; - BOOL *failed = (BOOL *)state; - - DEBUG(10,("Converting %s\n", (const char *)key.dptr)); - - p = strchr((const char *)key.dptr, '/'); - if (!p) - return 0; - - *p = 0; - fstrcpy(dom_name, (const char *)key.dptr); - *p++ = '/'; - - domain = find_domain_from_name(dom_name); - if (domain == NULL) { - /* We must delete the old record. */ - DEBUG(0,("Unable to find domain %s\n", dom_name )); - DEBUG(0,("deleting record %s\n", (const char *)key.dptr )); - - if (tdb_delete(tdb, key) != 0) { - DEBUG(0, ("Unable to delete record %s\n", (const char *)key.dptr)); - *failed = True; - return -1; - } - - return 0; - } - - rid = atoi(p); - - sid_copy(&sid, &domain->sid); - sid_append_rid(&sid, rid); - - sid_to_string(keystr, &sid); - key2 = string_term_tdb_data(keystr); - - if (tdb_store(tdb, key2, data, TDB_INSERT) != 0) { - DEBUG(0,("Unable to add record %s\n", (const char *)key2.dptr )); - *failed = True; - return -1; - } - - if (tdb_store(tdb, data, key2, TDB_REPLACE) != 0) { - DEBUG(0,("Unable to update record %s\n", (const char *)data.dptr )); - *failed = True; - return -1; - } - - if (tdb_delete(tdb, key) != 0) { - DEBUG(0,("Unable to delete record %s\n", (const char *)key.dptr )); - *failed = True; - return -1; - } - - return 0; -} - -/***************************************************************************** - Convert the idmap database from an older version. -*****************************************************************************/ - -static BOOL idmap_tdb_upgrade(const char *idmap_name) -{ - int32 vers; - BOOL bigendianheader; - BOOL failed = False; - TDB_CONTEXT *idmap_tdb; - - DEBUG(0, ("Upgrading winbindd_idmap.tdb from an old version\n")); - - if (!(idmap_tdb = tdb_open_log(idmap_name, 0, - TDB_DEFAULT, O_RDWR, - 0600))) { - DEBUG(0, ("Unable to open idmap database\n")); - return False; - } - - bigendianheader = (tdb_get_flags(idmap_tdb) & TDB_BIGENDIAN) ? True : False; - - vers = tdb_fetch_int32(idmap_tdb, "IDMAP_VERSION"); - - if (((vers == -1) && bigendianheader) || (IREV(vers) == IDMAP_VERSION)) { - /* Arrggghh ! Bytereversed or old big-endian - make order independent ! */ - /* - * high and low records were created on a - * big endian machine and will need byte-reversing. - */ - - int32 wm; - - wm = tdb_fetch_int32(idmap_tdb, HWM_USER); - - if (wm != -1) { - wm = IREV(wm); - } else { - wm = idmap_tdb_state.low_uid; - } - - if (tdb_store_int32(idmap_tdb, HWM_USER, wm) == -1) { - DEBUG(0, ("Unable to byteswap user hwm in idmap database\n")); - tdb_close(idmap_tdb); - return False; - } - - wm = tdb_fetch_int32(idmap_tdb, HWM_GROUP); - if (wm != -1) { - wm = IREV(wm); - } else { - wm = idmap_tdb_state.low_gid; - } - - if (tdb_store_int32(idmap_tdb, HWM_GROUP, wm) == -1) { - DEBUG(0, ("Unable to byteswap group hwm in idmap database\n")); - tdb_close(idmap_tdb); - return False; - } - } - - /* the old format stored as DOMAIN/rid - now we store the SID direct */ - tdb_traverse(idmap_tdb, convert_fn, &failed); - - if (failed) { - DEBUG(0, ("Problem during conversion\n")); - tdb_close(idmap_tdb); - return False; - } - - if (tdb_store_int32(idmap_tdb, "IDMAP_VERSION", IDMAP_VERSION) == -1) { - DEBUG(0, ("Unable to dtore idmap version in databse\n")); - tdb_close(idmap_tdb); - return False; - } - - tdb_close(idmap_tdb); - return True; -} - -/* WARNING: We can't open a tdb twice inthe same process, for that reason - * I'm going to use a hack with open ref counts to open the winbindd_idmap.tdb - * only once. We will later decide whether to split the db in multiple files - * or come up with a better solution to share them. */ - -static TDB_CONTEXT *idmap_tdb_common_ctx; -static int idmap_tdb_open_ref_count = 0; - -static NTSTATUS idmap_tdb_open_db(TALLOC_CTX *memctx, TDB_CONTEXT **tdbctx) -{ - NTSTATUS ret; - TALLOC_CTX *ctx; - SMB_STRUCT_STAT stbuf; - char *tdbfile = NULL; - int32 version; - BOOL tdb_is_new = False; - - if (idmap_tdb_open_ref_count) { /* the tdb has already been opened */ - idmap_tdb_open_ref_count++; - *tdbctx = idmap_tdb_common_ctx; - return NT_STATUS_OK; - } - - /* use our own context here */ - ctx = talloc_new(memctx); - if (!ctx) { - DEBUG(0, ("Out of memory!\n")); - return NT_STATUS_NO_MEMORY; - } - - /* use the old database if present */ - tdbfile = talloc_strdup(ctx, lock_path("winbindd_idmap.tdb")); - if (!tdbfile) { - DEBUG(0, ("Out of memory!\n")); - ret = NT_STATUS_NO_MEMORY; - goto done; - } - - if (!file_exist(tdbfile, &stbuf)) { - tdb_is_new = True; - } - - DEBUG(10,("Opening tdbfile %s\n", tdbfile )); - - /* Open idmap repository */ - if (!(idmap_tdb_common_ctx = tdb_open_log(tdbfile, 0, TDB_DEFAULT, O_RDWR | O_CREAT, 0644))) { - DEBUG(0, ("Unable to open idmap database\n")); - ret = NT_STATUS_UNSUCCESSFUL; - goto done; - } - - if (tdb_is_new) { - /* the file didn't existed before opening it, let's - * store idmap version as nobody else yet opened and - * stored it. I do not like this method but didn't - * found a way to understand if an opened tdb have - * been just created or not --- SSS */ - tdb_store_int32(idmap_tdb_common_ctx, "IDMAP_VERSION", IDMAP_VERSION); - } - - /* check against earlier versions */ - version = tdb_fetch_int32(idmap_tdb_common_ctx, "IDMAP_VERSION"); - if (version != IDMAP_VERSION) { - - /* backup_tdb expects the tdb not to be open */ - tdb_close(idmap_tdb_common_ctx); - - if ( ! idmap_tdb_upgrade(tdbfile)) { - - DEBUG(0, ("Unable to open idmap database, it's in an old formati, and upgrade failed!\n")); - ret = NT_STATUS_INTERNAL_DB_ERROR; - goto done; - } - - /* Re-Open idmap repository */ - if (!(idmap_tdb_common_ctx = tdb_open_log(tdbfile, 0, TDB_DEFAULT, O_RDWR | O_CREAT, 0644))) { - DEBUG(0, ("Unable to open idmap database\n")); - ret = NT_STATUS_UNSUCCESSFUL; - goto done; - } - } - - *tdbctx = idmap_tdb_common_ctx; - idmap_tdb_open_ref_count++; - ret = NT_STATUS_OK; - -done: - talloc_free(ctx); - return ret; -} - - /* NEVER use tdb_close() except for the conversion routines that are guaranteed - * to run only when the database is opened the first time, always use this function. */ - -BOOL idmap_tdb_tdb_close(TDB_CONTEXT *tdbctx) -{ - if (tdbctx != idmap_tdb_common_ctx) { - DEBUG(0, ("ERROR: Invalid tdb context!")); - return False; - } - - idmap_tdb_open_ref_count--; - if (idmap_tdb_open_ref_count) { - return True; - } - - return tdb_close(idmap_tdb_common_ctx); -} - -/********************************************************************** - IDMAP ALLOC TDB BACKEND -**********************************************************************/ - -static TDB_CONTEXT *idmap_alloc_tdb; - -/********************************** - Initialise idmap alloc database. -**********************************/ - -static NTSTATUS idmap_tdb_alloc_init( const char *params ) -{ - NTSTATUS ret; - TALLOC_CTX *ctx; - const char *range; - uid_t low_uid = 0; - uid_t high_uid = 0; - gid_t low_gid = 0; - gid_t high_gid = 0; - - /* use our own context here */ - ctx = talloc_new(NULL); - if (!ctx) { - DEBUG(0, ("Out of memory!\n")); - return NT_STATUS_NO_MEMORY; - } - - ret = idmap_tdb_open_db(ctx, &idmap_alloc_tdb); - if ( ! NT_STATUS_IS_OK(ret)) { - talloc_free(ctx); - return ret; - } - - talloc_free(ctx); - - /* load ranges */ - idmap_tdb_state.low_uid = 0; - idmap_tdb_state.high_uid = 0; - idmap_tdb_state.low_gid = 0; - idmap_tdb_state.high_gid = 0; - - range = lp_parm_const_string(-1, "idmap alloc config", "range", NULL); - if (range && range[0]) { - unsigned low_id, high_id; - - if (sscanf(range, "%u - %u", &low_id, &high_id) == 2) { - if (low_id < high_id) { - idmap_tdb_state.low_gid = idmap_tdb_state.low_uid = low_id; - idmap_tdb_state.high_gid = idmap_tdb_state.high_uid = high_id; - } else { - DEBUG(1, ("ERROR: invalid idmap alloc range [%s]", range)); - } - } else { - DEBUG(1, ("ERROR: invalid syntax for idmap alloc config:range [%s]", range)); - } - } - - /* Create high water marks for group and user id */ - if (lp_idmap_uid(&low_uid, &high_uid)) { - idmap_tdb_state.low_uid = low_uid; - idmap_tdb_state.high_uid = high_uid; - } - - if (lp_idmap_gid(&low_gid, &high_gid)) { - idmap_tdb_state.low_gid = low_gid; - idmap_tdb_state.high_gid = high_gid; - } - - if (idmap_tdb_state.high_uid <= idmap_tdb_state.low_uid) { - DEBUG(1, ("idmap uid range missing or invalid\n")); - DEBUGADD(1, ("idmap will be unable to map foreign SIDs\n")); - return NT_STATUS_UNSUCCESSFUL; - } else { - uint32 low_id; - - if (((low_id = tdb_fetch_int32(idmap_alloc_tdb, HWM_USER)) == -1) || - (low_id < idmap_tdb_state.low_uid)) { - if (tdb_store_int32(idmap_alloc_tdb, HWM_USER, idmap_tdb_state.low_uid) == -1) { - DEBUG(0, ("Unable to initialise user hwm in idmap database\n")); - return NT_STATUS_INTERNAL_DB_ERROR; - } - } - } - - if (idmap_tdb_state.high_gid <= idmap_tdb_state.low_gid) { - DEBUG(1, ("idmap gid range missing or invalid\n")); - DEBUGADD(1, ("idmap will be unable to map foreign SIDs\n")); - return NT_STATUS_UNSUCCESSFUL; - } else { - uint32 low_id; - - if (((low_id = tdb_fetch_int32(idmap_alloc_tdb, HWM_GROUP)) == -1) || - (low_id < idmap_tdb_state.low_gid)) { - if (tdb_store_int32(idmap_alloc_tdb, HWM_GROUP, idmap_tdb_state.low_gid) == -1) { - DEBUG(0, ("Unable to initialise group hwm in idmap database\n")); - return NT_STATUS_INTERNAL_DB_ERROR; - } - } - } - - return NT_STATUS_OK; -} - -/********************************** - Allocate a new id. -**********************************/ - -static NTSTATUS idmap_tdb_allocate_id(struct unixid *xid) -{ - BOOL ret; - const char *hwmkey; - const char *hwmtype; - uint32_t high_hwm; - uint32_t hwm; - - /* Get current high water mark */ - switch (xid->type) { - - case ID_TYPE_UID: - hwmkey = HWM_USER; - hwmtype = "UID"; - high_hwm = idmap_tdb_state.high_uid; - break; - - case ID_TYPE_GID: - hwmkey = HWM_GROUP; - hwmtype = "GID"; - high_hwm = idmap_tdb_state.high_gid; - break; - - default: - DEBUG(2, ("Invalid ID type (0x%x)\n", xid->type)); - return NT_STATUS_INVALID_PARAMETER; - } - - if ((hwm = tdb_fetch_int32(idmap_alloc_tdb, hwmkey)) == -1) { - return NT_STATUS_INTERNAL_DB_ERROR; - } - - /* check it is in the range */ - if (hwm > high_hwm) { - DEBUG(1, ("Fatal Error: %s range full!! (max: %lu)\n", - hwmtype, (unsigned long)high_hwm)); - return NT_STATUS_UNSUCCESSFUL; - } - - /* fetch a new id and increment it */ - ret = tdb_change_uint32_atomic(idmap_alloc_tdb, hwmkey, &hwm, 1); - if (!ret) { - DEBUG(1, ("Fatal error while fetching a new %s value\n!", hwmtype)); - return NT_STATUS_UNSUCCESSFUL; - } - - /* recheck it is in the range */ - if (hwm > high_hwm) { - DEBUG(1, ("Fatal Error: %s range full!! (max: %lu)\n", - hwmtype, (unsigned long)high_hwm)); - return NT_STATUS_UNSUCCESSFUL; - } - - xid->id = hwm; - DEBUG(10,("New %s = %d\n", hwmtype, hwm)); - - return NT_STATUS_OK; -} - -/********************************** - Get current highest id. -**********************************/ - -static NTSTATUS idmap_tdb_get_hwm(struct unixid *xid) -{ - const char *hwmkey; - const char *hwmtype; - uint32_t hwm; - uint32_t high_hwm; - - /* Get current high water mark */ - switch (xid->type) { - - case ID_TYPE_UID: - hwmkey = HWM_USER; - hwmtype = "UID"; - high_hwm = idmap_tdb_state.high_uid; - break; - - case ID_TYPE_GID: - hwmkey = HWM_GROUP; - hwmtype = "GID"; - high_hwm = idmap_tdb_state.high_gid; - break; - - default: - return NT_STATUS_INVALID_PARAMETER; - } - - if ((hwm = tdb_fetch_int32(idmap_alloc_tdb, hwmkey)) == -1) { - return NT_STATUS_INTERNAL_DB_ERROR; - } - - xid->id = hwm; - - /* Warn if it is out of range */ - if (hwm >= high_hwm) { - DEBUG(0, ("Warning: %s range full!! (max: %lu)\n", - hwmtype, (unsigned long)high_hwm)); - } - - return NT_STATUS_OK; -} - -/********************************** - Set high id. -**********************************/ - -static NTSTATUS idmap_tdb_set_hwm(struct unixid *xid) -{ - const char *hwmkey; - const char *hwmtype; - uint32_t hwm; - uint32_t high_hwm; - - /* Get current high water mark */ - switch (xid->type) { - - case ID_TYPE_UID: - hwmkey = HWM_USER; - hwmtype = "UID"; - high_hwm = idmap_tdb_state.high_uid; - break; - - case ID_TYPE_GID: - hwmkey = HWM_GROUP; - hwmtype = "GID"; - high_hwm = idmap_tdb_state.high_gid; - break; - - default: - return NT_STATUS_INVALID_PARAMETER; - } - - hwm = xid->id; - - if ((hwm = tdb_store_int32(idmap_alloc_tdb, hwmkey, hwm)) == -1) { - return NT_STATUS_INTERNAL_DB_ERROR; - } - - /* Warn if it is out of range */ - if (hwm >= high_hwm) { - DEBUG(0, ("Warning: %s range full!! (max: %lu)\n", - hwmtype, (unsigned long)high_hwm)); - } - - return NT_STATUS_OK; -} - -/********************************** - Close the alloc tdb -**********************************/ - -static NTSTATUS idmap_tdb_alloc_close(void) -{ - if (idmap_alloc_tdb) { - if (idmap_tdb_tdb_close(idmap_alloc_tdb) == 0) { - return NT_STATUS_OK; - } else { - return NT_STATUS_UNSUCCESSFUL; - } - } - return NT_STATUS_OK; -} - -/********************************************************************** - IDMAP MAPPING TDB BACKEND -**********************************************************************/ - -struct idmap_tdb_context { - TDB_CONTEXT *tdb; - uint32_t filter_low_id; - uint32_t filter_high_id; -}; - -/***************************** - Initialise idmap database. -*****************************/ - -static NTSTATUS idmap_tdb_db_init(struct idmap_domain *dom) -{ - NTSTATUS ret; - struct idmap_tdb_context *ctx; - char *config_option = NULL; - const char *range; - - ctx = talloc(dom, struct idmap_tdb_context); - if ( ! ctx) { - DEBUG(0, ("Out of memory!\n")); - return NT_STATUS_NO_MEMORY; - } - - config_option = talloc_asprintf(ctx, "idmap config %s", dom->name); - if ( ! config_option) { - DEBUG(0, ("Out of memory!\n")); - ret = NT_STATUS_NO_MEMORY; - goto failed; - } - - ret = idmap_tdb_open_db(ctx, &ctx->tdb); - if ( ! NT_STATUS_IS_OK(ret)) { - goto failed; - } - - range = lp_parm_const_string(-1, config_option, "range", NULL); - if (( ! range) || - (sscanf(range, "%u - %u", &ctx->filter_low_id, &ctx->filter_high_id) != 2) || - (ctx->filter_low_id > ctx->filter_high_id)) { - ctx->filter_low_id = 0; - ctx->filter_high_id = 0; - } - - dom->private_data = ctx; - dom->initialized = True; - - talloc_free(config_option); - return NT_STATUS_OK; - -failed: - talloc_free(ctx); - return ret; -} - -/********************************** - Single id to sid lookup function. -**********************************/ - -static NTSTATUS idmap_tdb_id_to_sid(struct idmap_tdb_context *ctx, struct id_map *map) -{ - NTSTATUS ret; - TDB_DATA data; - char *keystr; - - if (!ctx || !map) { - return NT_STATUS_INVALID_PARAMETER; - } - - /* apply filters before checking */ - if ((ctx->filter_low_id && (map->xid.id < ctx->filter_low_id)) || - (ctx->filter_high_id && (map->xid.id > ctx->filter_high_id))) { - DEBUG(5, ("Requested id (%u) out of range (%u - %u). Filtered!\n", - map->xid.id, ctx->filter_low_id, ctx->filter_high_id)); - return NT_STATUS_NONE_MAPPED; - } - - switch (map->xid.type) { - - case ID_TYPE_UID: - keystr = talloc_asprintf(ctx, "UID %lu", (unsigned long)map->xid.id); - break; - - case ID_TYPE_GID: - keystr = talloc_asprintf(ctx, "GID %lu", (unsigned long)map->xid.id); - break; - - default: - DEBUG(2, ("INVALID unix ID type: 0x02%x\n", map->xid.type)); - return NT_STATUS_INVALID_PARAMETER; - } - - /* final SAFE_FREE safe */ - data.dptr = NULL; - - if (keystr == NULL) { - DEBUG(0, ("Out of memory!\n")); - ret = NT_STATUS_NO_MEMORY; - goto done; - } - - DEBUG(10,("Fetching record %s\n", keystr)); - - /* Check if the mapping exists */ - data = tdb_fetch_bystring(ctx->tdb, keystr); - - if (!data.dptr) { - DEBUG(10,("Record %s not found\n", keystr)); - ret = NT_STATUS_NONE_MAPPED; - goto done; - } - - if (!string_to_sid(map->sid, (const char *)data.dptr)) { - DEBUG(10,("INVALID SID (%s) in record %s\n", - (const char *)data.dptr, keystr)); - ret = NT_STATUS_INTERNAL_DB_ERROR; - goto done; - } - - DEBUG(10,("Found record %s -> %s\n", keystr, (const char *)data.dptr)); - ret = NT_STATUS_OK; - -done: - SAFE_FREE(data.dptr); - talloc_free(keystr); - return ret; -} - -/********************************** - Single sid to id lookup function. -**********************************/ - -static NTSTATUS idmap_tdb_sid_to_id(struct idmap_tdb_context *ctx, struct id_map *map) -{ - NTSTATUS ret; - TDB_DATA data; - char *keystr; - unsigned long rec_id = 0; - - if ((keystr = talloc_asprintf(ctx, "%s", sid_string_static(map->sid))) == NULL) { - DEBUG(0, ("Out of memory!\n")); - ret = NT_STATUS_NO_MEMORY; - goto done; - } - - DEBUG(10,("Fetching record %s\n", keystr)); - - /* Check if sid is present in database */ - data = tdb_fetch_bystring(ctx->tdb, keystr); - if (!data.dptr) { - DEBUG(10,("Record %s not found\n", keystr)); - ret = NT_STATUS_NONE_MAPPED; - goto done; - } - - /* What type of record is this ? */ - if (sscanf((const char *)data.dptr, "UID %lu", &rec_id) == 1) { /* Try a UID record. */ - map->xid.id = rec_id; - map->xid.type = ID_TYPE_UID; - DEBUG(10,("Found uid record %s -> %s \n", keystr, (const char *)data.dptr )); - ret = NT_STATUS_OK; - - } else if (sscanf((const char *)data.dptr, "GID %lu", &rec_id) == 1) { /* Try a GID record. */ - map->xid.id = rec_id; - map->xid.type = ID_TYPE_GID; - DEBUG(10,("Found gid record %s -> %s \n", keystr, (const char *)data.dptr )); - ret = NT_STATUS_OK; - - } else { /* Unknown record type ! */ - DEBUG(2, ("Found INVALID record %s -> %s\n", keystr, (const char *)data.dptr)); - ret = NT_STATUS_INTERNAL_DB_ERROR; - } - - SAFE_FREE(data.dptr); - - /* apply filters before returning result */ - if ((ctx->filter_low_id && (map->xid.id < ctx->filter_low_id)) || - (ctx->filter_high_id && (map->xid.id > ctx->filter_high_id))) { - DEBUG(5, ("Requested id (%u) out of range (%u - %u). Filtered!\n", - map->xid.id, ctx->filter_low_id, ctx->filter_high_id)); - ret = NT_STATUS_NONE_MAPPED; - } - -done: - talloc_free(keystr); - return ret; -} - -/********************************** - lookup a set of unix ids. -**********************************/ - -static NTSTATUS idmap_tdb_unixids_to_sids(struct idmap_domain *dom, struct id_map **ids) -{ - struct idmap_tdb_context *ctx; - NTSTATUS ret; - int i; - - /* make sure we initialized */ - if ( ! dom->initialized) { - ret = idmap_tdb_db_init(dom); - if ( ! NT_STATUS_IS_OK(ret)) { - return ret; - } - } - - ctx = talloc_get_type(dom->private_data, struct idmap_tdb_context); - - for (i = 0; ids[i]; i++) { - ret = idmap_tdb_id_to_sid(ctx, ids[i]); - if ( ! NT_STATUS_IS_OK(ret)) { - - /* if it is just a failed mapping continue */ - if (NT_STATUS_EQUAL(ret, NT_STATUS_NONE_MAPPED)) { - - /* make sure it is marked as unmapped */ - ids[i]->status = ID_UNMAPPED; - continue; - } - - /* some fatal error occurred, return immediately */ - goto done; - } - - /* all ok, id is mapped */ - ids[i]->status = ID_MAPPED; - } - - ret = NT_STATUS_OK; - -done: - return ret; -} - -/********************************** - lookup a set of sids. -**********************************/ - -static NTSTATUS idmap_tdb_sids_to_unixids(struct idmap_domain *dom, struct id_map **ids) -{ - struct idmap_tdb_context *ctx; - NTSTATUS ret; - int i; - - /* make sure we initialized */ - if ( ! dom->initialized) { - ret = idmap_tdb_db_init(dom); - if ( ! NT_STATUS_IS_OK(ret)) { - return ret; - } - } - - ctx = talloc_get_type(dom->private_data, struct idmap_tdb_context); - - for (i = 0; ids[i]; i++) { - ret = idmap_tdb_sid_to_id(ctx, ids[i]); - if ( ! NT_STATUS_IS_OK(ret)) { - - /* if it is just a failed mapping continue */ - if (NT_STATUS_EQUAL(ret, NT_STATUS_NONE_MAPPED)) { - - /* make sure it is marked as unmapped */ - ids[i]->status = ID_UNMAPPED; - continue; - } - - /* some fatal error occurred, return immediately */ - goto done; - } - - /* all ok, id is mapped */ - ids[i]->status = ID_MAPPED; - } - - ret = NT_STATUS_OK; - -done: - return ret; -} - -/********************************** - set a mapping. -**********************************/ - -static NTSTATUS idmap_tdb_set_mapping(struct idmap_domain *dom, const struct id_map *map) -{ - struct idmap_tdb_context *ctx; - NTSTATUS ret; - TDB_DATA ksid, kid, data; - char *ksidstr, *kidstr; - - /* make sure we initialized */ - if ( ! dom->initialized) { - ret = idmap_tdb_db_init(dom); - if ( ! NT_STATUS_IS_OK(ret)) { - return ret; - } - } - - if (!map || !map->sid) { - return NT_STATUS_INVALID_PARAMETER; - } - - ksidstr = kidstr = NULL; - data.dptr = NULL; - - /* TODO: should we filter a set_mapping using low/high filters ? */ - - ctx = talloc_get_type(dom->private_data, struct idmap_tdb_context); - - switch (map->xid.type) { - - case ID_TYPE_UID: - kidstr = talloc_asprintf(ctx, "UID %lu", (unsigned long)map->xid.id); - break; - - case ID_TYPE_GID: - kidstr = talloc_asprintf(ctx, "GID %lu", (unsigned long)map->xid.id); - break; - - default: - DEBUG(2, ("INVALID unix ID type: 0x02%x\n", map->xid.type)); - return NT_STATUS_INVALID_PARAMETER; - } - - if (kidstr == NULL) { - DEBUG(0, ("ERROR: Out of memory!\n")); - ret = NT_STATUS_NO_MEMORY; - goto done; - } - - if ((ksidstr = talloc_asprintf(ctx, "%s", sid_string_static(map->sid))) == NULL) { - DEBUG(0, ("Out of memory!\n")); - ret = NT_STATUS_NO_MEMORY; - goto done; - } - - DEBUG(10, ("Storing %s <-> %s map\n", ksidstr, kidstr)); - kid = string_term_tdb_data(kidstr); - ksid = string_term_tdb_data(ksidstr); - - /* *DELETE* previous mappings if any. - * This is done both SID and [U|G]ID passed in */ - - /* Lock the record for this SID. */ - if (tdb_chainlock(ctx->tdb, ksid) != 0) { - DEBUG(10,("Failed to lock record %s. Error %s\n", - ksidstr, tdb_errorstr(ctx->tdb) )); - return NT_STATUS_UNSUCCESSFUL; - } - - data = tdb_fetch(ctx->tdb, ksid); - if (data.dptr) { - DEBUG(10, ("Deleting existing mapping %s <-> %s\n", (const char *)data.dptr, ksidstr )); - tdb_delete(ctx->tdb, data); - tdb_delete(ctx->tdb, ksid); - SAFE_FREE(data.dptr); - } - - data = tdb_fetch(ctx->tdb, kid); - if (data.dptr) { - DEBUG(10,("Deleting existing mapping %s <-> %s\n", (const char *)data.dptr, kidstr )); - tdb_delete(ctx->tdb, data); - tdb_delete(ctx->tdb, kid); - SAFE_FREE(data.dptr); - } - - if (tdb_store(ctx->tdb, ksid, kid, TDB_INSERT) == -1) { - DEBUG(0, ("Error storing SID -> ID: %s\n", tdb_errorstr(ctx->tdb))); - tdb_chainunlock(ctx->tdb, ksid); - ret = NT_STATUS_UNSUCCESSFUL; - goto done; - } - if (tdb_store(ctx->tdb, kid, ksid, TDB_INSERT) == -1) { - DEBUG(0, ("Error stroing ID -> SID: %s\n", tdb_errorstr(ctx->tdb))); - /* try to remove the previous stored SID -> ID map */ - tdb_delete(ctx->tdb, ksid); - tdb_chainunlock(ctx->tdb, ksid); - ret = NT_STATUS_UNSUCCESSFUL; - goto done; - } - - tdb_chainunlock(ctx->tdb, ksid); - DEBUG(10,("Stored %s <-> %s\n", ksidstr, kidstr)); - ret = NT_STATUS_OK; - -done: - talloc_free(ksidstr); - talloc_free(kidstr); - SAFE_FREE(data.dptr); - return ret; -} - -/********************************** - remove a mapping. -**********************************/ - -static NTSTATUS idmap_tdb_remove_mapping(struct idmap_domain *dom, const struct id_map *map) -{ - struct idmap_tdb_context *ctx; - NTSTATUS ret; - TDB_DATA ksid, kid, data; - char *ksidstr, *kidstr; - - /* make sure we initialized */ - if ( ! dom->initialized) { - ret = idmap_tdb_db_init(dom); - if ( ! NT_STATUS_IS_OK(ret)) { - return ret; - } - } - - if (!map || !map->sid) { - return NT_STATUS_INVALID_PARAMETER; - } - - ksidstr = kidstr = NULL; - data.dptr = NULL; - - /* TODO: should we filter a remove_mapping using low/high filters ? */ - - ctx = talloc_get_type(dom->private_data, struct idmap_tdb_context); - - switch (map->xid.type) { - - case ID_TYPE_UID: - kidstr = talloc_asprintf(ctx, "UID %lu", (unsigned long)map->xid.id); - break; - - case ID_TYPE_GID: - kidstr = talloc_asprintf(ctx, "GID %lu", (unsigned long)map->xid.id); - break; - - default: - DEBUG(2, ("INVALID unix ID type: 0x02%x\n", map->xid.type)); - return NT_STATUS_INVALID_PARAMETER; - } - - if (kidstr == NULL) { - DEBUG(0, ("ERROR: Out of memory!\n")); - ret = NT_STATUS_NO_MEMORY; - goto done; - } - - if ((ksidstr = talloc_asprintf(ctx, "%s", sid_string_static(map->sid))) == NULL) { - DEBUG(0, ("Out of memory!\n")); - ret = NT_STATUS_NO_MEMORY; - goto done; - } - - DEBUG(10, ("Checking %s <-> %s map\n", ksidstr, kidstr)); - ksid = string_term_tdb_data(ksidstr); - kid = string_term_tdb_data(kidstr); - - /* Lock the record for this SID. */ - if (tdb_chainlock(ctx->tdb, ksid) != 0) { - DEBUG(10,("Failed to lock record %s. Error %s\n", - ksidstr, tdb_errorstr(ctx->tdb) )); - return NT_STATUS_UNSUCCESSFUL; - } - - /* Check if sid is present in database */ - data = tdb_fetch(ctx->tdb, ksid); - if (!data.dptr) { - DEBUG(10,("Record %s not found\n", ksidstr)); - tdb_chainunlock(ctx->tdb, ksid); - ret = NT_STATUS_NONE_MAPPED; - goto done; - } - - /* Check if sid is mapped to the specified ID */ - if ((data.dsize != kid.dsize) || - (memcmp(data.dptr, kid.dptr, data.dsize) != 0)) { - DEBUG(10,("Specified SID does not map to specified ID\n")); - DEBUGADD(10,("Actual mapping is %s -> %s\n", ksidstr, (const char *)data.dptr)); - tdb_chainunlock(ctx->tdb, ksid); - ret = NT_STATUS_NONE_MAPPED; - goto done; - } - - DEBUG(10, ("Removing %s <-> %s map\n", ksidstr, kidstr)); - - /* Delete previous mappings. */ - - DEBUG(10, ("Deleting existing mapping %s -> %s\n", ksidstr, kidstr )); - tdb_delete(ctx->tdb, ksid); - - DEBUG(10,("Deleting existing mapping %s -> %s\n", kidstr, ksidstr )); - tdb_delete(ctx->tdb, kid); - - tdb_chainunlock(ctx->tdb, ksid); - ret = NT_STATUS_OK; - -done: - talloc_free(ksidstr); - talloc_free(kidstr); - SAFE_FREE(data.dptr); - return ret; -} - -/********************************** - Close the idmap tdb instance -**********************************/ - -static NTSTATUS idmap_tdb_close(struct idmap_domain *dom) -{ - struct idmap_tdb_context *ctx; - - if (dom->private_data) { - ctx = talloc_get_type(dom->private_data, struct idmap_tdb_context); - - if (idmap_tdb_tdb_close(ctx->tdb) == 0) { - return NT_STATUS_OK; - } else { - return NT_STATUS_UNSUCCESSFUL; - } - } - return NT_STATUS_OK; -} - -struct dump_data { - TALLOC_CTX *memctx; - struct id_map **maps; - int *num_maps; - NTSTATUS ret; -}; - -static int idmap_tdb_dump_one_entry(TDB_CONTEXT *tdb, TDB_DATA key, TDB_DATA value, void *pdata) -{ - struct dump_data *data = talloc_get_type(pdata, struct dump_data); - struct id_map *maps; - int num_maps = *data->num_maps; - - /* ignore any record but the ones with a SID as key */ - if (strncmp((const char *)key.dptr, "S-", 2) == 0) { - - maps = talloc_realloc(NULL, *data->maps, struct id_map, num_maps+1); - if ( ! maps) { - DEBUG(0, ("Out of memory!\n")); - data->ret = NT_STATUS_NO_MEMORY; - return -1; - } - *data->maps = maps; - maps[num_maps].sid = talloc(maps, DOM_SID); - if ( ! maps[num_maps].sid) { - DEBUG(0, ("Out of memory!\n")); - data->ret = NT_STATUS_NO_MEMORY; - return -1; - } - - if (!string_to_sid(maps[num_maps].sid, (const char *)key.dptr)) { - DEBUG(10,("INVALID record %s\n", (const char *)key.dptr)); - /* continue even with errors */ - return 0; - } - - /* Try a UID record. */ - if (sscanf((const char *)value.dptr, "UID %u", &(maps[num_maps].xid.id)) == 1) { - maps[num_maps].xid.type = ID_TYPE_UID; - maps[num_maps].status = ID_MAPPED; - *data->num_maps = num_maps + 1; - - /* Try a GID record. */ - } else - if (sscanf((const char *)value.dptr, "GID %u", &(maps[num_maps].xid.id)) == 1) { - maps[num_maps].xid.type = ID_TYPE_GID; - maps[num_maps].status = ID_MAPPED; - *data->num_maps = num_maps + 1; - - /* Unknown record type ! */ - } else { - maps[num_maps].status = ID_UNKNOWN; - DEBUG(2, ("Found INVALID record %s -> %s\n", - (const char *)key.dptr, (const char *)value.dptr)); - /* do not increment num_maps */ - } - } - - return 0; -} - -/********************************** - Dump all mappings out -**********************************/ - -static NTSTATUS idmap_tdb_dump_data(struct idmap_domain *dom, struct id_map **maps, int *num_maps) -{ - struct idmap_tdb_context *ctx; - struct dump_data *data; - NTSTATUS ret = NT_STATUS_OK; - - /* make sure we initialized */ - if ( ! dom->initialized) { - ret = idmap_tdb_db_init(dom); - if ( ! NT_STATUS_IS_OK(ret)) { - return ret; - } - } - - ctx = talloc_get_type(dom->private_data, struct idmap_tdb_context); - - data = TALLOC_ZERO_P(ctx, struct dump_data); - if ( ! data) { - DEBUG(0, ("Out of memory!\n")); - return NT_STATUS_NO_MEMORY; - } - data->maps = maps; - data->num_maps = num_maps; - data->ret = NT_STATUS_OK; - - tdb_traverse(ctx->tdb, idmap_tdb_dump_one_entry, data); - - if ( ! NT_STATUS_IS_OK(data->ret)) { - ret = data->ret; - } - - talloc_free(data); - return ret; -} - -static struct idmap_methods db_methods = { - - .init = idmap_tdb_db_init, - .unixids_to_sids = idmap_tdb_unixids_to_sids, - .sids_to_unixids = idmap_tdb_sids_to_unixids, - .set_mapping = idmap_tdb_set_mapping, - .remove_mapping = idmap_tdb_remove_mapping, - .dump_data = idmap_tdb_dump_data, - .close_fn = idmap_tdb_close -}; - -static struct idmap_alloc_methods db_alloc_methods = { - - .init = idmap_tdb_alloc_init, - .allocate_id = idmap_tdb_allocate_id, - .get_id_hwm = idmap_tdb_get_hwm, - .set_id_hwm = idmap_tdb_set_hwm, - .close_fn = idmap_tdb_alloc_close -}; - -NTSTATUS idmap_alloc_tdb_init(void) -{ - return smb_register_idmap_alloc(SMB_IDMAP_INTERFACE_VERSION, "tdb", &db_alloc_methods); -} - -NTSTATUS idmap_tdb_init(void) -{ - NTSTATUS ret; - - /* FIXME: bad hack to actually register also the alloc_tdb module without changining configure.in */ - ret = idmap_alloc_tdb_init(); - if (! NT_STATUS_IS_OK(ret)) { - return ret; - } - return smb_register_idmap(SMB_IDMAP_INTERFACE_VERSION, "tdb", &db_methods); -} diff --git a/source3/nsswitch/idmap_util.c b/source3/nsswitch/idmap_util.c deleted file mode 100644 index 077c599739..0000000000 --- a/source3/nsswitch/idmap_util.c +++ /dev/null @@ -1,171 +0,0 @@ -/* - Unix SMB/CIFS implementation. - ID Mapping - Copyright (C) Simo Sorce 2003 - Copyright (C) Jeremy Allison 2006 - - 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 3 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, see .*/ - -#include "includes.h" - -#undef DBGC_CLASS -#define DBGC_CLASS DBGC_IDMAP - -/***************************************************************** - Returns the SID mapped to the given UID. - If mapping is not possible returns an error. -*****************************************************************/ - -NTSTATUS idmap_uid_to_sid(DOM_SID *sid, uid_t uid) -{ - NTSTATUS ret; - struct id_map map; - struct id_map *maps[2]; - - DEBUG(10,("uid = [%lu]\n", (unsigned long)uid)); - - map.sid = sid; - map.xid.type = ID_TYPE_UID; - map.xid.id = uid; - - maps[0] = ↦ - maps[1] = NULL; - - ret = idmap_unixids_to_sids(maps); - if ( ! NT_STATUS_IS_OK(ret)) { - DEBUG(10, ("error mapping uid [%lu]\n", (unsigned long)uid)); - return ret; - } - - if (map.status != ID_MAPPED) { - DEBUG(10, ("uid [%lu] not mapped\n", (unsigned long)uid)); - return NT_STATUS_NONE_MAPPED; - } - - return NT_STATUS_OK; -} - -/***************************************************************** - Returns SID mapped to the given GID. - If mapping is not possible returns an error. -*****************************************************************/ - -NTSTATUS idmap_gid_to_sid(DOM_SID *sid, gid_t gid) -{ - NTSTATUS ret; - struct id_map map; - struct id_map *maps[2]; - - DEBUG(10,("gid = [%lu]\n", (unsigned long)gid)); - - map.sid = sid; - map.xid.type = ID_TYPE_GID; - map.xid.id = gid; - - maps[0] = ↦ - maps[1] = NULL; - - ret = idmap_unixids_to_sids(maps); - if ( ! NT_STATUS_IS_OK(ret)) { - DEBUG(10, ("error mapping gid [%lu]\n", (unsigned long)gid)); - return ret; - } - - if (map.status != ID_MAPPED) { - DEBUG(10, ("gid [%lu] not mapped\n", (unsigned long)gid)); - return NT_STATUS_NONE_MAPPED; - } - - return NT_STATUS_OK; -} - -/***************************************************************** - Returns the UID mapped to the given SID. - If mapping is not possible or SID maps to a GID returns an error. -*****************************************************************/ - -NTSTATUS idmap_sid_to_uid(DOM_SID *sid, uid_t *uid) -{ - NTSTATUS ret; - struct id_map map; - struct id_map *maps[2]; - - DEBUG(10,("idmap_sid_to_uid: sid = [%s]\n", sid_string_static(sid))); - - map.sid = sid; - map.xid.type = ID_TYPE_UID; - - maps[0] = ↦ - maps[1] = NULL; - - ret = idmap_sids_to_unixids(maps); - if ( ! NT_STATUS_IS_OK(ret)) { - DEBUG(10, ("error mapping sid [%s] to uid\n", - sid_string_static(sid))); - return ret; - } - - if ((map.status != ID_MAPPED) || (map.xid.type != ID_TYPE_UID)) { - DEBUG(10, ("sid [%s] not mapped to an uid [%u,%u,%u]\n", - sid_string_static(sid), - map.status, - map.xid.type, - map.xid.id)); - return NT_STATUS_NONE_MAPPED; - } - - *uid = map.xid.id; - - return NT_STATUS_OK; -} - -/***************************************************************** - Returns the GID mapped to the given SID. - If mapping is not possible or SID maps to a UID returns an error. -*****************************************************************/ - -NTSTATUS idmap_sid_to_gid(DOM_SID *sid, gid_t *gid) -{ - NTSTATUS ret; - struct id_map map; - struct id_map *maps[2]; - - DEBUG(10,("idmap_sid_to_gid: sid = [%s]\n", sid_string_static(sid))); - - map.sid = sid; - map.xid.type = ID_TYPE_GID; - - maps[0] = ↦ - maps[1] = NULL; - - ret = idmap_sids_to_unixids(maps); - if ( ! NT_STATUS_IS_OK(ret)) { - DEBUG(10, ("error mapping sid [%s] to gid\n", - sid_string_static(sid))); - return ret; - } - - if ((map.status != ID_MAPPED) || (map.xid.type != ID_TYPE_GID)) { - DEBUG(10, ("sid [%s] not mapped to a gid [%u,%u,%u]\n", - sid_string_static(sid), - map.status, - map.xid.type, - map.xid.id)); - return NT_STATUS_NONE_MAPPED; - } - - *gid = map.xid.id; - - return NT_STATUS_OK; -} diff --git a/source3/nsswitch/nss_info.c b/source3/nsswitch/nss_info.c deleted file mode 100644 index ea51e9dde8..0000000000 --- a/source3/nsswitch/nss_info.c +++ /dev/null @@ -1,301 +0,0 @@ -/* - Unix SMB/CIFS implementation. - Idmap NSS headers - - Copyright (C) Gerald Carter 2006 - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 3 of the License, or (at your option) any later version. - - This library 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 - Library General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with this program. If not, see . -*/ - -#include "includes.h" -#include "nss_info.h" - -static struct nss_function_entry *backends = NULL; -static struct nss_domain_entry *nss_domain_list = NULL; - -/********************************************************************** - Get idmap nss methods. -**********************************************************************/ - -static struct nss_function_entry *nss_get_backend(const char *name ) -{ - struct nss_function_entry *entry = backends; - - for(entry = backends; entry; entry = entry->next) { - if ( strequal(entry->name, name) ) - return entry; - } - - return NULL; -} - -/********************************************************************* - Allow a module to register itself as a backend. -**********************************************************************/ - - NTSTATUS smb_register_idmap_nss(int version, const char *name, struct nss_info_methods *methods) -{ - struct nss_function_entry *entry; - - if ((version != SMB_NSS_INFO_INTERFACE_VERSION)) { - DEBUG(0, ("smb_register_idmap_nss: Failed to register idmap_nss module.\n" - "The module was compiled against SMB_NSS_INFO_INTERFACE_VERSION %d,\n" - "current SMB_NSS_INFO_INTERFACE_VERSION is %d.\n" - "Please recompile against the current version of samba!\n", - version, SMB_NSS_INFO_INTERFACE_VERSION)); - return NT_STATUS_OBJECT_TYPE_MISMATCH; - } - - if (!name || !name[0] || !methods) { - DEBUG(0,("smb_register_idmap_nss: called with NULL pointer or empty name!\n")); - return NT_STATUS_INVALID_PARAMETER; - } - - if ( nss_get_backend(name) ) { - DEBUG(0,("smb_register_idmap_nss: idmap module %s " - "already registered!\n", name)); - return NT_STATUS_OBJECT_NAME_COLLISION; - } - - entry = SMB_XMALLOC_P(struct nss_function_entry); - entry->name = smb_xstrdup(name); - entry->methods = methods; - - DLIST_ADD(backends, entry); - DEBUG(5, ("smb_register_idmap_nss: Successfully added idmap " - "nss backend '%s'\n", name)); - - return NT_STATUS_OK; -} - -/******************************************************************** - *******************************************************************/ - -static BOOL parse_nss_parm( const char *config, char **backend, char **domain ) -{ - char *p; - char *q; - int len; - - *backend = *domain = NULL; - - if ( !config ) - return False; - - p = strchr( config, ':' ); - - /* if no : then the string must be the backend name only */ - - if ( !p ) { - *backend = SMB_STRDUP( config ); - return (*backend != NULL); - } - - /* split the string and return the two parts */ - - if ( strlen(p+1) > 0 ) { - *domain = SMB_STRDUP( p+1 ); - } - - len = PTR_DIFF(p,config)+1; - if ( (q = SMB_MALLOC_ARRAY( char, len )) == NULL ) { - SAFE_FREE( *backend ); - return False; - } - - StrnCpy( q, config, len-1); - q[len-1] = '\0'; - *backend = q; - - return True; -} - -/******************************************************************** - Each nss backend must not store global state, but rather be able - to initialize the state on a per domain basis. - *******************************************************************/ - - NTSTATUS nss_init( const char **nss_list ) -{ - NTSTATUS status; - static NTSTATUS nss_initialized = NT_STATUS_UNSUCCESSFUL; - int i; - char *backend, *domain; - struct nss_function_entry *nss_backend; - struct nss_domain_entry *nss_domain; - - /* check for previous successful initializations */ - - if ( NT_STATUS_IS_OK(nss_initialized) ) - return NT_STATUS_OK; - - /* The "template" backend should alqays be registered as it - is a static module */ - - if ( (nss_backend = nss_get_backend( "template" )) == NULL ) { - static_init_nss_info; - } - - /* Create the list of nss_domains (loading any shared plugins - as necessary) */ - - for ( i=0; nss_list && nss_list[i]; i++ ) { - - if ( !parse_nss_parm(nss_list[i], &backend, &domain) ) { - DEBUG(0,("nss_init: failed to parse \"%s\"!\n", - nss_list[i])); - continue; - } - - /* validate the backend */ - - if ( (nss_backend = nss_get_backend( backend )) == NULL ) { - /* attempt to register the backend */ - status = smb_probe_module( "nss_info", backend ); - if ( !NT_STATUS_IS_OK(status) ) { - continue; - } - - /* try again */ - if ( (nss_backend = nss_get_backend( backend )) == NULL ) { - DEBUG(0,("nss_init: unregistered backend %s!. Skipping\n", - backend)); - continue; - } - - } - - /* fill in the nss_domain_entry and add it to the - list of domains */ - - nss_domain = TALLOC_ZERO_P( nss_domain_list, struct nss_domain_entry ); - if ( !nss_domain ) { - DEBUG(0,("nss_init: talloc() failure!\n")); - return NT_STATUS_NO_MEMORY; - } - - nss_domain->backend = nss_backend; - nss_domain->domain = talloc_strdup( nss_domain, domain ); - - /* Try to init and ave the result */ - - nss_domain->init_status = nss_domain->backend->methods->init( nss_domain ); - DLIST_ADD( nss_domain_list, nss_domain ); - if ( !NT_STATUS_IS_OK(nss_domain->init_status) ) { - DEBUG(0,("nss_init: Failed to init backend for %s domain!\n", - nss_domain->domain)); - } - - /* cleanup */ - - SAFE_FREE( backend ); - SAFE_FREE( domain ); - } - - if ( !nss_domain_list ) { - DEBUG(3,("nss_init: no nss backends configured. " - "Defaulting to \"template\".\n")); - - - /* we shouild default to use template here */ - } - - - nss_initialized = NT_STATUS_OK; - - return NT_STATUS_OK; -} - -/******************************************************************** - *******************************************************************/ - -static struct nss_domain_entry *find_nss_domain( const char *domain ) -{ - NTSTATUS status; - struct nss_domain_entry *p; - - status = nss_init( lp_winbind_nss_info() ); - if ( !NT_STATUS_IS_OK(status) ) { - DEBUG(4,("nss_get_info: Failed to init nss_info API (%s)!\n", - nt_errstr(status))); - return NULL; - } - - for ( p=nss_domain_list; p; p=p->next ) { - if ( strequal( p->domain, domain ) ) - break; - } - - /* If we didn't find a match, then use the default nss info */ - - if ( !p ) { - if ( !nss_domain_list ) { - return NULL; - } - - p = nss_domain_list; - } - - if ( !NT_STATUS_IS_OK( p->init_status ) ) { - p->init_status = p->backend->methods->init( p ); - } - - return p; -} - -/******************************************************************** - *******************************************************************/ - - NTSTATUS nss_get_info( const char *domain, const DOM_SID *user_sid, - TALLOC_CTX *ctx, - ADS_STRUCT *ads, LDAPMessage *msg, - char **homedir, char **shell, char **gecos, - gid_t *p_gid) -{ - struct nss_domain_entry *p; - struct nss_info_methods *m; - - if ( (p = find_nss_domain( domain )) == NULL ) { - DEBUG(4,("nss_get_info: Failed to find nss domain pointer for %s\n", - domain )); - return NT_STATUS_NOT_FOUND; - } - - m = p->backend->methods; - - return m->get_nss_info( p, user_sid, ctx, ads, msg, - homedir, shell, gecos, p_gid ); -} - -/******************************************************************** - *******************************************************************/ - - NTSTATUS nss_close( const char *parameters ) -{ - struct nss_domain_entry *p = nss_domain_list; - struct nss_domain_entry *q; - - while ( p && p->backend && p->backend->methods ) { - /* close the backend */ - p->backend->methods->close_fn(); - - /* free the memory */ - q = p; - p = p->next; - TALLOC_FREE( q ); - } - - return NT_STATUS_OK; -} - diff --git a/source3/nsswitch/nss_info_template.c b/source3/nsswitch/nss_info_template.c deleted file mode 100644 index aaf02e4abe..0000000000 --- a/source3/nsswitch/nss_info_template.c +++ /dev/null @@ -1,83 +0,0 @@ -/* - Unix SMB/CIFS implementation. - idMap nss template plugin - - Copyright (C) Gerald Carter 2006 - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 3 of the License, or (at your option) any later version. - - This library 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 - Library General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with this program. If not, see . -*/ - -#include "includes.h" -#include "nss_info.h" - -/************************************************************************ - ***********************************************************************/ - -static NTSTATUS nss_template_init( struct nss_domain_entry *e ) -{ - return NT_STATUS_OK; -} - -/************************************************************************ - ***********************************************************************/ - -static NTSTATUS nss_template_get_info( struct nss_domain_entry *e, - const DOM_SID *sid, - TALLOC_CTX *ctx, - ADS_STRUCT *ads, - LDAPMessage *msg, - char **homedir, - char **shell, - char **gecos, - gid_t *gid ) -{ - if ( !homedir || !shell || !gecos ) - return NT_STATUS_INVALID_PARAMETER; - - *homedir = talloc_strdup( ctx, lp_template_homedir() ); - *shell = talloc_strdup( ctx, lp_template_shell() ); - *gecos = NULL; - - if ( !*homedir || !*shell ) { - return NT_STATUS_NO_MEMORY; - } - - return NT_STATUS_OK; -} - -/************************************************************************ - ***********************************************************************/ - -static NTSTATUS nss_template_close( void ) -{ - return NT_STATUS_OK; -} - - -/************************************************************************ - ***********************************************************************/ - -static struct nss_info_methods nss_template_methods = { - .init = nss_template_init, - .get_nss_info = nss_template_get_info, - .close_fn = nss_template_close -}; - -NTSTATUS nss_info_template_init( void ) -{ - return smb_register_idmap_nss(SMB_NSS_INFO_INTERFACE_VERSION, - "template", - &nss_template_methods); -} - diff --git a/source3/nsswitch/winbindd.c b/source3/nsswitch/winbindd.c deleted file mode 100644 index 17915fb01b..0000000000 --- a/source3/nsswitch/winbindd.c +++ /dev/null @@ -1,1254 +0,0 @@ -/* - Unix SMB/CIFS implementation. - - Winbind daemon for ntdom nss module - - Copyright (C) by Tim Potter 2000-2002 - Copyright (C) Andrew Tridgell 2002 - Copyright (C) Jelmer Vernooij 2003 - Copyright (C) Volker Lendecke 2004 - Copyright (C) James Peach 2007 - - 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 3 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, see . -*/ - -#include "includes.h" -#include "winbindd.h" - -#undef DBGC_CLASS -#define DBGC_CLASS DBGC_WINBIND - -BOOL opt_nocache = False; - -extern BOOL override_logfile; - -struct event_context *winbind_event_context(void) -{ - static struct event_context *ctx; - - if (!ctx && !(ctx = event_context_init(NULL))) { - smb_panic("Could not init winbind event context"); - } - return ctx; -} - -struct messaging_context *winbind_messaging_context(void) -{ - static struct messaging_context *ctx; - - if (!ctx && !(ctx = messaging_init(NULL, server_id_self(), - winbind_event_context()))) { - smb_panic("Could not init winbind messaging context"); - } - return ctx; -} - -/* Reload configuration */ - -static BOOL reload_services_file(void) -{ - BOOL ret; - - if (lp_loaded()) { - pstring fname; - - pstrcpy(fname,lp_configfile()); - if (file_exist(fname,NULL) && !strcsequal(fname,dyn_CONFIGFILE)) { - pstrcpy(dyn_CONFIGFILE,fname); - } - } - - reopen_logs(); - ret = lp_load(dyn_CONFIGFILE,False,False,True,True); - - reopen_logs(); - load_interfaces(); - - return(ret); -} - - -/**************************************************************************** ** - Handle a fault.. - **************************************************************************** */ - -static void fault_quit(void) -{ - dump_core(); -} - -static void winbindd_status(void) -{ - struct winbindd_cli_state *tmp; - - DEBUG(0, ("winbindd status:\n")); - - /* Print client state information */ - - DEBUG(0, ("\t%d clients currently active\n", winbindd_num_clients())); - - if (DEBUGLEVEL >= 2 && winbindd_num_clients()) { - DEBUG(2, ("\tclient list:\n")); - for(tmp = winbindd_client_list(); tmp; tmp = tmp->next) { - DEBUGADD(2, ("\t\tpid %lu, sock %d\n", - (unsigned long)tmp->pid, tmp->sock)); - } - } -} - -/* Print winbindd status to log file */ - -static void print_winbindd_status(void) -{ - winbindd_status(); -} - -/* Flush client cache */ - -static void flush_caches(void) -{ - /* We need to invalidate cached user list entries on a SIGHUP - otherwise cached access denied errors due to restrict anonymous - hang around until the sequence number changes. */ - - wcache_invalidate_cache(); -} - -/* Handle the signal by unlinking socket and exiting */ - -static void terminate(void) -{ - - winbindd_release_sockets(); - idmap_close(); - - trustdom_cache_shutdown(); - -#if 0 - if (interactive) { - TALLOC_CTX *mem_ctx = talloc_init("end_description"); - char *description = talloc_describe_all(mem_ctx); - - DEBUG(3, ("tallocs left:\n%s\n", description)); - talloc_destroy(mem_ctx); - } -#endif - - exit(0); -} - -static BOOL do_sigterm; - -static void termination_handler(int signum) -{ - do_sigterm = True; - sys_select_signal(signum); -} - -static BOOL do_sigusr2; - -static void sigusr2_handler(int signum) -{ - do_sigusr2 = True; - sys_select_signal(SIGUSR2); -} - -static BOOL do_sighup; - -static void sighup_handler(int signum) -{ - do_sighup = True; - sys_select_signal(SIGHUP); -} - -static BOOL do_sigchld; - -static void sigchld_handler(int signum) -{ - do_sigchld = True; - sys_select_signal(SIGCHLD); -} - -/* React on 'smbcontrol winbindd reload-config' in the same way as on SIGHUP*/ -static void msg_reload_services(struct messaging_context *msg, - void *private_data, - uint32_t msg_type, - struct server_id server_id, - DATA_BLOB *data) -{ - /* Flush various caches */ - flush_caches(); - reload_services_file(); -} - -/* React on 'smbcontrol winbindd shutdown' in the same way as on SIGTERM*/ -static void msg_shutdown(struct messaging_context *msg, - void *private_data, - uint32_t msg_type, - struct server_id server_id, - DATA_BLOB *data) -{ - do_sigterm = True; -} - - -static void winbind_msg_validate_cache(struct messaging_context *msg_ctx, - void *private_data, - uint32_t msg_type, - struct server_id server_id, - DATA_BLOB *data) -{ - uint8 ret; - pid_t child_pid; - struct sigaction act; - struct sigaction oldact; - - DEBUG(10, ("winbindd_msg_validate_cache: got validate-cache " - "message.\n")); - - /* - * call the validation code from a child: - * so we don't block the main winbindd and the validation - * code can safely use fork/waitpid... - */ - CatchChild(); - child_pid = sys_fork(); - - if (child_pid == -1) { - DEBUG(1, ("winbind_msg_validate_cache: Could not fork: %s\n", - strerror(errno))); - return; - } - - if (child_pid != 0) { - /* parent */ - DEBUG(5, ("winbind_msg_validate_cache: child created with " - "pid %d.\n", child_pid)); - return; - } - - /* child */ - - /* install default SIGCHLD handler: validation code uses fork/waitpid */ - ZERO_STRUCT(act); - act.sa_handler = SIG_DFL; -#ifdef SA_RESTART - /* We *want* SIGALRM to interrupt a system call. */ - act.sa_flags = SA_RESTART; -#endif - sigemptyset(&act.sa_mask); - sigaddset(&act.sa_mask,SIGCHLD); - sigaction(SIGCHLD,&act,&oldact); - - ret = (uint8)winbindd_validate_cache_nobackup(); - DEBUG(10, ("winbindd_msg_validata_cache: got return value %d\n", ret)); - messaging_send_buf(msg_ctx, server_id, MSG_WINBIND_VALIDATE_CACHE, &ret, - (size_t)1); - _exit(0); -} - -static struct winbindd_dispatch_table { - enum winbindd_cmd cmd; - void (*fn)(struct winbindd_cli_state *state); - const char *winbindd_cmd_name; -} dispatch_table[] = { - - /* User functions */ - - { WINBINDD_GETPWNAM, winbindd_getpwnam, "GETPWNAM" }, - { WINBINDD_GETPWUID, winbindd_getpwuid, "GETPWUID" }, - - { WINBINDD_SETPWENT, winbindd_setpwent, "SETPWENT" }, - { WINBINDD_ENDPWENT, winbindd_endpwent, "ENDPWENT" }, - { WINBINDD_GETPWENT, winbindd_getpwent, "GETPWENT" }, - - { WINBINDD_GETGROUPS, winbindd_getgroups, "GETGROUPS" }, - { WINBINDD_GETUSERSIDS, winbindd_getusersids, "GETUSERSIDS" }, - { WINBINDD_GETUSERDOMGROUPS, winbindd_getuserdomgroups, - "GETUSERDOMGROUPS" }, - - /* Group functions */ - - { WINBINDD_GETGRNAM, winbindd_getgrnam, "GETGRNAM" }, - { WINBINDD_GETGRGID, winbindd_getgrgid, "GETGRGID" }, - { WINBINDD_SETGRENT, winbindd_setgrent, "SETGRENT" }, - { WINBINDD_ENDGRENT, winbindd_endgrent, "ENDGRENT" }, - { WINBINDD_GETGRENT, winbindd_getgrent, "GETGRENT" }, - { WINBINDD_GETGRLST, winbindd_getgrent, "GETGRLST" }, - - /* PAM auth functions */ - - { WINBINDD_PAM_AUTH, winbindd_pam_auth, "PAM_AUTH" }, - { WINBINDD_PAM_AUTH_CRAP, winbindd_pam_auth_crap, "AUTH_CRAP" }, - { WINBINDD_PAM_CHAUTHTOK, winbindd_pam_chauthtok, "CHAUTHTOK" }, - { WINBINDD_PAM_LOGOFF, winbindd_pam_logoff, "PAM_LOGOFF" }, - { WINBINDD_PAM_CHNG_PSWD_AUTH_CRAP, winbindd_pam_chng_pswd_auth_crap, "CHNG_PSWD_AUTH_CRAP" }, - - /* Enumeration functions */ - - { WINBINDD_LIST_USERS, winbindd_list_users, "LIST_USERS" }, - { WINBINDD_LIST_GROUPS, winbindd_list_groups, "LIST_GROUPS" }, - { WINBINDD_LIST_TRUSTDOM, winbindd_list_trusted_domains, - "LIST_TRUSTDOM" }, - { WINBINDD_SHOW_SEQUENCE, winbindd_show_sequence, "SHOW_SEQUENCE" }, - - /* SID related functions */ - - { WINBINDD_LOOKUPSID, winbindd_lookupsid, "LOOKUPSID" }, - { WINBINDD_LOOKUPNAME, winbindd_lookupname, "LOOKUPNAME" }, - { WINBINDD_LOOKUPRIDS, winbindd_lookuprids, "LOOKUPRIDS" }, - - /* Lookup related functions */ - - { WINBINDD_SID_TO_UID, winbindd_sid_to_uid, "SID_TO_UID" }, - { WINBINDD_SID_TO_GID, winbindd_sid_to_gid, "SID_TO_GID" }, - { WINBINDD_UID_TO_SID, winbindd_uid_to_sid, "UID_TO_SID" }, - { WINBINDD_GID_TO_SID, winbindd_gid_to_sid, "GID_TO_SID" }, -#if 0 /* DISABLED until we fix the interface in Samba 3.0.26 --jerry */ - { WINBINDD_SIDS_TO_XIDS, winbindd_sids_to_unixids, "SIDS_TO_XIDS" }, -#endif /* end DISABLED */ - { WINBINDD_ALLOCATE_UID, winbindd_allocate_uid, "ALLOCATE_UID" }, - { WINBINDD_ALLOCATE_GID, winbindd_allocate_gid, "ALLOCATE_GID" }, - { WINBINDD_SET_MAPPING, winbindd_set_mapping, "SET_MAPPING" }, - { WINBINDD_SET_HWM, winbindd_set_hwm, "SET_HWMS" }, - - /* Miscellaneous */ - - { WINBINDD_DUMP_MAPS, winbindd_dump_maps, "DUMP_MAPS" }, - - { WINBINDD_CHECK_MACHACC, winbindd_check_machine_acct, "CHECK_MACHACC" }, - { WINBINDD_PING, winbindd_ping, "PING" }, - { WINBINDD_INFO, winbindd_info, "INFO" }, - { WINBINDD_INTERFACE_VERSION, winbindd_interface_version, - "INTERFACE_VERSION" }, - { WINBINDD_DOMAIN_NAME, winbindd_domain_name, "DOMAIN_NAME" }, - { WINBINDD_DOMAIN_INFO, winbindd_domain_info, "DOMAIN_INFO" }, - { WINBINDD_NETBIOS_NAME, winbindd_netbios_name, "NETBIOS_NAME" }, - { WINBINDD_PRIV_PIPE_DIR, winbindd_priv_pipe_dir, - "WINBINDD_PRIV_PIPE_DIR" }, - { WINBINDD_GETDCNAME, winbindd_getdcname, "GETDCNAME" }, - { WINBINDD_DSGETDCNAME, winbindd_dsgetdcname, "DSGETDCNAME" }, - - /* Credential cache access */ - { WINBINDD_CCACHE_NTLMAUTH, winbindd_ccache_ntlm_auth, "NTLMAUTH" }, - - /* WINS functions */ - - { WINBINDD_WINS_BYNAME, winbindd_wins_byname, "WINS_BYNAME" }, - { WINBINDD_WINS_BYIP, winbindd_wins_byip, "WINS_BYIP" }, - - /* End of list */ - - { WINBINDD_NUM_CMDS, NULL, "NONE" } -}; - -static void process_request(struct winbindd_cli_state *state) -{ - struct winbindd_dispatch_table *table = dispatch_table; - - /* Free response data - we may be interrupted and receive another - command before being able to send this data off. */ - - SAFE_FREE(state->response.extra_data.data); - - ZERO_STRUCT(state->response); - - state->response.result = WINBINDD_PENDING; - state->response.length = sizeof(struct winbindd_response); - - state->mem_ctx = talloc_init("winbind request"); - if (state->mem_ctx == NULL) - return; - - /* Remember who asked us. */ - state->pid = state->request.pid; - - /* Process command */ - - for (table = dispatch_table; table->fn; table++) { - if (state->request.cmd == table->cmd) { - DEBUG(10,("process_request: request fn %s\n", - table->winbindd_cmd_name )); - table->fn(state); - break; - } - } - - if (!table->fn) { - DEBUG(10,("process_request: unknown request fn number %d\n", - (int)state->request.cmd )); - request_error(state); - } -} - -/* - * A list of file descriptors being monitored by select in the main processing - * loop. fd_event->handler is called whenever the socket is readable/writable. - */ - -static struct fd_event *fd_events = NULL; - -void add_fd_event(struct fd_event *ev) -{ - struct fd_event *match; - - /* only add unique fd_event structs */ - - for (match=fd_events; match; match=match->next ) { -#ifdef DEVELOPER - SMB_ASSERT( match != ev ); -#else - if ( match == ev ) - return; -#endif - } - - DLIST_ADD(fd_events, ev); -} - -void remove_fd_event(struct fd_event *ev) -{ - DLIST_REMOVE(fd_events, ev); -} - -/* - * Handler for fd_events to complete a read/write request, set up by - * setup_async_read/setup_async_write. - */ - -static void rw_callback(struct fd_event *event, int flags) -{ - size_t todo; - ssize_t done = 0; - - todo = event->length - event->done; - - if (event->flags & EVENT_FD_WRITE) { - SMB_ASSERT(flags == EVENT_FD_WRITE); - done = sys_write(event->fd, - &((char *)event->data)[event->done], - todo); - - if (done <= 0) { - event->flags = 0; - event->finished(event->private_data, False); - return; - } - } - - if (event->flags & EVENT_FD_READ) { - SMB_ASSERT(flags == EVENT_FD_READ); - done = sys_read(event->fd, &((char *)event->data)[event->done], - todo); - - if (done <= 0) { - event->flags = 0; - event->finished(event->private_data, False); - return; - } - } - - event->done += done; - - if (event->done == event->length) { - event->flags = 0; - event->finished(event->private_data, True); - } -} - -/* - * Request an async read/write on a fd_event structure. (*finished) is called - * when the request is completed or an error had occurred. - */ - -void setup_async_read(struct fd_event *event, void *data, size_t length, - void (*finished)(void *private_data, BOOL success), - void *private_data) -{ - SMB_ASSERT(event->flags == 0); - event->data = data; - event->length = length; - event->done = 0; - event->handler = rw_callback; - event->finished = finished; - event->private_data = private_data; - event->flags = EVENT_FD_READ; -} - -void setup_async_write(struct fd_event *event, void *data, size_t length, - void (*finished)(void *private_data, BOOL success), - void *private_data) -{ - SMB_ASSERT(event->flags == 0); - event->data = data; - event->length = length; - event->done = 0; - event->handler = rw_callback; - event->finished = finished; - event->private_data = private_data; - event->flags = EVENT_FD_WRITE; -} - -/* - * This is the main event loop of winbind requests. It goes through a - * state-machine of 3 read/write requests, 4 if you have extra data to send. - * - * An idle winbind client has a read request of 4 bytes outstanding, - * finalizing function is request_len_recv, checking the length. request_recv - * then processes the packet. The processing function then at some point has - * to call request_finished which schedules sending the response. - */ - -static void request_len_recv(void *private_data, BOOL success); -static void request_recv(void *private_data, BOOL success); -static void request_main_recv(void *private_data, BOOL success); -static void request_finished(struct winbindd_cli_state *state); -void request_finished_cont(void *private_data, BOOL success); -static void response_main_sent(void *private_data, BOOL success); -static void response_extra_sent(void *private_data, BOOL success); - -static void response_extra_sent(void *private_data, BOOL success) -{ - struct winbindd_cli_state *state = - talloc_get_type_abort(private_data, struct winbindd_cli_state); - - if (state->mem_ctx != NULL) { - talloc_destroy(state->mem_ctx); - state->mem_ctx = NULL; - } - - if (!success) { - state->finished = True; - return; - } - - SAFE_FREE(state->request.extra_data.data); - SAFE_FREE(state->response.extra_data.data); - - setup_async_read(&state->fd_event, &state->request, sizeof(uint32), - request_len_recv, state); -} - -static void response_main_sent(void *private_data, BOOL success) -{ - struct winbindd_cli_state *state = - talloc_get_type_abort(private_data, struct winbindd_cli_state); - - if (!success) { - state->finished = True; - return; - } - - if (state->response.length == sizeof(state->response)) { - if (state->mem_ctx != NULL) { - talloc_destroy(state->mem_ctx); - state->mem_ctx = NULL; - } - - setup_async_read(&state->fd_event, &state->request, - sizeof(uint32), request_len_recv, state); - return; - } - - setup_async_write(&state->fd_event, state->response.extra_data.data, - state->response.length - sizeof(state->response), - response_extra_sent, state); -} - -static void request_finished(struct winbindd_cli_state *state) -{ - setup_async_write(&state->fd_event, &state->response, - sizeof(state->response), response_main_sent, state); -} - -void request_error(struct winbindd_cli_state *state) -{ - SMB_ASSERT(state->response.result == WINBINDD_PENDING); - state->response.result = WINBINDD_ERROR; - request_finished(state); -} - -void request_ok(struct winbindd_cli_state *state) -{ - SMB_ASSERT(state->response.result == WINBINDD_PENDING); - state->response.result = WINBINDD_OK; - request_finished(state); -} - -void request_finished_cont(void *private_data, BOOL success) -{ - struct winbindd_cli_state *state = - talloc_get_type_abort(private_data, struct winbindd_cli_state); - - if (success) - request_ok(state); - else - request_error(state); -} - -static void request_len_recv(void *private_data, BOOL success) -{ - struct winbindd_cli_state *state = - talloc_get_type_abort(private_data, struct winbindd_cli_state); - - if (!success) { - state->finished = True; - return; - } - - if (*(uint32 *)(&state->request) != sizeof(state->request)) { - DEBUG(0,("request_len_recv: Invalid request size received: %d\n", - *(uint32 *)(&state->request))); - state->finished = True; - return; - } - - setup_async_read(&state->fd_event, (uint32 *)(&state->request)+1, - sizeof(state->request) - sizeof(uint32), - request_main_recv, state); -} - -static void request_main_recv(void *private_data, BOOL success) -{ - struct winbindd_cli_state *state = - talloc_get_type_abort(private_data, struct winbindd_cli_state); - - if (!success) { - state->finished = True; - return; - } - - if (state->request.extra_len == 0) { - state->request.extra_data.data = NULL; - request_recv(state, True); - return; - } - - if ((!state->privileged) && - (state->request.extra_len > WINBINDD_MAX_EXTRA_DATA)) { - DEBUG(3, ("Got request with %d bytes extra data on " - "unprivileged socket\n", (int)state->request.extra_len)); - state->request.extra_data.data = NULL; - state->finished = True; - return; - } - - state->request.extra_data.data = - SMB_MALLOC_ARRAY(char, state->request.extra_len + 1); - - if (state->request.extra_data.data == NULL) { - DEBUG(0, ("malloc failed\n")); - state->finished = True; - return; - } - - /* Ensure null termination */ - state->request.extra_data.data[state->request.extra_len] = '\0'; - - setup_async_read(&state->fd_event, state->request.extra_data.data, - state->request.extra_len, request_recv, state); -} - -static void request_recv(void *private_data, BOOL success) -{ - struct winbindd_cli_state *state = - talloc_get_type_abort(private_data, struct winbindd_cli_state); - - if (!success) { - state->finished = True; - return; - } - - process_request(state); -} - -/* Process a new connection by adding it to the client connection list */ - -static void new_connection(int listen_sock, BOOL privileged) -{ - struct sockaddr_un sunaddr; - struct winbindd_cli_state *state; - socklen_t len; - int sock; - - /* Accept connection */ - - len = sizeof(sunaddr); - - do { - sock = accept(listen_sock, (struct sockaddr *)&sunaddr, &len); - } while (sock == -1 && errno == EINTR); - - if (sock == -1) - return; - - DEBUG(6,("accepted socket %d\n", sock)); - - /* Create new connection structure */ - - if ((state = TALLOC_ZERO_P(NULL, struct winbindd_cli_state)) == NULL) { - close(sock); - return; - } - - state->sock = sock; - - state->last_access = time(NULL); - - state->privileged = privileged; - - state->fd_event.fd = state->sock; - state->fd_event.flags = 0; - add_fd_event(&state->fd_event); - - setup_async_read(&state->fd_event, &state->request, sizeof(uint32), - request_len_recv, state); - - /* Add to connection list */ - - winbindd_add_client(state); -} - -/* Remove a client connection from client connection list */ - -static void remove_client(struct winbindd_cli_state *state) -{ - /* It's a dead client - hold a funeral */ - - if (state == NULL) { - return; - } - - /* Close socket */ - - close(state->sock); - - /* Free any getent state */ - - free_getent_state(state->getpwent_state); - free_getent_state(state->getgrent_state); - - /* We may have some extra data that was not freed if the client was - killed unexpectedly */ - - SAFE_FREE(state->response.extra_data.data); - - if (state->mem_ctx != NULL) { - talloc_destroy(state->mem_ctx); - state->mem_ctx = NULL; - } - - remove_fd_event(&state->fd_event); - - /* Remove from list and free */ - - winbindd_remove_client(state); - TALLOC_FREE(state); -} - -/* Shutdown client connection which has been idle for the longest time */ - -static BOOL remove_idle_client(void) -{ - struct winbindd_cli_state *state, *remove_state = NULL; - time_t last_access = 0; - int nidle = 0; - - for (state = winbindd_client_list(); state; state = state->next) { - if (state->response.result != WINBINDD_PENDING && - !state->getpwent_state && !state->getgrent_state) { - nidle++; - if (!last_access || state->last_access < last_access) { - last_access = state->last_access; - remove_state = state; - } - } - } - - if (remove_state) { - DEBUG(5,("Found %d idle client connections, shutting down sock %d, pid %u\n", - nidle, remove_state->sock, (unsigned int)remove_state->pid)); - remove_client(remove_state); - return True; - } - - return False; -} - -/* Process incoming clients on listen_sock. We use a tricky non-blocking, - non-forking, non-threaded model which allows us to handle many - simultaneous connections while remaining impervious to many denial of - service attacks. */ - -static int process_loop(int listen_sock, int listen_priv_sock) -{ - struct winbindd_cli_state *state; - struct fd_event *ev; - fd_set r_fds, w_fds; - int maxfd, selret; - struct timeval timeout, ev_timeout; - - /* We'll be doing this a lot */ - - /* Handle messages */ - - message_dispatch(winbind_messaging_context()); - - run_events(winbind_event_context(), 0, NULL, NULL); - - /* refresh the trusted domain cache */ - - rescan_trusted_domains(); - - /* Initialise fd lists for select() */ - - maxfd = MAX(listen_sock, listen_priv_sock); - - FD_ZERO(&r_fds); - FD_ZERO(&w_fds); - FD_SET(listen_sock, &r_fds); - FD_SET(listen_priv_sock, &r_fds); - - timeout.tv_sec = WINBINDD_ESTABLISH_LOOP; - timeout.tv_usec = 0; - - /* Check for any event timeouts. */ - if (get_timed_events_timeout(winbind_event_context(), &ev_timeout)) { - timeout = timeval_min(&timeout, &ev_timeout); - } - - /* Set up client readers and writers */ - - state = winbindd_client_list(); - - while (state) { - - struct winbindd_cli_state *next = state->next; - - /* Dispose of client connection if it is marked as - finished */ - - if (state->finished) - remove_client(state); - - state = next; - } - - for (ev = fd_events; ev; ev = ev->next) { - if (ev->flags & EVENT_FD_READ) { - FD_SET(ev->fd, &r_fds); - maxfd = MAX(ev->fd, maxfd); - } - if (ev->flags & EVENT_FD_WRITE) { - FD_SET(ev->fd, &w_fds); - maxfd = MAX(ev->fd, maxfd); - } - } - - /* Call select */ - - selret = sys_select(maxfd + 1, &r_fds, &w_fds, NULL, &timeout); - - if (selret == 0) { - goto no_fds_ready; - } - - if (selret == -1) { - if (errno == EINTR) { - goto no_fds_ready; - } - - /* Select error, something is badly wrong */ - - perror("select"); - exit(1); - } - - /* selret > 0 */ - - ev = fd_events; - while (ev != NULL) { - struct fd_event *next = ev->next; - int flags = 0; - if (FD_ISSET(ev->fd, &r_fds)) - flags |= EVENT_FD_READ; - if (FD_ISSET(ev->fd, &w_fds)) - flags |= EVENT_FD_WRITE; - if (flags) - ev->handler(ev, flags); - ev = next; - } - - if (FD_ISSET(listen_sock, &r_fds)) { - while (winbindd_num_clients() > - WINBINDD_MAX_SIMULTANEOUS_CLIENTS - 1) { - DEBUG(5,("winbindd: Exceeding %d client " - "connections, removing idle " - "connection.\n", - WINBINDD_MAX_SIMULTANEOUS_CLIENTS)); - if (!remove_idle_client()) { - DEBUG(0,("winbindd: Exceeding %d " - "client connections, no idle " - "connection found\n", - WINBINDD_MAX_SIMULTANEOUS_CLIENTS)); - break; - } - } - /* new, non-privileged connection */ - new_connection(listen_sock, False); - } - - if (FD_ISSET(listen_priv_sock, &r_fds)) { - while (winbindd_num_clients() > - WINBINDD_MAX_SIMULTANEOUS_CLIENTS - 1) { - DEBUG(5,("winbindd: Exceeding %d client " - "connections, removing idle " - "connection.\n", - WINBINDD_MAX_SIMULTANEOUS_CLIENTS)); - if (!remove_idle_client()) { - DEBUG(0,("winbindd: Exceeding %d " - "client connections, no idle " - "connection found\n", - WINBINDD_MAX_SIMULTANEOUS_CLIENTS)); - break; - } - } - /* new, privileged connection */ - new_connection(listen_priv_sock, True); - } - - no_fds_ready: - -#if 0 - winbindd_check_cache_size(time(NULL)); -#endif - - /* Check signal handling things */ - - if (do_sigterm) - terminate(); - - if (do_sighup) { - - DEBUG(3, ("got SIGHUP\n")); - - flush_caches(); - reload_services_file(); - - do_sighup = False; - } - - if (do_sigusr2) { - print_winbindd_status(); - do_sigusr2 = False; - } - - if (do_sigchld) { - pid_t pid; - - do_sigchld = False; - - while ((pid = sys_waitpid(-1, NULL, WNOHANG)) > 0) { - winbind_child_died(pid); - } - } - - - return winbindd_num_clients(); -} - -static void winbindd_process_loop(enum smb_server_mode server_mode) -{ - int idle_timeout_sec; - struct timeval starttime; - int listen_public, listen_priv; - - errno = 0; - if (!winbindd_init_sockets(&listen_public, &listen_priv, - &idle_timeout_sec)) { - terminate(); - } - - starttime = timeval_current(); - - if (listen_public == -1 || listen_priv == -1) { - DEBUG(0, ("failed to open winbindd pipes: %s\n", - errno ? strerror(errno) : "unknown error")); - terminate(); - } - - for (;;) { - TALLOC_CTX *frame = talloc_stackframe(); - int clients = process_loop(listen_public, listen_priv); - - /* Don't bother figuring out the idle time if we won't be - * timing out anyway. - */ - if (idle_timeout_sec < 0) { - TALLOC_FREE(frame); - continue; - } - - if (clients == 0 && server_mode == SERVER_MODE_FOREGROUND) { - struct timeval now; - - now = timeval_current(); - if (timeval_elapsed2(&starttime, &now) > - (double)idle_timeout_sec) { - DEBUG(0, ("idle for %d secs, exitting\n", - idle_timeout_sec)); - terminate(); - } - } else { - starttime = timeval_current(); - } - TALLOC_FREE(frame); - } -} - -/* Main function */ - -int main(int argc, char **argv, char **envp) -{ - pstring logfile; - BOOL log_stdout = False; - BOOL no_process_group = False; - - enum smb_server_mode server_mode = SERVER_MODE_DAEMON; - - struct poptOption long_options[] = { - POPT_AUTOHELP - { "stdout", 'S', POPT_ARG_VAL, &log_stdout, True, "Log to stdout" }, - { "foreground", 'F', POPT_ARG_VAL, &server_mode, SERVER_MODE_FOREGROUND, "Daemon in foreground mode" }, - { "no-process-group", 0, POPT_ARG_VAL, &no_process_group, True, "Don't create a new process group" }, - { "daemon", 'D', POPT_ARG_VAL, &server_mode, SERVER_MODE_DAEMON, "Become a daemon (default)" }, - { "interactive", 'i', POPT_ARG_VAL, &server_mode, SERVER_MODE_INTERACTIVE, "Interactive mode" }, - { "no-caching", 'n', POPT_ARG_VAL, &opt_nocache, True, "Disable caching" }, - POPT_COMMON_SAMBA - POPT_TABLEEND - }; - poptContext pc; - int opt; - - /* glibc (?) likes to print "User defined signal 1" and exit if a - SIGUSR[12] is received before a handler is installed */ - - CatchSignal(SIGUSR1, SIG_IGN); - CatchSignal(SIGUSR2, SIG_IGN); - - fault_setup((void (*)(void *))fault_quit ); - dump_core_setup("winbindd"); - - load_case_tables(); - - /* Initialise for running in non-root mode */ - - sec_init(); - - set_remote_machine_name("winbindd", False); - - /* Set environment variable so we don't recursively call ourselves. - This may also be useful interactively. */ - - if ( !winbind_off() ) { - DEBUG(0,("Failed to disable recusive winbindd calls. Exiting.\n")); - exit(1); - } - - /* Initialise samba/rpc client stuff */ - - pc = poptGetContext("winbindd", argc, (const char **)argv, long_options, 0); - - while ((opt = poptGetNextOpt(pc)) != -1) { - switch (opt) { - default: - d_fprintf(stderr, "\nInvalid option %s: %s\n\n", - poptBadOption(pc, 0), poptStrerror(opt)); - poptPrintUsage(pc, stderr, 0); - exit(1); - } - } - - if (server_mode == SERVER_MODE_INTERACTIVE) { - log_stdout = True; - if (DEBUGLEVEL >= 9) { - talloc_enable_leak_report(); - } - } - - if (log_stdout && server_mode == SERVER_MODE_DAEMON) { - printf("Can't log to stdout (-S) unless daemon is in foreground (-F) or interactive (-i)\n"); - poptPrintUsage(pc, stderr, 0); - exit(1); - } - - poptFreeContext(pc); - - if (!override_logfile) { - pstr_sprintf(logfile, "%s/log.winbindd", dyn_LOGFILEBASE); - lp_set_logfile(logfile); - } - setup_logging("winbindd", log_stdout); - reopen_logs(); - - DEBUG(1, ("winbindd version %s started.\n%s\n", - SAMBA_VERSION_STRING, - COPYRIGHT_STARTUP_MESSAGE) ); - - if (!reload_services_file()) { - DEBUG(0, ("error opening config file\n")); - exit(1); - } - - if (!directory_exist(lp_lockdir(), NULL)) { - mkdir(lp_lockdir(), 0755); - } - - /* Setup names. */ - - if (!init_names()) - exit(1); - - load_interfaces(); - - if (!secrets_init()) { - - DEBUG(0,("Could not initialize domain trust account secrets. Giving up\n")); - return False; - } - - /* Enable netbios namecache */ - - namecache_enable(); - - /* Winbind daemon initialisation */ - - if ( ! NT_STATUS_IS_OK(idmap_init_cache()) ) { - DEBUG(1, ("Could not init idmap cache!\n")); - } - - /* Unblock all signals we are interested in as they may have been - blocked by the parent process. */ - - BlockSignals(False, SIGINT); - BlockSignals(False, SIGQUIT); - BlockSignals(False, SIGTERM); - BlockSignals(False, SIGUSR1); - BlockSignals(False, SIGUSR2); - BlockSignals(False, SIGHUP); - BlockSignals(False, SIGCHLD); - - /* Setup signal handlers */ - - CatchSignal(SIGINT, termination_handler); /* Exit on these sigs */ - CatchSignal(SIGQUIT, termination_handler); - CatchSignal(SIGTERM, termination_handler); - CatchSignal(SIGCHLD, sigchld_handler); - - CatchSignal(SIGPIPE, SIG_IGN); /* Ignore sigpipe */ - - CatchSignal(SIGUSR2, sigusr2_handler); /* Debugging sigs */ - CatchSignal(SIGHUP, sighup_handler); - - if (server_mode == SERVER_MODE_DAEMON) { - DEBUG( 3, ( "Becoming a daemon.\n" ) ); - become_daemon(True, no_process_group); - } else if (server_mode == SERVER_MODE_FOREGROUND) { - become_daemon(False, no_process_group); - } - - pidfile_create("winbindd"); - - /* Ensure all cache and idmap caches are consistent - before we startup. */ - - if (winbindd_validate_cache()) { - /* We have a bad cache, but luckily we - just deleted it. Restart ourselves */ - int i; - /* Ensure we have no open low fd's. */ - for (i = 3; i < 100; i++) { - close(i); - } - return execve(argv[0], argv, envp); - } - -#if HAVE_SETPGID - /* - * If we're interactive we want to set our own process group for - * signal management. - */ - if (server_mode == SERVER_MODE_INTERACTIVE && !no_process_group) { - setpgid( (pid_t)0, (pid_t)0); - } -#endif - - TimeInit(); - - /* Initialise messaging system */ - - if (winbind_messaging_context() == NULL) { - DEBUG(0, ("unable to initialize messaging system\n")); - exit(1); - } - - /* Initialize cache (ensure version is correct). */ - if (!initialize_winbindd_cache()) { - exit(1); - } - - /* React on 'smbcontrol winbindd reload-config' in the same way - as to SIGHUP signal */ - messaging_register(winbind_messaging_context(), NULL, - MSG_SMB_CONF_UPDATED, msg_reload_services); - messaging_register(winbind_messaging_context(), NULL, - MSG_SHUTDOWN, msg_shutdown); - - /* Handle online/offline messages. */ - messaging_register(winbind_messaging_context(), NULL, - MSG_WINBIND_OFFLINE, winbind_msg_offline); - messaging_register(winbind_messaging_context(), NULL, - MSG_WINBIND_ONLINE, winbind_msg_online); - messaging_register(winbind_messaging_context(), NULL, - MSG_WINBIND_ONLINESTATUS, winbind_msg_onlinestatus); - - messaging_register(winbind_messaging_context(), NULL, - MSG_DUMP_EVENT_LIST, winbind_msg_dump_event_list); - - messaging_register(winbind_messaging_context(), NULL, - MSG_WINBIND_VALIDATE_CACHE, - winbind_msg_validate_cache); - - netsamlogon_cache_init(); /* Non-critical */ - - /* clear the cached list of trusted domains */ - - wcache_tdc_clear(); - - if (!init_domain_list()) { - DEBUG(0,("unable to initalize domain list\n")); - exit(1); - } - - init_idmap_child(); - init_locator_child(); - - smb_nscd_flush_user_cache(); - smb_nscd_flush_group_cache(); - - /* Loop waiting for requests */ - winbindd_process_loop(server_mode); - - return 0; -} diff --git a/source3/nsswitch/winbindd.h b/source3/nsswitch/winbindd.h deleted file mode 100644 index 27406ac438..0000000000 --- a/source3/nsswitch/winbindd.h +++ /dev/null @@ -1,365 +0,0 @@ -/* - Unix SMB/CIFS implementation. - - Winbind daemon for ntdom nss module - - Copyright (C) Tim Potter 2000 - Copyright (C) Jim McDonough 2003 - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 3 of the License, or (at your option) any later version. - - This library 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 - Library General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with this program. If not, see . -*/ - -#ifndef _WINBINDD_H -#define _WINBINDD_H - -#include "nsswitch/winbind_struct_protocol.h" - -#ifdef HAVE_LIBNSCD -#include -#endif - -#ifdef HAVE_SYS_MMAN_H -#include -#endif - -#undef DBGC_CLASS -#define DBGC_CLASS DBGC_WINBIND - -#define WB_REPLACE_CHAR '_' - -/* bits for fd_event.flags */ -#define EVENT_FD_READ 1 -#define EVENT_FD_WRITE 2 - -struct fd_event { - struct fd_event *next, *prev; - int fd; - int flags; /* see EVENT_FD_* flags */ - void (*handler)(struct fd_event *fde, int flags); - void *data; - size_t length, done; - void (*finished)(void *private_data, BOOL success); - void *private_data; -}; - -struct sid_ctr { - DOM_SID *sid; - BOOL finished; - const char *domain; - const char *name; - enum lsa_SidType type; -}; - -struct winbindd_cli_state { - struct winbindd_cli_state *prev, *next; /* Linked list pointers */ - int sock; /* Open socket from client */ - struct fd_event fd_event; - pid_t pid; /* pid of client */ - BOOL finished; /* Can delete from list */ - BOOL write_extra_data; /* Write extra_data field */ - time_t last_access; /* Time of last access (read or write) */ - BOOL privileged; /* Is the client 'privileged' */ - - TALLOC_CTX *mem_ctx; /* memory per request */ - struct winbindd_request request; /* Request from client */ - struct winbindd_response response; /* Respose to client */ - BOOL getpwent_initialized; /* Has getpwent_state been - * initialized? */ - BOOL getgrent_initialized; /* Has getgrent_state been - * initialized? */ - struct getent_state *getpwent_state; /* State for getpwent() */ - struct getent_state *getgrent_state; /* State for getgrent() */ -}; - -/* State between get{pw,gr}ent() calls */ - -struct getent_state { - struct getent_state *prev, *next; - void *sam_entries; - uint32 sam_entry_index, num_sam_entries; - BOOL got_sam_entries; - fstring domain_name; -}; - -/* Storage for cached getpwent() user entries */ - -struct getpwent_user { - fstring name; /* Account name */ - fstring gecos; /* User information */ - fstring homedir; /* User Home Directory */ - fstring shell; /* User Login Shell */ - DOM_SID user_sid; /* NT user and primary group SIDs */ - DOM_SID group_sid; -}; - -/* Server state structure */ - -typedef struct { - char *acct_name; - char *full_name; - char *homedir; - char *shell; - gid_t primary_gid; /* allow the nss_info - backend to set the primary group */ - DOM_SID user_sid; /* NT user and primary group SIDs */ - DOM_SID group_sid; -} WINBIND_USERINFO; - -/* Our connection to the DC */ - -struct winbindd_cm_conn { - struct cli_state *cli; - - struct rpc_pipe_client *samr_pipe; - POLICY_HND sam_connect_handle, sam_domain_handle; - - struct rpc_pipe_client *lsa_pipe; - POLICY_HND lsa_policy; - - struct rpc_pipe_client *netlogon_pipe; -}; - -struct winbindd_async_request; - -/* Async child */ - -struct winbindd_child { - struct winbindd_child *next, *prev; - - pid_t pid; - struct winbindd_domain *domain; - pstring logfilename; - - struct fd_event event; - struct timed_event *lockout_policy_event; - struct winbindd_async_request *requests; -}; - -/* Structures to hold per domain information */ - -struct winbindd_domain { - fstring name; /* Domain name (NetBIOS) */ - fstring alt_name; /* alt Domain name, if any (FQDN for ADS) */ - fstring forest_name; /* Name of the AD forest we're in */ - DOM_SID sid; /* SID for this domain */ - uint32 domain_flags; /* Domain flags from rpc_ds.h */ - uint32 domain_type; /* Domain type from rpc_ds.h */ - uint32 domain_trust_attribs; /* Trust attribs from rpc_ds.h */ - BOOL initialized; /* Did we already ask for the domain mode? */ - BOOL native_mode; /* is this a win2k domain in native mode ? */ - BOOL active_directory; /* is this a win2k active directory ? */ - BOOL primary; /* is this our primary domain ? */ - BOOL internal; /* BUILTIN and member SAM */ - BOOL online; /* is this domain available ? */ - time_t startup_time; /* When we set "startup" true. */ - BOOL startup; /* are we in the first 30 seconds after startup_time ? */ - - BOOL can_do_samlogon_ex; /* Due to the lack of finer control what type - * of DC we have, let us try to do a - * credential-chain less samlogon_ex call - * with AD and schannel. If this fails with - * DCERPC_FAULT_OP_RNG_ERROR, then set this - * to False. This variable is around so that - * we don't have to try _ex every time. */ - - /* Lookup methods for this domain (LDAP or RPC) */ - struct winbindd_methods *methods; - - /* the backend methods are used by the cache layer to find the right - backend */ - struct winbindd_methods *backend; - - /* Private data for the backends (used for connection cache) */ - - void *private_data; - - /* A working DC */ - fstring dcname; - struct sockaddr_in dcaddr; - - /* Sequence number stuff */ - - time_t last_seq_check; - uint32 sequence_number; - NTSTATUS last_status; - - /* The smb connection */ - - struct winbindd_cm_conn conn; - - /* The child pid we're talking to */ - - struct winbindd_child child; - - /* Callback we use to try put us back online. */ - - uint32 check_online_timeout; - struct timed_event *check_online_event; - - /* Linked list info */ - - struct winbindd_domain *prev, *next; -}; - -/* per-domain methods. This is how LDAP vs RPC is selected - */ -struct winbindd_methods { - /* does this backend provide a consistent view of the data? (ie. is the primary group - always correct) */ - BOOL consistent; - - /* get a list of users, returning a WINBIND_USERINFO for each one */ - NTSTATUS (*query_user_list)(struct winbindd_domain *domain, - TALLOC_CTX *mem_ctx, - uint32 *num_entries, - WINBIND_USERINFO **info); - - /* get a list of domain groups */ - NTSTATUS (*enum_dom_groups)(struct winbindd_domain *domain, - TALLOC_CTX *mem_ctx, - uint32 *num_entries, - struct acct_info **info); - - /* get a list of domain local groups */ - NTSTATUS (*enum_local_groups)(struct winbindd_domain *domain, - TALLOC_CTX *mem_ctx, - uint32 *num_entries, - struct acct_info **info); - - /* convert one user or group name to a sid */ - NTSTATUS (*name_to_sid)(struct winbindd_domain *domain, - TALLOC_CTX *mem_ctx, - enum winbindd_cmd orig_cmd, - const char *domain_name, - const char *name, - DOM_SID *sid, - enum lsa_SidType *type); - - /* convert a sid to a user or group name */ - NTSTATUS (*sid_to_name)(struct winbindd_domain *domain, - TALLOC_CTX *mem_ctx, - const DOM_SID *sid, - char **domain_name, - char **name, - enum lsa_SidType *type); - - NTSTATUS (*rids_to_names)(struct winbindd_domain *domain, - TALLOC_CTX *mem_ctx, - const DOM_SID *domain_sid, - uint32 *rids, - size_t num_rids, - char **domain_name, - char ***names, - enum lsa_SidType **types); - - /* lookup user info for a given SID */ - NTSTATUS (*query_user)(struct winbindd_domain *domain, - TALLOC_CTX *mem_ctx, - const DOM_SID *user_sid, - WINBIND_USERINFO *user_info); - - /* lookup all groups that a user is a member of. The backend - can also choose to lookup by username or rid for this - function */ - NTSTATUS (*lookup_usergroups)(struct winbindd_domain *domain, - TALLOC_CTX *mem_ctx, - const DOM_SID *user_sid, - uint32 *num_groups, DOM_SID **user_gids); - - /* Lookup all aliases that the sids delivered are member of. This is - * to implement 'domain local groups' correctly */ - NTSTATUS (*lookup_useraliases)(struct winbindd_domain *domain, - TALLOC_CTX *mem_ctx, - uint32 num_sids, - const DOM_SID *sids, - uint32 *num_aliases, - uint32 **alias_rids); - - /* find all members of the group with the specified group_rid */ - NTSTATUS (*lookup_groupmem)(struct winbindd_domain *domain, - TALLOC_CTX *mem_ctx, - const DOM_SID *group_sid, - uint32 *num_names, - DOM_SID **sid_mem, char ***names, - uint32 **name_types); - - /* return the current global sequence number */ - NTSTATUS (*sequence_number)(struct winbindd_domain *domain, uint32 *seq); - - /* return the lockout policy */ - NTSTATUS (*lockout_policy)(struct winbindd_domain *domain, - TALLOC_CTX *mem_ctx, - SAM_UNK_INFO_12 *lockout_policy); - - /* return the lockout policy */ - NTSTATUS (*password_policy)(struct winbindd_domain *domain, - TALLOC_CTX *mem_ctx, - SAM_UNK_INFO_1 *password_policy); - - /* enumerate trusted domains */ - NTSTATUS (*trusted_domains)(struct winbindd_domain *domain, - TALLOC_CTX *mem_ctx, - uint32 *num_domains, - char ***names, - char ***alt_names, - DOM_SID **dom_sids); -}; - -/* Used to glue a policy handle and cli_state together */ - -typedef struct { - struct cli_state *cli; - POLICY_HND pol; -} CLI_POLICY_HND; - -/* Filled out by IDMAP backends */ -struct winbindd_idmap_methods { - /* Called when backend is first loaded */ - BOOL (*init)(void); - - BOOL (*get_sid_from_uid)(uid_t uid, DOM_SID *sid); - BOOL (*get_sid_from_gid)(gid_t gid, DOM_SID *sid); - - BOOL (*get_uid_from_sid)(DOM_SID *sid, uid_t *uid); - BOOL (*get_gid_from_sid)(DOM_SID *sid, gid_t *gid); - - /* Called when backend is unloaded */ - BOOL (*close)(void); - /* Called to dump backend status */ - void (*status)(void); -}; - -/* Data structures for dealing with the trusted domain cache */ - -struct winbindd_tdc_domain { - const char *domain_name; - const char *dns_name; - DOM_SID sid; - uint32 trust_flags; - uint32 trust_attribs; - uint32 trust_type; -}; - - -#include "nsswitch/winbindd_proto.h" - -#define WINBINDD_ESTABLISH_LOOP 30 -#define WINBINDD_RESCAN_FREQ lp_winbind_cache_time() -#define WINBINDD_PAM_AUTH_KRB5_RENEW_TIME 2592000 /* one month */ -#define DOM_SEQUENCE_NONE ((uint32)-1) - -#define IS_DOMAIN_OFFLINE(x) ( lp_winbind_offline_logon() && \ - ( get_global_winbindd_state_offline() \ - || !(x)->online ) ) -#endif /* _WINBINDD_H */ diff --git a/source3/nsswitch/winbindd_ads.c b/source3/nsswitch/winbindd_ads.c deleted file mode 100644 index 65cc00bb97..0000000000 --- a/source3/nsswitch/winbindd_ads.c +++ /dev/null @@ -1,1312 +0,0 @@ -/* - Unix SMB/CIFS implementation. - - Winbind ADS backend functions - - Copyright (C) Andrew Tridgell 2001 - Copyright (C) Andrew Bartlett 2003 - Copyright (C) Gerald (Jerry) Carter 2004 - - 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 3 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, see . -*/ - -#include "includes.h" -#include "winbindd.h" - -#ifdef HAVE_ADS - -#undef DBGC_CLASS -#define DBGC_CLASS DBGC_WINBIND - -extern struct winbindd_methods reconnect_methods; - -/* - return our ads connections structure for a domain. We keep the connection - open to make things faster -*/ -static ADS_STRUCT *ads_cached_connection(struct winbindd_domain *domain) -{ - ADS_STRUCT *ads; - ADS_STATUS status; - fstring dc_name; - struct in_addr dc_ip; - - DEBUG(10,("ads_cached_connection\n")); - - if (domain->private_data) { - - time_t expire; - time_t now = time(NULL); - - /* check for a valid structure */ - ads = (ADS_STRUCT *)domain->private_data; - - expire = MIN(ads->auth.tgt_expire, ads->auth.tgs_expire); - - DEBUG(7, ("Current tickets expire in %d seconds (at %d, time is now %d)\n", - (uint32)expire-(uint32)now, (uint32) expire, (uint32) now)); - - if ( ads->config.realm && (expire > now)) { - return ads; - } else { - /* we own this ADS_STRUCT so make sure it goes away */ - DEBUG(7,("Deleting expired krb5 credential cache\n")); - ads->is_mine = True; - ads_destroy( &ads ); - ads_kdestroy("MEMORY:winbind_ccache"); - domain->private_data = NULL; - } - } - - /* we don't want this to affect the users ccache */ - setenv("KRB5CCNAME", "MEMORY:winbind_ccache", 1); - - ads = ads_init(domain->alt_name, domain->name, NULL); - if (!ads) { - DEBUG(1,("ads_init for domain %s failed\n", domain->name)); - return NULL; - } - - /* the machine acct password might have change - fetch it every time */ - - SAFE_FREE(ads->auth.password); - SAFE_FREE(ads->auth.realm); - - if ( IS_DC ) { - DOM_SID sid; - time_t last_set_time; - - if ( !pdb_get_trusteddom_pw( domain->name, &ads->auth.password, &sid, &last_set_time ) ) { - ads_destroy( &ads ); - return NULL; - } - ads->auth.realm = SMB_STRDUP( ads->server.realm ); - strupper_m( ads->auth.realm ); - } - else { - struct winbindd_domain *our_domain = domain; - - ads->auth.password = secrets_fetch_machine_password(lp_workgroup(), NULL, NULL); - - /* always give preference to the alt_name in our - primary domain if possible */ - - if ( !domain->primary ) - our_domain = find_our_domain(); - - if ( our_domain->alt_name[0] != '\0' ) { - ads->auth.realm = SMB_STRDUP( our_domain->alt_name ); - strupper_m( ads->auth.realm ); - } - else - ads->auth.realm = SMB_STRDUP( lp_realm() ); - } - - ads->auth.renewable = WINBINDD_PAM_AUTH_KRB5_RENEW_TIME; - - /* Setup the server affinity cache. We don't reaally care - about the name. Just setup affinity and the KRB5_CONFIG - file. */ - - get_dc_name( ads->server.workgroup, ads->server.realm, dc_name, &dc_ip ); - - status = ads_connect(ads); - if (!ADS_ERR_OK(status) || !ads->config.realm) { - DEBUG(1,("ads_connect for domain %s failed: %s\n", - domain->name, ads_errstr(status))); - ads_destroy(&ads); - - /* if we get ECONNREFUSED then it might be a NT4 - server, fall back to MSRPC */ - if (status.error_type == ENUM_ADS_ERROR_SYSTEM && - status.err.rc == ECONNREFUSED) { - /* 'reconnect_methods' is the MS-RPC backend. */ - DEBUG(1,("Trying MSRPC methods\n")); - domain->backend = &reconnect_methods; - } - return NULL; - } - - /* set the flag that says we don't own the memory even - though we do so that ads_destroy() won't destroy the - structure we pass back by reference */ - - ads->is_mine = False; - - domain->private_data = (void *)ads; - return ads; -} - - -/* Query display info for a realm. This is the basic user list fn */ -static NTSTATUS query_user_list(struct winbindd_domain *domain, - TALLOC_CTX *mem_ctx, - uint32 *num_entries, - WINBIND_USERINFO **info) -{ - ADS_STRUCT *ads = NULL; - const char *attrs[] = { "*", NULL }; - int i, count; - ADS_STATUS rc; - LDAPMessage *res = NULL; - LDAPMessage *msg = NULL; - NTSTATUS status = NT_STATUS_UNSUCCESSFUL; - - *num_entries = 0; - - DEBUG(3,("ads: query_user_list\n")); - - if ( !winbindd_can_contact_domain( domain ) ) { - DEBUG(10,("query_user_list: No incoming trust for domain %s\n", - domain->name)); - return NT_STATUS_OK; - } - - ads = ads_cached_connection(domain); - - if (!ads) { - domain->last_status = NT_STATUS_SERVER_DISABLED; - goto done; - } - - rc = ads_search_retry(ads, &res, "(objectCategory=user)", attrs); - if (!ADS_ERR_OK(rc) || !res) { - DEBUG(1,("query_user_list ads_search: %s\n", ads_errstr(rc))); - goto done; - } - - count = ads_count_replies(ads, res); - if (count == 0) { - DEBUG(1,("query_user_list: No users found\n")); - goto done; - } - - (*info) = TALLOC_ZERO_ARRAY(mem_ctx, WINBIND_USERINFO, count); - if (!*info) { - status = NT_STATUS_NO_MEMORY; - goto done; - } - - i = 0; - - for (msg = ads_first_entry(ads, res); msg; msg = ads_next_entry(ads, msg)) { - char *name, *gecos = NULL; - char *homedir = NULL; - char *shell = NULL; - uint32 group; - uint32 atype; - DOM_SID user_sid; - gid_t primary_gid = (gid_t)-1; - - if (!ads_pull_uint32(ads, msg, "sAMAccountType", &atype) || - ads_atype_map(atype) != SID_NAME_USER) { - DEBUG(1,("Not a user account? atype=0x%x\n", atype)); - continue; - } - - name = ads_pull_username(ads, mem_ctx, msg); - - if ( ads_pull_sid( ads, msg, "objectSid", &user_sid ) ) { - status = nss_get_info_cached( domain, &user_sid, mem_ctx, - ads, msg, &homedir, &shell, &gecos, - &primary_gid ); - } - - if (gecos == NULL) { - gecos = ads_pull_string(ads, mem_ctx, msg, "name"); - } - - if (!ads_pull_sid(ads, msg, "objectSid", - &(*info)[i].user_sid)) { - DEBUG(1,("No sid for %s !?\n", name)); - continue; - } - if (!ads_pull_uint32(ads, msg, "primaryGroupID", &group)) { - DEBUG(1,("No primary group for %s !?\n", name)); - continue; - } - - (*info)[i].acct_name = name; - (*info)[i].full_name = gecos; - (*info)[i].homedir = homedir; - (*info)[i].shell = shell; - (*info)[i].primary_gid = primary_gid; - sid_compose(&(*info)[i].group_sid, &domain->sid, group); - i++; - } - - (*num_entries) = i; - status = NT_STATUS_OK; - - DEBUG(3,("ads query_user_list gave %d entries\n", (*num_entries))); - -done: - if (res) - ads_msgfree(ads, res); - - return status; -} - -/* list all domain groups */ -static NTSTATUS enum_dom_groups(struct winbindd_domain *domain, - TALLOC_CTX *mem_ctx, - uint32 *num_entries, - struct acct_info **info) -{ - ADS_STRUCT *ads = NULL; - const char *attrs[] = {"userPrincipalName", "sAMAccountName", - "name", "objectSid", NULL}; - int i, count; - ADS_STATUS rc; - LDAPMessage *res = NULL; - LDAPMessage *msg = NULL; - NTSTATUS status = NT_STATUS_UNSUCCESSFUL; - const char *filter; - BOOL enum_dom_local_groups = False; - - *num_entries = 0; - - DEBUG(3,("ads: enum_dom_groups\n")); - - if ( !winbindd_can_contact_domain( domain ) ) { - DEBUG(10,("enum_dom_groups: No incoming trust for domain %s\n", - domain->name)); - return NT_STATUS_OK; - } - - /* only grab domain local groups for our domain */ - if ( domain->active_directory && strequal(lp_realm(), domain->alt_name) ) { - enum_dom_local_groups = True; - } - - /* Workaround ADS LDAP bug present in MS W2K3 SP0 and W2K SP4 w/o - * rollup-fixes: - * - * According to Section 5.1(4) of RFC 2251 if a value of a type is it's - * default value, it MUST be absent. In case of extensible matching the - * "dnattr" boolean defaults to FALSE and so it must be only be present - * when set to TRUE. - * - * When it is set to FALSE and the OpenLDAP lib (correctly) encodes a - * filter using bitwise matching rule then the buggy AD fails to decode - * the extensible match. As a workaround set it to TRUE and thereby add - * the dnAttributes "dn" field to cope with those older AD versions. - * It should not harm and won't put any additional load on the AD since - * none of the dn components have a bitmask-attribute. - * - * Thanks to Ralf Haferkamp for input and testing - Guenther */ - - filter = talloc_asprintf(mem_ctx, "(&(objectCategory=group)(&(groupType:dn:%s:=%d)(!(groupType:dn:%s:=%d))))", - ADS_LDAP_MATCHING_RULE_BIT_AND, GROUP_TYPE_SECURITY_ENABLED, - ADS_LDAP_MATCHING_RULE_BIT_AND, - enum_dom_local_groups ? GROUP_TYPE_BUILTIN_LOCAL_GROUP : GROUP_TYPE_RESOURCE_GROUP); - - if (filter == NULL) { - status = NT_STATUS_NO_MEMORY; - goto done; - } - - ads = ads_cached_connection(domain); - - if (!ads) { - domain->last_status = NT_STATUS_SERVER_DISABLED; - goto done; - } - - rc = ads_search_retry(ads, &res, filter, attrs); - if (!ADS_ERR_OK(rc) || !res) { - DEBUG(1,("enum_dom_groups ads_search: %s\n", ads_errstr(rc))); - goto done; - } - - count = ads_count_replies(ads, res); - if (count == 0) { - DEBUG(1,("enum_dom_groups: No groups found\n")); - goto done; - } - - (*info) = TALLOC_ZERO_ARRAY(mem_ctx, struct acct_info, count); - if (!*info) { - status = NT_STATUS_NO_MEMORY; - goto done; - } - - i = 0; - - for (msg = ads_first_entry(ads, res); msg; msg = ads_next_entry(ads, msg)) { - char *name, *gecos; - DOM_SID sid; - uint32 rid; - - name = ads_pull_username(ads, mem_ctx, msg); - gecos = ads_pull_string(ads, mem_ctx, msg, "name"); - if (!ads_pull_sid(ads, msg, "objectSid", &sid)) { - DEBUG(1,("No sid for %s !?\n", name)); - continue; - } - - if (!sid_peek_check_rid(&domain->sid, &sid, &rid)) { - DEBUG(1,("No rid for %s !?\n", name)); - continue; - } - - fstrcpy((*info)[i].acct_name, name); - fstrcpy((*info)[i].acct_desc, gecos); - (*info)[i].rid = rid; - i++; - } - - (*num_entries) = i; - - status = NT_STATUS_OK; - - DEBUG(3,("ads enum_dom_groups gave %d entries\n", (*num_entries))); - -done: - if (res) - ads_msgfree(ads, res); - - return status; -} - -/* list all domain local groups */ -static NTSTATUS enum_local_groups(struct winbindd_domain *domain, - TALLOC_CTX *mem_ctx, - uint32 *num_entries, - struct acct_info **info) -{ - /* - * This is a stub function only as we returned the domain - * local groups in enum_dom_groups() if the domain->native field - * was true. This is a simple performance optimization when - * using LDAP. - * - * if we ever need to enumerate domain local groups separately, - * then this the optimization in enum_dom_groups() will need - * to be split out - */ - *num_entries = 0; - - return NT_STATUS_OK; -} - -/* If you are looking for "dn_lookup": Yes, it used to be here! - * It has gone now since it was a major speed bottleneck in - * lookup_groupmem (its only use). It has been replaced by - * an rpc lookup sids call... R.I.P. */ - -/* Lookup user information from a rid */ -static NTSTATUS query_user(struct winbindd_domain *domain, - TALLOC_CTX *mem_ctx, - const DOM_SID *sid, - WINBIND_USERINFO *info) -{ - ADS_STRUCT *ads = NULL; - const char *attrs[] = { "*", NULL }; - ADS_STATUS rc; - int count; - LDAPMessage *msg = NULL; - char *ldap_exp; - char *sidstr; - uint32 group_rid; - NTSTATUS status = NT_STATUS_UNSUCCESSFUL; - NET_USER_INFO_3 *user; - - DEBUG(3,("ads: query_user\n")); - - info->homedir = NULL; - info->shell = NULL; - info->primary_gid = (gid_t)-1; - - /* try netsamlogon cache first */ - - if ( (user = netsamlogon_cache_get( mem_ctx, sid )) != NULL ) - { - - DEBUG(5,("query_user: Cache lookup succeeded for %s\n", - sid_string_static(sid))); - - sid_compose(&info->user_sid, &domain->sid, user->user_rid); - sid_compose(&info->group_sid, &domain->sid, user->group_rid); - - info->acct_name = unistr2_tdup(mem_ctx, &user->uni_user_name); - info->full_name = unistr2_tdup(mem_ctx, &user->uni_full_name); - - nss_get_info_cached( domain, sid, mem_ctx, NULL, NULL, - &info->homedir, &info->shell, &info->full_name, - &info->primary_gid ); - - TALLOC_FREE(user); - - return NT_STATUS_OK; - } - - if ( !winbindd_can_contact_domain(domain)) { - DEBUG(8,("query_user: No incoming trust from domain %s\n", - domain->name)); - - /* We still need to generate some basic information - about the user even if we cannot contact the - domain. Most of this stuff we can deduce. */ - - sid_copy( &info->user_sid, sid ); - - /* Assume "Domain Users" for the primary group */ - - sid_compose(&info->group_sid, &domain->sid, DOMAIN_GROUP_RID_USERS ); - - /* Try to fill in what the nss_info backend can do */ - - nss_get_info_cached( domain, sid, mem_ctx, NULL, NULL, - &info->homedir, &info->shell, &info->full_name, - &info->primary_gid ); - - status = NT_STATUS_OK; - goto done; - } - - /* no cache...do the query */ - - if ( (ads = ads_cached_connection(domain)) == NULL ) { - domain->last_status = NT_STATUS_SERVER_DISABLED; - goto done; - } - - sidstr = sid_binstring(sid); - asprintf(&ldap_exp, "(objectSid=%s)", sidstr); - rc = ads_search_retry(ads, &msg, ldap_exp, attrs); - free(ldap_exp); - free(sidstr); - if (!ADS_ERR_OK(rc) || !msg) { - DEBUG(1,("query_user(sid=%s) ads_search: %s\n", - sid_string_static(sid), ads_errstr(rc))); - goto done; - } - - count = ads_count_replies(ads, msg); - if (count != 1) { - DEBUG(1,("query_user(sid=%s): Not found\n", - sid_string_static(sid))); - goto done; - } - - info->acct_name = ads_pull_username(ads, mem_ctx, msg); - - nss_get_info_cached( domain, sid, mem_ctx, ads, msg, - &info->homedir, &info->shell, &info->full_name, - &info->primary_gid ); - - if (info->full_name == NULL) { - info->full_name = ads_pull_string(ads, mem_ctx, msg, "name"); - } - - if (!ads_pull_uint32(ads, msg, "primaryGroupID", &group_rid)) { - DEBUG(1,("No primary group for %s !?\n", - sid_string_static(sid))); - goto done; - } - - sid_copy(&info->user_sid, sid); - sid_compose(&info->group_sid, &domain->sid, group_rid); - - status = NT_STATUS_OK; - - DEBUG(3,("ads query_user gave %s\n", info->acct_name)); -done: - if (msg) - ads_msgfree(ads, msg); - - return status; -} - -/* Lookup groups a user is a member of - alternate method, for when - tokenGroups are not available. */ -static NTSTATUS lookup_usergroups_member(struct winbindd_domain *domain, - TALLOC_CTX *mem_ctx, - const char *user_dn, - DOM_SID *primary_group, - size_t *p_num_groups, DOM_SID **user_sids) -{ - ADS_STATUS rc; - NTSTATUS status = NT_STATUS_UNSUCCESSFUL; - int count; - LDAPMessage *res = NULL; - LDAPMessage *msg = NULL; - char *ldap_exp; - ADS_STRUCT *ads; - const char *group_attrs[] = {"objectSid", NULL}; - char *escaped_dn; - size_t num_groups = 0; - - DEBUG(3,("ads: lookup_usergroups_member\n")); - - if ( !winbindd_can_contact_domain( domain ) ) { - DEBUG(10,("lookup_usergroups_members: No incoming trust for domain %s\n", - domain->name)); - return NT_STATUS_OK; - } - - ads = ads_cached_connection(domain); - - if (!ads) { - domain->last_status = NT_STATUS_SERVER_DISABLED; - goto done; - } - - if (!(escaped_dn = escape_ldap_string_alloc(user_dn))) { - status = NT_STATUS_NO_MEMORY; - goto done; - } - - ldap_exp = talloc_asprintf(mem_ctx, - "(&(member=%s)(objectCategory=group)(groupType:dn:%s:=%d))", - escaped_dn, - ADS_LDAP_MATCHING_RULE_BIT_AND, - GROUP_TYPE_SECURITY_ENABLED); - if (!ldap_exp) { - DEBUG(1,("lookup_usergroups(dn=%s) asprintf failed!\n", user_dn)); - SAFE_FREE(escaped_dn); - status = NT_STATUS_NO_MEMORY; - goto done; - } - - SAFE_FREE(escaped_dn); - - rc = ads_search_retry(ads, &res, ldap_exp, group_attrs); - - if (!ADS_ERR_OK(rc) || !res) { - DEBUG(1,("lookup_usergroups ads_search member=%s: %s\n", user_dn, ads_errstr(rc))); - return ads_ntstatus(rc); - } - - count = ads_count_replies(ads, res); - - *user_sids = NULL; - num_groups = 0; - - /* always add the primary group to the sid array */ - if (!add_sid_to_array(mem_ctx, primary_group, user_sids, &num_groups)) { - status = NT_STATUS_NO_MEMORY; - goto done; - } - - if (count > 0) { - for (msg = ads_first_entry(ads, res); msg; - msg = ads_next_entry(ads, msg)) { - DOM_SID group_sid; - - if (!ads_pull_sid(ads, msg, "objectSid", &group_sid)) { - DEBUG(1,("No sid for this group ?!?\n")); - continue; - } - - /* ignore Builtin groups from ADS - Guenther */ - if (sid_check_is_in_builtin(&group_sid)) { - continue; - } - - if (!add_sid_to_array(mem_ctx, &group_sid, user_sids, - &num_groups)) { - status = NT_STATUS_NO_MEMORY; - goto done; - } - } - - } - - *p_num_groups = num_groups; - status = (user_sids != NULL) ? NT_STATUS_OK : NT_STATUS_NO_MEMORY; - - DEBUG(3,("ads lookup_usergroups (member) succeeded for dn=%s\n", user_dn)); -done: - if (res) - ads_msgfree(ads, res); - - return status; -} - -/* Lookup groups a user is a member of - alternate method, for when - tokenGroups are not available. */ -static NTSTATUS lookup_usergroups_memberof(struct winbindd_domain *domain, - TALLOC_CTX *mem_ctx, - const char *user_dn, - DOM_SID *primary_group, - size_t *p_num_groups, DOM_SID **user_sids) -{ - ADS_STATUS rc; - NTSTATUS status = NT_STATUS_UNSUCCESSFUL; - ADS_STRUCT *ads; - const char *attrs[] = {"memberOf", NULL}; - size_t num_groups = 0; - DOM_SID *group_sids = NULL; - int i; - char **strings; - size_t num_strings = 0; - - - DEBUG(3,("ads: lookup_usergroups_memberof\n")); - - if ( !winbindd_can_contact_domain( domain ) ) { - DEBUG(10,("lookup_usergroups_memberof: No incoming trust for domain %s\n", - domain->name)); - return NT_STATUS_OK; - } - - ads = ads_cached_connection(domain); - - if (!ads) { - domain->last_status = NT_STATUS_SERVER_DISABLED; - goto done; - } - - rc = ads_search_retry_extended_dn_ranged(ads, mem_ctx, user_dn, attrs, - ADS_EXTENDED_DN_HEX_STRING, - &strings, &num_strings); - - if (!ADS_ERR_OK(rc)) { - DEBUG(1,("lookup_usergroups_memberof ads_search member=%s: %s\n", - user_dn, ads_errstr(rc))); - return ads_ntstatus(rc); - } - - *user_sids = NULL; - num_groups = 0; - - /* always add the primary group to the sid array */ - if (!add_sid_to_array(mem_ctx, primary_group, user_sids, &num_groups)) { - status = NT_STATUS_NO_MEMORY; - goto done; - } - - group_sids = TALLOC_ZERO_ARRAY(mem_ctx, DOM_SID, num_strings + 1); - if (!group_sids) { - TALLOC_FREE(strings); - status = NT_STATUS_NO_MEMORY; - goto done; - } - - for (i=0; iname)); - - /* Tell the cache manager not to remember this one */ - - return NT_STATUS_SYNCHRONIZATION_REQUIRED; - } - - ads = ads_cached_connection(domain); - - if (!ads) { - domain->last_status = NT_STATUS_SERVER_DISABLED; - status = NT_STATUS_SERVER_DISABLED; - goto done; - } - - rc = ads_search_retry_sid(ads, &msg, sid, attrs); - - if (!ADS_ERR_OK(rc)) { - status = ads_ntstatus(rc); - DEBUG(1,("lookup_usergroups(sid=%s) ads_search tokenGroups: %s\n", - sid_to_string(sid_string, sid), ads_errstr(rc))); - goto done; - } - - count = ads_count_replies(ads, msg); - if (count != 1) { - status = NT_STATUS_UNSUCCESSFUL; - DEBUG(1,("lookup_usergroups(sid=%s) ads_search tokenGroups: " - "invalid number of results (count=%d)\n", - sid_to_string(sid_string, sid), count)); - goto done; - } - - if (!msg) { - DEBUG(1,("lookup_usergroups(sid=%s) ads_search tokenGroups: NULL msg\n", - sid_to_string(sid_string, sid))); - status = NT_STATUS_UNSUCCESSFUL; - goto done; - } - - user_dn = ads_get_dn(ads, msg); - if (user_dn == NULL) { - status = NT_STATUS_NO_MEMORY; - goto done; - } - - if (!ads_pull_uint32(ads, msg, "primaryGroupID", &primary_group_rid)) { - DEBUG(1,("%s: No primary group for sid=%s !?\n", - domain->name, sid_to_string(sid_string, sid))); - goto done; - } - - sid_copy(&primary_group, &domain->sid); - sid_append_rid(&primary_group, primary_group_rid); - - count = ads_pull_sids(ads, mem_ctx, msg, "tokenGroups", &sids); - - /* there must always be at least one group in the token, - unless we are talking to a buggy Win2k server */ - - /* actually this only happens when the machine account has no read - * permissions on the tokenGroup attribute - gd */ - - if (count == 0) { - - /* no tokenGroups */ - - /* lookup what groups this user is a member of by DN search on - * "memberOf" */ - - status = lookup_usergroups_memberof(domain, mem_ctx, user_dn, - &primary_group, - &num_groups, user_sids); - *p_num_groups = (uint32)num_groups; - if (NT_STATUS_IS_OK(status)) { - goto done; - } - - /* lookup what groups this user is a member of by DN search on - * "member" */ - - status = lookup_usergroups_member(domain, mem_ctx, user_dn, - &primary_group, - &num_groups, user_sids); - *p_num_groups = (uint32)num_groups; - goto done; - } - - *user_sids = NULL; - num_groups = 0; - - if (!add_sid_to_array(mem_ctx, &primary_group, user_sids, &num_groups)) { - status = NT_STATUS_NO_MEMORY; - goto done; - } - - for (i=0;iname, - sid_string_static(group_sid))); - - *num_names = 0; - - tmp_ctx = talloc_new(mem_ctx); - if (!tmp_ctx) { - DEBUG(1, ("ads: lookup_groupmem: talloc failed\n")); - status = NT_STATUS_NO_MEMORY; - goto done; - } - - if ( !winbindd_can_contact_domain( domain ) ) { - DEBUG(10,("lookup_groupmem: No incoming trust for domain %s\n", - domain->name)); - return NT_STATUS_OK; - } - - ads = ads_cached_connection(domain); - - if (!ads) { - domain->last_status = NT_STATUS_SERVER_DISABLED; - goto done; - } - - if ((sidbinstr = sid_binstring(group_sid)) == NULL) { - status = NT_STATUS_NO_MEMORY; - goto done; - } - - /* search for all members of the group */ - if (!(ldap_exp = talloc_asprintf(tmp_ctx, "(objectSid=%s)", - sidbinstr))) - { - SAFE_FREE(sidbinstr); - DEBUG(1, ("ads: lookup_groupmem: talloc_asprintf for ldap_exp failed!\n")); - status = NT_STATUS_NO_MEMORY; - goto done; - } - SAFE_FREE(sidbinstr); - - args.control = ADS_EXTENDED_DN_OID; - args.val = ADS_EXTENDED_DN_HEX_STRING; - args.critical = True; - - rc = ads_ranged_search(ads, tmp_ctx, LDAP_SCOPE_SUBTREE, ads->config.bind_path, - ldap_exp, &args, "member", &members, &num_members); - - if (!ADS_ERR_OK(rc)) { - DEBUG(0,("ads_ranged_search failed with: %s\n", ads_errstr(rc))); - status = NT_STATUS_UNSUCCESSFUL; - goto done; - } - - DEBUG(10, ("ads lookup_groupmem: got %d sids via extended dn call\n", (int)num_members)); - - /* Now that we have a list of sids, we need to get the - * lists of names and name_types belonging to these sids. - * even though conceptually not quite clean, we use the - * RPC call lsa_lookup_sids for this since it can handle a - * list of sids. ldap calls can just resolve one sid at a time. - * - * At this stage, the sids are still hidden in the exetended dn - * member output format. We actually do a little better than - * stated above: In extracting the sids from the member strings, - * we try to resolve as many sids as possible from the - * cache. Only the rest is passed to the lsa_lookup_sids call. */ - - if (num_members) { - (*sid_mem) = TALLOC_ZERO_ARRAY(mem_ctx, DOM_SID, num_members); - (*names) = TALLOC_ZERO_ARRAY(mem_ctx, char *, num_members); - (*name_types) = TALLOC_ZERO_ARRAY(mem_ctx, uint32, num_members); - (sid_mem_nocache) = TALLOC_ZERO_ARRAY(tmp_ctx, DOM_SID, num_members); - - if ((members == NULL) || (*sid_mem == NULL) || - (*names == NULL) || (*name_types == NULL) || - (sid_mem_nocache == NULL)) - { - DEBUG(1, ("ads: lookup_groupmem: talloc failed\n")); - status = NT_STATUS_NO_MEMORY; - goto done; - } - } - else { - (*sid_mem) = NULL; - (*names) = NULL; - (*name_types) = NULL; - } - - for (i=0; i 0) { - - status = cm_connect_lsa(domain, tmp_ctx, &cli, &lsa_policy); - - if (!NT_STATUS_IS_OK(status)) { - goto done; - } - - status = rpccli_lsa_lookup_sids_all(cli, tmp_ctx, - &lsa_policy, - num_nocache, - sid_mem_nocache, - &domains_nocache, - &names_nocache, - &name_types_nocache); - - if (NT_STATUS_IS_OK(status) || - NT_STATUS_EQUAL(status, STATUS_SOME_UNMAPPED)) - { - /* Copy the entries over from the "_nocache" arrays - * to the result arrays, skipping the gaps the - * lookup_sids call left. */ - for (i=0; i < num_nocache; i++) { - if (((names_nocache)[i] != NULL) && - ((name_types_nocache)[i] != SID_NAME_UNKNOWN)) - { - sid_copy(&(*sid_mem)[*num_names], - &sid_mem_nocache[i]); - (*names)[*num_names] = talloc_asprintf( *names, - "%s%c%s", - domains_nocache[i], - *lp_winbind_separator(), - names_nocache[i] ); - (*name_types)[*num_names] = name_types_nocache[i]; - (*num_names)++; - } - } - } - else if (NT_STATUS_EQUAL(status, NT_STATUS_NONE_MAPPED)) { - DEBUG(10, ("lookup_groupmem: lsa_lookup_sids could " - "not map any SIDs at all.\n")); - /* Don't handle this as an error here. - * There is nothing left to do with respect to the - * overall result... */ - } - else if (!NT_STATUS_IS_OK(status)) { - DEBUG(10, ("lookup_groupmem: Error looking up %d " - "sids via rpc_lsa_lookup_sids: %s\n", - (int)num_members, nt_errstr(status))); - goto done; - } - } - - status = NT_STATUS_OK; - DEBUG(3,("ads lookup_groupmem for sid=%s succeeded\n", - sid_string_static(group_sid))); - -done: - - TALLOC_FREE(tmp_ctx); - - return status; -} - -/* find the sequence number for a domain */ -static NTSTATUS sequence_number(struct winbindd_domain *domain, uint32 *seq) -{ - ADS_STRUCT *ads = NULL; - ADS_STATUS rc; - - DEBUG(3,("ads: fetch sequence_number for %s\n", domain->name)); - - if ( !winbindd_can_contact_domain( domain ) ) { - DEBUG(10,("sequence: No incoming trust for domain %s\n", - domain->name)); - *seq = time(NULL); - return NT_STATUS_OK; - } - - *seq = DOM_SEQUENCE_NONE; - - ads = ads_cached_connection(domain); - - if (!ads) { - domain->last_status = NT_STATUS_SERVER_DISABLED; - return NT_STATUS_UNSUCCESSFUL; - } - - rc = ads_USN(ads, seq); - - if (!ADS_ERR_OK(rc)) { - - /* its a dead connection, destroy it */ - - if (domain->private_data) { - ads = (ADS_STRUCT *)domain->private_data; - ads->is_mine = True; - ads_destroy(&ads); - ads_kdestroy("MEMORY:winbind_ccache"); - domain->private_data = NULL; - } - } - return ads_ntstatus(rc); -} - -/* get a list of trusted domains */ -static NTSTATUS trusted_domains(struct winbindd_domain *domain, - TALLOC_CTX *mem_ctx, - uint32 *num_domains, - char ***names, - char ***alt_names, - DOM_SID **dom_sids) -{ - NTSTATUS result = NT_STATUS_UNSUCCESSFUL; - struct ds_domain_trust *domains = NULL; - int count = 0; - int i; - uint32 flags; - struct rpc_pipe_client *cli; - uint32 fr_flags = (DS_DOMAIN_IN_FOREST | DS_DOMAIN_TREE_ROOT); - int ret_count; - - DEBUG(3,("ads: trusted_domains\n")); - - *num_domains = 0; - *alt_names = NULL; - *names = NULL; - *dom_sids = NULL; - - /* If this is our primary domain or a root in our forest, - query for all trusts. If not, then just look for domain - trusts in the target forest */ - - if ( domain->primary || - ((domain->domain_flags&fr_flags) == fr_flags) ) - { - flags = DS_DOMAIN_DIRECT_OUTBOUND | - DS_DOMAIN_DIRECT_INBOUND | - DS_DOMAIN_IN_FOREST; - } else { - flags = DS_DOMAIN_IN_FOREST; - } - - result = cm_connect_netlogon(domain, &cli); - - if (!NT_STATUS_IS_OK(result)) { - DEBUG(5, ("trusted_domains: Could not open a connection to %s " - "for PIPE_NETLOGON (%s)\n", - domain->name, nt_errstr(result))); - return NT_STATUS_UNSUCCESSFUL; - } - - if ( NT_STATUS_IS_OK(result) ) { - result = rpccli_ds_enum_domain_trusts(cli, mem_ctx, - cli->cli->desthost, - flags, &domains, - (unsigned int *)&count); - } - - if ( NT_STATUS_IS_OK(result) && count) { - - /* Allocate memory for trusted domain names and sids */ - - if ( !(*names = TALLOC_ARRAY(mem_ctx, char *, count)) ) { - DEBUG(0, ("trusted_domains: out of memory\n")); - return NT_STATUS_NO_MEMORY; - } - - if ( !(*alt_names = TALLOC_ARRAY(mem_ctx, char *, count)) ) { - DEBUG(0, ("trusted_domains: out of memory\n")); - return NT_STATUS_NO_MEMORY; - } - - if ( !(*dom_sids = TALLOC_ARRAY(mem_ctx, DOM_SID, count)) ) { - DEBUG(0, ("trusted_domains: out of memory\n")); - return NT_STATUS_NO_MEMORY; - } - - /* Copy across names and sids */ - - - ret_count = 0; - for (i = 0; i < count; i++) { - struct winbindd_domain d; - - /* drop external trusts if this is not our primary - domain. This means that the returned number of - domains may be less that the ones actually trusted - by the DC. */ - - if ( (domains[i].trust_attributes == DS_DOMAIN_TRUST_ATTRIB_QUARANTINED_DOMAIN) && - !domain->primary ) - { - DEBUG(10,("trusted_domains: Skipping external trusted domain " - "%s because it is outside of our primary domain\n", - domains[i].netbios_domain)); - continue; - } - - (*names)[ret_count] = domains[i].netbios_domain; - (*alt_names)[ret_count] = domains[i].dns_domain; - sid_copy(&(*dom_sids)[ret_count], &domains[i].sid); - - /* add to the trusted domain cache */ - - fstrcpy( d.name, domains[i].netbios_domain ); - fstrcpy( d.alt_name, domains[i].dns_domain ); - sid_copy( &d.sid, &domains[i].sid ); - - /* This gets a little tricky. If we are - following a transitive forest trust, then - innerit the flags, type, and attrins from - the domain we queried to make sure we don't - record the view of the trust from the wrong - side. Always view it from the side of our - primary domain. --jerry */ - if ( domain->primary || - ((domain->domain_flags&fr_flags) == fr_flags) ) - { - DEBUG(10,("trusted_domains(ads): Storing trust " - "flags for domain %s\n", d.alt_name)); - - /* Look this up in cache to make sure - we have the current trust flags and - attributes */ - - d.domain_flags = domains[i].flags; - d.domain_type = domains[i].trust_type; - d.domain_trust_attribs = domains[i].trust_attributes; - } else { - DEBUG(10,("trusted_domains(ads): Inheriting trust " - "flags for domain %s\n", d.alt_name)); - d.domain_flags = domain->domain_flags; - d.domain_type = domain->domain_type; - d.domain_trust_attribs = domain->domain_trust_attribs; - } - - wcache_tdc_add_domain( &d ); - - ret_count++; - - } - - *num_domains = ret_count; - } - - return result; -} - -/* the ADS backend methods are exposed via this structure */ -struct winbindd_methods ads_methods = { - True, - query_user_list, - enum_dom_groups, - enum_local_groups, - msrpc_name_to_sid, - msrpc_sid_to_name, - msrpc_rids_to_names, - query_user, - lookup_usergroups, - msrpc_lookup_useraliases, - lookup_groupmem, - sequence_number, - msrpc_lockout_policy, - msrpc_password_policy, - trusted_domains, -}; - -#endif diff --git a/source3/nsswitch/winbindd_async.c b/source3/nsswitch/winbindd_async.c deleted file mode 100644 index 5d31ff0a41..0000000000 --- a/source3/nsswitch/winbindd_async.c +++ /dev/null @@ -1,1695 +0,0 @@ -/* - Unix SMB/CIFS implementation. - - Async helpers for blocking functions - - Copyright (C) Volker Lendecke 2005 - Copyright (C) Gerald Carter 2006 - - The helpers always consist of three functions: - - * A request setup function that takes the necessary parameters together - with a continuation function that is to be called upon completion - - * A private continuation function that is internal only. This is to be - called by the lower-level functions in do_async(). Its only task is to - properly call the continuation function named above. - - * A worker function that is called inside the appropriate child process. - - 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 3 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, see . -*/ - -#include "includes.h" -#include "winbindd.h" - -#undef DBGC_CLASS -#define DBGC_CLASS DBGC_WINBIND - -struct do_async_state { - TALLOC_CTX *mem_ctx; - struct winbindd_request request; - struct winbindd_response response; - void (*cont)(TALLOC_CTX *mem_ctx, - BOOL success, - struct winbindd_response *response, - void *c, void *private_data); - void *c, *private_data; -}; - -static void do_async_recv(void *private_data, BOOL success) -{ - struct do_async_state *state = - talloc_get_type_abort(private_data, struct do_async_state); - - state->cont(state->mem_ctx, success, &state->response, - state->c, state->private_data); -} - -static void do_async(TALLOC_CTX *mem_ctx, struct winbindd_child *child, - const struct winbindd_request *request, - void (*cont)(TALLOC_CTX *mem_ctx, BOOL success, - struct winbindd_response *response, - void *c, void *private_data), - void *c, void *private_data) -{ - struct do_async_state *state; - - state = TALLOC_ZERO_P(mem_ctx, struct do_async_state); - if (state == NULL) { - DEBUG(0, ("talloc failed\n")); - cont(mem_ctx, False, NULL, c, private_data); - return; - } - - state->mem_ctx = mem_ctx; - state->request = *request; - state->request.length = sizeof(state->request); - state->cont = cont; - state->c = c; - state->private_data = private_data; - - async_request(mem_ctx, child, &state->request, - &state->response, do_async_recv, state); -} - -void do_async_domain(TALLOC_CTX *mem_ctx, struct winbindd_domain *domain, - const struct winbindd_request *request, - void (*cont)(TALLOC_CTX *mem_ctx, BOOL success, - struct winbindd_response *response, - void *c, void *private_data), - void *c, void *private_data) -{ - struct do_async_state *state; - - state = TALLOC_ZERO_P(mem_ctx, struct do_async_state); - if (state == NULL) { - DEBUG(0, ("talloc failed\n")); - cont(mem_ctx, False, NULL, c, private_data); - return; - } - - state->mem_ctx = mem_ctx; - state->request = *request; - state->request.length = sizeof(state->request); - state->cont = cont; - state->c = c; - state->private_data = private_data; - - async_domain_request(mem_ctx, domain, &state->request, - &state->response, do_async_recv, state); -} - -static void winbindd_set_mapping_recv(TALLOC_CTX *mem_ctx, BOOL success, - struct winbindd_response *response, - void *c, void *private_data) -{ - void (*cont)(void *priv, BOOL succ) = (void (*)(void *, BOOL))c; - - if (!success) { - DEBUG(5, ("Could not trigger idmap_set_mapping\n")); - cont(private_data, False); - return; - } - - if (response->result != WINBINDD_OK) { - DEBUG(5, ("idmap_set_mapping returned an error\n")); - cont(private_data, False); - return; - } - - cont(private_data, True); -} - -void winbindd_set_mapping_async(TALLOC_CTX *mem_ctx, const struct id_map *map, - void (*cont)(void *private_data, BOOL success), - void *private_data) -{ - struct winbindd_request request; - ZERO_STRUCT(request); - request.cmd = WINBINDD_DUAL_SET_MAPPING; - request.data.dual_idmapset.id = map->xid.id; - request.data.dual_idmapset.type = map->xid.type; - sid_to_string(request.data.dual_idmapset.sid, map->sid); - - do_async(mem_ctx, idmap_child(), &request, winbindd_set_mapping_recv, - (void *)cont, private_data); -} - -enum winbindd_result winbindd_dual_set_mapping(struct winbindd_domain *domain, - struct winbindd_cli_state *state) -{ - struct id_map map; - DOM_SID sid; - NTSTATUS result; - - DEBUG(3, ("[%5lu]: dual_idmapset\n", (unsigned long)state->pid)); - - if (!string_to_sid(&sid, state->request.data.dual_idmapset.sid)) - return WINBINDD_ERROR; - - map.sid = &sid; - map.xid.id = state->request.data.dual_idmapset.id; - map.xid.type = state->request.data.dual_idmapset.type; - map.status = ID_MAPPED; - - result = idmap_set_mapping(&map); - return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR; -} - -static void winbindd_set_hwm_recv(TALLOC_CTX *mem_ctx, BOOL success, - struct winbindd_response *response, - void *c, void *private_data) -{ - void (*cont)(void *priv, BOOL succ) = (void (*)(void *, BOOL))c; - - if (!success) { - DEBUG(5, ("Could not trigger idmap_set_hwm\n")); - cont(private_data, False); - return; - } - - if (response->result != WINBINDD_OK) { - DEBUG(5, ("idmap_set_hwm returned an error\n")); - cont(private_data, False); - return; - } - - cont(private_data, True); -} - -void winbindd_set_hwm_async(TALLOC_CTX *mem_ctx, const struct unixid *xid, - void (*cont)(void *private_data, BOOL success), - void *private_data) -{ - struct winbindd_request request; - ZERO_STRUCT(request); - request.cmd = WINBINDD_DUAL_SET_HWM; - request.data.dual_idmapset.id = xid->id; - request.data.dual_idmapset.type = xid->type; - - do_async(mem_ctx, idmap_child(), &request, winbindd_set_hwm_recv, - (void *)cont, private_data); -} - -enum winbindd_result winbindd_dual_set_hwm(struct winbindd_domain *domain, - struct winbindd_cli_state *state) -{ - struct unixid xid; - NTSTATUS result; - - DEBUG(3, ("[%5lu]: dual_set_hwm\n", (unsigned long)state->pid)); - - xid.id = state->request.data.dual_idmapset.id; - xid.type = state->request.data.dual_idmapset.type; - - switch (xid.type) { - case ID_TYPE_UID: - result = idmap_set_uid_hwm(&xid); - break; - case ID_TYPE_GID: - result = idmap_set_gid_hwm(&xid); - break; - default: - return WINBINDD_ERROR; - } - return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR; -} - -static void winbindd_sids2xids_recv(TALLOC_CTX *mem_ctx, BOOL success, - struct winbindd_response *response, - void *c, void *private_data) -{ - void (*cont)(void *priv, BOOL succ, void *, int) = - (void (*)(void *, BOOL, void *, int))c; - - if (!success) { - DEBUG(5, ("Could not trigger sids2xids\n")); - cont(private_data, False, NULL, 0); - return; - } - - if (response->result != WINBINDD_OK) { - DEBUG(5, ("sids2xids returned an error\n")); - cont(private_data, False, NULL, 0); - return; - } - - cont(private_data, True, response->extra_data.data, response->length - sizeof(response)); -} - -void winbindd_sids2xids_async(TALLOC_CTX *mem_ctx, void *sids, int size, - void (*cont)(void *private_data, BOOL success, void *data, int len), - void *private_data) -{ - struct winbindd_request request; - ZERO_STRUCT(request); - request.cmd = WINBINDD_DUAL_SIDS2XIDS; - request.extra_data.data = (char *)sids; - request.extra_len = size; - do_async(mem_ctx, idmap_child(), &request, winbindd_sids2xids_recv, - (void *)cont, private_data); -} - -enum winbindd_result winbindd_dual_sids2xids(struct winbindd_domain *domain, - struct winbindd_cli_state *state) -{ - DOM_SID *sids; - struct unixid *xids; - struct id_map **ids; - NTSTATUS result; - int num, i; - - DEBUG(3, ("[%5lu]: sids to unix ids\n", (unsigned long)state->pid)); - - if (state->request.extra_len == 0) { - DEBUG(0, ("Invalid buffer size!\n")); - return WINBINDD_ERROR; - } - - sids = (DOM_SID *)state->request.extra_data.data; - num = state->request.extra_len / sizeof(DOM_SID); - - ids = TALLOC_ZERO_ARRAY(state->mem_ctx, struct id_map *, num + 1); - if ( ! ids) { - DEBUG(0, ("Out of memory!\n")); - return WINBINDD_ERROR; - } - for (i = 0; i < num; i++) { - ids[i] = TALLOC_P(ids, struct id_map); - if ( ! ids[i]) { - DEBUG(0, ("Out of memory!\n")); - talloc_free(ids); - return WINBINDD_ERROR; - } - ids[i]->sid = &sids[i]; - } - - result = idmap_sids_to_unixids(ids); - - if (NT_STATUS_IS_OK(result)) { - - xids = SMB_MALLOC_ARRAY(struct unixid, num); - if ( ! xids) { - DEBUG(0, ("Out of memory!\n")); - talloc_free(ids); - return WINBINDD_ERROR; - } - - for (i = 0; i < num; i++) { - if (ids[i]->status == ID_MAPPED) { - xids[i].type = ids[i]->xid.type; - xids[i].id = ids[i]->xid.id; - } else { - xids[i].type = -1; - } - } - - state->response.length = sizeof(state->response) + (sizeof(struct unixid) * num); - state->response.extra_data.data = xids; - - } else { - DEBUG (2, ("idmap_sids_to_unixids returned an error: 0x%08x\n", NT_STATUS_V(result))); - talloc_free(ids); - return WINBINDD_ERROR; - } - - talloc_free(ids); - return WINBINDD_OK; -} - -static void winbindd_sid2uid_recv(TALLOC_CTX *mem_ctx, BOOL success, - struct winbindd_response *response, - void *c, void *private_data) -{ - void (*cont)(void *priv, BOOL succ, uid_t uid) = - (void (*)(void *, BOOL, uid_t))c; - - if (!success) { - DEBUG(5, ("Could not trigger sid2uid\n")); - cont(private_data, False, 0); - return; - } - - if (response->result != WINBINDD_OK) { - DEBUG(5, ("sid2uid returned an error\n")); - cont(private_data, False, 0); - return; - } - - cont(private_data, True, response->data.uid); -} - -void winbindd_sid2uid_async(TALLOC_CTX *mem_ctx, const DOM_SID *sid, - void (*cont)(void *private_data, BOOL success, uid_t uid), - void *private_data) -{ - struct winbindd_request request; - ZERO_STRUCT(request); - request.cmd = WINBINDD_DUAL_SID2UID; - sid_to_string(request.data.dual_sid2id.sid, sid); - do_async(mem_ctx, idmap_child(), &request, winbindd_sid2uid_recv, - (void *)cont, private_data); -} - -enum winbindd_result winbindd_dual_sid2uid(struct winbindd_domain *domain, - struct winbindd_cli_state *state) -{ - DOM_SID sid; - NTSTATUS result; - - DEBUG(3, ("[%5lu]: sid to uid %s\n", (unsigned long)state->pid, - state->request.data.dual_sid2id.sid)); - - if (!string_to_sid(&sid, state->request.data.dual_sid2id.sid)) { - DEBUG(1, ("Could not get convert sid %s from string\n", - state->request.data.dual_sid2id.sid)); - return WINBINDD_ERROR; - } - - /* Find uid for this sid and return it, possibly ask the slow remote idmap */ - - result = idmap_sid_to_uid(&sid, &(state->response.data.uid)); - - return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR; -} - -#if 0 /* not used */ -static void uid2name_recv(TALLOC_CTX *mem_ctx, BOOL success, - struct winbindd_response *response, - void *c, void *private_data); - -void winbindd_uid2name_async(TALLOC_CTX *mem_ctx, uid_t uid, - void (*cont)(void *private_data, BOOL success, - const char *name), - void *private_data) -{ - struct winbindd_request request; - ZERO_STRUCT(request); - request.cmd = WINBINDD_DUAL_UID2NAME; - request.data.uid = uid; - do_async(mem_ctx, idmap_child(), &request, uid2name_recv, - (void *)cont, private_data); -} -#endif /* not used */ - -enum winbindd_result winbindd_dual_uid2name(struct winbindd_domain *domain, - struct winbindd_cli_state *state) -{ - struct passwd *pw; - - DEBUG(3, ("[%5lu]: uid2name %lu\n", (unsigned long)state->pid, - (unsigned long)state->request.data.uid)); - - pw = getpwuid(state->request.data.uid); - if (pw == NULL) { - DEBUG(5, ("User %lu not found\n", - (unsigned long)state->request.data.uid)); - return WINBINDD_ERROR; - } - - fstrcpy(state->response.data.name.name, pw->pw_name); - return WINBINDD_OK; -} - -#if 0 /* not used */ -static void uid2name_recv(TALLOC_CTX *mem_ctx, BOOL success, - struct winbindd_response *response, - void *c, void *private_data) -{ - void (*cont)(void *priv, BOOL succ, const char *name) = - (void (*)(void *, BOOL, const char *))c; - - if (!success) { - DEBUG(5, ("Could not trigger uid2name\n")); - cont(private_data, False, NULL); - return; - } - - if (response->result != WINBINDD_OK) { - DEBUG(5, ("uid2name returned an error\n")); - cont(private_data, False, NULL); - return; - } - - cont(private_data, True, response->data.name.name); -} - -static void name2uid_recv(TALLOC_CTX *mem_ctx, BOOL success, - struct winbindd_response *response, - void *c, void *private_data); - -static void winbindd_name2uid_async(TALLOC_CTX *mem_ctx, const char *name, - void (*cont)(void *private_data, BOOL success, - uid_t uid), - void *private_data) -{ - struct winbindd_request request; - ZERO_STRUCT(request); - request.cmd = WINBINDD_DUAL_NAME2UID; - fstrcpy(request.data.username, name); - do_async(mem_ctx, idmap_child(), &request, name2uid_recv, - (void *)cont, private_data); -} -#endif /* not used */ - -enum winbindd_result winbindd_dual_name2uid(struct winbindd_domain *domain, - struct winbindd_cli_state *state) -{ - struct passwd *pw; - - /* Ensure null termination */ - state->request.data.username - [sizeof(state->request.data.username)-1] = '\0'; - - DEBUG(3, ("[%5lu]: name2uid %s\n", (unsigned long)state->pid, - state->request.data.username)); - - pw = getpwnam(state->request.data.username); - if (pw == NULL) { - return WINBINDD_ERROR; - } - - state->response.data.uid = pw->pw_uid; - return WINBINDD_OK; -} - -#if 0 /* not used */ -static void name2uid_recv(TALLOC_CTX *mem_ctx, BOOL success, - struct winbindd_response *response, - void *c, void *private_data) -{ - void (*cont)(void *priv, BOOL succ, uid_t uid) = - (void (*)(void *, BOOL, uid_t))c; - - if (!success) { - DEBUG(5, ("Could not trigger name2uid\n")); - cont(private_data, False, 0); - return; - } - - if (response->result != WINBINDD_OK) { - DEBUG(5, ("name2uid returned an error\n")); - cont(private_data, False, 0); - return; - } - - cont(private_data, True, response->data.uid); -} -#endif /* not used */ - -static void winbindd_sid2gid_recv(TALLOC_CTX *mem_ctx, BOOL success, - struct winbindd_response *response, - void *c, void *private_data) -{ - void (*cont)(void *priv, BOOL succ, gid_t gid) = - (void (*)(void *, BOOL, gid_t))c; - - if (!success) { - DEBUG(5, ("Could not trigger sid2gid\n")); - cont(private_data, False, 0); - return; - } - - if (response->result != WINBINDD_OK) { - DEBUG(5, ("sid2gid returned an error\n")); - cont(private_data, False, 0); - return; - } - - cont(private_data, True, response->data.gid); -} - -void winbindd_sid2gid_async(TALLOC_CTX *mem_ctx, const DOM_SID *sid, - void (*cont)(void *private_data, BOOL success, gid_t gid), - void *private_data) -{ - struct winbindd_request request; - ZERO_STRUCT(request); - request.cmd = WINBINDD_DUAL_SID2GID; - sid_to_string(request.data.dual_sid2id.sid, sid); - - DEBUG(7,("winbindd_sid2gid_async: Resolving %s to a gid\n", - request.data.dual_sid2id.sid)); - - do_async(mem_ctx, idmap_child(), &request, winbindd_sid2gid_recv, - (void *)cont, private_data); -} - -enum winbindd_result winbindd_dual_sid2gid(struct winbindd_domain *domain, - struct winbindd_cli_state *state) -{ - DOM_SID sid; - NTSTATUS result; - - DEBUG(3, ("[%5lu]: sid to gid %s\n", (unsigned long)state->pid, - state->request.data.dual_sid2id.sid)); - - if (!string_to_sid(&sid, state->request.data.dual_sid2id.sid)) { - DEBUG(1, ("Could not get convert sid %s from string\n", - state->request.data.dual_sid2id.sid)); - return WINBINDD_ERROR; - } - - /* Find gid for this sid and return it, possibly ask the slow remote idmap */ - - result = idmap_sid_to_gid(&sid, &state->response.data.gid); - - DEBUG(10, ("winbindd_dual_sid2gid: 0x%08x - %s - %u\n", NT_STATUS_V(result), sid_string_static(&sid), state->response.data.gid)); - - return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR; -} - -static void gid2name_recv(TALLOC_CTX *mem_ctx, BOOL success, - struct winbindd_response *response, - void *c, void *private_data) -{ - void (*cont)(void *priv, BOOL succ, const char *name) = - (void (*)(void *, BOOL, const char *))c; - - if (!success) { - DEBUG(5, ("Could not trigger gid2name\n")); - cont(private_data, False, NULL); - return; - } - - if (response->result != WINBINDD_OK) { - DEBUG(5, ("gid2name returned an error\n")); - cont(private_data, False, NULL); - return; - } - - cont(private_data, True, response->data.name.name); -} - -void winbindd_gid2name_async(TALLOC_CTX *mem_ctx, gid_t gid, - void (*cont)(void *private_data, BOOL success, - const char *name), - void *private_data) -{ - struct winbindd_request request; - ZERO_STRUCT(request); - request.cmd = WINBINDD_DUAL_GID2NAME; - request.data.gid = gid; - do_async(mem_ctx, idmap_child(), &request, gid2name_recv, - (void *)cont, private_data); -} - -enum winbindd_result winbindd_dual_gid2name(struct winbindd_domain *domain, - struct winbindd_cli_state *state) -{ - struct group *gr; - - DEBUG(3, ("[%5lu]: gid2name %lu\n", (unsigned long)state->pid, - (unsigned long)state->request.data.gid)); - - gr = getgrgid(state->request.data.gid); - if (gr == NULL) - return WINBINDD_ERROR; - - fstrcpy(state->response.data.name.name, gr->gr_name); - return WINBINDD_OK; -} - -#if 0 /* not used */ -static void name2gid_recv(TALLOC_CTX *mem_ctx, BOOL success, - struct winbindd_response *response, - void *c, void *private_data); - -static void winbindd_name2gid_async(TALLOC_CTX *mem_ctx, const char *name, - void (*cont)(void *private_data, BOOL success, - gid_t gid), - void *private_data) -{ - struct winbindd_request request; - ZERO_STRUCT(request); - request.cmd = WINBINDD_DUAL_NAME2GID; - fstrcpy(request.data.groupname, name); - do_async(mem_ctx, idmap_child(), &request, name2gid_recv, - (void *)cont, private_data); -} -#endif /* not used */ - -enum winbindd_result winbindd_dual_name2gid(struct winbindd_domain *domain, - struct winbindd_cli_state *state) -{ - struct group *gr; - - /* Ensure null termination */ - state->request.data.groupname - [sizeof(state->request.data.groupname)-1] = '\0'; - - DEBUG(3, ("[%5lu]: name2gid %s\n", (unsigned long)state->pid, - state->request.data.groupname)); - - gr = getgrnam(state->request.data.groupname); - if (gr == NULL) { - return WINBINDD_ERROR; - } - - state->response.data.gid = gr->gr_gid; - return WINBINDD_OK; -} - -#if 0 /* not used */ -static void name2gid_recv(TALLOC_CTX *mem_ctx, BOOL success, - struct winbindd_response *response, - void *c, void *private_data) -{ - void (*cont)(void *priv, BOOL succ, gid_t gid) = - (void (*)(void *, BOOL, gid_t))c; - - if (!success) { - DEBUG(5, ("Could not trigger name2gid\n")); - cont(private_data, False, 0); - return; - } - - if (response->result != WINBINDD_OK) { - DEBUG(5, ("name2gid returned an error\n")); - cont(private_data, False, 0); - return; - } - - cont(private_data, True, response->data.gid); -} -#endif /* not used */ - -struct lookupsid_state { - DOM_SID sid; - void *caller_private_data; -}; - - -static void lookupsid_recv2(TALLOC_CTX *mem_ctx, BOOL success, - struct winbindd_response *response, - void *c, void *private_data) -{ - void (*cont)(void *priv, BOOL succ, const char *dom_name, - const char *name, enum lsa_SidType type) = - (void (*)(void *, BOOL, const char *, const char *, - enum lsa_SidType))c; - struct lookupsid_state *s = talloc_get_type_abort(private_data, - struct lookupsid_state); - - if (!success) { - DEBUG(5, ("Could not trigger lookupsid\n")); - cont(s->caller_private_data, False, NULL, NULL, SID_NAME_UNKNOWN); - return; - } - - if (response->result != WINBINDD_OK) { - DEBUG(5, ("lookupsid (forest root) returned an error\n")); - cont(s->caller_private_data, False, NULL, NULL, SID_NAME_UNKNOWN); - return; - } - - cont(s->caller_private_data, True, response->data.name.dom_name, - response->data.name.name, - (enum lsa_SidType)response->data.name.type); -} - -static void lookupsid_recv(TALLOC_CTX *mem_ctx, BOOL success, - struct winbindd_response *response, - void *c, void *private_data) -{ - void (*cont)(void *priv, BOOL succ, const char *dom_name, - const char *name, enum lsa_SidType type) = - (void (*)(void *, BOOL, const char *, const char *, - enum lsa_SidType))c; - struct lookupsid_state *s = talloc_get_type_abort(private_data, - struct lookupsid_state); - - if (!success) { - DEBUG(5, ("Could not trigger lookupsid\n")); - cont(s->caller_private_data, False, NULL, NULL, SID_NAME_UNKNOWN); - return; - } - - if (response->result != WINBINDD_OK) { - /* Try again using the forest root */ - struct winbindd_domain *root_domain = find_root_domain(); - struct winbindd_request request; - - if ( !root_domain ) { - DEBUG(5,("lookupsid_recv: unable to determine forest root\n")); - cont(s->caller_private_data, False, NULL, NULL, SID_NAME_UNKNOWN); - return; - } - - ZERO_STRUCT(request); - request.cmd = WINBINDD_LOOKUPSID; - fstrcpy(request.data.sid, sid_string_static(&s->sid)); - - do_async_domain(mem_ctx, root_domain, &request, lookupsid_recv2, - (void *)cont, s); - - return; - } - - cont(s->caller_private_data, True, response->data.name.dom_name, - response->data.name.name, - (enum lsa_SidType)response->data.name.type); -} - -void winbindd_lookupsid_async(TALLOC_CTX *mem_ctx, const DOM_SID *sid, - void (*cont)(void *private_data, BOOL success, - const char *dom_name, - const char *name, - enum lsa_SidType type), - void *private_data) -{ - struct winbindd_domain *domain; - struct winbindd_request request; - struct lookupsid_state *s; - - domain = find_lookup_domain_from_sid(sid); - if (domain == NULL) { - DEBUG(5, ("Could not find domain for sid %s\n", - sid_string_static(sid))); - cont(private_data, False, NULL, NULL, SID_NAME_UNKNOWN); - return; - } - - ZERO_STRUCT(request); - request.cmd = WINBINDD_LOOKUPSID; - fstrcpy(request.data.sid, sid_string_static(sid)); - - if ( (s = TALLOC_ZERO_P(mem_ctx, struct lookupsid_state)) == NULL ) { - DEBUG(0, ("winbindd_lookupsid_async: talloc failed\n")); - cont(private_data, False, NULL, NULL, SID_NAME_UNKNOWN); - return; - } - - sid_copy( &s->sid, sid ); - s->caller_private_data = private_data; - - do_async_domain(mem_ctx, domain, &request, lookupsid_recv, - (void *)cont, s); -} - -enum winbindd_result winbindd_dual_lookupsid(struct winbindd_domain *domain, - struct winbindd_cli_state *state) -{ - enum lsa_SidType type; - DOM_SID sid; - char *name; - char *dom_name; - - /* Ensure null termination */ - state->request.data.sid[sizeof(state->request.data.sid)-1]='\0'; - - DEBUG(3, ("[%5lu]: lookupsid %s\n", (unsigned long)state->pid, - state->request.data.sid)); - - /* Lookup sid from PDC using lsa_lookup_sids() */ - - if (!string_to_sid(&sid, state->request.data.sid)) { - DEBUG(5, ("%s not a SID\n", state->request.data.sid)); - return WINBINDD_ERROR; - } - - /* Lookup the sid */ - - if (!winbindd_lookup_name_by_sid(state->mem_ctx, domain, &sid, - &dom_name, &name, &type)) - { - TALLOC_FREE(dom_name); - TALLOC_FREE(name); - return WINBINDD_ERROR; - } - - fstrcpy(state->response.data.name.dom_name, dom_name); - fstrcpy(state->response.data.name.name, name); - state->response.data.name.type = type; - - TALLOC_FREE(dom_name); - TALLOC_FREE(name); - return WINBINDD_OK; -} - -/******************************************************************** - This is the second callback after contacting the forest root -********************************************************************/ - -struct lookupname_state { - char *dom_name; - char *name; - void *caller_private_data; -}; - - -static void lookupname_recv2(TALLOC_CTX *mem_ctx, BOOL success, - struct winbindd_response *response, - void *c, void *private_data) -{ - void (*cont)(void *priv, BOOL succ, const DOM_SID *sid, - enum lsa_SidType type) = - (void (*)(void *, BOOL, const DOM_SID *, enum lsa_SidType))c; - DOM_SID sid; - struct lookupname_state *s = talloc_get_type_abort( private_data, - struct lookupname_state ); - - - if (!success) { - DEBUG(5, ("Could not trigger lookup_name\n")); - cont(s->caller_private_data, False, NULL, SID_NAME_UNKNOWN); - return; - } - - if (response->result != WINBINDD_OK) { - DEBUG(5, ("lookup_name returned an error\n")); - cont(s->caller_private_data, False, NULL, SID_NAME_UNKNOWN); - return; - } - - if (!string_to_sid(&sid, response->data.sid.sid)) { - DEBUG(0, ("Could not convert string %s to sid\n", - response->data.sid.sid)); - cont(s->caller_private_data, False, NULL, SID_NAME_UNKNOWN); - return; - } - - cont(s->caller_private_data, True, &sid, - (enum lsa_SidType)response->data.sid.type); -} - -/******************************************************************** - This is the first callback after contacting our own domain -********************************************************************/ - -static void lookupname_recv(TALLOC_CTX *mem_ctx, BOOL success, - struct winbindd_response *response, - void *c, void *private_data) -{ - void (*cont)(void *priv, BOOL succ, const DOM_SID *sid, - enum lsa_SidType type) = - (void (*)(void *, BOOL, const DOM_SID *, enum lsa_SidType))c; - DOM_SID sid; - struct lookupname_state *s = talloc_get_type_abort( private_data, - struct lookupname_state ); - - if (!success) { - DEBUG(5, ("lookupname_recv: lookup_name() failed!\n")); - cont(s->caller_private_data, False, NULL, SID_NAME_UNKNOWN); - return; - } - - if (response->result != WINBINDD_OK) { - /* Try again using the forest root */ - struct winbindd_domain *root_domain = find_root_domain(); - struct winbindd_request request; - - if ( !root_domain ) { - DEBUG(5,("lookupname_recv: unable to determine forest root\n")); - cont(s->caller_private_data, False, NULL, SID_NAME_UNKNOWN); - return; - } - - ZERO_STRUCT(request); - request.cmd = WINBINDD_LOOKUPNAME; - - fstrcpy( request.data.name.dom_name, s->dom_name ); - fstrcpy( request.data.name.name, s->name ); - - do_async_domain(mem_ctx, root_domain, &request, lookupname_recv2, - (void *)cont, s); - - return; - } - - if (!string_to_sid(&sid, response->data.sid.sid)) { - DEBUG(0, ("Could not convert string %s to sid\n", - response->data.sid.sid)); - cont(s->caller_private_data, False, NULL, SID_NAME_UNKNOWN); - return; - } - - cont(s->caller_private_data, True, &sid, - (enum lsa_SidType)response->data.sid.type); -} - -/******************************************************************** - The lookup name call first contacts a DC in its own domain - and fallbacks to contact a DC in the forest in our domain doesn't - know the name. -********************************************************************/ - -void winbindd_lookupname_async(TALLOC_CTX *mem_ctx, - const char *dom_name, const char *name, - void (*cont)(void *private_data, BOOL success, - const DOM_SID *sid, - enum lsa_SidType type), - enum winbindd_cmd orig_cmd, - void *private_data) -{ - struct winbindd_request request; - struct winbindd_domain *domain; - struct lookupname_state *s; - - if ( (domain = find_lookup_domain_from_name(dom_name)) == NULL ) { - DEBUG(5, ("Could not find domain for name %s\n", dom_name)); - cont(private_data, False, NULL, SID_NAME_UNKNOWN); - return; - } - - ZERO_STRUCT(request); - request.cmd = WINBINDD_LOOKUPNAME; - request.original_cmd = orig_cmd; - fstrcpy(request.data.name.dom_name, dom_name); - fstrcpy(request.data.name.name, name); - - if ( (s = TALLOC_ZERO_P(mem_ctx, struct lookupname_state)) == NULL ) { - DEBUG(0, ("winbindd_lookupname_async: talloc failed\n")); - cont(private_data, False, NULL, SID_NAME_UNKNOWN); - return; - } - - s->dom_name = talloc_strdup( s, dom_name ); - s->name = talloc_strdup( s, name ); - s->caller_private_data = private_data; - - do_async_domain(mem_ctx, domain, &request, lookupname_recv, - (void *)cont, s); -} - -enum winbindd_result winbindd_dual_lookupname(struct winbindd_domain *domain, - struct winbindd_cli_state *state) -{ - enum lsa_SidType type; - char *name_domain, *name_user; - DOM_SID sid; - char *p; - - /* Ensure null termination */ - state->request.data.name.dom_name[sizeof(state->request.data.name.dom_name)-1]='\0'; - - /* Ensure null termination */ - state->request.data.name.name[sizeof(state->request.data.name.name)-1]='\0'; - - /* cope with the name being a fully qualified name */ - p = strstr(state->request.data.name.name, lp_winbind_separator()); - if (p) { - *p = 0; - name_domain = state->request.data.name.name; - name_user = p+1; - } else { - name_domain = state->request.data.name.dom_name; - name_user = state->request.data.name.name; - } - - DEBUG(3, ("[%5lu]: lookupname %s%s%s\n", (unsigned long)state->pid, - name_domain, lp_winbind_separator(), name_user)); - - /* Lookup name from DC using lsa_lookup_names() */ - if (!winbindd_lookup_sid_by_name(state->mem_ctx, state->request.original_cmd, domain, name_domain, - name_user, &sid, &type)) { - return WINBINDD_ERROR; - } - - sid_to_string(state->response.data.sid.sid, &sid); - state->response.data.sid.type = type; - - return WINBINDD_OK; -} - -BOOL print_sidlist(TALLOC_CTX *mem_ctx, const DOM_SID *sids, - size_t num_sids, char **result, ssize_t *len) -{ - size_t i; - size_t buflen = 0; - - *len = 0; - *result = NULL; - for (i=0; irequest.domain_name, - state->request.data.sid)); - - if (!parse_ridlist(state->mem_ctx, state->request.extra_data.data, - &rids, &num_rids)) { - DEBUG(5, ("Could not parse ridlist\n")); - return WINBINDD_ERROR; - } - - if (!string_to_sid(&domain_sid, state->request.data.sid)) { - DEBUG(5, ("Could not parse domain sid %s\n", - state->request.data.sid)); - return WINBINDD_ERROR; - } - - status = domain->methods->rids_to_names(domain, state->mem_ctx, - &domain_sid, rids, num_rids, - &domain_name, - &names, &types); - - if (!NT_STATUS_IS_OK(status) && - !NT_STATUS_EQUAL(status, STATUS_SOME_UNMAPPED)) { - return WINBINDD_ERROR; - } - - len = 0; - buflen = 0; - result = NULL; - - for (i=0; imem_ctx, &result, &len, &buflen, - "%d %s\n", types[i], names[i]); - } - - fstrcpy(state->response.data.domain_name, domain_name); - - if (result != NULL) { - state->response.extra_data.data = SMB_STRDUP(result); - if (!state->response.extra_data.data) { - return WINBINDD_ERROR; - } - state->response.length += len+1; - } - - return WINBINDD_OK; -} - -static void getsidaliases_recv(TALLOC_CTX *mem_ctx, BOOL success, - struct winbindd_response *response, - void *c, void *private_data) -{ - void (*cont)(void *priv, BOOL succ, - DOM_SID *aliases, size_t num_aliases) = - (void (*)(void *, BOOL, DOM_SID *, size_t))c; - char *aliases_str; - DOM_SID *sids = NULL; - size_t num_sids = 0; - - if (!success) { - DEBUG(5, ("Could not trigger getsidaliases\n")); - cont(private_data, success, NULL, 0); - return; - } - - if (response->result != WINBINDD_OK) { - DEBUG(5, ("getsidaliases returned an error\n")); - cont(private_data, False, NULL, 0); - return; - } - - aliases_str = (char *)response->extra_data.data; - - if (aliases_str == NULL) { - DEBUG(10, ("getsidaliases return 0 SIDs\n")); - cont(private_data, True, NULL, 0); - return; - } - - if (!parse_sidlist(mem_ctx, aliases_str, &sids, &num_sids)) { - DEBUG(0, ("Could not parse sids\n")); - cont(private_data, False, NULL, 0); - return; - } - - SAFE_FREE(response->extra_data.data); - - cont(private_data, True, sids, num_sids); -} - -void winbindd_getsidaliases_async(struct winbindd_domain *domain, - TALLOC_CTX *mem_ctx, - const DOM_SID *sids, size_t num_sids, - void (*cont)(void *private_data, - BOOL success, - const DOM_SID *aliases, - size_t num_aliases), - void *private_data) -{ - struct winbindd_request request; - char *sidstr = NULL; - ssize_t len; - - if (num_sids == 0) { - cont(private_data, True, NULL, 0); - return; - } - - if (!print_sidlist(mem_ctx, sids, num_sids, &sidstr, &len)) { - cont(private_data, False, NULL, 0); - return; - } - - ZERO_STRUCT(request); - request.cmd = WINBINDD_DUAL_GETSIDALIASES; - request.extra_len = len; - request.extra_data.data = sidstr; - - do_async_domain(mem_ctx, domain, &request, getsidaliases_recv, - (void *)cont, private_data); -} - -enum winbindd_result winbindd_dual_getsidaliases(struct winbindd_domain *domain, - struct winbindd_cli_state *state) -{ - DOM_SID *sids = NULL; - size_t num_sids = 0; - char *sidstr = NULL; - ssize_t len; - size_t i; - uint32 num_aliases; - uint32 *alias_rids; - NTSTATUS result; - - DEBUG(3, ("[%5lu]: getsidaliases\n", (unsigned long)state->pid)); - - sidstr = state->request.extra_data.data; - if (sidstr == NULL) { - sidstr = talloc_strdup(state->mem_ctx, "\n"); /* No SID */ - if (!sidstr) { - DEBUG(0, ("Out of memory\n")); - return WINBINDD_ERROR; - } - } - - DEBUG(10, ("Sidlist: %s\n", sidstr)); - - if (!parse_sidlist(state->mem_ctx, sidstr, &sids, &num_sids)) { - DEBUG(0, ("Could not parse SID list: %s\n", sidstr)); - return WINBINDD_ERROR; - } - - num_aliases = 0; - alias_rids = NULL; - - result = domain->methods->lookup_useraliases(domain, - state->mem_ctx, - num_sids, sids, - &num_aliases, - &alias_rids); - - if (!NT_STATUS_IS_OK(result)) { - DEBUG(3, ("Could not lookup_useraliases: %s\n", - nt_errstr(result))); - return WINBINDD_ERROR; - } - - num_sids = 0; - sids = NULL; - sidstr = NULL; - - DEBUG(10, ("Got %d aliases\n", num_aliases)); - - for (i=0; isid); - sid_append_rid(&sid, alias_rids[i]); - if (!add_sid_to_array(state->mem_ctx, &sid, &sids, &num_sids)) { - return WINBINDD_ERROR; - } - } - - - if (!print_sidlist(state->mem_ctx, sids, num_sids, &sidstr, &len)) { - DEBUG(0, ("Could not print_sidlist\n")); - state->response.extra_data.data = NULL; - return WINBINDD_ERROR; - } - - state->response.extra_data.data = NULL; - - if (sidstr) { - state->response.extra_data.data = SMB_STRDUP(sidstr); - if (!state->response.extra_data.data) { - DEBUG(0, ("Out of memory\n")); - return WINBINDD_ERROR; - } - DEBUG(10, ("aliases_list: %s\n", - (char *)state->response.extra_data.data)); - state->response.length += len+1; - } - - return WINBINDD_OK; -} - -struct gettoken_state { - TALLOC_CTX *mem_ctx; - DOM_SID user_sid; - struct winbindd_domain *alias_domain; - struct winbindd_domain *local_alias_domain; - struct winbindd_domain *builtin_domain; - DOM_SID *sids; - size_t num_sids; - void (*cont)(void *private_data, BOOL success, DOM_SID *sids, size_t num_sids); - void *private_data; -}; - -static void gettoken_recvdomgroups(TALLOC_CTX *mem_ctx, BOOL success, - struct winbindd_response *response, - void *c, void *private_data); -static void gettoken_recvaliases(void *private_data, BOOL success, - const DOM_SID *aliases, - size_t num_aliases); - - -void winbindd_gettoken_async(TALLOC_CTX *mem_ctx, const DOM_SID *user_sid, - void (*cont)(void *private_data, BOOL success, - DOM_SID *sids, size_t num_sids), - void *private_data) -{ - struct winbindd_domain *domain; - struct winbindd_request request; - struct gettoken_state *state; - - state = TALLOC_ZERO_P(mem_ctx, struct gettoken_state); - if (state == NULL) { - DEBUG(0, ("talloc failed\n")); - cont(private_data, False, NULL, 0); - return; - } - - state->mem_ctx = mem_ctx; - sid_copy(&state->user_sid, user_sid); - state->alias_domain = find_our_domain(); - state->local_alias_domain = find_domain_from_name( get_global_sam_name() ); - state->builtin_domain = find_builtin_domain(); - state->cont = cont; - state->private_data = private_data; - - domain = find_domain_from_sid_noinit(user_sid); - if (domain == NULL) { - DEBUG(5, ("Could not find domain from SID %s\n", - sid_string_static(user_sid))); - cont(private_data, False, NULL, 0); - return; - } - - ZERO_STRUCT(request); - request.cmd = WINBINDD_GETUSERDOMGROUPS; - fstrcpy(request.data.sid, sid_string_static(user_sid)); - - do_async_domain(mem_ctx, domain, &request, gettoken_recvdomgroups, - NULL, state); -} - -static void gettoken_recvdomgroups(TALLOC_CTX *mem_ctx, BOOL success, - struct winbindd_response *response, - void *c, void *private_data) -{ - struct gettoken_state *state = - talloc_get_type_abort(private_data, struct gettoken_state); - char *sids_str; - - if (!success) { - DEBUG(10, ("Could not get domain groups\n")); - state->cont(state->private_data, False, NULL, 0); - return; - } - - sids_str = (char *)response->extra_data.data; - - if (sids_str == NULL) { - /* This could be normal if we are dealing with a - local user and local groups */ - - if ( !sid_check_is_in_our_domain( &state->user_sid ) ) { - DEBUG(10, ("Received no domain groups\n")); - state->cont(state->private_data, True, NULL, 0); - return; - } - } - - state->sids = NULL; - state->num_sids = 0; - - if (!add_sid_to_array(mem_ctx, &state->user_sid, &state->sids, - &state->num_sids)) { - DEBUG(0, ("Out of memory\n")); - state->cont(state->private_data, False, NULL, 0); - return; - } - - if (sids_str && !parse_sidlist(mem_ctx, sids_str, &state->sids, - &state->num_sids)) { - DEBUG(0, ("Could not parse sids\n")); - state->cont(state->private_data, False, NULL, 0); - return; - } - - SAFE_FREE(response->extra_data.data); - - if (state->alias_domain == NULL) { - DEBUG(10, ("Don't expand domain local groups\n")); - state->cont(state->private_data, True, state->sids, - state->num_sids); - return; - } - - winbindd_getsidaliases_async(state->alias_domain, mem_ctx, - state->sids, state->num_sids, - gettoken_recvaliases, state); -} - -static void gettoken_recvaliases(void *private_data, BOOL success, - const DOM_SID *aliases, - size_t num_aliases) -{ - struct gettoken_state *state = (struct gettoken_state *)private_data; - size_t i; - - if (!success) { - DEBUG(10, ("Could not receive domain local groups\n")); - state->cont(state->private_data, False, NULL, 0); - return; - } - - for (i=0; imem_ctx, &aliases[i], - &state->sids, &state->num_sids)) { - DEBUG(0, ("Out of memory\n")); - state->cont(state->private_data, False, NULL, 0); - return; - } - } - - if (state->local_alias_domain != NULL) { - struct winbindd_domain *local_domain = state->local_alias_domain; - DEBUG(10, ("Expanding our own local groups\n")); - state->local_alias_domain = NULL; - winbindd_getsidaliases_async(local_domain, state->mem_ctx, - state->sids, state->num_sids, - gettoken_recvaliases, state); - return; - } - - if (state->builtin_domain != NULL) { - struct winbindd_domain *builtin_domain = state->builtin_domain; - DEBUG(10, ("Expanding our own BUILTIN groups\n")); - state->builtin_domain = NULL; - winbindd_getsidaliases_async(builtin_domain, state->mem_ctx, - state->sids, state->num_sids, - gettoken_recvaliases, state); - return; - } - - state->cont(state->private_data, True, state->sids, state->num_sids); -} - -static void query_user_recv(TALLOC_CTX *mem_ctx, BOOL success, - struct winbindd_response *response, - void *c, void *private_data) -{ - void (*cont)(void *priv, BOOL succ, const char *acct_name, - const char *full_name, const char *homedir, - const char *shell, uint32 gid, uint32 group_rid) = - (void (*)(void *, BOOL, const char *, const char *, - const char *, const char *, uint32, uint32))c; - - if (!success) { - DEBUG(5, ("Could not trigger query_user\n")); - cont(private_data, False, NULL, NULL, NULL, NULL, -1, -1); - return; - } - - if (response->result != WINBINDD_OK) { - DEBUG(5, ("query_user returned an error\n")); - cont(private_data, False, NULL, NULL, NULL, NULL, -1, -1); - return; - } - - cont(private_data, True, response->data.user_info.acct_name, - response->data.user_info.full_name, - response->data.user_info.homedir, - response->data.user_info.shell, - response->data.user_info.primary_gid, - response->data.user_info.group_rid); -} - -void query_user_async(TALLOC_CTX *mem_ctx, struct winbindd_domain *domain, - const DOM_SID *sid, - void (*cont)(void *private_data, BOOL success, - const char *acct_name, - const char *full_name, - const char *homedir, - const char *shell, - gid_t gid, - uint32 group_rid), - void *private_data) -{ - struct winbindd_request request; - ZERO_STRUCT(request); - request.cmd = WINBINDD_DUAL_USERINFO; - sid_to_string(request.data.sid, sid); - do_async_domain(mem_ctx, domain, &request, query_user_recv, - (void *)cont, private_data); -} - -/* The following uid2sid/gid2sid functions has been contributed by - * Keith Reynolds */ - -static void winbindd_uid2sid_recv(TALLOC_CTX *mem_ctx, BOOL success, - struct winbindd_response *response, - void *c, void *private_data) -{ - void (*cont)(void *priv, BOOL succ, const char *sid) = - (void (*)(void *, BOOL, const char *))c; - - if (!success) { - DEBUG(5, ("Could not trigger uid2sid\n")); - cont(private_data, False, NULL); - return; - } - - if (response->result != WINBINDD_OK) { - DEBUG(5, ("uid2sid returned an error\n")); - cont(private_data, False, NULL); - return; - } - - cont(private_data, True, response->data.sid.sid); -} - -void winbindd_uid2sid_async(TALLOC_CTX *mem_ctx, uid_t uid, - void (*cont)(void *private_data, BOOL success, const char *sid), - void *private_data) -{ - struct winbindd_request request; - - ZERO_STRUCT(request); - request.cmd = WINBINDD_DUAL_UID2SID; - request.data.uid = uid; - do_async(mem_ctx, idmap_child(), &request, winbindd_uid2sid_recv, - (void *)cont, private_data); -} - -enum winbindd_result winbindd_dual_uid2sid(struct winbindd_domain *domain, - struct winbindd_cli_state *state) -{ - DOM_SID sid; - NTSTATUS result; - - DEBUG(3,("[%5lu]: uid to sid %lu\n", - (unsigned long)state->pid, - (unsigned long) state->request.data.uid)); - - /* Find sid for this uid and return it, possibly ask the slow remote idmap */ - result = idmap_uid_to_sid(&sid, state->request.data.uid); - - if (NT_STATUS_IS_OK(result)) { - sid_to_string(state->response.data.sid.sid, &sid); - state->response.data.sid.type = SID_NAME_USER; - return WINBINDD_OK; - } - - return WINBINDD_ERROR; -} - -static void winbindd_gid2sid_recv(TALLOC_CTX *mem_ctx, BOOL success, - struct winbindd_response *response, - void *c, void *private_data) -{ - void (*cont)(void *priv, BOOL succ, const char *sid) = - (void (*)(void *, BOOL, const char *))c; - - if (!success) { - DEBUG(5, ("Could not trigger gid2sid\n")); - cont(private_data, False, NULL); - return; - } - - if (response->result != WINBINDD_OK) { - DEBUG(5, ("gid2sid returned an error\n")); - cont(private_data, False, NULL); - return; - } - - cont(private_data, True, response->data.sid.sid); -} - -void winbindd_gid2sid_async(TALLOC_CTX *mem_ctx, gid_t gid, - void (*cont)(void *private_data, BOOL success, const char *sid), - void *private_data) -{ - struct winbindd_request request; - - ZERO_STRUCT(request); - request.cmd = WINBINDD_DUAL_GID2SID; - request.data.gid = gid; - do_async(mem_ctx, idmap_child(), &request, winbindd_gid2sid_recv, - (void *)cont, private_data); -} - -enum winbindd_result winbindd_dual_gid2sid(struct winbindd_domain *domain, - struct winbindd_cli_state *state) -{ - DOM_SID sid; - NTSTATUS result; - - DEBUG(3,("[%5lu]: gid %lu to sid\n", - (unsigned long)state->pid, - (unsigned long) state->request.data.gid)); - - /* Find sid for this gid and return it, possibly ask the slow remote idmap */ - result = idmap_gid_to_sid(&sid, state->request.data.gid); - - if (NT_STATUS_IS_OK(result)) { - sid_to_string(state->response.data.sid.sid, &sid); - DEBUG(10, ("[%5lu]: retrieved sid: %s\n", - (unsigned long)state->pid, - state->response.data.sid.sid)); - state->response.data.sid.type = SID_NAME_DOM_GRP; - return WINBINDD_OK; - } - - return WINBINDD_ERROR; -} - -static void winbindd_dump_id_maps_recv(TALLOC_CTX *mem_ctx, BOOL success, - struct winbindd_response *response, - void *c, void *private_data) -{ - void (*cont)(void *priv, BOOL succ) = - (void (*)(void *, BOOL))c; - - if (!success) { - DEBUG(5, ("Could not trigger a map dump\n")); - cont(private_data, False); - return; - } - - if (response->result != WINBINDD_OK) { - DEBUG(5, ("idmap dump maps returned an error\n")); - cont(private_data, False); - return; - } - - cont(private_data, True); -} - -void winbindd_dump_maps_async(TALLOC_CTX *mem_ctx, void *data, int size, - void (*cont)(void *private_data, BOOL success), - void *private_data) -{ - struct winbindd_request request; - ZERO_STRUCT(request); - request.cmd = WINBINDD_DUAL_DUMP_MAPS; - request.extra_data.data = (char *)data; - request.extra_len = size; - do_async(mem_ctx, idmap_child(), &request, winbindd_dump_id_maps_recv, - (void *)cont, private_data); -} - -enum winbindd_result winbindd_dual_dump_maps(struct winbindd_domain *domain, - struct winbindd_cli_state *state) -{ - DEBUG(3, ("[%5lu]: dual dump maps\n", (unsigned long)state->pid)); - - idmap_dump_maps((char *)state->request.extra_data.data); - - return WINBINDD_OK; -} - diff --git a/source3/nsswitch/winbindd_cache.c b/source3/nsswitch/winbindd_cache.c deleted file mode 100644 index e5090dfacf..0000000000 --- a/source3/nsswitch/winbindd_cache.c +++ /dev/null @@ -1,3892 +0,0 @@ -/* - Unix SMB/CIFS implementation. - - Winbind cache backend functions - - Copyright (C) Andrew Tridgell 2001 - Copyright (C) Gerald Carter 2003-2007 - Copyright (C) Volker Lendecke 2005 - Copyright (C) Guenther Deschner 2005 - Copyright (C) Michael Adam 2007 - - 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 3 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, see . -*/ - -#include "includes.h" -#include "winbindd.h" - -#undef DBGC_CLASS -#define DBGC_CLASS DBGC_WINBIND - -#define WINBINDD_CACHE_VERSION 1 -#define WINBINDD_CACHE_VERSION_KEYSTR "WINBINDD_CACHE_VERSION" - -extern struct winbindd_methods reconnect_methods; -extern BOOL opt_nocache; -#ifdef HAVE_ADS -extern struct winbindd_methods ads_methods; -#endif - -/* - * JRA. KEEP THIS LIST UP TO DATE IF YOU ADD CACHE ENTRIES. - * Here are the list of entry types that are *not* stored - * as form struct cache_entry in the cache. - */ - -static const char *non_centry_keys[] = { - "SEQNUM/", - "DR/", - "DE/", - "WINBINDD_OFFLINE", - WINBINDD_CACHE_VERSION_KEYSTR, - NULL -}; - -/************************************************************************ - Is this key a non-centry type ? -************************************************************************/ - -static BOOL is_non_centry_key(TDB_DATA kbuf) -{ - int i; - - if (kbuf.dptr == NULL || kbuf.dsize == 0) { - return False; - } - for (i = 0; non_centry_keys[i] != NULL; i++) { - size_t namelen = strlen(non_centry_keys[i]); - if (kbuf.dsize < namelen) { - continue; - } - if (strncmp(non_centry_keys[i], (const char *)kbuf.dptr, namelen) == 0) { - return True; - } - } - return False; -} - -/* Global online/offline state - False when online. winbindd starts up online - and sets this to true if the first query fails and there's an entry in - the cache tdb telling us to stay offline. */ - -static BOOL global_winbindd_offline_state; - -struct winbind_cache { - TDB_CONTEXT *tdb; -}; - -struct cache_entry { - NTSTATUS status; - uint32 sequence_number; - uint8 *data; - uint32 len, ofs; -}; - -void (*smb_panic_fn)(const char *const why) = smb_panic; - -#define WINBINDD_MAX_CACHE_SIZE (50*1024*1024) - -static struct winbind_cache *wcache; - -void winbindd_check_cache_size(time_t t) -{ - static time_t last_check_time; - struct stat st; - - if (last_check_time == (time_t)0) - last_check_time = t; - - if (t - last_check_time < 60 && t - last_check_time > 0) - return; - - if (wcache == NULL || wcache->tdb == NULL) { - DEBUG(0, ("Unable to check size of tdb cache - cache not open !\n")); - return; - } - - if (fstat(tdb_fd(wcache->tdb), &st) == -1) { - DEBUG(0, ("Unable to check size of tdb cache %s!\n", strerror(errno) )); - return; - } - - if (st.st_size > WINBINDD_MAX_CACHE_SIZE) { - DEBUG(10,("flushing cache due to size (%lu) > (%lu)\n", - (unsigned long)st.st_size, - (unsigned long)WINBINDD_MAX_CACHE_SIZE)); - wcache_flush_cache(); - } -} - -/* get the winbind_cache structure */ -static struct winbind_cache *get_cache(struct winbindd_domain *domain) -{ - struct winbind_cache *ret = wcache; - - /* We have to know what type of domain we are dealing with first. */ - - if ( !domain->initialized ) { - init_dc_connection( domain ); - } - - /* - OK. listen up becasue I'm only going to say this once. - We have the following scenarios to consider - (a) trusted AD domains on a Samba DC, - (b) trusted AD domains and we are joined to a non-kerberos domain - (c) trusted AD domains and we are joined to a kerberos (AD) domain - - For (a) we can always contact the trusted domain using krb5 - since we have the domain trust account password - - For (b) we can only use RPC since we have no way of - getting a krb5 ticket in our own domain - - For (c) we can always use krb5 since we have a kerberos trust - - --jerry - */ - - if (!domain->backend) { -#ifdef HAVE_ADS - struct winbindd_domain *our_domain = domain; - - /* find our domain first so we can figure out if we - are joined to a kerberized domain */ - - if ( !domain->primary ) - our_domain = find_our_domain(); - - if ((our_domain->active_directory || IS_DC) - && domain->active_directory - && !lp_winbind_rpc_only()) { - DEBUG(5,("get_cache: Setting ADS methods for domain %s\n", domain->name)); - domain->backend = &ads_methods; - } else { -#endif /* HAVE_ADS */ - DEBUG(5,("get_cache: Setting MS-RPC methods for domain %s\n", domain->name)); - domain->backend = &reconnect_methods; -#ifdef HAVE_ADS - } -#endif /* HAVE_ADS */ - } - - if (ret) - return ret; - - ret = SMB_XMALLOC_P(struct winbind_cache); - ZERO_STRUCTP(ret); - - wcache = ret; - wcache_flush_cache(); - - return ret; -} - -/* - free a centry structure -*/ -static void centry_free(struct cache_entry *centry) -{ - if (!centry) - return; - SAFE_FREE(centry->data); - free(centry); -} - -static BOOL centry_check_bytes(struct cache_entry *centry, size_t nbytes) -{ - if (centry->len - centry->ofs < nbytes) { - DEBUG(0,("centry corruption? needed %u bytes, have %d\n", - (unsigned int)nbytes, - centry->len - centry->ofs)); - return False; - } - return True; -} - -/* - pull a uint32 from a cache entry -*/ -static uint32 centry_uint32(struct cache_entry *centry) -{ - uint32 ret; - - if (!centry_check_bytes(centry, 4)) { - smb_panic_fn("centry_uint32"); - } - ret = IVAL(centry->data, centry->ofs); - centry->ofs += 4; - return ret; -} - -/* - pull a uint16 from a cache entry -*/ -static uint16 centry_uint16(struct cache_entry *centry) -{ - uint16 ret; - if (!centry_check_bytes(centry, 2)) { - smb_panic_fn("centry_uint16"); - } - ret = CVAL(centry->data, centry->ofs); - centry->ofs += 2; - return ret; -} - -/* - pull a uint8 from a cache entry -*/ -static uint8 centry_uint8(struct cache_entry *centry) -{ - uint8 ret; - if (!centry_check_bytes(centry, 1)) { - smb_panic_fn("centry_uint8"); - } - ret = CVAL(centry->data, centry->ofs); - centry->ofs += 1; - return ret; -} - -/* - pull a NTTIME from a cache entry -*/ -static NTTIME centry_nttime(struct cache_entry *centry) -{ - NTTIME ret; - if (!centry_check_bytes(centry, 8)) { - smb_panic_fn("centry_nttime"); - } - ret = IVAL(centry->data, centry->ofs); - centry->ofs += 4; - ret += (uint64_t)IVAL(centry->data, centry->ofs) << 32; - centry->ofs += 4; - return ret; -} - -/* - pull a time_t from a cache entry. time_t stored portably as a 64-bit time. -*/ -static time_t centry_time(struct cache_entry *centry) -{ - return (time_t)centry_nttime(centry); -} - -/* pull a string from a cache entry, using the supplied - talloc context -*/ -static char *centry_string(struct cache_entry *centry, TALLOC_CTX *mem_ctx) -{ - uint32 len; - char *ret; - - len = centry_uint8(centry); - - if (len == 0xFF) { - /* a deliberate NULL string */ - return NULL; - } - - if (!centry_check_bytes(centry, (size_t)len)) { - smb_panic_fn("centry_string"); - } - - ret = TALLOC_ARRAY(mem_ctx, char, len+1); - if (!ret) { - smb_panic_fn("centry_string out of memory\n"); - } - memcpy(ret,centry->data + centry->ofs, len); - ret[len] = 0; - centry->ofs += len; - return ret; -} - -/* pull a hash16 from a cache entry, using the supplied - talloc context -*/ -static char *centry_hash16(struct cache_entry *centry, TALLOC_CTX *mem_ctx) -{ - uint32 len; - char *ret; - - len = centry_uint8(centry); - - if (len != 16) { - DEBUG(0,("centry corruption? hash len (%u) != 16\n", - len )); - return NULL; - } - - if (!centry_check_bytes(centry, 16)) { - return NULL; - } - - ret = TALLOC_ARRAY(mem_ctx, char, 16); - if (!ret) { - smb_panic_fn("centry_hash out of memory\n"); - } - memcpy(ret,centry->data + centry->ofs, 16); - centry->ofs += 16; - return ret; -} - -/* pull a sid from a cache entry, using the supplied - talloc context -*/ -static BOOL centry_sid(struct cache_entry *centry, TALLOC_CTX *mem_ctx, DOM_SID *sid) -{ - char *sid_string; - sid_string = centry_string(centry, mem_ctx); - if ((sid_string == NULL) || (!string_to_sid(sid, sid_string))) { - return False; - } - return True; -} - - -/* - pull a NTSTATUS from a cache entry -*/ -static NTSTATUS centry_ntstatus(struct cache_entry *centry) -{ - NTSTATUS status; - - status = NT_STATUS(centry_uint32(centry)); - return status; -} - - -/* the server is considered down if it can't give us a sequence number */ -static BOOL wcache_server_down(struct winbindd_domain *domain) -{ - BOOL ret; - - if (!wcache->tdb) - return False; - - ret = (domain->sequence_number == DOM_SEQUENCE_NONE); - - if (ret) - DEBUG(10,("wcache_server_down: server for Domain %s down\n", - domain->name )); - return ret; -} - -static NTSTATUS fetch_cache_seqnum( struct winbindd_domain *domain, time_t now ) -{ - TDB_DATA data; - fstring key; - uint32 time_diff; - - if (!wcache->tdb) { - DEBUG(10,("fetch_cache_seqnum: tdb == NULL\n")); - return NT_STATUS_UNSUCCESSFUL; - } - - fstr_sprintf( key, "SEQNUM/%s", domain->name ); - - data = tdb_fetch_bystring( wcache->tdb, key ); - if ( !data.dptr || data.dsize!=8 ) { - DEBUG(10,("fetch_cache_seqnum: invalid data size key [%s]\n", key )); - return NT_STATUS_UNSUCCESSFUL; - } - - domain->sequence_number = IVAL(data.dptr, 0); - domain->last_seq_check = IVAL(data.dptr, 4); - - SAFE_FREE(data.dptr); - - /* have we expired? */ - - time_diff = now - domain->last_seq_check; - if ( time_diff > lp_winbind_cache_time() ) { - DEBUG(10,("fetch_cache_seqnum: timeout [%s][%u @ %u]\n", - domain->name, domain->sequence_number, - (uint32)domain->last_seq_check)); - return NT_STATUS_UNSUCCESSFUL; - } - - DEBUG(10,("fetch_cache_seqnum: success [%s][%u @ %u]\n", - domain->name, domain->sequence_number, - (uint32)domain->last_seq_check)); - - return NT_STATUS_OK; -} - -static NTSTATUS store_cache_seqnum( struct winbindd_domain *domain ) -{ - TDB_DATA data; - fstring key_str; - uint8 buf[8]; - - if (!wcache->tdb) { - DEBUG(10,("store_cache_seqnum: tdb == NULL\n")); - return NT_STATUS_UNSUCCESSFUL; - } - - fstr_sprintf( key_str, "SEQNUM/%s", domain->name ); - - SIVAL(buf, 0, domain->sequence_number); - SIVAL(buf, 4, domain->last_seq_check); - data.dptr = buf; - data.dsize = 8; - - if ( tdb_store_bystring( wcache->tdb, key_str, data, TDB_REPLACE) == -1 ) { - DEBUG(10,("store_cache_seqnum: tdb_store fail key [%s]\n", key_str )); - return NT_STATUS_UNSUCCESSFUL; - } - - DEBUG(10,("store_cache_seqnum: success [%s][%u @ %u]\n", - domain->name, domain->sequence_number, - (uint32)domain->last_seq_check)); - - return NT_STATUS_OK; -} - -/* - refresh the domain sequence number. If force is True - then always refresh it, no matter how recently we fetched it -*/ - -static void refresh_sequence_number(struct winbindd_domain *domain, BOOL force) -{ - NTSTATUS status; - unsigned time_diff; - time_t t = time(NULL); - unsigned cache_time = lp_winbind_cache_time(); - - if ( IS_DOMAIN_OFFLINE(domain) ) { - return; - } - - get_cache( domain ); - -#if 0 /* JERRY -- disable as the default cache time is now 5 minutes */ - /* trying to reconnect is expensive, don't do it too often */ - if (domain->sequence_number == DOM_SEQUENCE_NONE) { - cache_time *= 8; - } -#endif - - time_diff = t - domain->last_seq_check; - - /* see if we have to refetch the domain sequence number */ - if (!force && (time_diff < cache_time)) { - DEBUG(10, ("refresh_sequence_number: %s time ok\n", domain->name)); - goto done; - } - - /* try to get the sequence number from the tdb cache first */ - /* this will update the timestamp as well */ - - status = fetch_cache_seqnum( domain, t ); - if ( NT_STATUS_IS_OK(status) ) - goto done; - - /* important! make sure that we know if this is a native - mode domain or not. And that we can contact it. */ - - if ( winbindd_can_contact_domain( domain ) ) { - status = domain->backend->sequence_number(domain, - &domain->sequence_number); - } else { - /* just use the current time */ - status = NT_STATUS_OK; - domain->sequence_number = time(NULL); - } - - - /* the above call could have set our domain->backend to NULL when - * coming from offline to online mode, make sure to reinitialize the - * backend - Guenther */ - get_cache( domain ); - - if (!NT_STATUS_IS_OK(status)) { - DEBUG(10,("refresh_sequence_number: failed with %s\n", nt_errstr(status))); - domain->sequence_number = DOM_SEQUENCE_NONE; - } - - domain->last_status = status; - domain->last_seq_check = time(NULL); - - /* save the new sequence number ni the cache */ - store_cache_seqnum( domain ); - -done: - DEBUG(10, ("refresh_sequence_number: %s seq number is now %d\n", - domain->name, domain->sequence_number)); - - return; -} - -/* - decide if a cache entry has expired -*/ -static BOOL centry_expired(struct winbindd_domain *domain, const char *keystr, struct cache_entry *centry) -{ - /* If we've been told to be offline - stay in that state... */ - if (lp_winbind_offline_logon() && global_winbindd_offline_state) { - DEBUG(10,("centry_expired: Key %s for domain %s valid as winbindd is globally offline.\n", - keystr, domain->name )); - return False; - } - - /* when the domain is offline return the cached entry. - * This deals with transient offline states... */ - - if (!domain->online) { - DEBUG(10,("centry_expired: Key %s for domain %s valid as domain is offline.\n", - keystr, domain->name )); - return False; - } - - /* if the server is OK and our cache entry came from when it was down then - the entry is invalid */ - if ((domain->sequence_number != DOM_SEQUENCE_NONE) && - (centry->sequence_number == DOM_SEQUENCE_NONE)) { - DEBUG(10,("centry_expired: Key %s for domain %s invalid sequence.\n", - keystr, domain->name )); - return True; - } - - /* if the server is down or the cache entry is not older than the - current sequence number then it is OK */ - if (wcache_server_down(domain) || - centry->sequence_number == domain->sequence_number) { - DEBUG(10,("centry_expired: Key %s for domain %s is good.\n", - keystr, domain->name )); - return False; - } - - DEBUG(10,("centry_expired: Key %s for domain %s expired\n", - keystr, domain->name )); - - /* it's expired */ - return True; -} - -static struct cache_entry *wcache_fetch_raw(char *kstr) -{ - TDB_DATA data; - struct cache_entry *centry; - TDB_DATA key; - - key = string_tdb_data(kstr); - data = tdb_fetch(wcache->tdb, key); - if (!data.dptr) { - /* a cache miss */ - return NULL; - } - - centry = SMB_XMALLOC_P(struct cache_entry); - centry->data = (unsigned char *)data.dptr; - centry->len = data.dsize; - centry->ofs = 0; - - if (centry->len < 8) { - /* huh? corrupt cache? */ - DEBUG(10,("wcache_fetch_raw: Corrupt cache for key %s (len < 8) ?\n", kstr)); - centry_free(centry); - return NULL; - } - - centry->status = centry_ntstatus(centry); - centry->sequence_number = centry_uint32(centry); - - return centry; -} - -/* - fetch an entry from the cache, with a varargs key. auto-fetch the sequence - number and return status -*/ -static struct cache_entry *wcache_fetch(struct winbind_cache *cache, - struct winbindd_domain *domain, - const char *format, ...) PRINTF_ATTRIBUTE(3,4); -static struct cache_entry *wcache_fetch(struct winbind_cache *cache, - struct winbindd_domain *domain, - const char *format, ...) -{ - va_list ap; - char *kstr; - struct cache_entry *centry; - - if (opt_nocache) { - return NULL; - } - - refresh_sequence_number(domain, False); - - va_start(ap, format); - smb_xvasprintf(&kstr, format, ap); - va_end(ap); - - centry = wcache_fetch_raw(kstr); - if (centry == NULL) { - free(kstr); - return NULL; - } - - if (centry_expired(domain, kstr, centry)) { - - DEBUG(10,("wcache_fetch: entry %s expired for domain %s\n", - kstr, domain->name )); - - centry_free(centry); - free(kstr); - return NULL; - } - - DEBUG(10,("wcache_fetch: returning entry %s for domain %s\n", - kstr, domain->name )); - - free(kstr); - return centry; -} - -static void wcache_delete(const char *format, ...) PRINTF_ATTRIBUTE(1,2); -static void wcache_delete(const char *format, ...) -{ - va_list ap; - char *kstr; - TDB_DATA key; - - va_start(ap, format); - smb_xvasprintf(&kstr, format, ap); - va_end(ap); - - key = string_tdb_data(kstr); - - tdb_delete(wcache->tdb, key); - free(kstr); -} - -/* - make sure we have at least len bytes available in a centry -*/ -static void centry_expand(struct cache_entry *centry, uint32 len) -{ - if (centry->len - centry->ofs >= len) - return; - centry->len *= 2; - centry->data = SMB_REALLOC_ARRAY(centry->data, unsigned char, - centry->len); - if (!centry->data) { - DEBUG(0,("out of memory: needed %d bytes in centry_expand\n", centry->len)); - smb_panic_fn("out of memory in centry_expand"); - } -} - -/* - push a uint32 into a centry -*/ -static void centry_put_uint32(struct cache_entry *centry, uint32 v) -{ - centry_expand(centry, 4); - SIVAL(centry->data, centry->ofs, v); - centry->ofs += 4; -} - -/* - push a uint16 into a centry -*/ -static void centry_put_uint16(struct cache_entry *centry, uint16 v) -{ - centry_expand(centry, 2); - SIVAL(centry->data, centry->ofs, v); - centry->ofs += 2; -} - -/* - push a uint8 into a centry -*/ -static void centry_put_uint8(struct cache_entry *centry, uint8 v) -{ - centry_expand(centry, 1); - SCVAL(centry->data, centry->ofs, v); - centry->ofs += 1; -} - -/* - push a string into a centry - */ -static void centry_put_string(struct cache_entry *centry, const char *s) -{ - int len; - - if (!s) { - /* null strings are marked as len 0xFFFF */ - centry_put_uint8(centry, 0xFF); - return; - } - - len = strlen(s); - /* can't handle more than 254 char strings. Truncating is probably best */ - if (len > 254) { - DEBUG(10,("centry_put_string: truncating len (%d) to: 254\n", len)); - len = 254; - } - centry_put_uint8(centry, len); - centry_expand(centry, len); - memcpy(centry->data + centry->ofs, s, len); - centry->ofs += len; -} - -/* - push a 16 byte hash into a centry - treat as 16 byte string. - */ -static void centry_put_hash16(struct cache_entry *centry, const uint8 val[16]) -{ - centry_put_uint8(centry, 16); - centry_expand(centry, 16); - memcpy(centry->data + centry->ofs, val, 16); - centry->ofs += 16; -} - -static void centry_put_sid(struct cache_entry *centry, const DOM_SID *sid) -{ - fstring sid_string; - centry_put_string(centry, sid_to_string(sid_string, sid)); -} - - -/* - put NTSTATUS into a centry -*/ -static void centry_put_ntstatus(struct cache_entry *centry, NTSTATUS status) -{ - uint32 status_value = NT_STATUS_V(status); - centry_put_uint32(centry, status_value); -} - - -/* - push a NTTIME into a centry -*/ -static void centry_put_nttime(struct cache_entry *centry, NTTIME nt) -{ - centry_expand(centry, 8); - SIVAL(centry->data, centry->ofs, nt & 0xFFFFFFFF); - centry->ofs += 4; - SIVAL(centry->data, centry->ofs, nt >> 32); - centry->ofs += 4; -} - -/* - push a time_t into a centry - use a 64 bit size. - NTTIME here is being used as a convenient 64-bit size. -*/ -static void centry_put_time(struct cache_entry *centry, time_t t) -{ - NTTIME nt = (NTTIME)t; - centry_put_nttime(centry, nt); -} - -/* - start a centry for output. When finished, call centry_end() -*/ -struct cache_entry *centry_start(struct winbindd_domain *domain, NTSTATUS status) -{ - struct cache_entry *centry; - - if (!wcache->tdb) - return NULL; - - centry = SMB_XMALLOC_P(struct cache_entry); - - centry->len = 8192; /* reasonable default */ - centry->data = SMB_XMALLOC_ARRAY(uint8, centry->len); - centry->ofs = 0; - centry->sequence_number = domain->sequence_number; - centry_put_ntstatus(centry, status); - centry_put_uint32(centry, centry->sequence_number); - return centry; -} - -/* - finish a centry and write it to the tdb -*/ -static void centry_end(struct cache_entry *centry, const char *format, ...) PRINTF_ATTRIBUTE(2,3); -static void centry_end(struct cache_entry *centry, const char *format, ...) -{ - va_list ap; - char *kstr; - TDB_DATA key, data; - - if (opt_nocache) { - return; - } - - va_start(ap, format); - smb_xvasprintf(&kstr, format, ap); - va_end(ap); - - key = string_tdb_data(kstr); - data.dptr = centry->data; - data.dsize = centry->ofs; - - tdb_store(wcache->tdb, key, data, TDB_REPLACE); - free(kstr); -} - -static void wcache_save_name_to_sid(struct winbindd_domain *domain, - NTSTATUS status, const char *domain_name, - const char *name, const DOM_SID *sid, - enum lsa_SidType type) -{ - struct cache_entry *centry; - fstring uname; - - centry = centry_start(domain, status); - if (!centry) - return; - centry_put_uint32(centry, type); - centry_put_sid(centry, sid); - fstrcpy(uname, name); - strupper_m(uname); - centry_end(centry, "NS/%s/%s", domain_name, uname); - DEBUG(10,("wcache_save_name_to_sid: %s\\%s -> %s (%s)\n", domain_name, uname, - sid_string_static(sid), nt_errstr(status))); - centry_free(centry); -} - -static void wcache_save_sid_to_name(struct winbindd_domain *domain, NTSTATUS status, - const DOM_SID *sid, const char *domain_name, const char *name, enum lsa_SidType type) -{ - struct cache_entry *centry; - fstring sid_string; - - centry = centry_start(domain, status); - if (!centry) - return; - - if (NT_STATUS_IS_OK(status)) { - centry_put_uint32(centry, type); - centry_put_string(centry, domain_name); - centry_put_string(centry, name); - } - - centry_end(centry, "SN/%s", sid_to_string(sid_string, sid)); - DEBUG(10,("wcache_save_sid_to_name: %s -> %s (%s)\n", sid_string, - name, nt_errstr(status))); - centry_free(centry); -} - - -static void wcache_save_user(struct winbindd_domain *domain, NTSTATUS status, WINBIND_USERINFO *info) -{ - struct cache_entry *centry; - fstring sid_string; - - if (is_null_sid(&info->user_sid)) { - return; - } - - centry = centry_start(domain, status); - if (!centry) - return; - centry_put_string(centry, info->acct_name); - centry_put_string(centry, info->full_name); - centry_put_string(centry, info->homedir); - centry_put_string(centry, info->shell); - centry_put_uint32(centry, info->primary_gid); - centry_put_sid(centry, &info->user_sid); - centry_put_sid(centry, &info->group_sid); - centry_end(centry, "U/%s", sid_to_string(sid_string, &info->user_sid)); - DEBUG(10,("wcache_save_user: %s (acct_name %s)\n", sid_string, info->acct_name)); - centry_free(centry); -} - -static void wcache_save_lockout_policy(struct winbindd_domain *domain, NTSTATUS status, SAM_UNK_INFO_12 *lockout_policy) -{ - struct cache_entry *centry; - - centry = centry_start(domain, status); - if (!centry) - return; - - centry_put_nttime(centry, lockout_policy->duration); - centry_put_nttime(centry, lockout_policy->reset_count); - centry_put_uint16(centry, lockout_policy->bad_attempt_lockout); - - centry_end(centry, "LOC_POL/%s", domain->name); - - DEBUG(10,("wcache_save_lockout_policy: %s\n", domain->name)); - - centry_free(centry); -} - -static void wcache_save_password_policy(struct winbindd_domain *domain, NTSTATUS status, SAM_UNK_INFO_1 *policy) -{ - struct cache_entry *centry; - - centry = centry_start(domain, status); - if (!centry) - return; - - centry_put_uint16(centry, policy->min_length_password); - centry_put_uint16(centry, policy->password_history); - centry_put_uint32(centry, policy->password_properties); - centry_put_nttime(centry, policy->expire); - centry_put_nttime(centry, policy->min_passwordage); - - centry_end(centry, "PWD_POL/%s", domain->name); - - DEBUG(10,("wcache_save_password_policy: %s\n", domain->name)); - - centry_free(centry); -} - -NTSTATUS wcache_cached_creds_exist(struct winbindd_domain *domain, const DOM_SID *sid) -{ - struct winbind_cache *cache = get_cache(domain); - TDB_DATA data; - fstring key_str; - uint32 rid; - - if (!cache->tdb) { - return NT_STATUS_INTERNAL_DB_ERROR; - } - - if (is_null_sid(sid)) { - return NT_STATUS_INVALID_SID; - } - - if (!(sid_peek_rid(sid, &rid)) || (rid == 0)) { - return NT_STATUS_INVALID_SID; - } - - fstr_sprintf(key_str, "CRED/%s", sid_string_static(sid)); - - data = tdb_fetch(cache->tdb, string_tdb_data(key_str)); - if (!data.dptr) { - return NT_STATUS_OBJECT_NAME_NOT_FOUND; - } - - SAFE_FREE(data.dptr); - return NT_STATUS_OK; -} - -/* Lookup creds for a SID - copes with old (unsalted) creds as well - as new salted ones. */ - -NTSTATUS wcache_get_creds(struct winbindd_domain *domain, - TALLOC_CTX *mem_ctx, - const DOM_SID *sid, - const uint8 **cached_nt_pass, - const uint8 **cached_salt) -{ - struct winbind_cache *cache = get_cache(domain); - struct cache_entry *centry = NULL; - NTSTATUS status; - time_t t; - uint32 rid; - - if (!cache->tdb) { - return NT_STATUS_INTERNAL_DB_ERROR; - } - - if (is_null_sid(sid)) { - return NT_STATUS_INVALID_SID; - } - - if (!(sid_peek_rid(sid, &rid)) || (rid == 0)) { - return NT_STATUS_INVALID_SID; - } - - /* Try and get a salted cred first. If we can't - fall back to an unsalted cred. */ - - centry = wcache_fetch(cache, domain, "CRED/%s", sid_string_static(sid)); - if (!centry) { - DEBUG(10,("wcache_get_creds: entry for [CRED/%s] not found\n", - sid_string_static(sid))); - return NT_STATUS_OBJECT_NAME_NOT_FOUND; - } - - t = centry_time(centry); - - /* In the salted case this isn't actually the nt_hash itself, - but the MD5 of the salt + nt_hash. Let the caller - sort this out. It can tell as we only return the cached_salt - if we are returning a salted cred. */ - - *cached_nt_pass = (const uint8 *)centry_hash16(centry, mem_ctx); - if (*cached_nt_pass == NULL) { - const char *sidstr = sid_string_static(sid); - - /* Bad (old) cred cache. Delete and pretend we - don't have it. */ - DEBUG(0,("wcache_get_creds: bad entry for [CRED/%s] - deleting\n", - sidstr)); - wcache_delete("CRED/%s", sidstr); - centry_free(centry); - return NT_STATUS_OBJECT_NAME_NOT_FOUND; - } - - /* We only have 17 bytes more data in the salted cred case. */ - if (centry->len - centry->ofs == 17) { - *cached_salt = (const uint8 *)centry_hash16(centry, mem_ctx); - } else { - *cached_salt = NULL; - } - - dump_data_pw("cached_nt_pass", *cached_nt_pass, NT_HASH_LEN); - if (*cached_salt) { - dump_data_pw("cached_salt", *cached_salt, NT_HASH_LEN); - } - - status = centry->status; - - DEBUG(10,("wcache_get_creds: [Cached] - cached creds for user %s status: %s\n", - sid_string_static(sid), nt_errstr(status) )); - - centry_free(centry); - return status; -} - -/* Store creds for a SID - only writes out new salted ones. */ - -NTSTATUS wcache_save_creds(struct winbindd_domain *domain, - TALLOC_CTX *mem_ctx, - const DOM_SID *sid, - const uint8 nt_pass[NT_HASH_LEN]) -{ - struct cache_entry *centry; - fstring sid_string; - uint32 rid; - uint8 cred_salt[NT_HASH_LEN]; - uint8 salted_hash[NT_HASH_LEN]; - - if (is_null_sid(sid)) { - return NT_STATUS_INVALID_SID; - } - - if (!(sid_peek_rid(sid, &rid)) || (rid == 0)) { - return NT_STATUS_INVALID_SID; - } - - centry = centry_start(domain, NT_STATUS_OK); - if (!centry) { - return NT_STATUS_INTERNAL_DB_ERROR; - } - - dump_data_pw("nt_pass", nt_pass, NT_HASH_LEN); - - centry_put_time(centry, time(NULL)); - - /* Create a salt and then salt the hash. */ - generate_random_buffer(cred_salt, NT_HASH_LEN); - E_md5hash(cred_salt, nt_pass, salted_hash); - - centry_put_hash16(centry, salted_hash); - centry_put_hash16(centry, cred_salt); - centry_end(centry, "CRED/%s", sid_to_string(sid_string, sid)); - - DEBUG(10,("wcache_save_creds: %s\n", sid_string)); - - centry_free(centry); - - return NT_STATUS_OK; -} - - -/* Query display info. This is the basic user list fn */ -static NTSTATUS query_user_list(struct winbindd_domain *domain, - TALLOC_CTX *mem_ctx, - uint32 *num_entries, - WINBIND_USERINFO **info) -{ - struct winbind_cache *cache = get_cache(domain); - struct cache_entry *centry = NULL; - NTSTATUS status; - unsigned int i, retry; - - if (!cache->tdb) - goto do_query; - - centry = wcache_fetch(cache, domain, "UL/%s", domain->name); - if (!centry) - goto do_query; - - *num_entries = centry_uint32(centry); - - if (*num_entries == 0) - goto do_cached; - - (*info) = TALLOC_ARRAY(mem_ctx, WINBIND_USERINFO, *num_entries); - if (! (*info)) { - smb_panic_fn("query_user_list out of memory"); - } - for (i=0; i<(*num_entries); i++) { - (*info)[i].acct_name = centry_string(centry, mem_ctx); - (*info)[i].full_name = centry_string(centry, mem_ctx); - (*info)[i].homedir = centry_string(centry, mem_ctx); - (*info)[i].shell = centry_string(centry, mem_ctx); - centry_sid(centry, mem_ctx, &(*info)[i].user_sid); - centry_sid(centry, mem_ctx, &(*info)[i].group_sid); - } - -do_cached: - status = centry->status; - - DEBUG(10,("query_user_list: [Cached] - cached list for domain %s status: %s\n", - domain->name, nt_errstr(status) )); - - centry_free(centry); - return status; - -do_query: - *num_entries = 0; - *info = NULL; - - /* Return status value returned by seq number check */ - - if (!NT_STATUS_IS_OK(domain->last_status)) - return domain->last_status; - - /* Put the query_user_list() in a retry loop. There appears to be - * some bug either with Windows 2000 or Samba's handling of large - * rpc replies. This manifests itself as sudden disconnection - * at a random point in the enumeration of a large (60k) user list. - * The retry loop simply tries the operation again. )-: It's not - * pretty but an acceptable workaround until we work out what the - * real problem is. */ - - retry = 0; - do { - - DEBUG(10,("query_user_list: [Cached] - doing backend query for list for domain %s\n", - domain->name )); - - status = domain->backend->query_user_list(domain, mem_ctx, num_entries, info); - if (!NT_STATUS_IS_OK(status)) { - DEBUG(3, ("query_user_list: returned 0x%08x, " - "retrying\n", NT_STATUS_V(status))); - } - if (NT_STATUS_EQUAL(status, NT_STATUS_UNSUCCESSFUL)) { - DEBUG(3, ("query_user_list: flushing " - "connection cache\n")); - invalidate_cm_connection(&domain->conn); - } - - } while (NT_STATUS_V(status) == NT_STATUS_V(NT_STATUS_UNSUCCESSFUL) && - (retry++ < 5)); - - /* and save it */ - refresh_sequence_number(domain, False); - centry = centry_start(domain, status); - if (!centry) - goto skip_save; - centry_put_uint32(centry, *num_entries); - for (i=0; i<(*num_entries); i++) { - centry_put_string(centry, (*info)[i].acct_name); - centry_put_string(centry, (*info)[i].full_name); - centry_put_string(centry, (*info)[i].homedir); - centry_put_string(centry, (*info)[i].shell); - centry_put_sid(centry, &(*info)[i].user_sid); - centry_put_sid(centry, &(*info)[i].group_sid); - if (domain->backend && domain->backend->consistent) { - /* when the backend is consistent we can pre-prime some mappings */ - wcache_save_name_to_sid(domain, NT_STATUS_OK, - domain->name, - (*info)[i].acct_name, - &(*info)[i].user_sid, - SID_NAME_USER); - wcache_save_sid_to_name(domain, NT_STATUS_OK, - &(*info)[i].user_sid, - domain->name, - (*info)[i].acct_name, - SID_NAME_USER); - wcache_save_user(domain, NT_STATUS_OK, &(*info)[i]); - } - } - centry_end(centry, "UL/%s", domain->name); - centry_free(centry); - -skip_save: - return status; -} - -/* list all domain groups */ -static NTSTATUS enum_dom_groups(struct winbindd_domain *domain, - TALLOC_CTX *mem_ctx, - uint32 *num_entries, - struct acct_info **info) -{ - struct winbind_cache *cache = get_cache(domain); - struct cache_entry *centry = NULL; - NTSTATUS status; - unsigned int i; - - if (!cache->tdb) - goto do_query; - - centry = wcache_fetch(cache, domain, "GL/%s/domain", domain->name); - if (!centry) - goto do_query; - - *num_entries = centry_uint32(centry); - - if (*num_entries == 0) - goto do_cached; - - (*info) = TALLOC_ARRAY(mem_ctx, struct acct_info, *num_entries); - if (! (*info)) { - smb_panic_fn("enum_dom_groups out of memory"); - } - for (i=0; i<(*num_entries); i++) { - fstrcpy((*info)[i].acct_name, centry_string(centry, mem_ctx)); - fstrcpy((*info)[i].acct_desc, centry_string(centry, mem_ctx)); - (*info)[i].rid = centry_uint32(centry); - } - -do_cached: - status = centry->status; - - DEBUG(10,("enum_dom_groups: [Cached] - cached list for domain %s status: %s\n", - domain->name, nt_errstr(status) )); - - centry_free(centry); - return status; - -do_query: - *num_entries = 0; - *info = NULL; - - /* Return status value returned by seq number check */ - - if (!NT_STATUS_IS_OK(domain->last_status)) - return domain->last_status; - - DEBUG(10,("enum_dom_groups: [Cached] - doing backend query for list for domain %s\n", - domain->name )); - - status = domain->backend->enum_dom_groups(domain, mem_ctx, num_entries, info); - - /* and save it */ - refresh_sequence_number(domain, False); - centry = centry_start(domain, status); - if (!centry) - goto skip_save; - centry_put_uint32(centry, *num_entries); - for (i=0; i<(*num_entries); i++) { - centry_put_string(centry, (*info)[i].acct_name); - centry_put_string(centry, (*info)[i].acct_desc); - centry_put_uint32(centry, (*info)[i].rid); - } - centry_end(centry, "GL/%s/domain", domain->name); - centry_free(centry); - -skip_save: - return status; -} - -/* list all domain groups */ -static NTSTATUS enum_local_groups(struct winbindd_domain *domain, - TALLOC_CTX *mem_ctx, - uint32 *num_entries, - struct acct_info **info) -{ - struct winbind_cache *cache = get_cache(domain); - struct cache_entry *centry = NULL; - NTSTATUS status; - unsigned int i; - - if (!cache->tdb) - goto do_query; - - centry = wcache_fetch(cache, domain, "GL/%s/local", domain->name); - if (!centry) - goto do_query; - - *num_entries = centry_uint32(centry); - - if (*num_entries == 0) - goto do_cached; - - (*info) = TALLOC_ARRAY(mem_ctx, struct acct_info, *num_entries); - if (! (*info)) { - smb_panic_fn("enum_dom_groups out of memory"); - } - for (i=0; i<(*num_entries); i++) { - fstrcpy((*info)[i].acct_name, centry_string(centry, mem_ctx)); - fstrcpy((*info)[i].acct_desc, centry_string(centry, mem_ctx)); - (*info)[i].rid = centry_uint32(centry); - } - -do_cached: - - /* If we are returning cached data and the domain controller - is down then we don't know whether the data is up to date - or not. Return NT_STATUS_MORE_PROCESSING_REQUIRED to - indicate this. */ - - if (wcache_server_down(domain)) { - DEBUG(10, ("enum_local_groups: returning cached user list and server was down\n")); - status = NT_STATUS_MORE_PROCESSING_REQUIRED; - } else - status = centry->status; - - DEBUG(10,("enum_local_groups: [Cached] - cached list for domain %s status: %s\n", - domain->name, nt_errstr(status) )); - - centry_free(centry); - return status; - -do_query: - *num_entries = 0; - *info = NULL; - - /* Return status value returned by seq number check */ - - if (!NT_STATUS_IS_OK(domain->last_status)) - return domain->last_status; - - DEBUG(10,("enum_local_groups: [Cached] - doing backend query for list for domain %s\n", - domain->name )); - - status = domain->backend->enum_local_groups(domain, mem_ctx, num_entries, info); - - /* and save it */ - refresh_sequence_number(domain, False); - centry = centry_start(domain, status); - if (!centry) - goto skip_save; - centry_put_uint32(centry, *num_entries); - for (i=0; i<(*num_entries); i++) { - centry_put_string(centry, (*info)[i].acct_name); - centry_put_string(centry, (*info)[i].acct_desc); - centry_put_uint32(centry, (*info)[i].rid); - } - centry_end(centry, "GL/%s/local", domain->name); - centry_free(centry); - -skip_save: - return status; -} - -/* convert a single name to a sid in a domain */ -static NTSTATUS name_to_sid(struct winbindd_domain *domain, - TALLOC_CTX *mem_ctx, - enum winbindd_cmd orig_cmd, - const char *domain_name, - const char *name, - DOM_SID *sid, - enum lsa_SidType *type) -{ - struct winbind_cache *cache = get_cache(domain); - struct cache_entry *centry = NULL; - NTSTATUS status; - fstring uname; - - if (!cache->tdb) - goto do_query; - - fstrcpy(uname, name); - strupper_m(uname); - centry = wcache_fetch(cache, domain, "NS/%s/%s", domain_name, uname); - if (!centry) - goto do_query; - - status = centry->status; - if (NT_STATUS_IS_OK(status)) { - *type = (enum lsa_SidType)centry_uint32(centry); - centry_sid(centry, mem_ctx, sid); - } - - DEBUG(10,("name_to_sid: [Cached] - cached name for domain %s status: %s\n", - domain->name, nt_errstr(status) )); - - centry_free(centry); - return status; - -do_query: - ZERO_STRUCTP(sid); - - /* If the seq number check indicated that there is a problem - * with this DC, then return that status... except for - * access_denied. This is special because the dc may be in - * "restrict anonymous = 1" mode, in which case it will deny - * most unauthenticated operations, but *will* allow the LSA - * name-to-sid that we try as a fallback. */ - - if (!(NT_STATUS_IS_OK(domain->last_status) - || NT_STATUS_EQUAL(domain->last_status, NT_STATUS_ACCESS_DENIED))) - return domain->last_status; - - DEBUG(10,("name_to_sid: [Cached] - doing backend query for name for domain %s\n", - domain->name )); - - status = domain->backend->name_to_sid(domain, mem_ctx, orig_cmd, - domain_name, name, sid, type); - - /* and save it */ - refresh_sequence_number(domain, False); - - if (domain->online && - (NT_STATUS_IS_OK(status) || NT_STATUS_EQUAL(status, NT_STATUS_NONE_MAPPED))) { - wcache_save_name_to_sid(domain, status, domain_name, name, sid, *type); - - /* Only save the reverse mapping if this was not a UPN */ - if (!strchr(name, '@')) { - strupper_m(CONST_DISCARD(char *,domain_name)); - strlower_m(CONST_DISCARD(char *,name)); - wcache_save_sid_to_name(domain, status, sid, domain_name, name, *type); - } - } - - return status; -} - -/* convert a sid to a user or group name. The sid is guaranteed to be in the domain - given */ -static NTSTATUS sid_to_name(struct winbindd_domain *domain, - TALLOC_CTX *mem_ctx, - const DOM_SID *sid, - char **domain_name, - char **name, - enum lsa_SidType *type) -{ - struct winbind_cache *cache = get_cache(domain); - struct cache_entry *centry = NULL; - NTSTATUS status; - fstring sid_string; - - if (!cache->tdb) - goto do_query; - - centry = wcache_fetch(cache, domain, "SN/%s", sid_to_string(sid_string, sid)); - if (!centry) - goto do_query; - - status = centry->status; - if (NT_STATUS_IS_OK(status)) { - *type = (enum lsa_SidType)centry_uint32(centry); - *domain_name = centry_string(centry, mem_ctx); - *name = centry_string(centry, mem_ctx); - } - - DEBUG(10,("sid_to_name: [Cached] - cached name for domain %s status: %s\n", - domain->name, nt_errstr(status) )); - - centry_free(centry); - return status; - -do_query: - *name = NULL; - *domain_name = NULL; - - /* If the seq number check indicated that there is a problem - * with this DC, then return that status... except for - * access_denied. This is special because the dc may be in - * "restrict anonymous = 1" mode, in which case it will deny - * most unauthenticated operations, but *will* allow the LSA - * sid-to-name that we try as a fallback. */ - - if (!(NT_STATUS_IS_OK(domain->last_status) - || NT_STATUS_EQUAL(domain->last_status, NT_STATUS_ACCESS_DENIED))) - return domain->last_status; - - DEBUG(10,("sid_to_name: [Cached] - doing backend query for name for domain %s\n", - domain->name )); - - status = domain->backend->sid_to_name(domain, mem_ctx, sid, domain_name, name, type); - - /* and save it */ - refresh_sequence_number(domain, False); - wcache_save_sid_to_name(domain, status, sid, *domain_name, *name, *type); - - /* We can't save the name to sid mapping here, as with sid history a - * later name2sid would give the wrong sid. */ - - return status; -} - -static NTSTATUS rids_to_names(struct winbindd_domain *domain, - TALLOC_CTX *mem_ctx, - const DOM_SID *domain_sid, - uint32 *rids, - size_t num_rids, - char **domain_name, - char ***names, - enum lsa_SidType **types) -{ - struct winbind_cache *cache = get_cache(domain); - size_t i; - NTSTATUS result = NT_STATUS_UNSUCCESSFUL; - BOOL have_mapped; - BOOL have_unmapped; - - *domain_name = NULL; - *names = NULL; - *types = NULL; - - if (!cache->tdb) { - goto do_query; - } - - if (num_rids == 0) { - return NT_STATUS_OK; - } - - *names = TALLOC_ARRAY(mem_ctx, char *, num_rids); - *types = TALLOC_ARRAY(mem_ctx, enum lsa_SidType, num_rids); - - if ((*names == NULL) || (*types == NULL)) { - result = NT_STATUS_NO_MEMORY; - goto error; - } - - have_mapped = have_unmapped = False; - - for (i=0; istatus)) { - char *dom; - have_mapped = True; - (*types)[i] = (enum lsa_SidType)centry_uint32(centry); - - dom = centry_string(centry, mem_ctx); - if (*domain_name == NULL) { - *domain_name = dom; - } else { - talloc_free(dom); - } - - (*names)[i] = centry_string(centry, *names); - - } else if (NT_STATUS_EQUAL(centry->status, NT_STATUS_NONE_MAPPED)) { - have_unmapped = True; - - } else { - /* something's definitely wrong */ - result = centry->status; - goto error; - } - - centry_free(centry); - } - - if (!have_mapped) { - return NT_STATUS_NONE_MAPPED; - } - if (!have_unmapped) { - return NT_STATUS_OK; - } - return STATUS_SOME_UNMAPPED; - - do_query: - - TALLOC_FREE(*names); - TALLOC_FREE(*types); - - result = domain->backend->rids_to_names(domain, mem_ctx, domain_sid, - rids, num_rids, domain_name, - names, types); - - /* - None of the queried rids has been found so save all negative entries - */ - if (NT_STATUS_EQUAL(result, NT_STATUS_NONE_MAPPED)) { - for (i = 0; i < num_rids; i++) { - DOM_SID sid; - const char *name = ""; - const enum lsa_SidType type = SID_NAME_UNKNOWN; - NTSTATUS status = NT_STATUS_NONE_MAPPED; - - if (!sid_compose(&sid, domain_sid, rids[i])) { - return NT_STATUS_INTERNAL_ERROR; - } - - wcache_save_sid_to_name(domain, status, &sid, *domain_name, - name, type); - } - - return result; - } - - /* - Some or all of the queried rids have been found. - */ - if (!NT_STATUS_IS_OK(result) && - !NT_STATUS_EQUAL(result, STATUS_SOME_UNMAPPED)) { - return result; - } - - refresh_sequence_number(domain, False); - - for (i=0; itdb) - goto do_query; - - centry = wcache_fetch(cache, domain, "U/%s", sid_string_static(user_sid)); - - /* If we have an access denied cache entry and a cached info3 in the - samlogon cache then do a query. This will force the rpc back end - to return the info3 data. */ - - if (NT_STATUS_V(domain->last_status) == NT_STATUS_V(NT_STATUS_ACCESS_DENIED) && - netsamlogon_cache_have(user_sid)) { - DEBUG(10, ("query_user: cached access denied and have cached info3\n")); - domain->last_status = NT_STATUS_OK; - centry_free(centry); - goto do_query; - } - - if (!centry) - goto do_query; - - /* if status is not ok then this is a negative hit - and the rest of the data doesn't matter */ - status = centry->status; - if (NT_STATUS_IS_OK(status)) { - info->acct_name = centry_string(centry, mem_ctx); - info->full_name = centry_string(centry, mem_ctx); - info->homedir = centry_string(centry, mem_ctx); - info->shell = centry_string(centry, mem_ctx); - info->primary_gid = centry_uint32(centry); - centry_sid(centry, mem_ctx, &info->user_sid); - centry_sid(centry, mem_ctx, &info->group_sid); - } - - DEBUG(10,("query_user: [Cached] - cached info for domain %s status: %s\n", - domain->name, nt_errstr(status) )); - - centry_free(centry); - return status; - -do_query: - ZERO_STRUCTP(info); - - /* Return status value returned by seq number check */ - - if (!NT_STATUS_IS_OK(domain->last_status)) - return domain->last_status; - - DEBUG(10,("query_user: [Cached] - doing backend query for info for domain %s\n", - domain->name )); - - status = domain->backend->query_user(domain, mem_ctx, user_sid, info); - - /* and save it */ - refresh_sequence_number(domain, False); - wcache_save_user(domain, status, info); - - return status; -} - - -/* Lookup groups a user is a member of. */ -static NTSTATUS lookup_usergroups(struct winbindd_domain *domain, - TALLOC_CTX *mem_ctx, - const DOM_SID *user_sid, - uint32 *num_groups, DOM_SID **user_gids) -{ - struct winbind_cache *cache = get_cache(domain); - struct cache_entry *centry = NULL; - NTSTATUS status; - unsigned int i; - fstring sid_string; - - if (!cache->tdb) - goto do_query; - - centry = wcache_fetch(cache, domain, "UG/%s", sid_to_string(sid_string, user_sid)); - - /* If we have an access denied cache entry and a cached info3 in the - samlogon cache then do a query. This will force the rpc back end - to return the info3 data. */ - - if (NT_STATUS_V(domain->last_status) == NT_STATUS_V(NT_STATUS_ACCESS_DENIED) && - netsamlogon_cache_have(user_sid)) { - DEBUG(10, ("lookup_usergroups: cached access denied and have cached info3\n")); - domain->last_status = NT_STATUS_OK; - centry_free(centry); - goto do_query; - } - - if (!centry) - goto do_query; - - *num_groups = centry_uint32(centry); - - if (*num_groups == 0) - goto do_cached; - - (*user_gids) = TALLOC_ARRAY(mem_ctx, DOM_SID, *num_groups); - if (! (*user_gids)) { - smb_panic_fn("lookup_usergroups out of memory"); - } - for (i=0; i<(*num_groups); i++) { - centry_sid(centry, mem_ctx, &(*user_gids)[i]); - } - -do_cached: - status = centry->status; - - DEBUG(10,("lookup_usergroups: [Cached] - cached info for domain %s status: %s\n", - domain->name, nt_errstr(status) )); - - centry_free(centry); - return status; - -do_query: - (*num_groups) = 0; - (*user_gids) = NULL; - - /* Return status value returned by seq number check */ - - if (!NT_STATUS_IS_OK(domain->last_status)) - return domain->last_status; - - DEBUG(10,("lookup_usergroups: [Cached] - doing backend query for info for domain %s\n", - domain->name )); - - status = domain->backend->lookup_usergroups(domain, mem_ctx, user_sid, num_groups, user_gids); - - if ( NT_STATUS_EQUAL(status, NT_STATUS_SYNCHRONIZATION_REQUIRED) ) - goto skip_save; - - /* and save it */ - refresh_sequence_number(domain, False); - centry = centry_start(domain, status); - if (!centry) - goto skip_save; - - centry_put_uint32(centry, *num_groups); - for (i=0; i<(*num_groups); i++) { - centry_put_sid(centry, &(*user_gids)[i]); - } - - centry_end(centry, "UG/%s", sid_to_string(sid_string, user_sid)); - centry_free(centry); - -skip_save: - return status; -} - -static NTSTATUS lookup_useraliases(struct winbindd_domain *domain, - TALLOC_CTX *mem_ctx, - uint32 num_sids, const DOM_SID *sids, - uint32 *num_aliases, uint32 **alias_rids) -{ - struct winbind_cache *cache = get_cache(domain); - struct cache_entry *centry = NULL; - NTSTATUS status; - char *sidlist = talloc_strdup(mem_ctx, ""); - int i; - - if (!cache->tdb) - goto do_query; - - if (num_sids == 0) { - *num_aliases = 0; - *alias_rids = NULL; - return NT_STATUS_OK; - } - - /* We need to cache indexed by the whole list of SIDs, the aliases - * resulting might come from any of the SIDs. */ - - for (i=0; istatus; - - DEBUG(10,("lookup_useraliases: [Cached] - cached info for domain: %s " - "status %s\n", domain->name, nt_errstr(status))); - - centry_free(centry); - return status; - - do_query: - (*num_aliases) = 0; - (*alias_rids) = NULL; - - if (!NT_STATUS_IS_OK(domain->last_status)) - return domain->last_status; - - DEBUG(10,("lookup_usergroups: [Cached] - doing backend query for info " - "for domain %s\n", domain->name )); - - status = domain->backend->lookup_useraliases(domain, mem_ctx, - num_sids, sids, - num_aliases, alias_rids); - - /* and save it */ - refresh_sequence_number(domain, False); - centry = centry_start(domain, status); - if (!centry) - goto skip_save; - centry_put_uint32(centry, *num_aliases); - for (i=0; i<(*num_aliases); i++) - centry_put_uint32(centry, (*alias_rids)[i]); - centry_end(centry, "UA%s", sidlist); - centry_free(centry); - - skip_save: - return status; -} - - -static NTSTATUS lookup_groupmem(struct winbindd_domain *domain, - TALLOC_CTX *mem_ctx, - const DOM_SID *group_sid, uint32 *num_names, - DOM_SID **sid_mem, char ***names, - uint32 **name_types) -{ - struct winbind_cache *cache = get_cache(domain); - struct cache_entry *centry = NULL; - NTSTATUS status; - unsigned int i; - fstring sid_string; - - if (!cache->tdb) - goto do_query; - - centry = wcache_fetch(cache, domain, "GM/%s", sid_to_string(sid_string, group_sid)); - if (!centry) - goto do_query; - - *num_names = centry_uint32(centry); - - if (*num_names == 0) - goto do_cached; - - (*sid_mem) = TALLOC_ARRAY(mem_ctx, DOM_SID, *num_names); - (*names) = TALLOC_ARRAY(mem_ctx, char *, *num_names); - (*name_types) = TALLOC_ARRAY(mem_ctx, uint32, *num_names); - - if (! (*sid_mem) || ! (*names) || ! (*name_types)) { - smb_panic_fn("lookup_groupmem out of memory"); - } - - for (i=0; i<(*num_names); i++) { - centry_sid(centry, mem_ctx, &(*sid_mem)[i]); - (*names)[i] = centry_string(centry, mem_ctx); - (*name_types)[i] = centry_uint32(centry); - } - -do_cached: - status = centry->status; - - DEBUG(10,("lookup_groupmem: [Cached] - cached info for domain %s status: %s\n", - domain->name, nt_errstr(status))); - - centry_free(centry); - return status; - -do_query: - (*num_names) = 0; - (*sid_mem) = NULL; - (*names) = NULL; - (*name_types) = NULL; - - /* Return status value returned by seq number check */ - - if (!NT_STATUS_IS_OK(domain->last_status)) - return domain->last_status; - - DEBUG(10,("lookup_groupmem: [Cached] - doing backend query for info for domain %s\n", - domain->name )); - - status = domain->backend->lookup_groupmem(domain, mem_ctx, group_sid, num_names, - sid_mem, names, name_types); - - /* and save it */ - refresh_sequence_number(domain, False); - centry = centry_start(domain, status); - if (!centry) - goto skip_save; - centry_put_uint32(centry, *num_names); - for (i=0; i<(*num_names); i++) { - centry_put_sid(centry, &(*sid_mem)[i]); - centry_put_string(centry, (*names)[i]); - centry_put_uint32(centry, (*name_types)[i]); - } - centry_end(centry, "GM/%s", sid_to_string(sid_string, group_sid)); - centry_free(centry); - -skip_save: - return status; -} - -/* find the sequence number for a domain */ -static NTSTATUS sequence_number(struct winbindd_domain *domain, uint32 *seq) -{ - refresh_sequence_number(domain, False); - - *seq = domain->sequence_number; - - return NT_STATUS_OK; -} - -/* enumerate trusted domains - * (we need to have the list of trustdoms in the cache when we go offline) - - * Guenther */ -static NTSTATUS trusted_domains(struct winbindd_domain *domain, - TALLOC_CTX *mem_ctx, - uint32 *num_domains, - char ***names, - char ***alt_names, - DOM_SID **dom_sids) -{ - struct winbind_cache *cache = get_cache(domain); - struct cache_entry *centry = NULL; - NTSTATUS status; - int i; - - if (!cache->tdb) - goto do_query; - - centry = wcache_fetch(cache, domain, "TRUSTDOMS/%s", domain->name); - - if (!centry) { - goto do_query; - } - - *num_domains = centry_uint32(centry); - - if (*num_domains) { - (*names) = TALLOC_ARRAY(mem_ctx, char *, *num_domains); - (*alt_names) = TALLOC_ARRAY(mem_ctx, char *, *num_domains); - (*dom_sids) = TALLOC_ARRAY(mem_ctx, DOM_SID, *num_domains); - - if (! (*dom_sids) || ! (*names) || ! (*alt_names)) { - smb_panic_fn("trusted_domains out of memory"); - } - } else { - (*names) = NULL; - (*alt_names) = NULL; - (*dom_sids) = NULL; - } - - for (i=0; i<(*num_domains); i++) { - (*names)[i] = centry_string(centry, mem_ctx); - (*alt_names)[i] = centry_string(centry, mem_ctx); - centry_sid(centry, mem_ctx, &(*dom_sids)[i]); - } - - status = centry->status; - - DEBUG(10,("trusted_domains: [Cached] - cached info for domain %s (%d trusts) status: %s\n", - domain->name, *num_domains, nt_errstr(status) )); - - centry_free(centry); - return status; - -do_query: - (*num_domains) = 0; - (*dom_sids) = NULL; - (*names) = NULL; - (*alt_names) = NULL; - - /* Return status value returned by seq number check */ - - if (!NT_STATUS_IS_OK(domain->last_status)) - return domain->last_status; - - DEBUG(10,("trusted_domains: [Cached] - doing backend query for info for domain %s\n", - domain->name )); - - status = domain->backend->trusted_domains(domain, mem_ctx, num_domains, - names, alt_names, dom_sids); - - /* no trusts gives NT_STATUS_NO_MORE_ENTRIES resetting to NT_STATUS_OK - * so that the generic centry handling still applies correctly - - * Guenther*/ - - if (!NT_STATUS_IS_ERR(status)) { - status = NT_STATUS_OK; - } - - -#if 0 /* Disabled as we want the trust dom list to be managed by - the main parent and always to make the query. --jerry */ - - /* and save it */ - refresh_sequence_number(domain, False); - - centry = centry_start(domain, status); - if (!centry) - goto skip_save; - - centry_put_uint32(centry, *num_domains); - - for (i=0; i<(*num_domains); i++) { - centry_put_string(centry, (*names)[i]); - centry_put_string(centry, (*alt_names)[i]); - centry_put_sid(centry, &(*dom_sids)[i]); - } - - centry_end(centry, "TRUSTDOMS/%s", domain->name); - - centry_free(centry); - -skip_save: -#endif - - return status; -} - -/* get lockout policy */ -static NTSTATUS lockout_policy(struct winbindd_domain *domain, - TALLOC_CTX *mem_ctx, - SAM_UNK_INFO_12 *policy){ - struct winbind_cache *cache = get_cache(domain); - struct cache_entry *centry = NULL; - NTSTATUS status; - - if (!cache->tdb) - goto do_query; - - centry = wcache_fetch(cache, domain, "LOC_POL/%s", domain->name); - - if (!centry) - goto do_query; - - policy->duration = centry_nttime(centry); - policy->reset_count = centry_nttime(centry); - policy->bad_attempt_lockout = centry_uint16(centry); - - status = centry->status; - - DEBUG(10,("lockout_policy: [Cached] - cached info for domain %s status: %s\n", - domain->name, nt_errstr(status) )); - - centry_free(centry); - return status; - -do_query: - ZERO_STRUCTP(policy); - - /* Return status value returned by seq number check */ - - if (!NT_STATUS_IS_OK(domain->last_status)) - return domain->last_status; - - DEBUG(10,("lockout_policy: [Cached] - doing backend query for info for domain %s\n", - domain->name )); - - status = domain->backend->lockout_policy(domain, mem_ctx, policy); - - /* and save it */ - refresh_sequence_number(domain, False); - wcache_save_lockout_policy(domain, status, policy); - - return status; -} - -/* get password policy */ -static NTSTATUS password_policy(struct winbindd_domain *domain, - TALLOC_CTX *mem_ctx, - SAM_UNK_INFO_1 *policy) -{ - struct winbind_cache *cache = get_cache(domain); - struct cache_entry *centry = NULL; - NTSTATUS status; - - if (!cache->tdb) - goto do_query; - - centry = wcache_fetch(cache, domain, "PWD_POL/%s", domain->name); - - if (!centry) - goto do_query; - - policy->min_length_password = centry_uint16(centry); - policy->password_history = centry_uint16(centry); - policy->password_properties = centry_uint32(centry); - policy->expire = centry_nttime(centry); - policy->min_passwordage = centry_nttime(centry); - - status = centry->status; - - DEBUG(10,("lockout_policy: [Cached] - cached info for domain %s status: %s\n", - domain->name, nt_errstr(status) )); - - centry_free(centry); - return status; - -do_query: - ZERO_STRUCTP(policy); - - /* Return status value returned by seq number check */ - - if (!NT_STATUS_IS_OK(domain->last_status)) - return domain->last_status; - - DEBUG(10,("password_policy: [Cached] - doing backend query for info for domain %s\n", - domain->name )); - - status = domain->backend->password_policy(domain, mem_ctx, policy); - - /* and save it */ - refresh_sequence_number(domain, False); - wcache_save_password_policy(domain, status, policy); - - return status; -} - - -/* Invalidate cached user and group lists coherently */ - -static int traverse_fn(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf, - void *state) -{ - if (strncmp((const char *)kbuf.dptr, "UL/", 3) == 0 || - strncmp((const char *)kbuf.dptr, "GL/", 3) == 0) - tdb_delete(the_tdb, kbuf); - - return 0; -} - -/* Invalidate the getpwnam and getgroups entries for a winbindd domain */ - -void wcache_invalidate_samlogon(struct winbindd_domain *domain, - NET_USER_INFO_3 *info3) -{ - struct winbind_cache *cache; - - /* dont clear cached U/SID and UG/SID entries when we want to logon - * offline - gd */ - - if (lp_winbind_offline_logon()) { - return; - } - - if (!domain) - return; - - cache = get_cache(domain); - netsamlogon_clear_cached_user(cache->tdb, info3); -} - -void wcache_invalidate_cache(void) -{ - struct winbindd_domain *domain; - - for (domain = domain_list(); domain; domain = domain->next) { - struct winbind_cache *cache = get_cache(domain); - - DEBUG(10, ("wcache_invalidate_cache: invalidating cache " - "entries for %s\n", domain->name)); - if (cache) - tdb_traverse(cache->tdb, traverse_fn, NULL); - } -} - -BOOL init_wcache(void) -{ - if (wcache == NULL) { - wcache = SMB_XMALLOC_P(struct winbind_cache); - ZERO_STRUCTP(wcache); - } - - if (wcache->tdb != NULL) - return True; - - /* when working offline we must not clear the cache on restart */ - wcache->tdb = tdb_open_log(lock_path("winbindd_cache.tdb"), - WINBINDD_CACHE_TDB_DEFAULT_HASH_SIZE, - lp_winbind_offline_logon() ? TDB_DEFAULT : (TDB_DEFAULT | TDB_CLEAR_IF_FIRST), - O_RDWR|O_CREAT, 0600); - - if (wcache->tdb == NULL) { - DEBUG(0,("Failed to open winbindd_cache.tdb!\n")); - return False; - } - - return True; -} - -/************************************************************************ - This is called by the parent to initialize the cache file. - We don't need sophisticated locking here as we know we're the - only opener. -************************************************************************/ - -BOOL initialize_winbindd_cache(void) -{ - BOOL cache_bad = True; - uint32 vers; - - if (!init_wcache()) { - DEBUG(0,("initialize_winbindd_cache: init_wcache failed.\n")); - return False; - } - - /* Check version number. */ - if (tdb_fetch_uint32(wcache->tdb, WINBINDD_CACHE_VERSION_KEYSTR, &vers) && - vers == WINBINDD_CACHE_VERSION) { - cache_bad = False; - } - - if (cache_bad) { - DEBUG(0,("initialize_winbindd_cache: clearing cache " - "and re-creating with version number %d\n", - WINBINDD_CACHE_VERSION )); - - tdb_close(wcache->tdb); - wcache->tdb = NULL; - - if (unlink(lock_path("winbindd_cache.tdb")) == -1) { - DEBUG(0,("initialize_winbindd_cache: unlink %s failed %s ", - lock_path("winbindd_cache.tdb"), - strerror(errno) )); - return False; - } - if (!init_wcache()) { - DEBUG(0,("initialize_winbindd_cache: re-initialization " - "init_wcache failed.\n")); - return False; - } - - /* Write the version. */ - if (!tdb_store_uint32(wcache->tdb, WINBINDD_CACHE_VERSION_KEYSTR, WINBINDD_CACHE_VERSION)) { - DEBUG(0,("initialize_winbindd_cache: version number store failed %s\n", - tdb_errorstr(wcache->tdb) )); - return False; - } - } - - tdb_close(wcache->tdb); - wcache->tdb = NULL; - return True; -} - -void cache_store_response(pid_t pid, struct winbindd_response *response) -{ - fstring key_str; - - if (!init_wcache()) - return; - - DEBUG(10, ("Storing response for pid %d, len %d\n", - pid, response->length)); - - fstr_sprintf(key_str, "DR/%d", pid); - if (tdb_store(wcache->tdb, string_tdb_data(key_str), - make_tdb_data((uint8 *)response, sizeof(*response)), - TDB_REPLACE) == -1) - return; - - if (response->length == sizeof(*response)) - return; - - /* There's extra data */ - - DEBUG(10, ("Storing extra data: len=%d\n", - (int)(response->length - sizeof(*response)))); - - fstr_sprintf(key_str, "DE/%d", pid); - if (tdb_store(wcache->tdb, string_tdb_data(key_str), - make_tdb_data((uint8 *)response->extra_data.data, - response->length - sizeof(*response)), - TDB_REPLACE) == 0) - return; - - /* We could not store the extra data, make sure the tdb does not - * contain a main record with wrong dangling extra data */ - - fstr_sprintf(key_str, "DR/%d", pid); - tdb_delete(wcache->tdb, string_tdb_data(key_str)); - - return; -} - -BOOL cache_retrieve_response(pid_t pid, struct winbindd_response * response) -{ - TDB_DATA data; - fstring key_str; - - if (!init_wcache()) - return False; - - DEBUG(10, ("Retrieving response for pid %d\n", pid)); - - fstr_sprintf(key_str, "DR/%d", pid); - data = tdb_fetch(wcache->tdb, string_tdb_data(key_str)); - - if (data.dptr == NULL) - return False; - - if (data.dsize != sizeof(*response)) - return False; - - memcpy(response, data.dptr, data.dsize); - SAFE_FREE(data.dptr); - - if (response->length == sizeof(*response)) { - response->extra_data.data = NULL; - return True; - } - - /* There's extra data */ - - DEBUG(10, ("Retrieving extra data length=%d\n", - (int)(response->length - sizeof(*response)))); - - fstr_sprintf(key_str, "DE/%d", pid); - data = tdb_fetch(wcache->tdb, string_tdb_data(key_str)); - - if (data.dptr == NULL) { - DEBUG(0, ("Did not find extra data\n")); - return False; - } - - if (data.dsize != (response->length - sizeof(*response))) { - DEBUG(0, ("Invalid extra data length: %d\n", (int)data.dsize)); - SAFE_FREE(data.dptr); - return False; - } - - dump_data(11, (uint8 *)data.dptr, data.dsize); - - response->extra_data.data = data.dptr; - return True; -} - -void cache_cleanup_response(pid_t pid) -{ - fstring key_str; - - if (!init_wcache()) - return; - - fstr_sprintf(key_str, "DR/%d", pid); - tdb_delete(wcache->tdb, string_tdb_data(key_str)); - - fstr_sprintf(key_str, "DE/%d", pid); - tdb_delete(wcache->tdb, string_tdb_data(key_str)); - - return; -} - - -BOOL lookup_cached_sid(TALLOC_CTX *mem_ctx, const DOM_SID *sid, - char **domain_name, char **name, - enum lsa_SidType *type) -{ - struct winbindd_domain *domain; - struct winbind_cache *cache; - struct cache_entry *centry = NULL; - NTSTATUS status; - - domain = find_lookup_domain_from_sid(sid); - if (domain == NULL) { - return False; - } - - cache = get_cache(domain); - - if (cache->tdb == NULL) { - return False; - } - - centry = wcache_fetch(cache, domain, "SN/%s", sid_string_static(sid)); - if (centry == NULL) { - return False; - } - - if (NT_STATUS_IS_OK(centry->status)) { - *type = (enum lsa_SidType)centry_uint32(centry); - *domain_name = centry_string(centry, mem_ctx); - *name = centry_string(centry, mem_ctx); - } - - status = centry->status; - centry_free(centry); - return NT_STATUS_IS_OK(status); -} - -BOOL lookup_cached_name(TALLOC_CTX *mem_ctx, - const char *domain_name, - const char *name, - DOM_SID *sid, - enum lsa_SidType *type) -{ - struct winbindd_domain *domain; - struct winbind_cache *cache; - struct cache_entry *centry = NULL; - NTSTATUS status; - fstring uname; - BOOL original_online_state; - - domain = find_lookup_domain_from_name(domain_name); - if (domain == NULL) { - return False; - } - - cache = get_cache(domain); - - if (cache->tdb == NULL) { - return False; - } - - fstrcpy(uname, name); - strupper_m(uname); - - /* If we are doing a cached logon, temporarily set the domain - offline so the cache won't expire the entry */ - - original_online_state = domain->online; - domain->online = False; - centry = wcache_fetch(cache, domain, "NS/%s/%s", domain_name, uname); - domain->online = original_online_state; - - if (centry == NULL) { - return False; - } - - if (NT_STATUS_IS_OK(centry->status)) { - *type = (enum lsa_SidType)centry_uint32(centry); - centry_sid(centry, mem_ctx, sid); - } - - status = centry->status; - centry_free(centry); - - return NT_STATUS_IS_OK(status); -} - -void cache_name2sid(struct winbindd_domain *domain, - const char *domain_name, const char *name, - enum lsa_SidType type, const DOM_SID *sid) -{ - refresh_sequence_number(domain, False); - wcache_save_name_to_sid(domain, NT_STATUS_OK, domain_name, name, - sid, type); -} - -/* - * The original idea that this cache only contains centries has - * been blurred - now other stuff gets put in here. Ensure we - * ignore these things on cleanup. - */ - -static int traverse_fn_cleanup(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, - TDB_DATA dbuf, void *state) -{ - struct cache_entry *centry; - - if (is_non_centry_key(kbuf)) { - return 0; - } - - centry = wcache_fetch_raw((char *)kbuf.dptr); - if (!centry) { - return 0; - } - - if (!NT_STATUS_IS_OK(centry->status)) { - DEBUG(10,("deleting centry %s\n", (const char *)kbuf.dptr)); - tdb_delete(the_tdb, kbuf); - } - - centry_free(centry); - return 0; -} - -/* flush the cache */ -void wcache_flush_cache(void) -{ - if (!wcache) - return; - if (wcache->tdb) { - tdb_close(wcache->tdb); - wcache->tdb = NULL; - } - if (opt_nocache) - return; - - /* when working offline we must not clear the cache on restart */ - wcache->tdb = tdb_open_log(lock_path("winbindd_cache.tdb"), - WINBINDD_CACHE_TDB_DEFAULT_HASH_SIZE, - lp_winbind_offline_logon() ? TDB_DEFAULT : (TDB_DEFAULT | TDB_CLEAR_IF_FIRST), - O_RDWR|O_CREAT, 0600); - - if (!wcache->tdb) { - DEBUG(0,("Failed to open winbindd_cache.tdb!\n")); - return; - } - - tdb_traverse(wcache->tdb, traverse_fn_cleanup, NULL); - - DEBUG(10,("wcache_flush_cache success\n")); -} - -/* Count cached creds */ - -static int traverse_fn_cached_creds(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf, - void *state) -{ - int *cred_count = (int*)state; - - if (strncmp((const char *)kbuf.dptr, "CRED/", 5) == 0) { - (*cred_count)++; - } - return 0; -} - -NTSTATUS wcache_count_cached_creds(struct winbindd_domain *domain, int *count) -{ - struct winbind_cache *cache = get_cache(domain); - - *count = 0; - - if (!cache->tdb) { - return NT_STATUS_INTERNAL_DB_ERROR; - } - - tdb_traverse(cache->tdb, traverse_fn_cached_creds, (void *)count); - - return NT_STATUS_OK; -} - -struct cred_list { - struct cred_list *prev, *next; - TDB_DATA key; - fstring name; - time_t created; -}; -static struct cred_list *wcache_cred_list; - -static int traverse_fn_get_credlist(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf, - void *state) -{ - struct cred_list *cred; - - if (strncmp((const char *)kbuf.dptr, "CRED/", 5) == 0) { - - cred = SMB_MALLOC_P(struct cred_list); - if (cred == NULL) { - DEBUG(0,("traverse_fn_remove_first_creds: failed to malloc new entry for list\n")); - return -1; - } - - ZERO_STRUCTP(cred); - - /* save a copy of the key */ - - fstrcpy(cred->name, (const char *)kbuf.dptr); - DLIST_ADD(wcache_cred_list, cred); - } - - return 0; -} - -NTSTATUS wcache_remove_oldest_cached_creds(struct winbindd_domain *domain, const DOM_SID *sid) -{ - struct winbind_cache *cache = get_cache(domain); - NTSTATUS status; - int ret; - struct cred_list *cred, *oldest = NULL; - - if (!cache->tdb) { - return NT_STATUS_INTERNAL_DB_ERROR; - } - - /* we possibly already have an entry */ - if (sid && NT_STATUS_IS_OK(wcache_cached_creds_exist(domain, sid))) { - - fstring key_str; - - DEBUG(11,("we already have an entry, deleting that\n")); - - fstr_sprintf(key_str, "CRED/%s", sid_string_static(sid)); - - tdb_delete(cache->tdb, string_tdb_data(key_str)); - - return NT_STATUS_OK; - } - - ret = tdb_traverse(cache->tdb, traverse_fn_get_credlist, NULL); - if (ret == 0) { - return NT_STATUS_OK; - } else if ((ret == -1) || (wcache_cred_list == NULL)) { - return NT_STATUS_OBJECT_NAME_NOT_FOUND; - } - - ZERO_STRUCTP(oldest); - - for (cred = wcache_cred_list; cred; cred = cred->next) { - - TDB_DATA data; - time_t t; - - data = tdb_fetch(cache->tdb, string_tdb_data(cred->name)); - if (!data.dptr) { - DEBUG(10,("wcache_remove_oldest_cached_creds: entry for [%s] not found\n", - cred->name)); - status = NT_STATUS_OBJECT_NAME_NOT_FOUND; - goto done; - } - - t = IVAL(data.dptr, 0); - SAFE_FREE(data.dptr); - - if (!oldest) { - oldest = SMB_MALLOC_P(struct cred_list); - if (oldest == NULL) { - status = NT_STATUS_NO_MEMORY; - goto done; - } - - fstrcpy(oldest->name, cred->name); - oldest->created = t; - continue; - } - - if (t < oldest->created) { - fstrcpy(oldest->name, cred->name); - oldest->created = t; - } - } - - if (tdb_delete(cache->tdb, string_tdb_data(oldest->name)) == 0) { - status = NT_STATUS_OK; - } else { - status = NT_STATUS_UNSUCCESSFUL; - } -done: - SAFE_FREE(wcache_cred_list); - SAFE_FREE(oldest); - - return status; -} - -/* Change the global online/offline state. */ -BOOL set_global_winbindd_state_offline(void) -{ - TDB_DATA data; - - DEBUG(10,("set_global_winbindd_state_offline: offline requested.\n")); - - /* Only go offline if someone has created - the key "WINBINDD_OFFLINE" in the cache tdb. */ - - if (wcache == NULL || wcache->tdb == NULL) { - DEBUG(10,("set_global_winbindd_state_offline: wcache not open yet.\n")); - return False; - } - - if (!lp_winbind_offline_logon()) { - DEBUG(10,("set_global_winbindd_state_offline: rejecting.\n")); - return False; - } - - if (global_winbindd_offline_state) { - /* Already offline. */ - return True; - } - - data = tdb_fetch_bystring( wcache->tdb, "WINBINDD_OFFLINE" ); - - if (!data.dptr || data.dsize != 4) { - DEBUG(10,("set_global_winbindd_state_offline: offline state not set.\n")); - SAFE_FREE(data.dptr); - return False; - } else { - DEBUG(10,("set_global_winbindd_state_offline: offline state set.\n")); - global_winbindd_offline_state = True; - SAFE_FREE(data.dptr); - return True; - } -} - -void set_global_winbindd_state_online(void) -{ - DEBUG(10,("set_global_winbindd_state_online: online requested.\n")); - - if (!lp_winbind_offline_logon()) { - DEBUG(10,("set_global_winbindd_state_online: rejecting.\n")); - return; - } - - if (!global_winbindd_offline_state) { - /* Already online. */ - return; - } - global_winbindd_offline_state = False; - - if (!wcache->tdb) { - return; - } - - /* Ensure there is no key "WINBINDD_OFFLINE" in the cache tdb. */ - tdb_delete_bystring(wcache->tdb, "WINBINDD_OFFLINE"); -} - -BOOL get_global_winbindd_state_offline(void) -{ - return global_winbindd_offline_state; -} - -/*********************************************************************** - Validate functions for all possible cache tdb keys. -***********************************************************************/ - -static struct cache_entry *create_centry_validate(const char *kstr, TDB_DATA data, - struct tdb_validation_status *state) -{ - struct cache_entry *centry; - - centry = SMB_XMALLOC_P(struct cache_entry); - centry->data = (unsigned char *)memdup(data.dptr, data.dsize); - if (!centry->data) { - SAFE_FREE(centry); - return NULL; - } - centry->len = data.dsize; - centry->ofs = 0; - - if (centry->len < 8) { - /* huh? corrupt cache? */ - DEBUG(0,("create_centry_validate: Corrupt cache for key %s (len < 8) ?\n", kstr)); - centry_free(centry); - state->bad_entry = True; - state->success = False; - return NULL; - } - - centry->status = NT_STATUS(centry_uint32(centry)); - centry->sequence_number = centry_uint32(centry); - return centry; -} - -static int validate_seqnum(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf, - struct tdb_validation_status *state) -{ - if (dbuf.dsize != 8) { - DEBUG(0,("validate_seqnum: Corrupt cache for key %s (len %u != 8) ?\n", - keystr, (unsigned int)dbuf.dsize )); - state->bad_entry = True; - return 1; - } - return 0; -} - -static int validate_ns(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf, - struct tdb_validation_status *state) -{ - struct cache_entry *centry = create_centry_validate(keystr, dbuf, state); - if (!centry) { - return 1; - } - - (void)centry_uint32(centry); - if (NT_STATUS_IS_OK(centry->status)) { - DOM_SID sid; - (void)centry_sid(centry, mem_ctx, &sid); - } - - centry_free(centry); - - if (!(state->success)) { - return 1; - } - DEBUG(10,("validate_ns: %s ok\n", keystr)); - return 0; -} - -static int validate_sn(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf, - struct tdb_validation_status *state) -{ - struct cache_entry *centry = create_centry_validate(keystr, dbuf, state); - if (!centry) { - return 1; - } - - if (NT_STATUS_IS_OK(centry->status)) { - (void)centry_uint32(centry); - (void)centry_string(centry, mem_ctx); - (void)centry_string(centry, mem_ctx); - } - - centry_free(centry); - - if (!(state->success)) { - return 1; - } - DEBUG(10,("validate_sn: %s ok\n", keystr)); - return 0; -} - -static int validate_u(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf, - struct tdb_validation_status *state) -{ - struct cache_entry *centry = create_centry_validate(keystr, dbuf, state); - DOM_SID sid; - - if (!centry) { - return 1; - } - - (void)centry_string(centry, mem_ctx); - (void)centry_string(centry, mem_ctx); - (void)centry_string(centry, mem_ctx); - (void)centry_string(centry, mem_ctx); - (void)centry_uint32(centry); - (void)centry_sid(centry, mem_ctx, &sid); - (void)centry_sid(centry, mem_ctx, &sid); - - centry_free(centry); - - if (!(state->success)) { - return 1; - } - DEBUG(10,("validate_u: %s ok\n", keystr)); - return 0; -} - -static int validate_loc_pol(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf, - struct tdb_validation_status *state) -{ - struct cache_entry *centry = create_centry_validate(keystr, dbuf, state); - - if (!centry) { - return 1; - } - - (void)centry_nttime(centry); - (void)centry_nttime(centry); - (void)centry_uint16(centry); - - centry_free(centry); - - if (!(state->success)) { - return 1; - } - DEBUG(10,("validate_loc_pol: %s ok\n", keystr)); - return 0; -} - -static int validate_pwd_pol(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf, - struct tdb_validation_status *state) -{ - struct cache_entry *centry = create_centry_validate(keystr, dbuf, state); - - if (!centry) { - return 1; - } - - (void)centry_uint16(centry); - (void)centry_uint16(centry); - (void)centry_uint32(centry); - (void)centry_nttime(centry); - (void)centry_nttime(centry); - - centry_free(centry); - - if (!(state->success)) { - return 1; - } - DEBUG(10,("validate_pwd_pol: %s ok\n", keystr)); - return 0; -} - -static int validate_cred(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf, - struct tdb_validation_status *state) -{ - struct cache_entry *centry = create_centry_validate(keystr, dbuf, state); - - if (!centry) { - return 1; - } - - (void)centry_time(centry); - (void)centry_hash16(centry, mem_ctx); - - /* We only have 17 bytes more data in the salted cred case. */ - if (centry->len - centry->ofs == 17) { - (void)centry_hash16(centry, mem_ctx); - } - - centry_free(centry); - - if (!(state->success)) { - return 1; - } - DEBUG(10,("validate_cred: %s ok\n", keystr)); - return 0; -} - -static int validate_ul(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf, - struct tdb_validation_status *state) -{ - struct cache_entry *centry = create_centry_validate(keystr, dbuf, state); - int32 num_entries, i; - - if (!centry) { - return 1; - } - - num_entries = (int32)centry_uint32(centry); - - for (i=0; i< num_entries; i++) { - DOM_SID sid; - (void)centry_string(centry, mem_ctx); - (void)centry_string(centry, mem_ctx); - (void)centry_string(centry, mem_ctx); - (void)centry_string(centry, mem_ctx); - (void)centry_sid(centry, mem_ctx, &sid); - (void)centry_sid(centry, mem_ctx, &sid); - } - - centry_free(centry); - - if (!(state->success)) { - return 1; - } - DEBUG(10,("validate_ul: %s ok\n", keystr)); - return 0; -} - -static int validate_gl(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf, - struct tdb_validation_status *state) -{ - struct cache_entry *centry = create_centry_validate(keystr, dbuf, state); - int32 num_entries, i; - - if (!centry) { - return 1; - } - - num_entries = centry_uint32(centry); - - for (i=0; i< num_entries; i++) { - (void)centry_string(centry, mem_ctx); - (void)centry_string(centry, mem_ctx); - (void)centry_uint32(centry); - } - - centry_free(centry); - - if (!(state->success)) { - return 1; - } - DEBUG(10,("validate_gl: %s ok\n", keystr)); - return 0; -} - -static int validate_ug(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf, - struct tdb_validation_status *state) -{ - struct cache_entry *centry = create_centry_validate(keystr, dbuf, state); - int32 num_groups, i; - - if (!centry) { - return 1; - } - - num_groups = centry_uint32(centry); - - for (i=0; i< num_groups; i++) { - DOM_SID sid; - centry_sid(centry, mem_ctx, &sid); - } - - centry_free(centry); - - if (!(state->success)) { - return 1; - } - DEBUG(10,("validate_ug: %s ok\n", keystr)); - return 0; -} - -static int validate_ua(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf, - struct tdb_validation_status *state) -{ - struct cache_entry *centry = create_centry_validate(keystr, dbuf, state); - int32 num_aliases, i; - - if (!centry) { - return 1; - } - - num_aliases = centry_uint32(centry); - - for (i=0; i < num_aliases; i++) { - (void)centry_uint32(centry); - } - - centry_free(centry); - - if (!(state->success)) { - return 1; - } - DEBUG(10,("validate_ua: %s ok\n", keystr)); - return 0; -} - -static int validate_gm(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf, - struct tdb_validation_status *state) -{ - struct cache_entry *centry = create_centry_validate(keystr, dbuf, state); - int32 num_names, i; - - if (!centry) { - return 1; - } - - num_names = centry_uint32(centry); - - for (i=0; i< num_names; i++) { - DOM_SID sid; - centry_sid(centry, mem_ctx, &sid); - (void)centry_string(centry, mem_ctx); - (void)centry_uint32(centry); - } - - centry_free(centry); - - if (!(state->success)) { - return 1; - } - DEBUG(10,("validate_gm: %s ok\n", keystr)); - return 0; -} - -static int validate_dr(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf, - struct tdb_validation_status *state) -{ - /* Can't say anything about this other than must be nonzero. */ - if (dbuf.dsize == 0) { - DEBUG(0,("validate_dr: Corrupt cache for key %s (len == 0) ?\n", - keystr)); - state->bad_entry = True; - state->success = False; - return 1; - } - - DEBUG(10,("validate_dr: %s ok\n", keystr)); - return 0; -} - -static int validate_de(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf, - struct tdb_validation_status *state) -{ - /* Can't say anything about this other than must be nonzero. */ - if (dbuf.dsize == 0) { - DEBUG(0,("validate_de: Corrupt cache for key %s (len == 0) ?\n", - keystr)); - state->bad_entry = True; - state->success = False; - return 1; - } - - DEBUG(10,("validate_de: %s ok\n", keystr)); - return 0; -} - -static int validate_trustdoms(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf, - struct tdb_validation_status *state) -{ - struct cache_entry *centry = create_centry_validate(keystr, dbuf, state); - int32 num_domains, i; - - if (!centry) { - return 1; - } - - num_domains = centry_uint32(centry); - - for (i=0; i< num_domains; i++) { - DOM_SID sid; - (void)centry_string(centry, mem_ctx); - (void)centry_string(centry, mem_ctx); - (void)centry_sid(centry, mem_ctx, &sid); - } - - centry_free(centry); - - if (!(state->success)) { - return 1; - } - DEBUG(10,("validate_trustdoms: %s ok\n", keystr)); - return 0; -} - -static int validate_trustdomcache(TALLOC_CTX *mem_ctx, const char *keystr, - TDB_DATA dbuf, - struct tdb_validation_status *state) -{ - if (dbuf.dsize == 0) { - DEBUG(0, ("validate_trustdomcache: Corrupt cache for " - "key %s (len ==0) ?\n", keystr)); - state->bad_entry = True; - state->success = False; - return 1; - } - - DEBUG(10, ("validate_trustdomcache: %s ok\n", keystr)); - DEBUGADD(10, (" Don't trust me, I am a DUMMY!\n")); - return 0; -} - -static int validate_offline(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf, - struct tdb_validation_status *state) -{ - if (dbuf.dsize != 4) { - DEBUG(0,("validate_offline: Corrupt cache for key %s (len %u != 4) ?\n", - keystr, (unsigned int)dbuf.dsize )); - state->bad_entry = True; - state->success = False; - return 1; - } - DEBUG(10,("validate_offline: %s ok\n", keystr)); - return 0; -} - -static int validate_cache_version(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf, - struct tdb_validation_status *state) -{ - if (dbuf.dsize != 4) { - DEBUG(0, ("validate_cache_version: Corrupt cache for " - "key %s (len %u != 4) ?\n", - keystr, (unsigned int)dbuf.dsize)); - state->bad_entry = True; - state->success = False; - return 1; - } - - DEBUG(10, ("validate_cache_version: %s ok\n", keystr)); - return 0; -} - -/*********************************************************************** - A list of all possible cache tdb keys with associated validation - functions. -***********************************************************************/ - -struct key_val_struct { - const char *keyname; - int (*validate_data_fn)(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf, struct tdb_validation_status* state); -} key_val[] = { - {"SEQNUM/", validate_seqnum}, - {"NS/", validate_ns}, - {"SN/", validate_sn}, - {"U/", validate_u}, - {"LOC_POL/", validate_loc_pol}, - {"PWD_POL/", validate_pwd_pol}, - {"CRED/", validate_cred}, - {"UL/", validate_ul}, - {"GL/", validate_gl}, - {"UG/", validate_ug}, - {"UA", validate_ua}, - {"GM/", validate_gm}, - {"DR/", validate_dr}, - {"DE/", validate_de}, - {"TRUSTDOMS/", validate_trustdoms}, - {"TRUSTDOMCACHE/", validate_trustdomcache}, - {"WINBINDD_OFFLINE", validate_offline}, - {WINBINDD_CACHE_VERSION_KEYSTR, validate_cache_version}, - {NULL, NULL} -}; - -/*********************************************************************** - Function to look at every entry in the tdb and validate it as far as - possible. -***********************************************************************/ - -static int cache_traverse_validate_fn(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf, void *state) -{ - int i; - struct tdb_validation_status *v_state = (struct tdb_validation_status *)state; - - /* Paranoia check. */ - if (kbuf.dsize > 1024) { - DEBUG(0,("cache_traverse_validate_fn: key length too large (%u) > 1024\n\n", - (unsigned int)kbuf.dsize )); - return 1; - } - - for (i = 0; key_val[i].keyname; i++) { - size_t namelen = strlen(key_val[i].keyname); - if (kbuf.dsize >= namelen && ( - strncmp(key_val[i].keyname, (const char *)kbuf.dptr, namelen)) == 0) { - TALLOC_CTX *mem_ctx; - char *keystr; - int ret; - - keystr = SMB_MALLOC_ARRAY(char, kbuf.dsize+1); - if (!keystr) { - return 1; - } - memcpy(keystr, kbuf.dptr, kbuf.dsize); - keystr[kbuf.dsize] = '\0'; - - mem_ctx = talloc_init("validate_ctx"); - if (!mem_ctx) { - SAFE_FREE(keystr); - return 1; - } - - ret = key_val[i].validate_data_fn(mem_ctx, keystr, dbuf, - v_state); - - SAFE_FREE(keystr); - talloc_destroy(mem_ctx); - return ret; - } - } - - DEBUG(0,("cache_traverse_validate_fn: unknown cache entry\nkey :\n")); - dump_data(0, (uint8 *)kbuf.dptr, kbuf.dsize); - DEBUG(0,("data :\n")); - dump_data(0, (uint8 *)dbuf.dptr, dbuf.dsize); - v_state->unknown_key = True; - v_state->success = False; - return 1; /* terminate. */ -} - -static void validate_panic(const char *const why) -{ - DEBUG(0,("validating cache: would panic %s\n", why )); - DEBUGADD(0, ("exiting instead (cache validation mode)\n")); - exit(47); -} - -/*********************************************************************** - Try and validate every entry in the winbindd cache. If we fail here, - delete the cache tdb and return non-zero - the caller (main winbindd - function) will restart us as we don't know if we crashed or not. -***********************************************************************/ - -int winbindd_validate_cache(void) -{ - int ret = -1; - const char *tdb_path = lock_path("winbindd_cache.tdb"); - TDB_CONTEXT *tdb = NULL; - - DEBUG(10, ("winbindd_validate_cache: replacing panic function\n")); - smb_panic_fn = validate_panic; - - - tdb = tdb_open_log(tdb_path, - WINBINDD_CACHE_TDB_DEFAULT_HASH_SIZE, - ( lp_winbind_offline_logon() - ? TDB_DEFAULT - : TDB_DEFAULT | TDB_CLEAR_IF_FIRST ), - O_RDWR|O_CREAT, - 0600); - if (!tdb) { - DEBUG(0, ("winbindd_validate_cache: " - "error opening/initializing tdb\n")); - goto done; - } - tdb_close(tdb); - - ret = tdb_validate_and_backup(tdb_path, cache_traverse_validate_fn); - - if (ret != 0) { - DEBUG(10, ("winbindd_validate_cache: validation not successful.\n")); - DEBUGADD(10, ("removing tdb %s.\n", tdb_path)); - unlink(tdb_path); - } - -done: - DEBUG(10, ("winbindd_validate_cache: restoring panic function\n")); - smb_panic_fn = smb_panic; - return ret; -} - -/*********************************************************************** - Try and validate every entry in the winbindd cache. -***********************************************************************/ - -int winbindd_validate_cache_nobackup(void) -{ - int ret = -1; - const char *tdb_path = lock_path("winbindd_cache.tdb"); - - DEBUG(10, ("winbindd_validate_cache: replacing panic function\n")); - smb_panic_fn = validate_panic; - - - if (wcache == NULL || wcache->tdb == NULL) { - ret = tdb_validate_open(tdb_path, cache_traverse_validate_fn); - } else { - ret = tdb_validate(wcache->tdb, cache_traverse_validate_fn); - } - - if (ret != 0) { - DEBUG(10, ("winbindd_validate_cache_nobackup: validation not " - "successful.\n")); - } - - DEBUG(10, ("winbindd_validate_cache_nobackup: restoring panic " - "function\n")); - smb_panic_fn = smb_panic; - return ret; -} - -/********************************************************************* - ********************************************************************/ - -static BOOL add_wbdomain_to_tdc_array( struct winbindd_domain *new_dom, - struct winbindd_tdc_domain **domains, - size_t *num_domains ) -{ - struct winbindd_tdc_domain *list = NULL; - size_t idx; - int i; - BOOL set_only = False; - - /* don't allow duplicates */ - - idx = *num_domains; - list = *domains; - - for ( i=0; i< (*num_domains); i++ ) { - if ( strequal( new_dom->name, list[i].domain_name ) ) { - DEBUG(10,("add_wbdomain_to_tdc_array: Found existing record for %s\n", - new_dom->name)); - idx = i; - set_only = True; - - break; - } - } - - if ( !set_only ) { - if ( !*domains ) { - list = TALLOC_ARRAY( NULL, struct winbindd_tdc_domain, 1 ); - idx = 0; - } else { - list = TALLOC_REALLOC_ARRAY( *domains, *domains, - struct winbindd_tdc_domain, - (*num_domains)+1); - idx = *num_domains; - } - - ZERO_STRUCT( list[idx] ); - } - - if ( !list ) - return False; - - list[idx].domain_name = talloc_strdup( list, new_dom->name ); - list[idx].dns_name = talloc_strdup( list, new_dom->alt_name ); - - if ( !is_null_sid( &new_dom->sid ) ) - sid_copy( &list[idx].sid, &new_dom->sid ); - - if ( new_dom->domain_flags != 0x0 ) - list[idx].trust_flags = new_dom->domain_flags; - - if ( new_dom->domain_type != 0x0 ) - list[idx].trust_type = new_dom->domain_type; - - if ( new_dom->domain_trust_attribs != 0x0 ) - list[idx].trust_attribs = new_dom->domain_trust_attribs; - - if ( !set_only ) { - *domains = list; - *num_domains = idx + 1; - } - - return True; -} - -/********************************************************************* - ********************************************************************/ - -static TDB_DATA make_tdc_key( const char *domain_name ) -{ - char *keystr = NULL; - TDB_DATA key = { NULL, 0 }; - - if ( !domain_name ) { - DEBUG(5,("make_tdc_key: Keyname workgroup is NULL!\n")); - return key; - } - - - asprintf( &keystr, "TRUSTDOMCACHE/%s", domain_name ); - key = string_term_tdb_data(keystr); - - return key; -} - -/********************************************************************* - ********************************************************************/ - -static int pack_tdc_domains( struct winbindd_tdc_domain *domains, - size_t num_domains, - unsigned char **buf ) -{ - unsigned char *buffer = NULL; - int len = 0; - int buflen = 0; - int i = 0; - - DEBUG(10,("pack_tdc_domains: Packing %d trusted domains\n", - (int)num_domains)); - - buflen = 0; - - again: - len = 0; - - /* Store the number of array items first */ - len += tdb_pack( buffer+len, buflen-len, "d", - num_domains ); - - /* now pack each domain trust record */ - for ( i=0; i 0 ) { - DEBUG(10,("pack_tdc_domains: Packing domain %s (%s)\n", - domains[i].domain_name, - domains[i].dns_name ? domains[i].dns_name : "UNKNOWN" )); - } - - len += tdb_pack( buffer+len, buflen-len, "fffddd", - domains[i].domain_name, - domains[i].dns_name, - sid_string_static(&domains[i].sid), - domains[i].trust_flags, - domains[i].trust_attribs, - domains[i].trust_type ); - } - - if ( buflen < len ) { - SAFE_FREE(buffer); - if ( (buffer = SMB_MALLOC_ARRAY(unsigned char, len)) == NULL ) { - DEBUG(0,("pack_tdc_domains: failed to alloc buffer!\n")); - buflen = -1; - goto done; - } - buflen = len; - goto again; - } - - *buf = buffer; - - done: - return buflen; -} - -/********************************************************************* - ********************************************************************/ - -static size_t unpack_tdc_domains( unsigned char *buf, int buflen, - struct winbindd_tdc_domain **domains ) -{ - fstring domain_name, dns_name, sid_string; - uint32 type, attribs, flags; - int num_domains; - int len = 0; - int i; - struct winbindd_tdc_domain *list = NULL; - - /* get the number of domains */ - len += tdb_unpack( buf+len, buflen-len, "d", &num_domains); - if ( len == -1 ) { - DEBUG(5,("unpack_tdc_domains: Failed to unpack domain array\n")); - return 0; - } - - list = TALLOC_ARRAY( NULL, struct winbindd_tdc_domain, num_domains ); - if ( !list ) { - DEBUG(0,("unpack_tdc_domains: Failed to talloc() domain list!\n")); - return 0; - } - - for ( i=0; itdb, key ); - goto done; - } - - data.dsize = pack_tdc_domains( domains, num_domains, &data.dptr ); - - if ( !data.dptr ) { - ret = -1; - goto done; - } - - ret = tdb_store( wcache->tdb, key, data, 0 ); - - done: - SAFE_FREE( data.dptr ); - SAFE_FREE( key.dptr ); - - return ( ret != -1 ); -} - -/********************************************************************* - ********************************************************************/ - -BOOL wcache_tdc_fetch_list( struct winbindd_tdc_domain **domains, size_t *num_domains ) -{ - TDB_DATA key = make_tdc_key( lp_workgroup() ); - TDB_DATA data = { NULL, 0 }; - - *domains = NULL; - *num_domains = 0; - - if ( !key.dptr ) - return False; - - data = tdb_fetch( wcache->tdb, key ); - - SAFE_FREE( key.dptr ); - - if ( !data.dptr ) - return False; - - *num_domains = unpack_tdc_domains( data.dptr, data.dsize, domains ); - - SAFE_FREE( data.dptr ); - - if ( !*domains ) - return False; - - return True; -} - -/********************************************************************* - ********************************************************************/ - -BOOL wcache_tdc_add_domain( struct winbindd_domain *domain ) -{ - struct winbindd_tdc_domain *dom_list = NULL; - size_t num_domains = 0; - BOOL ret = False; - - DEBUG(10,("wcache_tdc_add_domain: Adding domain %s (%s), SID %s, " - "flags = 0x%x, attributes = 0x%x, type = 0x%x\n", - domain->name, domain->alt_name, - sid_string_static(&domain->sid), - domain->domain_flags, - domain->domain_trust_attribs, - domain->domain_type)); - - if ( !init_wcache() ) { - return False; - } - - /* fetch the list */ - - wcache_tdc_fetch_list( &dom_list, &num_domains ); - - /* add the new domain */ - - if ( !add_wbdomain_to_tdc_array( domain, &dom_list, &num_domains ) ) { - goto done; - } - - /* pack the domain */ - - if ( !wcache_tdc_store_list( dom_list, num_domains ) ) { - goto done; - } - - /* Success */ - - ret = True; - done: - TALLOC_FREE( dom_list ); - - return ret; -} - -/********************************************************************* - ********************************************************************/ - -struct winbindd_tdc_domain * wcache_tdc_fetch_domain( TALLOC_CTX *ctx, const char *name ) -{ - struct winbindd_tdc_domain *dom_list = NULL; - size_t num_domains = 0; - int i; - struct winbindd_tdc_domain *d = NULL; - - DEBUG(10,("wcache_tdc_fetch_domain: Searching for domain %s\n", name)); - - if ( !init_wcache() ) { - return False; - } - - /* fetch the list */ - - wcache_tdc_fetch_list( &dom_list, &num_domains ); - - for ( i=0; idomain_name = talloc_strdup( d, dom_list[i].domain_name ); - d->dns_name = talloc_strdup( d, dom_list[i].dns_name ); - sid_copy( &d->sid, &dom_list[i].sid ); - d->trust_flags = dom_list[i].trust_flags; - d->trust_type = dom_list[i].trust_type; - d->trust_attribs = dom_list[i].trust_attribs; - - break; - } - } - - TALLOC_FREE( dom_list ); - - return d; -} - - -/********************************************************************* - ********************************************************************/ - -void wcache_tdc_clear( void ) -{ - if ( !init_wcache() ) - return; - - wcache_tdc_store_list( NULL, 0 ); - - return; -} - - -/********************************************************************* - ********************************************************************/ - -static void wcache_save_user_pwinfo(struct winbindd_domain *domain, - NTSTATUS status, - const DOM_SID *user_sid, - const char *homedir, - const char *shell, - const char *gecos, - uint32 gid) -{ - struct cache_entry *centry; - - if ( (centry = centry_start(domain, status)) == NULL ) - return; - - centry_put_string( centry, homedir ); - centry_put_string( centry, shell ); - centry_put_string( centry, gecos ); - centry_put_uint32( centry, gid ); - - centry_end(centry, "NSS/PWINFO/%s", sid_string_static(user_sid) ); - - DEBUG(10,("wcache_save_user_pwinfo: %s\n", sid_string_static(user_sid) )); - - centry_free(centry); -} - -NTSTATUS nss_get_info_cached( struct winbindd_domain *domain, - const DOM_SID *user_sid, - TALLOC_CTX *ctx, - ADS_STRUCT *ads, LDAPMessage *msg, - char **homedir, char **shell, char **gecos, - gid_t *p_gid) -{ - struct winbind_cache *cache = get_cache(domain); - struct cache_entry *centry = NULL; - NTSTATUS nt_status; - - if (!cache->tdb) - goto do_query; - - centry = wcache_fetch(cache, domain, "NSS/PWINFO/%s", sid_string_static(user_sid)); - - if (!centry) - goto do_query; - - *homedir = centry_string( centry, ctx ); - *shell = centry_string( centry, ctx ); - *gecos = centry_string( centry, ctx ); - *p_gid = centry_uint32( centry ); - - centry_free(centry); - - DEBUG(10,("nss_get_info_cached: [Cached] - user_sid %s\n", - sid_string_static(user_sid))); - - return NT_STATUS_OK; - -do_query: - - nt_status = nss_get_info( domain->name, user_sid, ctx, ads, msg, - homedir, shell, gecos, p_gid ); - - if ( NT_STATUS_IS_OK(nt_status) ) { - wcache_save_user_pwinfo( domain, nt_status, user_sid, - *homedir, *shell, *gecos, *p_gid ); - } - - if ( NT_STATUS_EQUAL( nt_status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND ) ) { - DEBUG(5,("nss_get_info_cached: Setting domain %s offline\n", - domain->name )); - set_domain_offline( domain ); - } - - return nt_status; -} - - -/* the cache backend methods are exposed via this structure */ -struct winbindd_methods cache_methods = { - True, - query_user_list, - enum_dom_groups, - enum_local_groups, - name_to_sid, - sid_to_name, - rids_to_names, - query_user, - lookup_usergroups, - lookup_useraliases, - lookup_groupmem, - sequence_number, - lockout_policy, - password_policy, - trusted_domains -}; diff --git a/source3/nsswitch/winbindd_ccache_access.c b/source3/nsswitch/winbindd_ccache_access.c deleted file mode 100644 index 99c5c0c4d1..0000000000 --- a/source3/nsswitch/winbindd_ccache_access.c +++ /dev/null @@ -1,283 +0,0 @@ -/* - Unix SMB/CIFS implementation. - - Winbind daemon - cached credentials funcions - - Copyright (C) Robert O'Callahan 2006 - Copyright (C) Jeremy Allison 2006 (minor fixes to fit into Samba and - protect against integer wrap). - - 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 3 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, see . -*/ - -#include "includes.h" -#include "winbindd.h" - -#undef DBGC_CLASS -#define DBGC_CLASS DBGC_WINBIND - -static BOOL client_can_access_ccache_entry(uid_t client_uid, - struct WINBINDD_MEMORY_CREDS *entry) -{ - if (client_uid == entry->uid || client_uid == 0) { - DEBUG(10, ("Access granted to uid %d\n", client_uid)); - return True; - } - - DEBUG(1, ("Access denied to uid %d (expected %d)\n", client_uid, entry->uid)); - return False; -} - -static NTSTATUS do_ntlm_auth_with_hashes(const char *username, - const char *domain, - const unsigned char lm_hash[LM_HASH_LEN], - const unsigned char nt_hash[NT_HASH_LEN], - const DATA_BLOB initial_msg, - const DATA_BLOB challenge_msg, - DATA_BLOB *auth_msg) -{ - NTSTATUS status; - NTLMSSP_STATE *ntlmssp_state = NULL; - DATA_BLOB dummy_msg, reply; - - status = ntlmssp_client_start(&ntlmssp_state); - - if (!NT_STATUS_IS_OK(status)) { - DEBUG(1, ("Could not start NTLMSSP client: %s\n", - nt_errstr(status))); - goto done; - } - - status = ntlmssp_set_username(ntlmssp_state, username); - - if (!NT_STATUS_IS_OK(status)) { - DEBUG(1, ("Could not set username: %s\n", - nt_errstr(status))); - goto done; - } - - status = ntlmssp_set_domain(ntlmssp_state, domain); - - if (!NT_STATUS_IS_OK(status)) { - DEBUG(1, ("Could not set domain: %s\n", - nt_errstr(status))); - goto done; - } - - status = ntlmssp_set_hashes(ntlmssp_state, lm_hash, nt_hash); - - if (!NT_STATUS_IS_OK(status)) { - DEBUG(1, ("Could not set hashes: %s\n", - nt_errstr(status))); - goto done; - } - - /* We need to get our protocol handler into the right state. So first - we ask it to generate the initial message. Actually the client has already - sent its own initial message, so we're going to drop this one on the floor. - The client might have sent a different message, for example with different - negotiation options, but as far as I can tell this won't hurt us. (Unless - the client sent a different username or domain, in which case that's their - problem for telling us the wrong username or domain.) - Since we have a copy of the initial message that the client sent, we could - resolve any discrepancies if we had to. - */ - dummy_msg = data_blob_null; - reply = data_blob_null; - status = ntlmssp_update(ntlmssp_state, dummy_msg, &reply); - data_blob_free(&dummy_msg); - data_blob_free(&reply); - - if (!NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED)) { - DEBUG(1, ("Failed to create initial message! [%s]\n", - nt_errstr(status))); - goto done; - } - - /* Now we are ready to handle the server's actual response. */ - status = ntlmssp_update(ntlmssp_state, challenge_msg, &reply); - - if (!NT_STATUS_EQUAL(status, NT_STATUS_OK)) { - DEBUG(1, ("We didn't get a response to the challenge! [%s]\n", - nt_errstr(status))); - data_blob_free(&reply); - goto done; - } - *auth_msg = reply; - status = NT_STATUS_OK; - -done: - ntlmssp_end(&ntlmssp_state); - return status; -} - -static BOOL check_client_uid(struct winbindd_cli_state *state, uid_t uid) -{ - int ret; - uid_t ret_uid; - - ret_uid = (uid_t)-1; - - ret = sys_getpeereid(state->sock, &ret_uid); - if (ret != 0) { - DEBUG(1, ("check_client_uid: Could not get socket peer uid: %s; " - "denying access\n", strerror(errno))); - return False; - } - - if (uid != ret_uid) { - DEBUG(1, ("check_client_uid: Client lied about its uid: said %d, " - "actually was %d; denying access\n", - uid, ret_uid)); - return False; - } - - return True; -} - -void winbindd_ccache_ntlm_auth(struct winbindd_cli_state *state) -{ - struct winbindd_domain *domain; - fstring name_domain, name_user; - - /* Ensure null termination */ - state->request.data.ccache_ntlm_auth.user[ - sizeof(state->request.data.ccache_ntlm_auth.user)-1]='\0'; - - DEBUG(3, ("[%5lu]: perform NTLM auth on behalf of user %s\n", (unsigned long)state->pid, - state->request.data.ccache_ntlm_auth.user)); - - /* Parse domain and username */ - - if (!canonicalize_username(state->request.data.ccache_ntlm_auth.user, - name_domain, name_user)) { - DEBUG(5,("winbindd_ccache_ntlm_auth: cannot parse domain and user from name [%s]\n", - state->request.data.ccache_ntlm_auth.user)); - request_error(state); - return; - } - - domain = find_auth_domain(state, name_domain); - - if (domain == NULL) { - DEBUG(5,("winbindd_ccache_ntlm_auth: can't get domain [%s]\n", - name_domain)); - request_error(state); - return; - } - - if (!check_client_uid(state, state->request.data.ccache_ntlm_auth.uid)) { - request_error(state); - return; - } - - sendto_domain(state, domain); -} - -enum winbindd_result winbindd_dual_ccache_ntlm_auth(struct winbindd_domain *domain, - struct winbindd_cli_state *state) -{ - NTSTATUS result = NT_STATUS_NOT_SUPPORTED; - struct WINBINDD_MEMORY_CREDS *entry; - DATA_BLOB initial, challenge, auth; - fstring name_domain, name_user; - uint32 initial_blob_len, challenge_blob_len, extra_len; - - /* Ensure null termination */ - state->request.data.ccache_ntlm_auth.user[ - sizeof(state->request.data.ccache_ntlm_auth.user)-1]='\0'; - - DEBUG(3, ("winbindd_dual_ccache_ntlm_auth: [%5lu]: perform NTLM auth on " - "behalf of user %s (dual)\n", (unsigned long)state->pid, - state->request.data.ccache_ntlm_auth.user)); - - /* validate blob lengths */ - initial_blob_len = state->request.data.ccache_ntlm_auth.initial_blob_len; - challenge_blob_len = state->request.data.ccache_ntlm_auth.challenge_blob_len; - extra_len = state->request.extra_len; - - if (initial_blob_len > extra_len || challenge_blob_len > extra_len || - initial_blob_len + challenge_blob_len > extra_len || - initial_blob_len + challenge_blob_len < initial_blob_len || - initial_blob_len + challenge_blob_len < challenge_blob_len) { - - DEBUG(10,("winbindd_dual_ccache_ntlm_auth: blob lengths overrun " - "or wrap. Buffer [%d+%d > %d]\n", - initial_blob_len, - challenge_blob_len, - extra_len)); - goto process_result; - } - - /* Parse domain and username */ - if (!parse_domain_user(state->request.data.ccache_ntlm_auth.user, name_domain, name_user)) { - DEBUG(10,("winbindd_dual_ccache_ntlm_auth: cannot parse " - "domain and user from name [%s]\n", - state->request.data.ccache_ntlm_auth.user)); - goto process_result; - } - - entry = find_memory_creds_by_name(state->request.data.ccache_ntlm_auth.user); - if (entry == NULL || entry->nt_hash == NULL || entry->lm_hash == NULL) { - DEBUG(10,("winbindd_dual_ccache_ntlm_auth: could not find " - "credentials for user %s\n", - state->request.data.ccache_ntlm_auth.user)); - goto process_result; - } - - DEBUG(10,("winbindd_dual_ccache_ntlm_auth: found ccache [%s]\n", entry->username)); - - if (!client_can_access_ccache_entry(state->request.data.ccache_ntlm_auth.uid, entry)) { - goto process_result; - } - - if (initial_blob_len == 0 && challenge_blob_len == 0) { - /* this is just a probe to see if credentials are available. */ - result = NT_STATUS_OK; - state->response.data.ccache_ntlm_auth.auth_blob_len = 0; - goto process_result; - } - - initial = data_blob(state->request.extra_data.data, initial_blob_len); - challenge = data_blob(state->request.extra_data.data + initial_blob_len, - state->request.data.ccache_ntlm_auth.challenge_blob_len); - - if (!initial.data || !challenge.data) { - result = NT_STATUS_NO_MEMORY; - } else { - result = do_ntlm_auth_with_hashes(name_user, name_domain, - entry->lm_hash, entry->nt_hash, - initial, challenge, &auth); - } - - data_blob_free(&initial); - data_blob_free(&challenge); - - if (!NT_STATUS_IS_OK(result)) { - goto process_result; - } - - state->response.extra_data.data = smb_xmemdup(auth.data, auth.length); - if (!state->response.extra_data.data) { - result = NT_STATUS_NO_MEMORY; - goto process_result; - } - state->response.length += auth.length; - state->response.data.ccache_ntlm_auth.auth_blob_len = auth.length; - - data_blob_free(&auth); - - process_result: - return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR; -} diff --git a/source3/nsswitch/winbindd_cm.c b/source3/nsswitch/winbindd_cm.c deleted file mode 100644 index 9ffb3dfb23..0000000000 --- a/source3/nsswitch/winbindd_cm.c +++ /dev/null @@ -1,2271 +0,0 @@ -/* - Unix SMB/CIFS implementation. - - Winbind daemon connection manager - - Copyright (C) Tim Potter 2001 - Copyright (C) Andrew Bartlett 2002 - Copyright (C) Gerald (Jerry) Carter 2003-2005. - Copyright (C) Volker Lendecke 2004-2005 - Copyright (C) Jeremy Allison 2006 - - 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 3 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, see . -*/ - -/* - We need to manage connections to domain controllers without having to - mess up the main winbindd code with other issues. The aim of the - connection manager is to: - - - make connections to domain controllers and cache them - - re-establish connections when networks or servers go down - - centralise the policy on connection timeouts, domain controller - selection etc - - manage re-entrancy for when winbindd becomes able to handle - multiple outstanding rpc requests - - Why not have connection management as part of the rpc layer like tng? - Good question. This code may morph into libsmb/rpc_cache.c or something - like that but at the moment it's simply staying as part of winbind. I - think the TNG architecture of forcing every user of the rpc layer to use - the connection caching system is a bad idea. It should be an optional - method of using the routines. - - The TNG design is quite good but I disagree with some aspects of the - implementation. -tpot - - */ - -/* - TODO: - - - I'm pretty annoyed by all the make_nmb_name() stuff. It should be - moved down into another function. - - - Take care when destroying cli_structs as they can be shared between - various sam handles. - - */ - -#include "includes.h" -#include "winbindd.h" - -#undef DBGC_CLASS -#define DBGC_CLASS DBGC_WINBIND - -struct dc_name_ip { - fstring name; - struct in_addr ip; -}; - -extern struct winbindd_methods reconnect_methods; -extern BOOL override_logfile; - -static NTSTATUS init_dc_connection_network(struct winbindd_domain *domain); -static void set_dc_type_and_flags( struct winbindd_domain *domain ); -static BOOL get_dcs(TALLOC_CTX *mem_ctx, const struct winbindd_domain *domain, - struct dc_name_ip **dcs, int *num_dcs); - -/**************************************************************** - Child failed to find DC's. Reschedule check. -****************************************************************/ - -static void msg_failed_to_go_online(struct messaging_context *msg, - void *private_data, - uint32_t msg_type, - struct server_id server_id, - DATA_BLOB *data) -{ - struct winbindd_domain *domain; - const char *domainname = (const char *)data->data; - - if (data->data == NULL || data->length == 0) { - return; - } - - DEBUG(5,("msg_fail_to_go_online: received for domain %s.\n", domainname)); - - for (domain = domain_list(); domain; domain = domain->next) { - if (domain->internal) { - continue; - } - - if (strequal(domain->name, domainname)) { - if (domain->online) { - /* We're already online, ignore. */ - DEBUG(5,("msg_fail_to_go_online: domain %s " - "already online.\n", domainname)); - continue; - } - - /* Reschedule the online check. */ - set_domain_offline(domain); - break; - } - } -} - -/**************************************************************** - Actually cause a reconnect from a message. -****************************************************************/ - -static void msg_try_to_go_online(struct messaging_context *msg, - void *private_data, - uint32_t msg_type, - struct server_id server_id, - DATA_BLOB *data) -{ - struct winbindd_domain *domain; - const char *domainname = (const char *)data->data; - - if (data->data == NULL || data->length == 0) { - return; - } - - DEBUG(5,("msg_try_to_go_online: received for domain %s.\n", domainname)); - - for (domain = domain_list(); domain; domain = domain->next) { - if (domain->internal) { - continue; - } - - if (strequal(domain->name, domainname)) { - - if (domain->online) { - /* We're already online, ignore. */ - DEBUG(5,("msg_try_to_go_online: domain %s " - "already online.\n", domainname)); - continue; - } - - /* This call takes care of setting the online - flag to true if we connected, or re-adding - the offline handler if false. Bypasses online - check so always does network calls. */ - - init_dc_connection_network(domain); - break; - } - } -} - -/**************************************************************** - Fork a child to try and contact a DC. Do this as contacting a - DC requires blocking lookups and we don't want to block our - parent. -****************************************************************/ - -static BOOL fork_child_dc_connect(struct winbindd_domain *domain) -{ - struct dc_name_ip *dcs = NULL; - int num_dcs = 0; - TALLOC_CTX *mem_ctx = NULL; - pid_t child_pid; - pid_t parent_pid = sys_getpid(); - - /* Stop zombies */ - CatchChild(); - - child_pid = sys_fork(); - - if (child_pid == -1) { - DEBUG(0, ("fork_child_dc_connect: Could not fork: %s\n", strerror(errno))); - return False; - } - - if (child_pid != 0) { - /* Parent */ - messaging_register(winbind_messaging_context(), NULL, - MSG_WINBIND_TRY_TO_GO_ONLINE, - msg_try_to_go_online); - messaging_register(winbind_messaging_context(), NULL, - MSG_WINBIND_FAILED_TO_GO_ONLINE, - msg_failed_to_go_online); - return True; - } - - /* Child. */ - - /* Leave messages blocked - we will never process one. */ - - /* tdb needs special fork handling */ - if (tdb_reopen_all(1) == -1) { - DEBUG(0,("tdb_reopen_all failed.\n")); - _exit(0); - } - - close_conns_after_fork(); - - if (!override_logfile) { - pstring logfile; - pstr_sprintf(logfile, "%s/log.winbindd-dc-connect", dyn_LOGFILEBASE); - lp_set_logfile(logfile); - reopen_logs(); - } - - mem_ctx = talloc_init("fork_child_dc_connect"); - if (!mem_ctx) { - DEBUG(0,("talloc_init failed.\n")); - _exit(0); - } - - if ((!get_dcs(mem_ctx, domain, &dcs, &num_dcs)) || (num_dcs == 0)) { - /* Still offline ? Can't find DC's. */ - messaging_send_buf(winbind_messaging_context(), - pid_to_procid(parent_pid), - MSG_WINBIND_FAILED_TO_GO_ONLINE, - (uint8 *)domain->name, - strlen(domain->name)+1); - _exit(0); - } - - /* We got a DC. Send a message to our parent to get it to - try and do the same. */ - - messaging_send_buf(winbind_messaging_context(), - pid_to_procid(parent_pid), - MSG_WINBIND_TRY_TO_GO_ONLINE, - (uint8 *)domain->name, - strlen(domain->name)+1); - _exit(0); -} - -/**************************************************************** - Handler triggered if we're offline to try and detect a DC. -****************************************************************/ - -static void check_domain_online_handler(struct event_context *ctx, - struct timed_event *te, - const struct timeval *now, - void *private_data) -{ - struct winbindd_domain *domain = - (struct winbindd_domain *)private_data; - - DEBUG(10,("check_domain_online_handler: called for domain " - "%s (online = %s)\n", domain->name, - domain->online ? "True" : "False" )); - - TALLOC_FREE(domain->check_online_event); - - /* Are we still in "startup" mode ? */ - - if (domain->startup && (now->tv_sec > domain->startup_time + 30)) { - /* No longer in "startup" mode. */ - DEBUG(10,("check_domain_online_handler: domain %s no longer in 'startup' mode.\n", - domain->name )); - domain->startup = False; - } - - /* We've been told to stay offline, so stay - that way. */ - - if (get_global_winbindd_state_offline()) { - DEBUG(10,("check_domain_online_handler: domain %s remaining globally offline\n", - domain->name )); - return; - } - - /* Fork a child to test if it can contact a DC. - If it can then send ourselves a message to - cause a reconnect. */ - - fork_child_dc_connect(domain); -} - -/**************************************************************** - If we're still offline setup the timeout check. -****************************************************************/ - -static void calc_new_online_timeout_check(struct winbindd_domain *domain) -{ - int wbc = lp_winbind_cache_time(); - - if (domain->startup) { - domain->check_online_timeout = 10; - } else if (domain->check_online_timeout < wbc) { - domain->check_online_timeout = wbc; - } -} - -/**************************************************************** - Set domain offline and also add handler to put us back online - if we detect a DC. -****************************************************************/ - -void set_domain_offline(struct winbindd_domain *domain) -{ - DEBUG(10,("set_domain_offline: called for domain %s\n", - domain->name )); - - TALLOC_FREE(domain->check_online_event); - - if (domain->internal) { - DEBUG(3,("set_domain_offline: domain %s is internal - logic error.\n", - domain->name )); - return; - } - - domain->online = False; - - /* Offline domains are always initialized. They're - re-initialized when they go back online. */ - - domain->initialized = True; - - /* We only add the timeout handler that checks and - allows us to go back online when we've not - been told to remain offline. */ - - if (get_global_winbindd_state_offline()) { - DEBUG(10,("set_domain_offline: domain %s remaining globally offline\n", - domain->name )); - return; - } - - /* If we're in statup mode, check again in 10 seconds, not in - lp_winbind_cache_time() seconds (which is 5 mins by default). */ - - calc_new_online_timeout_check(domain); - - domain->check_online_event = event_add_timed(winbind_event_context(), - NULL, - timeval_current_ofs(domain->check_online_timeout,0), - "check_domain_online_handler", - check_domain_online_handler, - domain); - - /* The above *has* to succeed for winbindd to work. */ - if (!domain->check_online_event) { - smb_panic("set_domain_offline: failed to add online handler"); - } - - DEBUG(10,("set_domain_offline: added event handler for domain %s\n", - domain->name )); - - /* Send an offline message to the idmap child when our - primary domain goes offline */ - - if ( domain->primary ) { - struct winbindd_child *idmap = idmap_child(); - - if ( idmap->pid != 0 ) { - messaging_send_buf(winbind_messaging_context(), - pid_to_procid(idmap->pid), - MSG_WINBIND_OFFLINE, - (uint8 *)domain->name, - strlen(domain->name)+1); - } - } - - return; -} - -/**************************************************************** - Set domain online - if allowed. -****************************************************************/ - -static void set_domain_online(struct winbindd_domain *domain) -{ - struct timeval now; - - DEBUG(10,("set_domain_online: called for domain %s\n", - domain->name )); - - if (domain->internal) { - DEBUG(3,("set_domain_online: domain %s is internal - logic error.\n", - domain->name )); - return; - } - - if (get_global_winbindd_state_offline()) { - DEBUG(10,("set_domain_online: domain %s remaining globally offline\n", - domain->name )); - return; - } - - winbindd_set_locator_kdc_envs(domain); - - /* If we are waiting to get a krb5 ticket, trigger immediately. */ - GetTimeOfDay(&now); - set_event_dispatch_time(winbind_event_context(), - "krb5_ticket_gain_handler", now); - - /* Ok, we're out of any startup mode now... */ - domain->startup = False; - - if (domain->online == False) { - /* We were offline - now we're online. We default to - using the MS-RPC backend if we started offline, - and if we're going online for the first time we - should really re-initialize the backends and the - checks to see if we're talking to an AD or NT domain. - */ - - domain->initialized = False; - - /* 'reconnect_methods' is the MS-RPC backend. */ - if (domain->backend == &reconnect_methods) { - domain->backend = NULL; - } - } - - /* Ensure we have no online timeout checks. */ - domain->check_online_timeout = 0; - TALLOC_FREE(domain->check_online_event); - - /* Ensure we ignore any pending child messages. */ - messaging_deregister(winbind_messaging_context(), - MSG_WINBIND_TRY_TO_GO_ONLINE, NULL); - messaging_deregister(winbind_messaging_context(), - MSG_WINBIND_FAILED_TO_GO_ONLINE, NULL); - - domain->online = True; - - /* Send an online message to the idmap child when our - primary domain comes online */ - - if ( domain->primary ) { - struct winbindd_child *idmap = idmap_child(); - - if ( idmap->pid != 0 ) { - messaging_send_buf(winbind_messaging_context(), - pid_to_procid(idmap->pid), - MSG_WINBIND_ONLINE, - (uint8 *)domain->name, - strlen(domain->name)+1); - } - } - - return; -} - -/**************************************************************** - Requested to set a domain online. -****************************************************************/ - -void set_domain_online_request(struct winbindd_domain *domain) -{ - struct timeval tev; - - DEBUG(10,("set_domain_online_request: called for domain %s\n", - domain->name )); - - if (get_global_winbindd_state_offline()) { - DEBUG(10,("set_domain_online_request: domain %s remaining globally offline\n", - domain->name )); - return; - } - - /* We've been told it's safe to go online and - try and connect to a DC. But I don't believe it - because network manager seems to lie. - Wait at least 5 seconds. Heuristics suck... */ - - if (!domain->check_online_event) { - /* If we've come from being globally offline we - don't have a check online event handler set. - We need to add one now we're trying to go - back online. */ - - DEBUG(10,("set_domain_online_request: domain %s was globally offline.\n", - domain->name )); - - domain->check_online_event = event_add_timed(winbind_event_context(), - NULL, - timeval_current_ofs(5, 0), - "check_domain_online_handler", - check_domain_online_handler, - domain); - - /* The above *has* to succeed for winbindd to work. */ - if (!domain->check_online_event) { - smb_panic("set_domain_online_request: failed to add online handler"); - } - } - - GetTimeOfDay(&tev); - - /* Go into "startup" mode again. */ - domain->startup_time = tev.tv_sec; - domain->startup = True; - - tev.tv_sec += 5; - - set_event_dispatch_time(winbind_event_context(), "check_domain_online_handler", tev); -} - -/**************************************************************** - Add -ve connection cache entries for domain and realm. -****************************************************************/ - -void winbind_add_failed_connection_entry(const struct winbindd_domain *domain, - const char *server, - NTSTATUS result) -{ - add_failed_connection_entry(domain->name, server, result); - /* If this was the saf name for the last thing we talked to, - remove it. */ - saf_delete(domain->name); - if (*domain->alt_name) { - add_failed_connection_entry(domain->alt_name, server, result); - saf_delete(domain->alt_name); - } - winbindd_unset_locator_kdc_env(domain); -} - -/* Choose between anonymous or authenticated connections. We need to use - an authenticated connection if DCs have the RestrictAnonymous registry - entry set > 0, or the "Additional restrictions for anonymous - connections" set in the win2k Local Security Policy. - - Caller to free() result in domain, username, password -*/ - -static void cm_get_ipc_userpass(char **username, char **domain, char **password) -{ - *username = (char *)secrets_fetch(SECRETS_AUTH_USER, NULL); - *domain = (char *)secrets_fetch(SECRETS_AUTH_DOMAIN, NULL); - *password = (char *)secrets_fetch(SECRETS_AUTH_PASSWORD, NULL); - - if (*username && **username) { - - if (!*domain || !**domain) - *domain = smb_xstrdup(lp_workgroup()); - - if (!*password || !**password) - *password = smb_xstrdup(""); - - DEBUG(3, ("cm_get_ipc_userpass: Retrieved auth-user from secrets.tdb [%s\\%s]\n", - *domain, *username)); - - } else { - DEBUG(3, ("cm_get_ipc_userpass: No auth-user defined\n")); - *username = smb_xstrdup(""); - *domain = smb_xstrdup(""); - *password = smb_xstrdup(""); - } -} - -static BOOL get_dc_name_via_netlogon(const struct winbindd_domain *domain, - fstring dcname, struct in_addr *dc_ip) -{ - struct winbindd_domain *our_domain = NULL; - struct rpc_pipe_client *netlogon_pipe = NULL; - NTSTATUS result; - WERROR werr; - TALLOC_CTX *mem_ctx; - unsigned int orig_timeout; - fstring tmp; - char *p; - - /* Hmmmm. We can only open one connection to the NETLOGON pipe at the - * moment.... */ - - if (IS_DC) { - return False; - } - - if (domain->primary) { - return False; - } - - our_domain = find_our_domain(); - - if ((mem_ctx = talloc_init("get_dc_name_via_netlogon")) == NULL) { - return False; - } - - result = cm_connect_netlogon(our_domain, &netlogon_pipe); - if (!NT_STATUS_IS_OK(result)) { - talloc_destroy(mem_ctx); - return False; - } - - /* This call can take a long time - allow the server to time out. - 35 seconds should do it. */ - - orig_timeout = cli_set_timeout(netlogon_pipe->cli, 35000); - - werr = rpccli_netlogon_getanydcname(netlogon_pipe, mem_ctx, our_domain->dcname, - domain->name, tmp); - - /* And restore our original timeout. */ - cli_set_timeout(netlogon_pipe->cli, orig_timeout); - - talloc_destroy(mem_ctx); - - if (!W_ERROR_IS_OK(werr)) { - DEBUG(10, ("rpccli_netlogon_getanydcname failed: %s\n", - dos_errstr(werr))); - return False; - } - - /* cli_netlogon_getanydcname gives us a name with \\ */ - p = tmp; - if (*p == '\\') { - p+=1; - } - if (*p == '\\') { - p+=1; - } - - fstrcpy(dcname, p); - - DEBUG(10, ("rpccli_netlogon_getanydcname returned %s\n", dcname)); - - if (!resolve_name(dcname, dc_ip, 0x20)) { - return False; - } - - return True; -} - -/************************************************************************ - Given a fd with a just-connected TCP connection to a DC, open a connection - to the pipe. -************************************************************************/ - -static NTSTATUS cm_prepare_connection(const struct winbindd_domain *domain, - const int sockfd, - const char *controller, - struct cli_state **cli, - BOOL *retry) -{ - char *machine_password, *machine_krb5_principal, *machine_account; - char *ipc_username, *ipc_domain, *ipc_password; - - BOOL got_mutex; - - NTSTATUS result = NT_STATUS_UNSUCCESSFUL; - - struct sockaddr peeraddr; - socklen_t peeraddr_len; - - struct sockaddr_in *peeraddr_in = (struct sockaddr_in *)&peeraddr; - - DEBUG(10,("cm_prepare_connection: connecting to DC %s for domain %s\n", - controller, domain->name )); - - machine_password = secrets_fetch_machine_password(lp_workgroup(), NULL, - NULL); - - if (asprintf(&machine_account, "%s$", global_myname()) == -1) { - SAFE_FREE(machine_password); - return NT_STATUS_NO_MEMORY; - } - - if (asprintf(&machine_krb5_principal, "%s$@%s", global_myname(), - lp_realm()) == -1) { - SAFE_FREE(machine_account); - SAFE_FREE(machine_password); - return NT_STATUS_NO_MEMORY; - } - - cm_get_ipc_userpass(&ipc_username, &ipc_domain, &ipc_password); - - *retry = True; - - got_mutex = secrets_named_mutex(controller, - WINBIND_SERVER_MUTEX_WAIT_TIME); - - if (!got_mutex) { - DEBUG(0,("cm_prepare_connection: mutex grab failed for %s\n", - controller)); - result = NT_STATUS_POSSIBLE_DEADLOCK; - goto done; - } - - if ((*cli = cli_initialise()) == NULL) { - DEBUG(1, ("Could not cli_initialize\n")); - result = NT_STATUS_NO_MEMORY; - goto done; - } - - (*cli)->timeout = 10000; /* 10 seconds */ - (*cli)->fd = sockfd; - fstrcpy((*cli)->desthost, controller); - (*cli)->use_kerberos = True; - - peeraddr_len = sizeof(peeraddr); - - if ((getpeername((*cli)->fd, &peeraddr, &peeraddr_len) != 0) || - (peeraddr_len != sizeof(struct sockaddr_in)) || - (peeraddr_in->sin_family != PF_INET)) - { - DEBUG(0,("cm_prepare_connection: %s\n", strerror(errno))); - result = NT_STATUS_UNSUCCESSFUL; - goto done; - } - - if (ntohs(peeraddr_in->sin_port) == 139) { - struct nmb_name calling; - struct nmb_name called; - - make_nmb_name(&calling, global_myname(), 0x0); - make_nmb_name(&called, "*SMBSERVER", 0x20); - - if (!cli_session_request(*cli, &calling, &called)) { - DEBUG(8, ("cli_session_request failed for %s\n", - controller)); - result = NT_STATUS_UNSUCCESSFUL; - goto done; - } - } - - cli_setup_signing_state(*cli, Undefined); - - if (!cli_negprot(*cli)) { - DEBUG(1, ("cli_negprot failed\n")); - result = NT_STATUS_UNSUCCESSFUL; - goto done; - } - - if ((*cli)->protocol >= PROTOCOL_NT1 && (*cli)->capabilities & CAP_EXTENDED_SECURITY) { - ADS_STATUS ads_status; - - if (lp_security() == SEC_ADS) { - - /* Try a krb5 session */ - - (*cli)->use_kerberos = True; - DEBUG(5, ("connecting to %s from %s with kerberos principal " - "[%s]\n", controller, global_myname(), - machine_krb5_principal)); - - winbindd_set_locator_kdc_envs(domain); - - ads_status = cli_session_setup_spnego(*cli, - machine_krb5_principal, - machine_password, - lp_workgroup()); - - if (!ADS_ERR_OK(ads_status)) { - DEBUG(4,("failed kerberos session setup with %s\n", - ads_errstr(ads_status))); - } - - result = ads_ntstatus(ads_status); - if (NT_STATUS_IS_OK(result)) { - /* Ensure creds are stored for NTLMSSP authenticated pipe access. */ - cli_init_creds(*cli, machine_account, lp_workgroup(), machine_password); - goto session_setup_done; - } - } - - /* Fall back to non-kerberos session setup using NTLMSSP SPNEGO with the machine account. */ - (*cli)->use_kerberos = False; - - DEBUG(5, ("connecting to %s from %s with username " - "[%s]\\[%s]\n", controller, global_myname(), - lp_workgroup(), machine_account)); - - ads_status = cli_session_setup_spnego(*cli, - machine_account, - machine_password, - lp_workgroup()); - if (!ADS_ERR_OK(ads_status)) { - DEBUG(4, ("authenticated session setup failed with %s\n", - ads_errstr(ads_status))); - } - - result = ads_ntstatus(ads_status); - if (NT_STATUS_IS_OK(result)) { - /* Ensure creds are stored for NTLMSSP authenticated pipe access. */ - cli_init_creds(*cli, machine_account, lp_workgroup(), machine_password); - goto session_setup_done; - } - } - - /* Fall back to non-kerberos session setup */ - - (*cli)->use_kerberos = False; - - if ((((*cli)->sec_mode & NEGOTIATE_SECURITY_CHALLENGE_RESPONSE) != 0) && - (strlen(ipc_username) > 0)) { - - /* Only try authenticated if we have a username */ - - DEBUG(5, ("connecting to %s from %s with username " - "[%s]\\[%s]\n", controller, global_myname(), - ipc_domain, ipc_username)); - - if (NT_STATUS_IS_OK(cli_session_setup( - *cli, ipc_username, - ipc_password, strlen(ipc_password)+1, - ipc_password, strlen(ipc_password)+1, - ipc_domain))) { - /* Successful logon with given username. */ - cli_init_creds(*cli, ipc_username, ipc_domain, ipc_password); - goto session_setup_done; - } else { - DEBUG(4, ("authenticated session setup with user %s\\%s failed.\n", - ipc_domain, ipc_username )); - } - } - - /* Fall back to anonymous connection, this might fail later */ - - if (NT_STATUS_IS_OK(cli_session_setup(*cli, "", NULL, 0, - NULL, 0, ""))) { - DEBUG(5, ("Connected anonymously\n")); - cli_init_creds(*cli, "", "", ""); - goto session_setup_done; - } - - result = cli_nt_error(*cli); - - if (NT_STATUS_IS_OK(result)) - result = NT_STATUS_UNSUCCESSFUL; - - /* We can't session setup */ - - goto done; - - session_setup_done: - - /* cache the server name for later connections */ - - saf_store( domain->name, (*cli)->desthost ); - if (domain->alt_name && (*cli)->use_kerberos) { - saf_store( domain->alt_name, (*cli)->desthost ); - } - - winbindd_set_locator_kdc_envs(domain); - - if (!cli_send_tconX(*cli, "IPC$", "IPC", "", 0)) { - - result = cli_nt_error(*cli); - - DEBUG(1,("failed tcon_X with %s\n", nt_errstr(result))); - - if (NT_STATUS_IS_OK(result)) - result = NT_STATUS_UNSUCCESSFUL; - - goto done; - } - - secrets_named_mutex_release(controller); - got_mutex = False; - *retry = False; - - /* set the domain if empty; needed for schannel connections */ - if ( !*(*cli)->domain ) { - fstrcpy( (*cli)->domain, domain->name ); - } - - result = NT_STATUS_OK; - - done: - if (got_mutex) { - secrets_named_mutex_release(controller); - } - - SAFE_FREE(machine_account); - SAFE_FREE(machine_password); - SAFE_FREE(machine_krb5_principal); - SAFE_FREE(ipc_username); - SAFE_FREE(ipc_domain); - SAFE_FREE(ipc_password); - - if (!NT_STATUS_IS_OK(result)) { - winbind_add_failed_connection_entry(domain, controller, result); - if ((*cli) != NULL) { - cli_shutdown(*cli); - *cli = NULL; - } - } - - return result; -} - -static BOOL add_one_dc_unique(TALLOC_CTX *mem_ctx, const char *domain_name, - const char *dcname, struct in_addr ip, - struct dc_name_ip **dcs, int *num) -{ - if (!NT_STATUS_IS_OK(check_negative_conn_cache(domain_name, dcname))) { - DEBUG(10, ("DC %s was in the negative conn cache\n", dcname)); - return False; - } - - *dcs = TALLOC_REALLOC_ARRAY(mem_ctx, *dcs, struct dc_name_ip, (*num)+1); - - if (*dcs == NULL) - return False; - - fstrcpy((*dcs)[*num].name, dcname); - (*dcs)[*num].ip = ip; - *num += 1; - return True; -} - -static BOOL add_sockaddr_to_array(TALLOC_CTX *mem_ctx, - struct in_addr ip, uint16 port, - struct sockaddr_in **addrs, int *num) -{ - *addrs = TALLOC_REALLOC_ARRAY(mem_ctx, *addrs, struct sockaddr_in, (*num)+1); - - if (*addrs == NULL) { - *num = 0; - return False; - } - - (*addrs)[*num].sin_family = PF_INET; - putip((char *)&((*addrs)[*num].sin_addr), (char *)&ip); - (*addrs)[*num].sin_port = htons(port); - - *num += 1; - return True; -} - -static void mailslot_name(struct in_addr dc_ip, fstring name) -{ - fstr_sprintf(name, "\\MAILSLOT\\NET\\GETDC%X", dc_ip.s_addr); -} - -static BOOL send_getdc_request(struct in_addr dc_ip, - const char *domain_name, - const DOM_SID *sid) -{ - pstring outbuf; - char *p; - fstring my_acct_name; - fstring my_mailslot; - - mailslot_name(dc_ip, my_mailslot); - - memset(outbuf, '\0', sizeof(outbuf)); - - p = outbuf; - - SCVAL(p, 0, SAMLOGON); - p++; - - SCVAL(p, 0, 0); /* Count pointer ... */ - p++; - - SIVAL(p, 0, 0); /* The sender's token ... */ - p += 2; - - p += dos_PutUniCode(p, global_myname(), sizeof(pstring), True); - fstr_sprintf(my_acct_name, "%s$", global_myname()); - p += dos_PutUniCode(p, my_acct_name, sizeof(pstring), True); - - memcpy(p, my_mailslot, strlen(my_mailslot)+1); - p += strlen(my_mailslot)+1; - - SIVAL(p, 0, 0x80); - p+=4; - - SIVAL(p, 0, sid_size(sid)); - p+=4; - - p = ALIGN4(p, outbuf); - - sid_linearize(p, sid_size(sid), sid); - p += sid_size(sid); - - SIVAL(p, 0, 1); - SSVAL(p, 4, 0xffff); - SSVAL(p, 6, 0xffff); - p+=8; - - return cli_send_mailslot(winbind_messaging_context(), - False, "\\MAILSLOT\\NET\\NTLOGON", 0, - outbuf, PTR_DIFF(p, outbuf), - global_myname(), 0, domain_name, 0x1c, - dc_ip); -} - -static BOOL receive_getdc_response(struct in_addr dc_ip, - const char *domain_name, - fstring dc_name) -{ - struct packet_struct *packet; - fstring my_mailslot; - char *buf, *p; - fstring dcname, user, domain; - int len; - - mailslot_name(dc_ip, my_mailslot); - - packet = receive_unexpected(DGRAM_PACKET, 0, my_mailslot); - - if (packet == NULL) { - DEBUG(5, ("Did not receive packet for %s\n", my_mailslot)); - return False; - } - - DEBUG(5, ("Received packet for %s\n", my_mailslot)); - - buf = packet->packet.dgram.data; - len = packet->packet.dgram.datasize; - - if (len < 70) { - /* 70 is a completely arbitrary value to make sure - the SVAL below does not read uninitialized memory */ - DEBUG(3, ("GetDC got short response\n")); - return False; - } - - /* This should be (buf-4)+SVAL(buf-4, smb_vwv12)... */ - p = buf+SVAL(buf, smb_vwv10); - - if (CVAL(p,0) != SAMLOGON_R) { - DEBUG(8, ("GetDC got invalid response type %d\n", CVAL(p, 0))); - return False; - } - - p+=2; - pull_ucs2(buf, dcname, p, sizeof(dcname), PTR_DIFF(buf+len, p), - STR_TERMINATE|STR_NOALIGN); - p = skip_unibuf(p, PTR_DIFF(buf+len, p)); - pull_ucs2(buf, user, p, sizeof(dcname), PTR_DIFF(buf+len, p), - STR_TERMINATE|STR_NOALIGN); - p = skip_unibuf(p, PTR_DIFF(buf+len, p)); - pull_ucs2(buf, domain, p, sizeof(dcname), PTR_DIFF(buf+len, p), - STR_TERMINATE|STR_NOALIGN); - p = skip_unibuf(p, PTR_DIFF(buf+len, p)); - - if (!strequal(domain, domain_name)) { - DEBUG(3, ("GetDC: Expected domain %s, got %s\n", - domain_name, domain)); - return False; - } - - p = dcname; - if (*p == '\\') p += 1; - if (*p == '\\') p += 1; - - fstrcpy(dc_name, p); - - DEBUG(10, ("GetDC gave name %s for domain %s\n", - dc_name, domain)); - - return True; -} - -/******************************************************************* - convert an ip to a name -*******************************************************************/ - -static BOOL dcip_to_name(const struct winbindd_domain *domain, struct in_addr ip, fstring name ) -{ - struct ip_service ip_list; - - ip_list.ip = ip; - ip_list.port = 0; - -#ifdef WITH_ADS - /* For active directory servers, try to get the ldap server name. - None of these failures should be considered critical for now */ - - if (lp_security() == SEC_ADS) { - ADS_STRUCT *ads; - - ads = ads_init(domain->alt_name, domain->name, NULL); - ads->auth.flags |= ADS_AUTH_NO_BIND; - - if (ads_try_connect( ads, inet_ntoa(ip) ) ) { - /* We got a cldap packet. */ - fstrcpy(name, ads->config.ldap_server_name); - namecache_store(name, 0x20, 1, &ip_list); - - DEBUG(10,("dcip_to_name: flags = 0x%x\n", (unsigned int)ads->config.flags)); - - if (domain->primary && (ads->config.flags & ADS_KDC)) { - if (ads_closest_dc(ads)) { - char *sitename = sitename_fetch(ads->config.realm); - - /* We're going to use this KDC for this realm/domain. - If we are using sites, then force the krb5 libs - to use this KDC. */ - - create_local_private_krb5_conf_for_domain(domain->alt_name, - domain->name, - sitename, - ip); - - SAFE_FREE(sitename); - } else { - /* use an off site KDC */ - create_local_private_krb5_conf_for_domain(domain->alt_name, - domain->name, - NULL, - ip); - } - winbindd_set_locator_kdc_envs(domain); - - /* Ensure we contact this DC also. */ - saf_store( domain->name, name); - saf_store( domain->alt_name, name); - } - - ads_destroy( &ads ); - return True; - } - - ads_destroy( &ads ); - } -#endif - - /* try GETDC requests next */ - - if (send_getdc_request(ip, domain->name, &domain->sid)) { - int i; - smb_msleep(100); - for (i=0; i<5; i++) { - if (receive_getdc_response(ip, domain->name, name)) { - namecache_store(name, 0x20, 1, &ip_list); - return True; - } - smb_msleep(500); - } - } - - /* try node status request */ - - if ( name_status_find(domain->name, 0x1c, 0x20, ip, name) ) { - namecache_store(name, 0x20, 1, &ip_list); - return True; - } - return False; -} - -/******************************************************************* - Retreive a list of IP address for domain controllers. Fill in - the dcs[] with results. -*******************************************************************/ - -static BOOL get_dcs(TALLOC_CTX *mem_ctx, const struct winbindd_domain *domain, - struct dc_name_ip **dcs, int *num_dcs) -{ - fstring dcname; - struct in_addr ip; - struct ip_service *ip_list = NULL; - int iplist_size = 0; - int i; - BOOL is_our_domain; - enum security_types sec = (enum security_types)lp_security(); - - is_our_domain = strequal(domain->name, lp_workgroup()); - - if ( !is_our_domain - && get_dc_name_via_netlogon(domain, dcname, &ip) - && add_one_dc_unique(mem_ctx, domain->name, dcname, ip, dcs, num_dcs) ) - { - DEBUG(10, ("Retrieved DC %s at %s via netlogon\n", - dcname, inet_ntoa(ip))); - return True; - } - - if (sec == SEC_ADS) { - char *sitename = NULL; - - /* We need to make sure we know the local site before - doing any DNS queries, as this will restrict the - get_sorted_dc_list() call below to only fetching - DNS records for the correct site. */ - - /* Find any DC to get the site record. - We deliberately don't care about the - return here. */ - - get_dc_name(domain->name, domain->alt_name, dcname, &ip); - - sitename = sitename_fetch(domain->alt_name); - if (sitename) { - - /* Do the site-specific AD dns lookup first. */ - get_sorted_dc_list(domain->alt_name, sitename, &ip_list, &iplist_size, True); - - for ( i=0; iname, inet_ntoa(ip_list[i].ip), - ip_list[i].ip, dcs, num_dcs); - } - - SAFE_FREE(ip_list); - SAFE_FREE(sitename); - iplist_size = 0; - } - - /* Now we add DCs from the main AD dns lookup. */ - get_sorted_dc_list(domain->alt_name, NULL, &ip_list, &iplist_size, True); - - for ( i=0; iname, inet_ntoa(ip_list[i].ip), - ip_list[i].ip, dcs, num_dcs); - } - } - - /* try standard netbios queries if no ADS */ - - if (iplist_size==0) { - get_sorted_dc_list(domain->name, NULL, &ip_list, &iplist_size, False); - } - - /* FIXME!! this is where we should re-insert the GETDC requests --jerry */ - - /* now add to the dc array. We'll wait until the last minute - to look up the name of the DC. But we fill in the char* for - the ip now in to make the failed connection cache work */ - - for ( i=0; iname, inet_ntoa(ip_list[i].ip), - ip_list[i].ip, dcs, num_dcs); - } - - SAFE_FREE( ip_list ); - - return True; -} - -static BOOL find_new_dc(TALLOC_CTX *mem_ctx, - const struct winbindd_domain *domain, - fstring dcname, struct sockaddr_in *addr, int *fd) -{ - struct dc_name_ip *dcs = NULL; - int num_dcs = 0; - - const char **dcnames = NULL; - int num_dcnames = 0; - - struct sockaddr_in *addrs = NULL; - int num_addrs = 0; - - int i, fd_index; - - again: - if (!get_dcs(mem_ctx, domain, &dcs, &num_dcs) || (num_dcs == 0)) - return False; - - for (i=0; iname, inet_ntoa(dcs[i].ip), strerror(errno) )); - winbind_add_failed_connection_entry(domain, - dcs[i].name, NT_STATUS_UNSUCCESSFUL); - } - return False; - } - - *addr = addrs[fd_index]; - - if (*dcnames[fd_index] != '\0' && !is_ipaddress(dcnames[fd_index])) { - /* Ok, we've got a name for the DC */ - fstrcpy(dcname, dcnames[fd_index]); - return True; - } - - /* Try to figure out the name */ - if (dcip_to_name( domain, addr->sin_addr, dcname )) { - return True; - } - - /* We can not continue without the DC's name */ - winbind_add_failed_connection_entry(domain, dcs[fd_index].name, - NT_STATUS_UNSUCCESSFUL); - goto again; -} - -static NTSTATUS cm_open_connection(struct winbindd_domain *domain, - struct winbindd_cm_conn *new_conn) -{ - TALLOC_CTX *mem_ctx; - NTSTATUS result; - char *saf_servername = saf_fetch( domain->name ); - int retries; - - if ((mem_ctx = talloc_init("cm_open_connection")) == NULL) { - SAFE_FREE(saf_servername); - set_domain_offline(domain); - return NT_STATUS_NO_MEMORY; - } - - /* we have to check the server affinity cache here since - later we selecte a DC based on response time and not preference */ - - /* Check the negative connection cache - before talking to it. It going down may have - triggered the reconnection. */ - - if ( saf_servername && NT_STATUS_IS_OK(check_negative_conn_cache( domain->name, saf_servername))) { - - DEBUG(10,("cm_open_connection: saf_servername is '%s' for domain %s\n", - saf_servername, domain->name )); - - /* convert an ip address to a name */ - if ( is_ipaddress( saf_servername ) ) { - fstring saf_name; - struct in_addr ip; - - ip = *interpret_addr2( saf_servername ); - if (dcip_to_name( domain, ip, saf_name )) { - fstrcpy( domain->dcname, saf_name ); - } else { - winbind_add_failed_connection_entry( - domain, saf_servername, - NT_STATUS_UNSUCCESSFUL); - } - } else { - fstrcpy( domain->dcname, saf_servername ); - } - - SAFE_FREE( saf_servername ); - } - - for (retries = 0; retries < 3; retries++) { - - int fd = -1; - BOOL retry = False; - - result = NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND; - - DEBUG(10,("cm_open_connection: dcname is '%s' for domain %s\n", - domain->dcname, domain->name )); - - if (*domain->dcname - && NT_STATUS_IS_OK(check_negative_conn_cache( domain->name, domain->dcname)) - && (resolve_name(domain->dcname, &domain->dcaddr.sin_addr, 0x20))) - { - struct sockaddr_in *addrs = NULL; - int num_addrs = 0; - int dummy = 0; - - if (!add_sockaddr_to_array(mem_ctx, domain->dcaddr.sin_addr, 445, &addrs, &num_addrs)) { - set_domain_offline(domain); - talloc_destroy(mem_ctx); - return NT_STATUS_NO_MEMORY; - } - if (!add_sockaddr_to_array(mem_ctx, domain->dcaddr.sin_addr, 139, &addrs, &num_addrs)) { - set_domain_offline(domain); - talloc_destroy(mem_ctx); - return NT_STATUS_NO_MEMORY; - } - - /* 5 second timeout. */ - if (!open_any_socket_out(addrs, num_addrs, 5000, &dummy, &fd)) { - fd = -1; - } - } - - if ((fd == -1) - && !find_new_dc(mem_ctx, domain, domain->dcname, &domain->dcaddr, &fd)) - { - /* This is the one place where we will - set the global winbindd offline state - to true, if a "WINBINDD_OFFLINE" entry - is found in the winbindd cache. */ - set_global_winbindd_state_offline(); - break; - } - - new_conn->cli = NULL; - - result = cm_prepare_connection(domain, fd, domain->dcname, - &new_conn->cli, &retry); - - if (!retry) - break; - } - - if (NT_STATUS_IS_OK(result)) { - - winbindd_set_locator_kdc_envs(domain); - - if (domain->online == False) { - /* We're changing state from offline to online. */ - set_global_winbindd_state_online(); - } - set_domain_online(domain); - } else { - /* Ensure we setup the retry handler. */ - set_domain_offline(domain); - } - - talloc_destroy(mem_ctx); - return result; -} - -/* Close down all open pipes on a connection. */ - -void invalidate_cm_connection(struct winbindd_cm_conn *conn) -{ - /* We're closing down a possibly dead - connection. Don't have impossibly long (10s) timeouts. */ - - if (conn->cli) { - cli_set_timeout(conn->cli, 1000); /* 1 second. */ - } - - if (conn->samr_pipe != NULL) { - if (!cli_rpc_pipe_close(conn->samr_pipe)) { - /* Ok, it must be dead. Drop timeout to 0.5 sec. */ - if (conn->cli) { - cli_set_timeout(conn->cli, 500); - } - } - conn->samr_pipe = NULL; - } - - if (conn->lsa_pipe != NULL) { - if (!cli_rpc_pipe_close(conn->lsa_pipe)) { - /* Ok, it must be dead. Drop timeout to 0.5 sec. */ - if (conn->cli) { - cli_set_timeout(conn->cli, 500); - } - } - conn->lsa_pipe = NULL; - } - - if (conn->netlogon_pipe != NULL) { - if (!cli_rpc_pipe_close(conn->netlogon_pipe)) { - /* Ok, it must be dead. Drop timeout to 0.5 sec. */ - if (conn->cli) { - cli_set_timeout(conn->cli, 500); - } - } - conn->netlogon_pipe = NULL; - } - - if (conn->cli) { - cli_shutdown(conn->cli); - } - - conn->cli = NULL; -} - -void close_conns_after_fork(void) -{ - struct winbindd_domain *domain; - - for (domain = domain_list(); domain; domain = domain->next) { - if (domain->conn.cli == NULL) - continue; - - if (domain->conn.cli->fd == -1) - continue; - - close(domain->conn.cli->fd); - domain->conn.cli->fd = -1; - } -} - -static BOOL connection_ok(struct winbindd_domain *domain) -{ - if (domain->conn.cli == NULL) { - DEBUG(8, ("connection_ok: Connection to %s for domain %s has NULL " - "cli!\n", domain->dcname, domain->name)); - return False; - } - - if (!domain->conn.cli->initialised) { - DEBUG(3, ("connection_ok: Connection to %s for domain %s was never " - "initialised!\n", domain->dcname, domain->name)); - return False; - } - - if (domain->conn.cli->fd == -1) { - DEBUG(3, ("connection_ok: Connection to %s for domain %s has died or was " - "never started (fd == -1)\n", - domain->dcname, domain->name)); - return False; - } - - if (domain->online == False) { - DEBUG(3, ("connection_ok: Domain %s is offline\n", domain->name)); - return False; - } - - return True; -} - -/* Initialize a new connection up to the RPC BIND. - Bypass online status check so always does network calls. */ - -static NTSTATUS init_dc_connection_network(struct winbindd_domain *domain) -{ - NTSTATUS result; - - /* Internal connections never use the network. */ - if (domain->internal) { - domain->initialized = True; - return NT_STATUS_OK; - } - - if (connection_ok(domain)) { - if (!domain->initialized) { - set_dc_type_and_flags(domain); - } - return NT_STATUS_OK; - } - - invalidate_cm_connection(&domain->conn); - - result = cm_open_connection(domain, &domain->conn); - - if (NT_STATUS_IS_OK(result) && !domain->initialized) { - set_dc_type_and_flags(domain); - } - - return result; -} - -NTSTATUS init_dc_connection(struct winbindd_domain *domain) -{ - if (domain->initialized && !domain->online) { - /* We check for online status elsewhere. */ - return NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND; - } - - return init_dc_connection_network(domain); -} - -/****************************************************************************** - Set the trust flags (direction and forest location) for a domain -******************************************************************************/ - -static BOOL set_dc_type_and_flags_trustinfo( struct winbindd_domain *domain ) -{ - struct winbindd_domain *our_domain; - NTSTATUS result = NT_STATUS_UNSUCCESSFUL; - struct ds_domain_trust *domains = NULL; - int count = 0; - int i; - uint32 flags = (DS_DOMAIN_IN_FOREST | - DS_DOMAIN_DIRECT_OUTBOUND | - DS_DOMAIN_DIRECT_INBOUND); - struct rpc_pipe_client *cli; - TALLOC_CTX *mem_ctx = NULL; - - DEBUG(5, ("set_dc_type_and_flags_trustinfo: domain %s\n", domain->name )); - - /* Our primary domain doesn't need to worry about trust flags. - Force it to go through the network setup */ - if ( domain->primary ) { - return False; - } - - our_domain = find_our_domain(); - - if ( !connection_ok(our_domain) ) { - DEBUG(3,("set_dc_type_and_flags_trustinfo: No connection to our domain!\n")); - return False; - } - - /* This won't work unless our domain is AD */ - - if ( !our_domain->active_directory ) { - return False; - } - - /* Use DsEnumerateDomainTrusts to get us the trust direction - and type */ - - result = cm_connect_netlogon(our_domain, &cli); - - if (!NT_STATUS_IS_OK(result)) { - DEBUG(5, ("set_dc_type_and_flags_trustinfo: Could not open " - "a connection to %s for PIPE_NETLOGON (%s)\n", - domain->name, nt_errstr(result))); - return False; - } - - if ( (mem_ctx = talloc_init("set_dc_type_and_flags_trustinfo")) == NULL ) { - DEBUG(0,("set_dc_type_and_flags_trustinfo: talloc_init() failed!\n")); - return False; - } - - result = rpccli_ds_enum_domain_trusts(cli, mem_ctx, - cli->cli->desthost, - flags, &domains, - (unsigned int *)&count); - - /* Now find the domain name and get the flags */ - - for ( i=0; iname, domains[i].netbios_domain ) ) { - domain->domain_flags = domains[i].flags; - domain->domain_type = domains[i].trust_type; - domain->domain_trust_attribs = domains[i].trust_attributes; - - if ( domain->domain_type == DS_DOMAIN_TRUST_TYPE_UPLEVEL ) - domain->active_directory = True; - - /* This flag is only set if the domain is *our* - primary domain and the primary domain is in - native mode */ - - domain->native_mode = (domain->domain_flags & DS_DOMAIN_NATIVE_MODE); - - DEBUG(5, ("set_dc_type_and_flags_trustinfo: domain %s is %sin " - "native mode.\n", domain->name, - domain->native_mode ? "" : "NOT ")); - - DEBUG(5,("set_dc_type_and_flags_trustinfo: domain %s is %s" - "running active directory.\n", domain->name, - domain->active_directory ? "" : "NOT ")); - - - domain->initialized = True; - - if ( !winbindd_can_contact_domain( domain) ) - domain->internal = True; - - break; - } - } - - talloc_destroy( mem_ctx ); - - return domain->initialized; -} - -/****************************************************************************** - We can 'sense' certain things about the DC by it's replies to certain - questions. - - This tells us if this particular remote server is Active Directory, and if it - is native mode. -******************************************************************************/ - -static void set_dc_type_and_flags_connect( struct winbindd_domain *domain ) -{ - NTSTATUS result; - DS_DOMINFO_CTR ctr; - TALLOC_CTX *mem_ctx = NULL; - struct rpc_pipe_client *cli; - POLICY_HND pol; - - char *domain_name = NULL; - char *dns_name = NULL; - char *forest_name = NULL; - DOM_SID *dom_sid = NULL; - - ZERO_STRUCT( ctr ); - - if (!connection_ok(domain)) { - return; - } - - DEBUG(5, ("set_dc_type_and_flags_connect: domain %s\n", domain->name )); - - cli = cli_rpc_pipe_open_noauth(domain->conn.cli, PI_LSARPC_DS, - &result); - - if (cli == NULL) { - DEBUG(5, ("set_dc_type_and_flags_connect: Could not bind to " - "PI_LSARPC_DS on domain %s: (%s)\n", - domain->name, nt_errstr(result))); - - /* if this is just a non-AD domain we need to continue - * identifying so that we can in the end return with - * domain->initialized = True - gd */ - - goto no_lsarpc_ds; - } - - result = rpccli_ds_getprimarydominfo(cli, cli->cli->mem_ctx, - DsRolePrimaryDomainInfoBasic, - &ctr); - cli_rpc_pipe_close(cli); - - if (!NT_STATUS_IS_OK(result)) { - DEBUG(5, ("set_dc_type_and_flags_connect: rpccli_ds_getprimarydominfo " - "on domain %s failed: (%s)\n", - domain->name, nt_errstr(result))); - - /* older samba3 DCs will return DCERPC_FAULT_OP_RNG_ERROR for - * every opcode on the LSARPC_DS pipe, continue with - * no_lsarpc_ds mode here as well to get domain->initialized - * set - gd */ - - if (NT_STATUS_V(result) == DCERPC_FAULT_OP_RNG_ERROR) { - goto no_lsarpc_ds; - } - - return; - } - - if ((ctr.basic->flags & DSROLE_PRIMARY_DS_RUNNING) && - !(ctr.basic->flags & DSROLE_PRIMARY_DS_MIXED_MODE)) { - domain->native_mode = True; - } else { - domain->native_mode = False; - } - -no_lsarpc_ds: - cli = cli_rpc_pipe_open_noauth(domain->conn.cli, PI_LSARPC, &result); - - if (cli == NULL) { - DEBUG(5, ("set_dc_type_and_flags_connect: Could not bind to " - "PI_LSARPC on domain %s: (%s)\n", - domain->name, nt_errstr(result))); - cli_rpc_pipe_close(cli); - return; - } - - mem_ctx = talloc_init("set_dc_type_and_flags on domain %s\n", - domain->name); - if (!mem_ctx) { - DEBUG(1, ("set_dc_type_and_flags_connect: talloc_init() failed\n")); - cli_rpc_pipe_close(cli); - return; - } - - result = rpccli_lsa_open_policy2(cli, mem_ctx, True, - SEC_RIGHTS_MAXIMUM_ALLOWED, &pol); - - if (NT_STATUS_IS_OK(result)) { - /* This particular query is exactly what Win2k clients use - to determine that the DC is active directory */ - result = rpccli_lsa_query_info_policy2(cli, mem_ctx, &pol, - 12, &domain_name, - &dns_name, &forest_name, - NULL, &dom_sid); - } - - if (NT_STATUS_IS_OK(result)) { - domain->active_directory = True; - - if (domain_name) - fstrcpy(domain->name, domain_name); - - if (dns_name) - fstrcpy(domain->alt_name, dns_name); - - if ( forest_name ) - fstrcpy(domain->forest_name, forest_name); - - if (dom_sid) - sid_copy(&domain->sid, dom_sid); - } else { - domain->active_directory = False; - - result = rpccli_lsa_open_policy(cli, mem_ctx, True, - SEC_RIGHTS_MAXIMUM_ALLOWED, - &pol); - - if (!NT_STATUS_IS_OK(result)) - goto done; - - result = rpccli_lsa_query_info_policy(cli, mem_ctx, - &pol, 5, &domain_name, - &dom_sid); - - if (NT_STATUS_IS_OK(result)) { - if (domain_name) - fstrcpy(domain->name, domain_name); - - if (dom_sid) - sid_copy(&domain->sid, dom_sid); - } - } -done: - - DEBUG(5, ("set_dc_type_and_flags_connect: domain %s is %sin native mode.\n", - domain->name, domain->native_mode ? "" : "NOT ")); - - DEBUG(5,("set_dc_type_and_flags_connect: domain %s is %srunning active directory.\n", - domain->name, domain->active_directory ? "" : "NOT ")); - - cli_rpc_pipe_close(cli); - - talloc_destroy(mem_ctx); - - domain->initialized = True; -} - -/********************************************************************** - Set the domain_flags (trust attributes, domain operating modes, etc... -***********************************************************************/ - -static void set_dc_type_and_flags( struct winbindd_domain *domain ) -{ - /* we always have to contact our primary domain */ - - if ( domain->primary ) { - DEBUG(10,("set_dc_type_and_flags: setting up flags for " - "primary domain\n")); - set_dc_type_and_flags_connect( domain ); - return; - } - - /* Use our DC to get the information if possible */ - - if ( !set_dc_type_and_flags_trustinfo( domain ) ) { - /* Otherwise, fallback to contacting the - domain directly */ - set_dc_type_and_flags_connect( domain ); - } - - return; -} - - - -/********************************************************************** -***********************************************************************/ - -static BOOL cm_get_schannel_dcinfo(struct winbindd_domain *domain, - struct dcinfo **ppdc) -{ - NTSTATUS result; - struct rpc_pipe_client *netlogon_pipe; - - if (lp_client_schannel() == False) { - return False; - } - - result = cm_connect_netlogon(domain, &netlogon_pipe); - if (!NT_STATUS_IS_OK(result)) { - return False; - } - - /* Return a pointer to the struct dcinfo from the - netlogon pipe. */ - - *ppdc = domain->conn.netlogon_pipe->dc; - return True; -} - -NTSTATUS cm_connect_sam(struct winbindd_domain *domain, TALLOC_CTX *mem_ctx, - struct rpc_pipe_client **cli, POLICY_HND *sam_handle) -{ - struct winbindd_cm_conn *conn; - NTSTATUS result = NT_STATUS_UNSUCCESSFUL; - fstring conn_pwd; - struct dcinfo *p_dcinfo; - - result = init_dc_connection(domain); - if (!NT_STATUS_IS_OK(result)) { - return result; - } - - conn = &domain->conn; - - if (conn->samr_pipe != NULL) { - goto done; - } - - /* - * No SAMR pipe yet. Attempt to get an NTLMSSP SPNEGO authenticated - * sign and sealed pipe using the machine account password by - * preference. If we can't - try schannel, if that fails, try - * anonymous. - */ - - pwd_get_cleartext(&conn->cli->pwd, conn_pwd); - if ((conn->cli->user_name[0] == '\0') || - (conn->cli->domain[0] == '\0') || - (conn_pwd[0] == '\0')) { - DEBUG(10, ("cm_connect_sam: No no user available for " - "domain %s, trying schannel\n", conn->cli->domain)); - goto schannel; - } - - /* We have an authenticated connection. Use a NTLMSSP SPNEGO - authenticated SAMR pipe with sign & seal. */ - conn->samr_pipe = - cli_rpc_pipe_open_spnego_ntlmssp(conn->cli, PI_SAMR, - PIPE_AUTH_LEVEL_PRIVACY, - conn->cli->domain, - conn->cli->user_name, - conn_pwd, &result); - - if (conn->samr_pipe == NULL) { - DEBUG(10,("cm_connect_sam: failed to connect to SAMR " - "pipe for domain %s using NTLMSSP " - "authenticated pipe: user %s\\%s. Error was " - "%s\n", domain->name, conn->cli->domain, - conn->cli->user_name, nt_errstr(result))); - goto schannel; - } - - DEBUG(10,("cm_connect_sam: connected to SAMR pipe for " - "domain %s using NTLMSSP authenticated " - "pipe: user %s\\%s\n", domain->name, - conn->cli->domain, conn->cli->user_name )); - - result = rpccli_samr_connect(conn->samr_pipe, mem_ctx, - SEC_RIGHTS_MAXIMUM_ALLOWED, - &conn->sam_connect_handle); - if (NT_STATUS_IS_OK(result)) { - goto open_domain; - } - DEBUG(10,("cm_connect_sam: ntlmssp-sealed rpccli_samr_connect " - "failed for domain %s, error was %s. Trying schannel\n", - domain->name, nt_errstr(result) )); - cli_rpc_pipe_close(conn->samr_pipe); - - schannel: - - /* Fall back to schannel if it's a W2K pre-SP1 box. */ - - if (!cm_get_schannel_dcinfo(domain, &p_dcinfo)) { - /* If this call fails - conn->cli can now be NULL ! */ - DEBUG(10, ("cm_connect_sam: Could not get schannel auth info " - "for domain %s, trying anon\n", domain->name)); - goto anonymous; - } - conn->samr_pipe = cli_rpc_pipe_open_schannel_with_key - (conn->cli, PI_SAMR, PIPE_AUTH_LEVEL_PRIVACY, - domain->name, p_dcinfo, &result); - - if (conn->samr_pipe == NULL) { - DEBUG(10,("cm_connect_sam: failed to connect to SAMR pipe for " - "domain %s using schannel. Error was %s\n", - domain->name, nt_errstr(result) )); - goto anonymous; - } - DEBUG(10,("cm_connect_sam: connected to SAMR pipe for domain %s using " - "schannel.\n", domain->name )); - - result = rpccli_samr_connect(conn->samr_pipe, mem_ctx, - SEC_RIGHTS_MAXIMUM_ALLOWED, - &conn->sam_connect_handle); - if (NT_STATUS_IS_OK(result)) { - goto open_domain; - } - DEBUG(10,("cm_connect_sam: schannel-sealed rpccli_samr_connect failed " - "for domain %s, error was %s. Trying anonymous\n", - domain->name, nt_errstr(result) )); - cli_rpc_pipe_close(conn->samr_pipe); - - anonymous: - - /* Finally fall back to anonymous. */ - conn->samr_pipe = cli_rpc_pipe_open_noauth(conn->cli, PI_SAMR, - &result); - - if (conn->samr_pipe == NULL) { - result = NT_STATUS_PIPE_NOT_AVAILABLE; - goto done; - } - - result = rpccli_samr_connect(conn->samr_pipe, mem_ctx, - SEC_RIGHTS_MAXIMUM_ALLOWED, - &conn->sam_connect_handle); - if (!NT_STATUS_IS_OK(result)) { - DEBUG(10,("cm_connect_sam: rpccli_samr_connect failed " - "for domain %s Error was %s\n", - domain->name, nt_errstr(result) )); - goto done; - } - - open_domain: - result = rpccli_samr_open_domain(conn->samr_pipe, - mem_ctx, - &conn->sam_connect_handle, - SEC_RIGHTS_MAXIMUM_ALLOWED, - &domain->sid, - &conn->sam_domain_handle); - - done: - - if (!NT_STATUS_IS_OK(result)) { - invalidate_cm_connection(conn); - return result; - } - - *cli = conn->samr_pipe; - *sam_handle = conn->sam_domain_handle; - return result; -} - -NTSTATUS cm_connect_lsa(struct winbindd_domain *domain, TALLOC_CTX *mem_ctx, - struct rpc_pipe_client **cli, POLICY_HND *lsa_policy) -{ - struct winbindd_cm_conn *conn; - NTSTATUS result = NT_STATUS_UNSUCCESSFUL; - fstring conn_pwd; - struct dcinfo *p_dcinfo; - - result = init_dc_connection(domain); - if (!NT_STATUS_IS_OK(result)) - return result; - - conn = &domain->conn; - - if (conn->lsa_pipe != NULL) { - goto done; - } - - pwd_get_cleartext(&conn->cli->pwd, conn_pwd); - if ((conn->cli->user_name[0] == '\0') || - (conn->cli->domain[0] == '\0') || - (conn_pwd[0] == '\0')) { - DEBUG(10, ("cm_connect_lsa: No no user available for " - "domain %s, trying schannel\n", conn->cli->domain)); - goto schannel; - } - - /* We have an authenticated connection. Use a NTLMSSP SPNEGO - * authenticated LSA pipe with sign & seal. */ - conn->lsa_pipe = cli_rpc_pipe_open_spnego_ntlmssp - (conn->cli, PI_LSARPC, PIPE_AUTH_LEVEL_PRIVACY, - conn->cli->domain, conn->cli->user_name, conn_pwd, &result); - - if (conn->lsa_pipe == NULL) { - DEBUG(10,("cm_connect_lsa: failed to connect to LSA pipe for " - "domain %s using NTLMSSP authenticated pipe: user " - "%s\\%s. Error was %s. Trying schannel.\n", - domain->name, conn->cli->domain, - conn->cli->user_name, nt_errstr(result))); - goto schannel; - } - - DEBUG(10,("cm_connect_lsa: connected to LSA pipe for domain %s using " - "NTLMSSP authenticated pipe: user %s\\%s\n", - domain->name, conn->cli->domain, conn->cli->user_name )); - - result = rpccli_lsa_open_policy(conn->lsa_pipe, mem_ctx, True, - SEC_RIGHTS_MAXIMUM_ALLOWED, - &conn->lsa_policy); - if (NT_STATUS_IS_OK(result)) { - goto done; - } - - DEBUG(10,("cm_connect_lsa: rpccli_lsa_open_policy failed, trying " - "schannel\n")); - - cli_rpc_pipe_close(conn->lsa_pipe); - - schannel: - - /* Fall back to schannel if it's a W2K pre-SP1 box. */ - - if (!cm_get_schannel_dcinfo(domain, &p_dcinfo)) { - /* If this call fails - conn->cli can now be NULL ! */ - DEBUG(10, ("cm_connect_lsa: Could not get schannel auth info " - "for domain %s, trying anon\n", domain->name)); - goto anonymous; - } - conn->lsa_pipe = cli_rpc_pipe_open_schannel_with_key - (conn->cli, PI_LSARPC, PIPE_AUTH_LEVEL_PRIVACY, - domain->name, p_dcinfo, &result); - - if (conn->lsa_pipe == NULL) { - DEBUG(10,("cm_connect_lsa: failed to connect to LSA pipe for " - "domain %s using schannel. Error was %s\n", - domain->name, nt_errstr(result) )); - goto anonymous; - } - DEBUG(10,("cm_connect_lsa: connected to LSA pipe for domain %s using " - "schannel.\n", domain->name )); - - result = rpccli_lsa_open_policy(conn->lsa_pipe, mem_ctx, True, - SEC_RIGHTS_MAXIMUM_ALLOWED, - &conn->lsa_policy); - if (NT_STATUS_IS_OK(result)) { - goto done; - } - - DEBUG(10,("cm_connect_lsa: rpccli_lsa_open_policy failed, trying " - "anonymous\n")); - - cli_rpc_pipe_close(conn->lsa_pipe); - - anonymous: - - conn->lsa_pipe = cli_rpc_pipe_open_noauth(conn->cli, PI_LSARPC, - &result); - if (conn->lsa_pipe == NULL) { - result = NT_STATUS_PIPE_NOT_AVAILABLE; - goto done; - } - - result = rpccli_lsa_open_policy(conn->lsa_pipe, mem_ctx, True, - SEC_RIGHTS_MAXIMUM_ALLOWED, - &conn->lsa_policy); - done: - if (!NT_STATUS_IS_OK(result)) { - invalidate_cm_connection(conn); - return result; - } - - *cli = conn->lsa_pipe; - *lsa_policy = conn->lsa_policy; - return result; -} - -/**************************************************************************** - Open the netlogon pipe to this DC. Use schannel if specified in client conf. - session key stored in conn->netlogon_pipe->dc->sess_key. -****************************************************************************/ - -NTSTATUS cm_connect_netlogon(struct winbindd_domain *domain, - struct rpc_pipe_client **cli) -{ - struct winbindd_cm_conn *conn; - NTSTATUS result; - - uint32 neg_flags = NETLOGON_NEG_AUTH2_FLAGS; - uint8 mach_pwd[16]; - uint32 sec_chan_type; - const char *account_name; - struct rpc_pipe_client *netlogon_pipe = NULL; - - *cli = NULL; - - result = init_dc_connection(domain); - if (!NT_STATUS_IS_OK(result)) { - return result; - } - - conn = &domain->conn; - - if (conn->netlogon_pipe != NULL) { - *cli = conn->netlogon_pipe; - return NT_STATUS_OK; - } - - if ((IS_DC || domain->primary) && !get_trust_pw(domain->name, mach_pwd, &sec_chan_type)) { - return NT_STATUS_CANT_ACCESS_DOMAIN_INFO; - } - - netlogon_pipe = cli_rpc_pipe_open_noauth(conn->cli, PI_NETLOGON, - &result); - if (netlogon_pipe == NULL) { - return result; - } - - if ((!IS_DC) && (!domain->primary)) { - /* Clear the schannel request bit and drop down */ - neg_flags &= ~NETLOGON_NEG_SCHANNEL; - goto no_schannel; - } - - if (lp_client_schannel() != False) { - neg_flags |= NETLOGON_NEG_SCHANNEL; - } - - /* if we are a DC and this is a trusted domain, then we need to use our - domain name in the net_req_auth2() request */ - - if ( IS_DC - && !strequal(domain->name, lp_workgroup()) - && lp_allow_trusted_domains() ) - { - account_name = lp_workgroup(); - } else { - account_name = domain->primary ? - global_myname() : domain->name; - } - - if (account_name == NULL) { - cli_rpc_pipe_close(netlogon_pipe); - return NT_STATUS_NO_MEMORY; - } - - result = rpccli_netlogon_setup_creds( - netlogon_pipe, - domain->dcname, /* server name. */ - domain->name, /* domain name */ - global_myname(), /* client name */ - account_name, /* machine account */ - mach_pwd, /* machine password */ - sec_chan_type, /* from get_trust_pw */ - &neg_flags); - - if (!NT_STATUS_IS_OK(result)) { - cli_rpc_pipe_close(netlogon_pipe); - return result; - } - - if ((lp_client_schannel() == True) && - ((neg_flags & NETLOGON_NEG_SCHANNEL) == 0)) { - DEBUG(3, ("Server did not offer schannel\n")); - cli_rpc_pipe_close(netlogon_pipe); - return NT_STATUS_ACCESS_DENIED; - } - - no_schannel: - if ((lp_client_schannel() == False) || - ((neg_flags & NETLOGON_NEG_SCHANNEL) == 0)) { - - /* - * NetSamLogonEx only works for schannel - */ - domain->can_do_samlogon_ex = False; - - /* We're done - just keep the existing connection to NETLOGON - * open */ - conn->netlogon_pipe = netlogon_pipe; - *cli = conn->netlogon_pipe; - return NT_STATUS_OK; - } - - /* Using the credentials from the first pipe, open a signed and sealed - second netlogon pipe. The session key is stored in the schannel - part of the new pipe auth struct. - */ - - conn->netlogon_pipe = - cli_rpc_pipe_open_schannel_with_key(conn->cli, - PI_NETLOGON, - PIPE_AUTH_LEVEL_PRIVACY, - domain->name, - netlogon_pipe->dc, - &result); - - /* We can now close the initial netlogon pipe. */ - cli_rpc_pipe_close(netlogon_pipe); - - if (conn->netlogon_pipe == NULL) { - DEBUG(3, ("Could not open schannel'ed NETLOGON pipe. Error " - "was %s\n", nt_errstr(result))); - - /* make sure we return something besides OK */ - return !NT_STATUS_IS_OK(result) ? result : NT_STATUS_PIPE_NOT_AVAILABLE; - } - - /* - * Try NetSamLogonEx for AD domains - */ - domain->can_do_samlogon_ex = domain->active_directory; - - *cli = conn->netlogon_pipe; - return NT_STATUS_OK; -} diff --git a/source3/nsswitch/winbindd_cred_cache.c b/source3/nsswitch/winbindd_cred_cache.c deleted file mode 100644 index 65dcbe5a00..0000000000 --- a/source3/nsswitch/winbindd_cred_cache.c +++ /dev/null @@ -1,802 +0,0 @@ -/* - Unix SMB/CIFS implementation. - - Winbind daemon - krb5 credential cache functions - and in-memory cache functions. - - Copyright (C) Guenther Deschner 2005-2006 - Copyright (C) Jeremy Allison 2006 - - 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 3 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, see . -*/ - -#include "includes.h" -#include "winbindd.h" -#undef DBGC_CLASS -#define DBGC_CLASS DBGC_WINBIND - -/* uncomment this to do fast debugging on the krb5 ticket renewal event */ -#ifdef DEBUG_KRB5_TKT_RENEWAL -#undef DEBUG_KRB5_TKT_RENEWAL -#endif - -#define MAX_CCACHES 100 - -static struct WINBINDD_CCACHE_ENTRY *ccache_list; - -/* The Krb5 ticket refresh handler should be scheduled - at one-half of the period from now till the tkt - expiration */ -#define KRB5_EVENT_REFRESH_TIME(x) ((x) - (((x) - time(NULL))/2)) - -/**************************************************************** - Find an entry by name. -****************************************************************/ - -static struct WINBINDD_CCACHE_ENTRY *get_ccache_by_username(const char *username) -{ - struct WINBINDD_CCACHE_ENTRY *entry; - - for (entry = ccache_list; entry; entry = entry->next) { - if (strequal(entry->username, username)) { - return entry; - } - } - return NULL; -} - -/**************************************************************** - How many do we have ? -****************************************************************/ - -static int ccache_entry_count(void) -{ - struct WINBINDD_CCACHE_ENTRY *entry; - int i = 0; - - for (entry = ccache_list; entry; entry = entry->next) { - i++; - } - return i; -} - -/**************************************************************** - Do the work of refreshing the ticket. -****************************************************************/ - -static void krb5_ticket_refresh_handler(struct event_context *event_ctx, - struct timed_event *te, - const struct timeval *now, - void *private_data) -{ - struct WINBINDD_CCACHE_ENTRY *entry = - talloc_get_type_abort(private_data, struct WINBINDD_CCACHE_ENTRY); -#ifdef HAVE_KRB5 - int ret; - time_t new_start; - struct WINBINDD_MEMORY_CREDS *cred_ptr = entry->cred_ptr; -#endif - - DEBUG(10,("krb5_ticket_refresh_handler called\n")); - DEBUGADD(10,("event called for: %s, %s\n", - entry->ccname, entry->username)); - - TALLOC_FREE(entry->event); - -#ifdef HAVE_KRB5 - - /* Kinit again if we have the user password and we can't renew the old - * tgt anymore */ - - if ((entry->renew_until < time(NULL)) && cred_ptr && cred_ptr->pass) { - - set_effective_uid(entry->uid); - - ret = kerberos_kinit_password_ext(entry->principal_name, - cred_ptr->pass, - 0, /* hm, can we do time correction here ? */ - &entry->refresh_time, - &entry->renew_until, - entry->ccname, - False, /* no PAC required anymore */ - True, - WINBINDD_PAM_AUTH_KRB5_RENEW_TIME, - NULL); - gain_root_privilege(); - - if (ret) { - DEBUG(3,("krb5_ticket_refresh_handler: " - "could not re-kinit: %s\n", - error_message(ret))); - TALLOC_FREE(entry->event); - return; - } - - DEBUG(10,("krb5_ticket_refresh_handler: successful re-kinit " - "for: %s in ccache: %s\n", - entry->principal_name, entry->ccname)); - -#if defined(DEBUG_KRB5_TKT_RENEWAL) - new_start = time(NULL) + 30; -#else - /* The tkt should be refreshed at one-half the period - from now to the expiration time */ - new_start = KRB5_EVENT_REFRESH_TIME(entry->refresh_time); -#endif - goto done; - } - - set_effective_uid(entry->uid); - - ret = smb_krb5_renew_ticket(entry->ccname, - entry->principal_name, - entry->service, - &new_start); -#if defined(DEBUG_KRB5_TKT_RENEWAL) - new_start = time(NULL) + 30; -#else - new_start = KRB5_EVENT_REFRESH_TIME(new_start); -#endif - - gain_root_privilege(); - - if (ret) { - DEBUG(3,("krb5_ticket_refresh_handler: " - "could not renew tickets: %s\n", - error_message(ret))); - /* maybe we are beyond the renewing window */ - - /* avoid breaking the renewal chain: retry in - * lp_winbind_cache_time() seconds when the KDC was not - * available right now. */ - - if (ret == KRB5_KDC_UNREACH) { - new_start = time(NULL) + - MAX(30, lp_winbind_cache_time()); - goto done; - } - - return; - } - -done: - - entry->event = event_add_timed(winbind_event_context(), entry, - timeval_set(new_start, 0), - "krb5_ticket_refresh_handler", - krb5_ticket_refresh_handler, - entry); - -#endif -} - -/**************************************************************** - Do the work of regaining a ticket when coming from offline auth. -****************************************************************/ - -static void krb5_ticket_gain_handler(struct event_context *event_ctx, - struct timed_event *te, - const struct timeval *now, - void *private_data) -{ - struct WINBINDD_CCACHE_ENTRY *entry = - talloc_get_type_abort(private_data, struct WINBINDD_CCACHE_ENTRY); -#ifdef HAVE_KRB5 - int ret; - struct timeval t; - struct WINBINDD_MEMORY_CREDS *cred_ptr = entry->cred_ptr; - struct winbindd_domain *domain = NULL; -#endif - - DEBUG(10,("krb5_ticket_gain_handler called\n")); - DEBUGADD(10,("event called for: %s, %s\n", - entry->ccname, entry->username)); - - TALLOC_FREE(entry->event); - -#ifdef HAVE_KRB5 - - if (!cred_ptr || !cred_ptr->pass) { - DEBUG(10,("krb5_ticket_gain_handler: no memory creds\n")); - return; - } - - if ((domain = find_domain_from_name(entry->realm)) == NULL) { - DEBUG(0,("krb5_ticket_gain_handler: unknown domain\n")); - return; - } - - if (!domain->online) { - goto retry_later; - } - - set_effective_uid(entry->uid); - - ret = kerberos_kinit_password_ext(entry->principal_name, - cred_ptr->pass, - 0, /* hm, can we do time correction here ? */ - &entry->refresh_time, - &entry->renew_until, - entry->ccname, - False, /* no PAC required anymore */ - True, - WINBINDD_PAM_AUTH_KRB5_RENEW_TIME, - NULL); - gain_root_privilege(); - - if (ret) { - DEBUG(3,("krb5_ticket_gain_handler: " - "could not kinit: %s\n", - error_message(ret))); - goto retry_later; - } - - DEBUG(10,("krb5_ticket_gain_handler: " - "successful kinit for: %s in ccache: %s\n", - entry->principal_name, entry->ccname)); - - goto got_ticket; - - retry_later: - - t = timeval_current_ofs(MAX(30, lp_winbind_cache_time()), 0); - - entry->event = event_add_timed(winbind_event_context(), - entry, - t, - "krb5_ticket_gain_handler", - krb5_ticket_gain_handler, - entry); - - return; - - got_ticket: - -#if defined(DEBUG_KRB5_TKT_RENEWAL) - t = timeval_set(time(NULL) + 30, 0); -#else - t = timeval_set(KRB5_EVENT_REFRESH_TIME(entry->refresh_time), 0); -#endif - - entry->event = event_add_timed(winbind_event_context(), - entry, - t, - "krb5_ticket_refresh_handler", - krb5_ticket_refresh_handler, - entry); - - return; -#endif -} - -/**************************************************************** - Check if an ccache entry exists. -****************************************************************/ - -BOOL ccache_entry_exists(const char *username) -{ - struct WINBINDD_CCACHE_ENTRY *entry = get_ccache_by_username(username); - return (entry != NULL); -} - -/**************************************************************** - Ensure we're changing the correct entry. -****************************************************************/ - -BOOL ccache_entry_identical(const char *username, - uid_t uid, - const char *ccname) -{ - struct WINBINDD_CCACHE_ENTRY *entry = get_ccache_by_username(username); - - if (!entry) { - return False; - } - - if (entry->uid != uid) { - DEBUG(0,("cache_entry_identical: uid's differ: %u != %u\n", - (unsigned int)entry->uid, (unsigned int)uid)); - return False; - } - if (!strcsequal(entry->ccname, ccname)) { - DEBUG(0,("cache_entry_identical: " - "ccnames differ: (cache) %s != (client) %s\n", - entry->ccname, ccname)); - return False; - } - return True; -} - -NTSTATUS add_ccache_to_list(const char *princ_name, - const char *ccname, - const char *service, - const char *username, - const char *realm, - uid_t uid, - time_t create_time, - time_t ticket_end, - time_t renew_until, - BOOL postponed_request) -{ - struct WINBINDD_CCACHE_ENTRY *entry = NULL; - struct timeval t; - - if ((username == NULL && princ_name == NULL) || - ccname == NULL || uid < 0) { - return NT_STATUS_INVALID_PARAMETER; - } - - if (ccache_entry_count() + 1 > MAX_CCACHES) { - DEBUG(10,("add_ccache_to_list: " - "max number of ccaches reached\n")); - return NT_STATUS_NO_MORE_ENTRIES; - } - - /* Reference count old entries */ - entry = get_ccache_by_username(username); - if (entry) { - /* Check cached entries are identical. */ - if (!ccache_entry_identical(username, uid, ccname)) { - return NT_STATUS_INVALID_PARAMETER; - } - entry->ref_count++; - DEBUG(10,("add_ccache_to_list: " - "ref count on entry %s is now %d\n", - username, entry->ref_count)); - /* FIXME: in this case we still might want to have a krb5 cred - * event handler created - gd*/ - return NT_STATUS_OK; - } - - entry = TALLOC_P(NULL, struct WINBINDD_CCACHE_ENTRY); - if (!entry) { - return NT_STATUS_NO_MEMORY; - } - - ZERO_STRUCTP(entry); - - if (username) { - entry->username = talloc_strdup(entry, username); - if (!entry->username) { - goto no_mem; - } - } - if (princ_name) { - entry->principal_name = talloc_strdup(entry, princ_name); - if (!entry->principal_name) { - goto no_mem; - } - } - if (service) { - entry->service = talloc_strdup(entry, service); - if (!entry->service) { - goto no_mem; - } - } - - entry->ccname = talloc_strdup(entry, ccname); - if (!entry->ccname) { - goto no_mem; - } - - entry->realm = talloc_strdup(entry, realm); - if (!entry->realm) { - goto no_mem; - } - - entry->create_time = create_time; - entry->renew_until = renew_until; - entry->uid = uid; - entry->ref_count = 1; - - if (!lp_winbind_refresh_tickets() || renew_until <= 0) { - goto add_entry; - } - - if (postponed_request) { - t = timeval_current_ofs(MAX(30, lp_winbind_cache_time()), 0); - entry->event = event_add_timed(winbind_event_context(), - entry, - t, - "krb5_ticket_gain_handler", - krb5_ticket_gain_handler, - entry); - } else { - /* Renew at 1/2 the ticket expiration time */ -#if defined(DEBUG_KRB5_TKT_RENEWAL) - t = timeval_set(time(NULL)+30, 0); -#else - t = timeval_set(KRB5_EVENT_REFRESH_TIME(ticket_end), 0); -#endif - entry->event = event_add_timed(winbind_event_context(), - entry, - t, - "krb5_ticket_refresh_handler", - krb5_ticket_refresh_handler, - entry); - } - - if (!entry->event) { - goto no_mem; - } - - DEBUG(10,("add_ccache_to_list: added krb5_ticket handler\n")); - - add_entry: - - DLIST_ADD(ccache_list, entry); - - DEBUG(10,("add_ccache_to_list: " - "added ccache [%s] for user [%s] to the list\n", - ccname, username)); - - return NT_STATUS_OK; - - no_mem: - - TALLOC_FREE(entry); - return NT_STATUS_NO_MEMORY; -} - -/******************************************************************* - Remove a WINBINDD_CCACHE_ENTRY entry and the krb5 ccache if no longer - referenced. - *******************************************************************/ - -NTSTATUS remove_ccache(const char *username) -{ - struct WINBINDD_CCACHE_ENTRY *entry = get_ccache_by_username(username); - NTSTATUS status = NT_STATUS_OK; - #ifdef HAVE_KRB5 - krb5_error_code ret; -#endif - - if (!entry) { - return NT_STATUS_OBJECT_NAME_NOT_FOUND; - } - - if (entry->ref_count <= 0) { - DEBUG(0,("remove_ccache: logic error. " - "ref count for user %s = %d\n", - username, entry->ref_count)); - return NT_STATUS_INTERNAL_DB_CORRUPTION; - } - - entry->ref_count--; - - if (entry->ref_count > 0) { - DEBUG(10,("remove_ccache: entry %s ref count now %d\n", - username, entry->ref_count)); - return NT_STATUS_OK; - } - - /* no references any more */ - - DLIST_REMOVE(ccache_list, entry); - TALLOC_FREE(entry->event); /* unregisters events */ - -#ifdef HAVE_KRB5 - ret = ads_kdestroy(entry->ccname); - - /* we ignore the error when there has been no credential cache */ - if (ret == KRB5_FCC_NOFILE) { - ret = 0; - } else if (ret) { - DEBUG(0,("remove_ccache: " - "failed to destroy user krb5 ccache %s with: %s\n", - entry->ccname, error_message(ret))); - } else { - DEBUG(10,("remove_ccache: " - "successfully destroyed krb5 ccache %s for user %s\n", - entry->ccname, username)); - } - status = krb5_to_nt_status(ret); -#endif - - TALLOC_FREE(entry); - DEBUG(10,("remove_ccache: removed ccache for user %s\n", username)); - - return status; -} - -/******************************************************************* - In memory credentials cache code. -*******************************************************************/ - -static struct WINBINDD_MEMORY_CREDS *memory_creds_list; - -/*********************************************************** - Find an entry on the list by name. -***********************************************************/ - -struct WINBINDD_MEMORY_CREDS *find_memory_creds_by_name(const char *username) -{ - struct WINBINDD_MEMORY_CREDS *p; - - for (p = memory_creds_list; p; p = p->next) { - if (strequal(p->username, username)) { - return p; - } - } - return NULL; -} - -/*********************************************************** - Store the required creds and mlock them. -***********************************************************/ - -static NTSTATUS store_memory_creds(struct WINBINDD_MEMORY_CREDS *memcredp, - const char *pass) -{ -#if !defined(HAVE_MLOCK) - return NT_STATUS_OK; -#else - /* new_entry->nt_hash is the base pointer for the block - of memory pointed into by new_entry->lm_hash and - new_entry->pass (if we're storing plaintext). */ - - memcredp->len = NT_HASH_LEN + LM_HASH_LEN; - if (pass) { - memcredp->len += strlen(pass)+1; - } - - -#if defined(LINUX) - /* aligning the memory on on x86_64 and compiling - with gcc 4.1 using -O2 causes a segv in the - next memset() --jerry */ - memcredp->nt_hash = SMB_MALLOC_ARRAY(unsigned char, memcredp->len); -#else - /* On non-linux platforms, mlock()'d memory must be aligned */ - memcredp->nt_hash = SMB_MEMALIGN_ARRAY(unsigned char, - getpagesize(), memcredp->len); -#endif - if (!memcredp->nt_hash) { - return NT_STATUS_NO_MEMORY; - } - memset(memcredp->nt_hash, 0x0, memcredp->len); - - memcredp->lm_hash = memcredp->nt_hash + NT_HASH_LEN; - -#ifdef DEBUG_PASSWORD - DEBUG(10,("mlocking memory: %p\n", memcredp->nt_hash)); -#endif - if ((mlock(memcredp->nt_hash, memcredp->len)) == -1) { - DEBUG(0,("failed to mlock memory: %s (%d)\n", - strerror(errno), errno)); - SAFE_FREE(memcredp->nt_hash); - return map_nt_error_from_unix(errno); - } - -#ifdef DEBUG_PASSWORD - DEBUG(10,("mlocked memory: %p\n", memcredp->nt_hash)); -#endif - - /* Create and store the password hashes. */ - E_md4hash(pass, memcredp->nt_hash); - E_deshash(pass, memcredp->lm_hash); - - if (pass) { - memcredp->pass = (char *)memcredp->lm_hash + LM_HASH_LEN; - memcpy(memcredp->pass, pass, - memcredp->len - NT_HASH_LEN - LM_HASH_LEN); - } - - return NT_STATUS_OK; -#endif -} - -/*********************************************************** - Destroy existing creds. -***********************************************************/ - -static NTSTATUS delete_memory_creds(struct WINBINDD_MEMORY_CREDS *memcredp) -{ -#if !defined(HAVE_MUNLOCK) - return NT_STATUS_OK; -#else - if (munlock(memcredp->nt_hash, memcredp->len) == -1) { - DEBUG(0,("failed to munlock memory: %s (%d)\n", - strerror(errno), errno)); - return map_nt_error_from_unix(errno); - } - memset(memcredp->nt_hash, '\0', memcredp->len); - SAFE_FREE(memcredp->nt_hash); - memcredp->nt_hash = NULL; - memcredp->lm_hash = NULL; - memcredp->pass = NULL; - memcredp->len = 0; - return NT_STATUS_OK; -#endif -} - -/*********************************************************** - Replace the required creds with new ones (password change). -***********************************************************/ - -static NTSTATUS winbindd_replace_memory_creds_internal(struct WINBINDD_MEMORY_CREDS *memcredp, - const char *pass) -{ - NTSTATUS status = delete_memory_creds(memcredp); - if (!NT_STATUS_IS_OK(status)) { - return status; - } - return store_memory_creds(memcredp, pass); -} - -/************************************************************* - Store credentials in memory in a list. -*************************************************************/ - -static NTSTATUS winbindd_add_memory_creds_internal(const char *username, - uid_t uid, - const char *pass) -{ - /* Shortcut to ensure we don't store if no mlock. */ -#if !defined(HAVE_MLOCK) || !defined(HAVE_MUNLOCK) - return NT_STATUS_OK; -#else - NTSTATUS status; - struct WINBINDD_MEMORY_CREDS *memcredp = NULL; - - memcredp = find_memory_creds_by_name(username); - if (uid == (uid_t)-1) { - DEBUG(0,("winbindd_add_memory_creds_internal: " - "invalid uid for user %s.\n", username)); - return NT_STATUS_INVALID_PARAMETER; - } - - if (memcredp) { - /* Already exists. Increment the reference count and replace stored creds. */ - if (uid != memcredp->uid) { - DEBUG(0,("winbindd_add_memory_creds_internal: " - "uid %u for user %s doesn't " - "match stored uid %u. Replacing.\n", - (unsigned int)uid, username, - (unsigned int)memcredp->uid)); - memcredp->uid = uid; - } - memcredp->ref_count++; - DEBUG(10,("winbindd_add_memory_creds_internal: " - "ref count for user %s is now %d\n", - username, memcredp->ref_count)); - return winbindd_replace_memory_creds_internal(memcredp, pass); - } - - memcredp = TALLOC_ZERO_P(NULL, struct WINBINDD_MEMORY_CREDS); - if (!memcredp) { - return NT_STATUS_NO_MEMORY; - } - memcredp->username = talloc_strdup(memcredp, username); - if (!memcredp->username) { - talloc_destroy(memcredp); - return NT_STATUS_NO_MEMORY; - } - - status = store_memory_creds(memcredp, pass); - if (!NT_STATUS_IS_OK(status)) { - talloc_destroy(memcredp); - return status; - } - - memcredp->uid = uid; - memcredp->ref_count = 1; - DLIST_ADD(memory_creds_list, memcredp); - - DEBUG(10,("winbindd_add_memory_creds_internal: " - "added entry for user %s\n", username)); - - return NT_STATUS_OK; -#endif -} - -/************************************************************* - Store users credentials in memory. If we also have a - struct WINBINDD_CCACHE_ENTRY for this username with a - refresh timer, then store the plaintext of the password - and associate the new credentials with the struct WINBINDD_CCACHE_ENTRY. -*************************************************************/ - -NTSTATUS winbindd_add_memory_creds(const char *username, - uid_t uid, - const char *pass) -{ - struct WINBINDD_CCACHE_ENTRY *entry = get_ccache_by_username(username); - NTSTATUS status; - - status = winbindd_add_memory_creds_internal(username, uid, pass); - if (!NT_STATUS_IS_OK(status)) { - return status; - } - - if (entry) { - struct WINBINDD_MEMORY_CREDS *memcredp = NULL; - memcredp = find_memory_creds_by_name(username); - if (memcredp) { - entry->cred_ptr = memcredp; - } - } - - return status; -} - -/************************************************************* - Decrement the in-memory ref count - delete if zero. -*************************************************************/ - -NTSTATUS winbindd_delete_memory_creds(const char *username) -{ - struct WINBINDD_MEMORY_CREDS *memcredp = NULL; - struct WINBINDD_CCACHE_ENTRY *entry = NULL; - NTSTATUS status = NT_STATUS_OK; - - memcredp = find_memory_creds_by_name(username); - entry = get_ccache_by_username(username); - - if (!memcredp) { - DEBUG(10,("winbindd_delete_memory_creds: unknown user %s\n", - username)); - return NT_STATUS_OBJECT_NAME_NOT_FOUND; - } - - if (memcredp->ref_count <= 0) { - DEBUG(0,("winbindd_delete_memory_creds: logic error. " - "ref count for user %s = %d\n", - username, memcredp->ref_count)); - status = NT_STATUS_INTERNAL_DB_CORRUPTION; - } - - memcredp->ref_count--; - if (memcredp->ref_count <= 0) { - delete_memory_creds(memcredp); - DLIST_REMOVE(memory_creds_list, memcredp); - talloc_destroy(memcredp); - DEBUG(10,("winbindd_delete_memory_creds: " - "deleted entry for user %s\n", - username)); - } else { - DEBUG(10,("winbindd_delete_memory_creds: " - "entry for user %s ref_count now %d\n", - username, memcredp->ref_count)); - } - - if (entry) { - /* Ensure we have no dangling references to this. */ - entry->cred_ptr = NULL; - } - - return status; -} - -/*********************************************************** - Replace the required creds with new ones (password change). -***********************************************************/ - -NTSTATUS winbindd_replace_memory_creds(const char *username, - const char *pass) -{ - struct WINBINDD_MEMORY_CREDS *memcredp = NULL; - - memcredp = find_memory_creds_by_name(username); - if (!memcredp) { - DEBUG(10,("winbindd_replace_memory_creds: unknown user %s\n", - username)); - return NT_STATUS_OBJECT_NAME_NOT_FOUND; - } - - DEBUG(10,("winbindd_replace_memory_creds: replaced creds for user %s\n", - username)); - - return winbindd_replace_memory_creds_internal(memcredp, pass); -} diff --git a/source3/nsswitch/winbindd_creds.c b/source3/nsswitch/winbindd_creds.c deleted file mode 100644 index 62facb6769..0000000000 --- a/source3/nsswitch/winbindd_creds.c +++ /dev/null @@ -1,162 +0,0 @@ -/* - Unix SMB/CIFS implementation. - - Winbind daemon - cached credentials funcions - - Copyright (C) Guenther Deschner 2005 - - 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 3 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, see . -*/ - -#include "includes.h" -#include "winbindd.h" -#undef DBGC_CLASS -#define DBGC_CLASS DBGC_WINBIND - -#define MAX_CACHED_LOGINS 10 - -NTSTATUS winbindd_get_creds(struct winbindd_domain *domain, - TALLOC_CTX *mem_ctx, - const DOM_SID *sid, - NET_USER_INFO_3 **info3, - const uint8 *cached_nt_pass[NT_HASH_LEN], - const uint8 *cred_salt[NT_HASH_LEN]) -{ - NET_USER_INFO_3 *info; - NTSTATUS status; - - status = wcache_get_creds(domain, mem_ctx, sid, cached_nt_pass, cred_salt); - if (!NT_STATUS_IS_OK(status)) { - return status; - } - - info = netsamlogon_cache_get(mem_ctx, sid); - if (info == NULL) { - return NT_STATUS_OBJECT_NAME_NOT_FOUND; - } - - *info3 = info; - - return NT_STATUS_OK; -} - - -NTSTATUS winbindd_store_creds(struct winbindd_domain *domain, - TALLOC_CTX *mem_ctx, - const char *user, - const char *pass, - NET_USER_INFO_3 *info3, - const DOM_SID *user_sid) -{ - NTSTATUS status; - uchar nt_pass[NT_HASH_LEN]; - DOM_SID cred_sid; - - if (info3 != NULL) { - - DOM_SID sid; - sid_copy(&sid, &(info3->dom_sid.sid)); - sid_append_rid(&sid, info3->user_rid); - sid_copy(&cred_sid, &sid); - info3->user_flgs |= LOGON_CACHED_ACCOUNT; - - } else if (user_sid != NULL) { - - sid_copy(&cred_sid, user_sid); - - } else if (user != NULL) { - - /* do lookup ourself */ - - enum lsa_SidType type; - - if (!lookup_cached_name(mem_ctx, - domain->name, - user, - &cred_sid, - &type)) { - return NT_STATUS_NO_SUCH_USER; - } - } else { - return NT_STATUS_INVALID_PARAMETER; - } - - if (pass) { - - int count = 0; - - status = wcache_count_cached_creds(domain, &count); - if (!NT_STATUS_IS_OK(status)) { - return status; - } - - DEBUG(11,("we have %d cached creds\n", count)); - - if (count + 1 > MAX_CACHED_LOGINS) { - - DEBUG(10,("need to delete the oldest cached login\n")); - - status = wcache_remove_oldest_cached_creds(domain, &cred_sid); - if (!NT_STATUS_IS_OK(status)) { - DEBUG(10,("failed to remove oldest cached cred: %s\n", - nt_errstr(status))); - return status; - } - } - - E_md4hash(pass, nt_pass); - - dump_data_pw("nt_pass", nt_pass, NT_HASH_LEN); - - status = wcache_save_creds(domain, mem_ctx, &cred_sid, nt_pass); - if (!NT_STATUS_IS_OK(status)) { - return status; - } - } - - if (info3 != NULL && user != NULL) { - if (!netsamlogon_cache_store(user, info3)) { - return NT_STATUS_ACCESS_DENIED; - } - } - - return NT_STATUS_OK; -} - -NTSTATUS winbindd_update_creds_by_info3(struct winbindd_domain *domain, - TALLOC_CTX *mem_ctx, - const char *user, - const char *pass, - NET_USER_INFO_3 *info3) -{ - return winbindd_store_creds(domain, mem_ctx, user, pass, info3, NULL); -} - -NTSTATUS winbindd_update_creds_by_sid(struct winbindd_domain *domain, - TALLOC_CTX *mem_ctx, - const DOM_SID *sid, - const char *pass) -{ - return winbindd_store_creds(domain, mem_ctx, NULL, pass, NULL, sid); -} - -NTSTATUS winbindd_update_creds_by_name(struct winbindd_domain *domain, - TALLOC_CTX *mem_ctx, - const char *user, - const char *pass) -{ - return winbindd_store_creds(domain, mem_ctx, user, pass, NULL, NULL); -} - - diff --git a/source3/nsswitch/winbindd_dual.c b/source3/nsswitch/winbindd_dual.c deleted file mode 100644 index 67cf6abc2b..0000000000 --- a/source3/nsswitch/winbindd_dual.c +++ /dev/null @@ -1,1130 +0,0 @@ -/* - Unix SMB/CIFS implementation. - - Winbind child daemons - - Copyright (C) Andrew Tridgell 2002 - Copyright (C) Volker Lendecke 2004,2005 - - 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 3 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, see . -*/ - -/* - * We fork a child per domain to be able to act non-blocking in the main - * winbind daemon. A domain controller thousands of miles away being being - * slow replying with a 10.000 user list should not hold up netlogon calls - * that can be handled locally. - */ - -#include "includes.h" -#include "winbindd.h" - -#undef DBGC_CLASS -#define DBGC_CLASS DBGC_WINBIND - -extern BOOL override_logfile; - -/* Read some data from a client connection */ - -static void child_read_request(struct winbindd_cli_state *state) -{ - ssize_t len; - - /* Read data */ - - len = read_data(state->sock, (char *)&state->request, - sizeof(state->request)); - - if (len != sizeof(state->request)) { - DEBUG(len > 0 ? 0 : 3, ("Got invalid request length: %d\n", (int)len)); - state->finished = True; - return; - } - - if (state->request.extra_len == 0) { - state->request.extra_data.data = NULL; - return; - } - - DEBUG(10, ("Need to read %d extra bytes\n", (int)state->request.extra_len)); - - state->request.extra_data.data = - SMB_MALLOC_ARRAY(char, state->request.extra_len + 1); - - if (state->request.extra_data.data == NULL) { - DEBUG(0, ("malloc failed\n")); - state->finished = True; - return; - } - - /* Ensure null termination */ - state->request.extra_data.data[state->request.extra_len] = '\0'; - - len = read_data(state->sock, state->request.extra_data.data, - state->request.extra_len); - - if (len != state->request.extra_len) { - DEBUG(0, ("Could not read extra data\n")); - state->finished = True; - return; - } -} - -/* - * Machinery for async requests sent to children. You set up a - * winbindd_request, select a child to query, and issue a async_request - * call. When the request is completed, the callback function you specified is - * called back with the private pointer you gave to async_request. - */ - -struct winbindd_async_request { - struct winbindd_async_request *next, *prev; - TALLOC_CTX *mem_ctx; - struct winbindd_child *child; - struct winbindd_request *request; - struct winbindd_response *response; - void (*continuation)(void *private_data, BOOL success); - struct timed_event *reply_timeout_event; - pid_t child_pid; /* pid of the child we're waiting on. Used to detect - a restart of the child (child->pid != child_pid). */ - void *private_data; -}; - -static void async_main_request_sent(void *private_data, BOOL success); -static void async_request_sent(void *private_data, BOOL success); -static void async_reply_recv(void *private_data, BOOL success); -static void schedule_async_request(struct winbindd_child *child); - -void async_request(TALLOC_CTX *mem_ctx, struct winbindd_child *child, - struct winbindd_request *request, - struct winbindd_response *response, - void (*continuation)(void *private_data, BOOL success), - void *private_data) -{ - struct winbindd_async_request *state; - - SMB_ASSERT(continuation != NULL); - - state = TALLOC_P(mem_ctx, struct winbindd_async_request); - - if (state == NULL) { - DEBUG(0, ("talloc failed\n")); - continuation(private_data, False); - return; - } - - state->mem_ctx = mem_ctx; - state->child = child; - state->request = request; - state->response = response; - state->continuation = continuation; - state->private_data = private_data; - - DLIST_ADD_END(child->requests, state, struct winbindd_async_request *); - - schedule_async_request(child); - - return; -} - -static void async_main_request_sent(void *private_data, BOOL success) -{ - struct winbindd_async_request *state = - talloc_get_type_abort(private_data, struct winbindd_async_request); - - if (!success) { - DEBUG(5, ("Could not send async request\n")); - - state->response->length = sizeof(struct winbindd_response); - state->response->result = WINBINDD_ERROR; - state->continuation(state->private_data, False); - return; - } - - if (state->request->extra_len == 0) { - async_request_sent(private_data, True); - return; - } - - setup_async_write(&state->child->event, state->request->extra_data.data, - state->request->extra_len, - async_request_sent, state); -} - -/**************************************************************** - Handler triggered if the child winbindd doesn't respond within - a given timeout. -****************************************************************/ - -static void async_request_timeout_handler(struct event_context *ctx, - struct timed_event *te, - const struct timeval *now, - void *private_data) -{ - struct winbindd_async_request *state = - talloc_get_type_abort(private_data, struct winbindd_async_request); - - DEBUG(0,("async_request_timeout_handler: child pid %u is not responding. " - "Closing connection to it.\n", - state->child_pid )); - - /* Deal with the reply - set to error. */ - async_reply_recv(private_data, False); -} - -/************************************************************** - Common function called on both async send and recv fail. - Cleans up the child and schedules the next request. -**************************************************************/ - -static void async_request_fail(struct winbindd_async_request *state) -{ - DLIST_REMOVE(state->child->requests, state); - - TALLOC_FREE(state->reply_timeout_event); - - SMB_ASSERT(state->child_pid != (pid_t)0); - - /* If not already reaped, send kill signal to child. */ - if (state->child->pid == state->child_pid) { - kill(state->child_pid, SIGTERM); - - /* - * Close the socket to the child. - */ - winbind_child_died(state->child_pid); - } - - state->response->length = sizeof(struct winbindd_response); - state->response->result = WINBINDD_ERROR; - state->continuation(state->private_data, False); -} - -static void async_request_sent(void *private_data_data, BOOL success) -{ - struct winbindd_async_request *state = - talloc_get_type_abort(private_data_data, struct winbindd_async_request); - - if (!success) { - DEBUG(5, ("Could not send async request to child pid %u\n", - (unsigned int)state->child_pid )); - async_request_fail(state); - return; - } - - /* Request successfully sent to the child, setup the wait for reply */ - - setup_async_read(&state->child->event, - &state->response->result, - sizeof(state->response->result), - async_reply_recv, state); - - /* - * Set up a timeout of 300 seconds for the response. - * If we don't get it close the child socket and - * report failure. - */ - - state->reply_timeout_event = event_add_timed(winbind_event_context(), - NULL, - timeval_current_ofs(300,0), - "async_request_timeout", - async_request_timeout_handler, - state); - if (!state->reply_timeout_event) { - smb_panic("async_request_sent: failed to add timeout handler.\n"); - } -} - -static void async_reply_recv(void *private_data, BOOL success) -{ - struct winbindd_async_request *state = - talloc_get_type_abort(private_data, struct winbindd_async_request); - struct winbindd_child *child = state->child; - - TALLOC_FREE(state->reply_timeout_event); - - state->response->length = sizeof(struct winbindd_response); - - if (!success) { - DEBUG(5, ("Could not receive async reply from child pid %u\n", - (unsigned int)state->child_pid )); - - cache_cleanup_response(state->child_pid); - async_request_fail(state); - return; - } - - SMB_ASSERT(cache_retrieve_response(state->child_pid, - state->response)); - - cache_cleanup_response(state->child_pid); - - DLIST_REMOVE(child->requests, state); - - schedule_async_request(child); - - state->continuation(state->private_data, True); -} - -static BOOL fork_domain_child(struct winbindd_child *child); - -static void schedule_async_request(struct winbindd_child *child) -{ - struct winbindd_async_request *request = child->requests; - - if (request == NULL) { - return; - } - - if (child->event.flags != 0) { - return; /* Busy */ - } - - if ((child->pid == 0) && (!fork_domain_child(child))) { - /* Cancel all outstanding requests */ - - while (request != NULL) { - /* request might be free'd in the continuation */ - struct winbindd_async_request *next = request->next; - request->continuation(request->private_data, False); - request = next; - } - return; - } - - /* Now we know who we're sending to - remember the pid. */ - request->child_pid = child->pid; - - setup_async_write(&child->event, request->request, - sizeof(*request->request), - async_main_request_sent, request); - - return; -} - -struct domain_request_state { - TALLOC_CTX *mem_ctx; - struct winbindd_domain *domain; - struct winbindd_request *request; - struct winbindd_response *response; - void (*continuation)(void *private_data_data, BOOL success); - void *private_data_data; -}; - -static void domain_init_recv(void *private_data_data, BOOL success); - -void async_domain_request(TALLOC_CTX *mem_ctx, - struct winbindd_domain *domain, - struct winbindd_request *request, - struct winbindd_response *response, - void (*continuation)(void *private_data_data, BOOL success), - void *private_data_data) -{ - struct domain_request_state *state; - - if (domain->initialized) { - async_request(mem_ctx, &domain->child, request, response, - continuation, private_data_data); - return; - } - - state = TALLOC_P(mem_ctx, struct domain_request_state); - if (state == NULL) { - DEBUG(0, ("talloc failed\n")); - continuation(private_data_data, False); - return; - } - - state->mem_ctx = mem_ctx; - state->domain = domain; - state->request = request; - state->response = response; - state->continuation = continuation; - state->private_data_data = private_data_data; - - init_child_connection(domain, domain_init_recv, state); -} - -static void domain_init_recv(void *private_data_data, BOOL success) -{ - struct domain_request_state *state = - talloc_get_type_abort(private_data_data, struct domain_request_state); - - if (!success) { - DEBUG(5, ("Domain init returned an error\n")); - state->continuation(state->private_data_data, False); - return; - } - - async_request(state->mem_ctx, &state->domain->child, - state->request, state->response, - state->continuation, state->private_data_data); -} - -static void recvfrom_child(void *private_data_data, BOOL success) -{ - struct winbindd_cli_state *state = - talloc_get_type_abort(private_data_data, struct winbindd_cli_state); - enum winbindd_result result = state->response.result; - - /* This is an optimization: The child has written directly to the - * response buffer. The request itself is still in pending state, - * state that in the result code. */ - - state->response.result = WINBINDD_PENDING; - - if ((!success) || (result != WINBINDD_OK)) { - request_error(state); - return; - } - - request_ok(state); -} - -void sendto_child(struct winbindd_cli_state *state, - struct winbindd_child *child) -{ - async_request(state->mem_ctx, child, &state->request, - &state->response, recvfrom_child, state); -} - -void sendto_domain(struct winbindd_cli_state *state, - struct winbindd_domain *domain) -{ - async_domain_request(state->mem_ctx, domain, - &state->request, &state->response, - recvfrom_child, state); -} - - -struct winbindd_child_dispatch_table { - enum winbindd_cmd cmd; - enum winbindd_result (*fn)(struct winbindd_domain *domain, - struct winbindd_cli_state *state); - const char *winbindd_cmd_name; -}; - -static struct winbindd_child_dispatch_table child_dispatch_table[] = { - - { WINBINDD_LOOKUPSID, winbindd_dual_lookupsid, "LOOKUPSID" }, - { WINBINDD_LOOKUPNAME, winbindd_dual_lookupname, "LOOKUPNAME" }, - { WINBINDD_LOOKUPRIDS, winbindd_dual_lookuprids, "LOOKUPRIDS" }, - { WINBINDD_LIST_TRUSTDOM, winbindd_dual_list_trusted_domains, "LIST_TRUSTDOM" }, - { WINBINDD_INIT_CONNECTION, winbindd_dual_init_connection, "INIT_CONNECTION" }, - { WINBINDD_GETDCNAME, winbindd_dual_getdcname, "GETDCNAME" }, - { WINBINDD_DSGETDCNAME, winbindd_dual_dsgetdcname, "DSGETDCNAME" }, - { WINBINDD_SHOW_SEQUENCE, winbindd_dual_show_sequence, "SHOW_SEQUENCE" }, - { WINBINDD_PAM_AUTH, winbindd_dual_pam_auth, "PAM_AUTH" }, - { WINBINDD_PAM_AUTH_CRAP, winbindd_dual_pam_auth_crap, "AUTH_CRAP" }, - { WINBINDD_PAM_LOGOFF, winbindd_dual_pam_logoff, "PAM_LOGOFF" }, - { WINBINDD_PAM_CHNG_PSWD_AUTH_CRAP,winbindd_dual_pam_chng_pswd_auth_crap,"CHNG_PSWD_AUTH_CRAP" }, - { WINBINDD_PAM_CHAUTHTOK, winbindd_dual_pam_chauthtok, "PAM_CHAUTHTOK" }, - { WINBINDD_CHECK_MACHACC, winbindd_dual_check_machine_acct, "CHECK_MACHACC" }, - { WINBINDD_DUAL_SID2UID, winbindd_dual_sid2uid, "DUAL_SID2UID" }, - { WINBINDD_DUAL_SID2GID, winbindd_dual_sid2gid, "DUAL_SID2GID" }, -#if 0 /* DISABLED until we fix the interface in Samba 3.0.26 --jerry */ - { WINBINDD_DUAL_SIDS2XIDS, winbindd_dual_sids2xids, "DUAL_SIDS2XIDS" }, -#endif /* end DISABLED */ - { WINBINDD_DUAL_UID2SID, winbindd_dual_uid2sid, "DUAL_UID2SID" }, - { WINBINDD_DUAL_GID2SID, winbindd_dual_gid2sid, "DUAL_GID2SID" }, - { WINBINDD_DUAL_UID2NAME, winbindd_dual_uid2name, "DUAL_UID2NAME" }, - { WINBINDD_DUAL_NAME2UID, winbindd_dual_name2uid, "DUAL_NAME2UID" }, - { WINBINDD_DUAL_GID2NAME, winbindd_dual_gid2name, "DUAL_GID2NAME" }, - { WINBINDD_DUAL_NAME2GID, winbindd_dual_name2gid, "DUAL_NAME2GID" }, - { WINBINDD_DUAL_SET_MAPPING, winbindd_dual_set_mapping, "DUAL_SET_MAPPING" }, - { WINBINDD_DUAL_SET_HWM, winbindd_dual_set_hwm, "DUAL_SET_HWMS" }, - { WINBINDD_DUAL_DUMP_MAPS, winbindd_dual_dump_maps, "DUAL_DUMP_MAPS" }, - { WINBINDD_DUAL_USERINFO, winbindd_dual_userinfo, "DUAL_USERINFO" }, - { WINBINDD_ALLOCATE_UID, winbindd_dual_allocate_uid, "ALLOCATE_UID" }, - { WINBINDD_ALLOCATE_GID, winbindd_dual_allocate_gid, "ALLOCATE_GID" }, - { WINBINDD_GETUSERDOMGROUPS, winbindd_dual_getuserdomgroups, "GETUSERDOMGROUPS" }, - { WINBINDD_DUAL_GETSIDALIASES, winbindd_dual_getsidaliases, "GETSIDALIASES" }, - { WINBINDD_CCACHE_NTLMAUTH, winbindd_dual_ccache_ntlm_auth, "CCACHE_NTLM_AUTH" }, - /* End of list */ - - { WINBINDD_NUM_CMDS, NULL, "NONE" } -}; - -static void child_process_request(struct winbindd_domain *domain, - struct winbindd_cli_state *state) -{ - struct winbindd_child_dispatch_table *table; - - /* Free response data - we may be interrupted and receive another - command before being able to send this data off. */ - - state->response.result = WINBINDD_ERROR; - state->response.length = sizeof(struct winbindd_response); - - /* as all requests in the child are sync, we can use talloc_tos() */ - state->mem_ctx = talloc_tos(); - - /* Process command */ - - for (table = child_dispatch_table; table->fn; table++) { - if (state->request.cmd == table->cmd) { - DEBUG(10,("process_request: request fn %s\n", - table->winbindd_cmd_name )); - state->response.result = table->fn(domain, state); - break; - } - } - - if (!table->fn) { - DEBUG(10,("process_request: unknown request fn number %d\n", - (int)state->request.cmd )); - state->response.result = WINBINDD_ERROR; - } -} - -void setup_domain_child(struct winbindd_domain *domain, - struct winbindd_child *child, - const char *explicit_logfile) -{ - if (explicit_logfile != NULL) { - pstr_sprintf(child->logfilename, "%s/log.winbindd-%s", - dyn_LOGFILEBASE, explicit_logfile); - } else if (domain != NULL) { - pstr_sprintf(child->logfilename, "%s/log.wb-%s", - dyn_LOGFILEBASE, domain->name); - } else { - smb_panic("Internal error: domain == NULL && " - "explicit_logfile == NULL"); - } - - child->domain = domain; -} - -struct winbindd_child *children = NULL; - -void winbind_child_died(pid_t pid) -{ - struct winbindd_child *child; - - for (child = children; child != NULL; child = child->next) { - if (child->pid == pid) { - break; - } - } - - if (child == NULL) { - DEBUG(5, ("Already reaped child %u died\n", (unsigned int)pid)); - return; - } - - remove_fd_event(&child->event); - close(child->event.fd); - child->event.fd = 0; - child->event.flags = 0; - child->pid = 0; - - schedule_async_request(child); -} - -/* Ensure any negative cache entries with the netbios or realm names are removed. */ - -void winbindd_flush_negative_conn_cache(struct winbindd_domain *domain) -{ - flush_negative_conn_cache_for_domain(domain->name); - if (*domain->alt_name) { - flush_negative_conn_cache_for_domain(domain->alt_name); - } -} - -/* Set our domains as offline and forward the offline message to our children. */ - -void winbind_msg_offline(struct messaging_context *msg_ctx, - void *private_data, - uint32_t msg_type, - struct server_id server_id, - DATA_BLOB *data) -{ - struct winbindd_child *child; - struct winbindd_domain *domain; - - DEBUG(10,("winbind_msg_offline: got offline message.\n")); - - if (!lp_winbind_offline_logon()) { - DEBUG(10,("winbind_msg_offline: rejecting offline message.\n")); - return; - } - - /* Set our global state as offline. */ - if (!set_global_winbindd_state_offline()) { - DEBUG(10,("winbind_msg_offline: offline request failed.\n")); - return; - } - - /* Set all our domains as offline. */ - for (domain = domain_list(); domain; domain = domain->next) { - if (domain->internal) { - continue; - } - DEBUG(5,("winbind_msg_offline: marking %s offline.\n", domain->name)); - set_domain_offline(domain); - } - - for (child = children; child != NULL; child = child->next) { - /* Don't send message to internal childs. We've already - done so above. */ - if (!child->domain || winbindd_internal_child(child)) { - continue; - } - - /* Or internal domains (this should not be possible....) */ - if (child->domain->internal) { - continue; - } - - /* Each winbindd child should only process requests for one domain - make sure - we only set it online / offline for that domain. */ - - DEBUG(10,("winbind_msg_offline: sending message to pid %u for domain %s.\n", - (unsigned int)child->pid, domain->name )); - - messaging_send_buf(msg_ctx, pid_to_procid(child->pid), - MSG_WINBIND_OFFLINE, - (uint8 *)child->domain->name, - strlen(child->domain->name)+1); - } -} - -/* Set our domains as online and forward the online message to our children. */ - -void winbind_msg_online(struct messaging_context *msg_ctx, - void *private_data, - uint32_t msg_type, - struct server_id server_id, - DATA_BLOB *data) -{ - struct winbindd_child *child; - struct winbindd_domain *domain; - - DEBUG(10,("winbind_msg_online: got online message.\n")); - - if (!lp_winbind_offline_logon()) { - DEBUG(10,("winbind_msg_online: rejecting online message.\n")); - return; - } - - /* Set our global state as online. */ - set_global_winbindd_state_online(); - - smb_nscd_flush_user_cache(); - smb_nscd_flush_group_cache(); - - /* Set all our domains as online. */ - for (domain = domain_list(); domain; domain = domain->next) { - if (domain->internal) { - continue; - } - DEBUG(5,("winbind_msg_online: requesting %s to go online.\n", domain->name)); - - winbindd_flush_negative_conn_cache(domain); - set_domain_online_request(domain); - - /* Send an online message to the idmap child when our - primary domain comes back online */ - - if ( domain->primary ) { - struct winbindd_child *idmap = idmap_child(); - - if ( idmap->pid != 0 ) { - messaging_send_buf(msg_ctx, - pid_to_procid(idmap->pid), - MSG_WINBIND_ONLINE, - (uint8 *)domain->name, - strlen(domain->name)+1); - } - - } - } - - for (child = children; child != NULL; child = child->next) { - /* Don't send message to internal childs. */ - if (!child->domain || winbindd_internal_child(child)) { - continue; - } - - /* Or internal domains (this should not be possible....) */ - if (child->domain->internal) { - continue; - } - - /* Each winbindd child should only process requests for one domain - make sure - we only set it online / offline for that domain. */ - - DEBUG(10,("winbind_msg_online: sending message to pid %u for domain %s.\n", - (unsigned int)child->pid, child->domain->name )); - - messaging_send_buf(msg_ctx, pid_to_procid(child->pid), - MSG_WINBIND_ONLINE, - (uint8 *)child->domain->name, - strlen(child->domain->name)+1); - } -} - -/* Forward the online/offline messages to our children. */ -void winbind_msg_onlinestatus(struct messaging_context *msg_ctx, - void *private_data, - uint32_t msg_type, - struct server_id server_id, - DATA_BLOB *data) -{ - struct winbindd_child *child; - - DEBUG(10,("winbind_msg_onlinestatus: got onlinestatus message.\n")); - - for (child = children; child != NULL; child = child->next) { - if (child->domain && child->domain->primary) { - DEBUG(10,("winbind_msg_onlinestatus: " - "sending message to pid %u of primary domain.\n", - (unsigned int)child->pid)); - messaging_send_buf(msg_ctx, pid_to_procid(child->pid), - MSG_WINBIND_ONLINESTATUS, - (uint8 *)data->data, - data->length); - break; - } - } -} - -void winbind_msg_dump_event_list(struct messaging_context *msg_ctx, - void *private_data, - uint32_t msg_type, - struct server_id server_id, - DATA_BLOB *data) -{ - struct winbindd_child *child; - - DEBUG(10,("winbind_msg_dump_event_list received\n")); - - dump_event_list(winbind_event_context()); - - for (child = children; child != NULL; child = child->next) { - - DEBUG(10,("winbind_msg_dump_event_list: sending message to pid %u\n", - (unsigned int)child->pid)); - - messaging_send_buf(msg_ctx, pid_to_procid(child->pid), - MSG_DUMP_EVENT_LIST, - NULL, 0); - } - -} - -static void account_lockout_policy_handler(struct event_context *ctx, - struct timed_event *te, - const struct timeval *now, - void *private_data) -{ - struct winbindd_child *child = - (struct winbindd_child *)private_data; - TALLOC_CTX *mem_ctx = NULL; - struct winbindd_methods *methods; - SAM_UNK_INFO_12 lockout_policy; - NTSTATUS result; - - DEBUG(10,("account_lockout_policy_handler called\n")); - - TALLOC_FREE(child->lockout_policy_event); - - if ( !winbindd_can_contact_domain( child->domain ) ) { - DEBUG(10,("account_lockout_policy_handler: Removing myself since I " - "do not have an incoming trust to domain %s\n", - child->domain->name)); - - return; - } - - methods = child->domain->methods; - - mem_ctx = talloc_init("account_lockout_policy_handler ctx"); - if (!mem_ctx) { - result = NT_STATUS_NO_MEMORY; - } else { - result = methods->lockout_policy(child->domain, mem_ctx, &lockout_policy); - } - TALLOC_FREE(mem_ctx); - - if (!NT_STATUS_IS_OK(result)) { - DEBUG(10,("account_lockout_policy_handler: lockout_policy failed error %s\n", - nt_errstr(result))); - } - - child->lockout_policy_event = event_add_timed(winbind_event_context(), NULL, - timeval_current_ofs(3600, 0), - "account_lockout_policy_handler", - account_lockout_policy_handler, - child); -} - -/* Deal with a request to go offline. */ - -static void child_msg_offline(struct messaging_context *msg, - void *private_data, - uint32_t msg_type, - struct server_id server_id, - DATA_BLOB *data) -{ - struct winbindd_domain *domain; - const char *domainname = (const char *)data->data; - - if (data->data == NULL || data->length == 0) { - return; - } - - DEBUG(5,("child_msg_offline received for domain %s.\n", domainname)); - - if (!lp_winbind_offline_logon()) { - DEBUG(10,("child_msg_offline: rejecting offline message.\n")); - return; - } - - /* Mark the requested domain offline. */ - - for (domain = domain_list(); domain; domain = domain->next) { - if (domain->internal) { - continue; - } - if (strequal(domain->name, domainname)) { - DEBUG(5,("child_msg_offline: marking %s offline.\n", domain->name)); - set_domain_offline(domain); - } - } -} - -/* Deal with a request to go online. */ - -static void child_msg_online(struct messaging_context *msg, - void *private_data, - uint32_t msg_type, - struct server_id server_id, - DATA_BLOB *data) -{ - struct winbindd_domain *domain; - const char *domainname = (const char *)data->data; - - if (data->data == NULL || data->length == 0) { - return; - } - - DEBUG(5,("child_msg_online received for domain %s.\n", domainname)); - - if (!lp_winbind_offline_logon()) { - DEBUG(10,("child_msg_online: rejecting online message.\n")); - return; - } - - /* Set our global state as online. */ - set_global_winbindd_state_online(); - - /* Try and mark everything online - delete any negative cache entries - to force a reconnect now. */ - - for (domain = domain_list(); domain; domain = domain->next) { - if (domain->internal) { - continue; - } - if (strequal(domain->name, domainname)) { - DEBUG(5,("child_msg_online: requesting %s to go online.\n", domain->name)); - winbindd_flush_negative_conn_cache(domain); - set_domain_online_request(domain); - } - } -} - -static const char *collect_onlinestatus(TALLOC_CTX *mem_ctx) -{ - struct winbindd_domain *domain; - char *buf = NULL; - - if ((buf = talloc_asprintf(mem_ctx, "global:%s ", - get_global_winbindd_state_offline() ? - "Offline":"Online")) == NULL) { - return NULL; - } - - for (domain = domain_list(); domain; domain = domain->next) { - if ((buf = talloc_asprintf_append(buf, "%s:%s ", - domain->name, - domain->online ? - "Online":"Offline")) == NULL) { - return NULL; - } - } - - buf = talloc_asprintf_append(buf, "\n"); - - DEBUG(5,("collect_onlinestatus: %s", buf)); - - return buf; -} - -static void child_msg_onlinestatus(struct messaging_context *msg_ctx, - void *private_data, - uint32_t msg_type, - struct server_id server_id, - DATA_BLOB *data) -{ - TALLOC_CTX *mem_ctx; - const char *message; - struct server_id *sender; - - DEBUG(5,("winbind_msg_onlinestatus received.\n")); - - if (!data->data) { - return; - } - - sender = (struct server_id *)data->data; - - mem_ctx = talloc_init("winbind_msg_onlinestatus"); - if (mem_ctx == NULL) { - return; - } - - message = collect_onlinestatus(mem_ctx); - if (message == NULL) { - talloc_destroy(mem_ctx); - return; - } - - messaging_send_buf(msg_ctx, *sender, MSG_WINBIND_ONLINESTATUS, - (uint8 *)message, strlen(message) + 1); - - talloc_destroy(mem_ctx); -} - -static void child_msg_dump_event_list(struct messaging_context *msg, - void *private_data, - uint32_t msg_type, - struct server_id server_id, - DATA_BLOB *data) -{ - DEBUG(5,("child_msg_dump_event_list received\n")); - - dump_event_list(winbind_event_context()); -} - - -static BOOL fork_domain_child(struct winbindd_child *child) -{ - int fdpair[2]; - struct winbindd_cli_state state; - struct winbindd_domain *domain; - - if (socketpair(AF_UNIX, SOCK_STREAM, 0, fdpair) != 0) { - DEBUG(0, ("Could not open child pipe: %s\n", - strerror(errno))); - return False; - } - - ZERO_STRUCT(state); - state.pid = sys_getpid(); - - /* Stop zombies */ - CatchChild(); - - child->pid = sys_fork(); - - if (child->pid == -1) { - DEBUG(0, ("Could not fork: %s\n", strerror(errno))); - return False; - } - - if (child->pid != 0) { - /* Parent */ - close(fdpair[0]); - child->next = child->prev = NULL; - DLIST_ADD(children, child); - child->event.fd = fdpair[1]; - child->event.flags = 0; - child->requests = NULL; - add_fd_event(&child->event); - return True; - } - - /* Child */ - - state.sock = fdpair[0]; - close(fdpair[1]); - - /* tdb needs special fork handling */ - if (tdb_reopen_all(1) == -1) { - DEBUG(0,("tdb_reopen_all failed.\n")); - _exit(0); - } - - close_conns_after_fork(); - - if (!override_logfile) { - lp_set_logfile(child->logfilename); - reopen_logs(); - } - - /* - * For clustering, we need to re-init our ctdbd connection after the - * fork - */ - if (!NT_STATUS_IS_OK(messaging_reinit(winbind_messaging_context()))) - exit(1); - - /* Don't handle the same messages as our parent. */ - messaging_deregister(winbind_messaging_context(), - MSG_SMB_CONF_UPDATED, NULL); - messaging_deregister(winbind_messaging_context(), - MSG_SHUTDOWN, NULL); - messaging_deregister(winbind_messaging_context(), - MSG_WINBIND_OFFLINE, NULL); - messaging_deregister(winbind_messaging_context(), - MSG_WINBIND_ONLINE, NULL); - messaging_deregister(winbind_messaging_context(), - MSG_WINBIND_ONLINESTATUS, NULL); - messaging_deregister(winbind_messaging_context(), - MSG_DUMP_EVENT_LIST, NULL); - - /* Handle online/offline messages. */ - messaging_register(winbind_messaging_context(), NULL, - MSG_WINBIND_OFFLINE, child_msg_offline); - messaging_register(winbind_messaging_context(), NULL, - MSG_WINBIND_ONLINE, child_msg_online); - messaging_register(winbind_messaging_context(), NULL, - MSG_WINBIND_ONLINESTATUS, child_msg_onlinestatus); - messaging_register(winbind_messaging_context(), NULL, - MSG_DUMP_EVENT_LIST, child_msg_dump_event_list); - - if ( child->domain ) { - child->domain->startup = True; - child->domain->startup_time = time(NULL); - } - - /* Ensure we have no pending check_online events other - than one for this domain. */ - - for (domain = domain_list(); domain; domain = domain->next) { - if (domain != child->domain) { - TALLOC_FREE(domain->check_online_event); - } - } - - /* Ensure we're not handling an event inherited from - our parent. */ - - cancel_named_event(winbind_event_context(), - "krb5_ticket_refresh_handler"); - - /* We might be in the idmap child...*/ - if (child->domain && !(child->domain->internal) && - lp_winbind_offline_logon()) { - - set_domain_online_request(child->domain); - - child->lockout_policy_event = event_add_timed( - winbind_event_context(), NULL, timeval_zero(), - "account_lockout_policy_handler", - account_lockout_policy_handler, - child); - } - - while (1) { - - int ret; - fd_set read_fds; - struct timeval t; - struct timeval *tp; - struct timeval now; - TALLOC_CTX *frame = talloc_stackframe(); - - run_events(winbind_event_context(), 0, NULL, NULL); - - GetTimeOfDay(&now); - - if (child->domain && child->domain->startup && - (now.tv_sec > child->domain->startup_time + 30)) { - /* No longer in "startup" mode. */ - DEBUG(10,("fork_domain_child: domain %s no longer in 'startup' mode.\n", - child->domain->name )); - child->domain->startup = False; - } - - tp = get_timed_events_timeout(winbind_event_context(), &t); - if (tp) { - DEBUG(11,("select will use timeout of %u.%u seconds\n", - (unsigned int)tp->tv_sec, (unsigned int)tp->tv_usec )); - } - - /* Handle messages */ - - message_dispatch(winbind_messaging_context()); - - FD_ZERO(&read_fds); - FD_SET(state.sock, &read_fds); - - ret = sys_select(state.sock + 1, &read_fds, NULL, NULL, tp); - - if (ret == 0) { - DEBUG(11,("nothing is ready yet, continue\n")); - TALLOC_FREE(frame); - continue; - } - - if (ret == -1 && errno == EINTR) { - /* We got a signal - continue. */ - TALLOC_FREE(frame); - continue; - } - - if (ret == -1 && errno != EINTR) { - DEBUG(0,("select error occured\n")); - TALLOC_FREE(frame); - perror("select"); - return False; - } - - /* fetch a request from the main daemon */ - child_read_request(&state); - - if (state.finished) { - /* we lost contact with our parent */ - exit(0); - } - - DEBUG(4,("child daemon request %d\n", (int)state.request.cmd)); - - ZERO_STRUCT(state.response); - state.request.null_term = '\0'; - child_process_request(child->domain, &state); - - SAFE_FREE(state.request.extra_data.data); - - cache_store_response(sys_getpid(), &state.response); - - SAFE_FREE(state.response.extra_data.data); - - /* We just send the result code back, the result - * structure needs to be fetched via the - * winbindd_cache. Hmm. That needs fixing... */ - - if (write_data(state.sock, - (const char *)&state.response.result, - sizeof(state.response.result)) != - sizeof(state.response.result)) { - DEBUG(0, ("Could not write result\n")); - exit(1); - } - TALLOC_FREE(frame); - } -} diff --git a/source3/nsswitch/winbindd_group.c b/source3/nsswitch/winbindd_group.c deleted file mode 100644 index 9a4b02f734..0000000000 --- a/source3/nsswitch/winbindd_group.c +++ /dev/null @@ -1,1746 +0,0 @@ -/* - Unix SMB/CIFS implementation. - - Winbind daemon for ntdom nss module - - Copyright (C) Tim Potter 2000 - Copyright (C) Jeremy Allison 2001. - Copyright (C) Gerald (Jerry) Carter 2003. - Copyright (C) Volker Lendecke 2005 - - 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 3 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, see . -*/ - -#include "includes.h" -#include "winbindd.h" - -extern BOOL opt_nocache; - -#undef DBGC_CLASS -#define DBGC_CLASS DBGC_WINBIND - -static void add_member(const char *domain, const char *user, - char **pp_members, size_t *p_num_members) -{ - fstring name; - - fill_domain_username(name, domain, user, True); - safe_strcat(name, ",", sizeof(name)-1); - string_append(pp_members, name); - *p_num_members += 1; -} - -/********************************************************************** - Add member users resulting from sid. Expand if it is a domain group. -**********************************************************************/ - -static void add_expanded_sid(const DOM_SID *sid, char **pp_members, size_t *p_num_members) -{ - DOM_SID dom_sid; - uint32 rid; - struct winbindd_domain *domain; - size_t i; - - char *domain_name = NULL; - char *name = NULL; - enum lsa_SidType type; - - uint32 num_names; - DOM_SID *sid_mem; - char **names; - uint32 *types; - - NTSTATUS result; - - TALLOC_CTX *mem_ctx = talloc_init("add_expanded_sid"); - - if (mem_ctx == NULL) { - DEBUG(1, ("talloc_init failed\n")); - return; - } - - sid_copy(&dom_sid, sid); - sid_split_rid(&dom_sid, &rid); - - domain = find_lookup_domain_from_sid(sid); - - if (domain == NULL) { - DEBUG(3, ("Could not find domain for sid %s\n", - sid_string_static(sid))); - goto done; - } - - result = domain->methods->sid_to_name(domain, mem_ctx, sid, - &domain_name, &name, &type); - - if (!NT_STATUS_IS_OK(result)) { - DEBUG(3, ("sid_to_name failed for sid %s\n", - sid_string_static(sid))); - goto done; - } - - DEBUG(10, ("Found name %s, type %d\n", name, type)); - - if (type == SID_NAME_USER) { - add_member(domain_name, name, pp_members, p_num_members); - goto done; - } - - if (type != SID_NAME_DOM_GRP) { - DEBUG(10, ("Alias member %s neither user nor group, ignore\n", - name)); - goto done; - } - - /* Expand the domain group, this must be done via the target domain */ - - domain = find_domain_from_sid(sid); - - if (domain == NULL) { - DEBUG(3, ("Could not find domain from SID %s\n", - sid_string_static(sid))); - goto done; - } - - result = domain->methods->lookup_groupmem(domain, mem_ctx, - sid, &num_names, - &sid_mem, &names, - &types); - - if (!NT_STATUS_IS_OK(result)) { - DEBUG(10, ("Could not lookup group members for %s: %s\n", - name, nt_errstr(result))); - goto done; - } - - for (i=0; iname, names[i], pp_members, p_num_members); - } - - done: - talloc_destroy(mem_ctx); - return; -} - -static BOOL fill_passdb_alias_grmem(struct winbindd_domain *domain, - DOM_SID *group_sid, - size_t *num_gr_mem, char **gr_mem, size_t *gr_mem_len) -{ - DOM_SID *members; - size_t i, num_members; - - *num_gr_mem = 0; - *gr_mem = NULL; - *gr_mem_len = 0; - - if (!NT_STATUS_IS_OK(pdb_enum_aliasmem(group_sid, &members, - &num_members))) - return True; - - for (i=0; igr_gid = unix_gid; - - /* Group name and password */ - - safe_strcpy(gr->gr_name, full_group_name, sizeof(gr->gr_name) - 1); - safe_strcpy(gr->gr_passwd, "x", sizeof(gr->gr_passwd) - 1); - - return True; -} - -/*********************************************************************** - If "enum users" is set to false, and the group being looked - up is the Domain Users SID: S-1-5-domain-513, then for the - list of members check if the querying user is in that group, - and if so only return that user as the gr_mem array. - We can change this to a different parameter than "enum users" - if neccessaey, or parameterize the group list we do this for. -***********************************************************************/ - -static BOOL fill_grent_mem_domusers( TALLOC_CTX *mem_ctx, - struct winbindd_domain *domain, - struct winbindd_cli_state *state, - DOM_SID *group_sid, - enum lsa_SidType group_name_type, - size_t *num_gr_mem, char **gr_mem, - size_t *gr_mem_len) -{ - DOM_SID querying_user_sid; - DOM_SID *pquerying_user_sid = NULL; - uint32 num_groups = 0; - DOM_SID *user_sids = NULL; - BOOL u_in_group = False; - NTSTATUS status; - int i; - unsigned int buf_len = 0; - char *buf = NULL; - - DEBUG(10,("fill_grent_mem_domain_users: domain %s\n", - domain->name )); - - if (state) { - uid_t ret_uid = (uid_t)-1; - if (sys_getpeereid(state->sock, &ret_uid)==0) { - /* We know who's asking - look up their SID if - it's one we've mapped before. */ - status = idmap_uid_to_sid(&querying_user_sid, ret_uid); - if (NT_STATUS_IS_OK(status)) { - pquerying_user_sid = &querying_user_sid; - DEBUG(10,("fill_grent_mem_domain_users: querying uid %u -> %s\n", - (unsigned int)ret_uid, - sid_string_static(pquerying_user_sid) )); - } - } - } - - /* Only look up if it was a winbindd user in this domain. */ - if (pquerying_user_sid && - (sid_compare_domain(pquerying_user_sid, &domain->sid) == 0)) { - - DEBUG(10,("fill_grent_mem_domain_users: querying user = %s\n", - sid_string_static(pquerying_user_sid) )); - - status = domain->methods->lookup_usergroups(domain, - mem_ctx, - pquerying_user_sid, - &num_groups, - &user_sids); - if (!NT_STATUS_IS_OK(status)) { - DEBUG(1, ("fill_grent_mem_domain_users: lookup_usergroups failed " - "for sid %s in domain %s (error: %s)\n", - sid_string_static(pquerying_user_sid), - domain->name, - nt_errstr(status))); - return False; - } - - for (i = 0; i < num_groups; i++) { - if (sid_equal(group_sid, &user_sids[i])) { - /* User is in Domain Users, add their name - as the only group member. */ - u_in_group = True; - break; - } - } - } - - if (u_in_group) { - size_t len = 0; - char *domainname = NULL; - char *username = NULL; - fstring name; - enum lsa_SidType type; - - DEBUG(10,("fill_grent_mem_domain_users: sid %s in 'Domain Users' in domain %s\n", - sid_string_static(pquerying_user_sid), domain->name )); - - status = domain->methods->sid_to_name(domain, mem_ctx, - pquerying_user_sid, - &domainname, - &username, - &type); - if (!NT_STATUS_IS_OK(status)) { - DEBUG(1, ("could not lookup username for user " - "sid %s in domain %s (error: %s)\n", - sid_string_static(pquerying_user_sid), - domain->name, - nt_errstr(status))); - return False; - } - fill_domain_username(name, domain->name, username, True); - len = strlen(name); - buf_len = len + 1; - if (!(buf = (char *)SMB_MALLOC(buf_len))) { - DEBUG(1, ("out of memory\n")); - return False; - } - memcpy(buf, name, buf_len); - - DEBUG(10,("fill_grent_mem_domain_users: user %s in " - "'Domain Users' in domain %s\n", - name, domain->name )); - - /* user is the only member */ - *num_gr_mem = 1; - } - - *gr_mem = buf; - *gr_mem_len = buf_len; - - DEBUG(10, ("fill_grent_mem_domain_users: num_mem = %u, len = %u, mem = %s\n", - (unsigned int)*num_gr_mem, - (unsigned int)buf_len, *num_gr_mem ? buf : "NULL")); - - return True; -} - -/*********************************************************************** - Add names to a list. Assumes a canonical version of the string - in DOMAIN\user -***********************************************************************/ - -static int namecmp( const void *a, const void *b ) -{ - return StrCaseCmp( * (char * const *) a, * (char * const *) b); -} - -static NTSTATUS add_names_to_list( TALLOC_CTX *ctx, - char ***list, uint32 *n_list, - char **names, uint32 n_names ) -{ - char **new_list = NULL; - uint32 n_new_list = 0; - int i, j; - - if ( !names || (n_names == 0) ) - return NT_STATUS_OK; - - /* Alloc the maximum size we'll need */ - - if ( *list == NULL ) { - if ( (new_list = TALLOC_ARRAY( ctx, char *, n_names )) == NULL ) - return NT_STATUS_NO_MEMORY; - n_new_list = n_names; - } else { - new_list = TALLOC_REALLOC_ARRAY( ctx, *list, char *, - (*n_list) + n_names ); - if ( !new_list ) - return NT_STATUS_NO_MEMORY; - n_new_list = (*n_list) + n_names; - } - - /* Add all names */ - - for ( i=*n_list, j=0; imethods->lookup_groupmem(d, tmp_ctx, - &glist[i], &num_names, - &sid_mem, &names, - &name_types); - if ( !NT_STATUS_IS_OK(status) ) - goto out; - - /* Separate users and groups into two lists */ - - for ( j=0; jname))) - return False; - - DEBUG(10, ("group SID %s\n", sid_string_static(group_sid))); - - /* Initialize with no members */ - - *num_gr_mem = 0; - - /* HACK ALERT!! This whole routine does not cope with group members - * from more than one domain, ie aliases. Thus we have to work it out - * ourselves in a special routine. */ - - if (domain->internal) { - result = fill_passdb_alias_grmem(domain, group_sid, - num_gr_mem, - gr_mem, gr_mem_len); - goto done; - } - - /* Verify name type */ - - if ( !((group_name_type==SID_NAME_DOM_GRP) || - ((group_name_type==SID_NAME_ALIAS) && domain->primary)) ) - { - DEBUG(1, ("SID %s in domain %s isn't a domain group (%d)\n", - sid_string_static(group_sid), domain->name, - group_name_type)); - goto done; - } - - /* OPTIMIZATION / HACK. See comment in - fill_grent_mem_domusers() */ - - sid_peek_rid( group_sid, &group_rid ); - if (!lp_winbind_enum_users() && group_rid == DOMAIN_GROUP_RID_USERS) { - result = fill_grent_mem_domusers( mem_ctx, domain, state, - group_sid, group_name_type, - num_gr_mem, gr_mem, - gr_mem_len ); - goto done; - } - - /* Real work goes here. Create a list of group names to - expand startign with the initial one. Pass that to - expand_groups() which returns a list of more group names - to expand. Do this up to the max search depth. */ - - if ( (glist = TALLOC_ARRAY(mem_ctx, DOM_SID, 1 )) == NULL ) { - result = False; - DEBUG(0,("fill_grent_mem: talloc failure!\n")); - goto done; - } - sid_copy( &glist[0], group_sid ); - n_glist = 1; - - for ( i=0; i 0) { - buf[buf_ndx - 1] = '\0'; - } - - *gr_mem = buf; - *gr_mem_len = buf_len; - - DEBUG(10, ("num_mem = %u, len = %u, mem = %s\n", (unsigned int)*num_gr_mem, - (unsigned int)buf_len, *num_gr_mem ? buf : "NULL")); - result = True; - -done: - - talloc_destroy(mem_ctx); - - DEBUG(10, ("fill_grent_mem returning %d\n", result)); - - return result; -} - -static void winbindd_getgrsid( struct winbindd_cli_state *state, DOM_SID group_sid ); - -static void getgrnam_recv( void *private_data, BOOL success, const DOM_SID *sid, - enum lsa_SidType type ) -{ - struct winbindd_cli_state *state = (struct winbindd_cli_state*)private_data; - - if (!success) { - DEBUG(5,("getgrnam_recv: lookupname failed!\n")); - request_error(state); - return; - } - - if ( (type != SID_NAME_DOM_GRP) && (type != SID_NAME_ALIAS) ) { - DEBUG(5,("getgrnam_recv: not a group!\n")); - request_error(state); - return; - } - - winbindd_getgrsid( state, *sid ); -} - - -/* Return a group structure from a group name */ - -void winbindd_getgrnam(struct winbindd_cli_state *state) -{ - struct winbindd_domain *domain; - fstring name_domain, name_group; - char *tmp; - - /* Ensure null termination */ - state->request.data.groupname[sizeof(state->request.data.groupname)-1]='\0'; - - DEBUG(3, ("[%5lu]: getgrnam %s\n", (unsigned long)state->pid, - state->request.data.groupname)); - - /* Parse domain and groupname */ - - memset(name_group, 0, sizeof(fstring)); - - tmp = state->request.data.groupname; - - name_domain[0] = '\0'; - name_group[0] = '\0'; - - parse_domain_user(tmp, name_domain, name_group); - - /* if no domain or our local domain and no local tdb group, default to - * our local domain for aliases */ - - if ( !*name_domain || strequal(name_domain, get_global_sam_name()) ) { - fstrcpy(name_domain, get_global_sam_name()); - } - - /* Get info for the domain */ - - if ((domain = find_domain_from_name(name_domain)) == NULL) { - DEBUG(3, ("could not get domain sid for domain %s\n", - name_domain)); - request_error(state); - return; - } - /* should we deal with users for our domain? */ - - if ( lp_winbind_trusted_domains_only() && domain->primary) { - DEBUG(7,("winbindd_getgrnam: My domain -- rejecting " - "getgrnam() for %s\\%s.\n", name_domain, name_group)); - request_error(state); - return; - } - - /* Get rid and name type from name */ - - ws_name_replace( name_group, WB_REPLACE_CHAR ); - - winbindd_lookupname_async( state->mem_ctx, domain->name, name_group, - getgrnam_recv, WINBINDD_GETGRNAM, state ); -} - -struct getgrsid_state { - struct winbindd_cli_state *state; - struct winbindd_domain *domain; - char *group_name; - enum lsa_SidType group_type; - uid_t gid; - DOM_SID group_sid; -}; - -static void getgrsid_sid2gid_recv(void *private_data, BOOL success, gid_t gid) - { - struct getgrsid_state *s = - (struct getgrsid_state *)private_data; - struct winbindd_domain *domain; - size_t gr_mem_len; - size_t num_gr_mem; - char *gr_mem; - fstring dom_name, group_name; - - if (!success) { - DEBUG(5,("getgrsid_sid2gid_recv: sid2gid failed!\n")); - request_error(s->state); - return; - } - - s->gid = gid; - - if ( !parse_domain_user( s->group_name, dom_name, group_name ) ) { - DEBUG(5,("getgrsid_sid2gid_recv: parse_domain_user() failed!\n")); - request_error(s->state); - return; - } - - - /* Fill in group structure */ - - if ( (domain = find_domain_from_name_noinit(dom_name)) == NULL ) { - DEBUG(1,("Can't find domain from name (%s)\n", dom_name)); - request_error(s->state); - return; - } - - if (!fill_grent(&s->state->response.data.gr, dom_name, group_name, gid) || - !fill_grent_mem(domain, s->state, &s->group_sid, s->group_type, - &num_gr_mem, &gr_mem, &gr_mem_len)) - { - request_error(s->state); - return; - } - - s->state->response.data.gr.num_gr_mem = (uint32)num_gr_mem; - - /* Group membership lives at start of extra data */ - - s->state->response.data.gr.gr_mem_ofs = 0; - - s->state->response.length += gr_mem_len; - s->state->response.extra_data.data = gr_mem; - - request_ok(s->state); - } - -static void getgrsid_lookupsid_recv( void *private_data, BOOL success, - const char *dom_name, const char *name, - enum lsa_SidType name_type ) -{ - struct getgrsid_state *s = (struct getgrsid_state *)private_data; - - if (!success) { - DEBUG(5,("getgrsid_lookupsid_recv: lookupsid failed!\n")); - request_error(s->state); - return; - } - - /* either it's a domain group, a domain local group, or a - local group in an internal domain */ - - if ( !( (name_type==SID_NAME_DOM_GRP) || - ((name_type==SID_NAME_ALIAS) && - (s->domain->primary || s->domain->internal)) ) ) - { - DEBUG(1, ("name '%s\\%s' is not a local or domain group: %d\n", - dom_name, name, name_type)); - request_error(s->state); - return; -} - - if ( (s->group_name = talloc_asprintf( s->state->mem_ctx, - "%s\\%s", - dom_name, name )) == NULL ) -{ - DEBUG(1, ("getgrsid_lookupsid_recv: talloc_asprintf() Failed!\n")); - request_error(s->state); - return; - } - - s->group_type = name_type; - - winbindd_sid2gid_async(s->state->mem_ctx, &s->group_sid, - getgrsid_sid2gid_recv, s); - } - -static void winbindd_getgrsid( struct winbindd_cli_state *state, const DOM_SID group_sid ) - { - struct getgrsid_state *s; - - if ( (s = TALLOC_ZERO_P(state->mem_ctx, struct getgrsid_state)) == NULL ) { - DEBUG(0, ("talloc failed\n")); - request_error(state); - return; - } - - s->state = state; - - if ( (s->domain = find_domain_from_sid_noinit(&group_sid)) == NULL ) { - DEBUG(3, ("Could not find domain for sid %s\n", - sid_string_static(&group_sid))); - request_error(state); - return; - } - - sid_copy(&s->group_sid, &group_sid); - - winbindd_lookupsid_async( s->state->mem_ctx, &group_sid, - getgrsid_lookupsid_recv, s ); -} - - -static void getgrgid_recv(void *private_data, BOOL success, const char *sid) -{ - struct winbindd_cli_state *state = talloc_get_type_abort(private_data, struct winbindd_cli_state); - enum lsa_SidType name_type; - DOM_SID group_sid; - - if (success) { - DEBUG(10,("getgrgid_recv: gid %lu has sid %s\n", - (unsigned long)(state->request.data.gid), sid)); - - string_to_sid(&group_sid, sid); - winbindd_getgrsid(state, group_sid); - return; - } - - /* Ok, this might be "ours", i.e. an alias */ - if (pdb_gid_to_sid(state->request.data.gid, &group_sid) && - lookup_sid(state->mem_ctx, &group_sid, NULL, NULL, &name_type) && - (name_type == SID_NAME_ALIAS)) { - /* Hey, got an alias */ - DEBUG(10,("getgrgid_recv: we have an alias with gid %lu and sid %s\n", - (unsigned long)(state->request.data.gid), sid)); - winbindd_getgrsid(state, group_sid); - return; - } - - DEBUG(1, ("could not convert gid %lu to sid\n", - (unsigned long)state->request.data.gid)); - request_error(state); -} - -/* Return a group structure from a gid number */ -void winbindd_getgrgid(struct winbindd_cli_state *state) -{ - DEBUG(3, ("[%5lu]: getgrgid %lu\n", (unsigned long)state->pid, - (unsigned long)state->request.data.gid)); - - /* always use the async interface */ - winbindd_gid2sid_async(state->mem_ctx, state->request.data.gid, getgrgid_recv, state); -} - -/* - * set/get/endgrent functions - */ - -/* "Rewind" file pointer for group database enumeration */ - -static BOOL winbindd_setgrent_internal(struct winbindd_cli_state *state) -{ - struct winbindd_domain *domain; - - DEBUG(3, ("[%5lu]: setgrent\n", (unsigned long)state->pid)); - - /* Check user has enabled this */ - - if (!lp_winbind_enum_groups()) { - return False; - } - - /* Free old static data if it exists */ - - if (state->getgrent_state != NULL) { - free_getent_state(state->getgrent_state); - state->getgrent_state = NULL; - } - - /* Create sam pipes for each domain we know about */ - - for (domain = domain_list(); domain != NULL; domain = domain->next) { - struct getent_state *domain_state; - - /* Create a state record for this domain */ - - /* don't add our domaina if we are a PDC or if we - are a member of a Samba domain */ - - if ( lp_winbind_trusted_domains_only() && domain->primary ) - { - continue; - } - - - if ((domain_state = SMB_MALLOC_P(struct getent_state)) == NULL) { - DEBUG(1, ("winbindd_setgrent: malloc failed for domain_state!\n")); - return False; - } - - ZERO_STRUCTP(domain_state); - - fstrcpy(domain_state->domain_name, domain->name); - - /* Add to list of open domains */ - - DLIST_ADD(state->getgrent_state, domain_state); - } - - state->getgrent_initialized = True; - return True; -} - -void winbindd_setgrent(struct winbindd_cli_state *state) -{ - if (winbindd_setgrent_internal(state)) { - request_ok(state); - } else { - request_error(state); - } -} - -/* Close file pointer to ntdom group database */ - -void winbindd_endgrent(struct winbindd_cli_state *state) -{ - DEBUG(3, ("[%5lu]: endgrent\n", (unsigned long)state->pid)); - - free_getent_state(state->getgrent_state); - state->getgrent_initialized = False; - state->getgrent_state = NULL; - request_ok(state); -} - -/* Get the list of domain groups and domain aliases for a domain. We fill in - the sam_entries and num_sam_entries fields with domain group information. - The dispinfo_ndx field is incremented to the index of the next group to - fetch. Return True if some groups were returned, False otherwise. */ - -static BOOL get_sam_group_entries(struct getent_state *ent) -{ - NTSTATUS status; - uint32 num_entries; - struct acct_info *name_list = NULL; - TALLOC_CTX *mem_ctx; - BOOL result = False; - struct acct_info *sam_grp_entries = NULL; - struct winbindd_domain *domain; - - if (ent->got_sam_entries) - return False; - - if (!(mem_ctx = talloc_init("get_sam_group_entries(%s)", - ent->domain_name))) { - DEBUG(1, ("get_sam_group_entries: could not create talloc context!\n")); - return False; - } - - /* Free any existing group info */ - - SAFE_FREE(ent->sam_entries); - ent->num_sam_entries = 0; - ent->got_sam_entries = True; - - /* Enumerate domain groups */ - - num_entries = 0; - - if (!(domain = find_domain_from_name(ent->domain_name))) { - DEBUG(3, ("no such domain %s in get_sam_group_entries\n", ent->domain_name)); - goto done; - } - - /* always get the domain global groups */ - - status = domain->methods->enum_dom_groups(domain, mem_ctx, &num_entries, &sam_grp_entries); - - if (!NT_STATUS_IS_OK(status)) { - DEBUG(3, ("get_sam_group_entries: could not enumerate domain groups! Error: %s\n", nt_errstr(status))); - result = False; - goto done; - } - - /* Copy entries into return buffer */ - - if (num_entries) { - if ( !(name_list = SMB_MALLOC_ARRAY(struct acct_info, num_entries)) ) { - DEBUG(0,("get_sam_group_entries: Failed to malloc memory for %d domain groups!\n", - num_entries)); - result = False; - goto done; - } - memcpy( name_list, sam_grp_entries, num_entries * sizeof(struct acct_info) ); - } - - ent->num_sam_entries = num_entries; - - /* get the domain local groups if we are a member of a native win2k domain - and are not using LDAP to get the groups */ - - if ( ( lp_security() != SEC_ADS && domain->native_mode - && domain->primary) || domain->internal ) - { - DEBUG(4,("get_sam_group_entries: %s domain; enumerating local groups as well\n", - domain->native_mode ? "Native Mode 2k":"BUILTIN or local")); - - status = domain->methods->enum_local_groups(domain, mem_ctx, &num_entries, &sam_grp_entries); - - if ( !NT_STATUS_IS_OK(status) ) { - DEBUG(3,("get_sam_group_entries: Failed to enumerate domain local groups!\n")); - num_entries = 0; - } - else - DEBUG(4,("get_sam_group_entries: Returned %d local groups\n", num_entries)); - - /* Copy entries into return buffer */ - - if ( num_entries ) { - if ( !(name_list = SMB_REALLOC_ARRAY( name_list, struct acct_info, ent->num_sam_entries+num_entries)) ) - { - DEBUG(0,("get_sam_group_entries: Failed to realloc more memory for %d local groups!\n", - num_entries)); - result = False; - goto done; - } - - memcpy( &name_list[ent->num_sam_entries], sam_grp_entries, - num_entries * sizeof(struct acct_info) ); - } - - ent->num_sam_entries += num_entries; - } - - - /* Fill in remaining fields */ - - ent->sam_entries = name_list; - ent->sam_entry_index = 0; - - result = (ent->num_sam_entries > 0); - - done: - talloc_destroy(mem_ctx); - - return result; -} - -/* Fetch next group entry from ntdom database */ - -#define MAX_GETGRENT_GROUPS 500 - -void winbindd_getgrent(struct winbindd_cli_state *state) -{ - struct getent_state *ent; - struct winbindd_gr *group_list = NULL; - int num_groups, group_list_ndx, gr_mem_list_len = 0; - char *gr_mem_list = NULL; - - DEBUG(3, ("[%5lu]: getgrent\n", (unsigned long)state->pid)); - - /* Check user has enabled this */ - - if (!lp_winbind_enum_groups()) { - request_error(state); - return; - } - - num_groups = MIN(MAX_GETGRENT_GROUPS, state->request.data.num_entries); - - if (num_groups == 0) { - request_error(state); - return; - } - - if ((state->response.extra_data.data = SMB_MALLOC_ARRAY(struct winbindd_gr, num_groups)) == NULL) { - request_error(state); - return; - } - - memset(state->response.extra_data.data, '\0', - num_groups * sizeof(struct winbindd_gr) ); - - state->response.data.num_entries = 0; - - group_list = (struct winbindd_gr *)state->response.extra_data.data; - - if (!state->getgrent_initialized) - winbindd_setgrent_internal(state); - - if (!(ent = state->getgrent_state)) { - request_error(state); - return; - } - - /* Start sending back groups */ - - for (group_list_ndx = 0; group_list_ndx < num_groups; ) { - struct acct_info *name_list = NULL; - fstring domain_group_name; - uint32 result; - gid_t group_gid; - size_t gr_mem_len; - char *gr_mem; - DOM_SID group_sid; - struct winbindd_domain *domain; - - /* Do we need to fetch another chunk of groups? */ - - tryagain: - - DEBUG(10, ("entry_index = %d, num_entries = %d\n", - ent->sam_entry_index, ent->num_sam_entries)); - - if (ent->num_sam_entries == ent->sam_entry_index) { - - while(ent && !get_sam_group_entries(ent)) { - struct getent_state *next_ent; - - DEBUG(10, ("freeing state info for domain %s\n", ent->domain_name)); - - /* Free state information for this domain */ - - SAFE_FREE(ent->sam_entries); - - next_ent = ent->next; - DLIST_REMOVE(state->getgrent_state, ent); - - SAFE_FREE(ent); - ent = next_ent; - } - - /* No more domains */ - - if (!ent) - break; - } - - name_list = (struct acct_info *)ent->sam_entries; - - if (!(domain = - find_domain_from_name(ent->domain_name))) { - DEBUG(3, ("No such domain %s in winbindd_getgrent\n", ent->domain_name)); - result = False; - goto done; - } - - /* Lookup group info */ - - sid_copy(&group_sid, &domain->sid); - sid_append_rid(&group_sid, name_list[ent->sam_entry_index].rid); - - if (!NT_STATUS_IS_OK(idmap_sid_to_gid(&group_sid, &group_gid))) { - union unid_t id; - enum lsa_SidType type; - - DEBUG(10, ("SID %s not in idmap\n", - sid_string_static(&group_sid))); - - if (!pdb_sid_to_id(&group_sid, &id, &type)) { - DEBUG(1, ("could not look up gid for group " - "%s\n", - name_list[ent->sam_entry_index].acct_name)); - ent->sam_entry_index++; - goto tryagain; - } - - if ((type != SID_NAME_DOM_GRP) && - (type != SID_NAME_ALIAS) && - (type != SID_NAME_WKN_GRP)) { - DEBUG(1, ("Group %s is a %s, not a group\n", - sid_type_lookup(type), - name_list[ent->sam_entry_index].acct_name)); - ent->sam_entry_index++; - goto tryagain; - } - group_gid = id.gid; - } - - DEBUG(10, ("got gid %lu for group %lu\n", (unsigned long)group_gid, - (unsigned long)name_list[ent->sam_entry_index].rid)); - - /* Fill in group entry */ - - fill_domain_username(domain_group_name, ent->domain_name, - name_list[ent->sam_entry_index].acct_name, True); - - result = fill_grent(&group_list[group_list_ndx], - ent->domain_name, - name_list[ent->sam_entry_index].acct_name, - group_gid); - - /* Fill in group membership entry */ - - if (result) { - size_t num_gr_mem = 0; - DOM_SID member_sid; - group_list[group_list_ndx].num_gr_mem = 0; - gr_mem = NULL; - gr_mem_len = 0; - - /* Get group membership */ - if (state->request.cmd == WINBINDD_GETGRLST) { - result = True; - } else { - sid_copy(&member_sid, &domain->sid); - sid_append_rid(&member_sid, name_list[ent->sam_entry_index].rid); - result = fill_grent_mem( - domain, - NULL, - &member_sid, - SID_NAME_DOM_GRP, - &num_gr_mem, - &gr_mem, &gr_mem_len); - - group_list[group_list_ndx].num_gr_mem = (uint32)num_gr_mem; - } - } - - if (result) { - /* Append to group membership list */ - gr_mem_list = (char *)SMB_REALLOC( - gr_mem_list, gr_mem_list_len + gr_mem_len); - - if (!gr_mem_list && (group_list[group_list_ndx].num_gr_mem != 0)) { - DEBUG(0, ("out of memory\n")); - gr_mem_list_len = 0; - break; - } - - DEBUG(10, ("list_len = %d, mem_len = %u\n", - gr_mem_list_len, (unsigned int)gr_mem_len)); - - memcpy(&gr_mem_list[gr_mem_list_len], gr_mem, - gr_mem_len); - - SAFE_FREE(gr_mem); - - group_list[group_list_ndx].gr_mem_ofs = - gr_mem_list_len; - - gr_mem_list_len += gr_mem_len; - } - - ent->sam_entry_index++; - - /* Add group to return list */ - - if (result) { - - DEBUG(10, ("adding group num_entries = %d\n", - state->response.data.num_entries)); - - group_list_ndx++; - state->response.data.num_entries++; - - state->response.length += - sizeof(struct winbindd_gr); - - } else { - DEBUG(0, ("could not lookup domain group %s\n", - domain_group_name)); - } - } - - /* Copy the list of group memberships to the end of the extra data */ - - if (group_list_ndx == 0) - goto done; - - state->response.extra_data.data = SMB_REALLOC( - state->response.extra_data.data, - group_list_ndx * sizeof(struct winbindd_gr) + gr_mem_list_len); - - if (!state->response.extra_data.data) { - DEBUG(0, ("out of memory\n")); - group_list_ndx = 0; - SAFE_FREE(gr_mem_list); - request_error(state); - return; - } - - memcpy(&((char *)state->response.extra_data.data) - [group_list_ndx * sizeof(struct winbindd_gr)], - gr_mem_list, gr_mem_list_len); - - state->response.length += gr_mem_list_len; - - DEBUG(10, ("returning %d groups, length = %d\n", - group_list_ndx, gr_mem_list_len)); - - /* Out of domains */ - - done: - - SAFE_FREE(gr_mem_list); - - if (group_list_ndx > 0) - request_ok(state); - else - request_error(state); -} - -/* List domain groups without mapping to unix ids */ - -void winbindd_list_groups(struct winbindd_cli_state *state) -{ - uint32 total_entries = 0; - struct winbindd_domain *domain; - const char *which_domain; - char *extra_data = NULL; - unsigned int extra_data_len = 0, i; - - DEBUG(3, ("[%5lu]: list groups\n", (unsigned long)state->pid)); - - /* Ensure null termination */ - state->request.domain_name[sizeof(state->request.domain_name)-1]='\0'; - which_domain = state->request.domain_name; - - /* Enumerate over trusted domains */ - - for (domain = domain_list(); domain; domain = domain->next) { - struct getent_state groups; - - /* if we have a domain name restricting the request and this - one in the list doesn't match, then just bypass the remainder - of the loop */ - - if ( *which_domain && !strequal(which_domain, domain->name) ) - continue; - - ZERO_STRUCT(groups); - - /* Get list of sam groups */ - - fstrcpy(groups.domain_name, domain->name); - - get_sam_group_entries(&groups); - - if (groups.num_sam_entries == 0) { - /* this domain is empty or in an error state */ - continue; - } - - /* keep track the of the total number of groups seen so - far over all domains */ - total_entries += groups.num_sam_entries; - - /* Allocate some memory for extra data. Note that we limit - account names to sizeof(fstring) = 128 characters. */ - extra_data = (char *)SMB_REALLOC( - extra_data, sizeof(fstring) * total_entries); - - if (!extra_data) { - DEBUG(0,("failed to enlarge buffer!\n")); - request_error(state); - return; - } - - /* Pack group list into extra data fields */ - for (i = 0; i < groups.num_sam_entries; i++) { - char *group_name = ((struct acct_info *) - groups.sam_entries)[i].acct_name; - fstring name; - - fill_domain_username(name, domain->name, group_name, True); - /* Append to extra data */ - memcpy(&extra_data[extra_data_len], name, - strlen(name)); - extra_data_len += strlen(name); - extra_data[extra_data_len++] = ','; - } - - SAFE_FREE(groups.sam_entries); - } - - /* Assign extra_data fields in response structure */ - if (extra_data) { - extra_data[extra_data_len - 1] = '\0'; - state->response.extra_data.data = extra_data; - state->response.length += extra_data_len; - } - - /* No domains may have responded but that's still OK so don't - return an error. */ - - request_ok(state); -} - -/* Get user supplementary groups. This is much quicker than trying to - invert the groups database. We merge the groups from the gids and - other_sids info3 fields as trusted domain, universal group - memberships, and nested groups (win2k native mode only) are not - returned by the getgroups RPC call but are present in the info3. */ - -struct getgroups_state { - struct winbindd_cli_state *state; - struct winbindd_domain *domain; - char *domname; - char *username; - DOM_SID user_sid; - - const DOM_SID *token_sids; - size_t i, num_token_sids; - - gid_t *token_gids; - size_t num_token_gids; -}; - -static void getgroups_usersid_recv(void *private_data, BOOL success, - const DOM_SID *sid, enum lsa_SidType type); -static void getgroups_tokensids_recv(void *private_data, BOOL success, - DOM_SID *token_sids, size_t num_token_sids); -static void getgroups_sid2gid_recv(void *private_data, BOOL success, gid_t gid); - -void winbindd_getgroups(struct winbindd_cli_state *state) -{ - struct getgroups_state *s; - - /* Ensure null termination */ - state->request.data.username - [sizeof(state->request.data.username)-1]='\0'; - - DEBUG(3, ("[%5lu]: getgroups %s\n", (unsigned long)state->pid, - state->request.data.username)); - - /* Parse domain and username */ - - s = TALLOC_P(state->mem_ctx, struct getgroups_state); - if (s == NULL) { - DEBUG(0, ("talloc failed\n")); - request_error(state); - return; - } - - s->state = state; - - ws_name_return( state->request.data.username, WB_REPLACE_CHAR ); - - if (!parse_domain_user_talloc(state->mem_ctx, - state->request.data.username, - &s->domname, &s->username)) { - DEBUG(5, ("Could not parse domain user: %s\n", - state->request.data.username)); - - /* error out if we do not have nested group support */ - - if ( !lp_winbind_nested_groups() ) { - request_error(state); - return; - } - - s->domname = talloc_strdup( state->mem_ctx, get_global_sam_name() ); - s->username = talloc_strdup( state->mem_ctx, state->request.data.username ); - } - - /* Get info for the domain */ - - s->domain = find_domain_from_name_noinit(s->domname); - - if (s->domain == NULL) { - DEBUG(7, ("could not find domain entry for domain %s\n", - s->domname)); - request_error(state); - return; - } - - if ( s->domain->primary && lp_winbind_trusted_domains_only()) { - DEBUG(7,("winbindd_getgroups: My domain -- rejecting " - "getgroups() for %s\\%s.\n", s->domname, - s->username)); - request_error(state); - return; - } - - /* Get rid and name type from name. The following costs 1 packet */ - - winbindd_lookupname_async(state->mem_ctx, s->domname, s->username, - getgroups_usersid_recv, WINBINDD_GETGROUPS, s); -} - -static void getgroups_usersid_recv(void *private_data, BOOL success, - const DOM_SID *sid, enum lsa_SidType type) -{ - struct getgroups_state *s = - (struct getgroups_state *)private_data; - - if ((!success) || - ((type != SID_NAME_USER) && (type != SID_NAME_COMPUTER))) { - request_error(s->state); - return; - } - - sid_copy(&s->user_sid, sid); - - winbindd_gettoken_async(s->state->mem_ctx, &s->user_sid, - getgroups_tokensids_recv, s); -} - -static void getgroups_tokensids_recv(void *private_data, BOOL success, - DOM_SID *token_sids, size_t num_token_sids) -{ - struct getgroups_state *s = - (struct getgroups_state *)private_data; - - /* We need at least the user sid and the primary group in the token, - * otherwise it's an error */ - - if ((!success) || (num_token_sids < 2)) { - request_error(s->state); - return; - } - - s->token_sids = token_sids; - s->num_token_sids = num_token_sids; - s->i = 0; - - s->token_gids = NULL; - s->num_token_gids = 0; - - getgroups_sid2gid_recv(s, False, 0); -} - -static void getgroups_sid2gid_recv(void *private_data, BOOL success, gid_t gid) -{ - struct getgroups_state *s = - (struct getgroups_state *)private_data; - - if (success) { - if (!add_gid_to_array_unique(s->state->mem_ctx, gid, - &s->token_gids, - &s->num_token_gids)) { - return; - } - } - - if (s->i < s->num_token_sids) { - const DOM_SID *sid = &s->token_sids[s->i]; - s->i += 1; - - if (sid_equal(sid, &s->user_sid)) { - getgroups_sid2gid_recv(s, False, 0); - return; - } - - winbindd_sid2gid_async(s->state->mem_ctx, sid, - getgroups_sid2gid_recv, s); - return; - } - - s->state->response.data.num_entries = s->num_token_gids; - /* s->token_gids are talloced */ - s->state->response.extra_data.data = smb_xmemdup(s->token_gids, s->num_token_gids * sizeof(gid_t)); - s->state->response.length += s->num_token_gids * sizeof(gid_t); - request_ok(s->state); -} - -/* Get user supplementary sids. This is equivalent to the - winbindd_getgroups() function but it involves a SID->SIDs mapping - rather than a NAME->SID->SIDS->GIDS mapping, which means we avoid - idmap. This call is designed to be used with applications that need - to do ACL evaluation themselves. Note that the cached info3 data is - not used - - this function assumes that the SID that comes in is a user SID. If - you pass in another type of SID then you may get unpredictable - results. -*/ - -static void getusersids_recv(void *private_data, BOOL success, DOM_SID *sids, - size_t num_sids); - -void winbindd_getusersids(struct winbindd_cli_state *state) -{ - DOM_SID *user_sid; - - /* Ensure null termination */ - state->request.data.sid[sizeof(state->request.data.sid)-1]='\0'; - - user_sid = TALLOC_P(state->mem_ctx, DOM_SID); - if (user_sid == NULL) { - DEBUG(1, ("talloc failed\n")); - request_error(state); - return; - } - - if (!string_to_sid(user_sid, state->request.data.sid)) { - DEBUG(1, ("Could not get convert sid %s from string\n", - state->request.data.sid)); - request_error(state); - return; - } - - winbindd_gettoken_async(state->mem_ctx, user_sid, getusersids_recv, - state); -} - -static void getusersids_recv(void *private_data, BOOL success, DOM_SID *sids, - size_t num_sids) -{ - struct winbindd_cli_state *state = - (struct winbindd_cli_state *)private_data; - char *ret = NULL; - unsigned ofs, ret_size = 0; - size_t i; - - if (!success) { - request_error(state); - return; - } - - /* work out the response size */ - for (i = 0; i < num_sids; i++) { - const char *s = sid_string_static(&sids[i]); - ret_size += strlen(s) + 1; - } - - /* build the reply */ - ret = (char *)SMB_MALLOC(ret_size); - if (!ret) { - DEBUG(0, ("malloc failed\n")); - request_error(state); - return; - } - ofs = 0; - for (i = 0; i < num_sids; i++) { - const char *s = sid_string_static(&sids[i]); - safe_strcpy(ret + ofs, s, ret_size - ofs - 1); - ofs += strlen(ret+ofs) + 1; - } - - /* Send data back to client */ - state->response.data.num_entries = num_sids; - state->response.extra_data.data = ret; - state->response.length += ret_size; - request_ok(state); -} - -void winbindd_getuserdomgroups(struct winbindd_cli_state *state) -{ - DOM_SID user_sid; - struct winbindd_domain *domain; - - /* Ensure null termination */ - state->request.data.sid[sizeof(state->request.data.sid)-1]='\0'; - - if (!string_to_sid(&user_sid, state->request.data.sid)) { - DEBUG(1, ("Could not get convert sid %s from string\n", - state->request.data.sid)); - request_error(state); - return; - } - - /* Get info for the domain */ - if ((domain = find_domain_from_sid_noinit(&user_sid)) == NULL) { - DEBUG(0,("could not find domain entry for sid %s\n", - sid_string_static(&user_sid))); - request_error(state); - return; - } - - sendto_domain(state, domain); -} - -enum winbindd_result winbindd_dual_getuserdomgroups(struct winbindd_domain *domain, - struct winbindd_cli_state *state) -{ - DOM_SID user_sid; - NTSTATUS status; - - char *sidstring; - ssize_t len; - DOM_SID *groups; - uint32 num_groups; - - /* Ensure null termination */ - state->request.data.sid[sizeof(state->request.data.sid)-1]='\0'; - - if (!string_to_sid(&user_sid, state->request.data.sid)) { - DEBUG(1, ("Could not get convert sid %s from string\n", - state->request.data.sid)); - return WINBINDD_ERROR; - } - - status = domain->methods->lookup_usergroups(domain, state->mem_ctx, - &user_sid, &num_groups, - &groups); - if (!NT_STATUS_IS_OK(status)) - return WINBINDD_ERROR; - - if (num_groups == 0) { - state->response.data.num_entries = 0; - state->response.extra_data.data = NULL; - return WINBINDD_OK; - } - - if (!print_sidlist(state->mem_ctx, groups, num_groups, &sidstring, &len)) { - DEBUG(0, ("talloc failed\n")); - return WINBINDD_ERROR; - } - - state->response.extra_data.data = SMB_STRDUP(sidstring); - if (!state->response.extra_data.data) { - return WINBINDD_ERROR; - } - state->response.length += len+1; - state->response.data.num_entries = num_groups; - - return WINBINDD_OK; -} diff --git a/source3/nsswitch/winbindd_misc.c b/source3/nsswitch/winbindd_misc.c deleted file mode 100644 index 9c3f634534..0000000000 --- a/source3/nsswitch/winbindd_misc.c +++ /dev/null @@ -1,634 +0,0 @@ -/* - Unix SMB/CIFS implementation. - - Winbind daemon - miscellaneous other functions - - Copyright (C) Tim Potter 2000 - Copyright (C) Andrew Bartlett 2002 - - 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 3 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, see . -*/ - -#include "includes.h" -#include "winbindd.h" - -#undef DBGC_CLASS -#define DBGC_CLASS DBGC_WINBIND - -/* Check the machine account password is valid */ - -void winbindd_check_machine_acct(struct winbindd_cli_state *state) -{ - DEBUG(3, ("[%5lu]: check machine account\n", - (unsigned long)state->pid)); - - sendto_domain(state, find_our_domain()); -} - -enum winbindd_result winbindd_dual_check_machine_acct(struct winbindd_domain *domain, - struct winbindd_cli_state *state) -{ - NTSTATUS result = NT_STATUS_UNSUCCESSFUL; - int num_retries = 0; - struct winbindd_domain *contact_domain; - - DEBUG(3, ("[%5lu]: check machine account\n", (unsigned long)state->pid)); - - /* Get trust account password */ - - again: - - contact_domain = find_our_domain(); - - /* This call does a cli_nt_setup_creds() which implicitly checks - the trust account password. */ - - invalidate_cm_connection(&contact_domain->conn); - - { - struct rpc_pipe_client *netlogon_pipe; - result = cm_connect_netlogon(contact_domain, &netlogon_pipe); - } - - if (!NT_STATUS_IS_OK(result)) { - DEBUG(3, ("could not open handle to NETLOGON pipe\n")); - goto done; - } - - /* There is a race condition between fetching the trust account - password and the periodic machine password change. So it's - possible that the trust account password has been changed on us. - We are returned NT_STATUS_ACCESS_DENIED if this happens. */ - -#define MAX_RETRIES 8 - - if ((num_retries < MAX_RETRIES) && - NT_STATUS_V(result) == NT_STATUS_V(NT_STATUS_ACCESS_DENIED)) { - num_retries++; - goto again; - } - - /* Pass back result code - zero for success, other values for - specific failures. */ - - DEBUG(3, ("secret is %s\n", NT_STATUS_IS_OK(result) ? - "good" : "bad")); - - done: - state->response.data.auth.nt_status = NT_STATUS_V(result); - fstrcpy(state->response.data.auth.nt_status_string, nt_errstr(result)); - fstrcpy(state->response.data.auth.error_string, nt_errstr(result)); - state->response.data.auth.pam_error = nt_status_to_pam(result); - - DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2, ("Checking the trust account password returned %s\n", - state->response.data.auth.nt_status_string)); - - return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR; -} - -void winbindd_list_trusted_domains(struct winbindd_cli_state *state) -{ - struct winbindd_domain *d = NULL; - int extra_data_len = 0; - char *extra_data = NULL; - - DEBUG(3, ("[%5lu]: list trusted domains\n", - (unsigned long)state->pid)); - - for ( d=domain_list(); d; d=d->next ) { - if ( !extra_data ) { - extra_data = talloc_asprintf(state->mem_ctx, "%s\\%s\\%s", - d->name, - d->alt_name ? d->alt_name : d->name, - sid_string_static(&d->sid)); - } else { - extra_data = talloc_asprintf(state->mem_ctx, "%s\n%s\\%s\\%s", - extra_data, - d->name, - d->alt_name ? d->alt_name : d->name, - sid_string_static(&d->sid)); - } - } - - extra_data_len = 0; - if (extra_data != NULL) { - extra_data_len = strlen(extra_data); - } - - if (extra_data_len > 0) { - state->response.extra_data.data = SMB_STRDUP(extra_data); - state->response.length += extra_data_len+1; - } - - TALLOC_FREE( extra_data ); - - request_ok(state); -} - -enum winbindd_result winbindd_dual_list_trusted_domains(struct winbindd_domain *domain, - struct winbindd_cli_state *state) -{ - uint32 i, num_domains; - char **names, **alt_names; - DOM_SID *sids; - int extra_data_len = 0; - char *extra_data; - NTSTATUS result; - BOOL have_own_domain = False; - - DEBUG(3, ("[%5lu]: list trusted domains\n", - (unsigned long)state->pid)); - - result = domain->methods->trusted_domains(domain, state->mem_ctx, - &num_domains, &names, - &alt_names, &sids); - - if (!NT_STATUS_IS_OK(result)) { - DEBUG(3, ("winbindd_dual_list_trusted_domains: trusted_domains returned %s\n", - nt_errstr(result) )); - return WINBINDD_ERROR; - } - - extra_data = talloc_strdup(state->mem_ctx, ""); - - if (num_domains > 0) - extra_data = talloc_asprintf(state->mem_ctx, "%s\\%s\\%s", - names[0], - alt_names[0] ? alt_names[0] : names[0], - sid_string_static(&sids[0])); - - for (i=1; imem_ctx, "%s\n%s\\%s\\%s", - extra_data, - names[i], - alt_names[i] ? alt_names[i] : names[i], - sid_string_static(&sids[i])); - /* add our primary domain */ - - for (i=0; iname)) { - have_own_domain = True; - break; - } - } - - if (state->request.data.list_all_domains && !have_own_domain) { - extra_data = talloc_asprintf(state->mem_ctx, "%s\n%s\\%s\\%s", - extra_data, - domain->name, - domain->alt_name ? domain->alt_name : domain->name, - sid_string_static(&domain->sid)); - } - - /* This is a bit excessive, but the extra data sooner or later will be - talloc'ed */ - - extra_data_len = 0; - if (extra_data != NULL) { - extra_data_len = strlen(extra_data); - } - - if (extra_data_len > 0) { - state->response.extra_data.data = SMB_STRDUP(extra_data); - state->response.length += extra_data_len+1; - } - - return WINBINDD_OK; -} - -void winbindd_getdcname(struct winbindd_cli_state *state) -{ - state->request.domain_name - [sizeof(state->request.domain_name)-1] = '\0'; - - DEBUG(3, ("[%5lu]: Get DC name for %s\n", (unsigned long)state->pid, - state->request.domain_name)); - - sendto_domain(state, find_our_domain()); -} - -enum winbindd_result winbindd_dual_getdcname(struct winbindd_domain *domain, - struct winbindd_cli_state *state) -{ - fstring dcname_slash; - char *p; - struct rpc_pipe_client *netlogon_pipe; - NTSTATUS result; - WERROR werr; - unsigned int orig_timeout; - - state->request.domain_name - [sizeof(state->request.domain_name)-1] = '\0'; - - DEBUG(3, ("[%5lu]: Get DC name for %s\n", (unsigned long)state->pid, - state->request.domain_name)); - - result = cm_connect_netlogon(domain, &netlogon_pipe); - - if (!NT_STATUS_IS_OK(result)) { - DEBUG(1, ("Can't contact the NETLOGON pipe\n")); - return WINBINDD_ERROR; - } - - /* This call can take a long time - allow the server to time out. - 35 seconds should do it. */ - - orig_timeout = cli_set_timeout(netlogon_pipe->cli, 35000); - - werr = rpccli_netlogon_getanydcname(netlogon_pipe, state->mem_ctx, domain->dcname, - state->request.domain_name, - dcname_slash); - /* And restore our original timeout. */ - cli_set_timeout(netlogon_pipe->cli, orig_timeout); - - if (!W_ERROR_IS_OK(werr)) { - DEBUG(5, ("Error requesting DCname for domain %s: %s\n", - state->request.domain_name, dos_errstr(werr))); - return WINBINDD_ERROR; - } - - p = dcname_slash; - if (*p == '\\') { - p+=1; - } - if (*p == '\\') { - p+=1; - } - - fstrcpy(state->response.data.dc_name, p); - return WINBINDD_OK; -} - -static struct winbindd_child static_locator_child; - -void init_locator_child(void) -{ - setup_domain_child(NULL, &static_locator_child, "locator"); -} - -struct winbindd_child *locator_child(void) -{ - return &static_locator_child; -} - -void winbindd_dsgetdcname(struct winbindd_cli_state *state) -{ - state->request.domain_name - [sizeof(state->request.domain_name)-1] = '\0'; - - DEBUG(3, ("[%5lu]: DsGetDcName for %s\n", (unsigned long)state->pid, - state->request.domain_name)); - - sendto_child(state, locator_child()); -} - -enum winbindd_result winbindd_dual_dsgetdcname(struct winbindd_domain *domain, - struct winbindd_cli_state *state) -{ - NTSTATUS result; - struct DS_DOMAIN_CONTROLLER_INFO *info = NULL; - const char *dc = NULL; - - state->request.domain_name - [sizeof(state->request.domain_name)-1] = '\0'; - - DEBUG(3, ("[%5lu]: DsGetDcName for %s\n", (unsigned long)state->pid, - state->request.domain_name)); - - result = DsGetDcName(state->mem_ctx, NULL, state->request.domain_name, - NULL, NULL, state->request.flags, &info); - - if (!NT_STATUS_IS_OK(result)) { - return WINBINDD_ERROR; - } - - if (info->domain_controller_address) { - dc = info->domain_controller_address; - if ((dc[0] == '\\') && (dc[1] == '\\')) { - dc += 2; - } - } - - if ((!dc || !is_ipaddress(dc)) && info->domain_controller_name) { - dc = info->domain_controller_name; - } - - if (!dc || !*dc) { - return WINBINDD_ERROR; - } - - fstrcpy(state->response.data.dc_name, dc); - - return WINBINDD_OK; -} - - -struct sequence_state { - TALLOC_CTX *mem_ctx; - struct winbindd_cli_state *cli_state; - struct winbindd_domain *domain; - struct winbindd_request *request; - struct winbindd_response *response; - char *extra_data; -}; - -static void sequence_recv(void *private_data, BOOL success); - -void winbindd_show_sequence(struct winbindd_cli_state *state) -{ - struct sequence_state *seq; - - /* Ensure null termination */ - state->request.domain_name[sizeof(state->request.domain_name)-1]='\0'; - - if (strlen(state->request.domain_name) > 0) { - struct winbindd_domain *domain; - domain = find_domain_from_name_noinit( - state->request.domain_name); - if (domain == NULL) { - request_error(state); - return; - } - sendto_domain(state, domain); - return; - } - - /* Ask all domains in sequence, collect the results in sequence_recv */ - - seq = TALLOC_P(state->mem_ctx, struct sequence_state); - if (seq == NULL) { - DEBUG(0, ("talloc failed\n")); - request_error(state); - return; - } - - seq->mem_ctx = state->mem_ctx; - seq->cli_state = state; - seq->domain = domain_list(); - if (seq->domain == NULL) { - DEBUG(0, ("domain list empty\n")); - request_error(state); - return; - } - seq->request = TALLOC_ZERO_P(state->mem_ctx, - struct winbindd_request); - seq->response = TALLOC_ZERO_P(state->mem_ctx, - struct winbindd_response); - seq->extra_data = talloc_strdup(state->mem_ctx, ""); - - if ((seq->request == NULL) || (seq->response == NULL) || - (seq->extra_data == NULL)) { - DEBUG(0, ("talloc failed\n")); - request_error(state); - return; - } - - seq->request->length = sizeof(*seq->request); - seq->request->cmd = WINBINDD_SHOW_SEQUENCE; - fstrcpy(seq->request->domain_name, seq->domain->name); - - async_domain_request(state->mem_ctx, seq->domain, - seq->request, seq->response, - sequence_recv, seq); -} - -static void sequence_recv(void *private_data, BOOL success) -{ - struct sequence_state *state = - (struct sequence_state *)private_data; - uint32 seq = DOM_SEQUENCE_NONE; - - if ((success) && (state->response->result == WINBINDD_OK)) - seq = state->response->data.domain_info.sequence_number; - - if (seq == DOM_SEQUENCE_NONE) { - state->extra_data = talloc_asprintf(state->mem_ctx, - "%s%s : DISCONNECTED\n", - state->extra_data, - state->domain->name); - } else { - state->extra_data = talloc_asprintf(state->mem_ctx, - "%s%s : %d\n", - state->extra_data, - state->domain->name, seq); - } - - state->domain->sequence_number = seq; - - state->domain = state->domain->next; - - if (state->domain == NULL) { - struct winbindd_cli_state *cli_state = state->cli_state; - cli_state->response.length = - sizeof(cli_state->response) + - strlen(state->extra_data) + 1; - cli_state->response.extra_data.data = - SMB_STRDUP(state->extra_data); - request_ok(cli_state); - return; - } - - /* Ask the next domain */ - fstrcpy(state->request->domain_name, state->domain->name); - async_domain_request(state->mem_ctx, state->domain, - state->request, state->response, - sequence_recv, state); -} - -/* This is the child-only version of --sequence. It only allows for a single - * domain (ie "our" one) to be displayed. */ - -enum winbindd_result winbindd_dual_show_sequence(struct winbindd_domain *domain, - struct winbindd_cli_state *state) -{ - DEBUG(3, ("[%5lu]: show sequence\n", (unsigned long)state->pid)); - - /* Ensure null termination */ - state->request.domain_name[sizeof(state->request.domain_name)-1]='\0'; - - domain->methods->sequence_number(domain, &domain->sequence_number); - - state->response.data.domain_info.sequence_number = - domain->sequence_number; - - return WINBINDD_OK; -} - -struct domain_info_state { - struct winbindd_domain *domain; - struct winbindd_cli_state *cli_state; -}; - -static void domain_info_init_recv(void *private_data, BOOL success); - -void winbindd_domain_info(struct winbindd_cli_state *state) -{ - struct winbindd_domain *domain; - - DEBUG(3, ("[%5lu]: domain_info [%s]\n", (unsigned long)state->pid, - state->request.domain_name)); - - domain = find_domain_from_name_noinit(state->request.domain_name); - - if (domain == NULL) { - DEBUG(3, ("Did not find domain [%s]\n", - state->request.domain_name)); - request_error(state); - return; - } - - if (!domain->initialized) { - struct domain_info_state *istate; - - istate = TALLOC_P(state->mem_ctx, struct domain_info_state); - if (istate == NULL) { - DEBUG(0, ("talloc failed\n")); - request_error(state); - return; - } - - istate->cli_state = state; - istate->domain = domain; - - init_child_connection(domain, domain_info_init_recv, istate); - - return; - } - - fstrcpy(state->response.data.domain_info.name, - domain->name); - fstrcpy(state->response.data.domain_info.alt_name, - domain->alt_name); - fstrcpy(state->response.data.domain_info.sid, - sid_string_static(&domain->sid)); - - state->response.data.domain_info.native_mode = - domain->native_mode; - state->response.data.domain_info.active_directory = - domain->active_directory; - state->response.data.domain_info.primary = - domain->primary; - state->response.data.domain_info.sequence_number = - domain->sequence_number; - - request_ok(state); -} - -static void domain_info_init_recv(void *private_data, BOOL success) -{ - struct domain_info_state *istate = - (struct domain_info_state *)private_data; - struct winbindd_cli_state *state = istate->cli_state; - struct winbindd_domain *domain = istate->domain; - - DEBUG(10, ("Got back from child init: %d\n", success)); - - if ((!success) || (!domain->initialized)) { - DEBUG(5, ("Could not init child for domain %s\n", - domain->name)); - request_error(state); - return; - } - - fstrcpy(state->response.data.domain_info.name, - domain->name); - fstrcpy(state->response.data.domain_info.alt_name, - domain->alt_name); - fstrcpy(state->response.data.domain_info.sid, - sid_string_static(&domain->sid)); - - state->response.data.domain_info.native_mode = - domain->native_mode; - state->response.data.domain_info.active_directory = - domain->active_directory; - state->response.data.domain_info.primary = - domain->primary; - state->response.data.domain_info.sequence_number = - domain->sequence_number; - - request_ok(state); -} - -void winbindd_ping(struct winbindd_cli_state *state) -{ - DEBUG(3, ("[%5lu]: ping\n", (unsigned long)state->pid)); - request_ok(state); -} - -/* List various tidbits of information */ - -void winbindd_info(struct winbindd_cli_state *state) -{ - - DEBUG(3, ("[%5lu]: request misc info\n", (unsigned long)state->pid)); - - state->response.data.info.winbind_separator = *lp_winbind_separator(); - fstrcpy(state->response.data.info.samba_version, SAMBA_VERSION_STRING); - request_ok(state); -} - -/* Tell the client the current interface version */ - -void winbindd_interface_version(struct winbindd_cli_state *state) -{ - DEBUG(3, ("[%5lu]: request interface version\n", - (unsigned long)state->pid)); - - state->response.data.interface_version = WINBIND_INTERFACE_VERSION; - request_ok(state); -} - -/* What domain are we a member of? */ - -void winbindd_domain_name(struct winbindd_cli_state *state) -{ - DEBUG(3, ("[%5lu]: request domain name\n", (unsigned long)state->pid)); - - fstrcpy(state->response.data.domain_name, lp_workgroup()); - request_ok(state); -} - -/* What's my name again? */ - -void winbindd_netbios_name(struct winbindd_cli_state *state) -{ - DEBUG(3, ("[%5lu]: request netbios name\n", - (unsigned long)state->pid)); - - fstrcpy(state->response.data.netbios_name, global_myname()); - request_ok(state); -} - -/* Where can I find the privilaged pipe? */ - -void winbindd_priv_pipe_dir(struct winbindd_cli_state *state) -{ - - DEBUG(3, ("[%5lu]: request location of privileged pipe\n", - (unsigned long)state->pid)); - - state->response.extra_data.data = SMB_STRDUP(get_winbind_priv_pipe_dir()); - if (!state->response.extra_data.data) { - DEBUG(0, ("malloc failed\n")); - request_error(state); - return; - } - - /* must add one to length to copy the 0 for string termination */ - state->response.length += - strlen((char *)state->response.extra_data.data) + 1; - - request_ok(state); -} - diff --git a/source3/nsswitch/winbindd_pam.c b/source3/nsswitch/winbindd_pam.c deleted file mode 100644 index 50b24864b5..0000000000 --- a/source3/nsswitch/winbindd_pam.c +++ /dev/null @@ -1,2382 +0,0 @@ -/* - Unix SMB/CIFS implementation. - - Winbind daemon - pam auth funcions - - Copyright (C) Andrew Tridgell 2000 - Copyright (C) Tim Potter 2001 - Copyright (C) Andrew Bartlett 2001-2002 - Copyright (C) Guenther Deschner 2005 - - 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 3 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, see . -*/ - -#include "includes.h" -#include "winbindd.h" -#undef DBGC_CLASS -#define DBGC_CLASS DBGC_WINBIND - -static NTSTATUS append_info3_as_txt(TALLOC_CTX *mem_ctx, - struct winbindd_cli_state *state, - NET_USER_INFO_3 *info3) -{ - fstring str_sid; - - state->response.data.auth.info3.logon_time = - nt_time_to_unix(info3->logon_time); - state->response.data.auth.info3.logoff_time = - nt_time_to_unix(info3->logoff_time); - state->response.data.auth.info3.kickoff_time = - nt_time_to_unix(info3->kickoff_time); - state->response.data.auth.info3.pass_last_set_time = - nt_time_to_unix(info3->pass_last_set_time); - state->response.data.auth.info3.pass_can_change_time = - nt_time_to_unix(info3->pass_can_change_time); - state->response.data.auth.info3.pass_must_change_time = - nt_time_to_unix(info3->pass_must_change_time); - - state->response.data.auth.info3.logon_count = info3->logon_count; - state->response.data.auth.info3.bad_pw_count = info3->bad_pw_count; - - state->response.data.auth.info3.user_rid = info3->user_rid; - state->response.data.auth.info3.group_rid = info3->group_rid; - sid_to_string(str_sid, &(info3->dom_sid.sid)); - fstrcpy(state->response.data.auth.info3.dom_sid, str_sid); - - state->response.data.auth.info3.num_groups = info3->num_groups; - state->response.data.auth.info3.user_flgs = info3->user_flgs; - - state->response.data.auth.info3.acct_flags = info3->acct_flags; - state->response.data.auth.info3.num_other_sids = info3->num_other_sids; - - unistr2_to_ascii(state->response.data.auth.info3.user_name, - &info3->uni_user_name, -1); - unistr2_to_ascii(state->response.data.auth.info3.full_name, - &info3->uni_full_name, -1); - unistr2_to_ascii(state->response.data.auth.info3.logon_script, - &info3->uni_logon_script, -1); - unistr2_to_ascii(state->response.data.auth.info3.profile_path, - &info3->uni_profile_path, -1); - unistr2_to_ascii(state->response.data.auth.info3.home_dir, - &info3->uni_home_dir, -1); - unistr2_to_ascii(state->response.data.auth.info3.dir_drive, - &info3->uni_dir_drive, -1); - - unistr2_to_ascii(state->response.data.auth.info3.logon_srv, - &info3->uni_logon_srv, -1); - unistr2_to_ascii(state->response.data.auth.info3.logon_dom, - &info3->uni_logon_dom, -1); - - return NT_STATUS_OK; -} - -static NTSTATUS append_info3_as_ndr(TALLOC_CTX *mem_ctx, - struct winbindd_cli_state *state, - NET_USER_INFO_3 *info3) -{ - prs_struct ps; - uint32 size; - if (!prs_init(&ps, 256 /* Random, non-zero number */, mem_ctx, MARSHALL)) { - return NT_STATUS_NO_MEMORY; - } - if (!net_io_user_info3("", info3, &ps, 1, 3, False)) { - prs_mem_free(&ps); - return NT_STATUS_UNSUCCESSFUL; - } - - size = prs_data_size(&ps); - SAFE_FREE(state->response.extra_data.data); - state->response.extra_data.data = SMB_MALLOC(size); - if (!state->response.extra_data.data) { - prs_mem_free(&ps); - return NT_STATUS_NO_MEMORY; - } - memset( state->response.extra_data.data, '\0', size ); - prs_copy_all_data_out((char *)state->response.extra_data.data, &ps); - state->response.length += size; - prs_mem_free(&ps); - return NT_STATUS_OK; -} - -static NTSTATUS append_unix_username(TALLOC_CTX *mem_ctx, - struct winbindd_cli_state *state, - const NET_USER_INFO_3 *info3, - const char *name_domain, - const char *name_user) -{ - /* We've been asked to return the unix username, per - 'winbind use default domain' settings and the like */ - - fstring username_out; - const char *nt_username, *nt_domain; - - if (!(nt_domain = unistr2_tdup(mem_ctx, - &info3->uni_logon_dom))) { - /* If the server didn't give us one, just use the one - * we sent them */ - nt_domain = name_domain; - } - - if (!(nt_username = unistr2_tdup(mem_ctx, - &info3->uni_user_name))) { - /* If the server didn't give us one, just use the one - * we sent them */ - nt_username = name_user; - } - - fill_domain_username(username_out, nt_domain, nt_username, - True); - - DEBUG(5,("Setting unix username to [%s]\n", username_out)); - - SAFE_FREE(state->response.extra_data.data); - state->response.extra_data.data = SMB_STRDUP(username_out); - if (!state->response.extra_data.data) { - return NT_STATUS_NO_MEMORY; - } - state->response.length += - strlen((const char *)state->response.extra_data.data)+1; - - return NT_STATUS_OK; -} - -static NTSTATUS append_afs_token(TALLOC_CTX *mem_ctx, - struct winbindd_cli_state *state, - const NET_USER_INFO_3 *info3, - const char *name_domain, - const char *name_user) -{ - char *afsname = NULL; - char *cell; - - afsname = talloc_strdup(mem_ctx, lp_afs_username_map()); - if (afsname == NULL) { - return NT_STATUS_NO_MEMORY; - } - - afsname = talloc_string_sub(mem_ctx, - lp_afs_username_map(), - "%D", name_domain); - afsname = talloc_string_sub(mem_ctx, afsname, - "%u", name_user); - afsname = talloc_string_sub(mem_ctx, afsname, - "%U", name_user); - - { - DOM_SID user_sid; - fstring sidstr; - - sid_copy(&user_sid, &info3->dom_sid.sid); - sid_append_rid(&user_sid, info3->user_rid); - sid_to_string(sidstr, &user_sid); - afsname = talloc_string_sub(mem_ctx, afsname, - "%s", sidstr); - } - - if (afsname == NULL) { - return NT_STATUS_NO_MEMORY; - } - - strlower_m(afsname); - - DEBUG(10, ("Generating token for user %s\n", afsname)); - - cell = strchr(afsname, '@'); - - if (cell == NULL) { - return NT_STATUS_NO_MEMORY; - } - - *cell = '\0'; - cell += 1; - - /* Append an AFS token string */ - SAFE_FREE(state->response.extra_data.data); - state->response.extra_data.data = - afs_createtoken_str(afsname, cell); - - if (state->response.extra_data.data != NULL) { - state->response.length += - strlen((const char *)state->response.extra_data.data)+1; - } - - return NT_STATUS_OK; -} - -static NTSTATUS check_info3_in_group(TALLOC_CTX *mem_ctx, - NET_USER_INFO_3 *info3, - const char *group_sid) -/** - * Check whether a user belongs to a group or list of groups. - * - * @param mem_ctx talloc memory context. - * @param info3 user information, including group membership info. - * @param group_sid One or more groups , separated by commas. - * - * @return NT_STATUS_OK on success, - * NT_STATUS_LOGON_FAILURE if the user does not belong, - * or other NT_STATUS_IS_ERR(status) for other kinds of failure. - */ -{ - DOM_SID *require_membership_of_sid; - size_t num_require_membership_of_sid; - fstring req_sid; - const char *p; - DOM_SID sid; - size_t i; - struct nt_user_token *token; - NTSTATUS status; - - /* Parse the 'required group' SID */ - - if (!group_sid || !group_sid[0]) { - /* NO sid supplied, all users may access */ - return NT_STATUS_OK; - } - - if (!(token = TALLOC_ZERO_P(mem_ctx, struct nt_user_token))) { - DEBUG(0, ("talloc failed\n")); - return NT_STATUS_NO_MEMORY; - } - - num_require_membership_of_sid = 0; - require_membership_of_sid = NULL; - - p = group_sid; - - while (next_token(&p, req_sid, ",", sizeof(req_sid))) { - if (!string_to_sid(&sid, req_sid)) { - DEBUG(0, ("check_info3_in_group: could not parse %s " - "as a SID!", req_sid)); - return NT_STATUS_INVALID_PARAMETER; - } - - if (!add_sid_to_array(mem_ctx, &sid, - &require_membership_of_sid, - &num_require_membership_of_sid)) { - DEBUG(0, ("add_sid_to_array failed\n")); - return NT_STATUS_NO_MEMORY; - } - } - - status = sid_array_from_info3(mem_ctx, info3, - &token->user_sids, - &token->num_sids, - True); - if (!NT_STATUS_IS_OK(status)) { - return status; - } - - if (!NT_STATUS_IS_OK(status = add_aliases(get_global_sam_sid(), - token)) - || !NT_STATUS_IS_OK(status = add_aliases(&global_sid_Builtin, - token))) { - DEBUG(3, ("could not add aliases: %s\n", - nt_errstr(status))); - return status; - } - - debug_nt_user_token(DBGC_CLASS, 10, token); - - for (i=0; irequest.flags & WBFLAG_PAM_CONTACT_TRUSTDOM) { - domain = find_domain_from_name_noinit(domain_name); - if (domain == NULL) { - DEBUG(3, ("Authentication for domain [%s] skipped " - "as it is not a trusted domain\n", - domain_name)); - } else { - return domain; - } - } - - return find_our_domain(); -} - -static void set_auth_errors(struct winbindd_response *resp, NTSTATUS result) -{ - resp->data.auth.nt_status = NT_STATUS_V(result); - fstrcpy(resp->data.auth.nt_status_string, nt_errstr(result)); - - /* we might have given a more useful error above */ - if (*resp->data.auth.error_string == '\0') - fstrcpy(resp->data.auth.error_string, - get_friendly_nt_error_msg(result)); - resp->data.auth.pam_error = nt_status_to_pam(result); -} - -static NTSTATUS fillup_password_policy(struct winbindd_domain *domain, - struct winbindd_cli_state *state) -{ - struct winbindd_methods *methods; - NTSTATUS status = NT_STATUS_UNSUCCESSFUL; - SAM_UNK_INFO_1 password_policy; - - if ( !winbindd_can_contact_domain( domain ) ) { - DEBUG(5,("fillup_password_policy: No inbound trust to " - "contact domain %s\n", domain->name)); - return NT_STATUS_NOT_SUPPORTED; - } - - methods = domain->methods; - - status = methods->password_policy(domain, state->mem_ctx, &password_policy); - if (NT_STATUS_IS_ERR(status)) { - return status; - } - - state->response.data.auth.policy.min_length_password = - password_policy.min_length_password; - state->response.data.auth.policy.password_history = - password_policy.password_history; - state->response.data.auth.policy.password_properties = - password_policy.password_properties; - state->response.data.auth.policy.expire = - nt_time_to_unix_abs(&(password_policy.expire)); - state->response.data.auth.policy.min_passwordage = - nt_time_to_unix_abs(&(password_policy.min_passwordage)); - - return NT_STATUS_OK; -} - -static NTSTATUS get_max_bad_attempts_from_lockout_policy(struct winbindd_domain *domain, - TALLOC_CTX *mem_ctx, - uint16 *max_allowed_bad_attempts) -{ - struct winbindd_methods *methods; - NTSTATUS status = NT_STATUS_UNSUCCESSFUL; - SAM_UNK_INFO_12 lockout_policy; - - *max_allowed_bad_attempts = 0; - - methods = domain->methods; - - status = methods->lockout_policy(domain, mem_ctx, &lockout_policy); - if (NT_STATUS_IS_ERR(status)) { - return status; - } - - *max_allowed_bad_attempts = lockout_policy.bad_attempt_lockout; - - return NT_STATUS_OK; -} - -static NTSTATUS get_pwd_properties(struct winbindd_domain *domain, - TALLOC_CTX *mem_ctx, - uint32 *password_properties) -{ - struct winbindd_methods *methods; - NTSTATUS status = NT_STATUS_UNSUCCESSFUL; - SAM_UNK_INFO_1 password_policy; - - *password_properties = 0; - - methods = domain->methods; - - status = methods->password_policy(domain, mem_ctx, &password_policy); - if (NT_STATUS_IS_ERR(status)) { - return status; - } - - *password_properties = password_policy.password_properties; - - return NT_STATUS_OK; -} - -#ifdef HAVE_KRB5 - -static const char *generate_krb5_ccache(TALLOC_CTX *mem_ctx, - const char *type, - uid_t uid, - BOOL *internal_ccache) -{ - /* accept FILE and WRFILE as krb5_cc_type from the client and then - * build the full ccname string based on the user's uid here - - * Guenther*/ - - const char *gen_cc = NULL; - - *internal_ccache = True; - - if (uid == -1) { - goto memory_ccache; - } - - if (!type || type[0] == '\0') { - goto memory_ccache; - } - - if (strequal(type, "FILE")) { - gen_cc = talloc_asprintf(mem_ctx, "FILE:/tmp/krb5cc_%d", uid); - } else if (strequal(type, "WRFILE")) { - gen_cc = talloc_asprintf(mem_ctx, "WRFILE:/tmp/krb5cc_%d", uid); - } else { - DEBUG(10,("we don't allow to set a %s type ccache\n", type)); - goto memory_ccache; - } - - *internal_ccache = False; - goto done; - - memory_ccache: - gen_cc = talloc_strdup(mem_ctx, "MEMORY:winbindd_pam_ccache"); - - done: - if (gen_cc == NULL) { - DEBUG(0,("out of memory\n")); - return NULL; - } - - DEBUG(10,("using ccache: %s %s\n", gen_cc, *internal_ccache ? "(internal)":"")); - - return gen_cc; -} - -static void setup_return_cc_name(struct winbindd_cli_state *state, const char *cc) -{ - const char *type = state->request.data.auth.krb5_cc_type; - - state->response.data.auth.krb5ccname[0] = '\0'; - - if (type[0] == '\0') { - return; - } - - if (!strequal(type, "FILE") && - !strequal(type, "WRFILE")) { - DEBUG(10,("won't return krbccname for a %s type ccache\n", - type)); - return; - } - - fstrcpy(state->response.data.auth.krb5ccname, cc); -} - -#endif - -static uid_t get_uid_from_state(struct winbindd_cli_state *state) -{ - uid_t uid = -1; - - uid = state->request.data.auth.uid; - - if (uid < 0) { - DEBUG(1,("invalid uid: '%d'\n", uid)); - return -1; - } - return uid; -} - -/********************************************************************** - Authenticate a user with a clear text password using Kerberos and fill up - ccache if required - **********************************************************************/ - -static NTSTATUS winbindd_raw_kerberos_login(struct winbindd_domain *domain, - struct winbindd_cli_state *state, - NET_USER_INFO_3 **info3) -{ -#ifdef HAVE_KRB5 - NTSTATUS result = NT_STATUS_UNSUCCESSFUL; - krb5_error_code krb5_ret; - const char *cc = NULL; - const char *principal_s = NULL; - const char *service = NULL; - char *realm = NULL; - fstring name_domain, name_user; - time_t ticket_lifetime = 0; - time_t renewal_until = 0; - uid_t uid = -1; - ADS_STRUCT *ads; - time_t time_offset = 0; - BOOL internal_ccache = True; - - ZERO_STRUCTP(info3); - - *info3 = NULL; - - /* 1st step: - * prepare a krb5_cc_cache string for the user */ - - uid = get_uid_from_state(state); - if (uid == -1) { - DEBUG(0,("no valid uid\n")); - } - - cc = generate_krb5_ccache(state->mem_ctx, - state->request.data.auth.krb5_cc_type, - state->request.data.auth.uid, - &internal_ccache); - if (cc == NULL) { - return NT_STATUS_NO_MEMORY; - } - - - /* 2nd step: - * get kerberos properties */ - - if (domain->private_data) { - ads = (ADS_STRUCT *)domain->private_data; - time_offset = ads->auth.time_offset; - } - - - /* 3rd step: - * do kerberos auth and setup ccache as the user */ - - parse_domain_user(state->request.data.auth.user, name_domain, name_user); - - realm = domain->alt_name; - strupper_m(realm); - - principal_s = talloc_asprintf(state->mem_ctx, "%s@%s", name_user, realm); - if (principal_s == NULL) { - return NT_STATUS_NO_MEMORY; - } - - service = talloc_asprintf(state->mem_ctx, "%s/%s@%s", KRB5_TGS_NAME, realm, realm); - if (service == NULL) { - return NT_STATUS_NO_MEMORY; - } - - /* if this is a user ccache, we need to act as the user to let the krb5 - * library handle the chown, etc. */ - - /************************ ENTERING NON-ROOT **********************/ - - if (!internal_ccache) { - set_effective_uid(uid); - DEBUG(10,("winbindd_raw_kerberos_login: uid is %d\n", uid)); - } - - result = kerberos_return_info3_from_pac(state->mem_ctx, - principal_s, - state->request.data.auth.pass, - time_offset, - &ticket_lifetime, - &renewal_until, - cc, - True, - True, - WINBINDD_PAM_AUTH_KRB5_RENEW_TIME, - info3); - if (!internal_ccache) { - gain_root_privilege(); - } - - /************************ RETURNED TO ROOT **********************/ - - if (!NT_STATUS_IS_OK(result)) { - goto failed; - } - - DEBUG(10,("winbindd_raw_kerberos_login: winbindd validated ticket of %s\n", - principal_s)); - - /* if we had a user's ccache then return that string for the pam - * environment */ - - if (!internal_ccache) { - - setup_return_cc_name(state, cc); - - result = add_ccache_to_list(principal_s, - cc, - service, - state->request.data.auth.user, - realm, - uid, - time(NULL), - ticket_lifetime, - renewal_until, - False); - - if (!NT_STATUS_IS_OK(result)) { - DEBUG(10,("winbindd_raw_kerberos_login: failed to add ccache to list: %s\n", - nt_errstr(result))); - } - } else { - - /* need to delete the memory cred cache, it is not used anymore */ - - krb5_ret = ads_kdestroy(cc); - if (krb5_ret) { - DEBUG(3,("winbindd_raw_kerberos_login: " - "could not destroy krb5 credential cache: " - "%s\n", error_message(krb5_ret))); - } - - } - - return NT_STATUS_OK; - -failed: - - /* we could have created a new credential cache with a valid tgt in it - * but we werent able to get or verify the service ticket for this - * local host and therefor didn't get the PAC, we need to remove that - * cache entirely now */ - - krb5_ret = ads_kdestroy(cc); - if (krb5_ret) { - DEBUG(3,("winbindd_raw_kerberos_login: " - "could not destroy krb5 credential cache: " - "%s\n", error_message(krb5_ret))); - } - - if (!NT_STATUS_IS_OK(remove_ccache(state->request.data.auth.user))) { - DEBUG(3,("winbindd_raw_kerberos_login: " - "could not remove ccache for user %s\n", - state->request.data.auth.user)); - } - - return result; -#else - return NT_STATUS_NOT_SUPPORTED; -#endif /* HAVE_KRB5 */ -} - -/**************************************************************** -****************************************************************/ - -static BOOL check_request_flags(uint32_t flags) -{ - uint32_t flags_edata = WBFLAG_PAM_AFS_TOKEN | - WBFLAG_PAM_UNIX_NAME | - WBFLAG_PAM_INFO3_NDR; - - if ( ( (flags & flags_edata) == WBFLAG_PAM_AFS_TOKEN) || - ( (flags & flags_edata) == WBFLAG_PAM_INFO3_NDR) || - ( (flags & flags_edata) == WBFLAG_PAM_UNIX_NAME) || - !(flags & flags_edata) ) { - return True; - } - - DEBUG(1,("check_request_flags: invalid request flags\n")); - - return False; -} - -/**************************************************************** -****************************************************************/ - -static NTSTATUS append_data(struct winbindd_cli_state *state, - NET_USER_INFO_3 *info3, - const char *name_domain, - const char *name_user) -{ - NTSTATUS result; - uint32_t flags = state->request.flags; - - if (flags & WBFLAG_PAM_USER_SESSION_KEY) { - memcpy(state->response.data.auth.user_session_key, - info3->user_sess_key, - sizeof(state->response.data.auth.user_session_key) - /* 16 */); - } - - if (flags & WBFLAG_PAM_LMKEY) { - memcpy(state->response.data.auth.first_8_lm_hash, - info3->lm_sess_key, - sizeof(state->response.data.auth.first_8_lm_hash) - /* 8 */); - } - - if (flags & WBFLAG_PAM_INFO3_TEXT) { - result = append_info3_as_txt(state->mem_ctx, state, info3); - if (!NT_STATUS_IS_OK(result)) { - DEBUG(10,("Failed to append INFO3 (TXT): %s\n", - nt_errstr(result))); - return result; - } - } - - /* currently, anything from here on potentially overwrites extra_data. */ - - if (flags & WBFLAG_PAM_INFO3_NDR) { - result = append_info3_as_ndr(state->mem_ctx, state, info3); - if (!NT_STATUS_IS_OK(result)) { - DEBUG(10,("Failed to append INFO3 (NDR): %s\n", - nt_errstr(result))); - return result; - } - } - - if (flags & WBFLAG_PAM_UNIX_NAME) { - result = append_unix_username(state->mem_ctx, state, info3, - name_domain, name_user); - if (!NT_STATUS_IS_OK(result)) { - DEBUG(10,("Failed to append Unix Username: %s\n", - nt_errstr(result))); - return result; - } - } - - if (flags & WBFLAG_PAM_AFS_TOKEN) { - result = append_afs_token(state->mem_ctx, state, info3, - name_domain, name_user); - if (!NT_STATUS_IS_OK(result)) { - DEBUG(10,("Failed to append AFS token: %s\n", - nt_errstr(result))); - return result; - } - } - - return NT_STATUS_OK; -} - -void winbindd_pam_auth(struct winbindd_cli_state *state) -{ - struct winbindd_domain *domain; - fstring name_domain, name_user; - NTSTATUS result; - - /* Ensure null termination */ - state->request.data.auth.user - [sizeof(state->request.data.auth.user)-1]='\0'; - - /* Ensure null termination */ - state->request.data.auth.pass - [sizeof(state->request.data.auth.pass)-1]='\0'; - - DEBUG(3, ("[%5lu]: pam auth %s\n", (unsigned long)state->pid, - state->request.data.auth.user)); - - if (!check_request_flags(state->request.flags)) { - result = NT_STATUS_INVALID_PARAMETER_MIX; - goto done; - } - - /* Parse domain and username */ - - ws_name_return( state->request.data.auth.user, WB_REPLACE_CHAR ); - - if (!canonicalize_username(state->request.data.auth.user, - name_domain, name_user)) { - result = NT_STATUS_NO_SUCH_USER; - goto done; - } - - domain = find_auth_domain(state, name_domain); - - if (domain == NULL) { - result = NT_STATUS_NO_SUCH_USER; - goto done; - } - - sendto_domain(state, domain); - return; - done: - set_auth_errors(&state->response, result); - DEBUG(5, ("Plain text authentication for %s returned %s " - "(PAM: %d)\n", - state->request.data.auth.user, - state->response.data.auth.nt_status_string, - state->response.data.auth.pam_error)); - request_error(state); -} - -NTSTATUS winbindd_dual_pam_auth_cached(struct winbindd_domain *domain, - struct winbindd_cli_state *state, - NET_USER_INFO_3 **info3) -{ - NTSTATUS result = NT_STATUS_LOGON_FAILURE; - uint16 max_allowed_bad_attempts; - fstring name_domain, name_user; - DOM_SID sid; - enum lsa_SidType type; - uchar new_nt_pass[NT_HASH_LEN]; - const uint8 *cached_nt_pass; - const uint8 *cached_salt; - NET_USER_INFO_3 *my_info3; - time_t kickoff_time, must_change_time; - BOOL password_good = False; -#ifdef HAVE_KRB5 - struct winbindd_tdc_domain *tdc_domain = NULL; -#endif - - *info3 = NULL; - - ZERO_STRUCTP(info3); - - DEBUG(10,("winbindd_dual_pam_auth_cached\n")); - - /* Parse domain and username */ - - parse_domain_user(state->request.data.auth.user, name_domain, name_user); - - - if (!lookup_cached_name(state->mem_ctx, - name_domain, - name_user, - &sid, - &type)) { - DEBUG(10,("winbindd_dual_pam_auth_cached: no such user in the cache\n")); - return NT_STATUS_NO_SUCH_USER; - } - - if (type != SID_NAME_USER) { - DEBUG(10,("winbindd_dual_pam_auth_cached: not a user (%s)\n", sid_type_lookup(type))); - return NT_STATUS_LOGON_FAILURE; - } - - result = winbindd_get_creds(domain, - state->mem_ctx, - &sid, - &my_info3, - &cached_nt_pass, - &cached_salt); - if (!NT_STATUS_IS_OK(result)) { - DEBUG(10,("winbindd_dual_pam_auth_cached: failed to get creds: %s\n", nt_errstr(result))); - return result; - } - - *info3 = my_info3; - - E_md4hash(state->request.data.auth.pass, new_nt_pass); - - dump_data_pw("new_nt_pass", new_nt_pass, NT_HASH_LEN); - dump_data_pw("cached_nt_pass", cached_nt_pass, NT_HASH_LEN); - if (cached_salt) { - dump_data_pw("cached_salt", cached_salt, NT_HASH_LEN); - } - - if (cached_salt) { - /* In this case we didn't store the nt_hash itself, - but the MD5 combination of salt + nt_hash. */ - uchar salted_hash[NT_HASH_LEN]; - E_md5hash(cached_salt, new_nt_pass, salted_hash); - - password_good = (memcmp(cached_nt_pass, salted_hash, NT_HASH_LEN) == 0) ? - True : False; - } else { - /* Old cached cred - direct store of nt_hash (bad bad bad !). */ - password_good = (memcmp(cached_nt_pass, new_nt_pass, NT_HASH_LEN) == 0) ? - True : False; - } - - if (password_good) { - - /* User *DOES* know the password, update logon_time and reset - * bad_pw_count */ - - my_info3->user_flgs |= LOGON_CACHED_ACCOUNT; - - if (my_info3->acct_flags & ACB_AUTOLOCK) { - return NT_STATUS_ACCOUNT_LOCKED_OUT; - } - - if (my_info3->acct_flags & ACB_DISABLED) { - return NT_STATUS_ACCOUNT_DISABLED; - } - - if (my_info3->acct_flags & ACB_WSTRUST) { - return NT_STATUS_NOLOGON_WORKSTATION_TRUST_ACCOUNT; - } - - if (my_info3->acct_flags & ACB_SVRTRUST) { - return NT_STATUS_NOLOGON_SERVER_TRUST_ACCOUNT; - } - - if (my_info3->acct_flags & ACB_DOMTRUST) { - return NT_STATUS_NOLOGON_INTERDOMAIN_TRUST_ACCOUNT; - } - - if (!(my_info3->acct_flags & ACB_NORMAL)) { - DEBUG(0,("winbindd_dual_pam_auth_cached: whats wrong with that one?: 0x%08x\n", - my_info3->acct_flags)); - return NT_STATUS_LOGON_FAILURE; - } - - kickoff_time = nt_time_to_unix(my_info3->kickoff_time); - if (kickoff_time != 0 && time(NULL) > kickoff_time) { - return NT_STATUS_ACCOUNT_EXPIRED; - } - - must_change_time = nt_time_to_unix(my_info3->pass_must_change_time); - if (must_change_time != 0 && must_change_time < time(NULL)) { - /* we allow grace logons when the password has expired */ - my_info3->user_flgs |= LOGON_GRACE_LOGON; - /* return NT_STATUS_PASSWORD_EXPIRED; */ - goto success; - } - -#ifdef HAVE_KRB5 - if ((state->request.flags & WBFLAG_PAM_KRB5) && - ((tdc_domain = wcache_tdc_fetch_domain(state->mem_ctx, name_domain)) != NULL) && - (tdc_domain->trust_type & DS_DOMAIN_TRUST_TYPE_UPLEVEL)) { - - uid_t uid = -1; - const char *cc = NULL; - char *realm = NULL; - const char *principal_s = NULL; - const char *service = NULL; - BOOL internal_ccache = False; - - uid = get_uid_from_state(state); - if (uid == -1) { - DEBUG(0,("winbindd_dual_pam_auth_cached: invalid uid\n")); - return NT_STATUS_INVALID_PARAMETER; - } - - cc = generate_krb5_ccache(state->mem_ctx, - state->request.data.auth.krb5_cc_type, - state->request.data.auth.uid, - &internal_ccache); - if (cc == NULL) { - return NT_STATUS_NO_MEMORY; - } - - realm = domain->alt_name; - strupper_m(realm); - - principal_s = talloc_asprintf(state->mem_ctx, "%s@%s", name_user, realm); - if (principal_s == NULL) { - return NT_STATUS_NO_MEMORY; - } - - service = talloc_asprintf(state->mem_ctx, "%s/%s@%s", KRB5_TGS_NAME, realm, realm); - if (service == NULL) { - return NT_STATUS_NO_MEMORY; - } - - if (!internal_ccache) { - - setup_return_cc_name(state, cc); - - result = add_ccache_to_list(principal_s, - cc, - service, - state->request.data.auth.user, - domain->alt_name, - uid, - time(NULL), - time(NULL) + lp_winbind_cache_time(), - time(NULL) + WINBINDD_PAM_AUTH_KRB5_RENEW_TIME, - True); - - if (!NT_STATUS_IS_OK(result)) { - DEBUG(10,("winbindd_dual_pam_auth_cached: failed " - "to add ccache to list: %s\n", - nt_errstr(result))); - } - } - } -#endif /* HAVE_KRB5 */ - success: - /* FIXME: we possibly should handle logon hours as well (does xp when - * offline?) see auth/auth_sam.c:sam_account_ok for details */ - - unix_to_nt_time(&my_info3->logon_time, time(NULL)); - my_info3->bad_pw_count = 0; - - result = winbindd_update_creds_by_info3(domain, - state->mem_ctx, - state->request.data.auth.user, - state->request.data.auth.pass, - my_info3); - if (!NT_STATUS_IS_OK(result)) { - DEBUG(1,("winbindd_dual_pam_auth_cached: failed to update creds: %s\n", - nt_errstr(result))); - return result; - } - - return NT_STATUS_OK; - - } - - /* User does *NOT* know the correct password, modify info3 accordingly */ - - /* failure of this is not critical */ - result = get_max_bad_attempts_from_lockout_policy(domain, state->mem_ctx, &max_allowed_bad_attempts); - if (!NT_STATUS_IS_OK(result)) { - DEBUG(10,("winbindd_dual_pam_auth_cached: failed to get max_allowed_bad_attempts. " - "Won't be able to honour account lockout policies\n")); - } - - /* increase counter */ - my_info3->bad_pw_count++; - - if (max_allowed_bad_attempts == 0) { - goto failed; - } - - /* lockout user */ - if (my_info3->bad_pw_count >= max_allowed_bad_attempts) { - - uint32 password_properties; - - result = get_pwd_properties(domain, state->mem_ctx, &password_properties); - if (!NT_STATUS_IS_OK(result)) { - DEBUG(10,("winbindd_dual_pam_auth_cached: failed to get password properties.\n")); - } - - if ((my_info3->user_rid != DOMAIN_USER_RID_ADMIN) || - (password_properties & DOMAIN_LOCKOUT_ADMINS)) { - my_info3->acct_flags |= ACB_AUTOLOCK; - } - } - -failed: - result = winbindd_update_creds_by_info3(domain, - state->mem_ctx, - state->request.data.auth.user, - NULL, - my_info3); - - if (!NT_STATUS_IS_OK(result)) { - DEBUG(0,("winbindd_dual_pam_auth_cached: failed to update creds %s\n", - nt_errstr(result))); - } - - return NT_STATUS_LOGON_FAILURE; -} - -NTSTATUS winbindd_dual_pam_auth_kerberos(struct winbindd_domain *domain, - struct winbindd_cli_state *state, - NET_USER_INFO_3 **info3) -{ - struct winbindd_domain *contact_domain; - fstring name_domain, name_user; - NTSTATUS result; - - DEBUG(10,("winbindd_dual_pam_auth_kerberos\n")); - - /* Parse domain and username */ - - parse_domain_user(state->request.data.auth.user, name_domain, name_user); - - /* what domain should we contact? */ - - if ( IS_DC ) { - if (!(contact_domain = find_domain_from_name(name_domain))) { - DEBUG(3, ("Authentication for domain for [%s] -> [%s]\\[%s] failed as %s is not a trusted domain\n", - state->request.data.auth.user, name_domain, name_user, name_domain)); - result = NT_STATUS_NO_SUCH_USER; - goto done; - } - - } else { - if (is_myname(name_domain)) { - DEBUG(3, ("Authentication for domain %s (local domain to this server) not supported at this stage\n", name_domain)); - result = NT_STATUS_NO_SUCH_USER; - goto done; - } - - contact_domain = find_domain_from_name(name_domain); - if (contact_domain == NULL) { - DEBUG(3, ("Authentication for domain for [%s] -> [%s]\\[%s] failed as %s is not a trusted domain\n", - state->request.data.auth.user, name_domain, name_user, name_domain)); - - contact_domain = find_our_domain(); - } - } - - if (contact_domain->initialized && - contact_domain->active_directory) { - goto try_login; - } - - if (!contact_domain->initialized) { - init_dc_connection(contact_domain); - } - - if (!contact_domain->active_directory) { - DEBUG(3,("krb5 auth requested but domain is not Active Directory\n")); - return NT_STATUS_INVALID_LOGON_TYPE; - } -try_login: - result = winbindd_raw_kerberos_login(contact_domain, state, info3); -done: - return result; -} - -NTSTATUS winbindd_dual_pam_auth_samlogon(struct winbindd_domain *domain, - struct winbindd_cli_state *state, - NET_USER_INFO_3 **info3) -{ - - struct rpc_pipe_client *netlogon_pipe; - uchar chal[8]; - DATA_BLOB lm_resp; - DATA_BLOB nt_resp; - int attempts = 0; - unsigned char local_lm_response[24]; - unsigned char local_nt_response[24]; - struct winbindd_domain *contact_domain; - fstring name_domain, name_user; - BOOL retry; - NTSTATUS result; - NET_USER_INFO_3 *my_info3; - - ZERO_STRUCTP(info3); - - *info3 = NULL; - - my_info3 = TALLOC_ZERO_P(state->mem_ctx, NET_USER_INFO_3); - if (my_info3 == NULL) { - return NT_STATUS_NO_MEMORY; - } - - - DEBUG(10,("winbindd_dual_pam_auth_samlogon\n")); - - /* Parse domain and username */ - - parse_domain_user(state->request.data.auth.user, name_domain, name_user); - - /* do password magic */ - - - generate_random_buffer(chal, 8); - if (lp_client_ntlmv2_auth()) { - DATA_BLOB server_chal; - DATA_BLOB names_blob; - DATA_BLOB nt_response; - DATA_BLOB lm_response; - server_chal = data_blob_talloc(state->mem_ctx, chal, 8); - - /* note that the 'workgroup' here is a best guess - we don't know - the server's domain at this point. The 'server name' is also - dodgy... - */ - names_blob = NTLMv2_generate_names_blob(global_myname(), lp_workgroup()); - - if (!SMBNTLMv2encrypt(name_user, name_domain, - state->request.data.auth.pass, - &server_chal, - &names_blob, - &lm_response, &nt_response, NULL)) { - data_blob_free(&names_blob); - data_blob_free(&server_chal); - DEBUG(0, ("winbindd_pam_auth: SMBNTLMv2encrypt() failed!\n")); - result = NT_STATUS_NO_MEMORY; - goto done; - } - data_blob_free(&names_blob); - data_blob_free(&server_chal); - lm_resp = data_blob_talloc(state->mem_ctx, lm_response.data, - lm_response.length); - nt_resp = data_blob_talloc(state->mem_ctx, nt_response.data, - nt_response.length); - data_blob_free(&lm_response); - data_blob_free(&nt_response); - - } else { - if (lp_client_lanman_auth() - && SMBencrypt(state->request.data.auth.pass, - chal, - local_lm_response)) { - lm_resp = data_blob_talloc(state->mem_ctx, - local_lm_response, - sizeof(local_lm_response)); - } else { - lm_resp = data_blob_null; - } - SMBNTencrypt(state->request.data.auth.pass, - chal, - local_nt_response); - - nt_resp = data_blob_talloc(state->mem_ctx, - local_nt_response, - sizeof(local_nt_response)); - } - - /* what domain should we contact? */ - - if ( IS_DC ) { - if (!(contact_domain = find_domain_from_name(name_domain))) { - DEBUG(3, ("Authentication for domain for [%s] -> [%s]\\[%s] failed as %s is not a trusted domain\n", - state->request.data.auth.user, name_domain, name_user, name_domain)); - result = NT_STATUS_NO_SUCH_USER; - goto done; - } - - } else { - if (is_myname(name_domain)) { - DEBUG(3, ("Authentication for domain %s (local domain to this server) not supported at this stage\n", name_domain)); - result = NT_STATUS_NO_SUCH_USER; - goto done; - } - - contact_domain = find_our_domain(); - } - - /* check authentication loop */ - - do { - NTSTATUS (*logon_fn)(struct rpc_pipe_client - *cli, TALLOC_CTX *mem_ctx, - uint32 logon_parameters, - const char *server, - const char *username, - const char *domain, - const char *workstation, - const uint8 chal[8], - DATA_BLOB lm_response, - DATA_BLOB nt_response, - NET_USER_INFO_3 *info3); - - ZERO_STRUCTP(my_info3); - retry = False; - - result = cm_connect_netlogon(contact_domain, &netlogon_pipe); - - if (!NT_STATUS_IS_OK(result)) { - DEBUG(3, ("could not open handle to NETLOGON pipe\n")); - goto done; - } - - logon_fn = contact_domain->can_do_samlogon_ex - ? rpccli_netlogon_sam_network_logon_ex - : rpccli_netlogon_sam_network_logon; - - result = logon_fn(netlogon_pipe, - state->mem_ctx, - 0, - contact_domain->dcname, /* server name */ - name_user, /* user name */ - name_domain, /* target domain */ - global_myname(), /* workstation */ - chal, - lm_resp, - nt_resp, - my_info3); - - if ((NT_STATUS_V(result) == DCERPC_FAULT_OP_RNG_ERROR) - && contact_domain->can_do_samlogon_ex) { - DEBUG(3, ("Got a DC that can not do NetSamLogonEx, " - "retrying with NetSamLogon\n")); - contact_domain->can_do_samlogon_ex = False; - retry = True; - continue; - } - - attempts += 1; - - /* We have to try a second time as cm_connect_netlogon - might not yet have noticed that the DC has killed - our connection. */ - - if (NT_STATUS_EQUAL(result, NT_STATUS_UNSUCCESSFUL)) { - retry = True; - continue; - } - - /* if we get access denied, a possible cause was that we had - and open connection to the DC, but someone changed our - machine account password out from underneath us using 'net - rpc changetrustpw' */ - - if ( NT_STATUS_EQUAL(result, NT_STATUS_ACCESS_DENIED) ) { - DEBUG(3,("winbindd_pam_auth: sam_logon returned " - "ACCESS_DENIED. Maybe the trust account " - "password was changed and we didn't know it. " - "Killing connections to domain %s\n", - name_domain)); - invalidate_cm_connection(&contact_domain->conn); - retry = True; - } - - } while ( (attempts < 2) && retry ); - - /* handle the case where a NT4 DC does not fill in the acct_flags in - * the samlogon reply info3. When accurate info3 is required by the - * caller, we look up the account flags ourselve - gd */ - - if ((state->request.flags & WBFLAG_PAM_INFO3_TEXT) && - (my_info3->acct_flags == 0) && NT_STATUS_IS_OK(result)) { - - struct rpc_pipe_client *samr_pipe; - POLICY_HND samr_domain_handle, user_pol; - SAM_USERINFO_CTR *user_ctr; - NTSTATUS status_tmp; - uint32 acct_flags; - - ZERO_STRUCT(user_ctr); - - status_tmp = cm_connect_sam(contact_domain, state->mem_ctx, - &samr_pipe, &samr_domain_handle); - - if (!NT_STATUS_IS_OK(status_tmp)) { - DEBUG(3, ("could not open handle to SAMR pipe: %s\n", - nt_errstr(status_tmp))); - goto done; - } - - status_tmp = rpccli_samr_open_user(samr_pipe, state->mem_ctx, - &samr_domain_handle, - MAXIMUM_ALLOWED_ACCESS, - my_info3->user_rid, &user_pol); - - if (!NT_STATUS_IS_OK(status_tmp)) { - DEBUG(3, ("could not open user handle on SAMR pipe: %s\n", - nt_errstr(status_tmp))); - goto done; - } - - status_tmp = rpccli_samr_query_userinfo(samr_pipe, state->mem_ctx, - &user_pol, 16, &user_ctr); - - if (!NT_STATUS_IS_OK(status_tmp)) { - DEBUG(3, ("could not query user info on SAMR pipe: %s\n", - nt_errstr(status_tmp))); - rpccli_samr_close(samr_pipe, state->mem_ctx, &user_pol); - goto done; - } - - acct_flags = user_ctr->info.id16->acb_info; - - if (acct_flags == 0) { - rpccli_samr_close(samr_pipe, state->mem_ctx, &user_pol); - goto done; - } - - my_info3->acct_flags = acct_flags; - - DEBUG(10,("successfully retrieved acct_flags 0x%x\n", acct_flags)); - - rpccli_samr_close(samr_pipe, state->mem_ctx, &user_pol); - } - - *info3 = my_info3; -done: - return result; -} - -enum winbindd_result winbindd_dual_pam_auth(struct winbindd_domain *domain, - struct winbindd_cli_state *state) -{ - NTSTATUS result = NT_STATUS_LOGON_FAILURE; - NTSTATUS krb5_result = NT_STATUS_OK; - fstring name_domain, name_user; - NET_USER_INFO_3 *info3 = NULL; - - /* Ensure null termination */ - state->request.data.auth.user[sizeof(state->request.data.auth.user)-1]='\0'; - - /* Ensure null termination */ - state->request.data.auth.pass[sizeof(state->request.data.auth.pass)-1]='\0'; - - DEBUG(3, ("[%5lu]: dual pam auth %s\n", (unsigned long)state->pid, - state->request.data.auth.user)); - - if (!check_request_flags(state->request.flags)) { - result = NT_STATUS_INVALID_PARAMETER_MIX; - goto done; - } - - /* Parse domain and username */ - - ws_name_return( state->request.data.auth.user, WB_REPLACE_CHAR ); - - parse_domain_user(state->request.data.auth.user, name_domain, name_user); - - if (domain->online == False) { - result = NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND; - if (domain->startup) { - /* Logons are very important to users. If we're offline and - we get a request within the first 30 seconds of startup, - try very hard to find a DC and go online. */ - - DEBUG(10,("winbindd_dual_pam_auth: domain: %s offline and auth " - "request in startup mode.\n", domain->name )); - - winbindd_flush_negative_conn_cache(domain); - result = init_dc_connection(domain); - } - } - - DEBUG(10,("winbindd_dual_pam_auth: domain: %s last was %s\n", domain->name, domain->online ? "online":"offline")); - - /* Check for Kerberos authentication */ - if (domain->online && (state->request.flags & WBFLAG_PAM_KRB5)) { - - result = winbindd_dual_pam_auth_kerberos(domain, state, &info3); - /* save for later */ - krb5_result = result; - - - if (NT_STATUS_IS_OK(result)) { - DEBUG(10,("winbindd_dual_pam_auth_kerberos succeeded\n")); - goto process_result; - } else { - DEBUG(10,("winbindd_dual_pam_auth_kerberos failed: %s\n", nt_errstr(result))); - } - - if (NT_STATUS_EQUAL(result, NT_STATUS_NO_LOGON_SERVERS) || - NT_STATUS_EQUAL(result, NT_STATUS_IO_TIMEOUT) || - NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) { - DEBUG(10,("winbindd_dual_pam_auth_kerberos setting domain to offline\n")); - set_domain_offline( domain ); - goto cached_logon; - } - - /* there are quite some NT_STATUS errors where there is no - * point in retrying with a samlogon, we explictly have to take - * care not to increase the bad logon counter on the DC */ - - if (NT_STATUS_EQUAL(result, NT_STATUS_ACCOUNT_DISABLED) || - NT_STATUS_EQUAL(result, NT_STATUS_ACCOUNT_EXPIRED) || - NT_STATUS_EQUAL(result, NT_STATUS_ACCOUNT_LOCKED_OUT) || - NT_STATUS_EQUAL(result, NT_STATUS_INVALID_LOGON_HOURS) || - NT_STATUS_EQUAL(result, NT_STATUS_INVALID_WORKSTATION) || - NT_STATUS_EQUAL(result, NT_STATUS_LOGON_FAILURE) || - NT_STATUS_EQUAL(result, NT_STATUS_NO_SUCH_USER) || - NT_STATUS_EQUAL(result, NT_STATUS_PASSWORD_EXPIRED) || - NT_STATUS_EQUAL(result, NT_STATUS_PASSWORD_MUST_CHANGE) || - NT_STATUS_EQUAL(result, NT_STATUS_WRONG_PASSWORD)) { - goto process_result; - } - - if (state->request.flags & WBFLAG_PAM_FALLBACK_AFTER_KRB5) { - DEBUG(3,("falling back to samlogon\n")); - goto sam_logon; - } else { - goto cached_logon; - } - } - -sam_logon: - /* Check for Samlogon authentication */ - if (domain->online) { - result = winbindd_dual_pam_auth_samlogon(domain, state, &info3); - - if (NT_STATUS_IS_OK(result)) { - DEBUG(10,("winbindd_dual_pam_auth_samlogon succeeded\n")); - /* add the Krb5 err if we have one */ - if ( NT_STATUS_EQUAL(krb5_result, NT_STATUS_TIME_DIFFERENCE_AT_DC ) ) { - info3->user_flgs |= LOGON_KRB5_FAIL_CLOCK_SKEW; - } - goto process_result; - } - - DEBUG(10,("winbindd_dual_pam_auth_samlogon failed: %s\n", - nt_errstr(result))); - - if (NT_STATUS_EQUAL(result, NT_STATUS_NO_LOGON_SERVERS) || - NT_STATUS_EQUAL(result, NT_STATUS_IO_TIMEOUT) || - NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) - { - DEBUG(10,("winbindd_dual_pam_auth_samlogon setting domain to offline\n")); - set_domain_offline( domain ); - goto cached_logon; - } - - if (domain->online) { - /* We're still online - fail. */ - goto done; - } - } - -cached_logon: - /* Check for Cached logons */ - if (!domain->online && (state->request.flags & WBFLAG_PAM_CACHED_LOGIN) && - lp_winbind_offline_logon()) { - - result = winbindd_dual_pam_auth_cached(domain, state, &info3); - - if (NT_STATUS_IS_OK(result)) { - DEBUG(10,("winbindd_dual_pam_auth_cached succeeded\n")); - goto process_result; - } else { - DEBUG(10,("winbindd_dual_pam_auth_cached failed: %s\n", nt_errstr(result))); - goto done; - } - } - -process_result: - - if (NT_STATUS_IS_OK(result)) { - - DOM_SID user_sid; - - /* In all codepaths where result == NT_STATUS_OK info3 must have - been initialized. */ - if (!info3) { - result = NT_STATUS_INTERNAL_ERROR; - goto done; - } - - netsamlogon_cache_store(name_user, info3); - wcache_invalidate_samlogon(find_domain_from_name(name_domain), info3); - - /* save name_to_sid info as early as possible (only if - this is our primary domain so we don't invalidate - the cache entry by storing the seq_num for the wrong - domain). */ - if ( domain->primary ) { - sid_compose(&user_sid, &info3->dom_sid.sid, - info3->user_rid); - cache_name2sid(domain, name_domain, name_user, - SID_NAME_USER, &user_sid); - } - - /* Check if the user is in the right group */ - - if (!NT_STATUS_IS_OK(result = check_info3_in_group(state->mem_ctx, info3, - state->request.data.auth.require_membership_of_sid))) { - DEBUG(3, ("User %s is not in the required group (%s), so plaintext authentication is rejected\n", - state->request.data.auth.user, - state->request.data.auth.require_membership_of_sid)); - goto done; - } - - result = append_data(state, info3, name_domain, name_user); - if (!NT_STATUS_IS_OK(result)) { - goto done; - } - - if ((state->request.flags & WBFLAG_PAM_CACHED_LOGIN)) { - - /* Store in-memory creds for single-signon using ntlm_auth. */ - result = winbindd_add_memory_creds(state->request.data.auth.user, - get_uid_from_state(state), - state->request.data.auth.pass); - - if (!NT_STATUS_IS_OK(result)) { - DEBUG(10,("Failed to store memory creds: %s\n", nt_errstr(result))); - goto done; - } - - if (lp_winbind_offline_logon()) { - result = winbindd_store_creds(domain, - state->mem_ctx, - state->request.data.auth.user, - state->request.data.auth.pass, - info3, NULL); - if (!NT_STATUS_IS_OK(result)) { - - /* Release refcount. */ - winbindd_delete_memory_creds(state->request.data.auth.user); - - DEBUG(10,("Failed to store creds: %s\n", nt_errstr(result))); - goto done; - } - } - } - - result = fillup_password_policy(domain, state); - - if (!NT_STATUS_IS_OK(result) - && !NT_STATUS_EQUAL(result, NT_STATUS_NOT_SUPPORTED) ) - { - DEBUG(10,("Failed to get password policies: %s\n", nt_errstr(result))); - goto done; - } - - result = NT_STATUS_OK; - } - -done: - /* give us a more useful (more correct?) error code */ - if ((NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND) || - (NT_STATUS_EQUAL(result, NT_STATUS_UNSUCCESSFUL)))) { - result = NT_STATUS_NO_LOGON_SERVERS; - } - - state->response.data.auth.nt_status = NT_STATUS_V(result); - fstrcpy(state->response.data.auth.nt_status_string, nt_errstr(result)); - - /* we might have given a more useful error above */ - if (!*state->response.data.auth.error_string) - fstrcpy(state->response.data.auth.error_string, get_friendly_nt_error_msg(result)); - state->response.data.auth.pam_error = nt_status_to_pam(result); - - DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2, ("Plain-text authentication for user %s returned %s (PAM: %d)\n", - state->request.data.auth.user, - state->response.data.auth.nt_status_string, - state->response.data.auth.pam_error)); - - return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR; -} - - -/********************************************************************** - Challenge Response Authentication Protocol -**********************************************************************/ - -void winbindd_pam_auth_crap(struct winbindd_cli_state *state) -{ - struct winbindd_domain *domain = NULL; - const char *domain_name = NULL; - NTSTATUS result; - - if (!check_request_flags(state->request.flags)) { - result = NT_STATUS_INVALID_PARAMETER_MIX; - goto done; - } - - if (!state->privileged) { - char *error_string = NULL; - DEBUG(2, ("winbindd_pam_auth_crap: non-privileged access " - "denied. !\n")); - DEBUGADD(2, ("winbindd_pam_auth_crap: Ensure permissions " - "on %s are set correctly.\n", - get_winbind_priv_pipe_dir())); - /* send a better message than ACCESS_DENIED */ - error_string = talloc_asprintf(state->mem_ctx, - "winbind client not authorized " - "to use winbindd_pam_auth_crap." - " Ensure permissions on %s " - "are set correctly.", - get_winbind_priv_pipe_dir()); - fstrcpy(state->response.data.auth.error_string, error_string); - result = NT_STATUS_ACCESS_DENIED; - goto done; - } - - /* Ensure null termination */ - state->request.data.auth_crap.user - [sizeof(state->request.data.auth_crap.user)-1]=0; - state->request.data.auth_crap.domain - [sizeof(state->request.data.auth_crap.domain)-1]=0; - - DEBUG(3, ("[%5lu]: pam auth crap domain: [%s] user: %s\n", - (unsigned long)state->pid, - state->request.data.auth_crap.domain, - state->request.data.auth_crap.user)); - - if (*state->request.data.auth_crap.domain != '\0') { - domain_name = state->request.data.auth_crap.domain; - } else if (lp_winbind_use_default_domain()) { - domain_name = lp_workgroup(); - } - - if (domain_name != NULL) - domain = find_auth_domain(state, domain_name); - - if (domain != NULL) { - sendto_domain(state, domain); - return; - } - - result = NT_STATUS_NO_SUCH_USER; - - done: - set_auth_errors(&state->response, result); - DEBUG(5, ("CRAP authentication for %s\\%s returned %s (PAM: %d)\n", - state->request.data.auth_crap.domain, - state->request.data.auth_crap.user, - state->response.data.auth.nt_status_string, - state->response.data.auth.pam_error)); - request_error(state); - return; -} - - -enum winbindd_result winbindd_dual_pam_auth_crap(struct winbindd_domain *domain, - struct winbindd_cli_state *state) -{ - NTSTATUS result; - NET_USER_INFO_3 info3; - struct rpc_pipe_client *netlogon_pipe; - const char *name_user = NULL; - const char *name_domain = NULL; - const char *workstation; - struct winbindd_domain *contact_domain; - int attempts = 0; - BOOL retry; - - DATA_BLOB lm_resp, nt_resp; - - /* This is child-only, so no check for privileged access is needed - anymore */ - - /* Ensure null termination */ - state->request.data.auth_crap.user[sizeof(state->request.data.auth_crap.user)-1]=0; - state->request.data.auth_crap.domain[sizeof(state->request.data.auth_crap.domain)-1]=0; - - if (!check_request_flags(state->request.flags)) { - result = NT_STATUS_INVALID_PARAMETER_MIX; - goto done; - } - - name_user = state->request.data.auth_crap.user; - - if (*state->request.data.auth_crap.domain) { - name_domain = state->request.data.auth_crap.domain; - } else if (lp_winbind_use_default_domain()) { - name_domain = lp_workgroup(); - } else { - DEBUG(5,("no domain specified with username (%s) - failing auth\n", - name_user)); - result = NT_STATUS_NO_SUCH_USER; - goto done; - } - - DEBUG(3, ("[%5lu]: pam auth crap domain: %s user: %s\n", (unsigned long)state->pid, - name_domain, name_user)); - - if (*state->request.data.auth_crap.workstation) { - workstation = state->request.data.auth_crap.workstation; - } else { - workstation = global_myname(); - } - - if (state->request.data.auth_crap.lm_resp_len > sizeof(state->request.data.auth_crap.lm_resp) - || state->request.data.auth_crap.nt_resp_len > sizeof(state->request.data.auth_crap.nt_resp)) { - DEBUG(0, ("winbindd_pam_auth_crap: invalid password length %u/%u\n", - state->request.data.auth_crap.lm_resp_len, - state->request.data.auth_crap.nt_resp_len)); - result = NT_STATUS_INVALID_PARAMETER; - goto done; - } - - lm_resp = data_blob_talloc(state->mem_ctx, state->request.data.auth_crap.lm_resp, - state->request.data.auth_crap.lm_resp_len); - nt_resp = data_blob_talloc(state->mem_ctx, state->request.data.auth_crap.nt_resp, - state->request.data.auth_crap.nt_resp_len); - - /* what domain should we contact? */ - - if ( IS_DC ) { - if (!(contact_domain = find_domain_from_name(name_domain))) { - DEBUG(3, ("Authentication for domain for [%s] -> [%s]\\[%s] failed as %s is not a trusted domain\n", - state->request.data.auth_crap.user, name_domain, name_user, name_domain)); - result = NT_STATUS_NO_SUCH_USER; - goto done; - } - } else { - if (is_myname(name_domain)) { - DEBUG(3, ("Authentication for domain %s (local domain to this server) not supported at this stage\n", name_domain)); - result = NT_STATUS_NO_SUCH_USER; - goto done; - } - contact_domain = find_our_domain(); - } - - do { - NTSTATUS (*logon_fn)(struct rpc_pipe_client - *cli, TALLOC_CTX *mem_ctx, - uint32 logon_parameters, - const char *server, - const char *username, - const char *domain, - const char *workstation, - const uint8 chal[8], - DATA_BLOB lm_response, - DATA_BLOB nt_response, - NET_USER_INFO_3 *info3); - - ZERO_STRUCT(info3); - retry = False; - - netlogon_pipe = NULL; - result = cm_connect_netlogon(contact_domain, &netlogon_pipe); - - if (!NT_STATUS_IS_OK(result)) { - DEBUG(3, ("could not open handle to NETLOGON pipe (error: %s)\n", - nt_errstr(result))); - goto done; - } - - logon_fn = contact_domain->can_do_samlogon_ex - ? rpccli_netlogon_sam_network_logon_ex - : rpccli_netlogon_sam_network_logon; - - result = logon_fn(netlogon_pipe, - state->mem_ctx, - state->request.data.auth_crap.logon_parameters, - contact_domain->dcname, - name_user, - name_domain, - /* Bug #3248 - found by Stefan Burkei. */ - workstation, /* We carefully set this above so use it... */ - state->request.data.auth_crap.chal, - lm_resp, - nt_resp, - &info3); - - if ((NT_STATUS_V(result) == DCERPC_FAULT_OP_RNG_ERROR) - && contact_domain->can_do_samlogon_ex) { - DEBUG(3, ("Got a DC that can not do NetSamLogonEx, " - "retrying with NetSamLogon\n")); - contact_domain->can_do_samlogon_ex = False; - retry = True; - continue; - } - - attempts += 1; - - /* We have to try a second time as cm_connect_netlogon - might not yet have noticed that the DC has killed - our connection. */ - - if (NT_STATUS_EQUAL(result, NT_STATUS_UNSUCCESSFUL)) { - retry = True; - continue; - } - - /* if we get access denied, a possible cause was that we had and open - connection to the DC, but someone changed our machine account password - out from underneath us using 'net rpc changetrustpw' */ - - if ( NT_STATUS_EQUAL(result, NT_STATUS_ACCESS_DENIED) ) { - DEBUG(3,("winbindd_pam_auth: sam_logon returned " - "ACCESS_DENIED. Maybe the trust account " - "password was changed and we didn't know it. " - "Killing connections to domain %s\n", - name_domain)); - invalidate_cm_connection(&contact_domain->conn); - retry = True; - } - - } while ( (attempts < 2) && retry ); - - if (NT_STATUS_IS_OK(result)) { - - netsamlogon_cache_store(name_user, &info3); - wcache_invalidate_samlogon(find_domain_from_name(name_domain), &info3); - - /* Check if the user is in the right group */ - - if (!NT_STATUS_IS_OK(result = check_info3_in_group(state->mem_ctx, &info3, - state->request.data.auth_crap.require_membership_of_sid))) { - DEBUG(3, ("User %s is not in the required group (%s), so " - "crap authentication is rejected\n", - state->request.data.auth_crap.user, - state->request.data.auth_crap.require_membership_of_sid)); - goto done; - } - - result = append_data(state, &info3, name_domain, name_user); - if (!NT_STATUS_IS_OK(result)) { - goto done; - } - } - -done: - - /* give us a more useful (more correct?) error code */ - if ((NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND) || - (NT_STATUS_EQUAL(result, NT_STATUS_UNSUCCESSFUL)))) { - result = NT_STATUS_NO_LOGON_SERVERS; - } - - if (state->request.flags & WBFLAG_PAM_NT_STATUS_SQUASH) { - result = nt_status_squash(result); - } - - state->response.data.auth.nt_status = NT_STATUS_V(result); - fstrcpy(state->response.data.auth.nt_status_string, nt_errstr(result)); - - /* we might have given a more useful error above */ - if (!*state->response.data.auth.error_string) { - fstrcpy(state->response.data.auth.error_string, get_friendly_nt_error_msg(result)); - } - state->response.data.auth.pam_error = nt_status_to_pam(result); - - DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2, - ("NTLM CRAP authentication for user [%s]\\[%s] returned %s (PAM: %d)\n", - name_domain, - name_user, - state->response.data.auth.nt_status_string, - state->response.data.auth.pam_error)); - - return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR; -} - -/* Change a user password */ - -void winbindd_pam_chauthtok(struct winbindd_cli_state *state) -{ - fstring domain, user; - struct winbindd_domain *contact_domain; - - DEBUG(3, ("[%5lu]: pam chauthtok %s\n", (unsigned long)state->pid, - state->request.data.chauthtok.user)); - - /* Setup crap */ - - ws_name_return( state->request.data.auth.user, WB_REPLACE_CHAR ); - - if (!canonicalize_username(state->request.data.chauthtok.user, domain, user)) { - set_auth_errors(&state->response, NT_STATUS_NO_SUCH_USER); - DEBUG(5, ("winbindd_pam_chauthtok: canonicalize_username %s failed with %s" - "(PAM: %d)\n", - state->request.data.auth.user, - state->response.data.auth.nt_status_string, - state->response.data.auth.pam_error)); - request_error(state); - return; - } - - contact_domain = find_domain_from_name(domain); - if (!contact_domain) { - set_auth_errors(&state->response, NT_STATUS_NO_SUCH_USER); - DEBUG(3, ("Cannot change password for [%s] -> [%s]\\[%s] as %s is not a trusted domain\n", - state->request.data.chauthtok.user, domain, user, domain)); - request_error(state); - return; - } - - sendto_domain(state, contact_domain); -} - -enum winbindd_result winbindd_dual_pam_chauthtok(struct winbindd_domain *contact_domain, - struct winbindd_cli_state *state) -{ - char *oldpass; - char *newpass = NULL; - POLICY_HND dom_pol; - struct rpc_pipe_client *cli; - BOOL got_info = False; - SAM_UNK_INFO_1 info; - SAMR_CHANGE_REJECT reject; - NTSTATUS result = NT_STATUS_UNSUCCESSFUL; - fstring domain, user; - - DEBUG(3, ("[%5lu]: dual pam chauthtok %s\n", (unsigned long)state->pid, - state->request.data.auth.user)); - - if (!parse_domain_user(state->request.data.chauthtok.user, domain, user)) { - goto done; - } - - /* Change password */ - - oldpass = state->request.data.chauthtok.oldpass; - newpass = state->request.data.chauthtok.newpass; - - /* Initialize reject reason */ - state->response.data.auth.reject_reason = Undefined; - - /* Get sam handle */ - - result = cm_connect_sam(contact_domain, state->mem_ctx, &cli, - &dom_pol); - if (!NT_STATUS_IS_OK(result)) { - DEBUG(1, ("could not get SAM handle on DC for %s\n", domain)); - goto done; - } - - result = rpccli_samr_chgpasswd3(cli, state->mem_ctx, user, newpass, oldpass, &info, &reject); - - /* Windows 2003 returns NT_STATUS_PASSWORD_RESTRICTION */ - - if (NT_STATUS_EQUAL(result, NT_STATUS_PASSWORD_RESTRICTION) ) { - state->response.data.auth.policy.min_length_password = - info.min_length_password; - state->response.data.auth.policy.password_history = - info.password_history; - state->response.data.auth.policy.password_properties = - info.password_properties; - state->response.data.auth.policy.expire = - nt_time_to_unix_abs(&info.expire); - state->response.data.auth.policy.min_passwordage = - nt_time_to_unix_abs(&info.min_passwordage); - - state->response.data.auth.reject_reason = - reject.reject_reason; - - got_info = True; - } - - /* only fallback when the chgpasswd3 call is not supported */ - if ((NT_STATUS_EQUAL(result, NT_STATUS(DCERPC_FAULT_OP_RNG_ERROR))) || - (NT_STATUS_EQUAL(result, NT_STATUS_NOT_SUPPORTED)) || - (NT_STATUS_EQUAL(result, NT_STATUS_NOT_IMPLEMENTED))) { - - DEBUG(10,("Password change with chgpasswd3 failed with: %s, retrying chgpasswd_user\n", - nt_errstr(result))); - - result = rpccli_samr_chgpasswd_user(cli, state->mem_ctx, user, newpass, oldpass); - - /* Windows 2000 returns NT_STATUS_ACCOUNT_RESTRICTION. - Map to the same status code as Windows 2003. */ - - if ( NT_STATUS_EQUAL(NT_STATUS_ACCOUNT_RESTRICTION, result ) ) { - result = NT_STATUS_PASSWORD_RESTRICTION; - } - } - -done: - - if (NT_STATUS_IS_OK(result) && (state->request.flags & WBFLAG_PAM_CACHED_LOGIN)) { - - /* Update the single sign-on memory creds. */ - result = winbindd_replace_memory_creds(state->request.data.chauthtok.user, - newpass); - - if (!NT_STATUS_IS_OK(result)) { - DEBUG(10,("Failed to replace memory creds: %s\n", nt_errstr(result))); - goto process_result; - } - - if (lp_winbind_offline_logon()) { - result = winbindd_update_creds_by_name(contact_domain, - state->mem_ctx, user, - newpass); - if (!NT_STATUS_IS_OK(result)) { - DEBUG(10,("Failed to store creds: %s\n", nt_errstr(result))); - goto process_result; - } - } - } - - if (!NT_STATUS_IS_OK(result) && !got_info && contact_domain) { - - NTSTATUS policy_ret; - - policy_ret = fillup_password_policy(contact_domain, state); - - /* failure of this is non critical, it will just provide no - * additional information to the client why the change has - * failed - Guenther */ - - if (!NT_STATUS_IS_OK(policy_ret)) { - DEBUG(10,("Failed to get password policies: %s\n", nt_errstr(policy_ret))); - goto process_result; - } - } - -process_result: - - state->response.data.auth.nt_status = NT_STATUS_V(result); - fstrcpy(state->response.data.auth.nt_status_string, nt_errstr(result)); - fstrcpy(state->response.data.auth.error_string, get_friendly_nt_error_msg(result)); - state->response.data.auth.pam_error = nt_status_to_pam(result); - - DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2, - ("Password change for user [%s]\\[%s] returned %s (PAM: %d)\n", - domain, - user, - state->response.data.auth.nt_status_string, - state->response.data.auth.pam_error)); - - return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR; -} - -void winbindd_pam_logoff(struct winbindd_cli_state *state) -{ - struct winbindd_domain *domain; - fstring name_domain, user; - uid_t caller_uid = (uid_t)-1; - uid_t request_uid = state->request.data.logoff.uid; - - DEBUG(3, ("[%5lu]: pam logoff %s\n", (unsigned long)state->pid, - state->request.data.logoff.user)); - - /* Ensure null termination */ - state->request.data.logoff.user - [sizeof(state->request.data.logoff.user)-1]='\0'; - - state->request.data.logoff.krb5ccname - [sizeof(state->request.data.logoff.krb5ccname)-1]='\0'; - - if (request_uid == (gid_t)-1) { - goto failed; - } - - if (!canonicalize_username(state->request.data.logoff.user, name_domain, user)) { - goto failed; - } - - if ((domain = find_auth_domain(state, name_domain)) == NULL) { - goto failed; - } - - if ((sys_getpeereid(state->sock, &caller_uid)) != 0) { - DEBUG(1,("winbindd_pam_logoff: failed to check peerid: %s\n", - strerror(errno))); - goto failed; - } - - switch (caller_uid) { - case -1: - goto failed; - case 0: - /* root must be able to logoff any user - gd */ - state->request.data.logoff.uid = request_uid; - break; - default: - if (caller_uid != request_uid) { - DEBUG(1,("winbindd_pam_logoff: caller requested invalid uid\n")); - goto failed; - } - state->request.data.logoff.uid = caller_uid; - break; - } - - sendto_domain(state, domain); - return; - - failed: - set_auth_errors(&state->response, NT_STATUS_NO_SUCH_USER); - DEBUG(5, ("Pam Logoff for %s returned %s " - "(PAM: %d)\n", - state->request.data.logoff.user, - state->response.data.auth.nt_status_string, - state->response.data.auth.pam_error)); - request_error(state); - return; -} - -enum winbindd_result winbindd_dual_pam_logoff(struct winbindd_domain *domain, - struct winbindd_cli_state *state) -{ - NTSTATUS result = NT_STATUS_NOT_SUPPORTED; - - DEBUG(3, ("[%5lu]: pam dual logoff %s\n", (unsigned long)state->pid, - state->request.data.logoff.user)); - - if (!(state->request.flags & WBFLAG_PAM_KRB5)) { - result = NT_STATUS_OK; - goto process_result; - } - - if (state->request.data.logoff.krb5ccname[0] == '\0') { - result = NT_STATUS_OK; - goto process_result; - } - -#ifdef HAVE_KRB5 - - if (state->request.data.logoff.uid < 0) { - DEBUG(0,("winbindd_pam_logoff: invalid uid\n")); - goto process_result; - } - - /* what we need here is to find the corresponding krb5 ccache name *we* - * created for a given username and destroy it */ - - if (!ccache_entry_exists(state->request.data.logoff.user)) { - result = NT_STATUS_OK; - DEBUG(10,("winbindd_pam_logoff: no entry found.\n")); - goto process_result; - } - - if (!ccache_entry_identical(state->request.data.logoff.user, - state->request.data.logoff.uid, - state->request.data.logoff.krb5ccname)) { - DEBUG(0,("winbindd_pam_logoff: cached entry differs.\n")); - goto process_result; - } - - result = remove_ccache(state->request.data.logoff.user); - if (!NT_STATUS_IS_OK(result)) { - DEBUG(0,("winbindd_pam_logoff: failed to remove ccache: %s\n", - nt_errstr(result))); - goto process_result; - } - -#else - result = NT_STATUS_NOT_SUPPORTED; -#endif - -process_result: - - winbindd_delete_memory_creds(state->request.data.logoff.user); - - state->response.data.auth.nt_status = NT_STATUS_V(result); - fstrcpy(state->response.data.auth.nt_status_string, nt_errstr(result)); - fstrcpy(state->response.data.auth.error_string, get_friendly_nt_error_msg(result)); - state->response.data.auth.pam_error = nt_status_to_pam(result); - - return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR; -} - -/* Change user password with auth crap*/ - -void winbindd_pam_chng_pswd_auth_crap(struct winbindd_cli_state *state) -{ - struct winbindd_domain *domain = NULL; - const char *domain_name = NULL; - - /* Ensure null termination */ - state->request.data.chng_pswd_auth_crap.user[ - sizeof(state->request.data.chng_pswd_auth_crap.user)-1]=0; - state->request.data.chng_pswd_auth_crap.domain[ - sizeof(state->request.data.chng_pswd_auth_crap.domain)-1]=0; - - DEBUG(3, ("[%5lu]: pam change pswd auth crap domain: %s user: %s\n", - (unsigned long)state->pid, - state->request.data.chng_pswd_auth_crap.domain, - state->request.data.chng_pswd_auth_crap.user)); - - if (*state->request.data.chng_pswd_auth_crap.domain != '\0') { - domain_name = state->request.data.chng_pswd_auth_crap.domain; - } else if (lp_winbind_use_default_domain()) { - domain_name = lp_workgroup(); - } - - if (domain_name != NULL) - domain = find_domain_from_name(domain_name); - - if (domain != NULL) { - DEBUG(7, ("[%5lu]: pam auth crap changing pswd in domain: " - "%s\n", (unsigned long)state->pid,domain->name)); - sendto_domain(state, domain); - return; - } - - set_auth_errors(&state->response, NT_STATUS_NO_SUCH_USER); - DEBUG(5, ("CRAP change password for %s\\%s returned %s (PAM: %d)\n", - state->request.data.chng_pswd_auth_crap.domain, - state->request.data.chng_pswd_auth_crap.user, - state->response.data.auth.nt_status_string, - state->response.data.auth.pam_error)); - request_error(state); - return; -} - -enum winbindd_result winbindd_dual_pam_chng_pswd_auth_crap(struct winbindd_domain *domainSt, struct winbindd_cli_state *state) -{ - NTSTATUS result; - DATA_BLOB new_nt_password; - DATA_BLOB old_nt_hash_enc; - DATA_BLOB new_lm_password; - DATA_BLOB old_lm_hash_enc; - fstring domain,user; - POLICY_HND dom_pol; - struct winbindd_domain *contact_domain = domainSt; - struct rpc_pipe_client *cli; - - /* Ensure null termination */ - state->request.data.chng_pswd_auth_crap.user[ - sizeof(state->request.data.chng_pswd_auth_crap.user)-1]=0; - state->request.data.chng_pswd_auth_crap.domain[ - sizeof(state->request.data.chng_pswd_auth_crap.domain)-1]=0; - *domain = 0; - *user = 0; - - DEBUG(3, ("[%5lu]: pam change pswd auth crap domain: %s user: %s\n", - (unsigned long)state->pid, - state->request.data.chng_pswd_auth_crap.domain, - state->request.data.chng_pswd_auth_crap.user)); - - if (lp_winbind_offline_logon()) { - DEBUG(0,("Refusing password change as winbind offline logons are enabled. ")); - DEBUGADD(0,("Changing passwords here would risk inconsistent logons\n")); - result = NT_STATUS_ACCESS_DENIED; - goto done; - } - - if (*state->request.data.chng_pswd_auth_crap.domain) { - fstrcpy(domain,state->request.data.chng_pswd_auth_crap.domain); - } else { - parse_domain_user(state->request.data.chng_pswd_auth_crap.user, - domain, user); - - if(!*domain) { - DEBUG(3,("no domain specified with username (%s) - " - "failing auth\n", - state->request.data.chng_pswd_auth_crap.user)); - result = NT_STATUS_NO_SUCH_USER; - goto done; - } - } - - if (!*domain && lp_winbind_use_default_domain()) { - fstrcpy(domain,(char *)lp_workgroup()); - } - - if(!*user) { - fstrcpy(user, state->request.data.chng_pswd_auth_crap.user); - } - - DEBUG(3, ("[%5lu]: pam auth crap domain: %s user: %s\n", - (unsigned long)state->pid, domain, user)); - - /* Change password */ - new_nt_password = data_blob_talloc( - state->mem_ctx, - state->request.data.chng_pswd_auth_crap.new_nt_pswd, - state->request.data.chng_pswd_auth_crap.new_nt_pswd_len); - - old_nt_hash_enc = data_blob_talloc( - state->mem_ctx, - state->request.data.chng_pswd_auth_crap.old_nt_hash_enc, - state->request.data.chng_pswd_auth_crap.old_nt_hash_enc_len); - - if(state->request.data.chng_pswd_auth_crap.new_lm_pswd_len > 0) { - new_lm_password = data_blob_talloc( - state->mem_ctx, - state->request.data.chng_pswd_auth_crap.new_lm_pswd, - state->request.data.chng_pswd_auth_crap.new_lm_pswd_len); - - old_lm_hash_enc = data_blob_talloc( - state->mem_ctx, - state->request.data.chng_pswd_auth_crap.old_lm_hash_enc, - state->request.data.chng_pswd_auth_crap.old_lm_hash_enc_len); - } else { - new_lm_password.length = 0; - old_lm_hash_enc.length = 0; - } - - /* Get sam handle */ - - result = cm_connect_sam(contact_domain, state->mem_ctx, &cli, &dom_pol); - if (!NT_STATUS_IS_OK(result)) { - DEBUG(1, ("could not get SAM handle on DC for %s\n", domain)); - goto done; - } - - result = rpccli_samr_chng_pswd_auth_crap( - cli, state->mem_ctx, user, new_nt_password, old_nt_hash_enc, - new_lm_password, old_lm_hash_enc); - - done: - state->response.data.auth.nt_status = NT_STATUS_V(result); - fstrcpy(state->response.data.auth.nt_status_string, nt_errstr(result)); - fstrcpy(state->response.data.auth.error_string, - get_friendly_nt_error_msg(result)); - state->response.data.auth.pam_error = nt_status_to_pam(result); - - DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2, - ("Password change for user [%s]\\[%s] returned %s (PAM: %d)\n", - domain, user, - state->response.data.auth.nt_status_string, - state->response.data.auth.pam_error)); - - return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR; -} diff --git a/source3/nsswitch/winbindd_passdb.c b/source3/nsswitch/winbindd_passdb.c deleted file mode 100644 index 5c36c0c327..0000000000 --- a/source3/nsswitch/winbindd_passdb.c +++ /dev/null @@ -1,467 +0,0 @@ -/* - Unix SMB/CIFS implementation. - - Winbind rpc backend functions - - Copyright (C) Tim Potter 2000-2001,2003 - Copyright (C) Simo Sorce 2003 - Copyright (C) Volker Lendecke 2004 - - 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 3 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, see . -*/ - -#include "includes.h" -#include "winbindd.h" - -#undef DBGC_CLASS -#define DBGC_CLASS DBGC_WINBIND - -/* Query display info for a domain. This returns enough information plus a - bit extra to give an overview of domain users for the User Manager - application. */ -static NTSTATUS query_user_list(struct winbindd_domain *domain, - TALLOC_CTX *mem_ctx, - uint32 *num_entries, - WINBIND_USERINFO **info) -{ - /* We don't have users */ - *num_entries = 0; - *info = NULL; - return NT_STATUS_OK; -} - -/* list all domain groups */ -static NTSTATUS enum_dom_groups(struct winbindd_domain *domain, - TALLOC_CTX *mem_ctx, - uint32 *num_entries, - struct acct_info **info) -{ - /* We don't have domain groups */ - *num_entries = 0; - *info = NULL; - return NT_STATUS_OK; -} - -/* List all domain groups */ - -static NTSTATUS enum_local_groups(struct winbindd_domain *domain, - TALLOC_CTX *mem_ctx, - uint32 *num_entries, - struct acct_info **info) -{ - struct pdb_search *search; - struct samr_displayentry *aliases; - int i; - NTSTATUS result = NT_STATUS_UNSUCCESSFUL; - - search = pdb_search_aliases(&domain->sid); - if (search == NULL) goto done; - - *num_entries = pdb_search_entries(search, 0, 0xffffffff, &aliases); - if (*num_entries == 0) goto done; - - *info = TALLOC_ARRAY(mem_ctx, struct acct_info, *num_entries); - if (*info == NULL) { - result = NT_STATUS_NO_MEMORY; - goto done; - } - - for (i=0; i<*num_entries; i++) { - fstrcpy((*info)[i].acct_name, aliases[i].account_name); - fstrcpy((*info)[i].acct_desc, aliases[i].description); - (*info)[i].rid = aliases[i].rid; - } - - result = NT_STATUS_OK; - done: - pdb_search_destroy(search); - return result; -} - -/* convert a single name to a sid in a domain */ -static NTSTATUS name_to_sid(struct winbindd_domain *domain, - TALLOC_CTX *mem_ctx, - enum winbindd_cmd original_cmd, - const char *domain_name, - const char *name, - DOM_SID *sid, - enum lsa_SidType *type) -{ - uint32 flags = LOOKUP_NAME_ALL; - - switch ( original_cmd ) { - case WINBINDD_LOOKUPNAME: - /* This call is ok */ - break; - default: - /* Avoid any NSS calls in the lookup_name by default */ - flags |= LOOKUP_NAME_EXPLICIT; - DEBUG(10,("winbindd_passdb: limiting name_to_sid() to explicit mappings\n")); - break; - } - - DEBUG(10, ("Finding name %s\n", name)); - - if ( !lookup_name( mem_ctx, name, flags, NULL, NULL, sid, type ) ) { - return NT_STATUS_NONE_MAPPED; - } - - return NT_STATUS_OK; -} - -/* - convert a domain SID to a user or group name -*/ -static NTSTATUS sid_to_name(struct winbindd_domain *domain, - TALLOC_CTX *mem_ctx, - const DOM_SID *sid, - char **domain_name, - char **name, - enum lsa_SidType *type) -{ - const char *dom, *nam; - - DEBUG(10, ("Converting SID %s\n", sid_string_static(sid))); - - /* Paranoia check */ - if (!sid_check_is_in_builtin(sid) && - !sid_check_is_in_our_domain(sid) && - !sid_check_is_in_unix_users(sid) && - !sid_check_is_unix_users(sid) && - !sid_check_is_in_unix_groups(sid) && - !sid_check_is_unix_groups(sid) ) - { - DEBUG(0, ("Possible deadlock: Trying to lookup SID %s with " - "passdb backend\n", sid_string_static(sid))); - return NT_STATUS_NONE_MAPPED; - } - - if (!lookup_sid(mem_ctx, sid, &dom, &nam, type)) { - return NT_STATUS_NONE_MAPPED; - } - - *domain_name = talloc_strdup(mem_ctx, dom); - *name = talloc_strdup(mem_ctx, nam); - - return NT_STATUS_OK; -} - -static NTSTATUS rids_to_names(struct winbindd_domain *domain, - TALLOC_CTX *mem_ctx, - const DOM_SID *sid, - uint32 *rids, - size_t num_rids, - char **domain_name, - char ***names, - enum lsa_SidType **types) -{ - return NT_STATUS_UNSUCCESSFUL; -} - -/* Lookup user information from a rid or username. */ -static NTSTATUS query_user(struct winbindd_domain *domain, - TALLOC_CTX *mem_ctx, - const DOM_SID *user_sid, - WINBIND_USERINFO *user_info) -{ - return NT_STATUS_NO_SUCH_USER; -} - -/* Lookup groups a user is a member of. I wish Unix had a call like this! */ -static NTSTATUS lookup_usergroups(struct winbindd_domain *domain, - TALLOC_CTX *mem_ctx, - const DOM_SID *user_sid, - uint32 *num_groups, DOM_SID **user_gids) -{ - NTSTATUS result; - DOM_SID *groups = NULL; - gid_t *gids = NULL; - size_t ngroups = 0; - struct samu *user; - - if ( (user = samu_new(mem_ctx)) == NULL ) { - return NT_STATUS_NO_MEMORY; - } - - if ( !pdb_getsampwsid( user, user_sid ) ) { - return NT_STATUS_NO_SUCH_USER; - } - - result = pdb_enum_group_memberships( mem_ctx, user, &groups, &gids, &ngroups ); - - TALLOC_FREE( user ); - - *num_groups = (uint32)ngroups; - *user_gids = groups; - - return result; -} - -static NTSTATUS lookup_useraliases(struct winbindd_domain *domain, - TALLOC_CTX *mem_ctx, - uint32 num_sids, const DOM_SID *sids, - uint32 *p_num_aliases, uint32 **rids) -{ - NTSTATUS result; - size_t num_aliases = 0; - - result = pdb_enum_alias_memberships(mem_ctx, &domain->sid, - sids, num_sids, rids, &num_aliases); - - *p_num_aliases = num_aliases; - return result; -} - -/* Lookup group membership given a rid. */ -static NTSTATUS lookup_groupmem(struct winbindd_domain *domain, - TALLOC_CTX *mem_ctx, - const DOM_SID *group_sid, uint32 *num_names, - DOM_SID **sid_mem, char ***names, - uint32 **name_types) -{ - size_t i, num_members, num_mapped; - uint32 *rids; - NTSTATUS result; - const DOM_SID **sids; - struct lsa_dom_info *lsa_domains; - struct lsa_name_info *lsa_names; - TALLOC_CTX *tmp_ctx; - - if (!sid_check_is_in_our_domain(group_sid)) { - /* There's no groups, only aliases in BUILTIN */ - return NT_STATUS_NO_SUCH_GROUP; - } - - if (!(tmp_ctx = talloc_init("lookup_groupmem"))) { - return NT_STATUS_NO_MEMORY; - } - - result = pdb_enum_group_members(tmp_ctx, group_sid, &rids, - &num_members); - if (!NT_STATUS_IS_OK(result)) { - TALLOC_FREE(tmp_ctx); - return result; - } - - if (num_members == 0) { - *num_names = 0; - *sid_mem = NULL; - *names = NULL; - *name_types = NULL; - TALLOC_FREE(tmp_ctx); - return NT_STATUS_OK; - } - - *sid_mem = TALLOC_ARRAY(mem_ctx, DOM_SID, num_members); - *names = TALLOC_ARRAY(mem_ctx, char *, num_members); - *name_types = TALLOC_ARRAY(mem_ctx, uint32, num_members); - sids = TALLOC_ARRAY(tmp_ctx, const DOM_SID *, num_members); - - if (((*sid_mem) == NULL) || ((*names) == NULL) || - ((*name_types) == NULL) || (sids == NULL)) { - TALLOC_FREE(tmp_ctx); - return NT_STATUS_NO_MEMORY; - } - - /* - * Prepare an array of sid pointers for the lookup_sids calling - * convention. - */ - - for (i=0; isid, rids[i])) { - TALLOC_FREE(tmp_ctx); - return NT_STATUS_INTERNAL_ERROR; - } - sids[i] = sid; - } - - result = lookup_sids(tmp_ctx, num_members, sids, 1, - &lsa_domains, &lsa_names); - if (!NT_STATUS_IS_OK(result)) { - TALLOC_FREE(tmp_ctx); - return result; - } - - num_mapped = 0; - for (i=0; iname))) { - TALLOC_FREE(tmp_ctx); - return NT_STATUS_NO_MEMORY; - } - sid_copy(&(*dom_sids)[i], &domains[i]->sid); - } - - TALLOC_FREE(tmp_ctx); - return NT_STATUS_OK; -} - -/* the rpc backend methods are exposed via this structure */ -struct winbindd_methods passdb_methods = { - False, - query_user_list, - enum_dom_groups, - enum_local_groups, - name_to_sid, - sid_to_name, - rids_to_names, - query_user, - lookup_usergroups, - lookup_useraliases, - lookup_groupmem, - sequence_number, - lockout_policy, - password_policy, - trusted_domains, -}; diff --git a/source3/nsswitch/winbindd_reconnect.c b/source3/nsswitch/winbindd_reconnect.c deleted file mode 100644 index a1f96a0359..0000000000 --- a/source3/nsswitch/winbindd_reconnect.c +++ /dev/null @@ -1,316 +0,0 @@ -/* - Unix SMB/CIFS implementation. - - Wrapper around winbindd_rpc.c to centralize retry logic. - - Copyright (C) Volker Lendecke 2005 - - 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 3 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, see . -*/ - -#include "includes.h" -#include "winbindd.h" - -#undef DBGC_CLASS -#define DBGC_CLASS DBGC_WINBIND - -extern struct winbindd_methods msrpc_methods; - -/* List all users */ -static NTSTATUS query_user_list(struct winbindd_domain *domain, - TALLOC_CTX *mem_ctx, - uint32 *num_entries, - WINBIND_USERINFO **info) -{ - NTSTATUS result; - - result = msrpc_methods.query_user_list(domain, mem_ctx, - num_entries, info); - - if (NT_STATUS_EQUAL(result, NT_STATUS_UNSUCCESSFUL)) - result = msrpc_methods.query_user_list(domain, mem_ctx, - num_entries, info); - return result; -} - -/* list all domain groups */ -static NTSTATUS enum_dom_groups(struct winbindd_domain *domain, - TALLOC_CTX *mem_ctx, - uint32 *num_entries, - struct acct_info **info) -{ - NTSTATUS result; - - result = msrpc_methods.enum_dom_groups(domain, mem_ctx, - num_entries, info); - - if (NT_STATUS_EQUAL(result, NT_STATUS_UNSUCCESSFUL)) - result = msrpc_methods.enum_dom_groups(domain, mem_ctx, - num_entries, info); - return result; -} - -/* List all domain groups */ - -static NTSTATUS enum_local_groups(struct winbindd_domain *domain, - TALLOC_CTX *mem_ctx, - uint32 *num_entries, - struct acct_info **info) -{ - NTSTATUS result; - - result = msrpc_methods.enum_local_groups(domain, mem_ctx, - num_entries, info); - - if (NT_STATUS_EQUAL(result, NT_STATUS_UNSUCCESSFUL)) - result = msrpc_methods.enum_local_groups(domain, mem_ctx, - num_entries, info); - - return result; -} - -/* convert a single name to a sid in a domain */ -static NTSTATUS name_to_sid(struct winbindd_domain *domain, - TALLOC_CTX *mem_ctx, - enum winbindd_cmd orig_cmd, - const char *domain_name, - const char *name, - DOM_SID *sid, - enum lsa_SidType *type) -{ - NTSTATUS result; - - result = msrpc_methods.name_to_sid(domain, mem_ctx, orig_cmd, - domain_name, name, - sid, type); - - if (NT_STATUS_EQUAL(result, NT_STATUS_UNSUCCESSFUL)) - result = msrpc_methods.name_to_sid(domain, mem_ctx, orig_cmd, - domain_name, name, - sid, type); - - return result; -} - -/* - convert a domain SID to a user or group name -*/ -static NTSTATUS sid_to_name(struct winbindd_domain *domain, - TALLOC_CTX *mem_ctx, - const DOM_SID *sid, - char **domain_name, - char **name, - enum lsa_SidType *type) -{ - NTSTATUS result; - - result = msrpc_methods.sid_to_name(domain, mem_ctx, sid, - domain_name, name, type); - - if (NT_STATUS_EQUAL(result, NT_STATUS_UNSUCCESSFUL)) - result = msrpc_methods.sid_to_name(domain, mem_ctx, sid, - domain_name, name, type); - - return result; -} - -static NTSTATUS rids_to_names(struct winbindd_domain *domain, - TALLOC_CTX *mem_ctx, - const DOM_SID *sid, - uint32 *rids, - size_t num_rids, - char **domain_name, - char ***names, - enum lsa_SidType **types) -{ - NTSTATUS result; - - result = msrpc_methods.rids_to_names(domain, mem_ctx, sid, - rids, num_rids, - domain_name, names, types); - if (NT_STATUS_EQUAL(result, NT_STATUS_UNSUCCESSFUL)) { - result = msrpc_methods.rids_to_names(domain, mem_ctx, sid, - rids, num_rids, - domain_name, names, - types); - } - - return result; -} - -/* Lookup user information from a rid or username. */ -static NTSTATUS query_user(struct winbindd_domain *domain, - TALLOC_CTX *mem_ctx, - const DOM_SID *user_sid, - WINBIND_USERINFO *user_info) -{ - NTSTATUS result; - - result = msrpc_methods.query_user(domain, mem_ctx, user_sid, - user_info); - - if (NT_STATUS_EQUAL(result, NT_STATUS_UNSUCCESSFUL)) - result = msrpc_methods.query_user(domain, mem_ctx, user_sid, - user_info); - - return result; -} - -/* Lookup groups a user is a member of. I wish Unix had a call like this! */ -static NTSTATUS lookup_usergroups(struct winbindd_domain *domain, - TALLOC_CTX *mem_ctx, - const DOM_SID *user_sid, - uint32 *num_groups, DOM_SID **user_gids) -{ - NTSTATUS result; - - result = msrpc_methods.lookup_usergroups(domain, mem_ctx, - user_sid, num_groups, - user_gids); - - if (NT_STATUS_EQUAL(result, NT_STATUS_UNSUCCESSFUL)) - result = msrpc_methods.lookup_usergroups(domain, mem_ctx, - user_sid, num_groups, - user_gids); - - return result; -} - -static NTSTATUS lookup_useraliases(struct winbindd_domain *domain, - TALLOC_CTX *mem_ctx, - uint32 num_sids, const DOM_SID *sids, - uint32 *num_aliases, uint32 **alias_rids) -{ - NTSTATUS result; - - result = msrpc_methods.lookup_useraliases(domain, mem_ctx, - num_sids, sids, - num_aliases, - alias_rids); - - if (NT_STATUS_EQUAL(result, NT_STATUS_UNSUCCESSFUL)) - result = msrpc_methods.lookup_useraliases(domain, mem_ctx, - num_sids, sids, - num_aliases, - alias_rids); - - return result; -} - -/* Lookup group membership given a rid. */ -static NTSTATUS lookup_groupmem(struct winbindd_domain *domain, - TALLOC_CTX *mem_ctx, - const DOM_SID *group_sid, uint32 *num_names, - DOM_SID **sid_mem, char ***names, - uint32 **name_types) -{ - NTSTATUS result; - - result = msrpc_methods.lookup_groupmem(domain, mem_ctx, - group_sid, num_names, - sid_mem, names, - name_types); - - if (NT_STATUS_EQUAL(result, NT_STATUS_UNSUCCESSFUL)) - result = msrpc_methods.lookup_groupmem(domain, mem_ctx, - group_sid, num_names, - sid_mem, names, - name_types); - - return result; -} - -/* find the sequence number for a domain */ -static NTSTATUS sequence_number(struct winbindd_domain *domain, uint32 *seq) -{ - NTSTATUS result; - - result = msrpc_methods.sequence_number(domain, seq); - - if (NT_STATUS_EQUAL(result, NT_STATUS_UNSUCCESSFUL)) - result = msrpc_methods.sequence_number(domain, seq); - - return result; -} - -/* find the lockout policy of a domain */ -static NTSTATUS lockout_policy(struct winbindd_domain *domain, - TALLOC_CTX *mem_ctx, - SAM_UNK_INFO_12 *policy) -{ - NTSTATUS result; - - result = msrpc_methods.lockout_policy(domain, mem_ctx, policy); - - if (NT_STATUS_EQUAL(result, NT_STATUS_UNSUCCESSFUL)) - result = msrpc_methods.lockout_policy(domain, mem_ctx, policy); - - return result; -} - -/* find the password policy of a domain */ -static NTSTATUS password_policy(struct winbindd_domain *domain, - TALLOC_CTX *mem_ctx, - SAM_UNK_INFO_1 *policy) -{ - NTSTATUS result; - - result = msrpc_methods.password_policy(domain, mem_ctx, policy); - - if (NT_STATUS_EQUAL(result, NT_STATUS_UNSUCCESSFUL)) - result = msrpc_methods.password_policy(domain, mem_ctx, policy); - - return result; -} - -/* get a list of trusted domains */ -static NTSTATUS trusted_domains(struct winbindd_domain *domain, - TALLOC_CTX *mem_ctx, - uint32 *num_domains, - char ***names, - char ***alt_names, - DOM_SID **dom_sids) -{ - NTSTATUS result; - - result = msrpc_methods.trusted_domains(domain, mem_ctx, - num_domains, names, - alt_names, dom_sids); - - if (NT_STATUS_EQUAL(result, NT_STATUS_UNSUCCESSFUL)) - result = msrpc_methods.trusted_domains(domain, mem_ctx, - num_domains, names, - alt_names, dom_sids); - - return result; -} - -/* the rpc backend methods are exposed via this structure */ -struct winbindd_methods reconnect_methods = { - False, - query_user_list, - enum_dom_groups, - enum_local_groups, - name_to_sid, - sid_to_name, - rids_to_names, - query_user, - lookup_usergroups, - lookup_useraliases, - lookup_groupmem, - sequence_number, - lockout_policy, - password_policy, - trusted_domains, -}; diff --git a/source3/nsswitch/winbindd_rpc.c b/source3/nsswitch/winbindd_rpc.c deleted file mode 100644 index 6d2dd35080..0000000000 --- a/source3/nsswitch/winbindd_rpc.c +++ /dev/null @@ -1,1111 +0,0 @@ -/* - Unix SMB/CIFS implementation. - - Winbind rpc backend functions - - Copyright (C) Tim Potter 2000-2001,2003 - Copyright (C) Andrew Tridgell 2001 - Copyright (C) Volker Lendecke 2005 - - 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 3 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, see . -*/ - -#include "includes.h" -#include "winbindd.h" - -#undef DBGC_CLASS -#define DBGC_CLASS DBGC_WINBIND - - -/* Query display info for a domain. This returns enough information plus a - bit extra to give an overview of domain users for the User Manager - application. */ -static NTSTATUS query_user_list(struct winbindd_domain *domain, - TALLOC_CTX *mem_ctx, - uint32 *num_entries, - WINBIND_USERINFO **info) -{ - NTSTATUS result; - POLICY_HND dom_pol; - unsigned int i, start_idx; - uint32 loop_count; - struct rpc_pipe_client *cli; - - DEBUG(3,("rpc: query_user_list\n")); - - *num_entries = 0; - *info = NULL; - - if ( !winbindd_can_contact_domain( domain ) ) { - DEBUG(10,("query_user_list: No incoming trust for domain %s\n", - domain->name)); - return NT_STATUS_OK; - } - - result = cm_connect_sam(domain, mem_ctx, &cli, &dom_pol); - if (!NT_STATUS_IS_OK(result)) - return result; - - i = start_idx = 0; - loop_count = 0; - - do { - uint32 num_dom_users, j; - uint32 max_entries, max_size; - SAM_DISPINFO_CTR ctr; - SAM_DISPINFO_1 info1; - - ZERO_STRUCT( ctr ); - ZERO_STRUCT( info1 ); - ctr.sam.info1 = &info1; - - /* this next bit is copied from net_user_list_internal() */ - - get_query_dispinfo_params(loop_count, &max_entries, - &max_size); - - result = rpccli_samr_query_dispinfo(cli, mem_ctx, &dom_pol, - &start_idx, 1, - &num_dom_users, - max_entries, max_size, - &ctr); - - loop_count++; - - *num_entries += num_dom_users; - - *info = TALLOC_REALLOC_ARRAY(mem_ctx, *info, WINBIND_USERINFO, - *num_entries); - - if (!(*info)) { - return NT_STATUS_NO_MEMORY; - } - - for (j = 0; j < num_dom_users; i++, j++) { - fstring username, fullname; - uint32 rid = ctr.sam.info1->sam[j].rid_user; - - unistr2_to_ascii( username, &(&ctr.sam.info1->str[j])->uni_acct_name, sizeof(username)-1); - unistr2_to_ascii( fullname, &(&ctr.sam.info1->str[j])->uni_full_name, sizeof(fullname)-1); - - (*info)[i].acct_name = talloc_strdup(mem_ctx, username ); - (*info)[i].full_name = talloc_strdup(mem_ctx, fullname ); - (*info)[i].homedir = NULL; - (*info)[i].shell = NULL; - sid_compose(&(*info)[i].user_sid, &domain->sid, rid); - - /* For the moment we set the primary group for - every user to be the Domain Users group. - There are serious problems with determining - the actual primary group for large domains. - This should really be made into a 'winbind - force group' smb.conf parameter or - something like that. */ - - sid_compose(&(*info)[i].group_sid, &domain->sid, - DOMAIN_GROUP_RID_USERS); - } - - } while (NT_STATUS_EQUAL(result, STATUS_MORE_ENTRIES)); - - return result; -} - -/* list all domain groups */ -static NTSTATUS enum_dom_groups(struct winbindd_domain *domain, - TALLOC_CTX *mem_ctx, - uint32 *num_entries, - struct acct_info **info) -{ - POLICY_HND dom_pol; - NTSTATUS status; - uint32 start = 0; - struct rpc_pipe_client *cli; - - *num_entries = 0; - *info = NULL; - - DEBUG(3,("rpc: enum_dom_groups\n")); - - if ( !winbindd_can_contact_domain( domain ) ) { - DEBUG(10,("enum_domain_groups: No incoming trust for domain %s\n", - domain->name)); - return NT_STATUS_OK; - } - - status = cm_connect_sam(domain, mem_ctx, &cli, &dom_pol); - if (!NT_STATUS_IS_OK(status)) - return status; - - do { - struct acct_info *info2 = NULL; - uint32 count = 0; - TALLOC_CTX *mem_ctx2; - - mem_ctx2 = talloc_init("enum_dom_groups[rpc]"); - - /* start is updated by this call. */ - status = rpccli_samr_enum_dom_groups(cli, mem_ctx2, &dom_pol, - &start, - 0xFFFF, /* buffer size? */ - &info2, &count); - - if (!NT_STATUS_IS_OK(status) && - !NT_STATUS_EQUAL(status, STATUS_MORE_ENTRIES)) { - talloc_destroy(mem_ctx2); - break; - } - - (*info) = TALLOC_REALLOC_ARRAY(mem_ctx, *info, - struct acct_info, - (*num_entries) + count); - if (! *info) { - talloc_destroy(mem_ctx2); - return NT_STATUS_NO_MEMORY; - } - - memcpy(&(*info)[*num_entries], info2, count*sizeof(*info2)); - (*num_entries) += count; - talloc_destroy(mem_ctx2); - } while (NT_STATUS_EQUAL(status, STATUS_MORE_ENTRIES)); - - return NT_STATUS_OK; -} - -/* List all domain groups */ - -static NTSTATUS enum_local_groups(struct winbindd_domain *domain, - TALLOC_CTX *mem_ctx, - uint32 *num_entries, - struct acct_info **info) -{ - POLICY_HND dom_pol; - NTSTATUS result; - struct rpc_pipe_client *cli; - - *num_entries = 0; - *info = NULL; - - DEBUG(3,("rpc: enum_local_groups\n")); - - if ( !winbindd_can_contact_domain( domain ) ) { - DEBUG(10,("enum_local_groups: No incoming trust for domain %s\n", - domain->name)); - return NT_STATUS_OK; - } - - result = cm_connect_sam(domain, mem_ctx, &cli, &dom_pol); - if (!NT_STATUS_IS_OK(result)) - return result; - - do { - struct acct_info *info2 = NULL; - uint32 count = 0, start = *num_entries; - TALLOC_CTX *mem_ctx2; - - mem_ctx2 = talloc_init("enum_dom_local_groups[rpc]"); - - result = rpccli_samr_enum_als_groups( cli, mem_ctx2, &dom_pol, - &start, 0xFFFF, &info2, - &count); - - if (!NT_STATUS_IS_OK(result) && - !NT_STATUS_EQUAL(result, STATUS_MORE_ENTRIES) ) - { - talloc_destroy(mem_ctx2); - return result; - } - - (*info) = TALLOC_REALLOC_ARRAY(mem_ctx, *info, - struct acct_info, - (*num_entries) + count); - if (! *info) { - talloc_destroy(mem_ctx2); - return NT_STATUS_NO_MEMORY; - } - - memcpy(&(*info)[*num_entries], info2, count*sizeof(*info2)); - (*num_entries) += count; - talloc_destroy(mem_ctx2); - - } while (NT_STATUS_EQUAL(result, STATUS_MORE_ENTRIES)); - - return NT_STATUS_OK; -} - -/* convert a single name to a sid in a domain */ -NTSTATUS msrpc_name_to_sid(struct winbindd_domain *domain, - TALLOC_CTX *mem_ctx, - enum winbindd_cmd original_cmd, - const char *domain_name, - const char *name, - DOM_SID *sid, - enum lsa_SidType *type) -{ - NTSTATUS result; - DOM_SID *sids = NULL; - enum lsa_SidType *types = NULL; - char *full_name = NULL; - struct rpc_pipe_client *cli; - POLICY_HND lsa_policy; - - if (name == NULL || *name=='\0') { - full_name = talloc_asprintf(mem_ctx, "%s", domain_name); - } else if (domain_name == NULL || *domain_name == '\0') { - full_name = talloc_asprintf(mem_ctx, "%s", name); - } else { - full_name = talloc_asprintf(mem_ctx, "%s\\%s", domain_name, name); - } - if (!full_name) { - DEBUG(0, ("talloc_asprintf failed!\n")); - return NT_STATUS_NO_MEMORY; - } - - DEBUG(3,("rpc: name_to_sid name=%s\n", full_name)); - - ws_name_return( full_name, WB_REPLACE_CHAR ); - - DEBUG(3,("name_to_sid [rpc] %s for domain %s\n", full_name?full_name:"", domain_name )); - - result = cm_connect_lsa(domain, mem_ctx, &cli, &lsa_policy); - if (!NT_STATUS_IS_OK(result)) - return result; - - result = rpccli_lsa_lookup_names(cli, mem_ctx, &lsa_policy, 1, - (const char**) &full_name, NULL, 1, &sids, &types); - - if (!NT_STATUS_IS_OK(result)) - return result; - - /* Return rid and type if lookup successful */ - - sid_copy(sid, &sids[0]); - *type = types[0]; - - return NT_STATUS_OK; -} - -/* - convert a domain SID to a user or group name -*/ -NTSTATUS msrpc_sid_to_name(struct winbindd_domain *domain, - TALLOC_CTX *mem_ctx, - const DOM_SID *sid, - char **domain_name, - char **name, - enum lsa_SidType *type) -{ - char **domains; - char **names; - enum lsa_SidType *types; - NTSTATUS result; - struct rpc_pipe_client *cli; - POLICY_HND lsa_policy; - - DEBUG(3,("sid_to_name [rpc] %s for domain %s\n", sid_string_static(sid), - domain->name )); - - result = cm_connect_lsa(domain, mem_ctx, &cli, &lsa_policy); - if (!NT_STATUS_IS_OK(result)) { - DEBUG(2,("msrpc_sid_to_name: cm_connect_lsa() failed (%s)\n", - nt_errstr(result))); - return result; - } - - - result = rpccli_lsa_lookup_sids(cli, mem_ctx, &lsa_policy, - 1, sid, &domains, &names, &types); - if (!NT_STATUS_IS_OK(result)) { - DEBUG(2,("msrpc_sid_to_name: rpccli_lsa_lookup_sids() failed (%s)\n", - nt_errstr(result))); - return result; - } - - *type = (enum lsa_SidType)types[0]; - *domain_name = domains[0]; - *name = names[0]; - - ws_name_replace( *name, WB_REPLACE_CHAR ); - - DEBUG(5,("Mapped sid to [%s]\\[%s]\n", domains[0], *name)); - return NT_STATUS_OK; -} - -NTSTATUS msrpc_rids_to_names(struct winbindd_domain *domain, - TALLOC_CTX *mem_ctx, - const DOM_SID *sid, - uint32 *rids, - size_t num_rids, - char **domain_name, - char ***names, - enum lsa_SidType **types) -{ - char **domains; - NTSTATUS result; - struct rpc_pipe_client *cli; - POLICY_HND lsa_policy; - DOM_SID *sids; - size_t i; - char **ret_names; - - DEBUG(3, ("rids_to_names [rpc] for domain %s\n", domain->name )); - - if (num_rids) { - sids = TALLOC_ARRAY(mem_ctx, DOM_SID, num_rids); - if (sids == NULL) { - return NT_STATUS_NO_MEMORY; - } - } else { - sids = NULL; - } - - for (i=0; isid, user_sid, &user_rid)) - return NT_STATUS_UNSUCCESSFUL; - - user_info->homedir = NULL; - user_info->shell = NULL; - user_info->primary_gid = (gid_t)-1; - - /* try netsamlogon cache first */ - - if ( (user = netsamlogon_cache_get( mem_ctx, user_sid )) != NULL ) - { - - DEBUG(5,("query_user: Cache lookup succeeded for %s\n", - sid_string_static(user_sid))); - - sid_compose(&user_info->user_sid, &domain->sid, user->user_rid); - sid_compose(&user_info->group_sid, &domain->sid, - user->group_rid); - - user_info->acct_name = unistr2_tdup(mem_ctx, - &user->uni_user_name); - user_info->full_name = unistr2_tdup(mem_ctx, - &user->uni_full_name); - - TALLOC_FREE(user); - - return NT_STATUS_OK; - } - - if ( !winbindd_can_contact_domain( domain ) ) { - DEBUG(10,("query_user: No incoming trust for domain %s\n", - domain->name)); - return NT_STATUS_OK; - } - - if ( !winbindd_can_contact_domain( domain ) ) { - DEBUG(10,("query_user: No incoming trust for domain %s\n", - domain->name)); - return NT_STATUS_OK; - } - - /* no cache; hit the wire */ - - result = cm_connect_sam(domain, mem_ctx, &cli, &dom_pol); - if (!NT_STATUS_IS_OK(result)) - return result; - - /* Get user handle */ - result = rpccli_samr_open_user(cli, mem_ctx, &dom_pol, - SEC_RIGHTS_MAXIMUM_ALLOWED, user_rid, - &user_pol); - - if (!NT_STATUS_IS_OK(result)) - return result; - - /* Get user info */ - result = rpccli_samr_query_userinfo(cli, mem_ctx, &user_pol, - 0x15, &ctr); - - rpccli_samr_close(cli, mem_ctx, &user_pol); - - if (!NT_STATUS_IS_OK(result)) - return result; - - sid_compose(&user_info->user_sid, &domain->sid, user_rid); - sid_compose(&user_info->group_sid, &domain->sid, - ctr->info.id21->group_rid); - user_info->acct_name = unistr2_tdup(mem_ctx, - &ctr->info.id21->uni_user_name); - user_info->full_name = unistr2_tdup(mem_ctx, - &ctr->info.id21->uni_full_name); - user_info->homedir = NULL; - user_info->shell = NULL; - user_info->primary_gid = (gid_t)-1; - - return NT_STATUS_OK; -} - -/* Lookup groups a user is a member of. I wish Unix had a call like this! */ -static NTSTATUS lookup_usergroups(struct winbindd_domain *domain, - TALLOC_CTX *mem_ctx, - const DOM_SID *user_sid, - uint32 *num_groups, DOM_SID **user_grpsids) -{ - NTSTATUS result = NT_STATUS_UNSUCCESSFUL; - POLICY_HND dom_pol, user_pol; - uint32 des_access = SEC_RIGHTS_MAXIMUM_ALLOWED; - DOM_GID *user_groups; - unsigned int i; - fstring sid_string; - uint32 user_rid; - struct rpc_pipe_client *cli; - - DEBUG(3,("rpc: lookup_usergroups sid=%s\n", - sid_to_string(sid_string, user_sid))); - - if (!sid_peek_check_rid(&domain->sid, user_sid, &user_rid)) - return NT_STATUS_UNSUCCESSFUL; - - *num_groups = 0; - *user_grpsids = NULL; - - /* so lets see if we have a cached user_info_3 */ - result = lookup_usergroups_cached(domain, mem_ctx, user_sid, - num_groups, user_grpsids); - - if (NT_STATUS_IS_OK(result)) { - return NT_STATUS_OK; - } - - if ( !winbindd_can_contact_domain( domain ) ) { - DEBUG(10,("lookup_usergroups: No incoming trust for domain %s\n", - domain->name)); - - /* Tell the cache manager not to remember this one */ - - return NT_STATUS_SYNCHRONIZATION_REQUIRED; - } - - /* no cache; hit the wire */ - - result = cm_connect_sam(domain, mem_ctx, &cli, &dom_pol); - if (!NT_STATUS_IS_OK(result)) - return result; - - /* Get user handle */ - result = rpccli_samr_open_user(cli, mem_ctx, &dom_pol, - des_access, user_rid, &user_pol); - - if (!NT_STATUS_IS_OK(result)) - return result; - - /* Query user rids */ - result = rpccli_samr_query_usergroups(cli, mem_ctx, &user_pol, - num_groups, &user_groups); - - rpccli_samr_close(cli, mem_ctx, &user_pol); - - if (!NT_STATUS_IS_OK(result) || (*num_groups) == 0) - return result; - - (*user_grpsids) = TALLOC_ARRAY(mem_ctx, DOM_SID, *num_groups); - if (!(*user_grpsids)) - return NT_STATUS_NO_MEMORY; - - for (i=0;i<(*num_groups);i++) { - sid_copy(&((*user_grpsids)[i]), &domain->sid); - sid_append_rid(&((*user_grpsids)[i]), - user_groups[i].g_rid); - } - - return NT_STATUS_OK; -} - -NTSTATUS msrpc_lookup_useraliases(struct winbindd_domain *domain, - TALLOC_CTX *mem_ctx, - uint32 num_sids, const DOM_SID *sids, - uint32 *num_aliases, uint32 **alias_rids) -{ - NTSTATUS result = NT_STATUS_UNSUCCESSFUL; - POLICY_HND dom_pol; - DOM_SID2 *query_sids; - uint32 num_query_sids = 0; - int i; - struct rpc_pipe_client *cli; - uint32 *alias_rids_query, num_aliases_query; - int rangesize = MAX_SAM_ENTRIES_W2K; - uint32 total_sids = 0; - int num_queries = 1; - - *num_aliases = 0; - *alias_rids = NULL; - - DEBUG(3,("rpc: lookup_useraliases\n")); - - if ( !winbindd_can_contact_domain( domain ) ) { - DEBUG(10,("msrpc_lookup_useraliases: No incoming trust for domain %s\n", - domain->name)); - return NT_STATUS_OK; - } - - result = cm_connect_sam(domain, mem_ctx, &cli, &dom_pol); - if (!NT_STATUS_IS_OK(result)) - return result; - - do { - /* prepare query */ - - num_query_sids = MIN(num_sids - total_sids, rangesize); - - DEBUG(10,("rpc: lookup_useraliases: entering query %d for %d sids\n", - num_queries, num_query_sids)); - - if (num_query_sids) { - query_sids = TALLOC_ARRAY(mem_ctx, DOM_SID2, num_query_sids); - if (query_sids == NULL) { - return NT_STATUS_NO_MEMORY; - } - } else { - query_sids = NULL; - } - - for (i=0; iname, - sid_to_string(sid_string, group_sid))); - - if ( !winbindd_can_contact_domain( domain ) ) { - DEBUG(10,("lookup_groupmem: No incoming trust for domain %s\n", - domain->name)); - return NT_STATUS_OK; - } - - if (!sid_peek_check_rid(&domain->sid, group_sid, &group_rid)) - return NT_STATUS_UNSUCCESSFUL; - - *num_names = 0; - - result = cm_connect_sam(domain, mem_ctx, &cli, &dom_pol); - if (!NT_STATUS_IS_OK(result)) - return result; - - result = rpccli_samr_open_group(cli, mem_ctx, &dom_pol, - des_access, group_rid, &group_pol); - - if (!NT_STATUS_IS_OK(result)) - return result; - - /* Step #1: Get a list of user rids that are the members of the - group. */ - - /* This call can take a long time - allow the server to time out. - 35 seconds should do it. */ - - orig_timeout = cli_set_timeout(cli->cli, 35000); - - result = rpccli_samr_query_groupmem(cli, mem_ctx, - &group_pol, num_names, &rid_mem, - name_types); - - /* And restore our original timeout. */ - cli_set_timeout(cli->cli, orig_timeout); - - rpccli_samr_close(cli, mem_ctx, &group_pol); - - if (!NT_STATUS_IS_OK(result)) - return result; - - if (!*num_names) { - names = NULL; - name_types = NULL; - sid_mem = NULL; - return NT_STATUS_OK; - } - - /* Step #2: Convert list of rids into list of usernames. Do this - in bunches of ~1000 to avoid crashing NT4. It looks like there - is a buffer overflow or something like that lurking around - somewhere. */ - -#define MAX_LOOKUP_RIDS 900 - - *names = TALLOC_ZERO_ARRAY(mem_ctx, char *, *num_names); - *name_types = TALLOC_ZERO_ARRAY(mem_ctx, uint32, *num_names); - *sid_mem = TALLOC_ZERO_ARRAY(mem_ctx, DOM_SID, *num_names); - - for (j=0;j<(*num_names);j++) - sid_compose(&(*sid_mem)[j], &domain->sid, rid_mem[j]); - - if (*num_names>0 && (!*names || !*name_types)) - return NT_STATUS_NO_MEMORY; - - for (i = 0; i < *num_names; i += MAX_LOOKUP_RIDS) { - int num_lookup_rids = MIN(*num_names - i, MAX_LOOKUP_RIDS); - uint32 tmp_num_names = 0; - char **tmp_names = NULL; - uint32 *tmp_types = NULL; - - /* Lookup a chunk of rids */ - - result = rpccli_samr_lookup_rids(cli, mem_ctx, - &dom_pol, - num_lookup_rids, - &rid_mem[i], - &tmp_num_names, - &tmp_names, &tmp_types); - - /* see if we have a real error (and yes the - STATUS_SOME_UNMAPPED is the one returned from 2k) */ - - if (!NT_STATUS_IS_OK(result) && - !NT_STATUS_EQUAL(result, STATUS_SOME_UNMAPPED)) - return result; - - /* Copy result into array. The talloc system will take - care of freeing the temporary arrays later on. */ - - memcpy(&(*names)[i], tmp_names, sizeof(char *) * - tmp_num_names); - - memcpy(&(*name_types)[i], tmp_types, sizeof(uint32) * - tmp_num_names); - - total_names += tmp_num_names; - } - - *num_names = total_names; - - return NT_STATUS_OK; -} - -#ifdef HAVE_LDAP - -#include - -static int get_ldap_seq(const char *server, int port, uint32 *seq) -{ - int ret = -1; - struct timeval to; - const char *attrs[] = {"highestCommittedUSN", NULL}; - LDAPMessage *res = NULL; - char **values = NULL; - LDAP *ldp = NULL; - - *seq = DOM_SEQUENCE_NONE; - - /* - * Parameterised (5) second timeout on open. This is needed as the - * search timeout doesn't seem to apply to doing an open as well. JRA. - */ - - ldp = ldap_open_with_timeout(server, port, lp_ldap_timeout()); - if (ldp == NULL) - return -1; - - /* Timeout if no response within 20 seconds. */ - to.tv_sec = 10; - to.tv_usec = 0; - - if (ldap_search_st(ldp, "", LDAP_SCOPE_BASE, "(objectclass=*)", - CONST_DISCARD(char **, attrs), 0, &to, &res)) - goto done; - - if (ldap_count_entries(ldp, res) != 1) - goto done; - - values = ldap_get_values(ldp, res, "highestCommittedUSN"); - if (!values || !values[0]) - goto done; - - *seq = atoi(values[0]); - ret = 0; - - done: - - if (values) - ldap_value_free(values); - if (res) - ldap_msgfree(res); - if (ldp) - ldap_unbind(ldp); - return ret; -} - -/********************************************************************** - Get the sequence number for a Windows AD native mode domain using - LDAP queries. -**********************************************************************/ - -static int get_ldap_sequence_number(struct winbindd_domain *domain, uint32 *seq) -{ - int ret = -1; - fstring ipstr; - - fstrcpy( ipstr, inet_ntoa(domain->dcaddr.sin_addr)); - if ((ret = get_ldap_seq( ipstr, LDAP_PORT, seq)) == 0) { - DEBUG(3, ("get_ldap_sequence_number: Retrieved sequence " - "number for Domain (%s) from DC (%s)\n", - domain->name, ipstr)); - } - return ret; -} - -#endif /* HAVE_LDAP */ - -/* find the sequence number for a domain */ -static NTSTATUS sequence_number(struct winbindd_domain *domain, uint32 *seq) -{ - TALLOC_CTX *mem_ctx; - SAM_UNK_CTR ctr; - NTSTATUS result; - POLICY_HND dom_pol; - BOOL got_seq_num = False; - struct rpc_pipe_client *cli; - - DEBUG(10,("rpc: fetch sequence_number for %s\n", domain->name)); - - if ( !winbindd_can_contact_domain( domain ) ) { - DEBUG(10,("sequence_number: No incoming trust for domain %s\n", - domain->name)); - *seq = time(NULL); - return NT_STATUS_OK; - } - - *seq = DOM_SEQUENCE_NONE; - - if (!(mem_ctx = talloc_init("sequence_number[rpc]"))) - return NT_STATUS_NO_MEMORY; - -#ifdef HAVE_LDAP - if ( domain->active_directory ) - { - int res; - - DEBUG(8,("using get_ldap_seq() to retrieve the " - "sequence number\n")); - - res = get_ldap_sequence_number( domain, seq ); - if (res == 0) - { - result = NT_STATUS_OK; - DEBUG(10,("domain_sequence_number: LDAP for " - "domain %s is %u\n", - domain->name, *seq)); - goto done; - } - - DEBUG(10,("domain_sequence_number: failed to get LDAP " - "sequence number for domain %s\n", - domain->name )); - } -#endif /* HAVE_LDAP */ - - result = cm_connect_sam(domain, mem_ctx, &cli, &dom_pol); - if (!NT_STATUS_IS_OK(result)) { - goto done; - } - - /* Query domain info */ - - result = rpccli_samr_query_dom_info(cli, mem_ctx, &dom_pol, 8, &ctr); - - if (NT_STATUS_IS_OK(result)) { - *seq = ctr.info.inf8.seq_num; - got_seq_num = True; - goto seq_num; - } - - /* retry with info-level 2 in case the dc does not support info-level 8 - * (like all older samba2 and samba3 dc's - Guenther */ - - result = rpccli_samr_query_dom_info(cli, mem_ctx, &dom_pol, 2, &ctr); - - if (NT_STATUS_IS_OK(result)) { - *seq = ctr.info.inf2.seq_num; - got_seq_num = True; - } - - seq_num: - if (got_seq_num) { - DEBUG(10,("domain_sequence_number: for domain %s is %u\n", - domain->name, (unsigned)*seq)); - } else { - DEBUG(10,("domain_sequence_number: failed to get sequence " - "number (%u) for domain %s\n", - (unsigned)*seq, domain->name )); - } - - done: - - talloc_destroy(mem_ctx); - - return result; -} - -/* get a list of trusted domains */ -static NTSTATUS trusted_domains(struct winbindd_domain *domain, - TALLOC_CTX *mem_ctx, - uint32 *num_domains, - char ***names, - char ***alt_names, - DOM_SID **dom_sids) -{ - NTSTATUS result = NT_STATUS_UNSUCCESSFUL; - uint32 enum_ctx = 0; - struct rpc_pipe_client *cli; - POLICY_HND lsa_policy; - - DEBUG(3,("rpc: trusted_domains\n")); - - *num_domains = 0; - *names = NULL; - *alt_names = NULL; - *dom_sids = NULL; - - result = cm_connect_lsa(domain, mem_ctx, &cli, &lsa_policy); - if (!NT_STATUS_IS_OK(result)) - return result; - - result = STATUS_MORE_ENTRIES; - - while (NT_STATUS_EQUAL(result, STATUS_MORE_ENTRIES)) { - uint32 start_idx, num; - char **tmp_names; - DOM_SID *tmp_sids; - int i; - - result = rpccli_lsa_enum_trust_dom(cli, mem_ctx, - &lsa_policy, &enum_ctx, - &num, &tmp_names, - &tmp_sids); - - if (!NT_STATUS_IS_OK(result) && - !NT_STATUS_EQUAL(result, STATUS_MORE_ENTRIES)) - break; - - start_idx = *num_domains; - *num_domains += num; - *names = TALLOC_REALLOC_ARRAY(mem_ctx, *names, - char *, *num_domains); - *dom_sids = TALLOC_REALLOC_ARRAY(mem_ctx, *dom_sids, - DOM_SID, *num_domains); - *alt_names = TALLOC_REALLOC_ARRAY(mem_ctx, *alt_names, - char *, *num_domains); - if ((*names == NULL) || (*dom_sids == NULL) || - (*alt_names == NULL)) - return NT_STATUS_NO_MEMORY; - - for (i=0; iname)); - - if ( !winbindd_can_contact_domain( domain ) ) { - DEBUG(10,("msrpc_lockout_policy: No incoming trust for domain %s\n", - domain->name)); - return NT_STATUS_NOT_SUPPORTED; - } - - result = cm_connect_sam(domain, mem_ctx, &cli, &dom_pol); - if (!NT_STATUS_IS_OK(result)) { - goto done; - } - - result = rpccli_samr_query_dom_info(cli, mem_ctx, &dom_pol, 12, &ctr); - if (!NT_STATUS_IS_OK(result)) { - goto done; - } - - *lockout_policy = ctr.info.inf12; - - DEBUG(10,("msrpc_lockout_policy: bad_attempt_lockout %d\n", - ctr.info.inf12.bad_attempt_lockout)); - - done: - - return result; -} - -/* find the password policy for a domain */ -NTSTATUS msrpc_password_policy(struct winbindd_domain *domain, - TALLOC_CTX *mem_ctx, - SAM_UNK_INFO_1 *password_policy) -{ - NTSTATUS result; - struct rpc_pipe_client *cli; - POLICY_HND dom_pol; - SAM_UNK_CTR ctr; - - DEBUG(10,("rpc: fetch password policy for %s\n", domain->name)); - - if ( !winbindd_can_contact_domain( domain ) ) { - DEBUG(10,("msrpc_password_policy: No incoming trust for domain %s\n", - domain->name)); - return NT_STATUS_NOT_SUPPORTED; - } - - result = cm_connect_sam(domain, mem_ctx, &cli, &dom_pol); - if (!NT_STATUS_IS_OK(result)) { - goto done; - } - - result = rpccli_samr_query_dom_info(cli, mem_ctx, &dom_pol, 1, &ctr); - if (!NT_STATUS_IS_OK(result)) { - goto done; - } - - *password_policy = ctr.info.inf1; - - DEBUG(10,("msrpc_password_policy: min_length_password %d\n", - ctr.info.inf1.min_length_password)); - - done: - - return result; -} - - -/* the rpc backend methods are exposed via this structure */ -struct winbindd_methods msrpc_methods = { - False, - query_user_list, - enum_dom_groups, - enum_local_groups, - msrpc_name_to_sid, - msrpc_sid_to_name, - msrpc_rids_to_names, - query_user, - lookup_usergroups, - msrpc_lookup_useraliases, - lookup_groupmem, - sequence_number, - msrpc_lockout_policy, - msrpc_password_policy, - trusted_domains, -}; diff --git a/source3/nsswitch/winbindd_sid.c b/source3/nsswitch/winbindd_sid.c deleted file mode 100644 index 48e84d35e5..0000000000 --- a/source3/nsswitch/winbindd_sid.c +++ /dev/null @@ -1,560 +0,0 @@ -/* - Unix SMB/CIFS implementation. - - Winbind daemon - sid related functions - - Copyright (C) Tim Potter 2000 - - 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 3 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, see . -*/ - -#include "includes.h" -#include "winbindd.h" - -#undef DBGC_CLASS -#define DBGC_CLASS DBGC_WINBIND - -/* Convert a string */ - -static void lookupsid_recv(void *private_data, BOOL success, - const char *dom_name, const char *name, - enum lsa_SidType type); - -void winbindd_lookupsid(struct winbindd_cli_state *state) -{ - DOM_SID sid; - - /* Ensure null termination */ - state->request.data.sid[sizeof(state->request.data.sid)-1]='\0'; - - DEBUG(3, ("[%5lu]: lookupsid %s\n", (unsigned long)state->pid, - state->request.data.sid)); - - if (!string_to_sid(&sid, state->request.data.sid)) { - DEBUG(5, ("%s not a SID\n", state->request.data.sid)); - request_error(state); - return; - } - - winbindd_lookupsid_async(state->mem_ctx, &sid, lookupsid_recv, state); -} - -static void lookupsid_recv(void *private_data, BOOL success, - const char *dom_name, const char *name, - enum lsa_SidType type) -{ - struct winbindd_cli_state *state = - talloc_get_type_abort(private_data, struct winbindd_cli_state); - - if (!success) { - DEBUG(5, ("lookupsid returned an error\n")); - request_error(state); - return; - } - - fstrcpy(state->response.data.name.dom_name, dom_name); - fstrcpy(state->response.data.name.name, name); - state->response.data.name.type = type; - request_ok(state); -} - -/** - * Look up the SID for a qualified name. - **/ - -static void lookupname_recv(void *private_data, BOOL success, - const DOM_SID *sid, enum lsa_SidType type); - -void winbindd_lookupname(struct winbindd_cli_state *state) -{ - char *name_domain, *name_user; - char *p; - - /* Ensure null termination */ - state->request.data.name.dom_name[sizeof(state->request.data.name.dom_name)-1]='\0'; - - /* Ensure null termination */ - state->request.data.name.name[sizeof(state->request.data.name.name)-1]='\0'; - - /* cope with the name being a fully qualified name */ - p = strstr(state->request.data.name.name, lp_winbind_separator()); - if (p) { - *p = 0; - name_domain = state->request.data.name.name; - name_user = p+1; - } else { - name_domain = state->request.data.name.dom_name; - name_user = state->request.data.name.name; - } - - DEBUG(3, ("[%5lu]: lookupname %s%s%s\n", (unsigned long)state->pid, - name_domain, lp_winbind_separator(), name_user)); - - winbindd_lookupname_async(state->mem_ctx, name_domain, name_user, - lookupname_recv, WINBINDD_LOOKUPNAME, - state); -} - -static void lookupname_recv(void *private_data, BOOL success, - const DOM_SID *sid, enum lsa_SidType type) -{ - struct winbindd_cli_state *state = - talloc_get_type_abort(private_data, struct winbindd_cli_state); - - if (!success) { - DEBUG(5, ("lookupname returned an error\n")); - request_error(state); - return; - } - - sid_to_string(state->response.data.sid.sid, sid); - state->response.data.sid.type = type; - request_ok(state); - return; -} - -void winbindd_lookuprids(struct winbindd_cli_state *state) -{ - struct winbindd_domain *domain; - DOM_SID domain_sid; - - /* Ensure null termination */ - state->request.data.sid[sizeof(state->request.data.sid)-1]='\0'; - - DEBUG(10, ("lookup_rids: %s\n", state->request.data.sid)); - - if (!string_to_sid(&domain_sid, state->request.data.sid)) { - DEBUG(5, ("Could not convert %s to SID\n", - state->request.data.sid)); - request_error(state); - return; - } - - domain = find_lookup_domain_from_sid(&domain_sid); - if (domain == NULL) { - DEBUG(10, ("Could not find domain for name %s\n", - state->request.domain_name)); - request_error(state); - return; - } - - sendto_domain(state, domain); -} - -static struct winbindd_child static_idmap_child; - -void init_idmap_child(void) -{ - setup_domain_child(NULL, &static_idmap_child, "idmap"); -} - -struct winbindd_child *idmap_child(void) -{ - return &static_idmap_child; -} - -/* Convert a sid to a uid. We assume we only have one rid attached to the - sid. */ - -static void sid2uid_recv(void *private_data, BOOL success, uid_t uid) -{ - struct winbindd_cli_state *state = - talloc_get_type_abort(private_data, struct winbindd_cli_state); - - if (!success) { - DEBUG(5, ("Could not convert sid %s\n", - state->request.data.sid)); - request_error(state); - return; - } - - state->response.data.uid = uid; - request_ok(state); -} - -static void sid2uid_lookupsid_recv( void *private_data, BOOL success, - const char *domain_name, - const char *name, - enum lsa_SidType type) -{ - struct winbindd_cli_state *state = - talloc_get_type_abort(private_data, struct winbindd_cli_state); - DOM_SID sid; - - if (!success) { - DEBUG(5, ("sid2uid_lookupsid_recv Could not convert get sid type for %s\n", - state->request.data.sid)); - request_error(state); - return; - } - - if ( (type!=SID_NAME_USER) && (type!=SID_NAME_COMPUTER) ) { - DEBUG(5,("sid2uid_lookupsid_recv: Sid %s is not a user or a computer.\n", - state->request.data.sid)); - request_error(state); - return; - } - - if (!string_to_sid(&sid, state->request.data.sid)) { - DEBUG(1, ("sid2uid_lookupsid_recv: Could not get convert sid %s from string\n", - state->request.data.sid)); - request_error(state); - return; - } - - /* always use the async interface (may block) */ - winbindd_sid2uid_async(state->mem_ctx, &sid, sid2uid_recv, state); -} - -void winbindd_sid_to_uid(struct winbindd_cli_state *state) -{ - DOM_SID sid; - - /* Ensure null termination */ - state->request.data.sid[sizeof(state->request.data.sid)-1]='\0'; - - DEBUG(3, ("[%5lu]: sid to uid %s\n", (unsigned long)state->pid, - state->request.data.sid)); - - if (!string_to_sid(&sid, state->request.data.sid)) { - DEBUG(1, ("Could not get convert sid %s from string\n", - state->request.data.sid)); - request_error(state); - return; - } - - /* Validate the SID as a user. Hopefully this will hit cache. - Needed to prevent DoS by exhausting the uid allocation - range from random SIDs. */ - - winbindd_lookupsid_async( state->mem_ctx, &sid, sid2uid_lookupsid_recv, state ); -} - -/* Convert a sid to a gid. We assume we only have one rid attached to the - sid.*/ - -static void sid2gid_recv(void *private_data, BOOL success, gid_t gid) -{ - struct winbindd_cli_state *state = - talloc_get_type_abort(private_data, struct winbindd_cli_state); - - if (!success) { - DEBUG(5, ("Could not convert sid %s\n", - state->request.data.sid)); - request_error(state); - return; - } - - state->response.data.gid = gid; - request_ok(state); -} - -static void sid2gid_lookupsid_recv( void *private_data, BOOL success, - const char *domain_name, - const char *name, - enum lsa_SidType type) -{ - struct winbindd_cli_state *state = - talloc_get_type_abort(private_data, struct winbindd_cli_state); - DOM_SID sid; - - if (!success) { - DEBUG(5, ("sid2gid_lookupsid_recv: Could not get sid type for %s\n", - state->request.data.sid)); - request_error(state); - return; - } - - if ( (type!=SID_NAME_DOM_GRP) && - (type!=SID_NAME_ALIAS) && - (type!=SID_NAME_WKN_GRP) ) - { - DEBUG(5,("sid2gid_lookupsid_recv: Sid %s is not a group.\n", - state->request.data.sid)); - request_error(state); - return; - } - - if (!string_to_sid(&sid, state->request.data.sid)) { - DEBUG(1, ("sid2gid_lookupsid_recv: Could not get convert sid %s from string\n", - state->request.data.sid)); - request_error(state); - return; - } - - /* always use the async interface (may block) */ - winbindd_sid2gid_async(state->mem_ctx, &sid, sid2gid_recv, state); -} - -void winbindd_sid_to_gid(struct winbindd_cli_state *state) -{ - DOM_SID sid; - - /* Ensure null termination */ - state->request.data.sid[sizeof(state->request.data.sid)-1]='\0'; - - DEBUG(3, ("[%5lu]: sid to gid %s\n", (unsigned long)state->pid, - state->request.data.sid)); - - if (!string_to_sid(&sid, state->request.data.sid)) { - DEBUG(1, ("Could not get convert sid %s from string\n", - state->request.data.sid)); - request_error(state); - return; - } - - /* Validate the SID as a group. Hopefully this will hit cache. - Needed to prevent DoS by exhausting the uid allocation - range from random SIDs. */ - - winbindd_lookupsid_async( state->mem_ctx, &sid, sid2gid_lookupsid_recv, state ); -} - -static void sids2xids_recv(void *private_data, BOOL success, void *data, int len) -{ - struct winbindd_cli_state *state = - talloc_get_type_abort(private_data, struct winbindd_cli_state); - - if (!success) { - DEBUG(5, ("Could not convert sids to xids\n")); - request_error(state); - return; - } - - state->response.extra_data.data = data; - state->response.length = sizeof(state->response) + len; - request_ok(state); -} - -void winbindd_sids_to_unixids(struct winbindd_cli_state *state) -{ - DEBUG(3, ("[%5lu]: sids to xids\n", (unsigned long)state->pid)); - - winbindd_sids2xids_async(state->mem_ctx, - state->request.extra_data.data, - state->request.extra_len, - sids2xids_recv, state); -} - -static void set_mapping_recv(void *private_data, BOOL success) -{ - struct winbindd_cli_state *state = - talloc_get_type_abort(private_data, struct winbindd_cli_state); - - if (!success) { - DEBUG(5, ("Could not set sid mapping\n")); - request_error(state); - return; - } - - request_ok(state); -} - -void winbindd_set_mapping(struct winbindd_cli_state *state) -{ - struct id_map map; - DOM_SID sid; - - DEBUG(3, ("[%5lu]: set id map\n", (unsigned long)state->pid)); - - if ( ! state->privileged) { - DEBUG(0, ("Only root is allowed to set mappings!\n")); - request_error(state); - return; - } - - if (!string_to_sid(&sid, state->request.data.dual_idmapset.sid)) { - DEBUG(1, ("Could not get convert sid %s from string\n", - state->request.data.sid)); - request_error(state); - return; - } - - map.sid = &sid; - map.xid.id = state->request.data.dual_idmapset.id; - map.xid.type = state->request.data.dual_idmapset.type; - - winbindd_set_mapping_async(state->mem_ctx, &map, - set_mapping_recv, state); -} - -static void set_hwm_recv(void *private_data, BOOL success) -{ - struct winbindd_cli_state *state = - talloc_get_type_abort(private_data, struct winbindd_cli_state); - - if (!success) { - DEBUG(5, ("Could not set sid mapping\n")); - request_error(state); - return; - } - - request_ok(state); -} - -void winbindd_set_hwm(struct winbindd_cli_state *state) -{ - struct unixid xid; - - DEBUG(3, ("[%5lu]: set hwm\n", (unsigned long)state->pid)); - - if ( ! state->privileged) { - DEBUG(0, ("Only root is allowed to set mappings!\n")); - request_error(state); - return; - } - - xid.id = state->request.data.dual_idmapset.id; - xid.type = state->request.data.dual_idmapset.type; - - winbindd_set_hwm_async(state->mem_ctx, &xid, set_hwm_recv, state); -} - -/* Convert a uid to a sid */ - -static void uid2sid_recv(void *private_data, BOOL success, const char *sid) -{ - struct winbindd_cli_state *state = - (struct winbindd_cli_state *)private_data; - - if (success) { - DEBUG(10,("uid2sid: uid %lu has sid %s\n", - (unsigned long)(state->request.data.uid), sid)); - fstrcpy(state->response.data.sid.sid, sid); - state->response.data.sid.type = SID_NAME_USER; - request_ok(state); - return; - } - - request_error(state); - return; -} - -void winbindd_uid_to_sid(struct winbindd_cli_state *state) -{ - DEBUG(3, ("[%5lu]: uid to sid %lu\n", (unsigned long)state->pid, - (unsigned long)state->request.data.uid)); - - /* always go via the async interface (may block) */ - winbindd_uid2sid_async(state->mem_ctx, state->request.data.uid, uid2sid_recv, state); -} - -/* Convert a gid to a sid */ - -static void gid2sid_recv(void *private_data, BOOL success, const char *sid) -{ - struct winbindd_cli_state *state = - (struct winbindd_cli_state *)private_data; - - if (success) { - DEBUG(10,("gid2sid: gid %lu has sid %s\n", - (unsigned long)(state->request.data.gid), sid)); - fstrcpy(state->response.data.sid.sid, sid); - state->response.data.sid.type = SID_NAME_DOM_GRP; - request_ok(state); - return; - } - - request_error(state); - return; -} - - -void winbindd_gid_to_sid(struct winbindd_cli_state *state) -{ - DEBUG(3, ("[%5lu]: gid to sid %lu\n", (unsigned long)state->pid, - (unsigned long)state->request.data.gid)); - - /* always use async calls (may block) */ - winbindd_gid2sid_async(state->mem_ctx, state->request.data.gid, gid2sid_recv, state); -} - -void winbindd_allocate_uid(struct winbindd_cli_state *state) -{ - if ( !state->privileged ) { - DEBUG(2, ("winbindd_allocate_uid: non-privileged access " - "denied!\n")); - request_error(state); - return; - } - - sendto_child(state, idmap_child()); -} - -enum winbindd_result winbindd_dual_allocate_uid(struct winbindd_domain *domain, - struct winbindd_cli_state *state) -{ - struct unixid xid; - - if (!NT_STATUS_IS_OK(idmap_allocate_uid(&xid))) { - return WINBINDD_ERROR; - } - state->response.data.uid = xid.id; - return WINBINDD_OK; -} - -void winbindd_allocate_gid(struct winbindd_cli_state *state) -{ - if ( !state->privileged ) { - DEBUG(2, ("winbindd_allocate_gid: non-privileged access " - "denied!\n")); - request_error(state); - return; - } - - sendto_child(state, idmap_child()); -} - -enum winbindd_result winbindd_dual_allocate_gid(struct winbindd_domain *domain, - struct winbindd_cli_state *state) -{ - struct unixid xid; - - if (!NT_STATUS_IS_OK(idmap_allocate_gid(&xid))) { - return WINBINDD_ERROR; - } - state->response.data.gid = xid.id; - return WINBINDD_OK; -} - -static void dump_maps_recv(void *private_data, BOOL success) -{ - struct winbindd_cli_state *state = - talloc_get_type_abort(private_data, struct winbindd_cli_state); - - if (!success) { - DEBUG(5, ("Could not dump maps\n")); - request_error(state); - return; - } - - request_ok(state); -} - -void winbindd_dump_maps(struct winbindd_cli_state *state) -{ - if ( ! state->privileged) { - DEBUG(0, ("Only root is allowed to ask for an idmap dump!\n")); - request_error(state); - return; - } - - DEBUG(3, ("[%5lu]: dump maps\n", (unsigned long)state->pid)); - - winbindd_dump_maps_async(state->mem_ctx, - state->request.extra_data.data, - state->request.extra_len, - dump_maps_recv, state); -} - diff --git a/source3/nsswitch/winbindd_sockinit.c b/source3/nsswitch/winbindd_sockinit.c deleted file mode 100644 index 50c53a5045..0000000000 --- a/source3/nsswitch/winbindd_sockinit.c +++ /dev/null @@ -1,126 +0,0 @@ -/* - Unix SMB/CIFS implementation. - Copyright (C) Tim Potter 2000-2001 - Copyright (C) 2001 by Martin Pool - Copyright (C) James Peach 2007 - - 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 3 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, see . -*/ - -#include "includes.h" -#include "winbindd.h" -#include "smb_launchd.h" - -#undef DBGC_CLASS -#define DBGC_CLASS DBGC_WINBIND - -/* Open the winbindd socket */ - -static int _winbindd_socket = -1; -static int _winbindd_priv_socket = -1; -static BOOL unlink_winbindd_socket = True; - -static int open_winbindd_socket(void) -{ - if (_winbindd_socket == -1) { - _winbindd_socket = create_pipe_sock( - WINBINDD_SOCKET_DIR, WINBINDD_SOCKET_NAME, 0755); - DEBUG(10, ("open_winbindd_socket: opened socket fd %d\n", - _winbindd_socket)); - } - - return _winbindd_socket; -} - -static int open_winbindd_priv_socket(void) -{ - if (_winbindd_priv_socket == -1) { - _winbindd_priv_socket = create_pipe_sock( - get_winbind_priv_pipe_dir(), WINBINDD_SOCKET_NAME, 0750); - DEBUG(10, ("open_winbindd_priv_socket: opened socket fd %d\n", - _winbindd_priv_socket)); - } - - return _winbindd_priv_socket; -} - -/* Close the winbindd socket */ - -static void close_winbindd_socket(void) -{ - if (_winbindd_socket != -1) { - DEBUG(10, ("close_winbindd_socket: closing socket fd %d\n", - _winbindd_socket)); - close(_winbindd_socket); - _winbindd_socket = -1; - } - if (_winbindd_priv_socket != -1) { - DEBUG(10, ("close_winbindd_socket: closing socket fd %d\n", - _winbindd_priv_socket)); - close(_winbindd_priv_socket); - _winbindd_priv_socket = -1; - } -} - -BOOL winbindd_init_sockets(int *public_sock, int *priv_sock, - int *idle_timeout_sec) -{ - struct smb_launch_info linfo; - - if (smb_launchd_checkin_names(&linfo, "WinbindPublicPipe", - "WinbindPrivilegedPipe", NULL)) { - if (linfo.num_sockets != 2) { - DEBUG(0, ("invalid launchd configuration, " - "expected 2 sockets but got %d\n", - linfo.num_sockets)); - return False; - } - - *public_sock = _winbindd_socket = linfo.socket_list[0]; - *priv_sock = _winbindd_priv_socket = linfo.socket_list[1]; - *idle_timeout_sec = linfo.idle_timeout_secs; - - unlink_winbindd_socket = False; - - smb_launchd_checkout(&linfo); - return True; - } else { - *public_sock = open_winbindd_socket(); - *priv_sock = open_winbindd_priv_socket(); - *idle_timeout_sec = -1; - - if (*public_sock == -1 || *priv_sock == -1) { - DEBUG(0, ("failed to open winbindd pipes: %s\n", - errno ? strerror(errno) : "unknown error")); - return False; - } - - return True; - } -} - -void winbindd_release_sockets(void) -{ - pstring path; - - close_winbindd_socket(); - - /* Remove socket file */ - if (unlink_winbindd_socket) { - pstr_sprintf(path, "%s/%s", - WINBINDD_SOCKET_DIR, WINBINDD_SOCKET_NAME); - unlink(path); - } -} - diff --git a/source3/nsswitch/winbindd_user.c b/source3/nsswitch/winbindd_user.c deleted file mode 100644 index fac2832f56..0000000000 --- a/source3/nsswitch/winbindd_user.c +++ /dev/null @@ -1,875 +0,0 @@ -/* - Unix SMB/CIFS implementation. - - Winbind daemon - user related functions - - Copyright (C) Tim Potter 2000 - Copyright (C) Jeremy Allison 2001. - Copyright (C) Gerald (Jerry) Carter 2003. - - 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 3 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, see . -*/ - -#include "includes.h" -#include "winbindd.h" - -#undef DBGC_CLASS -#define DBGC_CLASS DBGC_WINBIND - -static BOOL fillup_pw_field(const char *lp_template, - const char *username, - const char *domname, - uid_t uid, - gid_t gid, - const char *in, - fstring out) -{ - char *templ; - - if (out == NULL) - return False; - - /* The substitution of %U and %D in the 'template - homedir' is done by talloc_sub_specified() below. - If we have an in string (which means the value has already - been set in the nss_info backend), then use that. - Otherwise use the template value passed in. */ - - if ( in && !strequal(in,"") && lp_security() == SEC_ADS ) { - templ = talloc_sub_specified(NULL, in, - username, domname, - uid, gid); - } else { - templ = talloc_sub_specified(NULL, lp_template, - username, domname, - uid, gid); - } - - if (!templ) - return False; - - safe_strcpy(out, templ, sizeof(fstring) - 1); - TALLOC_FREE(templ); - - return True; - -} -/* Fill a pwent structure with information we have obtained */ - -static BOOL winbindd_fill_pwent(char *dom_name, char *user_name, - DOM_SID *user_sid, DOM_SID *group_sid, - char *full_name, char *homedir, char *shell, - struct winbindd_pw *pw) -{ - fstring output_username; - fstring sid_string; - - if (!pw || !dom_name || !user_name) - return False; - - /* Resolve the uid number */ - - if (!NT_STATUS_IS_OK(idmap_sid_to_uid(user_sid, &pw->pw_uid))) { - DEBUG(1, ("error getting user id for sid %s\n", sid_to_string(sid_string, user_sid))); - return False; - } - - /* Resolve the gid number */ - - if (!NT_STATUS_IS_OK(idmap_sid_to_gid(group_sid, &pw->pw_gid))) { - DEBUG(1, ("error getting group id for sid %s\n", sid_to_string(sid_string, group_sid))); - return False; - } - - strlower_m(user_name); - - /* Username */ - - fill_domain_username(output_username, dom_name, user_name, True); - - safe_strcpy(pw->pw_name, output_username, sizeof(pw->pw_name) - 1); - - /* Full name (gecos) */ - - safe_strcpy(pw->pw_gecos, full_name, sizeof(pw->pw_gecos) - 1); - - /* Home directory and shell */ - - if (!fillup_pw_field(lp_template_homedir(), user_name, dom_name, - pw->pw_uid, pw->pw_gid, homedir, pw->pw_dir)) - return False; - - if (!fillup_pw_field(lp_template_shell(), user_name, dom_name, - pw->pw_uid, pw->pw_gid, shell, pw->pw_shell)) - return False; - - /* Password - set to "*" as we can't generate anything useful here. - Authentication can be done using the pam_winbind module. */ - - safe_strcpy(pw->pw_passwd, "*", sizeof(pw->pw_passwd) - 1); - - return True; -} - -/* Wrapper for domain->methods->query_user, only on the parent->child pipe */ - -enum winbindd_result winbindd_dual_userinfo(struct winbindd_domain *domain, - struct winbindd_cli_state *state) -{ - DOM_SID sid; - WINBIND_USERINFO user_info; - NTSTATUS status; - - /* Ensure null termination */ - state->request.data.sid[sizeof(state->request.data.sid)-1]='\0'; - - DEBUG(3, ("[%5lu]: lookupsid %s\n", (unsigned long)state->pid, - state->request.data.sid)); - - if (!string_to_sid(&sid, state->request.data.sid)) { - DEBUG(5, ("%s not a SID\n", state->request.data.sid)); - return WINBINDD_ERROR; - } - - status = domain->methods->query_user(domain, state->mem_ctx, - &sid, &user_info); - if (!NT_STATUS_IS_OK(status)) { - DEBUG(1, ("error getting user info for sid %s\n", - sid_string_static(&sid))); - return WINBINDD_ERROR; - } - - fstrcpy(state->response.data.user_info.acct_name, user_info.acct_name); - fstrcpy(state->response.data.user_info.full_name, user_info.full_name); - fstrcpy(state->response.data.user_info.homedir, user_info.homedir); - fstrcpy(state->response.data.user_info.shell, user_info.shell); - state->response.data.user_info.primary_gid = user_info.primary_gid; - if (!sid_peek_check_rid(&domain->sid, &user_info.group_sid, - &state->response.data.user_info.group_rid)) { - DEBUG(1, ("Could not extract group rid out of %s\n", - sid_string_static(&sid))); - return WINBINDD_ERROR; - } - - return WINBINDD_OK; -} - -struct getpwsid_state { - struct winbindd_cli_state *state; - struct winbindd_domain *domain; - char *username; - char *fullname; - char *homedir; - char *shell; - DOM_SID user_sid; - uid_t uid; - DOM_SID group_sid; - gid_t gid; -}; - -static void getpwsid_queryuser_recv(void *private_data, BOOL success, - const char *acct_name, - const char *full_name, - const char *homedir, - const char *shell, - uint32 gid, - uint32 group_rid); -static void getpwsid_sid2uid_recv(void *private_data, BOOL success, uid_t uid); -static void getpwsid_sid2gid_recv(void *private_data, BOOL success, gid_t gid); - -static void winbindd_getpwsid(struct winbindd_cli_state *state, - const DOM_SID *sid) -{ - struct getpwsid_state *s; - - s = TALLOC_ZERO_P(state->mem_ctx, struct getpwsid_state); - if (s == NULL) { - DEBUG(0, ("talloc failed\n")); - goto error; - } - - s->state = state; - s->domain = find_domain_from_sid_noinit(sid); - if (s->domain == NULL) { - DEBUG(3, ("Could not find domain for sid %s\n", - sid_string_static(sid))); - goto error; - } - - sid_copy(&s->user_sid, sid); - - query_user_async(s->state->mem_ctx, s->domain, sid, - getpwsid_queryuser_recv, s); - return; - - error: - request_error(state); -} - -static void getpwsid_queryuser_recv(void *private_data, BOOL success, - const char *acct_name, - const char *full_name, - const char *homedir, - const char *shell, - uint32 gid, - uint32 group_rid) -{ - fstring username; - struct getpwsid_state *s = - talloc_get_type_abort(private_data, struct getpwsid_state); - - if (!success) { - DEBUG(5, ("Could not query domain %s SID %s\n", s->domain->name, - sid_string_static(&s->user_sid))); - request_error(s->state); - return; - } - - if ( acct_name && *acct_name ) { - fstrcpy( username, acct_name ); - } else { - char *domain_name = NULL; - enum lsa_SidType type; - char *user_name = NULL; - struct winbindd_domain *domain = NULL; - - domain = find_lookup_domain_from_sid(&s->user_sid); - if (domain == NULL) { - DEBUG(5, ("find_lookup_domain_from_sid(%s) failed\n", - sid_string_static(&s->user_sid))); - request_error(s->state); - return; - } - winbindd_lookup_name_by_sid(s->state->mem_ctx, domain, - &s->user_sid, &domain_name, - &user_name, &type ); - - /* If this still fails we ar4e done. Just error out */ - if ( !user_name ) { - DEBUG(5,("Could not obtain a name for SID %s\n", - sid_string_static(&s->user_sid))); - request_error(s->state); - return; - } - - fstrcpy( username, user_name ); - } - - strlower_m( username ); - s->username = talloc_strdup(s->state->mem_ctx, username); - - ws_name_replace( s->username, WB_REPLACE_CHAR ); - - s->fullname = talloc_strdup(s->state->mem_ctx, full_name); - s->homedir = talloc_strdup(s->state->mem_ctx, homedir); - s->shell = talloc_strdup(s->state->mem_ctx, shell); - s->gid = gid; - sid_copy(&s->group_sid, &s->domain->sid); - sid_append_rid(&s->group_sid, group_rid); - - winbindd_sid2uid_async(s->state->mem_ctx, &s->user_sid, - getpwsid_sid2uid_recv, s); -} - -static void getpwsid_sid2uid_recv(void *private_data, BOOL success, uid_t uid) -{ - struct getpwsid_state *s = - talloc_get_type_abort(private_data, struct getpwsid_state); - - if (!success) { - DEBUG(5, ("Could not query uid for user %s\\%s\n", - s->domain->name, s->username)); - request_error(s->state); - return; - } - - s->uid = uid; - winbindd_sid2gid_async(s->state->mem_ctx, &s->group_sid, - getpwsid_sid2gid_recv, s); -} - -static void getpwsid_sid2gid_recv(void *private_data, BOOL success, gid_t gid) -{ - struct getpwsid_state *s = - talloc_get_type_abort(private_data, struct getpwsid_state); - struct winbindd_pw *pw; - fstring output_username; - - /* allow the nss backend to override the primary group ID. - If the gid has already been set, then keep it. - This makes me feel dirty. If the nss backend already - gave us a gid, we don't really care whether the sid2gid() - call worked or not. --jerry */ - - if ( s->gid == (gid_t)-1 ) { - - if (!success) { - DEBUG(5, ("Could not query gid for user %s\\%s\n", - s->domain->name, s->username)); - goto failed; - } - - /* take what the sid2gid() call gave us */ - s->gid = gid; - } - - pw = &s->state->response.data.pw; - pw->pw_uid = s->uid; - pw->pw_gid = s->gid; - fill_domain_username(output_username, s->domain->name, s->username, True); - safe_strcpy(pw->pw_name, output_username, sizeof(pw->pw_name) - 1); - safe_strcpy(pw->pw_gecos, s->fullname, sizeof(pw->pw_gecos) - 1); - - if (!fillup_pw_field(lp_template_homedir(), s->username, s->domain->name, - pw->pw_uid, pw->pw_gid, s->homedir, pw->pw_dir)) { - DEBUG(5, ("Could not compose homedir\n")); - goto failed; - } - - if (!fillup_pw_field(lp_template_shell(), s->username, s->domain->name, - pw->pw_uid, pw->pw_gid, s->shell, pw->pw_shell)) { - DEBUG(5, ("Could not compose shell\n")); - goto failed; - } - - /* Password - set to "*" as we can't generate anything useful here. - Authentication can be done using the pam_winbind module. */ - - safe_strcpy(pw->pw_passwd, "*", sizeof(pw->pw_passwd) - 1); - - request_ok(s->state); - return; - - failed: - request_error(s->state); -} - -/* Return a password structure from a username. */ - -static void getpwnam_name2sid_recv(void *private_data, BOOL success, - const DOM_SID *sid, enum lsa_SidType type); - -void winbindd_getpwnam(struct winbindd_cli_state *state) -{ - struct winbindd_domain *domain; - fstring domname, username; - - /* Ensure null termination */ - state->request.data.username[sizeof(state->request.data.username)-1]='\0'; - - DEBUG(3, ("[%5lu]: getpwnam %s\n", (unsigned long)state->pid, - state->request.data.username)); - - ws_name_return( state->request.data.username, WB_REPLACE_CHAR ); - - if (!parse_domain_user(state->request.data.username, domname, - username)) { - DEBUG(5, ("Could not parse domain user: %s\n", - state->request.data.username)); - request_error(state); - return; - } - - /* Get info for the domain */ - - domain = find_domain_from_name(domname); - - if (domain == NULL) { - DEBUG(7, ("could not find domain entry for domain %s. " - "Using primary domain\n", domname)); - if ( (domain = find_our_domain()) == NULL ) { - DEBUG(0,("Cannot find my primary domain structure!\n")); - request_error(state); - return; - } - } - - if ( strequal(domname, lp_workgroup()) && lp_winbind_trusted_domains_only() ) { - DEBUG(7,("winbindd_getpwnam: My domain -- rejecting getpwnam() for %s\\%s.\n", - domname, username)); - request_error(state); - return; - } - - /* Get rid and name type from name. The following costs 1 packet */ - - winbindd_lookupname_async(state->mem_ctx, domname, username, - getpwnam_name2sid_recv, WINBINDD_GETPWNAM, - state); -} - -static void getpwnam_name2sid_recv(void *private_data, BOOL success, - const DOM_SID *sid, enum lsa_SidType type) -{ - struct winbindd_cli_state *state = - (struct winbindd_cli_state *)private_data; - fstring domname, username; - - if (!success) { - DEBUG(5, ("Could not lookup name for user %s\n", - state->request.data.username)); - request_error(state); - return; - } - - if ((type != SID_NAME_USER) && (type != SID_NAME_COMPUTER)) { - DEBUG(5, ("%s is not a user\n", state->request.data.username)); - request_error(state); - return; - } - - if ( parse_domain_user(state->request.data.username, domname, username) ) { - check_domain_trusted( domname, sid ); - } - - - - winbindd_getpwsid(state, sid); -} - -static void getpwuid_recv(void *private_data, BOOL success, const char *sid) -{ - struct winbindd_cli_state *state = - (struct winbindd_cli_state *)private_data; - DOM_SID user_sid; - - if (!success) { - DEBUG(10,("uid2sid_recv: uid [%lu] to sid mapping failed\n.", - (unsigned long)(state->request.data.uid))); - request_error(state); - return; - } - - DEBUG(10,("uid2sid_recv: uid %lu has sid %s\n", - (unsigned long)(state->request.data.uid), sid)); - - string_to_sid(&user_sid, sid); - winbindd_getpwsid(state, &user_sid); -} - -/* Return a password structure given a uid number */ -void winbindd_getpwuid(struct winbindd_cli_state *state) -{ - DEBUG(3, ("[%5lu]: getpwuid %lu\n", (unsigned long)state->pid, - (unsigned long)state->request.data.uid)); - - /* always query idmap via the async interface */ - /* if this turns to be too slow we will add here a direct query to the cache */ - winbindd_uid2sid_async(state->mem_ctx, state->request.data.uid, getpwuid_recv, state); -} - -/* - * set/get/endpwent functions - */ - -/* Rewind file pointer for ntdom passwd database */ - -static BOOL winbindd_setpwent_internal(struct winbindd_cli_state *state) -{ - struct winbindd_domain *domain; - - DEBUG(3, ("[%5lu]: setpwent\n", (unsigned long)state->pid)); - - /* Check user has enabled this */ - - if (!lp_winbind_enum_users()) { - return False; - } - - /* Free old static data if it exists */ - - if (state->getpwent_state != NULL) { - free_getent_state(state->getpwent_state); - state->getpwent_state = NULL; - } - -#if 0 /* JERRY */ - /* add any local users we have */ - - if ( (domain_state = (struct getent_state *)malloc(sizeof(struct getent_state))) == NULL ) - return False; - - ZERO_STRUCTP(domain_state); - - /* Add to list of open domains */ - - DLIST_ADD(state->getpwent_state, domain_state); -#endif - - /* Create sam pipes for each domain we know about */ - - for(domain = domain_list(); domain != NULL; domain = domain->next) { - struct getent_state *domain_state; - - - /* don't add our domaina if we are a PDC or if we - are a member of a Samba domain */ - - if ( (IS_DC || lp_winbind_trusted_domains_only()) - && strequal(domain->name, lp_workgroup()) ) - { - continue; - } - - /* Create a state record for this domain */ - - if ((domain_state = SMB_MALLOC_P(struct getent_state)) == NULL) { - DEBUG(0, ("malloc failed\n")); - return False; - } - - ZERO_STRUCTP(domain_state); - - fstrcpy(domain_state->domain_name, domain->name); - - /* Add to list of open domains */ - - DLIST_ADD(state->getpwent_state, domain_state); - } - - state->getpwent_initialized = True; - return True; -} - -void winbindd_setpwent(struct winbindd_cli_state *state) -{ - if (winbindd_setpwent_internal(state)) { - request_ok(state); - } else { - request_error(state); - } -} - -/* Close file pointer to ntdom passwd database */ - -void winbindd_endpwent(struct winbindd_cli_state *state) -{ - DEBUG(3, ("[%5lu]: endpwent\n", (unsigned long)state->pid)); - - free_getent_state(state->getpwent_state); - state->getpwent_initialized = False; - state->getpwent_state = NULL; - request_ok(state); -} - -/* Get partial list of domain users for a domain. We fill in the sam_entries, - and num_sam_entries fields with domain user information. The dispinfo_ndx - field is incremented to the index of the next user to fetch. Return True if - some users were returned, False otherwise. */ - -static BOOL get_sam_user_entries(struct getent_state *ent, TALLOC_CTX *mem_ctx) -{ - NTSTATUS status; - uint32 num_entries; - WINBIND_USERINFO *info; - struct getpwent_user *name_list = NULL; - struct winbindd_domain *domain; - struct winbindd_methods *methods; - unsigned int i; - - if (ent->num_sam_entries) - return False; - - if (!(domain = find_domain_from_name(ent->domain_name))) { - DEBUG(3, ("no such domain %s in get_sam_user_entries\n", - ent->domain_name)); - return False; - } - - methods = domain->methods; - - /* Free any existing user info */ - - SAFE_FREE(ent->sam_entries); - ent->num_sam_entries = 0; - - /* Call query_user_list to get a list of usernames and user rids */ - - num_entries = 0; - - status = methods->query_user_list(domain, mem_ctx, &num_entries, - &info); - - if (!NT_STATUS_IS_OK(status)) { - DEBUG(10,("get_sam_user_entries: query_user_list failed with %s\n", - nt_errstr(status) )); - return False; - } - - if (num_entries) { - name_list = SMB_REALLOC_ARRAY(name_list, struct getpwent_user, ent->num_sam_entries + num_entries); - - if (!name_list) { - DEBUG(0,("get_sam_user_entries realloc failed.\n")); - return False; - } - } - - for (i = 0; i < num_entries; i++) { - /* Store account name and gecos */ - if (!info[i].acct_name) { - fstrcpy(name_list[ent->num_sam_entries + i].name, ""); - } else { - fstrcpy(name_list[ent->num_sam_entries + i].name, - info[i].acct_name); - } - if (!info[i].full_name) { - fstrcpy(name_list[ent->num_sam_entries + i].gecos, ""); - } else { - fstrcpy(name_list[ent->num_sam_entries + i].gecos, - info[i].full_name); - } - if (!info[i].homedir) { - fstrcpy(name_list[ent->num_sam_entries + i].homedir, ""); - } else { - fstrcpy(name_list[ent->num_sam_entries + i].homedir, - info[i].homedir); - } - if (!info[i].shell) { - fstrcpy(name_list[ent->num_sam_entries + i].shell, ""); - } else { - fstrcpy(name_list[ent->num_sam_entries + i].shell, - info[i].shell); - } - - - /* User and group ids */ - sid_copy(&name_list[ent->num_sam_entries+i].user_sid, - &info[i].user_sid); - sid_copy(&name_list[ent->num_sam_entries+i].group_sid, - &info[i].group_sid); - } - - ent->num_sam_entries += num_entries; - - /* Fill in remaining fields */ - - ent->sam_entries = name_list; - ent->sam_entry_index = 0; - return ent->num_sam_entries > 0; -} - -/* Fetch next passwd entry from ntdom database */ - -#define MAX_GETPWENT_USERS 500 - -void winbindd_getpwent(struct winbindd_cli_state *state) -{ - struct getent_state *ent; - struct winbindd_pw *user_list; - int num_users, user_list_ndx; - - DEBUG(3, ("[%5lu]: getpwent\n", (unsigned long)state->pid)); - - /* Check user has enabled this */ - - if (!lp_winbind_enum_users()) { - request_error(state); - return; - } - - /* Allocate space for returning a chunk of users */ - - num_users = MIN(MAX_GETPWENT_USERS, state->request.data.num_entries); - - if (num_users == 0) { - request_error(state); - return; - } - - if ((state->response.extra_data.data = SMB_MALLOC_ARRAY(struct winbindd_pw, num_users)) == NULL) { - request_error(state); - return; - } - - memset(state->response.extra_data.data, 0, num_users * - sizeof(struct winbindd_pw)); - - user_list = (struct winbindd_pw *)state->response.extra_data.data; - - if (!state->getpwent_initialized) - winbindd_setpwent_internal(state); - - if (!(ent = state->getpwent_state)) { - request_error(state); - return; - } - - /* Start sending back users */ - - for (user_list_ndx = 0; user_list_ndx < num_users; ) { - struct getpwent_user *name_list = NULL; - uint32 result; - - /* Do we need to fetch another chunk of users? */ - - if (ent->num_sam_entries == ent->sam_entry_index) { - - while(ent && - !get_sam_user_entries(ent, state->mem_ctx)) { - struct getent_state *next_ent; - - /* Free state information for this domain */ - - SAFE_FREE(ent->sam_entries); - - next_ent = ent->next; - DLIST_REMOVE(state->getpwent_state, ent); - - SAFE_FREE(ent); - ent = next_ent; - } - - /* No more domains */ - - if (!ent) - break; - } - - name_list = (struct getpwent_user *)ent->sam_entries; - - /* Lookup user info */ - - result = winbindd_fill_pwent( - ent->domain_name, - name_list[ent->sam_entry_index].name, - &name_list[ent->sam_entry_index].user_sid, - &name_list[ent->sam_entry_index].group_sid, - name_list[ent->sam_entry_index].gecos, - name_list[ent->sam_entry_index].homedir, - name_list[ent->sam_entry_index].shell, - &user_list[user_list_ndx]); - - /* Add user to return list */ - - if (result) { - - user_list_ndx++; - state->response.data.num_entries++; - state->response.length += - sizeof(struct winbindd_pw); - - } else - DEBUG(1, ("could not lookup domain user %s\n", - name_list[ent->sam_entry_index].name)); - - ent->sam_entry_index++; - - } - - /* Out of domains */ - - if (user_list_ndx > 0) - request_ok(state); - else - request_error(state); -} - -/* List domain users without mapping to unix ids */ - -void winbindd_list_users(struct winbindd_cli_state *state) -{ - struct winbindd_domain *domain; - WINBIND_USERINFO *info; - const char *which_domain; - uint32 num_entries = 0, total_entries = 0; - char *extra_data = NULL; - int extra_data_len = 0; - enum winbindd_result rv = WINBINDD_ERROR; - - DEBUG(3, ("[%5lu]: list users\n", (unsigned long)state->pid)); - - /* Ensure null termination */ - state->request.domain_name[sizeof(state->request.domain_name)-1]='\0'; - which_domain = state->request.domain_name; - - /* Enumerate over trusted domains */ - - for (domain = domain_list(); domain; domain = domain->next) { - NTSTATUS status; - struct winbindd_methods *methods; - unsigned int i; - - /* if we have a domain name restricting the request and this - one in the list doesn't match, then just bypass the remainder - of the loop */ - - if ( *which_domain && !strequal(which_domain, domain->name) ) - continue; - - methods = domain->methods; - - /* Query display info */ - status = methods->query_user_list(domain, state->mem_ctx, - &num_entries, &info); - - if (!NT_STATUS_IS_OK(status)) { - continue; - } - - if (num_entries == 0) - continue; - - /* Allocate some memory for extra data */ - total_entries += num_entries; - - extra_data = (char *)SMB_REALLOC( - extra_data, sizeof(fstring) * total_entries); - - if (!extra_data) { - DEBUG(0,("failed to enlarge buffer!\n")); - goto done; - } - - /* Pack user list into extra data fields */ - - for (i = 0; i < num_entries; i++) { - fstring acct_name, name; - - if (!info[i].acct_name) { - fstrcpy(acct_name, ""); - } else { - fstrcpy(acct_name, info[i].acct_name); - } - - fill_domain_username(name, domain->name, acct_name, True); - - /* Append to extra data */ - memcpy(&extra_data[extra_data_len], name, - strlen(name)); - extra_data_len += strlen(name); - extra_data[extra_data_len++] = ','; - } - } - - /* Assign extra_data fields in response structure */ - - if (extra_data) { - extra_data[extra_data_len - 1] = '\0'; - state->response.extra_data.data = extra_data; - state->response.length += extra_data_len; - } - - /* No domains responded but that's still OK so don't return an - error. */ - - rv = WINBINDD_OK; - - done: - - if (rv == WINBINDD_OK) - request_ok(state); - else - request_error(state); -} diff --git a/source3/nsswitch/winbindd_util.c b/source3/nsswitch/winbindd_util.c deleted file mode 100644 index 37d29e1765..0000000000 --- a/source3/nsswitch/winbindd_util.c +++ /dev/null @@ -1,1460 +0,0 @@ -/* - Unix SMB/CIFS implementation. - - Winbind daemon for ntdom nss module - - Copyright (C) Tim Potter 2000-2001 - Copyright (C) 2001 by Martin Pool - - 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 3 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, see . -*/ - -#include "includes.h" -#include "winbindd.h" - -#undef DBGC_CLASS -#define DBGC_CLASS DBGC_WINBIND - -extern struct winbindd_methods cache_methods; -extern struct winbindd_methods passdb_methods; - -/** - * @file winbindd_util.c - * - * Winbind daemon for NT domain authentication nss module. - **/ - -/* The list of trusted domains. Note that the list can be deleted and - recreated using the init_domain_list() function so pointers to - individual winbindd_domain structures cannot be made. Keep a copy of - the domain name instead. */ - -static struct winbindd_domain *_domain_list; - -/** - When was the last scan of trusted domains done? - - 0 == not ever -*/ - -static time_t last_trustdom_scan; - -struct winbindd_domain *domain_list(void) -{ - /* Initialise list */ - - if ((!_domain_list) && (!init_domain_list())) { - smb_panic("Init_domain_list failed"); - } - - return _domain_list; -} - -/* Free all entries in the trusted domain list */ - -void free_domain_list(void) -{ - struct winbindd_domain *domain = _domain_list; - - while(domain) { - struct winbindd_domain *next = domain->next; - - DLIST_REMOVE(_domain_list, domain); - SAFE_FREE(domain); - domain = next; - } -} - -static BOOL is_internal_domain(const DOM_SID *sid) -{ - if (sid == NULL) - return False; - - if ( IS_DC ) - return sid_check_is_builtin(sid); - - return (sid_check_is_domain(sid) || sid_check_is_builtin(sid)); -} - -static BOOL is_in_internal_domain(const DOM_SID *sid) -{ - if (sid == NULL) - return False; - - if ( IS_DC ) - return sid_check_is_in_builtin(sid); - - return (sid_check_is_in_our_domain(sid) || sid_check_is_in_builtin(sid)); -} - - -/* Add a trusted domain to our list of domains */ -static struct winbindd_domain *add_trusted_domain(const char *domain_name, const char *alt_name, - struct winbindd_methods *methods, - const DOM_SID *sid) -{ - struct winbindd_domain *domain; - const char *alternative_name = NULL; - - /* ignore alt_name if we are not in an AD domain */ - - if ( (lp_security() == SEC_ADS) && alt_name && *alt_name) { - alternative_name = alt_name; - } - - /* We can't call domain_list() as this function is called from - init_domain_list() and we'll get stuck in a loop. */ - for (domain = _domain_list; domain; domain = domain->next) { - if (strequal(domain_name, domain->name) || - strequal(domain_name, domain->alt_name)) - { - break; - } - - if (alternative_name && *alternative_name) - { - if (strequal(alternative_name, domain->name) || - strequal(alternative_name, domain->alt_name)) - { - break; - } - } - - if (sid) - { - if (is_null_sid(sid)) { - continue; - } - - if (sid_equal(sid, &domain->sid)) { - break; - } - } - } - - /* See if we found a match. Check if we need to update the - SID. */ - - if ( domain && sid) { - if ( sid_equal( &domain->sid, &global_sid_NULL ) ) - sid_copy( &domain->sid, sid ); - - return domain; - } - - /* Create new domain entry */ - - if ((domain = SMB_MALLOC_P(struct winbindd_domain)) == NULL) - return NULL; - - /* Fill in fields */ - - ZERO_STRUCTP(domain); - - /* prioritise the short name */ - if (strchr_m(domain_name, '.') && alternative_name && *alternative_name) { - fstrcpy(domain->name, alternative_name); - fstrcpy(domain->alt_name, domain_name); - } else { - fstrcpy(domain->name, domain_name); - if (alternative_name) { - fstrcpy(domain->alt_name, alternative_name); - } - } - - domain->methods = methods; - domain->backend = NULL; - domain->internal = is_internal_domain(sid); - domain->sequence_number = DOM_SEQUENCE_NONE; - domain->last_seq_check = 0; - domain->initialized = False; - domain->online = is_internal_domain(sid); - domain->check_online_timeout = 0; - if (sid) { - sid_copy(&domain->sid, sid); - } - - /* Link to domain list */ - DLIST_ADD(_domain_list, domain); - - wcache_tdc_add_domain( domain ); - - DEBUG(2,("Added domain %s %s %s\n", - domain->name, domain->alt_name, - &domain->sid?sid_string_static(&domain->sid):"")); - - return domain; -} - -/******************************************************************** - rescan our domains looking for new trusted domains -********************************************************************/ - -struct trustdom_state { - TALLOC_CTX *mem_ctx; - BOOL primary; - BOOL forest_root; - struct winbindd_response *response; -}; - -static void trustdom_recv(void *private_data, BOOL success); -static void rescan_forest_root_trusts( void ); -static void rescan_forest_trusts( void ); - -static void add_trusted_domains( struct winbindd_domain *domain ) -{ - TALLOC_CTX *mem_ctx; - struct winbindd_request *request; - struct winbindd_response *response; - uint32 fr_flags = (DS_DOMAIN_TREE_ROOT|DS_DOMAIN_IN_FOREST); - - struct trustdom_state *state; - - mem_ctx = talloc_init("add_trusted_domains"); - if (mem_ctx == NULL) { - DEBUG(0, ("talloc_init failed\n")); - return; - } - - request = TALLOC_ZERO_P(mem_ctx, struct winbindd_request); - response = TALLOC_P(mem_ctx, struct winbindd_response); - state = TALLOC_P(mem_ctx, struct trustdom_state); - - if ((request == NULL) || (response == NULL) || (state == NULL)) { - DEBUG(0, ("talloc failed\n")); - talloc_destroy(mem_ctx); - return; - } - - state->mem_ctx = mem_ctx; - state->response = response; - - /* Flags used to know how to continue the forest trust search */ - - state->primary = domain->primary; - state->forest_root = ((domain->domain_flags & fr_flags) == fr_flags ); - - request->length = sizeof(*request); - request->cmd = WINBINDD_LIST_TRUSTDOM; - - async_domain_request(mem_ctx, domain, request, response, - trustdom_recv, state); -} - -static void trustdom_recv(void *private_data, BOOL success) -{ - struct trustdom_state *state = - talloc_get_type_abort(private_data, struct trustdom_state); - struct winbindd_response *response = state->response; - char *p; - - if ((!success) || (response->result != WINBINDD_OK)) { - DEBUG(1, ("Could not receive trustdoms\n")); - talloc_destroy(state->mem_ctx); - return; - } - - p = (char *)response->extra_data.data; - - while ((p != NULL) && (*p != '\0')) { - char *q, *sidstr, *alt_name; - DOM_SID sid; - struct winbindd_domain *domain; - char *alternate_name = NULL; - - alt_name = strchr(p, '\\'); - if (alt_name == NULL) { - DEBUG(0, ("Got invalid trustdom response\n")); - break; - } - - *alt_name = '\0'; - alt_name += 1; - - sidstr = strchr(alt_name, '\\'); - if (sidstr == NULL) { - DEBUG(0, ("Got invalid trustdom response\n")); - break; - } - - *sidstr = '\0'; - sidstr += 1; - - q = strchr(sidstr, '\n'); - if (q != NULL) - *q = '\0'; - - if (!string_to_sid(&sid, sidstr)) { - /* Allow NULL sid for sibling domains */ - if ( strcmp(sidstr,"S-0-0") == 0) { - sid_copy( &sid, &global_sid_NULL); - } else { - DEBUG(0, ("Got invalid trustdom response\n")); - break; - } - } - - /* use the real alt_name if we have one, else pass in NULL */ - - if ( !strequal( alt_name, "(null)" ) ) - alternate_name = alt_name; - - /* If we have an existing domain structure, calling - add_trusted_domain() will update the SID if - necessary. This is important because we need the - SID for sibling domains */ - - if ( find_domain_from_name_noinit(p) != NULL ) { - domain = add_trusted_domain(p, alternate_name, - &cache_methods, - &sid); - } else { - domain = add_trusted_domain(p, alternate_name, - &cache_methods, - &sid); - if (domain) { - setup_domain_child(domain, &domain->child, NULL); - } - } - p=q; - if (p != NULL) - p += 1; - } - - SAFE_FREE(response->extra_data.data); - - /* - Cases to consider when scanning trusts: - (a) we are calling from a child domain (primary && !forest_root) - (b) we are calling from the root of the forest (primary && forest_root) - (c) we are calling from a trusted forest domain (!primary - && !forest_root) - */ - - if ( state->primary ) { - /* If this is our primary domain and we are not the in the - forest root, we have to scan the root trusts first */ - - if ( !state->forest_root ) - rescan_forest_root_trusts(); - else - rescan_forest_trusts(); - - } else if ( state->forest_root ) { - /* Once we have done root forest trust search, we can - go on to search thing trusted forests */ - - rescan_forest_trusts(); - } - - talloc_destroy(state->mem_ctx); - - return; -} - -/******************************************************************** - Scan the trusts of our forest root -********************************************************************/ - -static void rescan_forest_root_trusts( void ) -{ - struct winbindd_tdc_domain *dom_list = NULL; - size_t num_trusts = 0; - int i; - - /* The only transitive trusts supported by Windows 2003 AD are - (a) Parent-Child, (b) Tree-Root, and (c) Forest. The - first two are handled in forest and listed by - DsEnumerateDomainTrusts(). Forest trusts are not so we - have to do that ourselves. */ - - if ( !wcache_tdc_fetch_list( &dom_list, &num_trusts ) ) - return; - - for ( i=0; iname, d->alt_name )); - - d->domain_flags = dom_list[i].trust_flags; - d->domain_type = dom_list[i].trust_type; - d->domain_trust_attribs = dom_list[i].trust_attribs; - - add_trusted_domains( d ); - - break; - } - - TALLOC_FREE( dom_list ); - - return; -} - -/******************************************************************** - scan the transitive forest trists (not our own) -********************************************************************/ - - -static void rescan_forest_trusts( void ) -{ - struct winbindd_domain *d = NULL; - struct winbindd_tdc_domain *dom_list = NULL; - size_t num_trusts = 0; - int i; - - /* The only transitive trusts supported by Windows 2003 AD are - (a) Parent-Child, (b) Tree-Root, and (c) Forest. The - first two are handled in forest and listed by - DsEnumerateDomainTrusts(). Forest trusts are not so we - have to do that ourselves. */ - - if ( !wcache_tdc_fetch_list( &dom_list, &num_trusts ) ) - return; - - for ( i=0; iinternal || d->primary ) ) - continue; - - if ( (flags & DS_DOMAIN_DIRECT_INBOUND) && - (type == DS_DOMAIN_TRUST_TYPE_UPLEVEL) && - (attribs == DS_DOMAIN_TRUST_ATTRIB_FOREST_TRANSITIVE) ) - { - /* add the trusted domain if we don't know - about it */ - - if ( !d ) { - d = add_trusted_domain( dom_list[i].domain_name, - dom_list[i].dns_name, - &cache_methods, - &dom_list[i].sid ); - } - - DEBUG(10,("Following trust path for domain %s (%s)\n", - d->name, d->alt_name )); - add_trusted_domains( d ); - } - } - - TALLOC_FREE( dom_list ); - - return; -} - -/********************************************************************* - The process of updating the trusted domain list is a three step - async process: - (a) ask our domain - (b) ask the root domain in our forest - (c) ask the a DC in any Win2003 trusted forests -*********************************************************************/ - -void rescan_trusted_domains( void ) -{ - time_t now = time(NULL); - - /* see if the time has come... */ - - if ((now >= last_trustdom_scan) && - ((now-last_trustdom_scan) < WINBINDD_RESCAN_FREQ) ) - return; - - /* clear the TRUSTDOM cache first */ - - wcache_tdc_clear(); - - /* this will only add new domains we didn't already know about - in the domain_list()*/ - - add_trusted_domains( find_our_domain() ); - - last_trustdom_scan = now; - - return; -} - -struct init_child_state { - TALLOC_CTX *mem_ctx; - struct winbindd_domain *domain; - struct winbindd_request *request; - struct winbindd_response *response; - void (*continuation)(void *private_data, BOOL success); - void *private_data; -}; - -static void init_child_recv(void *private_data, BOOL success); -static void init_child_getdc_recv(void *private_data, BOOL success); - -enum winbindd_result init_child_connection(struct winbindd_domain *domain, - void (*continuation)(void *private_data, - BOOL success), - void *private_data) -{ - TALLOC_CTX *mem_ctx; - struct winbindd_request *request; - struct winbindd_response *response; - struct init_child_state *state; - struct winbindd_domain *request_domain; - - mem_ctx = talloc_init("init_child_connection"); - if (mem_ctx == NULL) { - DEBUG(0, ("talloc_init failed\n")); - return WINBINDD_ERROR; - } - - request = TALLOC_ZERO_P(mem_ctx, struct winbindd_request); - response = TALLOC_P(mem_ctx, struct winbindd_response); - state = TALLOC_P(mem_ctx, struct init_child_state); - - if ((request == NULL) || (response == NULL) || (state == NULL)) { - DEBUG(0, ("talloc failed\n")); - TALLOC_FREE(mem_ctx); - continuation(private_data, False); - return WINBINDD_ERROR; - } - - request->length = sizeof(*request); - - state->mem_ctx = mem_ctx; - state->domain = domain; - state->request = request; - state->response = response; - state->continuation = continuation; - state->private_data = private_data; - - if (IS_DC || domain->primary || domain->internal ) { - /* The primary domain has to find the DC name itself */ - request->cmd = WINBINDD_INIT_CONNECTION; - fstrcpy(request->domain_name, domain->name); - request->data.init_conn.is_primary = domain->internal ? False : True; - fstrcpy(request->data.init_conn.dcname, ""); - async_request(mem_ctx, &domain->child, request, response, - init_child_recv, state); - return WINBINDD_PENDING; - } - - /* This is *not* the primary domain, let's ask our DC about a DC - * name */ - - request->cmd = WINBINDD_GETDCNAME; - fstrcpy(request->domain_name, domain->name); - - request_domain = find_our_domain(); - async_domain_request(mem_ctx, request_domain, request, response, - init_child_getdc_recv, state); - return WINBINDD_PENDING; -} - -static void init_child_getdc_recv(void *private_data, BOOL success) -{ - struct init_child_state *state = - talloc_get_type_abort(private_data, struct init_child_state); - const char *dcname = ""; - - DEBUG(10, ("Received getdcname response\n")); - - if (success && (state->response->result == WINBINDD_OK)) { - dcname = state->response->data.dc_name; - } - - state->request->cmd = WINBINDD_INIT_CONNECTION; - fstrcpy(state->request->domain_name, state->domain->name); - state->request->data.init_conn.is_primary = False; - fstrcpy(state->request->data.init_conn.dcname, dcname); - - async_request(state->mem_ctx, &state->domain->child, - state->request, state->response, - init_child_recv, state); -} - -static void init_child_recv(void *private_data, BOOL success) -{ - struct init_child_state *state = - talloc_get_type_abort(private_data, struct init_child_state); - - DEBUG(5, ("Received child initialization response for domain %s\n", - state->domain->name)); - - if ((!success) || (state->response->result != WINBINDD_OK)) { - DEBUG(3, ("Could not init child\n")); - state->continuation(state->private_data, False); - talloc_destroy(state->mem_ctx); - return; - } - - fstrcpy(state->domain->name, - state->response->data.domain_info.name); - fstrcpy(state->domain->alt_name, - state->response->data.domain_info.alt_name); - string_to_sid(&state->domain->sid, - state->response->data.domain_info.sid); - state->domain->native_mode = - state->response->data.domain_info.native_mode; - state->domain->active_directory = - state->response->data.domain_info.active_directory; - state->domain->sequence_number = - state->response->data.domain_info.sequence_number; - - init_dc_connection(state->domain); - - if (state->continuation != NULL) - state->continuation(state->private_data, True); - talloc_destroy(state->mem_ctx); -} - -enum winbindd_result winbindd_dual_init_connection(struct winbindd_domain *domain, - struct winbindd_cli_state *state) -{ - /* Ensure null termination */ - state->request.domain_name - [sizeof(state->request.domain_name)-1]='\0'; - state->request.data.init_conn.dcname - [sizeof(state->request.data.init_conn.dcname)-1]='\0'; - - if (strlen(state->request.data.init_conn.dcname) > 0) { - fstrcpy(domain->dcname, state->request.data.init_conn.dcname); - } - - init_dc_connection(domain); - - if (!domain->initialized) { - /* If we return error here we can't do any cached authentication, - but we may be in disconnected mode and can't initialize correctly. - Do what the previous code did and just return without initialization, - once we go online we'll re-initialize. - */ - DEBUG(5, ("winbindd_dual_init_connection: %s returning without initialization " - "online = %d\n", domain->name, (int)domain->online )); - } - - fstrcpy(state->response.data.domain_info.name, domain->name); - fstrcpy(state->response.data.domain_info.alt_name, domain->alt_name); - fstrcpy(state->response.data.domain_info.sid, - sid_string_static(&domain->sid)); - - state->response.data.domain_info.native_mode - = domain->native_mode; - state->response.data.domain_info.active_directory - = domain->active_directory; - state->response.data.domain_info.primary - = domain->primary; - state->response.data.domain_info.sequence_number = - domain->sequence_number; - - return WINBINDD_OK; -} - -/* Look up global info for the winbind daemon */ -BOOL init_domain_list(void) -{ - struct winbindd_domain *domain; - int role = lp_server_role(); - - /* Free existing list */ - free_domain_list(); - - /* Add ourselves as the first entry. */ - - if ( role == ROLE_DOMAIN_MEMBER ) { - DOM_SID our_sid; - - if (!secrets_fetch_domain_sid(lp_workgroup(), &our_sid)) { - DEBUG(0, ("Could not fetch our SID - did we join?\n")); - return False; - } - - domain = add_trusted_domain( lp_workgroup(), lp_realm(), - &cache_methods, &our_sid); - if (domain) { - domain->primary = True; - setup_domain_child(domain, &domain->child, NULL); - - /* Even in the parent winbindd we'll need to - talk to the DC, so try and see if we can - contact it. Theoretically this isn't neccessary - as the init_dc_connection() in init_child_recv() - will do this, but we can start detecting the DC - early here. */ - set_domain_online_request(domain); - } - } - - /* Local SAM */ - - domain = add_trusted_domain(get_global_sam_name(), NULL, - &passdb_methods, get_global_sam_sid()); - if (domain) { - if ( role != ROLE_DOMAIN_MEMBER ) { - domain->primary = True; - } - setup_domain_child(domain, &domain->child, NULL); - } - - /* BUILTIN domain */ - - domain = add_trusted_domain("BUILTIN", NULL, &passdb_methods, - &global_sid_Builtin); - if (domain) { - setup_domain_child(domain, &domain->child, NULL); - } - - return True; -} - -void check_domain_trusted( const char *name, const DOM_SID *user_sid ) -{ - struct winbindd_domain *domain; - DOM_SID dom_sid; - uint32 rid; - - domain = find_domain_from_name_noinit( name ); - if ( domain ) - return; - - sid_copy( &dom_sid, user_sid ); - if ( !sid_split_rid( &dom_sid, &rid ) ) - return; - - /* add the newly discovered trusted domain */ - - domain = add_trusted_domain( name, NULL, &cache_methods, - &dom_sid); - - if ( !domain ) - return; - - /* assume this is a trust from a one-way transitive - forest trust */ - - domain->active_directory = True; - domain->domain_flags = DS_DOMAIN_DIRECT_OUTBOUND; - domain->domain_type = DS_DOMAIN_TRUST_TYPE_UPLEVEL; - domain->internal = False; - domain->online = True; - - setup_domain_child(domain, &domain->child, NULL); - - wcache_tdc_add_domain( domain ); - - return; -} - -/** - * Given a domain name, return the struct winbindd domain info for it - * - * @note Do *not* pass lp_workgroup() to this function. domain_list - * may modify it's value, and free that pointer. Instead, our local - * domain may be found by calling find_our_domain(). - * directly. - * - * - * @return The domain structure for the named domain, if it is working. - */ - -struct winbindd_domain *find_domain_from_name_noinit(const char *domain_name) -{ - struct winbindd_domain *domain; - - /* Search through list */ - - for (domain = domain_list(); domain != NULL; domain = domain->next) { - if (strequal(domain_name, domain->name) || - (domain->alt_name[0] && - strequal(domain_name, domain->alt_name))) { - return domain; - } - } - - /* Not found */ - - return NULL; -} - -struct winbindd_domain *find_domain_from_name(const char *domain_name) -{ - struct winbindd_domain *domain; - - domain = find_domain_from_name_noinit(domain_name); - - if (domain == NULL) - return NULL; - - if (!domain->initialized) - init_dc_connection(domain); - - return domain; -} - -/* Given a domain sid, return the struct winbindd domain info for it */ - -struct winbindd_domain *find_domain_from_sid_noinit(const DOM_SID *sid) -{ - struct winbindd_domain *domain; - - /* Search through list */ - - for (domain = domain_list(); domain != NULL; domain = domain->next) { - if (sid_compare_domain(sid, &domain->sid) == 0) - return domain; - } - - /* Not found */ - - return NULL; -} - -/* Given a domain sid, return the struct winbindd domain info for it */ - -struct winbindd_domain *find_domain_from_sid(const DOM_SID *sid) -{ - struct winbindd_domain *domain; - - domain = find_domain_from_sid_noinit(sid); - - if (domain == NULL) - return NULL; - - if (!domain->initialized) - init_dc_connection(domain); - - return domain; -} - -struct winbindd_domain *find_our_domain(void) -{ - struct winbindd_domain *domain; - - /* Search through list */ - - for (domain = domain_list(); domain != NULL; domain = domain->next) { - if (domain->primary) - return domain; - } - - smb_panic("Could not find our domain"); - return NULL; -} - -struct winbindd_domain *find_root_domain(void) -{ - struct winbindd_domain *ours = find_our_domain(); - - if ( !ours ) - return NULL; - - if ( strlen(ours->forest_name) == 0 ) - return NULL; - - return find_domain_from_name( ours->forest_name ); -} - -struct winbindd_domain *find_builtin_domain(void) -{ - DOM_SID sid; - struct winbindd_domain *domain; - - string_to_sid(&sid, "S-1-5-32"); - domain = find_domain_from_sid(&sid); - - if (domain == NULL) { - smb_panic("Could not find BUILTIN domain"); - } - - return domain; -} - -/* Find the appropriate domain to lookup a name or SID */ - -struct winbindd_domain *find_lookup_domain_from_sid(const DOM_SID *sid) -{ - /* SIDs in the S-1-22-{1,2} domain should be handled by our passdb */ - - if ( sid_check_is_in_unix_groups(sid) || - sid_check_is_unix_groups(sid) || - sid_check_is_in_unix_users(sid) || - sid_check_is_unix_users(sid) ) - { - return find_domain_from_sid(get_global_sam_sid()); - } - - /* A DC can't ask the local smbd for remote SIDs, here winbindd is the - * one to contact the external DC's. On member servers the internal - * domains are different: These are part of the local SAM. */ - - DEBUG(10, ("find_lookup_domain_from_sid(%s)\n", - sid_string_static(sid))); - - if (IS_DC || is_internal_domain(sid) || is_in_internal_domain(sid)) { - DEBUG(10, ("calling find_domain_from_sid\n")); - return find_domain_from_sid(sid); - } - - /* On a member server a query for SID or name can always go to our - * primary DC. */ - - DEBUG(10, ("calling find_our_domain\n")); - return find_our_domain(); -} - -struct winbindd_domain *find_lookup_domain_from_name(const char *domain_name) -{ - if ( strequal(domain_name, unix_users_domain_name() ) || - strequal(domain_name, unix_groups_domain_name() ) ) - { - return find_domain_from_name_noinit( get_global_sam_name() ); - } - - if (IS_DC || strequal(domain_name, "BUILTIN") || - strequal(domain_name, get_global_sam_name())) - return find_domain_from_name_noinit(domain_name); - - /* The "Unix User" and "Unix Group" domain our handled by passdb */ - - return find_our_domain(); -} - -/* Lookup a sid in a domain from a name */ - -BOOL winbindd_lookup_sid_by_name(TALLOC_CTX *mem_ctx, - enum winbindd_cmd orig_cmd, - struct winbindd_domain *domain, - const char *domain_name, - const char *name, DOM_SID *sid, - enum lsa_SidType *type) -{ - NTSTATUS result; - - /* Lookup name */ - result = domain->methods->name_to_sid(domain, mem_ctx, orig_cmd, - domain_name, name, sid, type); - - /* Return sid and type if lookup successful */ - if (!NT_STATUS_IS_OK(result)) { - *type = SID_NAME_UNKNOWN; - } - - return NT_STATUS_IS_OK(result); -} - -/** - * @brief Lookup a name in a domain from a sid. - * - * @param sid Security ID you want to look up. - * @param name On success, set to the name corresponding to @p sid. - * @param dom_name On success, set to the 'domain name' corresponding to @p sid. - * @param type On success, contains the type of name: alias, group or - * user. - * @retval True if the name exists, in which case @p name and @p type - * are set, otherwise False. - **/ -BOOL winbindd_lookup_name_by_sid(TALLOC_CTX *mem_ctx, - struct winbindd_domain *domain, - DOM_SID *sid, - char **dom_name, - char **name, - enum lsa_SidType *type) -{ - NTSTATUS result; - - *dom_name = NULL; - *name = NULL; - - /* Lookup name */ - - result = domain->methods->sid_to_name(domain, mem_ctx, sid, dom_name, name, type); - - /* Return name and type if successful */ - - if (NT_STATUS_IS_OK(result)) { - return True; - } - - *type = SID_NAME_UNKNOWN; - - return False; -} - -/* Free state information held for {set,get,end}{pw,gr}ent() functions */ - -void free_getent_state(struct getent_state *state) -{ - struct getent_state *temp; - - /* Iterate over state list */ - - temp = state; - - while(temp != NULL) { - struct getent_state *next; - - /* Free sam entries then list entry */ - - SAFE_FREE(state->sam_entries); - DLIST_REMOVE(state, state); - next = temp->next; - - SAFE_FREE(temp); - temp = next; - } -} - -/* Is this a domain which we may assume no DOMAIN\ prefix? */ - -static BOOL assume_domain(const char *domain) -{ - /* never assume the domain on a standalone server */ - - if ( lp_server_role() == ROLE_STANDALONE ) - return False; - - /* domain member servers may possibly assume for the domain name */ - - if ( lp_server_role() == ROLE_DOMAIN_MEMBER ) { - if ( !strequal(lp_workgroup(), domain) ) - return False; - - if ( lp_winbind_use_default_domain() || lp_winbind_trusted_domains_only() ) - return True; - } - - /* only left with a domain controller */ - - if ( strequal(get_global_sam_name(), domain) ) { - return True; - } - - return False; -} - -/* Parse a string of the form DOMAIN\user into a domain and a user */ - -BOOL parse_domain_user(const char *domuser, fstring domain, fstring user) -{ - char *p = strchr(domuser,*lp_winbind_separator()); - - if ( !p ) { - fstrcpy(user, domuser); - - if ( assume_domain(lp_workgroup())) { - fstrcpy(domain, lp_workgroup()); - } else if ((p = strchr(domuser, '@')) != NULL) { - fstrcpy(domain, ""); - } else { - return False; - } - } else { - fstrcpy(user, p+1); - fstrcpy(domain, domuser); - domain[PTR_DIFF(p, domuser)] = 0; - } - - strupper_m(domain); - - return True; -} - -BOOL parse_domain_user_talloc(TALLOC_CTX *mem_ctx, const char *domuser, - char **domain, char **user) -{ - fstring fstr_domain, fstr_user; - if (!parse_domain_user(domuser, fstr_domain, fstr_user)) { - return False; - } - *domain = talloc_strdup(mem_ctx, fstr_domain); - *user = talloc_strdup(mem_ctx, fstr_user); - return ((*domain != NULL) && (*user != NULL)); -} - -/* Ensure an incoming username from NSS is fully qualified. Replace the - incoming fstring with DOMAIN user. Returns the same - values as parse_domain_user() but also replaces the incoming username. - Used to ensure all names are fully qualified within winbindd. - Used by the NSS protocols of auth, chauthtok, logoff and ccache_ntlm_auth. - The protocol definitions of auth_crap, chng_pswd_auth_crap - really should be changed to use this instead of doing things - by hand. JRA. */ - -BOOL canonicalize_username(fstring username_inout, fstring domain, fstring user) -{ - if (!parse_domain_user(username_inout, domain, user)) { - return False; - } - slprintf(username_inout, sizeof(fstring) - 1, "%s%c%s", - domain, *lp_winbind_separator(), - user); - return True; -} - -/* - Fill DOMAIN\\USERNAME entry accounting 'winbind use default domain' and - 'winbind separator' options. - This means: - - omit DOMAIN when 'winbind use default domain = true' and DOMAIN is - lp_workgroup() - - If we are a PDC or BDC, and this is for our domain, do likewise. - - Also, if omit DOMAIN if 'winbind trusted domains only = true', as the - username is then unqualified in unix - - We always canonicalize as UPPERCASE DOMAIN, lowercase username. -*/ -void fill_domain_username(fstring name, const char *domain, const char *user, BOOL can_assume) -{ - fstring tmp_user; - - fstrcpy(tmp_user, user); - strlower_m(tmp_user); - - if (can_assume && assume_domain(domain)) { - strlcpy(name, tmp_user, sizeof(fstring)); - } else { - slprintf(name, sizeof(fstring) - 1, "%s%c%s", - domain, *lp_winbind_separator(), - tmp_user); - } -} - -/* - * Winbindd socket accessor functions - */ - -char *get_winbind_priv_pipe_dir(void) -{ - return lock_path(WINBINDD_PRIV_SOCKET_SUBDIR); -} - -/* - * Client list accessor functions - */ - -static struct winbindd_cli_state *_client_list; -static int _num_clients; - -/* Return list of all connected clients */ - -struct winbindd_cli_state *winbindd_client_list(void) -{ - return _client_list; -} - -/* Add a connection to the list */ - -void winbindd_add_client(struct winbindd_cli_state *cli) -{ - DLIST_ADD(_client_list, cli); - _num_clients++; -} - -/* Remove a client from the list */ - -void winbindd_remove_client(struct winbindd_cli_state *cli) -{ - DLIST_REMOVE(_client_list, cli); - _num_clients--; -} - -/* Close all open clients */ - -void winbindd_kill_all_clients(void) -{ - struct winbindd_cli_state *cl = winbindd_client_list(); - - DEBUG(10, ("winbindd_kill_all_clients: going postal\n")); - - while (cl) { - struct winbindd_cli_state *next; - - next = cl->next; - winbindd_remove_client(cl); - cl = next; - } -} - -/* Return number of open clients */ - -int winbindd_num_clients(void) -{ - return _num_clients; -} - -NTSTATUS lookup_usergroups_cached(struct winbindd_domain *domain, - TALLOC_CTX *mem_ctx, - const DOM_SID *user_sid, - uint32 *p_num_groups, DOM_SID **user_sids) -{ - NET_USER_INFO_3 *info3 = NULL; - NTSTATUS status = NT_STATUS_NO_MEMORY; - int i; - size_t num_groups = 0; - DOM_SID group_sid, primary_group; - - DEBUG(3,(": lookup_usergroups_cached\n")); - - *user_sids = NULL; - num_groups = 0; - *p_num_groups = 0; - - info3 = netsamlogon_cache_get(mem_ctx, user_sid); - - if (info3 == NULL) { - return NT_STATUS_OBJECT_NAME_NOT_FOUND; - } - - if (info3->num_groups == 0) { - TALLOC_FREE(info3); - return NT_STATUS_UNSUCCESSFUL; - } - - /* always add the primary group to the sid array */ - sid_compose(&primary_group, &info3->dom_sid.sid, info3->user_rid); - - if (!add_sid_to_array(mem_ctx, &primary_group, user_sids, &num_groups)) { - TALLOC_FREE(info3); - return NT_STATUS_NO_MEMORY; - } - - for (i=0; inum_groups; i++) { - sid_copy(&group_sid, &info3->dom_sid.sid); - sid_append_rid(&group_sid, info3->gids[i].g_rid); - - if (!add_sid_to_array(mem_ctx, &group_sid, user_sids, - &num_groups)) { - TALLOC_FREE(info3); - return NT_STATUS_NO_MEMORY; - } - } - - /* Add any Universal groups in the other_sids list */ - - for (i=0; inum_other_sids; i++) { - /* Skip Domain local groups outside our domain. - We'll get these from the getsidaliases() RPC call. */ - if (info3->other_sids_attrib[i] & SE_GROUP_RESOURCE) - continue; - - if (!add_sid_to_array(mem_ctx, &info3->other_sids[i].sid, - user_sids, &num_groups)) - { - TALLOC_FREE(info3); - return NT_STATUS_NO_MEMORY; - } - } - - - TALLOC_FREE(info3); - *p_num_groups = num_groups; - status = (user_sids != NULL) ? NT_STATUS_OK : NT_STATUS_NO_MEMORY; - - DEBUG(3,(": lookup_usergroups_cached succeeded\n")); - - return status; -} - -/********************************************************************* - We use this to remove spaces from user and group names -********************************************************************/ - -void ws_name_replace( char *name, char replace ) -{ - char replace_char[2] = { 0x0, 0x0 }; - - if ( !lp_winbind_normalize_names() || (replace == '\0') ) - return; - - replace_char[0] = replace; - all_string_sub( name, " ", replace_char, 0 ); - - return; -} - -/********************************************************************* - We use this to do the inverse of ws_name_replace() -********************************************************************/ - -void ws_name_return( char *name, char replace ) -{ - char replace_char[2] = { 0x0, 0x0 }; - - if ( !lp_winbind_normalize_names() || (replace == '\0') ) - return; - - replace_char[0] = replace; - all_string_sub( name, replace_char, " ", 0 ); - - return; -} - -/********************************************************************* - ********************************************************************/ - -BOOL winbindd_can_contact_domain( struct winbindd_domain *domain ) -{ - /* We can contact the domain if it is our primary domain */ - - if ( domain->primary ) - return True; - - /* Can always contact a domain that is in out forest */ - - if ( domain->domain_flags & DS_DOMAIN_IN_FOREST ) - return True; - - /* We cannot contact the domain if it is running AD and - we have no inbound trust */ - - if ( domain->active_directory && - ((domain->domain_flags&DS_DOMAIN_DIRECT_INBOUND) != DS_DOMAIN_DIRECT_INBOUND) ) - { - return False; - } - - /* Assume everything else is ok (probably not true but what - can you do?) */ - - return True; -} - -/********************************************************************* - ********************************************************************/ - -BOOL winbindd_internal_child(struct winbindd_child *child) -{ - if ((child == idmap_child()) || (child == locator_child())) { - return True; - } - - return False; -} - -#ifdef HAVE_KRB5_LOCATE_PLUGIN_H - -/********************************************************************* - ********************************************************************/ - -static void winbindd_set_locator_kdc_env(const struct winbindd_domain *domain) -{ - char *var = NULL; - const char *kdc = NULL; - int lvl = 11; - - if (!domain || !domain->alt_name || !*domain->alt_name) { - return; - } - - if (domain->initialized && !domain->active_directory) { - DEBUG(lvl,("winbindd_set_locator_kdc_env: %s not AD\n", - domain->alt_name)); - return; - } - - kdc = inet_ntoa(domain->dcaddr.sin_addr); - if (!kdc) { - DEBUG(lvl,("winbindd_set_locator_kdc_env: %s no DC IP\n", - domain->alt_name)); - kdc = domain->dcname; - } - - if (!kdc || !*kdc) { - DEBUG(lvl,("winbindd_set_locator_kdc_env: %s no DC at all\n", - domain->alt_name)); - return; - } - - if (asprintf(&var, "%s_%s", WINBINDD_LOCATOR_KDC_ADDRESS, - strupper_static(domain->alt_name)) == -1) { - return; - } - - DEBUG(lvl,("winbindd_set_locator_kdc_env: setting var: %s to: %s\n", - var, kdc)); - - setenv(var, kdc, 1); - free(var); -} - -/********************************************************************* - ********************************************************************/ - -void winbindd_set_locator_kdc_envs(const struct winbindd_domain *domain) -{ - struct winbindd_domain *our_dom = find_our_domain(); - - winbindd_set_locator_kdc_env(domain); - - if (domain != our_dom) { - winbindd_set_locator_kdc_env(our_dom); - } -} - -/********************************************************************* - ********************************************************************/ - -void winbindd_unset_locator_kdc_env(const struct winbindd_domain *domain) -{ - char *var = NULL; - - if (!domain || !domain->alt_name || !*domain->alt_name) { - return; - } - - if (asprintf(&var, "%s_%s", WINBINDD_LOCATOR_KDC_ADDRESS, - strupper_static(domain->alt_name)) == -1) { - return; - } - - unsetenv(var); - free(var); -} -#else - -void winbindd_set_locator_kdc_envs(const struct winbindd_domain *domain) -{ - return; -} - -void winbindd_unset_locator_kdc_env(const struct winbindd_domain *domain) -{ - return; -} - -#endif /* HAVE_KRB5_LOCATE_PLUGIN_H */ diff --git a/source3/nsswitch/winbindd_wins.c b/source3/nsswitch/winbindd_wins.c deleted file mode 100644 index f84dfdf2de..0000000000 --- a/source3/nsswitch/winbindd_wins.c +++ /dev/null @@ -1,234 +0,0 @@ -/* - Unix SMB/CIFS implementation. - - Winbind daemon - WINS related functions - - Copyright (C) Andrew Tridgell 1999 - Copyright (C) Herb Lewis 2002 - - 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 3 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, see . -*/ - -#include "includes.h" -#include "winbindd.h" - -#undef DBGC_CLASS -#define DBGC_CLASS DBGC_WINBIND - -/* Use our own create socket code so we don't recurse.... */ - -static int wins_lookup_open_socket_in(void) -{ - struct sockaddr_in sock; - int val=1; - int res; - - memset((char *)&sock,'\0',sizeof(sock)); - -#ifdef HAVE_SOCK_SIN_LEN - sock.sin_len = sizeof(sock); -#endif - sock.sin_port = 0; - sock.sin_family = AF_INET; - sock.sin_addr.s_addr = interpret_addr("0.0.0.0"); - res = socket(AF_INET, SOCK_DGRAM, 0); - if (res == -1) - return -1; - - setsockopt(res,SOL_SOCKET,SO_REUSEADDR,(char *)&val,sizeof(val)); -#ifdef SO_REUSEPORT - setsockopt(res,SOL_SOCKET,SO_REUSEPORT,(char *)&val,sizeof(val)); -#endif /* SO_REUSEPORT */ - - /* now we've got a socket - we need to bind it */ - - if (bind(res, (struct sockaddr * ) &sock,sizeof(sock)) < 0) { - close(res); - return(-1); - } - - set_socket_options(res,"SO_BROADCAST"); - - return res; -} - - -static NODE_STATUS_STRUCT *lookup_byaddr_backend(char *addr, int *count) -{ - int fd; - struct in_addr ip; - struct nmb_name nname; - NODE_STATUS_STRUCT *status; - - fd = wins_lookup_open_socket_in(); - if (fd == -1) - return NULL; - - make_nmb_name(&nname, "*", 0); - ip = *interpret_addr2(addr); - status = node_status_query(fd,&nname,ip, count, NULL); - - close(fd); - return status; -} - -static struct in_addr *lookup_byname_backend(const char *name, int *count) -{ - int fd; - struct ip_service *ret = NULL; - struct in_addr *return_ip = NULL; - int j, i, flags = 0; - - *count = 0; - - /* always try with wins first */ - if (NT_STATUS_IS_OK(resolve_wins(name,0x20,&ret,count))) { - if ( *count == 0 ) - return NULL; - if ( (return_ip = SMB_MALLOC_ARRAY(struct in_addr, *count)) == NULL ) { - free( ret ); - return NULL; - } - - /* copy the IP addresses */ - for ( i=0; i<(*count); i++ ) - return_ip[i] = ret[i].ip; - - free( ret ); - return return_ip; - } - - fd = wins_lookup_open_socket_in(); - if (fd == -1) { - return NULL; - } - - /* uggh, we have to broadcast to each interface in turn */ - for (j=iface_count() - 1; - j >= 0; - j--) { - struct in_addr *bcast = iface_n_bcast(j); - return_ip = name_query(fd,name,0x20,True,True,*bcast,count, &flags, NULL); - if (return_ip) { - break; - } - } - - close(fd); - return return_ip; -} - -/* Get hostname from IP */ - -void winbindd_wins_byip(struct winbindd_cli_state *state) -{ - fstring response; - int i, count, maxlen, size; - NODE_STATUS_STRUCT *status; - - /* Ensure null termination */ - state->request.data.winsreq[sizeof(state->request.data.winsreq)-1]='\0'; - - DEBUG(3, ("[%5lu]: wins_byip %s\n", (unsigned long)state->pid, - state->request.data.winsreq)); - - *response = '\0'; - maxlen = sizeof(response) - 1; - - if ((status = lookup_byaddr_backend(state->request.data.winsreq, &count))){ - size = strlen(state->request.data.winsreq); - if (size > maxlen) { - SAFE_FREE(status); - request_error(state); - return; - } - fstrcat(response,state->request.data.winsreq); - fstrcat(response,"\t"); - for (i = 0; i < count; i++) { - /* ignore group names */ - if (status[i].flags & 0x80) continue; - if (status[i].type == 0x20) { - size = sizeof(status[i].name) + strlen(response); - if (size > maxlen) { - SAFE_FREE(status); - request_error(state); - return; - } - fstrcat(response, status[i].name); - fstrcat(response, " "); - } - } - /* make last character a newline */ - response[strlen(response)-1] = '\n'; - SAFE_FREE(status); - } - fstrcpy(state->response.data.winsresp,response); - request_ok(state); -} - -/* Get IP from hostname */ - -void winbindd_wins_byname(struct winbindd_cli_state *state) -{ - struct in_addr *ip_list; - int i, count, maxlen, size; - fstring response; - char * addr; - - /* Ensure null termination */ - state->request.data.winsreq[sizeof(state->request.data.winsreq)-1]='\0'; - - DEBUG(3, ("[%5lu]: wins_byname %s\n", (unsigned long)state->pid, - state->request.data.winsreq)); - - *response = '\0'; - maxlen = sizeof(response) - 1; - - if ((ip_list = lookup_byname_backend(state->request.data.winsreq,&count))){ - for (i = count; i ; i--) { - addr = inet_ntoa(ip_list[i-1]); - size = strlen(addr); - if (size > maxlen) { - SAFE_FREE(ip_list); - request_error(state); - return; - } - if (i != 0) { - /* Clear out the newline character */ - /* But only if there is something in there, - otherwise we clobber something in the stack */ - if (strlen(response)) - response[strlen(response)-1] = ' '; - } - fstrcat(response,addr); - fstrcat(response,"\t"); - } - size = strlen(state->request.data.winsreq) + strlen(response); - if (size > maxlen) { - SAFE_FREE(ip_list); - request_error(state); - return; - } - fstrcat(response,state->request.data.winsreq); - fstrcat(response,"\n"); - SAFE_FREE(ip_list); - } else { - request_error(state); - return; - } - - fstrcpy(state->response.data.winsresp,response); - - request_ok(state); -} diff --git a/source3/winbindd/idmap.c b/source3/winbindd/idmap.c new file mode 100644 index 0000000000..2c7acc185c --- /dev/null +++ b/source3/winbindd/idmap.c @@ -0,0 +1,1562 @@ +/* + Unix SMB/CIFS implementation. + ID Mapping + Copyright (C) Tim Potter 2000 + Copyright (C) Jim McDonough 2003 + Copyright (C) Simo Sorce 2003-2007 + Copyright (C) Jeremy Allison 2006 + + 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 3 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, see . +*/ + +#include "includes.h" +#include "winbindd.h" + +#undef DBGC_CLASS +#define DBGC_CLASS DBGC_IDMAP + +static_decl_idmap; + +struct idmap_backend { + const char *name; + struct idmap_methods *methods; + struct idmap_backend *prev, *next; +}; + +struct idmap_alloc_backend { + const char *name; + struct idmap_alloc_methods *methods; + struct idmap_alloc_backend *prev, *next; +}; + +struct idmap_cache_ctx; + +struct idmap_alloc_context { + const char *params; + struct idmap_alloc_methods *methods; + BOOL initialized; +}; + +static TALLOC_CTX *idmap_ctx = NULL; +static struct idmap_cache_ctx *idmap_cache; + +static struct idmap_backend *backends = NULL; +static struct idmap_domain **idmap_domains = NULL; +static int num_domains = 0; +static int pdb_dom_num = -1; +static int def_dom_num = -1; + +static struct idmap_alloc_backend *alloc_backends = NULL; +static struct idmap_alloc_context *idmap_alloc_ctx = NULL; + +#define IDMAP_CHECK_RET(ret) do { \ + if ( ! NT_STATUS_IS_OK(ret)) { \ + DEBUG(2, ("ERROR: NTSTATUS = 0x%08x\n", NT_STATUS_V(ret))); \ + goto done; \ + } } while(0) +#define IDMAP_REPORT_RET(ret) do { \ + if ( ! NT_STATUS_IS_OK(ret)) { \ + DEBUG(2, ("ERROR: NTSTATUS = 0x%08x\n", NT_STATUS_V(ret))); \ + } } while(0) +#define IDMAP_CHECK_ALLOC(mem) do { \ + if (!mem) { \ + DEBUG(0, ("Out of memory!\n")); ret = NT_STATUS_NO_MEMORY; \ + goto done; \ + } } while(0) + +static struct idmap_methods *get_methods(struct idmap_backend *be, + const char *name) +{ + struct idmap_backend *b; + + for (b = be; b; b = b->next) { + if (strequal(b->name, name)) { + return b->methods; + } + } + + return NULL; +} + +static struct idmap_alloc_methods *get_alloc_methods( + struct idmap_alloc_backend *be, + const char *name) +{ + struct idmap_alloc_backend *b; + + for (b = be; b; b = b->next) { + if (strequal(b->name, name)) { + return b->methods; + } + } + + return NULL; +} + +BOOL idmap_is_offline(void) +{ + return ( lp_winbind_offline_logon() && + get_global_winbindd_state_offline() ); +} + +/********************************************************************** + Allow a module to register itself as a method. +**********************************************************************/ + +NTSTATUS smb_register_idmap(int version, const char *name, + struct idmap_methods *methods) +{ + struct idmap_methods *test; + struct idmap_backend *entry; + + if (!idmap_ctx) { + return NT_STATUS_INTERNAL_DB_ERROR; + } + + if ((version != SMB_IDMAP_INTERFACE_VERSION)) { + DEBUG(0, ("Failed to register idmap module.\n" + "The module was compiled against " + "SMB_IDMAP_INTERFACE_VERSION %d,\n" + "current SMB_IDMAP_INTERFACE_VERSION is %d.\n" + "Please recompile against the current version " + "of samba!\n", + version, SMB_IDMAP_INTERFACE_VERSION)); + return NT_STATUS_OBJECT_TYPE_MISMATCH; + } + + if (!name || !name[0] || !methods) { + DEBUG(0,("Called with NULL pointer or empty name!\n")); + return NT_STATUS_INVALID_PARAMETER; + } + + test = get_methods(backends, name); + if (test) { + DEBUG(0,("Idmap module %s already registered!\n", name)); + return NT_STATUS_OBJECT_NAME_COLLISION; + } + + entry = talloc(idmap_ctx, struct idmap_backend); + if ( ! entry) { + DEBUG(0,("Out of memory!\n")); + return NT_STATUS_NO_MEMORY; + } + entry->name = talloc_strdup(idmap_ctx, name); + if ( ! entry->name) { + DEBUG(0,("Out of memory!\n")); + return NT_STATUS_NO_MEMORY; + } + entry->methods = methods; + + DLIST_ADD(backends, entry); + DEBUG(5, ("Successfully added idmap backend '%s'\n", name)); + return NT_STATUS_OK; +} + +/********************************************************************** + Allow a module to register itself as a method. +**********************************************************************/ + +NTSTATUS smb_register_idmap_alloc(int version, const char *name, + struct idmap_alloc_methods *methods) +{ + struct idmap_alloc_methods *test; + struct idmap_alloc_backend *entry; + + if (!idmap_ctx) { + return NT_STATUS_INTERNAL_DB_ERROR; + } + + if ((version != SMB_IDMAP_INTERFACE_VERSION)) { + DEBUG(0, ("Failed to register idmap alloc module.\n" + "The module was compiled against " + "SMB_IDMAP_INTERFACE_VERSION %d,\n" + "current SMB_IDMAP_INTERFACE_VERSION is %d.\n" + "Please recompile against the current version " + "of samba!\n", + version, SMB_IDMAP_INTERFACE_VERSION)); + return NT_STATUS_OBJECT_TYPE_MISMATCH; + } + + if (!name || !name[0] || !methods) { + DEBUG(0,("Called with NULL pointer or empty name!\n")); + return NT_STATUS_INVALID_PARAMETER; + } + + test = get_alloc_methods(alloc_backends, name); + if (test) { + DEBUG(0,("idmap_alloc module %s already registered!\n", name)); + return NT_STATUS_OBJECT_NAME_COLLISION; + } + + entry = talloc(idmap_ctx, struct idmap_alloc_backend); + if ( ! entry) { + DEBUG(0,("Out of memory!\n")); + return NT_STATUS_NO_MEMORY; + } + entry->name = talloc_strdup(idmap_ctx, name); + if ( ! entry->name) { + DEBUG(0,("Out of memory!\n")); + return NT_STATUS_NO_MEMORY; + } + entry->methods = methods; + + DLIST_ADD(alloc_backends, entry); + DEBUG(5, ("Successfully added idmap alloc backend '%s'\n", name)); + return NT_STATUS_OK; +} + +static int close_domain_destructor(struct idmap_domain *dom) +{ + NTSTATUS ret; + + ret = dom->methods->close_fn(dom); + if (!NT_STATUS_IS_OK(ret)) { + DEBUG(3, ("Failed to close idmap domain [%s]!\n", dom->name)); + } + + return 0; +} + +/************************************************************************** + Shutdown. +**************************************************************************/ + +NTSTATUS idmap_close(void) +{ + /* close the alloc backend first before freeing idmap_ctx */ + if (idmap_alloc_ctx) { + idmap_alloc_ctx->methods->close_fn(); + idmap_alloc_ctx->methods = NULL; + } + alloc_backends = NULL; + + /* this talloc_free call will fire the talloc destructors + * that will free all active backends resources */ + TALLOC_FREE(idmap_ctx); + idmap_cache = NULL; + idmap_domains = NULL; + backends = NULL; + + return NT_STATUS_OK; +} + +/********************************************************************** + Initialise idmap cache and a remote backend (if configured). +**********************************************************************/ + +static const char *idmap_default_domain[] = { "default domain", NULL }; + +/**************************************************************************** + ****************************************************************************/ + +NTSTATUS idmap_init_cache(void) +{ + /* Always initialize the cache. We'll have to delay initialization + of backends if we are offline */ + + if ( idmap_ctx ) { + return NT_STATUS_OK; + } + + if ( (idmap_ctx = talloc_named_const(NULL, 0, "idmap_ctx")) == NULL ) { + return NT_STATUS_NO_MEMORY; + } + + if ( (idmap_cache = idmap_cache_init(idmap_ctx)) == NULL ) { + return NT_STATUS_UNSUCCESSFUL; + } + + return NT_STATUS_OK; +} + +/**************************************************************************** + ****************************************************************************/ + +NTSTATUS idmap_init(void) +{ + NTSTATUS ret; + static NTSTATUS idmap_init_status = NT_STATUS_UNSUCCESSFUL; + struct idmap_domain *dom; + char *compat_backend = NULL; + char *compat_params = NULL; + const char **dom_list = NULL; + char *alloc_backend = NULL; + BOOL default_already_defined = False; + BOOL pri_dom_is_in_list = False; + int compat = 0; + int i; + + ret = idmap_init_cache(); + if (!NT_STATUS_IS_OK(ret)) + return ret; + + if (NT_STATUS_IS_OK(idmap_init_status)) { + return NT_STATUS_OK; + } + + /* We can't reliably call intialization code here unless + we are online. But return NT_STATUS_OK so the upper + level code doesn't abort idmap lookups. */ + + if ( get_global_winbindd_state_offline() ) { + idmap_init_status = NT_STATUS_FILE_IS_OFFLINE; + return NT_STATUS_OK; + } + + static_init_idmap; + + dom_list = lp_idmap_domains(); + + if ( lp_idmap_backend() ) { + const char **compat_list = lp_idmap_backend(); + char *p = NULL; + const char *q = NULL; + + if ( dom_list ) { + DEBUG(0, ("WARNING: idmap backend and idmap domains are" + " mutually exclusive!\n")); + DEBUGADD(0,("idmap backend option will be IGNORED!\n")); + } else { + compat = 1; + + compat_backend = talloc_strdup(idmap_ctx, *compat_list); + if (compat_backend == NULL ) { + ret = NT_STATUS_NO_MEMORY; + goto done; + } + + /* strip any leading idmap_ prefix of */ + if (strncmp(*compat_list, "idmap_", 6) == 0 ) { + q = *compat_list += 6; + DEBUG(0, ("WARNING: idmap backend uses obsolete" + " and deprecated 'idmap_' prefix.\n" + "Please replace 'idmap_%s' by '%s' in" + " %s\n", q, q, dyn_CONFIGFILE)); + compat_backend = talloc_strdup(idmap_ctx, q); + } else { + compat_backend = talloc_strdup(idmap_ctx, + *compat_list); + } + + /* separate the backend and module arguements */ + if ((p = strchr(compat_backend, ':')) != NULL) { + *p = '\0'; + compat_params = p + 1; + } + } + } else if ( !dom_list ) { + /* Back compatible: without idmap domains and explicit + idmap backend. Taking default idmap backend: tdb */ + + compat = 1; + compat_backend = talloc_strdup( idmap_ctx, "tdb"); + compat_params = compat_backend; + } + + if ( ! dom_list) { + dom_list = idmap_default_domain; + } + + /*************************** + * initialize idmap domains + */ + DEBUG(1, ("Initializing idmap domains\n")); + + for (i = 0; dom_list[i]; i++) { + const char *parm_backend; + char *config_option; + + /* ignore BUILTIN and local MACHINE domains */ + if (strequal(dom_list[i], "BUILTIN") + || strequal(dom_list[i], get_global_sam_name())) + { + DEBUG(0,("idmap_init: Ignoring invalid domain %s\n", + dom_list[i])); + continue; + } + + if (strequal(dom_list[i], lp_workgroup())) { + pri_dom_is_in_list = True; + } + /* init domain */ + + dom = TALLOC_ZERO_P(idmap_ctx, struct idmap_domain); + IDMAP_CHECK_ALLOC(dom); + + dom->name = talloc_strdup(dom, dom_list[i]); + IDMAP_CHECK_ALLOC(dom->name); + + config_option = talloc_asprintf(dom, "idmap config %s", + dom->name); + IDMAP_CHECK_ALLOC(config_option); + + /* default or specific ? */ + + dom->default_domain = lp_parm_bool(-1, config_option, + "default", False); + + if (dom->default_domain || + strequal(dom_list[i], idmap_default_domain[0])) { + + /* make sure this is set even when we match + * idmap_default_domain[0] */ + dom->default_domain = True; + + if (default_already_defined) { + DEBUG(1, ("ERROR: Multiple domains defined as" + " default!\n")); + ret = NT_STATUS_INVALID_PARAMETER; + goto done; + } + + default_already_defined = True; + + } + + dom->readonly = lp_parm_bool(-1, config_option, + "readonly", False); + + /* find associated backend (default: tdb) */ + if (compat) { + parm_backend = talloc_strdup(idmap_ctx, compat_backend); + } else { + parm_backend = talloc_strdup(idmap_ctx, + lp_parm_const_string( + -1, config_option, + "backend", "tdb")); + } + IDMAP_CHECK_ALLOC(parm_backend); + + /* get the backend methods for this domain */ + dom->methods = get_methods(backends, parm_backend); + + if ( ! dom->methods) { + ret = smb_probe_module("idmap", parm_backend); + if (NT_STATUS_IS_OK(ret)) { + dom->methods = get_methods(backends, + parm_backend); + } + } + if ( ! dom->methods) { + DEBUG(0, ("ERROR: Could not get methods for " + "backend %s\n", parm_backend)); + ret = NT_STATUS_UNSUCCESSFUL; + goto done; + } + + /* check the set_mapping function exists otherwise mark the + * module as readonly */ + if ( ! dom->methods->set_mapping) { + DEBUG(5, ("Forcing to readonly, as this module can't" + " store arbitrary mappings.\n")); + dom->readonly = True; + } + + /* now that we have methods, + * set the destructor for this domain */ + talloc_set_destructor(dom, close_domain_destructor); + + if (compat_params) { + dom->params = talloc_strdup(dom, compat_params); + IDMAP_CHECK_ALLOC(dom->params); + } else { + dom->params = NULL; + } + + /* Finally instance a backend copy for this domain */ + ret = dom->methods->init(dom); + if ( ! NT_STATUS_IS_OK(ret)) { + DEBUG(0, ("ERROR: Initialization failed for backend " + "%s (domain %s), deferred!\n", + parm_backend, dom->name)); + } + idmap_domains = talloc_realloc(idmap_ctx, idmap_domains, + struct idmap_domain *, i+1); + if ( ! idmap_domains) { + DEBUG(0, ("Out of memory!\n")); + ret = NT_STATUS_NO_MEMORY; + goto done; + } + idmap_domains[i] = dom; + + /* save default domain position for future uses */ + if (dom->default_domain) { + def_dom_num = i; + } + + DEBUG(10, ("Domain %s - Backend %s - %sdefault - %sreadonly\n", + dom->name, parm_backend, + dom->default_domain?"":"not ", + dom->readonly?"":"not ")); + + talloc_free(config_option); + } + + /* save the number of domains we have */ + num_domains = i; + + /* automatically add idmap_nss backend if needed */ + if ((lp_server_role() == ROLE_DOMAIN_MEMBER) && + ( ! pri_dom_is_in_list) && + lp_winbind_trusted_domains_only()) { + + dom = TALLOC_ZERO_P(idmap_ctx, struct idmap_domain); + IDMAP_CHECK_ALLOC(dom); + + dom->name = talloc_strdup(dom, lp_workgroup()); + IDMAP_CHECK_ALLOC(dom->name); + + dom->default_domain = False; + dom->readonly = True; + + /* get the backend methods for passdb */ + dom->methods = get_methods(backends, "nss"); + + /* (the nss module is always statically linked) */ + if ( ! dom->methods) { + DEBUG(0, ("ERROR: No methods for idmap_nss ?!\n")); + ret = NT_STATUS_UNSUCCESSFUL; + goto done; + } + + /* now that we have methods, + * set the destructor for this domain */ + talloc_set_destructor(dom, close_domain_destructor); + + if (compat_params) { + dom->params = talloc_strdup(dom, compat_params); + IDMAP_CHECK_ALLOC(dom->params); + } else { + dom->params = NULL; + } + + /* Finally instance a backend copy for this domain */ + ret = dom->methods->init(dom); + if ( ! NT_STATUS_IS_OK(ret)) { + DEBUG(0, ("ERROR: Init. failed for idmap_nss ?!\n")); + ret = NT_STATUS_UNSUCCESSFUL; + goto done; + } + + idmap_domains = talloc_realloc(idmap_ctx, + idmap_domains, + struct idmap_domain *, + num_domains+1); + if ( ! idmap_domains) { + DEBUG(0, ("Out of memory!\n")); + ret = NT_STATUS_NO_MEMORY; + goto done; + } + idmap_domains[num_domains] = dom; + + DEBUG(10, ("Domain %s - Backend nss - not default - readonly\n", + dom->name )); + + num_domains++; + } + + /**** automatically add idmap_passdb backend ****/ + dom = TALLOC_ZERO_P(idmap_ctx, struct idmap_domain); + IDMAP_CHECK_ALLOC(dom); + + dom->name = talloc_strdup(dom, get_global_sam_name()); + IDMAP_CHECK_ALLOC(dom->name); + + dom->default_domain = False; + dom->readonly = True; + + /* get the backend methods for passdb */ + dom->methods = get_methods(backends, "passdb"); + + /* (the passdb module is always statically linked) */ + if ( ! dom->methods) { + DEBUG(0, ("ERROR: No methods for idmap_passdb ?!\n")); + ret = NT_STATUS_UNSUCCESSFUL; + goto done; + } + + /* now that we have methods, set the destructor for this domain */ + talloc_set_destructor(dom, close_domain_destructor); + + if (compat_params) { + dom->params = talloc_strdup(dom, compat_params); + IDMAP_CHECK_ALLOC(dom->params); + } else { + dom->params = NULL; + } + + /* Finally instance a backend copy for this domain */ + ret = dom->methods->init(dom); + if ( ! NT_STATUS_IS_OK(ret)) { + DEBUG(0, ("ERROR: Init. failed for idmap_passdb ?!\n")); + ret = NT_STATUS_UNSUCCESSFUL; + goto done; + } + + idmap_domains = talloc_realloc(idmap_ctx, + idmap_domains, + struct idmap_domain *, + num_domains+1); + if ( ! idmap_domains) { + DEBUG(0, ("Out of memory!\n")); + ret = NT_STATUS_NO_MEMORY; + goto done; + } + idmap_domains[num_domains] = dom; + + /* needed to handle special BUILTIN and wellknown SIDs cases */ + pdb_dom_num = num_domains; + + DEBUG(10, ("Domain %s - Backend passdb - not default - readonly\n", + dom->name)); + + num_domains++; + /**** finished adding idmap_passdb backend ****/ + + /* sort domains so that the default is the last one */ + /* don't sort if no default domain defined */ + if (def_dom_num != -1 && def_dom_num != num_domains-1) { + /* default is not last, move it */ + struct idmap_domain *tmp; + + if (pdb_dom_num > def_dom_num) { + pdb_dom_num --; + + } else if (pdb_dom_num == def_dom_num) { /* ?? */ + pdb_dom_num = num_domains - 1; + } + + tmp = idmap_domains[def_dom_num]; + + for (i = def_dom_num; i < num_domains-1; i++) { + idmap_domains[i] = idmap_domains[i+1]; + } + idmap_domains[i] = tmp; + def_dom_num = i; + } + + + /* Initialize alloc module */ + + DEBUG(3, ("Initializing idmap alloc module\n")); + + alloc_backend = NULL; + if (compat) { + alloc_backend = talloc_strdup(idmap_ctx, compat_backend); + } else { + char *ab = lp_idmap_alloc_backend(); + + if (ab && (ab[0] != '\0')) { + alloc_backend = talloc_strdup(idmap_ctx, + lp_idmap_alloc_backend()); + } + } + + if ( alloc_backend ) { + + idmap_alloc_ctx = TALLOC_ZERO_P(idmap_ctx, + struct idmap_alloc_context); + IDMAP_CHECK_ALLOC(idmap_alloc_ctx); + + idmap_alloc_ctx->methods = get_alloc_methods(alloc_backends, + alloc_backend); + if ( ! idmap_alloc_ctx->methods) { + ret = smb_probe_module("idmap", alloc_backend); + if (NT_STATUS_IS_OK(ret)) { + idmap_alloc_ctx->methods = + get_alloc_methods(alloc_backends, + alloc_backend); + } + } + if (idmap_alloc_ctx->methods) { + + if (compat_params) { + idmap_alloc_ctx->params = + talloc_strdup(idmap_alloc_ctx, + compat_params); + IDMAP_CHECK_ALLOC(idmap_alloc_ctx->params); + } else { + idmap_alloc_ctx->params = NULL; + } + + ret = idmap_alloc_ctx->methods->init(idmap_alloc_ctx->params); + if ( ! NT_STATUS_IS_OK(ret)) { + DEBUG(0, ("ERROR: Initialization failed for " + "alloc backend %s, deferred!\n", + alloc_backend)); + } else { + idmap_alloc_ctx->initialized = True; + } + } else { + DEBUG(2, ("idmap_init: Unable to get methods for " + "alloc backend %s\n", + alloc_backend)); + /* certain compat backends are just readonly */ + if ( compat ) { + TALLOC_FREE(idmap_alloc_ctx); + ret = NT_STATUS_OK; + } else { + ret = NT_STATUS_UNSUCCESSFUL; + } + } + } + + /* cleanpu temporary strings */ + TALLOC_FREE( compat_backend ); + + idmap_init_status = NT_STATUS_OK; + + return ret; + +done: + DEBUG(0, ("Aborting IDMAP Initialization ...\n")); + idmap_close(); + + return ret; +} + +static NTSTATUS idmap_alloc_init(void) +{ + NTSTATUS ret; + + if (! NT_STATUS_IS_OK(ret = idmap_init())) { + return ret; + } + + if ( ! idmap_alloc_ctx) { + return NT_STATUS_NOT_SUPPORTED; + } + + if ( ! idmap_alloc_ctx->initialized) { + ret = idmap_alloc_ctx->methods->init(idmap_alloc_ctx->params); + if ( ! NT_STATUS_IS_OK(ret)) { + DEBUG(0, ("ERROR: Initialization failed for alloc " + "backend, deferred!\n")); + return ret; + } else { + idmap_alloc_ctx->initialized = True; + } + } + + return NT_STATUS_OK; +} + +/************************************************************************** + idmap allocator interface functions +**************************************************************************/ + +NTSTATUS idmap_allocate_uid(struct unixid *id) +{ + NTSTATUS ret; + + if (! NT_STATUS_IS_OK(ret = idmap_alloc_init())) { + return ret; + } + + id->type = ID_TYPE_UID; + return idmap_alloc_ctx->methods->allocate_id(id); +} + +NTSTATUS idmap_allocate_gid(struct unixid *id) +{ + NTSTATUS ret; + + if (! NT_STATUS_IS_OK(ret = idmap_alloc_init())) { + return ret; + } + + id->type = ID_TYPE_GID; + return idmap_alloc_ctx->methods->allocate_id(id); +} + +NTSTATUS idmap_set_uid_hwm(struct unixid *id) +{ + NTSTATUS ret; + + if (! NT_STATUS_IS_OK(ret = idmap_alloc_init())) { + return ret; + } + + id->type = ID_TYPE_UID; + return idmap_alloc_ctx->methods->set_id_hwm(id); +} + +NTSTATUS idmap_set_gid_hwm(struct unixid *id) +{ + NTSTATUS ret; + + if (! NT_STATUS_IS_OK(ret = idmap_alloc_init())) { + return ret; + } + + id->type = ID_TYPE_GID; + return idmap_alloc_ctx->methods->set_id_hwm(id); +} + +/****************************************************************************** + Lookup an idmap_domain give a full user or group SID + ******************************************************************************/ + +static struct idmap_domain* find_idmap_domain_from_sid( DOM_SID *account_sid ) +{ + DOM_SID domain_sid; + uint32 rid; + struct winbindd_domain *domain = NULL; + int i; + + /* 1. Handle BUILTIN or Special SIDs and prevent them from + falling into the default domain space (if we have a + configured passdb backend. */ + + if ( (pdb_dom_num != -1) && + (sid_check_is_in_builtin(account_sid) || + sid_check_is_in_wellknown_domain(account_sid) || + sid_check_is_in_unix_groups(account_sid) || + sid_check_is_in_unix_users(account_sid)) ) + { + return idmap_domains[pdb_dom_num]; + } + + /* 2. Lookup the winbindd_domain from the account_sid */ + + sid_copy( &domain_sid, account_sid ); + sid_split_rid( &domain_sid, &rid ); + domain = find_domain_from_sid_noinit( &domain_sid ); + + for (i = 0; domain && i < num_domains; i++) { + if ( strequal( idmap_domains[i]->name, domain->name ) ) { + return idmap_domains[i]; + } + } + + /* 3. Fall back to the default domain */ + + if ( def_dom_num != -1 ) { + return idmap_domains[def_dom_num]; + } + + return NULL; +} + +/****************************************************************************** + Lookup an index given an idmap_domain pointer + ******************************************************************************/ + +static uint32 find_idmap_domain_index( struct idmap_domain *id_domain) +{ + int i; + + for (i = 0; i < num_domains; i++) { + if ( idmap_domains[i] == id_domain ) + return i; + } + + return -1; +} + + +/********************************************************* + Check if creating a mapping is permitted for the domain +*********************************************************/ + +static NTSTATUS idmap_can_map(const struct id_map *map, + struct idmap_domain **ret_dom) +{ + struct idmap_domain *dom; + + /* Check we do not create mappings for our own local domain, + * or BUILTIN or special SIDs */ + if ((sid_compare_domain(map->sid, get_global_sam_sid()) == 0) || + sid_check_is_in_builtin(map->sid) || + sid_check_is_in_wellknown_domain(map->sid) || + sid_check_is_in_unix_users(map->sid) || + sid_check_is_in_unix_groups(map->sid) ) + { + DEBUG(10, ("We are not supposed to create mappings for our own " + "domains (local, builtin, specials)\n")); + return NT_STATUS_UNSUCCESSFUL; + } + + /* Special check for trusted domain only = Yes */ + if (lp_winbind_trusted_domains_only()) { + struct winbindd_domain *wdom = find_our_domain(); + if (wdom && (sid_compare_domain(map->sid, &wdom->sid) == 0)) { + DEBUG(10, ("We are not supposed to create mappings for " + "our primary domain when is True\n")); + DEBUGADD(10, ("Leave [%s] unmapped\n", + sid_string_static(map->sid))); + return NT_STATUS_UNSUCCESSFUL; + } + } + + if ( (dom = find_idmap_domain_from_sid( map->sid )) == NULL ) { + /* huh, couldn't find a suitable domain, + * let's just leave it unmapped */ + DEBUG(10, ("Could not find idmap backend for SID %s", + sid_string_static(map->sid))); + return NT_STATUS_NO_SUCH_DOMAIN; + } + + if (dom->readonly) { + /* ouch the domain is read only, + * let's just leave it unmapped */ + DEBUG(10, ("idmap backend for SID %s is READONLY!\n", + sid_string_static(map->sid))); + return NT_STATUS_UNSUCCESSFUL; + } + + *ret_dom = dom; + return NT_STATUS_OK; +} + +static NTSTATUS idmap_new_mapping(TALLOC_CTX *ctx, struct id_map *map) +{ + NTSTATUS ret; + struct idmap_domain *dom; + + /* If we are offline we cannot lookup SIDs, deny mapping */ + if (idmap_is_offline()) { + return NT_STATUS_FILE_IS_OFFLINE; + } + + ret = idmap_can_map(map, &dom); + if ( ! NT_STATUS_IS_OK(ret)) { + return NT_STATUS_NONE_MAPPED; + } + + /* check if this is a valid SID and then map it */ + switch (map->xid.type) { + case ID_TYPE_UID: + ret = idmap_allocate_uid(&map->xid); + if ( ! NT_STATUS_IS_OK(ret)) { + /* can't allocate id, let's just leave it unmapped */ + DEBUG(2, ("uid allocation failed! " + "Can't create mapping\n")); + return NT_STATUS_NONE_MAPPED; + } + break; + case ID_TYPE_GID: + ret = idmap_allocate_gid(&map->xid); + if ( ! NT_STATUS_IS_OK(ret)) { + /* can't allocate id, let's just leave it unmapped */ + DEBUG(2, ("gid allocation failed! " + "Can't create mapping\n")); + return NT_STATUS_NONE_MAPPED; + } + break; + default: + /* invalid sid, let's just leave it unmapped */ + DEBUG(3,("idmap_new_mapping: Refusing to create a " + "mapping for an unspecified ID type.\n")); + return NT_STATUS_NONE_MAPPED; + } + + /* ok, got a new id, let's set a mapping */ + map->status = ID_MAPPED; + + DEBUG(10, ("Setting mapping: %s <-> %s %lu\n", + sid_string_static(map->sid), + (map->xid.type == ID_TYPE_UID) ? "UID" : "GID", + (unsigned long)map->xid.id)); + ret = dom->methods->set_mapping(dom, map); + + if ( ! NT_STATUS_IS_OK(ret)) { + /* something wrong here :-( */ + DEBUG(2, ("Failed to commit mapping\n!")); + + /* TODO: would it make sense to have an "unalloc_id function?" */ + + return NT_STATUS_NONE_MAPPED; + } + + return NT_STATUS_OK; +} + +static NTSTATUS idmap_backends_set_mapping(const struct id_map *map) +{ + struct idmap_domain *dom; + NTSTATUS ret; + + DEBUG(10, ("Setting mapping %s <-> %s %lu\n", + sid_string_static(map->sid), + (map->xid.type == ID_TYPE_UID) ? "UID" : "GID", + (unsigned long)map->xid.id)); + + ret = idmap_can_map(map, &dom); + if ( ! NT_STATUS_IS_OK(ret)) { + return ret; + } + + DEBUG(10,("set_mapping for domain %s\n", dom->name )); + + return dom->methods->set_mapping(dom, map); +} + +static NTSTATUS idmap_backends_unixids_to_sids(struct id_map **ids) +{ + struct idmap_domain *dom; + struct id_map **unmapped; + struct id_map **_ids; + TALLOC_CTX *ctx; + NTSTATUS ret; + int i, u, n; + + if (!ids || !*ids) { + DEBUG(1, ("Invalid list of maps\n")); + return NT_STATUS_INVALID_PARAMETER; + } + + ctx = talloc_named_const(NULL, 0, "idmap_backends_unixids_to_sids ctx"); + if ( ! ctx) { + DEBUG(0, ("Out of memory!\n")); + return NT_STATUS_NO_MEMORY; + } + + DEBUG(10, ("Query backends to map ids->sids\n")); + + /* start from the default (the last one) and then if there are still + * unmapped entries cycle through the others */ + + _ids = ids; + + unmapped = NULL; + for (n = num_domains-1; n >= 0; n--) { /* cycle backwards */ + + dom = idmap_domains[n]; + + DEBUG(10, ("Query sids from domain %s\n", dom->name)); + + ret = dom->methods->unixids_to_sids(dom, _ids); + IDMAP_REPORT_RET(ret); + + unmapped = NULL; + + for (i = 0, u = 0; _ids[i]; i++) { + if (_ids[i]->status != ID_MAPPED) { + unmapped = talloc_realloc(ctx, unmapped, + struct id_map *, u + 2); + IDMAP_CHECK_ALLOC(unmapped); + unmapped[u] = _ids[i]; + u++; + } + } + if (unmapped) { + /* terminate the unmapped list */ + unmapped[u] = NULL; + } else { /* no more entries, get out */ + break; + } + + _ids = unmapped; + + } + + if (unmapped) { + /* there are still unmapped ids, + * map them to the unix users/groups domains */ + /* except for expired entries, + * these will be returned as valid (offline mode) */ + for (i = 0; unmapped[i]; i++) { + if (unmapped[i]->status == ID_EXPIRED) continue; + switch (unmapped[i]->xid.type) { + case ID_TYPE_UID: + uid_to_unix_users_sid( + (uid_t)unmapped[i]->xid.id, + unmapped[i]->sid); + unmapped[i]->status = ID_MAPPED; + break; + case ID_TYPE_GID: + gid_to_unix_groups_sid( + (gid_t)unmapped[i]->xid.id, + unmapped[i]->sid); + unmapped[i]->status = ID_MAPPED; + break; + default: /* what?! */ + unmapped[i]->status = ID_UNKNOWN; + break; + } + } + } + + ret = NT_STATUS_OK; + +done: + talloc_free(ctx); + return ret; +} + +static NTSTATUS idmap_backends_sids_to_unixids(struct id_map **ids) +{ + struct id_map ***dom_ids; + struct idmap_domain *dom; + TALLOC_CTX *ctx; + NTSTATUS ret; + int i, *counters; + + if ( (ctx = talloc_named_const(NULL, 0, "be_sids_to_ids")) == NULL ) { + DEBUG(1, ("failed to allocate talloc context, OOM?\n")); + return NT_STATUS_NO_MEMORY; + } + + DEBUG(10, ("Query backends to map sids->ids\n")); + + /* split list per domain */ + if (num_domains == 0) { + DEBUG(1, ("No domains available?\n")); + return NT_STATUS_UNSUCCESSFUL; + } + + dom_ids = TALLOC_ZERO_ARRAY(ctx, struct id_map **, num_domains); + IDMAP_CHECK_ALLOC(dom_ids); + counters = TALLOC_ZERO_ARRAY(ctx, int, num_domains); + IDMAP_CHECK_ALLOC(counters); + + /* partition the requests by domain */ + + for (i = 0; ids[i]; i++) { + uint32 idx; + + if ((dom = find_idmap_domain_from_sid(ids[i]->sid)) == NULL) { + /* no available idmap_domain. Move on */ + continue; + } + + DEBUG(10,("SID %s is being handled by %s\n", + sid_string_static(ids[i]->sid), + dom ? dom->name : "none" )); + + idx = find_idmap_domain_index( dom ); + SMB_ASSERT( idx != -1 ); + + dom_ids[idx] = talloc_realloc(ctx, dom_ids[idx], + struct id_map *, + counters[idx] + 2); + IDMAP_CHECK_ALLOC(dom_ids[idx]); + + dom_ids[idx][counters[idx]] = ids[i]; + counters[idx]++; + dom_ids[idx][counters[idx]] = NULL; + } + + /* All the ids have been dispatched in the right queues. + Let's cycle through the filled ones */ + + for (i = 0; i < num_domains; i++) { + if (dom_ids[i]) { + dom = idmap_domains[i]; + DEBUG(10, ("Query ids from domain %s\n", dom->name)); + ret = dom->methods->sids_to_unixids(dom, dom_ids[i]); + IDMAP_REPORT_RET(ret); + } + } + + /* ok all the backends have been contacted at this point */ + /* let's see if we have any unmapped SID left and act accordingly */ + + for (i = 0; ids[i]; i++) { + /* NOTE: this will NOT touch ID_EXPIRED entries that the backend + * was not able to confirm/deny (offline mode) */ + if (ids[i]->status == ID_UNKNOWN || + ids[i]->status == ID_UNMAPPED) { + /* ok this is an unmapped one, see if we can map it */ + ret = idmap_new_mapping(ctx, ids[i]); + if (NT_STATUS_IS_OK(ret)) { + /* successfully mapped */ + ids[i]->status = ID_MAPPED; + } else + if (NT_STATUS_EQUAL(ret, NT_STATUS_NONE_MAPPED)) { + /* could not map it */ + ids[i]->status = ID_UNMAPPED; + } else { + /* Something very bad happened down there + * OR we are offline */ + ids[i]->status = ID_UNKNOWN; + } + } + } + + ret = NT_STATUS_OK; + +done: + talloc_free(ctx); + return ret; +} + +/************************************************************************** + idmap interface functions +**************************************************************************/ + +NTSTATUS idmap_unixids_to_sids(struct id_map **ids) +{ + TALLOC_CTX *ctx; + NTSTATUS ret; + struct id_map **bids; + int i, bi; + int bn = 0; + struct winbindd_domain *our_domain = find_our_domain(); + + if (! NT_STATUS_IS_OK(ret = idmap_init())) { + return ret; + } + + if (!ids || !*ids) { + DEBUG(1, ("Invalid list of maps\n")); + return NT_STATUS_INVALID_PARAMETER; + } + + ctx = talloc_named_const(NULL, 0, "idmap_unixids_to_sids ctx"); + if ( ! ctx) { + DEBUG(1, ("failed to allocate talloc context, OOM?\n")); + return NT_STATUS_NO_MEMORY; + } + + /* no ids to be asked to the backends by default */ + bids = NULL; + bi = 0; + + for (i = 0; ids[i]; i++) { + + if ( ! ids[i]->sid) { + DEBUG(1, ("invalid null SID in id_map array")); + talloc_free(ctx); + return NT_STATUS_INVALID_PARAMETER; + } + + ret = idmap_cache_map_id(idmap_cache, ids[i]); + + if ( ! NT_STATUS_IS_OK(ret)) { + + if ( ! bids) { + /* alloc space for ids to be resolved by + * backends (realloc ten by ten) */ + bids = TALLOC_ARRAY(ctx, struct id_map *, 10); + if ( ! bids) { + DEBUG(1, ("Out of memory!\n")); + talloc_free(ctx); + return NT_STATUS_NO_MEMORY; + } + bn = 10; + } + + /* add this id to the ones to be retrieved + * from the backends */ + bids[bi] = ids[i]; + bi++; + + /* check if we need to allocate new space + * on the rids array */ + if (bi == bn) { + bn += 10; + bids = talloc_realloc(ctx, bids, + struct id_map *, bn); + if ( ! bids) { + DEBUG(1, ("Out of memory!\n")); + talloc_free(ctx); + return NT_STATUS_NO_MEMORY; + } + } + + /* make sure the last element is NULL */ + bids[bi] = NULL; + } + } + + /* let's see if there is any id mapping to be retieved + * from the backends */ + if (bi) { + /* Only do query if we are online */ + if ( IS_DOMAIN_OFFLINE(our_domain) ) { + ret = NT_STATUS_FILE_IS_OFFLINE; + goto done; + } + + ret = idmap_backends_unixids_to_sids(bids); + IDMAP_CHECK_RET(ret); + + /* update the cache */ + for (i = 0; i < bi; i++) { + if (bids[i]->status == ID_MAPPED) { + ret = idmap_cache_set(idmap_cache, bids[i]); + } else if (bids[i]->status == ID_EXPIRED) { + /* the cache returned an expired entry and the + * backend was not able to clear the situation + * (offline). This handles a previous + * NT_STATUS_SYNCHRONIZATION_REQUIRED + * for disconnected mode, */ + bids[i]->status = ID_MAPPED; + } else if (bids[i]->status == ID_UNKNOWN) { + /* something bad here. We were not able to + * handle this for some reason, mark it as + * unmapped and hope next time things will + * settle down. */ + bids[i]->status = ID_UNMAPPED; + } else { /* unmapped */ + ret = idmap_cache_set_negative_id(idmap_cache, + bids[i]); + } + IDMAP_CHECK_RET(ret); + } + } + + ret = NT_STATUS_OK; +done: + talloc_free(ctx); + return ret; +} + +NTSTATUS idmap_sids_to_unixids(struct id_map **ids) +{ + TALLOC_CTX *ctx; + NTSTATUS ret; + struct id_map **bids; + int i, bi; + int bn = 0; + struct winbindd_domain *our_domain = find_our_domain(); + + if (! NT_STATUS_IS_OK(ret = idmap_init())) { + return ret; + } + + if (!ids || !*ids) { + DEBUG(1, ("Invalid list of maps\n")); + return NT_STATUS_INVALID_PARAMETER; + } + + ctx = talloc_named_const(NULL, 0, "idmap_sids_to_unixids ctx"); + if ( ! ctx) { + DEBUG(1, ("failed to allocate talloc context, OOM?\n")); + return NT_STATUS_NO_MEMORY; + } + + /* no ids to be asked to the backends by default */ + bids = NULL; + bi = 0; + + for (i = 0; ids[i]; i++) { + + if ( ! ids[i]->sid) { + DEBUG(1, ("invalid null SID in id_map array\n")); + talloc_free(ctx); + return NT_STATUS_INVALID_PARAMETER; + } + + ret = idmap_cache_map_sid(idmap_cache, ids[i]); + + if ( ! NT_STATUS_IS_OK(ret)) { + + if ( ! bids) { + /* alloc space for ids to be resolved + by backends (realloc ten by ten) */ + bids = TALLOC_ARRAY(ctx, struct id_map *, 10); + if ( ! bids) { + DEBUG(1, ("Out of memory!\n")); + talloc_free(ctx); + return NT_STATUS_NO_MEMORY; + } + bn = 10; + } + + /* add this id to the ones to be retrieved + * from the backends */ + bids[bi] = ids[i]; + bi++; + + /* check if we need to allocate new space + * on the ids array */ + if (bi == bn) { + bn += 10; + bids = talloc_realloc(ctx, bids, + struct id_map *, bn); + if ( ! bids) { + DEBUG(1, ("Out of memory!\n")); + talloc_free(ctx); + return NT_STATUS_NO_MEMORY; + } + } + + /* make sure the last element is NULL */ + bids[bi] = NULL; + } + } + + /* let's see if there is any id mapping to be retieved + * from the backends */ + if (bids) { + /* Only do query if we are online */ + if ( IS_DOMAIN_OFFLINE(our_domain) ) { + ret = NT_STATUS_FILE_IS_OFFLINE; + goto done; + } + + ret = idmap_backends_sids_to_unixids(bids); + IDMAP_CHECK_RET(ret); + + /* update the cache */ + for (i = 0; bids[i]; i++) { + if (bids[i]->status == ID_MAPPED) { + ret = idmap_cache_set(idmap_cache, bids[i]); + } else if (bids[i]->status == ID_EXPIRED) { + /* the cache returned an expired entry and the + * backend was not able to clear the situation + * (offline). This handles a previous + * NT_STATUS_SYNCHRONIZATION_REQUIRED + * for disconnected mode, */ + bids[i]->status = ID_MAPPED; + } else if (bids[i]->status == ID_UNKNOWN) { + /* something bad here. We were not able to + * handle this for some reason, mark it as + * unmapped and hope next time things will + * settle down. */ + bids[i]->status = ID_UNMAPPED; + } else { /* unmapped */ + ret = idmap_cache_set_negative_sid(idmap_cache, + bids[i]); + } + IDMAP_CHECK_RET(ret); + } + } + + ret = NT_STATUS_OK; +done: + talloc_free(ctx); + return ret; +} + +NTSTATUS idmap_set_mapping(const struct id_map *id) +{ + TALLOC_CTX *ctx; + NTSTATUS ret; + + if (! NT_STATUS_IS_OK(ret = idmap_init())) { + return ret; + } + + /* sanity checks */ + if ((id->sid == NULL) || (id->status != ID_MAPPED)) { + DEBUG(1, ("NULL SID or unmapped entry\n")); + return NT_STATUS_INVALID_PARAMETER; + } + + /* TODO: check uid/gid range ? */ + + ctx = talloc_named_const(NULL, 0, "idmap_set_mapping ctx"); + if ( ! ctx) { + DEBUG(1, ("failed to allocate talloc context, OOM?\n")); + return NT_STATUS_NO_MEMORY; + } + + /* set the new mapping */ + ret = idmap_backends_set_mapping(id); + IDMAP_CHECK_RET(ret); + + /* set the mapping in the cache */ + ret = idmap_cache_set(idmap_cache, id); + IDMAP_CHECK_RET(ret); + +done: + talloc_free(ctx); + return ret; +} + +/************************************************************************** + Dump backend status. +**************************************************************************/ + +void idmap_dump_maps(char *logfile) +{ + NTSTATUS ret; + struct unixid allid; + struct id_map *maps; + int num_maps; + FILE *dump; + int i; + + if (! NT_STATUS_IS_OK(ret = idmap_init())) { + return; + } + + dump = fopen(logfile, "w"); + if ( ! dump) { + DEBUG(0, ("Unable to open open stream for file [%s], " + "errno: %d\n", logfile, errno)); + return; + } + + if (NT_STATUS_IS_OK(ret = idmap_alloc_init())) { + allid.type = ID_TYPE_UID; + allid.id = 0; + idmap_alloc_ctx->methods->get_id_hwm(&allid); + fprintf(dump, "USER HWM %lu\n", (unsigned long)allid.id); + + allid.type = ID_TYPE_GID; + allid.id = 0; + idmap_alloc_ctx->methods->get_id_hwm(&allid); + fprintf(dump, "GROUP HWM %lu\n", (unsigned long)allid.id); + } + + maps = talloc(idmap_ctx, struct id_map); + num_maps = 0; + + for (i = 0; i < num_domains; i++) { + if (idmap_domains[i]->methods->dump_data) { + idmap_domains[i]->methods->dump_data(idmap_domains[i], + &maps, &num_maps); + } + } + + for (i = 0; i < num_maps; i++) { + switch (maps[i].xid.type) { + case ID_TYPE_UID: + fprintf(dump, "UID %lu %s\n", + (unsigned long)maps[i].xid.id, + sid_string_static(maps[i].sid)); + break; + case ID_TYPE_GID: + fprintf(dump, "GID %lu %s\n", + (unsigned long)maps[i].xid.id, + sid_string_static(maps[i].sid)); + break; + case ID_TYPE_NOT_SPECIFIED: + break; + } + } + + fflush(dump); + fclose(dump); +} + +char *idmap_fetch_secret(const char *backend, bool alloc, + const char *domain, const char *identity) +{ + char *tmp, *ret; + int r; + + if (alloc) { + r = asprintf(&tmp, "IDMAP_ALLOC_%s", backend); + } else { + r = asprintf(&tmp, "IDMAP_%s_%s", backend, domain); + } + + if (r < 0) + return NULL; + + strupper_m(tmp); /* make sure the key is case insensitive */ + ret = secrets_fetch_generic(tmp, identity); + + SAFE_FREE(tmp); + + return ret; +} + diff --git a/source3/winbindd/idmap_ad.c b/source3/winbindd/idmap_ad.c new file mode 100644 index 0000000000..41dbb471f9 --- /dev/null +++ b/source3/winbindd/idmap_ad.c @@ -0,0 +1,870 @@ +/* + * idmap_ad: map between Active Directory and RFC 2307 or "Services for Unix" (SFU) Accounts + * + * Unix SMB/CIFS implementation. + * + * Winbind ADS backend functions + * + * Copyright (C) Andrew Tridgell 2001 + * Copyright (C) Andrew Bartlett 2003 + * Copyright (C) Gerald (Jerry) Carter 2004-2007 + * Copyright (C) Luke Howard 2001-2004 + * + * 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 3 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, see . + */ + +#include "includes.h" + +#undef DBGC_CLASS +#define DBGC_CLASS DBGC_IDMAP + +#define WINBIND_CCACHE_NAME "MEMORY:winbind_ccache" + +#define IDMAP_AD_MAX_IDS 30 +#define CHECK_ALLOC_DONE(mem) do { \ + if (!mem) { \ + DEBUG(0, ("Out of memory!\n")); \ + ret = NT_STATUS_NO_MEMORY; \ + goto done; \ + } \ +} while (0) + +struct idmap_ad_context { + uint32_t filter_low_id; + uint32_t filter_high_id; +}; + +NTSTATUS init_module(void); + +static ADS_STRUCT *ad_idmap_ads = NULL; +static struct posix_schema *ad_schema = NULL; +static enum wb_posix_mapping ad_map_type = WB_POSIX_MAP_UNKNOWN; + +/************************************************************************ + ***********************************************************************/ + +static ADS_STRUCT *ad_idmap_cached_connection_internal(void) +{ + ADS_STRUCT *ads; + ADS_STATUS status; + BOOL local = False; + fstring dc_name; + struct in_addr dc_ip; + + if (ad_idmap_ads != NULL) { + + time_t expire; + time_t now = time(NULL); + + ads = ad_idmap_ads; + + expire = MIN(ads->auth.tgt_expire, ads->auth.tgs_expire); + + /* check for a valid structure */ + DEBUG(7, ("Current tickets expire in %d seconds (at %d, time is now %d)\n", + (uint32)expire-(uint32)now, (uint32) expire, (uint32) now)); + + if ( ads->config.realm && (expire > time(NULL))) { + return ads; + } else { + /* we own this ADS_STRUCT so make sure it goes away */ + DEBUG(7,("Deleting expired krb5 credential cache\n")); + ads->is_mine = True; + ads_destroy( &ads ); + ads_kdestroy(WINBIND_CCACHE_NAME); + ad_idmap_ads = NULL; + TALLOC_FREE( ad_schema ); + } + } + + if (!local) { + /* we don't want this to affect the users ccache */ + setenv("KRB5CCNAME", WINBIND_CCACHE_NAME, 1); + } + + if ( (ads = ads_init(lp_realm(), lp_workgroup(), NULL)) == NULL ) { + DEBUG(1,("ads_init failed\n")); + return NULL; + } + + /* the machine acct password might have change - fetch it every time */ + SAFE_FREE(ads->auth.password); + ads->auth.password = secrets_fetch_machine_password(lp_workgroup(), NULL, NULL); + + SAFE_FREE(ads->auth.realm); + ads->auth.realm = SMB_STRDUP(lp_realm()); + + /* setup server affinity */ + + get_dc_name( NULL, ads->auth.realm, dc_name, &dc_ip ); + + status = ads_connect(ads); + if (!ADS_ERR_OK(status)) { + DEBUG(1, ("ad_idmap_init: failed to connect to AD\n")); + ads_destroy(&ads); + return NULL; + } + + ads->is_mine = False; + + ad_idmap_ads = ads; + + return ads; +} + +/************************************************************************ + ***********************************************************************/ + +static ADS_STRUCT *ad_idmap_cached_connection(void) +{ + ADS_STRUCT *ads = ad_idmap_cached_connection_internal(); + + if ( !ads ) + return NULL; + + /* if we have a valid ADS_STRUCT and the schema model is + defined, then we can return here. */ + + if ( ad_schema ) + return ads; + + /* Otherwise, set the schema model */ + + if ( (ad_map_type == WB_POSIX_MAP_SFU) || + (ad_map_type == WB_POSIX_MAP_SFU20) || + (ad_map_type == WB_POSIX_MAP_RFC2307) ) + { + ADS_STATUS schema_status; + + schema_status = ads_check_posix_schema_mapping( NULL, ads, ad_map_type, &ad_schema); + if ( !ADS_ERR_OK(schema_status) ) { + DEBUG(2,("ad_idmap_cached_connection: Failed to obtain schema details!\n")); + return NULL; + } + } + + return ads; +} + +/************************************************************************ + ***********************************************************************/ + +static NTSTATUS idmap_ad_initialize(struct idmap_domain *dom) +{ + struct idmap_ad_context *ctx; + char *config_option; + const char *range = NULL; + const char *schema_mode = NULL; + + if ( (ctx = TALLOC_ZERO_P(dom, struct idmap_ad_context)) == NULL ) { + DEBUG(0, ("Out of memory!\n")); + return NT_STATUS_NO_MEMORY; + } + + if ( (config_option = talloc_asprintf(ctx, "idmap config %s", dom->name)) == NULL ) { + DEBUG(0, ("Out of memory!\n")); + talloc_free(ctx); + return NT_STATUS_NO_MEMORY; + } + + /* load ranges */ + range = lp_parm_const_string(-1, config_option, "range", NULL); + if (range && range[0]) { + if ((sscanf(range, "%u - %u", &ctx->filter_low_id, &ctx->filter_high_id) != 2) || + (ctx->filter_low_id > ctx->filter_high_id)) { + DEBUG(1, ("ERROR: invalid filter range [%s]", range)); + ctx->filter_low_id = 0; + ctx->filter_high_id = 0; + } + } + + /* schema mode */ + if ( ad_map_type == WB_POSIX_MAP_UNKNOWN ) + ad_map_type = WB_POSIX_MAP_RFC2307; + schema_mode = lp_parm_const_string(-1, config_option, "schema_mode", NULL); + if ( schema_mode && schema_mode[0] ) { + if ( strequal(schema_mode, "sfu") ) + ad_map_type = WB_POSIX_MAP_SFU; + else if ( strequal(schema_mode, "sfu20" ) ) + ad_map_type = WB_POSIX_MAP_SFU20; + else if ( strequal(schema_mode, "rfc2307" ) ) + ad_map_type = WB_POSIX_MAP_RFC2307; + else + DEBUG(0,("idmap_ad_initialize: Unknown schema_mode (%s)\n", + schema_mode)); + } + + dom->private_data = ctx; + dom->initialized = True; + + talloc_free(config_option); + + return NT_STATUS_OK; +} + +/************************************************************************ + Search up to IDMAP_AD_MAX_IDS entries in maps for a match. + ***********************************************************************/ + +static struct id_map *find_map_by_id(struct id_map **maps, enum id_type type, uint32_t id) +{ + int i; + + for (i = 0; maps[i] && ixid.type == type) && (maps[i]->xid.id == id)) { + return maps[i]; + } + } + + return NULL; +} + +/************************************************************************ + Search up to IDMAP_AD_MAX_IDS entries in maps for a match + ***********************************************************************/ + +static struct id_map *find_map_by_sid(struct id_map **maps, DOM_SID *sid) +{ + int i; + + for (i = 0; maps[i] && isid, sid)) { + return maps[i]; + } + } + + return NULL; +} + +/************************************************************************ + ***********************************************************************/ + +static NTSTATUS idmap_ad_unixids_to_sids(struct idmap_domain *dom, struct id_map **ids) +{ + NTSTATUS ret; + TALLOC_CTX *memctx; + struct idmap_ad_context *ctx; + ADS_STATUS rc; + ADS_STRUCT *ads; + const char *attrs[] = { "sAMAccountType", + "objectSid", + NULL, /* uidnumber */ + NULL, /* gidnumber */ + NULL }; + LDAPMessage *res = NULL; + LDAPMessage *entry = NULL; + char *filter = NULL; + int idx = 0; + int bidx = 0; + int count; + int i; + char *u_filter = NULL; + char *g_filter = NULL; + + /* Only do query if we are online */ + if (idmap_is_offline()) { + return NT_STATUS_FILE_IS_OFFLINE; + } + + /* Initilization my have been deferred because we were offline */ + if ( ! dom->initialized) { + ret = idmap_ad_initialize(dom); + if ( ! NT_STATUS_IS_OK(ret)) { + return ret; + } + } + + ctx = talloc_get_type(dom->private_data, struct idmap_ad_context); + + if ( (memctx = talloc_new(ctx)) == NULL ) { + DEBUG(0, ("Out of memory!\n")); + return NT_STATUS_NO_MEMORY; + } + + if ( (ads = ad_idmap_cached_connection()) == NULL ) { + DEBUG(1, ("ADS uninitialized\n")); + ret = NT_STATUS_UNSUCCESSFUL; + goto done; + } + + attrs[2] = ad_schema->posix_uidnumber_attr; + attrs[3] = ad_schema->posix_gidnumber_attr; + +again: + bidx = idx; + for (i = 0; (i < IDMAP_AD_MAX_IDS) && ids[idx]; i++, idx++) { + switch (ids[idx]->xid.type) { + case ID_TYPE_UID: + if ( ! u_filter) { + u_filter = talloc_asprintf(memctx, "(&(|" + "(sAMAccountType=%d)" + "(sAMAccountType=%d)" + "(sAMAccountType=%d))(|", + ATYPE_NORMAL_ACCOUNT, + ATYPE_WORKSTATION_TRUST, + ATYPE_INTERDOMAIN_TRUST); + } + u_filter = talloc_asprintf_append(u_filter, "(%s=%lu)", + ad_schema->posix_uidnumber_attr, + (unsigned long)ids[idx]->xid.id); + CHECK_ALLOC_DONE(u_filter); + break; + + case ID_TYPE_GID: + if ( ! g_filter) { + g_filter = talloc_asprintf(memctx, "(&(|" + "(sAMAccountType=%d)" + "(sAMAccountType=%d))(|", + ATYPE_SECURITY_GLOBAL_GROUP, + ATYPE_SECURITY_LOCAL_GROUP); + } + g_filter = talloc_asprintf_append(g_filter, "(%s=%lu)", + ad_schema->posix_gidnumber_attr, + (unsigned long)ids[idx]->xid.id); + CHECK_ALLOC_DONE(g_filter); + break; + + default: + DEBUG(3, ("Error: mapping requested but Unknown ID type\n")); + ids[idx]->status = ID_UNKNOWN; + continue; + } + } + filter = talloc_asprintf(memctx, "(|"); + CHECK_ALLOC_DONE(filter); + if ( u_filter) { + filter = talloc_asprintf_append(filter, "%s))", u_filter); + CHECK_ALLOC_DONE(filter); + TALLOC_FREE(u_filter); + } + if ( g_filter) { + filter = talloc_asprintf_append(filter, "%s))", g_filter); + CHECK_ALLOC_DONE(filter); + TALLOC_FREE(g_filter); + } + filter = talloc_asprintf_append(filter, ")"); + CHECK_ALLOC_DONE(filter); + + rc = ads_search_retry(ads, &res, filter, attrs); + if (!ADS_ERR_OK(rc)) { + DEBUG(1, ("ERROR: ads search returned: %s\n", ads_errstr(rc))); + ret = NT_STATUS_UNSUCCESSFUL; + goto done; + } + + if ( (count = ads_count_replies(ads, res)) == 0 ) { + DEBUG(10, ("No IDs found\n")); + } + + entry = res; + for (i = 0; (i < count) && entry; i++) { + DOM_SID sid; + enum id_type type; + struct id_map *map; + uint32_t id; + uint32_t atype; + + if (i == 0) { /* first entry */ + entry = ads_first_entry(ads, entry); + } else { /* following ones */ + entry = ads_next_entry(ads, entry); + } + + if ( !entry ) { + DEBUG(2, ("ERROR: Unable to fetch ldap entries from results\n")); + break; + } + + /* first check if the SID is present */ + if (!ads_pull_sid(ads, entry, "objectSid", &sid)) { + DEBUG(2, ("Could not retrieve SID from entry\n")); + continue; + } + + /* get type */ + if (!ads_pull_uint32(ads, entry, "sAMAccountType", &atype)) { + DEBUG(1, ("could not get SAM account type\n")); + continue; + } + + switch (atype & 0xF0000000) { + case ATYPE_SECURITY_GLOBAL_GROUP: + case ATYPE_SECURITY_LOCAL_GROUP: + type = ID_TYPE_GID; + break; + case ATYPE_NORMAL_ACCOUNT: + case ATYPE_WORKSTATION_TRUST: + case ATYPE_INTERDOMAIN_TRUST: + type = ID_TYPE_UID; + break; + default: + DEBUG(1, ("unrecognized SAM account type %08x\n", atype)); + continue; + } + + if (!ads_pull_uint32(ads, entry, (type==ID_TYPE_UID) ? + ad_schema->posix_uidnumber_attr : + ad_schema->posix_gidnumber_attr, + &id)) + { + DEBUG(1, ("Could not get unix ID\n")); + continue; + } + + if ((id == 0) || + (ctx->filter_low_id && (id < ctx->filter_low_id)) || + (ctx->filter_high_id && (id > ctx->filter_high_id))) { + DEBUG(5, ("Requested id (%u) out of range (%u - %u). Filtered!\n", + id, ctx->filter_low_id, ctx->filter_high_id)); + continue; + } + + map = find_map_by_id(&ids[bidx], type, id); + if (!map) { + DEBUG(2, ("WARNING: couldn't match result with requested ID\n")); + continue; + } + + sid_copy(map->sid, &sid); + + /* mapped */ + map->status = ID_MAPPED; + + DEBUG(10, ("Mapped %s -> %lu (%d)\n", + sid_string_static(map->sid), + (unsigned long)map->xid.id, + map->xid.type)); + } + + if (res) { + ads_msgfree(ads, res); + } + + if (ids[idx]) { /* still some values to map */ + goto again; + } + + ret = NT_STATUS_OK; + + /* mark all unknown/expired ones as unmapped */ + for (i = 0; ids[i]; i++) { + if (ids[i]->status != ID_MAPPED) + ids[i]->status = ID_UNMAPPED; + } + +done: + talloc_free(memctx); + return ret; +} + +/************************************************************************ + ***********************************************************************/ + +static NTSTATUS idmap_ad_sids_to_unixids(struct idmap_domain *dom, struct id_map **ids) +{ + NTSTATUS ret; + TALLOC_CTX *memctx; + struct idmap_ad_context *ctx; + ADS_STATUS rc; + ADS_STRUCT *ads; + const char *attrs[] = { "sAMAccountType", + "objectSid", + NULL, /* attr_uidnumber */ + NULL, /* attr_gidnumber */ + NULL }; + LDAPMessage *res = NULL; + LDAPMessage *entry = NULL; + char *filter = NULL; + int idx = 0; + int bidx = 0; + int count; + int i; + char *sidstr; + + /* Only do query if we are online */ + if (idmap_is_offline()) { + return NT_STATUS_FILE_IS_OFFLINE; + } + + /* Initilization my have been deferred because we were offline */ + if ( ! dom->initialized) { + ret = idmap_ad_initialize(dom); + if ( ! NT_STATUS_IS_OK(ret)) { + return ret; + } + } + + ctx = talloc_get_type(dom->private_data, struct idmap_ad_context); + + if ( (memctx = talloc_new(ctx)) == NULL ) { + DEBUG(0, ("Out of memory!\n")); + return NT_STATUS_NO_MEMORY; + } + + if ( (ads = ad_idmap_cached_connection()) == NULL ) { + DEBUG(1, ("ADS uninitialized\n")); + ret = NT_STATUS_UNSUCCESSFUL; + goto done; + } + + attrs[2] = ad_schema->posix_uidnumber_attr; + attrs[3] = ad_schema->posix_gidnumber_attr; + +again: + filter = talloc_asprintf(memctx, "(&(|" + "(sAMAccountType=%d)(sAMAccountType=%d)(sAMAccountType=%d)" /* user account types */ + "(sAMAccountType=%d)(sAMAccountType=%d)" /* group account types */ + ")(|", + ATYPE_NORMAL_ACCOUNT, ATYPE_WORKSTATION_TRUST, ATYPE_INTERDOMAIN_TRUST, + ATYPE_SECURITY_GLOBAL_GROUP, ATYPE_SECURITY_LOCAL_GROUP); + + CHECK_ALLOC_DONE(filter); + + bidx = idx; + for (i = 0; (i < IDMAP_AD_MAX_IDS) && ids[idx]; i++, idx++) { + + sidstr = sid_binstring(ids[idx]->sid); + filter = talloc_asprintf_append(filter, "(objectSid=%s)", sidstr); + + free(sidstr); + CHECK_ALLOC_DONE(filter); + } + filter = talloc_asprintf_append(filter, "))"); + CHECK_ALLOC_DONE(filter); + DEBUG(10, ("Filter: [%s]\n", filter)); + + rc = ads_search_retry(ads, &res, filter, attrs); + if (!ADS_ERR_OK(rc)) { + DEBUG(1, ("ERROR: ads search returned: %s\n", ads_errstr(rc))); + ret = NT_STATUS_UNSUCCESSFUL; + goto done; + } + + if ( (count = ads_count_replies(ads, res)) == 0 ) { + DEBUG(10, ("No IDs found\n")); + } + + entry = res; + for (i = 0; (i < count) && entry; i++) { + DOM_SID sid; + enum id_type type; + struct id_map *map; + uint32_t id; + uint32_t atype; + + if (i == 0) { /* first entry */ + entry = ads_first_entry(ads, entry); + } else { /* following ones */ + entry = ads_next_entry(ads, entry); + } + + if ( !entry ) { + DEBUG(2, ("ERROR: Unable to fetch ldap entries from results\n")); + break; + } + + /* first check if the SID is present */ + if (!ads_pull_sid(ads, entry, "objectSid", &sid)) { + DEBUG(2, ("Could not retrieve SID from entry\n")); + continue; + } + + map = find_map_by_sid(&ids[bidx], &sid); + if (!map) { + DEBUG(2, ("WARNING: couldn't match result with requested SID\n")); + continue; + } + + /* get type */ + if (!ads_pull_uint32(ads, entry, "sAMAccountType", &atype)) { + DEBUG(1, ("could not get SAM account type\n")); + continue; + } + + switch (atype & 0xF0000000) { + case ATYPE_SECURITY_GLOBAL_GROUP: + case ATYPE_SECURITY_LOCAL_GROUP: + type = ID_TYPE_GID; + break; + case ATYPE_NORMAL_ACCOUNT: + case ATYPE_WORKSTATION_TRUST: + case ATYPE_INTERDOMAIN_TRUST: + type = ID_TYPE_UID; + break; + default: + DEBUG(1, ("unrecognized SAM account type %08x\n", atype)); + continue; + } + + if (!ads_pull_uint32(ads, entry, (type==ID_TYPE_UID) ? + ad_schema->posix_uidnumber_attr : + ad_schema->posix_gidnumber_attr, + &id)) + { + DEBUG(1, ("Could not get unix ID\n")); + continue; + } + if ((id == 0) || + (ctx->filter_low_id && (id < ctx->filter_low_id)) || + (ctx->filter_high_id && (id > ctx->filter_high_id))) { + DEBUG(5, ("Requested id (%u) out of range (%u - %u). Filtered!\n", + id, ctx->filter_low_id, ctx->filter_high_id)); + continue; + } + + /* mapped */ + map->xid.type = type; + map->xid.id = id; + map->status = ID_MAPPED; + + DEBUG(10, ("Mapped %s -> %lu (%d)\n", + sid_string_static(map->sid), + (unsigned long)map->xid.id, + map->xid.type)); + } + + if (res) { + ads_msgfree(ads, res); + } + + if (ids[idx]) { /* still some values to map */ + goto again; + } + + ret = NT_STATUS_OK; + + /* mark all unknwoni/expired ones as unmapped */ + for (i = 0; ids[i]; i++) { + if (ids[i]->status != ID_MAPPED) + ids[i]->status = ID_UNMAPPED; + } + +done: + talloc_free(memctx); + return ret; +} + +/************************************************************************ + ***********************************************************************/ + +static NTSTATUS idmap_ad_close(struct idmap_domain *dom) +{ + ADS_STRUCT *ads = ad_idmap_ads; + + if (ads != NULL) { + /* we own this ADS_STRUCT so make sure it goes away */ + ads->is_mine = True; + ads_destroy( &ads ); + ad_idmap_ads = NULL; + } + + TALLOC_FREE( ad_schema ); + + return NT_STATUS_OK; +} + +/* + * nss_info_{sfu,sfu20,rfc2307} + */ + +/************************************************************************ + Initialize the {sfu,sfu20,rfc2307} state + ***********************************************************************/ + +static NTSTATUS nss_sfu_init( struct nss_domain_entry *e ) +{ + /* Sanity check if we have previously been called with a + different schema model */ + + if ( (ad_map_type != WB_POSIX_MAP_UNKNOWN) && + (ad_map_type != WB_POSIX_MAP_SFU) ) + { + DEBUG(0,("nss_sfu_init: Posix Map type has already been set. " + "Mixed schema models not supported!\n")); + return NT_STATUS_NOT_SUPPORTED; + } + + ad_map_type = WB_POSIX_MAP_SFU; + + return NT_STATUS_OK; +} + +static NTSTATUS nss_sfu20_init( struct nss_domain_entry *e ) +{ + /* Sanity check if we have previously been called with a + different schema model */ + + if ( (ad_map_type != WB_POSIX_MAP_UNKNOWN) && + (ad_map_type != WB_POSIX_MAP_SFU20) ) + { + DEBUG(0,("nss_sfu20_init: Posix Map type has already been set. " + "Mixed schema models not supported!\n")); + return NT_STATUS_NOT_SUPPORTED; + } + + ad_map_type = WB_POSIX_MAP_SFU20; + + return NT_STATUS_OK; +} + +static NTSTATUS nss_rfc2307_init( struct nss_domain_entry *e ) +{ + /* Sanity check if we have previously been called with a + different schema model */ + + if ( (ad_map_type != WB_POSIX_MAP_UNKNOWN) && + (ad_map_type != WB_POSIX_MAP_RFC2307) ) + { + DEBUG(0,("nss_rfc2307_init: Posix Map type has already been set. " + "Mixed schema models not supported!\n")); + return NT_STATUS_NOT_SUPPORTED; + } + + ad_map_type = WB_POSIX_MAP_RFC2307; + + return NT_STATUS_OK; +} + + +/************************************************************************ + ***********************************************************************/ +static NTSTATUS nss_ad_get_info( struct nss_domain_entry *e, + const DOM_SID *sid, + TALLOC_CTX *ctx, + ADS_STRUCT *ads, + LDAPMessage *msg, + char **homedir, + char **shell, + char **gecos, + uint32 *gid ) +{ + ADS_STRUCT *ads_internal = NULL; + + /* Only do query if we are online */ + if (idmap_is_offline()) { + return NT_STATUS_FILE_IS_OFFLINE; + } + + /* We are assuming that the internal ADS_STRUCT is for the + same forest as the incoming *ads pointer */ + + ads_internal = ad_idmap_cached_connection(); + + if ( !ads_internal || !ad_schema ) + return NT_STATUS_OBJECT_NAME_NOT_FOUND; + + if ( !homedir || !shell || !gecos ) + return NT_STATUS_INVALID_PARAMETER; + + *homedir = ads_pull_string( ads, ctx, msg, ad_schema->posix_homedir_attr ); + *shell = ads_pull_string( ads, ctx, msg, ad_schema->posix_shell_attr ); + *gecos = ads_pull_string( ads, ctx, msg, ad_schema->posix_gecos_attr ); + + if ( gid ) { + if ( !ads_pull_uint32(ads, msg, ad_schema->posix_gidnumber_attr, gid ) ) + *gid = (uint32)-1; + } + + return NT_STATUS_OK; +} + +/************************************************************************ + ***********************************************************************/ + +static NTSTATUS nss_ad_close( void ) +{ + /* nothing to do. All memory is free()'d by the idmap close_fn() */ + + return NT_STATUS_OK; +} + +/************************************************************************ + Function dispatch tables for the idmap and nss plugins + ***********************************************************************/ + +static struct idmap_methods ad_methods = { + .init = idmap_ad_initialize, + .unixids_to_sids = idmap_ad_unixids_to_sids, + .sids_to_unixids = idmap_ad_sids_to_unixids, + .close_fn = idmap_ad_close +}; + +/* The SFU and RFC2307 NSS plugins share everything but the init + function which sets the intended schema model to use */ + +static struct nss_info_methods nss_rfc2307_methods = { + .init = nss_rfc2307_init, + .get_nss_info = nss_ad_get_info, + .close_fn = nss_ad_close +}; + +static struct nss_info_methods nss_sfu_methods = { + .init = nss_sfu_init, + .get_nss_info = nss_ad_get_info, + .close_fn = nss_ad_close +}; + +static struct nss_info_methods nss_sfu20_methods = { + .init = nss_sfu20_init, + .get_nss_info = nss_ad_get_info, + .close_fn = nss_ad_close +}; + + + +/************************************************************************ + Initialize the plugins + ***********************************************************************/ + +NTSTATUS idmap_ad_init(void) +{ + static NTSTATUS status_idmap_ad = NT_STATUS_UNSUCCESSFUL; + static NTSTATUS status_nss_rfc2307 = NT_STATUS_UNSUCCESSFUL; + static NTSTATUS status_nss_sfu = NT_STATUS_UNSUCCESSFUL; + static NTSTATUS status_nss_sfu20 = NT_STATUS_UNSUCCESSFUL; + + /* Always register the AD method first in order to get the + idmap_domain interface called */ + + if ( !NT_STATUS_IS_OK(status_idmap_ad) ) { + status_idmap_ad = smb_register_idmap(SMB_IDMAP_INTERFACE_VERSION, + "ad", &ad_methods); + if ( !NT_STATUS_IS_OK(status_idmap_ad) ) + return status_idmap_ad; + } + + if ( !NT_STATUS_IS_OK( status_nss_rfc2307 ) ) { + status_nss_rfc2307 = smb_register_idmap_nss(SMB_NSS_INFO_INTERFACE_VERSION, + "rfc2307", &nss_rfc2307_methods ); + if ( !NT_STATUS_IS_OK(status_nss_rfc2307) ) + return status_nss_rfc2307; + } + + if ( !NT_STATUS_IS_OK( status_nss_sfu ) ) { + status_nss_sfu = smb_register_idmap_nss(SMB_NSS_INFO_INTERFACE_VERSION, + "sfu", &nss_sfu_methods ); + if ( !NT_STATUS_IS_OK(status_nss_sfu) ) + return status_nss_sfu; + } + + if ( !NT_STATUS_IS_OK( status_nss_sfu20 ) ) { + status_nss_sfu20 = smb_register_idmap_nss(SMB_NSS_INFO_INTERFACE_VERSION, + "sfu20", &nss_sfu20_methods ); + if ( !NT_STATUS_IS_OK(status_nss_sfu20) ) + return status_nss_sfu20; + } + + return NT_STATUS_OK; +} + diff --git a/source3/winbindd/idmap_cache.c b/source3/winbindd/idmap_cache.c new file mode 100644 index 0000000000..4f01cb1392 --- /dev/null +++ b/source3/winbindd/idmap_cache.c @@ -0,0 +1,531 @@ +/* + Unix SMB/CIFS implementation. + ID Mapping Cache + + based on gencache + + Copyright (C) Simo Sorce 2006 + Copyright (C) Rafal Szczesniak 2002 + + 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 3 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, see .*/ + +#include "includes.h" +#include "winbindd.h" + +#define TIMEOUT_LEN 12 +#define IDMAP_CACHE_DATA_FMT "%12u/%s" +#define IDMAP_READ_CACHE_DATA_FMT_TEMPLATE "%%12u/%%%us" + +struct idmap_cache_ctx { + TDB_CONTEXT *tdb; +}; + +static int idmap_cache_destructor(struct idmap_cache_ctx *cache) +{ + int ret = 0; + + if (cache && cache->tdb) { + ret = tdb_close(cache->tdb); + cache->tdb = NULL; + } + + return ret; +} + +struct idmap_cache_ctx *idmap_cache_init(TALLOC_CTX *memctx) +{ + struct idmap_cache_ctx *cache; + char* cache_fname = NULL; + + cache = talloc(memctx, struct idmap_cache_ctx); + if ( ! cache) { + DEBUG(0, ("Out of memory!\n")); + return NULL; + } + + cache_fname = lock_path("idmap_cache.tdb"); + + DEBUG(10, ("Opening cache file at %s\n", cache_fname)); + + cache->tdb = tdb_open_log(cache_fname, 0, TDB_DEFAULT, O_RDWR|O_CREAT, 0600); + + if (!cache->tdb) { + DEBUG(5, ("Attempt to open %s has failed.\n", cache_fname)); + return NULL; + } + + talloc_set_destructor(cache, idmap_cache_destructor); + + return cache; +} + +void idmap_cache_shutdown(struct idmap_cache_ctx *cache) +{ + talloc_free(cache); +} + +NTSTATUS idmap_cache_build_sidkey(TALLOC_CTX *ctx, char **sidkey, const struct id_map *id) +{ + *sidkey = talloc_asprintf(ctx, "IDMAP/SID/%s", sid_string_static(id->sid)); + if ( ! *sidkey) { + DEBUG(1, ("failed to build sidkey, OOM?\n")); + return NT_STATUS_NO_MEMORY; + } + + return NT_STATUS_OK; +} + +NTSTATUS idmap_cache_build_idkey(TALLOC_CTX *ctx, char **idkey, const struct id_map *id) +{ + *idkey = talloc_asprintf(ctx, "IDMAP/%s/%lu", + (id->xid.type==ID_TYPE_UID)?"UID":"GID", + (unsigned long)id->xid.id); + if ( ! *idkey) { + DEBUG(1, ("failed to build idkey, OOM?\n")); + return NT_STATUS_NO_MEMORY; + } + + return NT_STATUS_OK; +} + +NTSTATUS idmap_cache_set(struct idmap_cache_ctx *cache, const struct id_map *id) +{ + NTSTATUS ret; + time_t timeout = time(NULL) + lp_idmap_cache_time(); + TDB_DATA databuf; + char *sidkey; + char *idkey; + char *valstr; + + /* Don't cache lookups in the S-1-22-{1,2} domain */ + if ( (id->xid.type == ID_TYPE_UID) && + sid_check_is_in_unix_users(id->sid) ) + { + return NT_STATUS_OK; + } + if ( (id->xid.type == ID_TYPE_GID) && + sid_check_is_in_unix_groups(id->sid) ) + { + return NT_STATUS_OK; + } + + + ret = idmap_cache_build_sidkey(cache, &sidkey, id); + if (!NT_STATUS_IS_OK(ret)) return ret; + + /* use sidkey as the local memory ctx */ + ret = idmap_cache_build_idkey(sidkey, &idkey, id); + if (!NT_STATUS_IS_OK(ret)) { + goto done; + } + + /* save SID -> ID */ + + /* use sidkey as the local memory ctx */ + valstr = talloc_asprintf(sidkey, IDMAP_CACHE_DATA_FMT, (int)timeout, idkey); + if (!valstr) { + DEBUG(0, ("Out of memory!\n")); + ret = NT_STATUS_NO_MEMORY; + goto done; + } + + databuf = string_term_tdb_data(valstr); + DEBUG(10, ("Adding cache entry with key = %s; value = %s and timeout =" + " %s (%d seconds %s)\n", sidkey, valstr , ctime(&timeout), + (int)(timeout - time(NULL)), + timeout > time(NULL) ? "ahead" : "in the past")); + + if (tdb_store_bystring(cache->tdb, sidkey, databuf, TDB_REPLACE) != 0) { + DEBUG(3, ("Failed to store cache entry!\n")); + ret = NT_STATUS_UNSUCCESSFUL; + goto done; + } + + /* save ID -> SID */ + + /* use sidkey as the local memory ctx */ + valstr = talloc_asprintf(sidkey, IDMAP_CACHE_DATA_FMT, (int)timeout, sidkey); + if (!valstr) { + DEBUG(0, ("Out of memory!\n")); + ret = NT_STATUS_NO_MEMORY; + goto done; + } + + databuf = string_term_tdb_data(valstr); + DEBUG(10, ("Adding cache entry with key = %s; value = %s and timeout =" + " %s (%d seconds %s)\n", idkey, valstr, ctime(&timeout), + (int)(timeout - time(NULL)), + timeout > time(NULL) ? "ahead" : "in the past")); + + if (tdb_store_bystring(cache->tdb, idkey, databuf, TDB_REPLACE) != 0) { + DEBUG(3, ("Failed to store cache entry!\n")); + ret = NT_STATUS_UNSUCCESSFUL; + goto done; + } + + ret = NT_STATUS_OK; + +done: + talloc_free(sidkey); + return ret; +} + +NTSTATUS idmap_cache_set_negative_sid(struct idmap_cache_ctx *cache, const struct id_map *id) +{ + NTSTATUS ret; + time_t timeout = time(NULL) + lp_idmap_negative_cache_time(); + TDB_DATA databuf; + char *sidkey; + char *valstr; + + ret = idmap_cache_build_sidkey(cache, &sidkey, id); + if (!NT_STATUS_IS_OK(ret)) return ret; + + /* use sidkey as the local memory ctx */ + valstr = talloc_asprintf(sidkey, IDMAP_CACHE_DATA_FMT, (int)timeout, "IDMAP/NEGATIVE"); + if (!valstr) { + DEBUG(0, ("Out of memory!\n")); + ret = NT_STATUS_NO_MEMORY; + goto done; + } + + databuf = string_term_tdb_data(valstr); + DEBUG(10, ("Adding cache entry with key = %s; value = %s and timeout =" + " %s (%d seconds %s)\n", sidkey, valstr, ctime(&timeout), + (int)(timeout - time(NULL)), + timeout > time(NULL) ? "ahead" : "in the past")); + + if (tdb_store_bystring(cache->tdb, sidkey, databuf, TDB_REPLACE) != 0) { + DEBUG(3, ("Failed to store cache entry!\n")); + ret = NT_STATUS_UNSUCCESSFUL; + goto done; + } + +done: + talloc_free(sidkey); + return ret; +} + +NTSTATUS idmap_cache_set_negative_id(struct idmap_cache_ctx *cache, const struct id_map *id) +{ + NTSTATUS ret; + time_t timeout = time(NULL) + lp_idmap_negative_cache_time(); + TDB_DATA databuf; + char *idkey; + char *valstr; + + ret = idmap_cache_build_idkey(cache, &idkey, id); + if (!NT_STATUS_IS_OK(ret)) return ret; + + /* use idkey as the local memory ctx */ + valstr = talloc_asprintf(idkey, IDMAP_CACHE_DATA_FMT, (int)timeout, "IDMAP/NEGATIVE"); + if (!valstr) { + DEBUG(0, ("Out of memory!\n")); + ret = NT_STATUS_NO_MEMORY; + goto done; + } + + databuf = string_term_tdb_data(valstr); + DEBUG(10, ("Adding cache entry with key = %s; value = %s and timeout =" + " %s (%d seconds %s)\n", idkey, valstr, ctime(&timeout), + (int)(timeout - time(NULL)), + timeout > time(NULL) ? "ahead" : "in the past")); + + if (tdb_store_bystring(cache->tdb, idkey, databuf, TDB_REPLACE) != 0) { + DEBUG(3, ("Failed to store cache entry!\n")); + ret = NT_STATUS_UNSUCCESSFUL; + goto done; + } + +done: + talloc_free(idkey); + return ret; +} + +NTSTATUS idmap_cache_fill_map(struct id_map *id, const char *value) +{ + char *rem; + + /* see if it is a sid */ + if ( ! strncmp("IDMAP/SID/", value, 10)) { + + if ( ! string_to_sid(id->sid, &value[10])) { + goto failed; + } + + id->status = ID_MAPPED; + + return NT_STATUS_OK; + } + + /* not a SID see if it is an UID or a GID */ + if ( ! strncmp("IDMAP/UID/", value, 10)) { + + /* a uid */ + id->xid.type = ID_TYPE_UID; + + } else if ( ! strncmp("IDMAP/GID/", value, 10)) { + + /* a gid */ + id->xid.type = ID_TYPE_GID; + + } else { + + /* a completely bogus value bail out */ + goto failed; + } + + id->xid.id = strtol(&value[10], &rem, 0); + if (*rem != '\0') { + goto failed; + } + + id->status = ID_MAPPED; + + return NT_STATUS_OK; + +failed: + DEBUG(1, ("invalid value: %s\n", value)); + id->status = ID_UNKNOWN; + return NT_STATUS_INTERNAL_DB_CORRUPTION; +} + +BOOL idmap_cache_is_negative(const char *val) +{ + if ( ! strcmp("IDMAP/NEGATIVE", val)) { + return True; + } + return False; +} + +/* search the cahce for the SID an return a mapping if found * + * + * 4 cases are possible + * + * 1 map found + * in this case id->status = ID_MAPPED and NT_STATUS_OK is returned + * 2 map not found + * in this case id->status = ID_UNKNOWN and NT_STATUS_NONE_MAPPED is returned + * 3 negative cache found + * in this case id->status = ID_UNMAPPED and NT_STATUS_OK is returned + * 4 map found but timer expired + * in this case id->status = ID_EXPIRED and NT_STATUS_SYNCHRONIZATION_REQUIRED + * is returned. In this case revalidation of the cache is needed. + */ + +NTSTATUS idmap_cache_map_sid(struct idmap_cache_ctx *cache, struct id_map *id) +{ + NTSTATUS ret; + TDB_DATA databuf; + time_t t; + char *sidkey; + char *endptr; + struct winbindd_domain *our_domain = find_our_domain(); + time_t now = time(NULL); + + /* make sure it is marked as not mapped by default */ + id->status = ID_UNKNOWN; + + ret = idmap_cache_build_sidkey(cache, &sidkey, id); + if (!NT_STATUS_IS_OK(ret)) return ret; + + databuf = tdb_fetch_bystring(cache->tdb, sidkey); + + if (databuf.dptr == NULL) { + DEBUG(10, ("Cache entry with key = %s couldn't be found\n", sidkey)); + ret = NT_STATUS_NONE_MAPPED; + goto done; + } + + t = strtol((const char *)databuf.dptr, &endptr, 10); + + if ((endptr == NULL) || (*endptr != '/')) { + DEBUG(2, ("Invalid gencache data format: %s\n", (const char *)databuf.dptr)); + /* remove the entry */ + tdb_delete_bystring(cache->tdb, sidkey); + ret = NT_STATUS_NONE_MAPPED; + goto done; + } + + /* check it is not negative */ + if (strcmp("IDMAP/NEGATIVE", endptr+1) != 0) { + + DEBUG(10, ("Returning %s cache entry: key = %s, value = %s, " + "timeout = %s", t > now ? "valid" : + "expired", sidkey, endptr+1, ctime(&t))); + + /* this call if successful will also mark the entry as mapped */ + ret = idmap_cache_fill_map(id, endptr+1); + if ( ! NT_STATUS_IS_OK(ret)) { + /* if not valid form delete the entry */ + tdb_delete_bystring(cache->tdb, sidkey); + ret = NT_STATUS_NONE_MAPPED; + goto done; + } + + /* here ret == NT_STATUS_OK and id->status = ID_MAPPED */ + + if (t <= now) { + /* If we've been told to be offline - stay in + that state... */ + if ( IS_DOMAIN_OFFLINE(our_domain) ) { + DEBUG(10,("idmap_cache_map_sid: idmap is offline\n")); + goto done; + } + + /* We're expired, set an error code + for upper layer */ + ret = NT_STATUS_SYNCHRONIZATION_REQUIRED; + } + + goto done; + } + + /* Was a negative cache hit */ + + /* Ignore the negative cache when offline */ + + if ( IS_DOMAIN_OFFLINE(our_domain) ) { + DEBUG(10,("idmap_cache_map_sid: idmap is offline\n")); + goto done; + } + + + /* Check for valid or expired cache hits */ + if (t <= now) { + /* We're expired. Return not mapped */ + ret = NT_STATUS_NONE_MAPPED; + } else { + /* this is not mapped as it was a negative cache hit */ + id->status = ID_UNMAPPED; + ret = NT_STATUS_OK; + } + +done: + SAFE_FREE(databuf.dptr); + talloc_free(sidkey); + return ret; +} + +/* search the cahce for the ID an return a mapping if found * + * + * 4 cases are possible + * + * 1 map found + * in this case id->status = ID_MAPPED and NT_STATUS_OK is returned + * 2 map not found + * in this case id->status = ID_UNKNOWN and NT_STATUS_NONE_MAPPED is returned + * 3 negative cache found + * in this case id->status = ID_UNMAPPED and NT_STATUS_OK is returned + * 4 map found but timer expired + * in this case id->status = ID_EXPIRED and NT_STATUS_SYNCHRONIZATION_REQUIRED + * is returned. In this case revalidation of the cache is needed. + */ + +NTSTATUS idmap_cache_map_id(struct idmap_cache_ctx *cache, struct id_map *id) +{ + NTSTATUS ret; + TDB_DATA databuf; + time_t t; + char *idkey; + char *endptr; + struct winbindd_domain *our_domain = find_our_domain(); + time_t now = time(NULL); + + /* make sure it is marked as unknown by default */ + id->status = ID_UNKNOWN; + + ret = idmap_cache_build_idkey(cache, &idkey, id); + if (!NT_STATUS_IS_OK(ret)) return ret; + + databuf = tdb_fetch_bystring(cache->tdb, idkey); + + if (databuf.dptr == NULL) { + DEBUG(10, ("Cache entry with key = %s couldn't be found\n", idkey)); + ret = NT_STATUS_NONE_MAPPED; + goto done; + } + + t = strtol((const char *)databuf.dptr, &endptr, 10); + + if ((endptr == NULL) || (*endptr != '/')) { + DEBUG(2, ("Invalid gencache data format: %s\n", (const char *)databuf.dptr)); + /* remove the entry */ + tdb_delete_bystring(cache->tdb, idkey); + ret = NT_STATUS_NONE_MAPPED; + goto done; + } + + /* check it is not negative */ + if (strcmp("IDMAP/NEGATIVE", endptr+1) != 0) { + + DEBUG(10, ("Returning %s cache entry: key = %s, value = %s, " + "timeout = %s", t > now ? "valid" : + "expired", idkey, endptr+1, ctime(&t))); + + /* this call if successful will also mark the entry as mapped */ + ret = idmap_cache_fill_map(id, endptr+1); + if ( ! NT_STATUS_IS_OK(ret)) { + /* if not valid form delete the entry */ + tdb_delete_bystring(cache->tdb, idkey); + ret = NT_STATUS_NONE_MAPPED; + goto done; + } + + /* here ret == NT_STATUS_OK and id->mapped = ID_MAPPED */ + + if (t <= now) { + /* If we've been told to be offline - stay in + that state... */ + if ( IS_DOMAIN_OFFLINE(our_domain) ) { + DEBUG(10,("idmap_cache_map_sid: idmap is offline\n")); + goto done; + } + + /* We're expired, set an error code + for upper layer */ + ret = NT_STATUS_SYNCHRONIZATION_REQUIRED; + } + + goto done; + } + + /* Was a negative cache hit */ + + /* Ignore the negative cache when offline */ + + if ( IS_DOMAIN_OFFLINE(our_domain) ) { + DEBUG(10,("idmap_cache_map_sid: idmap is offline\n")); + ret = NT_STATUS_NONE_MAPPED; + + goto done; + } + + /* Process the negative cache hit */ + + if (t <= now) { + /* We're expired. Return not mapped */ + ret = NT_STATUS_NONE_MAPPED; + } else { + /* this is not mapped is it was a negative cache hit */ + id->status = ID_UNMAPPED; + ret = NT_STATUS_OK; + } + +done: + SAFE_FREE(databuf.dptr); + talloc_free(idkey); + return ret; +} + diff --git a/source3/winbindd/idmap_ldap.c b/source3/winbindd/idmap_ldap.c new file mode 100644 index 0000000000..3b63915b05 --- /dev/null +++ b/source3/winbindd/idmap_ldap.c @@ -0,0 +1,1505 @@ +/* + Unix SMB/CIFS implementation. + + idmap LDAP backend + + Copyright (C) Tim Potter 2000 + Copyright (C) Jim McDonough 2003 + Copyright (C) Gerald Carter 2003 + Copyright (C) Simo Sorce 2003-2007 + + 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 3 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, see . +*/ + +#include "includes.h" +#include "winbindd.h" + +#undef DBGC_CLASS +#define DBGC_CLASS DBGC_IDMAP + +#include +#include + +#include "smbldap.h" + +struct idmap_ldap_context { + struct smbldap_state *smbldap_state; + char *url; + char *suffix; + char *user_dn; + uint32_t filter_low_id, filter_high_id; /* Filter range */ + BOOL anon; +}; + +struct idmap_ldap_alloc_context { + struct smbldap_state *smbldap_state; + char *url; + char *suffix; + char *user_dn; + uid_t low_uid, high_uid; /* Range of uids */ + gid_t low_gid, high_gid; /* Range of gids */ + +}; + +#define CHECK_ALLOC_DONE(mem) do { \ + if (!mem) { \ + DEBUG(0, ("Out of memory!\n")); \ + ret = NT_STATUS_NO_MEMORY; \ + goto done; \ + } } while (0) + +/********************************************************************** + IDMAP ALLOC TDB BACKEND +**********************************************************************/ + +static struct idmap_ldap_alloc_context *idmap_alloc_ldap; + +/********************************************************************* + ********************************************************************/ + +static NTSTATUS get_credentials( TALLOC_CTX *mem_ctx, + struct smbldap_state *ldap_state, + const char *config_option, + struct idmap_domain *dom, + char **dn ) +{ + NTSTATUS ret = NT_STATUS_UNSUCCESSFUL; + char *secret = NULL; + const char *tmp = NULL; + char *user_dn = NULL; + BOOL anon = False; + + /* assume anonymous if we don't have a specified user */ + + tmp = lp_parm_const_string(-1, config_option, "ldap_user_dn", NULL); + + if ( tmp ) { + if (!dom) { + /* only the alloc backend can pass in a NULL dom */ + secret = idmap_fetch_secret("ldap", True, + NULL, tmp); + } else { + secret = idmap_fetch_secret("ldap", False, + dom->name, tmp); + } + + if (!secret) { + DEBUG(0, ("get_credentials: Unable to fetch " + "auth credentials for %s in %s\n", + tmp, (dom==NULL)?"ALLOC":dom->name)); + ret = NT_STATUS_ACCESS_DENIED; + goto done; + } + *dn = talloc_strdup(mem_ctx, tmp); + CHECK_ALLOC_DONE(*dn); + } else { + if (!fetch_ldap_pw(&user_dn, &secret)) { + DEBUG(2, ("get_credentials: Failed to lookup ldap " + "bind creds. Using anonymous connection.\n")); + anon = True; + } else { + *dn = talloc_strdup(mem_ctx, user_dn); + SAFE_FREE( user_dn ); + CHECK_ALLOC_DONE(*dn); + } + } + + smbldap_set_creds(ldap_state, anon, *dn, secret); + ret = NT_STATUS_OK; + +done: + SAFE_FREE(secret); + + return ret; +} + + +/********************************************************************** + Verify the sambaUnixIdPool entry in the directory. +**********************************************************************/ + +static NTSTATUS verify_idpool(void) +{ + NTSTATUS ret; + TALLOC_CTX *ctx; + LDAPMessage *result = NULL; + LDAPMod **mods = NULL; + const char **attr_list; + char *filter; + int count; + int rc; + + if ( ! idmap_alloc_ldap) { + return NT_STATUS_UNSUCCESSFUL; + } + + ctx = talloc_new(idmap_alloc_ldap); + if ( ! ctx) { + DEBUG(0, ("Out of memory!\n")); + return NT_STATUS_NO_MEMORY; + } + + filter = talloc_asprintf(ctx, "(objectclass=%s)", LDAP_OBJ_IDPOOL); + CHECK_ALLOC_DONE(filter); + + attr_list = get_attr_list(ctx, idpool_attr_list); + CHECK_ALLOC_DONE(attr_list); + + rc = smbldap_search(idmap_alloc_ldap->smbldap_state, + idmap_alloc_ldap->suffix, + LDAP_SCOPE_SUBTREE, + filter, + attr_list, + 0, + &result); + + if (rc != LDAP_SUCCESS) { + DEBUG(1, ("Unable to verify the idpool, " + "cannot continue initialization!\n")); + return NT_STATUS_UNSUCCESSFUL; + } + + count = ldap_count_entries(idmap_alloc_ldap->smbldap_state->ldap_struct, + result); + + ldap_msgfree(result); + + if ( count > 1 ) { + DEBUG(0,("Multiple entries returned from %s (base == %s)\n", + filter, idmap_alloc_ldap->suffix)); + ret = NT_STATUS_UNSUCCESSFUL; + goto done; + } + else if (count == 0) { + char *uid_str, *gid_str; + + uid_str = talloc_asprintf(ctx, "%lu", + (unsigned long)idmap_alloc_ldap->low_uid); + gid_str = talloc_asprintf(ctx, "%lu", + (unsigned long)idmap_alloc_ldap->low_gid); + + smbldap_set_mod(&mods, LDAP_MOD_ADD, + "objectClass", LDAP_OBJ_IDPOOL); + smbldap_set_mod(&mods, LDAP_MOD_ADD, + get_attr_key2string(idpool_attr_list, + LDAP_ATTR_UIDNUMBER), + uid_str); + smbldap_set_mod(&mods, LDAP_MOD_ADD, + get_attr_key2string(idpool_attr_list, + LDAP_ATTR_GIDNUMBER), + gid_str); + if (mods) { + rc = smbldap_modify(idmap_alloc_ldap->smbldap_state, + idmap_alloc_ldap->suffix, + mods); + ldap_mods_free(mods, True); + } else { + ret = NT_STATUS_UNSUCCESSFUL; + goto done; + } + } + + ret = (rc == LDAP_SUCCESS)?NT_STATUS_OK:NT_STATUS_UNSUCCESSFUL; +done: + talloc_free(ctx); + return ret; +} + +/***************************************************************************** + Initialise idmap database. +*****************************************************************************/ + +static NTSTATUS idmap_ldap_alloc_init(const char *params) +{ + NTSTATUS ret = NT_STATUS_UNSUCCESSFUL; + const char *range; + const char *tmp; + uid_t low_uid = 0; + uid_t high_uid = 0; + gid_t low_gid = 0; + gid_t high_gid = 0; + + /* Only do init if we are online */ + if (idmap_is_offline()) { + return NT_STATUS_FILE_IS_OFFLINE; + } + + idmap_alloc_ldap = TALLOC_ZERO_P(NULL, struct idmap_ldap_alloc_context); + CHECK_ALLOC_DONE( idmap_alloc_ldap ); + + /* load ranges */ + + idmap_alloc_ldap->low_uid = 0; + idmap_alloc_ldap->high_uid = 0; + idmap_alloc_ldap->low_gid = 0; + idmap_alloc_ldap->high_gid = 0; + + range = lp_parm_const_string(-1, "idmap alloc config", "range", NULL); + if (range && range[0]) { + unsigned low_id, high_id; + + if (sscanf(range, "%u - %u", &low_id, &high_id) == 2) { + if (low_id < high_id) { + idmap_alloc_ldap->low_gid = low_id; + idmap_alloc_ldap->low_uid = low_id; + idmap_alloc_ldap->high_gid = high_id; + idmap_alloc_ldap->high_uid = high_id; + } else { + DEBUG(1, ("ERROR: invalid idmap alloc range " + "[%s]", range)); + } + } else { + DEBUG(1, ("ERROR: invalid syntax for idmap alloc " + "config:range [%s]", range)); + } + } + + if (lp_idmap_uid(&low_uid, &high_uid)) { + idmap_alloc_ldap->low_uid = low_uid; + idmap_alloc_ldap->high_uid = high_uid; + } + + if (lp_idmap_gid(&low_gid, &high_gid)) { + idmap_alloc_ldap->low_gid = low_gid; + idmap_alloc_ldap->high_gid= high_gid; + } + + if (idmap_alloc_ldap->high_uid <= idmap_alloc_ldap->low_uid) { + DEBUG(1, ("idmap uid range missing or invalid\n")); + DEBUGADD(1, ("idmap will be unable to map foreign SIDs\n")); + ret = NT_STATUS_UNSUCCESSFUL; + goto done; + } + + if (idmap_alloc_ldap->high_gid <= idmap_alloc_ldap->low_gid) { + DEBUG(1, ("idmap gid range missing or invalid\n")); + DEBUGADD(1, ("idmap will be unable to map foreign SIDs\n")); + ret = NT_STATUS_UNSUCCESSFUL; + goto done; + } + + if (params && *params) { + /* assume location is the only parameter */ + idmap_alloc_ldap->url = talloc_strdup(idmap_alloc_ldap, params); + } else { + tmp = lp_parm_const_string(-1, "idmap alloc config", + "ldap_url", NULL); + + if ( ! tmp) { + DEBUG(1, ("ERROR: missing idmap ldap url\n")); + ret = NT_STATUS_UNSUCCESSFUL; + goto done; + } + + idmap_alloc_ldap->url = talloc_strdup(idmap_alloc_ldap, tmp); + } + CHECK_ALLOC_DONE( idmap_alloc_ldap->url ); + + tmp = lp_parm_const_string(-1, "idmap alloc config", + "ldap_base_dn", NULL); + if ( ! tmp || ! *tmp) { + tmp = lp_ldap_idmap_suffix(); + if ( ! tmp) { + DEBUG(1, ("ERROR: missing idmap ldap suffix\n")); + ret = NT_STATUS_UNSUCCESSFUL; + goto done; + } + } + + idmap_alloc_ldap->suffix = talloc_strdup(idmap_alloc_ldap, tmp); + CHECK_ALLOC_DONE( idmap_alloc_ldap->suffix ); + + ret = smbldap_init(idmap_alloc_ldap, winbind_event_context(), + idmap_alloc_ldap->url, + &idmap_alloc_ldap->smbldap_state); + if (!NT_STATUS_IS_OK(ret)) { + DEBUG(1, ("ERROR: smbldap_init (%s) failed!\n", + idmap_alloc_ldap->url)); + goto done; + } + + ret = get_credentials( idmap_alloc_ldap, + idmap_alloc_ldap->smbldap_state, + "idmap alloc config", NULL, + &idmap_alloc_ldap->user_dn ); + if ( !NT_STATUS_IS_OK(ret) ) { + DEBUG(1,("idmap_ldap_alloc_init: Failed to get connection " + "credentials (%s)\n", nt_errstr(ret))); + goto done; + } + + /* see if the idmap suffix and sub entries exists */ + + ret = verify_idpool(); + + done: + if ( !NT_STATUS_IS_OK( ret ) ) + TALLOC_FREE( idmap_alloc_ldap ); + + return ret; +} + +/******************************** + Allocate a new uid or gid +********************************/ + +static NTSTATUS idmap_ldap_allocate_id(struct unixid *xid) +{ + TALLOC_CTX *ctx; + NTSTATUS ret = NT_STATUS_UNSUCCESSFUL; + int rc = LDAP_SERVER_DOWN; + int count = 0; + LDAPMessage *result = NULL; + LDAPMessage *entry = NULL; + LDAPMod **mods = NULL; + char *id_str; + char *new_id_str; + char *filter = NULL; + const char *dn = NULL; + const char **attr_list; + const char *type; + + /* Only do query if we are online */ + if (idmap_is_offline()) { + return NT_STATUS_FILE_IS_OFFLINE; + } + + if ( ! idmap_alloc_ldap) { + return NT_STATUS_UNSUCCESSFUL; + } + + ctx = talloc_new(idmap_alloc_ldap); + if ( ! ctx) { + DEBUG(0, ("Out of memory!\n")); + return NT_STATUS_NO_MEMORY; + } + + /* get type */ + switch (xid->type) { + + case ID_TYPE_UID: + type = get_attr_key2string(idpool_attr_list, + LDAP_ATTR_UIDNUMBER); + break; + + case ID_TYPE_GID: + type = get_attr_key2string(idpool_attr_list, + LDAP_ATTR_GIDNUMBER); + break; + + default: + DEBUG(2, ("Invalid ID type (0x%x)\n", xid->type)); + return NT_STATUS_INVALID_PARAMETER; + } + + filter = talloc_asprintf(ctx, "(objectClass=%s)", LDAP_OBJ_IDPOOL); + CHECK_ALLOC_DONE(filter); + + attr_list = get_attr_list(ctx, idpool_attr_list); + CHECK_ALLOC_DONE(attr_list); + + DEBUG(10, ("Search of the id pool (filter: %s)\n", filter)); + + rc = smbldap_search(idmap_alloc_ldap->smbldap_state, + idmap_alloc_ldap->suffix, + LDAP_SCOPE_SUBTREE, filter, + attr_list, 0, &result); + + if (rc != LDAP_SUCCESS) { + DEBUG(0,("%s object not found\n", LDAP_OBJ_IDPOOL)); + goto done; + } + + talloc_autofree_ldapmsg(ctx, result); + + count = ldap_count_entries(idmap_alloc_ldap->smbldap_state->ldap_struct, + result); + if (count != 1) { + DEBUG(0,("Single %s object not found\n", LDAP_OBJ_IDPOOL)); + goto done; + } + + entry = ldap_first_entry(idmap_alloc_ldap->smbldap_state->ldap_struct, + result); + + dn = smbldap_talloc_dn(ctx, + idmap_alloc_ldap->smbldap_state->ldap_struct, + entry); + if ( ! dn) { + goto done; + } + + if ( ! (id_str = smbldap_talloc_single_attribute(idmap_alloc_ldap->smbldap_state->ldap_struct, + entry, type, ctx))) { + DEBUG(0,("%s attribute not found\n", type)); + goto done; + } + if ( ! id_str) { + DEBUG(0,("Out of memory\n")); + ret = NT_STATUS_NO_MEMORY; + goto done; + } + + xid->id = strtoul(id_str, NULL, 10); + + /* make sure we still have room to grow */ + + switch (xid->type) { + case ID_TYPE_UID: + if (xid->id > idmap_alloc_ldap->high_uid) { + DEBUG(0,("Cannot allocate uid above %lu!\n", + (unsigned long)idmap_alloc_ldap->high_uid)); + goto done; + } + break; + + case ID_TYPE_GID: + if (xid->id > idmap_alloc_ldap->high_gid) { + DEBUG(0,("Cannot allocate gid above %lu!\n", + (unsigned long)idmap_alloc_ldap->high_uid)); + goto done; + } + break; + + default: + /* impossible */ + goto done; + } + + new_id_str = talloc_asprintf(ctx, "%lu", (unsigned long)xid->id + 1); + if ( ! new_id_str) { + DEBUG(0,("Out of memory\n")); + ret = NT_STATUS_NO_MEMORY; + goto done; + } + + smbldap_set_mod(&mods, LDAP_MOD_DELETE, type, id_str); + smbldap_set_mod(&mods, LDAP_MOD_ADD, type, new_id_str); + + if (mods == NULL) { + DEBUG(0,("smbldap_set_mod() failed.\n")); + goto done; + } + + DEBUG(10, ("Try to atomically increment the id (%s -> %s)\n", + id_str, new_id_str)); + + rc = smbldap_modify(idmap_alloc_ldap->smbldap_state, dn, mods); + + ldap_mods_free(mods, True); + + if (rc != LDAP_SUCCESS) { + DEBUG(1,("Failed to allocate new %s. " + "smbldap_modify() failed.\n", type)); + goto done; + } + + ret = NT_STATUS_OK; + +done: + talloc_free(ctx); + return ret; +} + +/********************************** + Get current highest id. +**********************************/ + +static NTSTATUS idmap_ldap_get_hwm(struct unixid *xid) +{ + TALLOC_CTX *memctx; + NTSTATUS ret = NT_STATUS_UNSUCCESSFUL; + int rc = LDAP_SERVER_DOWN; + int count = 0; + LDAPMessage *result = NULL; + LDAPMessage *entry = NULL; + char *id_str; + char *filter = NULL; + const char **attr_list; + const char *type; + + /* Only do query if we are online */ + if (idmap_is_offline()) { + return NT_STATUS_FILE_IS_OFFLINE; + } + + if ( ! idmap_alloc_ldap) { + return NT_STATUS_UNSUCCESSFUL; + } + + memctx = talloc_new(idmap_alloc_ldap); + if ( ! memctx) { + DEBUG(0, ("Out of memory!\n")); + return NT_STATUS_NO_MEMORY; + } + + /* get type */ + switch (xid->type) { + + case ID_TYPE_UID: + type = get_attr_key2string(idpool_attr_list, + LDAP_ATTR_UIDNUMBER); + break; + + case ID_TYPE_GID: + type = get_attr_key2string(idpool_attr_list, + LDAP_ATTR_GIDNUMBER); + break; + + default: + DEBUG(2, ("Invalid ID type (0x%x)\n", xid->type)); + return NT_STATUS_INVALID_PARAMETER; + } + + filter = talloc_asprintf(memctx, "(objectClass=%s)", LDAP_OBJ_IDPOOL); + CHECK_ALLOC_DONE(filter); + + attr_list = get_attr_list(memctx, idpool_attr_list); + CHECK_ALLOC_DONE(attr_list); + + rc = smbldap_search(idmap_alloc_ldap->smbldap_state, + idmap_alloc_ldap->suffix, + LDAP_SCOPE_SUBTREE, filter, + attr_list, 0, &result); + + if (rc != LDAP_SUCCESS) { + DEBUG(0,("%s object not found\n", LDAP_OBJ_IDPOOL)); + goto done; + } + + talloc_autofree_ldapmsg(memctx, result); + + count = ldap_count_entries(idmap_alloc_ldap->smbldap_state->ldap_struct, + result); + if (count != 1) { + DEBUG(0,("Single %s object not found\n", LDAP_OBJ_IDPOOL)); + goto done; + } + + entry = ldap_first_entry(idmap_alloc_ldap->smbldap_state->ldap_struct, + result); + + id_str = smbldap_talloc_single_attribute(idmap_alloc_ldap->smbldap_state->ldap_struct, + entry, type, memctx); + if ( ! id_str) { + DEBUG(0,("%s attribute not found\n", type)); + goto done; + } + if ( ! id_str) { + DEBUG(0,("Out of memory\n")); + ret = NT_STATUS_NO_MEMORY; + goto done; + } + + xid->id = strtoul(id_str, NULL, 10); + + ret = NT_STATUS_OK; +done: + talloc_free(memctx); + return ret; +} +/********************************** + Set highest id. +**********************************/ + +static NTSTATUS idmap_ldap_set_hwm(struct unixid *xid) +{ + TALLOC_CTX *ctx; + NTSTATUS ret = NT_STATUS_UNSUCCESSFUL; + int rc = LDAP_SERVER_DOWN; + int count = 0; + LDAPMessage *result = NULL; + LDAPMessage *entry = NULL; + LDAPMod **mods = NULL; + char *new_id_str; + char *filter = NULL; + const char *dn = NULL; + const char **attr_list; + const char *type; + + /* Only do query if we are online */ + if (idmap_is_offline()) { + return NT_STATUS_FILE_IS_OFFLINE; + } + + if ( ! idmap_alloc_ldap) { + return NT_STATUS_UNSUCCESSFUL; + } + + ctx = talloc_new(idmap_alloc_ldap); + if ( ! ctx) { + DEBUG(0, ("Out of memory!\n")); + return NT_STATUS_NO_MEMORY; + } + + /* get type */ + switch (xid->type) { + + case ID_TYPE_UID: + type = get_attr_key2string(idpool_attr_list, + LDAP_ATTR_UIDNUMBER); + break; + + case ID_TYPE_GID: + type = get_attr_key2string(idpool_attr_list, + LDAP_ATTR_GIDNUMBER); + break; + + default: + DEBUG(2, ("Invalid ID type (0x%x)\n", xid->type)); + return NT_STATUS_INVALID_PARAMETER; + } + + filter = talloc_asprintf(ctx, "(objectClass=%s)", LDAP_OBJ_IDPOOL); + CHECK_ALLOC_DONE(filter); + + attr_list = get_attr_list(ctx, idpool_attr_list); + CHECK_ALLOC_DONE(attr_list); + + rc = smbldap_search(idmap_alloc_ldap->smbldap_state, + idmap_alloc_ldap->suffix, + LDAP_SCOPE_SUBTREE, filter, + attr_list, 0, &result); + + if (rc != LDAP_SUCCESS) { + DEBUG(0,("%s object not found\n", LDAP_OBJ_IDPOOL)); + goto done; + } + + talloc_autofree_ldapmsg(ctx, result); + + count = ldap_count_entries(idmap_alloc_ldap->smbldap_state->ldap_struct, + result); + if (count != 1) { + DEBUG(0,("Single %s object not found\n", LDAP_OBJ_IDPOOL)); + goto done; + } + + entry = ldap_first_entry(idmap_alloc_ldap->smbldap_state->ldap_struct, + result); + + dn = smbldap_talloc_dn(ctx, + idmap_alloc_ldap->smbldap_state->ldap_struct, + entry); + if ( ! dn) { + goto done; + } + + new_id_str = talloc_asprintf(ctx, "%lu", (unsigned long)xid->id); + if ( ! new_id_str) { + DEBUG(0,("Out of memory\n")); + ret = NT_STATUS_NO_MEMORY; + goto done; + } + + smbldap_set_mod(&mods, LDAP_MOD_REPLACE, type, new_id_str); + + if (mods == NULL) { + DEBUG(0,("smbldap_set_mod() failed.\n")); + goto done; + } + + rc = smbldap_modify(idmap_alloc_ldap->smbldap_state, dn, mods); + + ldap_mods_free(mods, True); + + if (rc != LDAP_SUCCESS) { + DEBUG(1,("Failed to allocate new %s. " + "smbldap_modify() failed.\n", type)); + goto done; + } + + ret = NT_STATUS_OK; + +done: + talloc_free(ctx); + return ret; +} + +/********************************** + Close idmap ldap alloc +**********************************/ + +static NTSTATUS idmap_ldap_alloc_close(void) +{ + if (idmap_alloc_ldap) { + smbldap_free_struct(&idmap_alloc_ldap->smbldap_state); + DEBUG(5,("The connection to the LDAP server was closed\n")); + /* maybe free the results here --metze */ + TALLOC_FREE(idmap_alloc_ldap); + } + return NT_STATUS_OK; +} + + +/********************************************************************** + IDMAP MAPPING LDAP BACKEND +**********************************************************************/ + +static int idmap_ldap_close_destructor(struct idmap_ldap_context *ctx) +{ + smbldap_free_struct(&ctx->smbldap_state); + DEBUG(5,("The connection to the LDAP server was closed\n")); + /* maybe free the results here --metze */ + + return 0; +} + +/******************************** + Initialise idmap database. +********************************/ + +static NTSTATUS idmap_ldap_db_init(struct idmap_domain *dom) +{ + NTSTATUS ret; + struct idmap_ldap_context *ctx = NULL; + char *config_option = NULL; + const char *range = NULL; + const char *tmp = NULL; + + /* Only do init if we are online */ + if (idmap_is_offline()) { + return NT_STATUS_FILE_IS_OFFLINE; + } + + ctx = TALLOC_ZERO_P(dom, struct idmap_ldap_context); + if ( ! ctx) { + DEBUG(0, ("Out of memory!\n")); + return NT_STATUS_NO_MEMORY; + } + + config_option = talloc_asprintf(ctx, "idmap config %s", dom->name); + if ( ! config_option) { + DEBUG(0, ("Out of memory!\n")); + ret = NT_STATUS_NO_MEMORY; + goto done; + } + + /* load ranges */ + range = lp_parm_const_string(-1, config_option, "range", NULL); + if (range && range[0]) { + if ((sscanf(range, "%u - %u", &ctx->filter_low_id, + &ctx->filter_high_id) != 2) || + (ctx->filter_low_id > ctx->filter_high_id)) { + DEBUG(1, ("ERROR: invalid filter range [%s]", range)); + ctx->filter_low_id = 0; + ctx->filter_high_id = 0; + } + } + + if (dom->params && *(dom->params)) { + /* assume location is the only parameter */ + ctx->url = talloc_strdup(ctx, dom->params); + } else { + tmp = lp_parm_const_string(-1, config_option, "ldap_url", NULL); + + if ( ! tmp) { + DEBUG(1, ("ERROR: missing idmap ldap url\n")); + ret = NT_STATUS_UNSUCCESSFUL; + goto done; + } + + ctx->url = talloc_strdup(ctx, tmp); + } + CHECK_ALLOC_DONE(ctx->url); + + tmp = lp_parm_const_string(-1, config_option, "ldap_base_dn", NULL); + if ( ! tmp || ! *tmp) { + tmp = lp_ldap_idmap_suffix(); + if ( ! tmp) { + DEBUG(1, ("ERROR: missing idmap ldap suffix\n")); + ret = NT_STATUS_UNSUCCESSFUL; + goto done; + } + } + + ctx->suffix = talloc_strdup(ctx, tmp); + CHECK_ALLOC_DONE(ctx->suffix); + + ret = smbldap_init(ctx, winbind_event_context(), ctx->url, + &ctx->smbldap_state); + if (!NT_STATUS_IS_OK(ret)) { + DEBUG(1, ("ERROR: smbldap_init (%s) failed!\n", ctx->url)); + goto done; + } + + ret = get_credentials( ctx, ctx->smbldap_state, config_option, + dom, &ctx->user_dn ); + if ( !NT_STATUS_IS_OK(ret) ) { + DEBUG(1,("idmap_ldap_db_init: Failed to get connection " + "credentials (%s)\n", nt_errstr(ret))); + goto done; + } + + /* set the destructor on the context, so that resource are properly + freed if the contexts is released */ + + talloc_set_destructor(ctx, idmap_ldap_close_destructor); + + dom->private_data = ctx; + dom->initialized = True; + + talloc_free(config_option); + return NT_STATUS_OK; + +/*failed */ +done: + talloc_free(ctx); + return ret; +} + +/* max number of ids requested per batch query */ +#define IDMAP_LDAP_MAX_IDS 30 + +/********************************** + lookup a set of unix ids. +**********************************/ + +/* this function searches up to IDMAP_LDAP_MAX_IDS entries + * in maps for a match */ +static struct id_map *find_map_by_id(struct id_map **maps, + enum id_type type, + uint32_t id) +{ + int i; + + for (i = 0; i < IDMAP_LDAP_MAX_IDS; i++) { + if (maps[i] == NULL) { /* end of the run */ + return NULL; + } + if ((maps[i]->xid.type == type) && (maps[i]->xid.id == id)) { + return maps[i]; + } + } + + return NULL; +} + +static NTSTATUS idmap_ldap_unixids_to_sids(struct idmap_domain *dom, + struct id_map **ids) +{ + NTSTATUS ret; + TALLOC_CTX *memctx; + struct idmap_ldap_context *ctx; + LDAPMessage *result = NULL; + const char *uidNumber; + const char *gidNumber; + const char **attr_list; + char *filter = NULL; + BOOL multi = False; + int idx = 0; + int bidx = 0; + int count; + int rc; + int i; + + /* Only do query if we are online */ + if (idmap_is_offline()) { + return NT_STATUS_FILE_IS_OFFLINE; + } + + /* Initilization my have been deferred because we were offline */ + if ( ! dom->initialized) { + ret = idmap_ldap_db_init(dom); + if ( ! NT_STATUS_IS_OK(ret)) { + return ret; + } + } + + ctx = talloc_get_type(dom->private_data, struct idmap_ldap_context); + + memctx = talloc_new(ctx); + if ( ! memctx) { + DEBUG(0, ("Out of memory!\n")); + return NT_STATUS_NO_MEMORY; + } + + uidNumber = get_attr_key2string(idpool_attr_list, LDAP_ATTR_UIDNUMBER); + gidNumber = get_attr_key2string(idpool_attr_list, LDAP_ATTR_GIDNUMBER); + + attr_list = get_attr_list(memctx, sidmap_attr_list); + + if ( ! ids[1]) { + /* if we are requested just one mapping use the simple filter */ + + filter = talloc_asprintf(memctx, "(&(objectClass=%s)(%s=%lu))", + LDAP_OBJ_IDMAP_ENTRY, + (ids[0]->xid.type==ID_TYPE_UID)?uidNumber:gidNumber, + (unsigned long)ids[0]->xid.id); + CHECK_ALLOC_DONE(filter); + DEBUG(10, ("Filter: [%s]\n", filter)); + } else { + /* multiple mappings */ + multi = True; + } + +again: + if (multi) { + + talloc_free(filter); + filter = talloc_asprintf(memctx, + "(&(objectClass=%s)(|", + LDAP_OBJ_IDMAP_ENTRY); + CHECK_ALLOC_DONE(filter); + + bidx = idx; + for (i = 0; (i < IDMAP_LDAP_MAX_IDS) && ids[idx]; i++, idx++) { + filter = talloc_asprintf_append(filter, "(%s=%lu)", + (ids[idx]->xid.type==ID_TYPE_UID)?uidNumber:gidNumber, + (unsigned long)ids[idx]->xid.id); + CHECK_ALLOC_DONE(filter); + } + filter = talloc_asprintf_append(filter, "))"); + CHECK_ALLOC_DONE(filter); + DEBUG(10, ("Filter: [%s]\n", filter)); + } else { + bidx = 0; + idx = 1; + } + + rc = smbldap_search(ctx->smbldap_state, ctx->suffix, LDAP_SCOPE_SUBTREE, + filter, attr_list, 0, &result); + + if (rc != LDAP_SUCCESS) { + DEBUG(3,("Failure looking up ids (%s)\n", ldap_err2string(rc))); + ret = NT_STATUS_UNSUCCESSFUL; + goto done; + } + + count = ldap_count_entries(ctx->smbldap_state->ldap_struct, result); + + if (count == 0) { + DEBUG(10, ("NO SIDs found\n")); + } + + for (i = 0; i < count; i++) { + LDAPMessage *entry = NULL; + char *sidstr = NULL; + char *tmp = NULL; + enum id_type type; + struct id_map *map; + uint32_t id; + + if (i == 0) { /* first entry */ + entry = ldap_first_entry(ctx->smbldap_state->ldap_struct, + result); + } else { /* following ones */ + entry = ldap_next_entry(ctx->smbldap_state->ldap_struct, + entry); + } + if ( ! entry) { + DEBUG(2, ("ERROR: Unable to fetch ldap entries " + "from results\n")); + break; + } + + /* first check if the SID is present */ + sidstr = smbldap_talloc_single_attribute( + ctx->smbldap_state->ldap_struct, + entry, LDAP_ATTRIBUTE_SID, memctx); + if ( ! sidstr) { /* no sid, skip entry */ + DEBUG(2, ("WARNING SID not found on entry\n")); + continue; + } + + /* now try to see if it is a uid, if not try with a gid + * (gid is more common, but in case both uidNumber and + * gidNumber are returned the SID is mapped to the uid + *not the gid) */ + type = ID_TYPE_UID; + tmp = smbldap_talloc_single_attribute( + ctx->smbldap_state->ldap_struct, + entry, uidNumber, memctx); + if ( ! tmp) { + type = ID_TYPE_GID; + tmp = smbldap_talloc_single_attribute( + ctx->smbldap_state->ldap_struct, + entry, gidNumber, memctx); + } + if ( ! tmp) { /* wow very strange entry, how did it match ? */ + DEBUG(5, ("Unprobable match on (%s), no uidNumber, " + "nor gidNumber returned\n", sidstr)); + TALLOC_FREE(sidstr); + continue; + } + + id = strtoul(tmp, NULL, 10); + if ((id == 0) || + (ctx->filter_low_id && (id < ctx->filter_low_id)) || + (ctx->filter_high_id && (id > ctx->filter_high_id))) { + DEBUG(5, ("Requested id (%u) out of range (%u - %u). " + "Filtered!\n", id, + ctx->filter_low_id, ctx->filter_high_id)); + TALLOC_FREE(sidstr); + TALLOC_FREE(tmp); + continue; + } + TALLOC_FREE(tmp); + + map = find_map_by_id(&ids[bidx], type, id); + if (!map) { + DEBUG(2, ("WARNING: couldn't match sid (%s) " + "with requested ids\n", sidstr)); + TALLOC_FREE(sidstr); + continue; + } + + if ( ! string_to_sid(map->sid, sidstr)) { + DEBUG(2, ("ERROR: Invalid SID on entry\n")); + TALLOC_FREE(sidstr); + continue; + } + TALLOC_FREE(sidstr); + + /* mapped */ + map->status = ID_MAPPED; + + DEBUG(10, ("Mapped %s -> %lu (%d)\n", + sid_string_static(map->sid), + (unsigned long)map->xid.id, map->xid.type)); + } + + /* free the ldap results */ + if (result) { + ldap_msgfree(result); + result = NULL; + } + + if (multi && ids[idx]) { /* still some values to map */ + goto again; + } + + ret = NT_STATUS_OK; + + /* mark all unknwon/expired ones as unmapped */ + for (i = 0; ids[i]; i++) { + if (ids[i]->status != ID_MAPPED) + ids[i]->status = ID_UNMAPPED; + } + +done: + talloc_free(memctx); + return ret; +} + +/********************************** + lookup a set of sids. +**********************************/ + +/* this function searches up to IDMAP_LDAP_MAX_IDS entries + * in maps for a match */ +static struct id_map *find_map_by_sid(struct id_map **maps, DOM_SID *sid) +{ + int i; + + for (i = 0; i < IDMAP_LDAP_MAX_IDS; i++) { + if (maps[i] == NULL) { /* end of the run */ + return NULL; + } + if (sid_equal(maps[i]->sid, sid)) { + return maps[i]; + } + } + + return NULL; +} + +static NTSTATUS idmap_ldap_sids_to_unixids(struct idmap_domain *dom, + struct id_map **ids) +{ + LDAPMessage *entry = NULL; + NTSTATUS ret; + TALLOC_CTX *memctx; + struct idmap_ldap_context *ctx; + LDAPMessage *result = NULL; + const char *uidNumber; + const char *gidNumber; + const char **attr_list; + char *filter = NULL; + BOOL multi = False; + int idx = 0; + int bidx = 0; + int count; + int rc; + int i; + + /* Only do query if we are online */ + if (idmap_is_offline()) { + return NT_STATUS_FILE_IS_OFFLINE; + } + + /* Initilization my have been deferred because we were offline */ + if ( ! dom->initialized) { + ret = idmap_ldap_db_init(dom); + if ( ! NT_STATUS_IS_OK(ret)) { + return ret; + } + } + + ctx = talloc_get_type(dom->private_data, struct idmap_ldap_context); + + memctx = talloc_new(ctx); + if ( ! memctx) { + DEBUG(0, ("Out of memory!\n")); + return NT_STATUS_NO_MEMORY; + } + + uidNumber = get_attr_key2string(idpool_attr_list, LDAP_ATTR_UIDNUMBER); + gidNumber = get_attr_key2string(idpool_attr_list, LDAP_ATTR_GIDNUMBER); + + attr_list = get_attr_list(memctx, sidmap_attr_list); + + if ( ! ids[1]) { + /* if we are requested just one mapping use the simple filter */ + + filter = talloc_asprintf(memctx, "(&(objectClass=%s)(%s=%s))", + LDAP_OBJ_IDMAP_ENTRY, + LDAP_ATTRIBUTE_SID, + sid_string_static(ids[0]->sid)); + CHECK_ALLOC_DONE(filter); + DEBUG(10, ("Filter: [%s]\n", filter)); + } else { + /* multiple mappings */ + multi = True; + } + +again: + if (multi) { + + TALLOC_FREE(filter); + filter = talloc_asprintf(memctx, + "(&(objectClass=%s)(|", + LDAP_OBJ_IDMAP_ENTRY); + CHECK_ALLOC_DONE(filter); + + bidx = idx; + for (i = 0; (i < IDMAP_LDAP_MAX_IDS) && ids[idx]; i++, idx++) { + filter = talloc_asprintf_append(filter, "(%s=%s)", + LDAP_ATTRIBUTE_SID, + sid_string_static(ids[idx]->sid)); + CHECK_ALLOC_DONE(filter); + } + filter = talloc_asprintf_append(filter, "))"); + CHECK_ALLOC_DONE(filter); + DEBUG(10, ("Filter: [%s]", filter)); + } else { + bidx = 0; + idx = 1; + } + + rc = smbldap_search(ctx->smbldap_state, ctx->suffix, LDAP_SCOPE_SUBTREE, + filter, attr_list, 0, &result); + + if (rc != LDAP_SUCCESS) { + DEBUG(3,("Failure looking up sids (%s)\n", + ldap_err2string(rc))); + ret = NT_STATUS_UNSUCCESSFUL; + goto done; + } + + count = ldap_count_entries(ctx->smbldap_state->ldap_struct, result); + + if (count == 0) { + DEBUG(10, ("NO SIDs found\n")); + } + + for (i = 0; i < count; i++) { + char *sidstr = NULL; + char *tmp = NULL; + enum id_type type; + struct id_map *map; + DOM_SID sid; + uint32_t id; + + if (i == 0) { /* first entry */ + entry = ldap_first_entry(ctx->smbldap_state->ldap_struct, + result); + } else { /* following ones */ + entry = ldap_next_entry(ctx->smbldap_state->ldap_struct, + entry); + } + if ( ! entry) { + DEBUG(2, ("ERROR: Unable to fetch ldap entries " + "from results\n")); + break; + } + + /* first check if the SID is present */ + sidstr = smbldap_talloc_single_attribute( + ctx->smbldap_state->ldap_struct, + entry, LDAP_ATTRIBUTE_SID, memctx); + if ( ! sidstr) { /* no sid ??, skip entry */ + DEBUG(2, ("WARNING SID not found on entry\n")); + continue; + } + + if ( ! string_to_sid(&sid, sidstr)) { + DEBUG(2, ("ERROR: Invalid SID on entry\n")); + TALLOC_FREE(sidstr); + continue; + } + + map = find_map_by_sid(&ids[bidx], &sid); + if (!map) { + DEBUG(2, ("WARNING: couldn't find entry sid (%s) " + "in ids", sidstr)); + TALLOC_FREE(sidstr); + continue; + } + + TALLOC_FREE(sidstr); + + /* now try to see if it is a uid, if not try with a gid + * (gid is more common, but in case both uidNumber and + * gidNumber are returned the SID is mapped to the uid + * not the gid) */ + type = ID_TYPE_UID; + tmp = smbldap_talloc_single_attribute( + ctx->smbldap_state->ldap_struct, + entry, uidNumber, memctx); + if ( ! tmp) { + type = ID_TYPE_GID; + tmp = smbldap_talloc_single_attribute( + ctx->smbldap_state->ldap_struct, + entry, gidNumber, memctx); + } + if ( ! tmp) { /* no ids ?? */ + DEBUG(5, ("no uidNumber, " + "nor gidNumber attributes found\n")); + continue; + } + + id = strtoul(tmp, NULL, 10); + if ((id == 0) || + (ctx->filter_low_id && (id < ctx->filter_low_id)) || + (ctx->filter_high_id && (id > ctx->filter_high_id))) { + DEBUG(5, ("Requested id (%u) out of range (%u - %u). " + "Filtered!\n", id, + ctx->filter_low_id, ctx->filter_high_id)); + TALLOC_FREE(tmp); + continue; + } + TALLOC_FREE(tmp); + + /* mapped */ + map->xid.type = type; + map->xid.id = id; + map->status = ID_MAPPED; + + DEBUG(10, ("Mapped %s -> %lu (%d)\n", + sid_string_static(map->sid), + (unsigned long)map->xid.id, map->xid.type)); + } + + /* free the ldap results */ + if (result) { + ldap_msgfree(result); + result = NULL; + } + + if (multi && ids[idx]) { /* still some values to map */ + goto again; + } + + ret = NT_STATUS_OK; + + /* mark all unknwon/expired ones as unmapped */ + for (i = 0; ids[i]; i++) { + if (ids[i]->status != ID_MAPPED) + ids[i]->status = ID_UNMAPPED; + } + +done: + talloc_free(memctx); + return ret; +} + +/********************************** + set a mapping. +**********************************/ + +/* TODO: change this: This function cannot be called to modify a mapping, + * only set a new one */ + +static NTSTATUS idmap_ldap_set_mapping(struct idmap_domain *dom, + const struct id_map *map) +{ + NTSTATUS ret; + TALLOC_CTX *memctx; + struct idmap_ldap_context *ctx; + LDAPMessage *entry = NULL; + LDAPMod **mods = NULL; + const char *type; + char *id_str; + char *sid; + char *dn; + int rc = -1; + + /* Only do query if we are online */ + if (idmap_is_offline()) { + return NT_STATUS_FILE_IS_OFFLINE; + } + + /* Initilization my have been deferred because we were offline */ + if ( ! dom->initialized) { + ret = idmap_ldap_db_init(dom); + if ( ! NT_STATUS_IS_OK(ret)) { + return ret; + } + } + + ctx = talloc_get_type(dom->private_data, struct idmap_ldap_context); + + switch(map->xid.type) { + case ID_TYPE_UID: + type = get_attr_key2string(sidmap_attr_list, + LDAP_ATTR_UIDNUMBER); + break; + + case ID_TYPE_GID: + type = get_attr_key2string(sidmap_attr_list, + LDAP_ATTR_GIDNUMBER); + break; + + default: + return NT_STATUS_INVALID_PARAMETER; + } + + memctx = talloc_new(ctx); + if ( ! memctx) { + DEBUG(0, ("Out of memory!\n")); + return NT_STATUS_NO_MEMORY; + } + + id_str = talloc_asprintf(memctx, "%lu", (unsigned long)map->xid.id); + CHECK_ALLOC_DONE(id_str); + + sid = talloc_strdup(memctx, sid_string_static(map->sid)); + CHECK_ALLOC_DONE(sid); + + dn = talloc_asprintf(memctx, "%s=%s,%s", + get_attr_key2string(sidmap_attr_list, LDAP_ATTR_SID), + sid, + ctx->suffix); + CHECK_ALLOC_DONE(dn); + + smbldap_set_mod(&mods, LDAP_MOD_ADD, + "objectClass", LDAP_OBJ_IDMAP_ENTRY); + + smbldap_make_mod(ctx->smbldap_state->ldap_struct, + entry, &mods, type, id_str); + + smbldap_make_mod(ctx->smbldap_state->ldap_struct, entry, &mods, + get_attr_key2string(sidmap_attr_list, LDAP_ATTR_SID), + sid); + + if ( ! mods) { + DEBUG(2, ("ERROR: No mods?\n")); + ret = NT_STATUS_UNSUCCESSFUL; + goto done; + } + + /* TODO: remove conflicting mappings! */ + + smbldap_set_mod(&mods, LDAP_MOD_ADD, "objectClass", LDAP_OBJ_SID_ENTRY); + + DEBUG(10, ("Set DN %s (%s -> %s)\n", dn, sid, id_str)); + + rc = smbldap_add(ctx->smbldap_state, dn, mods); + ldap_mods_free(mods, True); + + if (rc != LDAP_SUCCESS) { + char *ld_error = NULL; + ldap_get_option(ctx->smbldap_state->ldap_struct, + LDAP_OPT_ERROR_STRING, &ld_error); + DEBUG(0,("ldap_set_mapping_internals: Failed to add %s to %lu " + "mapping [%s]\n", sid, + (unsigned long)map->xid.id, type)); + DEBUG(0, ("ldap_set_mapping_internals: Error was: %s (%s)\n", + ld_error ? ld_error : "(NULL)", ldap_err2string (rc))); + if (ld_error) { + ldap_memfree(ld_error); + } + ret = NT_STATUS_UNSUCCESSFUL; + goto done; + } + + DEBUG(10,("ldap_set_mapping: Successfully created mapping from %s to " + "%lu [%s]\n", sid, (unsigned long)map->xid.id, type)); + + ret = NT_STATUS_OK; + +done: + talloc_free(memctx); + return ret; +} + +/********************************** + Close the idmap ldap instance +**********************************/ + +static NTSTATUS idmap_ldap_close(struct idmap_domain *dom) +{ + struct idmap_ldap_context *ctx; + + if (dom->private_data) { + ctx = talloc_get_type(dom->private_data, + struct idmap_ldap_context); + + talloc_free(ctx); + dom->private_data = NULL; + } + + return NT_STATUS_OK; +} + +static struct idmap_methods idmap_ldap_methods = { + + .init = idmap_ldap_db_init, + .unixids_to_sids = idmap_ldap_unixids_to_sids, + .sids_to_unixids = idmap_ldap_sids_to_unixids, + .set_mapping = idmap_ldap_set_mapping, + .close_fn = idmap_ldap_close +}; + +static struct idmap_alloc_methods idmap_ldap_alloc_methods = { + + .init = idmap_ldap_alloc_init, + .allocate_id = idmap_ldap_allocate_id, + .get_id_hwm = idmap_ldap_get_hwm, + .set_id_hwm = idmap_ldap_set_hwm, + .close_fn = idmap_ldap_alloc_close, + /* .dump_data = TODO */ +}; + +NTSTATUS idmap_alloc_ldap_init(void) +{ + return smb_register_idmap_alloc(SMB_IDMAP_INTERFACE_VERSION, "ldap", + &idmap_ldap_alloc_methods); +} + +NTSTATUS idmap_ldap_init(void) +{ + NTSTATUS ret; + + /* FIXME: bad hack to actually register also the alloc_ldap module + * without changining configure.in */ + ret = idmap_alloc_ldap_init(); + if (! NT_STATUS_IS_OK(ret)) { + return ret; + } + return smb_register_idmap(SMB_IDMAP_INTERFACE_VERSION, "ldap", + &idmap_ldap_methods); +} + diff --git a/source3/winbindd/idmap_nss.c b/source3/winbindd/idmap_nss.c new file mode 100644 index 0000000000..5bb2389c93 --- /dev/null +++ b/source3/winbindd/idmap_nss.c @@ -0,0 +1,223 @@ +/* + Unix SMB/CIFS implementation. + + idmap PASSDB backend + + Copyright (C) Simo Sorce 2006 + + 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 3 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, see . +*/ + +#include "includes.h" +#include "winbindd.h" + +#undef DBGC_CLASS +#define DBGC_CLASS DBGC_IDMAP + +/***************************** + Initialise idmap database. +*****************************/ + +static NTSTATUS idmap_nss_int_init(struct idmap_domain *dom) +{ + dom->initialized = True; + return NT_STATUS_OK; +} + +/********************************** + lookup a set of unix ids. +**********************************/ + +static NTSTATUS idmap_nss_unixids_to_sids(struct idmap_domain *dom, struct id_map **ids) +{ + TALLOC_CTX *ctx; + int i; + + if (! dom->initialized) { + return NT_STATUS_UNSUCCESSFUL; + } + + ctx = talloc_new(dom); + if ( ! ctx) { + DEBUG(0, ("Out of memory!\n")); + return NT_STATUS_NO_MEMORY; + } + + for (i = 0; ids[i]; i++) { + struct passwd *pw; + struct group *gr; + const char *name; + enum lsa_SidType type; + BOOL ret; + + switch (ids[i]->xid.type) { + case ID_TYPE_UID: + pw = getpwuid((uid_t)ids[i]->xid.id); + + if (!pw) { + ids[i]->status = ID_UNMAPPED; + continue; + } + name = pw->pw_name; + break; + case ID_TYPE_GID: + gr = getgrgid((gid_t)ids[i]->xid.id); + + if (!gr) { + ids[i]->status = ID_UNMAPPED; + continue; + } + name = gr->gr_name; + break; + default: /* ?? */ + ids[i]->status = ID_UNKNOWN; + continue; + } + + /* by default calls to winbindd are disabled + the following call will not recurse so this is safe */ + winbind_on(); + /* Lookup name from PDC using lsa_lookup_names() */ + ret = winbind_lookup_name(dom->name, name, ids[i]->sid, &type); + winbind_off(); + + if (!ret) { + /* TODO: how do we know if the name is really not mapped, + * or something just failed ? */ + ids[i]->status = ID_UNMAPPED; + continue; + } + + switch (type) { + case SID_NAME_USER: + if (ids[i]->xid.type == ID_TYPE_UID) { + ids[i]->status = ID_MAPPED; + } + break; + + case SID_NAME_DOM_GRP: + case SID_NAME_ALIAS: + case SID_NAME_WKN_GRP: + if (ids[i]->xid.type == ID_TYPE_GID) { + ids[i]->status = ID_MAPPED; + } + break; + + default: + ids[i]->status = ID_UNKNOWN; + break; + } + } + + + talloc_free(ctx); + return NT_STATUS_OK; +} + +/********************************** + lookup a set of sids. +**********************************/ + +static NTSTATUS idmap_nss_sids_to_unixids(struct idmap_domain *dom, struct id_map **ids) +{ + TALLOC_CTX *ctx; + int i; + + if (! dom->initialized) { + return NT_STATUS_UNSUCCESSFUL; + } + + ctx = talloc_new(dom); + if ( ! ctx) { + DEBUG(0, ("Out of memory!\n")); + return NT_STATUS_NO_MEMORY; + } + + for (i = 0; ids[i]; i++) { + struct passwd *pw; + struct group *gr; + enum lsa_SidType type; + const char *dom_name = NULL; + const char *name = NULL; + BOOL ret; + + /* by default calls to winbindd are disabled + the following call will not recurse so this is safe */ + winbind_on(); + ret = winbind_lookup_sid(ctx, ids[i]->sid, &dom_name, &name, &type); + winbind_off(); + + if (!ret) { + /* TODO: how do we know if the name is really not mapped, + * or something just failed ? */ + ids[i]->status = ID_UNMAPPED; + continue; + } + + switch (type) { + case SID_NAME_USER: + + /* this will find also all lower case name and use username level */ + + pw = Get_Pwnam(name); + if (pw) { + ids[i]->xid.id = pw->pw_uid; + ids[i]->xid.type = ID_TYPE_UID; + ids[i]->status = ID_MAPPED; + } + break; + + case SID_NAME_DOM_GRP: + case SID_NAME_ALIAS: + case SID_NAME_WKN_GRP: + + gr = getgrnam(name); + if (gr) { + ids[i]->xid.id = gr->gr_gid; + ids[i]->xid.type = ID_TYPE_GID; + ids[i]->status = ID_MAPPED; + } + break; + + default: + ids[i]->status = ID_UNKNOWN; + break; + } + } + + talloc_free(ctx); + return NT_STATUS_OK; +} + +/********************************** + Close the idmap tdb instance +**********************************/ + +static NTSTATUS idmap_nss_close(struct idmap_domain *dom) +{ + return NT_STATUS_OK; +} + +static struct idmap_methods nss_methods = { + + .init = idmap_nss_int_init, + .unixids_to_sids = idmap_nss_unixids_to_sids, + .sids_to_unixids = idmap_nss_sids_to_unixids, + .close_fn = idmap_nss_close +}; + +NTSTATUS idmap_nss_init(void) +{ + return smb_register_idmap(SMB_IDMAP_INTERFACE_VERSION, "nss", &nss_methods); +} diff --git a/source3/winbindd/idmap_passdb.c b/source3/winbindd/idmap_passdb.c new file mode 100644 index 0000000000..17afd71ab8 --- /dev/null +++ b/source3/winbindd/idmap_passdb.c @@ -0,0 +1,139 @@ +/* + Unix SMB/CIFS implementation. + + idmap PASSDB backend + + Copyright (C) Simo Sorce 2006 + + 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 3 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, see . +*/ + +#include "includes.h" + +#undef DBGC_CLASS +#define DBGC_CLASS DBGC_IDMAP + +/***************************** + Initialise idmap database. +*****************************/ + +static NTSTATUS idmap_pdb_init(struct idmap_domain *dom) +{ + dom->initialized = True; + return NT_STATUS_OK; +} + +/********************************** + lookup a set of unix ids. +**********************************/ + +static NTSTATUS idmap_pdb_unixids_to_sids(struct idmap_domain *dom, struct id_map **ids) +{ + int i; + + if (! dom->initialized) { + return NT_STATUS_UNSUCCESSFUL; + } + + for (i = 0; ids[i]; i++) { + + /* unmapped by default */ + ids[i]->status = ID_UNMAPPED; + + switch (ids[i]->xid.type) { + case ID_TYPE_UID: + if (pdb_uid_to_sid((uid_t)ids[i]->xid.id, ids[i]->sid)) { + ids[i]->status = ID_MAPPED; + } + break; + case ID_TYPE_GID: + if (pdb_gid_to_sid((gid_t)ids[i]->xid.id, ids[i]->sid)) { + ids[i]->status = ID_MAPPED; + } + break; + default: /* ?? */ + ids[i]->status = ID_UNKNOWN; + } + } + + return NT_STATUS_OK; +} + +/********************************** + lookup a set of sids. +**********************************/ + +static NTSTATUS idmap_pdb_sids_to_unixids(struct idmap_domain *dom, struct id_map **ids) +{ + int i; + + if (! dom->initialized) { + return NT_STATUS_UNSUCCESSFUL; + } + + for (i = 0; ids[i]; i++) { + enum lsa_SidType type; + union unid_t id; + + if (pdb_sid_to_id(ids[i]->sid, &id, &type)) { + switch (type) { + case SID_NAME_USER: + ids[i]->xid.id = id.uid; + ids[i]->xid.type = ID_TYPE_UID; + ids[i]->status = ID_MAPPED; + break; + + case SID_NAME_DOM_GRP: + case SID_NAME_ALIAS: + case SID_NAME_WKN_GRP: + ids[i]->xid.id = id.gid; + ids[i]->xid.type = ID_TYPE_GID; + ids[i]->status = ID_MAPPED; + break; + + default: /* ?? */ + /* make sure it is marked as unmapped */ + ids[i]->status = ID_UNKNOWN; + break; + } + } else { + /* Query Failed */ + ids[i]->status = ID_UNMAPPED; + } + } + + return NT_STATUS_OK; +} + +/********************************** + Close the idmap tdb instance +**********************************/ + +static NTSTATUS idmap_pdb_close(struct idmap_domain *dom) +{ + return NT_STATUS_OK; +} + +static struct idmap_methods passdb_methods = { + + .init = idmap_pdb_init, + .unixids_to_sids = idmap_pdb_unixids_to_sids, + .sids_to_unixids = idmap_pdb_sids_to_unixids, + .close_fn =idmap_pdb_close +}; + +NTSTATUS idmap_passdb_init(void) +{ + return smb_register_idmap(SMB_IDMAP_INTERFACE_VERSION, "passdb", &passdb_methods); +} diff --git a/source3/winbindd/idmap_rid.c b/source3/winbindd/idmap_rid.c new file mode 100644 index 0000000000..8e5f1302f7 --- /dev/null +++ b/source3/winbindd/idmap_rid.c @@ -0,0 +1,267 @@ +/* + * idmap_rid: static map between Active Directory/NT RIDs and RFC 2307 accounts + * Copyright (C) Guenther Deschner, 2004 + * Copyright (C) Sumit Bose, 2004 + * + * 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 3 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, see . + * + */ + +#include "includes.h" +#include "winbindd.h" + +#undef DBGC_CLASS +#define DBGC_CLASS DBGC_IDMAP + +struct idmap_rid_context { + const char *domain_name; + uint32_t low_id; + uint32_t high_id; + uint32_t base_rid; +}; + +/****************************************************************************** + compat params can't be used because of the completely different way + we support multiple domains in the new idmap + *****************************************************************************/ + +static NTSTATUS idmap_rid_initialize(struct idmap_domain *dom) +{ + NTSTATUS ret; + struct idmap_rid_context *ctx; + char *config_option = NULL; + const char *range; + uid_t low_uid = 0; + uid_t high_uid = 0; + gid_t low_gid = 0; + gid_t high_gid = 0; + + if ( (ctx = TALLOC_ZERO_P(dom, struct idmap_rid_context)) == NULL ) { + DEBUG(0, ("Out of memory!\n")); + return NT_STATUS_NO_MEMORY; + } + + config_option = talloc_asprintf(ctx, "idmap config %s", dom->name); + if ( ! config_option) { + DEBUG(0, ("Out of memory!\n")); + ret = NT_STATUS_NO_MEMORY; + goto failed; + } + + range = lp_parm_const_string(-1, config_option, "range", NULL); + if ( !range || + (sscanf(range, "%u - %u", &ctx->low_id, &ctx->high_id) != 2) || + (ctx->low_id > ctx->high_id)) + { + ctx->low_id = 0; + ctx->high_id = 0; + } + + /* lets see if the range is defined by the old idmap uid/idmap gid */ + if (!ctx->low_id && !ctx->high_id) { + if (lp_idmap_uid(&low_uid, &high_uid)) { + ctx->low_id = low_uid; + ctx->high_id = high_uid; + } + + if (lp_idmap_gid(&low_gid, &high_gid)) { + if ((ctx->low_id != low_gid) || + (ctx->high_id != high_uid)) { + DEBUG(1, ("ERROR: idmap uid range must match idmap gid range\n")); + ret = NT_STATUS_UNSUCCESSFUL; + goto failed; + } + } + } + + if (!ctx->low_id || !ctx->high_id) { + DEBUG(1, ("ERROR: Invalid configuration, ID range missing or invalid\n")); + ret = NT_STATUS_UNSUCCESSFUL; + goto failed; + } + + ctx->base_rid = lp_parm_int(-1, config_option, "base_rid", 0); + ctx->domain_name = talloc_strdup( ctx, dom->name ); + + dom->private_data = ctx; + dom->initialized = True; + + talloc_free(config_option); + return NT_STATUS_OK; + +failed: + talloc_free(ctx); + return ret; +} + +static NTSTATUS idmap_rid_id_to_sid(TALLOC_CTX *memctx, struct idmap_rid_context *ctx, struct id_map *map) +{ + struct winbindd_domain *domain; + + /* apply filters before checking */ + if ((map->xid.id < ctx->low_id) || (map->xid.id > ctx->high_id)) { + DEBUG(5, ("Requested id (%u) out of range (%u - %u). Filtered!\n", + map->xid.id, ctx->low_id, ctx->high_id)); + return NT_STATUS_NONE_MAPPED; + } + + if ( (domain = find_domain_from_name_noinit(ctx->domain_name)) == NULL ) { + return NT_STATUS_NO_SUCH_DOMAIN; + } + + sid_compose(map->sid, &domain->sid, map->xid.id - ctx->low_id + ctx->base_rid); + + /* We **really** should have some way of validating + the SID exists and is the correct type here. But + that is a deficiency in the idmap_rid design. */ + + map->status = ID_MAPPED; + + return NT_STATUS_OK; +} + +/********************************** + Single sid to id lookup function. +**********************************/ + +static NTSTATUS idmap_rid_sid_to_id(TALLOC_CTX *memctx, struct idmap_rid_context *ctx, struct id_map *map) +{ + uint32_t rid; + + sid_peek_rid(map->sid, &rid); + map->xid.id = rid - ctx->base_rid + ctx->low_id; + + /* apply filters before returning result */ + + if ((map->xid.id < ctx->low_id) || (map->xid.id > ctx->high_id)) { + DEBUG(5, ("Requested id (%u) out of range (%u - %u). Filtered!\n", + map->xid.id, ctx->low_id, ctx->high_id)); + map->status = ID_UNMAPPED; + return NT_STATUS_NONE_MAPPED; + } + + /* We **really** should have some way of validating + the SID exists and is the correct type here. But + that is a deficiency in the idmap_rid design. */ + + map->status = ID_MAPPED; + + return NT_STATUS_OK; +} + +/********************************** + lookup a set of unix ids. +**********************************/ + +static NTSTATUS idmap_rid_unixids_to_sids(struct idmap_domain *dom, struct id_map **ids) +{ + struct idmap_rid_context *ridctx; + TALLOC_CTX *ctx; + NTSTATUS ret; + int i; + + /* Initilization my have been deferred because of an error, retry or fail */ + if ( ! dom->initialized) { + ret = idmap_rid_initialize(dom); + if ( ! NT_STATUS_IS_OK(ret)) { + return ret; + } + } + + ridctx = talloc_get_type(dom->private_data, struct idmap_rid_context); + + ctx = talloc_new(dom); + if ( ! ctx) { + DEBUG(0, ("Out of memory!\n")); + return NT_STATUS_NO_MEMORY; + } + + for (i = 0; ids[i]; i++) { + + ret = idmap_rid_id_to_sid(ctx, ridctx, ids[i]); + + if (( ! NT_STATUS_IS_OK(ret)) && + ( ! NT_STATUS_EQUAL(ret, NT_STATUS_NONE_MAPPED))) { + /* some fatal error occurred, log it */ + DEBUG(3, ("Unexpected error resolving an ID (%d)\n", ids[i]->xid.id)); + } + } + + talloc_free(ctx); + return NT_STATUS_OK; +} + +/********************************** + lookup a set of sids. +**********************************/ + +static NTSTATUS idmap_rid_sids_to_unixids(struct idmap_domain *dom, struct id_map **ids) +{ + struct idmap_rid_context *ridctx; + TALLOC_CTX *ctx; + NTSTATUS ret; + int i; + + /* Initilization my have been deferred because of an error, retry or fail */ + if ( ! dom->initialized) { + ret = idmap_rid_initialize(dom); + if ( ! NT_STATUS_IS_OK(ret)) { + return ret; + } + } + + ridctx = talloc_get_type(dom->private_data, struct idmap_rid_context); + + ctx = talloc_new(dom); + if ( ! ctx) { + DEBUG(0, ("Out of memory!\n")); + return NT_STATUS_NO_MEMORY; + } + + for (i = 0; ids[i]; i++) { + + ret = idmap_rid_sid_to_id(ctx, ridctx, ids[i]); + + if (( ! NT_STATUS_IS_OK(ret)) && + ( ! NT_STATUS_EQUAL(ret, NT_STATUS_NONE_MAPPED))) { + /* some fatal error occurred, log it */ + DEBUG(3, ("Unexpected error resolving a SID (%s)\n", + sid_string_static(ids[i]->sid))); + } + } + + talloc_free(ctx); + return NT_STATUS_OK; +} + +static NTSTATUS idmap_rid_close(struct idmap_domain *dom) +{ + if (dom->private_data) { + TALLOC_FREE(dom->private_data); + } + return NT_STATUS_OK; +} + +static struct idmap_methods rid_methods = { + .init = idmap_rid_initialize, + .unixids_to_sids = idmap_rid_unixids_to_sids, + .sids_to_unixids = idmap_rid_sids_to_unixids, + .close_fn = idmap_rid_close +}; + +NTSTATUS idmap_rid_init(void) +{ + return smb_register_idmap(SMB_IDMAP_INTERFACE_VERSION, "rid", &rid_methods); +} + diff --git a/source3/winbindd/idmap_tdb.c b/source3/winbindd/idmap_tdb.c new file mode 100644 index 0000000000..97000689fa --- /dev/null +++ b/source3/winbindd/idmap_tdb.c @@ -0,0 +1,1232 @@ +/* + Unix SMB/CIFS implementation. + + idmap TDB backend + + Copyright (C) Tim Potter 2000 + Copyright (C) Jim McDonough 2003 + Copyright (C) Jeremy Allison 2006 + Copyright (C) Simo Sorce 2003-2006 + + 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 3 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, see . +*/ + +#include "includes.h" +#include "winbindd.h" + +#undef DBGC_CLASS +#define DBGC_CLASS DBGC_IDMAP + +/* High water mark keys */ +#define HWM_GROUP "GROUP HWM" +#define HWM_USER "USER HWM" + +static struct idmap_tdb_state { + + /* User and group id pool */ + uid_t low_uid, high_uid; /* Range of uids to allocate */ + gid_t low_gid, high_gid; /* Range of gids to allocate */ + +} idmap_tdb_state; + +/***************************************************************************** + For idmap conversion: convert one record to new format + Ancient versions (eg 2.2.3a) of winbindd_idmap.tdb mapped DOMAINNAME/rid + instead of the SID. +*****************************************************************************/ +static int convert_fn(TDB_CONTEXT *tdb, TDB_DATA key, TDB_DATA data, void *state) +{ + struct winbindd_domain *domain; + char *p; + DOM_SID sid; + uint32 rid; + fstring keystr; + fstring dom_name; + TDB_DATA key2; + BOOL *failed = (BOOL *)state; + + DEBUG(10,("Converting %s\n", (const char *)key.dptr)); + + p = strchr((const char *)key.dptr, '/'); + if (!p) + return 0; + + *p = 0; + fstrcpy(dom_name, (const char *)key.dptr); + *p++ = '/'; + + domain = find_domain_from_name(dom_name); + if (domain == NULL) { + /* We must delete the old record. */ + DEBUG(0,("Unable to find domain %s\n", dom_name )); + DEBUG(0,("deleting record %s\n", (const char *)key.dptr )); + + if (tdb_delete(tdb, key) != 0) { + DEBUG(0, ("Unable to delete record %s\n", (const char *)key.dptr)); + *failed = True; + return -1; + } + + return 0; + } + + rid = atoi(p); + + sid_copy(&sid, &domain->sid); + sid_append_rid(&sid, rid); + + sid_to_string(keystr, &sid); + key2 = string_term_tdb_data(keystr); + + if (tdb_store(tdb, key2, data, TDB_INSERT) != 0) { + DEBUG(0,("Unable to add record %s\n", (const char *)key2.dptr )); + *failed = True; + return -1; + } + + if (tdb_store(tdb, data, key2, TDB_REPLACE) != 0) { + DEBUG(0,("Unable to update record %s\n", (const char *)data.dptr )); + *failed = True; + return -1; + } + + if (tdb_delete(tdb, key) != 0) { + DEBUG(0,("Unable to delete record %s\n", (const char *)key.dptr )); + *failed = True; + return -1; + } + + return 0; +} + +/***************************************************************************** + Convert the idmap database from an older version. +*****************************************************************************/ + +static BOOL idmap_tdb_upgrade(const char *idmap_name) +{ + int32 vers; + BOOL bigendianheader; + BOOL failed = False; + TDB_CONTEXT *idmap_tdb; + + DEBUG(0, ("Upgrading winbindd_idmap.tdb from an old version\n")); + + if (!(idmap_tdb = tdb_open_log(idmap_name, 0, + TDB_DEFAULT, O_RDWR, + 0600))) { + DEBUG(0, ("Unable to open idmap database\n")); + return False; + } + + bigendianheader = (tdb_get_flags(idmap_tdb) & TDB_BIGENDIAN) ? True : False; + + vers = tdb_fetch_int32(idmap_tdb, "IDMAP_VERSION"); + + if (((vers == -1) && bigendianheader) || (IREV(vers) == IDMAP_VERSION)) { + /* Arrggghh ! Bytereversed or old big-endian - make order independent ! */ + /* + * high and low records were created on a + * big endian machine and will need byte-reversing. + */ + + int32 wm; + + wm = tdb_fetch_int32(idmap_tdb, HWM_USER); + + if (wm != -1) { + wm = IREV(wm); + } else { + wm = idmap_tdb_state.low_uid; + } + + if (tdb_store_int32(idmap_tdb, HWM_USER, wm) == -1) { + DEBUG(0, ("Unable to byteswap user hwm in idmap database\n")); + tdb_close(idmap_tdb); + return False; + } + + wm = tdb_fetch_int32(idmap_tdb, HWM_GROUP); + if (wm != -1) { + wm = IREV(wm); + } else { + wm = idmap_tdb_state.low_gid; + } + + if (tdb_store_int32(idmap_tdb, HWM_GROUP, wm) == -1) { + DEBUG(0, ("Unable to byteswap group hwm in idmap database\n")); + tdb_close(idmap_tdb); + return False; + } + } + + /* the old format stored as DOMAIN/rid - now we store the SID direct */ + tdb_traverse(idmap_tdb, convert_fn, &failed); + + if (failed) { + DEBUG(0, ("Problem during conversion\n")); + tdb_close(idmap_tdb); + return False; + } + + if (tdb_store_int32(idmap_tdb, "IDMAP_VERSION", IDMAP_VERSION) == -1) { + DEBUG(0, ("Unable to dtore idmap version in databse\n")); + tdb_close(idmap_tdb); + return False; + } + + tdb_close(idmap_tdb); + return True; +} + +/* WARNING: We can't open a tdb twice inthe same process, for that reason + * I'm going to use a hack with open ref counts to open the winbindd_idmap.tdb + * only once. We will later decide whether to split the db in multiple files + * or come up with a better solution to share them. */ + +static TDB_CONTEXT *idmap_tdb_common_ctx; +static int idmap_tdb_open_ref_count = 0; + +static NTSTATUS idmap_tdb_open_db(TALLOC_CTX *memctx, TDB_CONTEXT **tdbctx) +{ + NTSTATUS ret; + TALLOC_CTX *ctx; + SMB_STRUCT_STAT stbuf; + char *tdbfile = NULL; + int32 version; + BOOL tdb_is_new = False; + + if (idmap_tdb_open_ref_count) { /* the tdb has already been opened */ + idmap_tdb_open_ref_count++; + *tdbctx = idmap_tdb_common_ctx; + return NT_STATUS_OK; + } + + /* use our own context here */ + ctx = talloc_new(memctx); + if (!ctx) { + DEBUG(0, ("Out of memory!\n")); + return NT_STATUS_NO_MEMORY; + } + + /* use the old database if present */ + tdbfile = talloc_strdup(ctx, lock_path("winbindd_idmap.tdb")); + if (!tdbfile) { + DEBUG(0, ("Out of memory!\n")); + ret = NT_STATUS_NO_MEMORY; + goto done; + } + + if (!file_exist(tdbfile, &stbuf)) { + tdb_is_new = True; + } + + DEBUG(10,("Opening tdbfile %s\n", tdbfile )); + + /* Open idmap repository */ + if (!(idmap_tdb_common_ctx = tdb_open_log(tdbfile, 0, TDB_DEFAULT, O_RDWR | O_CREAT, 0644))) { + DEBUG(0, ("Unable to open idmap database\n")); + ret = NT_STATUS_UNSUCCESSFUL; + goto done; + } + + if (tdb_is_new) { + /* the file didn't existed before opening it, let's + * store idmap version as nobody else yet opened and + * stored it. I do not like this method but didn't + * found a way to understand if an opened tdb have + * been just created or not --- SSS */ + tdb_store_int32(idmap_tdb_common_ctx, "IDMAP_VERSION", IDMAP_VERSION); + } + + /* check against earlier versions */ + version = tdb_fetch_int32(idmap_tdb_common_ctx, "IDMAP_VERSION"); + if (version != IDMAP_VERSION) { + + /* backup_tdb expects the tdb not to be open */ + tdb_close(idmap_tdb_common_ctx); + + if ( ! idmap_tdb_upgrade(tdbfile)) { + + DEBUG(0, ("Unable to open idmap database, it's in an old formati, and upgrade failed!\n")); + ret = NT_STATUS_INTERNAL_DB_ERROR; + goto done; + } + + /* Re-Open idmap repository */ + if (!(idmap_tdb_common_ctx = tdb_open_log(tdbfile, 0, TDB_DEFAULT, O_RDWR | O_CREAT, 0644))) { + DEBUG(0, ("Unable to open idmap database\n")); + ret = NT_STATUS_UNSUCCESSFUL; + goto done; + } + } + + *tdbctx = idmap_tdb_common_ctx; + idmap_tdb_open_ref_count++; + ret = NT_STATUS_OK; + +done: + talloc_free(ctx); + return ret; +} + + /* NEVER use tdb_close() except for the conversion routines that are guaranteed + * to run only when the database is opened the first time, always use this function. */ + +BOOL idmap_tdb_tdb_close(TDB_CONTEXT *tdbctx) +{ + if (tdbctx != idmap_tdb_common_ctx) { + DEBUG(0, ("ERROR: Invalid tdb context!")); + return False; + } + + idmap_tdb_open_ref_count--; + if (idmap_tdb_open_ref_count) { + return True; + } + + return tdb_close(idmap_tdb_common_ctx); +} + +/********************************************************************** + IDMAP ALLOC TDB BACKEND +**********************************************************************/ + +static TDB_CONTEXT *idmap_alloc_tdb; + +/********************************** + Initialise idmap alloc database. +**********************************/ + +static NTSTATUS idmap_tdb_alloc_init( const char *params ) +{ + NTSTATUS ret; + TALLOC_CTX *ctx; + const char *range; + uid_t low_uid = 0; + uid_t high_uid = 0; + gid_t low_gid = 0; + gid_t high_gid = 0; + + /* use our own context here */ + ctx = talloc_new(NULL); + if (!ctx) { + DEBUG(0, ("Out of memory!\n")); + return NT_STATUS_NO_MEMORY; + } + + ret = idmap_tdb_open_db(ctx, &idmap_alloc_tdb); + if ( ! NT_STATUS_IS_OK(ret)) { + talloc_free(ctx); + return ret; + } + + talloc_free(ctx); + + /* load ranges */ + idmap_tdb_state.low_uid = 0; + idmap_tdb_state.high_uid = 0; + idmap_tdb_state.low_gid = 0; + idmap_tdb_state.high_gid = 0; + + range = lp_parm_const_string(-1, "idmap alloc config", "range", NULL); + if (range && range[0]) { + unsigned low_id, high_id; + + if (sscanf(range, "%u - %u", &low_id, &high_id) == 2) { + if (low_id < high_id) { + idmap_tdb_state.low_gid = idmap_tdb_state.low_uid = low_id; + idmap_tdb_state.high_gid = idmap_tdb_state.high_uid = high_id; + } else { + DEBUG(1, ("ERROR: invalid idmap alloc range [%s]", range)); + } + } else { + DEBUG(1, ("ERROR: invalid syntax for idmap alloc config:range [%s]", range)); + } + } + + /* Create high water marks for group and user id */ + if (lp_idmap_uid(&low_uid, &high_uid)) { + idmap_tdb_state.low_uid = low_uid; + idmap_tdb_state.high_uid = high_uid; + } + + if (lp_idmap_gid(&low_gid, &high_gid)) { + idmap_tdb_state.low_gid = low_gid; + idmap_tdb_state.high_gid = high_gid; + } + + if (idmap_tdb_state.high_uid <= idmap_tdb_state.low_uid) { + DEBUG(1, ("idmap uid range missing or invalid\n")); + DEBUGADD(1, ("idmap will be unable to map foreign SIDs\n")); + return NT_STATUS_UNSUCCESSFUL; + } else { + uint32 low_id; + + if (((low_id = tdb_fetch_int32(idmap_alloc_tdb, HWM_USER)) == -1) || + (low_id < idmap_tdb_state.low_uid)) { + if (tdb_store_int32(idmap_alloc_tdb, HWM_USER, idmap_tdb_state.low_uid) == -1) { + DEBUG(0, ("Unable to initialise user hwm in idmap database\n")); + return NT_STATUS_INTERNAL_DB_ERROR; + } + } + } + + if (idmap_tdb_state.high_gid <= idmap_tdb_state.low_gid) { + DEBUG(1, ("idmap gid range missing or invalid\n")); + DEBUGADD(1, ("idmap will be unable to map foreign SIDs\n")); + return NT_STATUS_UNSUCCESSFUL; + } else { + uint32 low_id; + + if (((low_id = tdb_fetch_int32(idmap_alloc_tdb, HWM_GROUP)) == -1) || + (low_id < idmap_tdb_state.low_gid)) { + if (tdb_store_int32(idmap_alloc_tdb, HWM_GROUP, idmap_tdb_state.low_gid) == -1) { + DEBUG(0, ("Unable to initialise group hwm in idmap database\n")); + return NT_STATUS_INTERNAL_DB_ERROR; + } + } + } + + return NT_STATUS_OK; +} + +/********************************** + Allocate a new id. +**********************************/ + +static NTSTATUS idmap_tdb_allocate_id(struct unixid *xid) +{ + BOOL ret; + const char *hwmkey; + const char *hwmtype; + uint32_t high_hwm; + uint32_t hwm; + + /* Get current high water mark */ + switch (xid->type) { + + case ID_TYPE_UID: + hwmkey = HWM_USER; + hwmtype = "UID"; + high_hwm = idmap_tdb_state.high_uid; + break; + + case ID_TYPE_GID: + hwmkey = HWM_GROUP; + hwmtype = "GID"; + high_hwm = idmap_tdb_state.high_gid; + break; + + default: + DEBUG(2, ("Invalid ID type (0x%x)\n", xid->type)); + return NT_STATUS_INVALID_PARAMETER; + } + + if ((hwm = tdb_fetch_int32(idmap_alloc_tdb, hwmkey)) == -1) { + return NT_STATUS_INTERNAL_DB_ERROR; + } + + /* check it is in the range */ + if (hwm > high_hwm) { + DEBUG(1, ("Fatal Error: %s range full!! (max: %lu)\n", + hwmtype, (unsigned long)high_hwm)); + return NT_STATUS_UNSUCCESSFUL; + } + + /* fetch a new id and increment it */ + ret = tdb_change_uint32_atomic(idmap_alloc_tdb, hwmkey, &hwm, 1); + if (!ret) { + DEBUG(1, ("Fatal error while fetching a new %s value\n!", hwmtype)); + return NT_STATUS_UNSUCCESSFUL; + } + + /* recheck it is in the range */ + if (hwm > high_hwm) { + DEBUG(1, ("Fatal Error: %s range full!! (max: %lu)\n", + hwmtype, (unsigned long)high_hwm)); + return NT_STATUS_UNSUCCESSFUL; + } + + xid->id = hwm; + DEBUG(10,("New %s = %d\n", hwmtype, hwm)); + + return NT_STATUS_OK; +} + +/********************************** + Get current highest id. +**********************************/ + +static NTSTATUS idmap_tdb_get_hwm(struct unixid *xid) +{ + const char *hwmkey; + const char *hwmtype; + uint32_t hwm; + uint32_t high_hwm; + + /* Get current high water mark */ + switch (xid->type) { + + case ID_TYPE_UID: + hwmkey = HWM_USER; + hwmtype = "UID"; + high_hwm = idmap_tdb_state.high_uid; + break; + + case ID_TYPE_GID: + hwmkey = HWM_GROUP; + hwmtype = "GID"; + high_hwm = idmap_tdb_state.high_gid; + break; + + default: + return NT_STATUS_INVALID_PARAMETER; + } + + if ((hwm = tdb_fetch_int32(idmap_alloc_tdb, hwmkey)) == -1) { + return NT_STATUS_INTERNAL_DB_ERROR; + } + + xid->id = hwm; + + /* Warn if it is out of range */ + if (hwm >= high_hwm) { + DEBUG(0, ("Warning: %s range full!! (max: %lu)\n", + hwmtype, (unsigned long)high_hwm)); + } + + return NT_STATUS_OK; +} + +/********************************** + Set high id. +**********************************/ + +static NTSTATUS idmap_tdb_set_hwm(struct unixid *xid) +{ + const char *hwmkey; + const char *hwmtype; + uint32_t hwm; + uint32_t high_hwm; + + /* Get current high water mark */ + switch (xid->type) { + + case ID_TYPE_UID: + hwmkey = HWM_USER; + hwmtype = "UID"; + high_hwm = idmap_tdb_state.high_uid; + break; + + case ID_TYPE_GID: + hwmkey = HWM_GROUP; + hwmtype = "GID"; + high_hwm = idmap_tdb_state.high_gid; + break; + + default: + return NT_STATUS_INVALID_PARAMETER; + } + + hwm = xid->id; + + if ((hwm = tdb_store_int32(idmap_alloc_tdb, hwmkey, hwm)) == -1) { + return NT_STATUS_INTERNAL_DB_ERROR; + } + + /* Warn if it is out of range */ + if (hwm >= high_hwm) { + DEBUG(0, ("Warning: %s range full!! (max: %lu)\n", + hwmtype, (unsigned long)high_hwm)); + } + + return NT_STATUS_OK; +} + +/********************************** + Close the alloc tdb +**********************************/ + +static NTSTATUS idmap_tdb_alloc_close(void) +{ + if (idmap_alloc_tdb) { + if (idmap_tdb_tdb_close(idmap_alloc_tdb) == 0) { + return NT_STATUS_OK; + } else { + return NT_STATUS_UNSUCCESSFUL; + } + } + return NT_STATUS_OK; +} + +/********************************************************************** + IDMAP MAPPING TDB BACKEND +**********************************************************************/ + +struct idmap_tdb_context { + TDB_CONTEXT *tdb; + uint32_t filter_low_id; + uint32_t filter_high_id; +}; + +/***************************** + Initialise idmap database. +*****************************/ + +static NTSTATUS idmap_tdb_db_init(struct idmap_domain *dom) +{ + NTSTATUS ret; + struct idmap_tdb_context *ctx; + char *config_option = NULL; + const char *range; + + ctx = talloc(dom, struct idmap_tdb_context); + if ( ! ctx) { + DEBUG(0, ("Out of memory!\n")); + return NT_STATUS_NO_MEMORY; + } + + config_option = talloc_asprintf(ctx, "idmap config %s", dom->name); + if ( ! config_option) { + DEBUG(0, ("Out of memory!\n")); + ret = NT_STATUS_NO_MEMORY; + goto failed; + } + + ret = idmap_tdb_open_db(ctx, &ctx->tdb); + if ( ! NT_STATUS_IS_OK(ret)) { + goto failed; + } + + range = lp_parm_const_string(-1, config_option, "range", NULL); + if (( ! range) || + (sscanf(range, "%u - %u", &ctx->filter_low_id, &ctx->filter_high_id) != 2) || + (ctx->filter_low_id > ctx->filter_high_id)) { + ctx->filter_low_id = 0; + ctx->filter_high_id = 0; + } + + dom->private_data = ctx; + dom->initialized = True; + + talloc_free(config_option); + return NT_STATUS_OK; + +failed: + talloc_free(ctx); + return ret; +} + +/********************************** + Single id to sid lookup function. +**********************************/ + +static NTSTATUS idmap_tdb_id_to_sid(struct idmap_tdb_context *ctx, struct id_map *map) +{ + NTSTATUS ret; + TDB_DATA data; + char *keystr; + + if (!ctx || !map) { + return NT_STATUS_INVALID_PARAMETER; + } + + /* apply filters before checking */ + if ((ctx->filter_low_id && (map->xid.id < ctx->filter_low_id)) || + (ctx->filter_high_id && (map->xid.id > ctx->filter_high_id))) { + DEBUG(5, ("Requested id (%u) out of range (%u - %u). Filtered!\n", + map->xid.id, ctx->filter_low_id, ctx->filter_high_id)); + return NT_STATUS_NONE_MAPPED; + } + + switch (map->xid.type) { + + case ID_TYPE_UID: + keystr = talloc_asprintf(ctx, "UID %lu", (unsigned long)map->xid.id); + break; + + case ID_TYPE_GID: + keystr = talloc_asprintf(ctx, "GID %lu", (unsigned long)map->xid.id); + break; + + default: + DEBUG(2, ("INVALID unix ID type: 0x02%x\n", map->xid.type)); + return NT_STATUS_INVALID_PARAMETER; + } + + /* final SAFE_FREE safe */ + data.dptr = NULL; + + if (keystr == NULL) { + DEBUG(0, ("Out of memory!\n")); + ret = NT_STATUS_NO_MEMORY; + goto done; + } + + DEBUG(10,("Fetching record %s\n", keystr)); + + /* Check if the mapping exists */ + data = tdb_fetch_bystring(ctx->tdb, keystr); + + if (!data.dptr) { + DEBUG(10,("Record %s not found\n", keystr)); + ret = NT_STATUS_NONE_MAPPED; + goto done; + } + + if (!string_to_sid(map->sid, (const char *)data.dptr)) { + DEBUG(10,("INVALID SID (%s) in record %s\n", + (const char *)data.dptr, keystr)); + ret = NT_STATUS_INTERNAL_DB_ERROR; + goto done; + } + + DEBUG(10,("Found record %s -> %s\n", keystr, (const char *)data.dptr)); + ret = NT_STATUS_OK; + +done: + SAFE_FREE(data.dptr); + talloc_free(keystr); + return ret; +} + +/********************************** + Single sid to id lookup function. +**********************************/ + +static NTSTATUS idmap_tdb_sid_to_id(struct idmap_tdb_context *ctx, struct id_map *map) +{ + NTSTATUS ret; + TDB_DATA data; + char *keystr; + unsigned long rec_id = 0; + + if ((keystr = talloc_asprintf(ctx, "%s", sid_string_static(map->sid))) == NULL) { + DEBUG(0, ("Out of memory!\n")); + ret = NT_STATUS_NO_MEMORY; + goto done; + } + + DEBUG(10,("Fetching record %s\n", keystr)); + + /* Check if sid is present in database */ + data = tdb_fetch_bystring(ctx->tdb, keystr); + if (!data.dptr) { + DEBUG(10,("Record %s not found\n", keystr)); + ret = NT_STATUS_NONE_MAPPED; + goto done; + } + + /* What type of record is this ? */ + if (sscanf((const char *)data.dptr, "UID %lu", &rec_id) == 1) { /* Try a UID record. */ + map->xid.id = rec_id; + map->xid.type = ID_TYPE_UID; + DEBUG(10,("Found uid record %s -> %s \n", keystr, (const char *)data.dptr )); + ret = NT_STATUS_OK; + + } else if (sscanf((const char *)data.dptr, "GID %lu", &rec_id) == 1) { /* Try a GID record. */ + map->xid.id = rec_id; + map->xid.type = ID_TYPE_GID; + DEBUG(10,("Found gid record %s -> %s \n", keystr, (const char *)data.dptr )); + ret = NT_STATUS_OK; + + } else { /* Unknown record type ! */ + DEBUG(2, ("Found INVALID record %s -> %s\n", keystr, (const char *)data.dptr)); + ret = NT_STATUS_INTERNAL_DB_ERROR; + } + + SAFE_FREE(data.dptr); + + /* apply filters before returning result */ + if ((ctx->filter_low_id && (map->xid.id < ctx->filter_low_id)) || + (ctx->filter_high_id && (map->xid.id > ctx->filter_high_id))) { + DEBUG(5, ("Requested id (%u) out of range (%u - %u). Filtered!\n", + map->xid.id, ctx->filter_low_id, ctx->filter_high_id)); + ret = NT_STATUS_NONE_MAPPED; + } + +done: + talloc_free(keystr); + return ret; +} + +/********************************** + lookup a set of unix ids. +**********************************/ + +static NTSTATUS idmap_tdb_unixids_to_sids(struct idmap_domain *dom, struct id_map **ids) +{ + struct idmap_tdb_context *ctx; + NTSTATUS ret; + int i; + + /* make sure we initialized */ + if ( ! dom->initialized) { + ret = idmap_tdb_db_init(dom); + if ( ! NT_STATUS_IS_OK(ret)) { + return ret; + } + } + + ctx = talloc_get_type(dom->private_data, struct idmap_tdb_context); + + for (i = 0; ids[i]; i++) { + ret = idmap_tdb_id_to_sid(ctx, ids[i]); + if ( ! NT_STATUS_IS_OK(ret)) { + + /* if it is just a failed mapping continue */ + if (NT_STATUS_EQUAL(ret, NT_STATUS_NONE_MAPPED)) { + + /* make sure it is marked as unmapped */ + ids[i]->status = ID_UNMAPPED; + continue; + } + + /* some fatal error occurred, return immediately */ + goto done; + } + + /* all ok, id is mapped */ + ids[i]->status = ID_MAPPED; + } + + ret = NT_STATUS_OK; + +done: + return ret; +} + +/********************************** + lookup a set of sids. +**********************************/ + +static NTSTATUS idmap_tdb_sids_to_unixids(struct idmap_domain *dom, struct id_map **ids) +{ + struct idmap_tdb_context *ctx; + NTSTATUS ret; + int i; + + /* make sure we initialized */ + if ( ! dom->initialized) { + ret = idmap_tdb_db_init(dom); + if ( ! NT_STATUS_IS_OK(ret)) { + return ret; + } + } + + ctx = talloc_get_type(dom->private_data, struct idmap_tdb_context); + + for (i = 0; ids[i]; i++) { + ret = idmap_tdb_sid_to_id(ctx, ids[i]); + if ( ! NT_STATUS_IS_OK(ret)) { + + /* if it is just a failed mapping continue */ + if (NT_STATUS_EQUAL(ret, NT_STATUS_NONE_MAPPED)) { + + /* make sure it is marked as unmapped */ + ids[i]->status = ID_UNMAPPED; + continue; + } + + /* some fatal error occurred, return immediately */ + goto done; + } + + /* all ok, id is mapped */ + ids[i]->status = ID_MAPPED; + } + + ret = NT_STATUS_OK; + +done: + return ret; +} + +/********************************** + set a mapping. +**********************************/ + +static NTSTATUS idmap_tdb_set_mapping(struct idmap_domain *dom, const struct id_map *map) +{ + struct idmap_tdb_context *ctx; + NTSTATUS ret; + TDB_DATA ksid, kid, data; + char *ksidstr, *kidstr; + + /* make sure we initialized */ + if ( ! dom->initialized) { + ret = idmap_tdb_db_init(dom); + if ( ! NT_STATUS_IS_OK(ret)) { + return ret; + } + } + + if (!map || !map->sid) { + return NT_STATUS_INVALID_PARAMETER; + } + + ksidstr = kidstr = NULL; + data.dptr = NULL; + + /* TODO: should we filter a set_mapping using low/high filters ? */ + + ctx = talloc_get_type(dom->private_data, struct idmap_tdb_context); + + switch (map->xid.type) { + + case ID_TYPE_UID: + kidstr = talloc_asprintf(ctx, "UID %lu", (unsigned long)map->xid.id); + break; + + case ID_TYPE_GID: + kidstr = talloc_asprintf(ctx, "GID %lu", (unsigned long)map->xid.id); + break; + + default: + DEBUG(2, ("INVALID unix ID type: 0x02%x\n", map->xid.type)); + return NT_STATUS_INVALID_PARAMETER; + } + + if (kidstr == NULL) { + DEBUG(0, ("ERROR: Out of memory!\n")); + ret = NT_STATUS_NO_MEMORY; + goto done; + } + + if ((ksidstr = talloc_asprintf(ctx, "%s", sid_string_static(map->sid))) == NULL) { + DEBUG(0, ("Out of memory!\n")); + ret = NT_STATUS_NO_MEMORY; + goto done; + } + + DEBUG(10, ("Storing %s <-> %s map\n", ksidstr, kidstr)); + kid = string_term_tdb_data(kidstr); + ksid = string_term_tdb_data(ksidstr); + + /* *DELETE* previous mappings if any. + * This is done both SID and [U|G]ID passed in */ + + /* Lock the record for this SID. */ + if (tdb_chainlock(ctx->tdb, ksid) != 0) { + DEBUG(10,("Failed to lock record %s. Error %s\n", + ksidstr, tdb_errorstr(ctx->tdb) )); + return NT_STATUS_UNSUCCESSFUL; + } + + data = tdb_fetch(ctx->tdb, ksid); + if (data.dptr) { + DEBUG(10, ("Deleting existing mapping %s <-> %s\n", (const char *)data.dptr, ksidstr )); + tdb_delete(ctx->tdb, data); + tdb_delete(ctx->tdb, ksid); + SAFE_FREE(data.dptr); + } + + data = tdb_fetch(ctx->tdb, kid); + if (data.dptr) { + DEBUG(10,("Deleting existing mapping %s <-> %s\n", (const char *)data.dptr, kidstr )); + tdb_delete(ctx->tdb, data); + tdb_delete(ctx->tdb, kid); + SAFE_FREE(data.dptr); + } + + if (tdb_store(ctx->tdb, ksid, kid, TDB_INSERT) == -1) { + DEBUG(0, ("Error storing SID -> ID: %s\n", tdb_errorstr(ctx->tdb))); + tdb_chainunlock(ctx->tdb, ksid); + ret = NT_STATUS_UNSUCCESSFUL; + goto done; + } + if (tdb_store(ctx->tdb, kid, ksid, TDB_INSERT) == -1) { + DEBUG(0, ("Error stroing ID -> SID: %s\n", tdb_errorstr(ctx->tdb))); + /* try to remove the previous stored SID -> ID map */ + tdb_delete(ctx->tdb, ksid); + tdb_chainunlock(ctx->tdb, ksid); + ret = NT_STATUS_UNSUCCESSFUL; + goto done; + } + + tdb_chainunlock(ctx->tdb, ksid); + DEBUG(10,("Stored %s <-> %s\n", ksidstr, kidstr)); + ret = NT_STATUS_OK; + +done: + talloc_free(ksidstr); + talloc_free(kidstr); + SAFE_FREE(data.dptr); + return ret; +} + +/********************************** + remove a mapping. +**********************************/ + +static NTSTATUS idmap_tdb_remove_mapping(struct idmap_domain *dom, const struct id_map *map) +{ + struct idmap_tdb_context *ctx; + NTSTATUS ret; + TDB_DATA ksid, kid, data; + char *ksidstr, *kidstr; + + /* make sure we initialized */ + if ( ! dom->initialized) { + ret = idmap_tdb_db_init(dom); + if ( ! NT_STATUS_IS_OK(ret)) { + return ret; + } + } + + if (!map || !map->sid) { + return NT_STATUS_INVALID_PARAMETER; + } + + ksidstr = kidstr = NULL; + data.dptr = NULL; + + /* TODO: should we filter a remove_mapping using low/high filters ? */ + + ctx = talloc_get_type(dom->private_data, struct idmap_tdb_context); + + switch (map->xid.type) { + + case ID_TYPE_UID: + kidstr = talloc_asprintf(ctx, "UID %lu", (unsigned long)map->xid.id); + break; + + case ID_TYPE_GID: + kidstr = talloc_asprintf(ctx, "GID %lu", (unsigned long)map->xid.id); + break; + + default: + DEBUG(2, ("INVALID unix ID type: 0x02%x\n", map->xid.type)); + return NT_STATUS_INVALID_PARAMETER; + } + + if (kidstr == NULL) { + DEBUG(0, ("ERROR: Out of memory!\n")); + ret = NT_STATUS_NO_MEMORY; + goto done; + } + + if ((ksidstr = talloc_asprintf(ctx, "%s", sid_string_static(map->sid))) == NULL) { + DEBUG(0, ("Out of memory!\n")); + ret = NT_STATUS_NO_MEMORY; + goto done; + } + + DEBUG(10, ("Checking %s <-> %s map\n", ksidstr, kidstr)); + ksid = string_term_tdb_data(ksidstr); + kid = string_term_tdb_data(kidstr); + + /* Lock the record for this SID. */ + if (tdb_chainlock(ctx->tdb, ksid) != 0) { + DEBUG(10,("Failed to lock record %s. Error %s\n", + ksidstr, tdb_errorstr(ctx->tdb) )); + return NT_STATUS_UNSUCCESSFUL; + } + + /* Check if sid is present in database */ + data = tdb_fetch(ctx->tdb, ksid); + if (!data.dptr) { + DEBUG(10,("Record %s not found\n", ksidstr)); + tdb_chainunlock(ctx->tdb, ksid); + ret = NT_STATUS_NONE_MAPPED; + goto done; + } + + /* Check if sid is mapped to the specified ID */ + if ((data.dsize != kid.dsize) || + (memcmp(data.dptr, kid.dptr, data.dsize) != 0)) { + DEBUG(10,("Specified SID does not map to specified ID\n")); + DEBUGADD(10,("Actual mapping is %s -> %s\n", ksidstr, (const char *)data.dptr)); + tdb_chainunlock(ctx->tdb, ksid); + ret = NT_STATUS_NONE_MAPPED; + goto done; + } + + DEBUG(10, ("Removing %s <-> %s map\n", ksidstr, kidstr)); + + /* Delete previous mappings. */ + + DEBUG(10, ("Deleting existing mapping %s -> %s\n", ksidstr, kidstr )); + tdb_delete(ctx->tdb, ksid); + + DEBUG(10,("Deleting existing mapping %s -> %s\n", kidstr, ksidstr )); + tdb_delete(ctx->tdb, kid); + + tdb_chainunlock(ctx->tdb, ksid); + ret = NT_STATUS_OK; + +done: + talloc_free(ksidstr); + talloc_free(kidstr); + SAFE_FREE(data.dptr); + return ret; +} + +/********************************** + Close the idmap tdb instance +**********************************/ + +static NTSTATUS idmap_tdb_close(struct idmap_domain *dom) +{ + struct idmap_tdb_context *ctx; + + if (dom->private_data) { + ctx = talloc_get_type(dom->private_data, struct idmap_tdb_context); + + if (idmap_tdb_tdb_close(ctx->tdb) == 0) { + return NT_STATUS_OK; + } else { + return NT_STATUS_UNSUCCESSFUL; + } + } + return NT_STATUS_OK; +} + +struct dump_data { + TALLOC_CTX *memctx; + struct id_map **maps; + int *num_maps; + NTSTATUS ret; +}; + +static int idmap_tdb_dump_one_entry(TDB_CONTEXT *tdb, TDB_DATA key, TDB_DATA value, void *pdata) +{ + struct dump_data *data = talloc_get_type(pdata, struct dump_data); + struct id_map *maps; + int num_maps = *data->num_maps; + + /* ignore any record but the ones with a SID as key */ + if (strncmp((const char *)key.dptr, "S-", 2) == 0) { + + maps = talloc_realloc(NULL, *data->maps, struct id_map, num_maps+1); + if ( ! maps) { + DEBUG(0, ("Out of memory!\n")); + data->ret = NT_STATUS_NO_MEMORY; + return -1; + } + *data->maps = maps; + maps[num_maps].sid = talloc(maps, DOM_SID); + if ( ! maps[num_maps].sid) { + DEBUG(0, ("Out of memory!\n")); + data->ret = NT_STATUS_NO_MEMORY; + return -1; + } + + if (!string_to_sid(maps[num_maps].sid, (const char *)key.dptr)) { + DEBUG(10,("INVALID record %s\n", (const char *)key.dptr)); + /* continue even with errors */ + return 0; + } + + /* Try a UID record. */ + if (sscanf((const char *)value.dptr, "UID %u", &(maps[num_maps].xid.id)) == 1) { + maps[num_maps].xid.type = ID_TYPE_UID; + maps[num_maps].status = ID_MAPPED; + *data->num_maps = num_maps + 1; + + /* Try a GID record. */ + } else + if (sscanf((const char *)value.dptr, "GID %u", &(maps[num_maps].xid.id)) == 1) { + maps[num_maps].xid.type = ID_TYPE_GID; + maps[num_maps].status = ID_MAPPED; + *data->num_maps = num_maps + 1; + + /* Unknown record type ! */ + } else { + maps[num_maps].status = ID_UNKNOWN; + DEBUG(2, ("Found INVALID record %s -> %s\n", + (const char *)key.dptr, (const char *)value.dptr)); + /* do not increment num_maps */ + } + } + + return 0; +} + +/********************************** + Dump all mappings out +**********************************/ + +static NTSTATUS idmap_tdb_dump_data(struct idmap_domain *dom, struct id_map **maps, int *num_maps) +{ + struct idmap_tdb_context *ctx; + struct dump_data *data; + NTSTATUS ret = NT_STATUS_OK; + + /* make sure we initialized */ + if ( ! dom->initialized) { + ret = idmap_tdb_db_init(dom); + if ( ! NT_STATUS_IS_OK(ret)) { + return ret; + } + } + + ctx = talloc_get_type(dom->private_data, struct idmap_tdb_context); + + data = TALLOC_ZERO_P(ctx, struct dump_data); + if ( ! data) { + DEBUG(0, ("Out of memory!\n")); + return NT_STATUS_NO_MEMORY; + } + data->maps = maps; + data->num_maps = num_maps; + data->ret = NT_STATUS_OK; + + tdb_traverse(ctx->tdb, idmap_tdb_dump_one_entry, data); + + if ( ! NT_STATUS_IS_OK(data->ret)) { + ret = data->ret; + } + + talloc_free(data); + return ret; +} + +static struct idmap_methods db_methods = { + + .init = idmap_tdb_db_init, + .unixids_to_sids = idmap_tdb_unixids_to_sids, + .sids_to_unixids = idmap_tdb_sids_to_unixids, + .set_mapping = idmap_tdb_set_mapping, + .remove_mapping = idmap_tdb_remove_mapping, + .dump_data = idmap_tdb_dump_data, + .close_fn = idmap_tdb_close +}; + +static struct idmap_alloc_methods db_alloc_methods = { + + .init = idmap_tdb_alloc_init, + .allocate_id = idmap_tdb_allocate_id, + .get_id_hwm = idmap_tdb_get_hwm, + .set_id_hwm = idmap_tdb_set_hwm, + .close_fn = idmap_tdb_alloc_close +}; + +NTSTATUS idmap_alloc_tdb_init(void) +{ + return smb_register_idmap_alloc(SMB_IDMAP_INTERFACE_VERSION, "tdb", &db_alloc_methods); +} + +NTSTATUS idmap_tdb_init(void) +{ + NTSTATUS ret; + + /* FIXME: bad hack to actually register also the alloc_tdb module without changining configure.in */ + ret = idmap_alloc_tdb_init(); + if (! NT_STATUS_IS_OK(ret)) { + return ret; + } + return smb_register_idmap(SMB_IDMAP_INTERFACE_VERSION, "tdb", &db_methods); +} diff --git a/source3/winbindd/idmap_util.c b/source3/winbindd/idmap_util.c new file mode 100644 index 0000000000..077c599739 --- /dev/null +++ b/source3/winbindd/idmap_util.c @@ -0,0 +1,171 @@ +/* + Unix SMB/CIFS implementation. + ID Mapping + Copyright (C) Simo Sorce 2003 + Copyright (C) Jeremy Allison 2006 + + 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 3 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, see .*/ + +#include "includes.h" + +#undef DBGC_CLASS +#define DBGC_CLASS DBGC_IDMAP + +/***************************************************************** + Returns the SID mapped to the given UID. + If mapping is not possible returns an error. +*****************************************************************/ + +NTSTATUS idmap_uid_to_sid(DOM_SID *sid, uid_t uid) +{ + NTSTATUS ret; + struct id_map map; + struct id_map *maps[2]; + + DEBUG(10,("uid = [%lu]\n", (unsigned long)uid)); + + map.sid = sid; + map.xid.type = ID_TYPE_UID; + map.xid.id = uid; + + maps[0] = ↦ + maps[1] = NULL; + + ret = idmap_unixids_to_sids(maps); + if ( ! NT_STATUS_IS_OK(ret)) { + DEBUG(10, ("error mapping uid [%lu]\n", (unsigned long)uid)); + return ret; + } + + if (map.status != ID_MAPPED) { + DEBUG(10, ("uid [%lu] not mapped\n", (unsigned long)uid)); + return NT_STATUS_NONE_MAPPED; + } + + return NT_STATUS_OK; +} + +/***************************************************************** + Returns SID mapped to the given GID. + If mapping is not possible returns an error. +*****************************************************************/ + +NTSTATUS idmap_gid_to_sid(DOM_SID *sid, gid_t gid) +{ + NTSTATUS ret; + struct id_map map; + struct id_map *maps[2]; + + DEBUG(10,("gid = [%lu]\n", (unsigned long)gid)); + + map.sid = sid; + map.xid.type = ID_TYPE_GID; + map.xid.id = gid; + + maps[0] = ↦ + maps[1] = NULL; + + ret = idmap_unixids_to_sids(maps); + if ( ! NT_STATUS_IS_OK(ret)) { + DEBUG(10, ("error mapping gid [%lu]\n", (unsigned long)gid)); + return ret; + } + + if (map.status != ID_MAPPED) { + DEBUG(10, ("gid [%lu] not mapped\n", (unsigned long)gid)); + return NT_STATUS_NONE_MAPPED; + } + + return NT_STATUS_OK; +} + +/***************************************************************** + Returns the UID mapped to the given SID. + If mapping is not possible or SID maps to a GID returns an error. +*****************************************************************/ + +NTSTATUS idmap_sid_to_uid(DOM_SID *sid, uid_t *uid) +{ + NTSTATUS ret; + struct id_map map; + struct id_map *maps[2]; + + DEBUG(10,("idmap_sid_to_uid: sid = [%s]\n", sid_string_static(sid))); + + map.sid = sid; + map.xid.type = ID_TYPE_UID; + + maps[0] = ↦ + maps[1] = NULL; + + ret = idmap_sids_to_unixids(maps); + if ( ! NT_STATUS_IS_OK(ret)) { + DEBUG(10, ("error mapping sid [%s] to uid\n", + sid_string_static(sid))); + return ret; + } + + if ((map.status != ID_MAPPED) || (map.xid.type != ID_TYPE_UID)) { + DEBUG(10, ("sid [%s] not mapped to an uid [%u,%u,%u]\n", + sid_string_static(sid), + map.status, + map.xid.type, + map.xid.id)); + return NT_STATUS_NONE_MAPPED; + } + + *uid = map.xid.id; + + return NT_STATUS_OK; +} + +/***************************************************************** + Returns the GID mapped to the given SID. + If mapping is not possible or SID maps to a UID returns an error. +*****************************************************************/ + +NTSTATUS idmap_sid_to_gid(DOM_SID *sid, gid_t *gid) +{ + NTSTATUS ret; + struct id_map map; + struct id_map *maps[2]; + + DEBUG(10,("idmap_sid_to_gid: sid = [%s]\n", sid_string_static(sid))); + + map.sid = sid; + map.xid.type = ID_TYPE_GID; + + maps[0] = ↦ + maps[1] = NULL; + + ret = idmap_sids_to_unixids(maps); + if ( ! NT_STATUS_IS_OK(ret)) { + DEBUG(10, ("error mapping sid [%s] to gid\n", + sid_string_static(sid))); + return ret; + } + + if ((map.status != ID_MAPPED) || (map.xid.type != ID_TYPE_GID)) { + DEBUG(10, ("sid [%s] not mapped to a gid [%u,%u,%u]\n", + sid_string_static(sid), + map.status, + map.xid.type, + map.xid.id)); + return NT_STATUS_NONE_MAPPED; + } + + *gid = map.xid.id; + + return NT_STATUS_OK; +} diff --git a/source3/winbindd/nss_info.c b/source3/winbindd/nss_info.c new file mode 100644 index 0000000000..ea51e9dde8 --- /dev/null +++ b/source3/winbindd/nss_info.c @@ -0,0 +1,301 @@ +/* + Unix SMB/CIFS implementation. + Idmap NSS headers + + Copyright (C) Gerald Carter 2006 + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 3 of the License, or (at your option) any later version. + + This library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program. If not, see . +*/ + +#include "includes.h" +#include "nss_info.h" + +static struct nss_function_entry *backends = NULL; +static struct nss_domain_entry *nss_domain_list = NULL; + +/********************************************************************** + Get idmap nss methods. +**********************************************************************/ + +static struct nss_function_entry *nss_get_backend(const char *name ) +{ + struct nss_function_entry *entry = backends; + + for(entry = backends; entry; entry = entry->next) { + if ( strequal(entry->name, name) ) + return entry; + } + + return NULL; +} + +/********************************************************************* + Allow a module to register itself as a backend. +**********************************************************************/ + + NTSTATUS smb_register_idmap_nss(int version, const char *name, struct nss_info_methods *methods) +{ + struct nss_function_entry *entry; + + if ((version != SMB_NSS_INFO_INTERFACE_VERSION)) { + DEBUG(0, ("smb_register_idmap_nss: Failed to register idmap_nss module.\n" + "The module was compiled against SMB_NSS_INFO_INTERFACE_VERSION %d,\n" + "current SMB_NSS_INFO_INTERFACE_VERSION is %d.\n" + "Please recompile against the current version of samba!\n", + version, SMB_NSS_INFO_INTERFACE_VERSION)); + return NT_STATUS_OBJECT_TYPE_MISMATCH; + } + + if (!name || !name[0] || !methods) { + DEBUG(0,("smb_register_idmap_nss: called with NULL pointer or empty name!\n")); + return NT_STATUS_INVALID_PARAMETER; + } + + if ( nss_get_backend(name) ) { + DEBUG(0,("smb_register_idmap_nss: idmap module %s " + "already registered!\n", name)); + return NT_STATUS_OBJECT_NAME_COLLISION; + } + + entry = SMB_XMALLOC_P(struct nss_function_entry); + entry->name = smb_xstrdup(name); + entry->methods = methods; + + DLIST_ADD(backends, entry); + DEBUG(5, ("smb_register_idmap_nss: Successfully added idmap " + "nss backend '%s'\n", name)); + + return NT_STATUS_OK; +} + +/******************************************************************** + *******************************************************************/ + +static BOOL parse_nss_parm( const char *config, char **backend, char **domain ) +{ + char *p; + char *q; + int len; + + *backend = *domain = NULL; + + if ( !config ) + return False; + + p = strchr( config, ':' ); + + /* if no : then the string must be the backend name only */ + + if ( !p ) { + *backend = SMB_STRDUP( config ); + return (*backend != NULL); + } + + /* split the string and return the two parts */ + + if ( strlen(p+1) > 0 ) { + *domain = SMB_STRDUP( p+1 ); + } + + len = PTR_DIFF(p,config)+1; + if ( (q = SMB_MALLOC_ARRAY( char, len )) == NULL ) { + SAFE_FREE( *backend ); + return False; + } + + StrnCpy( q, config, len-1); + q[len-1] = '\0'; + *backend = q; + + return True; +} + +/******************************************************************** + Each nss backend must not store global state, but rather be able + to initialize the state on a per domain basis. + *******************************************************************/ + + NTSTATUS nss_init( const char **nss_list ) +{ + NTSTATUS status; + static NTSTATUS nss_initialized = NT_STATUS_UNSUCCESSFUL; + int i; + char *backend, *domain; + struct nss_function_entry *nss_backend; + struct nss_domain_entry *nss_domain; + + /* check for previous successful initializations */ + + if ( NT_STATUS_IS_OK(nss_initialized) ) + return NT_STATUS_OK; + + /* The "template" backend should alqays be registered as it + is a static module */ + + if ( (nss_backend = nss_get_backend( "template" )) == NULL ) { + static_init_nss_info; + } + + /* Create the list of nss_domains (loading any shared plugins + as necessary) */ + + for ( i=0; nss_list && nss_list[i]; i++ ) { + + if ( !parse_nss_parm(nss_list[i], &backend, &domain) ) { + DEBUG(0,("nss_init: failed to parse \"%s\"!\n", + nss_list[i])); + continue; + } + + /* validate the backend */ + + if ( (nss_backend = nss_get_backend( backend )) == NULL ) { + /* attempt to register the backend */ + status = smb_probe_module( "nss_info", backend ); + if ( !NT_STATUS_IS_OK(status) ) { + continue; + } + + /* try again */ + if ( (nss_backend = nss_get_backend( backend )) == NULL ) { + DEBUG(0,("nss_init: unregistered backend %s!. Skipping\n", + backend)); + continue; + } + + } + + /* fill in the nss_domain_entry and add it to the + list of domains */ + + nss_domain = TALLOC_ZERO_P( nss_domain_list, struct nss_domain_entry ); + if ( !nss_domain ) { + DEBUG(0,("nss_init: talloc() failure!\n")); + return NT_STATUS_NO_MEMORY; + } + + nss_domain->backend = nss_backend; + nss_domain->domain = talloc_strdup( nss_domain, domain ); + + /* Try to init and ave the result */ + + nss_domain->init_status = nss_domain->backend->methods->init( nss_domain ); + DLIST_ADD( nss_domain_list, nss_domain ); + if ( !NT_STATUS_IS_OK(nss_domain->init_status) ) { + DEBUG(0,("nss_init: Failed to init backend for %s domain!\n", + nss_domain->domain)); + } + + /* cleanup */ + + SAFE_FREE( backend ); + SAFE_FREE( domain ); + } + + if ( !nss_domain_list ) { + DEBUG(3,("nss_init: no nss backends configured. " + "Defaulting to \"template\".\n")); + + + /* we shouild default to use template here */ + } + + + nss_initialized = NT_STATUS_OK; + + return NT_STATUS_OK; +} + +/******************************************************************** + *******************************************************************/ + +static struct nss_domain_entry *find_nss_domain( const char *domain ) +{ + NTSTATUS status; + struct nss_domain_entry *p; + + status = nss_init( lp_winbind_nss_info() ); + if ( !NT_STATUS_IS_OK(status) ) { + DEBUG(4,("nss_get_info: Failed to init nss_info API (%s)!\n", + nt_errstr(status))); + return NULL; + } + + for ( p=nss_domain_list; p; p=p->next ) { + if ( strequal( p->domain, domain ) ) + break; + } + + /* If we didn't find a match, then use the default nss info */ + + if ( !p ) { + if ( !nss_domain_list ) { + return NULL; + } + + p = nss_domain_list; + } + + if ( !NT_STATUS_IS_OK( p->init_status ) ) { + p->init_status = p->backend->methods->init( p ); + } + + return p; +} + +/******************************************************************** + *******************************************************************/ + + NTSTATUS nss_get_info( const char *domain, const DOM_SID *user_sid, + TALLOC_CTX *ctx, + ADS_STRUCT *ads, LDAPMessage *msg, + char **homedir, char **shell, char **gecos, + gid_t *p_gid) +{ + struct nss_domain_entry *p; + struct nss_info_methods *m; + + if ( (p = find_nss_domain( domain )) == NULL ) { + DEBUG(4,("nss_get_info: Failed to find nss domain pointer for %s\n", + domain )); + return NT_STATUS_NOT_FOUND; + } + + m = p->backend->methods; + + return m->get_nss_info( p, user_sid, ctx, ads, msg, + homedir, shell, gecos, p_gid ); +} + +/******************************************************************** + *******************************************************************/ + + NTSTATUS nss_close( const char *parameters ) +{ + struct nss_domain_entry *p = nss_domain_list; + struct nss_domain_entry *q; + + while ( p && p->backend && p->backend->methods ) { + /* close the backend */ + p->backend->methods->close_fn(); + + /* free the memory */ + q = p; + p = p->next; + TALLOC_FREE( q ); + } + + return NT_STATUS_OK; +} + diff --git a/source3/winbindd/nss_info_template.c b/source3/winbindd/nss_info_template.c new file mode 100644 index 0000000000..aaf02e4abe --- /dev/null +++ b/source3/winbindd/nss_info_template.c @@ -0,0 +1,83 @@ +/* + Unix SMB/CIFS implementation. + idMap nss template plugin + + Copyright (C) Gerald Carter 2006 + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 3 of the License, or (at your option) any later version. + + This library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program. If not, see . +*/ + +#include "includes.h" +#include "nss_info.h" + +/************************************************************************ + ***********************************************************************/ + +static NTSTATUS nss_template_init( struct nss_domain_entry *e ) +{ + return NT_STATUS_OK; +} + +/************************************************************************ + ***********************************************************************/ + +static NTSTATUS nss_template_get_info( struct nss_domain_entry *e, + const DOM_SID *sid, + TALLOC_CTX *ctx, + ADS_STRUCT *ads, + LDAPMessage *msg, + char **homedir, + char **shell, + char **gecos, + gid_t *gid ) +{ + if ( !homedir || !shell || !gecos ) + return NT_STATUS_INVALID_PARAMETER; + + *homedir = talloc_strdup( ctx, lp_template_homedir() ); + *shell = talloc_strdup( ctx, lp_template_shell() ); + *gecos = NULL; + + if ( !*homedir || !*shell ) { + return NT_STATUS_NO_MEMORY; + } + + return NT_STATUS_OK; +} + +/************************************************************************ + ***********************************************************************/ + +static NTSTATUS nss_template_close( void ) +{ + return NT_STATUS_OK; +} + + +/************************************************************************ + ***********************************************************************/ + +static struct nss_info_methods nss_template_methods = { + .init = nss_template_init, + .get_nss_info = nss_template_get_info, + .close_fn = nss_template_close +}; + +NTSTATUS nss_info_template_init( void ) +{ + return smb_register_idmap_nss(SMB_NSS_INFO_INTERFACE_VERSION, + "template", + &nss_template_methods); +} + diff --git a/source3/winbindd/winbindd.c b/source3/winbindd/winbindd.c new file mode 100644 index 0000000000..17915fb01b --- /dev/null +++ b/source3/winbindd/winbindd.c @@ -0,0 +1,1254 @@ +/* + Unix SMB/CIFS implementation. + + Winbind daemon for ntdom nss module + + Copyright (C) by Tim Potter 2000-2002 + Copyright (C) Andrew Tridgell 2002 + Copyright (C) Jelmer Vernooij 2003 + Copyright (C) Volker Lendecke 2004 + Copyright (C) James Peach 2007 + + 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 3 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, see . +*/ + +#include "includes.h" +#include "winbindd.h" + +#undef DBGC_CLASS +#define DBGC_CLASS DBGC_WINBIND + +BOOL opt_nocache = False; + +extern BOOL override_logfile; + +struct event_context *winbind_event_context(void) +{ + static struct event_context *ctx; + + if (!ctx && !(ctx = event_context_init(NULL))) { + smb_panic("Could not init winbind event context"); + } + return ctx; +} + +struct messaging_context *winbind_messaging_context(void) +{ + static struct messaging_context *ctx; + + if (!ctx && !(ctx = messaging_init(NULL, server_id_self(), + winbind_event_context()))) { + smb_panic("Could not init winbind messaging context"); + } + return ctx; +} + +/* Reload configuration */ + +static BOOL reload_services_file(void) +{ + BOOL ret; + + if (lp_loaded()) { + pstring fname; + + pstrcpy(fname,lp_configfile()); + if (file_exist(fname,NULL) && !strcsequal(fname,dyn_CONFIGFILE)) { + pstrcpy(dyn_CONFIGFILE,fname); + } + } + + reopen_logs(); + ret = lp_load(dyn_CONFIGFILE,False,False,True,True); + + reopen_logs(); + load_interfaces(); + + return(ret); +} + + +/**************************************************************************** ** + Handle a fault.. + **************************************************************************** */ + +static void fault_quit(void) +{ + dump_core(); +} + +static void winbindd_status(void) +{ + struct winbindd_cli_state *tmp; + + DEBUG(0, ("winbindd status:\n")); + + /* Print client state information */ + + DEBUG(0, ("\t%d clients currently active\n", winbindd_num_clients())); + + if (DEBUGLEVEL >= 2 && winbindd_num_clients()) { + DEBUG(2, ("\tclient list:\n")); + for(tmp = winbindd_client_list(); tmp; tmp = tmp->next) { + DEBUGADD(2, ("\t\tpid %lu, sock %d\n", + (unsigned long)tmp->pid, tmp->sock)); + } + } +} + +/* Print winbindd status to log file */ + +static void print_winbindd_status(void) +{ + winbindd_status(); +} + +/* Flush client cache */ + +static void flush_caches(void) +{ + /* We need to invalidate cached user list entries on a SIGHUP + otherwise cached access denied errors due to restrict anonymous + hang around until the sequence number changes. */ + + wcache_invalidate_cache(); +} + +/* Handle the signal by unlinking socket and exiting */ + +static void terminate(void) +{ + + winbindd_release_sockets(); + idmap_close(); + + trustdom_cache_shutdown(); + +#if 0 + if (interactive) { + TALLOC_CTX *mem_ctx = talloc_init("end_description"); + char *description = talloc_describe_all(mem_ctx); + + DEBUG(3, ("tallocs left:\n%s\n", description)); + talloc_destroy(mem_ctx); + } +#endif + + exit(0); +} + +static BOOL do_sigterm; + +static void termination_handler(int signum) +{ + do_sigterm = True; + sys_select_signal(signum); +} + +static BOOL do_sigusr2; + +static void sigusr2_handler(int signum) +{ + do_sigusr2 = True; + sys_select_signal(SIGUSR2); +} + +static BOOL do_sighup; + +static void sighup_handler(int signum) +{ + do_sighup = True; + sys_select_signal(SIGHUP); +} + +static BOOL do_sigchld; + +static void sigchld_handler(int signum) +{ + do_sigchld = True; + sys_select_signal(SIGCHLD); +} + +/* React on 'smbcontrol winbindd reload-config' in the same way as on SIGHUP*/ +static void msg_reload_services(struct messaging_context *msg, + void *private_data, + uint32_t msg_type, + struct server_id server_id, + DATA_BLOB *data) +{ + /* Flush various caches */ + flush_caches(); + reload_services_file(); +} + +/* React on 'smbcontrol winbindd shutdown' in the same way as on SIGTERM*/ +static void msg_shutdown(struct messaging_context *msg, + void *private_data, + uint32_t msg_type, + struct server_id server_id, + DATA_BLOB *data) +{ + do_sigterm = True; +} + + +static void winbind_msg_validate_cache(struct messaging_context *msg_ctx, + void *private_data, + uint32_t msg_type, + struct server_id server_id, + DATA_BLOB *data) +{ + uint8 ret; + pid_t child_pid; + struct sigaction act; + struct sigaction oldact; + + DEBUG(10, ("winbindd_msg_validate_cache: got validate-cache " + "message.\n")); + + /* + * call the validation code from a child: + * so we don't block the main winbindd and the validation + * code can safely use fork/waitpid... + */ + CatchChild(); + child_pid = sys_fork(); + + if (child_pid == -1) { + DEBUG(1, ("winbind_msg_validate_cache: Could not fork: %s\n", + strerror(errno))); + return; + } + + if (child_pid != 0) { + /* parent */ + DEBUG(5, ("winbind_msg_validate_cache: child created with " + "pid %d.\n", child_pid)); + return; + } + + /* child */ + + /* install default SIGCHLD handler: validation code uses fork/waitpid */ + ZERO_STRUCT(act); + act.sa_handler = SIG_DFL; +#ifdef SA_RESTART + /* We *want* SIGALRM to interrupt a system call. */ + act.sa_flags = SA_RESTART; +#endif + sigemptyset(&act.sa_mask); + sigaddset(&act.sa_mask,SIGCHLD); + sigaction(SIGCHLD,&act,&oldact); + + ret = (uint8)winbindd_validate_cache_nobackup(); + DEBUG(10, ("winbindd_msg_validata_cache: got return value %d\n", ret)); + messaging_send_buf(msg_ctx, server_id, MSG_WINBIND_VALIDATE_CACHE, &ret, + (size_t)1); + _exit(0); +} + +static struct winbindd_dispatch_table { + enum winbindd_cmd cmd; + void (*fn)(struct winbindd_cli_state *state); + const char *winbindd_cmd_name; +} dispatch_table[] = { + + /* User functions */ + + { WINBINDD_GETPWNAM, winbindd_getpwnam, "GETPWNAM" }, + { WINBINDD_GETPWUID, winbindd_getpwuid, "GETPWUID" }, + + { WINBINDD_SETPWENT, winbindd_setpwent, "SETPWENT" }, + { WINBINDD_ENDPWENT, winbindd_endpwent, "ENDPWENT" }, + { WINBINDD_GETPWENT, winbindd_getpwent, "GETPWENT" }, + + { WINBINDD_GETGROUPS, winbindd_getgroups, "GETGROUPS" }, + { WINBINDD_GETUSERSIDS, winbindd_getusersids, "GETUSERSIDS" }, + { WINBINDD_GETUSERDOMGROUPS, winbindd_getuserdomgroups, + "GETUSERDOMGROUPS" }, + + /* Group functions */ + + { WINBINDD_GETGRNAM, winbindd_getgrnam, "GETGRNAM" }, + { WINBINDD_GETGRGID, winbindd_getgrgid, "GETGRGID" }, + { WINBINDD_SETGRENT, winbindd_setgrent, "SETGRENT" }, + { WINBINDD_ENDGRENT, winbindd_endgrent, "ENDGRENT" }, + { WINBINDD_GETGRENT, winbindd_getgrent, "GETGRENT" }, + { WINBINDD_GETGRLST, winbindd_getgrent, "GETGRLST" }, + + /* PAM auth functions */ + + { WINBINDD_PAM_AUTH, winbindd_pam_auth, "PAM_AUTH" }, + { WINBINDD_PAM_AUTH_CRAP, winbindd_pam_auth_crap, "AUTH_CRAP" }, + { WINBINDD_PAM_CHAUTHTOK, winbindd_pam_chauthtok, "CHAUTHTOK" }, + { WINBINDD_PAM_LOGOFF, winbindd_pam_logoff, "PAM_LOGOFF" }, + { WINBINDD_PAM_CHNG_PSWD_AUTH_CRAP, winbindd_pam_chng_pswd_auth_crap, "CHNG_PSWD_AUTH_CRAP" }, + + /* Enumeration functions */ + + { WINBINDD_LIST_USERS, winbindd_list_users, "LIST_USERS" }, + { WINBINDD_LIST_GROUPS, winbindd_list_groups, "LIST_GROUPS" }, + { WINBINDD_LIST_TRUSTDOM, winbindd_list_trusted_domains, + "LIST_TRUSTDOM" }, + { WINBINDD_SHOW_SEQUENCE, winbindd_show_sequence, "SHOW_SEQUENCE" }, + + /* SID related functions */ + + { WINBINDD_LOOKUPSID, winbindd_lookupsid, "LOOKUPSID" }, + { WINBINDD_LOOKUPNAME, winbindd_lookupname, "LOOKUPNAME" }, + { WINBINDD_LOOKUPRIDS, winbindd_lookuprids, "LOOKUPRIDS" }, + + /* Lookup related functions */ + + { WINBINDD_SID_TO_UID, winbindd_sid_to_uid, "SID_TO_UID" }, + { WINBINDD_SID_TO_GID, winbindd_sid_to_gid, "SID_TO_GID" }, + { WINBINDD_UID_TO_SID, winbindd_uid_to_sid, "UID_TO_SID" }, + { WINBINDD_GID_TO_SID, winbindd_gid_to_sid, "GID_TO_SID" }, +#if 0 /* DISABLED until we fix the interface in Samba 3.0.26 --jerry */ + { WINBINDD_SIDS_TO_XIDS, winbindd_sids_to_unixids, "SIDS_TO_XIDS" }, +#endif /* end DISABLED */ + { WINBINDD_ALLOCATE_UID, winbindd_allocate_uid, "ALLOCATE_UID" }, + { WINBINDD_ALLOCATE_GID, winbindd_allocate_gid, "ALLOCATE_GID" }, + { WINBINDD_SET_MAPPING, winbindd_set_mapping, "SET_MAPPING" }, + { WINBINDD_SET_HWM, winbindd_set_hwm, "SET_HWMS" }, + + /* Miscellaneous */ + + { WINBINDD_DUMP_MAPS, winbindd_dump_maps, "DUMP_MAPS" }, + + { WINBINDD_CHECK_MACHACC, winbindd_check_machine_acct, "CHECK_MACHACC" }, + { WINBINDD_PING, winbindd_ping, "PING" }, + { WINBINDD_INFO, winbindd_info, "INFO" }, + { WINBINDD_INTERFACE_VERSION, winbindd_interface_version, + "INTERFACE_VERSION" }, + { WINBINDD_DOMAIN_NAME, winbindd_domain_name, "DOMAIN_NAME" }, + { WINBINDD_DOMAIN_INFO, winbindd_domain_info, "DOMAIN_INFO" }, + { WINBINDD_NETBIOS_NAME, winbindd_netbios_name, "NETBIOS_NAME" }, + { WINBINDD_PRIV_PIPE_DIR, winbindd_priv_pipe_dir, + "WINBINDD_PRIV_PIPE_DIR" }, + { WINBINDD_GETDCNAME, winbindd_getdcname, "GETDCNAME" }, + { WINBINDD_DSGETDCNAME, winbindd_dsgetdcname, "DSGETDCNAME" }, + + /* Credential cache access */ + { WINBINDD_CCACHE_NTLMAUTH, winbindd_ccache_ntlm_auth, "NTLMAUTH" }, + + /* WINS functions */ + + { WINBINDD_WINS_BYNAME, winbindd_wins_byname, "WINS_BYNAME" }, + { WINBINDD_WINS_BYIP, winbindd_wins_byip, "WINS_BYIP" }, + + /* End of list */ + + { WINBINDD_NUM_CMDS, NULL, "NONE" } +}; + +static void process_request(struct winbindd_cli_state *state) +{ + struct winbindd_dispatch_table *table = dispatch_table; + + /* Free response data - we may be interrupted and receive another + command before being able to send this data off. */ + + SAFE_FREE(state->response.extra_data.data); + + ZERO_STRUCT(state->response); + + state->response.result = WINBINDD_PENDING; + state->response.length = sizeof(struct winbindd_response); + + state->mem_ctx = talloc_init("winbind request"); + if (state->mem_ctx == NULL) + return; + + /* Remember who asked us. */ + state->pid = state->request.pid; + + /* Process command */ + + for (table = dispatch_table; table->fn; table++) { + if (state->request.cmd == table->cmd) { + DEBUG(10,("process_request: request fn %s\n", + table->winbindd_cmd_name )); + table->fn(state); + break; + } + } + + if (!table->fn) { + DEBUG(10,("process_request: unknown request fn number %d\n", + (int)state->request.cmd )); + request_error(state); + } +} + +/* + * A list of file descriptors being monitored by select in the main processing + * loop. fd_event->handler is called whenever the socket is readable/writable. + */ + +static struct fd_event *fd_events = NULL; + +void add_fd_event(struct fd_event *ev) +{ + struct fd_event *match; + + /* only add unique fd_event structs */ + + for (match=fd_events; match; match=match->next ) { +#ifdef DEVELOPER + SMB_ASSERT( match != ev ); +#else + if ( match == ev ) + return; +#endif + } + + DLIST_ADD(fd_events, ev); +} + +void remove_fd_event(struct fd_event *ev) +{ + DLIST_REMOVE(fd_events, ev); +} + +/* + * Handler for fd_events to complete a read/write request, set up by + * setup_async_read/setup_async_write. + */ + +static void rw_callback(struct fd_event *event, int flags) +{ + size_t todo; + ssize_t done = 0; + + todo = event->length - event->done; + + if (event->flags & EVENT_FD_WRITE) { + SMB_ASSERT(flags == EVENT_FD_WRITE); + done = sys_write(event->fd, + &((char *)event->data)[event->done], + todo); + + if (done <= 0) { + event->flags = 0; + event->finished(event->private_data, False); + return; + } + } + + if (event->flags & EVENT_FD_READ) { + SMB_ASSERT(flags == EVENT_FD_READ); + done = sys_read(event->fd, &((char *)event->data)[event->done], + todo); + + if (done <= 0) { + event->flags = 0; + event->finished(event->private_data, False); + return; + } + } + + event->done += done; + + if (event->done == event->length) { + event->flags = 0; + event->finished(event->private_data, True); + } +} + +/* + * Request an async read/write on a fd_event structure. (*finished) is called + * when the request is completed or an error had occurred. + */ + +void setup_async_read(struct fd_event *event, void *data, size_t length, + void (*finished)(void *private_data, BOOL success), + void *private_data) +{ + SMB_ASSERT(event->flags == 0); + event->data = data; + event->length = length; + event->done = 0; + event->handler = rw_callback; + event->finished = finished; + event->private_data = private_data; + event->flags = EVENT_FD_READ; +} + +void setup_async_write(struct fd_event *event, void *data, size_t length, + void (*finished)(void *private_data, BOOL success), + void *private_data) +{ + SMB_ASSERT(event->flags == 0); + event->data = data; + event->length = length; + event->done = 0; + event->handler = rw_callback; + event->finished = finished; + event->private_data = private_data; + event->flags = EVENT_FD_WRITE; +} + +/* + * This is the main event loop of winbind requests. It goes through a + * state-machine of 3 read/write requests, 4 if you have extra data to send. + * + * An idle winbind client has a read request of 4 bytes outstanding, + * finalizing function is request_len_recv, checking the length. request_recv + * then processes the packet. The processing function then at some point has + * to call request_finished which schedules sending the response. + */ + +static void request_len_recv(void *private_data, BOOL success); +static void request_recv(void *private_data, BOOL success); +static void request_main_recv(void *private_data, BOOL success); +static void request_finished(struct winbindd_cli_state *state); +void request_finished_cont(void *private_data, BOOL success); +static void response_main_sent(void *private_data, BOOL success); +static void response_extra_sent(void *private_data, BOOL success); + +static void response_extra_sent(void *private_data, BOOL success) +{ + struct winbindd_cli_state *state = + talloc_get_type_abort(private_data, struct winbindd_cli_state); + + if (state->mem_ctx != NULL) { + talloc_destroy(state->mem_ctx); + state->mem_ctx = NULL; + } + + if (!success) { + state->finished = True; + return; + } + + SAFE_FREE(state->request.extra_data.data); + SAFE_FREE(state->response.extra_data.data); + + setup_async_read(&state->fd_event, &state->request, sizeof(uint32), + request_len_recv, state); +} + +static void response_main_sent(void *private_data, BOOL success) +{ + struct winbindd_cli_state *state = + talloc_get_type_abort(private_data, struct winbindd_cli_state); + + if (!success) { + state->finished = True; + return; + } + + if (state->response.length == sizeof(state->response)) { + if (state->mem_ctx != NULL) { + talloc_destroy(state->mem_ctx); + state->mem_ctx = NULL; + } + + setup_async_read(&state->fd_event, &state->request, + sizeof(uint32), request_len_recv, state); + return; + } + + setup_async_write(&state->fd_event, state->response.extra_data.data, + state->response.length - sizeof(state->response), + response_extra_sent, state); +} + +static void request_finished(struct winbindd_cli_state *state) +{ + setup_async_write(&state->fd_event, &state->response, + sizeof(state->response), response_main_sent, state); +} + +void request_error(struct winbindd_cli_state *state) +{ + SMB_ASSERT(state->response.result == WINBINDD_PENDING); + state->response.result = WINBINDD_ERROR; + request_finished(state); +} + +void request_ok(struct winbindd_cli_state *state) +{ + SMB_ASSERT(state->response.result == WINBINDD_PENDING); + state->response.result = WINBINDD_OK; + request_finished(state); +} + +void request_finished_cont(void *private_data, BOOL success) +{ + struct winbindd_cli_state *state = + talloc_get_type_abort(private_data, struct winbindd_cli_state); + + if (success) + request_ok(state); + else + request_error(state); +} + +static void request_len_recv(void *private_data, BOOL success) +{ + struct winbindd_cli_state *state = + talloc_get_type_abort(private_data, struct winbindd_cli_state); + + if (!success) { + state->finished = True; + return; + } + + if (*(uint32 *)(&state->request) != sizeof(state->request)) { + DEBUG(0,("request_len_recv: Invalid request size received: %d\n", + *(uint32 *)(&state->request))); + state->finished = True; + return; + } + + setup_async_read(&state->fd_event, (uint32 *)(&state->request)+1, + sizeof(state->request) - sizeof(uint32), + request_main_recv, state); +} + +static void request_main_recv(void *private_data, BOOL success) +{ + struct winbindd_cli_state *state = + talloc_get_type_abort(private_data, struct winbindd_cli_state); + + if (!success) { + state->finished = True; + return; + } + + if (state->request.extra_len == 0) { + state->request.extra_data.data = NULL; + request_recv(state, True); + return; + } + + if ((!state->privileged) && + (state->request.extra_len > WINBINDD_MAX_EXTRA_DATA)) { + DEBUG(3, ("Got request with %d bytes extra data on " + "unprivileged socket\n", (int)state->request.extra_len)); + state->request.extra_data.data = NULL; + state->finished = True; + return; + } + + state->request.extra_data.data = + SMB_MALLOC_ARRAY(char, state->request.extra_len + 1); + + if (state->request.extra_data.data == NULL) { + DEBUG(0, ("malloc failed\n")); + state->finished = True; + return; + } + + /* Ensure null termination */ + state->request.extra_data.data[state->request.extra_len] = '\0'; + + setup_async_read(&state->fd_event, state->request.extra_data.data, + state->request.extra_len, request_recv, state); +} + +static void request_recv(void *private_data, BOOL success) +{ + struct winbindd_cli_state *state = + talloc_get_type_abort(private_data, struct winbindd_cli_state); + + if (!success) { + state->finished = True; + return; + } + + process_request(state); +} + +/* Process a new connection by adding it to the client connection list */ + +static void new_connection(int listen_sock, BOOL privileged) +{ + struct sockaddr_un sunaddr; + struct winbindd_cli_state *state; + socklen_t len; + int sock; + + /* Accept connection */ + + len = sizeof(sunaddr); + + do { + sock = accept(listen_sock, (struct sockaddr *)&sunaddr, &len); + } while (sock == -1 && errno == EINTR); + + if (sock == -1) + return; + + DEBUG(6,("accepted socket %d\n", sock)); + + /* Create new connection structure */ + + if ((state = TALLOC_ZERO_P(NULL, struct winbindd_cli_state)) == NULL) { + close(sock); + return; + } + + state->sock = sock; + + state->last_access = time(NULL); + + state->privileged = privileged; + + state->fd_event.fd = state->sock; + state->fd_event.flags = 0; + add_fd_event(&state->fd_event); + + setup_async_read(&state->fd_event, &state->request, sizeof(uint32), + request_len_recv, state); + + /* Add to connection list */ + + winbindd_add_client(state); +} + +/* Remove a client connection from client connection list */ + +static void remove_client(struct winbindd_cli_state *state) +{ + /* It's a dead client - hold a funeral */ + + if (state == NULL) { + return; + } + + /* Close socket */ + + close(state->sock); + + /* Free any getent state */ + + free_getent_state(state->getpwent_state); + free_getent_state(state->getgrent_state); + + /* We may have some extra data that was not freed if the client was + killed unexpectedly */ + + SAFE_FREE(state->response.extra_data.data); + + if (state->mem_ctx != NULL) { + talloc_destroy(state->mem_ctx); + state->mem_ctx = NULL; + } + + remove_fd_event(&state->fd_event); + + /* Remove from list and free */ + + winbindd_remove_client(state); + TALLOC_FREE(state); +} + +/* Shutdown client connection which has been idle for the longest time */ + +static BOOL remove_idle_client(void) +{ + struct winbindd_cli_state *state, *remove_state = NULL; + time_t last_access = 0; + int nidle = 0; + + for (state = winbindd_client_list(); state; state = state->next) { + if (state->response.result != WINBINDD_PENDING && + !state->getpwent_state && !state->getgrent_state) { + nidle++; + if (!last_access || state->last_access < last_access) { + last_access = state->last_access; + remove_state = state; + } + } + } + + if (remove_state) { + DEBUG(5,("Found %d idle client connections, shutting down sock %d, pid %u\n", + nidle, remove_state->sock, (unsigned int)remove_state->pid)); + remove_client(remove_state); + return True; + } + + return False; +} + +/* Process incoming clients on listen_sock. We use a tricky non-blocking, + non-forking, non-threaded model which allows us to handle many + simultaneous connections while remaining impervious to many denial of + service attacks. */ + +static int process_loop(int listen_sock, int listen_priv_sock) +{ + struct winbindd_cli_state *state; + struct fd_event *ev; + fd_set r_fds, w_fds; + int maxfd, selret; + struct timeval timeout, ev_timeout; + + /* We'll be doing this a lot */ + + /* Handle messages */ + + message_dispatch(winbind_messaging_context()); + + run_events(winbind_event_context(), 0, NULL, NULL); + + /* refresh the trusted domain cache */ + + rescan_trusted_domains(); + + /* Initialise fd lists for select() */ + + maxfd = MAX(listen_sock, listen_priv_sock); + + FD_ZERO(&r_fds); + FD_ZERO(&w_fds); + FD_SET(listen_sock, &r_fds); + FD_SET(listen_priv_sock, &r_fds); + + timeout.tv_sec = WINBINDD_ESTABLISH_LOOP; + timeout.tv_usec = 0; + + /* Check for any event timeouts. */ + if (get_timed_events_timeout(winbind_event_context(), &ev_timeout)) { + timeout = timeval_min(&timeout, &ev_timeout); + } + + /* Set up client readers and writers */ + + state = winbindd_client_list(); + + while (state) { + + struct winbindd_cli_state *next = state->next; + + /* Dispose of client connection if it is marked as + finished */ + + if (state->finished) + remove_client(state); + + state = next; + } + + for (ev = fd_events; ev; ev = ev->next) { + if (ev->flags & EVENT_FD_READ) { + FD_SET(ev->fd, &r_fds); + maxfd = MAX(ev->fd, maxfd); + } + if (ev->flags & EVENT_FD_WRITE) { + FD_SET(ev->fd, &w_fds); + maxfd = MAX(ev->fd, maxfd); + } + } + + /* Call select */ + + selret = sys_select(maxfd + 1, &r_fds, &w_fds, NULL, &timeout); + + if (selret == 0) { + goto no_fds_ready; + } + + if (selret == -1) { + if (errno == EINTR) { + goto no_fds_ready; + } + + /* Select error, something is badly wrong */ + + perror("select"); + exit(1); + } + + /* selret > 0 */ + + ev = fd_events; + while (ev != NULL) { + struct fd_event *next = ev->next; + int flags = 0; + if (FD_ISSET(ev->fd, &r_fds)) + flags |= EVENT_FD_READ; + if (FD_ISSET(ev->fd, &w_fds)) + flags |= EVENT_FD_WRITE; + if (flags) + ev->handler(ev, flags); + ev = next; + } + + if (FD_ISSET(listen_sock, &r_fds)) { + while (winbindd_num_clients() > + WINBINDD_MAX_SIMULTANEOUS_CLIENTS - 1) { + DEBUG(5,("winbindd: Exceeding %d client " + "connections, removing idle " + "connection.\n", + WINBINDD_MAX_SIMULTANEOUS_CLIENTS)); + if (!remove_idle_client()) { + DEBUG(0,("winbindd: Exceeding %d " + "client connections, no idle " + "connection found\n", + WINBINDD_MAX_SIMULTANEOUS_CLIENTS)); + break; + } + } + /* new, non-privileged connection */ + new_connection(listen_sock, False); + } + + if (FD_ISSET(listen_priv_sock, &r_fds)) { + while (winbindd_num_clients() > + WINBINDD_MAX_SIMULTANEOUS_CLIENTS - 1) { + DEBUG(5,("winbindd: Exceeding %d client " + "connections, removing idle " + "connection.\n", + WINBINDD_MAX_SIMULTANEOUS_CLIENTS)); + if (!remove_idle_client()) { + DEBUG(0,("winbindd: Exceeding %d " + "client connections, no idle " + "connection found\n", + WINBINDD_MAX_SIMULTANEOUS_CLIENTS)); + break; + } + } + /* new, privileged connection */ + new_connection(listen_priv_sock, True); + } + + no_fds_ready: + +#if 0 + winbindd_check_cache_size(time(NULL)); +#endif + + /* Check signal handling things */ + + if (do_sigterm) + terminate(); + + if (do_sighup) { + + DEBUG(3, ("got SIGHUP\n")); + + flush_caches(); + reload_services_file(); + + do_sighup = False; + } + + if (do_sigusr2) { + print_winbindd_status(); + do_sigusr2 = False; + } + + if (do_sigchld) { + pid_t pid; + + do_sigchld = False; + + while ((pid = sys_waitpid(-1, NULL, WNOHANG)) > 0) { + winbind_child_died(pid); + } + } + + + return winbindd_num_clients(); +} + +static void winbindd_process_loop(enum smb_server_mode server_mode) +{ + int idle_timeout_sec; + struct timeval starttime; + int listen_public, listen_priv; + + errno = 0; + if (!winbindd_init_sockets(&listen_public, &listen_priv, + &idle_timeout_sec)) { + terminate(); + } + + starttime = timeval_current(); + + if (listen_public == -1 || listen_priv == -1) { + DEBUG(0, ("failed to open winbindd pipes: %s\n", + errno ? strerror(errno) : "unknown error")); + terminate(); + } + + for (;;) { + TALLOC_CTX *frame = talloc_stackframe(); + int clients = process_loop(listen_public, listen_priv); + + /* Don't bother figuring out the idle time if we won't be + * timing out anyway. + */ + if (idle_timeout_sec < 0) { + TALLOC_FREE(frame); + continue; + } + + if (clients == 0 && server_mode == SERVER_MODE_FOREGROUND) { + struct timeval now; + + now = timeval_current(); + if (timeval_elapsed2(&starttime, &now) > + (double)idle_timeout_sec) { + DEBUG(0, ("idle for %d secs, exitting\n", + idle_timeout_sec)); + terminate(); + } + } else { + starttime = timeval_current(); + } + TALLOC_FREE(frame); + } +} + +/* Main function */ + +int main(int argc, char **argv, char **envp) +{ + pstring logfile; + BOOL log_stdout = False; + BOOL no_process_group = False; + + enum smb_server_mode server_mode = SERVER_MODE_DAEMON; + + struct poptOption long_options[] = { + POPT_AUTOHELP + { "stdout", 'S', POPT_ARG_VAL, &log_stdout, True, "Log to stdout" }, + { "foreground", 'F', POPT_ARG_VAL, &server_mode, SERVER_MODE_FOREGROUND, "Daemon in foreground mode" }, + { "no-process-group", 0, POPT_ARG_VAL, &no_process_group, True, "Don't create a new process group" }, + { "daemon", 'D', POPT_ARG_VAL, &server_mode, SERVER_MODE_DAEMON, "Become a daemon (default)" }, + { "interactive", 'i', POPT_ARG_VAL, &server_mode, SERVER_MODE_INTERACTIVE, "Interactive mode" }, + { "no-caching", 'n', POPT_ARG_VAL, &opt_nocache, True, "Disable caching" }, + POPT_COMMON_SAMBA + POPT_TABLEEND + }; + poptContext pc; + int opt; + + /* glibc (?) likes to print "User defined signal 1" and exit if a + SIGUSR[12] is received before a handler is installed */ + + CatchSignal(SIGUSR1, SIG_IGN); + CatchSignal(SIGUSR2, SIG_IGN); + + fault_setup((void (*)(void *))fault_quit ); + dump_core_setup("winbindd"); + + load_case_tables(); + + /* Initialise for running in non-root mode */ + + sec_init(); + + set_remote_machine_name("winbindd", False); + + /* Set environment variable so we don't recursively call ourselves. + This may also be useful interactively. */ + + if ( !winbind_off() ) { + DEBUG(0,("Failed to disable recusive winbindd calls. Exiting.\n")); + exit(1); + } + + /* Initialise samba/rpc client stuff */ + + pc = poptGetContext("winbindd", argc, (const char **)argv, long_options, 0); + + while ((opt = poptGetNextOpt(pc)) != -1) { + switch (opt) { + default: + d_fprintf(stderr, "\nInvalid option %s: %s\n\n", + poptBadOption(pc, 0), poptStrerror(opt)); + poptPrintUsage(pc, stderr, 0); + exit(1); + } + } + + if (server_mode == SERVER_MODE_INTERACTIVE) { + log_stdout = True; + if (DEBUGLEVEL >= 9) { + talloc_enable_leak_report(); + } + } + + if (log_stdout && server_mode == SERVER_MODE_DAEMON) { + printf("Can't log to stdout (-S) unless daemon is in foreground (-F) or interactive (-i)\n"); + poptPrintUsage(pc, stderr, 0); + exit(1); + } + + poptFreeContext(pc); + + if (!override_logfile) { + pstr_sprintf(logfile, "%s/log.winbindd", dyn_LOGFILEBASE); + lp_set_logfile(logfile); + } + setup_logging("winbindd", log_stdout); + reopen_logs(); + + DEBUG(1, ("winbindd version %s started.\n%s\n", + SAMBA_VERSION_STRING, + COPYRIGHT_STARTUP_MESSAGE) ); + + if (!reload_services_file()) { + DEBUG(0, ("error opening config file\n")); + exit(1); + } + + if (!directory_exist(lp_lockdir(), NULL)) { + mkdir(lp_lockdir(), 0755); + } + + /* Setup names. */ + + if (!init_names()) + exit(1); + + load_interfaces(); + + if (!secrets_init()) { + + DEBUG(0,("Could not initialize domain trust account secrets. Giving up\n")); + return False; + } + + /* Enable netbios namecache */ + + namecache_enable(); + + /* Winbind daemon initialisation */ + + if ( ! NT_STATUS_IS_OK(idmap_init_cache()) ) { + DEBUG(1, ("Could not init idmap cache!\n")); + } + + /* Unblock all signals we are interested in as they may have been + blocked by the parent process. */ + + BlockSignals(False, SIGINT); + BlockSignals(False, SIGQUIT); + BlockSignals(False, SIGTERM); + BlockSignals(False, SIGUSR1); + BlockSignals(False, SIGUSR2); + BlockSignals(False, SIGHUP); + BlockSignals(False, SIGCHLD); + + /* Setup signal handlers */ + + CatchSignal(SIGINT, termination_handler); /* Exit on these sigs */ + CatchSignal(SIGQUIT, termination_handler); + CatchSignal(SIGTERM, termination_handler); + CatchSignal(SIGCHLD, sigchld_handler); + + CatchSignal(SIGPIPE, SIG_IGN); /* Ignore sigpipe */ + + CatchSignal(SIGUSR2, sigusr2_handler); /* Debugging sigs */ + CatchSignal(SIGHUP, sighup_handler); + + if (server_mode == SERVER_MODE_DAEMON) { + DEBUG( 3, ( "Becoming a daemon.\n" ) ); + become_daemon(True, no_process_group); + } else if (server_mode == SERVER_MODE_FOREGROUND) { + become_daemon(False, no_process_group); + } + + pidfile_create("winbindd"); + + /* Ensure all cache and idmap caches are consistent + before we startup. */ + + if (winbindd_validate_cache()) { + /* We have a bad cache, but luckily we + just deleted it. Restart ourselves */ + int i; + /* Ensure we have no open low fd's. */ + for (i = 3; i < 100; i++) { + close(i); + } + return execve(argv[0], argv, envp); + } + +#if HAVE_SETPGID + /* + * If we're interactive we want to set our own process group for + * signal management. + */ + if (server_mode == SERVER_MODE_INTERACTIVE && !no_process_group) { + setpgid( (pid_t)0, (pid_t)0); + } +#endif + + TimeInit(); + + /* Initialise messaging system */ + + if (winbind_messaging_context() == NULL) { + DEBUG(0, ("unable to initialize messaging system\n")); + exit(1); + } + + /* Initialize cache (ensure version is correct). */ + if (!initialize_winbindd_cache()) { + exit(1); + } + + /* React on 'smbcontrol winbindd reload-config' in the same way + as to SIGHUP signal */ + messaging_register(winbind_messaging_context(), NULL, + MSG_SMB_CONF_UPDATED, msg_reload_services); + messaging_register(winbind_messaging_context(), NULL, + MSG_SHUTDOWN, msg_shutdown); + + /* Handle online/offline messages. */ + messaging_register(winbind_messaging_context(), NULL, + MSG_WINBIND_OFFLINE, winbind_msg_offline); + messaging_register(winbind_messaging_context(), NULL, + MSG_WINBIND_ONLINE, winbind_msg_online); + messaging_register(winbind_messaging_context(), NULL, + MSG_WINBIND_ONLINESTATUS, winbind_msg_onlinestatus); + + messaging_register(winbind_messaging_context(), NULL, + MSG_DUMP_EVENT_LIST, winbind_msg_dump_event_list); + + messaging_register(winbind_messaging_context(), NULL, + MSG_WINBIND_VALIDATE_CACHE, + winbind_msg_validate_cache); + + netsamlogon_cache_init(); /* Non-critical */ + + /* clear the cached list of trusted domains */ + + wcache_tdc_clear(); + + if (!init_domain_list()) { + DEBUG(0,("unable to initalize domain list\n")); + exit(1); + } + + init_idmap_child(); + init_locator_child(); + + smb_nscd_flush_user_cache(); + smb_nscd_flush_group_cache(); + + /* Loop waiting for requests */ + winbindd_process_loop(server_mode); + + return 0; +} diff --git a/source3/winbindd/winbindd.h b/source3/winbindd/winbindd.h new file mode 100644 index 0000000000..e7119c332f --- /dev/null +++ b/source3/winbindd/winbindd.h @@ -0,0 +1,365 @@ +/* + Unix SMB/CIFS implementation. + + Winbind daemon for ntdom nss module + + Copyright (C) Tim Potter 2000 + Copyright (C) Jim McDonough 2003 + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 3 of the License, or (at your option) any later version. + + This library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program. If not, see . +*/ + +#ifndef _WINBINDD_H +#define _WINBINDD_H + +#include "nsswitch/winbind_struct_protocol.h" + +#ifdef HAVE_LIBNSCD +#include +#endif + +#ifdef HAVE_SYS_MMAN_H +#include +#endif + +#undef DBGC_CLASS +#define DBGC_CLASS DBGC_WINBIND + +#define WB_REPLACE_CHAR '_' + +/* bits for fd_event.flags */ +#define EVENT_FD_READ 1 +#define EVENT_FD_WRITE 2 + +struct fd_event { + struct fd_event *next, *prev; + int fd; + int flags; /* see EVENT_FD_* flags */ + void (*handler)(struct fd_event *fde, int flags); + void *data; + size_t length, done; + void (*finished)(void *private_data, BOOL success); + void *private_data; +}; + +struct sid_ctr { + DOM_SID *sid; + BOOL finished; + const char *domain; + const char *name; + enum lsa_SidType type; +}; + +struct winbindd_cli_state { + struct winbindd_cli_state *prev, *next; /* Linked list pointers */ + int sock; /* Open socket from client */ + struct fd_event fd_event; + pid_t pid; /* pid of client */ + BOOL finished; /* Can delete from list */ + BOOL write_extra_data; /* Write extra_data field */ + time_t last_access; /* Time of last access (read or write) */ + BOOL privileged; /* Is the client 'privileged' */ + + TALLOC_CTX *mem_ctx; /* memory per request */ + struct winbindd_request request; /* Request from client */ + struct winbindd_response response; /* Respose to client */ + BOOL getpwent_initialized; /* Has getpwent_state been + * initialized? */ + BOOL getgrent_initialized; /* Has getgrent_state been + * initialized? */ + struct getent_state *getpwent_state; /* State for getpwent() */ + struct getent_state *getgrent_state; /* State for getgrent() */ +}; + +/* State between get{pw,gr}ent() calls */ + +struct getent_state { + struct getent_state *prev, *next; + void *sam_entries; + uint32 sam_entry_index, num_sam_entries; + BOOL got_sam_entries; + fstring domain_name; +}; + +/* Storage for cached getpwent() user entries */ + +struct getpwent_user { + fstring name; /* Account name */ + fstring gecos; /* User information */ + fstring homedir; /* User Home Directory */ + fstring shell; /* User Login Shell */ + DOM_SID user_sid; /* NT user and primary group SIDs */ + DOM_SID group_sid; +}; + +/* Server state structure */ + +typedef struct { + char *acct_name; + char *full_name; + char *homedir; + char *shell; + gid_t primary_gid; /* allow the nss_info + backend to set the primary group */ + DOM_SID user_sid; /* NT user and primary group SIDs */ + DOM_SID group_sid; +} WINBIND_USERINFO; + +/* Our connection to the DC */ + +struct winbindd_cm_conn { + struct cli_state *cli; + + struct rpc_pipe_client *samr_pipe; + POLICY_HND sam_connect_handle, sam_domain_handle; + + struct rpc_pipe_client *lsa_pipe; + POLICY_HND lsa_policy; + + struct rpc_pipe_client *netlogon_pipe; +}; + +struct winbindd_async_request; + +/* Async child */ + +struct winbindd_child { + struct winbindd_child *next, *prev; + + pid_t pid; + struct winbindd_domain *domain; + pstring logfilename; + + struct fd_event event; + struct timed_event *lockout_policy_event; + struct winbindd_async_request *requests; +}; + +/* Structures to hold per domain information */ + +struct winbindd_domain { + fstring name; /* Domain name (NetBIOS) */ + fstring alt_name; /* alt Domain name, if any (FQDN for ADS) */ + fstring forest_name; /* Name of the AD forest we're in */ + DOM_SID sid; /* SID for this domain */ + uint32 domain_flags; /* Domain flags from rpc_ds.h */ + uint32 domain_type; /* Domain type from rpc_ds.h */ + uint32 domain_trust_attribs; /* Trust attribs from rpc_ds.h */ + BOOL initialized; /* Did we already ask for the domain mode? */ + BOOL native_mode; /* is this a win2k domain in native mode ? */ + BOOL active_directory; /* is this a win2k active directory ? */ + BOOL primary; /* is this our primary domain ? */ + BOOL internal; /* BUILTIN and member SAM */ + BOOL online; /* is this domain available ? */ + time_t startup_time; /* When we set "startup" true. */ + BOOL startup; /* are we in the first 30 seconds after startup_time ? */ + + BOOL can_do_samlogon_ex; /* Due to the lack of finer control what type + * of DC we have, let us try to do a + * credential-chain less samlogon_ex call + * with AD and schannel. If this fails with + * DCERPC_FAULT_OP_RNG_ERROR, then set this + * to False. This variable is around so that + * we don't have to try _ex every time. */ + + /* Lookup methods for this domain (LDAP or RPC) */ + struct winbindd_methods *methods; + + /* the backend methods are used by the cache layer to find the right + backend */ + struct winbindd_methods *backend; + + /* Private data for the backends (used for connection cache) */ + + void *private_data; + + /* A working DC */ + fstring dcname; + struct sockaddr_in dcaddr; + + /* Sequence number stuff */ + + time_t last_seq_check; + uint32 sequence_number; + NTSTATUS last_status; + + /* The smb connection */ + + struct winbindd_cm_conn conn; + + /* The child pid we're talking to */ + + struct winbindd_child child; + + /* Callback we use to try put us back online. */ + + uint32 check_online_timeout; + struct timed_event *check_online_event; + + /* Linked list info */ + + struct winbindd_domain *prev, *next; +}; + +/* per-domain methods. This is how LDAP vs RPC is selected + */ +struct winbindd_methods { + /* does this backend provide a consistent view of the data? (ie. is the primary group + always correct) */ + BOOL consistent; + + /* get a list of users, returning a WINBIND_USERINFO for each one */ + NTSTATUS (*query_user_list)(struct winbindd_domain *domain, + TALLOC_CTX *mem_ctx, + uint32 *num_entries, + WINBIND_USERINFO **info); + + /* get a list of domain groups */ + NTSTATUS (*enum_dom_groups)(struct winbindd_domain *domain, + TALLOC_CTX *mem_ctx, + uint32 *num_entries, + struct acct_info **info); + + /* get a list of domain local groups */ + NTSTATUS (*enum_local_groups)(struct winbindd_domain *domain, + TALLOC_CTX *mem_ctx, + uint32 *num_entries, + struct acct_info **info); + + /* convert one user or group name to a sid */ + NTSTATUS (*name_to_sid)(struct winbindd_domain *domain, + TALLOC_CTX *mem_ctx, + enum winbindd_cmd orig_cmd, + const char *domain_name, + const char *name, + DOM_SID *sid, + enum lsa_SidType *type); + + /* convert a sid to a user or group name */ + NTSTATUS (*sid_to_name)(struct winbindd_domain *domain, + TALLOC_CTX *mem_ctx, + const DOM_SID *sid, + char **domain_name, + char **name, + enum lsa_SidType *type); + + NTSTATUS (*rids_to_names)(struct winbindd_domain *domain, + TALLOC_CTX *mem_ctx, + const DOM_SID *domain_sid, + uint32 *rids, + size_t num_rids, + char **domain_name, + char ***names, + enum lsa_SidType **types); + + /* lookup user info for a given SID */ + NTSTATUS (*query_user)(struct winbindd_domain *domain, + TALLOC_CTX *mem_ctx, + const DOM_SID *user_sid, + WINBIND_USERINFO *user_info); + + /* lookup all groups that a user is a member of. The backend + can also choose to lookup by username or rid for this + function */ + NTSTATUS (*lookup_usergroups)(struct winbindd_domain *domain, + TALLOC_CTX *mem_ctx, + const DOM_SID *user_sid, + uint32 *num_groups, DOM_SID **user_gids); + + /* Lookup all aliases that the sids delivered are member of. This is + * to implement 'domain local groups' correctly */ + NTSTATUS (*lookup_useraliases)(struct winbindd_domain *domain, + TALLOC_CTX *mem_ctx, + uint32 num_sids, + const DOM_SID *sids, + uint32 *num_aliases, + uint32 **alias_rids); + + /* find all members of the group with the specified group_rid */ + NTSTATUS (*lookup_groupmem)(struct winbindd_domain *domain, + TALLOC_CTX *mem_ctx, + const DOM_SID *group_sid, + uint32 *num_names, + DOM_SID **sid_mem, char ***names, + uint32 **name_types); + + /* return the current global sequence number */ + NTSTATUS (*sequence_number)(struct winbindd_domain *domain, uint32 *seq); + + /* return the lockout policy */ + NTSTATUS (*lockout_policy)(struct winbindd_domain *domain, + TALLOC_CTX *mem_ctx, + SAM_UNK_INFO_12 *lockout_policy); + + /* return the lockout policy */ + NTSTATUS (*password_policy)(struct winbindd_domain *domain, + TALLOC_CTX *mem_ctx, + SAM_UNK_INFO_1 *password_policy); + + /* enumerate trusted domains */ + NTSTATUS (*trusted_domains)(struct winbindd_domain *domain, + TALLOC_CTX *mem_ctx, + uint32 *num_domains, + char ***names, + char ***alt_names, + DOM_SID **dom_sids); +}; + +/* Used to glue a policy handle and cli_state together */ + +typedef struct { + struct cli_state *cli; + POLICY_HND pol; +} CLI_POLICY_HND; + +/* Filled out by IDMAP backends */ +struct winbindd_idmap_methods { + /* Called when backend is first loaded */ + BOOL (*init)(void); + + BOOL (*get_sid_from_uid)(uid_t uid, DOM_SID *sid); + BOOL (*get_sid_from_gid)(gid_t gid, DOM_SID *sid); + + BOOL (*get_uid_from_sid)(DOM_SID *sid, uid_t *uid); + BOOL (*get_gid_from_sid)(DOM_SID *sid, gid_t *gid); + + /* Called when backend is unloaded */ + BOOL (*close)(void); + /* Called to dump backend status */ + void (*status)(void); +}; + +/* Data structures for dealing with the trusted domain cache */ + +struct winbindd_tdc_domain { + const char *domain_name; + const char *dns_name; + DOM_SID sid; + uint32 trust_flags; + uint32 trust_attribs; + uint32 trust_type; +}; + + +#include "winbindd/winbindd_proto.h" + +#define WINBINDD_ESTABLISH_LOOP 30 +#define WINBINDD_RESCAN_FREQ lp_winbind_cache_time() +#define WINBINDD_PAM_AUTH_KRB5_RENEW_TIME 2592000 /* one month */ +#define DOM_SEQUENCE_NONE ((uint32)-1) + +#define IS_DOMAIN_OFFLINE(x) ( lp_winbind_offline_logon() && \ + ( get_global_winbindd_state_offline() \ + || !(x)->online ) ) +#endif /* _WINBINDD_H */ diff --git a/source3/winbindd/winbindd_ads.c b/source3/winbindd/winbindd_ads.c new file mode 100644 index 0000000000..65cc00bb97 --- /dev/null +++ b/source3/winbindd/winbindd_ads.c @@ -0,0 +1,1312 @@ +/* + Unix SMB/CIFS implementation. + + Winbind ADS backend functions + + Copyright (C) Andrew Tridgell 2001 + Copyright (C) Andrew Bartlett 2003 + Copyright (C) Gerald (Jerry) Carter 2004 + + 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 3 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, see . +*/ + +#include "includes.h" +#include "winbindd.h" + +#ifdef HAVE_ADS + +#undef DBGC_CLASS +#define DBGC_CLASS DBGC_WINBIND + +extern struct winbindd_methods reconnect_methods; + +/* + return our ads connections structure for a domain. We keep the connection + open to make things faster +*/ +static ADS_STRUCT *ads_cached_connection(struct winbindd_domain *domain) +{ + ADS_STRUCT *ads; + ADS_STATUS status; + fstring dc_name; + struct in_addr dc_ip; + + DEBUG(10,("ads_cached_connection\n")); + + if (domain->private_data) { + + time_t expire; + time_t now = time(NULL); + + /* check for a valid structure */ + ads = (ADS_STRUCT *)domain->private_data; + + expire = MIN(ads->auth.tgt_expire, ads->auth.tgs_expire); + + DEBUG(7, ("Current tickets expire in %d seconds (at %d, time is now %d)\n", + (uint32)expire-(uint32)now, (uint32) expire, (uint32) now)); + + if ( ads->config.realm && (expire > now)) { + return ads; + } else { + /* we own this ADS_STRUCT so make sure it goes away */ + DEBUG(7,("Deleting expired krb5 credential cache\n")); + ads->is_mine = True; + ads_destroy( &ads ); + ads_kdestroy("MEMORY:winbind_ccache"); + domain->private_data = NULL; + } + } + + /* we don't want this to affect the users ccache */ + setenv("KRB5CCNAME", "MEMORY:winbind_ccache", 1); + + ads = ads_init(domain->alt_name, domain->name, NULL); + if (!ads) { + DEBUG(1,("ads_init for domain %s failed\n", domain->name)); + return NULL; + } + + /* the machine acct password might have change - fetch it every time */ + + SAFE_FREE(ads->auth.password); + SAFE_FREE(ads->auth.realm); + + if ( IS_DC ) { + DOM_SID sid; + time_t last_set_time; + + if ( !pdb_get_trusteddom_pw( domain->name, &ads->auth.password, &sid, &last_set_time ) ) { + ads_destroy( &ads ); + return NULL; + } + ads->auth.realm = SMB_STRDUP( ads->server.realm ); + strupper_m( ads->auth.realm ); + } + else { + struct winbindd_domain *our_domain = domain; + + ads->auth.password = secrets_fetch_machine_password(lp_workgroup(), NULL, NULL); + + /* always give preference to the alt_name in our + primary domain if possible */ + + if ( !domain->primary ) + our_domain = find_our_domain(); + + if ( our_domain->alt_name[0] != '\0' ) { + ads->auth.realm = SMB_STRDUP( our_domain->alt_name ); + strupper_m( ads->auth.realm ); + } + else + ads->auth.realm = SMB_STRDUP( lp_realm() ); + } + + ads->auth.renewable = WINBINDD_PAM_AUTH_KRB5_RENEW_TIME; + + /* Setup the server affinity cache. We don't reaally care + about the name. Just setup affinity and the KRB5_CONFIG + file. */ + + get_dc_name( ads->server.workgroup, ads->server.realm, dc_name, &dc_ip ); + + status = ads_connect(ads); + if (!ADS_ERR_OK(status) || !ads->config.realm) { + DEBUG(1,("ads_connect for domain %s failed: %s\n", + domain->name, ads_errstr(status))); + ads_destroy(&ads); + + /* if we get ECONNREFUSED then it might be a NT4 + server, fall back to MSRPC */ + if (status.error_type == ENUM_ADS_ERROR_SYSTEM && + status.err.rc == ECONNREFUSED) { + /* 'reconnect_methods' is the MS-RPC backend. */ + DEBUG(1,("Trying MSRPC methods\n")); + domain->backend = &reconnect_methods; + } + return NULL; + } + + /* set the flag that says we don't own the memory even + though we do so that ads_destroy() won't destroy the + structure we pass back by reference */ + + ads->is_mine = False; + + domain->private_data = (void *)ads; + return ads; +} + + +/* Query display info for a realm. This is the basic user list fn */ +static NTSTATUS query_user_list(struct winbindd_domain *domain, + TALLOC_CTX *mem_ctx, + uint32 *num_entries, + WINBIND_USERINFO **info) +{ + ADS_STRUCT *ads = NULL; + const char *attrs[] = { "*", NULL }; + int i, count; + ADS_STATUS rc; + LDAPMessage *res = NULL; + LDAPMessage *msg = NULL; + NTSTATUS status = NT_STATUS_UNSUCCESSFUL; + + *num_entries = 0; + + DEBUG(3,("ads: query_user_list\n")); + + if ( !winbindd_can_contact_domain( domain ) ) { + DEBUG(10,("query_user_list: No incoming trust for domain %s\n", + domain->name)); + return NT_STATUS_OK; + } + + ads = ads_cached_connection(domain); + + if (!ads) { + domain->last_status = NT_STATUS_SERVER_DISABLED; + goto done; + } + + rc = ads_search_retry(ads, &res, "(objectCategory=user)", attrs); + if (!ADS_ERR_OK(rc) || !res) { + DEBUG(1,("query_user_list ads_search: %s\n", ads_errstr(rc))); + goto done; + } + + count = ads_count_replies(ads, res); + if (count == 0) { + DEBUG(1,("query_user_list: No users found\n")); + goto done; + } + + (*info) = TALLOC_ZERO_ARRAY(mem_ctx, WINBIND_USERINFO, count); + if (!*info) { + status = NT_STATUS_NO_MEMORY; + goto done; + } + + i = 0; + + for (msg = ads_first_entry(ads, res); msg; msg = ads_next_entry(ads, msg)) { + char *name, *gecos = NULL; + char *homedir = NULL; + char *shell = NULL; + uint32 group; + uint32 atype; + DOM_SID user_sid; + gid_t primary_gid = (gid_t)-1; + + if (!ads_pull_uint32(ads, msg, "sAMAccountType", &atype) || + ads_atype_map(atype) != SID_NAME_USER) { + DEBUG(1,("Not a user account? atype=0x%x\n", atype)); + continue; + } + + name = ads_pull_username(ads, mem_ctx, msg); + + if ( ads_pull_sid( ads, msg, "objectSid", &user_sid ) ) { + status = nss_get_info_cached( domain, &user_sid, mem_ctx, + ads, msg, &homedir, &shell, &gecos, + &primary_gid ); + } + + if (gecos == NULL) { + gecos = ads_pull_string(ads, mem_ctx, msg, "name"); + } + + if (!ads_pull_sid(ads, msg, "objectSid", + &(*info)[i].user_sid)) { + DEBUG(1,("No sid for %s !?\n", name)); + continue; + } + if (!ads_pull_uint32(ads, msg, "primaryGroupID", &group)) { + DEBUG(1,("No primary group for %s !?\n", name)); + continue; + } + + (*info)[i].acct_name = name; + (*info)[i].full_name = gecos; + (*info)[i].homedir = homedir; + (*info)[i].shell = shell; + (*info)[i].primary_gid = primary_gid; + sid_compose(&(*info)[i].group_sid, &domain->sid, group); + i++; + } + + (*num_entries) = i; + status = NT_STATUS_OK; + + DEBUG(3,("ads query_user_list gave %d entries\n", (*num_entries))); + +done: + if (res) + ads_msgfree(ads, res); + + return status; +} + +/* list all domain groups */ +static NTSTATUS enum_dom_groups(struct winbindd_domain *domain, + TALLOC_CTX *mem_ctx, + uint32 *num_entries, + struct acct_info **info) +{ + ADS_STRUCT *ads = NULL; + const char *attrs[] = {"userPrincipalName", "sAMAccountName", + "name", "objectSid", NULL}; + int i, count; + ADS_STATUS rc; + LDAPMessage *res = NULL; + LDAPMessage *msg = NULL; + NTSTATUS status = NT_STATUS_UNSUCCESSFUL; + const char *filter; + BOOL enum_dom_local_groups = False; + + *num_entries = 0; + + DEBUG(3,("ads: enum_dom_groups\n")); + + if ( !winbindd_can_contact_domain( domain ) ) { + DEBUG(10,("enum_dom_groups: No incoming trust for domain %s\n", + domain->name)); + return NT_STATUS_OK; + } + + /* only grab domain local groups for our domain */ + if ( domain->active_directory && strequal(lp_realm(), domain->alt_name) ) { + enum_dom_local_groups = True; + } + + /* Workaround ADS LDAP bug present in MS W2K3 SP0 and W2K SP4 w/o + * rollup-fixes: + * + * According to Section 5.1(4) of RFC 2251 if a value of a type is it's + * default value, it MUST be absent. In case of extensible matching the + * "dnattr" boolean defaults to FALSE and so it must be only be present + * when set to TRUE. + * + * When it is set to FALSE and the OpenLDAP lib (correctly) encodes a + * filter using bitwise matching rule then the buggy AD fails to decode + * the extensible match. As a workaround set it to TRUE and thereby add + * the dnAttributes "dn" field to cope with those older AD versions. + * It should not harm and won't put any additional load on the AD since + * none of the dn components have a bitmask-attribute. + * + * Thanks to Ralf Haferkamp for input and testing - Guenther */ + + filter = talloc_asprintf(mem_ctx, "(&(objectCategory=group)(&(groupType:dn:%s:=%d)(!(groupType:dn:%s:=%d))))", + ADS_LDAP_MATCHING_RULE_BIT_AND, GROUP_TYPE_SECURITY_ENABLED, + ADS_LDAP_MATCHING_RULE_BIT_AND, + enum_dom_local_groups ? GROUP_TYPE_BUILTIN_LOCAL_GROUP : GROUP_TYPE_RESOURCE_GROUP); + + if (filter == NULL) { + status = NT_STATUS_NO_MEMORY; + goto done; + } + + ads = ads_cached_connection(domain); + + if (!ads) { + domain->last_status = NT_STATUS_SERVER_DISABLED; + goto done; + } + + rc = ads_search_retry(ads, &res, filter, attrs); + if (!ADS_ERR_OK(rc) || !res) { + DEBUG(1,("enum_dom_groups ads_search: %s\n", ads_errstr(rc))); + goto done; + } + + count = ads_count_replies(ads, res); + if (count == 0) { + DEBUG(1,("enum_dom_groups: No groups found\n")); + goto done; + } + + (*info) = TALLOC_ZERO_ARRAY(mem_ctx, struct acct_info, count); + if (!*info) { + status = NT_STATUS_NO_MEMORY; + goto done; + } + + i = 0; + + for (msg = ads_first_entry(ads, res); msg; msg = ads_next_entry(ads, msg)) { + char *name, *gecos; + DOM_SID sid; + uint32 rid; + + name = ads_pull_username(ads, mem_ctx, msg); + gecos = ads_pull_string(ads, mem_ctx, msg, "name"); + if (!ads_pull_sid(ads, msg, "objectSid", &sid)) { + DEBUG(1,("No sid for %s !?\n", name)); + continue; + } + + if (!sid_peek_check_rid(&domain->sid, &sid, &rid)) { + DEBUG(1,("No rid for %s !?\n", name)); + continue; + } + + fstrcpy((*info)[i].acct_name, name); + fstrcpy((*info)[i].acct_desc, gecos); + (*info)[i].rid = rid; + i++; + } + + (*num_entries) = i; + + status = NT_STATUS_OK; + + DEBUG(3,("ads enum_dom_groups gave %d entries\n", (*num_entries))); + +done: + if (res) + ads_msgfree(ads, res); + + return status; +} + +/* list all domain local groups */ +static NTSTATUS enum_local_groups(struct winbindd_domain *domain, + TALLOC_CTX *mem_ctx, + uint32 *num_entries, + struct acct_info **info) +{ + /* + * This is a stub function only as we returned the domain + * local groups in enum_dom_groups() if the domain->native field + * was true. This is a simple performance optimization when + * using LDAP. + * + * if we ever need to enumerate domain local groups separately, + * then this the optimization in enum_dom_groups() will need + * to be split out + */ + *num_entries = 0; + + return NT_STATUS_OK; +} + +/* If you are looking for "dn_lookup": Yes, it used to be here! + * It has gone now since it was a major speed bottleneck in + * lookup_groupmem (its only use). It has been replaced by + * an rpc lookup sids call... R.I.P. */ + +/* Lookup user information from a rid */ +static NTSTATUS query_user(struct winbindd_domain *domain, + TALLOC_CTX *mem_ctx, + const DOM_SID *sid, + WINBIND_USERINFO *info) +{ + ADS_STRUCT *ads = NULL; + const char *attrs[] = { "*", NULL }; + ADS_STATUS rc; + int count; + LDAPMessage *msg = NULL; + char *ldap_exp; + char *sidstr; + uint32 group_rid; + NTSTATUS status = NT_STATUS_UNSUCCESSFUL; + NET_USER_INFO_3 *user; + + DEBUG(3,("ads: query_user\n")); + + info->homedir = NULL; + info->shell = NULL; + info->primary_gid = (gid_t)-1; + + /* try netsamlogon cache first */ + + if ( (user = netsamlogon_cache_get( mem_ctx, sid )) != NULL ) + { + + DEBUG(5,("query_user: Cache lookup succeeded for %s\n", + sid_string_static(sid))); + + sid_compose(&info->user_sid, &domain->sid, user->user_rid); + sid_compose(&info->group_sid, &domain->sid, user->group_rid); + + info->acct_name = unistr2_tdup(mem_ctx, &user->uni_user_name); + info->full_name = unistr2_tdup(mem_ctx, &user->uni_full_name); + + nss_get_info_cached( domain, sid, mem_ctx, NULL, NULL, + &info->homedir, &info->shell, &info->full_name, + &info->primary_gid ); + + TALLOC_FREE(user); + + return NT_STATUS_OK; + } + + if ( !winbindd_can_contact_domain(domain)) { + DEBUG(8,("query_user: No incoming trust from domain %s\n", + domain->name)); + + /* We still need to generate some basic information + about the user even if we cannot contact the + domain. Most of this stuff we can deduce. */ + + sid_copy( &info->user_sid, sid ); + + /* Assume "Domain Users" for the primary group */ + + sid_compose(&info->group_sid, &domain->sid, DOMAIN_GROUP_RID_USERS ); + + /* Try to fill in what the nss_info backend can do */ + + nss_get_info_cached( domain, sid, mem_ctx, NULL, NULL, + &info->homedir, &info->shell, &info->full_name, + &info->primary_gid ); + + status = NT_STATUS_OK; + goto done; + } + + /* no cache...do the query */ + + if ( (ads = ads_cached_connection(domain)) == NULL ) { + domain->last_status = NT_STATUS_SERVER_DISABLED; + goto done; + } + + sidstr = sid_binstring(sid); + asprintf(&ldap_exp, "(objectSid=%s)", sidstr); + rc = ads_search_retry(ads, &msg, ldap_exp, attrs); + free(ldap_exp); + free(sidstr); + if (!ADS_ERR_OK(rc) || !msg) { + DEBUG(1,("query_user(sid=%s) ads_search: %s\n", + sid_string_static(sid), ads_errstr(rc))); + goto done; + } + + count = ads_count_replies(ads, msg); + if (count != 1) { + DEBUG(1,("query_user(sid=%s): Not found\n", + sid_string_static(sid))); + goto done; + } + + info->acct_name = ads_pull_username(ads, mem_ctx, msg); + + nss_get_info_cached( domain, sid, mem_ctx, ads, msg, + &info->homedir, &info->shell, &info->full_name, + &info->primary_gid ); + + if (info->full_name == NULL) { + info->full_name = ads_pull_string(ads, mem_ctx, msg, "name"); + } + + if (!ads_pull_uint32(ads, msg, "primaryGroupID", &group_rid)) { + DEBUG(1,("No primary group for %s !?\n", + sid_string_static(sid))); + goto done; + } + + sid_copy(&info->user_sid, sid); + sid_compose(&info->group_sid, &domain->sid, group_rid); + + status = NT_STATUS_OK; + + DEBUG(3,("ads query_user gave %s\n", info->acct_name)); +done: + if (msg) + ads_msgfree(ads, msg); + + return status; +} + +/* Lookup groups a user is a member of - alternate method, for when + tokenGroups are not available. */ +static NTSTATUS lookup_usergroups_member(struct winbindd_domain *domain, + TALLOC_CTX *mem_ctx, + const char *user_dn, + DOM_SID *primary_group, + size_t *p_num_groups, DOM_SID **user_sids) +{ + ADS_STATUS rc; + NTSTATUS status = NT_STATUS_UNSUCCESSFUL; + int count; + LDAPMessage *res = NULL; + LDAPMessage *msg = NULL; + char *ldap_exp; + ADS_STRUCT *ads; + const char *group_attrs[] = {"objectSid", NULL}; + char *escaped_dn; + size_t num_groups = 0; + + DEBUG(3,("ads: lookup_usergroups_member\n")); + + if ( !winbindd_can_contact_domain( domain ) ) { + DEBUG(10,("lookup_usergroups_members: No incoming trust for domain %s\n", + domain->name)); + return NT_STATUS_OK; + } + + ads = ads_cached_connection(domain); + + if (!ads) { + domain->last_status = NT_STATUS_SERVER_DISABLED; + goto done; + } + + if (!(escaped_dn = escape_ldap_string_alloc(user_dn))) { + status = NT_STATUS_NO_MEMORY; + goto done; + } + + ldap_exp = talloc_asprintf(mem_ctx, + "(&(member=%s)(objectCategory=group)(groupType:dn:%s:=%d))", + escaped_dn, + ADS_LDAP_MATCHING_RULE_BIT_AND, + GROUP_TYPE_SECURITY_ENABLED); + if (!ldap_exp) { + DEBUG(1,("lookup_usergroups(dn=%s) asprintf failed!\n", user_dn)); + SAFE_FREE(escaped_dn); + status = NT_STATUS_NO_MEMORY; + goto done; + } + + SAFE_FREE(escaped_dn); + + rc = ads_search_retry(ads, &res, ldap_exp, group_attrs); + + if (!ADS_ERR_OK(rc) || !res) { + DEBUG(1,("lookup_usergroups ads_search member=%s: %s\n", user_dn, ads_errstr(rc))); + return ads_ntstatus(rc); + } + + count = ads_count_replies(ads, res); + + *user_sids = NULL; + num_groups = 0; + + /* always add the primary group to the sid array */ + if (!add_sid_to_array(mem_ctx, primary_group, user_sids, &num_groups)) { + status = NT_STATUS_NO_MEMORY; + goto done; + } + + if (count > 0) { + for (msg = ads_first_entry(ads, res); msg; + msg = ads_next_entry(ads, msg)) { + DOM_SID group_sid; + + if (!ads_pull_sid(ads, msg, "objectSid", &group_sid)) { + DEBUG(1,("No sid for this group ?!?\n")); + continue; + } + + /* ignore Builtin groups from ADS - Guenther */ + if (sid_check_is_in_builtin(&group_sid)) { + continue; + } + + if (!add_sid_to_array(mem_ctx, &group_sid, user_sids, + &num_groups)) { + status = NT_STATUS_NO_MEMORY; + goto done; + } + } + + } + + *p_num_groups = num_groups; + status = (user_sids != NULL) ? NT_STATUS_OK : NT_STATUS_NO_MEMORY; + + DEBUG(3,("ads lookup_usergroups (member) succeeded for dn=%s\n", user_dn)); +done: + if (res) + ads_msgfree(ads, res); + + return status; +} + +/* Lookup groups a user is a member of - alternate method, for when + tokenGroups are not available. */ +static NTSTATUS lookup_usergroups_memberof(struct winbindd_domain *domain, + TALLOC_CTX *mem_ctx, + const char *user_dn, + DOM_SID *primary_group, + size_t *p_num_groups, DOM_SID **user_sids) +{ + ADS_STATUS rc; + NTSTATUS status = NT_STATUS_UNSUCCESSFUL; + ADS_STRUCT *ads; + const char *attrs[] = {"memberOf", NULL}; + size_t num_groups = 0; + DOM_SID *group_sids = NULL; + int i; + char **strings; + size_t num_strings = 0; + + + DEBUG(3,("ads: lookup_usergroups_memberof\n")); + + if ( !winbindd_can_contact_domain( domain ) ) { + DEBUG(10,("lookup_usergroups_memberof: No incoming trust for domain %s\n", + domain->name)); + return NT_STATUS_OK; + } + + ads = ads_cached_connection(domain); + + if (!ads) { + domain->last_status = NT_STATUS_SERVER_DISABLED; + goto done; + } + + rc = ads_search_retry_extended_dn_ranged(ads, mem_ctx, user_dn, attrs, + ADS_EXTENDED_DN_HEX_STRING, + &strings, &num_strings); + + if (!ADS_ERR_OK(rc)) { + DEBUG(1,("lookup_usergroups_memberof ads_search member=%s: %s\n", + user_dn, ads_errstr(rc))); + return ads_ntstatus(rc); + } + + *user_sids = NULL; + num_groups = 0; + + /* always add the primary group to the sid array */ + if (!add_sid_to_array(mem_ctx, primary_group, user_sids, &num_groups)) { + status = NT_STATUS_NO_MEMORY; + goto done; + } + + group_sids = TALLOC_ZERO_ARRAY(mem_ctx, DOM_SID, num_strings + 1); + if (!group_sids) { + TALLOC_FREE(strings); + status = NT_STATUS_NO_MEMORY; + goto done; + } + + for (i=0; iname)); + + /* Tell the cache manager not to remember this one */ + + return NT_STATUS_SYNCHRONIZATION_REQUIRED; + } + + ads = ads_cached_connection(domain); + + if (!ads) { + domain->last_status = NT_STATUS_SERVER_DISABLED; + status = NT_STATUS_SERVER_DISABLED; + goto done; + } + + rc = ads_search_retry_sid(ads, &msg, sid, attrs); + + if (!ADS_ERR_OK(rc)) { + status = ads_ntstatus(rc); + DEBUG(1,("lookup_usergroups(sid=%s) ads_search tokenGroups: %s\n", + sid_to_string(sid_string, sid), ads_errstr(rc))); + goto done; + } + + count = ads_count_replies(ads, msg); + if (count != 1) { + status = NT_STATUS_UNSUCCESSFUL; + DEBUG(1,("lookup_usergroups(sid=%s) ads_search tokenGroups: " + "invalid number of results (count=%d)\n", + sid_to_string(sid_string, sid), count)); + goto done; + } + + if (!msg) { + DEBUG(1,("lookup_usergroups(sid=%s) ads_search tokenGroups: NULL msg\n", + sid_to_string(sid_string, sid))); + status = NT_STATUS_UNSUCCESSFUL; + goto done; + } + + user_dn = ads_get_dn(ads, msg); + if (user_dn == NULL) { + status = NT_STATUS_NO_MEMORY; + goto done; + } + + if (!ads_pull_uint32(ads, msg, "primaryGroupID", &primary_group_rid)) { + DEBUG(1,("%s: No primary group for sid=%s !?\n", + domain->name, sid_to_string(sid_string, sid))); + goto done; + } + + sid_copy(&primary_group, &domain->sid); + sid_append_rid(&primary_group, primary_group_rid); + + count = ads_pull_sids(ads, mem_ctx, msg, "tokenGroups", &sids); + + /* there must always be at least one group in the token, + unless we are talking to a buggy Win2k server */ + + /* actually this only happens when the machine account has no read + * permissions on the tokenGroup attribute - gd */ + + if (count == 0) { + + /* no tokenGroups */ + + /* lookup what groups this user is a member of by DN search on + * "memberOf" */ + + status = lookup_usergroups_memberof(domain, mem_ctx, user_dn, + &primary_group, + &num_groups, user_sids); + *p_num_groups = (uint32)num_groups; + if (NT_STATUS_IS_OK(status)) { + goto done; + } + + /* lookup what groups this user is a member of by DN search on + * "member" */ + + status = lookup_usergroups_member(domain, mem_ctx, user_dn, + &primary_group, + &num_groups, user_sids); + *p_num_groups = (uint32)num_groups; + goto done; + } + + *user_sids = NULL; + num_groups = 0; + + if (!add_sid_to_array(mem_ctx, &primary_group, user_sids, &num_groups)) { + status = NT_STATUS_NO_MEMORY; + goto done; + } + + for (i=0;iname, + sid_string_static(group_sid))); + + *num_names = 0; + + tmp_ctx = talloc_new(mem_ctx); + if (!tmp_ctx) { + DEBUG(1, ("ads: lookup_groupmem: talloc failed\n")); + status = NT_STATUS_NO_MEMORY; + goto done; + } + + if ( !winbindd_can_contact_domain( domain ) ) { + DEBUG(10,("lookup_groupmem: No incoming trust for domain %s\n", + domain->name)); + return NT_STATUS_OK; + } + + ads = ads_cached_connection(domain); + + if (!ads) { + domain->last_status = NT_STATUS_SERVER_DISABLED; + goto done; + } + + if ((sidbinstr = sid_binstring(group_sid)) == NULL) { + status = NT_STATUS_NO_MEMORY; + goto done; + } + + /* search for all members of the group */ + if (!(ldap_exp = talloc_asprintf(tmp_ctx, "(objectSid=%s)", + sidbinstr))) + { + SAFE_FREE(sidbinstr); + DEBUG(1, ("ads: lookup_groupmem: talloc_asprintf for ldap_exp failed!\n")); + status = NT_STATUS_NO_MEMORY; + goto done; + } + SAFE_FREE(sidbinstr); + + args.control = ADS_EXTENDED_DN_OID; + args.val = ADS_EXTENDED_DN_HEX_STRING; + args.critical = True; + + rc = ads_ranged_search(ads, tmp_ctx, LDAP_SCOPE_SUBTREE, ads->config.bind_path, + ldap_exp, &args, "member", &members, &num_members); + + if (!ADS_ERR_OK(rc)) { + DEBUG(0,("ads_ranged_search failed with: %s\n", ads_errstr(rc))); + status = NT_STATUS_UNSUCCESSFUL; + goto done; + } + + DEBUG(10, ("ads lookup_groupmem: got %d sids via extended dn call\n", (int)num_members)); + + /* Now that we have a list of sids, we need to get the + * lists of names and name_types belonging to these sids. + * even though conceptually not quite clean, we use the + * RPC call lsa_lookup_sids for this since it can handle a + * list of sids. ldap calls can just resolve one sid at a time. + * + * At this stage, the sids are still hidden in the exetended dn + * member output format. We actually do a little better than + * stated above: In extracting the sids from the member strings, + * we try to resolve as many sids as possible from the + * cache. Only the rest is passed to the lsa_lookup_sids call. */ + + if (num_members) { + (*sid_mem) = TALLOC_ZERO_ARRAY(mem_ctx, DOM_SID, num_members); + (*names) = TALLOC_ZERO_ARRAY(mem_ctx, char *, num_members); + (*name_types) = TALLOC_ZERO_ARRAY(mem_ctx, uint32, num_members); + (sid_mem_nocache) = TALLOC_ZERO_ARRAY(tmp_ctx, DOM_SID, num_members); + + if ((members == NULL) || (*sid_mem == NULL) || + (*names == NULL) || (*name_types == NULL) || + (sid_mem_nocache == NULL)) + { + DEBUG(1, ("ads: lookup_groupmem: talloc failed\n")); + status = NT_STATUS_NO_MEMORY; + goto done; + } + } + else { + (*sid_mem) = NULL; + (*names) = NULL; + (*name_types) = NULL; + } + + for (i=0; i 0) { + + status = cm_connect_lsa(domain, tmp_ctx, &cli, &lsa_policy); + + if (!NT_STATUS_IS_OK(status)) { + goto done; + } + + status = rpccli_lsa_lookup_sids_all(cli, tmp_ctx, + &lsa_policy, + num_nocache, + sid_mem_nocache, + &domains_nocache, + &names_nocache, + &name_types_nocache); + + if (NT_STATUS_IS_OK(status) || + NT_STATUS_EQUAL(status, STATUS_SOME_UNMAPPED)) + { + /* Copy the entries over from the "_nocache" arrays + * to the result arrays, skipping the gaps the + * lookup_sids call left. */ + for (i=0; i < num_nocache; i++) { + if (((names_nocache)[i] != NULL) && + ((name_types_nocache)[i] != SID_NAME_UNKNOWN)) + { + sid_copy(&(*sid_mem)[*num_names], + &sid_mem_nocache[i]); + (*names)[*num_names] = talloc_asprintf( *names, + "%s%c%s", + domains_nocache[i], + *lp_winbind_separator(), + names_nocache[i] ); + (*name_types)[*num_names] = name_types_nocache[i]; + (*num_names)++; + } + } + } + else if (NT_STATUS_EQUAL(status, NT_STATUS_NONE_MAPPED)) { + DEBUG(10, ("lookup_groupmem: lsa_lookup_sids could " + "not map any SIDs at all.\n")); + /* Don't handle this as an error here. + * There is nothing left to do with respect to the + * overall result... */ + } + else if (!NT_STATUS_IS_OK(status)) { + DEBUG(10, ("lookup_groupmem: Error looking up %d " + "sids via rpc_lsa_lookup_sids: %s\n", + (int)num_members, nt_errstr(status))); + goto done; + } + } + + status = NT_STATUS_OK; + DEBUG(3,("ads lookup_groupmem for sid=%s succeeded\n", + sid_string_static(group_sid))); + +done: + + TALLOC_FREE(tmp_ctx); + + return status; +} + +/* find the sequence number for a domain */ +static NTSTATUS sequence_number(struct winbindd_domain *domain, uint32 *seq) +{ + ADS_STRUCT *ads = NULL; + ADS_STATUS rc; + + DEBUG(3,("ads: fetch sequence_number for %s\n", domain->name)); + + if ( !winbindd_can_contact_domain( domain ) ) { + DEBUG(10,("sequence: No incoming trust for domain %s\n", + domain->name)); + *seq = time(NULL); + return NT_STATUS_OK; + } + + *seq = DOM_SEQUENCE_NONE; + + ads = ads_cached_connection(domain); + + if (!ads) { + domain->last_status = NT_STATUS_SERVER_DISABLED; + return NT_STATUS_UNSUCCESSFUL; + } + + rc = ads_USN(ads, seq); + + if (!ADS_ERR_OK(rc)) { + + /* its a dead connection, destroy it */ + + if (domain->private_data) { + ads = (ADS_STRUCT *)domain->private_data; + ads->is_mine = True; + ads_destroy(&ads); + ads_kdestroy("MEMORY:winbind_ccache"); + domain->private_data = NULL; + } + } + return ads_ntstatus(rc); +} + +/* get a list of trusted domains */ +static NTSTATUS trusted_domains(struct winbindd_domain *domain, + TALLOC_CTX *mem_ctx, + uint32 *num_domains, + char ***names, + char ***alt_names, + DOM_SID **dom_sids) +{ + NTSTATUS result = NT_STATUS_UNSUCCESSFUL; + struct ds_domain_trust *domains = NULL; + int count = 0; + int i; + uint32 flags; + struct rpc_pipe_client *cli; + uint32 fr_flags = (DS_DOMAIN_IN_FOREST | DS_DOMAIN_TREE_ROOT); + int ret_count; + + DEBUG(3,("ads: trusted_domains\n")); + + *num_domains = 0; + *alt_names = NULL; + *names = NULL; + *dom_sids = NULL; + + /* If this is our primary domain or a root in our forest, + query for all trusts. If not, then just look for domain + trusts in the target forest */ + + if ( domain->primary || + ((domain->domain_flags&fr_flags) == fr_flags) ) + { + flags = DS_DOMAIN_DIRECT_OUTBOUND | + DS_DOMAIN_DIRECT_INBOUND | + DS_DOMAIN_IN_FOREST; + } else { + flags = DS_DOMAIN_IN_FOREST; + } + + result = cm_connect_netlogon(domain, &cli); + + if (!NT_STATUS_IS_OK(result)) { + DEBUG(5, ("trusted_domains: Could not open a connection to %s " + "for PIPE_NETLOGON (%s)\n", + domain->name, nt_errstr(result))); + return NT_STATUS_UNSUCCESSFUL; + } + + if ( NT_STATUS_IS_OK(result) ) { + result = rpccli_ds_enum_domain_trusts(cli, mem_ctx, + cli->cli->desthost, + flags, &domains, + (unsigned int *)&count); + } + + if ( NT_STATUS_IS_OK(result) && count) { + + /* Allocate memory for trusted domain names and sids */ + + if ( !(*names = TALLOC_ARRAY(mem_ctx, char *, count)) ) { + DEBUG(0, ("trusted_domains: out of memory\n")); + return NT_STATUS_NO_MEMORY; + } + + if ( !(*alt_names = TALLOC_ARRAY(mem_ctx, char *, count)) ) { + DEBUG(0, ("trusted_domains: out of memory\n")); + return NT_STATUS_NO_MEMORY; + } + + if ( !(*dom_sids = TALLOC_ARRAY(mem_ctx, DOM_SID, count)) ) { + DEBUG(0, ("trusted_domains: out of memory\n")); + return NT_STATUS_NO_MEMORY; + } + + /* Copy across names and sids */ + + + ret_count = 0; + for (i = 0; i < count; i++) { + struct winbindd_domain d; + + /* drop external trusts if this is not our primary + domain. This means that the returned number of + domains may be less that the ones actually trusted + by the DC. */ + + if ( (domains[i].trust_attributes == DS_DOMAIN_TRUST_ATTRIB_QUARANTINED_DOMAIN) && + !domain->primary ) + { + DEBUG(10,("trusted_domains: Skipping external trusted domain " + "%s because it is outside of our primary domain\n", + domains[i].netbios_domain)); + continue; + } + + (*names)[ret_count] = domains[i].netbios_domain; + (*alt_names)[ret_count] = domains[i].dns_domain; + sid_copy(&(*dom_sids)[ret_count], &domains[i].sid); + + /* add to the trusted domain cache */ + + fstrcpy( d.name, domains[i].netbios_domain ); + fstrcpy( d.alt_name, domains[i].dns_domain ); + sid_copy( &d.sid, &domains[i].sid ); + + /* This gets a little tricky. If we are + following a transitive forest trust, then + innerit the flags, type, and attrins from + the domain we queried to make sure we don't + record the view of the trust from the wrong + side. Always view it from the side of our + primary domain. --jerry */ + if ( domain->primary || + ((domain->domain_flags&fr_flags) == fr_flags) ) + { + DEBUG(10,("trusted_domains(ads): Storing trust " + "flags for domain %s\n", d.alt_name)); + + /* Look this up in cache to make sure + we have the current trust flags and + attributes */ + + d.domain_flags = domains[i].flags; + d.domain_type = domains[i].trust_type; + d.domain_trust_attribs = domains[i].trust_attributes; + } else { + DEBUG(10,("trusted_domains(ads): Inheriting trust " + "flags for domain %s\n", d.alt_name)); + d.domain_flags = domain->domain_flags; + d.domain_type = domain->domain_type; + d.domain_trust_attribs = domain->domain_trust_attribs; + } + + wcache_tdc_add_domain( &d ); + + ret_count++; + + } + + *num_domains = ret_count; + } + + return result; +} + +/* the ADS backend methods are exposed via this structure */ +struct winbindd_methods ads_methods = { + True, + query_user_list, + enum_dom_groups, + enum_local_groups, + msrpc_name_to_sid, + msrpc_sid_to_name, + msrpc_rids_to_names, + query_user, + lookup_usergroups, + msrpc_lookup_useraliases, + lookup_groupmem, + sequence_number, + msrpc_lockout_policy, + msrpc_password_policy, + trusted_domains, +}; + +#endif diff --git a/source3/winbindd/winbindd_async.c b/source3/winbindd/winbindd_async.c new file mode 100644 index 0000000000..5d31ff0a41 --- /dev/null +++ b/source3/winbindd/winbindd_async.c @@ -0,0 +1,1695 @@ +/* + Unix SMB/CIFS implementation. + + Async helpers for blocking functions + + Copyright (C) Volker Lendecke 2005 + Copyright (C) Gerald Carter 2006 + + The helpers always consist of three functions: + + * A request setup function that takes the necessary parameters together + with a continuation function that is to be called upon completion + + * A private continuation function that is internal only. This is to be + called by the lower-level functions in do_async(). Its only task is to + properly call the continuation function named above. + + * A worker function that is called inside the appropriate child process. + + 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 3 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, see . +*/ + +#include "includes.h" +#include "winbindd.h" + +#undef DBGC_CLASS +#define DBGC_CLASS DBGC_WINBIND + +struct do_async_state { + TALLOC_CTX *mem_ctx; + struct winbindd_request request; + struct winbindd_response response; + void (*cont)(TALLOC_CTX *mem_ctx, + BOOL success, + struct winbindd_response *response, + void *c, void *private_data); + void *c, *private_data; +}; + +static void do_async_recv(void *private_data, BOOL success) +{ + struct do_async_state *state = + talloc_get_type_abort(private_data, struct do_async_state); + + state->cont(state->mem_ctx, success, &state->response, + state->c, state->private_data); +} + +static void do_async(TALLOC_CTX *mem_ctx, struct winbindd_child *child, + const struct winbindd_request *request, + void (*cont)(TALLOC_CTX *mem_ctx, BOOL success, + struct winbindd_response *response, + void *c, void *private_data), + void *c, void *private_data) +{ + struct do_async_state *state; + + state = TALLOC_ZERO_P(mem_ctx, struct do_async_state); + if (state == NULL) { + DEBUG(0, ("talloc failed\n")); + cont(mem_ctx, False, NULL, c, private_data); + return; + } + + state->mem_ctx = mem_ctx; + state->request = *request; + state->request.length = sizeof(state->request); + state->cont = cont; + state->c = c; + state->private_data = private_data; + + async_request(mem_ctx, child, &state->request, + &state->response, do_async_recv, state); +} + +void do_async_domain(TALLOC_CTX *mem_ctx, struct winbindd_domain *domain, + const struct winbindd_request *request, + void (*cont)(TALLOC_CTX *mem_ctx, BOOL success, + struct winbindd_response *response, + void *c, void *private_data), + void *c, void *private_data) +{ + struct do_async_state *state; + + state = TALLOC_ZERO_P(mem_ctx, struct do_async_state); + if (state == NULL) { + DEBUG(0, ("talloc failed\n")); + cont(mem_ctx, False, NULL, c, private_data); + return; + } + + state->mem_ctx = mem_ctx; + state->request = *request; + state->request.length = sizeof(state->request); + state->cont = cont; + state->c = c; + state->private_data = private_data; + + async_domain_request(mem_ctx, domain, &state->request, + &state->response, do_async_recv, state); +} + +static void winbindd_set_mapping_recv(TALLOC_CTX *mem_ctx, BOOL success, + struct winbindd_response *response, + void *c, void *private_data) +{ + void (*cont)(void *priv, BOOL succ) = (void (*)(void *, BOOL))c; + + if (!success) { + DEBUG(5, ("Could not trigger idmap_set_mapping\n")); + cont(private_data, False); + return; + } + + if (response->result != WINBINDD_OK) { + DEBUG(5, ("idmap_set_mapping returned an error\n")); + cont(private_data, False); + return; + } + + cont(private_data, True); +} + +void winbindd_set_mapping_async(TALLOC_CTX *mem_ctx, const struct id_map *map, + void (*cont)(void *private_data, BOOL success), + void *private_data) +{ + struct winbindd_request request; + ZERO_STRUCT(request); + request.cmd = WINBINDD_DUAL_SET_MAPPING; + request.data.dual_idmapset.id = map->xid.id; + request.data.dual_idmapset.type = map->xid.type; + sid_to_string(request.data.dual_idmapset.sid, map->sid); + + do_async(mem_ctx, idmap_child(), &request, winbindd_set_mapping_recv, + (void *)cont, private_data); +} + +enum winbindd_result winbindd_dual_set_mapping(struct winbindd_domain *domain, + struct winbindd_cli_state *state) +{ + struct id_map map; + DOM_SID sid; + NTSTATUS result; + + DEBUG(3, ("[%5lu]: dual_idmapset\n", (unsigned long)state->pid)); + + if (!string_to_sid(&sid, state->request.data.dual_idmapset.sid)) + return WINBINDD_ERROR; + + map.sid = &sid; + map.xid.id = state->request.data.dual_idmapset.id; + map.xid.type = state->request.data.dual_idmapset.type; + map.status = ID_MAPPED; + + result = idmap_set_mapping(&map); + return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR; +} + +static void winbindd_set_hwm_recv(TALLOC_CTX *mem_ctx, BOOL success, + struct winbindd_response *response, + void *c, void *private_data) +{ + void (*cont)(void *priv, BOOL succ) = (void (*)(void *, BOOL))c; + + if (!success) { + DEBUG(5, ("Could not trigger idmap_set_hwm\n")); + cont(private_data, False); + return; + } + + if (response->result != WINBINDD_OK) { + DEBUG(5, ("idmap_set_hwm returned an error\n")); + cont(private_data, False); + return; + } + + cont(private_data, True); +} + +void winbindd_set_hwm_async(TALLOC_CTX *mem_ctx, const struct unixid *xid, + void (*cont)(void *private_data, BOOL success), + void *private_data) +{ + struct winbindd_request request; + ZERO_STRUCT(request); + request.cmd = WINBINDD_DUAL_SET_HWM; + request.data.dual_idmapset.id = xid->id; + request.data.dual_idmapset.type = xid->type; + + do_async(mem_ctx, idmap_child(), &request, winbindd_set_hwm_recv, + (void *)cont, private_data); +} + +enum winbindd_result winbindd_dual_set_hwm(struct winbindd_domain *domain, + struct winbindd_cli_state *state) +{ + struct unixid xid; + NTSTATUS result; + + DEBUG(3, ("[%5lu]: dual_set_hwm\n", (unsigned long)state->pid)); + + xid.id = state->request.data.dual_idmapset.id; + xid.type = state->request.data.dual_idmapset.type; + + switch (xid.type) { + case ID_TYPE_UID: + result = idmap_set_uid_hwm(&xid); + break; + case ID_TYPE_GID: + result = idmap_set_gid_hwm(&xid); + break; + default: + return WINBINDD_ERROR; + } + return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR; +} + +static void winbindd_sids2xids_recv(TALLOC_CTX *mem_ctx, BOOL success, + struct winbindd_response *response, + void *c, void *private_data) +{ + void (*cont)(void *priv, BOOL succ, void *, int) = + (void (*)(void *, BOOL, void *, int))c; + + if (!success) { + DEBUG(5, ("Could not trigger sids2xids\n")); + cont(private_data, False, NULL, 0); + return; + } + + if (response->result != WINBINDD_OK) { + DEBUG(5, ("sids2xids returned an error\n")); + cont(private_data, False, NULL, 0); + return; + } + + cont(private_data, True, response->extra_data.data, response->length - sizeof(response)); +} + +void winbindd_sids2xids_async(TALLOC_CTX *mem_ctx, void *sids, int size, + void (*cont)(void *private_data, BOOL success, void *data, int len), + void *private_data) +{ + struct winbindd_request request; + ZERO_STRUCT(request); + request.cmd = WINBINDD_DUAL_SIDS2XIDS; + request.extra_data.data = (char *)sids; + request.extra_len = size; + do_async(mem_ctx, idmap_child(), &request, winbindd_sids2xids_recv, + (void *)cont, private_data); +} + +enum winbindd_result winbindd_dual_sids2xids(struct winbindd_domain *domain, + struct winbindd_cli_state *state) +{ + DOM_SID *sids; + struct unixid *xids; + struct id_map **ids; + NTSTATUS result; + int num, i; + + DEBUG(3, ("[%5lu]: sids to unix ids\n", (unsigned long)state->pid)); + + if (state->request.extra_len == 0) { + DEBUG(0, ("Invalid buffer size!\n")); + return WINBINDD_ERROR; + } + + sids = (DOM_SID *)state->request.extra_data.data; + num = state->request.extra_len / sizeof(DOM_SID); + + ids = TALLOC_ZERO_ARRAY(state->mem_ctx, struct id_map *, num + 1); + if ( ! ids) { + DEBUG(0, ("Out of memory!\n")); + return WINBINDD_ERROR; + } + for (i = 0; i < num; i++) { + ids[i] = TALLOC_P(ids, struct id_map); + if ( ! ids[i]) { + DEBUG(0, ("Out of memory!\n")); + talloc_free(ids); + return WINBINDD_ERROR; + } + ids[i]->sid = &sids[i]; + } + + result = idmap_sids_to_unixids(ids); + + if (NT_STATUS_IS_OK(result)) { + + xids = SMB_MALLOC_ARRAY(struct unixid, num); + if ( ! xids) { + DEBUG(0, ("Out of memory!\n")); + talloc_free(ids); + return WINBINDD_ERROR; + } + + for (i = 0; i < num; i++) { + if (ids[i]->status == ID_MAPPED) { + xids[i].type = ids[i]->xid.type; + xids[i].id = ids[i]->xid.id; + } else { + xids[i].type = -1; + } + } + + state->response.length = sizeof(state->response) + (sizeof(struct unixid) * num); + state->response.extra_data.data = xids; + + } else { + DEBUG (2, ("idmap_sids_to_unixids returned an error: 0x%08x\n", NT_STATUS_V(result))); + talloc_free(ids); + return WINBINDD_ERROR; + } + + talloc_free(ids); + return WINBINDD_OK; +} + +static void winbindd_sid2uid_recv(TALLOC_CTX *mem_ctx, BOOL success, + struct winbindd_response *response, + void *c, void *private_data) +{ + void (*cont)(void *priv, BOOL succ, uid_t uid) = + (void (*)(void *, BOOL, uid_t))c; + + if (!success) { + DEBUG(5, ("Could not trigger sid2uid\n")); + cont(private_data, False, 0); + return; + } + + if (response->result != WINBINDD_OK) { + DEBUG(5, ("sid2uid returned an error\n")); + cont(private_data, False, 0); + return; + } + + cont(private_data, True, response->data.uid); +} + +void winbindd_sid2uid_async(TALLOC_CTX *mem_ctx, const DOM_SID *sid, + void (*cont)(void *private_data, BOOL success, uid_t uid), + void *private_data) +{ + struct winbindd_request request; + ZERO_STRUCT(request); + request.cmd = WINBINDD_DUAL_SID2UID; + sid_to_string(request.data.dual_sid2id.sid, sid); + do_async(mem_ctx, idmap_child(), &request, winbindd_sid2uid_recv, + (void *)cont, private_data); +} + +enum winbindd_result winbindd_dual_sid2uid(struct winbindd_domain *domain, + struct winbindd_cli_state *state) +{ + DOM_SID sid; + NTSTATUS result; + + DEBUG(3, ("[%5lu]: sid to uid %s\n", (unsigned long)state->pid, + state->request.data.dual_sid2id.sid)); + + if (!string_to_sid(&sid, state->request.data.dual_sid2id.sid)) { + DEBUG(1, ("Could not get convert sid %s from string\n", + state->request.data.dual_sid2id.sid)); + return WINBINDD_ERROR; + } + + /* Find uid for this sid and return it, possibly ask the slow remote idmap */ + + result = idmap_sid_to_uid(&sid, &(state->response.data.uid)); + + return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR; +} + +#if 0 /* not used */ +static void uid2name_recv(TALLOC_CTX *mem_ctx, BOOL success, + struct winbindd_response *response, + void *c, void *private_data); + +void winbindd_uid2name_async(TALLOC_CTX *mem_ctx, uid_t uid, + void (*cont)(void *private_data, BOOL success, + const char *name), + void *private_data) +{ + struct winbindd_request request; + ZERO_STRUCT(request); + request.cmd = WINBINDD_DUAL_UID2NAME; + request.data.uid = uid; + do_async(mem_ctx, idmap_child(), &request, uid2name_recv, + (void *)cont, private_data); +} +#endif /* not used */ + +enum winbindd_result winbindd_dual_uid2name(struct winbindd_domain *domain, + struct winbindd_cli_state *state) +{ + struct passwd *pw; + + DEBUG(3, ("[%5lu]: uid2name %lu\n", (unsigned long)state->pid, + (unsigned long)state->request.data.uid)); + + pw = getpwuid(state->request.data.uid); + if (pw == NULL) { + DEBUG(5, ("User %lu not found\n", + (unsigned long)state->request.data.uid)); + return WINBINDD_ERROR; + } + + fstrcpy(state->response.data.name.name, pw->pw_name); + return WINBINDD_OK; +} + +#if 0 /* not used */ +static void uid2name_recv(TALLOC_CTX *mem_ctx, BOOL success, + struct winbindd_response *response, + void *c, void *private_data) +{ + void (*cont)(void *priv, BOOL succ, const char *name) = + (void (*)(void *, BOOL, const char *))c; + + if (!success) { + DEBUG(5, ("Could not trigger uid2name\n")); + cont(private_data, False, NULL); + return; + } + + if (response->result != WINBINDD_OK) { + DEBUG(5, ("uid2name returned an error\n")); + cont(private_data, False, NULL); + return; + } + + cont(private_data, True, response->data.name.name); +} + +static void name2uid_recv(TALLOC_CTX *mem_ctx, BOOL success, + struct winbindd_response *response, + void *c, void *private_data); + +static void winbindd_name2uid_async(TALLOC_CTX *mem_ctx, const char *name, + void (*cont)(void *private_data, BOOL success, + uid_t uid), + void *private_data) +{ + struct winbindd_request request; + ZERO_STRUCT(request); + request.cmd = WINBINDD_DUAL_NAME2UID; + fstrcpy(request.data.username, name); + do_async(mem_ctx, idmap_child(), &request, name2uid_recv, + (void *)cont, private_data); +} +#endif /* not used */ + +enum winbindd_result winbindd_dual_name2uid(struct winbindd_domain *domain, + struct winbindd_cli_state *state) +{ + struct passwd *pw; + + /* Ensure null termination */ + state->request.data.username + [sizeof(state->request.data.username)-1] = '\0'; + + DEBUG(3, ("[%5lu]: name2uid %s\n", (unsigned long)state->pid, + state->request.data.username)); + + pw = getpwnam(state->request.data.username); + if (pw == NULL) { + return WINBINDD_ERROR; + } + + state->response.data.uid = pw->pw_uid; + return WINBINDD_OK; +} + +#if 0 /* not used */ +static void name2uid_recv(TALLOC_CTX *mem_ctx, BOOL success, + struct winbindd_response *response, + void *c, void *private_data) +{ + void (*cont)(void *priv, BOOL succ, uid_t uid) = + (void (*)(void *, BOOL, uid_t))c; + + if (!success) { + DEBUG(5, ("Could not trigger name2uid\n")); + cont(private_data, False, 0); + return; + } + + if (response->result != WINBINDD_OK) { + DEBUG(5, ("name2uid returned an error\n")); + cont(private_data, False, 0); + return; + } + + cont(private_data, True, response->data.uid); +} +#endif /* not used */ + +static void winbindd_sid2gid_recv(TALLOC_CTX *mem_ctx, BOOL success, + struct winbindd_response *response, + void *c, void *private_data) +{ + void (*cont)(void *priv, BOOL succ, gid_t gid) = + (void (*)(void *, BOOL, gid_t))c; + + if (!success) { + DEBUG(5, ("Could not trigger sid2gid\n")); + cont(private_data, False, 0); + return; + } + + if (response->result != WINBINDD_OK) { + DEBUG(5, ("sid2gid returned an error\n")); + cont(private_data, False, 0); + return; + } + + cont(private_data, True, response->data.gid); +} + +void winbindd_sid2gid_async(TALLOC_CTX *mem_ctx, const DOM_SID *sid, + void (*cont)(void *private_data, BOOL success, gid_t gid), + void *private_data) +{ + struct winbindd_request request; + ZERO_STRUCT(request); + request.cmd = WINBINDD_DUAL_SID2GID; + sid_to_string(request.data.dual_sid2id.sid, sid); + + DEBUG(7,("winbindd_sid2gid_async: Resolving %s to a gid\n", + request.data.dual_sid2id.sid)); + + do_async(mem_ctx, idmap_child(), &request, winbindd_sid2gid_recv, + (void *)cont, private_data); +} + +enum winbindd_result winbindd_dual_sid2gid(struct winbindd_domain *domain, + struct winbindd_cli_state *state) +{ + DOM_SID sid; + NTSTATUS result; + + DEBUG(3, ("[%5lu]: sid to gid %s\n", (unsigned long)state->pid, + state->request.data.dual_sid2id.sid)); + + if (!string_to_sid(&sid, state->request.data.dual_sid2id.sid)) { + DEBUG(1, ("Could not get convert sid %s from string\n", + state->request.data.dual_sid2id.sid)); + return WINBINDD_ERROR; + } + + /* Find gid for this sid and return it, possibly ask the slow remote idmap */ + + result = idmap_sid_to_gid(&sid, &state->response.data.gid); + + DEBUG(10, ("winbindd_dual_sid2gid: 0x%08x - %s - %u\n", NT_STATUS_V(result), sid_string_static(&sid), state->response.data.gid)); + + return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR; +} + +static void gid2name_recv(TALLOC_CTX *mem_ctx, BOOL success, + struct winbindd_response *response, + void *c, void *private_data) +{ + void (*cont)(void *priv, BOOL succ, const char *name) = + (void (*)(void *, BOOL, const char *))c; + + if (!success) { + DEBUG(5, ("Could not trigger gid2name\n")); + cont(private_data, False, NULL); + return; + } + + if (response->result != WINBINDD_OK) { + DEBUG(5, ("gid2name returned an error\n")); + cont(private_data, False, NULL); + return; + } + + cont(private_data, True, response->data.name.name); +} + +void winbindd_gid2name_async(TALLOC_CTX *mem_ctx, gid_t gid, + void (*cont)(void *private_data, BOOL success, + const char *name), + void *private_data) +{ + struct winbindd_request request; + ZERO_STRUCT(request); + request.cmd = WINBINDD_DUAL_GID2NAME; + request.data.gid = gid; + do_async(mem_ctx, idmap_child(), &request, gid2name_recv, + (void *)cont, private_data); +} + +enum winbindd_result winbindd_dual_gid2name(struct winbindd_domain *domain, + struct winbindd_cli_state *state) +{ + struct group *gr; + + DEBUG(3, ("[%5lu]: gid2name %lu\n", (unsigned long)state->pid, + (unsigned long)state->request.data.gid)); + + gr = getgrgid(state->request.data.gid); + if (gr == NULL) + return WINBINDD_ERROR; + + fstrcpy(state->response.data.name.name, gr->gr_name); + return WINBINDD_OK; +} + +#if 0 /* not used */ +static void name2gid_recv(TALLOC_CTX *mem_ctx, BOOL success, + struct winbindd_response *response, + void *c, void *private_data); + +static void winbindd_name2gid_async(TALLOC_CTX *mem_ctx, const char *name, + void (*cont)(void *private_data, BOOL success, + gid_t gid), + void *private_data) +{ + struct winbindd_request request; + ZERO_STRUCT(request); + request.cmd = WINBINDD_DUAL_NAME2GID; + fstrcpy(request.data.groupname, name); + do_async(mem_ctx, idmap_child(), &request, name2gid_recv, + (void *)cont, private_data); +} +#endif /* not used */ + +enum winbindd_result winbindd_dual_name2gid(struct winbindd_domain *domain, + struct winbindd_cli_state *state) +{ + struct group *gr; + + /* Ensure null termination */ + state->request.data.groupname + [sizeof(state->request.data.groupname)-1] = '\0'; + + DEBUG(3, ("[%5lu]: name2gid %s\n", (unsigned long)state->pid, + state->request.data.groupname)); + + gr = getgrnam(state->request.data.groupname); + if (gr == NULL) { + return WINBINDD_ERROR; + } + + state->response.data.gid = gr->gr_gid; + return WINBINDD_OK; +} + +#if 0 /* not used */ +static void name2gid_recv(TALLOC_CTX *mem_ctx, BOOL success, + struct winbindd_response *response, + void *c, void *private_data) +{ + void (*cont)(void *priv, BOOL succ, gid_t gid) = + (void (*)(void *, BOOL, gid_t))c; + + if (!success) { + DEBUG(5, ("Could not trigger name2gid\n")); + cont(private_data, False, 0); + return; + } + + if (response->result != WINBINDD_OK) { + DEBUG(5, ("name2gid returned an error\n")); + cont(private_data, False, 0); + return; + } + + cont(private_data, True, response->data.gid); +} +#endif /* not used */ + +struct lookupsid_state { + DOM_SID sid; + void *caller_private_data; +}; + + +static void lookupsid_recv2(TALLOC_CTX *mem_ctx, BOOL success, + struct winbindd_response *response, + void *c, void *private_data) +{ + void (*cont)(void *priv, BOOL succ, const char *dom_name, + const char *name, enum lsa_SidType type) = + (void (*)(void *, BOOL, const char *, const char *, + enum lsa_SidType))c; + struct lookupsid_state *s = talloc_get_type_abort(private_data, + struct lookupsid_state); + + if (!success) { + DEBUG(5, ("Could not trigger lookupsid\n")); + cont(s->caller_private_data, False, NULL, NULL, SID_NAME_UNKNOWN); + return; + } + + if (response->result != WINBINDD_OK) { + DEBUG(5, ("lookupsid (forest root) returned an error\n")); + cont(s->caller_private_data, False, NULL, NULL, SID_NAME_UNKNOWN); + return; + } + + cont(s->caller_private_data, True, response->data.name.dom_name, + response->data.name.name, + (enum lsa_SidType)response->data.name.type); +} + +static void lookupsid_recv(TALLOC_CTX *mem_ctx, BOOL success, + struct winbindd_response *response, + void *c, void *private_data) +{ + void (*cont)(void *priv, BOOL succ, const char *dom_name, + const char *name, enum lsa_SidType type) = + (void (*)(void *, BOOL, const char *, const char *, + enum lsa_SidType))c; + struct lookupsid_state *s = talloc_get_type_abort(private_data, + struct lookupsid_state); + + if (!success) { + DEBUG(5, ("Could not trigger lookupsid\n")); + cont(s->caller_private_data, False, NULL, NULL, SID_NAME_UNKNOWN); + return; + } + + if (response->result != WINBINDD_OK) { + /* Try again using the forest root */ + struct winbindd_domain *root_domain = find_root_domain(); + struct winbindd_request request; + + if ( !root_domain ) { + DEBUG(5,("lookupsid_recv: unable to determine forest root\n")); + cont(s->caller_private_data, False, NULL, NULL, SID_NAME_UNKNOWN); + return; + } + + ZERO_STRUCT(request); + request.cmd = WINBINDD_LOOKUPSID; + fstrcpy(request.data.sid, sid_string_static(&s->sid)); + + do_async_domain(mem_ctx, root_domain, &request, lookupsid_recv2, + (void *)cont, s); + + return; + } + + cont(s->caller_private_data, True, response->data.name.dom_name, + response->data.name.name, + (enum lsa_SidType)response->data.name.type); +} + +void winbindd_lookupsid_async(TALLOC_CTX *mem_ctx, const DOM_SID *sid, + void (*cont)(void *private_data, BOOL success, + const char *dom_name, + const char *name, + enum lsa_SidType type), + void *private_data) +{ + struct winbindd_domain *domain; + struct winbindd_request request; + struct lookupsid_state *s; + + domain = find_lookup_domain_from_sid(sid); + if (domain == NULL) { + DEBUG(5, ("Could not find domain for sid %s\n", + sid_string_static(sid))); + cont(private_data, False, NULL, NULL, SID_NAME_UNKNOWN); + return; + } + + ZERO_STRUCT(request); + request.cmd = WINBINDD_LOOKUPSID; + fstrcpy(request.data.sid, sid_string_static(sid)); + + if ( (s = TALLOC_ZERO_P(mem_ctx, struct lookupsid_state)) == NULL ) { + DEBUG(0, ("winbindd_lookupsid_async: talloc failed\n")); + cont(private_data, False, NULL, NULL, SID_NAME_UNKNOWN); + return; + } + + sid_copy( &s->sid, sid ); + s->caller_private_data = private_data; + + do_async_domain(mem_ctx, domain, &request, lookupsid_recv, + (void *)cont, s); +} + +enum winbindd_result winbindd_dual_lookupsid(struct winbindd_domain *domain, + struct winbindd_cli_state *state) +{ + enum lsa_SidType type; + DOM_SID sid; + char *name; + char *dom_name; + + /* Ensure null termination */ + state->request.data.sid[sizeof(state->request.data.sid)-1]='\0'; + + DEBUG(3, ("[%5lu]: lookupsid %s\n", (unsigned long)state->pid, + state->request.data.sid)); + + /* Lookup sid from PDC using lsa_lookup_sids() */ + + if (!string_to_sid(&sid, state->request.data.sid)) { + DEBUG(5, ("%s not a SID\n", state->request.data.sid)); + return WINBINDD_ERROR; + } + + /* Lookup the sid */ + + if (!winbindd_lookup_name_by_sid(state->mem_ctx, domain, &sid, + &dom_name, &name, &type)) + { + TALLOC_FREE(dom_name); + TALLOC_FREE(name); + return WINBINDD_ERROR; + } + + fstrcpy(state->response.data.name.dom_name, dom_name); + fstrcpy(state->response.data.name.name, name); + state->response.data.name.type = type; + + TALLOC_FREE(dom_name); + TALLOC_FREE(name); + return WINBINDD_OK; +} + +/******************************************************************** + This is the second callback after contacting the forest root +********************************************************************/ + +struct lookupname_state { + char *dom_name; + char *name; + void *caller_private_data; +}; + + +static void lookupname_recv2(TALLOC_CTX *mem_ctx, BOOL success, + struct winbindd_response *response, + void *c, void *private_data) +{ + void (*cont)(void *priv, BOOL succ, const DOM_SID *sid, + enum lsa_SidType type) = + (void (*)(void *, BOOL, const DOM_SID *, enum lsa_SidType))c; + DOM_SID sid; + struct lookupname_state *s = talloc_get_type_abort( private_data, + struct lookupname_state ); + + + if (!success) { + DEBUG(5, ("Could not trigger lookup_name\n")); + cont(s->caller_private_data, False, NULL, SID_NAME_UNKNOWN); + return; + } + + if (response->result != WINBINDD_OK) { + DEBUG(5, ("lookup_name returned an error\n")); + cont(s->caller_private_data, False, NULL, SID_NAME_UNKNOWN); + return; + } + + if (!string_to_sid(&sid, response->data.sid.sid)) { + DEBUG(0, ("Could not convert string %s to sid\n", + response->data.sid.sid)); + cont(s->caller_private_data, False, NULL, SID_NAME_UNKNOWN); + return; + } + + cont(s->caller_private_data, True, &sid, + (enum lsa_SidType)response->data.sid.type); +} + +/******************************************************************** + This is the first callback after contacting our own domain +********************************************************************/ + +static void lookupname_recv(TALLOC_CTX *mem_ctx, BOOL success, + struct winbindd_response *response, + void *c, void *private_data) +{ + void (*cont)(void *priv, BOOL succ, const DOM_SID *sid, + enum lsa_SidType type) = + (void (*)(void *, BOOL, const DOM_SID *, enum lsa_SidType))c; + DOM_SID sid; + struct lookupname_state *s = talloc_get_type_abort( private_data, + struct lookupname_state ); + + if (!success) { + DEBUG(5, ("lookupname_recv: lookup_name() failed!\n")); + cont(s->caller_private_data, False, NULL, SID_NAME_UNKNOWN); + return; + } + + if (response->result != WINBINDD_OK) { + /* Try again using the forest root */ + struct winbindd_domain *root_domain = find_root_domain(); + struct winbindd_request request; + + if ( !root_domain ) { + DEBUG(5,("lookupname_recv: unable to determine forest root\n")); + cont(s->caller_private_data, False, NULL, SID_NAME_UNKNOWN); + return; + } + + ZERO_STRUCT(request); + request.cmd = WINBINDD_LOOKUPNAME; + + fstrcpy( request.data.name.dom_name, s->dom_name ); + fstrcpy( request.data.name.name, s->name ); + + do_async_domain(mem_ctx, root_domain, &request, lookupname_recv2, + (void *)cont, s); + + return; + } + + if (!string_to_sid(&sid, response->data.sid.sid)) { + DEBUG(0, ("Could not convert string %s to sid\n", + response->data.sid.sid)); + cont(s->caller_private_data, False, NULL, SID_NAME_UNKNOWN); + return; + } + + cont(s->caller_private_data, True, &sid, + (enum lsa_SidType)response->data.sid.type); +} + +/******************************************************************** + The lookup name call first contacts a DC in its own domain + and fallbacks to contact a DC in the forest in our domain doesn't + know the name. +********************************************************************/ + +void winbindd_lookupname_async(TALLOC_CTX *mem_ctx, + const char *dom_name, const char *name, + void (*cont)(void *private_data, BOOL success, + const DOM_SID *sid, + enum lsa_SidType type), + enum winbindd_cmd orig_cmd, + void *private_data) +{ + struct winbindd_request request; + struct winbindd_domain *domain; + struct lookupname_state *s; + + if ( (domain = find_lookup_domain_from_name(dom_name)) == NULL ) { + DEBUG(5, ("Could not find domain for name %s\n", dom_name)); + cont(private_data, False, NULL, SID_NAME_UNKNOWN); + return; + } + + ZERO_STRUCT(request); + request.cmd = WINBINDD_LOOKUPNAME; + request.original_cmd = orig_cmd; + fstrcpy(request.data.name.dom_name, dom_name); + fstrcpy(request.data.name.name, name); + + if ( (s = TALLOC_ZERO_P(mem_ctx, struct lookupname_state)) == NULL ) { + DEBUG(0, ("winbindd_lookupname_async: talloc failed\n")); + cont(private_data, False, NULL, SID_NAME_UNKNOWN); + return; + } + + s->dom_name = talloc_strdup( s, dom_name ); + s->name = talloc_strdup( s, name ); + s->caller_private_data = private_data; + + do_async_domain(mem_ctx, domain, &request, lookupname_recv, + (void *)cont, s); +} + +enum winbindd_result winbindd_dual_lookupname(struct winbindd_domain *domain, + struct winbindd_cli_state *state) +{ + enum lsa_SidType type; + char *name_domain, *name_user; + DOM_SID sid; + char *p; + + /* Ensure null termination */ + state->request.data.name.dom_name[sizeof(state->request.data.name.dom_name)-1]='\0'; + + /* Ensure null termination */ + state->request.data.name.name[sizeof(state->request.data.name.name)-1]='\0'; + + /* cope with the name being a fully qualified name */ + p = strstr(state->request.data.name.name, lp_winbind_separator()); + if (p) { + *p = 0; + name_domain = state->request.data.name.name; + name_user = p+1; + } else { + name_domain = state->request.data.name.dom_name; + name_user = state->request.data.name.name; + } + + DEBUG(3, ("[%5lu]: lookupname %s%s%s\n", (unsigned long)state->pid, + name_domain, lp_winbind_separator(), name_user)); + + /* Lookup name from DC using lsa_lookup_names() */ + if (!winbindd_lookup_sid_by_name(state->mem_ctx, state->request.original_cmd, domain, name_domain, + name_user, &sid, &type)) { + return WINBINDD_ERROR; + } + + sid_to_string(state->response.data.sid.sid, &sid); + state->response.data.sid.type = type; + + return WINBINDD_OK; +} + +BOOL print_sidlist(TALLOC_CTX *mem_ctx, const DOM_SID *sids, + size_t num_sids, char **result, ssize_t *len) +{ + size_t i; + size_t buflen = 0; + + *len = 0; + *result = NULL; + for (i=0; irequest.domain_name, + state->request.data.sid)); + + if (!parse_ridlist(state->mem_ctx, state->request.extra_data.data, + &rids, &num_rids)) { + DEBUG(5, ("Could not parse ridlist\n")); + return WINBINDD_ERROR; + } + + if (!string_to_sid(&domain_sid, state->request.data.sid)) { + DEBUG(5, ("Could not parse domain sid %s\n", + state->request.data.sid)); + return WINBINDD_ERROR; + } + + status = domain->methods->rids_to_names(domain, state->mem_ctx, + &domain_sid, rids, num_rids, + &domain_name, + &names, &types); + + if (!NT_STATUS_IS_OK(status) && + !NT_STATUS_EQUAL(status, STATUS_SOME_UNMAPPED)) { + return WINBINDD_ERROR; + } + + len = 0; + buflen = 0; + result = NULL; + + for (i=0; imem_ctx, &result, &len, &buflen, + "%d %s\n", types[i], names[i]); + } + + fstrcpy(state->response.data.domain_name, domain_name); + + if (result != NULL) { + state->response.extra_data.data = SMB_STRDUP(result); + if (!state->response.extra_data.data) { + return WINBINDD_ERROR; + } + state->response.length += len+1; + } + + return WINBINDD_OK; +} + +static void getsidaliases_recv(TALLOC_CTX *mem_ctx, BOOL success, + struct winbindd_response *response, + void *c, void *private_data) +{ + void (*cont)(void *priv, BOOL succ, + DOM_SID *aliases, size_t num_aliases) = + (void (*)(void *, BOOL, DOM_SID *, size_t))c; + char *aliases_str; + DOM_SID *sids = NULL; + size_t num_sids = 0; + + if (!success) { + DEBUG(5, ("Could not trigger getsidaliases\n")); + cont(private_data, success, NULL, 0); + return; + } + + if (response->result != WINBINDD_OK) { + DEBUG(5, ("getsidaliases returned an error\n")); + cont(private_data, False, NULL, 0); + return; + } + + aliases_str = (char *)response->extra_data.data; + + if (aliases_str == NULL) { + DEBUG(10, ("getsidaliases return 0 SIDs\n")); + cont(private_data, True, NULL, 0); + return; + } + + if (!parse_sidlist(mem_ctx, aliases_str, &sids, &num_sids)) { + DEBUG(0, ("Could not parse sids\n")); + cont(private_data, False, NULL, 0); + return; + } + + SAFE_FREE(response->extra_data.data); + + cont(private_data, True, sids, num_sids); +} + +void winbindd_getsidaliases_async(struct winbindd_domain *domain, + TALLOC_CTX *mem_ctx, + const DOM_SID *sids, size_t num_sids, + void (*cont)(void *private_data, + BOOL success, + const DOM_SID *aliases, + size_t num_aliases), + void *private_data) +{ + struct winbindd_request request; + char *sidstr = NULL; + ssize_t len; + + if (num_sids == 0) { + cont(private_data, True, NULL, 0); + return; + } + + if (!print_sidlist(mem_ctx, sids, num_sids, &sidstr, &len)) { + cont(private_data, False, NULL, 0); + return; + } + + ZERO_STRUCT(request); + request.cmd = WINBINDD_DUAL_GETSIDALIASES; + request.extra_len = len; + request.extra_data.data = sidstr; + + do_async_domain(mem_ctx, domain, &request, getsidaliases_recv, + (void *)cont, private_data); +} + +enum winbindd_result winbindd_dual_getsidaliases(struct winbindd_domain *domain, + struct winbindd_cli_state *state) +{ + DOM_SID *sids = NULL; + size_t num_sids = 0; + char *sidstr = NULL; + ssize_t len; + size_t i; + uint32 num_aliases; + uint32 *alias_rids; + NTSTATUS result; + + DEBUG(3, ("[%5lu]: getsidaliases\n", (unsigned long)state->pid)); + + sidstr = state->request.extra_data.data; + if (sidstr == NULL) { + sidstr = talloc_strdup(state->mem_ctx, "\n"); /* No SID */ + if (!sidstr) { + DEBUG(0, ("Out of memory\n")); + return WINBINDD_ERROR; + } + } + + DEBUG(10, ("Sidlist: %s\n", sidstr)); + + if (!parse_sidlist(state->mem_ctx, sidstr, &sids, &num_sids)) { + DEBUG(0, ("Could not parse SID list: %s\n", sidstr)); + return WINBINDD_ERROR; + } + + num_aliases = 0; + alias_rids = NULL; + + result = domain->methods->lookup_useraliases(domain, + state->mem_ctx, + num_sids, sids, + &num_aliases, + &alias_rids); + + if (!NT_STATUS_IS_OK(result)) { + DEBUG(3, ("Could not lookup_useraliases: %s\n", + nt_errstr(result))); + return WINBINDD_ERROR; + } + + num_sids = 0; + sids = NULL; + sidstr = NULL; + + DEBUG(10, ("Got %d aliases\n", num_aliases)); + + for (i=0; isid); + sid_append_rid(&sid, alias_rids[i]); + if (!add_sid_to_array(state->mem_ctx, &sid, &sids, &num_sids)) { + return WINBINDD_ERROR; + } + } + + + if (!print_sidlist(state->mem_ctx, sids, num_sids, &sidstr, &len)) { + DEBUG(0, ("Could not print_sidlist\n")); + state->response.extra_data.data = NULL; + return WINBINDD_ERROR; + } + + state->response.extra_data.data = NULL; + + if (sidstr) { + state->response.extra_data.data = SMB_STRDUP(sidstr); + if (!state->response.extra_data.data) { + DEBUG(0, ("Out of memory\n")); + return WINBINDD_ERROR; + } + DEBUG(10, ("aliases_list: %s\n", + (char *)state->response.extra_data.data)); + state->response.length += len+1; + } + + return WINBINDD_OK; +} + +struct gettoken_state { + TALLOC_CTX *mem_ctx; + DOM_SID user_sid; + struct winbindd_domain *alias_domain; + struct winbindd_domain *local_alias_domain; + struct winbindd_domain *builtin_domain; + DOM_SID *sids; + size_t num_sids; + void (*cont)(void *private_data, BOOL success, DOM_SID *sids, size_t num_sids); + void *private_data; +}; + +static void gettoken_recvdomgroups(TALLOC_CTX *mem_ctx, BOOL success, + struct winbindd_response *response, + void *c, void *private_data); +static void gettoken_recvaliases(void *private_data, BOOL success, + const DOM_SID *aliases, + size_t num_aliases); + + +void winbindd_gettoken_async(TALLOC_CTX *mem_ctx, const DOM_SID *user_sid, + void (*cont)(void *private_data, BOOL success, + DOM_SID *sids, size_t num_sids), + void *private_data) +{ + struct winbindd_domain *domain; + struct winbindd_request request; + struct gettoken_state *state; + + state = TALLOC_ZERO_P(mem_ctx, struct gettoken_state); + if (state == NULL) { + DEBUG(0, ("talloc failed\n")); + cont(private_data, False, NULL, 0); + return; + } + + state->mem_ctx = mem_ctx; + sid_copy(&state->user_sid, user_sid); + state->alias_domain = find_our_domain(); + state->local_alias_domain = find_domain_from_name( get_global_sam_name() ); + state->builtin_domain = find_builtin_domain(); + state->cont = cont; + state->private_data = private_data; + + domain = find_domain_from_sid_noinit(user_sid); + if (domain == NULL) { + DEBUG(5, ("Could not find domain from SID %s\n", + sid_string_static(user_sid))); + cont(private_data, False, NULL, 0); + return; + } + + ZERO_STRUCT(request); + request.cmd = WINBINDD_GETUSERDOMGROUPS; + fstrcpy(request.data.sid, sid_string_static(user_sid)); + + do_async_domain(mem_ctx, domain, &request, gettoken_recvdomgroups, + NULL, state); +} + +static void gettoken_recvdomgroups(TALLOC_CTX *mem_ctx, BOOL success, + struct winbindd_response *response, + void *c, void *private_data) +{ + struct gettoken_state *state = + talloc_get_type_abort(private_data, struct gettoken_state); + char *sids_str; + + if (!success) { + DEBUG(10, ("Could not get domain groups\n")); + state->cont(state->private_data, False, NULL, 0); + return; + } + + sids_str = (char *)response->extra_data.data; + + if (sids_str == NULL) { + /* This could be normal if we are dealing with a + local user and local groups */ + + if ( !sid_check_is_in_our_domain( &state->user_sid ) ) { + DEBUG(10, ("Received no domain groups\n")); + state->cont(state->private_data, True, NULL, 0); + return; + } + } + + state->sids = NULL; + state->num_sids = 0; + + if (!add_sid_to_array(mem_ctx, &state->user_sid, &state->sids, + &state->num_sids)) { + DEBUG(0, ("Out of memory\n")); + state->cont(state->private_data, False, NULL, 0); + return; + } + + if (sids_str && !parse_sidlist(mem_ctx, sids_str, &state->sids, + &state->num_sids)) { + DEBUG(0, ("Could not parse sids\n")); + state->cont(state->private_data, False, NULL, 0); + return; + } + + SAFE_FREE(response->extra_data.data); + + if (state->alias_domain == NULL) { + DEBUG(10, ("Don't expand domain local groups\n")); + state->cont(state->private_data, True, state->sids, + state->num_sids); + return; + } + + winbindd_getsidaliases_async(state->alias_domain, mem_ctx, + state->sids, state->num_sids, + gettoken_recvaliases, state); +} + +static void gettoken_recvaliases(void *private_data, BOOL success, + const DOM_SID *aliases, + size_t num_aliases) +{ + struct gettoken_state *state = (struct gettoken_state *)private_data; + size_t i; + + if (!success) { + DEBUG(10, ("Could not receive domain local groups\n")); + state->cont(state->private_data, False, NULL, 0); + return; + } + + for (i=0; imem_ctx, &aliases[i], + &state->sids, &state->num_sids)) { + DEBUG(0, ("Out of memory\n")); + state->cont(state->private_data, False, NULL, 0); + return; + } + } + + if (state->local_alias_domain != NULL) { + struct winbindd_domain *local_domain = state->local_alias_domain; + DEBUG(10, ("Expanding our own local groups\n")); + state->local_alias_domain = NULL; + winbindd_getsidaliases_async(local_domain, state->mem_ctx, + state->sids, state->num_sids, + gettoken_recvaliases, state); + return; + } + + if (state->builtin_domain != NULL) { + struct winbindd_domain *builtin_domain = state->builtin_domain; + DEBUG(10, ("Expanding our own BUILTIN groups\n")); + state->builtin_domain = NULL; + winbindd_getsidaliases_async(builtin_domain, state->mem_ctx, + state->sids, state->num_sids, + gettoken_recvaliases, state); + return; + } + + state->cont(state->private_data, True, state->sids, state->num_sids); +} + +static void query_user_recv(TALLOC_CTX *mem_ctx, BOOL success, + struct winbindd_response *response, + void *c, void *private_data) +{ + void (*cont)(void *priv, BOOL succ, const char *acct_name, + const char *full_name, const char *homedir, + const char *shell, uint32 gid, uint32 group_rid) = + (void (*)(void *, BOOL, const char *, const char *, + const char *, const char *, uint32, uint32))c; + + if (!success) { + DEBUG(5, ("Could not trigger query_user\n")); + cont(private_data, False, NULL, NULL, NULL, NULL, -1, -1); + return; + } + + if (response->result != WINBINDD_OK) { + DEBUG(5, ("query_user returned an error\n")); + cont(private_data, False, NULL, NULL, NULL, NULL, -1, -1); + return; + } + + cont(private_data, True, response->data.user_info.acct_name, + response->data.user_info.full_name, + response->data.user_info.homedir, + response->data.user_info.shell, + response->data.user_info.primary_gid, + response->data.user_info.group_rid); +} + +void query_user_async(TALLOC_CTX *mem_ctx, struct winbindd_domain *domain, + const DOM_SID *sid, + void (*cont)(void *private_data, BOOL success, + const char *acct_name, + const char *full_name, + const char *homedir, + const char *shell, + gid_t gid, + uint32 group_rid), + void *private_data) +{ + struct winbindd_request request; + ZERO_STRUCT(request); + request.cmd = WINBINDD_DUAL_USERINFO; + sid_to_string(request.data.sid, sid); + do_async_domain(mem_ctx, domain, &request, query_user_recv, + (void *)cont, private_data); +} + +/* The following uid2sid/gid2sid functions has been contributed by + * Keith Reynolds */ + +static void winbindd_uid2sid_recv(TALLOC_CTX *mem_ctx, BOOL success, + struct winbindd_response *response, + void *c, void *private_data) +{ + void (*cont)(void *priv, BOOL succ, const char *sid) = + (void (*)(void *, BOOL, const char *))c; + + if (!success) { + DEBUG(5, ("Could not trigger uid2sid\n")); + cont(private_data, False, NULL); + return; + } + + if (response->result != WINBINDD_OK) { + DEBUG(5, ("uid2sid returned an error\n")); + cont(private_data, False, NULL); + return; + } + + cont(private_data, True, response->data.sid.sid); +} + +void winbindd_uid2sid_async(TALLOC_CTX *mem_ctx, uid_t uid, + void (*cont)(void *private_data, BOOL success, const char *sid), + void *private_data) +{ + struct winbindd_request request; + + ZERO_STRUCT(request); + request.cmd = WINBINDD_DUAL_UID2SID; + request.data.uid = uid; + do_async(mem_ctx, idmap_child(), &request, winbindd_uid2sid_recv, + (void *)cont, private_data); +} + +enum winbindd_result winbindd_dual_uid2sid(struct winbindd_domain *domain, + struct winbindd_cli_state *state) +{ + DOM_SID sid; + NTSTATUS result; + + DEBUG(3,("[%5lu]: uid to sid %lu\n", + (unsigned long)state->pid, + (unsigned long) state->request.data.uid)); + + /* Find sid for this uid and return it, possibly ask the slow remote idmap */ + result = idmap_uid_to_sid(&sid, state->request.data.uid); + + if (NT_STATUS_IS_OK(result)) { + sid_to_string(state->response.data.sid.sid, &sid); + state->response.data.sid.type = SID_NAME_USER; + return WINBINDD_OK; + } + + return WINBINDD_ERROR; +} + +static void winbindd_gid2sid_recv(TALLOC_CTX *mem_ctx, BOOL success, + struct winbindd_response *response, + void *c, void *private_data) +{ + void (*cont)(void *priv, BOOL succ, const char *sid) = + (void (*)(void *, BOOL, const char *))c; + + if (!success) { + DEBUG(5, ("Could not trigger gid2sid\n")); + cont(private_data, False, NULL); + return; + } + + if (response->result != WINBINDD_OK) { + DEBUG(5, ("gid2sid returned an error\n")); + cont(private_data, False, NULL); + return; + } + + cont(private_data, True, response->data.sid.sid); +} + +void winbindd_gid2sid_async(TALLOC_CTX *mem_ctx, gid_t gid, + void (*cont)(void *private_data, BOOL success, const char *sid), + void *private_data) +{ + struct winbindd_request request; + + ZERO_STRUCT(request); + request.cmd = WINBINDD_DUAL_GID2SID; + request.data.gid = gid; + do_async(mem_ctx, idmap_child(), &request, winbindd_gid2sid_recv, + (void *)cont, private_data); +} + +enum winbindd_result winbindd_dual_gid2sid(struct winbindd_domain *domain, + struct winbindd_cli_state *state) +{ + DOM_SID sid; + NTSTATUS result; + + DEBUG(3,("[%5lu]: gid %lu to sid\n", + (unsigned long)state->pid, + (unsigned long) state->request.data.gid)); + + /* Find sid for this gid and return it, possibly ask the slow remote idmap */ + result = idmap_gid_to_sid(&sid, state->request.data.gid); + + if (NT_STATUS_IS_OK(result)) { + sid_to_string(state->response.data.sid.sid, &sid); + DEBUG(10, ("[%5lu]: retrieved sid: %s\n", + (unsigned long)state->pid, + state->response.data.sid.sid)); + state->response.data.sid.type = SID_NAME_DOM_GRP; + return WINBINDD_OK; + } + + return WINBINDD_ERROR; +} + +static void winbindd_dump_id_maps_recv(TALLOC_CTX *mem_ctx, BOOL success, + struct winbindd_response *response, + void *c, void *private_data) +{ + void (*cont)(void *priv, BOOL succ) = + (void (*)(void *, BOOL))c; + + if (!success) { + DEBUG(5, ("Could not trigger a map dump\n")); + cont(private_data, False); + return; + } + + if (response->result != WINBINDD_OK) { + DEBUG(5, ("idmap dump maps returned an error\n")); + cont(private_data, False); + return; + } + + cont(private_data, True); +} + +void winbindd_dump_maps_async(TALLOC_CTX *mem_ctx, void *data, int size, + void (*cont)(void *private_data, BOOL success), + void *private_data) +{ + struct winbindd_request request; + ZERO_STRUCT(request); + request.cmd = WINBINDD_DUAL_DUMP_MAPS; + request.extra_data.data = (char *)data; + request.extra_len = size; + do_async(mem_ctx, idmap_child(), &request, winbindd_dump_id_maps_recv, + (void *)cont, private_data); +} + +enum winbindd_result winbindd_dual_dump_maps(struct winbindd_domain *domain, + struct winbindd_cli_state *state) +{ + DEBUG(3, ("[%5lu]: dual dump maps\n", (unsigned long)state->pid)); + + idmap_dump_maps((char *)state->request.extra_data.data); + + return WINBINDD_OK; +} + diff --git a/source3/winbindd/winbindd_cache.c b/source3/winbindd/winbindd_cache.c new file mode 100644 index 0000000000..e5090dfacf --- /dev/null +++ b/source3/winbindd/winbindd_cache.c @@ -0,0 +1,3892 @@ +/* + Unix SMB/CIFS implementation. + + Winbind cache backend functions + + Copyright (C) Andrew Tridgell 2001 + Copyright (C) Gerald Carter 2003-2007 + Copyright (C) Volker Lendecke 2005 + Copyright (C) Guenther Deschner 2005 + Copyright (C) Michael Adam 2007 + + 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 3 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, see . +*/ + +#include "includes.h" +#include "winbindd.h" + +#undef DBGC_CLASS +#define DBGC_CLASS DBGC_WINBIND + +#define WINBINDD_CACHE_VERSION 1 +#define WINBINDD_CACHE_VERSION_KEYSTR "WINBINDD_CACHE_VERSION" + +extern struct winbindd_methods reconnect_methods; +extern BOOL opt_nocache; +#ifdef HAVE_ADS +extern struct winbindd_methods ads_methods; +#endif + +/* + * JRA. KEEP THIS LIST UP TO DATE IF YOU ADD CACHE ENTRIES. + * Here are the list of entry types that are *not* stored + * as form struct cache_entry in the cache. + */ + +static const char *non_centry_keys[] = { + "SEQNUM/", + "DR/", + "DE/", + "WINBINDD_OFFLINE", + WINBINDD_CACHE_VERSION_KEYSTR, + NULL +}; + +/************************************************************************ + Is this key a non-centry type ? +************************************************************************/ + +static BOOL is_non_centry_key(TDB_DATA kbuf) +{ + int i; + + if (kbuf.dptr == NULL || kbuf.dsize == 0) { + return False; + } + for (i = 0; non_centry_keys[i] != NULL; i++) { + size_t namelen = strlen(non_centry_keys[i]); + if (kbuf.dsize < namelen) { + continue; + } + if (strncmp(non_centry_keys[i], (const char *)kbuf.dptr, namelen) == 0) { + return True; + } + } + return False; +} + +/* Global online/offline state - False when online. winbindd starts up online + and sets this to true if the first query fails and there's an entry in + the cache tdb telling us to stay offline. */ + +static BOOL global_winbindd_offline_state; + +struct winbind_cache { + TDB_CONTEXT *tdb; +}; + +struct cache_entry { + NTSTATUS status; + uint32 sequence_number; + uint8 *data; + uint32 len, ofs; +}; + +void (*smb_panic_fn)(const char *const why) = smb_panic; + +#define WINBINDD_MAX_CACHE_SIZE (50*1024*1024) + +static struct winbind_cache *wcache; + +void winbindd_check_cache_size(time_t t) +{ + static time_t last_check_time; + struct stat st; + + if (last_check_time == (time_t)0) + last_check_time = t; + + if (t - last_check_time < 60 && t - last_check_time > 0) + return; + + if (wcache == NULL || wcache->tdb == NULL) { + DEBUG(0, ("Unable to check size of tdb cache - cache not open !\n")); + return; + } + + if (fstat(tdb_fd(wcache->tdb), &st) == -1) { + DEBUG(0, ("Unable to check size of tdb cache %s!\n", strerror(errno) )); + return; + } + + if (st.st_size > WINBINDD_MAX_CACHE_SIZE) { + DEBUG(10,("flushing cache due to size (%lu) > (%lu)\n", + (unsigned long)st.st_size, + (unsigned long)WINBINDD_MAX_CACHE_SIZE)); + wcache_flush_cache(); + } +} + +/* get the winbind_cache structure */ +static struct winbind_cache *get_cache(struct winbindd_domain *domain) +{ + struct winbind_cache *ret = wcache; + + /* We have to know what type of domain we are dealing with first. */ + + if ( !domain->initialized ) { + init_dc_connection( domain ); + } + + /* + OK. listen up becasue I'm only going to say this once. + We have the following scenarios to consider + (a) trusted AD domains on a Samba DC, + (b) trusted AD domains and we are joined to a non-kerberos domain + (c) trusted AD domains and we are joined to a kerberos (AD) domain + + For (a) we can always contact the trusted domain using krb5 + since we have the domain trust account password + + For (b) we can only use RPC since we have no way of + getting a krb5 ticket in our own domain + + For (c) we can always use krb5 since we have a kerberos trust + + --jerry + */ + + if (!domain->backend) { +#ifdef HAVE_ADS + struct winbindd_domain *our_domain = domain; + + /* find our domain first so we can figure out if we + are joined to a kerberized domain */ + + if ( !domain->primary ) + our_domain = find_our_domain(); + + if ((our_domain->active_directory || IS_DC) + && domain->active_directory + && !lp_winbind_rpc_only()) { + DEBUG(5,("get_cache: Setting ADS methods for domain %s\n", domain->name)); + domain->backend = &ads_methods; + } else { +#endif /* HAVE_ADS */ + DEBUG(5,("get_cache: Setting MS-RPC methods for domain %s\n", domain->name)); + domain->backend = &reconnect_methods; +#ifdef HAVE_ADS + } +#endif /* HAVE_ADS */ + } + + if (ret) + return ret; + + ret = SMB_XMALLOC_P(struct winbind_cache); + ZERO_STRUCTP(ret); + + wcache = ret; + wcache_flush_cache(); + + return ret; +} + +/* + free a centry structure +*/ +static void centry_free(struct cache_entry *centry) +{ + if (!centry) + return; + SAFE_FREE(centry->data); + free(centry); +} + +static BOOL centry_check_bytes(struct cache_entry *centry, size_t nbytes) +{ + if (centry->len - centry->ofs < nbytes) { + DEBUG(0,("centry corruption? needed %u bytes, have %d\n", + (unsigned int)nbytes, + centry->len - centry->ofs)); + return False; + } + return True; +} + +/* + pull a uint32 from a cache entry +*/ +static uint32 centry_uint32(struct cache_entry *centry) +{ + uint32 ret; + + if (!centry_check_bytes(centry, 4)) { + smb_panic_fn("centry_uint32"); + } + ret = IVAL(centry->data, centry->ofs); + centry->ofs += 4; + return ret; +} + +/* + pull a uint16 from a cache entry +*/ +static uint16 centry_uint16(struct cache_entry *centry) +{ + uint16 ret; + if (!centry_check_bytes(centry, 2)) { + smb_panic_fn("centry_uint16"); + } + ret = CVAL(centry->data, centry->ofs); + centry->ofs += 2; + return ret; +} + +/* + pull a uint8 from a cache entry +*/ +static uint8 centry_uint8(struct cache_entry *centry) +{ + uint8 ret; + if (!centry_check_bytes(centry, 1)) { + smb_panic_fn("centry_uint8"); + } + ret = CVAL(centry->data, centry->ofs); + centry->ofs += 1; + return ret; +} + +/* + pull a NTTIME from a cache entry +*/ +static NTTIME centry_nttime(struct cache_entry *centry) +{ + NTTIME ret; + if (!centry_check_bytes(centry, 8)) { + smb_panic_fn("centry_nttime"); + } + ret = IVAL(centry->data, centry->ofs); + centry->ofs += 4; + ret += (uint64_t)IVAL(centry->data, centry->ofs) << 32; + centry->ofs += 4; + return ret; +} + +/* + pull a time_t from a cache entry. time_t stored portably as a 64-bit time. +*/ +static time_t centry_time(struct cache_entry *centry) +{ + return (time_t)centry_nttime(centry); +} + +/* pull a string from a cache entry, using the supplied + talloc context +*/ +static char *centry_string(struct cache_entry *centry, TALLOC_CTX *mem_ctx) +{ + uint32 len; + char *ret; + + len = centry_uint8(centry); + + if (len == 0xFF) { + /* a deliberate NULL string */ + return NULL; + } + + if (!centry_check_bytes(centry, (size_t)len)) { + smb_panic_fn("centry_string"); + } + + ret = TALLOC_ARRAY(mem_ctx, char, len+1); + if (!ret) { + smb_panic_fn("centry_string out of memory\n"); + } + memcpy(ret,centry->data + centry->ofs, len); + ret[len] = 0; + centry->ofs += len; + return ret; +} + +/* pull a hash16 from a cache entry, using the supplied + talloc context +*/ +static char *centry_hash16(struct cache_entry *centry, TALLOC_CTX *mem_ctx) +{ + uint32 len; + char *ret; + + len = centry_uint8(centry); + + if (len != 16) { + DEBUG(0,("centry corruption? hash len (%u) != 16\n", + len )); + return NULL; + } + + if (!centry_check_bytes(centry, 16)) { + return NULL; + } + + ret = TALLOC_ARRAY(mem_ctx, char, 16); + if (!ret) { + smb_panic_fn("centry_hash out of memory\n"); + } + memcpy(ret,centry->data + centry->ofs, 16); + centry->ofs += 16; + return ret; +} + +/* pull a sid from a cache entry, using the supplied + talloc context +*/ +static BOOL centry_sid(struct cache_entry *centry, TALLOC_CTX *mem_ctx, DOM_SID *sid) +{ + char *sid_string; + sid_string = centry_string(centry, mem_ctx); + if ((sid_string == NULL) || (!string_to_sid(sid, sid_string))) { + return False; + } + return True; +} + + +/* + pull a NTSTATUS from a cache entry +*/ +static NTSTATUS centry_ntstatus(struct cache_entry *centry) +{ + NTSTATUS status; + + status = NT_STATUS(centry_uint32(centry)); + return status; +} + + +/* the server is considered down if it can't give us a sequence number */ +static BOOL wcache_server_down(struct winbindd_domain *domain) +{ + BOOL ret; + + if (!wcache->tdb) + return False; + + ret = (domain->sequence_number == DOM_SEQUENCE_NONE); + + if (ret) + DEBUG(10,("wcache_server_down: server for Domain %s down\n", + domain->name )); + return ret; +} + +static NTSTATUS fetch_cache_seqnum( struct winbindd_domain *domain, time_t now ) +{ + TDB_DATA data; + fstring key; + uint32 time_diff; + + if (!wcache->tdb) { + DEBUG(10,("fetch_cache_seqnum: tdb == NULL\n")); + return NT_STATUS_UNSUCCESSFUL; + } + + fstr_sprintf( key, "SEQNUM/%s", domain->name ); + + data = tdb_fetch_bystring( wcache->tdb, key ); + if ( !data.dptr || data.dsize!=8 ) { + DEBUG(10,("fetch_cache_seqnum: invalid data size key [%s]\n", key )); + return NT_STATUS_UNSUCCESSFUL; + } + + domain->sequence_number = IVAL(data.dptr, 0); + domain->last_seq_check = IVAL(data.dptr, 4); + + SAFE_FREE(data.dptr); + + /* have we expired? */ + + time_diff = now - domain->last_seq_check; + if ( time_diff > lp_winbind_cache_time() ) { + DEBUG(10,("fetch_cache_seqnum: timeout [%s][%u @ %u]\n", + domain->name, domain->sequence_number, + (uint32)domain->last_seq_check)); + return NT_STATUS_UNSUCCESSFUL; + } + + DEBUG(10,("fetch_cache_seqnum: success [%s][%u @ %u]\n", + domain->name, domain->sequence_number, + (uint32)domain->last_seq_check)); + + return NT_STATUS_OK; +} + +static NTSTATUS store_cache_seqnum( struct winbindd_domain *domain ) +{ + TDB_DATA data; + fstring key_str; + uint8 buf[8]; + + if (!wcache->tdb) { + DEBUG(10,("store_cache_seqnum: tdb == NULL\n")); + return NT_STATUS_UNSUCCESSFUL; + } + + fstr_sprintf( key_str, "SEQNUM/%s", domain->name ); + + SIVAL(buf, 0, domain->sequence_number); + SIVAL(buf, 4, domain->last_seq_check); + data.dptr = buf; + data.dsize = 8; + + if ( tdb_store_bystring( wcache->tdb, key_str, data, TDB_REPLACE) == -1 ) { + DEBUG(10,("store_cache_seqnum: tdb_store fail key [%s]\n", key_str )); + return NT_STATUS_UNSUCCESSFUL; + } + + DEBUG(10,("store_cache_seqnum: success [%s][%u @ %u]\n", + domain->name, domain->sequence_number, + (uint32)domain->last_seq_check)); + + return NT_STATUS_OK; +} + +/* + refresh the domain sequence number. If force is True + then always refresh it, no matter how recently we fetched it +*/ + +static void refresh_sequence_number(struct winbindd_domain *domain, BOOL force) +{ + NTSTATUS status; + unsigned time_diff; + time_t t = time(NULL); + unsigned cache_time = lp_winbind_cache_time(); + + if ( IS_DOMAIN_OFFLINE(domain) ) { + return; + } + + get_cache( domain ); + +#if 0 /* JERRY -- disable as the default cache time is now 5 minutes */ + /* trying to reconnect is expensive, don't do it too often */ + if (domain->sequence_number == DOM_SEQUENCE_NONE) { + cache_time *= 8; + } +#endif + + time_diff = t - domain->last_seq_check; + + /* see if we have to refetch the domain sequence number */ + if (!force && (time_diff < cache_time)) { + DEBUG(10, ("refresh_sequence_number: %s time ok\n", domain->name)); + goto done; + } + + /* try to get the sequence number from the tdb cache first */ + /* this will update the timestamp as well */ + + status = fetch_cache_seqnum( domain, t ); + if ( NT_STATUS_IS_OK(status) ) + goto done; + + /* important! make sure that we know if this is a native + mode domain or not. And that we can contact it. */ + + if ( winbindd_can_contact_domain( domain ) ) { + status = domain->backend->sequence_number(domain, + &domain->sequence_number); + } else { + /* just use the current time */ + status = NT_STATUS_OK; + domain->sequence_number = time(NULL); + } + + + /* the above call could have set our domain->backend to NULL when + * coming from offline to online mode, make sure to reinitialize the + * backend - Guenther */ + get_cache( domain ); + + if (!NT_STATUS_IS_OK(status)) { + DEBUG(10,("refresh_sequence_number: failed with %s\n", nt_errstr(status))); + domain->sequence_number = DOM_SEQUENCE_NONE; + } + + domain->last_status = status; + domain->last_seq_check = time(NULL); + + /* save the new sequence number ni the cache */ + store_cache_seqnum( domain ); + +done: + DEBUG(10, ("refresh_sequence_number: %s seq number is now %d\n", + domain->name, domain->sequence_number)); + + return; +} + +/* + decide if a cache entry has expired +*/ +static BOOL centry_expired(struct winbindd_domain *domain, const char *keystr, struct cache_entry *centry) +{ + /* If we've been told to be offline - stay in that state... */ + if (lp_winbind_offline_logon() && global_winbindd_offline_state) { + DEBUG(10,("centry_expired: Key %s for domain %s valid as winbindd is globally offline.\n", + keystr, domain->name )); + return False; + } + + /* when the domain is offline return the cached entry. + * This deals with transient offline states... */ + + if (!domain->online) { + DEBUG(10,("centry_expired: Key %s for domain %s valid as domain is offline.\n", + keystr, domain->name )); + return False; + } + + /* if the server is OK and our cache entry came from when it was down then + the entry is invalid */ + if ((domain->sequence_number != DOM_SEQUENCE_NONE) && + (centry->sequence_number == DOM_SEQUENCE_NONE)) { + DEBUG(10,("centry_expired: Key %s for domain %s invalid sequence.\n", + keystr, domain->name )); + return True; + } + + /* if the server is down or the cache entry is not older than the + current sequence number then it is OK */ + if (wcache_server_down(domain) || + centry->sequence_number == domain->sequence_number) { + DEBUG(10,("centry_expired: Key %s for domain %s is good.\n", + keystr, domain->name )); + return False; + } + + DEBUG(10,("centry_expired: Key %s for domain %s expired\n", + keystr, domain->name )); + + /* it's expired */ + return True; +} + +static struct cache_entry *wcache_fetch_raw(char *kstr) +{ + TDB_DATA data; + struct cache_entry *centry; + TDB_DATA key; + + key = string_tdb_data(kstr); + data = tdb_fetch(wcache->tdb, key); + if (!data.dptr) { + /* a cache miss */ + return NULL; + } + + centry = SMB_XMALLOC_P(struct cache_entry); + centry->data = (unsigned char *)data.dptr; + centry->len = data.dsize; + centry->ofs = 0; + + if (centry->len < 8) { + /* huh? corrupt cache? */ + DEBUG(10,("wcache_fetch_raw: Corrupt cache for key %s (len < 8) ?\n", kstr)); + centry_free(centry); + return NULL; + } + + centry->status = centry_ntstatus(centry); + centry->sequence_number = centry_uint32(centry); + + return centry; +} + +/* + fetch an entry from the cache, with a varargs key. auto-fetch the sequence + number and return status +*/ +static struct cache_entry *wcache_fetch(struct winbind_cache *cache, + struct winbindd_domain *domain, + const char *format, ...) PRINTF_ATTRIBUTE(3,4); +static struct cache_entry *wcache_fetch(struct winbind_cache *cache, + struct winbindd_domain *domain, + const char *format, ...) +{ + va_list ap; + char *kstr; + struct cache_entry *centry; + + if (opt_nocache) { + return NULL; + } + + refresh_sequence_number(domain, False); + + va_start(ap, format); + smb_xvasprintf(&kstr, format, ap); + va_end(ap); + + centry = wcache_fetch_raw(kstr); + if (centry == NULL) { + free(kstr); + return NULL; + } + + if (centry_expired(domain, kstr, centry)) { + + DEBUG(10,("wcache_fetch: entry %s expired for domain %s\n", + kstr, domain->name )); + + centry_free(centry); + free(kstr); + return NULL; + } + + DEBUG(10,("wcache_fetch: returning entry %s for domain %s\n", + kstr, domain->name )); + + free(kstr); + return centry; +} + +static void wcache_delete(const char *format, ...) PRINTF_ATTRIBUTE(1,2); +static void wcache_delete(const char *format, ...) +{ + va_list ap; + char *kstr; + TDB_DATA key; + + va_start(ap, format); + smb_xvasprintf(&kstr, format, ap); + va_end(ap); + + key = string_tdb_data(kstr); + + tdb_delete(wcache->tdb, key); + free(kstr); +} + +/* + make sure we have at least len bytes available in a centry +*/ +static void centry_expand(struct cache_entry *centry, uint32 len) +{ + if (centry->len - centry->ofs >= len) + return; + centry->len *= 2; + centry->data = SMB_REALLOC_ARRAY(centry->data, unsigned char, + centry->len); + if (!centry->data) { + DEBUG(0,("out of memory: needed %d bytes in centry_expand\n", centry->len)); + smb_panic_fn("out of memory in centry_expand"); + } +} + +/* + push a uint32 into a centry +*/ +static void centry_put_uint32(struct cache_entry *centry, uint32 v) +{ + centry_expand(centry, 4); + SIVAL(centry->data, centry->ofs, v); + centry->ofs += 4; +} + +/* + push a uint16 into a centry +*/ +static void centry_put_uint16(struct cache_entry *centry, uint16 v) +{ + centry_expand(centry, 2); + SIVAL(centry->data, centry->ofs, v); + centry->ofs += 2; +} + +/* + push a uint8 into a centry +*/ +static void centry_put_uint8(struct cache_entry *centry, uint8 v) +{ + centry_expand(centry, 1); + SCVAL(centry->data, centry->ofs, v); + centry->ofs += 1; +} + +/* + push a string into a centry + */ +static void centry_put_string(struct cache_entry *centry, const char *s) +{ + int len; + + if (!s) { + /* null strings are marked as len 0xFFFF */ + centry_put_uint8(centry, 0xFF); + return; + } + + len = strlen(s); + /* can't handle more than 254 char strings. Truncating is probably best */ + if (len > 254) { + DEBUG(10,("centry_put_string: truncating len (%d) to: 254\n", len)); + len = 254; + } + centry_put_uint8(centry, len); + centry_expand(centry, len); + memcpy(centry->data + centry->ofs, s, len); + centry->ofs += len; +} + +/* + push a 16 byte hash into a centry - treat as 16 byte string. + */ +static void centry_put_hash16(struct cache_entry *centry, const uint8 val[16]) +{ + centry_put_uint8(centry, 16); + centry_expand(centry, 16); + memcpy(centry->data + centry->ofs, val, 16); + centry->ofs += 16; +} + +static void centry_put_sid(struct cache_entry *centry, const DOM_SID *sid) +{ + fstring sid_string; + centry_put_string(centry, sid_to_string(sid_string, sid)); +} + + +/* + put NTSTATUS into a centry +*/ +static void centry_put_ntstatus(struct cache_entry *centry, NTSTATUS status) +{ + uint32 status_value = NT_STATUS_V(status); + centry_put_uint32(centry, status_value); +} + + +/* + push a NTTIME into a centry +*/ +static void centry_put_nttime(struct cache_entry *centry, NTTIME nt) +{ + centry_expand(centry, 8); + SIVAL(centry->data, centry->ofs, nt & 0xFFFFFFFF); + centry->ofs += 4; + SIVAL(centry->data, centry->ofs, nt >> 32); + centry->ofs += 4; +} + +/* + push a time_t into a centry - use a 64 bit size. + NTTIME here is being used as a convenient 64-bit size. +*/ +static void centry_put_time(struct cache_entry *centry, time_t t) +{ + NTTIME nt = (NTTIME)t; + centry_put_nttime(centry, nt); +} + +/* + start a centry for output. When finished, call centry_end() +*/ +struct cache_entry *centry_start(struct winbindd_domain *domain, NTSTATUS status) +{ + struct cache_entry *centry; + + if (!wcache->tdb) + return NULL; + + centry = SMB_XMALLOC_P(struct cache_entry); + + centry->len = 8192; /* reasonable default */ + centry->data = SMB_XMALLOC_ARRAY(uint8, centry->len); + centry->ofs = 0; + centry->sequence_number = domain->sequence_number; + centry_put_ntstatus(centry, status); + centry_put_uint32(centry, centry->sequence_number); + return centry; +} + +/* + finish a centry and write it to the tdb +*/ +static void centry_end(struct cache_entry *centry, const char *format, ...) PRINTF_ATTRIBUTE(2,3); +static void centry_end(struct cache_entry *centry, const char *format, ...) +{ + va_list ap; + char *kstr; + TDB_DATA key, data; + + if (opt_nocache) { + return; + } + + va_start(ap, format); + smb_xvasprintf(&kstr, format, ap); + va_end(ap); + + key = string_tdb_data(kstr); + data.dptr = centry->data; + data.dsize = centry->ofs; + + tdb_store(wcache->tdb, key, data, TDB_REPLACE); + free(kstr); +} + +static void wcache_save_name_to_sid(struct winbindd_domain *domain, + NTSTATUS status, const char *domain_name, + const char *name, const DOM_SID *sid, + enum lsa_SidType type) +{ + struct cache_entry *centry; + fstring uname; + + centry = centry_start(domain, status); + if (!centry) + return; + centry_put_uint32(centry, type); + centry_put_sid(centry, sid); + fstrcpy(uname, name); + strupper_m(uname); + centry_end(centry, "NS/%s/%s", domain_name, uname); + DEBUG(10,("wcache_save_name_to_sid: %s\\%s -> %s (%s)\n", domain_name, uname, + sid_string_static(sid), nt_errstr(status))); + centry_free(centry); +} + +static void wcache_save_sid_to_name(struct winbindd_domain *domain, NTSTATUS status, + const DOM_SID *sid, const char *domain_name, const char *name, enum lsa_SidType type) +{ + struct cache_entry *centry; + fstring sid_string; + + centry = centry_start(domain, status); + if (!centry) + return; + + if (NT_STATUS_IS_OK(status)) { + centry_put_uint32(centry, type); + centry_put_string(centry, domain_name); + centry_put_string(centry, name); + } + + centry_end(centry, "SN/%s", sid_to_string(sid_string, sid)); + DEBUG(10,("wcache_save_sid_to_name: %s -> %s (%s)\n", sid_string, + name, nt_errstr(status))); + centry_free(centry); +} + + +static void wcache_save_user(struct winbindd_domain *domain, NTSTATUS status, WINBIND_USERINFO *info) +{ + struct cache_entry *centry; + fstring sid_string; + + if (is_null_sid(&info->user_sid)) { + return; + } + + centry = centry_start(domain, status); + if (!centry) + return; + centry_put_string(centry, info->acct_name); + centry_put_string(centry, info->full_name); + centry_put_string(centry, info->homedir); + centry_put_string(centry, info->shell); + centry_put_uint32(centry, info->primary_gid); + centry_put_sid(centry, &info->user_sid); + centry_put_sid(centry, &info->group_sid); + centry_end(centry, "U/%s", sid_to_string(sid_string, &info->user_sid)); + DEBUG(10,("wcache_save_user: %s (acct_name %s)\n", sid_string, info->acct_name)); + centry_free(centry); +} + +static void wcache_save_lockout_policy(struct winbindd_domain *domain, NTSTATUS status, SAM_UNK_INFO_12 *lockout_policy) +{ + struct cache_entry *centry; + + centry = centry_start(domain, status); + if (!centry) + return; + + centry_put_nttime(centry, lockout_policy->duration); + centry_put_nttime(centry, lockout_policy->reset_count); + centry_put_uint16(centry, lockout_policy->bad_attempt_lockout); + + centry_end(centry, "LOC_POL/%s", domain->name); + + DEBUG(10,("wcache_save_lockout_policy: %s\n", domain->name)); + + centry_free(centry); +} + +static void wcache_save_password_policy(struct winbindd_domain *domain, NTSTATUS status, SAM_UNK_INFO_1 *policy) +{ + struct cache_entry *centry; + + centry = centry_start(domain, status); + if (!centry) + return; + + centry_put_uint16(centry, policy->min_length_password); + centry_put_uint16(centry, policy->password_history); + centry_put_uint32(centry, policy->password_properties); + centry_put_nttime(centry, policy->expire); + centry_put_nttime(centry, policy->min_passwordage); + + centry_end(centry, "PWD_POL/%s", domain->name); + + DEBUG(10,("wcache_save_password_policy: %s\n", domain->name)); + + centry_free(centry); +} + +NTSTATUS wcache_cached_creds_exist(struct winbindd_domain *domain, const DOM_SID *sid) +{ + struct winbind_cache *cache = get_cache(domain); + TDB_DATA data; + fstring key_str; + uint32 rid; + + if (!cache->tdb) { + return NT_STATUS_INTERNAL_DB_ERROR; + } + + if (is_null_sid(sid)) { + return NT_STATUS_INVALID_SID; + } + + if (!(sid_peek_rid(sid, &rid)) || (rid == 0)) { + return NT_STATUS_INVALID_SID; + } + + fstr_sprintf(key_str, "CRED/%s", sid_string_static(sid)); + + data = tdb_fetch(cache->tdb, string_tdb_data(key_str)); + if (!data.dptr) { + return NT_STATUS_OBJECT_NAME_NOT_FOUND; + } + + SAFE_FREE(data.dptr); + return NT_STATUS_OK; +} + +/* Lookup creds for a SID - copes with old (unsalted) creds as well + as new salted ones. */ + +NTSTATUS wcache_get_creds(struct winbindd_domain *domain, + TALLOC_CTX *mem_ctx, + const DOM_SID *sid, + const uint8 **cached_nt_pass, + const uint8 **cached_salt) +{ + struct winbind_cache *cache = get_cache(domain); + struct cache_entry *centry = NULL; + NTSTATUS status; + time_t t; + uint32 rid; + + if (!cache->tdb) { + return NT_STATUS_INTERNAL_DB_ERROR; + } + + if (is_null_sid(sid)) { + return NT_STATUS_INVALID_SID; + } + + if (!(sid_peek_rid(sid, &rid)) || (rid == 0)) { + return NT_STATUS_INVALID_SID; + } + + /* Try and get a salted cred first. If we can't + fall back to an unsalted cred. */ + + centry = wcache_fetch(cache, domain, "CRED/%s", sid_string_static(sid)); + if (!centry) { + DEBUG(10,("wcache_get_creds: entry for [CRED/%s] not found\n", + sid_string_static(sid))); + return NT_STATUS_OBJECT_NAME_NOT_FOUND; + } + + t = centry_time(centry); + + /* In the salted case this isn't actually the nt_hash itself, + but the MD5 of the salt + nt_hash. Let the caller + sort this out. It can tell as we only return the cached_salt + if we are returning a salted cred. */ + + *cached_nt_pass = (const uint8 *)centry_hash16(centry, mem_ctx); + if (*cached_nt_pass == NULL) { + const char *sidstr = sid_string_static(sid); + + /* Bad (old) cred cache. Delete and pretend we + don't have it. */ + DEBUG(0,("wcache_get_creds: bad entry for [CRED/%s] - deleting\n", + sidstr)); + wcache_delete("CRED/%s", sidstr); + centry_free(centry); + return NT_STATUS_OBJECT_NAME_NOT_FOUND; + } + + /* We only have 17 bytes more data in the salted cred case. */ + if (centry->len - centry->ofs == 17) { + *cached_salt = (const uint8 *)centry_hash16(centry, mem_ctx); + } else { + *cached_salt = NULL; + } + + dump_data_pw("cached_nt_pass", *cached_nt_pass, NT_HASH_LEN); + if (*cached_salt) { + dump_data_pw("cached_salt", *cached_salt, NT_HASH_LEN); + } + + status = centry->status; + + DEBUG(10,("wcache_get_creds: [Cached] - cached creds for user %s status: %s\n", + sid_string_static(sid), nt_errstr(status) )); + + centry_free(centry); + return status; +} + +/* Store creds for a SID - only writes out new salted ones. */ + +NTSTATUS wcache_save_creds(struct winbindd_domain *domain, + TALLOC_CTX *mem_ctx, + const DOM_SID *sid, + const uint8 nt_pass[NT_HASH_LEN]) +{ + struct cache_entry *centry; + fstring sid_string; + uint32 rid; + uint8 cred_salt[NT_HASH_LEN]; + uint8 salted_hash[NT_HASH_LEN]; + + if (is_null_sid(sid)) { + return NT_STATUS_INVALID_SID; + } + + if (!(sid_peek_rid(sid, &rid)) || (rid == 0)) { + return NT_STATUS_INVALID_SID; + } + + centry = centry_start(domain, NT_STATUS_OK); + if (!centry) { + return NT_STATUS_INTERNAL_DB_ERROR; + } + + dump_data_pw("nt_pass", nt_pass, NT_HASH_LEN); + + centry_put_time(centry, time(NULL)); + + /* Create a salt and then salt the hash. */ + generate_random_buffer(cred_salt, NT_HASH_LEN); + E_md5hash(cred_salt, nt_pass, salted_hash); + + centry_put_hash16(centry, salted_hash); + centry_put_hash16(centry, cred_salt); + centry_end(centry, "CRED/%s", sid_to_string(sid_string, sid)); + + DEBUG(10,("wcache_save_creds: %s\n", sid_string)); + + centry_free(centry); + + return NT_STATUS_OK; +} + + +/* Query display info. This is the basic user list fn */ +static NTSTATUS query_user_list(struct winbindd_domain *domain, + TALLOC_CTX *mem_ctx, + uint32 *num_entries, + WINBIND_USERINFO **info) +{ + struct winbind_cache *cache = get_cache(domain); + struct cache_entry *centry = NULL; + NTSTATUS status; + unsigned int i, retry; + + if (!cache->tdb) + goto do_query; + + centry = wcache_fetch(cache, domain, "UL/%s", domain->name); + if (!centry) + goto do_query; + + *num_entries = centry_uint32(centry); + + if (*num_entries == 0) + goto do_cached; + + (*info) = TALLOC_ARRAY(mem_ctx, WINBIND_USERINFO, *num_entries); + if (! (*info)) { + smb_panic_fn("query_user_list out of memory"); + } + for (i=0; i<(*num_entries); i++) { + (*info)[i].acct_name = centry_string(centry, mem_ctx); + (*info)[i].full_name = centry_string(centry, mem_ctx); + (*info)[i].homedir = centry_string(centry, mem_ctx); + (*info)[i].shell = centry_string(centry, mem_ctx); + centry_sid(centry, mem_ctx, &(*info)[i].user_sid); + centry_sid(centry, mem_ctx, &(*info)[i].group_sid); + } + +do_cached: + status = centry->status; + + DEBUG(10,("query_user_list: [Cached] - cached list for domain %s status: %s\n", + domain->name, nt_errstr(status) )); + + centry_free(centry); + return status; + +do_query: + *num_entries = 0; + *info = NULL; + + /* Return status value returned by seq number check */ + + if (!NT_STATUS_IS_OK(domain->last_status)) + return domain->last_status; + + /* Put the query_user_list() in a retry loop. There appears to be + * some bug either with Windows 2000 or Samba's handling of large + * rpc replies. This manifests itself as sudden disconnection + * at a random point in the enumeration of a large (60k) user list. + * The retry loop simply tries the operation again. )-: It's not + * pretty but an acceptable workaround until we work out what the + * real problem is. */ + + retry = 0; + do { + + DEBUG(10,("query_user_list: [Cached] - doing backend query for list for domain %s\n", + domain->name )); + + status = domain->backend->query_user_list(domain, mem_ctx, num_entries, info); + if (!NT_STATUS_IS_OK(status)) { + DEBUG(3, ("query_user_list: returned 0x%08x, " + "retrying\n", NT_STATUS_V(status))); + } + if (NT_STATUS_EQUAL(status, NT_STATUS_UNSUCCESSFUL)) { + DEBUG(3, ("query_user_list: flushing " + "connection cache\n")); + invalidate_cm_connection(&domain->conn); + } + + } while (NT_STATUS_V(status) == NT_STATUS_V(NT_STATUS_UNSUCCESSFUL) && + (retry++ < 5)); + + /* and save it */ + refresh_sequence_number(domain, False); + centry = centry_start(domain, status); + if (!centry) + goto skip_save; + centry_put_uint32(centry, *num_entries); + for (i=0; i<(*num_entries); i++) { + centry_put_string(centry, (*info)[i].acct_name); + centry_put_string(centry, (*info)[i].full_name); + centry_put_string(centry, (*info)[i].homedir); + centry_put_string(centry, (*info)[i].shell); + centry_put_sid(centry, &(*info)[i].user_sid); + centry_put_sid(centry, &(*info)[i].group_sid); + if (domain->backend && domain->backend->consistent) { + /* when the backend is consistent we can pre-prime some mappings */ + wcache_save_name_to_sid(domain, NT_STATUS_OK, + domain->name, + (*info)[i].acct_name, + &(*info)[i].user_sid, + SID_NAME_USER); + wcache_save_sid_to_name(domain, NT_STATUS_OK, + &(*info)[i].user_sid, + domain->name, + (*info)[i].acct_name, + SID_NAME_USER); + wcache_save_user(domain, NT_STATUS_OK, &(*info)[i]); + } + } + centry_end(centry, "UL/%s", domain->name); + centry_free(centry); + +skip_save: + return status; +} + +/* list all domain groups */ +static NTSTATUS enum_dom_groups(struct winbindd_domain *domain, + TALLOC_CTX *mem_ctx, + uint32 *num_entries, + struct acct_info **info) +{ + struct winbind_cache *cache = get_cache(domain); + struct cache_entry *centry = NULL; + NTSTATUS status; + unsigned int i; + + if (!cache->tdb) + goto do_query; + + centry = wcache_fetch(cache, domain, "GL/%s/domain", domain->name); + if (!centry) + goto do_query; + + *num_entries = centry_uint32(centry); + + if (*num_entries == 0) + goto do_cached; + + (*info) = TALLOC_ARRAY(mem_ctx, struct acct_info, *num_entries); + if (! (*info)) { + smb_panic_fn("enum_dom_groups out of memory"); + } + for (i=0; i<(*num_entries); i++) { + fstrcpy((*info)[i].acct_name, centry_string(centry, mem_ctx)); + fstrcpy((*info)[i].acct_desc, centry_string(centry, mem_ctx)); + (*info)[i].rid = centry_uint32(centry); + } + +do_cached: + status = centry->status; + + DEBUG(10,("enum_dom_groups: [Cached] - cached list for domain %s status: %s\n", + domain->name, nt_errstr(status) )); + + centry_free(centry); + return status; + +do_query: + *num_entries = 0; + *info = NULL; + + /* Return status value returned by seq number check */ + + if (!NT_STATUS_IS_OK(domain->last_status)) + return domain->last_status; + + DEBUG(10,("enum_dom_groups: [Cached] - doing backend query for list for domain %s\n", + domain->name )); + + status = domain->backend->enum_dom_groups(domain, mem_ctx, num_entries, info); + + /* and save it */ + refresh_sequence_number(domain, False); + centry = centry_start(domain, status); + if (!centry) + goto skip_save; + centry_put_uint32(centry, *num_entries); + for (i=0; i<(*num_entries); i++) { + centry_put_string(centry, (*info)[i].acct_name); + centry_put_string(centry, (*info)[i].acct_desc); + centry_put_uint32(centry, (*info)[i].rid); + } + centry_end(centry, "GL/%s/domain", domain->name); + centry_free(centry); + +skip_save: + return status; +} + +/* list all domain groups */ +static NTSTATUS enum_local_groups(struct winbindd_domain *domain, + TALLOC_CTX *mem_ctx, + uint32 *num_entries, + struct acct_info **info) +{ + struct winbind_cache *cache = get_cache(domain); + struct cache_entry *centry = NULL; + NTSTATUS status; + unsigned int i; + + if (!cache->tdb) + goto do_query; + + centry = wcache_fetch(cache, domain, "GL/%s/local", domain->name); + if (!centry) + goto do_query; + + *num_entries = centry_uint32(centry); + + if (*num_entries == 0) + goto do_cached; + + (*info) = TALLOC_ARRAY(mem_ctx, struct acct_info, *num_entries); + if (! (*info)) { + smb_panic_fn("enum_dom_groups out of memory"); + } + for (i=0; i<(*num_entries); i++) { + fstrcpy((*info)[i].acct_name, centry_string(centry, mem_ctx)); + fstrcpy((*info)[i].acct_desc, centry_string(centry, mem_ctx)); + (*info)[i].rid = centry_uint32(centry); + } + +do_cached: + + /* If we are returning cached data and the domain controller + is down then we don't know whether the data is up to date + or not. Return NT_STATUS_MORE_PROCESSING_REQUIRED to + indicate this. */ + + if (wcache_server_down(domain)) { + DEBUG(10, ("enum_local_groups: returning cached user list and server was down\n")); + status = NT_STATUS_MORE_PROCESSING_REQUIRED; + } else + status = centry->status; + + DEBUG(10,("enum_local_groups: [Cached] - cached list for domain %s status: %s\n", + domain->name, nt_errstr(status) )); + + centry_free(centry); + return status; + +do_query: + *num_entries = 0; + *info = NULL; + + /* Return status value returned by seq number check */ + + if (!NT_STATUS_IS_OK(domain->last_status)) + return domain->last_status; + + DEBUG(10,("enum_local_groups: [Cached] - doing backend query for list for domain %s\n", + domain->name )); + + status = domain->backend->enum_local_groups(domain, mem_ctx, num_entries, info); + + /* and save it */ + refresh_sequence_number(domain, False); + centry = centry_start(domain, status); + if (!centry) + goto skip_save; + centry_put_uint32(centry, *num_entries); + for (i=0; i<(*num_entries); i++) { + centry_put_string(centry, (*info)[i].acct_name); + centry_put_string(centry, (*info)[i].acct_desc); + centry_put_uint32(centry, (*info)[i].rid); + } + centry_end(centry, "GL/%s/local", domain->name); + centry_free(centry); + +skip_save: + return status; +} + +/* convert a single name to a sid in a domain */ +static NTSTATUS name_to_sid(struct winbindd_domain *domain, + TALLOC_CTX *mem_ctx, + enum winbindd_cmd orig_cmd, + const char *domain_name, + const char *name, + DOM_SID *sid, + enum lsa_SidType *type) +{ + struct winbind_cache *cache = get_cache(domain); + struct cache_entry *centry = NULL; + NTSTATUS status; + fstring uname; + + if (!cache->tdb) + goto do_query; + + fstrcpy(uname, name); + strupper_m(uname); + centry = wcache_fetch(cache, domain, "NS/%s/%s", domain_name, uname); + if (!centry) + goto do_query; + + status = centry->status; + if (NT_STATUS_IS_OK(status)) { + *type = (enum lsa_SidType)centry_uint32(centry); + centry_sid(centry, mem_ctx, sid); + } + + DEBUG(10,("name_to_sid: [Cached] - cached name for domain %s status: %s\n", + domain->name, nt_errstr(status) )); + + centry_free(centry); + return status; + +do_query: + ZERO_STRUCTP(sid); + + /* If the seq number check indicated that there is a problem + * with this DC, then return that status... except for + * access_denied. This is special because the dc may be in + * "restrict anonymous = 1" mode, in which case it will deny + * most unauthenticated operations, but *will* allow the LSA + * name-to-sid that we try as a fallback. */ + + if (!(NT_STATUS_IS_OK(domain->last_status) + || NT_STATUS_EQUAL(domain->last_status, NT_STATUS_ACCESS_DENIED))) + return domain->last_status; + + DEBUG(10,("name_to_sid: [Cached] - doing backend query for name for domain %s\n", + domain->name )); + + status = domain->backend->name_to_sid(domain, mem_ctx, orig_cmd, + domain_name, name, sid, type); + + /* and save it */ + refresh_sequence_number(domain, False); + + if (domain->online && + (NT_STATUS_IS_OK(status) || NT_STATUS_EQUAL(status, NT_STATUS_NONE_MAPPED))) { + wcache_save_name_to_sid(domain, status, domain_name, name, sid, *type); + + /* Only save the reverse mapping if this was not a UPN */ + if (!strchr(name, '@')) { + strupper_m(CONST_DISCARD(char *,domain_name)); + strlower_m(CONST_DISCARD(char *,name)); + wcache_save_sid_to_name(domain, status, sid, domain_name, name, *type); + } + } + + return status; +} + +/* convert a sid to a user or group name. The sid is guaranteed to be in the domain + given */ +static NTSTATUS sid_to_name(struct winbindd_domain *domain, + TALLOC_CTX *mem_ctx, + const DOM_SID *sid, + char **domain_name, + char **name, + enum lsa_SidType *type) +{ + struct winbind_cache *cache = get_cache(domain); + struct cache_entry *centry = NULL; + NTSTATUS status; + fstring sid_string; + + if (!cache->tdb) + goto do_query; + + centry = wcache_fetch(cache, domain, "SN/%s", sid_to_string(sid_string, sid)); + if (!centry) + goto do_query; + + status = centry->status; + if (NT_STATUS_IS_OK(status)) { + *type = (enum lsa_SidType)centry_uint32(centry); + *domain_name = centry_string(centry, mem_ctx); + *name = centry_string(centry, mem_ctx); + } + + DEBUG(10,("sid_to_name: [Cached] - cached name for domain %s status: %s\n", + domain->name, nt_errstr(status) )); + + centry_free(centry); + return status; + +do_query: + *name = NULL; + *domain_name = NULL; + + /* If the seq number check indicated that there is a problem + * with this DC, then return that status... except for + * access_denied. This is special because the dc may be in + * "restrict anonymous = 1" mode, in which case it will deny + * most unauthenticated operations, but *will* allow the LSA + * sid-to-name that we try as a fallback. */ + + if (!(NT_STATUS_IS_OK(domain->last_status) + || NT_STATUS_EQUAL(domain->last_status, NT_STATUS_ACCESS_DENIED))) + return domain->last_status; + + DEBUG(10,("sid_to_name: [Cached] - doing backend query for name for domain %s\n", + domain->name )); + + status = domain->backend->sid_to_name(domain, mem_ctx, sid, domain_name, name, type); + + /* and save it */ + refresh_sequence_number(domain, False); + wcache_save_sid_to_name(domain, status, sid, *domain_name, *name, *type); + + /* We can't save the name to sid mapping here, as with sid history a + * later name2sid would give the wrong sid. */ + + return status; +} + +static NTSTATUS rids_to_names(struct winbindd_domain *domain, + TALLOC_CTX *mem_ctx, + const DOM_SID *domain_sid, + uint32 *rids, + size_t num_rids, + char **domain_name, + char ***names, + enum lsa_SidType **types) +{ + struct winbind_cache *cache = get_cache(domain); + size_t i; + NTSTATUS result = NT_STATUS_UNSUCCESSFUL; + BOOL have_mapped; + BOOL have_unmapped; + + *domain_name = NULL; + *names = NULL; + *types = NULL; + + if (!cache->tdb) { + goto do_query; + } + + if (num_rids == 0) { + return NT_STATUS_OK; + } + + *names = TALLOC_ARRAY(mem_ctx, char *, num_rids); + *types = TALLOC_ARRAY(mem_ctx, enum lsa_SidType, num_rids); + + if ((*names == NULL) || (*types == NULL)) { + result = NT_STATUS_NO_MEMORY; + goto error; + } + + have_mapped = have_unmapped = False; + + for (i=0; istatus)) { + char *dom; + have_mapped = True; + (*types)[i] = (enum lsa_SidType)centry_uint32(centry); + + dom = centry_string(centry, mem_ctx); + if (*domain_name == NULL) { + *domain_name = dom; + } else { + talloc_free(dom); + } + + (*names)[i] = centry_string(centry, *names); + + } else if (NT_STATUS_EQUAL(centry->status, NT_STATUS_NONE_MAPPED)) { + have_unmapped = True; + + } else { + /* something's definitely wrong */ + result = centry->status; + goto error; + } + + centry_free(centry); + } + + if (!have_mapped) { + return NT_STATUS_NONE_MAPPED; + } + if (!have_unmapped) { + return NT_STATUS_OK; + } + return STATUS_SOME_UNMAPPED; + + do_query: + + TALLOC_FREE(*names); + TALLOC_FREE(*types); + + result = domain->backend->rids_to_names(domain, mem_ctx, domain_sid, + rids, num_rids, domain_name, + names, types); + + /* + None of the queried rids has been found so save all negative entries + */ + if (NT_STATUS_EQUAL(result, NT_STATUS_NONE_MAPPED)) { + for (i = 0; i < num_rids; i++) { + DOM_SID sid; + const char *name = ""; + const enum lsa_SidType type = SID_NAME_UNKNOWN; + NTSTATUS status = NT_STATUS_NONE_MAPPED; + + if (!sid_compose(&sid, domain_sid, rids[i])) { + return NT_STATUS_INTERNAL_ERROR; + } + + wcache_save_sid_to_name(domain, status, &sid, *domain_name, + name, type); + } + + return result; + } + + /* + Some or all of the queried rids have been found. + */ + if (!NT_STATUS_IS_OK(result) && + !NT_STATUS_EQUAL(result, STATUS_SOME_UNMAPPED)) { + return result; + } + + refresh_sequence_number(domain, False); + + for (i=0; itdb) + goto do_query; + + centry = wcache_fetch(cache, domain, "U/%s", sid_string_static(user_sid)); + + /* If we have an access denied cache entry and a cached info3 in the + samlogon cache then do a query. This will force the rpc back end + to return the info3 data. */ + + if (NT_STATUS_V(domain->last_status) == NT_STATUS_V(NT_STATUS_ACCESS_DENIED) && + netsamlogon_cache_have(user_sid)) { + DEBUG(10, ("query_user: cached access denied and have cached info3\n")); + domain->last_status = NT_STATUS_OK; + centry_free(centry); + goto do_query; + } + + if (!centry) + goto do_query; + + /* if status is not ok then this is a negative hit + and the rest of the data doesn't matter */ + status = centry->status; + if (NT_STATUS_IS_OK(status)) { + info->acct_name = centry_string(centry, mem_ctx); + info->full_name = centry_string(centry, mem_ctx); + info->homedir = centry_string(centry, mem_ctx); + info->shell = centry_string(centry, mem_ctx); + info->primary_gid = centry_uint32(centry); + centry_sid(centry, mem_ctx, &info->user_sid); + centry_sid(centry, mem_ctx, &info->group_sid); + } + + DEBUG(10,("query_user: [Cached] - cached info for domain %s status: %s\n", + domain->name, nt_errstr(status) )); + + centry_free(centry); + return status; + +do_query: + ZERO_STRUCTP(info); + + /* Return status value returned by seq number check */ + + if (!NT_STATUS_IS_OK(domain->last_status)) + return domain->last_status; + + DEBUG(10,("query_user: [Cached] - doing backend query for info for domain %s\n", + domain->name )); + + status = domain->backend->query_user(domain, mem_ctx, user_sid, info); + + /* and save it */ + refresh_sequence_number(domain, False); + wcache_save_user(domain, status, info); + + return status; +} + + +/* Lookup groups a user is a member of. */ +static NTSTATUS lookup_usergroups(struct winbindd_domain *domain, + TALLOC_CTX *mem_ctx, + const DOM_SID *user_sid, + uint32 *num_groups, DOM_SID **user_gids) +{ + struct winbind_cache *cache = get_cache(domain); + struct cache_entry *centry = NULL; + NTSTATUS status; + unsigned int i; + fstring sid_string; + + if (!cache->tdb) + goto do_query; + + centry = wcache_fetch(cache, domain, "UG/%s", sid_to_string(sid_string, user_sid)); + + /* If we have an access denied cache entry and a cached info3 in the + samlogon cache then do a query. This will force the rpc back end + to return the info3 data. */ + + if (NT_STATUS_V(domain->last_status) == NT_STATUS_V(NT_STATUS_ACCESS_DENIED) && + netsamlogon_cache_have(user_sid)) { + DEBUG(10, ("lookup_usergroups: cached access denied and have cached info3\n")); + domain->last_status = NT_STATUS_OK; + centry_free(centry); + goto do_query; + } + + if (!centry) + goto do_query; + + *num_groups = centry_uint32(centry); + + if (*num_groups == 0) + goto do_cached; + + (*user_gids) = TALLOC_ARRAY(mem_ctx, DOM_SID, *num_groups); + if (! (*user_gids)) { + smb_panic_fn("lookup_usergroups out of memory"); + } + for (i=0; i<(*num_groups); i++) { + centry_sid(centry, mem_ctx, &(*user_gids)[i]); + } + +do_cached: + status = centry->status; + + DEBUG(10,("lookup_usergroups: [Cached] - cached info for domain %s status: %s\n", + domain->name, nt_errstr(status) )); + + centry_free(centry); + return status; + +do_query: + (*num_groups) = 0; + (*user_gids) = NULL; + + /* Return status value returned by seq number check */ + + if (!NT_STATUS_IS_OK(domain->last_status)) + return domain->last_status; + + DEBUG(10,("lookup_usergroups: [Cached] - doing backend query for info for domain %s\n", + domain->name )); + + status = domain->backend->lookup_usergroups(domain, mem_ctx, user_sid, num_groups, user_gids); + + if ( NT_STATUS_EQUAL(status, NT_STATUS_SYNCHRONIZATION_REQUIRED) ) + goto skip_save; + + /* and save it */ + refresh_sequence_number(domain, False); + centry = centry_start(domain, status); + if (!centry) + goto skip_save; + + centry_put_uint32(centry, *num_groups); + for (i=0; i<(*num_groups); i++) { + centry_put_sid(centry, &(*user_gids)[i]); + } + + centry_end(centry, "UG/%s", sid_to_string(sid_string, user_sid)); + centry_free(centry); + +skip_save: + return status; +} + +static NTSTATUS lookup_useraliases(struct winbindd_domain *domain, + TALLOC_CTX *mem_ctx, + uint32 num_sids, const DOM_SID *sids, + uint32 *num_aliases, uint32 **alias_rids) +{ + struct winbind_cache *cache = get_cache(domain); + struct cache_entry *centry = NULL; + NTSTATUS status; + char *sidlist = talloc_strdup(mem_ctx, ""); + int i; + + if (!cache->tdb) + goto do_query; + + if (num_sids == 0) { + *num_aliases = 0; + *alias_rids = NULL; + return NT_STATUS_OK; + } + + /* We need to cache indexed by the whole list of SIDs, the aliases + * resulting might come from any of the SIDs. */ + + for (i=0; istatus; + + DEBUG(10,("lookup_useraliases: [Cached] - cached info for domain: %s " + "status %s\n", domain->name, nt_errstr(status))); + + centry_free(centry); + return status; + + do_query: + (*num_aliases) = 0; + (*alias_rids) = NULL; + + if (!NT_STATUS_IS_OK(domain->last_status)) + return domain->last_status; + + DEBUG(10,("lookup_usergroups: [Cached] - doing backend query for info " + "for domain %s\n", domain->name )); + + status = domain->backend->lookup_useraliases(domain, mem_ctx, + num_sids, sids, + num_aliases, alias_rids); + + /* and save it */ + refresh_sequence_number(domain, False); + centry = centry_start(domain, status); + if (!centry) + goto skip_save; + centry_put_uint32(centry, *num_aliases); + for (i=0; i<(*num_aliases); i++) + centry_put_uint32(centry, (*alias_rids)[i]); + centry_end(centry, "UA%s", sidlist); + centry_free(centry); + + skip_save: + return status; +} + + +static NTSTATUS lookup_groupmem(struct winbindd_domain *domain, + TALLOC_CTX *mem_ctx, + const DOM_SID *group_sid, uint32 *num_names, + DOM_SID **sid_mem, char ***names, + uint32 **name_types) +{ + struct winbind_cache *cache = get_cache(domain); + struct cache_entry *centry = NULL; + NTSTATUS status; + unsigned int i; + fstring sid_string; + + if (!cache->tdb) + goto do_query; + + centry = wcache_fetch(cache, domain, "GM/%s", sid_to_string(sid_string, group_sid)); + if (!centry) + goto do_query; + + *num_names = centry_uint32(centry); + + if (*num_names == 0) + goto do_cached; + + (*sid_mem) = TALLOC_ARRAY(mem_ctx, DOM_SID, *num_names); + (*names) = TALLOC_ARRAY(mem_ctx, char *, *num_names); + (*name_types) = TALLOC_ARRAY(mem_ctx, uint32, *num_names); + + if (! (*sid_mem) || ! (*names) || ! (*name_types)) { + smb_panic_fn("lookup_groupmem out of memory"); + } + + for (i=0; i<(*num_names); i++) { + centry_sid(centry, mem_ctx, &(*sid_mem)[i]); + (*names)[i] = centry_string(centry, mem_ctx); + (*name_types)[i] = centry_uint32(centry); + } + +do_cached: + status = centry->status; + + DEBUG(10,("lookup_groupmem: [Cached] - cached info for domain %s status: %s\n", + domain->name, nt_errstr(status))); + + centry_free(centry); + return status; + +do_query: + (*num_names) = 0; + (*sid_mem) = NULL; + (*names) = NULL; + (*name_types) = NULL; + + /* Return status value returned by seq number check */ + + if (!NT_STATUS_IS_OK(domain->last_status)) + return domain->last_status; + + DEBUG(10,("lookup_groupmem: [Cached] - doing backend query for info for domain %s\n", + domain->name )); + + status = domain->backend->lookup_groupmem(domain, mem_ctx, group_sid, num_names, + sid_mem, names, name_types); + + /* and save it */ + refresh_sequence_number(domain, False); + centry = centry_start(domain, status); + if (!centry) + goto skip_save; + centry_put_uint32(centry, *num_names); + for (i=0; i<(*num_names); i++) { + centry_put_sid(centry, &(*sid_mem)[i]); + centry_put_string(centry, (*names)[i]); + centry_put_uint32(centry, (*name_types)[i]); + } + centry_end(centry, "GM/%s", sid_to_string(sid_string, group_sid)); + centry_free(centry); + +skip_save: + return status; +} + +/* find the sequence number for a domain */ +static NTSTATUS sequence_number(struct winbindd_domain *domain, uint32 *seq) +{ + refresh_sequence_number(domain, False); + + *seq = domain->sequence_number; + + return NT_STATUS_OK; +} + +/* enumerate trusted domains + * (we need to have the list of trustdoms in the cache when we go offline) - + * Guenther */ +static NTSTATUS trusted_domains(struct winbindd_domain *domain, + TALLOC_CTX *mem_ctx, + uint32 *num_domains, + char ***names, + char ***alt_names, + DOM_SID **dom_sids) +{ + struct winbind_cache *cache = get_cache(domain); + struct cache_entry *centry = NULL; + NTSTATUS status; + int i; + + if (!cache->tdb) + goto do_query; + + centry = wcache_fetch(cache, domain, "TRUSTDOMS/%s", domain->name); + + if (!centry) { + goto do_query; + } + + *num_domains = centry_uint32(centry); + + if (*num_domains) { + (*names) = TALLOC_ARRAY(mem_ctx, char *, *num_domains); + (*alt_names) = TALLOC_ARRAY(mem_ctx, char *, *num_domains); + (*dom_sids) = TALLOC_ARRAY(mem_ctx, DOM_SID, *num_domains); + + if (! (*dom_sids) || ! (*names) || ! (*alt_names)) { + smb_panic_fn("trusted_domains out of memory"); + } + } else { + (*names) = NULL; + (*alt_names) = NULL; + (*dom_sids) = NULL; + } + + for (i=0; i<(*num_domains); i++) { + (*names)[i] = centry_string(centry, mem_ctx); + (*alt_names)[i] = centry_string(centry, mem_ctx); + centry_sid(centry, mem_ctx, &(*dom_sids)[i]); + } + + status = centry->status; + + DEBUG(10,("trusted_domains: [Cached] - cached info for domain %s (%d trusts) status: %s\n", + domain->name, *num_domains, nt_errstr(status) )); + + centry_free(centry); + return status; + +do_query: + (*num_domains) = 0; + (*dom_sids) = NULL; + (*names) = NULL; + (*alt_names) = NULL; + + /* Return status value returned by seq number check */ + + if (!NT_STATUS_IS_OK(domain->last_status)) + return domain->last_status; + + DEBUG(10,("trusted_domains: [Cached] - doing backend query for info for domain %s\n", + domain->name )); + + status = domain->backend->trusted_domains(domain, mem_ctx, num_domains, + names, alt_names, dom_sids); + + /* no trusts gives NT_STATUS_NO_MORE_ENTRIES resetting to NT_STATUS_OK + * so that the generic centry handling still applies correctly - + * Guenther*/ + + if (!NT_STATUS_IS_ERR(status)) { + status = NT_STATUS_OK; + } + + +#if 0 /* Disabled as we want the trust dom list to be managed by + the main parent and always to make the query. --jerry */ + + /* and save it */ + refresh_sequence_number(domain, False); + + centry = centry_start(domain, status); + if (!centry) + goto skip_save; + + centry_put_uint32(centry, *num_domains); + + for (i=0; i<(*num_domains); i++) { + centry_put_string(centry, (*names)[i]); + centry_put_string(centry, (*alt_names)[i]); + centry_put_sid(centry, &(*dom_sids)[i]); + } + + centry_end(centry, "TRUSTDOMS/%s", domain->name); + + centry_free(centry); + +skip_save: +#endif + + return status; +} + +/* get lockout policy */ +static NTSTATUS lockout_policy(struct winbindd_domain *domain, + TALLOC_CTX *mem_ctx, + SAM_UNK_INFO_12 *policy){ + struct winbind_cache *cache = get_cache(domain); + struct cache_entry *centry = NULL; + NTSTATUS status; + + if (!cache->tdb) + goto do_query; + + centry = wcache_fetch(cache, domain, "LOC_POL/%s", domain->name); + + if (!centry) + goto do_query; + + policy->duration = centry_nttime(centry); + policy->reset_count = centry_nttime(centry); + policy->bad_attempt_lockout = centry_uint16(centry); + + status = centry->status; + + DEBUG(10,("lockout_policy: [Cached] - cached info for domain %s status: %s\n", + domain->name, nt_errstr(status) )); + + centry_free(centry); + return status; + +do_query: + ZERO_STRUCTP(policy); + + /* Return status value returned by seq number check */ + + if (!NT_STATUS_IS_OK(domain->last_status)) + return domain->last_status; + + DEBUG(10,("lockout_policy: [Cached] - doing backend query for info for domain %s\n", + domain->name )); + + status = domain->backend->lockout_policy(domain, mem_ctx, policy); + + /* and save it */ + refresh_sequence_number(domain, False); + wcache_save_lockout_policy(domain, status, policy); + + return status; +} + +/* get password policy */ +static NTSTATUS password_policy(struct winbindd_domain *domain, + TALLOC_CTX *mem_ctx, + SAM_UNK_INFO_1 *policy) +{ + struct winbind_cache *cache = get_cache(domain); + struct cache_entry *centry = NULL; + NTSTATUS status; + + if (!cache->tdb) + goto do_query; + + centry = wcache_fetch(cache, domain, "PWD_POL/%s", domain->name); + + if (!centry) + goto do_query; + + policy->min_length_password = centry_uint16(centry); + policy->password_history = centry_uint16(centry); + policy->password_properties = centry_uint32(centry); + policy->expire = centry_nttime(centry); + policy->min_passwordage = centry_nttime(centry); + + status = centry->status; + + DEBUG(10,("lockout_policy: [Cached] - cached info for domain %s status: %s\n", + domain->name, nt_errstr(status) )); + + centry_free(centry); + return status; + +do_query: + ZERO_STRUCTP(policy); + + /* Return status value returned by seq number check */ + + if (!NT_STATUS_IS_OK(domain->last_status)) + return domain->last_status; + + DEBUG(10,("password_policy: [Cached] - doing backend query for info for domain %s\n", + domain->name )); + + status = domain->backend->password_policy(domain, mem_ctx, policy); + + /* and save it */ + refresh_sequence_number(domain, False); + wcache_save_password_policy(domain, status, policy); + + return status; +} + + +/* Invalidate cached user and group lists coherently */ + +static int traverse_fn(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf, + void *state) +{ + if (strncmp((const char *)kbuf.dptr, "UL/", 3) == 0 || + strncmp((const char *)kbuf.dptr, "GL/", 3) == 0) + tdb_delete(the_tdb, kbuf); + + return 0; +} + +/* Invalidate the getpwnam and getgroups entries for a winbindd domain */ + +void wcache_invalidate_samlogon(struct winbindd_domain *domain, + NET_USER_INFO_3 *info3) +{ + struct winbind_cache *cache; + + /* dont clear cached U/SID and UG/SID entries when we want to logon + * offline - gd */ + + if (lp_winbind_offline_logon()) { + return; + } + + if (!domain) + return; + + cache = get_cache(domain); + netsamlogon_clear_cached_user(cache->tdb, info3); +} + +void wcache_invalidate_cache(void) +{ + struct winbindd_domain *domain; + + for (domain = domain_list(); domain; domain = domain->next) { + struct winbind_cache *cache = get_cache(domain); + + DEBUG(10, ("wcache_invalidate_cache: invalidating cache " + "entries for %s\n", domain->name)); + if (cache) + tdb_traverse(cache->tdb, traverse_fn, NULL); + } +} + +BOOL init_wcache(void) +{ + if (wcache == NULL) { + wcache = SMB_XMALLOC_P(struct winbind_cache); + ZERO_STRUCTP(wcache); + } + + if (wcache->tdb != NULL) + return True; + + /* when working offline we must not clear the cache on restart */ + wcache->tdb = tdb_open_log(lock_path("winbindd_cache.tdb"), + WINBINDD_CACHE_TDB_DEFAULT_HASH_SIZE, + lp_winbind_offline_logon() ? TDB_DEFAULT : (TDB_DEFAULT | TDB_CLEAR_IF_FIRST), + O_RDWR|O_CREAT, 0600); + + if (wcache->tdb == NULL) { + DEBUG(0,("Failed to open winbindd_cache.tdb!\n")); + return False; + } + + return True; +} + +/************************************************************************ + This is called by the parent to initialize the cache file. + We don't need sophisticated locking here as we know we're the + only opener. +************************************************************************/ + +BOOL initialize_winbindd_cache(void) +{ + BOOL cache_bad = True; + uint32 vers; + + if (!init_wcache()) { + DEBUG(0,("initialize_winbindd_cache: init_wcache failed.\n")); + return False; + } + + /* Check version number. */ + if (tdb_fetch_uint32(wcache->tdb, WINBINDD_CACHE_VERSION_KEYSTR, &vers) && + vers == WINBINDD_CACHE_VERSION) { + cache_bad = False; + } + + if (cache_bad) { + DEBUG(0,("initialize_winbindd_cache: clearing cache " + "and re-creating with version number %d\n", + WINBINDD_CACHE_VERSION )); + + tdb_close(wcache->tdb); + wcache->tdb = NULL; + + if (unlink(lock_path("winbindd_cache.tdb")) == -1) { + DEBUG(0,("initialize_winbindd_cache: unlink %s failed %s ", + lock_path("winbindd_cache.tdb"), + strerror(errno) )); + return False; + } + if (!init_wcache()) { + DEBUG(0,("initialize_winbindd_cache: re-initialization " + "init_wcache failed.\n")); + return False; + } + + /* Write the version. */ + if (!tdb_store_uint32(wcache->tdb, WINBINDD_CACHE_VERSION_KEYSTR, WINBINDD_CACHE_VERSION)) { + DEBUG(0,("initialize_winbindd_cache: version number store failed %s\n", + tdb_errorstr(wcache->tdb) )); + return False; + } + } + + tdb_close(wcache->tdb); + wcache->tdb = NULL; + return True; +} + +void cache_store_response(pid_t pid, struct winbindd_response *response) +{ + fstring key_str; + + if (!init_wcache()) + return; + + DEBUG(10, ("Storing response for pid %d, len %d\n", + pid, response->length)); + + fstr_sprintf(key_str, "DR/%d", pid); + if (tdb_store(wcache->tdb, string_tdb_data(key_str), + make_tdb_data((uint8 *)response, sizeof(*response)), + TDB_REPLACE) == -1) + return; + + if (response->length == sizeof(*response)) + return; + + /* There's extra data */ + + DEBUG(10, ("Storing extra data: len=%d\n", + (int)(response->length - sizeof(*response)))); + + fstr_sprintf(key_str, "DE/%d", pid); + if (tdb_store(wcache->tdb, string_tdb_data(key_str), + make_tdb_data((uint8 *)response->extra_data.data, + response->length - sizeof(*response)), + TDB_REPLACE) == 0) + return; + + /* We could not store the extra data, make sure the tdb does not + * contain a main record with wrong dangling extra data */ + + fstr_sprintf(key_str, "DR/%d", pid); + tdb_delete(wcache->tdb, string_tdb_data(key_str)); + + return; +} + +BOOL cache_retrieve_response(pid_t pid, struct winbindd_response * response) +{ + TDB_DATA data; + fstring key_str; + + if (!init_wcache()) + return False; + + DEBUG(10, ("Retrieving response for pid %d\n", pid)); + + fstr_sprintf(key_str, "DR/%d", pid); + data = tdb_fetch(wcache->tdb, string_tdb_data(key_str)); + + if (data.dptr == NULL) + return False; + + if (data.dsize != sizeof(*response)) + return False; + + memcpy(response, data.dptr, data.dsize); + SAFE_FREE(data.dptr); + + if (response->length == sizeof(*response)) { + response->extra_data.data = NULL; + return True; + } + + /* There's extra data */ + + DEBUG(10, ("Retrieving extra data length=%d\n", + (int)(response->length - sizeof(*response)))); + + fstr_sprintf(key_str, "DE/%d", pid); + data = tdb_fetch(wcache->tdb, string_tdb_data(key_str)); + + if (data.dptr == NULL) { + DEBUG(0, ("Did not find extra data\n")); + return False; + } + + if (data.dsize != (response->length - sizeof(*response))) { + DEBUG(0, ("Invalid extra data length: %d\n", (int)data.dsize)); + SAFE_FREE(data.dptr); + return False; + } + + dump_data(11, (uint8 *)data.dptr, data.dsize); + + response->extra_data.data = data.dptr; + return True; +} + +void cache_cleanup_response(pid_t pid) +{ + fstring key_str; + + if (!init_wcache()) + return; + + fstr_sprintf(key_str, "DR/%d", pid); + tdb_delete(wcache->tdb, string_tdb_data(key_str)); + + fstr_sprintf(key_str, "DE/%d", pid); + tdb_delete(wcache->tdb, string_tdb_data(key_str)); + + return; +} + + +BOOL lookup_cached_sid(TALLOC_CTX *mem_ctx, const DOM_SID *sid, + char **domain_name, char **name, + enum lsa_SidType *type) +{ + struct winbindd_domain *domain; + struct winbind_cache *cache; + struct cache_entry *centry = NULL; + NTSTATUS status; + + domain = find_lookup_domain_from_sid(sid); + if (domain == NULL) { + return False; + } + + cache = get_cache(domain); + + if (cache->tdb == NULL) { + return False; + } + + centry = wcache_fetch(cache, domain, "SN/%s", sid_string_static(sid)); + if (centry == NULL) { + return False; + } + + if (NT_STATUS_IS_OK(centry->status)) { + *type = (enum lsa_SidType)centry_uint32(centry); + *domain_name = centry_string(centry, mem_ctx); + *name = centry_string(centry, mem_ctx); + } + + status = centry->status; + centry_free(centry); + return NT_STATUS_IS_OK(status); +} + +BOOL lookup_cached_name(TALLOC_CTX *mem_ctx, + const char *domain_name, + const char *name, + DOM_SID *sid, + enum lsa_SidType *type) +{ + struct winbindd_domain *domain; + struct winbind_cache *cache; + struct cache_entry *centry = NULL; + NTSTATUS status; + fstring uname; + BOOL original_online_state; + + domain = find_lookup_domain_from_name(domain_name); + if (domain == NULL) { + return False; + } + + cache = get_cache(domain); + + if (cache->tdb == NULL) { + return False; + } + + fstrcpy(uname, name); + strupper_m(uname); + + /* If we are doing a cached logon, temporarily set the domain + offline so the cache won't expire the entry */ + + original_online_state = domain->online; + domain->online = False; + centry = wcache_fetch(cache, domain, "NS/%s/%s", domain_name, uname); + domain->online = original_online_state; + + if (centry == NULL) { + return False; + } + + if (NT_STATUS_IS_OK(centry->status)) { + *type = (enum lsa_SidType)centry_uint32(centry); + centry_sid(centry, mem_ctx, sid); + } + + status = centry->status; + centry_free(centry); + + return NT_STATUS_IS_OK(status); +} + +void cache_name2sid(struct winbindd_domain *domain, + const char *domain_name, const char *name, + enum lsa_SidType type, const DOM_SID *sid) +{ + refresh_sequence_number(domain, False); + wcache_save_name_to_sid(domain, NT_STATUS_OK, domain_name, name, + sid, type); +} + +/* + * The original idea that this cache only contains centries has + * been blurred - now other stuff gets put in here. Ensure we + * ignore these things on cleanup. + */ + +static int traverse_fn_cleanup(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, + TDB_DATA dbuf, void *state) +{ + struct cache_entry *centry; + + if (is_non_centry_key(kbuf)) { + return 0; + } + + centry = wcache_fetch_raw((char *)kbuf.dptr); + if (!centry) { + return 0; + } + + if (!NT_STATUS_IS_OK(centry->status)) { + DEBUG(10,("deleting centry %s\n", (const char *)kbuf.dptr)); + tdb_delete(the_tdb, kbuf); + } + + centry_free(centry); + return 0; +} + +/* flush the cache */ +void wcache_flush_cache(void) +{ + if (!wcache) + return; + if (wcache->tdb) { + tdb_close(wcache->tdb); + wcache->tdb = NULL; + } + if (opt_nocache) + return; + + /* when working offline we must not clear the cache on restart */ + wcache->tdb = tdb_open_log(lock_path("winbindd_cache.tdb"), + WINBINDD_CACHE_TDB_DEFAULT_HASH_SIZE, + lp_winbind_offline_logon() ? TDB_DEFAULT : (TDB_DEFAULT | TDB_CLEAR_IF_FIRST), + O_RDWR|O_CREAT, 0600); + + if (!wcache->tdb) { + DEBUG(0,("Failed to open winbindd_cache.tdb!\n")); + return; + } + + tdb_traverse(wcache->tdb, traverse_fn_cleanup, NULL); + + DEBUG(10,("wcache_flush_cache success\n")); +} + +/* Count cached creds */ + +static int traverse_fn_cached_creds(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf, + void *state) +{ + int *cred_count = (int*)state; + + if (strncmp((const char *)kbuf.dptr, "CRED/", 5) == 0) { + (*cred_count)++; + } + return 0; +} + +NTSTATUS wcache_count_cached_creds(struct winbindd_domain *domain, int *count) +{ + struct winbind_cache *cache = get_cache(domain); + + *count = 0; + + if (!cache->tdb) { + return NT_STATUS_INTERNAL_DB_ERROR; + } + + tdb_traverse(cache->tdb, traverse_fn_cached_creds, (void *)count); + + return NT_STATUS_OK; +} + +struct cred_list { + struct cred_list *prev, *next; + TDB_DATA key; + fstring name; + time_t created; +}; +static struct cred_list *wcache_cred_list; + +static int traverse_fn_get_credlist(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf, + void *state) +{ + struct cred_list *cred; + + if (strncmp((const char *)kbuf.dptr, "CRED/", 5) == 0) { + + cred = SMB_MALLOC_P(struct cred_list); + if (cred == NULL) { + DEBUG(0,("traverse_fn_remove_first_creds: failed to malloc new entry for list\n")); + return -1; + } + + ZERO_STRUCTP(cred); + + /* save a copy of the key */ + + fstrcpy(cred->name, (const char *)kbuf.dptr); + DLIST_ADD(wcache_cred_list, cred); + } + + return 0; +} + +NTSTATUS wcache_remove_oldest_cached_creds(struct winbindd_domain *domain, const DOM_SID *sid) +{ + struct winbind_cache *cache = get_cache(domain); + NTSTATUS status; + int ret; + struct cred_list *cred, *oldest = NULL; + + if (!cache->tdb) { + return NT_STATUS_INTERNAL_DB_ERROR; + } + + /* we possibly already have an entry */ + if (sid && NT_STATUS_IS_OK(wcache_cached_creds_exist(domain, sid))) { + + fstring key_str; + + DEBUG(11,("we already have an entry, deleting that\n")); + + fstr_sprintf(key_str, "CRED/%s", sid_string_static(sid)); + + tdb_delete(cache->tdb, string_tdb_data(key_str)); + + return NT_STATUS_OK; + } + + ret = tdb_traverse(cache->tdb, traverse_fn_get_credlist, NULL); + if (ret == 0) { + return NT_STATUS_OK; + } else if ((ret == -1) || (wcache_cred_list == NULL)) { + return NT_STATUS_OBJECT_NAME_NOT_FOUND; + } + + ZERO_STRUCTP(oldest); + + for (cred = wcache_cred_list; cred; cred = cred->next) { + + TDB_DATA data; + time_t t; + + data = tdb_fetch(cache->tdb, string_tdb_data(cred->name)); + if (!data.dptr) { + DEBUG(10,("wcache_remove_oldest_cached_creds: entry for [%s] not found\n", + cred->name)); + status = NT_STATUS_OBJECT_NAME_NOT_FOUND; + goto done; + } + + t = IVAL(data.dptr, 0); + SAFE_FREE(data.dptr); + + if (!oldest) { + oldest = SMB_MALLOC_P(struct cred_list); + if (oldest == NULL) { + status = NT_STATUS_NO_MEMORY; + goto done; + } + + fstrcpy(oldest->name, cred->name); + oldest->created = t; + continue; + } + + if (t < oldest->created) { + fstrcpy(oldest->name, cred->name); + oldest->created = t; + } + } + + if (tdb_delete(cache->tdb, string_tdb_data(oldest->name)) == 0) { + status = NT_STATUS_OK; + } else { + status = NT_STATUS_UNSUCCESSFUL; + } +done: + SAFE_FREE(wcache_cred_list); + SAFE_FREE(oldest); + + return status; +} + +/* Change the global online/offline state. */ +BOOL set_global_winbindd_state_offline(void) +{ + TDB_DATA data; + + DEBUG(10,("set_global_winbindd_state_offline: offline requested.\n")); + + /* Only go offline if someone has created + the key "WINBINDD_OFFLINE" in the cache tdb. */ + + if (wcache == NULL || wcache->tdb == NULL) { + DEBUG(10,("set_global_winbindd_state_offline: wcache not open yet.\n")); + return False; + } + + if (!lp_winbind_offline_logon()) { + DEBUG(10,("set_global_winbindd_state_offline: rejecting.\n")); + return False; + } + + if (global_winbindd_offline_state) { + /* Already offline. */ + return True; + } + + data = tdb_fetch_bystring( wcache->tdb, "WINBINDD_OFFLINE" ); + + if (!data.dptr || data.dsize != 4) { + DEBUG(10,("set_global_winbindd_state_offline: offline state not set.\n")); + SAFE_FREE(data.dptr); + return False; + } else { + DEBUG(10,("set_global_winbindd_state_offline: offline state set.\n")); + global_winbindd_offline_state = True; + SAFE_FREE(data.dptr); + return True; + } +} + +void set_global_winbindd_state_online(void) +{ + DEBUG(10,("set_global_winbindd_state_online: online requested.\n")); + + if (!lp_winbind_offline_logon()) { + DEBUG(10,("set_global_winbindd_state_online: rejecting.\n")); + return; + } + + if (!global_winbindd_offline_state) { + /* Already online. */ + return; + } + global_winbindd_offline_state = False; + + if (!wcache->tdb) { + return; + } + + /* Ensure there is no key "WINBINDD_OFFLINE" in the cache tdb. */ + tdb_delete_bystring(wcache->tdb, "WINBINDD_OFFLINE"); +} + +BOOL get_global_winbindd_state_offline(void) +{ + return global_winbindd_offline_state; +} + +/*********************************************************************** + Validate functions for all possible cache tdb keys. +***********************************************************************/ + +static struct cache_entry *create_centry_validate(const char *kstr, TDB_DATA data, + struct tdb_validation_status *state) +{ + struct cache_entry *centry; + + centry = SMB_XMALLOC_P(struct cache_entry); + centry->data = (unsigned char *)memdup(data.dptr, data.dsize); + if (!centry->data) { + SAFE_FREE(centry); + return NULL; + } + centry->len = data.dsize; + centry->ofs = 0; + + if (centry->len < 8) { + /* huh? corrupt cache? */ + DEBUG(0,("create_centry_validate: Corrupt cache for key %s (len < 8) ?\n", kstr)); + centry_free(centry); + state->bad_entry = True; + state->success = False; + return NULL; + } + + centry->status = NT_STATUS(centry_uint32(centry)); + centry->sequence_number = centry_uint32(centry); + return centry; +} + +static int validate_seqnum(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf, + struct tdb_validation_status *state) +{ + if (dbuf.dsize != 8) { + DEBUG(0,("validate_seqnum: Corrupt cache for key %s (len %u != 8) ?\n", + keystr, (unsigned int)dbuf.dsize )); + state->bad_entry = True; + return 1; + } + return 0; +} + +static int validate_ns(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf, + struct tdb_validation_status *state) +{ + struct cache_entry *centry = create_centry_validate(keystr, dbuf, state); + if (!centry) { + return 1; + } + + (void)centry_uint32(centry); + if (NT_STATUS_IS_OK(centry->status)) { + DOM_SID sid; + (void)centry_sid(centry, mem_ctx, &sid); + } + + centry_free(centry); + + if (!(state->success)) { + return 1; + } + DEBUG(10,("validate_ns: %s ok\n", keystr)); + return 0; +} + +static int validate_sn(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf, + struct tdb_validation_status *state) +{ + struct cache_entry *centry = create_centry_validate(keystr, dbuf, state); + if (!centry) { + return 1; + } + + if (NT_STATUS_IS_OK(centry->status)) { + (void)centry_uint32(centry); + (void)centry_string(centry, mem_ctx); + (void)centry_string(centry, mem_ctx); + } + + centry_free(centry); + + if (!(state->success)) { + return 1; + } + DEBUG(10,("validate_sn: %s ok\n", keystr)); + return 0; +} + +static int validate_u(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf, + struct tdb_validation_status *state) +{ + struct cache_entry *centry = create_centry_validate(keystr, dbuf, state); + DOM_SID sid; + + if (!centry) { + return 1; + } + + (void)centry_string(centry, mem_ctx); + (void)centry_string(centry, mem_ctx); + (void)centry_string(centry, mem_ctx); + (void)centry_string(centry, mem_ctx); + (void)centry_uint32(centry); + (void)centry_sid(centry, mem_ctx, &sid); + (void)centry_sid(centry, mem_ctx, &sid); + + centry_free(centry); + + if (!(state->success)) { + return 1; + } + DEBUG(10,("validate_u: %s ok\n", keystr)); + return 0; +} + +static int validate_loc_pol(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf, + struct tdb_validation_status *state) +{ + struct cache_entry *centry = create_centry_validate(keystr, dbuf, state); + + if (!centry) { + return 1; + } + + (void)centry_nttime(centry); + (void)centry_nttime(centry); + (void)centry_uint16(centry); + + centry_free(centry); + + if (!(state->success)) { + return 1; + } + DEBUG(10,("validate_loc_pol: %s ok\n", keystr)); + return 0; +} + +static int validate_pwd_pol(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf, + struct tdb_validation_status *state) +{ + struct cache_entry *centry = create_centry_validate(keystr, dbuf, state); + + if (!centry) { + return 1; + } + + (void)centry_uint16(centry); + (void)centry_uint16(centry); + (void)centry_uint32(centry); + (void)centry_nttime(centry); + (void)centry_nttime(centry); + + centry_free(centry); + + if (!(state->success)) { + return 1; + } + DEBUG(10,("validate_pwd_pol: %s ok\n", keystr)); + return 0; +} + +static int validate_cred(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf, + struct tdb_validation_status *state) +{ + struct cache_entry *centry = create_centry_validate(keystr, dbuf, state); + + if (!centry) { + return 1; + } + + (void)centry_time(centry); + (void)centry_hash16(centry, mem_ctx); + + /* We only have 17 bytes more data in the salted cred case. */ + if (centry->len - centry->ofs == 17) { + (void)centry_hash16(centry, mem_ctx); + } + + centry_free(centry); + + if (!(state->success)) { + return 1; + } + DEBUG(10,("validate_cred: %s ok\n", keystr)); + return 0; +} + +static int validate_ul(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf, + struct tdb_validation_status *state) +{ + struct cache_entry *centry = create_centry_validate(keystr, dbuf, state); + int32 num_entries, i; + + if (!centry) { + return 1; + } + + num_entries = (int32)centry_uint32(centry); + + for (i=0; i< num_entries; i++) { + DOM_SID sid; + (void)centry_string(centry, mem_ctx); + (void)centry_string(centry, mem_ctx); + (void)centry_string(centry, mem_ctx); + (void)centry_string(centry, mem_ctx); + (void)centry_sid(centry, mem_ctx, &sid); + (void)centry_sid(centry, mem_ctx, &sid); + } + + centry_free(centry); + + if (!(state->success)) { + return 1; + } + DEBUG(10,("validate_ul: %s ok\n", keystr)); + return 0; +} + +static int validate_gl(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf, + struct tdb_validation_status *state) +{ + struct cache_entry *centry = create_centry_validate(keystr, dbuf, state); + int32 num_entries, i; + + if (!centry) { + return 1; + } + + num_entries = centry_uint32(centry); + + for (i=0; i< num_entries; i++) { + (void)centry_string(centry, mem_ctx); + (void)centry_string(centry, mem_ctx); + (void)centry_uint32(centry); + } + + centry_free(centry); + + if (!(state->success)) { + return 1; + } + DEBUG(10,("validate_gl: %s ok\n", keystr)); + return 0; +} + +static int validate_ug(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf, + struct tdb_validation_status *state) +{ + struct cache_entry *centry = create_centry_validate(keystr, dbuf, state); + int32 num_groups, i; + + if (!centry) { + return 1; + } + + num_groups = centry_uint32(centry); + + for (i=0; i< num_groups; i++) { + DOM_SID sid; + centry_sid(centry, mem_ctx, &sid); + } + + centry_free(centry); + + if (!(state->success)) { + return 1; + } + DEBUG(10,("validate_ug: %s ok\n", keystr)); + return 0; +} + +static int validate_ua(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf, + struct tdb_validation_status *state) +{ + struct cache_entry *centry = create_centry_validate(keystr, dbuf, state); + int32 num_aliases, i; + + if (!centry) { + return 1; + } + + num_aliases = centry_uint32(centry); + + for (i=0; i < num_aliases; i++) { + (void)centry_uint32(centry); + } + + centry_free(centry); + + if (!(state->success)) { + return 1; + } + DEBUG(10,("validate_ua: %s ok\n", keystr)); + return 0; +} + +static int validate_gm(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf, + struct tdb_validation_status *state) +{ + struct cache_entry *centry = create_centry_validate(keystr, dbuf, state); + int32 num_names, i; + + if (!centry) { + return 1; + } + + num_names = centry_uint32(centry); + + for (i=0; i< num_names; i++) { + DOM_SID sid; + centry_sid(centry, mem_ctx, &sid); + (void)centry_string(centry, mem_ctx); + (void)centry_uint32(centry); + } + + centry_free(centry); + + if (!(state->success)) { + return 1; + } + DEBUG(10,("validate_gm: %s ok\n", keystr)); + return 0; +} + +static int validate_dr(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf, + struct tdb_validation_status *state) +{ + /* Can't say anything about this other than must be nonzero. */ + if (dbuf.dsize == 0) { + DEBUG(0,("validate_dr: Corrupt cache for key %s (len == 0) ?\n", + keystr)); + state->bad_entry = True; + state->success = False; + return 1; + } + + DEBUG(10,("validate_dr: %s ok\n", keystr)); + return 0; +} + +static int validate_de(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf, + struct tdb_validation_status *state) +{ + /* Can't say anything about this other than must be nonzero. */ + if (dbuf.dsize == 0) { + DEBUG(0,("validate_de: Corrupt cache for key %s (len == 0) ?\n", + keystr)); + state->bad_entry = True; + state->success = False; + return 1; + } + + DEBUG(10,("validate_de: %s ok\n", keystr)); + return 0; +} + +static int validate_trustdoms(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf, + struct tdb_validation_status *state) +{ + struct cache_entry *centry = create_centry_validate(keystr, dbuf, state); + int32 num_domains, i; + + if (!centry) { + return 1; + } + + num_domains = centry_uint32(centry); + + for (i=0; i< num_domains; i++) { + DOM_SID sid; + (void)centry_string(centry, mem_ctx); + (void)centry_string(centry, mem_ctx); + (void)centry_sid(centry, mem_ctx, &sid); + } + + centry_free(centry); + + if (!(state->success)) { + return 1; + } + DEBUG(10,("validate_trustdoms: %s ok\n", keystr)); + return 0; +} + +static int validate_trustdomcache(TALLOC_CTX *mem_ctx, const char *keystr, + TDB_DATA dbuf, + struct tdb_validation_status *state) +{ + if (dbuf.dsize == 0) { + DEBUG(0, ("validate_trustdomcache: Corrupt cache for " + "key %s (len ==0) ?\n", keystr)); + state->bad_entry = True; + state->success = False; + return 1; + } + + DEBUG(10, ("validate_trustdomcache: %s ok\n", keystr)); + DEBUGADD(10, (" Don't trust me, I am a DUMMY!\n")); + return 0; +} + +static int validate_offline(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf, + struct tdb_validation_status *state) +{ + if (dbuf.dsize != 4) { + DEBUG(0,("validate_offline: Corrupt cache for key %s (len %u != 4) ?\n", + keystr, (unsigned int)dbuf.dsize )); + state->bad_entry = True; + state->success = False; + return 1; + } + DEBUG(10,("validate_offline: %s ok\n", keystr)); + return 0; +} + +static int validate_cache_version(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf, + struct tdb_validation_status *state) +{ + if (dbuf.dsize != 4) { + DEBUG(0, ("validate_cache_version: Corrupt cache for " + "key %s (len %u != 4) ?\n", + keystr, (unsigned int)dbuf.dsize)); + state->bad_entry = True; + state->success = False; + return 1; + } + + DEBUG(10, ("validate_cache_version: %s ok\n", keystr)); + return 0; +} + +/*********************************************************************** + A list of all possible cache tdb keys with associated validation + functions. +***********************************************************************/ + +struct key_val_struct { + const char *keyname; + int (*validate_data_fn)(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf, struct tdb_validation_status* state); +} key_val[] = { + {"SEQNUM/", validate_seqnum}, + {"NS/", validate_ns}, + {"SN/", validate_sn}, + {"U/", validate_u}, + {"LOC_POL/", validate_loc_pol}, + {"PWD_POL/", validate_pwd_pol}, + {"CRED/", validate_cred}, + {"UL/", validate_ul}, + {"GL/", validate_gl}, + {"UG/", validate_ug}, + {"UA", validate_ua}, + {"GM/", validate_gm}, + {"DR/", validate_dr}, + {"DE/", validate_de}, + {"TRUSTDOMS/", validate_trustdoms}, + {"TRUSTDOMCACHE/", validate_trustdomcache}, + {"WINBINDD_OFFLINE", validate_offline}, + {WINBINDD_CACHE_VERSION_KEYSTR, validate_cache_version}, + {NULL, NULL} +}; + +/*********************************************************************** + Function to look at every entry in the tdb and validate it as far as + possible. +***********************************************************************/ + +static int cache_traverse_validate_fn(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf, void *state) +{ + int i; + struct tdb_validation_status *v_state = (struct tdb_validation_status *)state; + + /* Paranoia check. */ + if (kbuf.dsize > 1024) { + DEBUG(0,("cache_traverse_validate_fn: key length too large (%u) > 1024\n\n", + (unsigned int)kbuf.dsize )); + return 1; + } + + for (i = 0; key_val[i].keyname; i++) { + size_t namelen = strlen(key_val[i].keyname); + if (kbuf.dsize >= namelen && ( + strncmp(key_val[i].keyname, (const char *)kbuf.dptr, namelen)) == 0) { + TALLOC_CTX *mem_ctx; + char *keystr; + int ret; + + keystr = SMB_MALLOC_ARRAY(char, kbuf.dsize+1); + if (!keystr) { + return 1; + } + memcpy(keystr, kbuf.dptr, kbuf.dsize); + keystr[kbuf.dsize] = '\0'; + + mem_ctx = talloc_init("validate_ctx"); + if (!mem_ctx) { + SAFE_FREE(keystr); + return 1; + } + + ret = key_val[i].validate_data_fn(mem_ctx, keystr, dbuf, + v_state); + + SAFE_FREE(keystr); + talloc_destroy(mem_ctx); + return ret; + } + } + + DEBUG(0,("cache_traverse_validate_fn: unknown cache entry\nkey :\n")); + dump_data(0, (uint8 *)kbuf.dptr, kbuf.dsize); + DEBUG(0,("data :\n")); + dump_data(0, (uint8 *)dbuf.dptr, dbuf.dsize); + v_state->unknown_key = True; + v_state->success = False; + return 1; /* terminate. */ +} + +static void validate_panic(const char *const why) +{ + DEBUG(0,("validating cache: would panic %s\n", why )); + DEBUGADD(0, ("exiting instead (cache validation mode)\n")); + exit(47); +} + +/*********************************************************************** + Try and validate every entry in the winbindd cache. If we fail here, + delete the cache tdb and return non-zero - the caller (main winbindd + function) will restart us as we don't know if we crashed or not. +***********************************************************************/ + +int winbindd_validate_cache(void) +{ + int ret = -1; + const char *tdb_path = lock_path("winbindd_cache.tdb"); + TDB_CONTEXT *tdb = NULL; + + DEBUG(10, ("winbindd_validate_cache: replacing panic function\n")); + smb_panic_fn = validate_panic; + + + tdb = tdb_open_log(tdb_path, + WINBINDD_CACHE_TDB_DEFAULT_HASH_SIZE, + ( lp_winbind_offline_logon() + ? TDB_DEFAULT + : TDB_DEFAULT | TDB_CLEAR_IF_FIRST ), + O_RDWR|O_CREAT, + 0600); + if (!tdb) { + DEBUG(0, ("winbindd_validate_cache: " + "error opening/initializing tdb\n")); + goto done; + } + tdb_close(tdb); + + ret = tdb_validate_and_backup(tdb_path, cache_traverse_validate_fn); + + if (ret != 0) { + DEBUG(10, ("winbindd_validate_cache: validation not successful.\n")); + DEBUGADD(10, ("removing tdb %s.\n", tdb_path)); + unlink(tdb_path); + } + +done: + DEBUG(10, ("winbindd_validate_cache: restoring panic function\n")); + smb_panic_fn = smb_panic; + return ret; +} + +/*********************************************************************** + Try and validate every entry in the winbindd cache. +***********************************************************************/ + +int winbindd_validate_cache_nobackup(void) +{ + int ret = -1; + const char *tdb_path = lock_path("winbindd_cache.tdb"); + + DEBUG(10, ("winbindd_validate_cache: replacing panic function\n")); + smb_panic_fn = validate_panic; + + + if (wcache == NULL || wcache->tdb == NULL) { + ret = tdb_validate_open(tdb_path, cache_traverse_validate_fn); + } else { + ret = tdb_validate(wcache->tdb, cache_traverse_validate_fn); + } + + if (ret != 0) { + DEBUG(10, ("winbindd_validate_cache_nobackup: validation not " + "successful.\n")); + } + + DEBUG(10, ("winbindd_validate_cache_nobackup: restoring panic " + "function\n")); + smb_panic_fn = smb_panic; + return ret; +} + +/********************************************************************* + ********************************************************************/ + +static BOOL add_wbdomain_to_tdc_array( struct winbindd_domain *new_dom, + struct winbindd_tdc_domain **domains, + size_t *num_domains ) +{ + struct winbindd_tdc_domain *list = NULL; + size_t idx; + int i; + BOOL set_only = False; + + /* don't allow duplicates */ + + idx = *num_domains; + list = *domains; + + for ( i=0; i< (*num_domains); i++ ) { + if ( strequal( new_dom->name, list[i].domain_name ) ) { + DEBUG(10,("add_wbdomain_to_tdc_array: Found existing record for %s\n", + new_dom->name)); + idx = i; + set_only = True; + + break; + } + } + + if ( !set_only ) { + if ( !*domains ) { + list = TALLOC_ARRAY( NULL, struct winbindd_tdc_domain, 1 ); + idx = 0; + } else { + list = TALLOC_REALLOC_ARRAY( *domains, *domains, + struct winbindd_tdc_domain, + (*num_domains)+1); + idx = *num_domains; + } + + ZERO_STRUCT( list[idx] ); + } + + if ( !list ) + return False; + + list[idx].domain_name = talloc_strdup( list, new_dom->name ); + list[idx].dns_name = talloc_strdup( list, new_dom->alt_name ); + + if ( !is_null_sid( &new_dom->sid ) ) + sid_copy( &list[idx].sid, &new_dom->sid ); + + if ( new_dom->domain_flags != 0x0 ) + list[idx].trust_flags = new_dom->domain_flags; + + if ( new_dom->domain_type != 0x0 ) + list[idx].trust_type = new_dom->domain_type; + + if ( new_dom->domain_trust_attribs != 0x0 ) + list[idx].trust_attribs = new_dom->domain_trust_attribs; + + if ( !set_only ) { + *domains = list; + *num_domains = idx + 1; + } + + return True; +} + +/********************************************************************* + ********************************************************************/ + +static TDB_DATA make_tdc_key( const char *domain_name ) +{ + char *keystr = NULL; + TDB_DATA key = { NULL, 0 }; + + if ( !domain_name ) { + DEBUG(5,("make_tdc_key: Keyname workgroup is NULL!\n")); + return key; + } + + + asprintf( &keystr, "TRUSTDOMCACHE/%s", domain_name ); + key = string_term_tdb_data(keystr); + + return key; +} + +/********************************************************************* + ********************************************************************/ + +static int pack_tdc_domains( struct winbindd_tdc_domain *domains, + size_t num_domains, + unsigned char **buf ) +{ + unsigned char *buffer = NULL; + int len = 0; + int buflen = 0; + int i = 0; + + DEBUG(10,("pack_tdc_domains: Packing %d trusted domains\n", + (int)num_domains)); + + buflen = 0; + + again: + len = 0; + + /* Store the number of array items first */ + len += tdb_pack( buffer+len, buflen-len, "d", + num_domains ); + + /* now pack each domain trust record */ + for ( i=0; i 0 ) { + DEBUG(10,("pack_tdc_domains: Packing domain %s (%s)\n", + domains[i].domain_name, + domains[i].dns_name ? domains[i].dns_name : "UNKNOWN" )); + } + + len += tdb_pack( buffer+len, buflen-len, "fffddd", + domains[i].domain_name, + domains[i].dns_name, + sid_string_static(&domains[i].sid), + domains[i].trust_flags, + domains[i].trust_attribs, + domains[i].trust_type ); + } + + if ( buflen < len ) { + SAFE_FREE(buffer); + if ( (buffer = SMB_MALLOC_ARRAY(unsigned char, len)) == NULL ) { + DEBUG(0,("pack_tdc_domains: failed to alloc buffer!\n")); + buflen = -1; + goto done; + } + buflen = len; + goto again; + } + + *buf = buffer; + + done: + return buflen; +} + +/********************************************************************* + ********************************************************************/ + +static size_t unpack_tdc_domains( unsigned char *buf, int buflen, + struct winbindd_tdc_domain **domains ) +{ + fstring domain_name, dns_name, sid_string; + uint32 type, attribs, flags; + int num_domains; + int len = 0; + int i; + struct winbindd_tdc_domain *list = NULL; + + /* get the number of domains */ + len += tdb_unpack( buf+len, buflen-len, "d", &num_domains); + if ( len == -1 ) { + DEBUG(5,("unpack_tdc_domains: Failed to unpack domain array\n")); + return 0; + } + + list = TALLOC_ARRAY( NULL, struct winbindd_tdc_domain, num_domains ); + if ( !list ) { + DEBUG(0,("unpack_tdc_domains: Failed to talloc() domain list!\n")); + return 0; + } + + for ( i=0; itdb, key ); + goto done; + } + + data.dsize = pack_tdc_domains( domains, num_domains, &data.dptr ); + + if ( !data.dptr ) { + ret = -1; + goto done; + } + + ret = tdb_store( wcache->tdb, key, data, 0 ); + + done: + SAFE_FREE( data.dptr ); + SAFE_FREE( key.dptr ); + + return ( ret != -1 ); +} + +/********************************************************************* + ********************************************************************/ + +BOOL wcache_tdc_fetch_list( struct winbindd_tdc_domain **domains, size_t *num_domains ) +{ + TDB_DATA key = make_tdc_key( lp_workgroup() ); + TDB_DATA data = { NULL, 0 }; + + *domains = NULL; + *num_domains = 0; + + if ( !key.dptr ) + return False; + + data = tdb_fetch( wcache->tdb, key ); + + SAFE_FREE( key.dptr ); + + if ( !data.dptr ) + return False; + + *num_domains = unpack_tdc_domains( data.dptr, data.dsize, domains ); + + SAFE_FREE( data.dptr ); + + if ( !*domains ) + return False; + + return True; +} + +/********************************************************************* + ********************************************************************/ + +BOOL wcache_tdc_add_domain( struct winbindd_domain *domain ) +{ + struct winbindd_tdc_domain *dom_list = NULL; + size_t num_domains = 0; + BOOL ret = False; + + DEBUG(10,("wcache_tdc_add_domain: Adding domain %s (%s), SID %s, " + "flags = 0x%x, attributes = 0x%x, type = 0x%x\n", + domain->name, domain->alt_name, + sid_string_static(&domain->sid), + domain->domain_flags, + domain->domain_trust_attribs, + domain->domain_type)); + + if ( !init_wcache() ) { + return False; + } + + /* fetch the list */ + + wcache_tdc_fetch_list( &dom_list, &num_domains ); + + /* add the new domain */ + + if ( !add_wbdomain_to_tdc_array( domain, &dom_list, &num_domains ) ) { + goto done; + } + + /* pack the domain */ + + if ( !wcache_tdc_store_list( dom_list, num_domains ) ) { + goto done; + } + + /* Success */ + + ret = True; + done: + TALLOC_FREE( dom_list ); + + return ret; +} + +/********************************************************************* + ********************************************************************/ + +struct winbindd_tdc_domain * wcache_tdc_fetch_domain( TALLOC_CTX *ctx, const char *name ) +{ + struct winbindd_tdc_domain *dom_list = NULL; + size_t num_domains = 0; + int i; + struct winbindd_tdc_domain *d = NULL; + + DEBUG(10,("wcache_tdc_fetch_domain: Searching for domain %s\n", name)); + + if ( !init_wcache() ) { + return False; + } + + /* fetch the list */ + + wcache_tdc_fetch_list( &dom_list, &num_domains ); + + for ( i=0; idomain_name = talloc_strdup( d, dom_list[i].domain_name ); + d->dns_name = talloc_strdup( d, dom_list[i].dns_name ); + sid_copy( &d->sid, &dom_list[i].sid ); + d->trust_flags = dom_list[i].trust_flags; + d->trust_type = dom_list[i].trust_type; + d->trust_attribs = dom_list[i].trust_attribs; + + break; + } + } + + TALLOC_FREE( dom_list ); + + return d; +} + + +/********************************************************************* + ********************************************************************/ + +void wcache_tdc_clear( void ) +{ + if ( !init_wcache() ) + return; + + wcache_tdc_store_list( NULL, 0 ); + + return; +} + + +/********************************************************************* + ********************************************************************/ + +static void wcache_save_user_pwinfo(struct winbindd_domain *domain, + NTSTATUS status, + const DOM_SID *user_sid, + const char *homedir, + const char *shell, + const char *gecos, + uint32 gid) +{ + struct cache_entry *centry; + + if ( (centry = centry_start(domain, status)) == NULL ) + return; + + centry_put_string( centry, homedir ); + centry_put_string( centry, shell ); + centry_put_string( centry, gecos ); + centry_put_uint32( centry, gid ); + + centry_end(centry, "NSS/PWINFO/%s", sid_string_static(user_sid) ); + + DEBUG(10,("wcache_save_user_pwinfo: %s\n", sid_string_static(user_sid) )); + + centry_free(centry); +} + +NTSTATUS nss_get_info_cached( struct winbindd_domain *domain, + const DOM_SID *user_sid, + TALLOC_CTX *ctx, + ADS_STRUCT *ads, LDAPMessage *msg, + char **homedir, char **shell, char **gecos, + gid_t *p_gid) +{ + struct winbind_cache *cache = get_cache(domain); + struct cache_entry *centry = NULL; + NTSTATUS nt_status; + + if (!cache->tdb) + goto do_query; + + centry = wcache_fetch(cache, domain, "NSS/PWINFO/%s", sid_string_static(user_sid)); + + if (!centry) + goto do_query; + + *homedir = centry_string( centry, ctx ); + *shell = centry_string( centry, ctx ); + *gecos = centry_string( centry, ctx ); + *p_gid = centry_uint32( centry ); + + centry_free(centry); + + DEBUG(10,("nss_get_info_cached: [Cached] - user_sid %s\n", + sid_string_static(user_sid))); + + return NT_STATUS_OK; + +do_query: + + nt_status = nss_get_info( domain->name, user_sid, ctx, ads, msg, + homedir, shell, gecos, p_gid ); + + if ( NT_STATUS_IS_OK(nt_status) ) { + wcache_save_user_pwinfo( domain, nt_status, user_sid, + *homedir, *shell, *gecos, *p_gid ); + } + + if ( NT_STATUS_EQUAL( nt_status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND ) ) { + DEBUG(5,("nss_get_info_cached: Setting domain %s offline\n", + domain->name )); + set_domain_offline( domain ); + } + + return nt_status; +} + + +/* the cache backend methods are exposed via this structure */ +struct winbindd_methods cache_methods = { + True, + query_user_list, + enum_dom_groups, + enum_local_groups, + name_to_sid, + sid_to_name, + rids_to_names, + query_user, + lookup_usergroups, + lookup_useraliases, + lookup_groupmem, + sequence_number, + lockout_policy, + password_policy, + trusted_domains +}; diff --git a/source3/winbindd/winbindd_ccache_access.c b/source3/winbindd/winbindd_ccache_access.c new file mode 100644 index 0000000000..99c5c0c4d1 --- /dev/null +++ b/source3/winbindd/winbindd_ccache_access.c @@ -0,0 +1,283 @@ +/* + Unix SMB/CIFS implementation. + + Winbind daemon - cached credentials funcions + + Copyright (C) Robert O'Callahan 2006 + Copyright (C) Jeremy Allison 2006 (minor fixes to fit into Samba and + protect against integer wrap). + + 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 3 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, see . +*/ + +#include "includes.h" +#include "winbindd.h" + +#undef DBGC_CLASS +#define DBGC_CLASS DBGC_WINBIND + +static BOOL client_can_access_ccache_entry(uid_t client_uid, + struct WINBINDD_MEMORY_CREDS *entry) +{ + if (client_uid == entry->uid || client_uid == 0) { + DEBUG(10, ("Access granted to uid %d\n", client_uid)); + return True; + } + + DEBUG(1, ("Access denied to uid %d (expected %d)\n", client_uid, entry->uid)); + return False; +} + +static NTSTATUS do_ntlm_auth_with_hashes(const char *username, + const char *domain, + const unsigned char lm_hash[LM_HASH_LEN], + const unsigned char nt_hash[NT_HASH_LEN], + const DATA_BLOB initial_msg, + const DATA_BLOB challenge_msg, + DATA_BLOB *auth_msg) +{ + NTSTATUS status; + NTLMSSP_STATE *ntlmssp_state = NULL; + DATA_BLOB dummy_msg, reply; + + status = ntlmssp_client_start(&ntlmssp_state); + + if (!NT_STATUS_IS_OK(status)) { + DEBUG(1, ("Could not start NTLMSSP client: %s\n", + nt_errstr(status))); + goto done; + } + + status = ntlmssp_set_username(ntlmssp_state, username); + + if (!NT_STATUS_IS_OK(status)) { + DEBUG(1, ("Could not set username: %s\n", + nt_errstr(status))); + goto done; + } + + status = ntlmssp_set_domain(ntlmssp_state, domain); + + if (!NT_STATUS_IS_OK(status)) { + DEBUG(1, ("Could not set domain: %s\n", + nt_errstr(status))); + goto done; + } + + status = ntlmssp_set_hashes(ntlmssp_state, lm_hash, nt_hash); + + if (!NT_STATUS_IS_OK(status)) { + DEBUG(1, ("Could not set hashes: %s\n", + nt_errstr(status))); + goto done; + } + + /* We need to get our protocol handler into the right state. So first + we ask it to generate the initial message. Actually the client has already + sent its own initial message, so we're going to drop this one on the floor. + The client might have sent a different message, for example with different + negotiation options, but as far as I can tell this won't hurt us. (Unless + the client sent a different username or domain, in which case that's their + problem for telling us the wrong username or domain.) + Since we have a copy of the initial message that the client sent, we could + resolve any discrepancies if we had to. + */ + dummy_msg = data_blob_null; + reply = data_blob_null; + status = ntlmssp_update(ntlmssp_state, dummy_msg, &reply); + data_blob_free(&dummy_msg); + data_blob_free(&reply); + + if (!NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED)) { + DEBUG(1, ("Failed to create initial message! [%s]\n", + nt_errstr(status))); + goto done; + } + + /* Now we are ready to handle the server's actual response. */ + status = ntlmssp_update(ntlmssp_state, challenge_msg, &reply); + + if (!NT_STATUS_EQUAL(status, NT_STATUS_OK)) { + DEBUG(1, ("We didn't get a response to the challenge! [%s]\n", + nt_errstr(status))); + data_blob_free(&reply); + goto done; + } + *auth_msg = reply; + status = NT_STATUS_OK; + +done: + ntlmssp_end(&ntlmssp_state); + return status; +} + +static BOOL check_client_uid(struct winbindd_cli_state *state, uid_t uid) +{ + int ret; + uid_t ret_uid; + + ret_uid = (uid_t)-1; + + ret = sys_getpeereid(state->sock, &ret_uid); + if (ret != 0) { + DEBUG(1, ("check_client_uid: Could not get socket peer uid: %s; " + "denying access\n", strerror(errno))); + return False; + } + + if (uid != ret_uid) { + DEBUG(1, ("check_client_uid: Client lied about its uid: said %d, " + "actually was %d; denying access\n", + uid, ret_uid)); + return False; + } + + return True; +} + +void winbindd_ccache_ntlm_auth(struct winbindd_cli_state *state) +{ + struct winbindd_domain *domain; + fstring name_domain, name_user; + + /* Ensure null termination */ + state->request.data.ccache_ntlm_auth.user[ + sizeof(state->request.data.ccache_ntlm_auth.user)-1]='\0'; + + DEBUG(3, ("[%5lu]: perform NTLM auth on behalf of user %s\n", (unsigned long)state->pid, + state->request.data.ccache_ntlm_auth.user)); + + /* Parse domain and username */ + + if (!canonicalize_username(state->request.data.ccache_ntlm_auth.user, + name_domain, name_user)) { + DEBUG(5,("winbindd_ccache_ntlm_auth: cannot parse domain and user from name [%s]\n", + state->request.data.ccache_ntlm_auth.user)); + request_error(state); + return; + } + + domain = find_auth_domain(state, name_domain); + + if (domain == NULL) { + DEBUG(5,("winbindd_ccache_ntlm_auth: can't get domain [%s]\n", + name_domain)); + request_error(state); + return; + } + + if (!check_client_uid(state, state->request.data.ccache_ntlm_auth.uid)) { + request_error(state); + return; + } + + sendto_domain(state, domain); +} + +enum winbindd_result winbindd_dual_ccache_ntlm_auth(struct winbindd_domain *domain, + struct winbindd_cli_state *state) +{ + NTSTATUS result = NT_STATUS_NOT_SUPPORTED; + struct WINBINDD_MEMORY_CREDS *entry; + DATA_BLOB initial, challenge, auth; + fstring name_domain, name_user; + uint32 initial_blob_len, challenge_blob_len, extra_len; + + /* Ensure null termination */ + state->request.data.ccache_ntlm_auth.user[ + sizeof(state->request.data.ccache_ntlm_auth.user)-1]='\0'; + + DEBUG(3, ("winbindd_dual_ccache_ntlm_auth: [%5lu]: perform NTLM auth on " + "behalf of user %s (dual)\n", (unsigned long)state->pid, + state->request.data.ccache_ntlm_auth.user)); + + /* validate blob lengths */ + initial_blob_len = state->request.data.ccache_ntlm_auth.initial_blob_len; + challenge_blob_len = state->request.data.ccache_ntlm_auth.challenge_blob_len; + extra_len = state->request.extra_len; + + if (initial_blob_len > extra_len || challenge_blob_len > extra_len || + initial_blob_len + challenge_blob_len > extra_len || + initial_blob_len + challenge_blob_len < initial_blob_len || + initial_blob_len + challenge_blob_len < challenge_blob_len) { + + DEBUG(10,("winbindd_dual_ccache_ntlm_auth: blob lengths overrun " + "or wrap. Buffer [%d+%d > %d]\n", + initial_blob_len, + challenge_blob_len, + extra_len)); + goto process_result; + } + + /* Parse domain and username */ + if (!parse_domain_user(state->request.data.ccache_ntlm_auth.user, name_domain, name_user)) { + DEBUG(10,("winbindd_dual_ccache_ntlm_auth: cannot parse " + "domain and user from name [%s]\n", + state->request.data.ccache_ntlm_auth.user)); + goto process_result; + } + + entry = find_memory_creds_by_name(state->request.data.ccache_ntlm_auth.user); + if (entry == NULL || entry->nt_hash == NULL || entry->lm_hash == NULL) { + DEBUG(10,("winbindd_dual_ccache_ntlm_auth: could not find " + "credentials for user %s\n", + state->request.data.ccache_ntlm_auth.user)); + goto process_result; + } + + DEBUG(10,("winbindd_dual_ccache_ntlm_auth: found ccache [%s]\n", entry->username)); + + if (!client_can_access_ccache_entry(state->request.data.ccache_ntlm_auth.uid, entry)) { + goto process_result; + } + + if (initial_blob_len == 0 && challenge_blob_len == 0) { + /* this is just a probe to see if credentials are available. */ + result = NT_STATUS_OK; + state->response.data.ccache_ntlm_auth.auth_blob_len = 0; + goto process_result; + } + + initial = data_blob(state->request.extra_data.data, initial_blob_len); + challenge = data_blob(state->request.extra_data.data + initial_blob_len, + state->request.data.ccache_ntlm_auth.challenge_blob_len); + + if (!initial.data || !challenge.data) { + result = NT_STATUS_NO_MEMORY; + } else { + result = do_ntlm_auth_with_hashes(name_user, name_domain, + entry->lm_hash, entry->nt_hash, + initial, challenge, &auth); + } + + data_blob_free(&initial); + data_blob_free(&challenge); + + if (!NT_STATUS_IS_OK(result)) { + goto process_result; + } + + state->response.extra_data.data = smb_xmemdup(auth.data, auth.length); + if (!state->response.extra_data.data) { + result = NT_STATUS_NO_MEMORY; + goto process_result; + } + state->response.length += auth.length; + state->response.data.ccache_ntlm_auth.auth_blob_len = auth.length; + + data_blob_free(&auth); + + process_result: + return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR; +} diff --git a/source3/winbindd/winbindd_cm.c b/source3/winbindd/winbindd_cm.c new file mode 100644 index 0000000000..9ffb3dfb23 --- /dev/null +++ b/source3/winbindd/winbindd_cm.c @@ -0,0 +1,2271 @@ +/* + Unix SMB/CIFS implementation. + + Winbind daemon connection manager + + Copyright (C) Tim Potter 2001 + Copyright (C) Andrew Bartlett 2002 + Copyright (C) Gerald (Jerry) Carter 2003-2005. + Copyright (C) Volker Lendecke 2004-2005 + Copyright (C) Jeremy Allison 2006 + + 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 3 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, see . +*/ + +/* + We need to manage connections to domain controllers without having to + mess up the main winbindd code with other issues. The aim of the + connection manager is to: + + - make connections to domain controllers and cache them + - re-establish connections when networks or servers go down + - centralise the policy on connection timeouts, domain controller + selection etc + - manage re-entrancy for when winbindd becomes able to handle + multiple outstanding rpc requests + + Why not have connection management as part of the rpc layer like tng? + Good question. This code may morph into libsmb/rpc_cache.c or something + like that but at the moment it's simply staying as part of winbind. I + think the TNG architecture of forcing every user of the rpc layer to use + the connection caching system is a bad idea. It should be an optional + method of using the routines. + + The TNG design is quite good but I disagree with some aspects of the + implementation. -tpot + + */ + +/* + TODO: + + - I'm pretty annoyed by all the make_nmb_name() stuff. It should be + moved down into another function. + + - Take care when destroying cli_structs as they can be shared between + various sam handles. + + */ + +#include "includes.h" +#include "winbindd.h" + +#undef DBGC_CLASS +#define DBGC_CLASS DBGC_WINBIND + +struct dc_name_ip { + fstring name; + struct in_addr ip; +}; + +extern struct winbindd_methods reconnect_methods; +extern BOOL override_logfile; + +static NTSTATUS init_dc_connection_network(struct winbindd_domain *domain); +static void set_dc_type_and_flags( struct winbindd_domain *domain ); +static BOOL get_dcs(TALLOC_CTX *mem_ctx, const struct winbindd_domain *domain, + struct dc_name_ip **dcs, int *num_dcs); + +/**************************************************************** + Child failed to find DC's. Reschedule check. +****************************************************************/ + +static void msg_failed_to_go_online(struct messaging_context *msg, + void *private_data, + uint32_t msg_type, + struct server_id server_id, + DATA_BLOB *data) +{ + struct winbindd_domain *domain; + const char *domainname = (const char *)data->data; + + if (data->data == NULL || data->length == 0) { + return; + } + + DEBUG(5,("msg_fail_to_go_online: received for domain %s.\n", domainname)); + + for (domain = domain_list(); domain; domain = domain->next) { + if (domain->internal) { + continue; + } + + if (strequal(domain->name, domainname)) { + if (domain->online) { + /* We're already online, ignore. */ + DEBUG(5,("msg_fail_to_go_online: domain %s " + "already online.\n", domainname)); + continue; + } + + /* Reschedule the online check. */ + set_domain_offline(domain); + break; + } + } +} + +/**************************************************************** + Actually cause a reconnect from a message. +****************************************************************/ + +static void msg_try_to_go_online(struct messaging_context *msg, + void *private_data, + uint32_t msg_type, + struct server_id server_id, + DATA_BLOB *data) +{ + struct winbindd_domain *domain; + const char *domainname = (const char *)data->data; + + if (data->data == NULL || data->length == 0) { + return; + } + + DEBUG(5,("msg_try_to_go_online: received for domain %s.\n", domainname)); + + for (domain = domain_list(); domain; domain = domain->next) { + if (domain->internal) { + continue; + } + + if (strequal(domain->name, domainname)) { + + if (domain->online) { + /* We're already online, ignore. */ + DEBUG(5,("msg_try_to_go_online: domain %s " + "already online.\n", domainname)); + continue; + } + + /* This call takes care of setting the online + flag to true if we connected, or re-adding + the offline handler if false. Bypasses online + check so always does network calls. */ + + init_dc_connection_network(domain); + break; + } + } +} + +/**************************************************************** + Fork a child to try and contact a DC. Do this as contacting a + DC requires blocking lookups and we don't want to block our + parent. +****************************************************************/ + +static BOOL fork_child_dc_connect(struct winbindd_domain *domain) +{ + struct dc_name_ip *dcs = NULL; + int num_dcs = 0; + TALLOC_CTX *mem_ctx = NULL; + pid_t child_pid; + pid_t parent_pid = sys_getpid(); + + /* Stop zombies */ + CatchChild(); + + child_pid = sys_fork(); + + if (child_pid == -1) { + DEBUG(0, ("fork_child_dc_connect: Could not fork: %s\n", strerror(errno))); + return False; + } + + if (child_pid != 0) { + /* Parent */ + messaging_register(winbind_messaging_context(), NULL, + MSG_WINBIND_TRY_TO_GO_ONLINE, + msg_try_to_go_online); + messaging_register(winbind_messaging_context(), NULL, + MSG_WINBIND_FAILED_TO_GO_ONLINE, + msg_failed_to_go_online); + return True; + } + + /* Child. */ + + /* Leave messages blocked - we will never process one. */ + + /* tdb needs special fork handling */ + if (tdb_reopen_all(1) == -1) { + DEBUG(0,("tdb_reopen_all failed.\n")); + _exit(0); + } + + close_conns_after_fork(); + + if (!override_logfile) { + pstring logfile; + pstr_sprintf(logfile, "%s/log.winbindd-dc-connect", dyn_LOGFILEBASE); + lp_set_logfile(logfile); + reopen_logs(); + } + + mem_ctx = talloc_init("fork_child_dc_connect"); + if (!mem_ctx) { + DEBUG(0,("talloc_init failed.\n")); + _exit(0); + } + + if ((!get_dcs(mem_ctx, domain, &dcs, &num_dcs)) || (num_dcs == 0)) { + /* Still offline ? Can't find DC's. */ + messaging_send_buf(winbind_messaging_context(), + pid_to_procid(parent_pid), + MSG_WINBIND_FAILED_TO_GO_ONLINE, + (uint8 *)domain->name, + strlen(domain->name)+1); + _exit(0); + } + + /* We got a DC. Send a message to our parent to get it to + try and do the same. */ + + messaging_send_buf(winbind_messaging_context(), + pid_to_procid(parent_pid), + MSG_WINBIND_TRY_TO_GO_ONLINE, + (uint8 *)domain->name, + strlen(domain->name)+1); + _exit(0); +} + +/**************************************************************** + Handler triggered if we're offline to try and detect a DC. +****************************************************************/ + +static void check_domain_online_handler(struct event_context *ctx, + struct timed_event *te, + const struct timeval *now, + void *private_data) +{ + struct winbindd_domain *domain = + (struct winbindd_domain *)private_data; + + DEBUG(10,("check_domain_online_handler: called for domain " + "%s (online = %s)\n", domain->name, + domain->online ? "True" : "False" )); + + TALLOC_FREE(domain->check_online_event); + + /* Are we still in "startup" mode ? */ + + if (domain->startup && (now->tv_sec > domain->startup_time + 30)) { + /* No longer in "startup" mode. */ + DEBUG(10,("check_domain_online_handler: domain %s no longer in 'startup' mode.\n", + domain->name )); + domain->startup = False; + } + + /* We've been told to stay offline, so stay + that way. */ + + if (get_global_winbindd_state_offline()) { + DEBUG(10,("check_domain_online_handler: domain %s remaining globally offline\n", + domain->name )); + return; + } + + /* Fork a child to test if it can contact a DC. + If it can then send ourselves a message to + cause a reconnect. */ + + fork_child_dc_connect(domain); +} + +/**************************************************************** + If we're still offline setup the timeout check. +****************************************************************/ + +static void calc_new_online_timeout_check(struct winbindd_domain *domain) +{ + int wbc = lp_winbind_cache_time(); + + if (domain->startup) { + domain->check_online_timeout = 10; + } else if (domain->check_online_timeout < wbc) { + domain->check_online_timeout = wbc; + } +} + +/**************************************************************** + Set domain offline and also add handler to put us back online + if we detect a DC. +****************************************************************/ + +void set_domain_offline(struct winbindd_domain *domain) +{ + DEBUG(10,("set_domain_offline: called for domain %s\n", + domain->name )); + + TALLOC_FREE(domain->check_online_event); + + if (domain->internal) { + DEBUG(3,("set_domain_offline: domain %s is internal - logic error.\n", + domain->name )); + return; + } + + domain->online = False; + + /* Offline domains are always initialized. They're + re-initialized when they go back online. */ + + domain->initialized = True; + + /* We only add the timeout handler that checks and + allows us to go back online when we've not + been told to remain offline. */ + + if (get_global_winbindd_state_offline()) { + DEBUG(10,("set_domain_offline: domain %s remaining globally offline\n", + domain->name )); + return; + } + + /* If we're in statup mode, check again in 10 seconds, not in + lp_winbind_cache_time() seconds (which is 5 mins by default). */ + + calc_new_online_timeout_check(domain); + + domain->check_online_event = event_add_timed(winbind_event_context(), + NULL, + timeval_current_ofs(domain->check_online_timeout,0), + "check_domain_online_handler", + check_domain_online_handler, + domain); + + /* The above *has* to succeed for winbindd to work. */ + if (!domain->check_online_event) { + smb_panic("set_domain_offline: failed to add online handler"); + } + + DEBUG(10,("set_domain_offline: added event handler for domain %s\n", + domain->name )); + + /* Send an offline message to the idmap child when our + primary domain goes offline */ + + if ( domain->primary ) { + struct winbindd_child *idmap = idmap_child(); + + if ( idmap->pid != 0 ) { + messaging_send_buf(winbind_messaging_context(), + pid_to_procid(idmap->pid), + MSG_WINBIND_OFFLINE, + (uint8 *)domain->name, + strlen(domain->name)+1); + } + } + + return; +} + +/**************************************************************** + Set domain online - if allowed. +****************************************************************/ + +static void set_domain_online(struct winbindd_domain *domain) +{ + struct timeval now; + + DEBUG(10,("set_domain_online: called for domain %s\n", + domain->name )); + + if (domain->internal) { + DEBUG(3,("set_domain_online: domain %s is internal - logic error.\n", + domain->name )); + return; + } + + if (get_global_winbindd_state_offline()) { + DEBUG(10,("set_domain_online: domain %s remaining globally offline\n", + domain->name )); + return; + } + + winbindd_set_locator_kdc_envs(domain); + + /* If we are waiting to get a krb5 ticket, trigger immediately. */ + GetTimeOfDay(&now); + set_event_dispatch_time(winbind_event_context(), + "krb5_ticket_gain_handler", now); + + /* Ok, we're out of any startup mode now... */ + domain->startup = False; + + if (domain->online == False) { + /* We were offline - now we're online. We default to + using the MS-RPC backend if we started offline, + and if we're going online for the first time we + should really re-initialize the backends and the + checks to see if we're talking to an AD or NT domain. + */ + + domain->initialized = False; + + /* 'reconnect_methods' is the MS-RPC backend. */ + if (domain->backend == &reconnect_methods) { + domain->backend = NULL; + } + } + + /* Ensure we have no online timeout checks. */ + domain->check_online_timeout = 0; + TALLOC_FREE(domain->check_online_event); + + /* Ensure we ignore any pending child messages. */ + messaging_deregister(winbind_messaging_context(), + MSG_WINBIND_TRY_TO_GO_ONLINE, NULL); + messaging_deregister(winbind_messaging_context(), + MSG_WINBIND_FAILED_TO_GO_ONLINE, NULL); + + domain->online = True; + + /* Send an online message to the idmap child when our + primary domain comes online */ + + if ( domain->primary ) { + struct winbindd_child *idmap = idmap_child(); + + if ( idmap->pid != 0 ) { + messaging_send_buf(winbind_messaging_context(), + pid_to_procid(idmap->pid), + MSG_WINBIND_ONLINE, + (uint8 *)domain->name, + strlen(domain->name)+1); + } + } + + return; +} + +/**************************************************************** + Requested to set a domain online. +****************************************************************/ + +void set_domain_online_request(struct winbindd_domain *domain) +{ + struct timeval tev; + + DEBUG(10,("set_domain_online_request: called for domain %s\n", + domain->name )); + + if (get_global_winbindd_state_offline()) { + DEBUG(10,("set_domain_online_request: domain %s remaining globally offline\n", + domain->name )); + return; + } + + /* We've been told it's safe to go online and + try and connect to a DC. But I don't believe it + because network manager seems to lie. + Wait at least 5 seconds. Heuristics suck... */ + + if (!domain->check_online_event) { + /* If we've come from being globally offline we + don't have a check online event handler set. + We need to add one now we're trying to go + back online. */ + + DEBUG(10,("set_domain_online_request: domain %s was globally offline.\n", + domain->name )); + + domain->check_online_event = event_add_timed(winbind_event_context(), + NULL, + timeval_current_ofs(5, 0), + "check_domain_online_handler", + check_domain_online_handler, + domain); + + /* The above *has* to succeed for winbindd to work. */ + if (!domain->check_online_event) { + smb_panic("set_domain_online_request: failed to add online handler"); + } + } + + GetTimeOfDay(&tev); + + /* Go into "startup" mode again. */ + domain->startup_time = tev.tv_sec; + domain->startup = True; + + tev.tv_sec += 5; + + set_event_dispatch_time(winbind_event_context(), "check_domain_online_handler", tev); +} + +/**************************************************************** + Add -ve connection cache entries for domain and realm. +****************************************************************/ + +void winbind_add_failed_connection_entry(const struct winbindd_domain *domain, + const char *server, + NTSTATUS result) +{ + add_failed_connection_entry(domain->name, server, result); + /* If this was the saf name for the last thing we talked to, + remove it. */ + saf_delete(domain->name); + if (*domain->alt_name) { + add_failed_connection_entry(domain->alt_name, server, result); + saf_delete(domain->alt_name); + } + winbindd_unset_locator_kdc_env(domain); +} + +/* Choose between anonymous or authenticated connections. We need to use + an authenticated connection if DCs have the RestrictAnonymous registry + entry set > 0, or the "Additional restrictions for anonymous + connections" set in the win2k Local Security Policy. + + Caller to free() result in domain, username, password +*/ + +static void cm_get_ipc_userpass(char **username, char **domain, char **password) +{ + *username = (char *)secrets_fetch(SECRETS_AUTH_USER, NULL); + *domain = (char *)secrets_fetch(SECRETS_AUTH_DOMAIN, NULL); + *password = (char *)secrets_fetch(SECRETS_AUTH_PASSWORD, NULL); + + if (*username && **username) { + + if (!*domain || !**domain) + *domain = smb_xstrdup(lp_workgroup()); + + if (!*password || !**password) + *password = smb_xstrdup(""); + + DEBUG(3, ("cm_get_ipc_userpass: Retrieved auth-user from secrets.tdb [%s\\%s]\n", + *domain, *username)); + + } else { + DEBUG(3, ("cm_get_ipc_userpass: No auth-user defined\n")); + *username = smb_xstrdup(""); + *domain = smb_xstrdup(""); + *password = smb_xstrdup(""); + } +} + +static BOOL get_dc_name_via_netlogon(const struct winbindd_domain *domain, + fstring dcname, struct in_addr *dc_ip) +{ + struct winbindd_domain *our_domain = NULL; + struct rpc_pipe_client *netlogon_pipe = NULL; + NTSTATUS result; + WERROR werr; + TALLOC_CTX *mem_ctx; + unsigned int orig_timeout; + fstring tmp; + char *p; + + /* Hmmmm. We can only open one connection to the NETLOGON pipe at the + * moment.... */ + + if (IS_DC) { + return False; + } + + if (domain->primary) { + return False; + } + + our_domain = find_our_domain(); + + if ((mem_ctx = talloc_init("get_dc_name_via_netlogon")) == NULL) { + return False; + } + + result = cm_connect_netlogon(our_domain, &netlogon_pipe); + if (!NT_STATUS_IS_OK(result)) { + talloc_destroy(mem_ctx); + return False; + } + + /* This call can take a long time - allow the server to time out. + 35 seconds should do it. */ + + orig_timeout = cli_set_timeout(netlogon_pipe->cli, 35000); + + werr = rpccli_netlogon_getanydcname(netlogon_pipe, mem_ctx, our_domain->dcname, + domain->name, tmp); + + /* And restore our original timeout. */ + cli_set_timeout(netlogon_pipe->cli, orig_timeout); + + talloc_destroy(mem_ctx); + + if (!W_ERROR_IS_OK(werr)) { + DEBUG(10, ("rpccli_netlogon_getanydcname failed: %s\n", + dos_errstr(werr))); + return False; + } + + /* cli_netlogon_getanydcname gives us a name with \\ */ + p = tmp; + if (*p == '\\') { + p+=1; + } + if (*p == '\\') { + p+=1; + } + + fstrcpy(dcname, p); + + DEBUG(10, ("rpccli_netlogon_getanydcname returned %s\n", dcname)); + + if (!resolve_name(dcname, dc_ip, 0x20)) { + return False; + } + + return True; +} + +/************************************************************************ + Given a fd with a just-connected TCP connection to a DC, open a connection + to the pipe. +************************************************************************/ + +static NTSTATUS cm_prepare_connection(const struct winbindd_domain *domain, + const int sockfd, + const char *controller, + struct cli_state **cli, + BOOL *retry) +{ + char *machine_password, *machine_krb5_principal, *machine_account; + char *ipc_username, *ipc_domain, *ipc_password; + + BOOL got_mutex; + + NTSTATUS result = NT_STATUS_UNSUCCESSFUL; + + struct sockaddr peeraddr; + socklen_t peeraddr_len; + + struct sockaddr_in *peeraddr_in = (struct sockaddr_in *)&peeraddr; + + DEBUG(10,("cm_prepare_connection: connecting to DC %s for domain %s\n", + controller, domain->name )); + + machine_password = secrets_fetch_machine_password(lp_workgroup(), NULL, + NULL); + + if (asprintf(&machine_account, "%s$", global_myname()) == -1) { + SAFE_FREE(machine_password); + return NT_STATUS_NO_MEMORY; + } + + if (asprintf(&machine_krb5_principal, "%s$@%s", global_myname(), + lp_realm()) == -1) { + SAFE_FREE(machine_account); + SAFE_FREE(machine_password); + return NT_STATUS_NO_MEMORY; + } + + cm_get_ipc_userpass(&ipc_username, &ipc_domain, &ipc_password); + + *retry = True; + + got_mutex = secrets_named_mutex(controller, + WINBIND_SERVER_MUTEX_WAIT_TIME); + + if (!got_mutex) { + DEBUG(0,("cm_prepare_connection: mutex grab failed for %s\n", + controller)); + result = NT_STATUS_POSSIBLE_DEADLOCK; + goto done; + } + + if ((*cli = cli_initialise()) == NULL) { + DEBUG(1, ("Could not cli_initialize\n")); + result = NT_STATUS_NO_MEMORY; + goto done; + } + + (*cli)->timeout = 10000; /* 10 seconds */ + (*cli)->fd = sockfd; + fstrcpy((*cli)->desthost, controller); + (*cli)->use_kerberos = True; + + peeraddr_len = sizeof(peeraddr); + + if ((getpeername((*cli)->fd, &peeraddr, &peeraddr_len) != 0) || + (peeraddr_len != sizeof(struct sockaddr_in)) || + (peeraddr_in->sin_family != PF_INET)) + { + DEBUG(0,("cm_prepare_connection: %s\n", strerror(errno))); + result = NT_STATUS_UNSUCCESSFUL; + goto done; + } + + if (ntohs(peeraddr_in->sin_port) == 139) { + struct nmb_name calling; + struct nmb_name called; + + make_nmb_name(&calling, global_myname(), 0x0); + make_nmb_name(&called, "*SMBSERVER", 0x20); + + if (!cli_session_request(*cli, &calling, &called)) { + DEBUG(8, ("cli_session_request failed for %s\n", + controller)); + result = NT_STATUS_UNSUCCESSFUL; + goto done; + } + } + + cli_setup_signing_state(*cli, Undefined); + + if (!cli_negprot(*cli)) { + DEBUG(1, ("cli_negprot failed\n")); + result = NT_STATUS_UNSUCCESSFUL; + goto done; + } + + if ((*cli)->protocol >= PROTOCOL_NT1 && (*cli)->capabilities & CAP_EXTENDED_SECURITY) { + ADS_STATUS ads_status; + + if (lp_security() == SEC_ADS) { + + /* Try a krb5 session */ + + (*cli)->use_kerberos = True; + DEBUG(5, ("connecting to %s from %s with kerberos principal " + "[%s]\n", controller, global_myname(), + machine_krb5_principal)); + + winbindd_set_locator_kdc_envs(domain); + + ads_status = cli_session_setup_spnego(*cli, + machine_krb5_principal, + machine_password, + lp_workgroup()); + + if (!ADS_ERR_OK(ads_status)) { + DEBUG(4,("failed kerberos session setup with %s\n", + ads_errstr(ads_status))); + } + + result = ads_ntstatus(ads_status); + if (NT_STATUS_IS_OK(result)) { + /* Ensure creds are stored for NTLMSSP authenticated pipe access. */ + cli_init_creds(*cli, machine_account, lp_workgroup(), machine_password); + goto session_setup_done; + } + } + + /* Fall back to non-kerberos session setup using NTLMSSP SPNEGO with the machine account. */ + (*cli)->use_kerberos = False; + + DEBUG(5, ("connecting to %s from %s with username " + "[%s]\\[%s]\n", controller, global_myname(), + lp_workgroup(), machine_account)); + + ads_status = cli_session_setup_spnego(*cli, + machine_account, + machine_password, + lp_workgroup()); + if (!ADS_ERR_OK(ads_status)) { + DEBUG(4, ("authenticated session setup failed with %s\n", + ads_errstr(ads_status))); + } + + result = ads_ntstatus(ads_status); + if (NT_STATUS_IS_OK(result)) { + /* Ensure creds are stored for NTLMSSP authenticated pipe access. */ + cli_init_creds(*cli, machine_account, lp_workgroup(), machine_password); + goto session_setup_done; + } + } + + /* Fall back to non-kerberos session setup */ + + (*cli)->use_kerberos = False; + + if ((((*cli)->sec_mode & NEGOTIATE_SECURITY_CHALLENGE_RESPONSE) != 0) && + (strlen(ipc_username) > 0)) { + + /* Only try authenticated if we have a username */ + + DEBUG(5, ("connecting to %s from %s with username " + "[%s]\\[%s]\n", controller, global_myname(), + ipc_domain, ipc_username)); + + if (NT_STATUS_IS_OK(cli_session_setup( + *cli, ipc_username, + ipc_password, strlen(ipc_password)+1, + ipc_password, strlen(ipc_password)+1, + ipc_domain))) { + /* Successful logon with given username. */ + cli_init_creds(*cli, ipc_username, ipc_domain, ipc_password); + goto session_setup_done; + } else { + DEBUG(4, ("authenticated session setup with user %s\\%s failed.\n", + ipc_domain, ipc_username )); + } + } + + /* Fall back to anonymous connection, this might fail later */ + + if (NT_STATUS_IS_OK(cli_session_setup(*cli, "", NULL, 0, + NULL, 0, ""))) { + DEBUG(5, ("Connected anonymously\n")); + cli_init_creds(*cli, "", "", ""); + goto session_setup_done; + } + + result = cli_nt_error(*cli); + + if (NT_STATUS_IS_OK(result)) + result = NT_STATUS_UNSUCCESSFUL; + + /* We can't session setup */ + + goto done; + + session_setup_done: + + /* cache the server name for later connections */ + + saf_store( domain->name, (*cli)->desthost ); + if (domain->alt_name && (*cli)->use_kerberos) { + saf_store( domain->alt_name, (*cli)->desthost ); + } + + winbindd_set_locator_kdc_envs(domain); + + if (!cli_send_tconX(*cli, "IPC$", "IPC", "", 0)) { + + result = cli_nt_error(*cli); + + DEBUG(1,("failed tcon_X with %s\n", nt_errstr(result))); + + if (NT_STATUS_IS_OK(result)) + result = NT_STATUS_UNSUCCESSFUL; + + goto done; + } + + secrets_named_mutex_release(controller); + got_mutex = False; + *retry = False; + + /* set the domain if empty; needed for schannel connections */ + if ( !*(*cli)->domain ) { + fstrcpy( (*cli)->domain, domain->name ); + } + + result = NT_STATUS_OK; + + done: + if (got_mutex) { + secrets_named_mutex_release(controller); + } + + SAFE_FREE(machine_account); + SAFE_FREE(machine_password); + SAFE_FREE(machine_krb5_principal); + SAFE_FREE(ipc_username); + SAFE_FREE(ipc_domain); + SAFE_FREE(ipc_password); + + if (!NT_STATUS_IS_OK(result)) { + winbind_add_failed_connection_entry(domain, controller, result); + if ((*cli) != NULL) { + cli_shutdown(*cli); + *cli = NULL; + } + } + + return result; +} + +static BOOL add_one_dc_unique(TALLOC_CTX *mem_ctx, const char *domain_name, + const char *dcname, struct in_addr ip, + struct dc_name_ip **dcs, int *num) +{ + if (!NT_STATUS_IS_OK(check_negative_conn_cache(domain_name, dcname))) { + DEBUG(10, ("DC %s was in the negative conn cache\n", dcname)); + return False; + } + + *dcs = TALLOC_REALLOC_ARRAY(mem_ctx, *dcs, struct dc_name_ip, (*num)+1); + + if (*dcs == NULL) + return False; + + fstrcpy((*dcs)[*num].name, dcname); + (*dcs)[*num].ip = ip; + *num += 1; + return True; +} + +static BOOL add_sockaddr_to_array(TALLOC_CTX *mem_ctx, + struct in_addr ip, uint16 port, + struct sockaddr_in **addrs, int *num) +{ + *addrs = TALLOC_REALLOC_ARRAY(mem_ctx, *addrs, struct sockaddr_in, (*num)+1); + + if (*addrs == NULL) { + *num = 0; + return False; + } + + (*addrs)[*num].sin_family = PF_INET; + putip((char *)&((*addrs)[*num].sin_addr), (char *)&ip); + (*addrs)[*num].sin_port = htons(port); + + *num += 1; + return True; +} + +static void mailslot_name(struct in_addr dc_ip, fstring name) +{ + fstr_sprintf(name, "\\MAILSLOT\\NET\\GETDC%X", dc_ip.s_addr); +} + +static BOOL send_getdc_request(struct in_addr dc_ip, + const char *domain_name, + const DOM_SID *sid) +{ + pstring outbuf; + char *p; + fstring my_acct_name; + fstring my_mailslot; + + mailslot_name(dc_ip, my_mailslot); + + memset(outbuf, '\0', sizeof(outbuf)); + + p = outbuf; + + SCVAL(p, 0, SAMLOGON); + p++; + + SCVAL(p, 0, 0); /* Count pointer ... */ + p++; + + SIVAL(p, 0, 0); /* The sender's token ... */ + p += 2; + + p += dos_PutUniCode(p, global_myname(), sizeof(pstring), True); + fstr_sprintf(my_acct_name, "%s$", global_myname()); + p += dos_PutUniCode(p, my_acct_name, sizeof(pstring), True); + + memcpy(p, my_mailslot, strlen(my_mailslot)+1); + p += strlen(my_mailslot)+1; + + SIVAL(p, 0, 0x80); + p+=4; + + SIVAL(p, 0, sid_size(sid)); + p+=4; + + p = ALIGN4(p, outbuf); + + sid_linearize(p, sid_size(sid), sid); + p += sid_size(sid); + + SIVAL(p, 0, 1); + SSVAL(p, 4, 0xffff); + SSVAL(p, 6, 0xffff); + p+=8; + + return cli_send_mailslot(winbind_messaging_context(), + False, "\\MAILSLOT\\NET\\NTLOGON", 0, + outbuf, PTR_DIFF(p, outbuf), + global_myname(), 0, domain_name, 0x1c, + dc_ip); +} + +static BOOL receive_getdc_response(struct in_addr dc_ip, + const char *domain_name, + fstring dc_name) +{ + struct packet_struct *packet; + fstring my_mailslot; + char *buf, *p; + fstring dcname, user, domain; + int len; + + mailslot_name(dc_ip, my_mailslot); + + packet = receive_unexpected(DGRAM_PACKET, 0, my_mailslot); + + if (packet == NULL) { + DEBUG(5, ("Did not receive packet for %s\n", my_mailslot)); + return False; + } + + DEBUG(5, ("Received packet for %s\n", my_mailslot)); + + buf = packet->packet.dgram.data; + len = packet->packet.dgram.datasize; + + if (len < 70) { + /* 70 is a completely arbitrary value to make sure + the SVAL below does not read uninitialized memory */ + DEBUG(3, ("GetDC got short response\n")); + return False; + } + + /* This should be (buf-4)+SVAL(buf-4, smb_vwv12)... */ + p = buf+SVAL(buf, smb_vwv10); + + if (CVAL(p,0) != SAMLOGON_R) { + DEBUG(8, ("GetDC got invalid response type %d\n", CVAL(p, 0))); + return False; + } + + p+=2; + pull_ucs2(buf, dcname, p, sizeof(dcname), PTR_DIFF(buf+len, p), + STR_TERMINATE|STR_NOALIGN); + p = skip_unibuf(p, PTR_DIFF(buf+len, p)); + pull_ucs2(buf, user, p, sizeof(dcname), PTR_DIFF(buf+len, p), + STR_TERMINATE|STR_NOALIGN); + p = skip_unibuf(p, PTR_DIFF(buf+len, p)); + pull_ucs2(buf, domain, p, sizeof(dcname), PTR_DIFF(buf+len, p), + STR_TERMINATE|STR_NOALIGN); + p = skip_unibuf(p, PTR_DIFF(buf+len, p)); + + if (!strequal(domain, domain_name)) { + DEBUG(3, ("GetDC: Expected domain %s, got %s\n", + domain_name, domain)); + return False; + } + + p = dcname; + if (*p == '\\') p += 1; + if (*p == '\\') p += 1; + + fstrcpy(dc_name, p); + + DEBUG(10, ("GetDC gave name %s for domain %s\n", + dc_name, domain)); + + return True; +} + +/******************************************************************* + convert an ip to a name +*******************************************************************/ + +static BOOL dcip_to_name(const struct winbindd_domain *domain, struct in_addr ip, fstring name ) +{ + struct ip_service ip_list; + + ip_list.ip = ip; + ip_list.port = 0; + +#ifdef WITH_ADS + /* For active directory servers, try to get the ldap server name. + None of these failures should be considered critical for now */ + + if (lp_security() == SEC_ADS) { + ADS_STRUCT *ads; + + ads = ads_init(domain->alt_name, domain->name, NULL); + ads->auth.flags |= ADS_AUTH_NO_BIND; + + if (ads_try_connect( ads, inet_ntoa(ip) ) ) { + /* We got a cldap packet. */ + fstrcpy(name, ads->config.ldap_server_name); + namecache_store(name, 0x20, 1, &ip_list); + + DEBUG(10,("dcip_to_name: flags = 0x%x\n", (unsigned int)ads->config.flags)); + + if (domain->primary && (ads->config.flags & ADS_KDC)) { + if (ads_closest_dc(ads)) { + char *sitename = sitename_fetch(ads->config.realm); + + /* We're going to use this KDC for this realm/domain. + If we are using sites, then force the krb5 libs + to use this KDC. */ + + create_local_private_krb5_conf_for_domain(domain->alt_name, + domain->name, + sitename, + ip); + + SAFE_FREE(sitename); + } else { + /* use an off site KDC */ + create_local_private_krb5_conf_for_domain(domain->alt_name, + domain->name, + NULL, + ip); + } + winbindd_set_locator_kdc_envs(domain); + + /* Ensure we contact this DC also. */ + saf_store( domain->name, name); + saf_store( domain->alt_name, name); + } + + ads_destroy( &ads ); + return True; + } + + ads_destroy( &ads ); + } +#endif + + /* try GETDC requests next */ + + if (send_getdc_request(ip, domain->name, &domain->sid)) { + int i; + smb_msleep(100); + for (i=0; i<5; i++) { + if (receive_getdc_response(ip, domain->name, name)) { + namecache_store(name, 0x20, 1, &ip_list); + return True; + } + smb_msleep(500); + } + } + + /* try node status request */ + + if ( name_status_find(domain->name, 0x1c, 0x20, ip, name) ) { + namecache_store(name, 0x20, 1, &ip_list); + return True; + } + return False; +} + +/******************************************************************* + Retreive a list of IP address for domain controllers. Fill in + the dcs[] with results. +*******************************************************************/ + +static BOOL get_dcs(TALLOC_CTX *mem_ctx, const struct winbindd_domain *domain, + struct dc_name_ip **dcs, int *num_dcs) +{ + fstring dcname; + struct in_addr ip; + struct ip_service *ip_list = NULL; + int iplist_size = 0; + int i; + BOOL is_our_domain; + enum security_types sec = (enum security_types)lp_security(); + + is_our_domain = strequal(domain->name, lp_workgroup()); + + if ( !is_our_domain + && get_dc_name_via_netlogon(domain, dcname, &ip) + && add_one_dc_unique(mem_ctx, domain->name, dcname, ip, dcs, num_dcs) ) + { + DEBUG(10, ("Retrieved DC %s at %s via netlogon\n", + dcname, inet_ntoa(ip))); + return True; + } + + if (sec == SEC_ADS) { + char *sitename = NULL; + + /* We need to make sure we know the local site before + doing any DNS queries, as this will restrict the + get_sorted_dc_list() call below to only fetching + DNS records for the correct site. */ + + /* Find any DC to get the site record. + We deliberately don't care about the + return here. */ + + get_dc_name(domain->name, domain->alt_name, dcname, &ip); + + sitename = sitename_fetch(domain->alt_name); + if (sitename) { + + /* Do the site-specific AD dns lookup first. */ + get_sorted_dc_list(domain->alt_name, sitename, &ip_list, &iplist_size, True); + + for ( i=0; iname, inet_ntoa(ip_list[i].ip), + ip_list[i].ip, dcs, num_dcs); + } + + SAFE_FREE(ip_list); + SAFE_FREE(sitename); + iplist_size = 0; + } + + /* Now we add DCs from the main AD dns lookup. */ + get_sorted_dc_list(domain->alt_name, NULL, &ip_list, &iplist_size, True); + + for ( i=0; iname, inet_ntoa(ip_list[i].ip), + ip_list[i].ip, dcs, num_dcs); + } + } + + /* try standard netbios queries if no ADS */ + + if (iplist_size==0) { + get_sorted_dc_list(domain->name, NULL, &ip_list, &iplist_size, False); + } + + /* FIXME!! this is where we should re-insert the GETDC requests --jerry */ + + /* now add to the dc array. We'll wait until the last minute + to look up the name of the DC. But we fill in the char* for + the ip now in to make the failed connection cache work */ + + for ( i=0; iname, inet_ntoa(ip_list[i].ip), + ip_list[i].ip, dcs, num_dcs); + } + + SAFE_FREE( ip_list ); + + return True; +} + +static BOOL find_new_dc(TALLOC_CTX *mem_ctx, + const struct winbindd_domain *domain, + fstring dcname, struct sockaddr_in *addr, int *fd) +{ + struct dc_name_ip *dcs = NULL; + int num_dcs = 0; + + const char **dcnames = NULL; + int num_dcnames = 0; + + struct sockaddr_in *addrs = NULL; + int num_addrs = 0; + + int i, fd_index; + + again: + if (!get_dcs(mem_ctx, domain, &dcs, &num_dcs) || (num_dcs == 0)) + return False; + + for (i=0; iname, inet_ntoa(dcs[i].ip), strerror(errno) )); + winbind_add_failed_connection_entry(domain, + dcs[i].name, NT_STATUS_UNSUCCESSFUL); + } + return False; + } + + *addr = addrs[fd_index]; + + if (*dcnames[fd_index] != '\0' && !is_ipaddress(dcnames[fd_index])) { + /* Ok, we've got a name for the DC */ + fstrcpy(dcname, dcnames[fd_index]); + return True; + } + + /* Try to figure out the name */ + if (dcip_to_name( domain, addr->sin_addr, dcname )) { + return True; + } + + /* We can not continue without the DC's name */ + winbind_add_failed_connection_entry(domain, dcs[fd_index].name, + NT_STATUS_UNSUCCESSFUL); + goto again; +} + +static NTSTATUS cm_open_connection(struct winbindd_domain *domain, + struct winbindd_cm_conn *new_conn) +{ + TALLOC_CTX *mem_ctx; + NTSTATUS result; + char *saf_servername = saf_fetch( domain->name ); + int retries; + + if ((mem_ctx = talloc_init("cm_open_connection")) == NULL) { + SAFE_FREE(saf_servername); + set_domain_offline(domain); + return NT_STATUS_NO_MEMORY; + } + + /* we have to check the server affinity cache here since + later we selecte a DC based on response time and not preference */ + + /* Check the negative connection cache + before talking to it. It going down may have + triggered the reconnection. */ + + if ( saf_servername && NT_STATUS_IS_OK(check_negative_conn_cache( domain->name, saf_servername))) { + + DEBUG(10,("cm_open_connection: saf_servername is '%s' for domain %s\n", + saf_servername, domain->name )); + + /* convert an ip address to a name */ + if ( is_ipaddress( saf_servername ) ) { + fstring saf_name; + struct in_addr ip; + + ip = *interpret_addr2( saf_servername ); + if (dcip_to_name( domain, ip, saf_name )) { + fstrcpy( domain->dcname, saf_name ); + } else { + winbind_add_failed_connection_entry( + domain, saf_servername, + NT_STATUS_UNSUCCESSFUL); + } + } else { + fstrcpy( domain->dcname, saf_servername ); + } + + SAFE_FREE( saf_servername ); + } + + for (retries = 0; retries < 3; retries++) { + + int fd = -1; + BOOL retry = False; + + result = NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND; + + DEBUG(10,("cm_open_connection: dcname is '%s' for domain %s\n", + domain->dcname, domain->name )); + + if (*domain->dcname + && NT_STATUS_IS_OK(check_negative_conn_cache( domain->name, domain->dcname)) + && (resolve_name(domain->dcname, &domain->dcaddr.sin_addr, 0x20))) + { + struct sockaddr_in *addrs = NULL; + int num_addrs = 0; + int dummy = 0; + + if (!add_sockaddr_to_array(mem_ctx, domain->dcaddr.sin_addr, 445, &addrs, &num_addrs)) { + set_domain_offline(domain); + talloc_destroy(mem_ctx); + return NT_STATUS_NO_MEMORY; + } + if (!add_sockaddr_to_array(mem_ctx, domain->dcaddr.sin_addr, 139, &addrs, &num_addrs)) { + set_domain_offline(domain); + talloc_destroy(mem_ctx); + return NT_STATUS_NO_MEMORY; + } + + /* 5 second timeout. */ + if (!open_any_socket_out(addrs, num_addrs, 5000, &dummy, &fd)) { + fd = -1; + } + } + + if ((fd == -1) + && !find_new_dc(mem_ctx, domain, domain->dcname, &domain->dcaddr, &fd)) + { + /* This is the one place where we will + set the global winbindd offline state + to true, if a "WINBINDD_OFFLINE" entry + is found in the winbindd cache. */ + set_global_winbindd_state_offline(); + break; + } + + new_conn->cli = NULL; + + result = cm_prepare_connection(domain, fd, domain->dcname, + &new_conn->cli, &retry); + + if (!retry) + break; + } + + if (NT_STATUS_IS_OK(result)) { + + winbindd_set_locator_kdc_envs(domain); + + if (domain->online == False) { + /* We're changing state from offline to online. */ + set_global_winbindd_state_online(); + } + set_domain_online(domain); + } else { + /* Ensure we setup the retry handler. */ + set_domain_offline(domain); + } + + talloc_destroy(mem_ctx); + return result; +} + +/* Close down all open pipes on a connection. */ + +void invalidate_cm_connection(struct winbindd_cm_conn *conn) +{ + /* We're closing down a possibly dead + connection. Don't have impossibly long (10s) timeouts. */ + + if (conn->cli) { + cli_set_timeout(conn->cli, 1000); /* 1 second. */ + } + + if (conn->samr_pipe != NULL) { + if (!cli_rpc_pipe_close(conn->samr_pipe)) { + /* Ok, it must be dead. Drop timeout to 0.5 sec. */ + if (conn->cli) { + cli_set_timeout(conn->cli, 500); + } + } + conn->samr_pipe = NULL; + } + + if (conn->lsa_pipe != NULL) { + if (!cli_rpc_pipe_close(conn->lsa_pipe)) { + /* Ok, it must be dead. Drop timeout to 0.5 sec. */ + if (conn->cli) { + cli_set_timeout(conn->cli, 500); + } + } + conn->lsa_pipe = NULL; + } + + if (conn->netlogon_pipe != NULL) { + if (!cli_rpc_pipe_close(conn->netlogon_pipe)) { + /* Ok, it must be dead. Drop timeout to 0.5 sec. */ + if (conn->cli) { + cli_set_timeout(conn->cli, 500); + } + } + conn->netlogon_pipe = NULL; + } + + if (conn->cli) { + cli_shutdown(conn->cli); + } + + conn->cli = NULL; +} + +void close_conns_after_fork(void) +{ + struct winbindd_domain *domain; + + for (domain = domain_list(); domain; domain = domain->next) { + if (domain->conn.cli == NULL) + continue; + + if (domain->conn.cli->fd == -1) + continue; + + close(domain->conn.cli->fd); + domain->conn.cli->fd = -1; + } +} + +static BOOL connection_ok(struct winbindd_domain *domain) +{ + if (domain->conn.cli == NULL) { + DEBUG(8, ("connection_ok: Connection to %s for domain %s has NULL " + "cli!\n", domain->dcname, domain->name)); + return False; + } + + if (!domain->conn.cli->initialised) { + DEBUG(3, ("connection_ok: Connection to %s for domain %s was never " + "initialised!\n", domain->dcname, domain->name)); + return False; + } + + if (domain->conn.cli->fd == -1) { + DEBUG(3, ("connection_ok: Connection to %s for domain %s has died or was " + "never started (fd == -1)\n", + domain->dcname, domain->name)); + return False; + } + + if (domain->online == False) { + DEBUG(3, ("connection_ok: Domain %s is offline\n", domain->name)); + return False; + } + + return True; +} + +/* Initialize a new connection up to the RPC BIND. + Bypass online status check so always does network calls. */ + +static NTSTATUS init_dc_connection_network(struct winbindd_domain *domain) +{ + NTSTATUS result; + + /* Internal connections never use the network. */ + if (domain->internal) { + domain->initialized = True; + return NT_STATUS_OK; + } + + if (connection_ok(domain)) { + if (!domain->initialized) { + set_dc_type_and_flags(domain); + } + return NT_STATUS_OK; + } + + invalidate_cm_connection(&domain->conn); + + result = cm_open_connection(domain, &domain->conn); + + if (NT_STATUS_IS_OK(result) && !domain->initialized) { + set_dc_type_and_flags(domain); + } + + return result; +} + +NTSTATUS init_dc_connection(struct winbindd_domain *domain) +{ + if (domain->initialized && !domain->online) { + /* We check for online status elsewhere. */ + return NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND; + } + + return init_dc_connection_network(domain); +} + +/****************************************************************************** + Set the trust flags (direction and forest location) for a domain +******************************************************************************/ + +static BOOL set_dc_type_and_flags_trustinfo( struct winbindd_domain *domain ) +{ + struct winbindd_domain *our_domain; + NTSTATUS result = NT_STATUS_UNSUCCESSFUL; + struct ds_domain_trust *domains = NULL; + int count = 0; + int i; + uint32 flags = (DS_DOMAIN_IN_FOREST | + DS_DOMAIN_DIRECT_OUTBOUND | + DS_DOMAIN_DIRECT_INBOUND); + struct rpc_pipe_client *cli; + TALLOC_CTX *mem_ctx = NULL; + + DEBUG(5, ("set_dc_type_and_flags_trustinfo: domain %s\n", domain->name )); + + /* Our primary domain doesn't need to worry about trust flags. + Force it to go through the network setup */ + if ( domain->primary ) { + return False; + } + + our_domain = find_our_domain(); + + if ( !connection_ok(our_domain) ) { + DEBUG(3,("set_dc_type_and_flags_trustinfo: No connection to our domain!\n")); + return False; + } + + /* This won't work unless our domain is AD */ + + if ( !our_domain->active_directory ) { + return False; + } + + /* Use DsEnumerateDomainTrusts to get us the trust direction + and type */ + + result = cm_connect_netlogon(our_domain, &cli); + + if (!NT_STATUS_IS_OK(result)) { + DEBUG(5, ("set_dc_type_and_flags_trustinfo: Could not open " + "a connection to %s for PIPE_NETLOGON (%s)\n", + domain->name, nt_errstr(result))); + return False; + } + + if ( (mem_ctx = talloc_init("set_dc_type_and_flags_trustinfo")) == NULL ) { + DEBUG(0,("set_dc_type_and_flags_trustinfo: talloc_init() failed!\n")); + return False; + } + + result = rpccli_ds_enum_domain_trusts(cli, mem_ctx, + cli->cli->desthost, + flags, &domains, + (unsigned int *)&count); + + /* Now find the domain name and get the flags */ + + for ( i=0; iname, domains[i].netbios_domain ) ) { + domain->domain_flags = domains[i].flags; + domain->domain_type = domains[i].trust_type; + domain->domain_trust_attribs = domains[i].trust_attributes; + + if ( domain->domain_type == DS_DOMAIN_TRUST_TYPE_UPLEVEL ) + domain->active_directory = True; + + /* This flag is only set if the domain is *our* + primary domain and the primary domain is in + native mode */ + + domain->native_mode = (domain->domain_flags & DS_DOMAIN_NATIVE_MODE); + + DEBUG(5, ("set_dc_type_and_flags_trustinfo: domain %s is %sin " + "native mode.\n", domain->name, + domain->native_mode ? "" : "NOT ")); + + DEBUG(5,("set_dc_type_and_flags_trustinfo: domain %s is %s" + "running active directory.\n", domain->name, + domain->active_directory ? "" : "NOT ")); + + + domain->initialized = True; + + if ( !winbindd_can_contact_domain( domain) ) + domain->internal = True; + + break; + } + } + + talloc_destroy( mem_ctx ); + + return domain->initialized; +} + +/****************************************************************************** + We can 'sense' certain things about the DC by it's replies to certain + questions. + + This tells us if this particular remote server is Active Directory, and if it + is native mode. +******************************************************************************/ + +static void set_dc_type_and_flags_connect( struct winbindd_domain *domain ) +{ + NTSTATUS result; + DS_DOMINFO_CTR ctr; + TALLOC_CTX *mem_ctx = NULL; + struct rpc_pipe_client *cli; + POLICY_HND pol; + + char *domain_name = NULL; + char *dns_name = NULL; + char *forest_name = NULL; + DOM_SID *dom_sid = NULL; + + ZERO_STRUCT( ctr ); + + if (!connection_ok(domain)) { + return; + } + + DEBUG(5, ("set_dc_type_and_flags_connect: domain %s\n", domain->name )); + + cli = cli_rpc_pipe_open_noauth(domain->conn.cli, PI_LSARPC_DS, + &result); + + if (cli == NULL) { + DEBUG(5, ("set_dc_type_and_flags_connect: Could not bind to " + "PI_LSARPC_DS on domain %s: (%s)\n", + domain->name, nt_errstr(result))); + + /* if this is just a non-AD domain we need to continue + * identifying so that we can in the end return with + * domain->initialized = True - gd */ + + goto no_lsarpc_ds; + } + + result = rpccli_ds_getprimarydominfo(cli, cli->cli->mem_ctx, + DsRolePrimaryDomainInfoBasic, + &ctr); + cli_rpc_pipe_close(cli); + + if (!NT_STATUS_IS_OK(result)) { + DEBUG(5, ("set_dc_type_and_flags_connect: rpccli_ds_getprimarydominfo " + "on domain %s failed: (%s)\n", + domain->name, nt_errstr(result))); + + /* older samba3 DCs will return DCERPC_FAULT_OP_RNG_ERROR for + * every opcode on the LSARPC_DS pipe, continue with + * no_lsarpc_ds mode here as well to get domain->initialized + * set - gd */ + + if (NT_STATUS_V(result) == DCERPC_FAULT_OP_RNG_ERROR) { + goto no_lsarpc_ds; + } + + return; + } + + if ((ctr.basic->flags & DSROLE_PRIMARY_DS_RUNNING) && + !(ctr.basic->flags & DSROLE_PRIMARY_DS_MIXED_MODE)) { + domain->native_mode = True; + } else { + domain->native_mode = False; + } + +no_lsarpc_ds: + cli = cli_rpc_pipe_open_noauth(domain->conn.cli, PI_LSARPC, &result); + + if (cli == NULL) { + DEBUG(5, ("set_dc_type_and_flags_connect: Could not bind to " + "PI_LSARPC on domain %s: (%s)\n", + domain->name, nt_errstr(result))); + cli_rpc_pipe_close(cli); + return; + } + + mem_ctx = talloc_init("set_dc_type_and_flags on domain %s\n", + domain->name); + if (!mem_ctx) { + DEBUG(1, ("set_dc_type_and_flags_connect: talloc_init() failed\n")); + cli_rpc_pipe_close(cli); + return; + } + + result = rpccli_lsa_open_policy2(cli, mem_ctx, True, + SEC_RIGHTS_MAXIMUM_ALLOWED, &pol); + + if (NT_STATUS_IS_OK(result)) { + /* This particular query is exactly what Win2k clients use + to determine that the DC is active directory */ + result = rpccli_lsa_query_info_policy2(cli, mem_ctx, &pol, + 12, &domain_name, + &dns_name, &forest_name, + NULL, &dom_sid); + } + + if (NT_STATUS_IS_OK(result)) { + domain->active_directory = True; + + if (domain_name) + fstrcpy(domain->name, domain_name); + + if (dns_name) + fstrcpy(domain->alt_name, dns_name); + + if ( forest_name ) + fstrcpy(domain->forest_name, forest_name); + + if (dom_sid) + sid_copy(&domain->sid, dom_sid); + } else { + domain->active_directory = False; + + result = rpccli_lsa_open_policy(cli, mem_ctx, True, + SEC_RIGHTS_MAXIMUM_ALLOWED, + &pol); + + if (!NT_STATUS_IS_OK(result)) + goto done; + + result = rpccli_lsa_query_info_policy(cli, mem_ctx, + &pol, 5, &domain_name, + &dom_sid); + + if (NT_STATUS_IS_OK(result)) { + if (domain_name) + fstrcpy(domain->name, domain_name); + + if (dom_sid) + sid_copy(&domain->sid, dom_sid); + } + } +done: + + DEBUG(5, ("set_dc_type_and_flags_connect: domain %s is %sin native mode.\n", + domain->name, domain->native_mode ? "" : "NOT ")); + + DEBUG(5,("set_dc_type_and_flags_connect: domain %s is %srunning active directory.\n", + domain->name, domain->active_directory ? "" : "NOT ")); + + cli_rpc_pipe_close(cli); + + talloc_destroy(mem_ctx); + + domain->initialized = True; +} + +/********************************************************************** + Set the domain_flags (trust attributes, domain operating modes, etc... +***********************************************************************/ + +static void set_dc_type_and_flags( struct winbindd_domain *domain ) +{ + /* we always have to contact our primary domain */ + + if ( domain->primary ) { + DEBUG(10,("set_dc_type_and_flags: setting up flags for " + "primary domain\n")); + set_dc_type_and_flags_connect( domain ); + return; + } + + /* Use our DC to get the information if possible */ + + if ( !set_dc_type_and_flags_trustinfo( domain ) ) { + /* Otherwise, fallback to contacting the + domain directly */ + set_dc_type_and_flags_connect( domain ); + } + + return; +} + + + +/********************************************************************** +***********************************************************************/ + +static BOOL cm_get_schannel_dcinfo(struct winbindd_domain *domain, + struct dcinfo **ppdc) +{ + NTSTATUS result; + struct rpc_pipe_client *netlogon_pipe; + + if (lp_client_schannel() == False) { + return False; + } + + result = cm_connect_netlogon(domain, &netlogon_pipe); + if (!NT_STATUS_IS_OK(result)) { + return False; + } + + /* Return a pointer to the struct dcinfo from the + netlogon pipe. */ + + *ppdc = domain->conn.netlogon_pipe->dc; + return True; +} + +NTSTATUS cm_connect_sam(struct winbindd_domain *domain, TALLOC_CTX *mem_ctx, + struct rpc_pipe_client **cli, POLICY_HND *sam_handle) +{ + struct winbindd_cm_conn *conn; + NTSTATUS result = NT_STATUS_UNSUCCESSFUL; + fstring conn_pwd; + struct dcinfo *p_dcinfo; + + result = init_dc_connection(domain); + if (!NT_STATUS_IS_OK(result)) { + return result; + } + + conn = &domain->conn; + + if (conn->samr_pipe != NULL) { + goto done; + } + + /* + * No SAMR pipe yet. Attempt to get an NTLMSSP SPNEGO authenticated + * sign and sealed pipe using the machine account password by + * preference. If we can't - try schannel, if that fails, try + * anonymous. + */ + + pwd_get_cleartext(&conn->cli->pwd, conn_pwd); + if ((conn->cli->user_name[0] == '\0') || + (conn->cli->domain[0] == '\0') || + (conn_pwd[0] == '\0')) { + DEBUG(10, ("cm_connect_sam: No no user available for " + "domain %s, trying schannel\n", conn->cli->domain)); + goto schannel; + } + + /* We have an authenticated connection. Use a NTLMSSP SPNEGO + authenticated SAMR pipe with sign & seal. */ + conn->samr_pipe = + cli_rpc_pipe_open_spnego_ntlmssp(conn->cli, PI_SAMR, + PIPE_AUTH_LEVEL_PRIVACY, + conn->cli->domain, + conn->cli->user_name, + conn_pwd, &result); + + if (conn->samr_pipe == NULL) { + DEBUG(10,("cm_connect_sam: failed to connect to SAMR " + "pipe for domain %s using NTLMSSP " + "authenticated pipe: user %s\\%s. Error was " + "%s\n", domain->name, conn->cli->domain, + conn->cli->user_name, nt_errstr(result))); + goto schannel; + } + + DEBUG(10,("cm_connect_sam: connected to SAMR pipe for " + "domain %s using NTLMSSP authenticated " + "pipe: user %s\\%s\n", domain->name, + conn->cli->domain, conn->cli->user_name )); + + result = rpccli_samr_connect(conn->samr_pipe, mem_ctx, + SEC_RIGHTS_MAXIMUM_ALLOWED, + &conn->sam_connect_handle); + if (NT_STATUS_IS_OK(result)) { + goto open_domain; + } + DEBUG(10,("cm_connect_sam: ntlmssp-sealed rpccli_samr_connect " + "failed for domain %s, error was %s. Trying schannel\n", + domain->name, nt_errstr(result) )); + cli_rpc_pipe_close(conn->samr_pipe); + + schannel: + + /* Fall back to schannel if it's a W2K pre-SP1 box. */ + + if (!cm_get_schannel_dcinfo(domain, &p_dcinfo)) { + /* If this call fails - conn->cli can now be NULL ! */ + DEBUG(10, ("cm_connect_sam: Could not get schannel auth info " + "for domain %s, trying anon\n", domain->name)); + goto anonymous; + } + conn->samr_pipe = cli_rpc_pipe_open_schannel_with_key + (conn->cli, PI_SAMR, PIPE_AUTH_LEVEL_PRIVACY, + domain->name, p_dcinfo, &result); + + if (conn->samr_pipe == NULL) { + DEBUG(10,("cm_connect_sam: failed to connect to SAMR pipe for " + "domain %s using schannel. Error was %s\n", + domain->name, nt_errstr(result) )); + goto anonymous; + } + DEBUG(10,("cm_connect_sam: connected to SAMR pipe for domain %s using " + "schannel.\n", domain->name )); + + result = rpccli_samr_connect(conn->samr_pipe, mem_ctx, + SEC_RIGHTS_MAXIMUM_ALLOWED, + &conn->sam_connect_handle); + if (NT_STATUS_IS_OK(result)) { + goto open_domain; + } + DEBUG(10,("cm_connect_sam: schannel-sealed rpccli_samr_connect failed " + "for domain %s, error was %s. Trying anonymous\n", + domain->name, nt_errstr(result) )); + cli_rpc_pipe_close(conn->samr_pipe); + + anonymous: + + /* Finally fall back to anonymous. */ + conn->samr_pipe = cli_rpc_pipe_open_noauth(conn->cli, PI_SAMR, + &result); + + if (conn->samr_pipe == NULL) { + result = NT_STATUS_PIPE_NOT_AVAILABLE; + goto done; + } + + result = rpccli_samr_connect(conn->samr_pipe, mem_ctx, + SEC_RIGHTS_MAXIMUM_ALLOWED, + &conn->sam_connect_handle); + if (!NT_STATUS_IS_OK(result)) { + DEBUG(10,("cm_connect_sam: rpccli_samr_connect failed " + "for domain %s Error was %s\n", + domain->name, nt_errstr(result) )); + goto done; + } + + open_domain: + result = rpccli_samr_open_domain(conn->samr_pipe, + mem_ctx, + &conn->sam_connect_handle, + SEC_RIGHTS_MAXIMUM_ALLOWED, + &domain->sid, + &conn->sam_domain_handle); + + done: + + if (!NT_STATUS_IS_OK(result)) { + invalidate_cm_connection(conn); + return result; + } + + *cli = conn->samr_pipe; + *sam_handle = conn->sam_domain_handle; + return result; +} + +NTSTATUS cm_connect_lsa(struct winbindd_domain *domain, TALLOC_CTX *mem_ctx, + struct rpc_pipe_client **cli, POLICY_HND *lsa_policy) +{ + struct winbindd_cm_conn *conn; + NTSTATUS result = NT_STATUS_UNSUCCESSFUL; + fstring conn_pwd; + struct dcinfo *p_dcinfo; + + result = init_dc_connection(domain); + if (!NT_STATUS_IS_OK(result)) + return result; + + conn = &domain->conn; + + if (conn->lsa_pipe != NULL) { + goto done; + } + + pwd_get_cleartext(&conn->cli->pwd, conn_pwd); + if ((conn->cli->user_name[0] == '\0') || + (conn->cli->domain[0] == '\0') || + (conn_pwd[0] == '\0')) { + DEBUG(10, ("cm_connect_lsa: No no user available for " + "domain %s, trying schannel\n", conn->cli->domain)); + goto schannel; + } + + /* We have an authenticated connection. Use a NTLMSSP SPNEGO + * authenticated LSA pipe with sign & seal. */ + conn->lsa_pipe = cli_rpc_pipe_open_spnego_ntlmssp + (conn->cli, PI_LSARPC, PIPE_AUTH_LEVEL_PRIVACY, + conn->cli->domain, conn->cli->user_name, conn_pwd, &result); + + if (conn->lsa_pipe == NULL) { + DEBUG(10,("cm_connect_lsa: failed to connect to LSA pipe for " + "domain %s using NTLMSSP authenticated pipe: user " + "%s\\%s. Error was %s. Trying schannel.\n", + domain->name, conn->cli->domain, + conn->cli->user_name, nt_errstr(result))); + goto schannel; + } + + DEBUG(10,("cm_connect_lsa: connected to LSA pipe for domain %s using " + "NTLMSSP authenticated pipe: user %s\\%s\n", + domain->name, conn->cli->domain, conn->cli->user_name )); + + result = rpccli_lsa_open_policy(conn->lsa_pipe, mem_ctx, True, + SEC_RIGHTS_MAXIMUM_ALLOWED, + &conn->lsa_policy); + if (NT_STATUS_IS_OK(result)) { + goto done; + } + + DEBUG(10,("cm_connect_lsa: rpccli_lsa_open_policy failed, trying " + "schannel\n")); + + cli_rpc_pipe_close(conn->lsa_pipe); + + schannel: + + /* Fall back to schannel if it's a W2K pre-SP1 box. */ + + if (!cm_get_schannel_dcinfo(domain, &p_dcinfo)) { + /* If this call fails - conn->cli can now be NULL ! */ + DEBUG(10, ("cm_connect_lsa: Could not get schannel auth info " + "for domain %s, trying anon\n", domain->name)); + goto anonymous; + } + conn->lsa_pipe = cli_rpc_pipe_open_schannel_with_key + (conn->cli, PI_LSARPC, PIPE_AUTH_LEVEL_PRIVACY, + domain->name, p_dcinfo, &result); + + if (conn->lsa_pipe == NULL) { + DEBUG(10,("cm_connect_lsa: failed to connect to LSA pipe for " + "domain %s using schannel. Error was %s\n", + domain->name, nt_errstr(result) )); + goto anonymous; + } + DEBUG(10,("cm_connect_lsa: connected to LSA pipe for domain %s using " + "schannel.\n", domain->name )); + + result = rpccli_lsa_open_policy(conn->lsa_pipe, mem_ctx, True, + SEC_RIGHTS_MAXIMUM_ALLOWED, + &conn->lsa_policy); + if (NT_STATUS_IS_OK(result)) { + goto done; + } + + DEBUG(10,("cm_connect_lsa: rpccli_lsa_open_policy failed, trying " + "anonymous\n")); + + cli_rpc_pipe_close(conn->lsa_pipe); + + anonymous: + + conn->lsa_pipe = cli_rpc_pipe_open_noauth(conn->cli, PI_LSARPC, + &result); + if (conn->lsa_pipe == NULL) { + result = NT_STATUS_PIPE_NOT_AVAILABLE; + goto done; + } + + result = rpccli_lsa_open_policy(conn->lsa_pipe, mem_ctx, True, + SEC_RIGHTS_MAXIMUM_ALLOWED, + &conn->lsa_policy); + done: + if (!NT_STATUS_IS_OK(result)) { + invalidate_cm_connection(conn); + return result; + } + + *cli = conn->lsa_pipe; + *lsa_policy = conn->lsa_policy; + return result; +} + +/**************************************************************************** + Open the netlogon pipe to this DC. Use schannel if specified in client conf. + session key stored in conn->netlogon_pipe->dc->sess_key. +****************************************************************************/ + +NTSTATUS cm_connect_netlogon(struct winbindd_domain *domain, + struct rpc_pipe_client **cli) +{ + struct winbindd_cm_conn *conn; + NTSTATUS result; + + uint32 neg_flags = NETLOGON_NEG_AUTH2_FLAGS; + uint8 mach_pwd[16]; + uint32 sec_chan_type; + const char *account_name; + struct rpc_pipe_client *netlogon_pipe = NULL; + + *cli = NULL; + + result = init_dc_connection(domain); + if (!NT_STATUS_IS_OK(result)) { + return result; + } + + conn = &domain->conn; + + if (conn->netlogon_pipe != NULL) { + *cli = conn->netlogon_pipe; + return NT_STATUS_OK; + } + + if ((IS_DC || domain->primary) && !get_trust_pw(domain->name, mach_pwd, &sec_chan_type)) { + return NT_STATUS_CANT_ACCESS_DOMAIN_INFO; + } + + netlogon_pipe = cli_rpc_pipe_open_noauth(conn->cli, PI_NETLOGON, + &result); + if (netlogon_pipe == NULL) { + return result; + } + + if ((!IS_DC) && (!domain->primary)) { + /* Clear the schannel request bit and drop down */ + neg_flags &= ~NETLOGON_NEG_SCHANNEL; + goto no_schannel; + } + + if (lp_client_schannel() != False) { + neg_flags |= NETLOGON_NEG_SCHANNEL; + } + + /* if we are a DC and this is a trusted domain, then we need to use our + domain name in the net_req_auth2() request */ + + if ( IS_DC + && !strequal(domain->name, lp_workgroup()) + && lp_allow_trusted_domains() ) + { + account_name = lp_workgroup(); + } else { + account_name = domain->primary ? + global_myname() : domain->name; + } + + if (account_name == NULL) { + cli_rpc_pipe_close(netlogon_pipe); + return NT_STATUS_NO_MEMORY; + } + + result = rpccli_netlogon_setup_creds( + netlogon_pipe, + domain->dcname, /* server name. */ + domain->name, /* domain name */ + global_myname(), /* client name */ + account_name, /* machine account */ + mach_pwd, /* machine password */ + sec_chan_type, /* from get_trust_pw */ + &neg_flags); + + if (!NT_STATUS_IS_OK(result)) { + cli_rpc_pipe_close(netlogon_pipe); + return result; + } + + if ((lp_client_schannel() == True) && + ((neg_flags & NETLOGON_NEG_SCHANNEL) == 0)) { + DEBUG(3, ("Server did not offer schannel\n")); + cli_rpc_pipe_close(netlogon_pipe); + return NT_STATUS_ACCESS_DENIED; + } + + no_schannel: + if ((lp_client_schannel() == False) || + ((neg_flags & NETLOGON_NEG_SCHANNEL) == 0)) { + + /* + * NetSamLogonEx only works for schannel + */ + domain->can_do_samlogon_ex = False; + + /* We're done - just keep the existing connection to NETLOGON + * open */ + conn->netlogon_pipe = netlogon_pipe; + *cli = conn->netlogon_pipe; + return NT_STATUS_OK; + } + + /* Using the credentials from the first pipe, open a signed and sealed + second netlogon pipe. The session key is stored in the schannel + part of the new pipe auth struct. + */ + + conn->netlogon_pipe = + cli_rpc_pipe_open_schannel_with_key(conn->cli, + PI_NETLOGON, + PIPE_AUTH_LEVEL_PRIVACY, + domain->name, + netlogon_pipe->dc, + &result); + + /* We can now close the initial netlogon pipe. */ + cli_rpc_pipe_close(netlogon_pipe); + + if (conn->netlogon_pipe == NULL) { + DEBUG(3, ("Could not open schannel'ed NETLOGON pipe. Error " + "was %s\n", nt_errstr(result))); + + /* make sure we return something besides OK */ + return !NT_STATUS_IS_OK(result) ? result : NT_STATUS_PIPE_NOT_AVAILABLE; + } + + /* + * Try NetSamLogonEx for AD domains + */ + domain->can_do_samlogon_ex = domain->active_directory; + + *cli = conn->netlogon_pipe; + return NT_STATUS_OK; +} diff --git a/source3/winbindd/winbindd_cred_cache.c b/source3/winbindd/winbindd_cred_cache.c new file mode 100644 index 0000000000..65dcbe5a00 --- /dev/null +++ b/source3/winbindd/winbindd_cred_cache.c @@ -0,0 +1,802 @@ +/* + Unix SMB/CIFS implementation. + + Winbind daemon - krb5 credential cache functions + and in-memory cache functions. + + Copyright (C) Guenther Deschner 2005-2006 + Copyright (C) Jeremy Allison 2006 + + 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 3 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, see . +*/ + +#include "includes.h" +#include "winbindd.h" +#undef DBGC_CLASS +#define DBGC_CLASS DBGC_WINBIND + +/* uncomment this to do fast debugging on the krb5 ticket renewal event */ +#ifdef DEBUG_KRB5_TKT_RENEWAL +#undef DEBUG_KRB5_TKT_RENEWAL +#endif + +#define MAX_CCACHES 100 + +static struct WINBINDD_CCACHE_ENTRY *ccache_list; + +/* The Krb5 ticket refresh handler should be scheduled + at one-half of the period from now till the tkt + expiration */ +#define KRB5_EVENT_REFRESH_TIME(x) ((x) - (((x) - time(NULL))/2)) + +/**************************************************************** + Find an entry by name. +****************************************************************/ + +static struct WINBINDD_CCACHE_ENTRY *get_ccache_by_username(const char *username) +{ + struct WINBINDD_CCACHE_ENTRY *entry; + + for (entry = ccache_list; entry; entry = entry->next) { + if (strequal(entry->username, username)) { + return entry; + } + } + return NULL; +} + +/**************************************************************** + How many do we have ? +****************************************************************/ + +static int ccache_entry_count(void) +{ + struct WINBINDD_CCACHE_ENTRY *entry; + int i = 0; + + for (entry = ccache_list; entry; entry = entry->next) { + i++; + } + return i; +} + +/**************************************************************** + Do the work of refreshing the ticket. +****************************************************************/ + +static void krb5_ticket_refresh_handler(struct event_context *event_ctx, + struct timed_event *te, + const struct timeval *now, + void *private_data) +{ + struct WINBINDD_CCACHE_ENTRY *entry = + talloc_get_type_abort(private_data, struct WINBINDD_CCACHE_ENTRY); +#ifdef HAVE_KRB5 + int ret; + time_t new_start; + struct WINBINDD_MEMORY_CREDS *cred_ptr = entry->cred_ptr; +#endif + + DEBUG(10,("krb5_ticket_refresh_handler called\n")); + DEBUGADD(10,("event called for: %s, %s\n", + entry->ccname, entry->username)); + + TALLOC_FREE(entry->event); + +#ifdef HAVE_KRB5 + + /* Kinit again if we have the user password and we can't renew the old + * tgt anymore */ + + if ((entry->renew_until < time(NULL)) && cred_ptr && cred_ptr->pass) { + + set_effective_uid(entry->uid); + + ret = kerberos_kinit_password_ext(entry->principal_name, + cred_ptr->pass, + 0, /* hm, can we do time correction here ? */ + &entry->refresh_time, + &entry->renew_until, + entry->ccname, + False, /* no PAC required anymore */ + True, + WINBINDD_PAM_AUTH_KRB5_RENEW_TIME, + NULL); + gain_root_privilege(); + + if (ret) { + DEBUG(3,("krb5_ticket_refresh_handler: " + "could not re-kinit: %s\n", + error_message(ret))); + TALLOC_FREE(entry->event); + return; + } + + DEBUG(10,("krb5_ticket_refresh_handler: successful re-kinit " + "for: %s in ccache: %s\n", + entry->principal_name, entry->ccname)); + +#if defined(DEBUG_KRB5_TKT_RENEWAL) + new_start = time(NULL) + 30; +#else + /* The tkt should be refreshed at one-half the period + from now to the expiration time */ + new_start = KRB5_EVENT_REFRESH_TIME(entry->refresh_time); +#endif + goto done; + } + + set_effective_uid(entry->uid); + + ret = smb_krb5_renew_ticket(entry->ccname, + entry->principal_name, + entry->service, + &new_start); +#if defined(DEBUG_KRB5_TKT_RENEWAL) + new_start = time(NULL) + 30; +#else + new_start = KRB5_EVENT_REFRESH_TIME(new_start); +#endif + + gain_root_privilege(); + + if (ret) { + DEBUG(3,("krb5_ticket_refresh_handler: " + "could not renew tickets: %s\n", + error_message(ret))); + /* maybe we are beyond the renewing window */ + + /* avoid breaking the renewal chain: retry in + * lp_winbind_cache_time() seconds when the KDC was not + * available right now. */ + + if (ret == KRB5_KDC_UNREACH) { + new_start = time(NULL) + + MAX(30, lp_winbind_cache_time()); + goto done; + } + + return; + } + +done: + + entry->event = event_add_timed(winbind_event_context(), entry, + timeval_set(new_start, 0), + "krb5_ticket_refresh_handler", + krb5_ticket_refresh_handler, + entry); + +#endif +} + +/**************************************************************** + Do the work of regaining a ticket when coming from offline auth. +****************************************************************/ + +static void krb5_ticket_gain_handler(struct event_context *event_ctx, + struct timed_event *te, + const struct timeval *now, + void *private_data) +{ + struct WINBINDD_CCACHE_ENTRY *entry = + talloc_get_type_abort(private_data, struct WINBINDD_CCACHE_ENTRY); +#ifdef HAVE_KRB5 + int ret; + struct timeval t; + struct WINBINDD_MEMORY_CREDS *cred_ptr = entry->cred_ptr; + struct winbindd_domain *domain = NULL; +#endif + + DEBUG(10,("krb5_ticket_gain_handler called\n")); + DEBUGADD(10,("event called for: %s, %s\n", + entry->ccname, entry->username)); + + TALLOC_FREE(entry->event); + +#ifdef HAVE_KRB5 + + if (!cred_ptr || !cred_ptr->pass) { + DEBUG(10,("krb5_ticket_gain_handler: no memory creds\n")); + return; + } + + if ((domain = find_domain_from_name(entry->realm)) == NULL) { + DEBUG(0,("krb5_ticket_gain_handler: unknown domain\n")); + return; + } + + if (!domain->online) { + goto retry_later; + } + + set_effective_uid(entry->uid); + + ret = kerberos_kinit_password_ext(entry->principal_name, + cred_ptr->pass, + 0, /* hm, can we do time correction here ? */ + &entry->refresh_time, + &entry->renew_until, + entry->ccname, + False, /* no PAC required anymore */ + True, + WINBINDD_PAM_AUTH_KRB5_RENEW_TIME, + NULL); + gain_root_privilege(); + + if (ret) { + DEBUG(3,("krb5_ticket_gain_handler: " + "could not kinit: %s\n", + error_message(ret))); + goto retry_later; + } + + DEBUG(10,("krb5_ticket_gain_handler: " + "successful kinit for: %s in ccache: %s\n", + entry->principal_name, entry->ccname)); + + goto got_ticket; + + retry_later: + + t = timeval_current_ofs(MAX(30, lp_winbind_cache_time()), 0); + + entry->event = event_add_timed(winbind_event_context(), + entry, + t, + "krb5_ticket_gain_handler", + krb5_ticket_gain_handler, + entry); + + return; + + got_ticket: + +#if defined(DEBUG_KRB5_TKT_RENEWAL) + t = timeval_set(time(NULL) + 30, 0); +#else + t = timeval_set(KRB5_EVENT_REFRESH_TIME(entry->refresh_time), 0); +#endif + + entry->event = event_add_timed(winbind_event_context(), + entry, + t, + "krb5_ticket_refresh_handler", + krb5_ticket_refresh_handler, + entry); + + return; +#endif +} + +/**************************************************************** + Check if an ccache entry exists. +****************************************************************/ + +BOOL ccache_entry_exists(const char *username) +{ + struct WINBINDD_CCACHE_ENTRY *entry = get_ccache_by_username(username); + return (entry != NULL); +} + +/**************************************************************** + Ensure we're changing the correct entry. +****************************************************************/ + +BOOL ccache_entry_identical(const char *username, + uid_t uid, + const char *ccname) +{ + struct WINBINDD_CCACHE_ENTRY *entry = get_ccache_by_username(username); + + if (!entry) { + return False; + } + + if (entry->uid != uid) { + DEBUG(0,("cache_entry_identical: uid's differ: %u != %u\n", + (unsigned int)entry->uid, (unsigned int)uid)); + return False; + } + if (!strcsequal(entry->ccname, ccname)) { + DEBUG(0,("cache_entry_identical: " + "ccnames differ: (cache) %s != (client) %s\n", + entry->ccname, ccname)); + return False; + } + return True; +} + +NTSTATUS add_ccache_to_list(const char *princ_name, + const char *ccname, + const char *service, + const char *username, + const char *realm, + uid_t uid, + time_t create_time, + time_t ticket_end, + time_t renew_until, + BOOL postponed_request) +{ + struct WINBINDD_CCACHE_ENTRY *entry = NULL; + struct timeval t; + + if ((username == NULL && princ_name == NULL) || + ccname == NULL || uid < 0) { + return NT_STATUS_INVALID_PARAMETER; + } + + if (ccache_entry_count() + 1 > MAX_CCACHES) { + DEBUG(10,("add_ccache_to_list: " + "max number of ccaches reached\n")); + return NT_STATUS_NO_MORE_ENTRIES; + } + + /* Reference count old entries */ + entry = get_ccache_by_username(username); + if (entry) { + /* Check cached entries are identical. */ + if (!ccache_entry_identical(username, uid, ccname)) { + return NT_STATUS_INVALID_PARAMETER; + } + entry->ref_count++; + DEBUG(10,("add_ccache_to_list: " + "ref count on entry %s is now %d\n", + username, entry->ref_count)); + /* FIXME: in this case we still might want to have a krb5 cred + * event handler created - gd*/ + return NT_STATUS_OK; + } + + entry = TALLOC_P(NULL, struct WINBINDD_CCACHE_ENTRY); + if (!entry) { + return NT_STATUS_NO_MEMORY; + } + + ZERO_STRUCTP(entry); + + if (username) { + entry->username = talloc_strdup(entry, username); + if (!entry->username) { + goto no_mem; + } + } + if (princ_name) { + entry->principal_name = talloc_strdup(entry, princ_name); + if (!entry->principal_name) { + goto no_mem; + } + } + if (service) { + entry->service = talloc_strdup(entry, service); + if (!entry->service) { + goto no_mem; + } + } + + entry->ccname = talloc_strdup(entry, ccname); + if (!entry->ccname) { + goto no_mem; + } + + entry->realm = talloc_strdup(entry, realm); + if (!entry->realm) { + goto no_mem; + } + + entry->create_time = create_time; + entry->renew_until = renew_until; + entry->uid = uid; + entry->ref_count = 1; + + if (!lp_winbind_refresh_tickets() || renew_until <= 0) { + goto add_entry; + } + + if (postponed_request) { + t = timeval_current_ofs(MAX(30, lp_winbind_cache_time()), 0); + entry->event = event_add_timed(winbind_event_context(), + entry, + t, + "krb5_ticket_gain_handler", + krb5_ticket_gain_handler, + entry); + } else { + /* Renew at 1/2 the ticket expiration time */ +#if defined(DEBUG_KRB5_TKT_RENEWAL) + t = timeval_set(time(NULL)+30, 0); +#else + t = timeval_set(KRB5_EVENT_REFRESH_TIME(ticket_end), 0); +#endif + entry->event = event_add_timed(winbind_event_context(), + entry, + t, + "krb5_ticket_refresh_handler", + krb5_ticket_refresh_handler, + entry); + } + + if (!entry->event) { + goto no_mem; + } + + DEBUG(10,("add_ccache_to_list: added krb5_ticket handler\n")); + + add_entry: + + DLIST_ADD(ccache_list, entry); + + DEBUG(10,("add_ccache_to_list: " + "added ccache [%s] for user [%s] to the list\n", + ccname, username)); + + return NT_STATUS_OK; + + no_mem: + + TALLOC_FREE(entry); + return NT_STATUS_NO_MEMORY; +} + +/******************************************************************* + Remove a WINBINDD_CCACHE_ENTRY entry and the krb5 ccache if no longer + referenced. + *******************************************************************/ + +NTSTATUS remove_ccache(const char *username) +{ + struct WINBINDD_CCACHE_ENTRY *entry = get_ccache_by_username(username); + NTSTATUS status = NT_STATUS_OK; + #ifdef HAVE_KRB5 + krb5_error_code ret; +#endif + + if (!entry) { + return NT_STATUS_OBJECT_NAME_NOT_FOUND; + } + + if (entry->ref_count <= 0) { + DEBUG(0,("remove_ccache: logic error. " + "ref count for user %s = %d\n", + username, entry->ref_count)); + return NT_STATUS_INTERNAL_DB_CORRUPTION; + } + + entry->ref_count--; + + if (entry->ref_count > 0) { + DEBUG(10,("remove_ccache: entry %s ref count now %d\n", + username, entry->ref_count)); + return NT_STATUS_OK; + } + + /* no references any more */ + + DLIST_REMOVE(ccache_list, entry); + TALLOC_FREE(entry->event); /* unregisters events */ + +#ifdef HAVE_KRB5 + ret = ads_kdestroy(entry->ccname); + + /* we ignore the error when there has been no credential cache */ + if (ret == KRB5_FCC_NOFILE) { + ret = 0; + } else if (ret) { + DEBUG(0,("remove_ccache: " + "failed to destroy user krb5 ccache %s with: %s\n", + entry->ccname, error_message(ret))); + } else { + DEBUG(10,("remove_ccache: " + "successfully destroyed krb5 ccache %s for user %s\n", + entry->ccname, username)); + } + status = krb5_to_nt_status(ret); +#endif + + TALLOC_FREE(entry); + DEBUG(10,("remove_ccache: removed ccache for user %s\n", username)); + + return status; +} + +/******************************************************************* + In memory credentials cache code. +*******************************************************************/ + +static struct WINBINDD_MEMORY_CREDS *memory_creds_list; + +/*********************************************************** + Find an entry on the list by name. +***********************************************************/ + +struct WINBINDD_MEMORY_CREDS *find_memory_creds_by_name(const char *username) +{ + struct WINBINDD_MEMORY_CREDS *p; + + for (p = memory_creds_list; p; p = p->next) { + if (strequal(p->username, username)) { + return p; + } + } + return NULL; +} + +/*********************************************************** + Store the required creds and mlock them. +***********************************************************/ + +static NTSTATUS store_memory_creds(struct WINBINDD_MEMORY_CREDS *memcredp, + const char *pass) +{ +#if !defined(HAVE_MLOCK) + return NT_STATUS_OK; +#else + /* new_entry->nt_hash is the base pointer for the block + of memory pointed into by new_entry->lm_hash and + new_entry->pass (if we're storing plaintext). */ + + memcredp->len = NT_HASH_LEN + LM_HASH_LEN; + if (pass) { + memcredp->len += strlen(pass)+1; + } + + +#if defined(LINUX) + /* aligning the memory on on x86_64 and compiling + with gcc 4.1 using -O2 causes a segv in the + next memset() --jerry */ + memcredp->nt_hash = SMB_MALLOC_ARRAY(unsigned char, memcredp->len); +#else + /* On non-linux platforms, mlock()'d memory must be aligned */ + memcredp->nt_hash = SMB_MEMALIGN_ARRAY(unsigned char, + getpagesize(), memcredp->len); +#endif + if (!memcredp->nt_hash) { + return NT_STATUS_NO_MEMORY; + } + memset(memcredp->nt_hash, 0x0, memcredp->len); + + memcredp->lm_hash = memcredp->nt_hash + NT_HASH_LEN; + +#ifdef DEBUG_PASSWORD + DEBUG(10,("mlocking memory: %p\n", memcredp->nt_hash)); +#endif + if ((mlock(memcredp->nt_hash, memcredp->len)) == -1) { + DEBUG(0,("failed to mlock memory: %s (%d)\n", + strerror(errno), errno)); + SAFE_FREE(memcredp->nt_hash); + return map_nt_error_from_unix(errno); + } + +#ifdef DEBUG_PASSWORD + DEBUG(10,("mlocked memory: %p\n", memcredp->nt_hash)); +#endif + + /* Create and store the password hashes. */ + E_md4hash(pass, memcredp->nt_hash); + E_deshash(pass, memcredp->lm_hash); + + if (pass) { + memcredp->pass = (char *)memcredp->lm_hash + LM_HASH_LEN; + memcpy(memcredp->pass, pass, + memcredp->len - NT_HASH_LEN - LM_HASH_LEN); + } + + return NT_STATUS_OK; +#endif +} + +/*********************************************************** + Destroy existing creds. +***********************************************************/ + +static NTSTATUS delete_memory_creds(struct WINBINDD_MEMORY_CREDS *memcredp) +{ +#if !defined(HAVE_MUNLOCK) + return NT_STATUS_OK; +#else + if (munlock(memcredp->nt_hash, memcredp->len) == -1) { + DEBUG(0,("failed to munlock memory: %s (%d)\n", + strerror(errno), errno)); + return map_nt_error_from_unix(errno); + } + memset(memcredp->nt_hash, '\0', memcredp->len); + SAFE_FREE(memcredp->nt_hash); + memcredp->nt_hash = NULL; + memcredp->lm_hash = NULL; + memcredp->pass = NULL; + memcredp->len = 0; + return NT_STATUS_OK; +#endif +} + +/*********************************************************** + Replace the required creds with new ones (password change). +***********************************************************/ + +static NTSTATUS winbindd_replace_memory_creds_internal(struct WINBINDD_MEMORY_CREDS *memcredp, + const char *pass) +{ + NTSTATUS status = delete_memory_creds(memcredp); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + return store_memory_creds(memcredp, pass); +} + +/************************************************************* + Store credentials in memory in a list. +*************************************************************/ + +static NTSTATUS winbindd_add_memory_creds_internal(const char *username, + uid_t uid, + const char *pass) +{ + /* Shortcut to ensure we don't store if no mlock. */ +#if !defined(HAVE_MLOCK) || !defined(HAVE_MUNLOCK) + return NT_STATUS_OK; +#else + NTSTATUS status; + struct WINBINDD_MEMORY_CREDS *memcredp = NULL; + + memcredp = find_memory_creds_by_name(username); + if (uid == (uid_t)-1) { + DEBUG(0,("winbindd_add_memory_creds_internal: " + "invalid uid for user %s.\n", username)); + return NT_STATUS_INVALID_PARAMETER; + } + + if (memcredp) { + /* Already exists. Increment the reference count and replace stored creds. */ + if (uid != memcredp->uid) { + DEBUG(0,("winbindd_add_memory_creds_internal: " + "uid %u for user %s doesn't " + "match stored uid %u. Replacing.\n", + (unsigned int)uid, username, + (unsigned int)memcredp->uid)); + memcredp->uid = uid; + } + memcredp->ref_count++; + DEBUG(10,("winbindd_add_memory_creds_internal: " + "ref count for user %s is now %d\n", + username, memcredp->ref_count)); + return winbindd_replace_memory_creds_internal(memcredp, pass); + } + + memcredp = TALLOC_ZERO_P(NULL, struct WINBINDD_MEMORY_CREDS); + if (!memcredp) { + return NT_STATUS_NO_MEMORY; + } + memcredp->username = talloc_strdup(memcredp, username); + if (!memcredp->username) { + talloc_destroy(memcredp); + return NT_STATUS_NO_MEMORY; + } + + status = store_memory_creds(memcredp, pass); + if (!NT_STATUS_IS_OK(status)) { + talloc_destroy(memcredp); + return status; + } + + memcredp->uid = uid; + memcredp->ref_count = 1; + DLIST_ADD(memory_creds_list, memcredp); + + DEBUG(10,("winbindd_add_memory_creds_internal: " + "added entry for user %s\n", username)); + + return NT_STATUS_OK; +#endif +} + +/************************************************************* + Store users credentials in memory. If we also have a + struct WINBINDD_CCACHE_ENTRY for this username with a + refresh timer, then store the plaintext of the password + and associate the new credentials with the struct WINBINDD_CCACHE_ENTRY. +*************************************************************/ + +NTSTATUS winbindd_add_memory_creds(const char *username, + uid_t uid, + const char *pass) +{ + struct WINBINDD_CCACHE_ENTRY *entry = get_ccache_by_username(username); + NTSTATUS status; + + status = winbindd_add_memory_creds_internal(username, uid, pass); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + if (entry) { + struct WINBINDD_MEMORY_CREDS *memcredp = NULL; + memcredp = find_memory_creds_by_name(username); + if (memcredp) { + entry->cred_ptr = memcredp; + } + } + + return status; +} + +/************************************************************* + Decrement the in-memory ref count - delete if zero. +*************************************************************/ + +NTSTATUS winbindd_delete_memory_creds(const char *username) +{ + struct WINBINDD_MEMORY_CREDS *memcredp = NULL; + struct WINBINDD_CCACHE_ENTRY *entry = NULL; + NTSTATUS status = NT_STATUS_OK; + + memcredp = find_memory_creds_by_name(username); + entry = get_ccache_by_username(username); + + if (!memcredp) { + DEBUG(10,("winbindd_delete_memory_creds: unknown user %s\n", + username)); + return NT_STATUS_OBJECT_NAME_NOT_FOUND; + } + + if (memcredp->ref_count <= 0) { + DEBUG(0,("winbindd_delete_memory_creds: logic error. " + "ref count for user %s = %d\n", + username, memcredp->ref_count)); + status = NT_STATUS_INTERNAL_DB_CORRUPTION; + } + + memcredp->ref_count--; + if (memcredp->ref_count <= 0) { + delete_memory_creds(memcredp); + DLIST_REMOVE(memory_creds_list, memcredp); + talloc_destroy(memcredp); + DEBUG(10,("winbindd_delete_memory_creds: " + "deleted entry for user %s\n", + username)); + } else { + DEBUG(10,("winbindd_delete_memory_creds: " + "entry for user %s ref_count now %d\n", + username, memcredp->ref_count)); + } + + if (entry) { + /* Ensure we have no dangling references to this. */ + entry->cred_ptr = NULL; + } + + return status; +} + +/*********************************************************** + Replace the required creds with new ones (password change). +***********************************************************/ + +NTSTATUS winbindd_replace_memory_creds(const char *username, + const char *pass) +{ + struct WINBINDD_MEMORY_CREDS *memcredp = NULL; + + memcredp = find_memory_creds_by_name(username); + if (!memcredp) { + DEBUG(10,("winbindd_replace_memory_creds: unknown user %s\n", + username)); + return NT_STATUS_OBJECT_NAME_NOT_FOUND; + } + + DEBUG(10,("winbindd_replace_memory_creds: replaced creds for user %s\n", + username)); + + return winbindd_replace_memory_creds_internal(memcredp, pass); +} diff --git a/source3/winbindd/winbindd_creds.c b/source3/winbindd/winbindd_creds.c new file mode 100644 index 0000000000..62facb6769 --- /dev/null +++ b/source3/winbindd/winbindd_creds.c @@ -0,0 +1,162 @@ +/* + Unix SMB/CIFS implementation. + + Winbind daemon - cached credentials funcions + + Copyright (C) Guenther Deschner 2005 + + 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 3 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, see . +*/ + +#include "includes.h" +#include "winbindd.h" +#undef DBGC_CLASS +#define DBGC_CLASS DBGC_WINBIND + +#define MAX_CACHED_LOGINS 10 + +NTSTATUS winbindd_get_creds(struct winbindd_domain *domain, + TALLOC_CTX *mem_ctx, + const DOM_SID *sid, + NET_USER_INFO_3 **info3, + const uint8 *cached_nt_pass[NT_HASH_LEN], + const uint8 *cred_salt[NT_HASH_LEN]) +{ + NET_USER_INFO_3 *info; + NTSTATUS status; + + status = wcache_get_creds(domain, mem_ctx, sid, cached_nt_pass, cred_salt); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + info = netsamlogon_cache_get(mem_ctx, sid); + if (info == NULL) { + return NT_STATUS_OBJECT_NAME_NOT_FOUND; + } + + *info3 = info; + + return NT_STATUS_OK; +} + + +NTSTATUS winbindd_store_creds(struct winbindd_domain *domain, + TALLOC_CTX *mem_ctx, + const char *user, + const char *pass, + NET_USER_INFO_3 *info3, + const DOM_SID *user_sid) +{ + NTSTATUS status; + uchar nt_pass[NT_HASH_LEN]; + DOM_SID cred_sid; + + if (info3 != NULL) { + + DOM_SID sid; + sid_copy(&sid, &(info3->dom_sid.sid)); + sid_append_rid(&sid, info3->user_rid); + sid_copy(&cred_sid, &sid); + info3->user_flgs |= LOGON_CACHED_ACCOUNT; + + } else if (user_sid != NULL) { + + sid_copy(&cred_sid, user_sid); + + } else if (user != NULL) { + + /* do lookup ourself */ + + enum lsa_SidType type; + + if (!lookup_cached_name(mem_ctx, + domain->name, + user, + &cred_sid, + &type)) { + return NT_STATUS_NO_SUCH_USER; + } + } else { + return NT_STATUS_INVALID_PARAMETER; + } + + if (pass) { + + int count = 0; + + status = wcache_count_cached_creds(domain, &count); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + DEBUG(11,("we have %d cached creds\n", count)); + + if (count + 1 > MAX_CACHED_LOGINS) { + + DEBUG(10,("need to delete the oldest cached login\n")); + + status = wcache_remove_oldest_cached_creds(domain, &cred_sid); + if (!NT_STATUS_IS_OK(status)) { + DEBUG(10,("failed to remove oldest cached cred: %s\n", + nt_errstr(status))); + return status; + } + } + + E_md4hash(pass, nt_pass); + + dump_data_pw("nt_pass", nt_pass, NT_HASH_LEN); + + status = wcache_save_creds(domain, mem_ctx, &cred_sid, nt_pass); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + } + + if (info3 != NULL && user != NULL) { + if (!netsamlogon_cache_store(user, info3)) { + return NT_STATUS_ACCESS_DENIED; + } + } + + return NT_STATUS_OK; +} + +NTSTATUS winbindd_update_creds_by_info3(struct winbindd_domain *domain, + TALLOC_CTX *mem_ctx, + const char *user, + const char *pass, + NET_USER_INFO_3 *info3) +{ + return winbindd_store_creds(domain, mem_ctx, user, pass, info3, NULL); +} + +NTSTATUS winbindd_update_creds_by_sid(struct winbindd_domain *domain, + TALLOC_CTX *mem_ctx, + const DOM_SID *sid, + const char *pass) +{ + return winbindd_store_creds(domain, mem_ctx, NULL, pass, NULL, sid); +} + +NTSTATUS winbindd_update_creds_by_name(struct winbindd_domain *domain, + TALLOC_CTX *mem_ctx, + const char *user, + const char *pass) +{ + return winbindd_store_creds(domain, mem_ctx, user, pass, NULL, NULL); +} + + diff --git a/source3/winbindd/winbindd_dual.c b/source3/winbindd/winbindd_dual.c new file mode 100644 index 0000000000..67cf6abc2b --- /dev/null +++ b/source3/winbindd/winbindd_dual.c @@ -0,0 +1,1130 @@ +/* + Unix SMB/CIFS implementation. + + Winbind child daemons + + Copyright (C) Andrew Tridgell 2002 + Copyright (C) Volker Lendecke 2004,2005 + + 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 3 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, see . +*/ + +/* + * We fork a child per domain to be able to act non-blocking in the main + * winbind daemon. A domain controller thousands of miles away being being + * slow replying with a 10.000 user list should not hold up netlogon calls + * that can be handled locally. + */ + +#include "includes.h" +#include "winbindd.h" + +#undef DBGC_CLASS +#define DBGC_CLASS DBGC_WINBIND + +extern BOOL override_logfile; + +/* Read some data from a client connection */ + +static void child_read_request(struct winbindd_cli_state *state) +{ + ssize_t len; + + /* Read data */ + + len = read_data(state->sock, (char *)&state->request, + sizeof(state->request)); + + if (len != sizeof(state->request)) { + DEBUG(len > 0 ? 0 : 3, ("Got invalid request length: %d\n", (int)len)); + state->finished = True; + return; + } + + if (state->request.extra_len == 0) { + state->request.extra_data.data = NULL; + return; + } + + DEBUG(10, ("Need to read %d extra bytes\n", (int)state->request.extra_len)); + + state->request.extra_data.data = + SMB_MALLOC_ARRAY(char, state->request.extra_len + 1); + + if (state->request.extra_data.data == NULL) { + DEBUG(0, ("malloc failed\n")); + state->finished = True; + return; + } + + /* Ensure null termination */ + state->request.extra_data.data[state->request.extra_len] = '\0'; + + len = read_data(state->sock, state->request.extra_data.data, + state->request.extra_len); + + if (len != state->request.extra_len) { + DEBUG(0, ("Could not read extra data\n")); + state->finished = True; + return; + } +} + +/* + * Machinery for async requests sent to children. You set up a + * winbindd_request, select a child to query, and issue a async_request + * call. When the request is completed, the callback function you specified is + * called back with the private pointer you gave to async_request. + */ + +struct winbindd_async_request { + struct winbindd_async_request *next, *prev; + TALLOC_CTX *mem_ctx; + struct winbindd_child *child; + struct winbindd_request *request; + struct winbindd_response *response; + void (*continuation)(void *private_data, BOOL success); + struct timed_event *reply_timeout_event; + pid_t child_pid; /* pid of the child we're waiting on. Used to detect + a restart of the child (child->pid != child_pid). */ + void *private_data; +}; + +static void async_main_request_sent(void *private_data, BOOL success); +static void async_request_sent(void *private_data, BOOL success); +static void async_reply_recv(void *private_data, BOOL success); +static void schedule_async_request(struct winbindd_child *child); + +void async_request(TALLOC_CTX *mem_ctx, struct winbindd_child *child, + struct winbindd_request *request, + struct winbindd_response *response, + void (*continuation)(void *private_data, BOOL success), + void *private_data) +{ + struct winbindd_async_request *state; + + SMB_ASSERT(continuation != NULL); + + state = TALLOC_P(mem_ctx, struct winbindd_async_request); + + if (state == NULL) { + DEBUG(0, ("talloc failed\n")); + continuation(private_data, False); + return; + } + + state->mem_ctx = mem_ctx; + state->child = child; + state->request = request; + state->response = response; + state->continuation = continuation; + state->private_data = private_data; + + DLIST_ADD_END(child->requests, state, struct winbindd_async_request *); + + schedule_async_request(child); + + return; +} + +static void async_main_request_sent(void *private_data, BOOL success) +{ + struct winbindd_async_request *state = + talloc_get_type_abort(private_data, struct winbindd_async_request); + + if (!success) { + DEBUG(5, ("Could not send async request\n")); + + state->response->length = sizeof(struct winbindd_response); + state->response->result = WINBINDD_ERROR; + state->continuation(state->private_data, False); + return; + } + + if (state->request->extra_len == 0) { + async_request_sent(private_data, True); + return; + } + + setup_async_write(&state->child->event, state->request->extra_data.data, + state->request->extra_len, + async_request_sent, state); +} + +/**************************************************************** + Handler triggered if the child winbindd doesn't respond within + a given timeout. +****************************************************************/ + +static void async_request_timeout_handler(struct event_context *ctx, + struct timed_event *te, + const struct timeval *now, + void *private_data) +{ + struct winbindd_async_request *state = + talloc_get_type_abort(private_data, struct winbindd_async_request); + + DEBUG(0,("async_request_timeout_handler: child pid %u is not responding. " + "Closing connection to it.\n", + state->child_pid )); + + /* Deal with the reply - set to error. */ + async_reply_recv(private_data, False); +} + +/************************************************************** + Common function called on both async send and recv fail. + Cleans up the child and schedules the next request. +**************************************************************/ + +static void async_request_fail(struct winbindd_async_request *state) +{ + DLIST_REMOVE(state->child->requests, state); + + TALLOC_FREE(state->reply_timeout_event); + + SMB_ASSERT(state->child_pid != (pid_t)0); + + /* If not already reaped, send kill signal to child. */ + if (state->child->pid == state->child_pid) { + kill(state->child_pid, SIGTERM); + + /* + * Close the socket to the child. + */ + winbind_child_died(state->child_pid); + } + + state->response->length = sizeof(struct winbindd_response); + state->response->result = WINBINDD_ERROR; + state->continuation(state->private_data, False); +} + +static void async_request_sent(void *private_data_data, BOOL success) +{ + struct winbindd_async_request *state = + talloc_get_type_abort(private_data_data, struct winbindd_async_request); + + if (!success) { + DEBUG(5, ("Could not send async request to child pid %u\n", + (unsigned int)state->child_pid )); + async_request_fail(state); + return; + } + + /* Request successfully sent to the child, setup the wait for reply */ + + setup_async_read(&state->child->event, + &state->response->result, + sizeof(state->response->result), + async_reply_recv, state); + + /* + * Set up a timeout of 300 seconds for the response. + * If we don't get it close the child socket and + * report failure. + */ + + state->reply_timeout_event = event_add_timed(winbind_event_context(), + NULL, + timeval_current_ofs(300,0), + "async_request_timeout", + async_request_timeout_handler, + state); + if (!state->reply_timeout_event) { + smb_panic("async_request_sent: failed to add timeout handler.\n"); + } +} + +static void async_reply_recv(void *private_data, BOOL success) +{ + struct winbindd_async_request *state = + talloc_get_type_abort(private_data, struct winbindd_async_request); + struct winbindd_child *child = state->child; + + TALLOC_FREE(state->reply_timeout_event); + + state->response->length = sizeof(struct winbindd_response); + + if (!success) { + DEBUG(5, ("Could not receive async reply from child pid %u\n", + (unsigned int)state->child_pid )); + + cache_cleanup_response(state->child_pid); + async_request_fail(state); + return; + } + + SMB_ASSERT(cache_retrieve_response(state->child_pid, + state->response)); + + cache_cleanup_response(state->child_pid); + + DLIST_REMOVE(child->requests, state); + + schedule_async_request(child); + + state->continuation(state->private_data, True); +} + +static BOOL fork_domain_child(struct winbindd_child *child); + +static void schedule_async_request(struct winbindd_child *child) +{ + struct winbindd_async_request *request = child->requests; + + if (request == NULL) { + return; + } + + if (child->event.flags != 0) { + return; /* Busy */ + } + + if ((child->pid == 0) && (!fork_domain_child(child))) { + /* Cancel all outstanding requests */ + + while (request != NULL) { + /* request might be free'd in the continuation */ + struct winbindd_async_request *next = request->next; + request->continuation(request->private_data, False); + request = next; + } + return; + } + + /* Now we know who we're sending to - remember the pid. */ + request->child_pid = child->pid; + + setup_async_write(&child->event, request->request, + sizeof(*request->request), + async_main_request_sent, request); + + return; +} + +struct domain_request_state { + TALLOC_CTX *mem_ctx; + struct winbindd_domain *domain; + struct winbindd_request *request; + struct winbindd_response *response; + void (*continuation)(void *private_data_data, BOOL success); + void *private_data_data; +}; + +static void domain_init_recv(void *private_data_data, BOOL success); + +void async_domain_request(TALLOC_CTX *mem_ctx, + struct winbindd_domain *domain, + struct winbindd_request *request, + struct winbindd_response *response, + void (*continuation)(void *private_data_data, BOOL success), + void *private_data_data) +{ + struct domain_request_state *state; + + if (domain->initialized) { + async_request(mem_ctx, &domain->child, request, response, + continuation, private_data_data); + return; + } + + state = TALLOC_P(mem_ctx, struct domain_request_state); + if (state == NULL) { + DEBUG(0, ("talloc failed\n")); + continuation(private_data_data, False); + return; + } + + state->mem_ctx = mem_ctx; + state->domain = domain; + state->request = request; + state->response = response; + state->continuation = continuation; + state->private_data_data = private_data_data; + + init_child_connection(domain, domain_init_recv, state); +} + +static void domain_init_recv(void *private_data_data, BOOL success) +{ + struct domain_request_state *state = + talloc_get_type_abort(private_data_data, struct domain_request_state); + + if (!success) { + DEBUG(5, ("Domain init returned an error\n")); + state->continuation(state->private_data_data, False); + return; + } + + async_request(state->mem_ctx, &state->domain->child, + state->request, state->response, + state->continuation, state->private_data_data); +} + +static void recvfrom_child(void *private_data_data, BOOL success) +{ + struct winbindd_cli_state *state = + talloc_get_type_abort(private_data_data, struct winbindd_cli_state); + enum winbindd_result result = state->response.result; + + /* This is an optimization: The child has written directly to the + * response buffer. The request itself is still in pending state, + * state that in the result code. */ + + state->response.result = WINBINDD_PENDING; + + if ((!success) || (result != WINBINDD_OK)) { + request_error(state); + return; + } + + request_ok(state); +} + +void sendto_child(struct winbindd_cli_state *state, + struct winbindd_child *child) +{ + async_request(state->mem_ctx, child, &state->request, + &state->response, recvfrom_child, state); +} + +void sendto_domain(struct winbindd_cli_state *state, + struct winbindd_domain *domain) +{ + async_domain_request(state->mem_ctx, domain, + &state->request, &state->response, + recvfrom_child, state); +} + + +struct winbindd_child_dispatch_table { + enum winbindd_cmd cmd; + enum winbindd_result (*fn)(struct winbindd_domain *domain, + struct winbindd_cli_state *state); + const char *winbindd_cmd_name; +}; + +static struct winbindd_child_dispatch_table child_dispatch_table[] = { + + { WINBINDD_LOOKUPSID, winbindd_dual_lookupsid, "LOOKUPSID" }, + { WINBINDD_LOOKUPNAME, winbindd_dual_lookupname, "LOOKUPNAME" }, + { WINBINDD_LOOKUPRIDS, winbindd_dual_lookuprids, "LOOKUPRIDS" }, + { WINBINDD_LIST_TRUSTDOM, winbindd_dual_list_trusted_domains, "LIST_TRUSTDOM" }, + { WINBINDD_INIT_CONNECTION, winbindd_dual_init_connection, "INIT_CONNECTION" }, + { WINBINDD_GETDCNAME, winbindd_dual_getdcname, "GETDCNAME" }, + { WINBINDD_DSGETDCNAME, winbindd_dual_dsgetdcname, "DSGETDCNAME" }, + { WINBINDD_SHOW_SEQUENCE, winbindd_dual_show_sequence, "SHOW_SEQUENCE" }, + { WINBINDD_PAM_AUTH, winbindd_dual_pam_auth, "PAM_AUTH" }, + { WINBINDD_PAM_AUTH_CRAP, winbindd_dual_pam_auth_crap, "AUTH_CRAP" }, + { WINBINDD_PAM_LOGOFF, winbindd_dual_pam_logoff, "PAM_LOGOFF" }, + { WINBINDD_PAM_CHNG_PSWD_AUTH_CRAP,winbindd_dual_pam_chng_pswd_auth_crap,"CHNG_PSWD_AUTH_CRAP" }, + { WINBINDD_PAM_CHAUTHTOK, winbindd_dual_pam_chauthtok, "PAM_CHAUTHTOK" }, + { WINBINDD_CHECK_MACHACC, winbindd_dual_check_machine_acct, "CHECK_MACHACC" }, + { WINBINDD_DUAL_SID2UID, winbindd_dual_sid2uid, "DUAL_SID2UID" }, + { WINBINDD_DUAL_SID2GID, winbindd_dual_sid2gid, "DUAL_SID2GID" }, +#if 0 /* DISABLED until we fix the interface in Samba 3.0.26 --jerry */ + { WINBINDD_DUAL_SIDS2XIDS, winbindd_dual_sids2xids, "DUAL_SIDS2XIDS" }, +#endif /* end DISABLED */ + { WINBINDD_DUAL_UID2SID, winbindd_dual_uid2sid, "DUAL_UID2SID" }, + { WINBINDD_DUAL_GID2SID, winbindd_dual_gid2sid, "DUAL_GID2SID" }, + { WINBINDD_DUAL_UID2NAME, winbindd_dual_uid2name, "DUAL_UID2NAME" }, + { WINBINDD_DUAL_NAME2UID, winbindd_dual_name2uid, "DUAL_NAME2UID" }, + { WINBINDD_DUAL_GID2NAME, winbindd_dual_gid2name, "DUAL_GID2NAME" }, + { WINBINDD_DUAL_NAME2GID, winbindd_dual_name2gid, "DUAL_NAME2GID" }, + { WINBINDD_DUAL_SET_MAPPING, winbindd_dual_set_mapping, "DUAL_SET_MAPPING" }, + { WINBINDD_DUAL_SET_HWM, winbindd_dual_set_hwm, "DUAL_SET_HWMS" }, + { WINBINDD_DUAL_DUMP_MAPS, winbindd_dual_dump_maps, "DUAL_DUMP_MAPS" }, + { WINBINDD_DUAL_USERINFO, winbindd_dual_userinfo, "DUAL_USERINFO" }, + { WINBINDD_ALLOCATE_UID, winbindd_dual_allocate_uid, "ALLOCATE_UID" }, + { WINBINDD_ALLOCATE_GID, winbindd_dual_allocate_gid, "ALLOCATE_GID" }, + { WINBINDD_GETUSERDOMGROUPS, winbindd_dual_getuserdomgroups, "GETUSERDOMGROUPS" }, + { WINBINDD_DUAL_GETSIDALIASES, winbindd_dual_getsidaliases, "GETSIDALIASES" }, + { WINBINDD_CCACHE_NTLMAUTH, winbindd_dual_ccache_ntlm_auth, "CCACHE_NTLM_AUTH" }, + /* End of list */ + + { WINBINDD_NUM_CMDS, NULL, "NONE" } +}; + +static void child_process_request(struct winbindd_domain *domain, + struct winbindd_cli_state *state) +{ + struct winbindd_child_dispatch_table *table; + + /* Free response data - we may be interrupted and receive another + command before being able to send this data off. */ + + state->response.result = WINBINDD_ERROR; + state->response.length = sizeof(struct winbindd_response); + + /* as all requests in the child are sync, we can use talloc_tos() */ + state->mem_ctx = talloc_tos(); + + /* Process command */ + + for (table = child_dispatch_table; table->fn; table++) { + if (state->request.cmd == table->cmd) { + DEBUG(10,("process_request: request fn %s\n", + table->winbindd_cmd_name )); + state->response.result = table->fn(domain, state); + break; + } + } + + if (!table->fn) { + DEBUG(10,("process_request: unknown request fn number %d\n", + (int)state->request.cmd )); + state->response.result = WINBINDD_ERROR; + } +} + +void setup_domain_child(struct winbindd_domain *domain, + struct winbindd_child *child, + const char *explicit_logfile) +{ + if (explicit_logfile != NULL) { + pstr_sprintf(child->logfilename, "%s/log.winbindd-%s", + dyn_LOGFILEBASE, explicit_logfile); + } else if (domain != NULL) { + pstr_sprintf(child->logfilename, "%s/log.wb-%s", + dyn_LOGFILEBASE, domain->name); + } else { + smb_panic("Internal error: domain == NULL && " + "explicit_logfile == NULL"); + } + + child->domain = domain; +} + +struct winbindd_child *children = NULL; + +void winbind_child_died(pid_t pid) +{ + struct winbindd_child *child; + + for (child = children; child != NULL; child = child->next) { + if (child->pid == pid) { + break; + } + } + + if (child == NULL) { + DEBUG(5, ("Already reaped child %u died\n", (unsigned int)pid)); + return; + } + + remove_fd_event(&child->event); + close(child->event.fd); + child->event.fd = 0; + child->event.flags = 0; + child->pid = 0; + + schedule_async_request(child); +} + +/* Ensure any negative cache entries with the netbios or realm names are removed. */ + +void winbindd_flush_negative_conn_cache(struct winbindd_domain *domain) +{ + flush_negative_conn_cache_for_domain(domain->name); + if (*domain->alt_name) { + flush_negative_conn_cache_for_domain(domain->alt_name); + } +} + +/* Set our domains as offline and forward the offline message to our children. */ + +void winbind_msg_offline(struct messaging_context *msg_ctx, + void *private_data, + uint32_t msg_type, + struct server_id server_id, + DATA_BLOB *data) +{ + struct winbindd_child *child; + struct winbindd_domain *domain; + + DEBUG(10,("winbind_msg_offline: got offline message.\n")); + + if (!lp_winbind_offline_logon()) { + DEBUG(10,("winbind_msg_offline: rejecting offline message.\n")); + return; + } + + /* Set our global state as offline. */ + if (!set_global_winbindd_state_offline()) { + DEBUG(10,("winbind_msg_offline: offline request failed.\n")); + return; + } + + /* Set all our domains as offline. */ + for (domain = domain_list(); domain; domain = domain->next) { + if (domain->internal) { + continue; + } + DEBUG(5,("winbind_msg_offline: marking %s offline.\n", domain->name)); + set_domain_offline(domain); + } + + for (child = children; child != NULL; child = child->next) { + /* Don't send message to internal childs. We've already + done so above. */ + if (!child->domain || winbindd_internal_child(child)) { + continue; + } + + /* Or internal domains (this should not be possible....) */ + if (child->domain->internal) { + continue; + } + + /* Each winbindd child should only process requests for one domain - make sure + we only set it online / offline for that domain. */ + + DEBUG(10,("winbind_msg_offline: sending message to pid %u for domain %s.\n", + (unsigned int)child->pid, domain->name )); + + messaging_send_buf(msg_ctx, pid_to_procid(child->pid), + MSG_WINBIND_OFFLINE, + (uint8 *)child->domain->name, + strlen(child->domain->name)+1); + } +} + +/* Set our domains as online and forward the online message to our children. */ + +void winbind_msg_online(struct messaging_context *msg_ctx, + void *private_data, + uint32_t msg_type, + struct server_id server_id, + DATA_BLOB *data) +{ + struct winbindd_child *child; + struct winbindd_domain *domain; + + DEBUG(10,("winbind_msg_online: got online message.\n")); + + if (!lp_winbind_offline_logon()) { + DEBUG(10,("winbind_msg_online: rejecting online message.\n")); + return; + } + + /* Set our global state as online. */ + set_global_winbindd_state_online(); + + smb_nscd_flush_user_cache(); + smb_nscd_flush_group_cache(); + + /* Set all our domains as online. */ + for (domain = domain_list(); domain; domain = domain->next) { + if (domain->internal) { + continue; + } + DEBUG(5,("winbind_msg_online: requesting %s to go online.\n", domain->name)); + + winbindd_flush_negative_conn_cache(domain); + set_domain_online_request(domain); + + /* Send an online message to the idmap child when our + primary domain comes back online */ + + if ( domain->primary ) { + struct winbindd_child *idmap = idmap_child(); + + if ( idmap->pid != 0 ) { + messaging_send_buf(msg_ctx, + pid_to_procid(idmap->pid), + MSG_WINBIND_ONLINE, + (uint8 *)domain->name, + strlen(domain->name)+1); + } + + } + } + + for (child = children; child != NULL; child = child->next) { + /* Don't send message to internal childs. */ + if (!child->domain || winbindd_internal_child(child)) { + continue; + } + + /* Or internal domains (this should not be possible....) */ + if (child->domain->internal) { + continue; + } + + /* Each winbindd child should only process requests for one domain - make sure + we only set it online / offline for that domain. */ + + DEBUG(10,("winbind_msg_online: sending message to pid %u for domain %s.\n", + (unsigned int)child->pid, child->domain->name )); + + messaging_send_buf(msg_ctx, pid_to_procid(child->pid), + MSG_WINBIND_ONLINE, + (uint8 *)child->domain->name, + strlen(child->domain->name)+1); + } +} + +/* Forward the online/offline messages to our children. */ +void winbind_msg_onlinestatus(struct messaging_context *msg_ctx, + void *private_data, + uint32_t msg_type, + struct server_id server_id, + DATA_BLOB *data) +{ + struct winbindd_child *child; + + DEBUG(10,("winbind_msg_onlinestatus: got onlinestatus message.\n")); + + for (child = children; child != NULL; child = child->next) { + if (child->domain && child->domain->primary) { + DEBUG(10,("winbind_msg_onlinestatus: " + "sending message to pid %u of primary domain.\n", + (unsigned int)child->pid)); + messaging_send_buf(msg_ctx, pid_to_procid(child->pid), + MSG_WINBIND_ONLINESTATUS, + (uint8 *)data->data, + data->length); + break; + } + } +} + +void winbind_msg_dump_event_list(struct messaging_context *msg_ctx, + void *private_data, + uint32_t msg_type, + struct server_id server_id, + DATA_BLOB *data) +{ + struct winbindd_child *child; + + DEBUG(10,("winbind_msg_dump_event_list received\n")); + + dump_event_list(winbind_event_context()); + + for (child = children; child != NULL; child = child->next) { + + DEBUG(10,("winbind_msg_dump_event_list: sending message to pid %u\n", + (unsigned int)child->pid)); + + messaging_send_buf(msg_ctx, pid_to_procid(child->pid), + MSG_DUMP_EVENT_LIST, + NULL, 0); + } + +} + +static void account_lockout_policy_handler(struct event_context *ctx, + struct timed_event *te, + const struct timeval *now, + void *private_data) +{ + struct winbindd_child *child = + (struct winbindd_child *)private_data; + TALLOC_CTX *mem_ctx = NULL; + struct winbindd_methods *methods; + SAM_UNK_INFO_12 lockout_policy; + NTSTATUS result; + + DEBUG(10,("account_lockout_policy_handler called\n")); + + TALLOC_FREE(child->lockout_policy_event); + + if ( !winbindd_can_contact_domain( child->domain ) ) { + DEBUG(10,("account_lockout_policy_handler: Removing myself since I " + "do not have an incoming trust to domain %s\n", + child->domain->name)); + + return; + } + + methods = child->domain->methods; + + mem_ctx = talloc_init("account_lockout_policy_handler ctx"); + if (!mem_ctx) { + result = NT_STATUS_NO_MEMORY; + } else { + result = methods->lockout_policy(child->domain, mem_ctx, &lockout_policy); + } + TALLOC_FREE(mem_ctx); + + if (!NT_STATUS_IS_OK(result)) { + DEBUG(10,("account_lockout_policy_handler: lockout_policy failed error %s\n", + nt_errstr(result))); + } + + child->lockout_policy_event = event_add_timed(winbind_event_context(), NULL, + timeval_current_ofs(3600, 0), + "account_lockout_policy_handler", + account_lockout_policy_handler, + child); +} + +/* Deal with a request to go offline. */ + +static void child_msg_offline(struct messaging_context *msg, + void *private_data, + uint32_t msg_type, + struct server_id server_id, + DATA_BLOB *data) +{ + struct winbindd_domain *domain; + const char *domainname = (const char *)data->data; + + if (data->data == NULL || data->length == 0) { + return; + } + + DEBUG(5,("child_msg_offline received for domain %s.\n", domainname)); + + if (!lp_winbind_offline_logon()) { + DEBUG(10,("child_msg_offline: rejecting offline message.\n")); + return; + } + + /* Mark the requested domain offline. */ + + for (domain = domain_list(); domain; domain = domain->next) { + if (domain->internal) { + continue; + } + if (strequal(domain->name, domainname)) { + DEBUG(5,("child_msg_offline: marking %s offline.\n", domain->name)); + set_domain_offline(domain); + } + } +} + +/* Deal with a request to go online. */ + +static void child_msg_online(struct messaging_context *msg, + void *private_data, + uint32_t msg_type, + struct server_id server_id, + DATA_BLOB *data) +{ + struct winbindd_domain *domain; + const char *domainname = (const char *)data->data; + + if (data->data == NULL || data->length == 0) { + return; + } + + DEBUG(5,("child_msg_online received for domain %s.\n", domainname)); + + if (!lp_winbind_offline_logon()) { + DEBUG(10,("child_msg_online: rejecting online message.\n")); + return; + } + + /* Set our global state as online. */ + set_global_winbindd_state_online(); + + /* Try and mark everything online - delete any negative cache entries + to force a reconnect now. */ + + for (domain = domain_list(); domain; domain = domain->next) { + if (domain->internal) { + continue; + } + if (strequal(domain->name, domainname)) { + DEBUG(5,("child_msg_online: requesting %s to go online.\n", domain->name)); + winbindd_flush_negative_conn_cache(domain); + set_domain_online_request(domain); + } + } +} + +static const char *collect_onlinestatus(TALLOC_CTX *mem_ctx) +{ + struct winbindd_domain *domain; + char *buf = NULL; + + if ((buf = talloc_asprintf(mem_ctx, "global:%s ", + get_global_winbindd_state_offline() ? + "Offline":"Online")) == NULL) { + return NULL; + } + + for (domain = domain_list(); domain; domain = domain->next) { + if ((buf = talloc_asprintf_append(buf, "%s:%s ", + domain->name, + domain->online ? + "Online":"Offline")) == NULL) { + return NULL; + } + } + + buf = talloc_asprintf_append(buf, "\n"); + + DEBUG(5,("collect_onlinestatus: %s", buf)); + + return buf; +} + +static void child_msg_onlinestatus(struct messaging_context *msg_ctx, + void *private_data, + uint32_t msg_type, + struct server_id server_id, + DATA_BLOB *data) +{ + TALLOC_CTX *mem_ctx; + const char *message; + struct server_id *sender; + + DEBUG(5,("winbind_msg_onlinestatus received.\n")); + + if (!data->data) { + return; + } + + sender = (struct server_id *)data->data; + + mem_ctx = talloc_init("winbind_msg_onlinestatus"); + if (mem_ctx == NULL) { + return; + } + + message = collect_onlinestatus(mem_ctx); + if (message == NULL) { + talloc_destroy(mem_ctx); + return; + } + + messaging_send_buf(msg_ctx, *sender, MSG_WINBIND_ONLINESTATUS, + (uint8 *)message, strlen(message) + 1); + + talloc_destroy(mem_ctx); +} + +static void child_msg_dump_event_list(struct messaging_context *msg, + void *private_data, + uint32_t msg_type, + struct server_id server_id, + DATA_BLOB *data) +{ + DEBUG(5,("child_msg_dump_event_list received\n")); + + dump_event_list(winbind_event_context()); +} + + +static BOOL fork_domain_child(struct winbindd_child *child) +{ + int fdpair[2]; + struct winbindd_cli_state state; + struct winbindd_domain *domain; + + if (socketpair(AF_UNIX, SOCK_STREAM, 0, fdpair) != 0) { + DEBUG(0, ("Could not open child pipe: %s\n", + strerror(errno))); + return False; + } + + ZERO_STRUCT(state); + state.pid = sys_getpid(); + + /* Stop zombies */ + CatchChild(); + + child->pid = sys_fork(); + + if (child->pid == -1) { + DEBUG(0, ("Could not fork: %s\n", strerror(errno))); + return False; + } + + if (child->pid != 0) { + /* Parent */ + close(fdpair[0]); + child->next = child->prev = NULL; + DLIST_ADD(children, child); + child->event.fd = fdpair[1]; + child->event.flags = 0; + child->requests = NULL; + add_fd_event(&child->event); + return True; + } + + /* Child */ + + state.sock = fdpair[0]; + close(fdpair[1]); + + /* tdb needs special fork handling */ + if (tdb_reopen_all(1) == -1) { + DEBUG(0,("tdb_reopen_all failed.\n")); + _exit(0); + } + + close_conns_after_fork(); + + if (!override_logfile) { + lp_set_logfile(child->logfilename); + reopen_logs(); + } + + /* + * For clustering, we need to re-init our ctdbd connection after the + * fork + */ + if (!NT_STATUS_IS_OK(messaging_reinit(winbind_messaging_context()))) + exit(1); + + /* Don't handle the same messages as our parent. */ + messaging_deregister(winbind_messaging_context(), + MSG_SMB_CONF_UPDATED, NULL); + messaging_deregister(winbind_messaging_context(), + MSG_SHUTDOWN, NULL); + messaging_deregister(winbind_messaging_context(), + MSG_WINBIND_OFFLINE, NULL); + messaging_deregister(winbind_messaging_context(), + MSG_WINBIND_ONLINE, NULL); + messaging_deregister(winbind_messaging_context(), + MSG_WINBIND_ONLINESTATUS, NULL); + messaging_deregister(winbind_messaging_context(), + MSG_DUMP_EVENT_LIST, NULL); + + /* Handle online/offline messages. */ + messaging_register(winbind_messaging_context(), NULL, + MSG_WINBIND_OFFLINE, child_msg_offline); + messaging_register(winbind_messaging_context(), NULL, + MSG_WINBIND_ONLINE, child_msg_online); + messaging_register(winbind_messaging_context(), NULL, + MSG_WINBIND_ONLINESTATUS, child_msg_onlinestatus); + messaging_register(winbind_messaging_context(), NULL, + MSG_DUMP_EVENT_LIST, child_msg_dump_event_list); + + if ( child->domain ) { + child->domain->startup = True; + child->domain->startup_time = time(NULL); + } + + /* Ensure we have no pending check_online events other + than one for this domain. */ + + for (domain = domain_list(); domain; domain = domain->next) { + if (domain != child->domain) { + TALLOC_FREE(domain->check_online_event); + } + } + + /* Ensure we're not handling an event inherited from + our parent. */ + + cancel_named_event(winbind_event_context(), + "krb5_ticket_refresh_handler"); + + /* We might be in the idmap child...*/ + if (child->domain && !(child->domain->internal) && + lp_winbind_offline_logon()) { + + set_domain_online_request(child->domain); + + child->lockout_policy_event = event_add_timed( + winbind_event_context(), NULL, timeval_zero(), + "account_lockout_policy_handler", + account_lockout_policy_handler, + child); + } + + while (1) { + + int ret; + fd_set read_fds; + struct timeval t; + struct timeval *tp; + struct timeval now; + TALLOC_CTX *frame = talloc_stackframe(); + + run_events(winbind_event_context(), 0, NULL, NULL); + + GetTimeOfDay(&now); + + if (child->domain && child->domain->startup && + (now.tv_sec > child->domain->startup_time + 30)) { + /* No longer in "startup" mode. */ + DEBUG(10,("fork_domain_child: domain %s no longer in 'startup' mode.\n", + child->domain->name )); + child->domain->startup = False; + } + + tp = get_timed_events_timeout(winbind_event_context(), &t); + if (tp) { + DEBUG(11,("select will use timeout of %u.%u seconds\n", + (unsigned int)tp->tv_sec, (unsigned int)tp->tv_usec )); + } + + /* Handle messages */ + + message_dispatch(winbind_messaging_context()); + + FD_ZERO(&read_fds); + FD_SET(state.sock, &read_fds); + + ret = sys_select(state.sock + 1, &read_fds, NULL, NULL, tp); + + if (ret == 0) { + DEBUG(11,("nothing is ready yet, continue\n")); + TALLOC_FREE(frame); + continue; + } + + if (ret == -1 && errno == EINTR) { + /* We got a signal - continue. */ + TALLOC_FREE(frame); + continue; + } + + if (ret == -1 && errno != EINTR) { + DEBUG(0,("select error occured\n")); + TALLOC_FREE(frame); + perror("select"); + return False; + } + + /* fetch a request from the main daemon */ + child_read_request(&state); + + if (state.finished) { + /* we lost contact with our parent */ + exit(0); + } + + DEBUG(4,("child daemon request %d\n", (int)state.request.cmd)); + + ZERO_STRUCT(state.response); + state.request.null_term = '\0'; + child_process_request(child->domain, &state); + + SAFE_FREE(state.request.extra_data.data); + + cache_store_response(sys_getpid(), &state.response); + + SAFE_FREE(state.response.extra_data.data); + + /* We just send the result code back, the result + * structure needs to be fetched via the + * winbindd_cache. Hmm. That needs fixing... */ + + if (write_data(state.sock, + (const char *)&state.response.result, + sizeof(state.response.result)) != + sizeof(state.response.result)) { + DEBUG(0, ("Could not write result\n")); + exit(1); + } + TALLOC_FREE(frame); + } +} diff --git a/source3/winbindd/winbindd_group.c b/source3/winbindd/winbindd_group.c new file mode 100644 index 0000000000..9a4b02f734 --- /dev/null +++ b/source3/winbindd/winbindd_group.c @@ -0,0 +1,1746 @@ +/* + Unix SMB/CIFS implementation. + + Winbind daemon for ntdom nss module + + Copyright (C) Tim Potter 2000 + Copyright (C) Jeremy Allison 2001. + Copyright (C) Gerald (Jerry) Carter 2003. + Copyright (C) Volker Lendecke 2005 + + 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 3 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, see . +*/ + +#include "includes.h" +#include "winbindd.h" + +extern BOOL opt_nocache; + +#undef DBGC_CLASS +#define DBGC_CLASS DBGC_WINBIND + +static void add_member(const char *domain, const char *user, + char **pp_members, size_t *p_num_members) +{ + fstring name; + + fill_domain_username(name, domain, user, True); + safe_strcat(name, ",", sizeof(name)-1); + string_append(pp_members, name); + *p_num_members += 1; +} + +/********************************************************************** + Add member users resulting from sid. Expand if it is a domain group. +**********************************************************************/ + +static void add_expanded_sid(const DOM_SID *sid, char **pp_members, size_t *p_num_members) +{ + DOM_SID dom_sid; + uint32 rid; + struct winbindd_domain *domain; + size_t i; + + char *domain_name = NULL; + char *name = NULL; + enum lsa_SidType type; + + uint32 num_names; + DOM_SID *sid_mem; + char **names; + uint32 *types; + + NTSTATUS result; + + TALLOC_CTX *mem_ctx = talloc_init("add_expanded_sid"); + + if (mem_ctx == NULL) { + DEBUG(1, ("talloc_init failed\n")); + return; + } + + sid_copy(&dom_sid, sid); + sid_split_rid(&dom_sid, &rid); + + domain = find_lookup_domain_from_sid(sid); + + if (domain == NULL) { + DEBUG(3, ("Could not find domain for sid %s\n", + sid_string_static(sid))); + goto done; + } + + result = domain->methods->sid_to_name(domain, mem_ctx, sid, + &domain_name, &name, &type); + + if (!NT_STATUS_IS_OK(result)) { + DEBUG(3, ("sid_to_name failed for sid %s\n", + sid_string_static(sid))); + goto done; + } + + DEBUG(10, ("Found name %s, type %d\n", name, type)); + + if (type == SID_NAME_USER) { + add_member(domain_name, name, pp_members, p_num_members); + goto done; + } + + if (type != SID_NAME_DOM_GRP) { + DEBUG(10, ("Alias member %s neither user nor group, ignore\n", + name)); + goto done; + } + + /* Expand the domain group, this must be done via the target domain */ + + domain = find_domain_from_sid(sid); + + if (domain == NULL) { + DEBUG(3, ("Could not find domain from SID %s\n", + sid_string_static(sid))); + goto done; + } + + result = domain->methods->lookup_groupmem(domain, mem_ctx, + sid, &num_names, + &sid_mem, &names, + &types); + + if (!NT_STATUS_IS_OK(result)) { + DEBUG(10, ("Could not lookup group members for %s: %s\n", + name, nt_errstr(result))); + goto done; + } + + for (i=0; iname, names[i], pp_members, p_num_members); + } + + done: + talloc_destroy(mem_ctx); + return; +} + +static BOOL fill_passdb_alias_grmem(struct winbindd_domain *domain, + DOM_SID *group_sid, + size_t *num_gr_mem, char **gr_mem, size_t *gr_mem_len) +{ + DOM_SID *members; + size_t i, num_members; + + *num_gr_mem = 0; + *gr_mem = NULL; + *gr_mem_len = 0; + + if (!NT_STATUS_IS_OK(pdb_enum_aliasmem(group_sid, &members, + &num_members))) + return True; + + for (i=0; igr_gid = unix_gid; + + /* Group name and password */ + + safe_strcpy(gr->gr_name, full_group_name, sizeof(gr->gr_name) - 1); + safe_strcpy(gr->gr_passwd, "x", sizeof(gr->gr_passwd) - 1); + + return True; +} + +/*********************************************************************** + If "enum users" is set to false, and the group being looked + up is the Domain Users SID: S-1-5-domain-513, then for the + list of members check if the querying user is in that group, + and if so only return that user as the gr_mem array. + We can change this to a different parameter than "enum users" + if neccessaey, or parameterize the group list we do this for. +***********************************************************************/ + +static BOOL fill_grent_mem_domusers( TALLOC_CTX *mem_ctx, + struct winbindd_domain *domain, + struct winbindd_cli_state *state, + DOM_SID *group_sid, + enum lsa_SidType group_name_type, + size_t *num_gr_mem, char **gr_mem, + size_t *gr_mem_len) +{ + DOM_SID querying_user_sid; + DOM_SID *pquerying_user_sid = NULL; + uint32 num_groups = 0; + DOM_SID *user_sids = NULL; + BOOL u_in_group = False; + NTSTATUS status; + int i; + unsigned int buf_len = 0; + char *buf = NULL; + + DEBUG(10,("fill_grent_mem_domain_users: domain %s\n", + domain->name )); + + if (state) { + uid_t ret_uid = (uid_t)-1; + if (sys_getpeereid(state->sock, &ret_uid)==0) { + /* We know who's asking - look up their SID if + it's one we've mapped before. */ + status = idmap_uid_to_sid(&querying_user_sid, ret_uid); + if (NT_STATUS_IS_OK(status)) { + pquerying_user_sid = &querying_user_sid; + DEBUG(10,("fill_grent_mem_domain_users: querying uid %u -> %s\n", + (unsigned int)ret_uid, + sid_string_static(pquerying_user_sid) )); + } + } + } + + /* Only look up if it was a winbindd user in this domain. */ + if (pquerying_user_sid && + (sid_compare_domain(pquerying_user_sid, &domain->sid) == 0)) { + + DEBUG(10,("fill_grent_mem_domain_users: querying user = %s\n", + sid_string_static(pquerying_user_sid) )); + + status = domain->methods->lookup_usergroups(domain, + mem_ctx, + pquerying_user_sid, + &num_groups, + &user_sids); + if (!NT_STATUS_IS_OK(status)) { + DEBUG(1, ("fill_grent_mem_domain_users: lookup_usergroups failed " + "for sid %s in domain %s (error: %s)\n", + sid_string_static(pquerying_user_sid), + domain->name, + nt_errstr(status))); + return False; + } + + for (i = 0; i < num_groups; i++) { + if (sid_equal(group_sid, &user_sids[i])) { + /* User is in Domain Users, add their name + as the only group member. */ + u_in_group = True; + break; + } + } + } + + if (u_in_group) { + size_t len = 0; + char *domainname = NULL; + char *username = NULL; + fstring name; + enum lsa_SidType type; + + DEBUG(10,("fill_grent_mem_domain_users: sid %s in 'Domain Users' in domain %s\n", + sid_string_static(pquerying_user_sid), domain->name )); + + status = domain->methods->sid_to_name(domain, mem_ctx, + pquerying_user_sid, + &domainname, + &username, + &type); + if (!NT_STATUS_IS_OK(status)) { + DEBUG(1, ("could not lookup username for user " + "sid %s in domain %s (error: %s)\n", + sid_string_static(pquerying_user_sid), + domain->name, + nt_errstr(status))); + return False; + } + fill_domain_username(name, domain->name, username, True); + len = strlen(name); + buf_len = len + 1; + if (!(buf = (char *)SMB_MALLOC(buf_len))) { + DEBUG(1, ("out of memory\n")); + return False; + } + memcpy(buf, name, buf_len); + + DEBUG(10,("fill_grent_mem_domain_users: user %s in " + "'Domain Users' in domain %s\n", + name, domain->name )); + + /* user is the only member */ + *num_gr_mem = 1; + } + + *gr_mem = buf; + *gr_mem_len = buf_len; + + DEBUG(10, ("fill_grent_mem_domain_users: num_mem = %u, len = %u, mem = %s\n", + (unsigned int)*num_gr_mem, + (unsigned int)buf_len, *num_gr_mem ? buf : "NULL")); + + return True; +} + +/*********************************************************************** + Add names to a list. Assumes a canonical version of the string + in DOMAIN\user +***********************************************************************/ + +static int namecmp( const void *a, const void *b ) +{ + return StrCaseCmp( * (char * const *) a, * (char * const *) b); +} + +static NTSTATUS add_names_to_list( TALLOC_CTX *ctx, + char ***list, uint32 *n_list, + char **names, uint32 n_names ) +{ + char **new_list = NULL; + uint32 n_new_list = 0; + int i, j; + + if ( !names || (n_names == 0) ) + return NT_STATUS_OK; + + /* Alloc the maximum size we'll need */ + + if ( *list == NULL ) { + if ( (new_list = TALLOC_ARRAY( ctx, char *, n_names )) == NULL ) + return NT_STATUS_NO_MEMORY; + n_new_list = n_names; + } else { + new_list = TALLOC_REALLOC_ARRAY( ctx, *list, char *, + (*n_list) + n_names ); + if ( !new_list ) + return NT_STATUS_NO_MEMORY; + n_new_list = (*n_list) + n_names; + } + + /* Add all names */ + + for ( i=*n_list, j=0; imethods->lookup_groupmem(d, tmp_ctx, + &glist[i], &num_names, + &sid_mem, &names, + &name_types); + if ( !NT_STATUS_IS_OK(status) ) + goto out; + + /* Separate users and groups into two lists */ + + for ( j=0; jname))) + return False; + + DEBUG(10, ("group SID %s\n", sid_string_static(group_sid))); + + /* Initialize with no members */ + + *num_gr_mem = 0; + + /* HACK ALERT!! This whole routine does not cope with group members + * from more than one domain, ie aliases. Thus we have to work it out + * ourselves in a special routine. */ + + if (domain->internal) { + result = fill_passdb_alias_grmem(domain, group_sid, + num_gr_mem, + gr_mem, gr_mem_len); + goto done; + } + + /* Verify name type */ + + if ( !((group_name_type==SID_NAME_DOM_GRP) || + ((group_name_type==SID_NAME_ALIAS) && domain->primary)) ) + { + DEBUG(1, ("SID %s in domain %s isn't a domain group (%d)\n", + sid_string_static(group_sid), domain->name, + group_name_type)); + goto done; + } + + /* OPTIMIZATION / HACK. See comment in + fill_grent_mem_domusers() */ + + sid_peek_rid( group_sid, &group_rid ); + if (!lp_winbind_enum_users() && group_rid == DOMAIN_GROUP_RID_USERS) { + result = fill_grent_mem_domusers( mem_ctx, domain, state, + group_sid, group_name_type, + num_gr_mem, gr_mem, + gr_mem_len ); + goto done; + } + + /* Real work goes here. Create a list of group names to + expand startign with the initial one. Pass that to + expand_groups() which returns a list of more group names + to expand. Do this up to the max search depth. */ + + if ( (glist = TALLOC_ARRAY(mem_ctx, DOM_SID, 1 )) == NULL ) { + result = False; + DEBUG(0,("fill_grent_mem: talloc failure!\n")); + goto done; + } + sid_copy( &glist[0], group_sid ); + n_glist = 1; + + for ( i=0; i 0) { + buf[buf_ndx - 1] = '\0'; + } + + *gr_mem = buf; + *gr_mem_len = buf_len; + + DEBUG(10, ("num_mem = %u, len = %u, mem = %s\n", (unsigned int)*num_gr_mem, + (unsigned int)buf_len, *num_gr_mem ? buf : "NULL")); + result = True; + +done: + + talloc_destroy(mem_ctx); + + DEBUG(10, ("fill_grent_mem returning %d\n", result)); + + return result; +} + +static void winbindd_getgrsid( struct winbindd_cli_state *state, DOM_SID group_sid ); + +static void getgrnam_recv( void *private_data, BOOL success, const DOM_SID *sid, + enum lsa_SidType type ) +{ + struct winbindd_cli_state *state = (struct winbindd_cli_state*)private_data; + + if (!success) { + DEBUG(5,("getgrnam_recv: lookupname failed!\n")); + request_error(state); + return; + } + + if ( (type != SID_NAME_DOM_GRP) && (type != SID_NAME_ALIAS) ) { + DEBUG(5,("getgrnam_recv: not a group!\n")); + request_error(state); + return; + } + + winbindd_getgrsid( state, *sid ); +} + + +/* Return a group structure from a group name */ + +void winbindd_getgrnam(struct winbindd_cli_state *state) +{ + struct winbindd_domain *domain; + fstring name_domain, name_group; + char *tmp; + + /* Ensure null termination */ + state->request.data.groupname[sizeof(state->request.data.groupname)-1]='\0'; + + DEBUG(3, ("[%5lu]: getgrnam %s\n", (unsigned long)state->pid, + state->request.data.groupname)); + + /* Parse domain and groupname */ + + memset(name_group, 0, sizeof(fstring)); + + tmp = state->request.data.groupname; + + name_domain[0] = '\0'; + name_group[0] = '\0'; + + parse_domain_user(tmp, name_domain, name_group); + + /* if no domain or our local domain and no local tdb group, default to + * our local domain for aliases */ + + if ( !*name_domain || strequal(name_domain, get_global_sam_name()) ) { + fstrcpy(name_domain, get_global_sam_name()); + } + + /* Get info for the domain */ + + if ((domain = find_domain_from_name(name_domain)) == NULL) { + DEBUG(3, ("could not get domain sid for domain %s\n", + name_domain)); + request_error(state); + return; + } + /* should we deal with users for our domain? */ + + if ( lp_winbind_trusted_domains_only() && domain->primary) { + DEBUG(7,("winbindd_getgrnam: My domain -- rejecting " + "getgrnam() for %s\\%s.\n", name_domain, name_group)); + request_error(state); + return; + } + + /* Get rid and name type from name */ + + ws_name_replace( name_group, WB_REPLACE_CHAR ); + + winbindd_lookupname_async( state->mem_ctx, domain->name, name_group, + getgrnam_recv, WINBINDD_GETGRNAM, state ); +} + +struct getgrsid_state { + struct winbindd_cli_state *state; + struct winbindd_domain *domain; + char *group_name; + enum lsa_SidType group_type; + uid_t gid; + DOM_SID group_sid; +}; + +static void getgrsid_sid2gid_recv(void *private_data, BOOL success, gid_t gid) + { + struct getgrsid_state *s = + (struct getgrsid_state *)private_data; + struct winbindd_domain *domain; + size_t gr_mem_len; + size_t num_gr_mem; + char *gr_mem; + fstring dom_name, group_name; + + if (!success) { + DEBUG(5,("getgrsid_sid2gid_recv: sid2gid failed!\n")); + request_error(s->state); + return; + } + + s->gid = gid; + + if ( !parse_domain_user( s->group_name, dom_name, group_name ) ) { + DEBUG(5,("getgrsid_sid2gid_recv: parse_domain_user() failed!\n")); + request_error(s->state); + return; + } + + + /* Fill in group structure */ + + if ( (domain = find_domain_from_name_noinit(dom_name)) == NULL ) { + DEBUG(1,("Can't find domain from name (%s)\n", dom_name)); + request_error(s->state); + return; + } + + if (!fill_grent(&s->state->response.data.gr, dom_name, group_name, gid) || + !fill_grent_mem(domain, s->state, &s->group_sid, s->group_type, + &num_gr_mem, &gr_mem, &gr_mem_len)) + { + request_error(s->state); + return; + } + + s->state->response.data.gr.num_gr_mem = (uint32)num_gr_mem; + + /* Group membership lives at start of extra data */ + + s->state->response.data.gr.gr_mem_ofs = 0; + + s->state->response.length += gr_mem_len; + s->state->response.extra_data.data = gr_mem; + + request_ok(s->state); + } + +static void getgrsid_lookupsid_recv( void *private_data, BOOL success, + const char *dom_name, const char *name, + enum lsa_SidType name_type ) +{ + struct getgrsid_state *s = (struct getgrsid_state *)private_data; + + if (!success) { + DEBUG(5,("getgrsid_lookupsid_recv: lookupsid failed!\n")); + request_error(s->state); + return; + } + + /* either it's a domain group, a domain local group, or a + local group in an internal domain */ + + if ( !( (name_type==SID_NAME_DOM_GRP) || + ((name_type==SID_NAME_ALIAS) && + (s->domain->primary || s->domain->internal)) ) ) + { + DEBUG(1, ("name '%s\\%s' is not a local or domain group: %d\n", + dom_name, name, name_type)); + request_error(s->state); + return; +} + + if ( (s->group_name = talloc_asprintf( s->state->mem_ctx, + "%s\\%s", + dom_name, name )) == NULL ) +{ + DEBUG(1, ("getgrsid_lookupsid_recv: talloc_asprintf() Failed!\n")); + request_error(s->state); + return; + } + + s->group_type = name_type; + + winbindd_sid2gid_async(s->state->mem_ctx, &s->group_sid, + getgrsid_sid2gid_recv, s); + } + +static void winbindd_getgrsid( struct winbindd_cli_state *state, const DOM_SID group_sid ) + { + struct getgrsid_state *s; + + if ( (s = TALLOC_ZERO_P(state->mem_ctx, struct getgrsid_state)) == NULL ) { + DEBUG(0, ("talloc failed\n")); + request_error(state); + return; + } + + s->state = state; + + if ( (s->domain = find_domain_from_sid_noinit(&group_sid)) == NULL ) { + DEBUG(3, ("Could not find domain for sid %s\n", + sid_string_static(&group_sid))); + request_error(state); + return; + } + + sid_copy(&s->group_sid, &group_sid); + + winbindd_lookupsid_async( s->state->mem_ctx, &group_sid, + getgrsid_lookupsid_recv, s ); +} + + +static void getgrgid_recv(void *private_data, BOOL success, const char *sid) +{ + struct winbindd_cli_state *state = talloc_get_type_abort(private_data, struct winbindd_cli_state); + enum lsa_SidType name_type; + DOM_SID group_sid; + + if (success) { + DEBUG(10,("getgrgid_recv: gid %lu has sid %s\n", + (unsigned long)(state->request.data.gid), sid)); + + string_to_sid(&group_sid, sid); + winbindd_getgrsid(state, group_sid); + return; + } + + /* Ok, this might be "ours", i.e. an alias */ + if (pdb_gid_to_sid(state->request.data.gid, &group_sid) && + lookup_sid(state->mem_ctx, &group_sid, NULL, NULL, &name_type) && + (name_type == SID_NAME_ALIAS)) { + /* Hey, got an alias */ + DEBUG(10,("getgrgid_recv: we have an alias with gid %lu and sid %s\n", + (unsigned long)(state->request.data.gid), sid)); + winbindd_getgrsid(state, group_sid); + return; + } + + DEBUG(1, ("could not convert gid %lu to sid\n", + (unsigned long)state->request.data.gid)); + request_error(state); +} + +/* Return a group structure from a gid number */ +void winbindd_getgrgid(struct winbindd_cli_state *state) +{ + DEBUG(3, ("[%5lu]: getgrgid %lu\n", (unsigned long)state->pid, + (unsigned long)state->request.data.gid)); + + /* always use the async interface */ + winbindd_gid2sid_async(state->mem_ctx, state->request.data.gid, getgrgid_recv, state); +} + +/* + * set/get/endgrent functions + */ + +/* "Rewind" file pointer for group database enumeration */ + +static BOOL winbindd_setgrent_internal(struct winbindd_cli_state *state) +{ + struct winbindd_domain *domain; + + DEBUG(3, ("[%5lu]: setgrent\n", (unsigned long)state->pid)); + + /* Check user has enabled this */ + + if (!lp_winbind_enum_groups()) { + return False; + } + + /* Free old static data if it exists */ + + if (state->getgrent_state != NULL) { + free_getent_state(state->getgrent_state); + state->getgrent_state = NULL; + } + + /* Create sam pipes for each domain we know about */ + + for (domain = domain_list(); domain != NULL; domain = domain->next) { + struct getent_state *domain_state; + + /* Create a state record for this domain */ + + /* don't add our domaina if we are a PDC or if we + are a member of a Samba domain */ + + if ( lp_winbind_trusted_domains_only() && domain->primary ) + { + continue; + } + + + if ((domain_state = SMB_MALLOC_P(struct getent_state)) == NULL) { + DEBUG(1, ("winbindd_setgrent: malloc failed for domain_state!\n")); + return False; + } + + ZERO_STRUCTP(domain_state); + + fstrcpy(domain_state->domain_name, domain->name); + + /* Add to list of open domains */ + + DLIST_ADD(state->getgrent_state, domain_state); + } + + state->getgrent_initialized = True; + return True; +} + +void winbindd_setgrent(struct winbindd_cli_state *state) +{ + if (winbindd_setgrent_internal(state)) { + request_ok(state); + } else { + request_error(state); + } +} + +/* Close file pointer to ntdom group database */ + +void winbindd_endgrent(struct winbindd_cli_state *state) +{ + DEBUG(3, ("[%5lu]: endgrent\n", (unsigned long)state->pid)); + + free_getent_state(state->getgrent_state); + state->getgrent_initialized = False; + state->getgrent_state = NULL; + request_ok(state); +} + +/* Get the list of domain groups and domain aliases for a domain. We fill in + the sam_entries and num_sam_entries fields with domain group information. + The dispinfo_ndx field is incremented to the index of the next group to + fetch. Return True if some groups were returned, False otherwise. */ + +static BOOL get_sam_group_entries(struct getent_state *ent) +{ + NTSTATUS status; + uint32 num_entries; + struct acct_info *name_list = NULL; + TALLOC_CTX *mem_ctx; + BOOL result = False; + struct acct_info *sam_grp_entries = NULL; + struct winbindd_domain *domain; + + if (ent->got_sam_entries) + return False; + + if (!(mem_ctx = talloc_init("get_sam_group_entries(%s)", + ent->domain_name))) { + DEBUG(1, ("get_sam_group_entries: could not create talloc context!\n")); + return False; + } + + /* Free any existing group info */ + + SAFE_FREE(ent->sam_entries); + ent->num_sam_entries = 0; + ent->got_sam_entries = True; + + /* Enumerate domain groups */ + + num_entries = 0; + + if (!(domain = find_domain_from_name(ent->domain_name))) { + DEBUG(3, ("no such domain %s in get_sam_group_entries\n", ent->domain_name)); + goto done; + } + + /* always get the domain global groups */ + + status = domain->methods->enum_dom_groups(domain, mem_ctx, &num_entries, &sam_grp_entries); + + if (!NT_STATUS_IS_OK(status)) { + DEBUG(3, ("get_sam_group_entries: could not enumerate domain groups! Error: %s\n", nt_errstr(status))); + result = False; + goto done; + } + + /* Copy entries into return buffer */ + + if (num_entries) { + if ( !(name_list = SMB_MALLOC_ARRAY(struct acct_info, num_entries)) ) { + DEBUG(0,("get_sam_group_entries: Failed to malloc memory for %d domain groups!\n", + num_entries)); + result = False; + goto done; + } + memcpy( name_list, sam_grp_entries, num_entries * sizeof(struct acct_info) ); + } + + ent->num_sam_entries = num_entries; + + /* get the domain local groups if we are a member of a native win2k domain + and are not using LDAP to get the groups */ + + if ( ( lp_security() != SEC_ADS && domain->native_mode + && domain->primary) || domain->internal ) + { + DEBUG(4,("get_sam_group_entries: %s domain; enumerating local groups as well\n", + domain->native_mode ? "Native Mode 2k":"BUILTIN or local")); + + status = domain->methods->enum_local_groups(domain, mem_ctx, &num_entries, &sam_grp_entries); + + if ( !NT_STATUS_IS_OK(status) ) { + DEBUG(3,("get_sam_group_entries: Failed to enumerate domain local groups!\n")); + num_entries = 0; + } + else + DEBUG(4,("get_sam_group_entries: Returned %d local groups\n", num_entries)); + + /* Copy entries into return buffer */ + + if ( num_entries ) { + if ( !(name_list = SMB_REALLOC_ARRAY( name_list, struct acct_info, ent->num_sam_entries+num_entries)) ) + { + DEBUG(0,("get_sam_group_entries: Failed to realloc more memory for %d local groups!\n", + num_entries)); + result = False; + goto done; + } + + memcpy( &name_list[ent->num_sam_entries], sam_grp_entries, + num_entries * sizeof(struct acct_info) ); + } + + ent->num_sam_entries += num_entries; + } + + + /* Fill in remaining fields */ + + ent->sam_entries = name_list; + ent->sam_entry_index = 0; + + result = (ent->num_sam_entries > 0); + + done: + talloc_destroy(mem_ctx); + + return result; +} + +/* Fetch next group entry from ntdom database */ + +#define MAX_GETGRENT_GROUPS 500 + +void winbindd_getgrent(struct winbindd_cli_state *state) +{ + struct getent_state *ent; + struct winbindd_gr *group_list = NULL; + int num_groups, group_list_ndx, gr_mem_list_len = 0; + char *gr_mem_list = NULL; + + DEBUG(3, ("[%5lu]: getgrent\n", (unsigned long)state->pid)); + + /* Check user has enabled this */ + + if (!lp_winbind_enum_groups()) { + request_error(state); + return; + } + + num_groups = MIN(MAX_GETGRENT_GROUPS, state->request.data.num_entries); + + if (num_groups == 0) { + request_error(state); + return; + } + + if ((state->response.extra_data.data = SMB_MALLOC_ARRAY(struct winbindd_gr, num_groups)) == NULL) { + request_error(state); + return; + } + + memset(state->response.extra_data.data, '\0', + num_groups * sizeof(struct winbindd_gr) ); + + state->response.data.num_entries = 0; + + group_list = (struct winbindd_gr *)state->response.extra_data.data; + + if (!state->getgrent_initialized) + winbindd_setgrent_internal(state); + + if (!(ent = state->getgrent_state)) { + request_error(state); + return; + } + + /* Start sending back groups */ + + for (group_list_ndx = 0; group_list_ndx < num_groups; ) { + struct acct_info *name_list = NULL; + fstring domain_group_name; + uint32 result; + gid_t group_gid; + size_t gr_mem_len; + char *gr_mem; + DOM_SID group_sid; + struct winbindd_domain *domain; + + /* Do we need to fetch another chunk of groups? */ + + tryagain: + + DEBUG(10, ("entry_index = %d, num_entries = %d\n", + ent->sam_entry_index, ent->num_sam_entries)); + + if (ent->num_sam_entries == ent->sam_entry_index) { + + while(ent && !get_sam_group_entries(ent)) { + struct getent_state *next_ent; + + DEBUG(10, ("freeing state info for domain %s\n", ent->domain_name)); + + /* Free state information for this domain */ + + SAFE_FREE(ent->sam_entries); + + next_ent = ent->next; + DLIST_REMOVE(state->getgrent_state, ent); + + SAFE_FREE(ent); + ent = next_ent; + } + + /* No more domains */ + + if (!ent) + break; + } + + name_list = (struct acct_info *)ent->sam_entries; + + if (!(domain = + find_domain_from_name(ent->domain_name))) { + DEBUG(3, ("No such domain %s in winbindd_getgrent\n", ent->domain_name)); + result = False; + goto done; + } + + /* Lookup group info */ + + sid_copy(&group_sid, &domain->sid); + sid_append_rid(&group_sid, name_list[ent->sam_entry_index].rid); + + if (!NT_STATUS_IS_OK(idmap_sid_to_gid(&group_sid, &group_gid))) { + union unid_t id; + enum lsa_SidType type; + + DEBUG(10, ("SID %s not in idmap\n", + sid_string_static(&group_sid))); + + if (!pdb_sid_to_id(&group_sid, &id, &type)) { + DEBUG(1, ("could not look up gid for group " + "%s\n", + name_list[ent->sam_entry_index].acct_name)); + ent->sam_entry_index++; + goto tryagain; + } + + if ((type != SID_NAME_DOM_GRP) && + (type != SID_NAME_ALIAS) && + (type != SID_NAME_WKN_GRP)) { + DEBUG(1, ("Group %s is a %s, not a group\n", + sid_type_lookup(type), + name_list[ent->sam_entry_index].acct_name)); + ent->sam_entry_index++; + goto tryagain; + } + group_gid = id.gid; + } + + DEBUG(10, ("got gid %lu for group %lu\n", (unsigned long)group_gid, + (unsigned long)name_list[ent->sam_entry_index].rid)); + + /* Fill in group entry */ + + fill_domain_username(domain_group_name, ent->domain_name, + name_list[ent->sam_entry_index].acct_name, True); + + result = fill_grent(&group_list[group_list_ndx], + ent->domain_name, + name_list[ent->sam_entry_index].acct_name, + group_gid); + + /* Fill in group membership entry */ + + if (result) { + size_t num_gr_mem = 0; + DOM_SID member_sid; + group_list[group_list_ndx].num_gr_mem = 0; + gr_mem = NULL; + gr_mem_len = 0; + + /* Get group membership */ + if (state->request.cmd == WINBINDD_GETGRLST) { + result = True; + } else { + sid_copy(&member_sid, &domain->sid); + sid_append_rid(&member_sid, name_list[ent->sam_entry_index].rid); + result = fill_grent_mem( + domain, + NULL, + &member_sid, + SID_NAME_DOM_GRP, + &num_gr_mem, + &gr_mem, &gr_mem_len); + + group_list[group_list_ndx].num_gr_mem = (uint32)num_gr_mem; + } + } + + if (result) { + /* Append to group membership list */ + gr_mem_list = (char *)SMB_REALLOC( + gr_mem_list, gr_mem_list_len + gr_mem_len); + + if (!gr_mem_list && (group_list[group_list_ndx].num_gr_mem != 0)) { + DEBUG(0, ("out of memory\n")); + gr_mem_list_len = 0; + break; + } + + DEBUG(10, ("list_len = %d, mem_len = %u\n", + gr_mem_list_len, (unsigned int)gr_mem_len)); + + memcpy(&gr_mem_list[gr_mem_list_len], gr_mem, + gr_mem_len); + + SAFE_FREE(gr_mem); + + group_list[group_list_ndx].gr_mem_ofs = + gr_mem_list_len; + + gr_mem_list_len += gr_mem_len; + } + + ent->sam_entry_index++; + + /* Add group to return list */ + + if (result) { + + DEBUG(10, ("adding group num_entries = %d\n", + state->response.data.num_entries)); + + group_list_ndx++; + state->response.data.num_entries++; + + state->response.length += + sizeof(struct winbindd_gr); + + } else { + DEBUG(0, ("could not lookup domain group %s\n", + domain_group_name)); + } + } + + /* Copy the list of group memberships to the end of the extra data */ + + if (group_list_ndx == 0) + goto done; + + state->response.extra_data.data = SMB_REALLOC( + state->response.extra_data.data, + group_list_ndx * sizeof(struct winbindd_gr) + gr_mem_list_len); + + if (!state->response.extra_data.data) { + DEBUG(0, ("out of memory\n")); + group_list_ndx = 0; + SAFE_FREE(gr_mem_list); + request_error(state); + return; + } + + memcpy(&((char *)state->response.extra_data.data) + [group_list_ndx * sizeof(struct winbindd_gr)], + gr_mem_list, gr_mem_list_len); + + state->response.length += gr_mem_list_len; + + DEBUG(10, ("returning %d groups, length = %d\n", + group_list_ndx, gr_mem_list_len)); + + /* Out of domains */ + + done: + + SAFE_FREE(gr_mem_list); + + if (group_list_ndx > 0) + request_ok(state); + else + request_error(state); +} + +/* List domain groups without mapping to unix ids */ + +void winbindd_list_groups(struct winbindd_cli_state *state) +{ + uint32 total_entries = 0; + struct winbindd_domain *domain; + const char *which_domain; + char *extra_data = NULL; + unsigned int extra_data_len = 0, i; + + DEBUG(3, ("[%5lu]: list groups\n", (unsigned long)state->pid)); + + /* Ensure null termination */ + state->request.domain_name[sizeof(state->request.domain_name)-1]='\0'; + which_domain = state->request.domain_name; + + /* Enumerate over trusted domains */ + + for (domain = domain_list(); domain; domain = domain->next) { + struct getent_state groups; + + /* if we have a domain name restricting the request and this + one in the list doesn't match, then just bypass the remainder + of the loop */ + + if ( *which_domain && !strequal(which_domain, domain->name) ) + continue; + + ZERO_STRUCT(groups); + + /* Get list of sam groups */ + + fstrcpy(groups.domain_name, domain->name); + + get_sam_group_entries(&groups); + + if (groups.num_sam_entries == 0) { + /* this domain is empty or in an error state */ + continue; + } + + /* keep track the of the total number of groups seen so + far over all domains */ + total_entries += groups.num_sam_entries; + + /* Allocate some memory for extra data. Note that we limit + account names to sizeof(fstring) = 128 characters. */ + extra_data = (char *)SMB_REALLOC( + extra_data, sizeof(fstring) * total_entries); + + if (!extra_data) { + DEBUG(0,("failed to enlarge buffer!\n")); + request_error(state); + return; + } + + /* Pack group list into extra data fields */ + for (i = 0; i < groups.num_sam_entries; i++) { + char *group_name = ((struct acct_info *) + groups.sam_entries)[i].acct_name; + fstring name; + + fill_domain_username(name, domain->name, group_name, True); + /* Append to extra data */ + memcpy(&extra_data[extra_data_len], name, + strlen(name)); + extra_data_len += strlen(name); + extra_data[extra_data_len++] = ','; + } + + SAFE_FREE(groups.sam_entries); + } + + /* Assign extra_data fields in response structure */ + if (extra_data) { + extra_data[extra_data_len - 1] = '\0'; + state->response.extra_data.data = extra_data; + state->response.length += extra_data_len; + } + + /* No domains may have responded but that's still OK so don't + return an error. */ + + request_ok(state); +} + +/* Get user supplementary groups. This is much quicker than trying to + invert the groups database. We merge the groups from the gids and + other_sids info3 fields as trusted domain, universal group + memberships, and nested groups (win2k native mode only) are not + returned by the getgroups RPC call but are present in the info3. */ + +struct getgroups_state { + struct winbindd_cli_state *state; + struct winbindd_domain *domain; + char *domname; + char *username; + DOM_SID user_sid; + + const DOM_SID *token_sids; + size_t i, num_token_sids; + + gid_t *token_gids; + size_t num_token_gids; +}; + +static void getgroups_usersid_recv(void *private_data, BOOL success, + const DOM_SID *sid, enum lsa_SidType type); +static void getgroups_tokensids_recv(void *private_data, BOOL success, + DOM_SID *token_sids, size_t num_token_sids); +static void getgroups_sid2gid_recv(void *private_data, BOOL success, gid_t gid); + +void winbindd_getgroups(struct winbindd_cli_state *state) +{ + struct getgroups_state *s; + + /* Ensure null termination */ + state->request.data.username + [sizeof(state->request.data.username)-1]='\0'; + + DEBUG(3, ("[%5lu]: getgroups %s\n", (unsigned long)state->pid, + state->request.data.username)); + + /* Parse domain and username */ + + s = TALLOC_P(state->mem_ctx, struct getgroups_state); + if (s == NULL) { + DEBUG(0, ("talloc failed\n")); + request_error(state); + return; + } + + s->state = state; + + ws_name_return( state->request.data.username, WB_REPLACE_CHAR ); + + if (!parse_domain_user_talloc(state->mem_ctx, + state->request.data.username, + &s->domname, &s->username)) { + DEBUG(5, ("Could not parse domain user: %s\n", + state->request.data.username)); + + /* error out if we do not have nested group support */ + + if ( !lp_winbind_nested_groups() ) { + request_error(state); + return; + } + + s->domname = talloc_strdup( state->mem_ctx, get_global_sam_name() ); + s->username = talloc_strdup( state->mem_ctx, state->request.data.username ); + } + + /* Get info for the domain */ + + s->domain = find_domain_from_name_noinit(s->domname); + + if (s->domain == NULL) { + DEBUG(7, ("could not find domain entry for domain %s\n", + s->domname)); + request_error(state); + return; + } + + if ( s->domain->primary && lp_winbind_trusted_domains_only()) { + DEBUG(7,("winbindd_getgroups: My domain -- rejecting " + "getgroups() for %s\\%s.\n", s->domname, + s->username)); + request_error(state); + return; + } + + /* Get rid and name type from name. The following costs 1 packet */ + + winbindd_lookupname_async(state->mem_ctx, s->domname, s->username, + getgroups_usersid_recv, WINBINDD_GETGROUPS, s); +} + +static void getgroups_usersid_recv(void *private_data, BOOL success, + const DOM_SID *sid, enum lsa_SidType type) +{ + struct getgroups_state *s = + (struct getgroups_state *)private_data; + + if ((!success) || + ((type != SID_NAME_USER) && (type != SID_NAME_COMPUTER))) { + request_error(s->state); + return; + } + + sid_copy(&s->user_sid, sid); + + winbindd_gettoken_async(s->state->mem_ctx, &s->user_sid, + getgroups_tokensids_recv, s); +} + +static void getgroups_tokensids_recv(void *private_data, BOOL success, + DOM_SID *token_sids, size_t num_token_sids) +{ + struct getgroups_state *s = + (struct getgroups_state *)private_data; + + /* We need at least the user sid and the primary group in the token, + * otherwise it's an error */ + + if ((!success) || (num_token_sids < 2)) { + request_error(s->state); + return; + } + + s->token_sids = token_sids; + s->num_token_sids = num_token_sids; + s->i = 0; + + s->token_gids = NULL; + s->num_token_gids = 0; + + getgroups_sid2gid_recv(s, False, 0); +} + +static void getgroups_sid2gid_recv(void *private_data, BOOL success, gid_t gid) +{ + struct getgroups_state *s = + (struct getgroups_state *)private_data; + + if (success) { + if (!add_gid_to_array_unique(s->state->mem_ctx, gid, + &s->token_gids, + &s->num_token_gids)) { + return; + } + } + + if (s->i < s->num_token_sids) { + const DOM_SID *sid = &s->token_sids[s->i]; + s->i += 1; + + if (sid_equal(sid, &s->user_sid)) { + getgroups_sid2gid_recv(s, False, 0); + return; + } + + winbindd_sid2gid_async(s->state->mem_ctx, sid, + getgroups_sid2gid_recv, s); + return; + } + + s->state->response.data.num_entries = s->num_token_gids; + /* s->token_gids are talloced */ + s->state->response.extra_data.data = smb_xmemdup(s->token_gids, s->num_token_gids * sizeof(gid_t)); + s->state->response.length += s->num_token_gids * sizeof(gid_t); + request_ok(s->state); +} + +/* Get user supplementary sids. This is equivalent to the + winbindd_getgroups() function but it involves a SID->SIDs mapping + rather than a NAME->SID->SIDS->GIDS mapping, which means we avoid + idmap. This call is designed to be used with applications that need + to do ACL evaluation themselves. Note that the cached info3 data is + not used + + this function assumes that the SID that comes in is a user SID. If + you pass in another type of SID then you may get unpredictable + results. +*/ + +static void getusersids_recv(void *private_data, BOOL success, DOM_SID *sids, + size_t num_sids); + +void winbindd_getusersids(struct winbindd_cli_state *state) +{ + DOM_SID *user_sid; + + /* Ensure null termination */ + state->request.data.sid[sizeof(state->request.data.sid)-1]='\0'; + + user_sid = TALLOC_P(state->mem_ctx, DOM_SID); + if (user_sid == NULL) { + DEBUG(1, ("talloc failed\n")); + request_error(state); + return; + } + + if (!string_to_sid(user_sid, state->request.data.sid)) { + DEBUG(1, ("Could not get convert sid %s from string\n", + state->request.data.sid)); + request_error(state); + return; + } + + winbindd_gettoken_async(state->mem_ctx, user_sid, getusersids_recv, + state); +} + +static void getusersids_recv(void *private_data, BOOL success, DOM_SID *sids, + size_t num_sids) +{ + struct winbindd_cli_state *state = + (struct winbindd_cli_state *)private_data; + char *ret = NULL; + unsigned ofs, ret_size = 0; + size_t i; + + if (!success) { + request_error(state); + return; + } + + /* work out the response size */ + for (i = 0; i < num_sids; i++) { + const char *s = sid_string_static(&sids[i]); + ret_size += strlen(s) + 1; + } + + /* build the reply */ + ret = (char *)SMB_MALLOC(ret_size); + if (!ret) { + DEBUG(0, ("malloc failed\n")); + request_error(state); + return; + } + ofs = 0; + for (i = 0; i < num_sids; i++) { + const char *s = sid_string_static(&sids[i]); + safe_strcpy(ret + ofs, s, ret_size - ofs - 1); + ofs += strlen(ret+ofs) + 1; + } + + /* Send data back to client */ + state->response.data.num_entries = num_sids; + state->response.extra_data.data = ret; + state->response.length += ret_size; + request_ok(state); +} + +void winbindd_getuserdomgroups(struct winbindd_cli_state *state) +{ + DOM_SID user_sid; + struct winbindd_domain *domain; + + /* Ensure null termination */ + state->request.data.sid[sizeof(state->request.data.sid)-1]='\0'; + + if (!string_to_sid(&user_sid, state->request.data.sid)) { + DEBUG(1, ("Could not get convert sid %s from string\n", + state->request.data.sid)); + request_error(state); + return; + } + + /* Get info for the domain */ + if ((domain = find_domain_from_sid_noinit(&user_sid)) == NULL) { + DEBUG(0,("could not find domain entry for sid %s\n", + sid_string_static(&user_sid))); + request_error(state); + return; + } + + sendto_domain(state, domain); +} + +enum winbindd_result winbindd_dual_getuserdomgroups(struct winbindd_domain *domain, + struct winbindd_cli_state *state) +{ + DOM_SID user_sid; + NTSTATUS status; + + char *sidstring; + ssize_t len; + DOM_SID *groups; + uint32 num_groups; + + /* Ensure null termination */ + state->request.data.sid[sizeof(state->request.data.sid)-1]='\0'; + + if (!string_to_sid(&user_sid, state->request.data.sid)) { + DEBUG(1, ("Could not get convert sid %s from string\n", + state->request.data.sid)); + return WINBINDD_ERROR; + } + + status = domain->methods->lookup_usergroups(domain, state->mem_ctx, + &user_sid, &num_groups, + &groups); + if (!NT_STATUS_IS_OK(status)) + return WINBINDD_ERROR; + + if (num_groups == 0) { + state->response.data.num_entries = 0; + state->response.extra_data.data = NULL; + return WINBINDD_OK; + } + + if (!print_sidlist(state->mem_ctx, groups, num_groups, &sidstring, &len)) { + DEBUG(0, ("talloc failed\n")); + return WINBINDD_ERROR; + } + + state->response.extra_data.data = SMB_STRDUP(sidstring); + if (!state->response.extra_data.data) { + return WINBINDD_ERROR; + } + state->response.length += len+1; + state->response.data.num_entries = num_groups; + + return WINBINDD_OK; +} diff --git a/source3/winbindd/winbindd_misc.c b/source3/winbindd/winbindd_misc.c new file mode 100644 index 0000000000..9c3f634534 --- /dev/null +++ b/source3/winbindd/winbindd_misc.c @@ -0,0 +1,634 @@ +/* + Unix SMB/CIFS implementation. + + Winbind daemon - miscellaneous other functions + + Copyright (C) Tim Potter 2000 + Copyright (C) Andrew Bartlett 2002 + + 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 3 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, see . +*/ + +#include "includes.h" +#include "winbindd.h" + +#undef DBGC_CLASS +#define DBGC_CLASS DBGC_WINBIND + +/* Check the machine account password is valid */ + +void winbindd_check_machine_acct(struct winbindd_cli_state *state) +{ + DEBUG(3, ("[%5lu]: check machine account\n", + (unsigned long)state->pid)); + + sendto_domain(state, find_our_domain()); +} + +enum winbindd_result winbindd_dual_check_machine_acct(struct winbindd_domain *domain, + struct winbindd_cli_state *state) +{ + NTSTATUS result = NT_STATUS_UNSUCCESSFUL; + int num_retries = 0; + struct winbindd_domain *contact_domain; + + DEBUG(3, ("[%5lu]: check machine account\n", (unsigned long)state->pid)); + + /* Get trust account password */ + + again: + + contact_domain = find_our_domain(); + + /* This call does a cli_nt_setup_creds() which implicitly checks + the trust account password. */ + + invalidate_cm_connection(&contact_domain->conn); + + { + struct rpc_pipe_client *netlogon_pipe; + result = cm_connect_netlogon(contact_domain, &netlogon_pipe); + } + + if (!NT_STATUS_IS_OK(result)) { + DEBUG(3, ("could not open handle to NETLOGON pipe\n")); + goto done; + } + + /* There is a race condition between fetching the trust account + password and the periodic machine password change. So it's + possible that the trust account password has been changed on us. + We are returned NT_STATUS_ACCESS_DENIED if this happens. */ + +#define MAX_RETRIES 8 + + if ((num_retries < MAX_RETRIES) && + NT_STATUS_V(result) == NT_STATUS_V(NT_STATUS_ACCESS_DENIED)) { + num_retries++; + goto again; + } + + /* Pass back result code - zero for success, other values for + specific failures. */ + + DEBUG(3, ("secret is %s\n", NT_STATUS_IS_OK(result) ? + "good" : "bad")); + + done: + state->response.data.auth.nt_status = NT_STATUS_V(result); + fstrcpy(state->response.data.auth.nt_status_string, nt_errstr(result)); + fstrcpy(state->response.data.auth.error_string, nt_errstr(result)); + state->response.data.auth.pam_error = nt_status_to_pam(result); + + DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2, ("Checking the trust account password returned %s\n", + state->response.data.auth.nt_status_string)); + + return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR; +} + +void winbindd_list_trusted_domains(struct winbindd_cli_state *state) +{ + struct winbindd_domain *d = NULL; + int extra_data_len = 0; + char *extra_data = NULL; + + DEBUG(3, ("[%5lu]: list trusted domains\n", + (unsigned long)state->pid)); + + for ( d=domain_list(); d; d=d->next ) { + if ( !extra_data ) { + extra_data = talloc_asprintf(state->mem_ctx, "%s\\%s\\%s", + d->name, + d->alt_name ? d->alt_name : d->name, + sid_string_static(&d->sid)); + } else { + extra_data = talloc_asprintf(state->mem_ctx, "%s\n%s\\%s\\%s", + extra_data, + d->name, + d->alt_name ? d->alt_name : d->name, + sid_string_static(&d->sid)); + } + } + + extra_data_len = 0; + if (extra_data != NULL) { + extra_data_len = strlen(extra_data); + } + + if (extra_data_len > 0) { + state->response.extra_data.data = SMB_STRDUP(extra_data); + state->response.length += extra_data_len+1; + } + + TALLOC_FREE( extra_data ); + + request_ok(state); +} + +enum winbindd_result winbindd_dual_list_trusted_domains(struct winbindd_domain *domain, + struct winbindd_cli_state *state) +{ + uint32 i, num_domains; + char **names, **alt_names; + DOM_SID *sids; + int extra_data_len = 0; + char *extra_data; + NTSTATUS result; + BOOL have_own_domain = False; + + DEBUG(3, ("[%5lu]: list trusted domains\n", + (unsigned long)state->pid)); + + result = domain->methods->trusted_domains(domain, state->mem_ctx, + &num_domains, &names, + &alt_names, &sids); + + if (!NT_STATUS_IS_OK(result)) { + DEBUG(3, ("winbindd_dual_list_trusted_domains: trusted_domains returned %s\n", + nt_errstr(result) )); + return WINBINDD_ERROR; + } + + extra_data = talloc_strdup(state->mem_ctx, ""); + + if (num_domains > 0) + extra_data = talloc_asprintf(state->mem_ctx, "%s\\%s\\%s", + names[0], + alt_names[0] ? alt_names[0] : names[0], + sid_string_static(&sids[0])); + + for (i=1; imem_ctx, "%s\n%s\\%s\\%s", + extra_data, + names[i], + alt_names[i] ? alt_names[i] : names[i], + sid_string_static(&sids[i])); + /* add our primary domain */ + + for (i=0; iname)) { + have_own_domain = True; + break; + } + } + + if (state->request.data.list_all_domains && !have_own_domain) { + extra_data = talloc_asprintf(state->mem_ctx, "%s\n%s\\%s\\%s", + extra_data, + domain->name, + domain->alt_name ? domain->alt_name : domain->name, + sid_string_static(&domain->sid)); + } + + /* This is a bit excessive, but the extra data sooner or later will be + talloc'ed */ + + extra_data_len = 0; + if (extra_data != NULL) { + extra_data_len = strlen(extra_data); + } + + if (extra_data_len > 0) { + state->response.extra_data.data = SMB_STRDUP(extra_data); + state->response.length += extra_data_len+1; + } + + return WINBINDD_OK; +} + +void winbindd_getdcname(struct winbindd_cli_state *state) +{ + state->request.domain_name + [sizeof(state->request.domain_name)-1] = '\0'; + + DEBUG(3, ("[%5lu]: Get DC name for %s\n", (unsigned long)state->pid, + state->request.domain_name)); + + sendto_domain(state, find_our_domain()); +} + +enum winbindd_result winbindd_dual_getdcname(struct winbindd_domain *domain, + struct winbindd_cli_state *state) +{ + fstring dcname_slash; + char *p; + struct rpc_pipe_client *netlogon_pipe; + NTSTATUS result; + WERROR werr; + unsigned int orig_timeout; + + state->request.domain_name + [sizeof(state->request.domain_name)-1] = '\0'; + + DEBUG(3, ("[%5lu]: Get DC name for %s\n", (unsigned long)state->pid, + state->request.domain_name)); + + result = cm_connect_netlogon(domain, &netlogon_pipe); + + if (!NT_STATUS_IS_OK(result)) { + DEBUG(1, ("Can't contact the NETLOGON pipe\n")); + return WINBINDD_ERROR; + } + + /* This call can take a long time - allow the server to time out. + 35 seconds should do it. */ + + orig_timeout = cli_set_timeout(netlogon_pipe->cli, 35000); + + werr = rpccli_netlogon_getanydcname(netlogon_pipe, state->mem_ctx, domain->dcname, + state->request.domain_name, + dcname_slash); + /* And restore our original timeout. */ + cli_set_timeout(netlogon_pipe->cli, orig_timeout); + + if (!W_ERROR_IS_OK(werr)) { + DEBUG(5, ("Error requesting DCname for domain %s: %s\n", + state->request.domain_name, dos_errstr(werr))); + return WINBINDD_ERROR; + } + + p = dcname_slash; + if (*p == '\\') { + p+=1; + } + if (*p == '\\') { + p+=1; + } + + fstrcpy(state->response.data.dc_name, p); + return WINBINDD_OK; +} + +static struct winbindd_child static_locator_child; + +void init_locator_child(void) +{ + setup_domain_child(NULL, &static_locator_child, "locator"); +} + +struct winbindd_child *locator_child(void) +{ + return &static_locator_child; +} + +void winbindd_dsgetdcname(struct winbindd_cli_state *state) +{ + state->request.domain_name + [sizeof(state->request.domain_name)-1] = '\0'; + + DEBUG(3, ("[%5lu]: DsGetDcName for %s\n", (unsigned long)state->pid, + state->request.domain_name)); + + sendto_child(state, locator_child()); +} + +enum winbindd_result winbindd_dual_dsgetdcname(struct winbindd_domain *domain, + struct winbindd_cli_state *state) +{ + NTSTATUS result; + struct DS_DOMAIN_CONTROLLER_INFO *info = NULL; + const char *dc = NULL; + + state->request.domain_name + [sizeof(state->request.domain_name)-1] = '\0'; + + DEBUG(3, ("[%5lu]: DsGetDcName for %s\n", (unsigned long)state->pid, + state->request.domain_name)); + + result = DsGetDcName(state->mem_ctx, NULL, state->request.domain_name, + NULL, NULL, state->request.flags, &info); + + if (!NT_STATUS_IS_OK(result)) { + return WINBINDD_ERROR; + } + + if (info->domain_controller_address) { + dc = info->domain_controller_address; + if ((dc[0] == '\\') && (dc[1] == '\\')) { + dc += 2; + } + } + + if ((!dc || !is_ipaddress(dc)) && info->domain_controller_name) { + dc = info->domain_controller_name; + } + + if (!dc || !*dc) { + return WINBINDD_ERROR; + } + + fstrcpy(state->response.data.dc_name, dc); + + return WINBINDD_OK; +} + + +struct sequence_state { + TALLOC_CTX *mem_ctx; + struct winbindd_cli_state *cli_state; + struct winbindd_domain *domain; + struct winbindd_request *request; + struct winbindd_response *response; + char *extra_data; +}; + +static void sequence_recv(void *private_data, BOOL success); + +void winbindd_show_sequence(struct winbindd_cli_state *state) +{ + struct sequence_state *seq; + + /* Ensure null termination */ + state->request.domain_name[sizeof(state->request.domain_name)-1]='\0'; + + if (strlen(state->request.domain_name) > 0) { + struct winbindd_domain *domain; + domain = find_domain_from_name_noinit( + state->request.domain_name); + if (domain == NULL) { + request_error(state); + return; + } + sendto_domain(state, domain); + return; + } + + /* Ask all domains in sequence, collect the results in sequence_recv */ + + seq = TALLOC_P(state->mem_ctx, struct sequence_state); + if (seq == NULL) { + DEBUG(0, ("talloc failed\n")); + request_error(state); + return; + } + + seq->mem_ctx = state->mem_ctx; + seq->cli_state = state; + seq->domain = domain_list(); + if (seq->domain == NULL) { + DEBUG(0, ("domain list empty\n")); + request_error(state); + return; + } + seq->request = TALLOC_ZERO_P(state->mem_ctx, + struct winbindd_request); + seq->response = TALLOC_ZERO_P(state->mem_ctx, + struct winbindd_response); + seq->extra_data = talloc_strdup(state->mem_ctx, ""); + + if ((seq->request == NULL) || (seq->response == NULL) || + (seq->extra_data == NULL)) { + DEBUG(0, ("talloc failed\n")); + request_error(state); + return; + } + + seq->request->length = sizeof(*seq->request); + seq->request->cmd = WINBINDD_SHOW_SEQUENCE; + fstrcpy(seq->request->domain_name, seq->domain->name); + + async_domain_request(state->mem_ctx, seq->domain, + seq->request, seq->response, + sequence_recv, seq); +} + +static void sequence_recv(void *private_data, BOOL success) +{ + struct sequence_state *state = + (struct sequence_state *)private_data; + uint32 seq = DOM_SEQUENCE_NONE; + + if ((success) && (state->response->result == WINBINDD_OK)) + seq = state->response->data.domain_info.sequence_number; + + if (seq == DOM_SEQUENCE_NONE) { + state->extra_data = talloc_asprintf(state->mem_ctx, + "%s%s : DISCONNECTED\n", + state->extra_data, + state->domain->name); + } else { + state->extra_data = talloc_asprintf(state->mem_ctx, + "%s%s : %d\n", + state->extra_data, + state->domain->name, seq); + } + + state->domain->sequence_number = seq; + + state->domain = state->domain->next; + + if (state->domain == NULL) { + struct winbindd_cli_state *cli_state = state->cli_state; + cli_state->response.length = + sizeof(cli_state->response) + + strlen(state->extra_data) + 1; + cli_state->response.extra_data.data = + SMB_STRDUP(state->extra_data); + request_ok(cli_state); + return; + } + + /* Ask the next domain */ + fstrcpy(state->request->domain_name, state->domain->name); + async_domain_request(state->mem_ctx, state->domain, + state->request, state->response, + sequence_recv, state); +} + +/* This is the child-only version of --sequence. It only allows for a single + * domain (ie "our" one) to be displayed. */ + +enum winbindd_result winbindd_dual_show_sequence(struct winbindd_domain *domain, + struct winbindd_cli_state *state) +{ + DEBUG(3, ("[%5lu]: show sequence\n", (unsigned long)state->pid)); + + /* Ensure null termination */ + state->request.domain_name[sizeof(state->request.domain_name)-1]='\0'; + + domain->methods->sequence_number(domain, &domain->sequence_number); + + state->response.data.domain_info.sequence_number = + domain->sequence_number; + + return WINBINDD_OK; +} + +struct domain_info_state { + struct winbindd_domain *domain; + struct winbindd_cli_state *cli_state; +}; + +static void domain_info_init_recv(void *private_data, BOOL success); + +void winbindd_domain_info(struct winbindd_cli_state *state) +{ + struct winbindd_domain *domain; + + DEBUG(3, ("[%5lu]: domain_info [%s]\n", (unsigned long)state->pid, + state->request.domain_name)); + + domain = find_domain_from_name_noinit(state->request.domain_name); + + if (domain == NULL) { + DEBUG(3, ("Did not find domain [%s]\n", + state->request.domain_name)); + request_error(state); + return; + } + + if (!domain->initialized) { + struct domain_info_state *istate; + + istate = TALLOC_P(state->mem_ctx, struct domain_info_state); + if (istate == NULL) { + DEBUG(0, ("talloc failed\n")); + request_error(state); + return; + } + + istate->cli_state = state; + istate->domain = domain; + + init_child_connection(domain, domain_info_init_recv, istate); + + return; + } + + fstrcpy(state->response.data.domain_info.name, + domain->name); + fstrcpy(state->response.data.domain_info.alt_name, + domain->alt_name); + fstrcpy(state->response.data.domain_info.sid, + sid_string_static(&domain->sid)); + + state->response.data.domain_info.native_mode = + domain->native_mode; + state->response.data.domain_info.active_directory = + domain->active_directory; + state->response.data.domain_info.primary = + domain->primary; + state->response.data.domain_info.sequence_number = + domain->sequence_number; + + request_ok(state); +} + +static void domain_info_init_recv(void *private_data, BOOL success) +{ + struct domain_info_state *istate = + (struct domain_info_state *)private_data; + struct winbindd_cli_state *state = istate->cli_state; + struct winbindd_domain *domain = istate->domain; + + DEBUG(10, ("Got back from child init: %d\n", success)); + + if ((!success) || (!domain->initialized)) { + DEBUG(5, ("Could not init child for domain %s\n", + domain->name)); + request_error(state); + return; + } + + fstrcpy(state->response.data.domain_info.name, + domain->name); + fstrcpy(state->response.data.domain_info.alt_name, + domain->alt_name); + fstrcpy(state->response.data.domain_info.sid, + sid_string_static(&domain->sid)); + + state->response.data.domain_info.native_mode = + domain->native_mode; + state->response.data.domain_info.active_directory = + domain->active_directory; + state->response.data.domain_info.primary = + domain->primary; + state->response.data.domain_info.sequence_number = + domain->sequence_number; + + request_ok(state); +} + +void winbindd_ping(struct winbindd_cli_state *state) +{ + DEBUG(3, ("[%5lu]: ping\n", (unsigned long)state->pid)); + request_ok(state); +} + +/* List various tidbits of information */ + +void winbindd_info(struct winbindd_cli_state *state) +{ + + DEBUG(3, ("[%5lu]: request misc info\n", (unsigned long)state->pid)); + + state->response.data.info.winbind_separator = *lp_winbind_separator(); + fstrcpy(state->response.data.info.samba_version, SAMBA_VERSION_STRING); + request_ok(state); +} + +/* Tell the client the current interface version */ + +void winbindd_interface_version(struct winbindd_cli_state *state) +{ + DEBUG(3, ("[%5lu]: request interface version\n", + (unsigned long)state->pid)); + + state->response.data.interface_version = WINBIND_INTERFACE_VERSION; + request_ok(state); +} + +/* What domain are we a member of? */ + +void winbindd_domain_name(struct winbindd_cli_state *state) +{ + DEBUG(3, ("[%5lu]: request domain name\n", (unsigned long)state->pid)); + + fstrcpy(state->response.data.domain_name, lp_workgroup()); + request_ok(state); +} + +/* What's my name again? */ + +void winbindd_netbios_name(struct winbindd_cli_state *state) +{ + DEBUG(3, ("[%5lu]: request netbios name\n", + (unsigned long)state->pid)); + + fstrcpy(state->response.data.netbios_name, global_myname()); + request_ok(state); +} + +/* Where can I find the privilaged pipe? */ + +void winbindd_priv_pipe_dir(struct winbindd_cli_state *state) +{ + + DEBUG(3, ("[%5lu]: request location of privileged pipe\n", + (unsigned long)state->pid)); + + state->response.extra_data.data = SMB_STRDUP(get_winbind_priv_pipe_dir()); + if (!state->response.extra_data.data) { + DEBUG(0, ("malloc failed\n")); + request_error(state); + return; + } + + /* must add one to length to copy the 0 for string termination */ + state->response.length += + strlen((char *)state->response.extra_data.data) + 1; + + request_ok(state); +} + diff --git a/source3/winbindd/winbindd_pam.c b/source3/winbindd/winbindd_pam.c new file mode 100644 index 0000000000..50b24864b5 --- /dev/null +++ b/source3/winbindd/winbindd_pam.c @@ -0,0 +1,2382 @@ +/* + Unix SMB/CIFS implementation. + + Winbind daemon - pam auth funcions + + Copyright (C) Andrew Tridgell 2000 + Copyright (C) Tim Potter 2001 + Copyright (C) Andrew Bartlett 2001-2002 + Copyright (C) Guenther Deschner 2005 + + 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 3 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, see . +*/ + +#include "includes.h" +#include "winbindd.h" +#undef DBGC_CLASS +#define DBGC_CLASS DBGC_WINBIND + +static NTSTATUS append_info3_as_txt(TALLOC_CTX *mem_ctx, + struct winbindd_cli_state *state, + NET_USER_INFO_3 *info3) +{ + fstring str_sid; + + state->response.data.auth.info3.logon_time = + nt_time_to_unix(info3->logon_time); + state->response.data.auth.info3.logoff_time = + nt_time_to_unix(info3->logoff_time); + state->response.data.auth.info3.kickoff_time = + nt_time_to_unix(info3->kickoff_time); + state->response.data.auth.info3.pass_last_set_time = + nt_time_to_unix(info3->pass_last_set_time); + state->response.data.auth.info3.pass_can_change_time = + nt_time_to_unix(info3->pass_can_change_time); + state->response.data.auth.info3.pass_must_change_time = + nt_time_to_unix(info3->pass_must_change_time); + + state->response.data.auth.info3.logon_count = info3->logon_count; + state->response.data.auth.info3.bad_pw_count = info3->bad_pw_count; + + state->response.data.auth.info3.user_rid = info3->user_rid; + state->response.data.auth.info3.group_rid = info3->group_rid; + sid_to_string(str_sid, &(info3->dom_sid.sid)); + fstrcpy(state->response.data.auth.info3.dom_sid, str_sid); + + state->response.data.auth.info3.num_groups = info3->num_groups; + state->response.data.auth.info3.user_flgs = info3->user_flgs; + + state->response.data.auth.info3.acct_flags = info3->acct_flags; + state->response.data.auth.info3.num_other_sids = info3->num_other_sids; + + unistr2_to_ascii(state->response.data.auth.info3.user_name, + &info3->uni_user_name, -1); + unistr2_to_ascii(state->response.data.auth.info3.full_name, + &info3->uni_full_name, -1); + unistr2_to_ascii(state->response.data.auth.info3.logon_script, + &info3->uni_logon_script, -1); + unistr2_to_ascii(state->response.data.auth.info3.profile_path, + &info3->uni_profile_path, -1); + unistr2_to_ascii(state->response.data.auth.info3.home_dir, + &info3->uni_home_dir, -1); + unistr2_to_ascii(state->response.data.auth.info3.dir_drive, + &info3->uni_dir_drive, -1); + + unistr2_to_ascii(state->response.data.auth.info3.logon_srv, + &info3->uni_logon_srv, -1); + unistr2_to_ascii(state->response.data.auth.info3.logon_dom, + &info3->uni_logon_dom, -1); + + return NT_STATUS_OK; +} + +static NTSTATUS append_info3_as_ndr(TALLOC_CTX *mem_ctx, + struct winbindd_cli_state *state, + NET_USER_INFO_3 *info3) +{ + prs_struct ps; + uint32 size; + if (!prs_init(&ps, 256 /* Random, non-zero number */, mem_ctx, MARSHALL)) { + return NT_STATUS_NO_MEMORY; + } + if (!net_io_user_info3("", info3, &ps, 1, 3, False)) { + prs_mem_free(&ps); + return NT_STATUS_UNSUCCESSFUL; + } + + size = prs_data_size(&ps); + SAFE_FREE(state->response.extra_data.data); + state->response.extra_data.data = SMB_MALLOC(size); + if (!state->response.extra_data.data) { + prs_mem_free(&ps); + return NT_STATUS_NO_MEMORY; + } + memset( state->response.extra_data.data, '\0', size ); + prs_copy_all_data_out((char *)state->response.extra_data.data, &ps); + state->response.length += size; + prs_mem_free(&ps); + return NT_STATUS_OK; +} + +static NTSTATUS append_unix_username(TALLOC_CTX *mem_ctx, + struct winbindd_cli_state *state, + const NET_USER_INFO_3 *info3, + const char *name_domain, + const char *name_user) +{ + /* We've been asked to return the unix username, per + 'winbind use default domain' settings and the like */ + + fstring username_out; + const char *nt_username, *nt_domain; + + if (!(nt_domain = unistr2_tdup(mem_ctx, + &info3->uni_logon_dom))) { + /* If the server didn't give us one, just use the one + * we sent them */ + nt_domain = name_domain; + } + + if (!(nt_username = unistr2_tdup(mem_ctx, + &info3->uni_user_name))) { + /* If the server didn't give us one, just use the one + * we sent them */ + nt_username = name_user; + } + + fill_domain_username(username_out, nt_domain, nt_username, + True); + + DEBUG(5,("Setting unix username to [%s]\n", username_out)); + + SAFE_FREE(state->response.extra_data.data); + state->response.extra_data.data = SMB_STRDUP(username_out); + if (!state->response.extra_data.data) { + return NT_STATUS_NO_MEMORY; + } + state->response.length += + strlen((const char *)state->response.extra_data.data)+1; + + return NT_STATUS_OK; +} + +static NTSTATUS append_afs_token(TALLOC_CTX *mem_ctx, + struct winbindd_cli_state *state, + const NET_USER_INFO_3 *info3, + const char *name_domain, + const char *name_user) +{ + char *afsname = NULL; + char *cell; + + afsname = talloc_strdup(mem_ctx, lp_afs_username_map()); + if (afsname == NULL) { + return NT_STATUS_NO_MEMORY; + } + + afsname = talloc_string_sub(mem_ctx, + lp_afs_username_map(), + "%D", name_domain); + afsname = talloc_string_sub(mem_ctx, afsname, + "%u", name_user); + afsname = talloc_string_sub(mem_ctx, afsname, + "%U", name_user); + + { + DOM_SID user_sid; + fstring sidstr; + + sid_copy(&user_sid, &info3->dom_sid.sid); + sid_append_rid(&user_sid, info3->user_rid); + sid_to_string(sidstr, &user_sid); + afsname = talloc_string_sub(mem_ctx, afsname, + "%s", sidstr); + } + + if (afsname == NULL) { + return NT_STATUS_NO_MEMORY; + } + + strlower_m(afsname); + + DEBUG(10, ("Generating token for user %s\n", afsname)); + + cell = strchr(afsname, '@'); + + if (cell == NULL) { + return NT_STATUS_NO_MEMORY; + } + + *cell = '\0'; + cell += 1; + + /* Append an AFS token string */ + SAFE_FREE(state->response.extra_data.data); + state->response.extra_data.data = + afs_createtoken_str(afsname, cell); + + if (state->response.extra_data.data != NULL) { + state->response.length += + strlen((const char *)state->response.extra_data.data)+1; + } + + return NT_STATUS_OK; +} + +static NTSTATUS check_info3_in_group(TALLOC_CTX *mem_ctx, + NET_USER_INFO_3 *info3, + const char *group_sid) +/** + * Check whether a user belongs to a group or list of groups. + * + * @param mem_ctx talloc memory context. + * @param info3 user information, including group membership info. + * @param group_sid One or more groups , separated by commas. + * + * @return NT_STATUS_OK on success, + * NT_STATUS_LOGON_FAILURE if the user does not belong, + * or other NT_STATUS_IS_ERR(status) for other kinds of failure. + */ +{ + DOM_SID *require_membership_of_sid; + size_t num_require_membership_of_sid; + fstring req_sid; + const char *p; + DOM_SID sid; + size_t i; + struct nt_user_token *token; + NTSTATUS status; + + /* Parse the 'required group' SID */ + + if (!group_sid || !group_sid[0]) { + /* NO sid supplied, all users may access */ + return NT_STATUS_OK; + } + + if (!(token = TALLOC_ZERO_P(mem_ctx, struct nt_user_token))) { + DEBUG(0, ("talloc failed\n")); + return NT_STATUS_NO_MEMORY; + } + + num_require_membership_of_sid = 0; + require_membership_of_sid = NULL; + + p = group_sid; + + while (next_token(&p, req_sid, ",", sizeof(req_sid))) { + if (!string_to_sid(&sid, req_sid)) { + DEBUG(0, ("check_info3_in_group: could not parse %s " + "as a SID!", req_sid)); + return NT_STATUS_INVALID_PARAMETER; + } + + if (!add_sid_to_array(mem_ctx, &sid, + &require_membership_of_sid, + &num_require_membership_of_sid)) { + DEBUG(0, ("add_sid_to_array failed\n")); + return NT_STATUS_NO_MEMORY; + } + } + + status = sid_array_from_info3(mem_ctx, info3, + &token->user_sids, + &token->num_sids, + True); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + if (!NT_STATUS_IS_OK(status = add_aliases(get_global_sam_sid(), + token)) + || !NT_STATUS_IS_OK(status = add_aliases(&global_sid_Builtin, + token))) { + DEBUG(3, ("could not add aliases: %s\n", + nt_errstr(status))); + return status; + } + + debug_nt_user_token(DBGC_CLASS, 10, token); + + for (i=0; irequest.flags & WBFLAG_PAM_CONTACT_TRUSTDOM) { + domain = find_domain_from_name_noinit(domain_name); + if (domain == NULL) { + DEBUG(3, ("Authentication for domain [%s] skipped " + "as it is not a trusted domain\n", + domain_name)); + } else { + return domain; + } + } + + return find_our_domain(); +} + +static void set_auth_errors(struct winbindd_response *resp, NTSTATUS result) +{ + resp->data.auth.nt_status = NT_STATUS_V(result); + fstrcpy(resp->data.auth.nt_status_string, nt_errstr(result)); + + /* we might have given a more useful error above */ + if (*resp->data.auth.error_string == '\0') + fstrcpy(resp->data.auth.error_string, + get_friendly_nt_error_msg(result)); + resp->data.auth.pam_error = nt_status_to_pam(result); +} + +static NTSTATUS fillup_password_policy(struct winbindd_domain *domain, + struct winbindd_cli_state *state) +{ + struct winbindd_methods *methods; + NTSTATUS status = NT_STATUS_UNSUCCESSFUL; + SAM_UNK_INFO_1 password_policy; + + if ( !winbindd_can_contact_domain( domain ) ) { + DEBUG(5,("fillup_password_policy: No inbound trust to " + "contact domain %s\n", domain->name)); + return NT_STATUS_NOT_SUPPORTED; + } + + methods = domain->methods; + + status = methods->password_policy(domain, state->mem_ctx, &password_policy); + if (NT_STATUS_IS_ERR(status)) { + return status; + } + + state->response.data.auth.policy.min_length_password = + password_policy.min_length_password; + state->response.data.auth.policy.password_history = + password_policy.password_history; + state->response.data.auth.policy.password_properties = + password_policy.password_properties; + state->response.data.auth.policy.expire = + nt_time_to_unix_abs(&(password_policy.expire)); + state->response.data.auth.policy.min_passwordage = + nt_time_to_unix_abs(&(password_policy.min_passwordage)); + + return NT_STATUS_OK; +} + +static NTSTATUS get_max_bad_attempts_from_lockout_policy(struct winbindd_domain *domain, + TALLOC_CTX *mem_ctx, + uint16 *max_allowed_bad_attempts) +{ + struct winbindd_methods *methods; + NTSTATUS status = NT_STATUS_UNSUCCESSFUL; + SAM_UNK_INFO_12 lockout_policy; + + *max_allowed_bad_attempts = 0; + + methods = domain->methods; + + status = methods->lockout_policy(domain, mem_ctx, &lockout_policy); + if (NT_STATUS_IS_ERR(status)) { + return status; + } + + *max_allowed_bad_attempts = lockout_policy.bad_attempt_lockout; + + return NT_STATUS_OK; +} + +static NTSTATUS get_pwd_properties(struct winbindd_domain *domain, + TALLOC_CTX *mem_ctx, + uint32 *password_properties) +{ + struct winbindd_methods *methods; + NTSTATUS status = NT_STATUS_UNSUCCESSFUL; + SAM_UNK_INFO_1 password_policy; + + *password_properties = 0; + + methods = domain->methods; + + status = methods->password_policy(domain, mem_ctx, &password_policy); + if (NT_STATUS_IS_ERR(status)) { + return status; + } + + *password_properties = password_policy.password_properties; + + return NT_STATUS_OK; +} + +#ifdef HAVE_KRB5 + +static const char *generate_krb5_ccache(TALLOC_CTX *mem_ctx, + const char *type, + uid_t uid, + BOOL *internal_ccache) +{ + /* accept FILE and WRFILE as krb5_cc_type from the client and then + * build the full ccname string based on the user's uid here - + * Guenther*/ + + const char *gen_cc = NULL; + + *internal_ccache = True; + + if (uid == -1) { + goto memory_ccache; + } + + if (!type || type[0] == '\0') { + goto memory_ccache; + } + + if (strequal(type, "FILE")) { + gen_cc = talloc_asprintf(mem_ctx, "FILE:/tmp/krb5cc_%d", uid); + } else if (strequal(type, "WRFILE")) { + gen_cc = talloc_asprintf(mem_ctx, "WRFILE:/tmp/krb5cc_%d", uid); + } else { + DEBUG(10,("we don't allow to set a %s type ccache\n", type)); + goto memory_ccache; + } + + *internal_ccache = False; + goto done; + + memory_ccache: + gen_cc = talloc_strdup(mem_ctx, "MEMORY:winbindd_pam_ccache"); + + done: + if (gen_cc == NULL) { + DEBUG(0,("out of memory\n")); + return NULL; + } + + DEBUG(10,("using ccache: %s %s\n", gen_cc, *internal_ccache ? "(internal)":"")); + + return gen_cc; +} + +static void setup_return_cc_name(struct winbindd_cli_state *state, const char *cc) +{ + const char *type = state->request.data.auth.krb5_cc_type; + + state->response.data.auth.krb5ccname[0] = '\0'; + + if (type[0] == '\0') { + return; + } + + if (!strequal(type, "FILE") && + !strequal(type, "WRFILE")) { + DEBUG(10,("won't return krbccname for a %s type ccache\n", + type)); + return; + } + + fstrcpy(state->response.data.auth.krb5ccname, cc); +} + +#endif + +static uid_t get_uid_from_state(struct winbindd_cli_state *state) +{ + uid_t uid = -1; + + uid = state->request.data.auth.uid; + + if (uid < 0) { + DEBUG(1,("invalid uid: '%d'\n", uid)); + return -1; + } + return uid; +} + +/********************************************************************** + Authenticate a user with a clear text password using Kerberos and fill up + ccache if required + **********************************************************************/ + +static NTSTATUS winbindd_raw_kerberos_login(struct winbindd_domain *domain, + struct winbindd_cli_state *state, + NET_USER_INFO_3 **info3) +{ +#ifdef HAVE_KRB5 + NTSTATUS result = NT_STATUS_UNSUCCESSFUL; + krb5_error_code krb5_ret; + const char *cc = NULL; + const char *principal_s = NULL; + const char *service = NULL; + char *realm = NULL; + fstring name_domain, name_user; + time_t ticket_lifetime = 0; + time_t renewal_until = 0; + uid_t uid = -1; + ADS_STRUCT *ads; + time_t time_offset = 0; + BOOL internal_ccache = True; + + ZERO_STRUCTP(info3); + + *info3 = NULL; + + /* 1st step: + * prepare a krb5_cc_cache string for the user */ + + uid = get_uid_from_state(state); + if (uid == -1) { + DEBUG(0,("no valid uid\n")); + } + + cc = generate_krb5_ccache(state->mem_ctx, + state->request.data.auth.krb5_cc_type, + state->request.data.auth.uid, + &internal_ccache); + if (cc == NULL) { + return NT_STATUS_NO_MEMORY; + } + + + /* 2nd step: + * get kerberos properties */ + + if (domain->private_data) { + ads = (ADS_STRUCT *)domain->private_data; + time_offset = ads->auth.time_offset; + } + + + /* 3rd step: + * do kerberos auth and setup ccache as the user */ + + parse_domain_user(state->request.data.auth.user, name_domain, name_user); + + realm = domain->alt_name; + strupper_m(realm); + + principal_s = talloc_asprintf(state->mem_ctx, "%s@%s", name_user, realm); + if (principal_s == NULL) { + return NT_STATUS_NO_MEMORY; + } + + service = talloc_asprintf(state->mem_ctx, "%s/%s@%s", KRB5_TGS_NAME, realm, realm); + if (service == NULL) { + return NT_STATUS_NO_MEMORY; + } + + /* if this is a user ccache, we need to act as the user to let the krb5 + * library handle the chown, etc. */ + + /************************ ENTERING NON-ROOT **********************/ + + if (!internal_ccache) { + set_effective_uid(uid); + DEBUG(10,("winbindd_raw_kerberos_login: uid is %d\n", uid)); + } + + result = kerberos_return_info3_from_pac(state->mem_ctx, + principal_s, + state->request.data.auth.pass, + time_offset, + &ticket_lifetime, + &renewal_until, + cc, + True, + True, + WINBINDD_PAM_AUTH_KRB5_RENEW_TIME, + info3); + if (!internal_ccache) { + gain_root_privilege(); + } + + /************************ RETURNED TO ROOT **********************/ + + if (!NT_STATUS_IS_OK(result)) { + goto failed; + } + + DEBUG(10,("winbindd_raw_kerberos_login: winbindd validated ticket of %s\n", + principal_s)); + + /* if we had a user's ccache then return that string for the pam + * environment */ + + if (!internal_ccache) { + + setup_return_cc_name(state, cc); + + result = add_ccache_to_list(principal_s, + cc, + service, + state->request.data.auth.user, + realm, + uid, + time(NULL), + ticket_lifetime, + renewal_until, + False); + + if (!NT_STATUS_IS_OK(result)) { + DEBUG(10,("winbindd_raw_kerberos_login: failed to add ccache to list: %s\n", + nt_errstr(result))); + } + } else { + + /* need to delete the memory cred cache, it is not used anymore */ + + krb5_ret = ads_kdestroy(cc); + if (krb5_ret) { + DEBUG(3,("winbindd_raw_kerberos_login: " + "could not destroy krb5 credential cache: " + "%s\n", error_message(krb5_ret))); + } + + } + + return NT_STATUS_OK; + +failed: + + /* we could have created a new credential cache with a valid tgt in it + * but we werent able to get or verify the service ticket for this + * local host and therefor didn't get the PAC, we need to remove that + * cache entirely now */ + + krb5_ret = ads_kdestroy(cc); + if (krb5_ret) { + DEBUG(3,("winbindd_raw_kerberos_login: " + "could not destroy krb5 credential cache: " + "%s\n", error_message(krb5_ret))); + } + + if (!NT_STATUS_IS_OK(remove_ccache(state->request.data.auth.user))) { + DEBUG(3,("winbindd_raw_kerberos_login: " + "could not remove ccache for user %s\n", + state->request.data.auth.user)); + } + + return result; +#else + return NT_STATUS_NOT_SUPPORTED; +#endif /* HAVE_KRB5 */ +} + +/**************************************************************** +****************************************************************/ + +static BOOL check_request_flags(uint32_t flags) +{ + uint32_t flags_edata = WBFLAG_PAM_AFS_TOKEN | + WBFLAG_PAM_UNIX_NAME | + WBFLAG_PAM_INFO3_NDR; + + if ( ( (flags & flags_edata) == WBFLAG_PAM_AFS_TOKEN) || + ( (flags & flags_edata) == WBFLAG_PAM_INFO3_NDR) || + ( (flags & flags_edata) == WBFLAG_PAM_UNIX_NAME) || + !(flags & flags_edata) ) { + return True; + } + + DEBUG(1,("check_request_flags: invalid request flags\n")); + + return False; +} + +/**************************************************************** +****************************************************************/ + +static NTSTATUS append_data(struct winbindd_cli_state *state, + NET_USER_INFO_3 *info3, + const char *name_domain, + const char *name_user) +{ + NTSTATUS result; + uint32_t flags = state->request.flags; + + if (flags & WBFLAG_PAM_USER_SESSION_KEY) { + memcpy(state->response.data.auth.user_session_key, + info3->user_sess_key, + sizeof(state->response.data.auth.user_session_key) + /* 16 */); + } + + if (flags & WBFLAG_PAM_LMKEY) { + memcpy(state->response.data.auth.first_8_lm_hash, + info3->lm_sess_key, + sizeof(state->response.data.auth.first_8_lm_hash) + /* 8 */); + } + + if (flags & WBFLAG_PAM_INFO3_TEXT) { + result = append_info3_as_txt(state->mem_ctx, state, info3); + if (!NT_STATUS_IS_OK(result)) { + DEBUG(10,("Failed to append INFO3 (TXT): %s\n", + nt_errstr(result))); + return result; + } + } + + /* currently, anything from here on potentially overwrites extra_data. */ + + if (flags & WBFLAG_PAM_INFO3_NDR) { + result = append_info3_as_ndr(state->mem_ctx, state, info3); + if (!NT_STATUS_IS_OK(result)) { + DEBUG(10,("Failed to append INFO3 (NDR): %s\n", + nt_errstr(result))); + return result; + } + } + + if (flags & WBFLAG_PAM_UNIX_NAME) { + result = append_unix_username(state->mem_ctx, state, info3, + name_domain, name_user); + if (!NT_STATUS_IS_OK(result)) { + DEBUG(10,("Failed to append Unix Username: %s\n", + nt_errstr(result))); + return result; + } + } + + if (flags & WBFLAG_PAM_AFS_TOKEN) { + result = append_afs_token(state->mem_ctx, state, info3, + name_domain, name_user); + if (!NT_STATUS_IS_OK(result)) { + DEBUG(10,("Failed to append AFS token: %s\n", + nt_errstr(result))); + return result; + } + } + + return NT_STATUS_OK; +} + +void winbindd_pam_auth(struct winbindd_cli_state *state) +{ + struct winbindd_domain *domain; + fstring name_domain, name_user; + NTSTATUS result; + + /* Ensure null termination */ + state->request.data.auth.user + [sizeof(state->request.data.auth.user)-1]='\0'; + + /* Ensure null termination */ + state->request.data.auth.pass + [sizeof(state->request.data.auth.pass)-1]='\0'; + + DEBUG(3, ("[%5lu]: pam auth %s\n", (unsigned long)state->pid, + state->request.data.auth.user)); + + if (!check_request_flags(state->request.flags)) { + result = NT_STATUS_INVALID_PARAMETER_MIX; + goto done; + } + + /* Parse domain and username */ + + ws_name_return( state->request.data.auth.user, WB_REPLACE_CHAR ); + + if (!canonicalize_username(state->request.data.auth.user, + name_domain, name_user)) { + result = NT_STATUS_NO_SUCH_USER; + goto done; + } + + domain = find_auth_domain(state, name_domain); + + if (domain == NULL) { + result = NT_STATUS_NO_SUCH_USER; + goto done; + } + + sendto_domain(state, domain); + return; + done: + set_auth_errors(&state->response, result); + DEBUG(5, ("Plain text authentication for %s returned %s " + "(PAM: %d)\n", + state->request.data.auth.user, + state->response.data.auth.nt_status_string, + state->response.data.auth.pam_error)); + request_error(state); +} + +NTSTATUS winbindd_dual_pam_auth_cached(struct winbindd_domain *domain, + struct winbindd_cli_state *state, + NET_USER_INFO_3 **info3) +{ + NTSTATUS result = NT_STATUS_LOGON_FAILURE; + uint16 max_allowed_bad_attempts; + fstring name_domain, name_user; + DOM_SID sid; + enum lsa_SidType type; + uchar new_nt_pass[NT_HASH_LEN]; + const uint8 *cached_nt_pass; + const uint8 *cached_salt; + NET_USER_INFO_3 *my_info3; + time_t kickoff_time, must_change_time; + BOOL password_good = False; +#ifdef HAVE_KRB5 + struct winbindd_tdc_domain *tdc_domain = NULL; +#endif + + *info3 = NULL; + + ZERO_STRUCTP(info3); + + DEBUG(10,("winbindd_dual_pam_auth_cached\n")); + + /* Parse domain and username */ + + parse_domain_user(state->request.data.auth.user, name_domain, name_user); + + + if (!lookup_cached_name(state->mem_ctx, + name_domain, + name_user, + &sid, + &type)) { + DEBUG(10,("winbindd_dual_pam_auth_cached: no such user in the cache\n")); + return NT_STATUS_NO_SUCH_USER; + } + + if (type != SID_NAME_USER) { + DEBUG(10,("winbindd_dual_pam_auth_cached: not a user (%s)\n", sid_type_lookup(type))); + return NT_STATUS_LOGON_FAILURE; + } + + result = winbindd_get_creds(domain, + state->mem_ctx, + &sid, + &my_info3, + &cached_nt_pass, + &cached_salt); + if (!NT_STATUS_IS_OK(result)) { + DEBUG(10,("winbindd_dual_pam_auth_cached: failed to get creds: %s\n", nt_errstr(result))); + return result; + } + + *info3 = my_info3; + + E_md4hash(state->request.data.auth.pass, new_nt_pass); + + dump_data_pw("new_nt_pass", new_nt_pass, NT_HASH_LEN); + dump_data_pw("cached_nt_pass", cached_nt_pass, NT_HASH_LEN); + if (cached_salt) { + dump_data_pw("cached_salt", cached_salt, NT_HASH_LEN); + } + + if (cached_salt) { + /* In this case we didn't store the nt_hash itself, + but the MD5 combination of salt + nt_hash. */ + uchar salted_hash[NT_HASH_LEN]; + E_md5hash(cached_salt, new_nt_pass, salted_hash); + + password_good = (memcmp(cached_nt_pass, salted_hash, NT_HASH_LEN) == 0) ? + True : False; + } else { + /* Old cached cred - direct store of nt_hash (bad bad bad !). */ + password_good = (memcmp(cached_nt_pass, new_nt_pass, NT_HASH_LEN) == 0) ? + True : False; + } + + if (password_good) { + + /* User *DOES* know the password, update logon_time and reset + * bad_pw_count */ + + my_info3->user_flgs |= LOGON_CACHED_ACCOUNT; + + if (my_info3->acct_flags & ACB_AUTOLOCK) { + return NT_STATUS_ACCOUNT_LOCKED_OUT; + } + + if (my_info3->acct_flags & ACB_DISABLED) { + return NT_STATUS_ACCOUNT_DISABLED; + } + + if (my_info3->acct_flags & ACB_WSTRUST) { + return NT_STATUS_NOLOGON_WORKSTATION_TRUST_ACCOUNT; + } + + if (my_info3->acct_flags & ACB_SVRTRUST) { + return NT_STATUS_NOLOGON_SERVER_TRUST_ACCOUNT; + } + + if (my_info3->acct_flags & ACB_DOMTRUST) { + return NT_STATUS_NOLOGON_INTERDOMAIN_TRUST_ACCOUNT; + } + + if (!(my_info3->acct_flags & ACB_NORMAL)) { + DEBUG(0,("winbindd_dual_pam_auth_cached: whats wrong with that one?: 0x%08x\n", + my_info3->acct_flags)); + return NT_STATUS_LOGON_FAILURE; + } + + kickoff_time = nt_time_to_unix(my_info3->kickoff_time); + if (kickoff_time != 0 && time(NULL) > kickoff_time) { + return NT_STATUS_ACCOUNT_EXPIRED; + } + + must_change_time = nt_time_to_unix(my_info3->pass_must_change_time); + if (must_change_time != 0 && must_change_time < time(NULL)) { + /* we allow grace logons when the password has expired */ + my_info3->user_flgs |= LOGON_GRACE_LOGON; + /* return NT_STATUS_PASSWORD_EXPIRED; */ + goto success; + } + +#ifdef HAVE_KRB5 + if ((state->request.flags & WBFLAG_PAM_KRB5) && + ((tdc_domain = wcache_tdc_fetch_domain(state->mem_ctx, name_domain)) != NULL) && + (tdc_domain->trust_type & DS_DOMAIN_TRUST_TYPE_UPLEVEL)) { + + uid_t uid = -1; + const char *cc = NULL; + char *realm = NULL; + const char *principal_s = NULL; + const char *service = NULL; + BOOL internal_ccache = False; + + uid = get_uid_from_state(state); + if (uid == -1) { + DEBUG(0,("winbindd_dual_pam_auth_cached: invalid uid\n")); + return NT_STATUS_INVALID_PARAMETER; + } + + cc = generate_krb5_ccache(state->mem_ctx, + state->request.data.auth.krb5_cc_type, + state->request.data.auth.uid, + &internal_ccache); + if (cc == NULL) { + return NT_STATUS_NO_MEMORY; + } + + realm = domain->alt_name; + strupper_m(realm); + + principal_s = talloc_asprintf(state->mem_ctx, "%s@%s", name_user, realm); + if (principal_s == NULL) { + return NT_STATUS_NO_MEMORY; + } + + service = talloc_asprintf(state->mem_ctx, "%s/%s@%s", KRB5_TGS_NAME, realm, realm); + if (service == NULL) { + return NT_STATUS_NO_MEMORY; + } + + if (!internal_ccache) { + + setup_return_cc_name(state, cc); + + result = add_ccache_to_list(principal_s, + cc, + service, + state->request.data.auth.user, + domain->alt_name, + uid, + time(NULL), + time(NULL) + lp_winbind_cache_time(), + time(NULL) + WINBINDD_PAM_AUTH_KRB5_RENEW_TIME, + True); + + if (!NT_STATUS_IS_OK(result)) { + DEBUG(10,("winbindd_dual_pam_auth_cached: failed " + "to add ccache to list: %s\n", + nt_errstr(result))); + } + } + } +#endif /* HAVE_KRB5 */ + success: + /* FIXME: we possibly should handle logon hours as well (does xp when + * offline?) see auth/auth_sam.c:sam_account_ok for details */ + + unix_to_nt_time(&my_info3->logon_time, time(NULL)); + my_info3->bad_pw_count = 0; + + result = winbindd_update_creds_by_info3(domain, + state->mem_ctx, + state->request.data.auth.user, + state->request.data.auth.pass, + my_info3); + if (!NT_STATUS_IS_OK(result)) { + DEBUG(1,("winbindd_dual_pam_auth_cached: failed to update creds: %s\n", + nt_errstr(result))); + return result; + } + + return NT_STATUS_OK; + + } + + /* User does *NOT* know the correct password, modify info3 accordingly */ + + /* failure of this is not critical */ + result = get_max_bad_attempts_from_lockout_policy(domain, state->mem_ctx, &max_allowed_bad_attempts); + if (!NT_STATUS_IS_OK(result)) { + DEBUG(10,("winbindd_dual_pam_auth_cached: failed to get max_allowed_bad_attempts. " + "Won't be able to honour account lockout policies\n")); + } + + /* increase counter */ + my_info3->bad_pw_count++; + + if (max_allowed_bad_attempts == 0) { + goto failed; + } + + /* lockout user */ + if (my_info3->bad_pw_count >= max_allowed_bad_attempts) { + + uint32 password_properties; + + result = get_pwd_properties(domain, state->mem_ctx, &password_properties); + if (!NT_STATUS_IS_OK(result)) { + DEBUG(10,("winbindd_dual_pam_auth_cached: failed to get password properties.\n")); + } + + if ((my_info3->user_rid != DOMAIN_USER_RID_ADMIN) || + (password_properties & DOMAIN_LOCKOUT_ADMINS)) { + my_info3->acct_flags |= ACB_AUTOLOCK; + } + } + +failed: + result = winbindd_update_creds_by_info3(domain, + state->mem_ctx, + state->request.data.auth.user, + NULL, + my_info3); + + if (!NT_STATUS_IS_OK(result)) { + DEBUG(0,("winbindd_dual_pam_auth_cached: failed to update creds %s\n", + nt_errstr(result))); + } + + return NT_STATUS_LOGON_FAILURE; +} + +NTSTATUS winbindd_dual_pam_auth_kerberos(struct winbindd_domain *domain, + struct winbindd_cli_state *state, + NET_USER_INFO_3 **info3) +{ + struct winbindd_domain *contact_domain; + fstring name_domain, name_user; + NTSTATUS result; + + DEBUG(10,("winbindd_dual_pam_auth_kerberos\n")); + + /* Parse domain and username */ + + parse_domain_user(state->request.data.auth.user, name_domain, name_user); + + /* what domain should we contact? */ + + if ( IS_DC ) { + if (!(contact_domain = find_domain_from_name(name_domain))) { + DEBUG(3, ("Authentication for domain for [%s] -> [%s]\\[%s] failed as %s is not a trusted domain\n", + state->request.data.auth.user, name_domain, name_user, name_domain)); + result = NT_STATUS_NO_SUCH_USER; + goto done; + } + + } else { + if (is_myname(name_domain)) { + DEBUG(3, ("Authentication for domain %s (local domain to this server) not supported at this stage\n", name_domain)); + result = NT_STATUS_NO_SUCH_USER; + goto done; + } + + contact_domain = find_domain_from_name(name_domain); + if (contact_domain == NULL) { + DEBUG(3, ("Authentication for domain for [%s] -> [%s]\\[%s] failed as %s is not a trusted domain\n", + state->request.data.auth.user, name_domain, name_user, name_domain)); + + contact_domain = find_our_domain(); + } + } + + if (contact_domain->initialized && + contact_domain->active_directory) { + goto try_login; + } + + if (!contact_domain->initialized) { + init_dc_connection(contact_domain); + } + + if (!contact_domain->active_directory) { + DEBUG(3,("krb5 auth requested but domain is not Active Directory\n")); + return NT_STATUS_INVALID_LOGON_TYPE; + } +try_login: + result = winbindd_raw_kerberos_login(contact_domain, state, info3); +done: + return result; +} + +NTSTATUS winbindd_dual_pam_auth_samlogon(struct winbindd_domain *domain, + struct winbindd_cli_state *state, + NET_USER_INFO_3 **info3) +{ + + struct rpc_pipe_client *netlogon_pipe; + uchar chal[8]; + DATA_BLOB lm_resp; + DATA_BLOB nt_resp; + int attempts = 0; + unsigned char local_lm_response[24]; + unsigned char local_nt_response[24]; + struct winbindd_domain *contact_domain; + fstring name_domain, name_user; + BOOL retry; + NTSTATUS result; + NET_USER_INFO_3 *my_info3; + + ZERO_STRUCTP(info3); + + *info3 = NULL; + + my_info3 = TALLOC_ZERO_P(state->mem_ctx, NET_USER_INFO_3); + if (my_info3 == NULL) { + return NT_STATUS_NO_MEMORY; + } + + + DEBUG(10,("winbindd_dual_pam_auth_samlogon\n")); + + /* Parse domain and username */ + + parse_domain_user(state->request.data.auth.user, name_domain, name_user); + + /* do password magic */ + + + generate_random_buffer(chal, 8); + if (lp_client_ntlmv2_auth()) { + DATA_BLOB server_chal; + DATA_BLOB names_blob; + DATA_BLOB nt_response; + DATA_BLOB lm_response; + server_chal = data_blob_talloc(state->mem_ctx, chal, 8); + + /* note that the 'workgroup' here is a best guess - we don't know + the server's domain at this point. The 'server name' is also + dodgy... + */ + names_blob = NTLMv2_generate_names_blob(global_myname(), lp_workgroup()); + + if (!SMBNTLMv2encrypt(name_user, name_domain, + state->request.data.auth.pass, + &server_chal, + &names_blob, + &lm_response, &nt_response, NULL)) { + data_blob_free(&names_blob); + data_blob_free(&server_chal); + DEBUG(0, ("winbindd_pam_auth: SMBNTLMv2encrypt() failed!\n")); + result = NT_STATUS_NO_MEMORY; + goto done; + } + data_blob_free(&names_blob); + data_blob_free(&server_chal); + lm_resp = data_blob_talloc(state->mem_ctx, lm_response.data, + lm_response.length); + nt_resp = data_blob_talloc(state->mem_ctx, nt_response.data, + nt_response.length); + data_blob_free(&lm_response); + data_blob_free(&nt_response); + + } else { + if (lp_client_lanman_auth() + && SMBencrypt(state->request.data.auth.pass, + chal, + local_lm_response)) { + lm_resp = data_blob_talloc(state->mem_ctx, + local_lm_response, + sizeof(local_lm_response)); + } else { + lm_resp = data_blob_null; + } + SMBNTencrypt(state->request.data.auth.pass, + chal, + local_nt_response); + + nt_resp = data_blob_talloc(state->mem_ctx, + local_nt_response, + sizeof(local_nt_response)); + } + + /* what domain should we contact? */ + + if ( IS_DC ) { + if (!(contact_domain = find_domain_from_name(name_domain))) { + DEBUG(3, ("Authentication for domain for [%s] -> [%s]\\[%s] failed as %s is not a trusted domain\n", + state->request.data.auth.user, name_domain, name_user, name_domain)); + result = NT_STATUS_NO_SUCH_USER; + goto done; + } + + } else { + if (is_myname(name_domain)) { + DEBUG(3, ("Authentication for domain %s (local domain to this server) not supported at this stage\n", name_domain)); + result = NT_STATUS_NO_SUCH_USER; + goto done; + } + + contact_domain = find_our_domain(); + } + + /* check authentication loop */ + + do { + NTSTATUS (*logon_fn)(struct rpc_pipe_client + *cli, TALLOC_CTX *mem_ctx, + uint32 logon_parameters, + const char *server, + const char *username, + const char *domain, + const char *workstation, + const uint8 chal[8], + DATA_BLOB lm_response, + DATA_BLOB nt_response, + NET_USER_INFO_3 *info3); + + ZERO_STRUCTP(my_info3); + retry = False; + + result = cm_connect_netlogon(contact_domain, &netlogon_pipe); + + if (!NT_STATUS_IS_OK(result)) { + DEBUG(3, ("could not open handle to NETLOGON pipe\n")); + goto done; + } + + logon_fn = contact_domain->can_do_samlogon_ex + ? rpccli_netlogon_sam_network_logon_ex + : rpccli_netlogon_sam_network_logon; + + result = logon_fn(netlogon_pipe, + state->mem_ctx, + 0, + contact_domain->dcname, /* server name */ + name_user, /* user name */ + name_domain, /* target domain */ + global_myname(), /* workstation */ + chal, + lm_resp, + nt_resp, + my_info3); + + if ((NT_STATUS_V(result) == DCERPC_FAULT_OP_RNG_ERROR) + && contact_domain->can_do_samlogon_ex) { + DEBUG(3, ("Got a DC that can not do NetSamLogonEx, " + "retrying with NetSamLogon\n")); + contact_domain->can_do_samlogon_ex = False; + retry = True; + continue; + } + + attempts += 1; + + /* We have to try a second time as cm_connect_netlogon + might not yet have noticed that the DC has killed + our connection. */ + + if (NT_STATUS_EQUAL(result, NT_STATUS_UNSUCCESSFUL)) { + retry = True; + continue; + } + + /* if we get access denied, a possible cause was that we had + and open connection to the DC, but someone changed our + machine account password out from underneath us using 'net + rpc changetrustpw' */ + + if ( NT_STATUS_EQUAL(result, NT_STATUS_ACCESS_DENIED) ) { + DEBUG(3,("winbindd_pam_auth: sam_logon returned " + "ACCESS_DENIED. Maybe the trust account " + "password was changed and we didn't know it. " + "Killing connections to domain %s\n", + name_domain)); + invalidate_cm_connection(&contact_domain->conn); + retry = True; + } + + } while ( (attempts < 2) && retry ); + + /* handle the case where a NT4 DC does not fill in the acct_flags in + * the samlogon reply info3. When accurate info3 is required by the + * caller, we look up the account flags ourselve - gd */ + + if ((state->request.flags & WBFLAG_PAM_INFO3_TEXT) && + (my_info3->acct_flags == 0) && NT_STATUS_IS_OK(result)) { + + struct rpc_pipe_client *samr_pipe; + POLICY_HND samr_domain_handle, user_pol; + SAM_USERINFO_CTR *user_ctr; + NTSTATUS status_tmp; + uint32 acct_flags; + + ZERO_STRUCT(user_ctr); + + status_tmp = cm_connect_sam(contact_domain, state->mem_ctx, + &samr_pipe, &samr_domain_handle); + + if (!NT_STATUS_IS_OK(status_tmp)) { + DEBUG(3, ("could not open handle to SAMR pipe: %s\n", + nt_errstr(status_tmp))); + goto done; + } + + status_tmp = rpccli_samr_open_user(samr_pipe, state->mem_ctx, + &samr_domain_handle, + MAXIMUM_ALLOWED_ACCESS, + my_info3->user_rid, &user_pol); + + if (!NT_STATUS_IS_OK(status_tmp)) { + DEBUG(3, ("could not open user handle on SAMR pipe: %s\n", + nt_errstr(status_tmp))); + goto done; + } + + status_tmp = rpccli_samr_query_userinfo(samr_pipe, state->mem_ctx, + &user_pol, 16, &user_ctr); + + if (!NT_STATUS_IS_OK(status_tmp)) { + DEBUG(3, ("could not query user info on SAMR pipe: %s\n", + nt_errstr(status_tmp))); + rpccli_samr_close(samr_pipe, state->mem_ctx, &user_pol); + goto done; + } + + acct_flags = user_ctr->info.id16->acb_info; + + if (acct_flags == 0) { + rpccli_samr_close(samr_pipe, state->mem_ctx, &user_pol); + goto done; + } + + my_info3->acct_flags = acct_flags; + + DEBUG(10,("successfully retrieved acct_flags 0x%x\n", acct_flags)); + + rpccli_samr_close(samr_pipe, state->mem_ctx, &user_pol); + } + + *info3 = my_info3; +done: + return result; +} + +enum winbindd_result winbindd_dual_pam_auth(struct winbindd_domain *domain, + struct winbindd_cli_state *state) +{ + NTSTATUS result = NT_STATUS_LOGON_FAILURE; + NTSTATUS krb5_result = NT_STATUS_OK; + fstring name_domain, name_user; + NET_USER_INFO_3 *info3 = NULL; + + /* Ensure null termination */ + state->request.data.auth.user[sizeof(state->request.data.auth.user)-1]='\0'; + + /* Ensure null termination */ + state->request.data.auth.pass[sizeof(state->request.data.auth.pass)-1]='\0'; + + DEBUG(3, ("[%5lu]: dual pam auth %s\n", (unsigned long)state->pid, + state->request.data.auth.user)); + + if (!check_request_flags(state->request.flags)) { + result = NT_STATUS_INVALID_PARAMETER_MIX; + goto done; + } + + /* Parse domain and username */ + + ws_name_return( state->request.data.auth.user, WB_REPLACE_CHAR ); + + parse_domain_user(state->request.data.auth.user, name_domain, name_user); + + if (domain->online == False) { + result = NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND; + if (domain->startup) { + /* Logons are very important to users. If we're offline and + we get a request within the first 30 seconds of startup, + try very hard to find a DC and go online. */ + + DEBUG(10,("winbindd_dual_pam_auth: domain: %s offline and auth " + "request in startup mode.\n", domain->name )); + + winbindd_flush_negative_conn_cache(domain); + result = init_dc_connection(domain); + } + } + + DEBUG(10,("winbindd_dual_pam_auth: domain: %s last was %s\n", domain->name, domain->online ? "online":"offline")); + + /* Check for Kerberos authentication */ + if (domain->online && (state->request.flags & WBFLAG_PAM_KRB5)) { + + result = winbindd_dual_pam_auth_kerberos(domain, state, &info3); + /* save for later */ + krb5_result = result; + + + if (NT_STATUS_IS_OK(result)) { + DEBUG(10,("winbindd_dual_pam_auth_kerberos succeeded\n")); + goto process_result; + } else { + DEBUG(10,("winbindd_dual_pam_auth_kerberos failed: %s\n", nt_errstr(result))); + } + + if (NT_STATUS_EQUAL(result, NT_STATUS_NO_LOGON_SERVERS) || + NT_STATUS_EQUAL(result, NT_STATUS_IO_TIMEOUT) || + NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) { + DEBUG(10,("winbindd_dual_pam_auth_kerberos setting domain to offline\n")); + set_domain_offline( domain ); + goto cached_logon; + } + + /* there are quite some NT_STATUS errors where there is no + * point in retrying with a samlogon, we explictly have to take + * care not to increase the bad logon counter on the DC */ + + if (NT_STATUS_EQUAL(result, NT_STATUS_ACCOUNT_DISABLED) || + NT_STATUS_EQUAL(result, NT_STATUS_ACCOUNT_EXPIRED) || + NT_STATUS_EQUAL(result, NT_STATUS_ACCOUNT_LOCKED_OUT) || + NT_STATUS_EQUAL(result, NT_STATUS_INVALID_LOGON_HOURS) || + NT_STATUS_EQUAL(result, NT_STATUS_INVALID_WORKSTATION) || + NT_STATUS_EQUAL(result, NT_STATUS_LOGON_FAILURE) || + NT_STATUS_EQUAL(result, NT_STATUS_NO_SUCH_USER) || + NT_STATUS_EQUAL(result, NT_STATUS_PASSWORD_EXPIRED) || + NT_STATUS_EQUAL(result, NT_STATUS_PASSWORD_MUST_CHANGE) || + NT_STATUS_EQUAL(result, NT_STATUS_WRONG_PASSWORD)) { + goto process_result; + } + + if (state->request.flags & WBFLAG_PAM_FALLBACK_AFTER_KRB5) { + DEBUG(3,("falling back to samlogon\n")); + goto sam_logon; + } else { + goto cached_logon; + } + } + +sam_logon: + /* Check for Samlogon authentication */ + if (domain->online) { + result = winbindd_dual_pam_auth_samlogon(domain, state, &info3); + + if (NT_STATUS_IS_OK(result)) { + DEBUG(10,("winbindd_dual_pam_auth_samlogon succeeded\n")); + /* add the Krb5 err if we have one */ + if ( NT_STATUS_EQUAL(krb5_result, NT_STATUS_TIME_DIFFERENCE_AT_DC ) ) { + info3->user_flgs |= LOGON_KRB5_FAIL_CLOCK_SKEW; + } + goto process_result; + } + + DEBUG(10,("winbindd_dual_pam_auth_samlogon failed: %s\n", + nt_errstr(result))); + + if (NT_STATUS_EQUAL(result, NT_STATUS_NO_LOGON_SERVERS) || + NT_STATUS_EQUAL(result, NT_STATUS_IO_TIMEOUT) || + NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) + { + DEBUG(10,("winbindd_dual_pam_auth_samlogon setting domain to offline\n")); + set_domain_offline( domain ); + goto cached_logon; + } + + if (domain->online) { + /* We're still online - fail. */ + goto done; + } + } + +cached_logon: + /* Check for Cached logons */ + if (!domain->online && (state->request.flags & WBFLAG_PAM_CACHED_LOGIN) && + lp_winbind_offline_logon()) { + + result = winbindd_dual_pam_auth_cached(domain, state, &info3); + + if (NT_STATUS_IS_OK(result)) { + DEBUG(10,("winbindd_dual_pam_auth_cached succeeded\n")); + goto process_result; + } else { + DEBUG(10,("winbindd_dual_pam_auth_cached failed: %s\n", nt_errstr(result))); + goto done; + } + } + +process_result: + + if (NT_STATUS_IS_OK(result)) { + + DOM_SID user_sid; + + /* In all codepaths where result == NT_STATUS_OK info3 must have + been initialized. */ + if (!info3) { + result = NT_STATUS_INTERNAL_ERROR; + goto done; + } + + netsamlogon_cache_store(name_user, info3); + wcache_invalidate_samlogon(find_domain_from_name(name_domain), info3); + + /* save name_to_sid info as early as possible (only if + this is our primary domain so we don't invalidate + the cache entry by storing the seq_num for the wrong + domain). */ + if ( domain->primary ) { + sid_compose(&user_sid, &info3->dom_sid.sid, + info3->user_rid); + cache_name2sid(domain, name_domain, name_user, + SID_NAME_USER, &user_sid); + } + + /* Check if the user is in the right group */ + + if (!NT_STATUS_IS_OK(result = check_info3_in_group(state->mem_ctx, info3, + state->request.data.auth.require_membership_of_sid))) { + DEBUG(3, ("User %s is not in the required group (%s), so plaintext authentication is rejected\n", + state->request.data.auth.user, + state->request.data.auth.require_membership_of_sid)); + goto done; + } + + result = append_data(state, info3, name_domain, name_user); + if (!NT_STATUS_IS_OK(result)) { + goto done; + } + + if ((state->request.flags & WBFLAG_PAM_CACHED_LOGIN)) { + + /* Store in-memory creds for single-signon using ntlm_auth. */ + result = winbindd_add_memory_creds(state->request.data.auth.user, + get_uid_from_state(state), + state->request.data.auth.pass); + + if (!NT_STATUS_IS_OK(result)) { + DEBUG(10,("Failed to store memory creds: %s\n", nt_errstr(result))); + goto done; + } + + if (lp_winbind_offline_logon()) { + result = winbindd_store_creds(domain, + state->mem_ctx, + state->request.data.auth.user, + state->request.data.auth.pass, + info3, NULL); + if (!NT_STATUS_IS_OK(result)) { + + /* Release refcount. */ + winbindd_delete_memory_creds(state->request.data.auth.user); + + DEBUG(10,("Failed to store creds: %s\n", nt_errstr(result))); + goto done; + } + } + } + + result = fillup_password_policy(domain, state); + + if (!NT_STATUS_IS_OK(result) + && !NT_STATUS_EQUAL(result, NT_STATUS_NOT_SUPPORTED) ) + { + DEBUG(10,("Failed to get password policies: %s\n", nt_errstr(result))); + goto done; + } + + result = NT_STATUS_OK; + } + +done: + /* give us a more useful (more correct?) error code */ + if ((NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND) || + (NT_STATUS_EQUAL(result, NT_STATUS_UNSUCCESSFUL)))) { + result = NT_STATUS_NO_LOGON_SERVERS; + } + + state->response.data.auth.nt_status = NT_STATUS_V(result); + fstrcpy(state->response.data.auth.nt_status_string, nt_errstr(result)); + + /* we might have given a more useful error above */ + if (!*state->response.data.auth.error_string) + fstrcpy(state->response.data.auth.error_string, get_friendly_nt_error_msg(result)); + state->response.data.auth.pam_error = nt_status_to_pam(result); + + DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2, ("Plain-text authentication for user %s returned %s (PAM: %d)\n", + state->request.data.auth.user, + state->response.data.auth.nt_status_string, + state->response.data.auth.pam_error)); + + return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR; +} + + +/********************************************************************** + Challenge Response Authentication Protocol +**********************************************************************/ + +void winbindd_pam_auth_crap(struct winbindd_cli_state *state) +{ + struct winbindd_domain *domain = NULL; + const char *domain_name = NULL; + NTSTATUS result; + + if (!check_request_flags(state->request.flags)) { + result = NT_STATUS_INVALID_PARAMETER_MIX; + goto done; + } + + if (!state->privileged) { + char *error_string = NULL; + DEBUG(2, ("winbindd_pam_auth_crap: non-privileged access " + "denied. !\n")); + DEBUGADD(2, ("winbindd_pam_auth_crap: Ensure permissions " + "on %s are set correctly.\n", + get_winbind_priv_pipe_dir())); + /* send a better message than ACCESS_DENIED */ + error_string = talloc_asprintf(state->mem_ctx, + "winbind client not authorized " + "to use winbindd_pam_auth_crap." + " Ensure permissions on %s " + "are set correctly.", + get_winbind_priv_pipe_dir()); + fstrcpy(state->response.data.auth.error_string, error_string); + result = NT_STATUS_ACCESS_DENIED; + goto done; + } + + /* Ensure null termination */ + state->request.data.auth_crap.user + [sizeof(state->request.data.auth_crap.user)-1]=0; + state->request.data.auth_crap.domain + [sizeof(state->request.data.auth_crap.domain)-1]=0; + + DEBUG(3, ("[%5lu]: pam auth crap domain: [%s] user: %s\n", + (unsigned long)state->pid, + state->request.data.auth_crap.domain, + state->request.data.auth_crap.user)); + + if (*state->request.data.auth_crap.domain != '\0') { + domain_name = state->request.data.auth_crap.domain; + } else if (lp_winbind_use_default_domain()) { + domain_name = lp_workgroup(); + } + + if (domain_name != NULL) + domain = find_auth_domain(state, domain_name); + + if (domain != NULL) { + sendto_domain(state, domain); + return; + } + + result = NT_STATUS_NO_SUCH_USER; + + done: + set_auth_errors(&state->response, result); + DEBUG(5, ("CRAP authentication for %s\\%s returned %s (PAM: %d)\n", + state->request.data.auth_crap.domain, + state->request.data.auth_crap.user, + state->response.data.auth.nt_status_string, + state->response.data.auth.pam_error)); + request_error(state); + return; +} + + +enum winbindd_result winbindd_dual_pam_auth_crap(struct winbindd_domain *domain, + struct winbindd_cli_state *state) +{ + NTSTATUS result; + NET_USER_INFO_3 info3; + struct rpc_pipe_client *netlogon_pipe; + const char *name_user = NULL; + const char *name_domain = NULL; + const char *workstation; + struct winbindd_domain *contact_domain; + int attempts = 0; + BOOL retry; + + DATA_BLOB lm_resp, nt_resp; + + /* This is child-only, so no check for privileged access is needed + anymore */ + + /* Ensure null termination */ + state->request.data.auth_crap.user[sizeof(state->request.data.auth_crap.user)-1]=0; + state->request.data.auth_crap.domain[sizeof(state->request.data.auth_crap.domain)-1]=0; + + if (!check_request_flags(state->request.flags)) { + result = NT_STATUS_INVALID_PARAMETER_MIX; + goto done; + } + + name_user = state->request.data.auth_crap.user; + + if (*state->request.data.auth_crap.domain) { + name_domain = state->request.data.auth_crap.domain; + } else if (lp_winbind_use_default_domain()) { + name_domain = lp_workgroup(); + } else { + DEBUG(5,("no domain specified with username (%s) - failing auth\n", + name_user)); + result = NT_STATUS_NO_SUCH_USER; + goto done; + } + + DEBUG(3, ("[%5lu]: pam auth crap domain: %s user: %s\n", (unsigned long)state->pid, + name_domain, name_user)); + + if (*state->request.data.auth_crap.workstation) { + workstation = state->request.data.auth_crap.workstation; + } else { + workstation = global_myname(); + } + + if (state->request.data.auth_crap.lm_resp_len > sizeof(state->request.data.auth_crap.lm_resp) + || state->request.data.auth_crap.nt_resp_len > sizeof(state->request.data.auth_crap.nt_resp)) { + DEBUG(0, ("winbindd_pam_auth_crap: invalid password length %u/%u\n", + state->request.data.auth_crap.lm_resp_len, + state->request.data.auth_crap.nt_resp_len)); + result = NT_STATUS_INVALID_PARAMETER; + goto done; + } + + lm_resp = data_blob_talloc(state->mem_ctx, state->request.data.auth_crap.lm_resp, + state->request.data.auth_crap.lm_resp_len); + nt_resp = data_blob_talloc(state->mem_ctx, state->request.data.auth_crap.nt_resp, + state->request.data.auth_crap.nt_resp_len); + + /* what domain should we contact? */ + + if ( IS_DC ) { + if (!(contact_domain = find_domain_from_name(name_domain))) { + DEBUG(3, ("Authentication for domain for [%s] -> [%s]\\[%s] failed as %s is not a trusted domain\n", + state->request.data.auth_crap.user, name_domain, name_user, name_domain)); + result = NT_STATUS_NO_SUCH_USER; + goto done; + } + } else { + if (is_myname(name_domain)) { + DEBUG(3, ("Authentication for domain %s (local domain to this server) not supported at this stage\n", name_domain)); + result = NT_STATUS_NO_SUCH_USER; + goto done; + } + contact_domain = find_our_domain(); + } + + do { + NTSTATUS (*logon_fn)(struct rpc_pipe_client + *cli, TALLOC_CTX *mem_ctx, + uint32 logon_parameters, + const char *server, + const char *username, + const char *domain, + const char *workstation, + const uint8 chal[8], + DATA_BLOB lm_response, + DATA_BLOB nt_response, + NET_USER_INFO_3 *info3); + + ZERO_STRUCT(info3); + retry = False; + + netlogon_pipe = NULL; + result = cm_connect_netlogon(contact_domain, &netlogon_pipe); + + if (!NT_STATUS_IS_OK(result)) { + DEBUG(3, ("could not open handle to NETLOGON pipe (error: %s)\n", + nt_errstr(result))); + goto done; + } + + logon_fn = contact_domain->can_do_samlogon_ex + ? rpccli_netlogon_sam_network_logon_ex + : rpccli_netlogon_sam_network_logon; + + result = logon_fn(netlogon_pipe, + state->mem_ctx, + state->request.data.auth_crap.logon_parameters, + contact_domain->dcname, + name_user, + name_domain, + /* Bug #3248 - found by Stefan Burkei. */ + workstation, /* We carefully set this above so use it... */ + state->request.data.auth_crap.chal, + lm_resp, + nt_resp, + &info3); + + if ((NT_STATUS_V(result) == DCERPC_FAULT_OP_RNG_ERROR) + && contact_domain->can_do_samlogon_ex) { + DEBUG(3, ("Got a DC that can not do NetSamLogonEx, " + "retrying with NetSamLogon\n")); + contact_domain->can_do_samlogon_ex = False; + retry = True; + continue; + } + + attempts += 1; + + /* We have to try a second time as cm_connect_netlogon + might not yet have noticed that the DC has killed + our connection. */ + + if (NT_STATUS_EQUAL(result, NT_STATUS_UNSUCCESSFUL)) { + retry = True; + continue; + } + + /* if we get access denied, a possible cause was that we had and open + connection to the DC, but someone changed our machine account password + out from underneath us using 'net rpc changetrustpw' */ + + if ( NT_STATUS_EQUAL(result, NT_STATUS_ACCESS_DENIED) ) { + DEBUG(3,("winbindd_pam_auth: sam_logon returned " + "ACCESS_DENIED. Maybe the trust account " + "password was changed and we didn't know it. " + "Killing connections to domain %s\n", + name_domain)); + invalidate_cm_connection(&contact_domain->conn); + retry = True; + } + + } while ( (attempts < 2) && retry ); + + if (NT_STATUS_IS_OK(result)) { + + netsamlogon_cache_store(name_user, &info3); + wcache_invalidate_samlogon(find_domain_from_name(name_domain), &info3); + + /* Check if the user is in the right group */ + + if (!NT_STATUS_IS_OK(result = check_info3_in_group(state->mem_ctx, &info3, + state->request.data.auth_crap.require_membership_of_sid))) { + DEBUG(3, ("User %s is not in the required group (%s), so " + "crap authentication is rejected\n", + state->request.data.auth_crap.user, + state->request.data.auth_crap.require_membership_of_sid)); + goto done; + } + + result = append_data(state, &info3, name_domain, name_user); + if (!NT_STATUS_IS_OK(result)) { + goto done; + } + } + +done: + + /* give us a more useful (more correct?) error code */ + if ((NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND) || + (NT_STATUS_EQUAL(result, NT_STATUS_UNSUCCESSFUL)))) { + result = NT_STATUS_NO_LOGON_SERVERS; + } + + if (state->request.flags & WBFLAG_PAM_NT_STATUS_SQUASH) { + result = nt_status_squash(result); + } + + state->response.data.auth.nt_status = NT_STATUS_V(result); + fstrcpy(state->response.data.auth.nt_status_string, nt_errstr(result)); + + /* we might have given a more useful error above */ + if (!*state->response.data.auth.error_string) { + fstrcpy(state->response.data.auth.error_string, get_friendly_nt_error_msg(result)); + } + state->response.data.auth.pam_error = nt_status_to_pam(result); + + DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2, + ("NTLM CRAP authentication for user [%s]\\[%s] returned %s (PAM: %d)\n", + name_domain, + name_user, + state->response.data.auth.nt_status_string, + state->response.data.auth.pam_error)); + + return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR; +} + +/* Change a user password */ + +void winbindd_pam_chauthtok(struct winbindd_cli_state *state) +{ + fstring domain, user; + struct winbindd_domain *contact_domain; + + DEBUG(3, ("[%5lu]: pam chauthtok %s\n", (unsigned long)state->pid, + state->request.data.chauthtok.user)); + + /* Setup crap */ + + ws_name_return( state->request.data.auth.user, WB_REPLACE_CHAR ); + + if (!canonicalize_username(state->request.data.chauthtok.user, domain, user)) { + set_auth_errors(&state->response, NT_STATUS_NO_SUCH_USER); + DEBUG(5, ("winbindd_pam_chauthtok: canonicalize_username %s failed with %s" + "(PAM: %d)\n", + state->request.data.auth.user, + state->response.data.auth.nt_status_string, + state->response.data.auth.pam_error)); + request_error(state); + return; + } + + contact_domain = find_domain_from_name(domain); + if (!contact_domain) { + set_auth_errors(&state->response, NT_STATUS_NO_SUCH_USER); + DEBUG(3, ("Cannot change password for [%s] -> [%s]\\[%s] as %s is not a trusted domain\n", + state->request.data.chauthtok.user, domain, user, domain)); + request_error(state); + return; + } + + sendto_domain(state, contact_domain); +} + +enum winbindd_result winbindd_dual_pam_chauthtok(struct winbindd_domain *contact_domain, + struct winbindd_cli_state *state) +{ + char *oldpass; + char *newpass = NULL; + POLICY_HND dom_pol; + struct rpc_pipe_client *cli; + BOOL got_info = False; + SAM_UNK_INFO_1 info; + SAMR_CHANGE_REJECT reject; + NTSTATUS result = NT_STATUS_UNSUCCESSFUL; + fstring domain, user; + + DEBUG(3, ("[%5lu]: dual pam chauthtok %s\n", (unsigned long)state->pid, + state->request.data.auth.user)); + + if (!parse_domain_user(state->request.data.chauthtok.user, domain, user)) { + goto done; + } + + /* Change password */ + + oldpass = state->request.data.chauthtok.oldpass; + newpass = state->request.data.chauthtok.newpass; + + /* Initialize reject reason */ + state->response.data.auth.reject_reason = Undefined; + + /* Get sam handle */ + + result = cm_connect_sam(contact_domain, state->mem_ctx, &cli, + &dom_pol); + if (!NT_STATUS_IS_OK(result)) { + DEBUG(1, ("could not get SAM handle on DC for %s\n", domain)); + goto done; + } + + result = rpccli_samr_chgpasswd3(cli, state->mem_ctx, user, newpass, oldpass, &info, &reject); + + /* Windows 2003 returns NT_STATUS_PASSWORD_RESTRICTION */ + + if (NT_STATUS_EQUAL(result, NT_STATUS_PASSWORD_RESTRICTION) ) { + state->response.data.auth.policy.min_length_password = + info.min_length_password; + state->response.data.auth.policy.password_history = + info.password_history; + state->response.data.auth.policy.password_properties = + info.password_properties; + state->response.data.auth.policy.expire = + nt_time_to_unix_abs(&info.expire); + state->response.data.auth.policy.min_passwordage = + nt_time_to_unix_abs(&info.min_passwordage); + + state->response.data.auth.reject_reason = + reject.reject_reason; + + got_info = True; + } + + /* only fallback when the chgpasswd3 call is not supported */ + if ((NT_STATUS_EQUAL(result, NT_STATUS(DCERPC_FAULT_OP_RNG_ERROR))) || + (NT_STATUS_EQUAL(result, NT_STATUS_NOT_SUPPORTED)) || + (NT_STATUS_EQUAL(result, NT_STATUS_NOT_IMPLEMENTED))) { + + DEBUG(10,("Password change with chgpasswd3 failed with: %s, retrying chgpasswd_user\n", + nt_errstr(result))); + + result = rpccli_samr_chgpasswd_user(cli, state->mem_ctx, user, newpass, oldpass); + + /* Windows 2000 returns NT_STATUS_ACCOUNT_RESTRICTION. + Map to the same status code as Windows 2003. */ + + if ( NT_STATUS_EQUAL(NT_STATUS_ACCOUNT_RESTRICTION, result ) ) { + result = NT_STATUS_PASSWORD_RESTRICTION; + } + } + +done: + + if (NT_STATUS_IS_OK(result) && (state->request.flags & WBFLAG_PAM_CACHED_LOGIN)) { + + /* Update the single sign-on memory creds. */ + result = winbindd_replace_memory_creds(state->request.data.chauthtok.user, + newpass); + + if (!NT_STATUS_IS_OK(result)) { + DEBUG(10,("Failed to replace memory creds: %s\n", nt_errstr(result))); + goto process_result; + } + + if (lp_winbind_offline_logon()) { + result = winbindd_update_creds_by_name(contact_domain, + state->mem_ctx, user, + newpass); + if (!NT_STATUS_IS_OK(result)) { + DEBUG(10,("Failed to store creds: %s\n", nt_errstr(result))); + goto process_result; + } + } + } + + if (!NT_STATUS_IS_OK(result) && !got_info && contact_domain) { + + NTSTATUS policy_ret; + + policy_ret = fillup_password_policy(contact_domain, state); + + /* failure of this is non critical, it will just provide no + * additional information to the client why the change has + * failed - Guenther */ + + if (!NT_STATUS_IS_OK(policy_ret)) { + DEBUG(10,("Failed to get password policies: %s\n", nt_errstr(policy_ret))); + goto process_result; + } + } + +process_result: + + state->response.data.auth.nt_status = NT_STATUS_V(result); + fstrcpy(state->response.data.auth.nt_status_string, nt_errstr(result)); + fstrcpy(state->response.data.auth.error_string, get_friendly_nt_error_msg(result)); + state->response.data.auth.pam_error = nt_status_to_pam(result); + + DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2, + ("Password change for user [%s]\\[%s] returned %s (PAM: %d)\n", + domain, + user, + state->response.data.auth.nt_status_string, + state->response.data.auth.pam_error)); + + return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR; +} + +void winbindd_pam_logoff(struct winbindd_cli_state *state) +{ + struct winbindd_domain *domain; + fstring name_domain, user; + uid_t caller_uid = (uid_t)-1; + uid_t request_uid = state->request.data.logoff.uid; + + DEBUG(3, ("[%5lu]: pam logoff %s\n", (unsigned long)state->pid, + state->request.data.logoff.user)); + + /* Ensure null termination */ + state->request.data.logoff.user + [sizeof(state->request.data.logoff.user)-1]='\0'; + + state->request.data.logoff.krb5ccname + [sizeof(state->request.data.logoff.krb5ccname)-1]='\0'; + + if (request_uid == (gid_t)-1) { + goto failed; + } + + if (!canonicalize_username(state->request.data.logoff.user, name_domain, user)) { + goto failed; + } + + if ((domain = find_auth_domain(state, name_domain)) == NULL) { + goto failed; + } + + if ((sys_getpeereid(state->sock, &caller_uid)) != 0) { + DEBUG(1,("winbindd_pam_logoff: failed to check peerid: %s\n", + strerror(errno))); + goto failed; + } + + switch (caller_uid) { + case -1: + goto failed; + case 0: + /* root must be able to logoff any user - gd */ + state->request.data.logoff.uid = request_uid; + break; + default: + if (caller_uid != request_uid) { + DEBUG(1,("winbindd_pam_logoff: caller requested invalid uid\n")); + goto failed; + } + state->request.data.logoff.uid = caller_uid; + break; + } + + sendto_domain(state, domain); + return; + + failed: + set_auth_errors(&state->response, NT_STATUS_NO_SUCH_USER); + DEBUG(5, ("Pam Logoff for %s returned %s " + "(PAM: %d)\n", + state->request.data.logoff.user, + state->response.data.auth.nt_status_string, + state->response.data.auth.pam_error)); + request_error(state); + return; +} + +enum winbindd_result winbindd_dual_pam_logoff(struct winbindd_domain *domain, + struct winbindd_cli_state *state) +{ + NTSTATUS result = NT_STATUS_NOT_SUPPORTED; + + DEBUG(3, ("[%5lu]: pam dual logoff %s\n", (unsigned long)state->pid, + state->request.data.logoff.user)); + + if (!(state->request.flags & WBFLAG_PAM_KRB5)) { + result = NT_STATUS_OK; + goto process_result; + } + + if (state->request.data.logoff.krb5ccname[0] == '\0') { + result = NT_STATUS_OK; + goto process_result; + } + +#ifdef HAVE_KRB5 + + if (state->request.data.logoff.uid < 0) { + DEBUG(0,("winbindd_pam_logoff: invalid uid\n")); + goto process_result; + } + + /* what we need here is to find the corresponding krb5 ccache name *we* + * created for a given username and destroy it */ + + if (!ccache_entry_exists(state->request.data.logoff.user)) { + result = NT_STATUS_OK; + DEBUG(10,("winbindd_pam_logoff: no entry found.\n")); + goto process_result; + } + + if (!ccache_entry_identical(state->request.data.logoff.user, + state->request.data.logoff.uid, + state->request.data.logoff.krb5ccname)) { + DEBUG(0,("winbindd_pam_logoff: cached entry differs.\n")); + goto process_result; + } + + result = remove_ccache(state->request.data.logoff.user); + if (!NT_STATUS_IS_OK(result)) { + DEBUG(0,("winbindd_pam_logoff: failed to remove ccache: %s\n", + nt_errstr(result))); + goto process_result; + } + +#else + result = NT_STATUS_NOT_SUPPORTED; +#endif + +process_result: + + winbindd_delete_memory_creds(state->request.data.logoff.user); + + state->response.data.auth.nt_status = NT_STATUS_V(result); + fstrcpy(state->response.data.auth.nt_status_string, nt_errstr(result)); + fstrcpy(state->response.data.auth.error_string, get_friendly_nt_error_msg(result)); + state->response.data.auth.pam_error = nt_status_to_pam(result); + + return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR; +} + +/* Change user password with auth crap*/ + +void winbindd_pam_chng_pswd_auth_crap(struct winbindd_cli_state *state) +{ + struct winbindd_domain *domain = NULL; + const char *domain_name = NULL; + + /* Ensure null termination */ + state->request.data.chng_pswd_auth_crap.user[ + sizeof(state->request.data.chng_pswd_auth_crap.user)-1]=0; + state->request.data.chng_pswd_auth_crap.domain[ + sizeof(state->request.data.chng_pswd_auth_crap.domain)-1]=0; + + DEBUG(3, ("[%5lu]: pam change pswd auth crap domain: %s user: %s\n", + (unsigned long)state->pid, + state->request.data.chng_pswd_auth_crap.domain, + state->request.data.chng_pswd_auth_crap.user)); + + if (*state->request.data.chng_pswd_auth_crap.domain != '\0') { + domain_name = state->request.data.chng_pswd_auth_crap.domain; + } else if (lp_winbind_use_default_domain()) { + domain_name = lp_workgroup(); + } + + if (domain_name != NULL) + domain = find_domain_from_name(domain_name); + + if (domain != NULL) { + DEBUG(7, ("[%5lu]: pam auth crap changing pswd in domain: " + "%s\n", (unsigned long)state->pid,domain->name)); + sendto_domain(state, domain); + return; + } + + set_auth_errors(&state->response, NT_STATUS_NO_SUCH_USER); + DEBUG(5, ("CRAP change password for %s\\%s returned %s (PAM: %d)\n", + state->request.data.chng_pswd_auth_crap.domain, + state->request.data.chng_pswd_auth_crap.user, + state->response.data.auth.nt_status_string, + state->response.data.auth.pam_error)); + request_error(state); + return; +} + +enum winbindd_result winbindd_dual_pam_chng_pswd_auth_crap(struct winbindd_domain *domainSt, struct winbindd_cli_state *state) +{ + NTSTATUS result; + DATA_BLOB new_nt_password; + DATA_BLOB old_nt_hash_enc; + DATA_BLOB new_lm_password; + DATA_BLOB old_lm_hash_enc; + fstring domain,user; + POLICY_HND dom_pol; + struct winbindd_domain *contact_domain = domainSt; + struct rpc_pipe_client *cli; + + /* Ensure null termination */ + state->request.data.chng_pswd_auth_crap.user[ + sizeof(state->request.data.chng_pswd_auth_crap.user)-1]=0; + state->request.data.chng_pswd_auth_crap.domain[ + sizeof(state->request.data.chng_pswd_auth_crap.domain)-1]=0; + *domain = 0; + *user = 0; + + DEBUG(3, ("[%5lu]: pam change pswd auth crap domain: %s user: %s\n", + (unsigned long)state->pid, + state->request.data.chng_pswd_auth_crap.domain, + state->request.data.chng_pswd_auth_crap.user)); + + if (lp_winbind_offline_logon()) { + DEBUG(0,("Refusing password change as winbind offline logons are enabled. ")); + DEBUGADD(0,("Changing passwords here would risk inconsistent logons\n")); + result = NT_STATUS_ACCESS_DENIED; + goto done; + } + + if (*state->request.data.chng_pswd_auth_crap.domain) { + fstrcpy(domain,state->request.data.chng_pswd_auth_crap.domain); + } else { + parse_domain_user(state->request.data.chng_pswd_auth_crap.user, + domain, user); + + if(!*domain) { + DEBUG(3,("no domain specified with username (%s) - " + "failing auth\n", + state->request.data.chng_pswd_auth_crap.user)); + result = NT_STATUS_NO_SUCH_USER; + goto done; + } + } + + if (!*domain && lp_winbind_use_default_domain()) { + fstrcpy(domain,(char *)lp_workgroup()); + } + + if(!*user) { + fstrcpy(user, state->request.data.chng_pswd_auth_crap.user); + } + + DEBUG(3, ("[%5lu]: pam auth crap domain: %s user: %s\n", + (unsigned long)state->pid, domain, user)); + + /* Change password */ + new_nt_password = data_blob_talloc( + state->mem_ctx, + state->request.data.chng_pswd_auth_crap.new_nt_pswd, + state->request.data.chng_pswd_auth_crap.new_nt_pswd_len); + + old_nt_hash_enc = data_blob_talloc( + state->mem_ctx, + state->request.data.chng_pswd_auth_crap.old_nt_hash_enc, + state->request.data.chng_pswd_auth_crap.old_nt_hash_enc_len); + + if(state->request.data.chng_pswd_auth_crap.new_lm_pswd_len > 0) { + new_lm_password = data_blob_talloc( + state->mem_ctx, + state->request.data.chng_pswd_auth_crap.new_lm_pswd, + state->request.data.chng_pswd_auth_crap.new_lm_pswd_len); + + old_lm_hash_enc = data_blob_talloc( + state->mem_ctx, + state->request.data.chng_pswd_auth_crap.old_lm_hash_enc, + state->request.data.chng_pswd_auth_crap.old_lm_hash_enc_len); + } else { + new_lm_password.length = 0; + old_lm_hash_enc.length = 0; + } + + /* Get sam handle */ + + result = cm_connect_sam(contact_domain, state->mem_ctx, &cli, &dom_pol); + if (!NT_STATUS_IS_OK(result)) { + DEBUG(1, ("could not get SAM handle on DC for %s\n", domain)); + goto done; + } + + result = rpccli_samr_chng_pswd_auth_crap( + cli, state->mem_ctx, user, new_nt_password, old_nt_hash_enc, + new_lm_password, old_lm_hash_enc); + + done: + state->response.data.auth.nt_status = NT_STATUS_V(result); + fstrcpy(state->response.data.auth.nt_status_string, nt_errstr(result)); + fstrcpy(state->response.data.auth.error_string, + get_friendly_nt_error_msg(result)); + state->response.data.auth.pam_error = nt_status_to_pam(result); + + DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2, + ("Password change for user [%s]\\[%s] returned %s (PAM: %d)\n", + domain, user, + state->response.data.auth.nt_status_string, + state->response.data.auth.pam_error)); + + return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR; +} diff --git a/source3/winbindd/winbindd_passdb.c b/source3/winbindd/winbindd_passdb.c new file mode 100644 index 0000000000..5c36c0c327 --- /dev/null +++ b/source3/winbindd/winbindd_passdb.c @@ -0,0 +1,467 @@ +/* + Unix SMB/CIFS implementation. + + Winbind rpc backend functions + + Copyright (C) Tim Potter 2000-2001,2003 + Copyright (C) Simo Sorce 2003 + Copyright (C) Volker Lendecke 2004 + + 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 3 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, see . +*/ + +#include "includes.h" +#include "winbindd.h" + +#undef DBGC_CLASS +#define DBGC_CLASS DBGC_WINBIND + +/* Query display info for a domain. This returns enough information plus a + bit extra to give an overview of domain users for the User Manager + application. */ +static NTSTATUS query_user_list(struct winbindd_domain *domain, + TALLOC_CTX *mem_ctx, + uint32 *num_entries, + WINBIND_USERINFO **info) +{ + /* We don't have users */ + *num_entries = 0; + *info = NULL; + return NT_STATUS_OK; +} + +/* list all domain groups */ +static NTSTATUS enum_dom_groups(struct winbindd_domain *domain, + TALLOC_CTX *mem_ctx, + uint32 *num_entries, + struct acct_info **info) +{ + /* We don't have domain groups */ + *num_entries = 0; + *info = NULL; + return NT_STATUS_OK; +} + +/* List all domain groups */ + +static NTSTATUS enum_local_groups(struct winbindd_domain *domain, + TALLOC_CTX *mem_ctx, + uint32 *num_entries, + struct acct_info **info) +{ + struct pdb_search *search; + struct samr_displayentry *aliases; + int i; + NTSTATUS result = NT_STATUS_UNSUCCESSFUL; + + search = pdb_search_aliases(&domain->sid); + if (search == NULL) goto done; + + *num_entries = pdb_search_entries(search, 0, 0xffffffff, &aliases); + if (*num_entries == 0) goto done; + + *info = TALLOC_ARRAY(mem_ctx, struct acct_info, *num_entries); + if (*info == NULL) { + result = NT_STATUS_NO_MEMORY; + goto done; + } + + for (i=0; i<*num_entries; i++) { + fstrcpy((*info)[i].acct_name, aliases[i].account_name); + fstrcpy((*info)[i].acct_desc, aliases[i].description); + (*info)[i].rid = aliases[i].rid; + } + + result = NT_STATUS_OK; + done: + pdb_search_destroy(search); + return result; +} + +/* convert a single name to a sid in a domain */ +static NTSTATUS name_to_sid(struct winbindd_domain *domain, + TALLOC_CTX *mem_ctx, + enum winbindd_cmd original_cmd, + const char *domain_name, + const char *name, + DOM_SID *sid, + enum lsa_SidType *type) +{ + uint32 flags = LOOKUP_NAME_ALL; + + switch ( original_cmd ) { + case WINBINDD_LOOKUPNAME: + /* This call is ok */ + break; + default: + /* Avoid any NSS calls in the lookup_name by default */ + flags |= LOOKUP_NAME_EXPLICIT; + DEBUG(10,("winbindd_passdb: limiting name_to_sid() to explicit mappings\n")); + break; + } + + DEBUG(10, ("Finding name %s\n", name)); + + if ( !lookup_name( mem_ctx, name, flags, NULL, NULL, sid, type ) ) { + return NT_STATUS_NONE_MAPPED; + } + + return NT_STATUS_OK; +} + +/* + convert a domain SID to a user or group name +*/ +static NTSTATUS sid_to_name(struct winbindd_domain *domain, + TALLOC_CTX *mem_ctx, + const DOM_SID *sid, + char **domain_name, + char **name, + enum lsa_SidType *type) +{ + const char *dom, *nam; + + DEBUG(10, ("Converting SID %s\n", sid_string_static(sid))); + + /* Paranoia check */ + if (!sid_check_is_in_builtin(sid) && + !sid_check_is_in_our_domain(sid) && + !sid_check_is_in_unix_users(sid) && + !sid_check_is_unix_users(sid) && + !sid_check_is_in_unix_groups(sid) && + !sid_check_is_unix_groups(sid) ) + { + DEBUG(0, ("Possible deadlock: Trying to lookup SID %s with " + "passdb backend\n", sid_string_static(sid))); + return NT_STATUS_NONE_MAPPED; + } + + if (!lookup_sid(mem_ctx, sid, &dom, &nam, type)) { + return NT_STATUS_NONE_MAPPED; + } + + *domain_name = talloc_strdup(mem_ctx, dom); + *name = talloc_strdup(mem_ctx, nam); + + return NT_STATUS_OK; +} + +static NTSTATUS rids_to_names(struct winbindd_domain *domain, + TALLOC_CTX *mem_ctx, + const DOM_SID *sid, + uint32 *rids, + size_t num_rids, + char **domain_name, + char ***names, + enum lsa_SidType **types) +{ + return NT_STATUS_UNSUCCESSFUL; +} + +/* Lookup user information from a rid or username. */ +static NTSTATUS query_user(struct winbindd_domain *domain, + TALLOC_CTX *mem_ctx, + const DOM_SID *user_sid, + WINBIND_USERINFO *user_info) +{ + return NT_STATUS_NO_SUCH_USER; +} + +/* Lookup groups a user is a member of. I wish Unix had a call like this! */ +static NTSTATUS lookup_usergroups(struct winbindd_domain *domain, + TALLOC_CTX *mem_ctx, + const DOM_SID *user_sid, + uint32 *num_groups, DOM_SID **user_gids) +{ + NTSTATUS result; + DOM_SID *groups = NULL; + gid_t *gids = NULL; + size_t ngroups = 0; + struct samu *user; + + if ( (user = samu_new(mem_ctx)) == NULL ) { + return NT_STATUS_NO_MEMORY; + } + + if ( !pdb_getsampwsid( user, user_sid ) ) { + return NT_STATUS_NO_SUCH_USER; + } + + result = pdb_enum_group_memberships( mem_ctx, user, &groups, &gids, &ngroups ); + + TALLOC_FREE( user ); + + *num_groups = (uint32)ngroups; + *user_gids = groups; + + return result; +} + +static NTSTATUS lookup_useraliases(struct winbindd_domain *domain, + TALLOC_CTX *mem_ctx, + uint32 num_sids, const DOM_SID *sids, + uint32 *p_num_aliases, uint32 **rids) +{ + NTSTATUS result; + size_t num_aliases = 0; + + result = pdb_enum_alias_memberships(mem_ctx, &domain->sid, + sids, num_sids, rids, &num_aliases); + + *p_num_aliases = num_aliases; + return result; +} + +/* Lookup group membership given a rid. */ +static NTSTATUS lookup_groupmem(struct winbindd_domain *domain, + TALLOC_CTX *mem_ctx, + const DOM_SID *group_sid, uint32 *num_names, + DOM_SID **sid_mem, char ***names, + uint32 **name_types) +{ + size_t i, num_members, num_mapped; + uint32 *rids; + NTSTATUS result; + const DOM_SID **sids; + struct lsa_dom_info *lsa_domains; + struct lsa_name_info *lsa_names; + TALLOC_CTX *tmp_ctx; + + if (!sid_check_is_in_our_domain(group_sid)) { + /* There's no groups, only aliases in BUILTIN */ + return NT_STATUS_NO_SUCH_GROUP; + } + + if (!(tmp_ctx = talloc_init("lookup_groupmem"))) { + return NT_STATUS_NO_MEMORY; + } + + result = pdb_enum_group_members(tmp_ctx, group_sid, &rids, + &num_members); + if (!NT_STATUS_IS_OK(result)) { + TALLOC_FREE(tmp_ctx); + return result; + } + + if (num_members == 0) { + *num_names = 0; + *sid_mem = NULL; + *names = NULL; + *name_types = NULL; + TALLOC_FREE(tmp_ctx); + return NT_STATUS_OK; + } + + *sid_mem = TALLOC_ARRAY(mem_ctx, DOM_SID, num_members); + *names = TALLOC_ARRAY(mem_ctx, char *, num_members); + *name_types = TALLOC_ARRAY(mem_ctx, uint32, num_members); + sids = TALLOC_ARRAY(tmp_ctx, const DOM_SID *, num_members); + + if (((*sid_mem) == NULL) || ((*names) == NULL) || + ((*name_types) == NULL) || (sids == NULL)) { + TALLOC_FREE(tmp_ctx); + return NT_STATUS_NO_MEMORY; + } + + /* + * Prepare an array of sid pointers for the lookup_sids calling + * convention. + */ + + for (i=0; isid, rids[i])) { + TALLOC_FREE(tmp_ctx); + return NT_STATUS_INTERNAL_ERROR; + } + sids[i] = sid; + } + + result = lookup_sids(tmp_ctx, num_members, sids, 1, + &lsa_domains, &lsa_names); + if (!NT_STATUS_IS_OK(result)) { + TALLOC_FREE(tmp_ctx); + return result; + } + + num_mapped = 0; + for (i=0; iname))) { + TALLOC_FREE(tmp_ctx); + return NT_STATUS_NO_MEMORY; + } + sid_copy(&(*dom_sids)[i], &domains[i]->sid); + } + + TALLOC_FREE(tmp_ctx); + return NT_STATUS_OK; +} + +/* the rpc backend methods are exposed via this structure */ +struct winbindd_methods passdb_methods = { + False, + query_user_list, + enum_dom_groups, + enum_local_groups, + name_to_sid, + sid_to_name, + rids_to_names, + query_user, + lookup_usergroups, + lookup_useraliases, + lookup_groupmem, + sequence_number, + lockout_policy, + password_policy, + trusted_domains, +}; diff --git a/source3/winbindd/winbindd_reconnect.c b/source3/winbindd/winbindd_reconnect.c new file mode 100644 index 0000000000..a1f96a0359 --- /dev/null +++ b/source3/winbindd/winbindd_reconnect.c @@ -0,0 +1,316 @@ +/* + Unix SMB/CIFS implementation. + + Wrapper around winbindd_rpc.c to centralize retry logic. + + Copyright (C) Volker Lendecke 2005 + + 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 3 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, see . +*/ + +#include "includes.h" +#include "winbindd.h" + +#undef DBGC_CLASS +#define DBGC_CLASS DBGC_WINBIND + +extern struct winbindd_methods msrpc_methods; + +/* List all users */ +static NTSTATUS query_user_list(struct winbindd_domain *domain, + TALLOC_CTX *mem_ctx, + uint32 *num_entries, + WINBIND_USERINFO **info) +{ + NTSTATUS result; + + result = msrpc_methods.query_user_list(domain, mem_ctx, + num_entries, info); + + if (NT_STATUS_EQUAL(result, NT_STATUS_UNSUCCESSFUL)) + result = msrpc_methods.query_user_list(domain, mem_ctx, + num_entries, info); + return result; +} + +/* list all domain groups */ +static NTSTATUS enum_dom_groups(struct winbindd_domain *domain, + TALLOC_CTX *mem_ctx, + uint32 *num_entries, + struct acct_info **info) +{ + NTSTATUS result; + + result = msrpc_methods.enum_dom_groups(domain, mem_ctx, + num_entries, info); + + if (NT_STATUS_EQUAL(result, NT_STATUS_UNSUCCESSFUL)) + result = msrpc_methods.enum_dom_groups(domain, mem_ctx, + num_entries, info); + return result; +} + +/* List all domain groups */ + +static NTSTATUS enum_local_groups(struct winbindd_domain *domain, + TALLOC_CTX *mem_ctx, + uint32 *num_entries, + struct acct_info **info) +{ + NTSTATUS result; + + result = msrpc_methods.enum_local_groups(domain, mem_ctx, + num_entries, info); + + if (NT_STATUS_EQUAL(result, NT_STATUS_UNSUCCESSFUL)) + result = msrpc_methods.enum_local_groups(domain, mem_ctx, + num_entries, info); + + return result; +} + +/* convert a single name to a sid in a domain */ +static NTSTATUS name_to_sid(struct winbindd_domain *domain, + TALLOC_CTX *mem_ctx, + enum winbindd_cmd orig_cmd, + const char *domain_name, + const char *name, + DOM_SID *sid, + enum lsa_SidType *type) +{ + NTSTATUS result; + + result = msrpc_methods.name_to_sid(domain, mem_ctx, orig_cmd, + domain_name, name, + sid, type); + + if (NT_STATUS_EQUAL(result, NT_STATUS_UNSUCCESSFUL)) + result = msrpc_methods.name_to_sid(domain, mem_ctx, orig_cmd, + domain_name, name, + sid, type); + + return result; +} + +/* + convert a domain SID to a user or group name +*/ +static NTSTATUS sid_to_name(struct winbindd_domain *domain, + TALLOC_CTX *mem_ctx, + const DOM_SID *sid, + char **domain_name, + char **name, + enum lsa_SidType *type) +{ + NTSTATUS result; + + result = msrpc_methods.sid_to_name(domain, mem_ctx, sid, + domain_name, name, type); + + if (NT_STATUS_EQUAL(result, NT_STATUS_UNSUCCESSFUL)) + result = msrpc_methods.sid_to_name(domain, mem_ctx, sid, + domain_name, name, type); + + return result; +} + +static NTSTATUS rids_to_names(struct winbindd_domain *domain, + TALLOC_CTX *mem_ctx, + const DOM_SID *sid, + uint32 *rids, + size_t num_rids, + char **domain_name, + char ***names, + enum lsa_SidType **types) +{ + NTSTATUS result; + + result = msrpc_methods.rids_to_names(domain, mem_ctx, sid, + rids, num_rids, + domain_name, names, types); + if (NT_STATUS_EQUAL(result, NT_STATUS_UNSUCCESSFUL)) { + result = msrpc_methods.rids_to_names(domain, mem_ctx, sid, + rids, num_rids, + domain_name, names, + types); + } + + return result; +} + +/* Lookup user information from a rid or username. */ +static NTSTATUS query_user(struct winbindd_domain *domain, + TALLOC_CTX *mem_ctx, + const DOM_SID *user_sid, + WINBIND_USERINFO *user_info) +{ + NTSTATUS result; + + result = msrpc_methods.query_user(domain, mem_ctx, user_sid, + user_info); + + if (NT_STATUS_EQUAL(result, NT_STATUS_UNSUCCESSFUL)) + result = msrpc_methods.query_user(domain, mem_ctx, user_sid, + user_info); + + return result; +} + +/* Lookup groups a user is a member of. I wish Unix had a call like this! */ +static NTSTATUS lookup_usergroups(struct winbindd_domain *domain, + TALLOC_CTX *mem_ctx, + const DOM_SID *user_sid, + uint32 *num_groups, DOM_SID **user_gids) +{ + NTSTATUS result; + + result = msrpc_methods.lookup_usergroups(domain, mem_ctx, + user_sid, num_groups, + user_gids); + + if (NT_STATUS_EQUAL(result, NT_STATUS_UNSUCCESSFUL)) + result = msrpc_methods.lookup_usergroups(domain, mem_ctx, + user_sid, num_groups, + user_gids); + + return result; +} + +static NTSTATUS lookup_useraliases(struct winbindd_domain *domain, + TALLOC_CTX *mem_ctx, + uint32 num_sids, const DOM_SID *sids, + uint32 *num_aliases, uint32 **alias_rids) +{ + NTSTATUS result; + + result = msrpc_methods.lookup_useraliases(domain, mem_ctx, + num_sids, sids, + num_aliases, + alias_rids); + + if (NT_STATUS_EQUAL(result, NT_STATUS_UNSUCCESSFUL)) + result = msrpc_methods.lookup_useraliases(domain, mem_ctx, + num_sids, sids, + num_aliases, + alias_rids); + + return result; +} + +/* Lookup group membership given a rid. */ +static NTSTATUS lookup_groupmem(struct winbindd_domain *domain, + TALLOC_CTX *mem_ctx, + const DOM_SID *group_sid, uint32 *num_names, + DOM_SID **sid_mem, char ***names, + uint32 **name_types) +{ + NTSTATUS result; + + result = msrpc_methods.lookup_groupmem(domain, mem_ctx, + group_sid, num_names, + sid_mem, names, + name_types); + + if (NT_STATUS_EQUAL(result, NT_STATUS_UNSUCCESSFUL)) + result = msrpc_methods.lookup_groupmem(domain, mem_ctx, + group_sid, num_names, + sid_mem, names, + name_types); + + return result; +} + +/* find the sequence number for a domain */ +static NTSTATUS sequence_number(struct winbindd_domain *domain, uint32 *seq) +{ + NTSTATUS result; + + result = msrpc_methods.sequence_number(domain, seq); + + if (NT_STATUS_EQUAL(result, NT_STATUS_UNSUCCESSFUL)) + result = msrpc_methods.sequence_number(domain, seq); + + return result; +} + +/* find the lockout policy of a domain */ +static NTSTATUS lockout_policy(struct winbindd_domain *domain, + TALLOC_CTX *mem_ctx, + SAM_UNK_INFO_12 *policy) +{ + NTSTATUS result; + + result = msrpc_methods.lockout_policy(domain, mem_ctx, policy); + + if (NT_STATUS_EQUAL(result, NT_STATUS_UNSUCCESSFUL)) + result = msrpc_methods.lockout_policy(domain, mem_ctx, policy); + + return result; +} + +/* find the password policy of a domain */ +static NTSTATUS password_policy(struct winbindd_domain *domain, + TALLOC_CTX *mem_ctx, + SAM_UNK_INFO_1 *policy) +{ + NTSTATUS result; + + result = msrpc_methods.password_policy(domain, mem_ctx, policy); + + if (NT_STATUS_EQUAL(result, NT_STATUS_UNSUCCESSFUL)) + result = msrpc_methods.password_policy(domain, mem_ctx, policy); + + return result; +} + +/* get a list of trusted domains */ +static NTSTATUS trusted_domains(struct winbindd_domain *domain, + TALLOC_CTX *mem_ctx, + uint32 *num_domains, + char ***names, + char ***alt_names, + DOM_SID **dom_sids) +{ + NTSTATUS result; + + result = msrpc_methods.trusted_domains(domain, mem_ctx, + num_domains, names, + alt_names, dom_sids); + + if (NT_STATUS_EQUAL(result, NT_STATUS_UNSUCCESSFUL)) + result = msrpc_methods.trusted_domains(domain, mem_ctx, + num_domains, names, + alt_names, dom_sids); + + return result; +} + +/* the rpc backend methods are exposed via this structure */ +struct winbindd_methods reconnect_methods = { + False, + query_user_list, + enum_dom_groups, + enum_local_groups, + name_to_sid, + sid_to_name, + rids_to_names, + query_user, + lookup_usergroups, + lookup_useraliases, + lookup_groupmem, + sequence_number, + lockout_policy, + password_policy, + trusted_domains, +}; diff --git a/source3/winbindd/winbindd_rpc.c b/source3/winbindd/winbindd_rpc.c new file mode 100644 index 0000000000..6d2dd35080 --- /dev/null +++ b/source3/winbindd/winbindd_rpc.c @@ -0,0 +1,1111 @@ +/* + Unix SMB/CIFS implementation. + + Winbind rpc backend functions + + Copyright (C) Tim Potter 2000-2001,2003 + Copyright (C) Andrew Tridgell 2001 + Copyright (C) Volker Lendecke 2005 + + 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 3 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, see . +*/ + +#include "includes.h" +#include "winbindd.h" + +#undef DBGC_CLASS +#define DBGC_CLASS DBGC_WINBIND + + +/* Query display info for a domain. This returns enough information plus a + bit extra to give an overview of domain users for the User Manager + application. */ +static NTSTATUS query_user_list(struct winbindd_domain *domain, + TALLOC_CTX *mem_ctx, + uint32 *num_entries, + WINBIND_USERINFO **info) +{ + NTSTATUS result; + POLICY_HND dom_pol; + unsigned int i, start_idx; + uint32 loop_count; + struct rpc_pipe_client *cli; + + DEBUG(3,("rpc: query_user_list\n")); + + *num_entries = 0; + *info = NULL; + + if ( !winbindd_can_contact_domain( domain ) ) { + DEBUG(10,("query_user_list: No incoming trust for domain %s\n", + domain->name)); + return NT_STATUS_OK; + } + + result = cm_connect_sam(domain, mem_ctx, &cli, &dom_pol); + if (!NT_STATUS_IS_OK(result)) + return result; + + i = start_idx = 0; + loop_count = 0; + + do { + uint32 num_dom_users, j; + uint32 max_entries, max_size; + SAM_DISPINFO_CTR ctr; + SAM_DISPINFO_1 info1; + + ZERO_STRUCT( ctr ); + ZERO_STRUCT( info1 ); + ctr.sam.info1 = &info1; + + /* this next bit is copied from net_user_list_internal() */ + + get_query_dispinfo_params(loop_count, &max_entries, + &max_size); + + result = rpccli_samr_query_dispinfo(cli, mem_ctx, &dom_pol, + &start_idx, 1, + &num_dom_users, + max_entries, max_size, + &ctr); + + loop_count++; + + *num_entries += num_dom_users; + + *info = TALLOC_REALLOC_ARRAY(mem_ctx, *info, WINBIND_USERINFO, + *num_entries); + + if (!(*info)) { + return NT_STATUS_NO_MEMORY; + } + + for (j = 0; j < num_dom_users; i++, j++) { + fstring username, fullname; + uint32 rid = ctr.sam.info1->sam[j].rid_user; + + unistr2_to_ascii( username, &(&ctr.sam.info1->str[j])->uni_acct_name, sizeof(username)-1); + unistr2_to_ascii( fullname, &(&ctr.sam.info1->str[j])->uni_full_name, sizeof(fullname)-1); + + (*info)[i].acct_name = talloc_strdup(mem_ctx, username ); + (*info)[i].full_name = talloc_strdup(mem_ctx, fullname ); + (*info)[i].homedir = NULL; + (*info)[i].shell = NULL; + sid_compose(&(*info)[i].user_sid, &domain->sid, rid); + + /* For the moment we set the primary group for + every user to be the Domain Users group. + There are serious problems with determining + the actual primary group for large domains. + This should really be made into a 'winbind + force group' smb.conf parameter or + something like that. */ + + sid_compose(&(*info)[i].group_sid, &domain->sid, + DOMAIN_GROUP_RID_USERS); + } + + } while (NT_STATUS_EQUAL(result, STATUS_MORE_ENTRIES)); + + return result; +} + +/* list all domain groups */ +static NTSTATUS enum_dom_groups(struct winbindd_domain *domain, + TALLOC_CTX *mem_ctx, + uint32 *num_entries, + struct acct_info **info) +{ + POLICY_HND dom_pol; + NTSTATUS status; + uint32 start = 0; + struct rpc_pipe_client *cli; + + *num_entries = 0; + *info = NULL; + + DEBUG(3,("rpc: enum_dom_groups\n")); + + if ( !winbindd_can_contact_domain( domain ) ) { + DEBUG(10,("enum_domain_groups: No incoming trust for domain %s\n", + domain->name)); + return NT_STATUS_OK; + } + + status = cm_connect_sam(domain, mem_ctx, &cli, &dom_pol); + if (!NT_STATUS_IS_OK(status)) + return status; + + do { + struct acct_info *info2 = NULL; + uint32 count = 0; + TALLOC_CTX *mem_ctx2; + + mem_ctx2 = talloc_init("enum_dom_groups[rpc]"); + + /* start is updated by this call. */ + status = rpccli_samr_enum_dom_groups(cli, mem_ctx2, &dom_pol, + &start, + 0xFFFF, /* buffer size? */ + &info2, &count); + + if (!NT_STATUS_IS_OK(status) && + !NT_STATUS_EQUAL(status, STATUS_MORE_ENTRIES)) { + talloc_destroy(mem_ctx2); + break; + } + + (*info) = TALLOC_REALLOC_ARRAY(mem_ctx, *info, + struct acct_info, + (*num_entries) + count); + if (! *info) { + talloc_destroy(mem_ctx2); + return NT_STATUS_NO_MEMORY; + } + + memcpy(&(*info)[*num_entries], info2, count*sizeof(*info2)); + (*num_entries) += count; + talloc_destroy(mem_ctx2); + } while (NT_STATUS_EQUAL(status, STATUS_MORE_ENTRIES)); + + return NT_STATUS_OK; +} + +/* List all domain groups */ + +static NTSTATUS enum_local_groups(struct winbindd_domain *domain, + TALLOC_CTX *mem_ctx, + uint32 *num_entries, + struct acct_info **info) +{ + POLICY_HND dom_pol; + NTSTATUS result; + struct rpc_pipe_client *cli; + + *num_entries = 0; + *info = NULL; + + DEBUG(3,("rpc: enum_local_groups\n")); + + if ( !winbindd_can_contact_domain( domain ) ) { + DEBUG(10,("enum_local_groups: No incoming trust for domain %s\n", + domain->name)); + return NT_STATUS_OK; + } + + result = cm_connect_sam(domain, mem_ctx, &cli, &dom_pol); + if (!NT_STATUS_IS_OK(result)) + return result; + + do { + struct acct_info *info2 = NULL; + uint32 count = 0, start = *num_entries; + TALLOC_CTX *mem_ctx2; + + mem_ctx2 = talloc_init("enum_dom_local_groups[rpc]"); + + result = rpccli_samr_enum_als_groups( cli, mem_ctx2, &dom_pol, + &start, 0xFFFF, &info2, + &count); + + if (!NT_STATUS_IS_OK(result) && + !NT_STATUS_EQUAL(result, STATUS_MORE_ENTRIES) ) + { + talloc_destroy(mem_ctx2); + return result; + } + + (*info) = TALLOC_REALLOC_ARRAY(mem_ctx, *info, + struct acct_info, + (*num_entries) + count); + if (! *info) { + talloc_destroy(mem_ctx2); + return NT_STATUS_NO_MEMORY; + } + + memcpy(&(*info)[*num_entries], info2, count*sizeof(*info2)); + (*num_entries) += count; + talloc_destroy(mem_ctx2); + + } while (NT_STATUS_EQUAL(result, STATUS_MORE_ENTRIES)); + + return NT_STATUS_OK; +} + +/* convert a single name to a sid in a domain */ +NTSTATUS msrpc_name_to_sid(struct winbindd_domain *domain, + TALLOC_CTX *mem_ctx, + enum winbindd_cmd original_cmd, + const char *domain_name, + const char *name, + DOM_SID *sid, + enum lsa_SidType *type) +{ + NTSTATUS result; + DOM_SID *sids = NULL; + enum lsa_SidType *types = NULL; + char *full_name = NULL; + struct rpc_pipe_client *cli; + POLICY_HND lsa_policy; + + if (name == NULL || *name=='\0') { + full_name = talloc_asprintf(mem_ctx, "%s", domain_name); + } else if (domain_name == NULL || *domain_name == '\0') { + full_name = talloc_asprintf(mem_ctx, "%s", name); + } else { + full_name = talloc_asprintf(mem_ctx, "%s\\%s", domain_name, name); + } + if (!full_name) { + DEBUG(0, ("talloc_asprintf failed!\n")); + return NT_STATUS_NO_MEMORY; + } + + DEBUG(3,("rpc: name_to_sid name=%s\n", full_name)); + + ws_name_return( full_name, WB_REPLACE_CHAR ); + + DEBUG(3,("name_to_sid [rpc] %s for domain %s\n", full_name?full_name:"", domain_name )); + + result = cm_connect_lsa(domain, mem_ctx, &cli, &lsa_policy); + if (!NT_STATUS_IS_OK(result)) + return result; + + result = rpccli_lsa_lookup_names(cli, mem_ctx, &lsa_policy, 1, + (const char**) &full_name, NULL, 1, &sids, &types); + + if (!NT_STATUS_IS_OK(result)) + return result; + + /* Return rid and type if lookup successful */ + + sid_copy(sid, &sids[0]); + *type = types[0]; + + return NT_STATUS_OK; +} + +/* + convert a domain SID to a user or group name +*/ +NTSTATUS msrpc_sid_to_name(struct winbindd_domain *domain, + TALLOC_CTX *mem_ctx, + const DOM_SID *sid, + char **domain_name, + char **name, + enum lsa_SidType *type) +{ + char **domains; + char **names; + enum lsa_SidType *types; + NTSTATUS result; + struct rpc_pipe_client *cli; + POLICY_HND lsa_policy; + + DEBUG(3,("sid_to_name [rpc] %s for domain %s\n", sid_string_static(sid), + domain->name )); + + result = cm_connect_lsa(domain, mem_ctx, &cli, &lsa_policy); + if (!NT_STATUS_IS_OK(result)) { + DEBUG(2,("msrpc_sid_to_name: cm_connect_lsa() failed (%s)\n", + nt_errstr(result))); + return result; + } + + + result = rpccli_lsa_lookup_sids(cli, mem_ctx, &lsa_policy, + 1, sid, &domains, &names, &types); + if (!NT_STATUS_IS_OK(result)) { + DEBUG(2,("msrpc_sid_to_name: rpccli_lsa_lookup_sids() failed (%s)\n", + nt_errstr(result))); + return result; + } + + *type = (enum lsa_SidType)types[0]; + *domain_name = domains[0]; + *name = names[0]; + + ws_name_replace( *name, WB_REPLACE_CHAR ); + + DEBUG(5,("Mapped sid to [%s]\\[%s]\n", domains[0], *name)); + return NT_STATUS_OK; +} + +NTSTATUS msrpc_rids_to_names(struct winbindd_domain *domain, + TALLOC_CTX *mem_ctx, + const DOM_SID *sid, + uint32 *rids, + size_t num_rids, + char **domain_name, + char ***names, + enum lsa_SidType **types) +{ + char **domains; + NTSTATUS result; + struct rpc_pipe_client *cli; + POLICY_HND lsa_policy; + DOM_SID *sids; + size_t i; + char **ret_names; + + DEBUG(3, ("rids_to_names [rpc] for domain %s\n", domain->name )); + + if (num_rids) { + sids = TALLOC_ARRAY(mem_ctx, DOM_SID, num_rids); + if (sids == NULL) { + return NT_STATUS_NO_MEMORY; + } + } else { + sids = NULL; + } + + for (i=0; isid, user_sid, &user_rid)) + return NT_STATUS_UNSUCCESSFUL; + + user_info->homedir = NULL; + user_info->shell = NULL; + user_info->primary_gid = (gid_t)-1; + + /* try netsamlogon cache first */ + + if ( (user = netsamlogon_cache_get( mem_ctx, user_sid )) != NULL ) + { + + DEBUG(5,("query_user: Cache lookup succeeded for %s\n", + sid_string_static(user_sid))); + + sid_compose(&user_info->user_sid, &domain->sid, user->user_rid); + sid_compose(&user_info->group_sid, &domain->sid, + user->group_rid); + + user_info->acct_name = unistr2_tdup(mem_ctx, + &user->uni_user_name); + user_info->full_name = unistr2_tdup(mem_ctx, + &user->uni_full_name); + + TALLOC_FREE(user); + + return NT_STATUS_OK; + } + + if ( !winbindd_can_contact_domain( domain ) ) { + DEBUG(10,("query_user: No incoming trust for domain %s\n", + domain->name)); + return NT_STATUS_OK; + } + + if ( !winbindd_can_contact_domain( domain ) ) { + DEBUG(10,("query_user: No incoming trust for domain %s\n", + domain->name)); + return NT_STATUS_OK; + } + + /* no cache; hit the wire */ + + result = cm_connect_sam(domain, mem_ctx, &cli, &dom_pol); + if (!NT_STATUS_IS_OK(result)) + return result; + + /* Get user handle */ + result = rpccli_samr_open_user(cli, mem_ctx, &dom_pol, + SEC_RIGHTS_MAXIMUM_ALLOWED, user_rid, + &user_pol); + + if (!NT_STATUS_IS_OK(result)) + return result; + + /* Get user info */ + result = rpccli_samr_query_userinfo(cli, mem_ctx, &user_pol, + 0x15, &ctr); + + rpccli_samr_close(cli, mem_ctx, &user_pol); + + if (!NT_STATUS_IS_OK(result)) + return result; + + sid_compose(&user_info->user_sid, &domain->sid, user_rid); + sid_compose(&user_info->group_sid, &domain->sid, + ctr->info.id21->group_rid); + user_info->acct_name = unistr2_tdup(mem_ctx, + &ctr->info.id21->uni_user_name); + user_info->full_name = unistr2_tdup(mem_ctx, + &ctr->info.id21->uni_full_name); + user_info->homedir = NULL; + user_info->shell = NULL; + user_info->primary_gid = (gid_t)-1; + + return NT_STATUS_OK; +} + +/* Lookup groups a user is a member of. I wish Unix had a call like this! */ +static NTSTATUS lookup_usergroups(struct winbindd_domain *domain, + TALLOC_CTX *mem_ctx, + const DOM_SID *user_sid, + uint32 *num_groups, DOM_SID **user_grpsids) +{ + NTSTATUS result = NT_STATUS_UNSUCCESSFUL; + POLICY_HND dom_pol, user_pol; + uint32 des_access = SEC_RIGHTS_MAXIMUM_ALLOWED; + DOM_GID *user_groups; + unsigned int i; + fstring sid_string; + uint32 user_rid; + struct rpc_pipe_client *cli; + + DEBUG(3,("rpc: lookup_usergroups sid=%s\n", + sid_to_string(sid_string, user_sid))); + + if (!sid_peek_check_rid(&domain->sid, user_sid, &user_rid)) + return NT_STATUS_UNSUCCESSFUL; + + *num_groups = 0; + *user_grpsids = NULL; + + /* so lets see if we have a cached user_info_3 */ + result = lookup_usergroups_cached(domain, mem_ctx, user_sid, + num_groups, user_grpsids); + + if (NT_STATUS_IS_OK(result)) { + return NT_STATUS_OK; + } + + if ( !winbindd_can_contact_domain( domain ) ) { + DEBUG(10,("lookup_usergroups: No incoming trust for domain %s\n", + domain->name)); + + /* Tell the cache manager not to remember this one */ + + return NT_STATUS_SYNCHRONIZATION_REQUIRED; + } + + /* no cache; hit the wire */ + + result = cm_connect_sam(domain, mem_ctx, &cli, &dom_pol); + if (!NT_STATUS_IS_OK(result)) + return result; + + /* Get user handle */ + result = rpccli_samr_open_user(cli, mem_ctx, &dom_pol, + des_access, user_rid, &user_pol); + + if (!NT_STATUS_IS_OK(result)) + return result; + + /* Query user rids */ + result = rpccli_samr_query_usergroups(cli, mem_ctx, &user_pol, + num_groups, &user_groups); + + rpccli_samr_close(cli, mem_ctx, &user_pol); + + if (!NT_STATUS_IS_OK(result) || (*num_groups) == 0) + return result; + + (*user_grpsids) = TALLOC_ARRAY(mem_ctx, DOM_SID, *num_groups); + if (!(*user_grpsids)) + return NT_STATUS_NO_MEMORY; + + for (i=0;i<(*num_groups);i++) { + sid_copy(&((*user_grpsids)[i]), &domain->sid); + sid_append_rid(&((*user_grpsids)[i]), + user_groups[i].g_rid); + } + + return NT_STATUS_OK; +} + +NTSTATUS msrpc_lookup_useraliases(struct winbindd_domain *domain, + TALLOC_CTX *mem_ctx, + uint32 num_sids, const DOM_SID *sids, + uint32 *num_aliases, uint32 **alias_rids) +{ + NTSTATUS result = NT_STATUS_UNSUCCESSFUL; + POLICY_HND dom_pol; + DOM_SID2 *query_sids; + uint32 num_query_sids = 0; + int i; + struct rpc_pipe_client *cli; + uint32 *alias_rids_query, num_aliases_query; + int rangesize = MAX_SAM_ENTRIES_W2K; + uint32 total_sids = 0; + int num_queries = 1; + + *num_aliases = 0; + *alias_rids = NULL; + + DEBUG(3,("rpc: lookup_useraliases\n")); + + if ( !winbindd_can_contact_domain( domain ) ) { + DEBUG(10,("msrpc_lookup_useraliases: No incoming trust for domain %s\n", + domain->name)); + return NT_STATUS_OK; + } + + result = cm_connect_sam(domain, mem_ctx, &cli, &dom_pol); + if (!NT_STATUS_IS_OK(result)) + return result; + + do { + /* prepare query */ + + num_query_sids = MIN(num_sids - total_sids, rangesize); + + DEBUG(10,("rpc: lookup_useraliases: entering query %d for %d sids\n", + num_queries, num_query_sids)); + + if (num_query_sids) { + query_sids = TALLOC_ARRAY(mem_ctx, DOM_SID2, num_query_sids); + if (query_sids == NULL) { + return NT_STATUS_NO_MEMORY; + } + } else { + query_sids = NULL; + } + + for (i=0; iname, + sid_to_string(sid_string, group_sid))); + + if ( !winbindd_can_contact_domain( domain ) ) { + DEBUG(10,("lookup_groupmem: No incoming trust for domain %s\n", + domain->name)); + return NT_STATUS_OK; + } + + if (!sid_peek_check_rid(&domain->sid, group_sid, &group_rid)) + return NT_STATUS_UNSUCCESSFUL; + + *num_names = 0; + + result = cm_connect_sam(domain, mem_ctx, &cli, &dom_pol); + if (!NT_STATUS_IS_OK(result)) + return result; + + result = rpccli_samr_open_group(cli, mem_ctx, &dom_pol, + des_access, group_rid, &group_pol); + + if (!NT_STATUS_IS_OK(result)) + return result; + + /* Step #1: Get a list of user rids that are the members of the + group. */ + + /* This call can take a long time - allow the server to time out. + 35 seconds should do it. */ + + orig_timeout = cli_set_timeout(cli->cli, 35000); + + result = rpccli_samr_query_groupmem(cli, mem_ctx, + &group_pol, num_names, &rid_mem, + name_types); + + /* And restore our original timeout. */ + cli_set_timeout(cli->cli, orig_timeout); + + rpccli_samr_close(cli, mem_ctx, &group_pol); + + if (!NT_STATUS_IS_OK(result)) + return result; + + if (!*num_names) { + names = NULL; + name_types = NULL; + sid_mem = NULL; + return NT_STATUS_OK; + } + + /* Step #2: Convert list of rids into list of usernames. Do this + in bunches of ~1000 to avoid crashing NT4. It looks like there + is a buffer overflow or something like that lurking around + somewhere. */ + +#define MAX_LOOKUP_RIDS 900 + + *names = TALLOC_ZERO_ARRAY(mem_ctx, char *, *num_names); + *name_types = TALLOC_ZERO_ARRAY(mem_ctx, uint32, *num_names); + *sid_mem = TALLOC_ZERO_ARRAY(mem_ctx, DOM_SID, *num_names); + + for (j=0;j<(*num_names);j++) + sid_compose(&(*sid_mem)[j], &domain->sid, rid_mem[j]); + + if (*num_names>0 && (!*names || !*name_types)) + return NT_STATUS_NO_MEMORY; + + for (i = 0; i < *num_names; i += MAX_LOOKUP_RIDS) { + int num_lookup_rids = MIN(*num_names - i, MAX_LOOKUP_RIDS); + uint32 tmp_num_names = 0; + char **tmp_names = NULL; + uint32 *tmp_types = NULL; + + /* Lookup a chunk of rids */ + + result = rpccli_samr_lookup_rids(cli, mem_ctx, + &dom_pol, + num_lookup_rids, + &rid_mem[i], + &tmp_num_names, + &tmp_names, &tmp_types); + + /* see if we have a real error (and yes the + STATUS_SOME_UNMAPPED is the one returned from 2k) */ + + if (!NT_STATUS_IS_OK(result) && + !NT_STATUS_EQUAL(result, STATUS_SOME_UNMAPPED)) + return result; + + /* Copy result into array. The talloc system will take + care of freeing the temporary arrays later on. */ + + memcpy(&(*names)[i], tmp_names, sizeof(char *) * + tmp_num_names); + + memcpy(&(*name_types)[i], tmp_types, sizeof(uint32) * + tmp_num_names); + + total_names += tmp_num_names; + } + + *num_names = total_names; + + return NT_STATUS_OK; +} + +#ifdef HAVE_LDAP + +#include + +static int get_ldap_seq(const char *server, int port, uint32 *seq) +{ + int ret = -1; + struct timeval to; + const char *attrs[] = {"highestCommittedUSN", NULL}; + LDAPMessage *res = NULL; + char **values = NULL; + LDAP *ldp = NULL; + + *seq = DOM_SEQUENCE_NONE; + + /* + * Parameterised (5) second timeout on open. This is needed as the + * search timeout doesn't seem to apply to doing an open as well. JRA. + */ + + ldp = ldap_open_with_timeout(server, port, lp_ldap_timeout()); + if (ldp == NULL) + return -1; + + /* Timeout if no response within 20 seconds. */ + to.tv_sec = 10; + to.tv_usec = 0; + + if (ldap_search_st(ldp, "", LDAP_SCOPE_BASE, "(objectclass=*)", + CONST_DISCARD(char **, attrs), 0, &to, &res)) + goto done; + + if (ldap_count_entries(ldp, res) != 1) + goto done; + + values = ldap_get_values(ldp, res, "highestCommittedUSN"); + if (!values || !values[0]) + goto done; + + *seq = atoi(values[0]); + ret = 0; + + done: + + if (values) + ldap_value_free(values); + if (res) + ldap_msgfree(res); + if (ldp) + ldap_unbind(ldp); + return ret; +} + +/********************************************************************** + Get the sequence number for a Windows AD native mode domain using + LDAP queries. +**********************************************************************/ + +static int get_ldap_sequence_number(struct winbindd_domain *domain, uint32 *seq) +{ + int ret = -1; + fstring ipstr; + + fstrcpy( ipstr, inet_ntoa(domain->dcaddr.sin_addr)); + if ((ret = get_ldap_seq( ipstr, LDAP_PORT, seq)) == 0) { + DEBUG(3, ("get_ldap_sequence_number: Retrieved sequence " + "number for Domain (%s) from DC (%s)\n", + domain->name, ipstr)); + } + return ret; +} + +#endif /* HAVE_LDAP */ + +/* find the sequence number for a domain */ +static NTSTATUS sequence_number(struct winbindd_domain *domain, uint32 *seq) +{ + TALLOC_CTX *mem_ctx; + SAM_UNK_CTR ctr; + NTSTATUS result; + POLICY_HND dom_pol; + BOOL got_seq_num = False; + struct rpc_pipe_client *cli; + + DEBUG(10,("rpc: fetch sequence_number for %s\n", domain->name)); + + if ( !winbindd_can_contact_domain( domain ) ) { + DEBUG(10,("sequence_number: No incoming trust for domain %s\n", + domain->name)); + *seq = time(NULL); + return NT_STATUS_OK; + } + + *seq = DOM_SEQUENCE_NONE; + + if (!(mem_ctx = talloc_init("sequence_number[rpc]"))) + return NT_STATUS_NO_MEMORY; + +#ifdef HAVE_LDAP + if ( domain->active_directory ) + { + int res; + + DEBUG(8,("using get_ldap_seq() to retrieve the " + "sequence number\n")); + + res = get_ldap_sequence_number( domain, seq ); + if (res == 0) + { + result = NT_STATUS_OK; + DEBUG(10,("domain_sequence_number: LDAP for " + "domain %s is %u\n", + domain->name, *seq)); + goto done; + } + + DEBUG(10,("domain_sequence_number: failed to get LDAP " + "sequence number for domain %s\n", + domain->name )); + } +#endif /* HAVE_LDAP */ + + result = cm_connect_sam(domain, mem_ctx, &cli, &dom_pol); + if (!NT_STATUS_IS_OK(result)) { + goto done; + } + + /* Query domain info */ + + result = rpccli_samr_query_dom_info(cli, mem_ctx, &dom_pol, 8, &ctr); + + if (NT_STATUS_IS_OK(result)) { + *seq = ctr.info.inf8.seq_num; + got_seq_num = True; + goto seq_num; + } + + /* retry with info-level 2 in case the dc does not support info-level 8 + * (like all older samba2 and samba3 dc's - Guenther */ + + result = rpccli_samr_query_dom_info(cli, mem_ctx, &dom_pol, 2, &ctr); + + if (NT_STATUS_IS_OK(result)) { + *seq = ctr.info.inf2.seq_num; + got_seq_num = True; + } + + seq_num: + if (got_seq_num) { + DEBUG(10,("domain_sequence_number: for domain %s is %u\n", + domain->name, (unsigned)*seq)); + } else { + DEBUG(10,("domain_sequence_number: failed to get sequence " + "number (%u) for domain %s\n", + (unsigned)*seq, domain->name )); + } + + done: + + talloc_destroy(mem_ctx); + + return result; +} + +/* get a list of trusted domains */ +static NTSTATUS trusted_domains(struct winbindd_domain *domain, + TALLOC_CTX *mem_ctx, + uint32 *num_domains, + char ***names, + char ***alt_names, + DOM_SID **dom_sids) +{ + NTSTATUS result = NT_STATUS_UNSUCCESSFUL; + uint32 enum_ctx = 0; + struct rpc_pipe_client *cli; + POLICY_HND lsa_policy; + + DEBUG(3,("rpc: trusted_domains\n")); + + *num_domains = 0; + *names = NULL; + *alt_names = NULL; + *dom_sids = NULL; + + result = cm_connect_lsa(domain, mem_ctx, &cli, &lsa_policy); + if (!NT_STATUS_IS_OK(result)) + return result; + + result = STATUS_MORE_ENTRIES; + + while (NT_STATUS_EQUAL(result, STATUS_MORE_ENTRIES)) { + uint32 start_idx, num; + char **tmp_names; + DOM_SID *tmp_sids; + int i; + + result = rpccli_lsa_enum_trust_dom(cli, mem_ctx, + &lsa_policy, &enum_ctx, + &num, &tmp_names, + &tmp_sids); + + if (!NT_STATUS_IS_OK(result) && + !NT_STATUS_EQUAL(result, STATUS_MORE_ENTRIES)) + break; + + start_idx = *num_domains; + *num_domains += num; + *names = TALLOC_REALLOC_ARRAY(mem_ctx, *names, + char *, *num_domains); + *dom_sids = TALLOC_REALLOC_ARRAY(mem_ctx, *dom_sids, + DOM_SID, *num_domains); + *alt_names = TALLOC_REALLOC_ARRAY(mem_ctx, *alt_names, + char *, *num_domains); + if ((*names == NULL) || (*dom_sids == NULL) || + (*alt_names == NULL)) + return NT_STATUS_NO_MEMORY; + + for (i=0; iname)); + + if ( !winbindd_can_contact_domain( domain ) ) { + DEBUG(10,("msrpc_lockout_policy: No incoming trust for domain %s\n", + domain->name)); + return NT_STATUS_NOT_SUPPORTED; + } + + result = cm_connect_sam(domain, mem_ctx, &cli, &dom_pol); + if (!NT_STATUS_IS_OK(result)) { + goto done; + } + + result = rpccli_samr_query_dom_info(cli, mem_ctx, &dom_pol, 12, &ctr); + if (!NT_STATUS_IS_OK(result)) { + goto done; + } + + *lockout_policy = ctr.info.inf12; + + DEBUG(10,("msrpc_lockout_policy: bad_attempt_lockout %d\n", + ctr.info.inf12.bad_attempt_lockout)); + + done: + + return result; +} + +/* find the password policy for a domain */ +NTSTATUS msrpc_password_policy(struct winbindd_domain *domain, + TALLOC_CTX *mem_ctx, + SAM_UNK_INFO_1 *password_policy) +{ + NTSTATUS result; + struct rpc_pipe_client *cli; + POLICY_HND dom_pol; + SAM_UNK_CTR ctr; + + DEBUG(10,("rpc: fetch password policy for %s\n", domain->name)); + + if ( !winbindd_can_contact_domain( domain ) ) { + DEBUG(10,("msrpc_password_policy: No incoming trust for domain %s\n", + domain->name)); + return NT_STATUS_NOT_SUPPORTED; + } + + result = cm_connect_sam(domain, mem_ctx, &cli, &dom_pol); + if (!NT_STATUS_IS_OK(result)) { + goto done; + } + + result = rpccli_samr_query_dom_info(cli, mem_ctx, &dom_pol, 1, &ctr); + if (!NT_STATUS_IS_OK(result)) { + goto done; + } + + *password_policy = ctr.info.inf1; + + DEBUG(10,("msrpc_password_policy: min_length_password %d\n", + ctr.info.inf1.min_length_password)); + + done: + + return result; +} + + +/* the rpc backend methods are exposed via this structure */ +struct winbindd_methods msrpc_methods = { + False, + query_user_list, + enum_dom_groups, + enum_local_groups, + msrpc_name_to_sid, + msrpc_sid_to_name, + msrpc_rids_to_names, + query_user, + lookup_usergroups, + msrpc_lookup_useraliases, + lookup_groupmem, + sequence_number, + msrpc_lockout_policy, + msrpc_password_policy, + trusted_domains, +}; diff --git a/source3/winbindd/winbindd_sid.c b/source3/winbindd/winbindd_sid.c new file mode 100644 index 0000000000..48e84d35e5 --- /dev/null +++ b/source3/winbindd/winbindd_sid.c @@ -0,0 +1,560 @@ +/* + Unix SMB/CIFS implementation. + + Winbind daemon - sid related functions + + Copyright (C) Tim Potter 2000 + + 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 3 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, see . +*/ + +#include "includes.h" +#include "winbindd.h" + +#undef DBGC_CLASS +#define DBGC_CLASS DBGC_WINBIND + +/* Convert a string */ + +static void lookupsid_recv(void *private_data, BOOL success, + const char *dom_name, const char *name, + enum lsa_SidType type); + +void winbindd_lookupsid(struct winbindd_cli_state *state) +{ + DOM_SID sid; + + /* Ensure null termination */ + state->request.data.sid[sizeof(state->request.data.sid)-1]='\0'; + + DEBUG(3, ("[%5lu]: lookupsid %s\n", (unsigned long)state->pid, + state->request.data.sid)); + + if (!string_to_sid(&sid, state->request.data.sid)) { + DEBUG(5, ("%s not a SID\n", state->request.data.sid)); + request_error(state); + return; + } + + winbindd_lookupsid_async(state->mem_ctx, &sid, lookupsid_recv, state); +} + +static void lookupsid_recv(void *private_data, BOOL success, + const char *dom_name, const char *name, + enum lsa_SidType type) +{ + struct winbindd_cli_state *state = + talloc_get_type_abort(private_data, struct winbindd_cli_state); + + if (!success) { + DEBUG(5, ("lookupsid returned an error\n")); + request_error(state); + return; + } + + fstrcpy(state->response.data.name.dom_name, dom_name); + fstrcpy(state->response.data.name.name, name); + state->response.data.name.type = type; + request_ok(state); +} + +/** + * Look up the SID for a qualified name. + **/ + +static void lookupname_recv(void *private_data, BOOL success, + const DOM_SID *sid, enum lsa_SidType type); + +void winbindd_lookupname(struct winbindd_cli_state *state) +{ + char *name_domain, *name_user; + char *p; + + /* Ensure null termination */ + state->request.data.name.dom_name[sizeof(state->request.data.name.dom_name)-1]='\0'; + + /* Ensure null termination */ + state->request.data.name.name[sizeof(state->request.data.name.name)-1]='\0'; + + /* cope with the name being a fully qualified name */ + p = strstr(state->request.data.name.name, lp_winbind_separator()); + if (p) { + *p = 0; + name_domain = state->request.data.name.name; + name_user = p+1; + } else { + name_domain = state->request.data.name.dom_name; + name_user = state->request.data.name.name; + } + + DEBUG(3, ("[%5lu]: lookupname %s%s%s\n", (unsigned long)state->pid, + name_domain, lp_winbind_separator(), name_user)); + + winbindd_lookupname_async(state->mem_ctx, name_domain, name_user, + lookupname_recv, WINBINDD_LOOKUPNAME, + state); +} + +static void lookupname_recv(void *private_data, BOOL success, + const DOM_SID *sid, enum lsa_SidType type) +{ + struct winbindd_cli_state *state = + talloc_get_type_abort(private_data, struct winbindd_cli_state); + + if (!success) { + DEBUG(5, ("lookupname returned an error\n")); + request_error(state); + return; + } + + sid_to_string(state->response.data.sid.sid, sid); + state->response.data.sid.type = type; + request_ok(state); + return; +} + +void winbindd_lookuprids(struct winbindd_cli_state *state) +{ + struct winbindd_domain *domain; + DOM_SID domain_sid; + + /* Ensure null termination */ + state->request.data.sid[sizeof(state->request.data.sid)-1]='\0'; + + DEBUG(10, ("lookup_rids: %s\n", state->request.data.sid)); + + if (!string_to_sid(&domain_sid, state->request.data.sid)) { + DEBUG(5, ("Could not convert %s to SID\n", + state->request.data.sid)); + request_error(state); + return; + } + + domain = find_lookup_domain_from_sid(&domain_sid); + if (domain == NULL) { + DEBUG(10, ("Could not find domain for name %s\n", + state->request.domain_name)); + request_error(state); + return; + } + + sendto_domain(state, domain); +} + +static struct winbindd_child static_idmap_child; + +void init_idmap_child(void) +{ + setup_domain_child(NULL, &static_idmap_child, "idmap"); +} + +struct winbindd_child *idmap_child(void) +{ + return &static_idmap_child; +} + +/* Convert a sid to a uid. We assume we only have one rid attached to the + sid. */ + +static void sid2uid_recv(void *private_data, BOOL success, uid_t uid) +{ + struct winbindd_cli_state *state = + talloc_get_type_abort(private_data, struct winbindd_cli_state); + + if (!success) { + DEBUG(5, ("Could not convert sid %s\n", + state->request.data.sid)); + request_error(state); + return; + } + + state->response.data.uid = uid; + request_ok(state); +} + +static void sid2uid_lookupsid_recv( void *private_data, BOOL success, + const char *domain_name, + const char *name, + enum lsa_SidType type) +{ + struct winbindd_cli_state *state = + talloc_get_type_abort(private_data, struct winbindd_cli_state); + DOM_SID sid; + + if (!success) { + DEBUG(5, ("sid2uid_lookupsid_recv Could not convert get sid type for %s\n", + state->request.data.sid)); + request_error(state); + return; + } + + if ( (type!=SID_NAME_USER) && (type!=SID_NAME_COMPUTER) ) { + DEBUG(5,("sid2uid_lookupsid_recv: Sid %s is not a user or a computer.\n", + state->request.data.sid)); + request_error(state); + return; + } + + if (!string_to_sid(&sid, state->request.data.sid)) { + DEBUG(1, ("sid2uid_lookupsid_recv: Could not get convert sid %s from string\n", + state->request.data.sid)); + request_error(state); + return; + } + + /* always use the async interface (may block) */ + winbindd_sid2uid_async(state->mem_ctx, &sid, sid2uid_recv, state); +} + +void winbindd_sid_to_uid(struct winbindd_cli_state *state) +{ + DOM_SID sid; + + /* Ensure null termination */ + state->request.data.sid[sizeof(state->request.data.sid)-1]='\0'; + + DEBUG(3, ("[%5lu]: sid to uid %s\n", (unsigned long)state->pid, + state->request.data.sid)); + + if (!string_to_sid(&sid, state->request.data.sid)) { + DEBUG(1, ("Could not get convert sid %s from string\n", + state->request.data.sid)); + request_error(state); + return; + } + + /* Validate the SID as a user. Hopefully this will hit cache. + Needed to prevent DoS by exhausting the uid allocation + range from random SIDs. */ + + winbindd_lookupsid_async( state->mem_ctx, &sid, sid2uid_lookupsid_recv, state ); +} + +/* Convert a sid to a gid. We assume we only have one rid attached to the + sid.*/ + +static void sid2gid_recv(void *private_data, BOOL success, gid_t gid) +{ + struct winbindd_cli_state *state = + talloc_get_type_abort(private_data, struct winbindd_cli_state); + + if (!success) { + DEBUG(5, ("Could not convert sid %s\n", + state->request.data.sid)); + request_error(state); + return; + } + + state->response.data.gid = gid; + request_ok(state); +} + +static void sid2gid_lookupsid_recv( void *private_data, BOOL success, + const char *domain_name, + const char *name, + enum lsa_SidType type) +{ + struct winbindd_cli_state *state = + talloc_get_type_abort(private_data, struct winbindd_cli_state); + DOM_SID sid; + + if (!success) { + DEBUG(5, ("sid2gid_lookupsid_recv: Could not get sid type for %s\n", + state->request.data.sid)); + request_error(state); + return; + } + + if ( (type!=SID_NAME_DOM_GRP) && + (type!=SID_NAME_ALIAS) && + (type!=SID_NAME_WKN_GRP) ) + { + DEBUG(5,("sid2gid_lookupsid_recv: Sid %s is not a group.\n", + state->request.data.sid)); + request_error(state); + return; + } + + if (!string_to_sid(&sid, state->request.data.sid)) { + DEBUG(1, ("sid2gid_lookupsid_recv: Could not get convert sid %s from string\n", + state->request.data.sid)); + request_error(state); + return; + } + + /* always use the async interface (may block) */ + winbindd_sid2gid_async(state->mem_ctx, &sid, sid2gid_recv, state); +} + +void winbindd_sid_to_gid(struct winbindd_cli_state *state) +{ + DOM_SID sid; + + /* Ensure null termination */ + state->request.data.sid[sizeof(state->request.data.sid)-1]='\0'; + + DEBUG(3, ("[%5lu]: sid to gid %s\n", (unsigned long)state->pid, + state->request.data.sid)); + + if (!string_to_sid(&sid, state->request.data.sid)) { + DEBUG(1, ("Could not get convert sid %s from string\n", + state->request.data.sid)); + request_error(state); + return; + } + + /* Validate the SID as a group. Hopefully this will hit cache. + Needed to prevent DoS by exhausting the uid allocation + range from random SIDs. */ + + winbindd_lookupsid_async( state->mem_ctx, &sid, sid2gid_lookupsid_recv, state ); +} + +static void sids2xids_recv(void *private_data, BOOL success, void *data, int len) +{ + struct winbindd_cli_state *state = + talloc_get_type_abort(private_data, struct winbindd_cli_state); + + if (!success) { + DEBUG(5, ("Could not convert sids to xids\n")); + request_error(state); + return; + } + + state->response.extra_data.data = data; + state->response.length = sizeof(state->response) + len; + request_ok(state); +} + +void winbindd_sids_to_unixids(struct winbindd_cli_state *state) +{ + DEBUG(3, ("[%5lu]: sids to xids\n", (unsigned long)state->pid)); + + winbindd_sids2xids_async(state->mem_ctx, + state->request.extra_data.data, + state->request.extra_len, + sids2xids_recv, state); +} + +static void set_mapping_recv(void *private_data, BOOL success) +{ + struct winbindd_cli_state *state = + talloc_get_type_abort(private_data, struct winbindd_cli_state); + + if (!success) { + DEBUG(5, ("Could not set sid mapping\n")); + request_error(state); + return; + } + + request_ok(state); +} + +void winbindd_set_mapping(struct winbindd_cli_state *state) +{ + struct id_map map; + DOM_SID sid; + + DEBUG(3, ("[%5lu]: set id map\n", (unsigned long)state->pid)); + + if ( ! state->privileged) { + DEBUG(0, ("Only root is allowed to set mappings!\n")); + request_error(state); + return; + } + + if (!string_to_sid(&sid, state->request.data.dual_idmapset.sid)) { + DEBUG(1, ("Could not get convert sid %s from string\n", + state->request.data.sid)); + request_error(state); + return; + } + + map.sid = &sid; + map.xid.id = state->request.data.dual_idmapset.id; + map.xid.type = state->request.data.dual_idmapset.type; + + winbindd_set_mapping_async(state->mem_ctx, &map, + set_mapping_recv, state); +} + +static void set_hwm_recv(void *private_data, BOOL success) +{ + struct winbindd_cli_state *state = + talloc_get_type_abort(private_data, struct winbindd_cli_state); + + if (!success) { + DEBUG(5, ("Could not set sid mapping\n")); + request_error(state); + return; + } + + request_ok(state); +} + +void winbindd_set_hwm(struct winbindd_cli_state *state) +{ + struct unixid xid; + + DEBUG(3, ("[%5lu]: set hwm\n", (unsigned long)state->pid)); + + if ( ! state->privileged) { + DEBUG(0, ("Only root is allowed to set mappings!\n")); + request_error(state); + return; + } + + xid.id = state->request.data.dual_idmapset.id; + xid.type = state->request.data.dual_idmapset.type; + + winbindd_set_hwm_async(state->mem_ctx, &xid, set_hwm_recv, state); +} + +/* Convert a uid to a sid */ + +static void uid2sid_recv(void *private_data, BOOL success, const char *sid) +{ + struct winbindd_cli_state *state = + (struct winbindd_cli_state *)private_data; + + if (success) { + DEBUG(10,("uid2sid: uid %lu has sid %s\n", + (unsigned long)(state->request.data.uid), sid)); + fstrcpy(state->response.data.sid.sid, sid); + state->response.data.sid.type = SID_NAME_USER; + request_ok(state); + return; + } + + request_error(state); + return; +} + +void winbindd_uid_to_sid(struct winbindd_cli_state *state) +{ + DEBUG(3, ("[%5lu]: uid to sid %lu\n", (unsigned long)state->pid, + (unsigned long)state->request.data.uid)); + + /* always go via the async interface (may block) */ + winbindd_uid2sid_async(state->mem_ctx, state->request.data.uid, uid2sid_recv, state); +} + +/* Convert a gid to a sid */ + +static void gid2sid_recv(void *private_data, BOOL success, const char *sid) +{ + struct winbindd_cli_state *state = + (struct winbindd_cli_state *)private_data; + + if (success) { + DEBUG(10,("gid2sid: gid %lu has sid %s\n", + (unsigned long)(state->request.data.gid), sid)); + fstrcpy(state->response.data.sid.sid, sid); + state->response.data.sid.type = SID_NAME_DOM_GRP; + request_ok(state); + return; + } + + request_error(state); + return; +} + + +void winbindd_gid_to_sid(struct winbindd_cli_state *state) +{ + DEBUG(3, ("[%5lu]: gid to sid %lu\n", (unsigned long)state->pid, + (unsigned long)state->request.data.gid)); + + /* always use async calls (may block) */ + winbindd_gid2sid_async(state->mem_ctx, state->request.data.gid, gid2sid_recv, state); +} + +void winbindd_allocate_uid(struct winbindd_cli_state *state) +{ + if ( !state->privileged ) { + DEBUG(2, ("winbindd_allocate_uid: non-privileged access " + "denied!\n")); + request_error(state); + return; + } + + sendto_child(state, idmap_child()); +} + +enum winbindd_result winbindd_dual_allocate_uid(struct winbindd_domain *domain, + struct winbindd_cli_state *state) +{ + struct unixid xid; + + if (!NT_STATUS_IS_OK(idmap_allocate_uid(&xid))) { + return WINBINDD_ERROR; + } + state->response.data.uid = xid.id; + return WINBINDD_OK; +} + +void winbindd_allocate_gid(struct winbindd_cli_state *state) +{ + if ( !state->privileged ) { + DEBUG(2, ("winbindd_allocate_gid: non-privileged access " + "denied!\n")); + request_error(state); + return; + } + + sendto_child(state, idmap_child()); +} + +enum winbindd_result winbindd_dual_allocate_gid(struct winbindd_domain *domain, + struct winbindd_cli_state *state) +{ + struct unixid xid; + + if (!NT_STATUS_IS_OK(idmap_allocate_gid(&xid))) { + return WINBINDD_ERROR; + } + state->response.data.gid = xid.id; + return WINBINDD_OK; +} + +static void dump_maps_recv(void *private_data, BOOL success) +{ + struct winbindd_cli_state *state = + talloc_get_type_abort(private_data, struct winbindd_cli_state); + + if (!success) { + DEBUG(5, ("Could not dump maps\n")); + request_error(state); + return; + } + + request_ok(state); +} + +void winbindd_dump_maps(struct winbindd_cli_state *state) +{ + if ( ! state->privileged) { + DEBUG(0, ("Only root is allowed to ask for an idmap dump!\n")); + request_error(state); + return; + } + + DEBUG(3, ("[%5lu]: dump maps\n", (unsigned long)state->pid)); + + winbindd_dump_maps_async(state->mem_ctx, + state->request.extra_data.data, + state->request.extra_len, + dump_maps_recv, state); +} + diff --git a/source3/winbindd/winbindd_sockinit.c b/source3/winbindd/winbindd_sockinit.c new file mode 100644 index 0000000000..50c53a5045 --- /dev/null +++ b/source3/winbindd/winbindd_sockinit.c @@ -0,0 +1,126 @@ +/* + Unix SMB/CIFS implementation. + Copyright (C) Tim Potter 2000-2001 + Copyright (C) 2001 by Martin Pool + Copyright (C) James Peach 2007 + + 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 3 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, see . +*/ + +#include "includes.h" +#include "winbindd.h" +#include "smb_launchd.h" + +#undef DBGC_CLASS +#define DBGC_CLASS DBGC_WINBIND + +/* Open the winbindd socket */ + +static int _winbindd_socket = -1; +static int _winbindd_priv_socket = -1; +static BOOL unlink_winbindd_socket = True; + +static int open_winbindd_socket(void) +{ + if (_winbindd_socket == -1) { + _winbindd_socket = create_pipe_sock( + WINBINDD_SOCKET_DIR, WINBINDD_SOCKET_NAME, 0755); + DEBUG(10, ("open_winbindd_socket: opened socket fd %d\n", + _winbindd_socket)); + } + + return _winbindd_socket; +} + +static int open_winbindd_priv_socket(void) +{ + if (_winbindd_priv_socket == -1) { + _winbindd_priv_socket = create_pipe_sock( + get_winbind_priv_pipe_dir(), WINBINDD_SOCKET_NAME, 0750); + DEBUG(10, ("open_winbindd_priv_socket: opened socket fd %d\n", + _winbindd_priv_socket)); + } + + return _winbindd_priv_socket; +} + +/* Close the winbindd socket */ + +static void close_winbindd_socket(void) +{ + if (_winbindd_socket != -1) { + DEBUG(10, ("close_winbindd_socket: closing socket fd %d\n", + _winbindd_socket)); + close(_winbindd_socket); + _winbindd_socket = -1; + } + if (_winbindd_priv_socket != -1) { + DEBUG(10, ("close_winbindd_socket: closing socket fd %d\n", + _winbindd_priv_socket)); + close(_winbindd_priv_socket); + _winbindd_priv_socket = -1; + } +} + +BOOL winbindd_init_sockets(int *public_sock, int *priv_sock, + int *idle_timeout_sec) +{ + struct smb_launch_info linfo; + + if (smb_launchd_checkin_names(&linfo, "WinbindPublicPipe", + "WinbindPrivilegedPipe", NULL)) { + if (linfo.num_sockets != 2) { + DEBUG(0, ("invalid launchd configuration, " + "expected 2 sockets but got %d\n", + linfo.num_sockets)); + return False; + } + + *public_sock = _winbindd_socket = linfo.socket_list[0]; + *priv_sock = _winbindd_priv_socket = linfo.socket_list[1]; + *idle_timeout_sec = linfo.idle_timeout_secs; + + unlink_winbindd_socket = False; + + smb_launchd_checkout(&linfo); + return True; + } else { + *public_sock = open_winbindd_socket(); + *priv_sock = open_winbindd_priv_socket(); + *idle_timeout_sec = -1; + + if (*public_sock == -1 || *priv_sock == -1) { + DEBUG(0, ("failed to open winbindd pipes: %s\n", + errno ? strerror(errno) : "unknown error")); + return False; + } + + return True; + } +} + +void winbindd_release_sockets(void) +{ + pstring path; + + close_winbindd_socket(); + + /* Remove socket file */ + if (unlink_winbindd_socket) { + pstr_sprintf(path, "%s/%s", + WINBINDD_SOCKET_DIR, WINBINDD_SOCKET_NAME); + unlink(path); + } +} + diff --git a/source3/winbindd/winbindd_user.c b/source3/winbindd/winbindd_user.c new file mode 100644 index 0000000000..fac2832f56 --- /dev/null +++ b/source3/winbindd/winbindd_user.c @@ -0,0 +1,875 @@ +/* + Unix SMB/CIFS implementation. + + Winbind daemon - user related functions + + Copyright (C) Tim Potter 2000 + Copyright (C) Jeremy Allison 2001. + Copyright (C) Gerald (Jerry) Carter 2003. + + 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 3 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, see . +*/ + +#include "includes.h" +#include "winbindd.h" + +#undef DBGC_CLASS +#define DBGC_CLASS DBGC_WINBIND + +static BOOL fillup_pw_field(const char *lp_template, + const char *username, + const char *domname, + uid_t uid, + gid_t gid, + const char *in, + fstring out) +{ + char *templ; + + if (out == NULL) + return False; + + /* The substitution of %U and %D in the 'template + homedir' is done by talloc_sub_specified() below. + If we have an in string (which means the value has already + been set in the nss_info backend), then use that. + Otherwise use the template value passed in. */ + + if ( in && !strequal(in,"") && lp_security() == SEC_ADS ) { + templ = talloc_sub_specified(NULL, in, + username, domname, + uid, gid); + } else { + templ = talloc_sub_specified(NULL, lp_template, + username, domname, + uid, gid); + } + + if (!templ) + return False; + + safe_strcpy(out, templ, sizeof(fstring) - 1); + TALLOC_FREE(templ); + + return True; + +} +/* Fill a pwent structure with information we have obtained */ + +static BOOL winbindd_fill_pwent(char *dom_name, char *user_name, + DOM_SID *user_sid, DOM_SID *group_sid, + char *full_name, char *homedir, char *shell, + struct winbindd_pw *pw) +{ + fstring output_username; + fstring sid_string; + + if (!pw || !dom_name || !user_name) + return False; + + /* Resolve the uid number */ + + if (!NT_STATUS_IS_OK(idmap_sid_to_uid(user_sid, &pw->pw_uid))) { + DEBUG(1, ("error getting user id for sid %s\n", sid_to_string(sid_string, user_sid))); + return False; + } + + /* Resolve the gid number */ + + if (!NT_STATUS_IS_OK(idmap_sid_to_gid(group_sid, &pw->pw_gid))) { + DEBUG(1, ("error getting group id for sid %s\n", sid_to_string(sid_string, group_sid))); + return False; + } + + strlower_m(user_name); + + /* Username */ + + fill_domain_username(output_username, dom_name, user_name, True); + + safe_strcpy(pw->pw_name, output_username, sizeof(pw->pw_name) - 1); + + /* Full name (gecos) */ + + safe_strcpy(pw->pw_gecos, full_name, sizeof(pw->pw_gecos) - 1); + + /* Home directory and shell */ + + if (!fillup_pw_field(lp_template_homedir(), user_name, dom_name, + pw->pw_uid, pw->pw_gid, homedir, pw->pw_dir)) + return False; + + if (!fillup_pw_field(lp_template_shell(), user_name, dom_name, + pw->pw_uid, pw->pw_gid, shell, pw->pw_shell)) + return False; + + /* Password - set to "*" as we can't generate anything useful here. + Authentication can be done using the pam_winbind module. */ + + safe_strcpy(pw->pw_passwd, "*", sizeof(pw->pw_passwd) - 1); + + return True; +} + +/* Wrapper for domain->methods->query_user, only on the parent->child pipe */ + +enum winbindd_result winbindd_dual_userinfo(struct winbindd_domain *domain, + struct winbindd_cli_state *state) +{ + DOM_SID sid; + WINBIND_USERINFO user_info; + NTSTATUS status; + + /* Ensure null termination */ + state->request.data.sid[sizeof(state->request.data.sid)-1]='\0'; + + DEBUG(3, ("[%5lu]: lookupsid %s\n", (unsigned long)state->pid, + state->request.data.sid)); + + if (!string_to_sid(&sid, state->request.data.sid)) { + DEBUG(5, ("%s not a SID\n", state->request.data.sid)); + return WINBINDD_ERROR; + } + + status = domain->methods->query_user(domain, state->mem_ctx, + &sid, &user_info); + if (!NT_STATUS_IS_OK(status)) { + DEBUG(1, ("error getting user info for sid %s\n", + sid_string_static(&sid))); + return WINBINDD_ERROR; + } + + fstrcpy(state->response.data.user_info.acct_name, user_info.acct_name); + fstrcpy(state->response.data.user_info.full_name, user_info.full_name); + fstrcpy(state->response.data.user_info.homedir, user_info.homedir); + fstrcpy(state->response.data.user_info.shell, user_info.shell); + state->response.data.user_info.primary_gid = user_info.primary_gid; + if (!sid_peek_check_rid(&domain->sid, &user_info.group_sid, + &state->response.data.user_info.group_rid)) { + DEBUG(1, ("Could not extract group rid out of %s\n", + sid_string_static(&sid))); + return WINBINDD_ERROR; + } + + return WINBINDD_OK; +} + +struct getpwsid_state { + struct winbindd_cli_state *state; + struct winbindd_domain *domain; + char *username; + char *fullname; + char *homedir; + char *shell; + DOM_SID user_sid; + uid_t uid; + DOM_SID group_sid; + gid_t gid; +}; + +static void getpwsid_queryuser_recv(void *private_data, BOOL success, + const char *acct_name, + const char *full_name, + const char *homedir, + const char *shell, + uint32 gid, + uint32 group_rid); +static void getpwsid_sid2uid_recv(void *private_data, BOOL success, uid_t uid); +static void getpwsid_sid2gid_recv(void *private_data, BOOL success, gid_t gid); + +static void winbindd_getpwsid(struct winbindd_cli_state *state, + const DOM_SID *sid) +{ + struct getpwsid_state *s; + + s = TALLOC_ZERO_P(state->mem_ctx, struct getpwsid_state); + if (s == NULL) { + DEBUG(0, ("talloc failed\n")); + goto error; + } + + s->state = state; + s->domain = find_domain_from_sid_noinit(sid); + if (s->domain == NULL) { + DEBUG(3, ("Could not find domain for sid %s\n", + sid_string_static(sid))); + goto error; + } + + sid_copy(&s->user_sid, sid); + + query_user_async(s->state->mem_ctx, s->domain, sid, + getpwsid_queryuser_recv, s); + return; + + error: + request_error(state); +} + +static void getpwsid_queryuser_recv(void *private_data, BOOL success, + const char *acct_name, + const char *full_name, + const char *homedir, + const char *shell, + uint32 gid, + uint32 group_rid) +{ + fstring username; + struct getpwsid_state *s = + talloc_get_type_abort(private_data, struct getpwsid_state); + + if (!success) { + DEBUG(5, ("Could not query domain %s SID %s\n", s->domain->name, + sid_string_static(&s->user_sid))); + request_error(s->state); + return; + } + + if ( acct_name && *acct_name ) { + fstrcpy( username, acct_name ); + } else { + char *domain_name = NULL; + enum lsa_SidType type; + char *user_name = NULL; + struct winbindd_domain *domain = NULL; + + domain = find_lookup_domain_from_sid(&s->user_sid); + if (domain == NULL) { + DEBUG(5, ("find_lookup_domain_from_sid(%s) failed\n", + sid_string_static(&s->user_sid))); + request_error(s->state); + return; + } + winbindd_lookup_name_by_sid(s->state->mem_ctx, domain, + &s->user_sid, &domain_name, + &user_name, &type ); + + /* If this still fails we ar4e done. Just error out */ + if ( !user_name ) { + DEBUG(5,("Could not obtain a name for SID %s\n", + sid_string_static(&s->user_sid))); + request_error(s->state); + return; + } + + fstrcpy( username, user_name ); + } + + strlower_m( username ); + s->username = talloc_strdup(s->state->mem_ctx, username); + + ws_name_replace( s->username, WB_REPLACE_CHAR ); + + s->fullname = talloc_strdup(s->state->mem_ctx, full_name); + s->homedir = talloc_strdup(s->state->mem_ctx, homedir); + s->shell = talloc_strdup(s->state->mem_ctx, shell); + s->gid = gid; + sid_copy(&s->group_sid, &s->domain->sid); + sid_append_rid(&s->group_sid, group_rid); + + winbindd_sid2uid_async(s->state->mem_ctx, &s->user_sid, + getpwsid_sid2uid_recv, s); +} + +static void getpwsid_sid2uid_recv(void *private_data, BOOL success, uid_t uid) +{ + struct getpwsid_state *s = + talloc_get_type_abort(private_data, struct getpwsid_state); + + if (!success) { + DEBUG(5, ("Could not query uid for user %s\\%s\n", + s->domain->name, s->username)); + request_error(s->state); + return; + } + + s->uid = uid; + winbindd_sid2gid_async(s->state->mem_ctx, &s->group_sid, + getpwsid_sid2gid_recv, s); +} + +static void getpwsid_sid2gid_recv(void *private_data, BOOL success, gid_t gid) +{ + struct getpwsid_state *s = + talloc_get_type_abort(private_data, struct getpwsid_state); + struct winbindd_pw *pw; + fstring output_username; + + /* allow the nss backend to override the primary group ID. + If the gid has already been set, then keep it. + This makes me feel dirty. If the nss backend already + gave us a gid, we don't really care whether the sid2gid() + call worked or not. --jerry */ + + if ( s->gid == (gid_t)-1 ) { + + if (!success) { + DEBUG(5, ("Could not query gid for user %s\\%s\n", + s->domain->name, s->username)); + goto failed; + } + + /* take what the sid2gid() call gave us */ + s->gid = gid; + } + + pw = &s->state->response.data.pw; + pw->pw_uid = s->uid; + pw->pw_gid = s->gid; + fill_domain_username(output_username, s->domain->name, s->username, True); + safe_strcpy(pw->pw_name, output_username, sizeof(pw->pw_name) - 1); + safe_strcpy(pw->pw_gecos, s->fullname, sizeof(pw->pw_gecos) - 1); + + if (!fillup_pw_field(lp_template_homedir(), s->username, s->domain->name, + pw->pw_uid, pw->pw_gid, s->homedir, pw->pw_dir)) { + DEBUG(5, ("Could not compose homedir\n")); + goto failed; + } + + if (!fillup_pw_field(lp_template_shell(), s->username, s->domain->name, + pw->pw_uid, pw->pw_gid, s->shell, pw->pw_shell)) { + DEBUG(5, ("Could not compose shell\n")); + goto failed; + } + + /* Password - set to "*" as we can't generate anything useful here. + Authentication can be done using the pam_winbind module. */ + + safe_strcpy(pw->pw_passwd, "*", sizeof(pw->pw_passwd) - 1); + + request_ok(s->state); + return; + + failed: + request_error(s->state); +} + +/* Return a password structure from a username. */ + +static void getpwnam_name2sid_recv(void *private_data, BOOL success, + const DOM_SID *sid, enum lsa_SidType type); + +void winbindd_getpwnam(struct winbindd_cli_state *state) +{ + struct winbindd_domain *domain; + fstring domname, username; + + /* Ensure null termination */ + state->request.data.username[sizeof(state->request.data.username)-1]='\0'; + + DEBUG(3, ("[%5lu]: getpwnam %s\n", (unsigned long)state->pid, + state->request.data.username)); + + ws_name_return( state->request.data.username, WB_REPLACE_CHAR ); + + if (!parse_domain_user(state->request.data.username, domname, + username)) { + DEBUG(5, ("Could not parse domain user: %s\n", + state->request.data.username)); + request_error(state); + return; + } + + /* Get info for the domain */ + + domain = find_domain_from_name(domname); + + if (domain == NULL) { + DEBUG(7, ("could not find domain entry for domain %s. " + "Using primary domain\n", domname)); + if ( (domain = find_our_domain()) == NULL ) { + DEBUG(0,("Cannot find my primary domain structure!\n")); + request_error(state); + return; + } + } + + if ( strequal(domname, lp_workgroup()) && lp_winbind_trusted_domains_only() ) { + DEBUG(7,("winbindd_getpwnam: My domain -- rejecting getpwnam() for %s\\%s.\n", + domname, username)); + request_error(state); + return; + } + + /* Get rid and name type from name. The following costs 1 packet */ + + winbindd_lookupname_async(state->mem_ctx, domname, username, + getpwnam_name2sid_recv, WINBINDD_GETPWNAM, + state); +} + +static void getpwnam_name2sid_recv(void *private_data, BOOL success, + const DOM_SID *sid, enum lsa_SidType type) +{ + struct winbindd_cli_state *state = + (struct winbindd_cli_state *)private_data; + fstring domname, username; + + if (!success) { + DEBUG(5, ("Could not lookup name for user %s\n", + state->request.data.username)); + request_error(state); + return; + } + + if ((type != SID_NAME_USER) && (type != SID_NAME_COMPUTER)) { + DEBUG(5, ("%s is not a user\n", state->request.data.username)); + request_error(state); + return; + } + + if ( parse_domain_user(state->request.data.username, domname, username) ) { + check_domain_trusted( domname, sid ); + } + + + + winbindd_getpwsid(state, sid); +} + +static void getpwuid_recv(void *private_data, BOOL success, const char *sid) +{ + struct winbindd_cli_state *state = + (struct winbindd_cli_state *)private_data; + DOM_SID user_sid; + + if (!success) { + DEBUG(10,("uid2sid_recv: uid [%lu] to sid mapping failed\n.", + (unsigned long)(state->request.data.uid))); + request_error(state); + return; + } + + DEBUG(10,("uid2sid_recv: uid %lu has sid %s\n", + (unsigned long)(state->request.data.uid), sid)); + + string_to_sid(&user_sid, sid); + winbindd_getpwsid(state, &user_sid); +} + +/* Return a password structure given a uid number */ +void winbindd_getpwuid(struct winbindd_cli_state *state) +{ + DEBUG(3, ("[%5lu]: getpwuid %lu\n", (unsigned long)state->pid, + (unsigned long)state->request.data.uid)); + + /* always query idmap via the async interface */ + /* if this turns to be too slow we will add here a direct query to the cache */ + winbindd_uid2sid_async(state->mem_ctx, state->request.data.uid, getpwuid_recv, state); +} + +/* + * set/get/endpwent functions + */ + +/* Rewind file pointer for ntdom passwd database */ + +static BOOL winbindd_setpwent_internal(struct winbindd_cli_state *state) +{ + struct winbindd_domain *domain; + + DEBUG(3, ("[%5lu]: setpwent\n", (unsigned long)state->pid)); + + /* Check user has enabled this */ + + if (!lp_winbind_enum_users()) { + return False; + } + + /* Free old static data if it exists */ + + if (state->getpwent_state != NULL) { + free_getent_state(state->getpwent_state); + state->getpwent_state = NULL; + } + +#if 0 /* JERRY */ + /* add any local users we have */ + + if ( (domain_state = (struct getent_state *)malloc(sizeof(struct getent_state))) == NULL ) + return False; + + ZERO_STRUCTP(domain_state); + + /* Add to list of open domains */ + + DLIST_ADD(state->getpwent_state, domain_state); +#endif + + /* Create sam pipes for each domain we know about */ + + for(domain = domain_list(); domain != NULL; domain = domain->next) { + struct getent_state *domain_state; + + + /* don't add our domaina if we are a PDC or if we + are a member of a Samba domain */ + + if ( (IS_DC || lp_winbind_trusted_domains_only()) + && strequal(domain->name, lp_workgroup()) ) + { + continue; + } + + /* Create a state record for this domain */ + + if ((domain_state = SMB_MALLOC_P(struct getent_state)) == NULL) { + DEBUG(0, ("malloc failed\n")); + return False; + } + + ZERO_STRUCTP(domain_state); + + fstrcpy(domain_state->domain_name, domain->name); + + /* Add to list of open domains */ + + DLIST_ADD(state->getpwent_state, domain_state); + } + + state->getpwent_initialized = True; + return True; +} + +void winbindd_setpwent(struct winbindd_cli_state *state) +{ + if (winbindd_setpwent_internal(state)) { + request_ok(state); + } else { + request_error(state); + } +} + +/* Close file pointer to ntdom passwd database */ + +void winbindd_endpwent(struct winbindd_cli_state *state) +{ + DEBUG(3, ("[%5lu]: endpwent\n", (unsigned long)state->pid)); + + free_getent_state(state->getpwent_state); + state->getpwent_initialized = False; + state->getpwent_state = NULL; + request_ok(state); +} + +/* Get partial list of domain users for a domain. We fill in the sam_entries, + and num_sam_entries fields with domain user information. The dispinfo_ndx + field is incremented to the index of the next user to fetch. Return True if + some users were returned, False otherwise. */ + +static BOOL get_sam_user_entries(struct getent_state *ent, TALLOC_CTX *mem_ctx) +{ + NTSTATUS status; + uint32 num_entries; + WINBIND_USERINFO *info; + struct getpwent_user *name_list = NULL; + struct winbindd_domain *domain; + struct winbindd_methods *methods; + unsigned int i; + + if (ent->num_sam_entries) + return False; + + if (!(domain = find_domain_from_name(ent->domain_name))) { + DEBUG(3, ("no such domain %s in get_sam_user_entries\n", + ent->domain_name)); + return False; + } + + methods = domain->methods; + + /* Free any existing user info */ + + SAFE_FREE(ent->sam_entries); + ent->num_sam_entries = 0; + + /* Call query_user_list to get a list of usernames and user rids */ + + num_entries = 0; + + status = methods->query_user_list(domain, mem_ctx, &num_entries, + &info); + + if (!NT_STATUS_IS_OK(status)) { + DEBUG(10,("get_sam_user_entries: query_user_list failed with %s\n", + nt_errstr(status) )); + return False; + } + + if (num_entries) { + name_list = SMB_REALLOC_ARRAY(name_list, struct getpwent_user, ent->num_sam_entries + num_entries); + + if (!name_list) { + DEBUG(0,("get_sam_user_entries realloc failed.\n")); + return False; + } + } + + for (i = 0; i < num_entries; i++) { + /* Store account name and gecos */ + if (!info[i].acct_name) { + fstrcpy(name_list[ent->num_sam_entries + i].name, ""); + } else { + fstrcpy(name_list[ent->num_sam_entries + i].name, + info[i].acct_name); + } + if (!info[i].full_name) { + fstrcpy(name_list[ent->num_sam_entries + i].gecos, ""); + } else { + fstrcpy(name_list[ent->num_sam_entries + i].gecos, + info[i].full_name); + } + if (!info[i].homedir) { + fstrcpy(name_list[ent->num_sam_entries + i].homedir, ""); + } else { + fstrcpy(name_list[ent->num_sam_entries + i].homedir, + info[i].homedir); + } + if (!info[i].shell) { + fstrcpy(name_list[ent->num_sam_entries + i].shell, ""); + } else { + fstrcpy(name_list[ent->num_sam_entries + i].shell, + info[i].shell); + } + + + /* User and group ids */ + sid_copy(&name_list[ent->num_sam_entries+i].user_sid, + &info[i].user_sid); + sid_copy(&name_list[ent->num_sam_entries+i].group_sid, + &info[i].group_sid); + } + + ent->num_sam_entries += num_entries; + + /* Fill in remaining fields */ + + ent->sam_entries = name_list; + ent->sam_entry_index = 0; + return ent->num_sam_entries > 0; +} + +/* Fetch next passwd entry from ntdom database */ + +#define MAX_GETPWENT_USERS 500 + +void winbindd_getpwent(struct winbindd_cli_state *state) +{ + struct getent_state *ent; + struct winbindd_pw *user_list; + int num_users, user_list_ndx; + + DEBUG(3, ("[%5lu]: getpwent\n", (unsigned long)state->pid)); + + /* Check user has enabled this */ + + if (!lp_winbind_enum_users()) { + request_error(state); + return; + } + + /* Allocate space for returning a chunk of users */ + + num_users = MIN(MAX_GETPWENT_USERS, state->request.data.num_entries); + + if (num_users == 0) { + request_error(state); + return; + } + + if ((state->response.extra_data.data = SMB_MALLOC_ARRAY(struct winbindd_pw, num_users)) == NULL) { + request_error(state); + return; + } + + memset(state->response.extra_data.data, 0, num_users * + sizeof(struct winbindd_pw)); + + user_list = (struct winbindd_pw *)state->response.extra_data.data; + + if (!state->getpwent_initialized) + winbindd_setpwent_internal(state); + + if (!(ent = state->getpwent_state)) { + request_error(state); + return; + } + + /* Start sending back users */ + + for (user_list_ndx = 0; user_list_ndx < num_users; ) { + struct getpwent_user *name_list = NULL; + uint32 result; + + /* Do we need to fetch another chunk of users? */ + + if (ent->num_sam_entries == ent->sam_entry_index) { + + while(ent && + !get_sam_user_entries(ent, state->mem_ctx)) { + struct getent_state *next_ent; + + /* Free state information for this domain */ + + SAFE_FREE(ent->sam_entries); + + next_ent = ent->next; + DLIST_REMOVE(state->getpwent_state, ent); + + SAFE_FREE(ent); + ent = next_ent; + } + + /* No more domains */ + + if (!ent) + break; + } + + name_list = (struct getpwent_user *)ent->sam_entries; + + /* Lookup user info */ + + result = winbindd_fill_pwent( + ent->domain_name, + name_list[ent->sam_entry_index].name, + &name_list[ent->sam_entry_index].user_sid, + &name_list[ent->sam_entry_index].group_sid, + name_list[ent->sam_entry_index].gecos, + name_list[ent->sam_entry_index].homedir, + name_list[ent->sam_entry_index].shell, + &user_list[user_list_ndx]); + + /* Add user to return list */ + + if (result) { + + user_list_ndx++; + state->response.data.num_entries++; + state->response.length += + sizeof(struct winbindd_pw); + + } else + DEBUG(1, ("could not lookup domain user %s\n", + name_list[ent->sam_entry_index].name)); + + ent->sam_entry_index++; + + } + + /* Out of domains */ + + if (user_list_ndx > 0) + request_ok(state); + else + request_error(state); +} + +/* List domain users without mapping to unix ids */ + +void winbindd_list_users(struct winbindd_cli_state *state) +{ + struct winbindd_domain *domain; + WINBIND_USERINFO *info; + const char *which_domain; + uint32 num_entries = 0, total_entries = 0; + char *extra_data = NULL; + int extra_data_len = 0; + enum winbindd_result rv = WINBINDD_ERROR; + + DEBUG(3, ("[%5lu]: list users\n", (unsigned long)state->pid)); + + /* Ensure null termination */ + state->request.domain_name[sizeof(state->request.domain_name)-1]='\0'; + which_domain = state->request.domain_name; + + /* Enumerate over trusted domains */ + + for (domain = domain_list(); domain; domain = domain->next) { + NTSTATUS status; + struct winbindd_methods *methods; + unsigned int i; + + /* if we have a domain name restricting the request and this + one in the list doesn't match, then just bypass the remainder + of the loop */ + + if ( *which_domain && !strequal(which_domain, domain->name) ) + continue; + + methods = domain->methods; + + /* Query display info */ + status = methods->query_user_list(domain, state->mem_ctx, + &num_entries, &info); + + if (!NT_STATUS_IS_OK(status)) { + continue; + } + + if (num_entries == 0) + continue; + + /* Allocate some memory for extra data */ + total_entries += num_entries; + + extra_data = (char *)SMB_REALLOC( + extra_data, sizeof(fstring) * total_entries); + + if (!extra_data) { + DEBUG(0,("failed to enlarge buffer!\n")); + goto done; + } + + /* Pack user list into extra data fields */ + + for (i = 0; i < num_entries; i++) { + fstring acct_name, name; + + if (!info[i].acct_name) { + fstrcpy(acct_name, ""); + } else { + fstrcpy(acct_name, info[i].acct_name); + } + + fill_domain_username(name, domain->name, acct_name, True); + + /* Append to extra data */ + memcpy(&extra_data[extra_data_len], name, + strlen(name)); + extra_data_len += strlen(name); + extra_data[extra_data_len++] = ','; + } + } + + /* Assign extra_data fields in response structure */ + + if (extra_data) { + extra_data[extra_data_len - 1] = '\0'; + state->response.extra_data.data = extra_data; + state->response.length += extra_data_len; + } + + /* No domains responded but that's still OK so don't return an + error. */ + + rv = WINBINDD_OK; + + done: + + if (rv == WINBINDD_OK) + request_ok(state); + else + request_error(state); +} diff --git a/source3/winbindd/winbindd_util.c b/source3/winbindd/winbindd_util.c new file mode 100644 index 0000000000..37d29e1765 --- /dev/null +++ b/source3/winbindd/winbindd_util.c @@ -0,0 +1,1460 @@ +/* + Unix SMB/CIFS implementation. + + Winbind daemon for ntdom nss module + + Copyright (C) Tim Potter 2000-2001 + Copyright (C) 2001 by Martin Pool + + 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 3 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, see . +*/ + +#include "includes.h" +#include "winbindd.h" + +#undef DBGC_CLASS +#define DBGC_CLASS DBGC_WINBIND + +extern struct winbindd_methods cache_methods; +extern struct winbindd_methods passdb_methods; + +/** + * @file winbindd_util.c + * + * Winbind daemon for NT domain authentication nss module. + **/ + +/* The list of trusted domains. Note that the list can be deleted and + recreated using the init_domain_list() function so pointers to + individual winbindd_domain structures cannot be made. Keep a copy of + the domain name instead. */ + +static struct winbindd_domain *_domain_list; + +/** + When was the last scan of trusted domains done? + + 0 == not ever +*/ + +static time_t last_trustdom_scan; + +struct winbindd_domain *domain_list(void) +{ + /* Initialise list */ + + if ((!_domain_list) && (!init_domain_list())) { + smb_panic("Init_domain_list failed"); + } + + return _domain_list; +} + +/* Free all entries in the trusted domain list */ + +void free_domain_list(void) +{ + struct winbindd_domain *domain = _domain_list; + + while(domain) { + struct winbindd_domain *next = domain->next; + + DLIST_REMOVE(_domain_list, domain); + SAFE_FREE(domain); + domain = next; + } +} + +static BOOL is_internal_domain(const DOM_SID *sid) +{ + if (sid == NULL) + return False; + + if ( IS_DC ) + return sid_check_is_builtin(sid); + + return (sid_check_is_domain(sid) || sid_check_is_builtin(sid)); +} + +static BOOL is_in_internal_domain(const DOM_SID *sid) +{ + if (sid == NULL) + return False; + + if ( IS_DC ) + return sid_check_is_in_builtin(sid); + + return (sid_check_is_in_our_domain(sid) || sid_check_is_in_builtin(sid)); +} + + +/* Add a trusted domain to our list of domains */ +static struct winbindd_domain *add_trusted_domain(const char *domain_name, const char *alt_name, + struct winbindd_methods *methods, + const DOM_SID *sid) +{ + struct winbindd_domain *domain; + const char *alternative_name = NULL; + + /* ignore alt_name if we are not in an AD domain */ + + if ( (lp_security() == SEC_ADS) && alt_name && *alt_name) { + alternative_name = alt_name; + } + + /* We can't call domain_list() as this function is called from + init_domain_list() and we'll get stuck in a loop. */ + for (domain = _domain_list; domain; domain = domain->next) { + if (strequal(domain_name, domain->name) || + strequal(domain_name, domain->alt_name)) + { + break; + } + + if (alternative_name && *alternative_name) + { + if (strequal(alternative_name, domain->name) || + strequal(alternative_name, domain->alt_name)) + { + break; + } + } + + if (sid) + { + if (is_null_sid(sid)) { + continue; + } + + if (sid_equal(sid, &domain->sid)) { + break; + } + } + } + + /* See if we found a match. Check if we need to update the + SID. */ + + if ( domain && sid) { + if ( sid_equal( &domain->sid, &global_sid_NULL ) ) + sid_copy( &domain->sid, sid ); + + return domain; + } + + /* Create new domain entry */ + + if ((domain = SMB_MALLOC_P(struct winbindd_domain)) == NULL) + return NULL; + + /* Fill in fields */ + + ZERO_STRUCTP(domain); + + /* prioritise the short name */ + if (strchr_m(domain_name, '.') && alternative_name && *alternative_name) { + fstrcpy(domain->name, alternative_name); + fstrcpy(domain->alt_name, domain_name); + } else { + fstrcpy(domain->name, domain_name); + if (alternative_name) { + fstrcpy(domain->alt_name, alternative_name); + } + } + + domain->methods = methods; + domain->backend = NULL; + domain->internal = is_internal_domain(sid); + domain->sequence_number = DOM_SEQUENCE_NONE; + domain->last_seq_check = 0; + domain->initialized = False; + domain->online = is_internal_domain(sid); + domain->check_online_timeout = 0; + if (sid) { + sid_copy(&domain->sid, sid); + } + + /* Link to domain list */ + DLIST_ADD(_domain_list, domain); + + wcache_tdc_add_domain( domain ); + + DEBUG(2,("Added domain %s %s %s\n", + domain->name, domain->alt_name, + &domain->sid?sid_string_static(&domain->sid):"")); + + return domain; +} + +/******************************************************************** + rescan our domains looking for new trusted domains +********************************************************************/ + +struct trustdom_state { + TALLOC_CTX *mem_ctx; + BOOL primary; + BOOL forest_root; + struct winbindd_response *response; +}; + +static void trustdom_recv(void *private_data, BOOL success); +static void rescan_forest_root_trusts( void ); +static void rescan_forest_trusts( void ); + +static void add_trusted_domains( struct winbindd_domain *domain ) +{ + TALLOC_CTX *mem_ctx; + struct winbindd_request *request; + struct winbindd_response *response; + uint32 fr_flags = (DS_DOMAIN_TREE_ROOT|DS_DOMAIN_IN_FOREST); + + struct trustdom_state *state; + + mem_ctx = talloc_init("add_trusted_domains"); + if (mem_ctx == NULL) { + DEBUG(0, ("talloc_init failed\n")); + return; + } + + request = TALLOC_ZERO_P(mem_ctx, struct winbindd_request); + response = TALLOC_P(mem_ctx, struct winbindd_response); + state = TALLOC_P(mem_ctx, struct trustdom_state); + + if ((request == NULL) || (response == NULL) || (state == NULL)) { + DEBUG(0, ("talloc failed\n")); + talloc_destroy(mem_ctx); + return; + } + + state->mem_ctx = mem_ctx; + state->response = response; + + /* Flags used to know how to continue the forest trust search */ + + state->primary = domain->primary; + state->forest_root = ((domain->domain_flags & fr_flags) == fr_flags ); + + request->length = sizeof(*request); + request->cmd = WINBINDD_LIST_TRUSTDOM; + + async_domain_request(mem_ctx, domain, request, response, + trustdom_recv, state); +} + +static void trustdom_recv(void *private_data, BOOL success) +{ + struct trustdom_state *state = + talloc_get_type_abort(private_data, struct trustdom_state); + struct winbindd_response *response = state->response; + char *p; + + if ((!success) || (response->result != WINBINDD_OK)) { + DEBUG(1, ("Could not receive trustdoms\n")); + talloc_destroy(state->mem_ctx); + return; + } + + p = (char *)response->extra_data.data; + + while ((p != NULL) && (*p != '\0')) { + char *q, *sidstr, *alt_name; + DOM_SID sid; + struct winbindd_domain *domain; + char *alternate_name = NULL; + + alt_name = strchr(p, '\\'); + if (alt_name == NULL) { + DEBUG(0, ("Got invalid trustdom response\n")); + break; + } + + *alt_name = '\0'; + alt_name += 1; + + sidstr = strchr(alt_name, '\\'); + if (sidstr == NULL) { + DEBUG(0, ("Got invalid trustdom response\n")); + break; + } + + *sidstr = '\0'; + sidstr += 1; + + q = strchr(sidstr, '\n'); + if (q != NULL) + *q = '\0'; + + if (!string_to_sid(&sid, sidstr)) { + /* Allow NULL sid for sibling domains */ + if ( strcmp(sidstr,"S-0-0") == 0) { + sid_copy( &sid, &global_sid_NULL); + } else { + DEBUG(0, ("Got invalid trustdom response\n")); + break; + } + } + + /* use the real alt_name if we have one, else pass in NULL */ + + if ( !strequal( alt_name, "(null)" ) ) + alternate_name = alt_name; + + /* If we have an existing domain structure, calling + add_trusted_domain() will update the SID if + necessary. This is important because we need the + SID for sibling domains */ + + if ( find_domain_from_name_noinit(p) != NULL ) { + domain = add_trusted_domain(p, alternate_name, + &cache_methods, + &sid); + } else { + domain = add_trusted_domain(p, alternate_name, + &cache_methods, + &sid); + if (domain) { + setup_domain_child(domain, &domain->child, NULL); + } + } + p=q; + if (p != NULL) + p += 1; + } + + SAFE_FREE(response->extra_data.data); + + /* + Cases to consider when scanning trusts: + (a) we are calling from a child domain (primary && !forest_root) + (b) we are calling from the root of the forest (primary && forest_root) + (c) we are calling from a trusted forest domain (!primary + && !forest_root) + */ + + if ( state->primary ) { + /* If this is our primary domain and we are not the in the + forest root, we have to scan the root trusts first */ + + if ( !state->forest_root ) + rescan_forest_root_trusts(); + else + rescan_forest_trusts(); + + } else if ( state->forest_root ) { + /* Once we have done root forest trust search, we can + go on to search thing trusted forests */ + + rescan_forest_trusts(); + } + + talloc_destroy(state->mem_ctx); + + return; +} + +/******************************************************************** + Scan the trusts of our forest root +********************************************************************/ + +static void rescan_forest_root_trusts( void ) +{ + struct winbindd_tdc_domain *dom_list = NULL; + size_t num_trusts = 0; + int i; + + /* The only transitive trusts supported by Windows 2003 AD are + (a) Parent-Child, (b) Tree-Root, and (c) Forest. The + first two are handled in forest and listed by + DsEnumerateDomainTrusts(). Forest trusts are not so we + have to do that ourselves. */ + + if ( !wcache_tdc_fetch_list( &dom_list, &num_trusts ) ) + return; + + for ( i=0; iname, d->alt_name )); + + d->domain_flags = dom_list[i].trust_flags; + d->domain_type = dom_list[i].trust_type; + d->domain_trust_attribs = dom_list[i].trust_attribs; + + add_trusted_domains( d ); + + break; + } + + TALLOC_FREE( dom_list ); + + return; +} + +/******************************************************************** + scan the transitive forest trists (not our own) +********************************************************************/ + + +static void rescan_forest_trusts( void ) +{ + struct winbindd_domain *d = NULL; + struct winbindd_tdc_domain *dom_list = NULL; + size_t num_trusts = 0; + int i; + + /* The only transitive trusts supported by Windows 2003 AD are + (a) Parent-Child, (b) Tree-Root, and (c) Forest. The + first two are handled in forest and listed by + DsEnumerateDomainTrusts(). Forest trusts are not so we + have to do that ourselves. */ + + if ( !wcache_tdc_fetch_list( &dom_list, &num_trusts ) ) + return; + + for ( i=0; iinternal || d->primary ) ) + continue; + + if ( (flags & DS_DOMAIN_DIRECT_INBOUND) && + (type == DS_DOMAIN_TRUST_TYPE_UPLEVEL) && + (attribs == DS_DOMAIN_TRUST_ATTRIB_FOREST_TRANSITIVE) ) + { + /* add the trusted domain if we don't know + about it */ + + if ( !d ) { + d = add_trusted_domain( dom_list[i].domain_name, + dom_list[i].dns_name, + &cache_methods, + &dom_list[i].sid ); + } + + DEBUG(10,("Following trust path for domain %s (%s)\n", + d->name, d->alt_name )); + add_trusted_domains( d ); + } + } + + TALLOC_FREE( dom_list ); + + return; +} + +/********************************************************************* + The process of updating the trusted domain list is a three step + async process: + (a) ask our domain + (b) ask the root domain in our forest + (c) ask the a DC in any Win2003 trusted forests +*********************************************************************/ + +void rescan_trusted_domains( void ) +{ + time_t now = time(NULL); + + /* see if the time has come... */ + + if ((now >= last_trustdom_scan) && + ((now-last_trustdom_scan) < WINBINDD_RESCAN_FREQ) ) + return; + + /* clear the TRUSTDOM cache first */ + + wcache_tdc_clear(); + + /* this will only add new domains we didn't already know about + in the domain_list()*/ + + add_trusted_domains( find_our_domain() ); + + last_trustdom_scan = now; + + return; +} + +struct init_child_state { + TALLOC_CTX *mem_ctx; + struct winbindd_domain *domain; + struct winbindd_request *request; + struct winbindd_response *response; + void (*continuation)(void *private_data, BOOL success); + void *private_data; +}; + +static void init_child_recv(void *private_data, BOOL success); +static void init_child_getdc_recv(void *private_data, BOOL success); + +enum winbindd_result init_child_connection(struct winbindd_domain *domain, + void (*continuation)(void *private_data, + BOOL success), + void *private_data) +{ + TALLOC_CTX *mem_ctx; + struct winbindd_request *request; + struct winbindd_response *response; + struct init_child_state *state; + struct winbindd_domain *request_domain; + + mem_ctx = talloc_init("init_child_connection"); + if (mem_ctx == NULL) { + DEBUG(0, ("talloc_init failed\n")); + return WINBINDD_ERROR; + } + + request = TALLOC_ZERO_P(mem_ctx, struct winbindd_request); + response = TALLOC_P(mem_ctx, struct winbindd_response); + state = TALLOC_P(mem_ctx, struct init_child_state); + + if ((request == NULL) || (response == NULL) || (state == NULL)) { + DEBUG(0, ("talloc failed\n")); + TALLOC_FREE(mem_ctx); + continuation(private_data, False); + return WINBINDD_ERROR; + } + + request->length = sizeof(*request); + + state->mem_ctx = mem_ctx; + state->domain = domain; + state->request = request; + state->response = response; + state->continuation = continuation; + state->private_data = private_data; + + if (IS_DC || domain->primary || domain->internal ) { + /* The primary domain has to find the DC name itself */ + request->cmd = WINBINDD_INIT_CONNECTION; + fstrcpy(request->domain_name, domain->name); + request->data.init_conn.is_primary = domain->internal ? False : True; + fstrcpy(request->data.init_conn.dcname, ""); + async_request(mem_ctx, &domain->child, request, response, + init_child_recv, state); + return WINBINDD_PENDING; + } + + /* This is *not* the primary domain, let's ask our DC about a DC + * name */ + + request->cmd = WINBINDD_GETDCNAME; + fstrcpy(request->domain_name, domain->name); + + request_domain = find_our_domain(); + async_domain_request(mem_ctx, request_domain, request, response, + init_child_getdc_recv, state); + return WINBINDD_PENDING; +} + +static void init_child_getdc_recv(void *private_data, BOOL success) +{ + struct init_child_state *state = + talloc_get_type_abort(private_data, struct init_child_state); + const char *dcname = ""; + + DEBUG(10, ("Received getdcname response\n")); + + if (success && (state->response->result == WINBINDD_OK)) { + dcname = state->response->data.dc_name; + } + + state->request->cmd = WINBINDD_INIT_CONNECTION; + fstrcpy(state->request->domain_name, state->domain->name); + state->request->data.init_conn.is_primary = False; + fstrcpy(state->request->data.init_conn.dcname, dcname); + + async_request(state->mem_ctx, &state->domain->child, + state->request, state->response, + init_child_recv, state); +} + +static void init_child_recv(void *private_data, BOOL success) +{ + struct init_child_state *state = + talloc_get_type_abort(private_data, struct init_child_state); + + DEBUG(5, ("Received child initialization response for domain %s\n", + state->domain->name)); + + if ((!success) || (state->response->result != WINBINDD_OK)) { + DEBUG(3, ("Could not init child\n")); + state->continuation(state->private_data, False); + talloc_destroy(state->mem_ctx); + return; + } + + fstrcpy(state->domain->name, + state->response->data.domain_info.name); + fstrcpy(state->domain->alt_name, + state->response->data.domain_info.alt_name); + string_to_sid(&state->domain->sid, + state->response->data.domain_info.sid); + state->domain->native_mode = + state->response->data.domain_info.native_mode; + state->domain->active_directory = + state->response->data.domain_info.active_directory; + state->domain->sequence_number = + state->response->data.domain_info.sequence_number; + + init_dc_connection(state->domain); + + if (state->continuation != NULL) + state->continuation(state->private_data, True); + talloc_destroy(state->mem_ctx); +} + +enum winbindd_result winbindd_dual_init_connection(struct winbindd_domain *domain, + struct winbindd_cli_state *state) +{ + /* Ensure null termination */ + state->request.domain_name + [sizeof(state->request.domain_name)-1]='\0'; + state->request.data.init_conn.dcname + [sizeof(state->request.data.init_conn.dcname)-1]='\0'; + + if (strlen(state->request.data.init_conn.dcname) > 0) { + fstrcpy(domain->dcname, state->request.data.init_conn.dcname); + } + + init_dc_connection(domain); + + if (!domain->initialized) { + /* If we return error here we can't do any cached authentication, + but we may be in disconnected mode and can't initialize correctly. + Do what the previous code did and just return without initialization, + once we go online we'll re-initialize. + */ + DEBUG(5, ("winbindd_dual_init_connection: %s returning without initialization " + "online = %d\n", domain->name, (int)domain->online )); + } + + fstrcpy(state->response.data.domain_info.name, domain->name); + fstrcpy(state->response.data.domain_info.alt_name, domain->alt_name); + fstrcpy(state->response.data.domain_info.sid, + sid_string_static(&domain->sid)); + + state->response.data.domain_info.native_mode + = domain->native_mode; + state->response.data.domain_info.active_directory + = domain->active_directory; + state->response.data.domain_info.primary + = domain->primary; + state->response.data.domain_info.sequence_number = + domain->sequence_number; + + return WINBINDD_OK; +} + +/* Look up global info for the winbind daemon */ +BOOL init_domain_list(void) +{ + struct winbindd_domain *domain; + int role = lp_server_role(); + + /* Free existing list */ + free_domain_list(); + + /* Add ourselves as the first entry. */ + + if ( role == ROLE_DOMAIN_MEMBER ) { + DOM_SID our_sid; + + if (!secrets_fetch_domain_sid(lp_workgroup(), &our_sid)) { + DEBUG(0, ("Could not fetch our SID - did we join?\n")); + return False; + } + + domain = add_trusted_domain( lp_workgroup(), lp_realm(), + &cache_methods, &our_sid); + if (domain) { + domain->primary = True; + setup_domain_child(domain, &domain->child, NULL); + + /* Even in the parent winbindd we'll need to + talk to the DC, so try and see if we can + contact it. Theoretically this isn't neccessary + as the init_dc_connection() in init_child_recv() + will do this, but we can start detecting the DC + early here. */ + set_domain_online_request(domain); + } + } + + /* Local SAM */ + + domain = add_trusted_domain(get_global_sam_name(), NULL, + &passdb_methods, get_global_sam_sid()); + if (domain) { + if ( role != ROLE_DOMAIN_MEMBER ) { + domain->primary = True; + } + setup_domain_child(domain, &domain->child, NULL); + } + + /* BUILTIN domain */ + + domain = add_trusted_domain("BUILTIN", NULL, &passdb_methods, + &global_sid_Builtin); + if (domain) { + setup_domain_child(domain, &domain->child, NULL); + } + + return True; +} + +void check_domain_trusted( const char *name, const DOM_SID *user_sid ) +{ + struct winbindd_domain *domain; + DOM_SID dom_sid; + uint32 rid; + + domain = find_domain_from_name_noinit( name ); + if ( domain ) + return; + + sid_copy( &dom_sid, user_sid ); + if ( !sid_split_rid( &dom_sid, &rid ) ) + return; + + /* add the newly discovered trusted domain */ + + domain = add_trusted_domain( name, NULL, &cache_methods, + &dom_sid); + + if ( !domain ) + return; + + /* assume this is a trust from a one-way transitive + forest trust */ + + domain->active_directory = True; + domain->domain_flags = DS_DOMAIN_DIRECT_OUTBOUND; + domain->domain_type = DS_DOMAIN_TRUST_TYPE_UPLEVEL; + domain->internal = False; + domain->online = True; + + setup_domain_child(domain, &domain->child, NULL); + + wcache_tdc_add_domain( domain ); + + return; +} + +/** + * Given a domain name, return the struct winbindd domain info for it + * + * @note Do *not* pass lp_workgroup() to this function. domain_list + * may modify it's value, and free that pointer. Instead, our local + * domain may be found by calling find_our_domain(). + * directly. + * + * + * @return The domain structure for the named domain, if it is working. + */ + +struct winbindd_domain *find_domain_from_name_noinit(const char *domain_name) +{ + struct winbindd_domain *domain; + + /* Search through list */ + + for (domain = domain_list(); domain != NULL; domain = domain->next) { + if (strequal(domain_name, domain->name) || + (domain->alt_name[0] && + strequal(domain_name, domain->alt_name))) { + return domain; + } + } + + /* Not found */ + + return NULL; +} + +struct winbindd_domain *find_domain_from_name(const char *domain_name) +{ + struct winbindd_domain *domain; + + domain = find_domain_from_name_noinit(domain_name); + + if (domain == NULL) + return NULL; + + if (!domain->initialized) + init_dc_connection(domain); + + return domain; +} + +/* Given a domain sid, return the struct winbindd domain info for it */ + +struct winbindd_domain *find_domain_from_sid_noinit(const DOM_SID *sid) +{ + struct winbindd_domain *domain; + + /* Search through list */ + + for (domain = domain_list(); domain != NULL; domain = domain->next) { + if (sid_compare_domain(sid, &domain->sid) == 0) + return domain; + } + + /* Not found */ + + return NULL; +} + +/* Given a domain sid, return the struct winbindd domain info for it */ + +struct winbindd_domain *find_domain_from_sid(const DOM_SID *sid) +{ + struct winbindd_domain *domain; + + domain = find_domain_from_sid_noinit(sid); + + if (domain == NULL) + return NULL; + + if (!domain->initialized) + init_dc_connection(domain); + + return domain; +} + +struct winbindd_domain *find_our_domain(void) +{ + struct winbindd_domain *domain; + + /* Search through list */ + + for (domain = domain_list(); domain != NULL; domain = domain->next) { + if (domain->primary) + return domain; + } + + smb_panic("Could not find our domain"); + return NULL; +} + +struct winbindd_domain *find_root_domain(void) +{ + struct winbindd_domain *ours = find_our_domain(); + + if ( !ours ) + return NULL; + + if ( strlen(ours->forest_name) == 0 ) + return NULL; + + return find_domain_from_name( ours->forest_name ); +} + +struct winbindd_domain *find_builtin_domain(void) +{ + DOM_SID sid; + struct winbindd_domain *domain; + + string_to_sid(&sid, "S-1-5-32"); + domain = find_domain_from_sid(&sid); + + if (domain == NULL) { + smb_panic("Could not find BUILTIN domain"); + } + + return domain; +} + +/* Find the appropriate domain to lookup a name or SID */ + +struct winbindd_domain *find_lookup_domain_from_sid(const DOM_SID *sid) +{ + /* SIDs in the S-1-22-{1,2} domain should be handled by our passdb */ + + if ( sid_check_is_in_unix_groups(sid) || + sid_check_is_unix_groups(sid) || + sid_check_is_in_unix_users(sid) || + sid_check_is_unix_users(sid) ) + { + return find_domain_from_sid(get_global_sam_sid()); + } + + /* A DC can't ask the local smbd for remote SIDs, here winbindd is the + * one to contact the external DC's. On member servers the internal + * domains are different: These are part of the local SAM. */ + + DEBUG(10, ("find_lookup_domain_from_sid(%s)\n", + sid_string_static(sid))); + + if (IS_DC || is_internal_domain(sid) || is_in_internal_domain(sid)) { + DEBUG(10, ("calling find_domain_from_sid\n")); + return find_domain_from_sid(sid); + } + + /* On a member server a query for SID or name can always go to our + * primary DC. */ + + DEBUG(10, ("calling find_our_domain\n")); + return find_our_domain(); +} + +struct winbindd_domain *find_lookup_domain_from_name(const char *domain_name) +{ + if ( strequal(domain_name, unix_users_domain_name() ) || + strequal(domain_name, unix_groups_domain_name() ) ) + { + return find_domain_from_name_noinit( get_global_sam_name() ); + } + + if (IS_DC || strequal(domain_name, "BUILTIN") || + strequal(domain_name, get_global_sam_name())) + return find_domain_from_name_noinit(domain_name); + + /* The "Unix User" and "Unix Group" domain our handled by passdb */ + + return find_our_domain(); +} + +/* Lookup a sid in a domain from a name */ + +BOOL winbindd_lookup_sid_by_name(TALLOC_CTX *mem_ctx, + enum winbindd_cmd orig_cmd, + struct winbindd_domain *domain, + const char *domain_name, + const char *name, DOM_SID *sid, + enum lsa_SidType *type) +{ + NTSTATUS result; + + /* Lookup name */ + result = domain->methods->name_to_sid(domain, mem_ctx, orig_cmd, + domain_name, name, sid, type); + + /* Return sid and type if lookup successful */ + if (!NT_STATUS_IS_OK(result)) { + *type = SID_NAME_UNKNOWN; + } + + return NT_STATUS_IS_OK(result); +} + +/** + * @brief Lookup a name in a domain from a sid. + * + * @param sid Security ID you want to look up. + * @param name On success, set to the name corresponding to @p sid. + * @param dom_name On success, set to the 'domain name' corresponding to @p sid. + * @param type On success, contains the type of name: alias, group or + * user. + * @retval True if the name exists, in which case @p name and @p type + * are set, otherwise False. + **/ +BOOL winbindd_lookup_name_by_sid(TALLOC_CTX *mem_ctx, + struct winbindd_domain *domain, + DOM_SID *sid, + char **dom_name, + char **name, + enum lsa_SidType *type) +{ + NTSTATUS result; + + *dom_name = NULL; + *name = NULL; + + /* Lookup name */ + + result = domain->methods->sid_to_name(domain, mem_ctx, sid, dom_name, name, type); + + /* Return name and type if successful */ + + if (NT_STATUS_IS_OK(result)) { + return True; + } + + *type = SID_NAME_UNKNOWN; + + return False; +} + +/* Free state information held for {set,get,end}{pw,gr}ent() functions */ + +void free_getent_state(struct getent_state *state) +{ + struct getent_state *temp; + + /* Iterate over state list */ + + temp = state; + + while(temp != NULL) { + struct getent_state *next; + + /* Free sam entries then list entry */ + + SAFE_FREE(state->sam_entries); + DLIST_REMOVE(state, state); + next = temp->next; + + SAFE_FREE(temp); + temp = next; + } +} + +/* Is this a domain which we may assume no DOMAIN\ prefix? */ + +static BOOL assume_domain(const char *domain) +{ + /* never assume the domain on a standalone server */ + + if ( lp_server_role() == ROLE_STANDALONE ) + return False; + + /* domain member servers may possibly assume for the domain name */ + + if ( lp_server_role() == ROLE_DOMAIN_MEMBER ) { + if ( !strequal(lp_workgroup(), domain) ) + return False; + + if ( lp_winbind_use_default_domain() || lp_winbind_trusted_domains_only() ) + return True; + } + + /* only left with a domain controller */ + + if ( strequal(get_global_sam_name(), domain) ) { + return True; + } + + return False; +} + +/* Parse a string of the form DOMAIN\user into a domain and a user */ + +BOOL parse_domain_user(const char *domuser, fstring domain, fstring user) +{ + char *p = strchr(domuser,*lp_winbind_separator()); + + if ( !p ) { + fstrcpy(user, domuser); + + if ( assume_domain(lp_workgroup())) { + fstrcpy(domain, lp_workgroup()); + } else if ((p = strchr(domuser, '@')) != NULL) { + fstrcpy(domain, ""); + } else { + return False; + } + } else { + fstrcpy(user, p+1); + fstrcpy(domain, domuser); + domain[PTR_DIFF(p, domuser)] = 0; + } + + strupper_m(domain); + + return True; +} + +BOOL parse_domain_user_talloc(TALLOC_CTX *mem_ctx, const char *domuser, + char **domain, char **user) +{ + fstring fstr_domain, fstr_user; + if (!parse_domain_user(domuser, fstr_domain, fstr_user)) { + return False; + } + *domain = talloc_strdup(mem_ctx, fstr_domain); + *user = talloc_strdup(mem_ctx, fstr_user); + return ((*domain != NULL) && (*user != NULL)); +} + +/* Ensure an incoming username from NSS is fully qualified. Replace the + incoming fstring with DOMAIN user. Returns the same + values as parse_domain_user() but also replaces the incoming username. + Used to ensure all names are fully qualified within winbindd. + Used by the NSS protocols of auth, chauthtok, logoff and ccache_ntlm_auth. + The protocol definitions of auth_crap, chng_pswd_auth_crap + really should be changed to use this instead of doing things + by hand. JRA. */ + +BOOL canonicalize_username(fstring username_inout, fstring domain, fstring user) +{ + if (!parse_domain_user(username_inout, domain, user)) { + return False; + } + slprintf(username_inout, sizeof(fstring) - 1, "%s%c%s", + domain, *lp_winbind_separator(), + user); + return True; +} + +/* + Fill DOMAIN\\USERNAME entry accounting 'winbind use default domain' and + 'winbind separator' options. + This means: + - omit DOMAIN when 'winbind use default domain = true' and DOMAIN is + lp_workgroup() + + If we are a PDC or BDC, and this is for our domain, do likewise. + + Also, if omit DOMAIN if 'winbind trusted domains only = true', as the + username is then unqualified in unix + + We always canonicalize as UPPERCASE DOMAIN, lowercase username. +*/ +void fill_domain_username(fstring name, const char *domain, const char *user, BOOL can_assume) +{ + fstring tmp_user; + + fstrcpy(tmp_user, user); + strlower_m(tmp_user); + + if (can_assume && assume_domain(domain)) { + strlcpy(name, tmp_user, sizeof(fstring)); + } else { + slprintf(name, sizeof(fstring) - 1, "%s%c%s", + domain, *lp_winbind_separator(), + tmp_user); + } +} + +/* + * Winbindd socket accessor functions + */ + +char *get_winbind_priv_pipe_dir(void) +{ + return lock_path(WINBINDD_PRIV_SOCKET_SUBDIR); +} + +/* + * Client list accessor functions + */ + +static struct winbindd_cli_state *_client_list; +static int _num_clients; + +/* Return list of all connected clients */ + +struct winbindd_cli_state *winbindd_client_list(void) +{ + return _client_list; +} + +/* Add a connection to the list */ + +void winbindd_add_client(struct winbindd_cli_state *cli) +{ + DLIST_ADD(_client_list, cli); + _num_clients++; +} + +/* Remove a client from the list */ + +void winbindd_remove_client(struct winbindd_cli_state *cli) +{ + DLIST_REMOVE(_client_list, cli); + _num_clients--; +} + +/* Close all open clients */ + +void winbindd_kill_all_clients(void) +{ + struct winbindd_cli_state *cl = winbindd_client_list(); + + DEBUG(10, ("winbindd_kill_all_clients: going postal\n")); + + while (cl) { + struct winbindd_cli_state *next; + + next = cl->next; + winbindd_remove_client(cl); + cl = next; + } +} + +/* Return number of open clients */ + +int winbindd_num_clients(void) +{ + return _num_clients; +} + +NTSTATUS lookup_usergroups_cached(struct winbindd_domain *domain, + TALLOC_CTX *mem_ctx, + const DOM_SID *user_sid, + uint32 *p_num_groups, DOM_SID **user_sids) +{ + NET_USER_INFO_3 *info3 = NULL; + NTSTATUS status = NT_STATUS_NO_MEMORY; + int i; + size_t num_groups = 0; + DOM_SID group_sid, primary_group; + + DEBUG(3,(": lookup_usergroups_cached\n")); + + *user_sids = NULL; + num_groups = 0; + *p_num_groups = 0; + + info3 = netsamlogon_cache_get(mem_ctx, user_sid); + + if (info3 == NULL) { + return NT_STATUS_OBJECT_NAME_NOT_FOUND; + } + + if (info3->num_groups == 0) { + TALLOC_FREE(info3); + return NT_STATUS_UNSUCCESSFUL; + } + + /* always add the primary group to the sid array */ + sid_compose(&primary_group, &info3->dom_sid.sid, info3->user_rid); + + if (!add_sid_to_array(mem_ctx, &primary_group, user_sids, &num_groups)) { + TALLOC_FREE(info3); + return NT_STATUS_NO_MEMORY; + } + + for (i=0; inum_groups; i++) { + sid_copy(&group_sid, &info3->dom_sid.sid); + sid_append_rid(&group_sid, info3->gids[i].g_rid); + + if (!add_sid_to_array(mem_ctx, &group_sid, user_sids, + &num_groups)) { + TALLOC_FREE(info3); + return NT_STATUS_NO_MEMORY; + } + } + + /* Add any Universal groups in the other_sids list */ + + for (i=0; inum_other_sids; i++) { + /* Skip Domain local groups outside our domain. + We'll get these from the getsidaliases() RPC call. */ + if (info3->other_sids_attrib[i] & SE_GROUP_RESOURCE) + continue; + + if (!add_sid_to_array(mem_ctx, &info3->other_sids[i].sid, + user_sids, &num_groups)) + { + TALLOC_FREE(info3); + return NT_STATUS_NO_MEMORY; + } + } + + + TALLOC_FREE(info3); + *p_num_groups = num_groups; + status = (user_sids != NULL) ? NT_STATUS_OK : NT_STATUS_NO_MEMORY; + + DEBUG(3,(": lookup_usergroups_cached succeeded\n")); + + return status; +} + +/********************************************************************* + We use this to remove spaces from user and group names +********************************************************************/ + +void ws_name_replace( char *name, char replace ) +{ + char replace_char[2] = { 0x0, 0x0 }; + + if ( !lp_winbind_normalize_names() || (replace == '\0') ) + return; + + replace_char[0] = replace; + all_string_sub( name, " ", replace_char, 0 ); + + return; +} + +/********************************************************************* + We use this to do the inverse of ws_name_replace() +********************************************************************/ + +void ws_name_return( char *name, char replace ) +{ + char replace_char[2] = { 0x0, 0x0 }; + + if ( !lp_winbind_normalize_names() || (replace == '\0') ) + return; + + replace_char[0] = replace; + all_string_sub( name, replace_char, " ", 0 ); + + return; +} + +/********************************************************************* + ********************************************************************/ + +BOOL winbindd_can_contact_domain( struct winbindd_domain *domain ) +{ + /* We can contact the domain if it is our primary domain */ + + if ( domain->primary ) + return True; + + /* Can always contact a domain that is in out forest */ + + if ( domain->domain_flags & DS_DOMAIN_IN_FOREST ) + return True; + + /* We cannot contact the domain if it is running AD and + we have no inbound trust */ + + if ( domain->active_directory && + ((domain->domain_flags&DS_DOMAIN_DIRECT_INBOUND) != DS_DOMAIN_DIRECT_INBOUND) ) + { + return False; + } + + /* Assume everything else is ok (probably not true but what + can you do?) */ + + return True; +} + +/********************************************************************* + ********************************************************************/ + +BOOL winbindd_internal_child(struct winbindd_child *child) +{ + if ((child == idmap_child()) || (child == locator_child())) { + return True; + } + + return False; +} + +#ifdef HAVE_KRB5_LOCATE_PLUGIN_H + +/********************************************************************* + ********************************************************************/ + +static void winbindd_set_locator_kdc_env(const struct winbindd_domain *domain) +{ + char *var = NULL; + const char *kdc = NULL; + int lvl = 11; + + if (!domain || !domain->alt_name || !*domain->alt_name) { + return; + } + + if (domain->initialized && !domain->active_directory) { + DEBUG(lvl,("winbindd_set_locator_kdc_env: %s not AD\n", + domain->alt_name)); + return; + } + + kdc = inet_ntoa(domain->dcaddr.sin_addr); + if (!kdc) { + DEBUG(lvl,("winbindd_set_locator_kdc_env: %s no DC IP\n", + domain->alt_name)); + kdc = domain->dcname; + } + + if (!kdc || !*kdc) { + DEBUG(lvl,("winbindd_set_locator_kdc_env: %s no DC at all\n", + domain->alt_name)); + return; + } + + if (asprintf(&var, "%s_%s", WINBINDD_LOCATOR_KDC_ADDRESS, + strupper_static(domain->alt_name)) == -1) { + return; + } + + DEBUG(lvl,("winbindd_set_locator_kdc_env: setting var: %s to: %s\n", + var, kdc)); + + setenv(var, kdc, 1); + free(var); +} + +/********************************************************************* + ********************************************************************/ + +void winbindd_set_locator_kdc_envs(const struct winbindd_domain *domain) +{ + struct winbindd_domain *our_dom = find_our_domain(); + + winbindd_set_locator_kdc_env(domain); + + if (domain != our_dom) { + winbindd_set_locator_kdc_env(our_dom); + } +} + +/********************************************************************* + ********************************************************************/ + +void winbindd_unset_locator_kdc_env(const struct winbindd_domain *domain) +{ + char *var = NULL; + + if (!domain || !domain->alt_name || !*domain->alt_name) { + return; + } + + if (asprintf(&var, "%s_%s", WINBINDD_LOCATOR_KDC_ADDRESS, + strupper_static(domain->alt_name)) == -1) { + return; + } + + unsetenv(var); + free(var); +} +#else + +void winbindd_set_locator_kdc_envs(const struct winbindd_domain *domain) +{ + return; +} + +void winbindd_unset_locator_kdc_env(const struct winbindd_domain *domain) +{ + return; +} + +#endif /* HAVE_KRB5_LOCATE_PLUGIN_H */ diff --git a/source3/winbindd/winbindd_wins.c b/source3/winbindd/winbindd_wins.c new file mode 100644 index 0000000000..f84dfdf2de --- /dev/null +++ b/source3/winbindd/winbindd_wins.c @@ -0,0 +1,234 @@ +/* + Unix SMB/CIFS implementation. + + Winbind daemon - WINS related functions + + Copyright (C) Andrew Tridgell 1999 + Copyright (C) Herb Lewis 2002 + + 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 3 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, see . +*/ + +#include "includes.h" +#include "winbindd.h" + +#undef DBGC_CLASS +#define DBGC_CLASS DBGC_WINBIND + +/* Use our own create socket code so we don't recurse.... */ + +static int wins_lookup_open_socket_in(void) +{ + struct sockaddr_in sock; + int val=1; + int res; + + memset((char *)&sock,'\0',sizeof(sock)); + +#ifdef HAVE_SOCK_SIN_LEN + sock.sin_len = sizeof(sock); +#endif + sock.sin_port = 0; + sock.sin_family = AF_INET; + sock.sin_addr.s_addr = interpret_addr("0.0.0.0"); + res = socket(AF_INET, SOCK_DGRAM, 0); + if (res == -1) + return -1; + + setsockopt(res,SOL_SOCKET,SO_REUSEADDR,(char *)&val,sizeof(val)); +#ifdef SO_REUSEPORT + setsockopt(res,SOL_SOCKET,SO_REUSEPORT,(char *)&val,sizeof(val)); +#endif /* SO_REUSEPORT */ + + /* now we've got a socket - we need to bind it */ + + if (bind(res, (struct sockaddr * ) &sock,sizeof(sock)) < 0) { + close(res); + return(-1); + } + + set_socket_options(res,"SO_BROADCAST"); + + return res; +} + + +static NODE_STATUS_STRUCT *lookup_byaddr_backend(char *addr, int *count) +{ + int fd; + struct in_addr ip; + struct nmb_name nname; + NODE_STATUS_STRUCT *status; + + fd = wins_lookup_open_socket_in(); + if (fd == -1) + return NULL; + + make_nmb_name(&nname, "*", 0); + ip = *interpret_addr2(addr); + status = node_status_query(fd,&nname,ip, count, NULL); + + close(fd); + return status; +} + +static struct in_addr *lookup_byname_backend(const char *name, int *count) +{ + int fd; + struct ip_service *ret = NULL; + struct in_addr *return_ip = NULL; + int j, i, flags = 0; + + *count = 0; + + /* always try with wins first */ + if (NT_STATUS_IS_OK(resolve_wins(name,0x20,&ret,count))) { + if ( *count == 0 ) + return NULL; + if ( (return_ip = SMB_MALLOC_ARRAY(struct in_addr, *count)) == NULL ) { + free( ret ); + return NULL; + } + + /* copy the IP addresses */ + for ( i=0; i<(*count); i++ ) + return_ip[i] = ret[i].ip; + + free( ret ); + return return_ip; + } + + fd = wins_lookup_open_socket_in(); + if (fd == -1) { + return NULL; + } + + /* uggh, we have to broadcast to each interface in turn */ + for (j=iface_count() - 1; + j >= 0; + j--) { + struct in_addr *bcast = iface_n_bcast(j); + return_ip = name_query(fd,name,0x20,True,True,*bcast,count, &flags, NULL); + if (return_ip) { + break; + } + } + + close(fd); + return return_ip; +} + +/* Get hostname from IP */ + +void winbindd_wins_byip(struct winbindd_cli_state *state) +{ + fstring response; + int i, count, maxlen, size; + NODE_STATUS_STRUCT *status; + + /* Ensure null termination */ + state->request.data.winsreq[sizeof(state->request.data.winsreq)-1]='\0'; + + DEBUG(3, ("[%5lu]: wins_byip %s\n", (unsigned long)state->pid, + state->request.data.winsreq)); + + *response = '\0'; + maxlen = sizeof(response) - 1; + + if ((status = lookup_byaddr_backend(state->request.data.winsreq, &count))){ + size = strlen(state->request.data.winsreq); + if (size > maxlen) { + SAFE_FREE(status); + request_error(state); + return; + } + fstrcat(response,state->request.data.winsreq); + fstrcat(response,"\t"); + for (i = 0; i < count; i++) { + /* ignore group names */ + if (status[i].flags & 0x80) continue; + if (status[i].type == 0x20) { + size = sizeof(status[i].name) + strlen(response); + if (size > maxlen) { + SAFE_FREE(status); + request_error(state); + return; + } + fstrcat(response, status[i].name); + fstrcat(response, " "); + } + } + /* make last character a newline */ + response[strlen(response)-1] = '\n'; + SAFE_FREE(status); + } + fstrcpy(state->response.data.winsresp,response); + request_ok(state); +} + +/* Get IP from hostname */ + +void winbindd_wins_byname(struct winbindd_cli_state *state) +{ + struct in_addr *ip_list; + int i, count, maxlen, size; + fstring response; + char * addr; + + /* Ensure null termination */ + state->request.data.winsreq[sizeof(state->request.data.winsreq)-1]='\0'; + + DEBUG(3, ("[%5lu]: wins_byname %s\n", (unsigned long)state->pid, + state->request.data.winsreq)); + + *response = '\0'; + maxlen = sizeof(response) - 1; + + if ((ip_list = lookup_byname_backend(state->request.data.winsreq,&count))){ + for (i = count; i ; i--) { + addr = inet_ntoa(ip_list[i-1]); + size = strlen(addr); + if (size > maxlen) { + SAFE_FREE(ip_list); + request_error(state); + return; + } + if (i != 0) { + /* Clear out the newline character */ + /* But only if there is something in there, + otherwise we clobber something in the stack */ + if (strlen(response)) + response[strlen(response)-1] = ' '; + } + fstrcat(response,addr); + fstrcat(response,"\t"); + } + size = strlen(state->request.data.winsreq) + strlen(response); + if (size > maxlen) { + SAFE_FREE(ip_list); + request_error(state); + return; + } + fstrcat(response,state->request.data.winsreq); + fstrcat(response,"\n"); + SAFE_FREE(ip_list); + } else { + request_error(state); + return; + } + + fstrcpy(state->response.data.winsresp,response); + + request_ok(state); +} -- cgit