From d6de32db2f8d080cb746b3032c128f210154b75e Mon Sep 17 00:00:00 2001 From: "Gerald (Jerry) Carter" Date: Mon, 15 Sep 2008 15:41:37 -0500 Subject: winbindd: Add support for name aliasing. * Add support user and group name aliasing by expanding the ws_name_replace() and ws_name_return() functions. The lookup path is aliases -> qualified name -> SID SID -> fully qualified name -> alias In other words, the name aliasing support is a thin layer built on top of SID/NAME translation. * Rename the ws_name_XX() functions to normalize_name_map() and normalize_name_unmap(). Chaneg interface to return NTSTATUS rather than char *. * Add associated cache validation functions. --- source3/winbindd/nss_info.c | 41 ++++++ source3/winbindd/nss_info_template.c | 32 ++++- source3/winbindd/winbindd_cache.c | 249 +++++++++++++++++++++++++++++++++++ source3/winbindd/winbindd_proto.h | 18 ++- source3/winbindd/winbindd_util.c | 107 ++++++++++++--- 5 files changed, 425 insertions(+), 22 deletions(-) (limited to 'source3/winbindd') diff --git a/source3/winbindd/nss_info.c b/source3/winbindd/nss_info.c index daa3dd037d..0e8cb60257 100644 --- a/source3/winbindd/nss_info.c +++ b/source3/winbindd/nss_info.c @@ -278,6 +278,47 @@ static struct nss_domain_entry *find_nss_domain( const char *domain ) homedir, shell, gecos, p_gid ); } +/******************************************************************** + *******************************************************************/ + + NTSTATUS nss_map_to_alias( TALLOC_CTX *mem_ctx, const char *domain, + const char *name, char **alias ) +{ + struct nss_domain_entry *p; + struct nss_info_methods *m; + + if ( (p = find_nss_domain( domain )) == NULL ) { + DEBUG(4,("nss_map_to_alias: Failed to find nss domain pointer for %s\n", + domain )); + return NT_STATUS_NOT_FOUND; + } + + m = p->backend->methods; + + return m->map_to_alias( mem_ctx, domain, name, alias ); +} + + +/******************************************************************** + *******************************************************************/ + + NTSTATUS nss_map_from_alias( TALLOC_CTX *mem_ctx, const char *domain, + const char *alias, char **name ) +{ + struct nss_domain_entry *p; + struct nss_info_methods *m; + + if ( (p = find_nss_domain( domain )) == NULL ) { + DEBUG(4,("nss_map_from_alias: Failed to find nss domain pointer for %s\n", + domain )); + return NT_STATUS_NOT_FOUND; + } + + m = p->backend->methods; + + return m->map_from_alias( mem_ctx, domain, alias, name ); +} + /******************************************************************** *******************************************************************/ diff --git a/source3/winbindd/nss_info_template.c b/source3/winbindd/nss_info_template.c index aaf02e4abe..d8f903ddd0 100644 --- a/source3/winbindd/nss_info_template.c +++ b/source3/winbindd/nss_info_template.c @@ -45,6 +45,8 @@ static NTSTATUS nss_template_get_info( struct nss_domain_entry *e, if ( !homedir || !shell || !gecos ) return NT_STATUS_INVALID_PARAMETER; + /* protect against home directories using whitespace in the + username */ *homedir = talloc_strdup( ctx, lp_template_homedir() ); *shell = talloc_strdup( ctx, lp_template_shell() ); *gecos = NULL; @@ -56,6 +58,28 @@ static NTSTATUS nss_template_get_info( struct nss_domain_entry *e, return NT_STATUS_OK; } +/********************************************************************** + *********************************************************************/ + +static NTSTATUS nss_template_map_to_alias( TALLOC_CTX *mem_ctx, + const char *domain, + const char *name, + char **alias ) +{ + return NT_STATUS_NOT_IMPLEMENTED; +} + +/********************************************************************** + *********************************************************************/ + +static NTSTATUS nss_template_map_from_alias( TALLOC_CTX *mem_ctx, + const char *domain, + const char *alias, + char **name ) +{ + return NT_STATUS_NOT_IMPLEMENTED; +} + /************************************************************************ ***********************************************************************/ @@ -69,9 +93,11 @@ static NTSTATUS nss_template_close( void ) ***********************************************************************/ static struct nss_info_methods nss_template_methods = { - .init = nss_template_init, - .get_nss_info = nss_template_get_info, - .close_fn = nss_template_close + .init = nss_template_init, + .get_nss_info = nss_template_get_info, + .map_to_alias = nss_template_map_to_alias, + .map_from_alias = nss_template_map_from_alias, + .close_fn = nss_template_close }; NTSTATUS nss_info_template_init( void ) diff --git a/source3/winbindd/winbindd_cache.c b/source3/winbindd/winbindd_cache.c index 2fbb01b623..360e915bc4 100644 --- a/source3/winbindd/winbindd_cache.c +++ b/source3/winbindd/winbindd_cache.c @@ -934,6 +934,8 @@ static void wcache_save_lockout_policy(struct winbindd_domain *domain, centry_free(centry); } + + static void wcache_save_password_policy(struct winbindd_domain *domain, NTSTATUS status, struct samr_DomInfo1 *policy) @@ -957,6 +959,209 @@ static void wcache_save_password_policy(struct winbindd_domain *domain, centry_free(centry); } +/*************************************************************************** + ***************************************************************************/ + +static void wcache_save_username_alias(struct winbindd_domain *domain, + NTSTATUS status, + const char *name, const char *alias) +{ + struct cache_entry *centry; + fstring uname; + + if ( (centry = centry_start(domain, status)) == NULL ) + return; + + centry_put_string( centry, alias ); + + fstrcpy(uname, name); + strupper_m(uname); + centry_end(centry, "NSS/NA/%s", uname); + + DEBUG(10,("wcache_save_username_alias: %s -> %s\n", name, alias )); + + centry_free(centry); +} + +static void wcache_save_alias_username(struct winbindd_domain *domain, + NTSTATUS status, + const char *alias, const char *name) +{ + struct cache_entry *centry; + fstring uname; + + if ( (centry = centry_start(domain, status)) == NULL ) + return; + + centry_put_string( centry, name ); + + fstrcpy(uname, alias); + strupper_m(uname); + centry_end(centry, "NSS/AN/%s", uname); + + DEBUG(10,("wcache_save_alias_username: %s -> %s\n", alias, name )); + + centry_free(centry); +} + +/*************************************************************************** + ***************************************************************************/ + +NTSTATUS resolve_username_to_alias( TALLOC_CTX *mem_ctx, + struct winbindd_domain *domain, + const char *name, char **alias ) +{ + struct winbind_cache *cache = get_cache(domain); + struct cache_entry *centry = NULL; + NTSTATUS status; + char *upper_name; + + if ( domain->internal ) + return NT_STATUS_NOT_SUPPORTED; + + if (!cache->tdb) + goto do_query; + + if ( (upper_name = SMB_STRDUP(name)) == NULL ) + return NT_STATUS_NO_MEMORY; + strupper_m(upper_name); + + centry = wcache_fetch(cache, domain, "NSS/NA/%s", upper_name); + + SAFE_FREE( upper_name ); + + if (!centry) + goto do_query; + + status = centry->status; + + if (!NT_STATUS_IS_OK(status)) { + centry_free(centry); + return status; + } + + *alias = centry_string( centry, mem_ctx ); + + centry_free(centry); + + DEBUG(10,("resolve_username_to_alias: [Cached] - mapped %s to %s\n", + name, *alias ? *alias : "(none)")); + + return (*alias) ? NT_STATUS_OK : NT_STATUS_OBJECT_NAME_NOT_FOUND; + +do_query: + + /* If its not in cache and we are offline, then fail */ + + if ( get_global_winbindd_state_offline() || !domain->online ) { + DEBUG(8,("resolve_username_to_alias: rejecting query " + "in offline mode\n")); + return NT_STATUS_NOT_FOUND; + } + + status = nss_map_to_alias( mem_ctx, domain->name, name, alias ); + + if ( NT_STATUS_IS_OK( status ) ) { + wcache_save_username_alias(domain, status, name, *alias); + } + + if ( NT_STATUS_EQUAL( status, NT_STATUS_NONE_MAPPED ) ) { + wcache_save_username_alias(domain, status, name, "(NULL)"); + } + + DEBUG(5,("resolve_username_to_alias: backend query returned %s\n", + nt_errstr(status))); + + if ( NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND) ) { + set_domain_offline( domain ); + } + + return status; +} + +/*************************************************************************** + ***************************************************************************/ + +NTSTATUS resolve_alias_to_username( TALLOC_CTX *mem_ctx, + struct winbindd_domain *domain, + const char *alias, char **name ) +{ + struct winbind_cache *cache = get_cache(domain); + struct cache_entry *centry = NULL; + NTSTATUS status; + char *upper_name; + + if ( domain->internal ) + return NT_STATUS_NOT_SUPPORTED; + + if (!cache->tdb) + goto do_query; + + if ( (upper_name = SMB_STRDUP(alias)) == NULL ) + return NT_STATUS_NO_MEMORY; + strupper_m(upper_name); + + centry = wcache_fetch(cache, domain, "NSS/AN/%s", upper_name); + + SAFE_FREE( upper_name ); + + if (!centry) + goto do_query; + + status = centry->status; + + if (!NT_STATUS_IS_OK(status)) { + centry_free(centry); + return status; + } + + *name = centry_string( centry, mem_ctx ); + + centry_free(centry); + + DEBUG(10,("resolve_alias_to_username: [Cached] - mapped %s to %s\n", + alias, *name ? *name : "(none)")); + + return (*name) ? NT_STATUS_OK : NT_STATUS_OBJECT_NAME_NOT_FOUND; + +do_query: + + /* If its not in cache and we are offline, then fail */ + + if ( get_global_winbindd_state_offline() || !domain->online ) { + DEBUG(8,("resolve_alias_to_username: rejecting query " + "in offline mode\n")); + return NT_STATUS_NOT_FOUND; + } + + /* an alias cannot contain a domain prefix or '@' */ + + if (strchr(alias, '\\') || strchr(alias, '@')) { + DEBUG(10,("resolve_alias_to_username: skipping fully " + "qualified name %s\n", alias)); + return NT_STATUS_OBJECT_NAME_INVALID; + } + + status = nss_map_from_alias( mem_ctx, domain->name, alias, name ); + + if ( NT_STATUS_IS_OK( status ) ) { + wcache_save_alias_username( domain, status, alias, *name ); + } + + if (NT_STATUS_EQUAL(status, NT_STATUS_NONE_MAPPED)) { + wcache_save_alias_username(domain, status, alias, "(NULL)"); + } + + DEBUG(5,("resolve_alias_to_username: backend query returned %s\n", + nt_errstr(status))); + + if ( NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND) ) { + set_domain_offline( domain ); + } + + return status; +} + NTSTATUS wcache_cached_creds_exist(struct winbindd_domain *domain, const DOM_SID *sid) { struct winbind_cache *cache = get_cache(domain); @@ -3257,6 +3462,48 @@ static int validate_pwinfo(TALLOC_CTX *mem_ctx, const char *keystr, return 0; } +static int validate_nss_an(TALLOC_CTX *mem_ctx, const char *keystr, + TDB_DATA dbuf, + struct tdb_validation_status *state) +{ + struct cache_entry *centry = create_centry_validate(keystr, dbuf, state); + + if (!centry) { + return 1; + } + + (void)centry_string( centry, mem_ctx ); + + centry_free(centry); + + if (!(state->success)) { + return 1; + } + DEBUG(10,("validate_pwinfo: %s ok\n", keystr)); + return 0; +} + +static int validate_nss_na(TALLOC_CTX *mem_ctx, const char *keystr, + TDB_DATA dbuf, + struct tdb_validation_status *state) +{ + struct cache_entry *centry = create_centry_validate(keystr, dbuf, state); + + if (!centry) { + return 1; + } + + (void)centry_string( centry, mem_ctx ); + + centry_free(centry); + + if (!(state->success)) { + return 1; + } + DEBUG(10,("validate_pwinfo: %s ok\n", keystr)); + return 0; +} + static int validate_trustdoms(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf, struct tdb_validation_status *state) { @@ -3358,6 +3605,8 @@ struct key_val_struct { {"NSS/PWINFO/", validate_pwinfo}, {"TRUSTDOMS/", validate_trustdoms}, {"TRUSTDOMCACHE/", validate_trustdomcache}, + {"NSS/NA/", validate_nss_na}, + {"NSS/AN/", validate_nss_an}, {"WINBINDD_OFFLINE", validate_offline}, {WINBINDD_CACHE_VERSION_KEYSTR, validate_cache_version}, {NULL, NULL} diff --git a/source3/winbindd/winbindd_proto.h b/source3/winbindd/winbindd_proto.h index e0fc073a0a..4774bc8106 100644 --- a/source3/winbindd/winbindd_proto.h +++ b/source3/winbindd/winbindd_proto.h @@ -583,8 +583,22 @@ NTSTATUS lookup_usergroups_cached(struct winbindd_domain *domain, TALLOC_CTX *mem_ctx, const DOM_SID *user_sid, uint32 *p_num_groups, DOM_SID **user_sids); -void ws_name_replace( char *name, char replace ); -void ws_name_return( char *name, char replace ); + +NTSTATUS normalize_name_map(TALLOC_CTX *mem_ctx, + struct winbindd_domain *domain, + char *name, + char **normalized); +NTSTATUS normalize_name_unmap(TALLOC_CTX *mem_ctx, + char *name, + char **normalized); + +NTSTATUS resolve_username_to_alias(TALLOC_CTX *mem_ctx, + struct winbindd_domain *domain, + const char *name, char **alias); +NTSTATUS resolve_alias_to_username(TALLOC_CTX *mem_ctx, + struct winbindd_domain *domain, + const char *alias, char **name); + bool winbindd_can_contact_domain(struct winbindd_domain *domain); bool winbindd_internal_child(struct winbindd_child *child); void winbindd_set_locator_kdc_envs(const struct winbindd_domain *domain); diff --git a/source3/winbindd/winbindd_util.c b/source3/winbindd/winbindd_util.c index 132c96f1ee..e7b6576317 100644 --- a/source3/winbindd/winbindd_util.c +++ b/source3/winbindd/winbindd_util.c @@ -1378,34 +1378,107 @@ NTSTATUS lookup_usergroups_cached(struct winbindd_domain *domain, We use this to remove spaces from user and group names ********************************************************************/ -void ws_name_replace( char *name, char replace ) +NTSTATUS normalize_name_map(TALLOC_CTX *mem_ctx, + struct winbindd_domain *domain, + char *name, + char **normalized) { - char replace_char[2] = { 0x0, 0x0 }; - - if ( !lp_winbind_normalize_names() || (replace == '\0') ) - return; + NTSTATUS nt_status; - replace_char[0] = replace; - all_string_sub( name, " ", replace_char, 0 ); + if (!name || !normalized) { + return NT_STATUS_INVALID_PARAMETER; + } - return; + if (!lp_winbind_normalize_names()) { + return NT_STATUS_PROCEDURE_NOT_FOUND; + } + + /* Alias support and whitespace replacement are mutually + exclusive */ + + nt_status = resolve_username_to_alias(mem_ctx, domain, + name, normalized ); + if (NT_STATUS_IS_OK(nt_status)) { + /* special return code to let the caller know we + mapped to an alias */ + return NT_STATUS_FILE_RENAMED; + } + + /* check for an unreachable domain */ + + if (NT_STATUS_EQUAL(nt_status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) { + DEBUG(5,("normalize_name_map: Setting domain %s offline\n", + domain->name)); + set_domain_offline(domain); + return nt_status; + } + + /* deal with whitespace */ + + *normalized = talloc_strdup(mem_ctx, name); + if (!(*normalized)) { + return NT_STATUS_NO_MEMORY; + } + + all_string_sub( *normalized, " ", "_", 0 ); + + return NT_STATUS_OK; } /********************************************************************* - We use this to do the inverse of ws_name_replace() + We use this to do the inverse of normalize_name_map() ********************************************************************/ -void ws_name_return( char *name, char replace ) +NTSTATUS normalize_name_unmap(TALLOC_CTX *mem_ctx, + char *name, + char **normalized) { - char replace_char[2] = { 0x0, 0x0 }; - - if ( !lp_winbind_normalize_names() || (replace == '\0') ) - return; + NTSTATUS nt_status; + struct winbindd_domain *domain = find_our_domain(); + + if (!name || !normalized) { + return NT_STATUS_INVALID_PARAMETER; + } - replace_char[0] = replace; - all_string_sub( name, replace_char, " ", 0 ); + if (!lp_winbind_normalize_names()) { + return NT_STATUS_PROCEDURE_NOT_FOUND; + } - return; + /* Alias support and whitespace replacement are mutally + exclusive */ + + /* When mapping from an alias to a username, we don't know the + domain. But we only need a domain structure to cache + a successful lookup , so just our own domain structure for + the seqnum. */ + + nt_status = resolve_alias_to_username(mem_ctx, domain, + name, normalized); + if (NT_STATUS_IS_OK(nt_status)) { + /* Special return code to let the caller know we mapped + from an alias */ + return NT_STATUS_FILE_RENAMED; + } + + /* check for an unreachable domain */ + + if (NT_STATUS_EQUAL(nt_status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) { + DEBUG(5,("normalize_name_unmap: Setting domain %s offline\n", + domain->name)); + set_domain_offline(domain); + return nt_status; + } + + /* deal with whitespace */ + + *normalized = talloc_strdup(mem_ctx, name); + if (!(*normalized)) { + return NT_STATUS_NO_MEMORY; + } + + all_string_sub(*normalized, "_", " ", 0); + + return NT_STATUS_OK; } /********************************************************************* -- cgit