/* call into samdb_rodc() */ static PyObject *py_dsdb_am_rodc(PyObject *self, PyObject *args) { PyObject *py_ldb; struct ldb_context *ldb; int ret; bool am_rodc; if (!PyArg_ParseTuple(args, "O", &py_ldb)) return NULL; PyErr_LDB_OR_RAISE(py_ldb, ldb); ret = samdb_rodc(ldb, &am_rodc); if (ret != LDB_SUCCESS) { PyErr_SetString(PyExc_RuntimeError, ldb_errstring(ldb)); return NULL; } return PyBool_FromLong(am_rodc); }
/* connect to the local SAM */ static WERROR kccsrv_connect_samdb(struct kccsrv_service *service, struct loadparm_context *lp_ctx) { const struct GUID *ntds_guid; service->samdb = samdb_connect(service, service->task->event_ctx, lp_ctx, service->system_session_info, 0); if (!service->samdb) { return WERR_DS_UNAVAILABLE; } ntds_guid = samdb_ntds_objectGUID(service->samdb); if (!ntds_guid) { return WERR_DS_UNAVAILABLE; } service->ntds_guid = *ntds_guid; if (samdb_rodc(service->samdb, &service->am_rodc) != LDB_SUCCESS) { DEBUG(0,(__location__ ": Failed to determine RODC status\n")); return WERR_DS_UNAVAILABLE; } return WERR_OK; }
/* startup the kdc task */ static void kdc_task_init(struct task_server *task) { struct kdc_server *kdc; krb5_kdc_configuration *kdc_config = NULL; NTSTATUS status; krb5_error_code ret; struct interface *ifaces; int ldb_ret; switch (lpcfg_server_role(task->lp_ctx)) { case ROLE_STANDALONE: task_server_terminate(task, "kdc: no KDC required in standalone configuration", false); return; case ROLE_DOMAIN_MEMBER: task_server_terminate(task, "kdc: no KDC required in member server configuration", false); return; case ROLE_DOMAIN_PDC: case ROLE_DOMAIN_BDC: task_server_terminate(task, "Cannot start KDC as a 'classic Samba' DC", true); return; case ROLE_ACTIVE_DIRECTORY_DC: /* Yes, we want a KDC */ break; } load_interface_list(task, task->lp_ctx, &ifaces); if (iface_list_count(ifaces) == 0) { task_server_terminate(task, "kdc: no network interfaces configured", false); return; } task_server_set_title(task, "task[kdc]"); kdc = talloc_zero(task, struct kdc_server); if (kdc == NULL) { task_server_terminate(task, "kdc: out of memory", true); return; } kdc->task = task; /* get a samdb connection */ kdc->samdb = samdb_connect(kdc, kdc->task->event_ctx, kdc->task->lp_ctx, system_session(kdc->task->lp_ctx), NULL, 0); if (!kdc->samdb) { DEBUG(1,("kdc_task_init: unable to connect to samdb\n")); task_server_terminate(task, "kdc: krb5_init_context samdb connect failed", true); return; } ldb_ret = samdb_rodc(kdc->samdb, &kdc->am_rodc); if (ldb_ret != LDB_SUCCESS) { DEBUG(1, ("kdc_task_init: Cannot determine if we are an RODC: %s\n", ldb_errstring(kdc->samdb))); task_server_terminate(task, "kdc: krb5_init_context samdb RODC connect failed", true); return; } kdc->proxy_timeout = lpcfg_parm_int(kdc->task->lp_ctx, NULL, "kdc", "proxy timeout", 5); initialize_krb5_error_table(); ret = smb_krb5_init_context(kdc, task->lp_ctx, &kdc->smb_krb5_context); if (ret) { DEBUG(1,("kdc_task_init: krb5_init_context failed (%s)\n", error_message(ret))); task_server_terminate(task, "kdc: krb5_init_context failed", true); return; } krb5_add_et_list(kdc->smb_krb5_context->krb5_context, initialize_hdb_error_table_r); ret = krb5_kdc_get_config(kdc->smb_krb5_context->krb5_context, &kdc_config); if(ret) { task_server_terminate(task, "kdc: failed to get KDC configuration", true); return; } kdc_config->logf = (krb5_log_facility *)kdc->smb_krb5_context->pvt_log_data; kdc_config->db = talloc(kdc, struct HDB *); if (!kdc_config->db) { task_server_terminate(task, "kdc: out of memory", true); return; } kdc_config->num_db = 1; /* * This restores the behavior before * commit 255e3e18e00f717d99f3bc57c8a8895ff624f3c3 * s4:heimdal: import lorikeet-heimdal-201107150856 * (commit 48936803fae4a2fb362c79365d31f420c917b85b) * * as_use_strongest_session_key,preauth_use_strongest_session_key * and tgs_use_strongest_session_key are input to the * _kdc_find_etype() function. The old bahavior is in * the use_strongest_session_key=FALSE code path. * (The only remaining difference in _kdc_find_etype() * is the is_preauth parameter.) * * The old behavior in the _kdc_get_preferred_key() * function is use_strongest_server_key=TRUE. */ kdc_config->as_use_strongest_session_key = false; kdc_config->preauth_use_strongest_session_key = false; kdc_config->tgs_use_strongest_session_key = false; kdc_config->use_strongest_server_key = true; kdc_config->autodetect_referrals = false; /* Register hdb-samba4 hooks for use as a keytab */ kdc->base_ctx = talloc_zero(kdc, struct samba_kdc_base_context); if (!kdc->base_ctx) { task_server_terminate(task, "kdc: out of memory", true); return; } kdc->base_ctx->ev_ctx = task->event_ctx; kdc->base_ctx->lp_ctx = task->lp_ctx; kdc->base_ctx->msg_ctx = task->msg_ctx; status = hdb_samba4_create_kdc(kdc->base_ctx, kdc->smb_krb5_context->krb5_context, &kdc_config->db[0]); if (!NT_STATUS_IS_OK(status)) { task_server_terminate(task, "kdc: hdb_samba4_create_kdc (setup KDC database) failed", true); return; } ret = krb5_plugin_register(kdc->smb_krb5_context->krb5_context, PLUGIN_TYPE_DATA, "hdb", &hdb_samba4_interface); if(ret) { task_server_terminate(task, "kdc: failed to register hdb plugin", true); return; } ret = krb5_kt_register(kdc->smb_krb5_context->krb5_context, &hdb_kt_ops); if(ret) { task_server_terminate(task, "kdc: failed to register keytab plugin", true); return; } kdc->keytab_name = talloc_asprintf(kdc, "HDB:samba4&%p", kdc->base_ctx); if (kdc->keytab_name == NULL) { task_server_terminate(task, "kdc: Failed to set keytab name", true); return; } /* Register WinDC hooks */ ret = krb5_plugin_register(kdc->smb_krb5_context->krb5_context, PLUGIN_TYPE_DATA, "windc", &windc_plugin_table); if(ret) { task_server_terminate(task, "kdc: failed to register windc plugin", true); return; } ret = krb5_kdc_windc_init(kdc->smb_krb5_context->krb5_context); if(ret) { task_server_terminate(task, "kdc: failed to init windc plugin", true); return; } ret = krb5_kdc_pkinit_config(kdc->smb_krb5_context->krb5_context, kdc_config); if(ret) { task_server_terminate(task, "kdc: failed to init kdc pkinit subsystem", true); return; } kdc->private_data = kdc_config; /* start listening on the configured network interfaces */ status = kdc_startup_interfaces(kdc, task->lp_ctx, ifaces, task->model_ops); if (!NT_STATUS_IS_OK(status)) { task_server_terminate(task, "kdc failed to setup interfaces", true); return; } status = IRPC_REGISTER(task->msg_ctx, irpc, KDC_CHECK_GENERIC_KERBEROS, kdc_check_generic_kerberos, kdc); if (!NT_STATUS_IS_OK(status)) { task_server_terminate(task, "kdc failed to setup monitoring", true); return; } irpc_add_name(task->msg_ctx, "kdc_server"); }
/* fill in the cldap netlogon union for a given version */ NTSTATUS fill_netlogon_samlogon_response(struct ldb_context *sam_ctx, TALLOC_CTX *mem_ctx, const char *domain, const char *netbios_domain, struct dom_sid *domain_sid, const char *domain_guid, const char *user, uint32_t acct_control, const char *src_address, uint32_t version, struct loadparm_context *lp_ctx, struct netlogon_samlogon_response *netlogon, bool fill_on_blank_request) { const char *dom_attrs[] = {"objectGUID", NULL}; const char *none_attrs[] = {NULL}; struct ldb_result *dom_res = NULL, *user_res = NULL; int ret; const char **services = lpcfg_server_services(lp_ctx); uint32_t server_type; const char *pdc_name; struct GUID domain_uuid; const char *dns_domain; const char *forest_domain; const char *pdc_dns_name; const char *flatname; const char *server_site; const char *client_site; const char *pdc_ip; struct ldb_dn *domain_dn = NULL; struct interface *ifaces; bool user_known, am_rodc; NTSTATUS status; /* the domain parameter could have an optional trailing "." */ if (domain && domain[strlen(domain)-1] == '.') { domain = talloc_strndup(mem_ctx, domain, strlen(domain)-1); NT_STATUS_HAVE_NO_MEMORY(domain); } /* Lookup using long or short domainname */ if (domain && (strcasecmp_m(domain, lpcfg_dnsdomain(lp_ctx)) == 0)) { domain_dn = ldb_get_default_basedn(sam_ctx); } if (netbios_domain && (strcasecmp_m(netbios_domain, lpcfg_sam_name(lp_ctx)) == 0)) { domain_dn = ldb_get_default_basedn(sam_ctx); } if (domain_dn) { const char *domain_identifier = domain != NULL ? domain : netbios_domain; ret = ldb_search(sam_ctx, mem_ctx, &dom_res, domain_dn, LDB_SCOPE_BASE, dom_attrs, "objectClass=domain"); if (ret != LDB_SUCCESS) { DEBUG(2,("Error finding domain '%s'/'%s' in sam: %s\n", domain_identifier, ldb_dn_get_linearized(domain_dn), ldb_errstring(sam_ctx))); return NT_STATUS_NO_SUCH_DOMAIN; } if (dom_res->count != 1) { DEBUG(2,("Error finding domain '%s'/'%s' in sam\n", domain_identifier, ldb_dn_get_linearized(domain_dn))); return NT_STATUS_NO_SUCH_DOMAIN; } } /* Lookup using GUID or SID */ if ((dom_res == NULL) && (domain_guid || domain_sid)) { if (domain_guid) { struct GUID binary_guid; struct ldb_val guid_val; /* By this means, we ensure we don't have funny stuff in the GUID */ status = GUID_from_string(domain_guid, &binary_guid); if (!NT_STATUS_IS_OK(status)) { return status; } /* And this gets the result into the binary format we want anyway */ status = GUID_to_ndr_blob(&binary_guid, mem_ctx, &guid_val); if (!NT_STATUS_IS_OK(status)) { return status; } ret = ldb_search(sam_ctx, mem_ctx, &dom_res, NULL, LDB_SCOPE_SUBTREE, dom_attrs, "(&(objectCategory=DomainDNS)(objectGUID=%s))", ldb_binary_encode(mem_ctx, guid_val)); } else { /* domain_sid case */ ret = ldb_search(sam_ctx, mem_ctx, &dom_res, NULL, LDB_SCOPE_SUBTREE, dom_attrs, "(&(objectCategory=DomainDNS)(objectSid=%s))", dom_sid_string(mem_ctx, domain_sid)); } if (ret != LDB_SUCCESS) { DEBUG(2,("Unable to find a correct reference to GUID '%s' or SID '%s' in sam: %s\n", domain_guid, dom_sid_string(mem_ctx, domain_sid), ldb_errstring(sam_ctx))); return NT_STATUS_NO_SUCH_DOMAIN; } else if (dom_res->count == 1) { /* Ok, now just check it is our domain */ if (ldb_dn_compare(ldb_get_default_basedn(sam_ctx), dom_res->msgs[0]->dn) != 0) { DEBUG(2,("The GUID '%s' or SID '%s' doesn't identify our domain\n", domain_guid, dom_sid_string(mem_ctx, domain_sid))); return NT_STATUS_NO_SUCH_DOMAIN; } } else { DEBUG(2,("Unable to find a correct reference to GUID '%s' or SID '%s' in sam\n", domain_guid, dom_sid_string(mem_ctx, domain_sid))); return NT_STATUS_NO_SUCH_DOMAIN; } } if (dom_res == NULL && fill_on_blank_request) { /* blank inputs gives our domain - tested against w2k8r2. Without this ADUC on Win7 won't start */ domain_dn = ldb_get_default_basedn(sam_ctx); ret = ldb_search(sam_ctx, mem_ctx, &dom_res, domain_dn, LDB_SCOPE_BASE, dom_attrs, "objectClass=domain"); if (ret != LDB_SUCCESS) { DEBUG(2,("Error finding domain '%s'/'%s' in sam: %s\n", lpcfg_dnsdomain(lp_ctx), ldb_dn_get_linearized(domain_dn), ldb_errstring(sam_ctx))); return NT_STATUS_NO_SUCH_DOMAIN; } } if (dom_res == NULL) { DEBUG(2,(__location__ ": Unable to get domain information with no inputs\n")); return NT_STATUS_NO_SUCH_DOMAIN; } /* work around different inputs for not-specified users */ if (!user) { user = ""; } /* Enquire about any valid username with just a CLDAP packet - * if kerberos didn't also do this, the security folks would * scream... */ if (user[0]) { \ /* Only allow some bits to be enquired: [MS-ATDS] 7.3.3.2 */ if (acct_control == (uint32_t)-1) { acct_control = 0; } acct_control = acct_control & (ACB_TEMPDUP | ACB_NORMAL | ACB_DOMTRUST | ACB_WSTRUST | ACB_SVRTRUST); /* We must exclude disabled accounts, but otherwise do the bitwise match the client asked for */ ret = ldb_search(sam_ctx, mem_ctx, &user_res, dom_res->msgs[0]->dn, LDB_SCOPE_SUBTREE, none_attrs, "(&(objectClass=user)(samAccountName=%s)" "(!(userAccountControl:" LDB_OID_COMPARATOR_AND ":=%u))" "(userAccountControl:" LDB_OID_COMPARATOR_OR ":=%u))", ldb_binary_encode_string(mem_ctx, user), UF_ACCOUNTDISABLE, ds_acb2uf(acct_control)); if (ret != LDB_SUCCESS) { DEBUG(2,("Unable to find reference to user '%s' with ACB 0x%8x under %s: %s\n", user, acct_control, ldb_dn_get_linearized(dom_res->msgs[0]->dn), ldb_errstring(sam_ctx))); return NT_STATUS_NO_SUCH_USER; } else if (user_res->count == 1) { user_known = true; } else { user_known = false; } } else { user_known = true; } server_type = DS_SERVER_DS | DS_SERVER_TIMESERV | DS_SERVER_GOOD_TIMESERV; if (samdb_is_pdc(sam_ctx)) { server_type |= DS_SERVER_PDC; } if (dsdb_functional_level(sam_ctx) >= DS_DOMAIN_FUNCTION_2008) { server_type |= DS_SERVER_FULL_SECRET_DOMAIN_6; } if (samdb_is_gc(sam_ctx)) { server_type |= DS_SERVER_GC; } if (str_list_check(services, "ldap")) { server_type |= DS_SERVER_LDAP; } if (str_list_check(services, "kdc")) { server_type |= DS_SERVER_KDC; } if (samdb_rodc(sam_ctx, &am_rodc) == LDB_SUCCESS && !am_rodc) { server_type |= DS_SERVER_WRITABLE; } pdc_name = talloc_asprintf(mem_ctx, "\\\\%s", lpcfg_netbios_name(lp_ctx)); NT_STATUS_HAVE_NO_MEMORY(pdc_name); domain_uuid = samdb_result_guid(dom_res->msgs[0], "objectGUID"); dns_domain = lpcfg_dnsdomain(lp_ctx); forest_domain = samdb_forest_name(sam_ctx, mem_ctx); NT_STATUS_HAVE_NO_MEMORY(forest_domain); pdc_dns_name = talloc_asprintf(mem_ctx, "%s.%s", strlower_talloc(mem_ctx, lpcfg_netbios_name(lp_ctx)), dns_domain); NT_STATUS_HAVE_NO_MEMORY(pdc_dns_name); flatname = lpcfg_workgroup(lp_ctx); server_site = samdb_server_site_name(sam_ctx, mem_ctx); NT_STATUS_HAVE_NO_MEMORY(server_site); client_site = samdb_client_site_name(sam_ctx, mem_ctx, src_address, NULL); NT_STATUS_HAVE_NO_MEMORY(client_site); if (strcasecmp(server_site, client_site) == 0) { server_type |= DS_SERVER_CLOSEST; } load_interface_list(mem_ctx, lp_ctx, &ifaces); if (src_address) { pdc_ip = iface_list_best_ip(ifaces, src_address); } else { pdc_ip = iface_list_first_v4(ifaces); } if (pdc_ip == NULL || !is_ipaddress_v4(pdc_ip)) { /* this matches windows behaviour */ pdc_ip = "127.0.0.1"; } ZERO_STRUCTP(netlogon); /* check if either of these bits is present */ if (version & (NETLOGON_NT_VERSION_5EX|NETLOGON_NT_VERSION_5EX_WITH_IP)) { uint32_t extra_flags = 0; netlogon->ntver = NETLOGON_NT_VERSION_5EX; /* could check if the user exists */ if (user_known) { netlogon->data.nt5_ex.command = LOGON_SAM_LOGON_RESPONSE_EX; } else { netlogon->data.nt5_ex.command = LOGON_SAM_LOGON_USER_UNKNOWN_EX; } netlogon->data.nt5_ex.pdc_name = pdc_name; netlogon->data.nt5_ex.user_name = user; netlogon->data.nt5_ex.domain_name = flatname; netlogon->data.nt5_ex.domain_uuid = domain_uuid; netlogon->data.nt5_ex.forest = forest_domain; netlogon->data.nt5_ex.dns_domain = dns_domain; netlogon->data.nt5_ex.pdc_dns_name = pdc_dns_name; netlogon->data.nt5_ex.server_site = server_site; netlogon->data.nt5_ex.client_site = client_site; if (version & NETLOGON_NT_VERSION_5EX_WITH_IP) { /* note that this is always a IPV4 address */ extra_flags = NETLOGON_NT_VERSION_5EX_WITH_IP; netlogon->data.nt5_ex.sockaddr.sockaddr_family = 2; netlogon->data.nt5_ex.sockaddr.pdc_ip = pdc_ip; netlogon->data.nt5_ex.sockaddr.remaining = data_blob_talloc_zero(mem_ctx, 8); } netlogon->data.nt5_ex.server_type = server_type; netlogon->data.nt5_ex.nt_version = NETLOGON_NT_VERSION_1|NETLOGON_NT_VERSION_5EX|extra_flags; netlogon->data.nt5_ex.lmnt_token = 0xFFFF; netlogon->data.nt5_ex.lm20_token = 0xFFFF; } else if (version & NETLOGON_NT_VERSION_5) { netlogon->ntver = NETLOGON_NT_VERSION_5; /* could check if the user exists */ if (user_known) { netlogon->data.nt5.command = LOGON_SAM_LOGON_RESPONSE; } else { netlogon->data.nt5.command = LOGON_SAM_LOGON_USER_UNKNOWN; } netlogon->data.nt5.pdc_name = pdc_name; netlogon->data.nt5.user_name = user; netlogon->data.nt5.domain_name = flatname; netlogon->data.nt5.domain_uuid = domain_uuid; netlogon->data.nt5.forest = forest_domain; netlogon->data.nt5.dns_domain = dns_domain; netlogon->data.nt5.pdc_dns_name = pdc_dns_name; netlogon->data.nt5.pdc_ip = pdc_ip; netlogon->data.nt5.server_type = server_type; netlogon->data.nt5.nt_version = NETLOGON_NT_VERSION_1|NETLOGON_NT_VERSION_5; netlogon->data.nt5.lmnt_token = 0xFFFF; netlogon->data.nt5.lm20_token = 0xFFFF; } else /* (version & NETLOGON_NT_VERSION_1) and all other cases */ { netlogon->ntver = NETLOGON_NT_VERSION_1; /* could check if the user exists */ if (user_known) { netlogon->data.nt4.command = LOGON_SAM_LOGON_RESPONSE; } else { netlogon->data.nt4.command = LOGON_SAM_LOGON_USER_UNKNOWN; } netlogon->data.nt4.pdc_name = pdc_name; netlogon->data.nt4.user_name = user; netlogon->data.nt4.domain_name = flatname; netlogon->data.nt4.nt_version = NETLOGON_NT_VERSION_1; netlogon->data.nt4.lmnt_token = 0xFFFF; netlogon->data.nt4.lm20_token = 0xFFFF; } return NT_STATUS_OK; }
static NTSTATUS authsam_authenticate(struct auth_context *auth_context, TALLOC_CTX *mem_ctx, struct ldb_context *sam_ctx, struct ldb_dn *domain_dn, struct ldb_message *msg, const struct auth_usersupplied_info *user_info, DATA_BLOB *user_sess_key, DATA_BLOB *lm_sess_key) { struct samr_Password *lm_pwd, *nt_pwd; NTSTATUS nt_status; uint16_t acct_flags = samdb_result_acct_flags(auth_context->sam_ctx, mem_ctx, msg, domain_dn); /* Quit if the account was locked out. */ if (acct_flags & ACB_AUTOLOCK) { DEBUG(3,("check_sam_security: Account for user %s was locked out.\n", user_info->mapped.account_name)); return NT_STATUS_ACCOUNT_LOCKED_OUT; } /* You can only do an interactive login to normal accounts */ if (user_info->flags & USER_INFO_INTERACTIVE_LOGON) { if (!(acct_flags & ACB_NORMAL)) { return NT_STATUS_NO_SUCH_USER; } } nt_status = samdb_result_passwords(mem_ctx, auth_context->lp_ctx, msg, &lm_pwd, &nt_pwd); NT_STATUS_NOT_OK_RETURN(nt_status); if (lm_pwd == NULL && nt_pwd == NULL) { bool am_rodc; 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. */ auth_sam_trigger_repl_secret(mem_ctx, auth_context, msg->dn); return NT_STATUS_NOT_IMPLEMENTED; } } nt_status = authsam_password_ok(auth_context, mem_ctx, acct_flags, lm_pwd, nt_pwd, user_info, user_sess_key, lm_sess_key); NT_STATUS_NOT_OK_RETURN(nt_status); nt_status = authsam_account_ok(mem_ctx, auth_context->sam_ctx, user_info->logon_parameters, domain_dn, msg, user_info->workstation_name, user_info->mapped.account_name, false, false); return nt_status; }
static WERROR dreplsrv_connect_samdb(struct dreplsrv_service *service, struct loadparm_context *lp_ctx) { const struct GUID *ntds_guid; struct drsuapi_DsBindInfo28 *bind_info28; service->samdb = samdb_connect(service, service->task->event_ctx, lp_ctx, service->system_session_info, NULL, 0); if (!service->samdb) { return WERR_DS_UNAVAILABLE; } ntds_guid = samdb_ntds_objectGUID(service->samdb); if (!ntds_guid) { return WERR_DS_UNAVAILABLE; } service->ntds_guid = *ntds_guid; if (samdb_rodc(service->samdb, &service->am_rodc) != LDB_SUCCESS) { DEBUG(0,(__location__ ": Failed to determine RODC status\n")); return WERR_DS_UNAVAILABLE; } bind_info28 = &service->bind_info28; bind_info28->supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_BASE; bind_info28->supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_ASYNC_REPLICATION; bind_info28->supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_REMOVEAPI; bind_info28->supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_MOVEREQ_V2; bind_info28->supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_GETCHG_COMPRESS; bind_info28->supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_DCINFO_V1; bind_info28->supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_RESTORE_USN_OPTIMIZATION; bind_info28->supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_KCC_EXECUTE; bind_info28->supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_ADDENTRY_V2; bind_info28->supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_LINKED_VALUE_REPLICATION; bind_info28->supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_DCINFO_V2; bind_info28->supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_INSTANCE_TYPE_NOT_REQ_ON_MOD; bind_info28->supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_CRYPTO_BIND; bind_info28->supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_GET_REPL_INFO; bind_info28->supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_STRONG_ENCRYPTION; bind_info28->supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_DCINFO_V01; bind_info28->supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_TRANSITIVE_MEMBERSHIP; bind_info28->supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_ADD_SID_HISTORY; bind_info28->supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_POST_BETA3; bind_info28->supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_GETCHGREQ_V5; bind_info28->supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_GET_MEMBERSHIPS2; bind_info28->supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_GETCHGREQ_V6; bind_info28->supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_NONDOMAIN_NCS; bind_info28->supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_GETCHGREQ_V8; bind_info28->supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_GETCHGREPLY_V5; bind_info28->supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_GETCHGREPLY_V6; bind_info28->supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_ADDENTRYREPLY_V3; bind_info28->supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_GETCHGREPLY_V7; bind_info28->supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_VERIFY_OBJECT; #if 0 /* we don't support XPRESS compression yet */ bind_info28->supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_XPRESS_COMPRESS; #endif /* TODO: fill in site_guid */ bind_info28->site_guid = GUID_zero(); /* TODO: find out how this is really triggered! */ bind_info28->pid = 0; bind_info28->repl_epoch = 0; return WERR_OK; }
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; }