X-Git-Url: http://git.samba.org/?a=blobdiff_plain;f=source4%2Fdsdb%2Fcommon%2Futil.c;h=411d1dd22b05d2d55fe674b5f9837b7d7b69cf45;hb=e18610a197aab80a32cae8c1e09b96496679bbad;hp=0bbf402252395b165520f264cdff56c4aab1e0b3;hpb=5ce969d0c70afc1f14a9b223edbaec7a847c64de;p=samba.git diff --git a/source4/dsdb/common/util.c b/source4/dsdb/common/util.c index 0bbf4022523..411d1dd22b0 100644 --- a/source4/dsdb/common/util.c +++ b/source4/dsdb/common/util.c @@ -45,7 +45,18 @@ #include "lib/socket/socket.h" #include "librpc/gen_ndr/irpc.h" #include "libds/common/flag_mapping.h" -#include "../lib/util/util_runcmd.h" +#include "lib/util/access.h" +#include "lib/util/util_str_hex.h" +#include "libcli/util/ntstatus.h" + +/* + * This included to allow us to handle DSDB_FLAG_REPLICATED_UPDATE in + * dsdb_request_add_controls() + */ +#include "dsdb/samdb/ldb_modules/util.h" + +/* default is 30 minutes: -1e7 * 30 * 60 */ +#define DEFAULT_OBSERVATION_WINDOW -18000000000 /* search the sam for the specified attributes in a specific domain, filter on @@ -191,26 +202,6 @@ struct dom_sid *samdb_search_dom_sid(struct ldb_context *sam_ldb, return sid; } -/* - return the count of the number of records in the sam matching the query -*/ -int samdb_search_count(struct ldb_context *sam_ldb, - TALLOC_CTX *mem_ctx, - struct ldb_dn *basedn, - const char *format, ...) _PRINTF_ATTRIBUTE(4,5) -{ - va_list ap; - const char *attrs[] = { NULL }; - int ret; - - va_start(ap, format); - ret = gendb_search_v(sam_ldb, mem_ctx, basedn, NULL, attrs, format, ap); - va_end(ap); - - return ret; -} - - /* search the sam for a single integer attribute in exactly 1 record */ @@ -351,7 +342,7 @@ uint32_t samdb_result_rid_from_sid(TALLOC_CTX *mem_ctx, const struct ldb_message struct dom_sid *samdb_result_dom_sid(TALLOC_CTX *mem_ctx, const struct ldb_message *msg, const char *attr) { - bool ok; + struct sid_parse_ret ret; const struct ldb_val *v; struct dom_sid *sid; v = ldb_msg_find_ldb_val(msg, attr); @@ -362,8 +353,8 @@ struct dom_sid *samdb_result_dom_sid(TALLOC_CTX *mem_ctx, const struct ldb_messa if (sid == NULL) { return NULL; } - ok = sid_parse(v->data, v->length, sid); - if (!ok) { + ret = sid_parse(v->data, v->length, sid); + if (ret.len == -1) { talloc_free(sid); return NULL; } @@ -1287,6 +1278,61 @@ failed: return false; } +/* + work out the domain guid for the current open ldb +*/ +const struct GUID *samdb_domain_guid(struct ldb_context *ldb) +{ + TALLOC_CTX *tmp_ctx = NULL; + struct GUID *domain_guid = NULL; + const char *attrs[] = { + "objectGUID", + NULL + }; + struct ldb_result *res = NULL; + int ret; + + /* see if we have a cached copy */ + domain_guid = (struct GUID *)ldb_get_opaque(ldb, "cache.domain_guid"); + if (domain_guid) { + return domain_guid; + } + + tmp_ctx = talloc_new(ldb); + if (tmp_ctx == NULL) { + goto failed; + } + + ret = ldb_search(ldb, tmp_ctx, &res, ldb_get_default_basedn(ldb), LDB_SCOPE_BASE, attrs, "objectGUID=*"); + if (ret != LDB_SUCCESS) { + goto failed; + } + + if (res->count != 1) { + goto failed; + } + + domain_guid = talloc(tmp_ctx, struct GUID); + if (domain_guid == NULL) { + goto failed; + } + *domain_guid = samdb_result_guid(res->msgs[0], "objectGUID"); + + /* cache the domain_sid in the ldb */ + if (ldb_set_opaque(ldb, "cache.domain_guid", domain_guid) != LDB_SUCCESS) { + goto failed; + } + + talloc_steal(ldb, domain_guid); + talloc_free(tmp_ctx); + + return domain_guid; + +failed: + talloc_free(tmp_ctx); + return NULL; +} + bool samdb_set_ntds_settings_dn(struct ldb_context *ldb, struct ldb_dn *ntds_settings_dn_in) { TALLOC_CTX *tmp_ctx; @@ -1375,21 +1421,24 @@ failed: } /* - work out the ntds settings invocationId for the current open ldb + work out the ntds settings invocationID/objectGUID for the current open ldb */ -const struct GUID *samdb_ntds_invocation_id(struct ldb_context *ldb) +static const struct GUID *samdb_ntds_GUID(struct ldb_context *ldb, + const char *attribute, + const char *cache_name) { TALLOC_CTX *tmp_ctx; - const char *attrs[] = { "invocationId", NULL }; + const char *attrs[] = { attribute, NULL }; int ret; struct ldb_result *res; - struct GUID *invocation_id; + struct GUID *ntds_guid; + struct ldb_dn *ntds_settings_dn = NULL; + const char *errstr = NULL; /* see if we have a cached copy */ - invocation_id = (struct GUID *)ldb_get_opaque(ldb, "cache.invocation_id"); - if (invocation_id) { - SMB_ASSERT(!GUID_all_zero(invocation_id)); - return invocation_id; + ntds_guid = (struct GUID *)ldb_get_opaque(ldb, cache_name); + if (ntds_guid != NULL) { + return ntds_guid; } tmp_ctx = talloc_new(ldb); @@ -1397,148 +1446,85 @@ const struct GUID *samdb_ntds_invocation_id(struct ldb_context *ldb) goto failed; } - ret = ldb_search(ldb, tmp_ctx, &res, samdb_ntds_settings_dn(ldb, tmp_ctx), LDB_SCOPE_BASE, attrs, NULL); + ntds_settings_dn = samdb_ntds_settings_dn(ldb, tmp_ctx); + if (ntds_settings_dn == NULL) { + errstr = "samdb_ntds_settings_dn() returned NULL"; + goto failed; + } + + ret = ldb_search(ldb, tmp_ctx, &res, ntds_settings_dn, + LDB_SCOPE_BASE, attrs, NULL); if (ret) { + errstr = ldb_errstring(ldb); goto failed; } if (res->count != 1) { + errstr = "incorrect number of results from base search"; goto failed; } - invocation_id = talloc(tmp_ctx, struct GUID); - if (!invocation_id) { + ntds_guid = talloc(tmp_ctx, struct GUID); + if (ntds_guid == NULL) { goto failed; } - *invocation_id = samdb_result_guid(res->msgs[0], "invocationId"); - if (GUID_all_zero(invocation_id)) { - if (ldb_msg_find_ldb_val(res->msgs[0], "invocationId")) { - DEBUG(0, ("Failed to find our own NTDS Settings invocationId in the ldb!\n")); + *ntds_guid = samdb_result_guid(res->msgs[0], attribute); + + if (GUID_all_zero(ntds_guid)) { + if (ldb_msg_find_ldb_val(res->msgs[0], attribute)) { + errstr = "failed to find the GUID attribute"; } else { - DEBUG(0, ("Failed to find parse own NTDS Settings invocationId from the ldb!\n")); + errstr = "failed to parse the GUID"; } goto failed; } /* cache the domain_sid in the ldb */ - if (ldb_set_opaque(ldb, "cache.invocation_id", invocation_id) != LDB_SUCCESS) { + if (ldb_set_opaque(ldb, cache_name, ntds_guid) != LDB_SUCCESS) { + errstr = "ldb_set_opaque() failed"; goto failed; } - talloc_steal(ldb, invocation_id); + talloc_steal(ldb, ntds_guid); talloc_free(tmp_ctx); - return invocation_id; + return ntds_guid; failed: - DEBUG(1,("Failed to find our own NTDS Settings invocationId in the ldb!\n")); + DBG_WARNING("Failed to find our own NTDS Settings %s in the ldb: %s!\n", + attribute, errstr); talloc_free(tmp_ctx); return NULL; } -bool samdb_set_ntds_invocation_id(struct ldb_context *ldb, const struct GUID *invocation_id_in) -{ - TALLOC_CTX *tmp_ctx; - struct GUID *invocation_id_new; - struct GUID *invocation_id_old; - - /* see if we have a cached copy */ - invocation_id_old = (struct GUID *)ldb_get_opaque(ldb, - "cache.invocation_id"); - - tmp_ctx = talloc_new(ldb); - if (tmp_ctx == NULL) { - goto failed; - } - - invocation_id_new = talloc(tmp_ctx, struct GUID); - if (!invocation_id_new) { - goto failed; - } - - SMB_ASSERT(!GUID_all_zero(invocation_id_in)); - *invocation_id_new = *invocation_id_in; - - /* cache the domain_sid in the ldb */ - if (ldb_set_opaque(ldb, "cache.invocation_id", invocation_id_new) != LDB_SUCCESS) { - goto failed; - } - - talloc_steal(ldb, invocation_id_new); - talloc_free(tmp_ctx); - talloc_free(invocation_id_old); - - return true; - -failed: - DEBUG(1,("Failed to set our own cached invocationId in the ldb!\n")); - talloc_free(tmp_ctx); - return false; -} - /* work out the ntds settings objectGUID for the current open ldb */ const struct GUID *samdb_ntds_objectGUID(struct ldb_context *ldb) { - TALLOC_CTX *tmp_ctx; - const char *attrs[] = { "objectGUID", NULL }; - int ret; - struct ldb_result *res; - struct GUID *ntds_guid; - - /* see if we have a cached copy */ - ntds_guid = (struct GUID *)ldb_get_opaque(ldb, "cache.ntds_guid"); - if (ntds_guid) { - return ntds_guid; - } - - tmp_ctx = talloc_new(ldb); - if (tmp_ctx == NULL) { - goto failed; - } - - ret = ldb_search(ldb, tmp_ctx, &res, samdb_ntds_settings_dn(ldb, tmp_ctx), LDB_SCOPE_BASE, attrs, NULL); - if (ret) { - goto failed; - } - - if (res->count != 1) { - goto failed; - } - - ntds_guid = talloc(tmp_ctx, struct GUID); - if (!ntds_guid) { - goto failed; - } - - *ntds_guid = samdb_result_guid(res->msgs[0], "objectGUID"); - - /* cache the domain_sid in the ldb */ - if (ldb_set_opaque(ldb, "cache.ntds_guid", ntds_guid) != LDB_SUCCESS) { - goto failed; - } - - talloc_steal(ldb, ntds_guid); - talloc_free(tmp_ctx); - - return ntds_guid; + return samdb_ntds_GUID(ldb, "objectGUID", "cache.ntds_guid"); +} -failed: - DEBUG(1,("Failed to find our own NTDS Settings objectGUID in the ldb!\n")); - talloc_free(tmp_ctx); - return NULL; +/* + work out the ntds settings invocationId for the current open ldb +*/ +const struct GUID *samdb_ntds_invocation_id(struct ldb_context *ldb) +{ + return samdb_ntds_GUID(ldb, "invocationId", "cache.invocation_id"); } -bool samdb_set_ntds_objectGUID(struct ldb_context *ldb, const struct GUID *ntds_guid_in) +static bool samdb_set_ntds_GUID(struct ldb_context *ldb, + const struct GUID *ntds_guid_in, + const char *attribute, + const char *cache_name) { TALLOC_CTX *tmp_ctx; struct GUID *ntds_guid_new; struct GUID *ntds_guid_old; /* see if we have a cached copy */ - ntds_guid_old = (struct GUID *)ldb_get_opaque(ldb, "cache.ntds_guid"); + ntds_guid_old = (struct GUID *)ldb_get_opaque(ldb, cache_name); tmp_ctx = talloc_new(ldb); if (tmp_ctx == NULL) { @@ -1553,7 +1539,7 @@ bool samdb_set_ntds_objectGUID(struct ldb_context *ldb, const struct GUID *ntds_ *ntds_guid_new = *ntds_guid_in; /* cache the domain_sid in the ldb */ - if (ldb_set_opaque(ldb, "cache.ntds_guid", ntds_guid_new) != LDB_SUCCESS) { + if (ldb_set_opaque(ldb, cache_name, ntds_guid_new) != LDB_SUCCESS) { goto failed; } @@ -1564,11 +1550,28 @@ bool samdb_set_ntds_objectGUID(struct ldb_context *ldb, const struct GUID *ntds_ return true; failed: - DEBUG(1,("Failed to set our own cached invocationId in the ldb!\n")); + DBG_WARNING("Failed to set our own cached %s in the ldb!\n", + attribute); talloc_free(tmp_ctx); return false; } +bool samdb_set_ntds_objectGUID(struct ldb_context *ldb, const struct GUID *ntds_guid_in) +{ + return samdb_set_ntds_GUID(ldb, + ntds_guid_in, + "objectGUID", + "cache.ntds_guid"); +} + +bool samdb_set_ntds_invocation_id(struct ldb_context *ldb, const struct GUID *invocation_id_in) +{ + return samdb_set_ntds_GUID(ldb, + invocation_id_in, + "invocationId", + "cache.invocation_id"); +} + /* work out the server dn for the current open ldb */ @@ -1818,18 +1821,25 @@ const char *samdb_server_site_name(struct ldb_context *ldb, TALLOC_CTX *mem_ctx) /* * Finds the client site by using the client's IP address. * The "subnet_name" returns the name of the subnet if parameter != NULL + * + * Has a Windows-based fallback to provide the only site available, or an empty + * string if there are multiple sites. */ const char *samdb_client_site_name(struct ldb_context *ldb, TALLOC_CTX *mem_ctx, - const char *ip_address, char **subnet_name) + const char *ip_address, char **subnet_name, + bool fallback) { const char *attrs[] = { "cn", "siteObject", NULL }; - struct ldb_dn *sites_container_dn, *subnets_dn, *sites_dn; - struct ldb_result *res; - const struct ldb_val *val; - const char *site_name = NULL, *l_subnet_name = NULL; + struct ldb_dn *sites_container_dn = NULL; + struct ldb_dn *subnets_dn = NULL; + struct ldb_dn *sites_dn = NULL; + struct ldb_result *res = NULL; + const struct ldb_val *val = NULL; + const char *site_name = NULL; + const char *l_subnet_name = NULL; const char *allow_list[2] = { NULL, NULL }; unsigned int i, count; - int cnt, ret; + int ret; /* * if we don't have a client ip e.g. ncalrpc @@ -1841,14 +1851,12 @@ const char *samdb_client_site_name(struct ldb_context *ldb, TALLOC_CTX *mem_ctx, sites_container_dn = samdb_sites_dn(ldb, mem_ctx); if (sites_container_dn == NULL) { - return NULL; + goto exit; } subnets_dn = ldb_dn_copy(mem_ctx, sites_container_dn); if ( ! ldb_dn_add_child_fmt(subnets_dn, "CN=Subnets")) { - talloc_free(sites_container_dn); - talloc_free(subnets_dn); - return NULL; + goto exit; } ret = ldb_search(ldb, mem_ctx, &res, subnets_dn, LDB_SCOPE_ONELEVEL, @@ -1856,9 +1864,7 @@ const char *samdb_client_site_name(struct ldb_context *ldb, TALLOC_CTX *mem_ctx, if (ret == LDB_ERR_NO_SUCH_OBJECT) { count = 0; } else if (ret != LDB_SUCCESS) { - talloc_free(sites_container_dn); - talloc_free(subnets_dn); - return NULL; + goto exit; } else { count = res->count; } @@ -1869,7 +1875,7 @@ const char *samdb_client_site_name(struct ldb_context *ldb, TALLOC_CTX *mem_ctx, allow_list[0] = l_subnet_name; - if (socket_allow_access(mem_ctx, NULL, allow_list, "", ip_address)) { + if (allow_access_nolog(NULL, allow_list, "", ip_address)) { sites_dn = ldb_msg_find_attr_as_dn(ldb, mem_ctx, res->msgs[i], "siteObject"); @@ -1883,20 +1889,29 @@ const char *samdb_client_site_name(struct ldb_context *ldb, TALLOC_CTX *mem_ctx, site_name = talloc_strdup(mem_ctx, (const char *) val->data); - talloc_free(sites_dn); + TALLOC_FREE(sites_dn); break; } } - if (site_name == NULL) { + if (site_name == NULL && fallback) { /* This is the Windows Server fallback rule: when no subnet * exists and we have only one site available then use it (it * is for sure the same as our server site). If more sites do * exist then we don't know which one to use and set the site * name to "". */ - cnt = samdb_search_count(ldb, mem_ctx, sites_container_dn, - "(objectClass=site)"); + size_t cnt = 0; + ret = dsdb_domain_count( + ldb, + &cnt, + sites_container_dn, + NULL, + LDB_SCOPE_SUBTREE, + "(objectClass=site)"); + if (ret != LDB_SUCCESS) { + goto exit; + } if (cnt == 1) { site_name = samdb_server_site_name(ldb, mem_ctx); } else { @@ -1909,9 +1924,10 @@ const char *samdb_client_site_name(struct ldb_context *ldb, TALLOC_CTX *mem_ctx, *subnet_name = talloc_strdup(mem_ctx, l_subnet_name); } - talloc_free(sites_container_dn); - talloc_free(subnets_dn); - talloc_free(res); +exit: + TALLOC_FREE(sites_container_dn); + TALLOC_FREE(subnets_dn); + TALLOC_FREE(res); return site_name; } @@ -2013,6 +2029,9 @@ static void pwd_timeout_debug(struct tevent_context *unused1, */ enum samr_ValidationStatus samdb_check_password(TALLOC_CTX *mem_ctx, struct loadparm_context *lp_ctx, + const char *account_name, + const char *user_principal_name, + const char *full_name, const DATA_BLOB *utf8_blob, const uint32_t pwdProperties, const uint32_t minPwdLength) @@ -2041,7 +2060,7 @@ enum samr_ValidationStatus samdb_check_password(TALLOC_CTX *mem_ctx, int error = 0; struct tevent_context *event_ctx = NULL; struct tevent_req *req = NULL; - struct samba_runcmd_state *run_cmd = NULL; + int cps_stdin = -1; const char * const cmd[4] = { "/bin/sh", "-c", password_script, @@ -2059,18 +2078,58 @@ enum samr_ValidationStatus samdb_check_password(TALLOC_CTX *mem_ctx, tevent_timeval_current_ofs(1, 0), pwd_timeout_debug, NULL); + check_ret = setenv("SAMBA_CPS_ACCOUNT_NAME", account_name, 1); + if (check_ret != 0) { + TALLOC_FREE(password_script); + TALLOC_FREE(event_ctx); + return SAMR_VALIDATION_STATUS_PASSWORD_FILTER_ERROR; + } + if (user_principal_name != NULL) { + check_ret = setenv("SAMBA_CPS_USER_PRINCIPAL_NAME", + user_principal_name, 1); + } else { + unsetenv("SAMBA_CPS_USER_PRINCIPAL_NAME"); + } + if (check_ret != 0) { + TALLOC_FREE(password_script); + TALLOC_FREE(event_ctx); + return SAMR_VALIDATION_STATUS_PASSWORD_FILTER_ERROR; + } + if (full_name != NULL) { + check_ret = setenv("SAMBA_CPS_FULL_NAME", full_name, 1); + } else { + unsetenv("SAMBA_CPS_FULL_NAME"); + } + if (check_ret != 0) { + TALLOC_FREE(password_script); + TALLOC_FREE(event_ctx); + return SAMR_VALIDATION_STATUS_PASSWORD_FILTER_ERROR; + } + req = samba_runcmd_send(event_ctx, event_ctx, tevent_timeval_current_ofs(10, 0), 100, 100, cmd, NULL); - run_cmd = tevent_req_data(req, struct samba_runcmd_state); - if (write(run_cmd->fd_stdin, utf8_pw, utf8_len) != utf8_len) { + unsetenv("SAMBA_CPS_ACCOUNT_NAME"); + unsetenv("SAMBA_CPS_USER_PRINCIPAL_NAME"); + unsetenv("SAMBA_CPS_FULL_NAME"); + if (req == NULL) { + TALLOC_FREE(password_script); + TALLOC_FREE(event_ctx); + return SAMR_VALIDATION_STATUS_PASSWORD_FILTER_ERROR; + } + + cps_stdin = samba_runcmd_export_stdin(req); + + if (write(cps_stdin, utf8_pw, utf8_len) != utf8_len) { + close(cps_stdin); + cps_stdin = -1; TALLOC_FREE(password_script); TALLOC_FREE(event_ctx); return SAMR_VALIDATION_STATUS_PASSWORD_FILTER_ERROR; } - close(run_cmd->fd_stdin); - run_cmd->fd_stdin = -1; + close(cps_stdin); + cps_stdin = -1; if (!tevent_req_poll(req, event_ctx)) { TALLOC_FREE(password_script); @@ -2085,17 +2144,16 @@ enum samr_ValidationStatus samdb_check_password(TALLOC_CTX *mem_ctx, DEBUG(0, ("check_password_complexity: check password script took too long!\n")); TALLOC_FREE(password_script); return SAMR_VALIDATION_STATUS_PASSWORD_FILTER_ERROR; - } else { - DEBUG(5,("check_password_complexity: check password script (%s) " - "returned [%d]\n", password_script, check_ret)); - - if (check_ret != 0) { - DEBUG(1,("check_password_complexity: " - "check password script said new password is not good " - "enough!\n")); - TALLOC_FREE(password_script); - return SAMR_VALIDATION_STATUS_NOT_COMPLEX_ENOUGH; - } + } + DEBUG(5,("check_password_complexity: check password script (%s) " + "returned [%d]\n", password_script, check_ret)); + + if (check_ret != 0) { + DEBUG(1,("check_password_complexity: " + "check password script said new password is not good " + "enough!\n")); + TALLOC_FREE(password_script); + return SAMR_VALIDATION_STATUS_NOT_COMPLEX_ENOUGH; } TALLOC_FREE(password_script); @@ -2104,6 +2162,11 @@ enum samr_ValidationStatus samdb_check_password(TALLOC_CTX *mem_ctx, TALLOC_FREE(password_script); + /* + * Here are the standard AD password quality rules, which we + * run after the script. + */ + if (!check_password_quality(utf8_pw)) { return SAMR_VALIDATION_STATUS_NOT_COMPLEX_ENOUGH; } @@ -2318,7 +2381,7 @@ static NTSTATUS samdb_set_password_internal(struct ldb_context *ldb, TALLOC_CTX if (ret == LDB_ERR_CONSTRAINT_VIOLATION) { const char *errmsg = ldb_errstring(ldb); char *endptr = NULL; - WERROR werr = WERR_GENERAL_FAILURE; + WERROR werr = WERR_GEN_FAILURE; status = NT_STATUS_UNSUCCESSFUL; if (errmsg != NULL) { werr = W_ERROR(strtol(errmsg, &endptr, 16)); @@ -2339,7 +2402,7 @@ static NTSTATUS samdb_set_password_internal(struct ldb_context *ldb, TALLOC_CTX status = NT_STATUS_ACCESS_DENIED; } else if (ret != LDB_SUCCESS) { DEBUG(1, ("Failed to set password on %s: %s\n", - ldb_dn_get_linearized(msg->dn), + ldb_dn_get_linearized(user_dn), ldb_errstring(ldb))); status = NT_STATUS_UNSUCCESSFUL; } @@ -2450,12 +2513,18 @@ NTSTATUS samdb_set_password_sid(struct ldb_context *ldb, TALLOC_CTX *mem_ctx, uint32_t trust_direction; uint32_t i; const struct ldb_val *old_val = NULL; - struct trustAuthInOutBlob old_blob = {}; + struct trustAuthInOutBlob old_blob = { + .count = 0, + }; uint32_t old_version = 0; struct AuthenticationInformation *old_version_a = NULL; uint32_t _new_version = 0; - struct trustAuthInOutBlob new_blob = {}; - struct ldb_val new_val = {}; + struct trustAuthInOutBlob new_blob = { + .count = 0, + }; + struct ldb_val new_val = { + .length = 0, + }; struct timeval tv = timeval_current(); NTTIME now = timeval_to_nttime(&tv); enum ndr_err_code ndr_err; @@ -3478,6 +3547,53 @@ int samdb_rodc(struct ldb_context *sam_ctx, bool *am_rodc) return LDB_SUCCESS; } +int samdb_dns_host_name(struct ldb_context *sam_ctx, const char **host_name) +{ + const char *_host_name = NULL; + const char *attrs[] = { "dnsHostName", NULL }; + TALLOC_CTX *tmp_ctx = NULL; + int ret; + struct ldb_result *res = NULL; + + _host_name = (const char *)ldb_get_opaque(sam_ctx, "cache.dns_host_name"); + if (_host_name != NULL) { + *host_name = _host_name; + return LDB_SUCCESS; + } + + tmp_ctx = talloc_new(sam_ctx); + + ret = dsdb_search_dn(sam_ctx, tmp_ctx, &res, NULL, attrs, 0); + + if (res->count != 1 || ret != LDB_SUCCESS) { + DEBUG(0, ("Failed to get rootDSE for dnsHostName: %s", + ldb_errstring(sam_ctx))); + TALLOC_FREE(tmp_ctx); + return ret; + } + + + _host_name = ldb_msg_find_attr_as_string(res->msgs[0], + "dnsHostName", + NULL); + if (_host_name == NULL) { + DEBUG(0, ("Failed to get dnsHostName from rootDSE")); + TALLOC_FREE(tmp_ctx); + return LDB_ERR_OPERATIONS_ERROR; + } + ret = ldb_set_opaque(sam_ctx, "cache.dns_host_name", + discard_const_p(char *, _host_name)); + if (ret != LDB_SUCCESS) { + TALLOC_FREE(tmp_ctx); + return ldb_operr(sam_ctx); + } + + *host_name = talloc_steal(sam_ctx, _host_name); + + TALLOC_FREE(tmp_ctx); + return LDB_SUCCESS; +} + bool samdb_set_am_rodc(struct ldb_context *ldb, bool am_rodc) { TALLOC_CTX *tmp_ctx; @@ -3714,18 +3830,25 @@ NTSTATUS dsdb_get_extended_dn_guid(struct ldb_dn *dn, struct GUID *guid, const c NTSTATUS dsdb_get_extended_dn_uint64(struct ldb_dn *dn, uint64_t *val, const char *component_name) { const struct ldb_val *v; + int error = 0; v = ldb_dn_get_extended_component(dn, component_name); if (v == NULL) { return NT_STATUS_OBJECT_NAME_NOT_FOUND; } - { + /* Just check we don't allow the caller to fill our stack */ + if (v->length >= 64) { + return NT_STATUS_INVALID_PARAMETER; + } else { char s[v->length+1]; memcpy(s, v->data, v->length); s[v->length] = 0; - *val = strtoull(s, NULL, 0); + *val = strtoull_err(s, NULL, 0, &error); + if (error != 0) { + return NT_STATUS_INVALID_PARAMETER; + } } return NT_STATUS_OK; } @@ -3744,17 +3867,24 @@ NTSTATUS dsdb_get_extended_dn_nttime(struct ldb_dn *dn, NTTIME *nttime, const ch NTSTATUS dsdb_get_extended_dn_uint32(struct ldb_dn *dn, uint32_t *val, const char *component_name) { const struct ldb_val *v; + int error = 0; v = ldb_dn_get_extended_component(dn, component_name); if (v == NULL) { return NT_STATUS_OBJECT_NAME_NOT_FOUND; } - { + /* Just check we don't allow the caller to fill our stack */ + if (v->length >= 32) { + return NT_STATUS_INVALID_PARAMETER; + } else { char s[v->length + 1]; memcpy(s, v->data, v->length); s[v->length] = 0; - *val = strtoul(s, NULL, 0); + *val = strtoul_err(s, NULL, 0, &error); + if (error != 0) { + return NT_STATUS_INVALID_PARAMETER; + } } return NT_STATUS_OK; @@ -3790,13 +3920,13 @@ NTSTATUS dsdb_get_extended_dn_sid(struct ldb_dn *dn, struct dom_sid *sid, const */ uint32_t dsdb_dn_rmd_flags(struct ldb_dn *dn) { - const struct ldb_val *v; - char buf[32]; - v = ldb_dn_get_extended_component(dn, "RMD_FLAGS"); - if (!v || v->length > sizeof(buf)-1) return 0; - strncpy(buf, (const char *)v->data, v->length); - buf[v->length] = 0; - return strtoul(buf, NULL, 10); + uint32_t rmd_flags = 0; + NTSTATUS status = dsdb_get_extended_dn_uint32(dn, &rmd_flags, + "RMD_FLAGS"); + if (NT_STATUS_IS_OK(status)) { + return rmd_flags; + } + return 0; } /* @@ -3808,6 +3938,7 @@ uint32_t dsdb_dn_val_rmd_flags(const struct ldb_val *val) const char *p; uint32_t flags; char *end; + int error = 0; if (val->length < 13) { return 0; @@ -3816,8 +3947,8 @@ uint32_t dsdb_dn_val_rmd_flags(const struct ldb_val *val) if (!p) { return 0; } - flags = strtoul(p+11, &end, 10); - if (!end || *end != '>') { + flags = strtoul_err(p+11, &end, 10, &error); + if (!end || *end != '>' || error != 0) { /* it must end in a > */ return 0; } @@ -3912,7 +4043,7 @@ int dsdb_find_nc_root(struct ldb_context *samdb, TALLOC_CTX *mem_ctx, struct ldb if ((el == NULL) || (el->num_values < 3)) { struct ldb_message *tmp_msg; - DEBUG(5,("dsdb_find_nc_root: Finding a valid 'namingContexts' element in the RootDSE failed. Using a temporary list.")); + DEBUG(5,("dsdb_find_nc_root: Finding a valid 'namingContexts' element in the RootDSE failed. Using a temporary list.\n")); /* This generates a temporary list of NCs in order to let the * provisioning work. */ @@ -4302,9 +4433,24 @@ int dsdb_request_add_controls(struct ldb_request *req, uint32_t dsdb_flags) } } + if (dsdb_flags & DSDB_FLAG_REPLICATED_UPDATE) { + ret = ldb_request_add_control(req, DSDB_CONTROL_REPLICATED_UPDATE_OID, false, NULL); + if (ret != LDB_SUCCESS) { + return ret; + } + } + return LDB_SUCCESS; } +/* + returns true if a control with the specified "oid" exists +*/ +bool dsdb_request_has_control(struct ldb_request *req, const char *oid) +{ + return (ldb_request_get_control(req, oid) != NULL); +} + /* an add with a set of controls */ @@ -5110,12 +5256,12 @@ _PUBLIC_ NTSTATUS NS_GUID_from_string(const char *s, struct GUID *guid) return NT_STATUS_INVALID_PARAMETER; } - if (11 == sscanf(s, "%08x-%04x%04x-%02x%02x%02x%02x-%02x%02x%02x%02x", - &time_low, &time_mid, &time_hi_and_version, - &clock_seq[0], &clock_seq[1], - &node[0], &node[1], &node[2], &node[3], &node[4], &node[5])) { - status = NT_STATUS_OK; - } + status = parse_guid_string(s, + &time_low, + &time_mid, + &time_hi_and_version, + clock_seq, + node); if (!NT_STATUS_IS_OK(status)) { return status; @@ -5170,13 +5316,47 @@ static int dsdb_effective_badPwdCount(const struct ldb_message *user_msg, } } +/* + * Returns a user's PSO, or NULL if none was found + */ +static struct ldb_result *lookup_user_pso(struct ldb_context *sam_ldb, + TALLOC_CTX *mem_ctx, + const struct ldb_message *user_msg, + const char * const *attrs) +{ + struct ldb_result *res = NULL; + struct ldb_dn *pso_dn = NULL; + int ret; + + /* if the user has a PSO that applies, then use the PSO's setting */ + pso_dn = ldb_msg_find_attr_as_dn(sam_ldb, mem_ctx, user_msg, + "msDS-ResultantPSO"); + + if (pso_dn != NULL) { + + ret = dsdb_search_dn(sam_ldb, mem_ctx, &res, pso_dn, attrs, 0); + if (ret != LDB_SUCCESS) { + + /* + * log the error. The caller should fallback to using + * the default domain password settings + */ + DBG_ERR("Error retrieving msDS-ResultantPSO %s for %s", + ldb_dn_get_linearized(pso_dn), + ldb_dn_get_linearized(user_msg->dn)); + } + talloc_free(pso_dn); + } + return res; +} + /* * Return the effective badPwdCount * * This requires that the user_msg have (if present): * - badPasswordTime * - badPwdCount - * + * - msDS-ResultantPSO */ int samdb_result_effective_badPwdCount(struct ldb_context *sam_ldb, TALLOC_CTX *mem_ctx, @@ -5185,11 +5365,64 @@ int samdb_result_effective_badPwdCount(struct ldb_context *sam_ldb, { struct timeval tv_now = timeval_current(); NTTIME now = timeval_to_nttime(&tv_now); - int64_t lockOutObservationWindow = samdb_search_int64(sam_ldb, mem_ctx, 0, domain_dn, - "lockOutObservationWindow", NULL); + int64_t lockOutObservationWindow; + struct ldb_result *res = NULL; + const char *attrs[] = { "msDS-LockoutObservationWindow", + NULL }; + + res = lookup_user_pso(sam_ldb, mem_ctx, user_msg, attrs); + + if (res != NULL) { + lockOutObservationWindow = + ldb_msg_find_attr_as_int64(res->msgs[0], + "msDS-LockoutObservationWindow", + DEFAULT_OBSERVATION_WINDOW); + talloc_free(res); + } else { + + /* no PSO was found, lookup the default domain setting */ + lockOutObservationWindow = + samdb_search_int64(sam_ldb, mem_ctx, 0, domain_dn, + "lockOutObservationWindow", NULL); + } + return dsdb_effective_badPwdCount(user_msg, lockOutObservationWindow, now); } +/* + * Returns the lockoutThreshold that applies. If a PSO is specified, then that + * setting is used over the domain defaults + */ +static int64_t get_lockout_threshold(struct ldb_message *domain_msg, + struct ldb_message *pso_msg) +{ + if (pso_msg != NULL) { + return ldb_msg_find_attr_as_int(pso_msg, + "msDS-LockoutThreshold", 0); + } else { + return ldb_msg_find_attr_as_int(domain_msg, + "lockoutThreshold", 0); + } +} + +/* + * Returns the lockOutObservationWindow that applies. If a PSO is specified, + * then that setting is used over the domain defaults + */ +static int64_t get_lockout_observation_window(struct ldb_message *domain_msg, + struct ldb_message *pso_msg) +{ + if (pso_msg != NULL) { + return ldb_msg_find_attr_as_int64(pso_msg, + "msDS-LockoutObservationWindow", + DEFAULT_OBSERVATION_WINDOW); + } else { + return ldb_msg_find_attr_as_int64(domain_msg, + "lockOutObservationWindow", + DEFAULT_OBSERVATION_WINDOW); + } +} + /* * Prepare an update to the badPwdCount and associated attributes. * @@ -5202,11 +5435,16 @@ int samdb_result_effective_badPwdCount(struct ldb_context *sam_ldb, * - pwdProperties * - lockoutThreshold * - lockOutObservationWindow + * + * This also requires that the pso_msg have (if present): + * - msDS-LockoutThreshold + * - msDS-LockoutObservationWindow */ NTSTATUS dsdb_update_bad_pwd_count(TALLOC_CTX *mem_ctx, struct ldb_context *sam_ctx, struct ldb_message *user_msg, struct ldb_message *domain_msg, + struct ldb_message *pso_msg, struct ldb_message **_mod_msg) { int i, ret, badPwdCount; @@ -5239,8 +5477,7 @@ NTSTATUS dsdb_update_bad_pwd_count(TALLOC_CTX *mem_ctx, * Also, the built in administrator account is exempt: * http://msdn.microsoft.com/en-us/library/windows/desktop/aa375371%28v=vs.85%29.aspx */ - lockoutThreshold = ldb_msg_find_attr_as_int(domain_msg, - "lockoutThreshold", 0); + lockoutThreshold = get_lockout_threshold(domain_msg, pso_msg); if (lockoutThreshold == 0 || (rid == DOMAIN_RID_ADMINISTRATOR)) { DEBUG(5, ("Not updating badPwdCount on %s after wrong password\n", ldb_dn_get_linearized(user_msg->dn))); @@ -5257,8 +5494,8 @@ NTSTATUS dsdb_update_bad_pwd_count(TALLOC_CTX *mem_ctx, return NT_STATUS_NO_MEMORY; } - lockOutObservationWindow = ldb_msg_find_attr_as_int64(domain_msg, - "lockOutObservationWindow", 0); + lockOutObservationWindow = get_lockout_observation_window(domain_msg, + pso_msg); badPwdCount = dsdb_effective_badPwdCount(user_msg, lockOutObservationWindow, now); @@ -5281,10 +5518,10 @@ NTSTATUS dsdb_update_bad_pwd_count(TALLOC_CTX *mem_ctx, TALLOC_FREE(mod_msg); return NT_STATUS_NO_MEMORY; } - DEBUG(5, ("Locked out user %s after %d wrong passwords\n", + DEBUGC( DBGC_AUTH, 1, ("Locked out user %s after %d wrong passwords\n", ldb_dn_get_linearized(user_msg->dn), badPwdCount)); } else { - DEBUG(5, ("Updated badPwdCount on %s after %d wrong passwords\n", + DEBUGC( DBGC_AUTH, 5, ("Updated badPwdCount on %s after %d wrong passwords\n", ldb_dn_get_linearized(user_msg->dn), badPwdCount)); } @@ -5483,3 +5720,222 @@ int dsdb_user_obj_set_primary_group_id(struct ldb_context *ldb, struct ldb_messa return LDB_SUCCESS; } + +/** + * Returns True if the source and target DNs both have the same naming context, + * i.e. they're both in the same partition. + */ +bool dsdb_objects_have_same_nc(struct ldb_context *ldb, + TALLOC_CTX *mem_ctx, + struct ldb_dn *source_dn, + struct ldb_dn *target_dn) +{ + TALLOC_CTX *tmp_ctx; + struct ldb_dn *source_nc; + struct ldb_dn *target_nc; + int ret; + bool same_nc = true; + + tmp_ctx = talloc_new(mem_ctx); + + ret = dsdb_find_nc_root(ldb, tmp_ctx, source_dn, &source_nc); + if (ret != LDB_SUCCESS) { + DBG_ERR("Failed to find base DN for source %s\n", + ldb_dn_get_linearized(source_dn)); + talloc_free(tmp_ctx); + return true; + } + + ret = dsdb_find_nc_root(ldb, tmp_ctx, target_dn, &target_nc); + if (ret != LDB_SUCCESS) { + DBG_ERR("Failed to find base DN for target %s\n", + ldb_dn_get_linearized(target_dn)); + talloc_free(tmp_ctx); + return true; + } + + same_nc = (ldb_dn_compare(source_nc, target_nc) == 0); + + talloc_free(tmp_ctx); + + return same_nc; +} +/* + * Context for dsdb_count_domain_callback + */ +struct dsdb_count_domain_context { + /* + * Number of matching records + */ + size_t count; + /* + * sid of the domain that the records must belong to. + * if NULL records can belong to any domain. + */ + struct dom_sid *dom_sid; +}; + +/* + * @brief ldb aysnc callback for dsdb_domain_count. + * + * count the number of records in the database matching an LDAP query, + * optionally filtering for domain membership. + * + * @param [in,out] req the ldb request being processed + * req->context contains: + * count The number of matching records + * dom_sid The domain sid, if present records must belong + * to the domain to be counted. + *@param [in,out] ares The query result. + * + * @return an LDB error code + * + */ +static int dsdb_count_domain_callback( + struct ldb_request *req, + struct ldb_reply *ares) +{ + + if (ares == NULL) { + return ldb_request_done(req, LDB_ERR_OPERATIONS_ERROR); + } + if (ares->error != LDB_SUCCESS) { + int error = ares->error; + TALLOC_FREE(ares); + return ldb_request_done(req, error); + } + + switch (ares->type) { + case LDB_REPLY_ENTRY: + { + struct dsdb_count_domain_context *context = NULL; + struct sid_parse_ret ret; + bool in_domain; + struct dom_sid sid; + const struct ldb_val *v; + + context = req->context; + if (context->dom_sid == NULL) { + context->count++; + break; + } + + v = ldb_msg_find_ldb_val(ares->message, "objectSid"); + if (v == NULL) { + break; + } + + ret = sid_parse(v->data, v->length, &sid); + if (ret.len == -1) { + break; + } + + in_domain = dom_sid_in_domain(context->dom_sid, &sid); + if (!in_domain) { + break; + } + + context->count++; + break; + } + case LDB_REPLY_REFERRAL: + break; + + case LDB_REPLY_DONE: + TALLOC_FREE(ares); + return ldb_request_done(req, LDB_SUCCESS); + } + + TALLOC_FREE(ares); + + return LDB_SUCCESS; +} + +/* + * @brief Count the number of records matching a query. + * + * Count the number of entries in the database matching the supplied query, + * optionally filtering only those entries belonging to the supplied domain. + * + * @param ldb [in] Current ldb context + * @param count [out] Pointer to the count + * @param base [in] The base dn for the quey + * @param dom_sid [in] The domain sid, if non NULL records that are not a member + * of the domain are ignored. + * @param scope [in] Search scope. + * @param exp_fmt [in] format string for the query. + * + * @return LDB_STATUS code. + */ +int dsdb_domain_count( + struct ldb_context *ldb, + size_t *count, + struct ldb_dn *base, + struct dom_sid *dom_sid, + enum ldb_scope scope, + const char *exp_fmt, ...) +{ + TALLOC_CTX *tmp_ctx = NULL; + struct ldb_request *req = NULL; + struct dsdb_count_domain_context *context = NULL; + char *expression = NULL; + const char *object_sid[] = {"objectSid", NULL}; + const char *none[] = {NULL}; + va_list ap; + int ret; + + *count = 0; + tmp_ctx = talloc_new(ldb); + + context = talloc_zero(tmp_ctx, struct dsdb_count_domain_context); + if (context == NULL) { + return LDB_ERR_OPERATIONS_ERROR; + } + context->dom_sid = dom_sid; + + if (exp_fmt) { + va_start(ap, exp_fmt); + expression = talloc_vasprintf(tmp_ctx, exp_fmt, ap); + va_end(ap); + + if (expression == NULL) { + TALLOC_FREE(context); + TALLOC_FREE(tmp_ctx); + return LDB_ERR_OPERATIONS_ERROR; + } + } + + ret = ldb_build_search_req( + &req, + ldb, + tmp_ctx, + base, + scope, + expression, + (dom_sid == NULL) ? none : object_sid, + NULL, + context, + dsdb_count_domain_callback, + NULL); + ldb_req_set_location(req, "dsdb_domain_count"); + + if (ret != LDB_SUCCESS) goto done; + + ret = ldb_request(ldb, req); + + if (ret == LDB_SUCCESS) { + ret = ldb_wait(req->handle, LDB_WAIT_ALL); + if (ret == LDB_SUCCESS) { + *count = context->count; + } + } + + +done: + TALLOC_FREE(expression); + TALLOC_FREE(req); + TALLOC_FREE(context); + TALLOC_FREE(tmp_ctx); + + return ret; +}