#include "libcli/auth/libcli_auth.h"
#include "librpc/gen_ndr/ndr_drsblobs.h"
#include "system/locale.h"
+#include "system/filesys.h"
#include "lib/util/tsort.h"
#include "dsdb/common/util.h"
#include "lib/socket/socket.h"
#include "librpc/gen_ndr/irpc.h"
#include "libds/common/flag_mapping.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_blob_parse(*v, sid);
- if (!ok) {
+ ret = sid_parse(v->data, v->length, sid);
+ if (ret.len == -1) {
talloc_free(sid);
return NULL;
}
return attr_time;
}
-/*
- construct the force_password_change field from the PwdLastSet
- attribute, the userAccountControl and the domain password settings
-*/
-NTTIME samdb_result_force_password_change(struct ldb_context *sam_ldb,
- TALLOC_CTX *mem_ctx,
- struct ldb_dn *domain_dn,
- struct ldb_message *msg)
-{
- int64_t attr_time = ldb_msg_find_attr_as_int64(msg, "pwdLastSet", 0);
- uint32_t userAccountControl = ldb_msg_find_attr_as_uint(msg,
- "userAccountControl",
- 0);
- int64_t maxPwdAge;
-
- /* Machine accounts don't expire, and there is a flag for 'no expiry' */
- if (!(userAccountControl & UF_NORMAL_ACCOUNT)
- || (userAccountControl & UF_DONT_EXPIRE_PASSWD)) {
- return 0x7FFFFFFFFFFFFFFFULL;
- }
-
- if (attr_time == 0) {
- return 0;
- }
- if (attr_time == -1) {
- return 0x7FFFFFFFFFFFFFFFULL;
- }
-
- maxPwdAge = samdb_search_int64(sam_ldb, mem_ctx, 0, domain_dn,
- "maxPwdAge", NULL);
- if (maxPwdAge == 0 || maxPwdAge == -0x8000000000000000ULL) {
- return 0x7FFFFFFFFFFFFFFFULL;
- } else {
- attr_time -= maxPwdAge;
- }
-
- return attr_time;
-}
-
/*
pull a samr_Password structutre from a result set.
*/
return count;
}
+NTSTATUS samdb_result_passwords_from_history(TALLOC_CTX *mem_ctx,
+ struct loadparm_context *lp_ctx,
+ struct ldb_message *msg,
+ unsigned int idx,
+ struct samr_Password **lm_pwd,
+ struct samr_Password **nt_pwd)
+{
+ struct samr_Password *lmPwdHash, *ntPwdHash;
+
+ if (nt_pwd) {
+ unsigned int num_nt;
+ num_nt = samdb_result_hashes(mem_ctx, msg, "ntPwdHistory", &ntPwdHash);
+ if (num_nt <= idx) {
+ *nt_pwd = NULL;
+ } else {
+ *nt_pwd = &ntPwdHash[idx];
+ }
+ }
+ if (lm_pwd) {
+ /* Ensure that if we have turned off LM
+ * authentication, that we never use the LM hash, even
+ * if we store it */
+ if (lpcfg_lanman_auth(lp_ctx)) {
+ unsigned int num_lm;
+ num_lm = samdb_result_hashes(mem_ctx, msg, "lmPwdHistory", &lmPwdHash);
+ if (num_lm <= idx) {
+ *lm_pwd = NULL;
+ } else {
+ *lm_pwd = &lmPwdHash[idx];
+ }
+ } else {
+ *lm_pwd = NULL;
+ }
+ }
+ return NT_STATUS_OK;
+}
+
NTSTATUS samdb_result_passwords_no_lockout(TALLOC_CTX *mem_ctx,
struct loadparm_context *lp_ctx,
- struct ldb_message *msg,
+ const struct ldb_message *msg,
struct samr_Password **lm_pwd,
struct samr_Password **nt_pwd)
{
NTSTATUS samdb_result_passwords(TALLOC_CTX *mem_ctx,
struct loadparm_context *lp_ctx,
- struct ldb_message *msg,
+ const struct ldb_message *msg,
struct samr_Password **lm_pwd,
struct samr_Password **nt_pwd)
{
(if not null) the attributes 'attr' be already
included in msg
*/
-uint32_t samdb_result_acct_flags(struct ldb_message *msg, const char *attr)
+uint32_t samdb_result_acct_flags(const struct ldb_message *msg, const char *attr)
{
uint32_t userAccountControl = ldb_msg_find_attr_as_uint(msg, "userAccountControl", 0);
uint32_t attr_flags = 0;
return acct_flags;
}
-struct lsa_BinaryString samdb_result_parameters(TALLOC_CTX *mem_ctx,
- struct ldb_message *msg,
- const char *attr)
+NTSTATUS samdb_result_parameters(TALLOC_CTX *mem_ctx,
+ struct ldb_message *msg,
+ const char *attr,
+ struct lsa_BinaryString *s)
{
- struct lsa_BinaryString s;
+ int i;
const struct ldb_val *val = ldb_msg_find_ldb_val(msg, attr);
- ZERO_STRUCT(s);
+ ZERO_STRUCTP(s);
if (!val) {
- return s;
+ return NT_STATUS_OK;
}
- s.array = talloc_array(mem_ctx, uint16_t, val->length/2);
- if (!s.array) {
- return s;
+ if ((val->length % 2) != 0) {
+ /*
+ * If the on-disk data is not even in length, we know
+ * it is corrupt, and can not be safely pushed. We
+ * would either truncate, send either a un-initilaised
+ * byte or send a forced zero byte
+ */
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+
+ s->array = talloc_array(mem_ctx, uint16_t, val->length/2);
+ if (!s->array) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ s->length = s->size = val->length;
+
+ /* The on-disk format is the 'network' format, being UTF16LE (sort of) */
+ for (i = 0; i < s->length / 2; i++) {
+ s->array[i] = SVAL(val->data, i * 2);
}
- s.length = s.size = val->length;
- memcpy(s.array, val->data, val->length);
- return s;
+ return NT_STATUS_OK;
}
/* Find an attribute, with a particular value */
return NULL;
}
-int samdb_find_or_add_attribute(struct ldb_context *ldb, struct ldb_message *msg, const char *name, const char *set_value)
+static int samdb_find_or_add_attribute_ex(struct ldb_context *ldb,
+ struct ldb_message *msg,
+ const char *name,
+ const char *set_value,
+ unsigned attr_flags,
+ bool *added)
{
+ int ret;
struct ldb_message_element *el;
+ SMB_ASSERT(attr_flags != 0);
+
el = ldb_msg_find_element(msg, name);
if (el) {
+ if (added != NULL) {
+ *added = false;
+ }
+
return LDB_SUCCESS;
}
- return ldb_msg_add_string(msg, name, set_value);
+ ret = ldb_msg_add_empty(msg, name,
+ attr_flags,
+ &el);
+ if (ret != LDB_SUCCESS) {
+ return ret;
+ }
+
+ if (set_value != NULL) {
+ ret = ldb_msg_add_string(msg, name, set_value);
+ if (ret != LDB_SUCCESS) {
+ return ret;
+ }
+ }
+
+ if (added != NULL) {
+ *added = true;
+ }
+ return LDB_SUCCESS;
+}
+
+int samdb_find_or_add_attribute(struct ldb_context *ldb, struct ldb_message *msg, const char *name, const char *set_value)
+{
+ return samdb_find_or_add_attribute_ex(ldb, msg, name, set_value, LDB_FLAG_MOD_ADD, NULL);
}
/*
int samdb_msg_add_parameters(struct ldb_context *sam_ldb, TALLOC_CTX *mem_ctx, struct ldb_message *msg,
const char *attr_name, struct lsa_BinaryString *parameters)
{
+ int i;
struct ldb_val val;
+ if ((parameters->length % 2) != 0) {
+ return LDB_ERR_INVALID_ATTRIBUTE_SYNTAX;
+ }
+
+ val.data = talloc_array(mem_ctx, uint8_t, parameters->length);
+ if (val.data == NULL) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
val.length = parameters->length;
- val.data = (uint8_t *)parameters->array;
- return ldb_msg_add_value(msg, attr_name, &val, NULL);
+ for (i = 0; i < parameters->length / 2; i++) {
+ /*
+ * The on-disk format needs to be in the 'network'
+ * format, parmeters->array is a uint16_t array of
+ * length parameters->length / 2
+ */
+ SSVAL(val.data, i * 2, parameters->array[i]);
+ }
+ return ldb_msg_add_steal_value(msg, attr_name, &val);
}
/*
/*
* Handle ldb_request in transaction
*/
-static int dsdb_autotransaction_request(struct ldb_context *sam_ldb,
- struct ldb_request *req)
+int dsdb_autotransaction_request(struct ldb_context *sam_ldb,
+ struct ldb_request *req)
{
int ret;
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
*/
server_dn = samdb_server_dn(ldb, mem_ctx);
if (server_dn == NULL) {
- return LDB_ERR_NO_SUCH_OBJECT;
+ return ldb_error(ldb, LDB_ERR_NO_SUCH_OBJECT, __func__);
}
ret = samdb_reference_dn(ldb, mem_ctx, server_dn, "serverReference", dn);
/*
* 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;
}
return ret;
}
+static void pwd_timeout_debug(struct tevent_context *unused1,
+ struct tevent_timer *unused2,
+ struct timeval unused3,
+ void *unused4)
+{
+ DEBUG(0, ("WARNING: check_password_complexity: password script "
+ "took more than 1 second to run\n"));
+}
+
/*
* Performs checks on a user password (plaintext UNIX format - attribute
*
* Result codes from "enum samr_ValidationStatus" (consider "samr.idl")
*/
-enum samr_ValidationStatus samdb_check_password(const DATA_BLOB *utf8_blob,
+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)
{
const char *utf8_pw = (const char *)utf8_blob->data;
size_t utf8_len = strlen_m(utf8_pw);
+ char *password_script = NULL;
/* checks if the "minPwdLength" property is satisfied */
if (minPwdLength > utf8_len) {
return SAMR_VALIDATION_STATUS_NOT_COMPLEX_ENOUGH;
}
- if (!check_password_quality(utf8_pw)) {
- return SAMR_VALIDATION_STATUS_NOT_COMPLEX_ENOUGH;
- }
+ password_script = lpcfg_check_password_script(lp_ctx, mem_ctx);
+ if (password_script != NULL && *password_script != '\0') {
+ int check_ret = 0;
+ int error = 0;
+ struct tevent_context *event_ctx = NULL;
+ struct tevent_req *req = NULL;
+ int cps_stdin = -1;
+ const char * const cmd[4] = {
+ "/bin/sh", "-c",
+ password_script,
+ NULL
+ };
+
+ event_ctx = tevent_context_init(mem_ctx);
+ if (event_ctx == NULL) {
+ TALLOC_FREE(password_script);
+ return SAMR_VALIDATION_STATUS_PASSWORD_FILTER_ERROR;
+ }
- return SAMR_VALIDATION_STATUS_SUCCESS;
-}
+ /* Gives a warning after 1 second, terminates after 10 */
+ tevent_add_timer(event_ctx, event_ctx,
+ tevent_timeval_current_ofs(1, 0),
+ pwd_timeout_debug, NULL);
-/*
- * Callback for "samdb_set_password" password change
+ 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);
+ 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(cps_stdin);
+ cps_stdin = -1;
+
+ if (!tevent_req_poll(req, event_ctx)) {
+ TALLOC_FREE(password_script);
+ TALLOC_FREE(event_ctx);
+ return SAMR_VALIDATION_STATUS_PASSWORD_FILTER_ERROR;
+ }
+
+ check_ret = samba_runcmd_recv(req, &error);
+ TALLOC_FREE(event_ctx);
+
+ if (error == ETIMEDOUT) {
+ DEBUG(0, ("check_password_complexity: check password script took too long!\n"));
+ TALLOC_FREE(password_script);
+ return SAMR_VALIDATION_STATUS_PASSWORD_FILTER_ERROR;
+ }
+ 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);
+ return SAMR_VALIDATION_STATUS_SUCCESS;
+ }
+
+ 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;
+ }
+
+ return SAMR_VALIDATION_STATUS_SUCCESS;
+}
+
+/*
+ * Callback for "samdb_set_password" password change
*/
int samdb_set_password_callback(struct ldb_request *req, struct ldb_reply *ares)
{
* Results: NT_STATUS_OK, NT_STATUS_INVALID_PARAMETER, NT_STATUS_UNSUCCESSFUL,
* NT_STATUS_WRONG_PASSWORD, NT_STATUS_PASSWORD_RESTRICTION
*/
-NTSTATUS samdb_set_password(struct ldb_context *ldb, TALLOC_CTX *mem_ctx,
+static NTSTATUS samdb_set_password_internal(struct ldb_context *ldb, TALLOC_CTX *mem_ctx,
struct ldb_dn *user_dn, struct ldb_dn *domain_dn,
const DATA_BLOB *new_password,
const struct samr_Password *lmNewHash,
const struct samr_Password *lmOldHash,
const struct samr_Password *ntOldHash,
enum samPwdChangeReason *reject_reason,
- struct samr_DomInfo1 **_dominfo)
+ struct samr_DomInfo1 **_dominfo,
+ bool permit_interdomain_trust)
{
struct ldb_message *msg;
struct ldb_message_element *el;
return NT_STATUS_NO_MEMORY;
}
}
+ if (permit_interdomain_trust) {
+ ret = ldb_request_add_control(req,
+ DSDB_CONTROL_PERMIT_INTERDOMAIN_TRUST_UAC_OID,
+ false, NULL);
+ if (ret != LDB_SUCCESS) {
+ talloc_free(req);
+ talloc_free(msg);
+ return NT_STATUS_NO_MEMORY;
+ }
+ }
ret = ldb_request_add_control(req,
DSDB_CONTROL_PASSWORD_CHANGE_STATUS_OID,
true, NULL);
ret = dsdb_autotransaction_request(ldb, req);
if (req->context != NULL) {
- pwd_stat = talloc_steal(mem_ctx,
- ((struct ldb_control *)req->context)->data);
+ struct ldb_control *control = talloc_get_type_abort(req->context,
+ struct ldb_control);
+ pwd_stat = talloc_get_type_abort(control->data,
+ struct dsdb_control_password_change_status);
+ talloc_steal(mem_ctx, pwd_stat);
}
talloc_free(req);
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));
+ DBG_WARNING("%s\n", errmsg);
}
if (endptr != errmsg) {
if (W_ERROR_EQUAL(werr, WERR_INVALID_PASSWORD)) {
} else if (ret == LDB_ERR_NO_SUCH_OBJECT) {
/* don't let the caller know if an account doesn't exist */
status = NT_STATUS_WRONG_PASSWORD;
+ } else if (ret == LDB_ERR_INSUFFICIENT_ACCESS_RIGHTS) {
+ status = NT_STATUS_ACCESS_DENIED;
} else if (ret != LDB_SUCCESS) {
+ DEBUG(1, ("Failed to set password on %s: %s\n",
+ ldb_dn_get_linearized(user_dn),
+ ldb_errstring(ldb)));
status = NT_STATUS_UNSUCCESSFUL;
}
return status;
}
+NTSTATUS samdb_set_password(struct ldb_context *ldb, TALLOC_CTX *mem_ctx,
+ struct ldb_dn *user_dn, struct ldb_dn *domain_dn,
+ const DATA_BLOB *new_password,
+ const struct samr_Password *lmNewHash,
+ const struct samr_Password *ntNewHash,
+ const struct samr_Password *lmOldHash,
+ const struct samr_Password *ntOldHash,
+ enum samPwdChangeReason *reject_reason,
+ struct samr_DomInfo1 **_dominfo)
+{
+ return samdb_set_password_internal(ldb, mem_ctx,
+ user_dn, domain_dn,
+ new_password,
+ lmNewHash, ntNewHash,
+ lmOldHash, ntOldHash,
+ reject_reason, _dominfo,
+ false); /* reject trusts */
+}
+
/*
* Sets the user password using plaintext UTF16 (attribute "new_password") or
* LM (attribute "lmNewHash") or NT (attribute "ntNewHash") hash. Also pass
*/
NTSTATUS samdb_set_password_sid(struct ldb_context *ldb, TALLOC_CTX *mem_ctx,
const struct dom_sid *user_sid,
+ const uint32_t *new_version, /* optional for trusts */
const DATA_BLOB *new_password,
const struct samr_Password *lmNewHash,
const struct samr_Password *ntNewHash,
enum samPwdChangeReason *reject_reason,
struct samr_DomInfo1 **_dominfo)
{
+ TALLOC_CTX *frame = talloc_stackframe();
NTSTATUS nt_status;
- struct ldb_dn *user_dn;
+ const char * const user_attrs[] = {
+ "userAccountControl",
+ "sAMAccountName",
+ NULL
+ };
+ struct ldb_message *user_msg = NULL;
int ret;
+ uint32_t uac = 0;
ret = ldb_transaction_start(ldb);
if (ret != LDB_SUCCESS) {
DEBUG(1, ("Failed to start transaction: %s\n", ldb_errstring(ldb)));
+ TALLOC_FREE(frame);
return NT_STATUS_TRANSACTION_ABORTED;
}
- user_dn = samdb_search_dn(ldb, mem_ctx, NULL,
- "(&(objectSid=%s)(objectClass=user))",
- ldap_encode_ndr_dom_sid(mem_ctx, user_sid));
- if (!user_dn) {
+ ret = dsdb_search_one(ldb, frame, &user_msg, ldb_get_default_basedn(ldb),
+ LDB_SCOPE_SUBTREE, user_attrs, 0,
+ "(&(objectSid=%s)(objectClass=user))",
+ ldap_encode_ndr_dom_sid(frame, user_sid));
+ if (ret != LDB_SUCCESS) {
+ ldb_transaction_cancel(ldb);
+ DEBUG(3, ("samdb_set_password_sid: SID[%s] not found in samdb %s - %s, "
+ "returning NO_SUCH_USER\n",
+ dom_sid_string(frame, user_sid),
+ ldb_strerror(ret), ldb_errstring(ldb)));
+ TALLOC_FREE(frame);
+ return NT_STATUS_NO_SUCH_USER;
+ }
+
+ uac = ldb_msg_find_attr_as_uint(user_msg, "userAccountControl", 0);
+ if (!(uac & UF_ACCOUNT_TYPE_MASK)) {
ldb_transaction_cancel(ldb);
- DEBUG(3, ("samdb_set_password_sid: SID %s not found in samdb, returning NO_SUCH_USER\n",
- dom_sid_string(mem_ctx, user_sid)));
+ DEBUG(1, ("samdb_set_password_sid: invalid "
+ "userAccountControl[0x%08X] for SID[%s] DN[%s], "
+ "returning NO_SUCH_USER\n",
+ (unsigned)uac, dom_sid_string(frame, user_sid),
+ ldb_dn_get_linearized(user_msg->dn)));
+ TALLOC_FREE(frame);
return NT_STATUS_NO_SUCH_USER;
}
- nt_status = samdb_set_password(ldb, mem_ctx,
- user_dn, NULL,
- new_password,
- lmNewHash, ntNewHash,
- lmOldHash, ntOldHash,
- reject_reason, _dominfo);
+ if (uac & UF_INTERDOMAIN_TRUST_ACCOUNT) {
+ const char * const tdo_attrs[] = {
+ "trustAuthIncoming",
+ "trustDirection",
+ NULL
+ };
+ struct ldb_message *tdo_msg = NULL;
+ const char *account_name = NULL;
+ uint32_t trust_direction;
+ uint32_t i;
+ const struct ldb_val *old_val = NULL;
+ 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 = {
+ .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;
+
+ if (new_password == NULL && ntNewHash == NULL) {
+ ldb_transaction_cancel(ldb);
+ DEBUG(1, ("samdb_set_password_sid: "
+ "no new password provided "
+ "sAMAccountName for SID[%s] DN[%s], "
+ "returning INVALID_PARAMETER\n",
+ dom_sid_string(frame, user_sid),
+ ldb_dn_get_linearized(user_msg->dn)));
+ TALLOC_FREE(frame);
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ if (new_password != NULL && ntNewHash != NULL) {
+ ldb_transaction_cancel(ldb);
+ DEBUG(1, ("samdb_set_password_sid: "
+ "two new passwords provided "
+ "sAMAccountName for SID[%s] DN[%s], "
+ "returning INVALID_PARAMETER\n",
+ dom_sid_string(frame, user_sid),
+ ldb_dn_get_linearized(user_msg->dn)));
+ TALLOC_FREE(frame);
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ if (new_password != NULL && (new_password->length % 2)) {
+ ldb_transaction_cancel(ldb);
+ DEBUG(2, ("samdb_set_password_sid: "
+ "invalid utf16 length (%zu) "
+ "sAMAccountName for SID[%s] DN[%s], "
+ "returning WRONG_PASSWORD\n",
+ new_password->length,
+ dom_sid_string(frame, user_sid),
+ ldb_dn_get_linearized(user_msg->dn)));
+ TALLOC_FREE(frame);
+ return NT_STATUS_WRONG_PASSWORD;
+ }
+
+ if (new_password != NULL && new_password->length >= 500) {
+ ldb_transaction_cancel(ldb);
+ DEBUG(2, ("samdb_set_password_sid: "
+ "utf16 password too long (%zu) "
+ "sAMAccountName for SID[%s] DN[%s], "
+ "returning WRONG_PASSWORD\n",
+ new_password->length,
+ dom_sid_string(frame, user_sid),
+ ldb_dn_get_linearized(user_msg->dn)));
+ TALLOC_FREE(frame);
+ return NT_STATUS_WRONG_PASSWORD;
+ }
+
+ account_name = ldb_msg_find_attr_as_string(user_msg,
+ "sAMAccountName", NULL);
+ if (account_name == NULL) {
+ ldb_transaction_cancel(ldb);
+ DEBUG(1, ("samdb_set_password_sid: missing "
+ "sAMAccountName for SID[%s] DN[%s], "
+ "returning NO_SUCH_USER\n",
+ dom_sid_string(frame, user_sid),
+ ldb_dn_get_linearized(user_msg->dn)));
+ TALLOC_FREE(frame);
+ return NT_STATUS_NO_SUCH_USER;
+ }
+
+ nt_status = dsdb_trust_search_tdo_by_type(ldb,
+ SEC_CHAN_DOMAIN,
+ account_name,
+ tdo_attrs,
+ frame, &tdo_msg);
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ ldb_transaction_cancel(ldb);
+ DEBUG(1, ("samdb_set_password_sid: dsdb_trust_search_tdo "
+ "failed(%s) for sAMAccountName[%s] SID[%s] DN[%s], "
+ "returning INTERNAL_DB_CORRUPTION\n",
+ nt_errstr(nt_status), account_name,
+ dom_sid_string(frame, user_sid),
+ ldb_dn_get_linearized(user_msg->dn)));
+ TALLOC_FREE(frame);
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+
+ trust_direction = ldb_msg_find_attr_as_int(tdo_msg,
+ "trustDirection", 0);
+ if (!(trust_direction & LSA_TRUST_DIRECTION_INBOUND)) {
+ ldb_transaction_cancel(ldb);
+ DEBUG(1, ("samdb_set_password_sid: direction[0x%08X] is "
+ "not inbound for sAMAccountName[%s] "
+ "DN[%s] TDO[%s], "
+ "returning INTERNAL_DB_CORRUPTION\n",
+ (unsigned)trust_direction,
+ account_name,
+ ldb_dn_get_linearized(user_msg->dn),
+ ldb_dn_get_linearized(tdo_msg->dn)));
+ TALLOC_FREE(frame);
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+
+ old_val = ldb_msg_find_ldb_val(tdo_msg, "trustAuthIncoming");
+ if (old_val != NULL) {
+ ndr_err = ndr_pull_struct_blob(old_val, frame, &old_blob,
+ (ndr_pull_flags_fn_t)ndr_pull_trustAuthInOutBlob);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ ldb_transaction_cancel(ldb);
+ DEBUG(1, ("samdb_set_password_sid: "
+ "failed(%s) to parse "
+ "trustAuthOutgoing sAMAccountName[%s] "
+ "DN[%s] TDO[%s], "
+ "returning INTERNAL_DB_CORRUPTION\n",
+ ndr_map_error2string(ndr_err),
+ account_name,
+ ldb_dn_get_linearized(user_msg->dn),
+ ldb_dn_get_linearized(tdo_msg->dn)));
+
+ TALLOC_FREE(frame);
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+ }
+
+ for (i = old_blob.current.count; i > 0; i--) {
+ struct AuthenticationInformation *a =
+ &old_blob.current.array[i - 1];
+
+ switch (a->AuthType) {
+ case TRUST_AUTH_TYPE_NONE:
+ if (i == old_blob.current.count) {
+ /*
+ * remove TRUST_AUTH_TYPE_NONE at the
+ * end
+ */
+ old_blob.current.count--;
+ }
+ break;
+
+ case TRUST_AUTH_TYPE_VERSION:
+ old_version_a = a;
+ old_version = a->AuthInfo.version.version;
+ break;
+
+ case TRUST_AUTH_TYPE_CLEAR:
+ break;
+
+ case TRUST_AUTH_TYPE_NT4OWF:
+ break;
+ }
+ }
+
+ if (new_version == NULL) {
+ _new_version = 0;
+ new_version = &_new_version;
+ }
+
+ if (old_version_a != NULL && *new_version != (old_version + 1)) {
+ old_version_a->LastUpdateTime = now;
+ old_version_a->AuthType = TRUST_AUTH_TYPE_NONE;
+ }
+
+ new_blob.count = MAX(old_blob.current.count, 2);
+ new_blob.current.array = talloc_zero_array(frame,
+ struct AuthenticationInformation,
+ new_blob.count);
+ if (new_blob.current.array == NULL) {
+ ldb_transaction_cancel(ldb);
+ TALLOC_FREE(frame);
+ return NT_STATUS_NO_MEMORY;
+ }
+ new_blob.previous.array = talloc_zero_array(frame,
+ struct AuthenticationInformation,
+ new_blob.count);
+ if (new_blob.current.array == NULL) {
+ ldb_transaction_cancel(ldb);
+ TALLOC_FREE(frame);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ for (i = 0; i < old_blob.current.count; i++) {
+ struct AuthenticationInformation *o =
+ &old_blob.current.array[i];
+ struct AuthenticationInformation *p =
+ &new_blob.previous.array[i];
+
+ *p = *o;
+ new_blob.previous.count++;
+ }
+ for (; i < new_blob.count; i++) {
+ struct AuthenticationInformation *pi =
+ &new_blob.previous.array[i];
+
+ if (i == 0) {
+ /*
+ * new_blob.previous is still empty so
+ * we'll do new_blob.previous = new_blob.current
+ * below.
+ */
+ break;
+ }
+
+ pi->LastUpdateTime = now;
+ pi->AuthType = TRUST_AUTH_TYPE_NONE;
+ new_blob.previous.count++;
+ }
+
+ for (i = 0; i < new_blob.count; i++) {
+ struct AuthenticationInformation *ci =
+ &new_blob.current.array[i];
+
+ ci->LastUpdateTime = now;
+ switch (i) {
+ case 0:
+ if (ntNewHash != NULL) {
+ ci->AuthType = TRUST_AUTH_TYPE_NT4OWF;
+ ci->AuthInfo.nt4owf.password = *ntNewHash;
+ break;
+ }
+
+ ci->AuthType = TRUST_AUTH_TYPE_CLEAR;
+ ci->AuthInfo.clear.size = new_password->length;
+ ci->AuthInfo.clear.password = new_password->data;
+ break;
+ case 1:
+ ci->AuthType = TRUST_AUTH_TYPE_VERSION;
+ ci->AuthInfo.version.version = *new_version;
+ break;
+ default:
+ ci->AuthType = TRUST_AUTH_TYPE_NONE;
+ break;
+ }
+
+ new_blob.current.count++;
+ }
+
+ if (new_blob.previous.count == 0) {
+ TALLOC_FREE(new_blob.previous.array);
+ new_blob.previous = new_blob.current;
+ }
+
+ ndr_err = ndr_push_struct_blob(&new_val, frame, &new_blob,
+ (ndr_push_flags_fn_t)ndr_push_trustAuthInOutBlob);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ ldb_transaction_cancel(ldb);
+ DEBUG(1, ("samdb_set_password_sid: "
+ "failed(%s) to generate "
+ "trustAuthOutgoing sAMAccountName[%s] "
+ "DN[%s] TDO[%s], "
+ "returning UNSUCCESSFUL\n",
+ ndr_map_error2string(ndr_err),
+ account_name,
+ ldb_dn_get_linearized(user_msg->dn),
+ ldb_dn_get_linearized(tdo_msg->dn)));
+ TALLOC_FREE(frame);
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ tdo_msg->num_elements = 0;
+ TALLOC_FREE(tdo_msg->elements);
+
+ ret = ldb_msg_add_empty(tdo_msg, "trustAuthIncoming",
+ LDB_FLAG_MOD_REPLACE, NULL);
+ if (ret != LDB_SUCCESS) {
+ ldb_transaction_cancel(ldb);
+ TALLOC_FREE(frame);
+ return NT_STATUS_NO_MEMORY;
+ }
+ ret = ldb_msg_add_value(tdo_msg, "trustAuthIncoming",
+ &new_val, NULL);
+ if (ret != LDB_SUCCESS) {
+ ldb_transaction_cancel(ldb);
+ TALLOC_FREE(frame);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ ret = ldb_modify(ldb, tdo_msg);
+ if (ret != LDB_SUCCESS) {
+ nt_status = dsdb_ldb_err_to_ntstatus(ret);
+ ldb_transaction_cancel(ldb);
+ DEBUG(1, ("samdb_set_password_sid: "
+ "failed to replace "
+ "trustAuthOutgoing sAMAccountName[%s] "
+ "DN[%s] TDO[%s], "
+ "%s - %s\n",
+ account_name,
+ ldb_dn_get_linearized(user_msg->dn),
+ ldb_dn_get_linearized(tdo_msg->dn),
+ nt_errstr(nt_status), ldb_errstring(ldb)));
+ TALLOC_FREE(frame);
+ return nt_status;
+ }
+ }
+
+ nt_status = samdb_set_password_internal(ldb, mem_ctx,
+ user_msg->dn, NULL,
+ new_password,
+ lmNewHash, ntNewHash,
+ lmOldHash, ntOldHash,
+ reject_reason, _dominfo,
+ true); /* permit trusts */
if (!NT_STATUS_IS_OK(nt_status)) {
ldb_transaction_cancel(ldb);
- talloc_free(user_dn);
+ TALLOC_FREE(frame);
return nt_status;
}
ret = ldb_transaction_commit(ldb);
if (ret != LDB_SUCCESS) {
DEBUG(0,("Failed to commit transaction to change password on %s: %s\n",
- ldb_dn_get_linearized(user_dn),
+ ldb_dn_get_linearized(user_msg->dn),
ldb_errstring(ldb)));
- talloc_free(user_dn);
+ TALLOC_FREE(frame);
return NT_STATUS_TRANSACTION_ABORTED;
}
- talloc_free(user_dn);
+ TALLOC_FREE(frame);
return NT_STATUS_OK;
}
}
if (res->count < 1) {
talloc_free(tmp_ctx);
- return LDB_ERR_NO_SUCH_OBJECT;
+ return ldb_error(ldb, LDB_ERR_NO_SUCH_OBJECT, __func__);
}
*guid = samdb_result_guid(res->msgs[0], attribute);
talloc_free(tmp_ctx);
}
if (res->count < 1) {
talloc_free(tmp_ctx);
- return LDB_ERR_NO_SUCH_OBJECT;
+ return ldb_error(ldb, LDB_ERR_NO_SUCH_OBJECT, __func__);
}
s = samdb_result_dom_sid(tmp_ctx, res->msgs[0], "objectSid");
if (s == NULL) {
talloc_free(tmp_ctx);
- return LDB_ERR_NO_SUCH_OBJECT;
+ return ldb_error(ldb, LDB_ERR_NO_SUCH_OBJECT, __func__);
}
*sid = *s;
talloc_free(tmp_ctx);
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;
failed:
DEBUG(1,("Failed to find our NTDS Site Settings options in ldb!\n"));
talloc_free(tmp_ctx);
- return LDB_ERR_NO_SUCH_OBJECT;
+ return ldb_error(ldb_ctx, LDB_ERR_NO_SUCH_OBJECT, __func__);
}
/*
failed:
DEBUG(1,("Failed to find our own NTDS Settings options in the ldb!\n"));
talloc_free(tmp_ctx);
- return LDB_ERR_NO_SUCH_OBJECT;
+ return ldb_error(ldb, LDB_ERR_NO_SUCH_OBJECT, __func__);
}
const char* samdb_ntds_object_category(TALLOC_CTX *tmp_ctx, struct ldb_context *ldb)
size_t i;
tokens = str_list_make(mem_ctx, cn, " -_");
- if (tokens == NULL)
+ if (tokens == NULL || tokens[0] == NULL) {
return NULL;
+ }
/* "tolower()" and "toupper()" should also work properly on 0x00 */
tokens[0][0] = tolower(tokens[0][0]);
- for (i = 1; i < str_list_length((const char * const *)tokens); i++)
+ for (i = 1; tokens[i] != NULL; i++)
tokens[i][0] = toupper(tokens[i][0]);
ret = talloc_strdup(mem_ctx, tokens[0]);
- for (i = 1; i < str_list_length((const char * const *)tokens); i++)
+ for (i = 1; tokens[i] != NULL; i++)
ret = talloc_asprintf_append_buffer(ret, "%s", tokens[i]);
talloc_free(tokens);
NTSTATUS dsdb_get_extended_dn_uint64(struct ldb_dn *dn, uint64_t *val, const char *component_name)
{
const struct ldb_val *v;
- char *s;
+ int error = 0;
v = ldb_dn_get_extended_component(dn, component_name);
if (v == NULL) {
return NT_STATUS_OBJECT_NAME_NOT_FOUND;
}
- s = talloc_strndup(dn, (const char *)v->data, v->length);
- NT_STATUS_HAVE_NO_MEMORY(s);
- *val = strtoull(s, NULL, 0);
+ /* 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;
- talloc_free(s);
+ *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;
- char *s;
+ int error = 0;
v = ldb_dn_get_extended_component(dn, component_name);
if (v == NULL) {
return NT_STATUS_OBJECT_NAME_NOT_FOUND;
}
- s = talloc_strndup(dn, (const char *)v->data, v->length);
- NT_STATUS_HAVE_NO_MEMORY(s);
-
- *val = strtoul(s, NULL, 0);
+ /* 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_err(s, NULL, 0, &error);
+ if (error != 0) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+ }
- talloc_free(s);
return NT_STATUS_OK;
}
NTSTATUS dsdb_get_extended_dn_sid(struct ldb_dn *dn, struct dom_sid *sid, const char *component_name)
{
const struct ldb_val *sid_blob;
- struct TALLOC_CTX *tmp_ctx;
enum ndr_err_code ndr_err;
sid_blob = ldb_dn_get_extended_component(dn, component_name);
return NT_STATUS_OBJECT_NAME_NOT_FOUND;
}
- tmp_ctx = talloc_new(NULL);
-
- ndr_err = ndr_pull_struct_blob_all(sid_blob, tmp_ctx, sid,
- (ndr_pull_flags_fn_t)ndr_pull_dom_sid);
+ ndr_err = ndr_pull_struct_blob_all_noalloc(sid_blob, sid,
+ (ndr_pull_flags_fn_t)ndr_pull_dom_sid);
if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
NTSTATUS status = ndr_map_error2ntstatus(ndr_err);
- talloc_free(tmp_ctx);
return status;
}
- talloc_free(tmp_ctx);
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;
}
return true if a ldb_val containing a DN in storage form is
in the upgraded w2k3 linked attribute format
*/
-bool dsdb_dn_is_upgraded_link_val(struct ldb_val *val)
+bool dsdb_dn_is_upgraded_link_val(const struct ldb_val *val)
{
return memmem(val->data, val->length, "<RMD_VERSION=", 13) != NULL;
}
ret = ldb_search(samdb, tmp_ctx, &root_res,
ldb_dn_new(tmp_ctx, samdb, ""), LDB_SCOPE_BASE, root_attrs, NULL);
- if (ret != LDB_SUCCESS) {
+ if (ret != LDB_SUCCESS || root_res->count == 0) {
DEBUG(1,("Searching for namingContexts in rootDSE failed: %s\n", ldb_errstring(samdb)));
talloc_free(tmp_ctx);
return ret;
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. */
}
talloc_free(tmp_ctx);
- return LDB_ERR_NO_SUCH_OBJECT;
+ return ldb_error(samdb, LDB_ERR_NO_SUCH_OBJECT, __func__);
}
struct ldb_dn *dn;
dn = ldb_get_config_basedn(ldb);
if (!dn) {
- return LDB_ERR_NO_SUCH_OBJECT;
+ return ldb_error(ldb, LDB_ERR_NO_SUCH_OBJECT, __func__);
}
dn = ldb_dn_copy(ldb, dn);
if (!dn) {
static const struct timeval tv1970;
NTTIME nt1970 = timeval_to_nttime(&tv1970);
- ret = ldb_search(samdb, mem_ctx, &r, dn, LDB_SCOPE_BASE, attrs, NULL);
+ ret = dsdb_search_dn(samdb, mem_ctx, &r, dn, attrs, DSDB_SEARCH_SHOW_RECYCLED|DSDB_SEARCH_SHOW_DELETED);
if (ret != LDB_SUCCESS) {
return ret;
}
}
}
+ if (dsdb_flags & DSDB_REPLMD_VANISH_LINKS) {
+ ret = ldb_request_add_control(req, DSDB_CONTROL_REPLMD_VANISH_LINKS, true, NULL);
+ if (ret != LDB_SUCCESS) {
+ return ret;
+ }
+ }
+
if (dsdb_flags & DSDB_MODIFY_PARTIAL_REPLICA) {
ret = ldb_request_add_control(req, DSDB_CONTROL_PARTIAL_REPLICA, false, NULL);
if (ret != LDB_SUCCESS) {
}
}
+ 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
*/
if (res->count == 0) {
talloc_free(tmp_ctx);
ldb_reset_err_string(ldb);
- return LDB_ERR_NO_SUCH_OBJECT;
+ return ldb_error(ldb, LDB_ERR_NO_SUCH_OBJECT, __func__);
}
if (res->count != 1) {
talloc_free(tmp_ctx);
account_dn = ldb_msg_find_attr_as_dn(ldb, tmp_ctx, msg, "serverReference");
if (account_dn == NULL) {
- DEBUG(1,(__location__ ": Failed to find account_dn for DSA with objectGUID %s, sid %s\n",
- GUID_string(tmp_ctx, dsa_guid), dom_sid_string(tmp_ctx, sid)));
+ DEBUG(1,(__location__ ": Failed to find account dn "
+ "(serverReference) for %s, parent of DSA with "
+ "objectGUID %s, sid %s\n",
+ ldb_dn_get_linearized(msg->dn),
+ GUID_string(tmp_ctx, dsa_guid),
+ dom_sid_string(tmp_ctx, sid)));
talloc_free(tmp_ctx);
return ldb_operr(ldb);
}
return false;
}
+int dsdb_werror_at(struct ldb_context *ldb, int ldb_ecode, WERROR werr,
+ const char *location, const char *func,
+ const char *reason)
+{
+ if (reason == NULL) {
+ reason = win_errstr(werr);
+ }
+ ldb_asprintf_errstring(ldb, "%08X: %s at %s:%s",
+ W_ERROR_V(werr), reason, location, func);
+ return ldb_ecode;
+}
/*
map an ldb error code to an approximate NTSTATUS code
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;
guid->node[2], guid->node[3],
guid->node[4], guid->node[5]);
}
+
+/*
+ * Return the effective badPwdCount
+ *
+ * This requires that the user_msg have (if present):
+ * - badPasswordTime
+ * - badPwdCount
+ *
+ * This also requires that the domain_msg have (if present):
+ * - lockOutObservationWindow
+ */
+static int dsdb_effective_badPwdCount(const struct ldb_message *user_msg,
+ int64_t lockOutObservationWindow,
+ NTTIME now)
+{
+ int64_t badPasswordTime;
+ badPasswordTime = ldb_msg_find_attr_as_int64(user_msg, "badPasswordTime", 0);
+
+ if (badPasswordTime - lockOutObservationWindow >= now) {
+ return ldb_msg_find_attr_as_int(user_msg, "badPwdCount", 0);
+ } else {
+ return 0;
+ }
+}
+
+/*
+ * 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 ldb_dn *domain_dn,
+ const struct ldb_message *user_msg)
+{
+ struct timeval tv_now = timeval_current();
+ NTTIME now = timeval_to_nttime(&tv_now);
+ 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.
+ *
+ * This requires that the user_msg have (if present):
+ * - objectSid
+ * - badPasswordTime
+ * - badPwdCount
+ *
+ * This also requires that the domain_msg have (if present):
+ * - 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;
+ int64_t lockoutThreshold, lockOutObservationWindow;
+ struct dom_sid *sid;
+ struct timeval tv_now = timeval_current();
+ NTTIME now = timeval_to_nttime(&tv_now);
+ NTSTATUS status;
+ uint32_t pwdProperties, rid = 0;
+ struct ldb_message *mod_msg;
+
+ sid = samdb_result_dom_sid(mem_ctx, user_msg, "objectSid");
+
+ pwdProperties = ldb_msg_find_attr_as_uint(domain_msg,
+ "pwdProperties", -1);
+ if (sid && !(pwdProperties & DOMAIN_PASSWORD_LOCKOUT_ADMINS)) {
+ status = dom_sid_split_rid(NULL, sid, NULL, &rid);
+ if (!NT_STATUS_IS_OK(status)) {
+ /*
+ * This can't happen anyway, but always try
+ * and update the badPwdCount on failure
+ */
+ rid = 0;
+ }
+ }
+ TALLOC_FREE(sid);
+
+ /*
+ * Work out if we are doing password lockout on the domain.
+ * Also, the built in administrator account is exempt:
+ * http://msdn.microsoft.com/en-us/library/windows/desktop/aa375371%28v=vs.85%29.aspx
+ */
+ 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_OK;
+ }
+
+ mod_msg = ldb_msg_new(mem_ctx);
+ if (mod_msg == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ mod_msg->dn = ldb_dn_copy(mod_msg, user_msg->dn);
+ if (mod_msg->dn == NULL) {
+ TALLOC_FREE(mod_msg);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ lockOutObservationWindow = get_lockout_observation_window(domain_msg,
+ pso_msg);
+
+ badPwdCount = dsdb_effective_badPwdCount(user_msg, lockOutObservationWindow, now);
+
+ badPwdCount++;
+
+ ret = samdb_msg_add_int(sam_ctx, mod_msg, mod_msg, "badPwdCount", badPwdCount);
+ if (ret != LDB_SUCCESS) {
+ TALLOC_FREE(mod_msg);
+ return NT_STATUS_NO_MEMORY;
+ }
+ ret = samdb_msg_add_int64(sam_ctx, mod_msg, mod_msg, "badPasswordTime", now);
+ if (ret != LDB_SUCCESS) {
+ TALLOC_FREE(mod_msg);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ if (badPwdCount >= lockoutThreshold) {
+ ret = samdb_msg_add_int64(sam_ctx, mod_msg, mod_msg, "lockoutTime", now);
+ if (ret != LDB_SUCCESS) {
+ TALLOC_FREE(mod_msg);
+ return NT_STATUS_NO_MEMORY;
+ }
+ DEBUGC( DBGC_AUTH, 1, ("Locked out user %s after %d wrong passwords\n",
+ ldb_dn_get_linearized(user_msg->dn), badPwdCount));
+ } else {
+ DEBUGC( DBGC_AUTH, 5, ("Updated badPwdCount on %s after %d wrong passwords\n",
+ ldb_dn_get_linearized(user_msg->dn), badPwdCount));
+ }
+
+ /* mark all the message elements as LDB_FLAG_MOD_REPLACE */
+ for (i=0; i< mod_msg->num_elements; i++) {
+ mod_msg->elements[i].flags = LDB_FLAG_MOD_REPLACE;
+ }
+
+ *_mod_msg = mod_msg;
+ return NT_STATUS_OK;
+}
+
+/**
+ * Sets defaults for a User object
+ * List of default attributes set:
+ * accountExpires, badPasswordTime, badPwdCount,
+ * codePage, countryCode, lastLogoff, lastLogon
+ * logonCount, pwdLastSet
+ */
+int dsdb_user_obj_set_defaults(struct ldb_context *ldb,
+ struct ldb_message *usr_obj,
+ struct ldb_request *req)
+{
+ int i, ret;
+ const struct attribute_values {
+ const char *name;
+ const char *value;
+ const char *add_value;
+ const char *mod_value;
+ const char *control;
+ unsigned add_flags;
+ unsigned mod_flags;
+ } map[] = {
+ {
+ .name = "accountExpires",
+ .add_value = "9223372036854775807",
+ .mod_value = "0",
+ },
+ {
+ .name = "badPasswordTime",
+ .value = "0"
+ },
+ {
+ .name = "badPwdCount",
+ .value = "0"
+ },
+ {
+ .name = "codePage",
+ .value = "0"
+ },
+ {
+ .name = "countryCode",
+ .value = "0"
+ },
+ {
+ .name = "lastLogoff",
+ .value = "0"
+ },
+ {
+ .name = "lastLogon",
+ .value = "0"
+ },
+ {
+ .name = "logonCount",
+ .value = "0"
+ },
+ {
+ .name = "logonHours",
+ .add_flags = DSDB_FLAG_INTERNAL_FORCE_META_DATA,
+ },
+ {
+ .name = "pwdLastSet",
+ .value = "0",
+ .control = DSDB_CONTROL_PASSWORD_DEFAULT_LAST_SET_OID,
+ },
+ {
+ .name = "adminCount",
+ .mod_value = "0",
+ },
+ {
+ .name = "operatorCount",
+ .mod_value = "0",
+ },
+ };
+
+ for (i = 0; i < ARRAY_SIZE(map); i++) {
+ bool added = false;
+ const char *value = NULL;
+ unsigned flags = 0;
+
+ if (req != NULL && req->operation == LDB_ADD) {
+ value = map[i].add_value;
+ flags = map[i].add_flags;
+ } else {
+ value = map[i].mod_value;
+ flags = map[i].mod_flags;
+ }
+
+ if (value == NULL) {
+ value = map[i].value;
+ }
+
+ if (value != NULL) {
+ flags |= LDB_FLAG_MOD_ADD;
+ }
+
+ if (flags == 0) {
+ continue;
+ }
+
+ ret = samdb_find_or_add_attribute_ex(ldb, usr_obj,
+ map[i].name,
+ value, flags,
+ &added);
+ if (ret != LDB_SUCCESS) {
+ return ret;
+ }
+
+ if (req != NULL && added && map[i].control != NULL) {
+ ret = ldb_request_add_control(req,
+ map[i].control,
+ false, NULL);
+ if (ret != LDB_SUCCESS) {
+ return ret;
+ }
+ }
+ }
+
+ return LDB_SUCCESS;
+}
+
+/**
+ * Sets 'sAMAccountType on user object based on userAccountControl
+ * @param ldb Current ldb_context
+ * @param usr_obj ldb_message representing User object
+ * @param user_account_control Value for userAccountControl flags
+ * @param account_type_p Optional pointer to account_type to return
+ * @return LDB_SUCCESS or LDB_ERR* code on failure
+ */
+int dsdb_user_obj_set_account_type(struct ldb_context *ldb, struct ldb_message *usr_obj,
+ uint32_t user_account_control, uint32_t *account_type_p)
+{
+ int ret;
+ uint32_t account_type;
+ struct ldb_message_element *el;
+
+ account_type = ds_uf2atype(user_account_control);
+ if (account_type == 0) {
+ ldb_set_errstring(ldb, "dsdb: Unrecognized account type!");
+ return LDB_ERR_UNWILLING_TO_PERFORM;
+ }
+ ret = samdb_msg_add_uint(ldb, usr_obj, usr_obj,
+ "sAMAccountType",
+ account_type);
+ if (ret != LDB_SUCCESS) {
+ return ret;
+ }
+ el = ldb_msg_find_element(usr_obj, "sAMAccountType");
+ el->flags = LDB_FLAG_MOD_REPLACE;
+
+ if (account_type_p) {
+ *account_type_p = account_type;
+ }
+
+ return LDB_SUCCESS;
+}
+
+/**
+ * Determine and set primaryGroupID based on userAccountControl value
+ * @param ldb Current ldb_context
+ * @param usr_obj ldb_message representing User object
+ * @param user_account_control Value for userAccountControl flags
+ * @param group_rid_p Optional pointer to group RID to return
+ * @return LDB_SUCCESS or LDB_ERR* code on failure
+ */
+int dsdb_user_obj_set_primary_group_id(struct ldb_context *ldb, struct ldb_message *usr_obj,
+ uint32_t user_account_control, uint32_t *group_rid_p)
+{
+ int ret;
+ uint32_t rid;
+ struct ldb_message_element *el;
+
+ rid = ds_uf2prim_group_rid(user_account_control);
+
+ ret = samdb_msg_add_uint(ldb, usr_obj, usr_obj,
+ "primaryGroupID", rid);
+ if (ret != LDB_SUCCESS) {
+ return ret;
+ }
+ el = ldb_msg_find_element(usr_obj, "primaryGroupID");
+ el->flags = LDB_FLAG_MOD_REPLACE;
+
+ if (group_rid_p) {
+ *group_rid_p = rid;
+ }
+
+ 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;
+}