+ /* use a new message structure so that we can modify it */
+ ac->down_req->op.mod.message = msg = ldb_msg_copy_shallow(ac->down_req, req->op.mod.message);
+
+ /* - remove any imodification to the password from the first commit
+ * we will make the real modification later */
+ if (sambaAttr) ldb_msg_remove_attr(msg, "sambaPassword");
+ if (ntAttr) ldb_msg_remove_attr(msg, "unicodePwd");
+ if (lmAttr) ldb_msg_remove_attr(msg, "dBCSPwd");
+
+ /* if there was nothing else to be modify skip to next step */
+ if (msg->num_elements == 0) {
+ talloc_free(ac->down_req);
+ ac->down_req = NULL;
+ return password_hash_mod_search_self(h);
+ }
+
+ ac->down_req->context = NULL;
+ ac->down_req->callback = NULL;
+
+ ac->step = PH_MOD_DO_REQ;
+
+ ldb_set_timeout_from_prev_req(module->ldb, req, ac->down_req);
+
+ return ldb_next_request(module, ac->down_req);
+}
+
+static int get_self_callback(struct ldb_context *ldb, void *context, struct ldb_reply *ares)
+{
+ struct ph_context *ac;
+
+ ac = talloc_get_type(context, struct ph_context);
+
+ /* we are interested only in the single reply (base search) we receive here */
+ if (ares->type == LDB_REPLY_ENTRY) {
+ if (ac->search_res != NULL) {
+ ldb_set_errstring(ldb, "Too many results");
+ talloc_free(ares);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ /* if it is not an entry of type person this is an error */
+ /* TODO: remove this when sambaPassword will be in schema */
+ if (!ldb_msg_check_string_attribute(ares->message, "objectClass", "person")) {
+ ldb_set_errstring(ldb, "Object class violation");
+ talloc_free(ares);
+ return LDB_ERR_OBJECT_CLASS_VIOLATION;
+ }
+
+ ac->search_res = talloc_steal(ac, ares);
+ } else {
+ talloc_free(ares);
+ }
+
+ return LDB_SUCCESS;
+}
+
+static int password_hash_mod_search_self(struct ldb_handle *h) {
+
+ struct ph_context *ac;
+ static const char * const attrs[] = { "userAccountControl", "lmPwdHistory",
+ "ntPwdHistory",
+ "objectSid", "msDS-KeyVersionNumber",
+ "objectClass", "userPrincipalName",
+ "sAMAccountName",
+ "dBCSPwd", "unicodePwd",
+ "supplementalCredentials",
+ NULL };
+
+ ac = talloc_get_type(h->private_data, struct ph_context);
+
+ /* prepare the search operation */
+ ac->search_req = talloc_zero(ac, struct ldb_request);
+ if (ac->search_req == NULL) {
+ ldb_debug(ac->module->ldb, LDB_DEBUG_ERROR, "Out of Memory!\n");
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ ac->search_req->operation = LDB_SEARCH;
+ ac->search_req->op.search.base = ac->orig_req->op.mod.message->dn;
+ ac->search_req->op.search.scope = LDB_SCOPE_BASE;
+ ac->search_req->op.search.tree = ldb_parse_tree(ac->search_req, NULL);
+ if (ac->search_req->op.search.tree == NULL) {
+ ldb_set_errstring(ac->module->ldb, "Invalid search filter");
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+ ac->search_req->op.search.attrs = attrs;
+ ac->search_req->controls = NULL;
+ ac->search_req->context = ac;
+ ac->search_req->callback = get_self_callback;
+ ldb_set_timeout_from_prev_req(ac->module->ldb, ac->orig_req, ac->search_req);
+
+ ac->step = PH_MOD_SEARCH_SELF;
+
+ return ldb_next_request(ac->module, ac->search_req);
+}
+
+static int password_hash_mod_search_dom(struct ldb_handle *h) {
+
+ struct ph_context *ac;
+ int ret;
+
+ ac = talloc_get_type(h->private_data, struct ph_context);
+
+ /* get object domain sid */
+ ac->domain_sid = samdb_result_sid_prefix(ac, ac->search_res->message, "objectSid");
+ if (ac->domain_sid == NULL) {
+ ldb_debug(ac->module->ldb, LDB_DEBUG_ERROR, "can't handle entry with missing objectSid!\n");
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ /* get user domain data */
+ ret = build_domain_data_request(ac);
+ if (ret != LDB_SUCCESS) {
+ return ret;
+ }
+
+ ac->step = PH_MOD_SEARCH_DOM;
+
+ return ldb_next_request(ac->module, ac->dom_req);
+}
+
+static int password_hash_mod_do_mod(struct ldb_handle *h) {
+
+ struct ph_context *ac;
+ struct domain_data *domain;
+ struct smb_krb5_context *smb_krb5_context;
+ struct ldb_message *msg;
+ struct ldb_message *orig_msg;
+ struct ldb_message *searched_msg;
+ struct setup_password_fields_io io;
+ int ret;
+
+ ac = talloc_get_type(h->private_data, struct ph_context);
+
+ domain = get_domain_data(ac->module, ac, ac->dom_res);
+ if (domain == NULL) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ ac->mod_req = talloc(ac, struct ldb_request);
+ if (ac->mod_req == NULL) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ *(ac->mod_req) = *(ac->orig_req);
+
+ /* use a new message structure so that we can modify it */
+ ac->mod_req->op.mod.message = msg = ldb_msg_new(ac->mod_req);
+ if (msg == NULL) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ /* modify dn */
+ msg->dn = ac->orig_req->op.mod.message->dn;
+
+ /* Some operations below require kerberos contexts */
+ if (smb_krb5_init_context(ac->mod_req,
+ ldb_get_opaque(h->module->ldb, "EventContext"),
+ (struct loadparm_context *)ldb_get_opaque(h->module->ldb, "loadparm"),
+ &smb_krb5_context) != 0) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ orig_msg = discard_const(ac->orig_req->op.mod.message);
+ searched_msg = ac->search_res->message;
+
+ ZERO_STRUCT(io);
+ io.ac = ac;
+ io.domain = domain;
+ io.smb_krb5_context = smb_krb5_context;
+
+ io.u.user_account_control = samdb_result_uint(searched_msg, "userAccountControl", 0);
+ io.u.sAMAccountName = samdb_result_string(searched_msg, "samAccountName", NULL);
+ io.u.user_principal_name = samdb_result_string(searched_msg, "userPrincipalName", NULL);
+ io.u.is_computer = ldb_msg_check_string_attribute(searched_msg, "objectClass", "computer");
+
+ io.n.cleartext = samdb_result_string(orig_msg, "sambaPassword", NULL);
+ io.n.nt_hash = samdb_result_hash(io.ac, orig_msg, "unicodePwd");
+ io.n.lm_hash = samdb_result_hash(io.ac, orig_msg, "dBCSPwd");
+
+ io.o.kvno = samdb_result_uint(searched_msg, "msDs-KeyVersionNumber", 0);
+ io.o.nt_history_len = samdb_result_hashes(io.ac, searched_msg, "ntPwdHistory", &io.o.nt_history);
+ io.o.lm_history_len = samdb_result_hashes(io.ac, searched_msg, "lmPwdHistory", &io.o.lm_history);
+ io.o.supplemental = ldb_msg_find_ldb_val(searched_msg, "supplementalCredentials");
+
+ ret = setup_password_fields(&io);
+ if (ret != LDB_SUCCESS) {
+ return ret;
+ }
+
+ /* make sure we replace all the old attributes */
+ ret = ldb_msg_add_empty(msg, "unicodePwd", LDB_FLAG_MOD_REPLACE, NULL);
+ ret = ldb_msg_add_empty(msg, "dBCSPwd", LDB_FLAG_MOD_REPLACE, NULL);
+ ret = ldb_msg_add_empty(msg, "ntPwdHistory", LDB_FLAG_MOD_REPLACE, NULL);
+ ret = ldb_msg_add_empty(msg, "lmPwdHistory", LDB_FLAG_MOD_REPLACE, NULL);
+ ret = ldb_msg_add_empty(msg, "supplementalCredentials", LDB_FLAG_MOD_REPLACE, NULL);
+ ret = ldb_msg_add_empty(msg, "pwdLastSet", LDB_FLAG_MOD_REPLACE, NULL);
+ ret = ldb_msg_add_empty(msg, "msDs-KeyVersionNumber", LDB_FLAG_MOD_REPLACE, NULL);
+
+ if (io.g.nt_hash) {
+ ret = samdb_msg_add_hash(ac->module->ldb, ac, msg,
+ "unicodePwd", io.g.nt_hash);
+ if (ret != LDB_SUCCESS) {
+ return ret;
+ }
+ }
+ if (io.g.lm_hash) {
+ ret = samdb_msg_add_hash(ac->module->ldb, ac, msg,
+ "dBCSPwd", io.g.lm_hash);
+ if (ret != LDB_SUCCESS) {
+ return ret;
+ }
+ }
+ if (io.g.nt_history_len > 0) {
+ ret = samdb_msg_add_hashes(ac, msg,
+ "ntPwdHistory",
+ io.g.nt_history,
+ io.g.nt_history_len);
+ if (ret != LDB_SUCCESS) {
+ return ret;
+ }
+ }
+ if (io.g.lm_history_len > 0) {
+ ret = samdb_msg_add_hashes(ac, msg,
+ "lmPwdHistory",
+ io.g.lm_history,
+ io.g.lm_history_len);
+ if (ret != LDB_SUCCESS) {
+ return ret;
+ }
+ }
+ if (io.g.supplemental.length > 0) {
+ ret = ldb_msg_add_value(msg, "supplementalCredentials",
+ &io.g.supplemental, NULL);
+ if (ret != LDB_SUCCESS) {
+ return ret;
+ }
+ }
+ ret = samdb_msg_add_uint64(ac->module->ldb, ac, msg,
+ "pwdLastSet",
+ io.g.last_set);
+ if (ret != LDB_SUCCESS) {
+ return ret;
+ }
+ ret = samdb_msg_add_uint(ac->module->ldb, ac, msg,
+ "msDs-KeyVersionNumber",
+ io.g.kvno);
+ if (ret != LDB_SUCCESS) {
+ return ret;
+ }
+
+ h->state = LDB_ASYNC_INIT;
+ h->status = LDB_SUCCESS;
+
+ ac->step = PH_MOD_DO_MOD;
+
+ ldb_set_timeout_from_prev_req(ac->module->ldb, ac->orig_req, ac->mod_req);
+
+ /* perform the search */
+ return ldb_next_request(ac->module, ac->mod_req);
+}
+
+static int ph_wait(struct ldb_handle *handle) {
+ struct ph_context *ac;
+ int ret;
+
+ if (!handle || !handle->private_data) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ if (handle->state == LDB_ASYNC_DONE) {
+ return handle->status;
+ }
+
+ handle->state = LDB_ASYNC_PENDING;
+ handle->status = LDB_SUCCESS;
+
+ ac = talloc_get_type(handle->private_data, struct ph_context);
+
+ switch (ac->step) {
+ case PH_ADD_SEARCH_DOM:
+ ret = ldb_wait(ac->dom_req->handle, LDB_WAIT_NONE);
+
+ if (ret != LDB_SUCCESS) {
+ handle->status = ret;
+ goto done;
+ }
+ if (ac->dom_req->handle->status != LDB_SUCCESS) {
+ handle->status = ac->dom_req->handle->status;
+ goto done;
+ }
+
+ if (ac->dom_req->handle->state != LDB_ASYNC_DONE) {
+ return LDB_SUCCESS;
+ }
+
+ /* domain search done, go on */
+ return password_hash_add_do_add(handle);
+
+ case PH_ADD_DO_ADD:
+ ret = ldb_wait(ac->down_req->handle, LDB_WAIT_NONE);
+
+ if (ret != LDB_SUCCESS) {
+ handle->status = ret;
+ goto done;
+ }
+ if (ac->down_req->handle->status != LDB_SUCCESS) {
+ handle->status = ac->down_req->handle->status;
+ goto done;
+ }
+
+ if (ac->down_req->handle->state != LDB_ASYNC_DONE) {
+ return LDB_SUCCESS;
+ }
+
+ break;
+
+ case PH_MOD_DO_REQ:
+ ret = ldb_wait(ac->down_req->handle, LDB_WAIT_NONE);
+
+ if (ret != LDB_SUCCESS) {
+ handle->status = ret;
+ goto done;
+ }
+ if (ac->down_req->handle->status != LDB_SUCCESS) {
+ handle->status = ac->down_req->handle->status;
+ goto done;
+ }
+
+ if (ac->down_req->handle->state != LDB_ASYNC_DONE) {
+ return LDB_SUCCESS;
+ }
+
+ /* non-password mods done, go on */
+ return password_hash_mod_search_self(handle);
+
+ case PH_MOD_SEARCH_SELF:
+ ret = ldb_wait(ac->search_req->handle, LDB_WAIT_NONE);
+
+ if (ret != LDB_SUCCESS) {
+ handle->status = ret;
+ goto done;
+ }
+ if (ac->search_req->handle->status != LDB_SUCCESS) {
+ handle->status = ac->search_req->handle->status;
+ goto done;
+ }
+
+ if (ac->search_req->handle->state != LDB_ASYNC_DONE) {
+ return LDB_SUCCESS;
+ }
+
+ if (ac->search_res == NULL) {
+ return LDB_ERR_NO_SUCH_OBJECT;
+ }
+
+ /* self search done, go on */
+ return password_hash_mod_search_dom(handle);
+
+ case PH_MOD_SEARCH_DOM:
+ ret = ldb_wait(ac->dom_req->handle, LDB_WAIT_NONE);
+
+ if (ret != LDB_SUCCESS) {
+ handle->status = ret;
+ goto done;
+ }
+ if (ac->dom_req->handle->status != LDB_SUCCESS) {
+ handle->status = ac->dom_req->handle->status;
+ goto done;
+ }
+
+ if (ac->dom_req->handle->state != LDB_ASYNC_DONE) {
+ return LDB_SUCCESS;
+ }
+
+ /* domain search done, go on */
+ return password_hash_mod_do_mod(handle);
+
+ case PH_MOD_DO_MOD:
+ ret = ldb_wait(ac->mod_req->handle, LDB_WAIT_NONE);
+
+ if (ret != LDB_SUCCESS) {
+ handle->status = ret;
+ goto done;
+ }
+ if (ac->mod_req->handle->status != LDB_SUCCESS) {
+ handle->status = ac->mod_req->handle->status;
+ goto done;
+ }
+
+ if (ac->mod_req->handle->state != LDB_ASYNC_DONE) {
+ return LDB_SUCCESS;
+ }
+
+ break;
+
+ default:
+ ret = LDB_ERR_OPERATIONS_ERROR;
+ goto done;
+ }
+
+ ret = LDB_SUCCESS;
+
+done:
+ handle->state = LDB_ASYNC_DONE;
+ return ret;
+}
+
+static int ph_wait_all(struct ldb_handle *handle) {
+
+ int ret;
+
+ while (handle->state != LDB_ASYNC_DONE) {
+ ret = ph_wait(handle);
+ if (ret != LDB_SUCCESS) {
+ return ret;
+ }
+ }
+
+ return handle->status;
+}
+
+static int password_hash_wait(struct ldb_handle *handle, enum ldb_wait_type type)
+{
+ if (type == LDB_WAIT_ALL) {
+ return ph_wait_all(handle);
+ } else {
+ return ph_wait(handle);