winbindd: Add support for name aliasing.
authorGerald (Jerry) Carter <jerry@samba.org>
Mon, 15 Sep 2008 20:41:37 +0000 (15:41 -0500)
committerJeremy Allison <jra@samba.org>
Tue, 16 Sep 2008 17:27:49 +0000 (10:27 -0700)
* 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/include/nss_info.h
source3/winbindd/nss_info.c
source3/winbindd/nss_info_template.c
source3/winbindd/winbindd_cache.c
source3/winbindd/winbindd_proto.h
source3/winbindd/winbindd_util.c

index 1ff9ebcd55945b91ca75c90a6b353883fedf25af..e756136b76ab062af280794c04aee0c6e79e8d3c 100644 (file)
@@ -66,6 +66,10 @@ struct nss_info_methods {
                                  TALLOC_CTX *ctx, 
                                  ADS_STRUCT *ads, LDAPMessage *msg,
                                  char **homedir, char **shell, char **gecos, gid_t *p_gid);
+       NTSTATUS (*map_to_alias)( TALLOC_CTX *mem_ctx, const char *domain,
+                                 const char *name, char **alias );
+       NTSTATUS (*map_from_alias)( TALLOC_CTX *mem_ctx, const char *domain,
+                                   const char *alias, char **name );
        NTSTATUS (*close_fn)( void );
 };
 
@@ -84,6 +88,12 @@ NTSTATUS nss_get_info( const char *domain, const DOM_SID *user_sid,
                        char **homedir, char **shell, char **gecos,
                        gid_t *p_gid);
 
+NTSTATUS nss_map_to_alias( TALLOC_CTX *mem_ctx, const char *domain,
+                          const char *name, char **alias );
+
+NTSTATUS nss_map_from_alias( TALLOC_CTX *mem_ctx, const char *domain,
+                            const char *alias, char **name );
+
 NTSTATUS nss_close( const char *parameters );
 
 #endif /* _IDMAP_NSS_H_ */
index daa3dd037d85836a1e28e3c505c1babf0624e38e..0e8cb602571bb53b9adae25958a4292292bc88b6 100644 (file)
@@ -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 );
+}
+
 /********************************************************************
  *******************************************************************/
 
index aaf02e4abe9ff134b4b65c07559349d9479e9591..d8f903ddd08348edb5ffc3c12975c42b95c5d5a5 100644 (file)
@@ -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 )
index 2fbb01b623ac24b412fc49a2ce53cd238244cc4f..360e915bc47542e9ac52e3df6618ef3e0de1297f 100644 (file)
@@ -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}
index e0fc073a0a5456a43fb82cc0150067154dae2b43..4774bc8106adef83a7edd7fcb1aff844ffd5cbc6 100644 (file)
@@ -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);
index 132c96f1eefb6d7013dcc5e0fadbcd49273130ef..e7b6576317557372af5cd0863c70fdae478343a4 100644 (file)
@@ -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;
 }
 
 /*********************************************************************