From f9e87b9ba65f37bafa45eacb1a6c9b8c5483d46b Mon Sep 17 00:00:00 2001 From: Volker Lendecke Date: Fri, 12 Nov 2004 15:49:47 +0000 Subject: r3705: Nobody has commented, so I'll take this as an ack... abartlet, I'd like to ask you to take a severe look at this! We have solved the problem to find the global groups a user is in twice: Once in auth_util.c and another time for the corresponding samr call. The attached patch unifies these and sends them through the passdb backend (new function pdb_enum_group_memberships). Thus it gives pdb_ldap.c the chance to further optimize the corresponding call if the samba and posix accounts are unified by issuing a specialized ldap query. The parameter to activate this ldapsam behaviour is ldapsam:trusted = yes Volker (This used to be commit b94838aff1a009f8d8c2c3efd48756a5b8f3f989) --- source3/Makefile.in | 9 ++-- source3/auth/auth_util.c | 52 ++++++------------- source3/include/passdb.h | 12 +++++ source3/lib/system_smbd.c | 90 +++++++++++++++++++++++++++++++- source3/lib/util_smbd.c | 16 ++---- source3/passdb/pdb_interface.c | 35 +++++++++++++ source3/passdb/pdb_ldap.c | 108 +++++++++++++++++++++++++++++++++++++++ source3/rpc_server/srv_samr_nt.c | 49 ++++++++++++++++-- source3/rpc_server/srv_util.c | 59 --------------------- source3/smbd/lanman.c | 45 ++++++++-------- 10 files changed, 334 insertions(+), 141 deletions(-) (limited to 'source3') diff --git a/source3/Makefile.in b/source3/Makefile.in index 0b39fd3dce..a6e434464f 100644 --- a/source3/Makefile.in +++ b/source3/Makefile.in @@ -208,8 +208,6 @@ LIB_OBJ = $(VERSION_OBJ) lib/charcnv.o lib/debug.o lib/fault.o \ lib/module.o lib/ldap_escape.o @CHARSET_STATIC@ \ lib/privileges.o lib/secdesc.o lib/secace.o lib/secacl.o -LIB_SMBD_OBJ = lib/system_smbd.o lib/util_smbd.o - LIB_NONSMBD_OBJ = $(LIB_OBJ) lib/dummysmbd.o READLINE_OBJ = lib/readline.o @@ -313,7 +311,8 @@ PASSDB_GET_SET_OBJ = passdb/pdb_get_set.o PASSDB_OBJ = $(PASSDB_GET_SET_OBJ) passdb/passdb.o passdb/pdb_interface.o \ passdb/util_sam_sid.o passdb/pdb_compat.o \ passdb/privileges.o passdb/lookup_sid.o \ - passdb/login_cache.o @PDB_STATIC@ passdb/pdb_sql.o + passdb/login_cache.o @PDB_STATIC@ passdb/pdb_sql.o \ + lib/system_smbd.o XML_OBJ = passdb/pdb_xml.o MYSQL_OBJ = passdb/pdb_mysql.o @@ -395,7 +394,7 @@ SMBD_OBJ_BASE = $(PARAM_OBJ) $(SMBD_OBJ_SRV) $(LIBSMB_OBJ) \ $(NOTIFY_OBJ) $(GROUPDB_OBJ) $(AUTH_OBJ) \ $(LIBMSRPC_OBJ) \ $(LIBADS_OBJ) $(KRBCLIENT_OBJ) $(LIBADS_SERVER_OBJ) \ - $(LIB_SMBD_OBJ) $(REGISTRY_OBJ) $(POPT_LIB_OBJ) \ + $(REGISTRY_OBJ) $(POPT_LIB_OBJ) \ $(BUILDOPT_OBJ) $(SMBLDAP_OBJ) PRINTING_OBJ = printing/pcap.o printing/print_svid.o \ @@ -619,7 +618,7 @@ PROTO_OBJ = $(SMBD_OBJ_MAIN) \ $(PRINTING_OBJ) $(PRINTBACKEND_OBJ) $(OPLOCK_OBJ) $(NOTIFY_OBJ) \ $(PASSDB_OBJ) $(GROUPDB_OBJ) \ $(READLINE_OBJ) $(PROFILE_OBJ) $(LIBADS_OBJ) $(LIBADS_SERVER_OBJ) \ - $(LIB_SMBD_OBJ) $(AUTH_SAM_OBJ) $(REGISTRY_OBJ) $(POPT_LIB_OBJ) \ + $(AUTH_SAM_OBJ) $(REGISTRY_OBJ) $(POPT_LIB_OBJ) \ $(RPC_LSA_OBJ) $(RPC_NETLOG_OBJ) $(RPC_SAMR_OBJ) $(RPC_REG_OBJ) $(RPC_LSA_DS_OBJ) \ $(RPC_SVC_OBJ) $(RPC_WKS_OBJ) $(RPC_DFS_OBJ) $(RPC_SPOOLSS_OBJ) \ $(RPC_ECHO_OBJ) $(SMBLDAP_OBJ) $(IDMAP_OBJ) libsmb/spnego.o $(PASSCHANGE_OBJ) diff --git a/source3/auth/auth_util.c b/source3/auth/auth_util.c index 96a229f0dc..1ef64ab845 100644 --- a/source3/auth/auth_util.c +++ b/source3/auth/auth_util.c @@ -657,47 +657,27 @@ static NTSTATUS get_user_groups(const char *username, uid_t uid, gid_t gid, *n_groups = 0; *groups = NULL; - - /* Try winbind first */ - if ( strchr(username, *lp_winbind_separator()) ) { - n_unix_groups = winbind_getgroups( username, unix_groups ); + if (strchr(username, *lp_winbind_separator()) == NULL) { + NTSTATUS result; - DEBUG(10,("get_user_groups: winbind_getgroups(%s): result = %s\n", username, - n_unix_groups == -1 ? "FAIL" : "SUCCESS")); - - if ( n_unix_groups == -1 ) - return NT_STATUS_NO_SUCH_USER; /* what should this return value be? */ + become_root(); + result = pdb_enum_group_memberships(username, gid, groups, + unix_groups, n_groups); + unbecome_root(); + return result; } - else { - /* fallback to getgrouplist() */ - - n_unix_groups = groups_max(); - - if ((*unix_groups = malloc( sizeof(gid_t) * n_unix_groups ) ) == NULL) { - DEBUG(0, ("get_user_groups: Out of memory allocating unix group list\n")); - return NT_STATUS_NO_MEMORY; - } + + /* We have the separator, this must be winbind */ - if (sys_getgrouplist(username, gid, *unix_groups, &n_unix_groups) == -1) { - - gid_t *groups_tmp; - - groups_tmp = Realloc(*unix_groups, sizeof(gid_t) * n_unix_groups); - - if (!groups_tmp) { - SAFE_FREE(*unix_groups); - return NT_STATUS_NO_MEMORY; - } - *unix_groups = groups_tmp; + n_unix_groups = winbind_getgroups( username, unix_groups ); - if (sys_getgrouplist(username, gid, *unix_groups, &n_unix_groups) == -1) { - DEBUG(0, ("get_user_groups: failed to get the unix group list\n")); - SAFE_FREE(*unix_groups); - return NT_STATUS_NO_SUCH_USER; /* what should this return value be? */ - } - } - } + DEBUG(10,("get_user_groups: winbind_getgroups(%s): result = %s\n", + username, n_unix_groups == -1 ? "FAIL" : "SUCCESS")); + + if ( n_unix_groups == -1 ) + return NT_STATUS_NO_SUCH_USER; /* what should this return + * value be? */ debug_unix_user_token(DBGC_CLASS, 5, uid, gid, n_unix_groups, *unix_groups); diff --git a/source3/include/passdb.h b/source3/include/passdb.h index 8219e90f2b..db6bc2ac75 100644 --- a/source3/include/passdb.h +++ b/source3/include/passdb.h @@ -287,6 +287,12 @@ typedef struct pdb_context GROUP_MAP **rmap, int *num_entries, BOOL unix_only); + NTSTATUS (*pdb_enum_group_memberships)(struct pdb_context *context, + const char *username, + gid_t primary_gid, + DOM_SID **sids, gid_t **gids, + int *num_groups); + NTSTATUS (*pdb_find_alias)(struct pdb_context *context, const char *name, DOM_SID *sid); @@ -379,6 +385,12 @@ typedef struct pdb_methods GROUP_MAP **rmap, int *num_entries, BOOL unix_only); + NTSTATUS (*enum_group_memberships)(struct pdb_methods *methods, + const char *username, + gid_t primary_gid, + DOM_SID **sids, gid_t **gids, + int *num_groups); + NTSTATUS (*find_alias)(struct pdb_methods *methods, const char *name, DOM_SID *sid); diff --git a/source3/lib/system_smbd.c b/source3/lib/system_smbd.c index 55c2338ebd..fd2ed24a17 100644 --- a/source3/lib/system_smbd.c +++ b/source3/lib/system_smbd.c @@ -109,7 +109,7 @@ static int getgrouplist_internals(const char *user, gid_t gid, gid_t *groups, in } #endif -int sys_getgrouplist(const char *user, gid_t gid, gid_t *groups, int *grpcnt) +static int sys_getgrouplist(const char *user, gid_t gid, gid_t *groups, int *grpcnt) { char *p; int retval; @@ -139,3 +139,91 @@ int sys_getgrouplist(const char *user, gid_t gid, gid_t *groups, int *grpcnt) return retval; } + +BOOL getgroups_user(const char *user, gid_t primary_gid, + gid_t **ret_groups, int *ngroups) +{ + int ngrp, max_grp; + gid_t *temp_groups; + gid_t *groups; + int i; + + max_grp = groups_max(); + temp_groups = (gid_t *)malloc(sizeof(gid_t) * max_grp); + if (! temp_groups) { + return False; + } + + if (sys_getgrouplist(user, primary_gid, temp_groups, &max_grp) == -1) { + + gid_t *groups_tmp; + + groups_tmp = Realloc(temp_groups, sizeof(gid_t) * max_grp); + + if (!groups_tmp) { + SAFE_FREE(temp_groups); + return False; + } + temp_groups = groups_tmp; + + if (sys_getgrouplist(user, primary_gid, + temp_groups, &max_grp) == -1) { + DEBUG(0, ("get_user_groups: failed to get the unix " + "group list\n")); + SAFE_FREE(temp_groups); + return False; + } + } + + ngrp = 0; + groups = NULL; + + /* Add in primary group first */ + add_gid_to_array_unique(primary_gid, &groups, &ngrp); + + for (i=0; ipw_gid, temp_groups, &max_grp) == -1) { + if (sys_getgrouplist(user, primary_gid, temp_groups, &max_grp) == -1) { gid_t *groups_tmp; @@ -67,9 +62,8 @@ BOOL getgroups_user(const char *user, gid_t **ret_groups, int *ngroups) } temp_groups = groups_tmp; - if (sys_getgrouplist(user, pwd->pw_gid, temp_groups, &max_grp) == -1) { + if (sys_getgrouplist(user, primary_gid, temp_groups, &max_grp) == -1) { DEBUG(0, ("get_user_groups: failed to get the unix group list\n")); - passwd_free(&pwd); SAFE_FREE(temp_groups); return False; } @@ -79,9 +73,7 @@ BOOL getgroups_user(const char *user, gid_t **ret_groups, int *ngroups) groups = NULL; /* Add in primary group first */ - add_gid_to_array_unique(pwd->pw_gid, &groups, &ngrp); - - passwd_free(&pwd); + add_gid_to_array_unique(primary_gid, &groups, &ngrp); for (i=0; ipdb_methods)) { + DEBUG(0, ("invalid pdb_context specified!\n")); + return ret; + } + + return context->pdb_methods-> + enum_group_memberships(context->pdb_methods, username, + primary_gid, sids, gids, num_groups); +} + static NTSTATUS context_find_alias(struct pdb_context *context, const char *name, DOM_SID *sid) { @@ -718,6 +736,7 @@ static NTSTATUS make_pdb_context(struct pdb_context **context) (*context)->pdb_update_group_mapping_entry = context_update_group_mapping_entry; (*context)->pdb_delete_group_mapping_entry = context_delete_group_mapping_entry; (*context)->pdb_enum_group_mapping = context_enum_group_mapping; + (*context)->pdb_enum_group_memberships = context_enum_group_memberships; (*context)->pdb_find_alias = context_find_alias; (*context)->pdb_create_alias = context_create_alias; @@ -1038,6 +1057,21 @@ BOOL pdb_enum_group_mapping(enum SID_NAME_USE sid_name_use, GROUP_MAP **rmap, rmap, num_entries, unix_only)); } +NTSTATUS pdb_enum_group_memberships(const char *username, gid_t primary_gid, + DOM_SID **sids, gid_t **gids, + int *num_groups) +{ + struct pdb_context *pdb_context = pdb_get_static_context(False); + + if (!pdb_context) { + return NT_STATUS_UNSUCCESSFUL; + } + + return pdb_context->pdb_enum_group_memberships(pdb_context, username, + primary_gid, sids, gids, + num_groups); +} + BOOL pdb_find_alias(const char *name, DOM_SID *sid) { struct pdb_context *pdb_context = pdb_get_static_context(False); @@ -1250,6 +1284,7 @@ NTSTATUS make_pdb_methods(TALLOC_CTX *mem_ctx, PDB_METHODS **methods) (*methods)->update_group_mapping_entry = pdb_default_update_group_mapping_entry; (*methods)->delete_group_mapping_entry = pdb_default_delete_group_mapping_entry; (*methods)->enum_group_mapping = pdb_default_enum_group_mapping; + (*methods)->enum_group_memberships = pdb_default_enum_group_memberships; (*methods)->find_alias = pdb_default_find_alias; (*methods)->create_alias = pdb_default_create_alias; (*methods)->delete_alias = pdb_default_delete_alias; diff --git a/source3/passdb/pdb_ldap.c b/source3/passdb/pdb_ldap.c index 41d650fad5..685d393eb9 100644 --- a/source3/passdb/pdb_ldap.c +++ b/source3/passdb/pdb_ldap.c @@ -2199,6 +2199,113 @@ static NTSTATUS ldapsam_getgrnam(struct pdb_methods *methods, GROUP_MAP *map, return ldapsam_getgroup(methods, filter, map); } +static NTSTATUS ldapsam_enum_group_memberships(struct pdb_methods *methods, + const char *username, + gid_t primary_gid, + DOM_SID **sids, gid_t **gids, + int *num_groups) +{ + struct ldapsam_privates *ldap_state = + (struct ldapsam_privates *)methods->private_data; + struct smbldap_state *conn = ldap_state->smbldap_state; + pstring filter; + char *attrs[] = { "gidNumber", "sambaSID", NULL }; + char *escape_name = escape_ldap_string_alloc(username); + int rc; + LDAPMessage *msg = NULL; + LDAPMessage *entry; + NTSTATUS result = NT_STATUS_UNSUCCESSFUL; + int num_sids, num_gids; + extern DOM_SID global_sid_NULL; + + if (!lp_parm_bool(-1, "ldapsam", "trusted", False)) + return pdb_default_enum_group_memberships(methods, username, + primary_gid, sids, + gids, num_groups); + + *sids = NULL; + num_sids = 0; + + if (escape_name == NULL) + return NT_STATUS_UNSUCCESSFUL; + + pstr_sprintf(filter, "(&(objectClass=posixGroup)" + "(|(memberUid=%s)(gidNumber=%d)))", + username, primary_gid); + + rc = smbldap_search(conn, lp_ldap_group_suffix(), + LDAP_SCOPE_SUBTREE, filter, attrs, 0, &msg); + + if (rc != LDAP_SUCCESS) + goto done; + + num_gids = 0; + *gids = NULL; + + num_sids = 0; + *sids = NULL; + + /* We need to add the primary group as the first gid/sid */ + + add_gid_to_array_unique(primary_gid, gids, &num_gids); + + /* This sid will be replaced later */ + + add_sid_to_array_unique(&global_sid_NULL, sids, &num_sids); + + for (entry = ldap_first_entry(conn->ldap_struct, msg); + entry != NULL; + entry = ldap_next_entry(conn->ldap_struct, entry)) + { + fstring str; + DOM_SID sid; + gid_t gid; + char *end; + + if (!smbldap_get_single_attribute(conn->ldap_struct, + entry, "sambaSID", + str, sizeof(str)-1)) + goto done; + + if (!string_to_sid(&sid, str)) + goto done; + + if (!smbldap_get_single_attribute(conn->ldap_struct, + entry, "gidNumber", + str, sizeof(str)-1)) + goto done; + + gid = strtoul(str, &end, 10); + + if (PTR_DIFF(end, str) != strlen(str)) + goto done; + + if (gid == primary_gid) { + sid_copy(&(*sids)[0], &sid); + } else { + add_gid_to_array_unique(gid, gids, &num_gids); + add_sid_to_array_unique(&sid, sids, &num_sids); + } + } + + if (sid_compare(&global_sid_NULL, &(*sids)[0]) == 0) { + DEBUG(3, ("primary group not found\n")); + goto done; + } + + *num_groups = num_sids; + + result = NT_STATUS_OK; + + done: + + SAFE_FREE(escape_name); + if (msg != NULL) + ldap_msgfree(msg); + + return result; +} + /********************************************************************** *********************************************************************/ @@ -2858,6 +2965,7 @@ static NTSTATUS pdb_init_ldapsam_common(PDB_CONTEXT *pdb_context, PDB_METHODS ** (*pdb_method)->update_group_mapping_entry = ldapsam_update_group_mapping_entry; (*pdb_method)->delete_group_mapping_entry = ldapsam_delete_group_mapping_entry; (*pdb_method)->enum_group_mapping = ldapsam_enum_group_mapping; + (*pdb_method)->enum_group_memberships = ldapsam_enum_group_memberships; /* TODO: Setup private data and free */ diff --git a/source3/rpc_server/srv_samr_nt.c b/source3/rpc_server/srv_samr_nt.c index f4348fc83e..0c52e859ca 100644 --- a/source3/rpc_server/srv_samr_nt.c +++ b/source3/rpc_server/srv_samr_nt.c @@ -1943,11 +1943,16 @@ NTSTATUS _samr_query_userinfo(pipes_struct *p, SAMR_Q_QUERY_USERINFO *q_u, SAMR_ NTSTATUS _samr_query_usergroups(pipes_struct *p, SAMR_Q_QUERY_USERGROUPS *q_u, SAMR_R_QUERY_USERGROUPS *r_u) { SAM_ACCOUNT *sam_pass=NULL; + struct passwd *passwd; DOM_SID sid; + DOM_SID *sids; DOM_GID *gids = NULL; int num_groups = 0; + gid_t *unix_gids; + int i, num_gids, num_sids; uint32 acc_granted; BOOL ret; + NTSTATUS result; /* * from the SID in the request: @@ -1986,19 +1991,53 @@ NTSTATUS _samr_query_usergroups(pipes_struct *p, SAMR_Q_QUERY_USERGROUPS *q_u, S pdb_free_sam(&sam_pass); return NT_STATUS_NO_SUCH_USER; } - - if(!get_domain_user_groups(p->mem_ctx, &num_groups, &gids, sam_pass)) { + + passwd = getpwnam_alloc(pdb_get_username(sam_pass)); + if (passwd == NULL) { pdb_free_sam(&sam_pass); - return NT_STATUS_NO_SUCH_GROUP; + return NT_STATUS_NO_SUCH_USER; } + + sids = NULL; + num_sids = 0; + + become_root(); + result = pdb_enum_group_memberships(pdb_get_username(sam_pass), + passwd->pw_gid, + &sids, &unix_gids, &num_groups); + unbecome_root(); + + pdb_free_sam(&sam_pass); + passwd_free(&passwd); + + if (!NT_STATUS_IS_OK(result)) + return result; + + SAFE_FREE(unix_gids); + + gids = NULL; + num_gids = 0; + + for (i=0; imem_ctx, gids, + sizeof(*gids) * (num_gids+1)); + gids[num_gids].attr=7; + gids[num_gids].g_rid = rid; + num_gids += 1; + } + SAFE_FREE(sids); /* construct the response. lkclXXXX: gids are not copied! */ init_samr_r_query_usergroups(r_u, num_groups, gids, r_u->status); DEBUG(5,("_samr_query_usergroups: %d\n", __LINE__)); - pdb_free_sam(&sam_pass); - return r_u->status; } diff --git a/source3/rpc_server/srv_util.c b/source3/rpc_server/srv_util.c index 215471b444..2689d89972 100644 --- a/source3/rpc_server/srv_util.c +++ b/source3/rpc_server/srv_util.c @@ -79,65 +79,6 @@ static const rid_name domain_group_rids[] = { 0 , NULL } }; -/******************************************************************* - gets a domain user's groups - ********************************************************************/ -BOOL get_domain_user_groups(TALLOC_CTX *ctx, int *numgroups, DOM_GID **pgids, SAM_ACCOUNT *sam_pass) -{ - - const char *username = pdb_get_username(sam_pass); - int n_unix_groups; - int i,j; - gid_t *unix_groups; - - *numgroups = 0; - *pgids = NULL; - - if (!getgroups_user(username, &unix_groups, &n_unix_groups)) { - return False; - } - - /* now setup the space for storing the SIDS */ - - if (n_unix_groups > 0) { - - *pgids = talloc(ctx, sizeof(DOM_GID) * n_unix_groups); - - if (!*pgids) { - DEBUG(0, ("get_user_group: malloc() failed for DOM_GID list!\n")); - SAFE_FREE(unix_groups); - return False; - } - } - - become_root(); - j = 0; - for (i = 0; i < n_unix_groups; i++) { - GROUP_MAP map; - uint32 rid; - - if (!pdb_getgrgid(&map, unix_groups[i])) { - DEBUG(3, ("get_user_groups: failed to convert gid %ld to a domain group!\n", - (long int)unix_groups[i+1])); - if (i == 0) { - DEBUG(1,("get_domain_user_groups: primary gid of user [%s] is not a Domain group !\n", username)); - DEBUGADD(1,("get_domain_user_groups: You should fix it, NT doesn't like that\n")); - } - } else if ((map.sid_name_use == SID_NAME_DOM_GRP) - && sid_peek_check_rid(get_global_sam_sid(), &map.sid, &rid)) { - (*pgids)[j].attr=7; - (*pgids)[j].g_rid=rid; - j++; - } - } - unbecome_root(); - - *numgroups = j; - - SAFE_FREE(unix_groups); - - return True; -} /******************************************************************* gets a domain user's groups from their already-calculated NT_USER_TOKEN diff --git a/source3/smbd/lanman.c b/source3/smbd/lanman.c index e7aa05b54a..1379877efc 100644 --- a/source3/smbd/lanman.c +++ b/source3/smbd/lanman.c @@ -1741,13 +1741,15 @@ static BOOL api_NetUserGetGroups(connection_struct *conn,uint16 vuid, char *para int count=0; SAM_ACCOUNT *sampw = NULL; BOOL ret = False; - DOM_GID *gids = NULL; - int num_groups = 0; + DOM_SID *sids; + gid_t *gids; + int num_groups; int i; fstring grp_domain; fstring grp_name; enum SID_NAME_USE grp_type; - DOM_SID sid, dom_sid; + struct passwd *passwd; + NTSTATUS result; *rparam_len = 8; *rparam = REALLOC(*rparam,*rparam_len); @@ -1778,6 +1780,11 @@ static BOOL api_NetUserGetGroups(connection_struct *conn,uint16 vuid, char *para /* Lookup the user information; This should only be one of our accounts (not remote domains) */ + + passwd = getpwnam_alloc(UserName); + + if (passwd == NULL) + return False; pdb_init_sam( &sampw ); @@ -1786,35 +1793,26 @@ static BOOL api_NetUserGetGroups(connection_struct *conn,uint16 vuid, char *para if ( !pdb_getsampwnam(sampw, UserName) ) goto out; - /* this next set of code is horribly inefficient, but since - it is rarely called, I'm going to leave it like this since - it easier to follow --jerry */ - - /* get the list of group SIDs */ - - if ( !get_domain_user_groups(conn->mem_ctx, &num_groups, &gids, sampw) ) { - DEBUG(1,("api_NetUserGetGroups: get_domain_user_groups() failed!\n")); + sids = NULL; + num_groups = 0; + + result = pdb_enum_group_memberships(pdb_get_username(sampw), + passwd->pw_gid, + &sids, &gids, &num_groups); + + if (!NT_STATUS_IS_OK(result)) goto out; - } - /* convert to names (we don't support universal groups so the domain - can only be ours) */ - - sid_copy( &dom_sid, get_global_sam_sid() ); for (i=0; i