diff options
-rw-r--r-- | source3/Makefile.in | 5 | ||||
-rw-r--r-- | source3/configure.in | 1 | ||||
-rw-r--r-- | source3/sam/idmap_rid.c | 513 |
3 files changed, 519 insertions, 0 deletions
diff --git a/source3/Makefile.in b/source3/Makefile.in index b6fe22b593..3a32bab1ba 100644 --- a/source3/Makefile.in +++ b/source3/Makefile.in @@ -1137,6 +1137,11 @@ bin/smbpasswd.@SHLIBEXT@: passdb/pdb_smbpasswd.@PICSUFFIX@ @$(SHLD) $(LDSHFLAGS) -o $@ passdb/pdb_smbpasswd.@PICSUFFIX@ \ @SONAMEFLAG@`basename $@` +bin/idmap_rid.@SHLIBEXT@: sam/idmap_rid.@PICSUFFIX@ + @echo "Building plugin $@" + @$(SHLD) $(LDSHFLAGS) -o $@ sam/idmap_rid.@PICSUFFIX@ \ + @SONAMEFLAG@`basename $@` + bin/weird.@SHLIBEXT@: $(DEVEL_HELP_WEIRD_OBJ:.o=.@PICSUFFIX@) @echo "Building plugin $@" @$(SHLD) $(LDSHFLAGS) -o $@ $(DEVEL_HELP_WEIRD_OBJ:.o=.@PICSUFFIX@) \ diff --git a/source3/configure.in b/source3/configure.in index 6a36fbcdea..df78765559 100644 --- a/source3/configure.in +++ b/source3/configure.in @@ -4457,6 +4457,7 @@ SMB_SUBSYSTEM(RPC,smbd/server.o) SMB_MODULE(idmap_ldap, sam/idmap_ldap.o, "bin/idmap_ldap.$SHLIBEXT", IDMAP) SMB_MODULE(idmap_tdb, sam/idmap_tdb.o, "bin/idmap_tdb.$SHLIBEXT", IDMAP) +SMB_MODULE(idmap_rid, sam/idmap_rid.o, "bin/idmap_rid.$SHLIBEXT", IDMAP) SMB_SUBSYSTEM(IDMAP,sam/idmap.o) SMB_MODULE(charset_weird, modules/weird.o, "bin/weird.$SHLIBEXT", CHARSET) diff --git a/source3/sam/idmap_rid.c b/source3/sam/idmap_rid.c new file mode 100644 index 0000000000..0a81475378 --- /dev/null +++ b/source3/sam/idmap_rid.c @@ -0,0 +1,513 @@ +/* + * idmap_rid: static map between Active Directory/NT RIDs and RFC 2307 accounts + * Copyright (C) Guenther Deschner, 2004 + * Copyright (C) Sumit Bose, 2004 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * TODO: + * - use str_list_copy to work on a either space or comma-separated list + * - assure somehow that we cannot swap uids/gids (difficult without any + * knowledge about the sid-type) + * - fix memory allocation + * - further cleanup + * - add spnego-login + */ + +#include "includes.h" + +#undef DBGC_CLASS +#define DBGC_CLASS DBGC_IDMAP + +NTSTATUS init_module(void); + +struct dom_entry { + char *name; + char *sid; + int min_id; + int max_id; +}; + +typedef struct trust_dom_array { + int number; + struct dom_entry *dom; +} trust_dom_array; + +static trust_dom_array trust; + +static NTSTATUS rid_idmap_parse(char *init_param, + uint32 num_domains, + char **domain_names, + DOM_SID *domain_sids, + uid_t u_low, + uid_t u_high) +{ + char *p, *e; + int i; + trust.number = 0; + trust.dom = NULL; + fstring sid_str; + BOOL known_domain = False; + p = init_param; + + /* init sizes */ + trust.dom = (struct dom_entry *) malloc(sizeof(struct dom_entry)); + if (trust.dom == NULL) + return NT_STATUS_NO_MEMORY; + + trust.dom[0].sid = (char *) malloc(strlen(sid_str)); + if (trust.dom[0].sid == NULL) + return NT_STATUS_NO_MEMORY; + + trust.dom[0].name = (char *) malloc(strlen(p)); + if (trust.dom[0].name == NULL) + return NT_STATUS_NO_MEMORY; + + /* falling back to automatic mapping when there were no options given */ + if (!*init_param) { + + DEBUG(3,("rid_idmap_parse: no domain list given, falling back to automatic mapping for own domain:\n")); + + sid_to_string(sid_str, &domain_sids[num_domains-1]); + trust.dom[0].name = domain_names[num_domains-1]; + trust.dom[0].sid = strdup(sid_str); + trust.dom[0].min_id = u_low; + trust.dom[0].max_id = u_high; + trust.number = 1; + DEBUGADD(3,("rid_idmap_parse:\tdomain: [%s], sid: [%s], range=[%d-%d]\n", + trust.dom[0].name, trust.dom[0].sid, trust.dom[0].min_id, trust.dom[0].max_id)); + return NT_STATUS_OK; + } + + /* scan through the init_param-list */ + for(;;) { + + DEBUG(3,("rid_idmap_parse: parsing entry: %d\n", trust.number)); + + /* reinit sizes */ + trust.dom = (struct dom_entry *) realloc(trust.dom,sizeof(struct dom_entry)*(trust.number+1)); + if ( trust.dom == NULL ) + return NT_STATUS_NO_MEMORY; + + trust.dom[trust.number].sid = (char *) malloc(strlen(p)); + trust.dom[trust.number].name = (char *) malloc(strlen(p)); + if (trust.dom[trust.number].sid == NULL || trust.dom[trust.number].name == NULL) + return NT_STATUS_NO_MEMORY; + + + if ( (e = strchr(p,'=')) == NULL ) { + DEBUG(0, ("rid_idmap_parse: no '=' sign found in domain list [%s]\n", init_param)); + return NT_STATUS_INVALID_PARAMETER; + } + *e = '\0'; + + /* add the name */ + i=0; + do { + if ( *p != ' ' && *p != '\t' ) { + trust.dom[trust.number].name[i++] = *p; + } + p++; + } while(*p); + + trust.dom[trust.number].name[i++] = '\0'; + DEBUGADD(3,("rid_idmap_parse:\tentry %d has name: [%s]\n", trust.number, trust.dom[trust.number].name)); + + /* add the domain-sid */ + for (i=0; i<num_domains; i++) { + known_domain = False; + if (strequal(domain_names[i],trust.dom[trust.number].name)) { + sid_to_string(sid_str, &domain_sids[i]); + trust.dom[trust.number].sid = strdup(sid_str); + DEBUGADD(3,("rid_idmap_parse:\tentry %d has sid: [%s]\n", trust.number, trust.dom[trust.number].sid)); + known_domain = True; + } + } + + if (!known_domain) { + DEBUG(0,("rid_idmap_parse: your DC does not know anything about: [%s]\n", trust.dom[trust.number].name)); + return NT_STATUS_INVALID_PARAMETER; + } + + p = e + 1; + if ( (e = strchr(p,'-')) == NULL ) + return NT_STATUS_INVALID_PARAMETER; + + *e = '\0'; + + /* add min_id */ + trust.dom[trust.number].min_id = atoi(p); + DEBUGADD(3,("rid_idmap_parse:\tentry %d has min_id: [%d]\n", trust.number, trust.dom[trust.number].min_id)); + p = e + 1; + + /* add max_id */ + trust.dom[trust.number].max_id = atoi(p); + DEBUGADD(3,("rid_idmap_parse:\tentry %d has max_id: [%d]\n", trust.number, trust.dom[trust.number].max_id)); + trust.number++; + + if ( (e = strchr(p,',')) == NULL ) { + break; + } else { + *e = '\0'; + p = e + 1; + } + + } + return NT_STATUS_OK; + +} + +static NTSTATUS rid_idmap_get_domains(uint32 *num_domains, char ***domain_names, DOM_SID **domain_sids) +{ + NTSTATUS status = NT_STATUS_UNSUCCESSFUL; + struct cli_state *cli; + TALLOC_CTX *mem_ctx; + POLICY_HND pol; + uint32 des_access = SEC_RIGHTS_MAXIMUM_ALLOWED; + uint32 enum_ctx = 0; + fstring dc_name; + struct in_addr dc_ip; + + char *password = NULL; + char *username = NULL; + char *domain = NULL; + uint32 info_class = 5; + char *domain_name = NULL; + DOM_SID *domain_sid; + fstring sid_str; + int i; + uint32 trusted_num_domains = 0; + char **trusted_domain_names; + DOM_SID *trusted_domain_sids; + + /* create mem_ctx */ + if (!(mem_ctx = talloc_init("rid_idmap_get_trusted_domains"))) { + DEBUG(0, ("rid_idmap_get_domains: talloc_init() failed\n")); + return NT_STATUS_NO_MEMORY; + } + + if (!get_dc_name(lp_workgroup(), 0, dc_name, &dc_ip)) { + DEBUG(1, ("rid_idmap_get_domains: could not get dc-name\n")); + return NT_STATUS_UNSUCCESSFUL; + } + + /* open a connection to the dc */ + username = secrets_fetch(SECRETS_AUTH_USER, NULL); + password = secrets_fetch(SECRETS_AUTH_PASSWORD, NULL); + domain = secrets_fetch(SECRETS_AUTH_DOMAIN, NULL); + + if (username) { + + if (!domain) + domain = smb_xstrdup(lp_workgroup()); + + if (!password) + password = smb_xstrdup(""); + + DEBUG(3, ("rid_idmap_get_domains: IPC$ connections done by user %s\\%s\n", domain, username)); + + } else { + + DEBUG(3, ("rid_idmap_get_domains: IPC$ connections done anonymously\n")); + username = smb_xstrdup(""); + domain = smb_xstrdup(""); + password = smb_xstrdup(""); + } + + DEBUG(10, ("rid_idmap_get_domains: opening connection to [%s]\n", dc_name)); + status = cli_full_connection(&cli, global_myname(), dc_name, + NULL, 0, + "IPC$", "IPC", + username, + lp_workgroup(), + password, + CLI_FULL_CONNECTION_ANNONYMOUS_FALLBACK, True, NULL); + if (!NT_STATUS_IS_OK(status)) { + DEBUG(1, ("rid_idmap_get_domains: could not setup connection to dc\n")); + return status; + } + + /* query the lsa-pipe */ + if (!cli_nt_session_open (cli, PI_LSARPC)) { + DEBUG(1, ("rid_idmap_get_domains: could not setup connection to dc\n")); + goto out; + } + + /* query policies */ + status = cli_lsa_open_policy(cli, mem_ctx, False, des_access, &pol); + if (!NT_STATUS_IS_OK(status)) { + goto out; + } + status = cli_lsa_query_info_policy(cli, mem_ctx, &pol, info_class, &domain_name, &domain_sid); + if (!NT_STATUS_IS_OK(status)) { + DEBUG(1, ("rid_idmap_get_domains: cannot retrieve domain-info\n")); + goto out; + } + + sid_to_string(sid_str, domain_sid); + DEBUG(10,("rid_idmap_get_domains: my domain: [%s], sid: [%s]\n", domain_name, sid_str)); + + /* scan trusted domains */ + DEBUG(10, ("rid_idmap_get_domains: enumerating trusted domains\n")); + status = cli_lsa_enum_trust_dom(cli, mem_ctx, &pol, &enum_ctx, + &trusted_num_domains, + &trusted_domain_names, + &trusted_domain_sids); + + if (!NT_STATUS_IS_OK(status) && + !NT_STATUS_EQUAL(status, NT_STATUS_NO_MORE_ENTRIES) && + !NT_STATUS_EQUAL(status, STATUS_MORE_ENTRIES)) { + DEBUG(1, ("rid_idmap_get_domains: could not enumerate trusted domains\n")); + goto out; + } + + /* show trusted domains */ + DEBUG(10,("rid_idmap_get_domains: scan for domains gave %d results:\n", trusted_num_domains)); + for (i=0;i<trusted_num_domains;i++) { + sid_to_string(sid_str, &trusted_domain_sids[i]); + DEBUGADD(10,("rid_idmap_get_domains:\t#%d\tDOMAIN: [%s], SID: [%s]\n", + i, trusted_domain_names[i], sid_str)); + } + + /* put the results together */ + *num_domains = trusted_num_domains + 1; + *domain_names = (char **) malloc(sizeof(char *) * *num_domains); + *domain_sids = (DOM_SID *) malloc(sizeof(DOM_SID) * *num_domains); + + /* first add myself at the end*/ + (*domain_names)[*num_domains-1] = strdup(domain_name); + sid_copy(&(*domain_sids)[*num_domains-1], domain_sid); + + /* add trusted domains */ + for (i=0;i<trusted_num_domains;i++) { + (*domain_names)[i] = strdup(trusted_domain_names[i]); + sid_copy(&((*domain_sids)[i]), &(trusted_domain_sids[i])); + } + + /* show complete domain list */ + DEBUG(5,("rid_idmap_get_domains: complete domain-list has %d entries:\n", *num_domains)); + for (i=0;i<*num_domains;i++) { + sid_to_string(sid_str, &((*domain_sids)[i])); + DEBUGADD(5,("rid_idmap_get_domains:\t#%d\tdomain: [%s], sid: [%s]\n", + i, (*domain_names)[i], sid_str )); + } + + status = NT_STATUS_OK; + +out: + cli_lsa_close(cli, mem_ctx, &pol); + cli_nt_session_close(cli); + talloc_destroy(mem_ctx); + cli_shutdown(cli); + return status; +} + +static NTSTATUS rid_idmap_init(char *init_param) +{ + int i, j; + uid_t u_low, u_high; + gid_t g_low, g_high; + uint32 num_domains = 0; + char **domain_names; + DOM_SID *domain_sids; + NTSTATUS nt_status; + + /* basic sanity checks */ + if (!lp_idmap_uid(&u_low, &u_high) || !lp_idmap_gid(&g_low, &g_high)) { + DEBUG(0, ("rid_idmap_init: cannot get global idmap-ranges.\n")); + return NT_STATUS_INVALID_PARAMETER; + } + + if (u_low != g_low || u_high != g_high) { + DEBUG(0, ("rid_idmap_init: range defined in \"idmap uid\" must match range of \"idmap gid\".\n")); + return NT_STATUS_INVALID_PARAMETER; + } + + /* retrieve full domain list */ + nt_status = rid_idmap_get_domains(&num_domains, &domain_names, &domain_sids); + if (!NT_STATUS_IS_OK(nt_status) && + !NT_STATUS_EQUAL(nt_status, NT_STATUS_NO_MORE_ENTRIES) && + !NT_STATUS_EQUAL(nt_status, STATUS_MORE_ENTRIES)) { + DEBUG(0, ("rid_idmap_init: cannot fetch sids for domain and/or trusted-domains from domain-controller.\n")); + return NT_STATUS_INVALID_PARAMETER; + } + + /* parse the init string */ + nt_status = rid_idmap_parse(init_param, num_domains, domain_names, domain_sids, u_low, u_high); + if (!NT_STATUS_IS_OK(nt_status)) { + DEBUG(0, ("rid_idmap_init: cannot parse module-configuration\n")); + return NT_STATUS_INVALID_PARAMETER; + } + + /* some basic sanity checks */ + for(i=0;i<trust.number;i++) { + DEBUG(10,("rid_idmap_init: sanity checks on mapping of domain: [%s]\n", trust.dom[i].name)); + if (trust.dom[i].min_id > trust.dom[i].max_id) { + DEBUG(0, ("rid_idmap_init: min_id has to be smaller than max_id for %s\n",trust.dom[i].name)); + return NT_STATUS_INVALID_PARAMETER; + } + if (trust.dom[i].min_id < u_low || trust.dom[i].max_id > u_high) { + DEBUG(0, ("rid_idmap_init: mapping of domain %s (%d-%d) has to fit into global idmap range (%d-%d).\n", + trust.dom[i].name, trust.dom[i].min_id, trust.dom[i].max_id, u_low, u_high)); + return NT_STATUS_INVALID_PARAMETER; + } + } + + /* check for overlaps */ + for(i=0;i<trust.number-1;i++) { + for(j=i+1;j<trust.number;j++) { + if (trust.dom[i].min_id <= trust.dom[j].max_id && trust.dom[j].min_id <= trust.dom[i].max_id) { + DEBUG(0, ("rid_idmap_init: the ranges of %s and %s overlap\n", + trust.dom[i+1].name, trust.dom[i].name)); + return NT_STATUS_INVALID_PARAMETER; + } + } + } + + DEBUG(3, ("rid_idmap_init: using the %d following mappings:\n", trust.number)); + for(i=0;i<trust.number;i++) { + DEBUGADD(3, ("rid_idmap_init:\tdomain: [%s], sid: [%s], min_id: [%d], max_id: [%d]\n", + trust.dom[i].name, trust.dom[i].sid, trust.dom[i].min_id, trust.dom[i].max_id)); + } + + return NT_STATUS_OK; +} + +static NTSTATUS rid_idmap_get_sid_from_id(DOM_SID *sid, unid_t unid, int id_type) +{ + fstring sid_string; + int i; + DOM_SID sidstr; + + if (sid == NULL) { + return NT_STATUS_INVALID_PARAMETER; + } + + /* find range */ + for(i=0;i<trust.number;i++) { + if (trust.dom[i].min_id <= unid.uid && trust.dom[i].max_id >= unid.uid ) + break; + } + + if (i == trust.number) { + DEBUG(0,("no range found for uid: %d\n",unid.uid)); + return NT_STATUS_NOT_IMPLEMENTED; + } + + /* use lower-end of idmap-range as offset for users and groups*/ + (unsigned long)unid.uid -= trust.dom[i].min_id; + + string_to_sid(&sidstr,trust.dom[i].sid); + sid_copy(sid, &sidstr); + if (!sid_append_rid( sid, (unsigned long)unid.uid )) { + DEBUG(0,("rid_idmap_get_sid_from_id: could not append rid to domain sid\n")); + return NT_STATUS_NO_MEMORY; + } + + DEBUG(3, ("rid_idmap_get_sid_from_id: mapped POSIX %s %d to SID [%s]\n", + (id_type == ID_GROUPID) ? "GID" : "UID", unid.uid, + sid_to_string(sid_string, sid))); + + return NT_STATUS_OK; +} + +static NTSTATUS rid_idmap_get_id_from_sid(unid_t *unid, int *id_type, const DOM_SID *sid) +{ + fstring sid_string; + int i; + uint32 rid; + DOM_SID sidstr; + + if (unid == NULL) { + return NT_STATUS_INVALID_PARAMETER; + } + + /* check if we have a mapping for the sid*/ + for(i=0;i<trust.number;i++) { + string_to_sid(&sidstr,trust.dom[i].sid); + if ( sid_compare_domain(sid, &sidstr) == 0 ) + break; + } + + if (i == trust.number) { + DEBUG(0,("rid_idmap_get_id_from_sid: we have no mapping for sid: [%s] in domain: [%s]\n", + sid_to_string(sid_string, sid), trust.dom[i].name)); + return NT_STATUS_NOT_IMPLEMENTED; + } + + if (!sid_peek_rid(sid, &rid)) { + DEBUG(0,("rid_idmap_get_id_from_sid: could not peek rid\n")); + return NT_STATUS_NOT_IMPLEMENTED; + } + + /* use lower-end of idmap-range as offset for users and groups */ + (unsigned long)unid->uid = rid + trust.dom[i].min_id; + + if (unid->uid > trust.dom[i].max_id) { + DEBUG(0,("rid_idmap_get_id_from_sid: rid: %d too high for mapping of domain: %s\n", rid, trust.dom[i].name)); + return NT_STATUS_INVALID_PARAMETER; + } + if (unid->uid < trust.dom[i].min_id) { + DEBUG(0,("rid_idmap_get_id_from_sid: rid: %d too low for mapping of domain: %s\n", rid, trust.dom[i].name)); + return NT_STATUS_INVALID_PARAMETER; + } + + DEBUG(3,("rid_idmap_get_id_from_sid: mapped SID [%s] to POSIX %s %d\n", + sid_to_string(sid_string, sid), + (*id_type == ID_GROUPID) ? "GID" : "UID", unid->uid)); + + return NT_STATUS_OK; + +} + +static NTSTATUS rid_idmap_set_mapping(const DOM_SID *sid, unid_t id, int id_type) +{ + return NT_STATUS_NOT_IMPLEMENTED; +} + +static NTSTATUS rid_idmap_close(void) +{ + int i; + for(i=0;i<trust.number;i++) { + free(trust.dom[i].sid); + free(trust.dom[i].name); + } + free(trust.dom); + + return NT_STATUS_OK; +} + +static NTSTATUS rid_idmap_allocate_rid(uint32 *rid, int rid_type) +{ + return NT_STATUS_NOT_IMPLEMENTED; +} + +static NTSTATUS rid_idmap_allocate_id(unid_t *id, int id_type) +{ + return NT_STATUS_NOT_IMPLEMENTED; +} + +static void rid_idmap_status(void) +{ + DEBUG(0, ("RID IDMAP Status not available\n")); +} + +static struct idmap_methods rid_methods = { + rid_idmap_init, + rid_idmap_allocate_rid, + rid_idmap_allocate_id, + rid_idmap_get_sid_from_id, + rid_idmap_get_id_from_sid, + rid_idmap_set_mapping, + rid_idmap_close, + rid_idmap_status +}; + +NTSTATUS init_module(void) +{ + return smb_register_idmap(SMB_IDMAP_INTERFACE_VERSION, "idmap_rid", &rid_methods); +} + |