winbindd_ads.c

来自「samba-3.0.22.tar.gz 编译smb服务器的源码」· C语言 代码 · 共 970 行 · 第 1/2 页

C
970
字号
/*   Unix SMB/CIFS implementation.   Winbind ADS backend functions   Copyright (C) Andrew Tridgell 2001   Copyright (C) Andrew Bartlett <abartlet@samba.org> 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 2 of the License, or   (at your option) any later version.      This program is distributed in the hope that it will be useful,   but WITHOUT ANY WARRANTY; without even the implied warranty of   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the   GNU General Public License for more details.      You should have received a copy of the GNU General Public License   along with this program; if not, write to the Free Software   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.*/#include "includes.h"#include "winbindd.h"#ifdef HAVE_ADS#undef DBGC_CLASS#define DBGC_CLASS DBGC_WINBIND/*  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;	if (domain->private_data) {		ads = (ADS_STRUCT *)domain->private_data;		/* check for a valid structure */		DEBUG(7, ("Current tickets expire at %d, time is now %d\n",			  (uint32) ads->auth.expire, (uint32) time(NULL)));		if ( ads->config.realm && (ads->auth.expire > time(NULL))) {			return ads;		}		else {			/* we own this ADS_STRUCT so make sure it goes away */			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 ( !secrets_fetch_trusted_domain_password( 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() );	}	status = ads_connect(ads);	if (!ADS_ERR_OK(status) || !ads->config.realm) {		extern struct winbindd_methods msrpc_methods, cache_methods;		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) {			DEBUG(1,("Trying MSRPC methods\n"));			if (domain->methods == &cache_methods) {				domain->backend = &msrpc_methods;			} else {				domain->methods = &msrpc_methods;			}		}		return NULL;	}	if (use_nss_info("sfu") && (!ads_check_sfu_mapping(ads))) {		DEBUG(0,("ads_cached_connection: failed to check sfu attributes\n"));		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[] = {"userPrincipalName",			       "sAMAccountName",			       "name", "objectSid", "primaryGroupID", 			       "sAMAccountType", 			       ADS_ATTR_SFU_HOMEDIR_OID, 			       ADS_ATTR_SFU_SHELL_OID,			       ADS_ATTR_SFU_GECOS_OID,			       NULL};	int i, count;	ADS_STATUS rc;	void *res = NULL;	void *msg = NULL;	NTSTATUS status = NT_STATUS_UNSUCCESSFUL;	*num_entries = 0;	DEBUG(3,("ads: query_user_list\n"));	ads = ads_cached_connection(domain);		if (!ads) {		domain->last_status = NT_STATUS_SERVER_DISABLED;		goto done;	}	rc = ads_search_retry(ads, &res, "(objectClass=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;		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 (use_nss_info("sfu")) {			homedir = ads_pull_string(ads, mem_ctx, msg, 						  ads->schema.sfu_homedir_attr);			shell 	= ads_pull_string(ads, mem_ctx, msg, 						  ads->schema.sfu_shell_attr);			gecos 	= ads_pull_string(ads, mem_ctx, msg, 						  ads->schema.sfu_gecos_attr);		}		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;		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;	void *res = NULL;	void *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"));	/* only grab domain local groups for our domain */	if ( domain->native_mode && 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;}/* convert a DN to a name, SID and name type    this might become a major speed bottleneck if groups have   lots of users, in which case we could cache the results*/static BOOL dn_lookup(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,		      const char *dn,		      char **name, uint32 *name_type, DOM_SID *sid){	void *res = NULL;	const char *attrs[] = {"userPrincipalName", "sAMAccountName",			       "objectSid", "sAMAccountType", NULL};	ADS_STATUS rc;	uint32 atype;	DEBUG(3,("ads: dn_lookup\n"));	rc = ads_search_retry_dn(ads, &res, dn, attrs);	if (!ADS_ERR_OK(rc) || !res) {		goto failed;	}	(*name) = ads_pull_username(ads, mem_ctx, res);	if (!ads_pull_uint32(ads, res, "sAMAccountType", &atype)) {		goto failed;	}	(*name_type) = ads_atype_map(atype);	if (!ads_pull_sid(ads, res, "objectSid", sid)) {		goto failed;	}	if (res) 		ads_msgfree(ads, res);	return True;failed:	if (res) 		ads_msgfree(ads, res);	return False;}/* 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[] = {"userPrincipalName", 			       "sAMAccountName",			       "name", 			       "primaryGroupID", 			       ADS_ATTR_SFU_HOMEDIR_OID, 			       ADS_ATTR_SFU_SHELL_OID,			       ADS_ATTR_SFU_GECOS_OID,			       NULL};	ADS_STATUS rc;	int count;	void *msg = NULL;	char *ldap_exp;	char *sidstr;	uint32 group_rid;	NTSTATUS status = NT_STATUS_UNSUCCESSFUL;	DEBUG(3,("ads: query_user\n"));	ads = ads_cached_connection(domain);		if (!ads) {		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);	if (use_nss_info("sfu")) {

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?