s3: winbind: Make WBC_AUTH_USER_LEVEL_PAC prime the name2sid cache.
[metze/samba/wip.git] / source3 / winbindd / winbindd_pam.c
index 1fb7e3c35fd51329adb41fd7cef733adcee8ae56..da874c74a0a2a0e18d21c4725bbd89dcdac162df 100644 (file)
@@ -429,7 +429,7 @@ done:
 
 static NTSTATUS get_max_bad_attempts_from_lockout_policy(struct winbindd_domain *domain,
                                                         TALLOC_CTX *mem_ctx,
-                                                        uint16 *lockout_threshold)
+                                                        uint16_t *lockout_threshold)
 {
        struct winbindd_methods *methods;
        NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
@@ -451,7 +451,7 @@ static NTSTATUS get_max_bad_attempts_from_lockout_policy(struct winbindd_domain
 
 static NTSTATUS get_pwd_properties(struct winbindd_domain *domain,
                                   TALLOC_CTX *mem_ctx,
-                                  uint32 *password_properties)
+                                  uint32_t *password_properties)
 {
        struct winbindd_methods *methods;
        NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
@@ -512,7 +512,20 @@ static const char *generate_krb5_ccache(TALLOC_CTX *mem_ctx,
                                p++;
 
                                if (p != NULL && *p == 'u' && strchr(p, '%') == NULL) {
-                                       gen_cc = talloc_asprintf(mem_ctx, type, uid);
+                                       char uid_str[sizeof("18446744073709551615")];
+
+                                       snprintf(uid_str, sizeof(uid_str), "%u", uid);
+
+                                       gen_cc = talloc_string_sub2(mem_ctx,
+                                                       type,
+                                                       "%u",
+                                                       uid_str,
+                                                       /* remove_unsafe_characters */
+                                                       false,
+                                                       /* replace_once */
+                                                       true,
+                                                       /* allow_trailing_dollar */
+                                                       false);
                                }
                        }
                }
@@ -542,7 +555,7 @@ uid_t get_uid_from_request(struct winbindd_request *request)
 
        uid = request->data.auth.uid;
 
-       if (uid < 0) {
+       if (uid == (uid_t)-1) {
                DEBUG(1,("invalid uid: '%u'\n", (unsigned int)uid));
                return -1;
        }
@@ -581,6 +594,7 @@ static NTSTATUS winbindd_raw_kerberos_login(TALLOC_CTX *mem_ctx,
        struct PAC_DATA_CTR *pac_data_ctr = NULL;
        const char *local_service;
        int i;
+       struct netr_SamInfo3 *info3_copy = NULL;
 
        *info3 = NULL;
 
@@ -700,11 +714,20 @@ static NTSTATUS winbindd_raw_kerberos_login(TALLOC_CTX *mem_ctx,
                break;
        }
 
-       *info3 = &logon_info->info3;
+       if (logon_info == NULL) {
+               DEBUG(10,("Missing logon_info in ticket of %s\n",
+                       principal_s));
+               return NT_STATUS_INVALID_PARAMETER;
+       }
 
        DEBUG(10,("winbindd_raw_kerberos_login: winbindd validated ticket of %s\n",
                principal_s));
 
+       result = create_info3_from_pac_logon_info(mem_ctx, logon_info, &info3_copy);
+       if (!NT_STATUS_IS_OK(result)) {
+               goto failed;
+       }
+
        /* if we had a user's ccache then return that string for the pam
         * environment */
 
@@ -740,7 +763,7 @@ static NTSTATUS winbindd_raw_kerberos_login(TALLOC_CTX *mem_ctx,
                }
 
        }
-
+       *info3 = info3_copy;
        return NT_STATUS_OK;
 
 failed:
@@ -873,13 +896,13 @@ static NTSTATUS winbindd_dual_pam_auth_cached(struct winbindd_domain *domain,
                                              struct netr_SamInfo3 **info3)
 {
        NTSTATUS result = NT_STATUS_LOGON_FAILURE;
-       uint16 max_allowed_bad_attempts;
+       uint16_t max_allowed_bad_attempts;
        fstring name_domain, name_user;
        struct dom_sid sid;
        enum lsa_SidType type;
        uchar new_nt_pass[NT_HASH_LEN];
-       const uint8 *cached_nt_pass;
-       const uint8 *cached_salt;
+       const uint8_t *cached_nt_pass;
+       const uint8_t *cached_salt;
        struct netr_SamInfo3 *my_info3;
        time_t kickoff_time, must_change_time;
        bool password_good = false;
@@ -995,7 +1018,7 @@ static NTSTATUS winbindd_dual_pam_auth_cached(struct winbindd_domain *domain,
 #ifdef HAVE_KRB5
                if ((state->request->flags & WBFLAG_PAM_KRB5) &&
                    ((tdc_domain = wcache_tdc_fetch_domain(state->mem_ctx, name_domain)) != NULL) &&
-                   ((tdc_domain->trust_type & NETR_TRUST_TYPE_UPLEVEL) ||
+                   ((tdc_domain->trust_type & LSA_TRUST_TYPE_UPLEVEL) ||
                    /* used to cope with the case winbindd starting without network. */
                    !strequal(tdc_domain->domain_name, tdc_domain->dns_name))) {
 
@@ -1111,7 +1134,7 @@ static NTSTATUS winbindd_dual_pam_auth_cached(struct winbindd_domain *domain,
        /* lockout user */
        if (my_info3->base.bad_password_count >= max_allowed_bad_attempts) {
 
-               uint32 password_properties;
+               uint32_t password_properties;
 
                result = get_pwd_properties(domain, state->mem_ctx, &password_properties);
                if (!NT_STATUS_IS_OK(result)) {
@@ -1210,6 +1233,7 @@ static NTSTATUS winbindd_dual_auth_passdb(TALLOC_CTX *mem_ctx,
                                          const DATA_BLOB *challenge,
                                          const DATA_BLOB *lm_resp,
                                          const DATA_BLOB *nt_resp,
+                                         bool interactive,
                                          struct netr_SamInfo3 **pinfo3)
 {
        struct auth_context *auth_context;
@@ -1247,6 +1271,10 @@ static NTSTATUS winbindd_dual_auth_passdb(TALLOC_CTX *mem_ctx,
        /* We don't want to come back to winbindd or to do PAM account checks */
        user_info->flags |= USER_INFO_LOCAL_SAM_ONLY | USER_INFO_INFO3_AND_NO_AUTHZ;
 
+       if (interactive) {
+               user_info->flags |= USER_INFO_INTERACTIVE_LOGON;
+       }
+
        status = make_auth_context_fixed(frame, &auth_context, challenge->data);
 
        if (!NT_STATUS_IS_OK(status)) {
@@ -1290,7 +1318,6 @@ static NTSTATUS winbindd_dual_auth_passdb(TALLOC_CTX *mem_ctx,
 static NTSTATUS winbind_samlogon_retry_loop(struct winbindd_domain *domain,
                                            TALLOC_CTX *mem_ctx,
                                            uint32_t logon_parameters,
-                                           const char *server,
                                            const char *username,
                                            const char *password,
                                            const char *domainname,
@@ -1326,7 +1353,7 @@ static NTSTATUS winbind_samlogon_retry_loop(struct winbindd_domain *domain,
                                DEBUG(3, ("This is again a problem for this "
                                          "particular call, forcing the close "
                                          "of this connection\n"));
-                               invalidate_cm_connection(&domain->conn);
+                               invalidate_cm_connection(domain);
                        }
 
                        /* After the second retry failover to the next DC */
@@ -1357,8 +1384,11 @@ static NTSTATUS winbind_samlogon_retry_loop(struct winbindd_domain *domain,
                        return result;
                }
                netr_attempts = 0;
-
-               if (interactive && username != NULL && password != NULL) {
+               if (domain->conn.netlogon_creds == NULL) {
+                       DBG_NOTICE("No security credentials available for "
+                                 "domain [%s]\n", domainname);
+                       result = NT_STATUS_CANT_ACCESS_DOMAIN_INFO;
+               } else if (interactive && username != NULL && password != NULL) {
                        result = rpccli_netlogon_password_logon(domain->conn.netlogon_creds,
                                                                netlogon_pipe->binding_handle,
                                                                mem_ctx,
@@ -1406,12 +1436,13 @@ static NTSTATUS winbind_samlogon_retry_loop(struct winbindd_domain *domain,
                   rpc changetrustpw' */
 
                if ( NT_STATUS_EQUAL(result, NT_STATUS_ACCESS_DENIED) ) {
-                       DEBUG(3,("winbind_samlogon_retry_loop: sam_logon returned "
-                                "ACCESS_DENIED.  Maybe the trust account "
+                       DEBUG(1,("winbind_samlogon_retry_loop: sam_logon returned "
+                                "ACCESS_DENIED.  Maybe the DC has Restrict "
+                                "NTLM set or the trust account "
                                "password was changed and we didn't know it. "
                                 "Killing connections to domain %s\n",
                                domainname));
-                       invalidate_cm_connection(&domain->conn);
+                       invalidate_cm_connection(domain);
                        retry = true;
                }
 
@@ -1434,7 +1465,7 @@ static NTSTATUS winbind_samlogon_retry_loop(struct winbindd_domain *domain,
                         * In order to recover from this situation, we need to
                         * drop the connection.
                         */
-                       invalidate_cm_connection(&domain->conn);
+                       invalidate_cm_connection(domain);
                        result = NT_STATUS_LOGON_FAILURE;
                        break;
                }
@@ -1446,7 +1477,7 @@ static NTSTATUS winbind_samlogon_retry_loop(struct winbindd_domain *domain,
                                "returned NT_STATUS_IO_TIMEOUT after the retry."
                                "Killing connections to domain %s\n",
                        domainname));
-               invalidate_cm_connection(&domain->conn);
+               invalidate_cm_connection(domain);
        }
        return result;
 }
@@ -1515,7 +1546,9 @@ static NTSTATUS winbindd_dual_pam_auth_samlogon(TALLOC_CTX *mem_ctx,
 
                result = winbindd_dual_auth_passdb(
                        mem_ctx, 0, name_domain, name_user,
-                       &chal_blob, &lm_resp, &nt_resp, info3);
+                       &chal_blob, &lm_resp, &nt_resp,
+                       true, /* interactive */
+                       info3);
 
                /* 
                 * We need to try the remote NETLOGON server if this is NOT_IMPLEMENTED 
@@ -1530,7 +1563,6 @@ static NTSTATUS winbindd_dual_pam_auth_samlogon(TALLOC_CTX *mem_ctx,
        result = winbind_samlogon_retry_loop(domain,
                                             mem_ctx,
                                             0,
-                                            domain->dcname,
                                             name_user,
                                             pass,
                                             name_domain,
@@ -1555,7 +1587,7 @@ static NTSTATUS winbindd_dual_pam_auth_samlogon(TALLOC_CTX *mem_ctx,
                struct policy_handle samr_domain_handle, user_pol;
                union samr_UserInfo *info = NULL;
                NTSTATUS status_tmp, result_tmp;
-               uint32 acct_flags;
+               uint32_t acct_flags;
                struct dcerpc_binding_handle *b;
 
                status_tmp = cm_connect_sam(domain, mem_ctx, false,
@@ -1804,6 +1836,26 @@ process_result:
                sid_compose(&user_sid, info3->base.domain_sid,
                            info3->base.rid);
 
+               if (info3->base.full_name.string == NULL) {
+                       struct netr_SamInfo3 *cached_info3;
+
+                       cached_info3 = netsamlogon_cache_get(state->mem_ctx,
+                                                            &user_sid);
+                       if (cached_info3 != NULL &&
+                           cached_info3->base.full_name.string != NULL) {
+                               info3->base.full_name.string =
+                                       talloc_strdup(info3,
+                                                     cached_info3->base.full_name.string);
+                       } else {
+
+                               /* this might fail so we don't check the return code */
+                               wcache_query_user_fullname(domain,
+                                               info3,
+                                               &user_sid,
+                                               &info3->base.full_name.string);
+                       }
+               }
+
                wcache_invalidate_samlogon(find_domain_from_name(name_domain),
                                           &user_sid);
                netsamlogon_cache_store(name_user, info3);
@@ -1910,7 +1962,9 @@ NTSTATUS winbind_dual_SamLogon(struct winbindd_domain *domain,
                        mem_ctx,
                        logon_parameters,
                        name_domain, name_user,
-                       &chal_blob, &lm_response, &nt_response, info3);
+                       &chal_blob, &lm_response, &nt_response,
+                       false, /* interactive */
+                       info3);
 
                /* 
                 * We need to try the remote NETLOGON server if this is NOT_IMPLEMENTED 
@@ -1923,7 +1977,6 @@ NTSTATUS winbind_dual_SamLogon(struct winbindd_domain *domain,
        result = winbind_samlogon_retry_loop(domain,
                                             mem_ctx,
                                             logon_parameters,
-                                            domain->dcname,
                                             name_user,
                                             NULL, /* password */
                                             name_domain,
@@ -1945,6 +1998,27 @@ process_result:
 
                sid_compose(&user_sid, (*info3)->base.domain_sid,
                            (*info3)->base.rid);
+
+               if ((*info3)->base.full_name.string == NULL) {
+                       struct netr_SamInfo3 *cached_info3;
+
+                       cached_info3 = netsamlogon_cache_get(mem_ctx,
+                                                            &user_sid);
+                       if (cached_info3 != NULL &&
+                           cached_info3->base.full_name.string != NULL) {
+                               (*info3)->base.full_name.string =
+                                       talloc_strdup(*info3,
+                                                     cached_info3->base.full_name.string);
+                       } else {
+
+                               /* this might fail so we don't check the return code */
+                               wcache_query_user_fullname(domain,
+                                               *info3,
+                                               &user_sid,
+                                               &(*info3)->base.full_name.string);
+                       }
+               }
+
                wcache_invalidate_samlogon(find_domain_from_name(name_domain),
                                           &user_sid);
                netsamlogon_cache_store(name_user, *info3);
@@ -2237,7 +2311,7 @@ enum winbindd_result winbindd_dual_pam_logoff(struct winbindd_domain *domain,
 
 #ifdef HAVE_KRB5
 
-       if (state->request->data.logoff.uid < 0) {
+       if (state->request->data.logoff.uid == (uid_t)-1) {
                DEBUG(0,("winbindd_pam_logoff: invalid uid\n"));
                goto process_result;
        }
@@ -2494,7 +2568,15 @@ NTSTATUS winbindd_pam_auth_pac_send(struct winbindd_cli_state *state,
        }
 
        if (logon_info) {
-               /* Signature verification succeeded, trust the PAC */
+               /*
+                * Signature verification succeeded, we can
+                * trust the PAC and prime the netsamlogon
+                * and name2sid caches. DO NOT DO THIS
+                * in the signature verification failed
+                * code path.
+                */
+               struct winbindd_domain *domain = NULL;
+
                result = create_info3_from_pac_logon_info(state->mem_ctx,
                                                        logon_info,
                                                        &info3_copy);
@@ -2503,6 +2585,31 @@ NTSTATUS winbindd_pam_auth_pac_send(struct winbindd_cli_state *state,
                }
                netsamlogon_cache_store(NULL, info3_copy);
 
+               /*
+                * We're in the parent here, so find the child
+                * pointer from the PAC domain name.
+                */
+               domain = find_domain_from_name_noinit(
+                               info3_copy->base.logon_domain.string);
+               if (domain && domain->primary ) {
+                       struct dom_sid user_sid;
+
+                       sid_compose(&user_sid,
+                               info3_copy->base.domain_sid,
+                               info3_copy->base.rid);
+
+                       cache_name2sid(domain,
+                               info3_copy->base.logon_domain.string,
+                               info3_copy->base.account_name.string,
+                               SID_NAME_USER,
+                               &user_sid);
+
+                       DBG_INFO("PAC for user %s\%s SID %s primed cache\n",
+                               info3_copy->base.logon_domain.string,
+                               info3_copy->base.account_name.string,
+                               sid_string_dbg(&user_sid));
+               }
+
        } else {
                /* Try without signature verification */
                result = kerberos_pac_logon_info(state->mem_ctx, pac_blob, NULL,