Winbind updates!
authorAndrew Bartlett <abartlet@samba.org>
Wed, 31 Jul 2002 12:05:30 +0000 (12:05 +0000)
committerAndrew Bartlett <abartlet@samba.org>
Wed, 31 Jul 2002 12:05:30 +0000 (12:05 +0000)
This updates the 'winbind' authentication module and winbind's 'PAM' (actually
netlogon) code to allow smbd to cache connections to the DC.

This is particulary relevent when we need mutex locks already - there is no
parallelism to be gained anyway.

The winbind code authenticates the user, and if successful, passes back the
'info3' struct describing the user.  smbd then interprets that in exactly the
same way as an 'ntdomain' logon.

Also, add parinoia to winbind about null termination.

Andrew Bartlett

source/auth/auth_winbind.c
source/nsswitch/winbindd.c
source/nsswitch/winbindd_group.c
source/nsswitch/winbindd_nss.h
source/nsswitch/winbindd_pam.c
source/nsswitch/winbindd_sid.c
source/nsswitch/winbindd_user.c
source/nsswitch/winbindd_wins.c
source/rpc_parse/parse_net.c

index 671e198bf59beaf1845f8baaac47527405195392..5bdccd39f3e88543689091e12ba3a5c2bea42881 100644 (file)
@@ -32,6 +32,30 @@ NSS_STATUS winbindd_request(int req_type,
                            struct winbindd_request *request,
                            struct winbindd_response *response);
 
+NTSTATUS get_info3_from_ndr(TALLOC_CTX *mem_ctx, struct winbindd_response *response, NET_USER_INFO_3 *info3)
+{
+       uint8 *info3_ndr;
+       size_t len = response->length - sizeof(response);
+       prs_struct ps;
+       if (len > 0) {
+               info3_ndr = response->extra_data;
+               if (!prs_init(&ps, len, mem_ctx, UNMARSHALL)) {
+                       return NT_STATUS_NO_MEMORY;
+               }
+               prs_append_data(&ps, info3_ndr, len);
+               ps.data_offset = 0;
+               if (!net_io_user_info3("", info3, &ps, 1, 3)) {
+                       DEBUG(2, ("get_info3_from_ndr: could not parse info3 struct!\n"));
+                       return NT_STATUS_UNSUCCESSFUL;
+               }
+               prs_mem_free(&ps);
+
+               return NT_STATUS_OK;
+       } else {
+               DEBUG(2, ("get_info3_from_ndr: No info3 struct found!\n"));
+               return NT_STATUS_UNSUCCESSFUL;
+       }
+}
 
 /* Authenticate a user with a challenge/response */
 
@@ -44,11 +68,11 @@ static NTSTATUS check_winbind_security(const struct auth_context *auth_context,
        struct winbindd_request request;
        struct winbindd_response response;
         NSS_STATUS result;
-        struct passwd *pw;
        NTSTATUS nt_status;
+        NET_USER_INFO_3 info3;
 
        if (!user_info) {
-               return NT_STATUS_UNSUCCESSFUL;
+               return NT_STATUS_INVALID_PARAMETER;
        }
 
        if (!auth_context) {
@@ -62,11 +86,14 @@ static NTSTATUS check_winbind_security(const struct auth_context *auth_context,
        ZERO_STRUCT(request);
        ZERO_STRUCT(response);
 
-       snprintf(request.data.auth_crap.user, sizeof(request.data.auth_crap.user),
-                "%s\\%s", user_info->domain.str, user_info->smb_name.str);
+       request.data.auth_crap.flags = WINBIND_PAM_INFO3_NDR;
 
-       fstrcpy(request.data.auth_crap.user, user_info->smb_name.str);
-       fstrcpy(request.data.auth_crap.domain, user_info->domain.str);
+       push_utf8_fstring(request.data.auth_crap.user, 
+                         user_info->smb_name.str);
+       push_utf8_fstring(request.data.auth_crap.domain, 
+                         user_info->domain.str);
+       push_utf8_fstring(request.data.auth_crap.workstation, 
+                         user_info->wksta_name.str);
 
        memcpy(request.data.auth_crap.chal, auth_context->challenge.data, sizeof(request.data.auth_crap.chal));
        
@@ -76,27 +103,28 @@ static NTSTATUS check_winbind_security(const struct auth_context *auth_context,
                                                 sizeof(request.data.auth_crap.nt_resp));
        
        memcpy(request.data.auth_crap.lm_resp, user_info->lm_resp.data, 
-              sizeof(request.data.auth_crap.lm_resp_len));
-       memcpy(request.data.auth_crap.nt_resp, user_info->nt_resp.data, 
               request.data.auth_crap.lm_resp_len);
+       memcpy(request.data.auth_crap.nt_resp, user_info->nt_resp.data, 
+              request.data.auth_crap.nt_resp_len);
        
        result = winbindd_request(WINBINDD_PAM_AUTH_CRAP, &request, &response);
 
-       if (result == NSS_STATUS_SUCCESS) {
-               
-               pw = Get_Pwnam(user_info->internal_username.str);
-               
-               if (pw) {                       
-                       if (make_server_info_pw(server_info, pw)) {
-                               nt_status = NT_STATUS_OK;
-                       } else {
-                               nt_status = NT_STATUS_NO_MEMORY;
+       nt_status = NT_STATUS(response.data.auth.nt_status);
+
+       if (result == NSS_STATUS_SUCCESS && response.extra_data) {
+               if (NT_STATUS_IS_OK(nt_status)) {
+                       if (NT_STATUS_IS_OK(nt_status = get_info3_from_ndr(mem_ctx, &response, &info3))) { 
+                               nt_status = 
+                                       make_server_info_info3(mem_ctx, 
+                                                              user_info->internal_username.str, 
+                                                              user_info->smb_name.str, 
+                                                              user_info->domain.str, 
+                                                              server_info, 
+                                                              &info3); 
                        }
-               } else {
-                       nt_status = NT_STATUS_NO_SUCH_USER;
                }
-       } else {
-               nt_status = NT_STATUS_LOGON_FAILURE;
+       } else if (NT_STATUS_IS_OK(nt_status)) {
+               nt_status = NT_STATUS_UNSUCCESSFUL;
        }
 
         return nt_status;
index fbeb6b6347fc265eaa92a265a0677ef97e8ef886..047ea1accca7d829987e217ea4676fbd6ce01f7d 100644 (file)
@@ -375,6 +375,9 @@ void winbind_process_packet(struct winbindd_cli_state *state)
 {
        /* Process request */
        
+       /* Ensure null termination of entire request */
+       state->request.domain[sizeof(state->request.domain)-1]='\0';
+
        state->pid = state->request.pid;
        
        process_request(state);
index 20563ba7bd7cf856b6a5441e798706bd37f50e31..abb6b9da75745b93b1fa846361442d161d221655 100644 (file)
@@ -196,6 +196,9 @@ enum winbindd_result winbindd_getgrnam(struct winbindd_cli_state *state)
        gid_t gid;
        int gr_mem_len;
        
+       /* Ensure null termination */
+       state->request.data.groupname[sizeof(state->request.data.groupname)-1]='\0';
+
        DEBUG(3, ("[%5d]: getgrnam %s\n", state->pid,
                  state->request.data.groupname));
 
@@ -783,6 +786,9 @@ enum winbindd_result winbindd_getgroups(struct winbindd_cli_state *state)
        int i;
        TALLOC_CTX *mem_ctx;
        
+       /* Ensure null termination */
+       state->request.data.username[sizeof(state->request.data.username)-1]='\0';
+
        DEBUG(3, ("[%5d]: getgroups %s\n", state->pid,
                  state->request.data.username));
 
index 0f0e40a2ecad24f1782bf7b8d1725dfd22afdc1c..9eea94e7c0d60cbeed27fe04eca6ad0d4c4a7c3d 100644 (file)
@@ -36,7 +36,7 @@
 
 /* Update this when you change the interface.  */
 
-#define WINBIND_INTERFACE_VERSION 4
+#define WINBIND_INTERFACE_VERSION 5
 
 /* Socket commands */
 
@@ -107,6 +107,12 @@ enum winbindd_cmd {
        WINBINDD_NUM_CMDS
 };
 
+#define WINBIND_PAM_INFO3_NDR  0x0001
+#define WINBIND_PAM_INFO3_TEXT 0x0002
+#define WINBIND_PAM_NTKEY      0x0004
+#define WINBIND_PAM_LMKEY      0x0008
+#define WINBIND_PAM_CONTACT_TRUSTDOM 0x0010
+
 /* Winbind request structure */
 
 struct winbindd_request {
@@ -132,6 +138,8 @@ struct winbindd_request {
                         uint16 lm_resp_len;
                         fstring nt_resp;
                         uint16 nt_resp_len;
+                       fstring workstation;
+                       uint32 flags;
                 } auth_crap;
                 struct {
                     fstring user;
@@ -216,6 +224,8 @@ struct winbindd_response {
                        fstring nt_status_string;
                        fstring error_string;
                        int pam_error;
+                       char nt_session_key[16];
+                       char first_8_lm_hash[8];
                } auth;
        } data;
 
index a73b2ccd29ea7c9325e213dd1b087b6b24b2866c..a8b508a49c6b93ae4f374b460dc30a57fc0b247c 100644 (file)
 */
 
 #include "winbindd.h"
-
 #undef DBGC_CLASS
 #define DBGC_CLASS DBGC_WINBIND
 
+
+static NTSTATUS append_info3_as_ndr(TALLOC_CTX *mem_ctx, 
+                                   struct winbindd_cli_state *state, 
+                                   NET_USER_INFO_3 *info3) 
+{
+       prs_struct ps;
+       uint32 size;
+       if (!prs_init(&ps, 256 /* Random, non-zero number */, mem_ctx, MARSHALL)) {
+               return NT_STATUS_NO_MEMORY;
+       }
+       if (!net_io_user_info3("", info3, &ps, 1, 3)) {
+               prs_mem_free(&ps);
+               return NT_STATUS_UNSUCCESSFUL;
+       }
+
+       size = prs_data_size(&ps);
+       state->response.extra_data = memdup(prs_data_p(&ps), size);
+       if (!state->response.extra_data) {
+               prs_mem_free(&ps);
+               return NT_STATUS_NO_MEMORY;
+       }
+       state->response.length += size;
+       prs_mem_free(&ps);
+       return NT_STATUS_OK;
+}
+
 /* Return a password structure from a username.  */
 
 enum winbindd_result winbindd_pam_auth(struct winbindd_cli_state *state) 
 {
        NTSTATUS result;
        fstring name_domain, name_user;
-       int passlen;
        unsigned char trust_passwd[16];
        time_t last_change_time;
         uint32 smb_uid_low;
@@ -46,6 +70,12 @@ enum winbindd_result winbindd_pam_auth(struct winbindd_cli_state *state)
 
        extern pstring global_myname;
 
+       /* Ensure null termination */
+       state->request.data.auth.user[sizeof(state->request.data.auth.user)-1]='\0';
+
+       /* Ensure null termination */
+       state->request.data.auth.pass[sizeof(state->request.data.auth.pass)-1]='\0';
+
        DEBUG(3, ("[%5d]: pam auth %s\n", state->pid,
                  state->request.data.auth.user));
 
@@ -64,8 +94,6 @@ enum winbindd_result winbindd_pam_auth(struct winbindd_cli_state *state)
                goto done;
        }
 
-       passlen = strlen(state->request.data.auth.pass);
-               
        {
                unsigned char local_lm_response[24];
                unsigned char local_nt_response[24];
@@ -140,34 +168,67 @@ enum winbindd_result winbindd_pam_auth_crap(struct winbindd_cli_state *state)
         NET_USER_INFO_3 info3;
         struct cli_state *cli = NULL;
        TALLOC_CTX *mem_ctx = NULL;
-       const char *domain = NULL;
+       char *user = NULL;
+       char *domain = NULL;
+       char *contact_domain;
+       char *workstation;
 
        DATA_BLOB lm_resp, nt_resp;
 
        extern pstring global_myname;
 
-       DEBUG(3, ("[%5d]: pam auth crap domain: %s user: %s\n", state->pid,
-                 state->request.data.auth_crap.domain, state->request.data.auth_crap.user));
+       /* Ensure null termination */
+       state->request.data.auth_crap.user[sizeof(state->request.data.auth_crap.user)-1]='\0';
+
+       /* Ensure null termination */
+       state->request.data.auth_crap.domain[sizeof(state->request.data.auth_crap.domain)-1]='\0';
 
-       if (!(mem_ctx = talloc_init_named("winbind pam auth crap for %s", state->request.data.auth.user))) {
+       if (!(mem_ctx = talloc_init_named("winbind pam auth crap for (utf8) %s", state->request.data.auth.user))) {
                DEBUG(0, ("winbindd_pam_auth_crap: could not talloc_init()!\n"));
                result = NT_STATUS_NO_MEMORY;
                goto done;
        }
 
+        if (pull_utf8_talloc(mem_ctx, &user, state->request.data.auth_crap.user) < 0) {
+               DEBUG(0, ("winbindd_pam_auth_crap: pull_utf8_talloc failed!\n"));
+       }
+
        if (*state->request.data.auth_crap.domain) {
-               domain = talloc_strdup(mem_ctx, state->request.data.auth_crap.domain);
+               if (pull_utf8_talloc(mem_ctx, &domain, state->request.data.auth_crap.domain) < 0) {
+                       DEBUG(0, ("winbindd_pam_auth_crap: pull_utf8_talloc failed!\n"));
+               }
        } else if (lp_winbind_use_default_domain()) {
-               domain = talloc_strdup(mem_ctx, lp_workgroup());
+               domain = lp_workgroup();
        } else {
-               DEBUG(5,("no domain specified with username (%s) - failing auth\n", state->request.data.auth.user));
+               DEBUG(5,("no domain specified with username (%s) - failing auth\n", 
+                        user));
                result = NT_STATUS_INVALID_PARAMETER;
                goto done;
        }
 
-       if (!domain) {
-               DEBUG(0,("winbindd_pam_auth_crap: talloc_strdup failed!\n"));
-               result = NT_STATUS_NO_MEMORY;
+       DEBUG(3, ("[%5d]: pam auth crap domain: %s user: %s\n", state->pid,
+                 domain, user));
+
+       if (lp_allow_trusted_domains() && (state->request.data.auth_crap.flags & WINBIND_PAM_CONTACT_TRUSTDOM)) {
+               contact_domain = domain;
+       } else {
+               contact_domain = lp_workgroup();
+       }
+
+       if (*state->request.data.auth_crap.workstation) {
+               if (pull_utf8_talloc(mem_ctx, &workstation, state->request.data.auth_crap.workstation) < 0) {
+                       DEBUG(0, ("winbindd_pam_auth_crap: pull_utf8_talloc failed!\n"));
+               }
+       } else {
+               workstation = global_myname;
+       }
+
+       if (state->request.data.auth_crap.lm_resp_len > sizeof(state->request.data.auth_crap.lm_resp)
+               || state->request.data.auth_crap.nt_resp_len > sizeof(state->request.data.auth_crap.nt_resp)) {
+               DEBUG(0, ("winbindd_pam_auth_crap: invalid password length %u/%u\n", 
+                         state->request.data.auth_crap.lm_resp_len, 
+                         state->request.data.auth_crap.nt_resp_len));
+               result = NT_STATUS_INVALID_PARAMETER;
                goto done;
        }
 
@@ -175,13 +236,15 @@ enum winbindd_result winbindd_pam_auth_crap(struct winbindd_cli_state *state)
        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 our primary domain
+        * 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(
-                lp_workgroup(), trust_passwd, &last_change_time)) {
+       if (!secrets_fetch_trust_account_password (
+                contact_domain, trust_passwd, &last_change_time)) {
                DEBUG(0, ("winbindd_pam_auth: could not fetch trust account "
-                          "password for domain %s\n", lp_workgroup()));
+                          "password for domain %s\n", contact_domain));
                result = NT_STATUS_CANT_ACCESS_DOMAIN_INFO;
                goto done;
        }
@@ -189,7 +252,7 @@ enum winbindd_result winbindd_pam_auth_crap(struct winbindd_cli_state *state)
        ZERO_STRUCT(info3);
 
        /* Don't shut this down - it belongs to the connection cache code */
-        result = cm_get_netlogon_cli(lp_workgroup(), trust_passwd, &cli);
+        result = cm_get_netlogon_cli(contact_domain, trust_passwd, &cli);
 
         if (!NT_STATUS_IS_OK(result)) {
                 DEBUG(3, ("could not open handle to NETLOGON pipe (error: %s)\n", nt_errstr(result)));
@@ -197,27 +260,43 @@ enum winbindd_result winbindd_pam_auth_crap(struct winbindd_cli_state *state)
         }
 
        result = cli_netlogon_sam_network_logon(cli, mem_ctx,
-                                               state->request.data.auth_crap.user, domain,
-                                               global_myname, state->request.data.auth_crap.chal, 
+                                               user, domain,
+                                               workstation, state->request.data.auth_crap.chal, 
                                                lm_resp, nt_resp, 
                                                &info3);
         
        if (NT_STATUS_IS_OK(result)) {
                uni_group_cache_store_netlogon(mem_ctx, &info3);
+               if (state->request.data.auth_crap.flags & WINBIND_PAM_INFO3_NDR) {
+                       result = append_info3_as_ndr(mem_ctx, state, &info3);
+               }
+
+#if 0
+               /* we don't currently do this stuff right */
+               if (state->request.data.auth_crap.flags & WINBIND_PAM_NTKEY) {
+                       SMB_ASSERT(sizeof(state->response.data.auth.nt_session_key) == sizeof(info3.user_sess_key)); 
+                       memcpy(state->response.data.auth.nt_session_key, info3.user_sess_key, sizeof(state->response.data.auth.nt_session_key) /* 16 */);
+               }
+               if (state->request.data.auth_crap.flags & WINBIND_PAM_LMKEY) {
+                       SMB_ASSERT(sizeof(state->response.data.auth.nt_session_key) <= sizeof(info3.user_sess_key)); 
+                       memcpy(state->response.data.auth.first_8_lm_hash, info3.padding, sizeof(state->response.data.auth.nt_session_key) /* 16 */);
+               }
+#endif
        }
 
 done:
 
        state->response.data.auth.nt_status = NT_STATUS_V(result);
-       fstrcpy(state->response.data.auth.nt_status_string, nt_errstr(result));
-       fstrcpy(state->response.data.auth.error_string, nt_errstr(result));
+       push_utf8_fstring(state->response.data.auth.nt_status_string, nt_errstr(result));
+       push_utf8_fstring(state->response.data.auth.error_string, nt_errstr(result));
        state->response.data.auth.pam_error = nt_status_to_pam(result);
 
-       DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2, ("NTLM CRAP authenticaion for user [%s]\\[%s] returned %s (PAM: %d)\n", 
-             state->request.data.auth_crap.domain, 
-             state->request.data.auth_crap.user, 
-             state->response.data.auth.nt_status_string,
-             state->response.data.auth.pam_error));          
+       DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2, 
+             ("NTLM CRAP authenticaion for user [%s]\\[%s] returned %s (PAM: %d)\n", 
+              domain,
+              user,
+              state->response.data.auth.nt_status_string,
+              state->response.data.auth.pam_error));         
 
        if (mem_ctx) 
                talloc_destroy(mem_ctx);
index 372898a08a4562fd7de3b6793c9a938c67e5106f..44f857d6be017a3718f9b9f6f8c8c76420d3e646 100644 (file)
@@ -36,6 +36,9 @@ enum winbindd_result winbindd_lookupsid(struct winbindd_cli_state *state)
        fstring name;
        fstring dom_name;
 
+       /* Ensure null termination */
+       state->request.data.sid[sizeof(state->request.data.sid)-1]='\0';
+
        DEBUG(3, ("[%5d]: lookupsid %s\n", state->pid, 
                  state->request.data.sid));
 
@@ -79,6 +82,12 @@ enum winbindd_result winbindd_lookupname(struct winbindd_cli_state *state)
        DOM_SID sid;
        struct winbindd_domain *domain;
 
+       /* Ensure null termination */
+       state->request.data.sid[sizeof(state->request.data.name.dom_name)-1]='\0';
+
+       /* Ensure null termination */
+       state->request.data.sid[sizeof(state->request.data.name.name)-1]='\0';
+
        DEBUG(3, ("[%5d]: lookupname %s%s%s\n", state->pid,
                  state->request.data.name.dom_name, 
                  lp_winbind_separator(),
@@ -112,6 +121,9 @@ enum winbindd_result winbindd_sid_to_uid(struct winbindd_cli_state *state)
 {
        DOM_SID sid;
 
+       /* Ensure null termination */
+       state->request.data.sid[sizeof(state->request.data.sid)-1]='\0';
+
        DEBUG(3, ("[%5d]: sid to uid %s\n", state->pid,
                  state->request.data.sid));
 
@@ -139,6 +151,9 @@ enum winbindd_result winbindd_sid_to_gid(struct winbindd_cli_state *state)
 {
        DOM_SID sid;
 
+       /* Ensure null termination */
+       state->request.data.sid[sizeof(state->request.data.sid)-1]='\0';
+
        DEBUG(3, ("[%5d]: sid to gid %s\n", state->pid, 
                  state->request.data.sid));
 
index 55593d6ae57dc8c6325d950c50381d4872a30b75..4f57fd2c7225ce786bc148de89566cad26141811 100644 (file)
@@ -103,6 +103,9 @@ enum winbindd_result winbindd_getpwnam(struct winbindd_cli_state *state)
        struct winbindd_domain *domain;
        TALLOC_CTX *mem_ctx;
        
+       /* Ensure null termination */
+       state->request.data.username[sizeof(state->request.data.username)-1]='\0';
+
        DEBUG(3, ("[%5d]: getpwnam %s\n", state->pid,
                  state->request.data.username));
        
index 8f9a7414bdc3208c5eca5b5e96d67a4a4236683d..8ddd5dc10df1522558e020cb0761c5ff47bf1f0d 100644 (file)
@@ -122,6 +122,9 @@ enum winbindd_result winbindd_wins_byip(struct winbindd_cli_state *state)
        int i, count, maxlen, size;
        struct node_status *status;
 
+       /* Ensure null termination */
+       state->request.data.winsreq[sizeof(state->request.data.winsreq)-1]='\0';
+
        DEBUG(3, ("[%5d]: wins_byip %s\n", state->pid,
                state->request.data.winsreq));
 
@@ -166,6 +169,9 @@ enum winbindd_result winbindd_wins_byname(struct winbindd_cli_state *state)
        fstring response;
        char * addr;
 
+       /* Ensure null termination */
+       state->request.data.winsreq[sizeof(state->request.data.winsreq)-1]='\0';
+
        DEBUG(3, ("[%5d]: wins_byname %s\n", state->pid,
                state->request.data.winsreq));
 
index 46fdce63ff4233505a8014bae13ef8be04032356..da49a6531d057d715e1bb5fd4822c95c952269d5 100644 (file)
@@ -1335,7 +1335,7 @@ void init_net_user_info3(TALLOC_CTX *ctx, NET_USER_INFO_3 *usr,
 ********************************************************************/
 
 BOOL net_io_user_info3(const char *desc, NET_USER_INFO_3 *usr, prs_struct *ps, 
-                             int depth, uint16 validation_level)
+                      int depth, uint16 validation_level)
 {
        int i;