#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
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
*/
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);
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;
}
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;
}
/*
- 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);
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) {
*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;
}
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
*/
/*
* 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
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,
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;
}
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");
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 {
*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;
}
*/
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)
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,
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);
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);
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;
}
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));
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;
}
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;
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;
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;
}
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;
*/
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;
}
/*
const char *p;
uint32_t flags;
char *end;
+ int error = 0;
if (val->length < 13) {
return 0;
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;
}
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. */
}
}
+ 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
*/
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;
}
}
+/*
+ * 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,
{
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.
*
* - 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;
* 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)));
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);
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));
}
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;
+}