diff --git a/source3/Makefile.in b/source3/Makefile.in index 3bb578ea84b..51b32b44311 100644 --- a/source3/Makefile.in +++ b/source3/Makefile.in @@ -627,6 +627,7 @@ WINBINDD_OBJ1 = \ nsswitch/winbindd_wins.o \ nsswitch/winbindd_rpc.o \ nsswitch/winbindd_ads.o \ + nsswitch/winbindd_passdb.o \ nsswitch/winbindd_dual.o \ nsswitch/winbindd_acct.o diff --git a/source3/groupdb/mapping.c b/source3/groupdb/mapping.c index d10a7decb7e..548651dfd53 100644 --- a/source3/groupdb/mapping.c +++ b/source3/groupdb/mapping.c @@ -28,6 +28,13 @@ static TDB_CONTEXT *tdb; /* used for driver files */ #define GROUP_PREFIX "UNIXGROUP/" +/* Alias memberships are stored reverse, as memberships. The performance + * critical operation is to determine the aliases a SID is member of, not + * listing alias members. So we store a list of alias SIDs a SID is member of + * hanging of the member as key. + */ +#define MEMBEROF_PREFIX "MEMBEROF/" + PRIVS privs[] = { {SE_PRIV_NONE, "no_privs", "No privilege" }, /* this one MUST be first */ {SE_PRIV_ADD_MACHINES, "SeMachineAccountPrivilege", "Add workstations to the domain" }, @@ -489,6 +496,284 @@ static BOOL enum_group_mapping(enum SID_NAME_USE sid_name_use, GROUP_MAP **rmap, return True; } +/* This operation happens on session setup, so it should better be fast. We + * store a list of aliases a SID is member of hanging off MEMBEROF/SID. */ + +static NTSTATUS alias_memberships(const DOM_SID *sid, DOM_SID **sids, int *num) +{ + fstring key, string_sid; + TDB_DATA kbuf, dbuf; + const char *p; + + *num = 0; + *sids = NULL; + + if (!init_group_mapping()) { + DEBUG(0,("failed to initialize group mapping\n")); + return NT_STATUS_ACCESS_DENIED; + } + + sid_to_string(string_sid, sid); + slprintf(key, sizeof(key), "%s%s", MEMBEROF_PREFIX, string_sid); + + kbuf.dsize = strlen(key)+1; + kbuf.dptr = key; + + dbuf = tdb_fetch(tdb, kbuf); + + if (dbuf.dptr == NULL) { + return NT_STATUS_OK; + } + + p = dbuf.dptr; + + while (next_token(&p, string_sid, " ", sizeof(string_sid))) { + + DOM_SID alias; + + if (!string_to_sid(&alias, string_sid)) + continue; + + add_sid_to_array(&alias, sids, num); + + if (sids == NULL) + return NT_STATUS_NO_MEMORY; + } + + SAFE_FREE(dbuf.dptr); + return NT_STATUS_OK; +} + +static BOOL is_aliasmem(const DOM_SID *alias, const DOM_SID *member) +{ + DOM_SID *sids; + int i, num; + + /* This feels the wrong way round, but the on-disk data structure + * dictates it this way. */ + if (!NT_STATUS_IS_OK(alias_memberships(member, &sids, &num))) + return False; + + for (i=0; ialias, &alias) != 0) + continue; + + /* Ok, we found the alias we're looking for in the membership + * list currently scanned. The key represents the alias + * member. Add that. */ + + member_string = strchr(key.dptr, '/'); + + /* Above we tested for MEMBEROF_PREFIX which includes the + * slash. */ + + SMB_ASSERT(member_string != NULL); + member_string += 1; + + if (!string_to_sid(&member, member_string)) + continue; + + add_sid_to_array(&member, closure->sids, closure->num); + } + + return 0; +} + +static NTSTATUS enum_aliasmem(const DOM_SID *alias, DOM_SID **sids, int *num) +{ + GROUP_MAP map; + struct aliasmem_closure closure; + + if(!init_group_mapping()) { + DEBUG(0,("failed to initialize group mapping\n")); + return NT_STATUS_ACCESS_DENIED; + } + + if (!get_group_map_from_sid(*alias, &map)) + return NT_STATUS_NO_SUCH_ALIAS; + + if ( (map.sid_name_use != SID_NAME_ALIAS) && + (map.sid_name_use != SID_NAME_WKN_GRP) ) + return NT_STATUS_NO_SUCH_ALIAS; + + *sids = NULL; + *num = 0; + + closure.alias = alias; + closure.sids = sids; + closure.num = num; + + tdb_traverse(tdb, collect_aliasmem, &closure); + return NT_STATUS_OK; +} + +static NTSTATUS del_aliasmem(const DOM_SID *alias, const DOM_SID *member) +{ + NTSTATUS result; + DOM_SID *sids; + int i, num; + BOOL found = False; + char *member_string; + TDB_DATA kbuf, dbuf; + pstring key; + fstring sid_string; + + result = alias_memberships(member, &sids, &num); + + if (!NT_STATUS_IS_OK(result)) + return result; + + for (i=0; isid_name_use != SID_NAME_ALIAS) + if ( ( (map->sid_name_use != SID_NAME_ALIAS) && + (map->sid_name_use != SID_NAME_WKN_GRP) ) || (map->gid == -1) || (getgrgid(map->gid) == NULL) ) { @@ -1029,6 +1315,178 @@ NTSTATUS pdb_default_enum_group_mapping(struct pdb_methods *methods, NT_STATUS_OK : NT_STATUS_UNSUCCESSFUL; } +NTSTATUS pdb_default_find_alias(struct pdb_methods *methods, + const char *name, DOM_SID *sid) +{ + GROUP_MAP map; + + if (!pdb_getgrnam(&map, name)) + return NT_STATUS_NO_SUCH_ALIAS; + + if ((map.sid_name_use != SID_NAME_WKN_GRP) && + (map.sid_name_use != SID_NAME_ALIAS)) + return NT_STATUS_OBJECT_TYPE_MISMATCH; + + sid_copy(sid, &map.sid); + return NT_STATUS_OK; +} + +NTSTATUS pdb_default_create_alias(struct pdb_methods *methods, + const char *name, uint32 *rid) +{ + DOM_SID sid; + enum SID_NAME_USE type; + uint32 new_rid; + gid_t gid; + + GROUP_MAP map; + + if (lookup_name(get_global_sam_name(), name, &sid, &type)) + return NT_STATUS_ALIAS_EXISTS; + + if (!winbind_allocate_rid(&new_rid)) + return NT_STATUS_ACCESS_DENIED; + + sid_copy(&sid, get_global_sam_sid()); + sid_append_rid(&sid, new_rid); + + /* Here we allocate the gid */ + if (!winbind_sid_to_gid(&gid, &sid)) { + DEBUG(0, ("Could not get gid for new RID\n")); + return NT_STATUS_ACCESS_DENIED; + } + + map.gid = gid; + sid_copy(&map.sid, &sid); + map.sid_name_use = SID_NAME_ALIAS; + fstrcpy(map.nt_name, name); + fstrcpy(map.comment, ""); + + if (!pdb_add_group_mapping_entry(&map)) { + DEBUG(0, ("Could not add group mapping entry for alias %s\n", + name)); + return NT_STATUS_ACCESS_DENIED; + } + + *rid = new_rid; + + return NT_STATUS_OK; +} + +NTSTATUS pdb_default_delete_alias(struct pdb_methods *methods, + const DOM_SID *sid) +{ + return pdb_delete_group_mapping_entry(*sid) ? + NT_STATUS_OK : NT_STATUS_ACCESS_DENIED; +} + +NTSTATUS pdb_default_enum_aliases(struct pdb_methods *methods, + const DOM_SID *sid, + uint32 start_idx, uint32 max_entries, + uint32 *num_aliases, + struct acct_info **info) +{ + extern DOM_SID global_sid_Builtin; + + GROUP_MAP *map; + int i, num_maps; + enum SID_NAME_USE type = SID_NAME_UNKNOWN; + + if (sid_compare(sid, get_global_sam_sid()) == 0) + type = SID_NAME_ALIAS; + + if (sid_compare(sid, &global_sid_Builtin) == 0) + type = SID_NAME_WKN_GRP; + + if (!pdb_enum_group_mapping(type, &map, &num_maps, False) || + (num_maps == 0)) { + *num_aliases = 0; + *info = NULL; + goto done; + } + + if (start_idx > num_maps) { + *num_aliases = 0; + *info = NULL; + goto done; + } + + *num_aliases = num_maps - start_idx; + + if (*num_aliases > max_entries) + *num_aliases = max_entries; + + *info = malloc(sizeof(struct acct_info) * (*num_aliases)); + + for (i=0; i<*num_aliases; i++) { + fstrcpy((*info)[i].acct_name, map[i+start_idx].nt_name); + fstrcpy((*info)[i].acct_desc, map[i+start_idx].comment); + sid_peek_rid(&map[i].sid, &(*info)[i+start_idx].rid); + } + + done: + SAFE_FREE(map); + return NT_STATUS_OK; +} + +NTSTATUS pdb_default_get_aliasinfo(struct pdb_methods *methods, + const DOM_SID *sid, + struct acct_info *info) +{ + GROUP_MAP map; + + if (!pdb_getgrsid(&map, *sid)) + return NT_STATUS_NO_SUCH_ALIAS; + + fstrcpy(info->acct_name, map.nt_name); + fstrcpy(info->acct_desc, map.comment); + sid_peek_rid(&map.sid, &info->rid); + return NT_STATUS_OK; +} + +NTSTATUS pdb_default_set_aliasinfo(struct pdb_methods *methods, + const DOM_SID *sid, + struct acct_info *info) +{ + GROUP_MAP map; + + if (!pdb_getgrsid(&map, *sid)) + return NT_STATUS_NO_SUCH_ALIAS; + + fstrcpy(map.comment, info->acct_desc); + + if (!pdb_update_group_mapping_entry(&map)) + return NT_STATUS_ACCESS_DENIED; + + return NT_STATUS_OK; +} + +NTSTATUS pdb_default_add_aliasmem(struct pdb_methods *methods, + const DOM_SID *alias, const DOM_SID *member) +{ + return add_aliasmem(alias, member); +} + +NTSTATUS pdb_default_del_aliasmem(struct pdb_methods *methods, + const DOM_SID *alias, const DOM_SID *member) +{ + return del_aliasmem(alias, member); +} + +NTSTATUS pdb_default_enum_aliasmem(struct pdb_methods *methods, + const DOM_SID *alias, DOM_SID **members, + int *num_members) +{ + return enum_aliasmem(alias, members, num_members); +} + +NTSTATUS pdb_default_alias_memberships(struct pdb_methods *methods, + const DOM_SID *sid, + DOM_SID **aliases, int *num) +{ + return alias_memberships(sid, aliases, num); +} + /********************************************************************** no ops for passdb backends that don't implement group mapping *********************************************************************/ diff --git a/source3/include/ntdomain.h b/source3/include/ntdomain.h index b1a4107980d..9640f2e53bf 100644 --- a/source3/include/ntdomain.h +++ b/source3/include/ntdomain.h @@ -366,13 +366,6 @@ typedef struct } rid_name; -struct acct_info -{ - fstring acct_name; /* account name */ - fstring acct_desc; /* account name */ - uint32 rid; /* domain-relative RID */ -}; - /* * higher order functions for use with msrpc client code */ diff --git a/source3/include/passdb.h b/source3/include/passdb.h index 75c4fd215bf..d08fd13a723 100644 --- a/source3/include/passdb.h +++ b/source3/include/passdb.h @@ -223,6 +223,12 @@ typedef struct sam_group { } SAM_GROUP; +struct acct_info +{ + fstring acct_name; /* account name */ + fstring acct_desc; /* account name */ + uint32 rid; /* domain-relative RID */ +}; /***************************************************************** Functions to be implemented by the new (v2) passdb API @@ -233,7 +239,7 @@ typedef struct sam_group { * this SAMBA will load. Increment this if *ANY* changes are made to the interface. */ -#define PASSDB_INTERFACE_VERSION 4 +#define PASSDB_INTERFACE_VERSION 5 typedef struct pdb_context { @@ -279,6 +285,46 @@ typedef struct pdb_context GROUP_MAP **rmap, int *num_entries, BOOL unix_only); + NTSTATUS (*pdb_find_alias)(struct pdb_context *context, + const char *name, DOM_SID *sid); + + NTSTATUS (*pdb_create_alias)(struct pdb_context *context, + const char *name, uint32 *rid); + + NTSTATUS (*pdb_delete_alias)(struct pdb_context *context, + const DOM_SID *sid); + + NTSTATUS (*pdb_enum_aliases)(struct pdb_context *context, + const DOM_SID *domain_sid, + uint32 start_idx, uint32 num_entries, + uint32 *num_aliases, + struct acct_info **aliases); + + NTSTATUS (*pdb_get_aliasinfo)(struct pdb_context *context, + const DOM_SID *sid, + struct acct_info *info); + + NTSTATUS (*pdb_set_aliasinfo)(struct pdb_context *context, + const DOM_SID *sid, + struct acct_info *info); + + NTSTATUS (*pdb_add_aliasmem)(struct pdb_context *context, + const DOM_SID *alias, + const DOM_SID *member); + + NTSTATUS (*pdb_del_aliasmem)(struct pdb_context *context, + const DOM_SID *alias, + const DOM_SID *member); + + NTSTATUS (*pdb_enum_aliasmem)(struct pdb_context *context, + const DOM_SID *alias, + DOM_SID **members, int *num_members); + + NTSTATUS (*pdb_enum_alias_memberships)(struct pdb_context *context, + const DOM_SID *alias, + DOM_SID **aliases, + int *num); + void (*free_fn)(struct pdb_context **); TALLOC_CTX *mem_ctx; @@ -330,6 +376,39 @@ typedef struct pdb_methods GROUP_MAP **rmap, int *num_entries, BOOL unix_only); + NTSTATUS (*find_alias)(struct pdb_methods *methods, + const char *name, DOM_SID *sid); + + NTSTATUS (*create_alias)(struct pdb_methods *methods, + const char *name, uint32 *rid); + + NTSTATUS (*delete_alias)(struct pdb_methods *methods, + const DOM_SID *sid); + + NTSTATUS (*enum_aliases)(struct pdb_methods *methods, + const DOM_SID *domain_sid, + uint32 start_idx, uint32 max_entries, + uint32 *num_aliases, struct acct_info **info); + + NTSTATUS (*get_aliasinfo)(struct pdb_methods *methods, + const DOM_SID *sid, + struct acct_info *info); + + NTSTATUS (*set_aliasinfo)(struct pdb_methods *methods, + const DOM_SID *sid, + struct acct_info *info); + + NTSTATUS (*add_aliasmem)(struct pdb_methods *methods, + const DOM_SID *alias, const DOM_SID *member); + NTSTATUS (*del_aliasmem)(struct pdb_methods *methods, + const DOM_SID *alias, const DOM_SID *member); + NTSTATUS (*enum_aliasmem)(struct pdb_methods *methods, + const DOM_SID *alias, DOM_SID **members, + int *num_members); + NTSTATUS (*enum_alias_memberships)(struct pdb_methods *methods, + const DOM_SID *sid, + DOM_SID **aliases, int *num); + void *private_data; /* Private data of some kind */ void (*free_private_data)(void **); diff --git a/source3/include/smbldap.h b/source3/include/smbldap.h index 2f71f971d92..61b8df0f0ef 100644 --- a/source3/include/smbldap.h +++ b/source3/include/smbldap.h @@ -49,6 +49,7 @@ #define LDAP_ATTRIBUTE_SID "sambaSID" #define LDAP_ATTRIBUTE_UIDNUMBER "uidNumber" #define LDAP_ATTRIBUTE_GIDNUMBER "gidNumber" +#define LDAP_ATTRIBUTE_SID_LIST "sambaSIDList" /* attribute map table indexes */ @@ -93,6 +94,8 @@ #define LDAP_ATTR_MUNGED_DIAL 37 #define LDAP_ATTR_BAD_PASSWORD_TIME 38 +#define LDAP_ATTR_SID_LIST 40 + typedef struct _attrib_map_entry { int attrib; const char *name; diff --git a/source3/lib/smbldap.c b/source3/lib/smbldap.c index f4f0170479f..21e2a7c1018 100644 --- a/source3/lib/smbldap.c +++ b/source3/lib/smbldap.c @@ -122,6 +122,7 @@ ATTRIB_MAP_ENTRY groupmap_attr_list[] = { { LDAP_ATTR_GIDNUMBER, LDAP_ATTRIBUTE_GIDNUMBER}, { LDAP_ATTR_GROUP_SID, LDAP_ATTRIBUTE_SID }, { LDAP_ATTR_GROUP_TYPE, "sambaGroupType" }, + { LDAP_ATTR_SID_LIST, "sambaSIDList" }, { LDAP_ATTR_DESC, "description" }, { LDAP_ATTR_DISPLAY_NAME, "displayName" }, { LDAP_ATTR_CN, "cn" }, @@ -134,6 +135,7 @@ ATTRIB_MAP_ENTRY groupmap_attr_list_to_delete[] = { { LDAP_ATTR_GROUP_TYPE, "sambaGroupType" }, { LDAP_ATTR_DESC, "description" }, { LDAP_ATTR_DISPLAY_NAME, "displayName" }, + { LDAP_ATTR_SID_LIST, "sambaSIDList" }, { LDAP_ATTR_LIST_END, NULL } }; diff --git a/source3/lib/util_str.c b/source3/lib/util_str.c index e4b07a4b731..600c830aced 100644 --- a/source3/lib/util_str.c +++ b/source3/lib/util_str.c @@ -2038,3 +2038,21 @@ SMB_BIG_UINT STR_TO_SMB_BIG_UINT(const char *nptr, const char **entptr) return val; } + +void string_append(char **left, const char *right) +{ + int new_len = strlen(right) + 1; + + if (*left == NULL) { + *left = malloc(new_len); + *left[0] = '\0'; + } else { + new_len += strlen(*left); + *left = Realloc(*left, new_len); + } + + if (*left == NULL) + return; + + safe_strcat(*left, right, new_len-1); +} diff --git a/source3/libsmb/libsmbclient.c b/source3/libsmb/libsmbclient.c index 68bb6661ebd..ebbf28a12db 100644 --- a/source3/libsmb/libsmbclient.c +++ b/source3/libsmb/libsmbclient.c @@ -246,7 +246,7 @@ smbc_parse_path(SMBCCTX *context, p += 2; /* Skip the double slash */ /* See if any options were specified */ - if (q = strrchr(p, '?')) { + if ( (q = strrchr(p, '?')) != NULL ) { /* There are options. Null terminate here and point to them */ *q++ = '\0'; @@ -537,9 +537,7 @@ SMBCSRV *smbc_server(SMBCCTX *context, SMBCSRV *srv=NULL; struct cli_state c; struct nmb_name called, calling; - char *p; const char *server_n = server; - fstring group; pstring ipenv; struct in_addr ip; int tried_reverse = 0; diff --git a/source3/nsswitch/wb_client.c b/source3/nsswitch/wb_client.c index 90e4584daba..5d431392450 100644 --- a/source3/nsswitch/wb_client.c +++ b/source3/nsswitch/wb_client.c @@ -235,6 +235,30 @@ BOOL winbind_gid_to_sid(DOM_SID *sid, gid_t gid) return (result == NSS_STATUS_SUCCESS); } +BOOL winbind_allocate_rid(uint32 *rid) +{ + struct winbindd_request request; + struct winbindd_response response; + int result; + + /* Initialise request */ + + ZERO_STRUCT(request); + ZERO_STRUCT(response); + + /* Make request */ + + result = winbindd_request(WINBINDD_ALLOCATE_RID, &request, &response); + + if (result != NSS_STATUS_SUCCESS) + return False; + + /* Copy out result */ + *rid = response.data.rid; + + return True; +} + /* Fetch the list of groups a user is a member of from winbindd. This is used by winbind_getgroups. */ diff --git a/source3/nsswitch/wbinfo.c b/source3/nsswitch/wbinfo.c index 2cea4130adc..ce48e9ae652 100644 --- a/source3/nsswitch/wbinfo.c +++ b/source3/nsswitch/wbinfo.c @@ -436,6 +436,18 @@ static BOOL wbinfo_sid_to_gid(char *sid) return True; } +static BOOL wbinfo_allocate_rid(void) +{ + uint32 rid; + + if (!winbind_allocate_rid(&rid)) + return False; + + d_printf("New rid: %d\n", rid); + + return True; +} + /* Convert sid to string */ static BOOL wbinfo_lookupsid(char *sid) @@ -1042,6 +1054,7 @@ int main(int argc, char **argv) { "gid-to-sid", 'G', POPT_ARG_INT, &int_arg, 'G', "Converts gid to sid", "GID" }, { "sid-to-uid", 'S', POPT_ARG_STRING, &string_arg, 'S', "Converts sid to uid", "SID" }, { "sid-to-gid", 'Y', POPT_ARG_STRING, &string_arg, 'Y', "Converts sid to gid", "SID" }, + { "allocate-rid", 'A', POPT_ARG_NONE, 0, 'A', "Get a new RID out of idmap" }, { "create-user", 'c', POPT_ARG_STRING, &string_arg, 'c', "Create a local user account", "name" }, { "delete-user", 'x', POPT_ARG_STRING, &string_arg, 'x', "Delete a local user account", "name" }, { "create-group", 'C', POPT_ARG_STRING, &string_arg, 'C', "Create a local group", "name" }, @@ -1164,6 +1177,12 @@ int main(int argc, char **argv) goto done; } break; + case 'A': + if (!wbinfo_allocate_rid()) { + d_printf("Could not allocate a RID\n"); + goto done; + } + break; case 't': if (!wbinfo_check_secret()) { d_printf("Could not check secret\n"); diff --git a/source3/nsswitch/winbindd.c b/source3/nsswitch/winbindd.c index b55ea297b49..283b2e4a89c 100644 --- a/source3/nsswitch/winbindd.c +++ b/source3/nsswitch/winbindd.c @@ -255,6 +255,7 @@ static struct dispatch_table dispatch_table[] = { { WINBINDD_SID_TO_GID, winbindd_sid_to_gid, "SID_TO_GID" }, { WINBINDD_GID_TO_SID, winbindd_gid_to_sid, "GID_TO_SID" }, { WINBINDD_UID_TO_SID, winbindd_uid_to_sid, "UID_TO_SID" }, + { WINBINDD_ALLOCATE_RID, winbindd_allocate_rid, "ALLOCATE_RID" }, /* Miscellaneous */ diff --git a/source3/nsswitch/winbindd.h b/source3/nsswitch/winbindd.h index 0087d58195d..5c05a1b0457 100644 --- a/source3/nsswitch/winbindd.h +++ b/source3/nsswitch/winbindd.h @@ -97,6 +97,7 @@ struct winbindd_domain { 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 */ /* Lookup methods for this domain (LDAP or RPC) */ struct winbindd_methods *methods; diff --git a/source3/nsswitch/winbindd_group.c b/source3/nsswitch/winbindd_group.c index 3ee8c0877b5..f9b6df1aed9 100644 --- a/source3/nsswitch/winbindd_group.c +++ b/source3/nsswitch/winbindd_group.c @@ -106,6 +106,15 @@ static BOOL fill_grent_mem(struct winbindd_domain *domain, DEBUG(10, ("group SID %s\n", sid_to_string(sid_string, group_sid))); *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) + return fill_passdb_alias_grmem(domain, group_sid, + num_gr_mem, + gr_mem, gr_mem_len); if ( !((group_name_type==SID_NAME_DOM_GRP) || ((group_name_type==SID_NAME_ALIAS) && domain->primary)) ) @@ -243,14 +252,11 @@ enum winbindd_result winbindd_getgrnam(struct winbindd_cli_state *state) /* if no domain or our local domain, then do a local tdb search */ - if ( !*name_domain || strequal(name_domain, get_global_sam_name()) ) { + if ( (!*name_domain || strequal(name_domain, get_global_sam_name())) && + ((grp = wb_getgrnam(name_group)) != NULL) ) { + char *buffer = NULL; - if ( !(grp=wb_getgrnam(name_group)) ) { - DEBUG(5,("winbindd_getgrnam: lookup for %s\\%s failed\n", - name_domain, name_group)); - return WINBINDD_ERROR; - } memcpy( &state->response.data.gr, grp, sizeof(WINBINDD_GR) ); gr_mem_len = gr_mem_buffer( &buffer, grp->gr_mem, grp->num_gr_mem ); @@ -262,6 +268,13 @@ enum winbindd_result winbindd_getgrnam(struct winbindd_cli_state *state) return WINBINDD_OK; } + /* 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) { @@ -287,7 +300,8 @@ enum winbindd_result winbindd_getgrnam(struct winbindd_cli_state *state) } if ( !((name_type==SID_NAME_DOM_GRP) || - ((name_type==SID_NAME_ALIAS) && domain->primary)) ) + ((name_type==SID_NAME_ALIAS) && domain->primary) || + ((name_type==SID_NAME_ALIAS) && domain->internal)) ) { DEBUG(1, ("name '%s' is not a local or domain group: %d\n", name_group, name_type)); @@ -378,7 +392,8 @@ enum winbindd_result winbindd_getgrgid(struct winbindd_cli_state *state) } if ( !((name_type==SID_NAME_DOM_GRP) || - ((name_type==SID_NAME_ALIAS) && domain->primary) )) + ((name_type==SID_NAME_ALIAS) && domain->primary) || + ((name_type==SID_NAME_ALIAS) && domain->internal)) ) { DEBUG(1, ("name '%s' is not a local or domain group: %d\n", group_name, name_type)); @@ -541,8 +556,8 @@ static BOOL get_sam_group_entries(struct getent_state *ent) /* 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 ) + if ( ( lp_security() != SEC_ADS && domain->native_mode + && domain->primary) || domain->internal ) { DEBUG(4,("get_sam_group_entries: Native Mode 2k domain; enumerating local groups as well\n")); @@ -898,6 +913,61 @@ enum winbindd_result winbindd_list_groups(struct winbindd_cli_state *state) return WINBINDD_OK; } +static void add_gid_to_array_unique(gid_t gid, gid_t **gids, int *num) +{ + int i; + + if ((*num) >= groups_max()) + return; + + for (i=0; i<*num; i++) { + if ((*gids)[i] == gid) + return; + } + + *gids = Realloc(*gids, (*num+1) * sizeof(gid_t)); + + if (*gids == NULL) + return; + + (*gids)[*num] = gid; + *num += 1; +} + +static void add_gids_from_sid(DOM_SID *sid, gid_t **gids, int *num) +{ + gid_t gid; + DOM_SID *aliases; + int j, num_aliases; + + DEBUG(10, ("Adding gids from SID: %s\n", sid_string_static(sid))); + + if (NT_STATUS_IS_OK(idmap_sid_to_gid(sid, &gid, 0))) + add_gid_to_array_unique(gid, gids, num); + + /* Don't expand aliases if not explicitly activated -- for now */ + /* we don't support windows local nested groups if we are a DC. + refer to to sid_to_gid() in the smbd server code to see why + -- jerry */ + + if (!lp_winbind_nested_groups() || IS_DC) + return; + + /* Add nested group memberships */ + + if (!pdb_enum_alias_memberships(sid, &aliases, &num_aliases)) + return; + + for (j=0; jnum_groups2, info3->num_other_sids)); num_groups = info3->num_other_sids + info3->num_groups2; - gid_list = calloc(sizeof(gid_t), num_groups); /* Go through each other sid and convert it to a gid */ @@ -1006,23 +1077,11 @@ enum winbindd_result winbindd_getgroups(struct winbindd_cli_state *state) continue; } - /* Map to a gid */ + add_gids_from_sid(&info3->other_sids[i].sid, + &gid_list, &num_gids); - if (!NT_STATUS_IS_OK(idmap_sid_to_gid(&info3->other_sids[i].sid, &gid_list[num_gids], 0)) ) - { - DEBUG(10, ("winbindd_getgroups: could not map sid %s to gid\n", - sid_string_static(&info3->other_sids[i].sid))); - continue; - } - - /* We've jumped through a lot of hoops to get here */ - - DEBUG(10, ("winbindd_getgroups: mapped other sid %s to " - "gid %lu\n", sid_string_static( - &info3->other_sids[i].sid), - (unsigned long)gid_list[num_gids])); - - num_gids++; + if (gid_list == NULL) + goto done; } for (i = 0; i < info3->num_groups2; i++) { @@ -1032,12 +1091,10 @@ enum winbindd_result winbindd_getgroups(struct winbindd_cli_state *state) sid_copy( &group_sid, &domain->sid ); sid_append_rid( &group_sid, info3->gids[i].g_rid ); - if (!NT_STATUS_IS_OK(idmap_sid_to_gid(&group_sid, &gid_list[num_gids], 0)) ) { - DEBUG(10, ("winbindd_getgroups: could not map sid %s to gid\n", - sid_string_static(&group_sid))); - } + add_gids_from_sid(&group_sid, &gid_list, &num_gids); - num_gids++; + if (gid_list == NULL) + goto done; } SAFE_FREE(info3); @@ -1049,18 +1106,15 @@ enum winbindd_result winbindd_getgroups(struct winbindd_cli_state *state) if (!NT_STATUS_IS_OK(status)) goto done; - gid_list = malloc(sizeof(gid_t) * num_groups); - if (state->response.extra_data) goto done; for (i = 0; i < num_groups; i++) { - if (!NT_STATUS_IS_OK(idmap_sid_to_gid(user_grpsids[i], &gid_list[num_gids], 0))) { - DEBUG(1, ("unable to convert group sid %s to gid\n", - sid_string_static(user_grpsids[i]))); - continue; - } - num_gids++; + add_gids_from_sid(user_grpsids[i], + &gid_list, &num_gids); + + if (gid_list == NULL) + goto done; } } diff --git a/source3/nsswitch/winbindd_nss.h b/source3/nsswitch/winbindd_nss.h index 73b39569a96..8167cfafa86 100644 --- a/source3/nsswitch/winbindd_nss.h +++ b/source3/nsswitch/winbindd_nss.h @@ -84,6 +84,7 @@ enum winbindd_cmd { WINBINDD_SID_TO_GID, WINBINDD_UID_TO_SID, WINBINDD_GID_TO_SID, + WINBINDD_ALLOCATE_RID, /* Miscellaneous other stuff */ @@ -270,7 +271,7 @@ struct winbindd_response { char user_session_key[16]; char first_8_lm_hash[8]; } auth; - uint32 rid; /* create user or group */ + uint32 rid; /* create user or group or allocate rid */ struct { fstring name; fstring alt_name; diff --git a/source3/nsswitch/winbindd_passdb.c b/source3/nsswitch/winbindd_passdb.c new file mode 100644 index 00000000000..36f5297efeb --- /dev/null +++ b/source3/nsswitch/winbindd_passdb.c @@ -0,0 +1,339 @@ +/* + 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 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" + +#undef DBGC_CLASS +#define DBGC_CLASS DBGC_WINBIND + +static void +add_member(const char *domain, const char *user, + char **members, int *num_members) +{ + fstring name; + + fill_domain_username(name, domain, user); + safe_strcat(name, ",", sizeof(name)-1); + string_append(members, name); + *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 **members, int *num_members) +{ + DOM_SID dom_sid; + uint32 rid; + struct winbindd_domain *domain; + int i; + + char *name = NULL; + enum SID_NAME_USE 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_domain_from_sid(&dom_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, + &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, members, 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 */ + + 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], members, num_members); + } + + done: + talloc_destroy(mem_ctx); + return; +} + +BOOL fill_passdb_alias_grmem(struct winbindd_domain *domain, + DOM_SID *group_sid, + int *num_gr_mem, char **gr_mem, int *gr_mem_len) +{ + DOM_SID *members; + int i, num_members; + + *num_gr_mem = 0; + *gr_mem = NULL; + *gr_mem_len = 0; + + if (!pdb_enum_aliasmem(group_sid, &members, &num_members)) + return True; + + for (i=0; isid, 0, 1000000000, + num_entries, info)) { + /* Nothing to report, just exit. */ + return NT_STATUS_OK; + } + + talloced_info = (struct acct_info *) + talloc_memdup(mem_ctx, *info, + *num_entries * sizeof(struct acct_info)); + + SAFE_FREE(*info); + *info = talloced_info; + + return NT_STATUS_OK; +} + +/* convert a single name to a sid in a domain */ +static NTSTATUS name_to_sid(struct winbindd_domain *domain, + TALLOC_CTX *mem_ctx, + const char *name, + DOM_SID *sid, + enum SID_NAME_USE *type) +{ + DEBUG(10, ("Finding name %s\n", name)); + + if (!pdb_find_alias(name, sid)) + return NT_STATUS_NONE_MAPPED; + + *type = SID_NAME_ALIAS; + 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 **name, + enum SID_NAME_USE *type) +{ + struct acct_info info; + + DEBUG(10, ("Converting SID %s\n", sid_string_static(sid))); + + if (!pdb_get_aliasinfo(sid, &info)) + return NT_STATUS_NONE_MAPPED; + + *name = talloc_strdup(mem_ctx, info.acct_name); + *type = SID_NAME_ALIAS; + + return NT_STATUS_OK; +} + +/* 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) +{ + return NT_STATUS_NO_SUCH_USER; +} + + +/* 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) +{ + return NT_STATUS_OK; +} + +/* find the sequence number for a domain */ +static NTSTATUS sequence_number(struct winbindd_domain *domain, uint32 *seq) +{ + *seq = 1; + return NT_STATUS_OK; +} + +/* 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) +{ + return NT_STATUS_OK; +} + +/* find the domain sid for a domain */ +static NTSTATUS domain_sid(struct winbindd_domain *domain, DOM_SID *sid) +{ + sid_copy(sid, &domain->sid); + return NT_STATUS_OK; +} + +/* find alternate names list for the domain + * should we look for netbios aliases?? + SSS */ +static NTSTATUS alternate_name(struct winbindd_domain *domain) +{ + DEBUG(3,("pdb: alternate_name\n")); + + 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, + query_user, + lookup_usergroups, + lookup_groupmem, + sequence_number, + trusted_domains, + domain_sid, + alternate_name +}; diff --git a/source3/nsswitch/winbindd_sid.c b/source3/nsswitch/winbindd_sid.c index 9fbf47046d6..d4206558c5e 100644 --- a/source3/nsswitch/winbindd_sid.c +++ b/source3/nsswitch/winbindd_sid.c @@ -30,10 +30,8 @@ enum winbindd_result winbindd_lookupsid(struct winbindd_cli_state *state) { - extern DOM_SID global_sid_Builtin; enum SID_NAME_USE type; - DOM_SID sid, tmp_sid; - uint32 rid; + DOM_SID sid; fstring name; fstring dom_name; @@ -50,15 +48,6 @@ enum winbindd_result winbindd_lookupsid(struct winbindd_cli_state *state) return WINBINDD_ERROR; } - /* Don't look up BUILTIN sids */ - - sid_copy(&tmp_sid, &sid); - sid_split_rid(&tmp_sid, &rid); - - if (sid_equal(&tmp_sid, &global_sid_Builtin)) { - return WINBINDD_ERROR; - } - /* Lookup the sid */ if (!winbindd_lookup_name_by_sid(&sid, dom_name, name, &type)) { @@ -445,3 +434,23 @@ done: return WINBINDD_OK; } + +enum winbindd_result winbindd_allocate_rid(struct winbindd_cli_state *state) +{ + if ( !state->privileged ) { + DEBUG(2, ("winbindd_allocate_rid: non-privileged access " + "denied!\n")); + return WINBINDD_ERROR; + } + + /* We tell idmap to always allocate a user RID. There might be a good + * reason to keep RID allocation for users to even and groups to + * odd. This needs discussion I think. For now only allocate user + * rids. */ + + if (!NT_STATUS_IS_OK(idmap_allocate_rid(&state->response.data.rid, + USER_RID_TYPE))) + return WINBINDD_ERROR; + + return WINBINDD_OK; +} diff --git a/source3/nsswitch/winbindd_util.c b/source3/nsswitch/winbindd_util.c index 403ba399c88..1a01f1c05e6 100644 --- a/source3/nsswitch/winbindd_util.c +++ b/source3/nsswitch/winbindd_util.c @@ -83,6 +83,20 @@ void free_domain_list(void) } } +static BOOL is_internal_domain(const DOM_SID *sid) +{ + DOM_SID tmp_sid; + + if (sid_equal(sid, get_global_sam_sid())) + return True; + + string_to_sid(&tmp_sid, "S-1-5-32"); + if (sid_equal(sid, &tmp_sid)) + return True; + + return False; +} + /* Add a trusted domain to our list of domains */ static struct winbindd_domain *add_trusted_domain(const char *domain_name, const char *alt_name, @@ -143,6 +157,7 @@ static struct winbindd_domain *add_trusted_domain(const char *domain_name, const domain->methods = methods; domain->backend = NULL; + domain->internal = is_internal_domain(sid); domain->sequence_number = DOM_SEQUENCE_NONE; domain->last_seq_check = 0; if (sid) { @@ -150,8 +165,9 @@ static struct winbindd_domain *add_trusted_domain(const char *domain_name, const } /* set flags about native_mode, active_directory */ - - set_dc_type_and_flags( domain ); + + if (!domain->internal) + set_dc_type_and_flags( domain ); DEBUG(3,("add_trusted_domain: %s is an %s %s domain\n", domain->name, domain->active_directory ? "ADS" : "NT4", @@ -303,6 +319,29 @@ BOOL init_domain_list(void) /* do an initial scan for trusted domains */ add_trusted_domains(domain); + + /* Don't expand aliases if not explicitly activated -- for now */ + /* we don't support windows local nested groups if we are a DC. + refer to to sid_to_gid() in the smbd server code to see why + -- jerry */ + + + if (lp_winbind_nested_groups() || IS_DC) { + + /* Add our local SAM domains */ + DOM_SID sid; + extern struct winbindd_methods passdb_methods; + struct winbindd_domain *dom; + + string_to_sid(&sid, "S-1-5-32"); + + dom = add_trusted_domain("BUILTIN", NULL, &passdb_methods, + &sid); + + dom = add_trusted_domain(get_global_sam_name(), NULL, + &passdb_methods, + get_global_sam_sid()); + } /* avoid rescanning this right away */ last_trustdom_scan = time(NULL); diff --git a/source3/param/loadparm.c b/source3/param/loadparm.c index 6b09faf7bf9..894e7ecc9c1 100644 --- a/source3/param/loadparm.c +++ b/source3/param/loadparm.c @@ -174,6 +174,7 @@ typedef struct BOOL bWinbindEnumGroups; BOOL bWinbindUseDefaultDomain; BOOL bWinbindTrustedDomainsOnly; + BOOL bWinbindNestedGroups; char *szWinbindBackend; char *szIdmapBackend; char *szAddShareCommand; @@ -1168,6 +1169,7 @@ static struct parm_struct parm_table[] = { {"winbind enum groups", P_BOOL, P_GLOBAL, &Globals.bWinbindEnumGroups, NULL, NULL, FLAG_ADVANCED}, {"winbind use default domain", P_BOOL, P_GLOBAL, &Globals.bWinbindUseDefaultDomain, NULL, NULL, FLAG_ADVANCED}, {"winbind trusted domains only", P_BOOL, P_GLOBAL, &Globals.bWinbindTrustedDomainsOnly, NULL, NULL, FLAG_ADVANCED}, + {"winbind nested groups", P_BOOL, P_GLOBAL, &Globals.bWinbindNestedGroups, NULL, NULL, FLAG_ADVANCED}, {NULL, P_BOOL, P_NONE, NULL, NULL, NULL, 0} }; @@ -1515,6 +1517,7 @@ static void init_globals(void) Globals.bWinbindEnumGroups = True; Globals.bWinbindUseDefaultDomain = False; Globals.bWinbindTrustedDomainsOnly = False; + Globals.bWinbindNestedGroups = False; Globals.bEnableRidAlgorithm = True; @@ -1687,6 +1690,7 @@ FN_GLOBAL_BOOL(lp_winbind_enum_users, &Globals.bWinbindEnumUsers) FN_GLOBAL_BOOL(lp_winbind_enum_groups, &Globals.bWinbindEnumGroups) FN_GLOBAL_BOOL(lp_winbind_use_default_domain, &Globals.bWinbindUseDefaultDomain) FN_GLOBAL_BOOL(lp_winbind_trusted_domains_only, &Globals.bWinbindTrustedDomainsOnly) +FN_GLOBAL_BOOL(lp_winbind_nested_groups, &Globals.bWinbindNestedGroups) FN_GLOBAL_STRING(lp_idmap_backend, &Globals.szIdmapBackend) FN_GLOBAL_BOOL(lp_enable_rid_algorithm, &Globals.bEnableRidAlgorithm) diff --git a/source3/passdb/pdb_interface.c b/source3/passdb/pdb_interface.c index 06097d3557b..a3b2706c35b 100644 --- a/source3/passdb/pdb_interface.c +++ b/source3/passdb/pdb_interface.c @@ -452,6 +452,156 @@ static NTSTATUS context_enum_group_mapping(struct pdb_context *context, num_entries, unix_only); } +static NTSTATUS context_find_alias(struct pdb_context *context, + const char *name, DOM_SID *sid) +{ + NTSTATUS ret = NT_STATUS_UNSUCCESSFUL; + + if ((!context) || (!context->pdb_methods)) { + DEBUG(0, ("invalid pdb_context specified!\n")); + return ret; + } + + return context->pdb_methods->find_alias(context->pdb_methods, + name, sid); +} + +static NTSTATUS context_create_alias(struct pdb_context *context, + const char *name, uint32 *rid) +{ + NTSTATUS ret = NT_STATUS_UNSUCCESSFUL; + + if ((!context) || (!context->pdb_methods)) { + DEBUG(0, ("invalid pdb_context specified!\n")); + return ret; + } + + return context->pdb_methods->create_alias(context->pdb_methods, + name, rid); +} + +static NTSTATUS context_delete_alias(struct pdb_context *context, + const DOM_SID *sid) +{ + NTSTATUS ret = NT_STATUS_UNSUCCESSFUL; + + if ((!context) || (!context->pdb_methods)) { + DEBUG(0, ("invalid pdb_context specified!\n")); + return ret; + } + + return context->pdb_methods->delete_alias(context->pdb_methods, sid); +} + +static NTSTATUS context_enum_aliases(struct pdb_context *context, + const DOM_SID *sid, + uint32 start_idx, uint32 max_entries, + uint32 *num_aliases, + struct acct_info **info) +{ + NTSTATUS ret = NT_STATUS_UNSUCCESSFUL; + + if ((!context) || (!context->pdb_methods)) { + DEBUG(0, ("invalid pdb_context specified!\n")); + return ret; + } + + return context->pdb_methods->enum_aliases(context->pdb_methods, + sid, start_idx, max_entries, + num_aliases, info); +} + +static NTSTATUS context_get_aliasinfo(struct pdb_context *context, + const DOM_SID *sid, + struct acct_info *info) +{ + NTSTATUS ret = NT_STATUS_UNSUCCESSFUL; + + if ((!context) || (!context->pdb_methods)) { + DEBUG(0, ("invalid pdb_context specified!\n")); + return ret; + } + + return context->pdb_methods->get_aliasinfo(context->pdb_methods, + sid, info); +} + +static NTSTATUS context_set_aliasinfo(struct pdb_context *context, + const DOM_SID *sid, + struct acct_info *info) +{ + NTSTATUS ret = NT_STATUS_UNSUCCESSFUL; + + if ((!context) || (!context->pdb_methods)) { + DEBUG(0, ("invalid pdb_context specified!\n")); + return ret; + } + + return context->pdb_methods->set_aliasinfo(context->pdb_methods, + sid, info); +} + +static NTSTATUS context_add_aliasmem(struct pdb_context *context, + const DOM_SID *alias, + const DOM_SID *member) +{ + NTSTATUS ret = NT_STATUS_UNSUCCESSFUL; + + if ((!context) || (!context->pdb_methods)) { + DEBUG(0, ("invalid pdb_context specified!\n")); + return ret; + } + + return context->pdb_methods->add_aliasmem(context->pdb_methods, + alias, member); +} + +static NTSTATUS context_del_aliasmem(struct pdb_context *context, + const DOM_SID *alias, + const DOM_SID *member) +{ + NTSTATUS ret = NT_STATUS_UNSUCCESSFUL; + + if ((!context) || (!context->pdb_methods)) { + DEBUG(0, ("invalid pdb_context specified!\n")); + return ret; + } + + return context->pdb_methods->del_aliasmem(context->pdb_methods, + alias, member); +} + +static NTSTATUS context_enum_aliasmem(struct pdb_context *context, + const DOM_SID *alias, DOM_SID **members, + int *num) +{ + NTSTATUS ret = NT_STATUS_UNSUCCESSFUL; + + if ((!context) || (!context->pdb_methods)) { + DEBUG(0, ("invalid pdb_context specified!\n")); + return ret; + } + + return context->pdb_methods->enum_aliasmem(context->pdb_methods, + alias, members, num); +} + +static NTSTATUS context_enum_alias_memberships(struct pdb_context *context, + const DOM_SID *sid, + DOM_SID **aliases, int *num) +{ + NTSTATUS ret = NT_STATUS_UNSUCCESSFUL; + + if ((!context) || (!context->pdb_methods)) { + DEBUG(0, ("invalid pdb_context specified!\n")); + return ret; + } + + return context->pdb_methods-> + enum_alias_memberships(context->pdb_methods, sid, aliases, + num); +} + /****************************************************************** Free and cleanup a pdb context, any associated data and anything that the attached modules might have associated. @@ -568,6 +718,17 @@ static NTSTATUS make_pdb_context(struct pdb_context **context) (*context)->pdb_delete_group_mapping_entry = context_delete_group_mapping_entry; (*context)->pdb_enum_group_mapping = context_enum_group_mapping; + (*context)->pdb_find_alias = context_find_alias; + (*context)->pdb_create_alias = context_create_alias; + (*context)->pdb_delete_alias = context_delete_alias; + (*context)->pdb_enum_aliases = context_enum_aliases; + (*context)->pdb_get_aliasinfo = context_get_aliasinfo; + (*context)->pdb_set_aliasinfo = context_set_aliasinfo; + (*context)->pdb_add_aliasmem = context_add_aliasmem; + (*context)->pdb_del_aliasmem = context_del_aliasmem; + (*context)->pdb_enum_aliasmem = context_enum_aliasmem; + (*context)->pdb_enum_alias_memberships = context_enum_alias_memberships; + (*context)->free_fn = free_pdb_context; return NT_STATUS_OK; @@ -850,6 +1011,135 @@ BOOL pdb_enum_group_mapping(enum SID_NAME_USE sid_name_use, GROUP_MAP **rmap, rmap, num_entries, unix_only)); } +BOOL pdb_find_alias(const char *name, DOM_SID *sid) +{ + struct pdb_context *pdb_context = pdb_get_static_context(False); + + if (!pdb_context) { + return False; + } + + return NT_STATUS_IS_OK(pdb_context->pdb_find_alias(pdb_context, + name, sid)); +} + +BOOL pdb_create_alias(const char *name, uint32 *rid) +{ + struct pdb_context *pdb_context = pdb_get_static_context(False); + + if (!pdb_context) { + return False; + } + + return NT_STATUS_IS_OK(pdb_context->pdb_create_alias(pdb_context, + name, rid)); +} + +BOOL pdb_delete_alias(const DOM_SID *sid) +{ + struct pdb_context *pdb_context = pdb_get_static_context(False); + + if (!pdb_context) { + return False; + } + + return NT_STATUS_IS_OK(pdb_context->pdb_delete_alias(pdb_context, + sid)); + +} + +BOOL pdb_enum_aliases(const DOM_SID *sid, uint32 start_idx, uint32 max_entries, + uint32 *num_aliases, struct acct_info **info) +{ + struct pdb_context *pdb_context = pdb_get_static_context(False); + + if (!pdb_context) { + return False; + } + + return NT_STATUS_IS_OK(pdb_context->pdb_enum_aliases(pdb_context, sid, + start_idx, + max_entries, + num_aliases, + info)); +} + +BOOL pdb_get_aliasinfo(const DOM_SID *sid, struct acct_info *info) +{ + struct pdb_context *pdb_context = pdb_get_static_context(False); + + if (!pdb_context) { + return False; + } + + return NT_STATUS_IS_OK(pdb_context->pdb_get_aliasinfo(pdb_context, sid, + info)); +} + +BOOL pdb_set_aliasinfo(const DOM_SID *sid, struct acct_info *info) +{ + struct pdb_context *pdb_context = pdb_get_static_context(False); + + if (!pdb_context) { + return False; + } + + return NT_STATUS_IS_OK(pdb_context->pdb_set_aliasinfo(pdb_context, sid, + info)); +} + +BOOL pdb_add_aliasmem(const DOM_SID *alias, const DOM_SID *member) +{ + struct pdb_context *pdb_context = pdb_get_static_context(False); + + if (!pdb_context) { + return False; + } + + return NT_STATUS_IS_OK(pdb_context-> + pdb_add_aliasmem(pdb_context, alias, member)); +} + +BOOL pdb_del_aliasmem(const DOM_SID *alias, const DOM_SID *member) +{ + struct pdb_context *pdb_context = pdb_get_static_context(False); + + if (!pdb_context) { + return False; + } + + return NT_STATUS_IS_OK(pdb_context-> + pdb_del_aliasmem(pdb_context, alias, member)); +} + +BOOL pdb_enum_aliasmem(const DOM_SID *alias, + DOM_SID **members, int *num_members) +{ + struct pdb_context *pdb_context = pdb_get_static_context(False); + + if (!pdb_context) { + return False; + } + + return NT_STATUS_IS_OK(pdb_context-> + pdb_enum_aliasmem(pdb_context, alias, + members, num_members)); +} + +BOOL pdb_enum_alias_memberships(const DOM_SID *sid, + DOM_SID **aliases, int *num) +{ + struct pdb_context *pdb_context = pdb_get_static_context(False); + + if (!pdb_context) { + return False; + } + + return NT_STATUS_IS_OK(pdb_context-> + pdb_enum_alias_memberships(pdb_context, sid, + aliases, num)); +} + /*************************************************************** Initialize the static context (at smbd startup etc). @@ -933,6 +1223,16 @@ 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)->find_alias = pdb_default_find_alias; + (*methods)->create_alias = pdb_default_create_alias; + (*methods)->delete_alias = pdb_default_delete_alias; + (*methods)->enum_aliases = pdb_default_enum_aliases; + (*methods)->get_aliasinfo = pdb_default_get_aliasinfo; + (*methods)->set_aliasinfo = pdb_default_set_aliasinfo; + (*methods)->add_aliasmem = pdb_default_add_aliasmem; + (*methods)->del_aliasmem = pdb_default_del_aliasmem; + (*methods)->enum_aliasmem = pdb_default_enum_aliasmem; + (*methods)->enum_alias_memberships = pdb_default_alias_memberships; return NT_STATUS_OK; } diff --git a/source3/passdb/pdb_ldap.c b/source3/passdb/pdb_ldap.c index 307d187f667..15635a034cc 100644 --- a/source3/passdb/pdb_ldap.c +++ b/source3/passdb/pdb_ldap.c @@ -1989,8 +1989,8 @@ static int ldapsam_search_one_group_by_gid(struct ldapsam_privates *ldap_state, { pstring filter; - pstr_sprintf(filter, "(&(objectClass=%s)(%s=%lu))", - LDAP_OBJ_POSIXGROUP, + pstr_sprintf(filter, "(&(|(objectClass=%s)(objectclass=%s))(%s=%lu))", + LDAP_OBJ_POSIXGROUP, LDAP_OBJ_IDMAP_ENTRY, get_attr_key2string(groupmap_attr_list, LDAP_ATTR_GIDNUMBER), (unsigned long)gid); @@ -2031,6 +2031,37 @@ static NTSTATUS ldapsam_add_group_mapping_entry(struct pdb_methods *methods, count = ldap_count_entries(ldap_state->smbldap_state->ldap_struct, result); + if ( count == 0 ) { + /* There's no posixGroup account, let's try to find an + * appropriate idmap entry for aliases */ + + pstring suffix; + pstring filter; + char **attr_list; + + ldap_msgfree(result); + + pstrcpy( suffix, lp_ldap_idmap_suffix() ); + pstr_sprintf(filter, "(&(objectClass=%s)(%s=%u))", + LDAP_OBJ_IDMAP_ENTRY, LDAP_ATTRIBUTE_GIDNUMBER, + map->gid); + + attr_list = get_attr_list( sidmap_attr_list ); + rc = smbldap_search(ldap_state->smbldap_state, suffix, + LDAP_SCOPE_SUBTREE, filter, attr_list, + 0, &result); + + free_attr_list(attr_list); + + if (rc != LDAP_SUCCESS) { + DEBUG(3,("Failure looking up entry (%s)\n", + ldap_err2string(rc) )); + ldap_msgfree(result); + return NT_STATUS_UNSUCCESSFUL; + } + } + + count = ldap_count_entries(ldap_state->smbldap_state->ldap_struct, result); if ( count == 0 ) { ldap_msgfree(result); return NT_STATUS_UNSUCCESSFUL; @@ -2306,6 +2337,254 @@ static NTSTATUS ldapsam_enum_group_mapping(struct pdb_methods *methods, return NT_STATUS_OK; } +static NTSTATUS ldapsam_modify_aliasmem(struct pdb_methods *methods, + const DOM_SID *alias, + const DOM_SID *member, + int modop) +{ + struct ldapsam_privates *ldap_state = + (struct ldapsam_privates *)methods->private_data; + char *dn; + LDAPMessage *result = NULL; + LDAPMessage *entry = NULL; + int count; + LDAPMod **mods = NULL; + int rc; + + pstring filter; + + pstr_sprintf(filter, "(&(|(objectClass=%s)(objectclass=%s))(%s=%s))", + LDAP_OBJ_GROUPMAP, LDAP_OBJ_IDMAP_ENTRY, + get_attr_key2string(groupmap_attr_list, + LDAP_ATTR_GROUP_SID), + sid_string_static(alias)); + + if (ldapsam_search_one_group(ldap_state, filter, + &result) != LDAP_SUCCESS) + return NT_STATUS_NO_SUCH_ALIAS; + + count = ldap_count_entries(ldap_state->smbldap_state->ldap_struct, + result); + + if (count < 1) { + DEBUG(4, ("ldapsam_add_aliasmem: Did not find alias\n")); + ldap_msgfree(result); + return NT_STATUS_NO_SUCH_ALIAS; + } + + if (count > 1) { + DEBUG(1, ("ldapsam_getgroup: Duplicate entries for filter %s: " + "count=%d\n", filter, count)); + ldap_msgfree(result); + return NT_STATUS_NO_SUCH_ALIAS; + } + + entry = ldap_first_entry(ldap_state->smbldap_state->ldap_struct, + result); + + if (!entry) { + ldap_msgfree(result); + return NT_STATUS_UNSUCCESSFUL; + } + + dn = smbldap_get_dn(ldap_state->smbldap_state->ldap_struct, entry); + if (!dn) { + ldap_msgfree(result); + return NT_STATUS_UNSUCCESSFUL; + } + + smbldap_set_mod(&mods, modop, + get_attr_key2string(groupmap_attr_list, + LDAP_ATTR_SID_LIST), + sid_string_static(member)); + + rc = smbldap_modify(ldap_state->smbldap_state, dn, mods); + + ldap_mods_free(mods, True); + ldap_msgfree(result); + + if (rc != LDAP_SUCCESS) { + char *ld_error = NULL; + ldap_get_option(ldap_state->smbldap_state->ldap_struct, + LDAP_OPT_ERROR_STRING,&ld_error); + + DEBUG(0, ("ldapsam_delete_entry: Could not delete attributes " + "for %s, error: %s (%s)\n", dn, ldap_err2string(rc), + ld_error?ld_error:"unknown")); + SAFE_FREE(ld_error); + SAFE_FREE(dn); + return NT_STATUS_UNSUCCESSFUL; + } + + SAFE_FREE(dn); + + return NT_STATUS_OK; +} + +static NTSTATUS ldapsam_add_aliasmem(struct pdb_methods *methods, + const DOM_SID *alias, + const DOM_SID *member) +{ + return ldapsam_modify_aliasmem(methods, alias, member, LDAP_MOD_ADD); +} + +static NTSTATUS ldapsam_del_aliasmem(struct pdb_methods *methods, + const DOM_SID *alias, + const DOM_SID *member) +{ + return ldapsam_modify_aliasmem(methods, alias, member, + LDAP_MOD_DELETE); +} + +static NTSTATUS ldapsam_enum_aliasmem(struct pdb_methods *methods, + const DOM_SID *alias, DOM_SID **members, + int *num_members) +{ + struct ldapsam_privates *ldap_state = + (struct ldapsam_privates *)methods->private_data; + LDAPMessage *result = NULL; + LDAPMessage *entry = NULL; + int count; + char **values; + int i; + pstring filter; + + *members = NULL; + *num_members = 0; + + pstr_sprintf(filter, "(&(|(objectClass=%s)(objectclass=%s))(%s=%s))", + LDAP_OBJ_GROUPMAP, LDAP_OBJ_IDMAP_ENTRY, + get_attr_key2string(groupmap_attr_list, + LDAP_ATTR_GROUP_SID), + sid_string_static(alias)); + + if (ldapsam_search_one_group(ldap_state, filter, + &result) != LDAP_SUCCESS) + return NT_STATUS_NO_SUCH_ALIAS; + + count = ldap_count_entries(ldap_state->smbldap_state->ldap_struct, + result); + + if (count < 1) { + DEBUG(4, ("ldapsam_add_aliasmem: Did not find alias\n")); + ldap_msgfree(result); + return NT_STATUS_NO_SUCH_ALIAS; + } + + if (count > 1) { + DEBUG(1, ("ldapsam_getgroup: Duplicate entries for filter %s: " + "count=%d\n", filter, count)); + ldap_msgfree(result); + return NT_STATUS_NO_SUCH_ALIAS; + } + + entry = ldap_first_entry(ldap_state->smbldap_state->ldap_struct, + result); + + if (!entry) { + ldap_msgfree(result); + return NT_STATUS_UNSUCCESSFUL; + } + + values = ldap_get_values(ldap_state->smbldap_state->ldap_struct, + entry, + get_attr_key2string(groupmap_attr_list, + LDAP_ATTR_SID_LIST)); + + if (values == NULL) { + ldap_msgfree(result); + return NT_STATUS_OK; + } + + count = ldap_count_values(values); + + for (i=0; iprivate_data; + + fstring sid_string; + const char *attrs[] = { LDAP_ATTRIBUTE_SID, NULL }; + + LDAPMessage *result = NULL; + LDAPMessage *entry = NULL; + int count; + int rc; + pstring filter; + + sid_to_string(sid_string, sid); + pstr_sprintf(filter, "(&(|(objectclass=%s)(objectclass=%s))(%s=%s))", + LDAP_OBJ_GROUPMAP, LDAP_OBJ_IDMAP_ENTRY, + get_attr_key2string(groupmap_attr_list, + LDAP_ATTR_SID_LIST), sid_string); + + rc = smbldap_search(ldap_state->smbldap_state, lp_ldap_group_suffix(), + LDAP_SCOPE_SUBTREE, filter, attrs, 0, &result); + + if (rc != LDAP_SUCCESS) + return NT_STATUS_UNSUCCESSFUL; + + *aliases = NULL; + *num = 0; + + count = ldap_count_entries(ldap_state->smbldap_state->ldap_struct, + result); + + if (count < 1) { + ldap_msgfree(result); + return NT_STATUS_OK; + } + + + for (entry = ldap_first_entry(ldap_state->smbldap_state->ldap_struct, + result); + entry != NULL; + entry = ldap_next_entry(ldap_state->smbldap_state->ldap_struct, + entry)) + { + DOM_SID alias; + char **vals; + vals = ldap_get_values(ldap_state->smbldap_state->ldap_struct, + entry, LDAP_ATTRIBUTE_SID); + + if (vals == NULL) + continue; + + if (vals[0] == NULL) { + ldap_value_free(vals); + continue; + } + + if (!string_to_sid(&alias, vals[0])) { + ldap_value_free(vals); + continue; + } + + add_sid_to_array(&alias, aliases, num); + ldap_value_free(vals); + } + + ldap_msgfree(result); + return NT_STATUS_OK; +} + /********************************************************************** Housekeeping *********************************************************************/ @@ -2444,6 +2723,11 @@ static NTSTATUS pdb_init_ldapsam(PDB_CONTEXT *pdb_context, PDB_METHODS **pdb_met (*pdb_method)->name = "ldapsam"; + (*pdb_method)->add_aliasmem = ldapsam_add_aliasmem; + (*pdb_method)->del_aliasmem = ldapsam_del_aliasmem; + (*pdb_method)->enum_aliasmem = ldapsam_enum_aliasmem; + (*pdb_method)->enum_alias_memberships = ldapsam_alias_memberships; + ldap_state = (*pdb_method)->private_data; ldap_state->schema_ver = SCHEMAVER_SAMBASAMACCOUNT; diff --git a/source3/passdb/util_sam_sid.c b/source3/passdb/util_sam_sid.c index f6cc2491a8b..63ef5914af8 100644 --- a/source3/passdb/util_sam_sid.c +++ b/source3/passdb/util_sam_sid.c @@ -305,3 +305,16 @@ BOOL map_name_to_wellknown_sid(DOM_SID *sid, enum SID_NAME_USE *use, const char return False; } + +void add_sid_to_array(const DOM_SID *sid, DOM_SID **sids, int *num) +{ + *sids = Realloc(*sids, ((*num)+1) * sizeof(DOM_SID)); + + if (*sids == NULL) + return; + + sid_copy(&((*sids)[*num]), sid); + *num += 1; + + return; +} diff --git a/source3/rpc_server/srv_lsa_nt.c b/source3/rpc_server/srv_lsa_nt.c index 9801ce47f8f..de4fdceba7a 100644 --- a/source3/rpc_server/srv_lsa_nt.c +++ b/source3/rpc_server/srv_lsa_nt.c @@ -171,6 +171,11 @@ static void init_lsa_rid2s(DOM_R_REF *ref, DOM_RID2 *rid2, status = lookup_name(dom_name, user, &sid, &name_type); } + if (name_type == SID_NAME_WKN_GRP) { + /* BUILTIN aliases are still aliases :-) */ + name_type = SID_NAME_ALIAS; + } + DEBUG(5, ("init_lsa_rid2s: %s\n", status ? "found" : "not found")); diff --git a/source3/rpc_server/srv_samr_nt.c b/source3/rpc_server/srv_samr_nt.c index 70ae4d170e4..2e5fe295ecb 100644 --- a/source3/rpc_server/srv_samr_nt.c +++ b/source3/rpc_server/srv_samr_nt.c @@ -879,7 +879,7 @@ static void make_group_sam_entry_list(TALLOC_CTX *ctx, SAM_ENTRY **sam_pp, UNIST Get the group entries - similar to get_sampwd_entries(). ******************************************************************/ -static NTSTATUS get_group_entries( enum SID_NAME_USE type, TALLOC_CTX *ctx, +static NTSTATUS get_group_domain_entries( TALLOC_CTX *ctx, DOMAIN_GRP **d_grp, DOM_SID *sid, uint32 start_idx, uint32 *p_num_entries, uint32 max_entries ) { @@ -894,7 +894,8 @@ static NTSTATUS get_group_entries( enum SID_NAME_USE type, TALLOC_CTX *ctx, needed for some passdb backends to enumerate groups */ become_root(); - pdb_enum_group_mapping(type, &map, (int *)&group_entries, ENUM_ONLY_MAPPED); + pdb_enum_group_mapping(SID_NAME_DOM_GRP, &map, (int *)&group_entries, + ENUM_ONLY_MAPPED); unbecome_root(); num_entries=group_entries-start_idx; @@ -915,51 +916,57 @@ static NTSTATUS get_group_entries( enum SID_NAME_USE type, TALLOC_CTX *ctx, fstrcpy((*d_grp)[i].name, map[i+start_idx].nt_name); fstrcpy((*d_grp)[i].comment, map[i+start_idx].comment); sid_split_rid(&map[i+start_idx].sid, &(*d_grp)[i].rid); - (*d_grp)[i].attr=type; + (*d_grp)[i].attr=SID_NAME_DOM_GRP; } SAFE_FREE(map); *p_num_entries = num_entries; - DEBUG(10,("get_group_entries: returning %d entries\n", *p_num_entries)); + DEBUG(10,("get_group_domain_entries: returning %d entries\n", + *p_num_entries)); return NT_STATUS_OK; } -/******************************************************************* - Wrapper for enuemrating domain groups - ******************************************************************/ - -static NTSTATUS get_group_domain_entries( TALLOC_CTX *ctx, DOMAIN_GRP **d_grp, - DOM_SID *sid, uint32 start_idx, - uint32 *p_num_entries, uint32 max_entries ) -{ - return get_group_entries( SID_NAME_DOM_GRP, ctx, d_grp, sid, start_idx, - p_num_entries, max_entries ); -} - /******************************************************************* Wrapper for enumerating local groups ******************************************************************/ -static NTSTATUS get_group_alias_entries( TALLOC_CTX *ctx, DOMAIN_GRP **d_grp, - DOM_SID *sid, uint32 start_idx, - uint32 *p_num_entries, uint32 max_entries) +static NTSTATUS get_alias_entries( TALLOC_CTX *ctx, DOMAIN_GRP **d_grp, + const DOM_SID *sid, uint32 start_idx, + uint32 *p_num_entries, uint32 max_entries ) { - if ( sid_equal(sid, &global_sid_Builtin) ) { - return get_group_entries( SID_NAME_WKN_GRP, ctx, d_grp, - sid, start_idx, p_num_entries, max_entries ); - } - else if ( sid_equal(sid, get_global_sam_sid()) ) { - return get_group_entries( SID_NAME_ALIAS, ctx, d_grp, - sid, start_idx, p_num_entries, max_entries ); + struct acct_info *info; + int i; + BOOL res; + + become_root(); + res = pdb_enum_aliases(sid, start_idx, max_entries, + p_num_entries, &info); + unbecome_root(); + + if (!res) + return NT_STATUS_ACCESS_DENIED; + + if (*p_num_entries == 0) + return NT_STATUS_OK; + + *d_grp = talloc(ctx, sizeof(DOMAIN_GRP) * (*p_num_entries)); + + if (*d_grp == NULL) { + SAFE_FREE(info); + return NT_STATUS_NO_MEMORY; } - /* can't do anything with this SID */ - - *p_num_entries = 0; + for (i=0; i<*p_num_entries; i++) { + fstrcpy((*d_grp)[i].name, info[i].acct_name); + fstrcpy((*d_grp)[i].comment, info[i].acct_desc); + (*d_grp)[i].rid = info[i].rid; + (*d_grp)[i].attr = SID_NAME_ALIAS; + } + SAFE_FREE(info); return NT_STATUS_OK; } @@ -1025,9 +1032,9 @@ NTSTATUS _samr_enum_dom_aliases(pipes_struct *p, SAMR_Q_ENUM_DOM_ALIASES *q_u, S sid_to_string(sid_str, &sid); DEBUG(5,("samr_reply_enum_dom_aliases: sid %s\n", sid_str)); - status = get_group_alias_entries(p->mem_ctx, &grp, &sid, q_u->start_idx, - &num_entries, MAX_SAM_ENTRIES); - if (NT_STATUS_IS_ERR(status)) return status; + status = get_alias_entries(p->mem_ctx, &grp, &sid, q_u->start_idx, + &num_entries, MAX_SAM_ENTRIES); + if (!NT_STATUS_IS_OK(status)) return status; make_group_sam_entry_list(p->mem_ctx, &r_u->sam, &r_u->uni_grp_name, num_entries, grp); @@ -1244,7 +1251,7 @@ NTSTATUS _samr_query_dispinfo(pipes_struct *p, SAMR_Q_QUERY_DISPINFO *q_u, NTSTATUS _samr_query_aliasinfo(pipes_struct *p, SAMR_Q_QUERY_ALIASINFO *q_u, SAMR_R_QUERY_ALIASINFO *r_u) { DOM_SID sid; - GROUP_MAP map; + struct acct_info info; uint32 acc_granted; BOOL ret; @@ -1259,12 +1266,8 @@ NTSTATUS _samr_query_aliasinfo(pipes_struct *p, SAMR_Q_QUERY_ALIASINFO *q_u, SAM return r_u->status; } - if (!sid_check_is_in_our_domain(&sid) && - !sid_check_is_in_builtin(&sid)) - return NT_STATUS_OBJECT_TYPE_MISMATCH; - become_root(); - ret = pdb_getgrsid(&map, sid); + ret = pdb_get_aliasinfo(&sid, &info); unbecome_root(); if ( !ret ) @@ -1274,12 +1277,13 @@ NTSTATUS _samr_query_aliasinfo(pipes_struct *p, SAMR_Q_QUERY_ALIASINFO *q_u, SAM case 1: r_u->ptr = 1; r_u->ctr.switch_value1 = 1; - init_samr_alias_info1(&r_u->ctr.alias.info1, map.nt_name, 1, map.comment); + init_samr_alias_info1(&r_u->ctr.alias.info1, + info.acct_name, 1, info.acct_desc); break; case 3: r_u->ptr = 1; r_u->ctr.switch_value1 = 3; - init_samr_alias_info3(&r_u->ctr.alias.info3, map.comment); + init_samr_alias_info3(&r_u->ctr.alias.info3, info.acct_desc); break; default: return NT_STATUS_INVALID_INFO_CLASS; @@ -3191,15 +3195,11 @@ NTSTATUS _samr_query_aliasmem(pipes_struct *p, SAMR_Q_QUERY_ALIASMEM *q_u, SAMR_ { int i; - GROUP_MAP map; int num_sids = 0; DOM_SID2 *sid; DOM_SID *sids=NULL; DOM_SID alias_sid; - DOM_SID als_sid; - uint32 alias_rid; - fstring alias_sid_str; uint32 acc_granted; @@ -3211,35 +3211,12 @@ NTSTATUS _samr_query_aliasmem(pipes_struct *p, SAMR_Q_QUERY_ALIASMEM *q_u, SAMR_ access_check_samr_function(acc_granted, SA_RIGHT_ALIAS_GET_MEMBERS, "_samr_query_aliasmem"))) { return r_u->status; } - - sid_copy(&als_sid, &alias_sid); - sid_to_string(alias_sid_str, &alias_sid); - sid_split_rid(&alias_sid, &alias_rid); - DEBUG(10, ("sid is %s\n", alias_sid_str)); + DEBUG(10, ("sid is %s\n", sid_string_static(&alias_sid))); - if (sid_equal(&alias_sid, &global_sid_Builtin)) { - DEBUG(10, ("lookup on Builtin SID (S-1-5-32)\n")); - if(!get_builtin_group_from_sid(&als_sid, &map)) - return NT_STATUS_NO_SUCH_ALIAS; - } else { - if (sid_equal(&alias_sid, get_global_sam_sid())) { - DEBUG(10, ("lookup on Server SID\n")); - if(!get_local_group_from_sid(&als_sid, &map)) { - fstring alias_sid_string; - DEBUG(10, ("Alias %s not found\n", sid_to_string(alias_sid_string, &als_sid))); - return NT_STATUS_NO_SUCH_ALIAS; - } - } - } - - if (!get_sid_list_of_group(map.gid, &sids, &num_sids)) { - fstring alias_sid_string; - DEBUG(10, ("Alias %s found, but member list unavailable\n", sid_to_string(alias_sid_string, &als_sid))); + if (!pdb_enum_aliasmem(&alias_sid, &sids, &num_sids)) return NT_STATUS_NO_SUCH_ALIAS; - } - DEBUG(10, ("sid is %s\n", alias_sid_str)); sid = (DOM_SID2 *)talloc_zero(p->mem_ctx, sizeof(DOM_SID2) * num_sids); if (num_sids!=0 && sid == NULL) { SAFE_FREE(sids); @@ -3250,7 +3227,6 @@ NTSTATUS _samr_query_aliasmem(pipes_struct *p, SAMR_Q_QUERY_ALIASMEM *q_u, SAMR_ init_dom_sid2(&sid[i], &sids[i]); } - DEBUG(10, ("sid is %s\n", alias_sid_str)); init_samr_r_query_aliasmem(r_u, num_sids, sid, NT_STATUS_OK); SAFE_FREE(sids); @@ -3258,20 +3234,89 @@ NTSTATUS _samr_query_aliasmem(pipes_struct *p, SAMR_Q_QUERY_ALIASMEM *q_u, SAMR_ return NT_STATUS_OK; } +static void add_uid_to_array_unique(uid_t uid, uid_t **uids, int *num) +{ + int i; + + if ((*num) >= groups_max()) + return; + + for (i=0; i<*num; i++) { + if ((*uids)[i] == uid) + return; + } + + *uids = Realloc(*uids, (*num+1) * sizeof(uid_t)); + + if (*uids == NULL) + return; + + (*uids)[*num] = uid; + *num += 1; +} + + +static BOOL get_memberuids(gid_t gid, uid_t **uids, int *num) +{ + struct group *grp; + char **gr; + struct sys_pwent *userlist, *user; + + *uids = NULL; + *num = 0; + + /* We only look at our own sam, so don't care about imported stuff */ + + winbind_off(); + + if ((grp = getgrgid(gid)) == NULL) { + winbind_on(); + return False; + } + + /* Primary group members */ + + userlist = getpwent_list(); + + for (user = userlist; user != NULL; user = user->next) { + if (user->pw_gid != gid) + continue; + add_uid_to_array_unique(user->pw_uid, uids, num); + } + + pwent_free(userlist); + + /* Secondary group members */ + + gr = grp->gr_mem; + while ((*gr != NULL) && ((*gr)[0] != '\0')) { + struct passwd *pw = getpwnam(*gr); + + if (pw == NULL) + continue; + + add_uid_to_array_unique(pw->pw_uid, uids, num); + + gr += 1; + } + + winbind_on(); + + return True; +} + /********************************************************************* _samr_query_groupmem *********************************************************************/ NTSTATUS _samr_query_groupmem(pipes_struct *p, SAMR_Q_QUERY_GROUPMEM *q_u, SAMR_R_QUERY_GROUPMEM *r_u) { - int num_sids = 0; - int final_num_sids = 0; - int i; + int final_num_rids, i; DOM_SID group_sid; fstring group_sid_str; - DOM_SID *sids=NULL; - - GROUP_MAP map; + uid_t *uids; + int num; + gid_t gid; uint32 *rid=NULL; uint32 *attr=NULL; @@ -3296,35 +3341,46 @@ NTSTATUS _samr_query_groupmem(pipes_struct *p, SAMR_Q_QUERY_GROUPMEM *q_u, SAMR_ DEBUG(10, ("lookup on Domain SID\n")); - if(!get_domain_group_from_sid(group_sid, &map)) + if (!NT_STATUS_IS_OK(sid_to_gid(&group_sid, &gid))) return NT_STATUS_NO_SUCH_GROUP; - if(!get_sid_list_of_group(map.gid, &sids, &num_sids)) + if(!get_memberuids(gid, &uids, &num)) return NT_STATUS_NO_SUCH_GROUP; - rid=talloc_zero(p->mem_ctx, sizeof(uint32)*num_sids); - attr=talloc_zero(p->mem_ctx, sizeof(uint32)*num_sids); + rid=talloc_zero(p->mem_ctx, sizeof(uint32)*num); + attr=talloc_zero(p->mem_ctx, sizeof(uint32)*num); - if (num_sids!=0 && (rid==NULL || attr==NULL)) + if (num!=0 && (rid==NULL || attr==NULL)) return NT_STATUS_NO_MEMORY; - for (i=0; istatus; } - sid_to_string(alias_sid_str, &alias_sid); - DEBUG(10, ("sid is %s\n", alias_sid_str)); + DEBUG(10, ("sid is %s\n", sid_string_static(&alias_sid))); - if (sid_compare(&alias_sid, get_global_sam_sid())>0) { - DEBUG(10, ("adding member on Server SID\n")); - if(!get_local_group_from_sid(&alias_sid, &map)) - return NT_STATUS_NO_SUCH_ALIAS; - - } else { - if (sid_compare(&alias_sid, &global_sid_Builtin)>0) { - DEBUG(10, ("adding member on BUILTIN SID\n")); - if( !get_builtin_group_from_sid(&alias_sid, &map)) - return NT_STATUS_NO_SUCH_ALIAS; + if (!pdb_add_aliasmem(&alias_sid, &q_u->sid.sid)) + return NT_STATUS_ACCESS_DENIED; - } else - return NT_STATUS_NO_SUCH_ALIAS; - } - - ret = pdb_init_sam(&sam_user); - if (!NT_STATUS_IS_OK(ret)) - return ret; - - check = pdb_getsampwsid(sam_user, &q_u->sid.sid); - - if (check != True) { - pdb_free_sam(&sam_user); - return NT_STATUS_NO_SUCH_USER; - } - - /* check a real user exist before we run the script to add a user to a group */ - if (!NT_STATUS_IS_OK(sid_to_uid(pdb_get_user_sid(sam_user), &uid))) { - pdb_free_sam(&sam_user); - return NT_STATUS_NO_SUCH_USER; - } - - pdb_free_sam(&sam_user); - - if ((pwd=getpwuid_alloc(uid)) == NULL) { - return NT_STATUS_NO_SUCH_USER; - } - - if ((grp=getgrgid(map.gid)) == NULL) { - passwd_free(&pwd); - return NT_STATUS_NO_SUCH_ALIAS; - } - - /* we need to copy the name otherwise it's overloaded in user_in_group_list */ - fstrcpy(grp_name, grp->gr_name); - - /* if the user is already in the group */ - if(user_in_unix_group_list(pwd->pw_name, grp_name)) { - passwd_free(&pwd); - return NT_STATUS_MEMBER_IN_ALIAS; - } - - /* - * ok, the group exist, the user exist, the user is not in the group, - * we can (finally) add it to the group ! - */ - smb_add_user_group(grp_name, pwd->pw_name); - - /* check if the user has been added then ... */ - if(!user_in_unix_group_list(pwd->pw_name, grp_name)) { - passwd_free(&pwd); - return NT_STATUS_MEMBER_NOT_IN_ALIAS; /* don't know what to reply else */ - } - - passwd_free(&pwd); return NT_STATUS_OK; } @@ -3433,11 +3417,6 @@ NTSTATUS _samr_add_aliasmem(pipes_struct *p, SAMR_Q_ADD_ALIASMEM *q_u, SAMR_R_AD NTSTATUS _samr_del_aliasmem(pipes_struct *p, SAMR_Q_DEL_ALIASMEM *q_u, SAMR_R_DEL_ALIASMEM *r_u) { DOM_SID alias_sid; - fstring alias_sid_str; - struct group *grp; - fstring grp_name; - GROUP_MAP map; - SAM_ACCOUNT *sam_pass=NULL; uint32 acc_granted; /* Find the policy handle. Open a policy on it. */ @@ -3448,47 +3427,12 @@ NTSTATUS _samr_del_aliasmem(pipes_struct *p, SAMR_Q_DEL_ALIASMEM *q_u, SAMR_R_DE return r_u->status; } - sid_to_string(alias_sid_str, &alias_sid); - DEBUG(10, ("_samr_del_aliasmem:sid is %s\n", alias_sid_str)); + DEBUG(10, ("_samr_del_aliasmem:sid is %s\n", + sid_string_static(&alias_sid))); - if (!sid_check_is_in_our_domain(&alias_sid) && - !sid_check_is_in_builtin(&alias_sid)) { - DEBUG(10, ("_samr_del_aliasmem:invalid alias group\n")); - return NT_STATUS_NO_SUCH_ALIAS; - } - - if( !get_local_group_from_sid(&alias_sid, &map)) - return NT_STATUS_NO_SUCH_ALIAS; - - if ((grp=getgrgid(map.gid)) == NULL) - return NT_STATUS_NO_SUCH_ALIAS; - - /* we need to copy the name otherwise it's overloaded in user_in_unix_group_list */ - fstrcpy(grp_name, grp->gr_name); - - /* check if the user exists before trying to remove it from the group */ - pdb_init_sam(&sam_pass); - if(!pdb_getsampwsid(sam_pass, &q_u->sid.sid)) { - DEBUG(5,("_samr_del_aliasmem:User %s doesn't exist.\n", pdb_get_username(sam_pass))); - pdb_free_sam(&sam_pass); - return NT_STATUS_NO_SUCH_USER; - } - - /* if the user is not in the group */ - if(!user_in_unix_group_list(pdb_get_username(sam_pass), grp_name)) { - pdb_free_sam(&sam_pass); - return NT_STATUS_MEMBER_NOT_IN_ALIAS; - } - - smb_delete_user_group(grp_name, pdb_get_username(sam_pass)); - - /* check if the user has been removed then ... */ - if(user_in_unix_group_list(pdb_get_username(sam_pass), grp_name)) { - pdb_free_sam(&sam_pass); - return NT_STATUS_MEMBER_NOT_IN_ALIAS; /* don't know what to reply else */ - } - - pdb_free_sam(&sam_pass); + if (!pdb_del_aliasmem(&alias_sid, &q_u->sid.sid)) + return NT_STATUS_ACCESS_DENIED; + return NT_STATUS_OK; } @@ -3815,12 +3759,6 @@ NTSTATUS _samr_delete_dom_group(pipes_struct *p, SAMR_Q_DELETE_DOM_GROUP *q_u, S NTSTATUS _samr_delete_dom_alias(pipes_struct *p, SAMR_Q_DELETE_DOM_ALIAS *q_u, SAMR_R_DELETE_DOM_ALIAS *r_u) { DOM_SID alias_sid; - DOM_SID dom_sid; - uint32 alias_rid; - fstring alias_sid_str; - gid_t gid; - struct group *grp; - GROUP_MAP map; uint32 acc_granted; DEBUG(5, ("_samr_delete_dom_alias: %d\n", __LINE__)); @@ -3832,38 +3770,18 @@ NTSTATUS _samr_delete_dom_alias(pipes_struct *p, SAMR_Q_DELETE_DOM_ALIAS *q_u, S if (!NT_STATUS_IS_OK(r_u->status = access_check_samr_function(acc_granted, STD_RIGHT_DELETE_ACCESS, "_samr_delete_dom_alias"))) { return r_u->status; } - - sid_copy(&dom_sid, &alias_sid); - sid_to_string(alias_sid_str, &dom_sid); - sid_split_rid(&dom_sid, &alias_rid); - DEBUG(10, ("sid is %s\n", alias_sid_str)); + DEBUG(10, ("sid is %s\n", sid_string_static(&alias_sid))); - /* we check if it's our SID before deleting */ - if (!sid_equal(&dom_sid, get_global_sam_sid())) + if (!sid_check_is_in_our_domain(&alias_sid)) return NT_STATUS_NO_SUCH_ALIAS; - + DEBUG(10, ("lookup on Local SID\n")); - if(!get_local_group_from_sid(&alias_sid, &map)) - return NT_STATUS_NO_SUCH_ALIAS; - - gid=map.gid; - - /* check if group really exists */ - if ( (grp=getgrgid(gid)) == NULL) - return NT_STATUS_NO_SUCH_ALIAS; - - /* we can delete the UNIX group */ - smb_delete_group(grp->gr_name); - - /* check if the group has been successfully deleted */ - if ( (grp=getgrgid(gid)) != NULL) + /* Have passdb delete the alias */ + if (!pdb_delete_alias(&alias_sid)) return NT_STATUS_ACCESS_DENIED; - /* don't check if we removed it as it could be an un-mapped group */ - pdb_delete_group_mapping_entry(alias_sid); - if (!close_policy_hnd(p, &q_u->alias_pol)) return NT_STATUS_OBJECT_NAME_INVALID; @@ -3941,7 +3859,6 @@ NTSTATUS _samr_create_dom_alias(pipes_struct *p, SAMR_Q_CREATE_DOM_ALIAS *q_u, S DOM_SID dom_sid; DOM_SID info_sid; fstring name; - fstring sid_string; struct group *grp; struct samr_info *info; uint32 acc_granted; @@ -3962,28 +3879,20 @@ NTSTATUS _samr_create_dom_alias(pipes_struct *p, SAMR_Q_CREATE_DOM_ALIAS *q_u, S unistr2_to_ascii(name, &q_u->uni_acct_desc, sizeof(name)-1); - /* check if group already exists */ - if ( (grp=getgrnam(name)) != NULL) - return NT_STATUS_ALIAS_EXISTS; + /* Have passdb create the alias */ + if (!pdb_create_alias(name, &r_u->rid)) + return NT_STATUS_ACCESS_DENIED; - /* we can create the UNIX group */ - if (smb_create_group(name, &gid) != 0) + sid_copy(&info_sid, get_global_sam_sid()); + sid_append_rid(&info_sid, r_u->rid); + + if (!NT_STATUS_IS_OK(sid_to_gid(&info_sid, &gid))) return NT_STATUS_ACCESS_DENIED; /* check if the group has been successfully created */ if ((grp=getgrgid(gid)) == NULL) return NT_STATUS_ACCESS_DENIED; - r_u->rid=pdb_gid_to_group_rid(grp->gr_gid); - - sid_copy(&info_sid, get_global_sam_sid()); - sid_append_rid(&info_sid, r_u->rid); - sid_to_string(sid_string, &info_sid); - - /* add the group to the mapping table */ - if(!add_initial_entry(grp->gr_gid, sid_string, SID_NAME_ALIAS, name, NULL)) - return NT_STATUS_ACCESS_DENIED; - if ((info = get_samr_info_by_sid(&info_sid)) == NULL) return NT_STATUS_NO_MEMORY; @@ -4006,7 +3915,8 @@ NTSTATUS _samr_query_groupinfo(pipes_struct *p, SAMR_Q_QUERY_GROUPINFO *q_u, SAM DOM_SID group_sid; GROUP_MAP map; DOM_SID *sids=NULL; - int num_sids=0; + uid_t *uids; + int num=0; GROUP_INFO_CTR *ctr; uint32 acc_granted; BOOL ret; @@ -4031,9 +3941,10 @@ NTSTATUS _samr_query_groupinfo(pipes_struct *p, SAMR_Q_QUERY_GROUPINFO *q_u, SAM switch (q_u->switch_level) { case 1: ctr->switch_value1 = 1; - if(!get_sid_list_of_group(map.gid, &sids, &num_sids)) + if(!get_memberuids(map.gid, &uids, &num)) return NT_STATUS_NO_SUCH_GROUP; - init_samr_group_info1(&ctr->group.info1, map.nt_name, map.comment, num_sids); + SAFE_FREE(uids); + init_samr_group_info1(&ctr->group.info1, map.nt_name, map.comment, num); SAFE_FREE(sids); break; case 3: @@ -4105,7 +4016,7 @@ NTSTATUS _samr_set_groupinfo(pipes_struct *p, SAMR_Q_SET_GROUPINFO *q_u, SAMR_R_ NTSTATUS _samr_set_aliasinfo(pipes_struct *p, SAMR_Q_SET_ALIASINFO *q_u, SAMR_R_SET_ALIASINFO *r_u) { DOM_SID group_sid; - GROUP_MAP map; + struct acct_info info; ALIAS_INFO_CTR *ctr; uint32 acc_granted; @@ -4116,22 +4027,20 @@ NTSTATUS _samr_set_aliasinfo(pipes_struct *p, SAMR_Q_SET_ALIASINFO *q_u, SAMR_R_ return r_u->status; } - if (!get_local_group_from_sid(&group_sid, &map) && - !get_builtin_group_from_sid(&group_sid, &map)) - return NT_STATUS_NO_SUCH_GROUP; - ctr=&q_u->ctr; switch (ctr->switch_value1) { case 3: - unistr2_to_ascii(map.comment, &(ctr->alias.info3.uni_acct_desc), sizeof(map.comment)-1); + unistr2_to_ascii(info.acct_desc, + &(ctr->alias.info3.uni_acct_desc), + sizeof(info.acct_desc)-1); break; default: return NT_STATUS_INVALID_INFO_CLASS; } - if(!pdb_update_group_mapping_entry(&map)) { - return NT_STATUS_NO_SUCH_GROUP; + if(!pdb_set_aliasinfo(&group_sid, &info)) { + return NT_STATUS_ACCESS_DENIED; } return NT_STATUS_OK; diff --git a/source3/utils/net_groupmap.c b/source3/utils/net_groupmap.c index 2b487ef17b4..a3a13e1dd88 100644 --- a/source3/utils/net_groupmap.c +++ b/source3/utils/net_groupmap.c @@ -608,6 +608,102 @@ static int net_groupmap_cleanup(int argc, const char **argv) return 0; } +static int net_groupmap_addmem(int argc, const char **argv) +{ + DOM_SID alias, member; + + if ( (argc != 2) || + !string_to_sid(&alias, argv[0]) || + !string_to_sid(&member, argv[1]) ) { + d_printf("Usage: net groupmap addmem alias-sid member-sid\n"); + return -1; + } + + if (!pdb_add_aliasmem(&alias, &member)) { + d_printf("Could not add sid %s to alias %s\n", + argv[1], argv[0]); + return -1; + } + + return 0; +} + +static int net_groupmap_delmem(int argc, const char **argv) +{ + DOM_SID alias, member; + + if ( (argc != 2) || + !string_to_sid(&alias, argv[0]) || + !string_to_sid(&member, argv[1]) ) { + d_printf("Usage: net groupmap delmem alias-sid member-sid\n"); + return -1; + } + + if (!pdb_del_aliasmem(&alias, &member)) { + d_printf("Could not delete sid %s from alias %s\n", + argv[1], argv[0]); + return -1; + } + + return 0; +} + +static int net_groupmap_listmem(int argc, const char **argv) +{ + DOM_SID alias; + DOM_SID *members; + int i, num; + NTSTATUS result; + + if ( (argc != 1) || + !string_to_sid(&alias, argv[0]) ) { + d_printf("Usage: net groupmap listmem alias-sid\n"); + return -1; + } + + if (!pdb_enum_aliasmem(&alias, &members, &num)) { + d_printf("Could not list members for sid %s: %s\n", + argv[0], nt_errstr(result)); + return -1; + } + + for (i = 0; i < num; i++) { + printf("%s\n", sid_string_static(&(members[i]))); + } + + SAFE_FREE(members); + + return 0; +} + +static int net_groupmap_memberships(int argc, const char **argv) +{ + DOM_SID member; + DOM_SID *aliases; + int i, num; + NTSTATUS result; + + if ( (argc != 1) || + !string_to_sid(&member, argv[0]) ) { + d_printf("Usage: net groupmap memberof sid\n"); + return -1; + } + + if (!pdb_enum_alias_memberships(&member, &aliases, &num)) { + d_printf("Could not list memberships for sid %s: %s\n", + argv[0], nt_errstr(result)); + return -1; + } + + for (i = 0; i < num; i++) { + printf("%s\n", sid_string_static(&(aliases[i]))); + } + + SAFE_FREE(aliases); + + return 0; +} + int net_help_groupmap(int argc, const char **argv) { d_printf("net groupmap add"\ @@ -616,6 +712,14 @@ int net_help_groupmap(int argc, const char **argv) "\n Update a group mapping\n"); d_printf("net groupmap delete"\ "\n Remove a group mapping\n"); + d_printf("net groupmap addmember"\ + "\n Add a foreign alias member\n"); + d_printf("net groupmap delmember"\ + "\n Delete a foreign alias member\n"); + d_printf("net groupmap listmembers"\ + "\n List foreign group members\n"); + d_printf("net groupmap memberships"\ + "\n List foreign group memberships\n"); d_printf("net groupmap list"\ "\n List current group map\n"); d_printf("net groupmap set"\ @@ -638,6 +742,10 @@ int net_groupmap(int argc, const char **argv) {"delete", net_groupmap_delete}, {"set", net_groupmap_set}, {"cleanup", net_groupmap_cleanup}, + {"addmem", net_groupmap_addmem}, + {"delmem", net_groupmap_delmem}, + {"listmem", net_groupmap_listmem}, + {"memberships", net_groupmap_memberships}, {"list", net_groupmap_list}, {"help", net_help_groupmap}, {NULL, NULL}