Here's the code to make winbindd work on a Samba DC
authorGerald Carter <jerry@samba.org>
Sun, 29 Jun 2003 03:39:50 +0000 (03:39 +0000)
committerGerald Carter <jerry@samba.org>
Sun, 29 Jun 2003 03:39:50 +0000 (03:39 +0000)
to handle domain trusts.  Jeremy and I talked about this
and it's going in as working code.  It keeps winbind clean
and solves the trust problem with minimal changes.

To summarize, there are 2 basic cases where the deadlock would
occur.  (1) lookuping up secondary groups for a user, and
(2) get[gr|pw]nam() calls that fall through the NSS layer because
they don't exist anywhere.

o To handle case #1, we bypass winbindd in sys_getgrouplist() unless
  the username includes the 'winbind separator'.

o Case #2 is handled by adding checks in winbindd to return failure
  if we are a DC and the domain matches our own.

This code has been tested using basic share connections, domain
logons, and with pam_winbind (both with and without 'winbind
use default domain').  The 'trustdomain' auth module should work
as well if an admin wants to manually create UNIX users for
acounts in the trusted domains.

Other misc fixes:

  * we need to fix check_ntlm_password() to be able to determine
    if an auth module is authoritative over a user (NT_STATUS_WRONG_PASSWORD,
    etc...).  I worked around my specific situation, but this needs to be
    fixed.  the winbindd auth module was causing delays.
  * fix named server mutex deadlock between trust domain auth module
    and winbindd looking up a uid
  * make sure SAM_ACCOUNT gets stored in the server_info struct for the
    _net_sam_logon() reply.

Configuration details:

The recommended method for supporting trusts is to use winbind.
The gets us around some of the server mutex issues as well.

  * set 'files winbind' for passwd: and group: in /etc/nsswitch.conf
  * create domain trusts like normal
  * join winbind on the pdc to the Samba domain using 'net rpc join'
  * add normal parameters to smb.conf for winbind
  * set 'auth method = guest sam winbind'
  * start smbd, nmbd, & winbindd

Problems that remain:

  * join a Windows 2k/XP box to a Samba domain.
  * create a 2-way trust between the Samba domain
    and an NT domain
  * logon to the windows client as a user from theh trusted
    domain
  * try to browse server in the trusted domain (or other
    workstations).  an NT client seems to work ok, but 2k
    and XP either prompt for passwords or fail with errors.

apparanently this never got tested since no one has ever been
able to logon as a trusted user to a Samba domain from a Windows
client.
(This used to be commit f804b590f9dbf1f0147c06a0a2f12e221ae6fc3b)

source3/auth/auth.c
source3/auth/auth_domain.c
source3/auth/auth_util.c
source3/lib/system_smbd.c
source3/nsswitch/winbindd_cm.c
source3/nsswitch/winbindd_group.c
source3/nsswitch/winbindd_pam.c
source3/nsswitch/winbindd_user.c
source3/rpc_server/srv_netlog_nt.c

index 25b856cd57b489b4cf8aa0285d5865404f935bd4..c50fc1a9b76ef41388b5be150fec7371d6fd223a 100644 (file)
@@ -269,9 +269,15 @@ static NTSTATUS check_ntlm_password(const struct auth_context *auth_context,
                }
 
                talloc_destroy(mem_ctx);
-
-               if (NT_STATUS_IS_OK(nt_status))
-                       break;
+               
+               /* this sucks.  Somehow we have to know if an authentication module is 
+                  authoritative for a user.  Fixme!!!  --jerry */
+               
+               if ( NT_STATUS_IS_OK(nt_status) || 
+                       NT_STATUS_V(nt_status) == NT_STATUS_V(NT_STATUS_WRONG_PASSWORD) )
+               {
+                               break;                  
+               }
        }
 
        if (NT_STATUS_IS_OK(nt_status)) {
index 84453ac3b5843bea80bcf09d30f93d79e6699da8..39ec864f754ac9f0a3fa43408d182ae9dc22d8a5 100644 (file)
@@ -346,6 +346,8 @@ static NTSTATUS domain_client_validate(TALLOC_CTX *mem_ctx,
                                                   user_info->lm_resp, user_info->nt_resp, 
                                                   &info3);
         
+       release_server_mutex();
+
        if (!NT_STATUS_IS_OK(nt_status)) {
                DEBUG(0,("domain_client_validate: unable to validate password "
                          "for user %s in domain %s to Domain controller %s. "
@@ -386,7 +388,6 @@ static NTSTATUS domain_client_validate(TALLOC_CTX *mem_ctx,
        cli_nt_session_close(cli);
        cli_ulogoff(cli);
        cli_shutdown(cli);
-       release_server_mutex();
        return nt_status;
 }
 
index fe4900f9f4912a7b0c623090f3b0547e13078917..71fdb0050be31ef564bf306bfa060043dee32417 100644 (file)
@@ -1031,15 +1031,17 @@ NTSTATUS make_server_info_info3(TALLOC_CTX *mem_ctx,
                return NT_STATUS_NO_MEMORY;
        }
 
-       /* now that we have a SAM_ACCOUNT that looks real, make a server_info
-          to wrap it in, and use pass it on down */
-
        if (!NT_STATUS_IS_OK(nt_status = make_server_info(server_info))) {
                DEBUG(4, ("make_server_info failed!\n"));
                pdb_free_sam(&sam_account);
                return nt_status;
        }
 
+       /* save this here to _net_sam_logon() doesn't fail (it assumes a 
+          valid SAM_ACCOUNT) */
+                  
+       (*server_info)->sam_account = sam_account;
+
        /* Fill in the unix info we found on the way */
 
        (*server_info)->sam_fill_level = SAM_FILL_ALL;
index 3ae0a6395edecf2c4aae32a6b9f35fe31aa19fd2..ca7803166083f0d9fd3a98c16baf2f6e9be222b1 100644 (file)
@@ -107,13 +107,31 @@ static int getgrouplist_internals(const char *user, gid_t gid, gid_t *groups, in
 
 int sys_getgrouplist(const char *user, gid_t gid, gid_t *groups, int *grpcnt)
 {
+       char *p;
+       int retval;
+
+       DEBUG(10,("sys_getgrouplist: user [%s]\n", user));
+       
+       /* see if we should disable winbindd lookups for local users */
+       if ( (p = strchr(user, *lp_winbind_separator())) == NULL ) {
+               if ( setenv(WINBINDD_DONT_ENV, "1", True) == -1 )
+                       DEBUG(0,("sys_getgroup_list: Insufficient environment space for %s\n",
+                               WINBINDD_DONT_ENV));
+               else
+                       DEBUG(10,("sys_getgrouplist(): disabled winbindd for group lookup [user == %s]\n",
+                               user));
+       }
+
 #ifdef HAVE_GETGROUPLIST
-       return getgrouplist(user, gid, groups, grpcnt);
+       retval = getgrouplist(user, gid, groups, grpcnt);
 #else
-       int retval;
        become_root();
        retval = getgrouplist_internals(user, gid, groups, grpcnt);
        unbecome_root();
-       return retval;
 #endif
+
+       /* allow winbindd lookups */
+       unsetenv( WINBINDD_DONT_ENV );
+       
+       return retval;
 }
index 9f5bf3f11a8f23cc45be72baa58c3fca0244f05d..0cb0f0cb99ad2bcacb2da40e2f13dfd3e2931ddc 100644 (file)
@@ -559,6 +559,9 @@ NTSTATUS cm_get_netlogon_cli(const char *domain,
        if (!(got_mutex = secrets_named_mutex(lock_name, WINBIND_SERVER_MUTEX_WAIT_TIME))) {
                DEBUG(0,("cm_get_netlogon_cli: mutex grab failed for %s\n", conn->controller));
        }
+       
+       if ( sec_channel_type == SEC_CHAN_DOMAIN )
+               snprintf(conn->cli->mach_acct, sizeof(conn->cli->mach_acct) - 1, "%s$", lp_workgroup());
                        
        result = cli_nt_establish_netlogon(conn->cli, sec_channel_type, trust_passwd);
        
index e4b0e78e2e358bbd36e3444be5aa2bdccbe762cd..265297ca083aa94320349b45e9e13f564b8ba694 100644 (file)
@@ -213,6 +213,17 @@ enum winbindd_result winbindd_getgrnam(struct winbindd_cli_state *state)
        if (!parse_domain_user(tmp, name_domain, name_group))
                return WINBINDD_ERROR;
 
+       /* don't handle our own domain if we are a DC.  This code handles cases where
+          the account doesn't exist anywhere and gets passed on down the NSS layer */
+
+       if ( ((lp_server_role() == ROLE_DOMAIN_PDC) || (lp_server_role()==ROLE_DOMAIN_BDC)) &&
+               strequal(name_domain, lp_workgroup()) ) 
+       {
+               DEBUG(7,("winbindd_getgrnam: rejecting getpwnam() for %s\\%s since I am on the PDC for this domain\n", 
+                       name_domain, name_group));
+               return WINBINDD_ERROR;
+       }       
+       
        /* Get info for the domain */
 
        if ((domain = find_domain_from_name(name_domain)) == NULL) {
index 7b93015b5d6b758eeaff25f5693f55d5ce1ea023..a3b08497219cb4f2ac2fe929ddc1b7dcf16307ba 100644 (file)
@@ -235,11 +235,52 @@ enum winbindd_result winbindd_pam_auth_crap(struct winbindd_cli_state *state)
 
        DEBUG(3, ("[%5d]: pam auth crap domain: %s user: %s\n", state->pid,
                  domain, user));
+                 
+       /* check our role as a domain member first */
+       if ( lp_server_role() == ROLE_DOMAIN_MEMBER ) {
+               if ( !lp_allow_trusted_domains() && !strequal(domain, lp_workgroup()) ) {
+                       DEBUG(5,("winbindd_pam_auth_crap: failing autghentication becuase of disallowed trust domains\n"));
+                       result = NT_STATUS_LOGON_FAILURE;
+                       goto done;
+               }
+               
+               contact_domain = domain;
 
-       if (lp_allow_trusted_domains() && (state->request.data.auth_crap.flags & WINBIND_PAM_CONTACT_TRUSTDOM)) {
+               /*
+                * Get the machine account password for the domain to contact.
+                * This is either our own domain for a workstation, or possibly
+                * any domain for a PDC with trusted domains.
+                */
+
+               if (!secrets_fetch_trust_account_password (contact_domain,
+                                                          trust_passwd,
+                                                          &last_change_time,
+                                                          &sec_channel_type)) {
+                       DEBUG(0, ("winbindd_pam_auth_crap: could not fetch trust account "
+                                 "password for domain %s\n", contact_domain));
+                       result = NT_STATUS_CANT_ACCESS_DOMAIN_INFO;
+                       goto done;
+               }
+       }
+       else if ( lp_allow_trusted_domains() ) {
+               /* if we are not a domain member, then we must be a DC.  Must never 
+                  see a logon for our domain */
+               DOM_SID sid;
+               char *pwd;
                contact_domain = domain;
-       } else {
-               contact_domain = lp_workgroup();
+
+               if (!secrets_fetch_trusted_domain_password (contact_domain,
+                                                           &pwd, &sid,
+                                                           &last_change_time)) {
+                       DEBUG(0, ("winbindd_pam_auth_crap: could not fetch trust account "
+                                 "password for domain %s\n", contact_domain));
+                       result = NT_STATUS_CANT_ACCESS_DOMAIN_INFO;
+                       goto done;
+               }
+               sec_channel_type = SEC_CHAN_DOMAIN;
+               E_md4hash(pwd, trust_passwd);
+               SAFE_FREE(pwd);
+
        }
 
        if (*state->request.data.auth_crap.workstation) {
@@ -264,21 +305,6 @@ enum winbindd_result winbindd_pam_auth_crap(struct winbindd_cli_state *state)
        lm_resp = data_blob_talloc(mem_ctx, state->request.data.auth_crap.lm_resp, state->request.data.auth_crap.lm_resp_len);
        nt_resp = data_blob_talloc(mem_ctx, state->request.data.auth_crap.nt_resp, state->request.data.auth_crap.nt_resp_len);
        
-       /*
-        * Get the machine account password for the domain to contact.
-        * This is either our own domain for a workstation, or possibly
-        * any domain for a PDC with trusted domains.
-        */
-
-       if (!secrets_fetch_trust_account_password (
-                contact_domain, trust_passwd, &last_change_time,
-               &sec_channel_type)) {
-               DEBUG(0, ("winbindd_pam_auth: could not fetch trust account "
-                          "password for domain %s\n", contact_domain));
-               result = NT_STATUS_CANT_ACCESS_DOMAIN_INFO;
-               goto done;
-       }
-
        do {
                ZERO_STRUCT(info3);
                ZERO_STRUCT(ret_creds);
index ccfddc83bab928fa22c5dac46382a83602e25e42..518f335f271195f30901cbef20278386c7a174fd 100644 (file)
@@ -115,6 +115,17 @@ enum winbindd_result winbindd_getpwnam(struct winbindd_cli_state *state)
                               name_user))
                return WINBINDD_ERROR;
        
+       /* don't handle our own domain if we are a DC.  This code handles cases where
+          the account doesn't exist anywhere and gets passed on down the NSS layer */
+
+       if ( ((lp_server_role() == ROLE_DOMAIN_PDC) || (lp_server_role()==ROLE_DOMAIN_BDC)) &&
+               strequal(name_domain, lp_workgroup()) ) 
+       {
+               DEBUG(7,("winbindd_getpwnam: rejecting getpwnam() for %s\\%s since I am on the PDC for this domain\n", 
+                       name_domain, name_user));
+               return WINBINDD_ERROR;
+       }       
+       
        if ((domain = find_domain_from_name(name_domain)) == NULL) {
                DEBUG(5, ("no such domain: %s\n", name_domain));
                return WINBINDD_ERROR;
index aa573b815402d597d16ad8181401c94b2a855de1..738ededde1bca50e16c1b190879320546fb6c43a 100644 (file)
@@ -644,7 +644,9 @@ NTSTATUS _net_sam_logon(pipes_struct *p, NET_Q_SAM_LOGON *q_u, NET_R_SAM_LOGON *
                                                         p->dc.sess_key)) {
                        status = NT_STATUS_NO_MEMORY;
                } else {
+                       become_root();
                        status = auth_context->check_ntlm_password(auth_context, user_info, &server_info);
+                       unbecome_root();
                }
 
                (auth_context->free)(&auth_context);