void mit_samba_update_bad_password_count(krb5_db_entry *db_entry) { struct samba_kdc_entry *p; p = (struct samba_kdc_entry *)db_entry->e_data; authsam_update_bad_pwd_count(p->kdc_db_ctx->samdb, p->msg, ldb_get_default_basedn(p->kdc_db_ctx->samdb)); }
static krb5_error_code hdb_samba4_auth_status(krb5_context context, HDB *db, hdb_entry_ex *entry, int hdb_auth_status) { struct samba_kdc_db_context *kdc_db_ctx = talloc_get_type_abort(db->hdb_db, struct samba_kdc_db_context); struct samba_kdc_entry *p = talloc_get_type(entry->ctx, struct samba_kdc_entry); struct ldb_dn *domain_dn = ldb_get_default_basedn(kdc_db_ctx->samdb); if (hdb_auth_status == HDB_AUTH_WRONG_PASSWORD) { authsam_update_bad_pwd_count(kdc_db_ctx->samdb, p->msg, domain_dn); } else if (hdb_auth_status == HDB_AUTH_SUCCESS) { authsam_logon_success_accounting(kdc_db_ctx->samdb, p->msg, domain_dn, true); } return 0; }
static krb5_error_code hdb_samba4_auth_status(krb5_context context, HDB *db, hdb_entry_ex *entry, struct sockaddr *from_addr, const char *original_client_name, const char *auth_type, int hdb_auth_status) { struct samba_kdc_db_context *kdc_db_ctx = talloc_get_type_abort(db->hdb_db, struct samba_kdc_db_context); struct ldb_dn *domain_dn = ldb_get_default_basedn(kdc_db_ctx->samdb); /* * Forcing this via the NTLM auth structure is not ideal, but * it is the most practical option right now, and ensures the * logs are consistent, even if some elements are always NULL. */ struct auth_usersupplied_info ui = { .mapped_state = true, .was_mapped = true, .client = { .account_name = original_client_name, .domain_name = NULL, }, .service_description = "Kerberos KDC", .auth_description = "ENC-TS Pre-authentication", .password_type = auth_type }; size_t sa_socklen = 0; switch (from_addr->sa_family) { case AF_INET: sa_socklen = sizeof(struct sockaddr_in); break; #ifdef HAVE_IPV6 case AF_INET6: sa_socklen = sizeof(struct sockaddr_in6); break; #endif } switch (hdb_auth_status) { case HDB_AUTHZ_SUCCESS: { struct samba_kdc_entry *p = talloc_get_type(entry->ctx, struct samba_kdc_entry); /* * TODO: We could log the AS-REQ authorization success here as * well. However before we do that, we need to pass * in the PAC here or re-calculate it. */ authsam_logon_success_accounting(kdc_db_ctx->samdb, p->msg, domain_dn, true); break; } case HDB_AUTH_INVALID_SIGNATURE: break; case HDB_AUTH_CORRECT_PASSWORD: case HDB_AUTH_WRONG_PASSWORD: { TALLOC_CTX *frame = talloc_stackframe(); struct samba_kdc_entry *p = talloc_get_type(entry->ctx, struct samba_kdc_entry); struct dom_sid *sid = samdb_result_dom_sid(frame, p->msg, "objectSid"); const char *account_name = ldb_msg_find_attr_as_string(p->msg, "sAMAccountName", NULL); const char *domain_name = lpcfg_sam_name(p->kdc_db_ctx->lp_ctx); struct tsocket_address *remote_host; NTSTATUS status; int ret; if (hdb_auth_status == HDB_AUTH_WRONG_PASSWORD) { authsam_update_bad_pwd_count(kdc_db_ctx->samdb, p->msg, domain_dn); status = NT_STATUS_WRONG_PASSWORD; } else { status = NT_STATUS_OK; } ret = tsocket_address_bsd_from_sockaddr(frame, from_addr, sa_socklen, &remote_host); if (ret != 0) { ui.remote_host = NULL; } else { ui.remote_host = remote_host; } ui.mapped.account_name = account_name; ui.mapped.domain_name = domain_name; log_authentication_event(kdc_db_ctx->msg_ctx, kdc_db_ctx->lp_ctx, &ui, status, domain_name, account_name, NULL, sid); TALLOC_FREE(frame); break; } case HDB_AUTH_CLIENT_UNKNOWN: { struct tsocket_address *remote_host; int ret; TALLOC_CTX *frame = talloc_stackframe(); ret = tsocket_address_bsd_from_sockaddr(frame, from_addr, sa_socklen, &remote_host); if (ret != 0) { ui.remote_host = NULL; } else { ui.remote_host = remote_host; } log_authentication_event(kdc_db_ctx->msg_ctx, kdc_db_ctx->lp_ctx, &ui, NT_STATUS_NO_SUCH_USER, NULL, NULL, NULL, NULL); TALLOC_FREE(frame); break; } } return 0; }
static NTSTATUS authsam_password_check_and_record(struct auth4_context *auth_context, TALLOC_CTX *mem_ctx, struct ldb_dn *domain_dn, struct ldb_message *msg, uint16_t acct_flags, const struct auth_usersupplied_info *user_info, DATA_BLOB *user_sess_key, DATA_BLOB *lm_sess_key, bool *authoritative) { NTSTATUS nt_status; NTSTATUS auth_status; TALLOC_CTX *tmp_ctx; int i, ret; int history_len = 0; struct ldb_context *sam_ctx = auth_context->sam_ctx; const char * const attrs[] = { "pwdHistoryLength", NULL }; struct ldb_message *dom_msg; struct samr_Password *lm_pwd; struct samr_Password *nt_pwd; bool am_rodc; tmp_ctx = talloc_new(mem_ctx); if (tmp_ctx == NULL) { return NT_STATUS_NO_MEMORY; } /* * This call does more than what it appears to do, it also * checks for the account lockout. * * It is done here so that all parts of Samba that read the * password refuse to even operate on it if the account is * locked out, to avoid mistakes like CVE-2013-4496. */ nt_status = samdb_result_passwords(tmp_ctx, auth_context->lp_ctx, msg, &lm_pwd, &nt_pwd); if (!NT_STATUS_IS_OK(nt_status)) { TALLOC_FREE(tmp_ctx); return nt_status; } if (lm_pwd == NULL && nt_pwd == NULL) { if (samdb_rodc(auth_context->sam_ctx, &am_rodc) == LDB_SUCCESS && am_rodc) { /* * we don't have passwords for this * account. We are an RODC, and this account * may be one for which we either are denied * REPL_SECRET replication or we haven't yet * done the replication. We return * NT_STATUS_NOT_IMPLEMENTED which tells the * auth code to try the next authentication * mechanism. We also send a message to our * drepl server to tell it to try and * replicate the secrets for this account. * * TODO: Should we only trigger this is detected * there's a chance that the password might be * replicated, we should be able to detect this * based on msDS-NeverRevealGroup. */ auth_sam_trigger_repl_secret(auth_context, auth_context->msg_ctx, auth_context->event_ctx, msg->dn); TALLOC_FREE(tmp_ctx); return NT_STATUS_NOT_IMPLEMENTED; } } auth_status = authsam_password_ok(auth_context, tmp_ctx, acct_flags, lm_pwd, nt_pwd, user_info, user_sess_key, lm_sess_key); if (NT_STATUS_IS_OK(auth_status)) { if (user_sess_key->data) { talloc_steal(mem_ctx, user_sess_key->data); } if (lm_sess_key->data) { talloc_steal(mem_ctx, lm_sess_key->data); } TALLOC_FREE(tmp_ctx); return NT_STATUS_OK; } *user_sess_key = data_blob_null; *lm_sess_key = data_blob_null; if (!NT_STATUS_EQUAL(auth_status, NT_STATUS_WRONG_PASSWORD)) { TALLOC_FREE(tmp_ctx); return auth_status; } /* * We only continue if this was a wrong password * and we'll always return NT_STATUS_WRONG_PASSWORD * no matter what error happens. */ /* pull the domain password property attributes */ ret = dsdb_search_one(sam_ctx, tmp_ctx, &dom_msg, domain_dn, LDB_SCOPE_BASE, attrs, 0, "objectClass=domain"); if (ret == LDB_SUCCESS) { history_len = ldb_msg_find_attr_as_uint(dom_msg, "pwdHistoryLength", 0); } else if (ret == LDB_ERR_NO_SUCH_OBJECT) { DEBUG(3,("Couldn't find domain %s: %s!\n", ldb_dn_get_linearized(domain_dn), ldb_errstring(sam_ctx))); } else { DEBUG(3,("error finding domain %s: %s!\n", ldb_dn_get_linearized(domain_dn), ldb_errstring(sam_ctx))); } for (i = 1; i < MIN(history_len, 3); i++) { struct samr_Password zero_string_hash; struct samr_Password zero_string_des_hash; struct samr_Password *nt_history_pwd = NULL; struct samr_Password *lm_history_pwd = NULL; NTTIME pwdLastSet; struct timeval tv_now; NTTIME now; int allowed_period_mins; NTTIME allowed_period; nt_status = samdb_result_passwords_from_history(tmp_ctx, auth_context->lp_ctx, msg, i, &lm_history_pwd, &nt_history_pwd); if (!NT_STATUS_IS_OK(nt_status)) { /* * If we don't find element 'i' we won't find * 'i+1' ... */ break; } /* * We choose to avoid any issues * around different LM and NT history * lengths by only checking the NT * history */ if (nt_history_pwd == NULL) { /* * If we don't find element 'i' we won't find * 'i+1' ... */ break; } /* Skip over all-zero hashes in the history */ if (all_zero(nt_history_pwd->hash, sizeof(nt_history_pwd->hash))) { continue; } /* * This looks odd, but the password_hash module writes this in if * (somehow) we didn't have an old NT hash */ E_md4hash("", zero_string_hash.hash); if (memcmp(nt_history_pwd->hash, zero_string_hash.hash, 16) == 0) { continue; } E_deshash("", zero_string_des_hash.hash); if (!lm_history_pwd || memcmp(lm_history_pwd->hash, zero_string_des_hash.hash, 16) == 0) { lm_history_pwd = NULL; } auth_status = authsam_password_ok(auth_context, tmp_ctx, acct_flags, lm_history_pwd, nt_history_pwd, user_info, user_sess_key, lm_sess_key); if (!NT_STATUS_IS_OK(auth_status)) { /* * If this was not a correct password, try the next * one from the history */ *user_sess_key = data_blob_null; *lm_sess_key = data_blob_null; continue; } if (i != 1) { /* * The authentication was OK, but not against * the previous password, which is stored at index 1. * * We just return the original wrong password. * This skips the update of the bad pwd count, * because this is almost certainly user error * (or automatic login on a computer using a cached * password from before the password change), * not an attack. */ TALLOC_FREE(tmp_ctx); return NT_STATUS_WRONG_PASSWORD; } if (user_info->password_state != AUTH_PASSWORD_RESPONSE) { /* * The authentication was OK against the previous password, * but it's not a NTLM network authentication. * * We just return the original wrong password. * This skips the update of the bad pwd count, * because this is almost certainly user error * (or automatic login on a computer using a cached * password from before the password change), * not an attack. */ TALLOC_FREE(tmp_ctx); return NT_STATUS_WRONG_PASSWORD; } /* * If the password was OK, it's a NTLM network authentication * and it was the previous password. * * Now we see if it is within the grace period, * so that we don't break cached sessions on other computers * before the user can lock and unlock their other screens * (resetting their cached password). * * See http://support.microsoft.com/kb/906305 * OldPasswordAllowedPeriod ("old password allowed period") * is specified in minutes. The default is 60. */ allowed_period_mins = lpcfg_old_password_allowed_period(auth_context->lp_ctx); /* * NTTIME uses 100ns units */ allowed_period = allowed_period_mins * 60 * 1000*1000*10; pwdLastSet = samdb_result_nttime(msg, "pwdLastSet", 0); tv_now = timeval_current(); now = timeval_to_nttime(&tv_now); if (now < pwdLastSet) { /* * time jump? * * We just return the original wrong password. * This skips the update of the bad pwd count, * because this is almost certainly user error * (or automatic login on a computer using a cached * password from before the password change), * not an attack. */ TALLOC_FREE(tmp_ctx); return NT_STATUS_WRONG_PASSWORD; } if ((now - pwdLastSet) >= allowed_period) { /* * The allowed period is over. * * We just return the original wrong password. * This skips the update of the bad pwd count, * because this is almost certainly user error * (or automatic login on a computer using a cached * password from before the password change), * not an attack. */ TALLOC_FREE(tmp_ctx); return NT_STATUS_WRONG_PASSWORD; } /* * We finally allow the authentication with the * previous password within the allowed period. */ if (user_sess_key->data) { talloc_steal(mem_ctx, user_sess_key->data); } if (lm_sess_key->data) { talloc_steal(mem_ctx, lm_sess_key->data); } TALLOC_FREE(tmp_ctx); return auth_status; } /* * If we are not in the allowed period or match an old password, * we didn't return early. Now update the badPwdCount et al. */ nt_status = authsam_update_bad_pwd_count(auth_context->sam_ctx, msg, domain_dn); if (!NT_STATUS_IS_OK(nt_status)) { /* * We need to return the original * NT_STATUS_WRONG_PASSWORD error, so there isn't * anything more we can do than write something into * the log */ DEBUG(0, ("Failed to note bad password for user [%s]: %s\n", user_info->mapped.account_name, nt_errstr(nt_status))); } if (samdb_rodc(auth_context->sam_ctx, &am_rodc) == LDB_SUCCESS && am_rodc) { *authoritative = false; } TALLOC_FREE(tmp_ctx); return NT_STATUS_WRONG_PASSWORD; }