4 Copyright (C) Simo Sorce 2004
5 Copyright (C) Andrew Bartlett <abartlet@samba.org> 2005
6 Copyright (C) Andrew Tridgell 2004
8 This program is free software; you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation; either version 2 of the License, or
11 (at your option) any later version.
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
18 You should have received a copy of the GNU General Public License
19 along with this program; if not, write to the Free Software
20 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
26 * Component: ldb password_hash module
28 * Description: correctly update hash values based on changes to sambaPassword and friends
30 * Author: Andrew Bartlett
34 #include "libcli/ldap/ldap.h"
35 #include "ldb/include/ldb_errors.h"
36 #include "ldb/include/ldb_private.h"
37 #include "librpc/gen_ndr/ndr_misc.h"
38 #include "librpc/gen_ndr/ndr_samr.h"
39 #include "auth/credentials/credentials.h"
40 #include "libcli/auth/proto.h"
41 #include "system/kerberos.h"
42 #include "auth/kerberos/kerberos.h"
43 #include "system/time.h"
44 #include "dsdb/samdb/samdb.h"
48 /* If we have decided there is reason to work on this request, then
49 * setup all the password hash types correctly.
51 * If the administrator doesn't want the sambaPassword stored (set in the
52 * domain and per-account policies) then we must strip that out before
53 * we do the first operation.
55 * Once this is done (which could update anything at all), we
56 * calculate the password hashes.
58 * This function must not only update the ntPwdHash, lmPwdHash and
59 * krb5Key fields, it must also atomicly increment the
60 * msDS-KeyVersionNumber. We should be in a transaction, so all this
61 * should be quite safe...
63 * Finally, if the administrator has requested that a password history
64 * be maintained, then this should also be written out.
69 static int password_hash_handle(struct ldb_module *module, struct ldb_request *req,
70 const struct ldb_message *msg)
72 int ret, old_ret = -1;
73 uint_t pwdProperties, pwdHistoryLength;
74 uint_t userAccountControl;
75 const char *dnsDomain, *realm;
76 const char *sambaPassword = NULL;
77 struct samr_Password *sambaLMPwdHistory, *sambaNTPwdHistory;
78 struct samr_Password *lmPwdHash, *ntPwdHash;
79 struct samr_Password *lmOldHash = NULL, *ntOldHash = NULL;
80 struct samr_Password *new_sambaLMPwdHistory, *new_sambaNTPwdHistory;
81 struct samr_Password local_lmNewHash, local_ntNewHash;
82 int sambaLMPwdHistory_len, sambaNTPwdHistory_len;
84 struct dom_sid *domain_sid;
85 time_t now = time(NULL);
88 krb5_error_code krb5_ret;
90 struct smb_krb5_context *smb_krb5_context;
92 struct ldb_message_element *attribute;
93 struct ldb_dn *dn = msg->dn;
94 struct ldb_message *msg2;
96 struct ldb_request *search_request = NULL;
97 struct ldb_request *modify_request;
98 struct ldb_request *modified_orig_request;
99 struct ldb_result *res, *dom_res, *old_res;
101 struct ldb_message_element *objectclasses;
102 struct ldb_val computer_val;
103 struct ldb_val person_val;
106 struct ldb_message *modify_msg;
108 const char *domain_expression;
109 const char *old_user_attrs[] = { "lmPwdHash", "ntPwdHash", NULL };
110 const char *user_attrs[] = { "userAccountControl", "sambaLMPwdHistory",
113 "objectSid", "msDS-KeyVersionNumber",
114 "objectClass", "userPrincipalName",
117 const char * const domain_attrs[] = { "pwdProperties", "pwdHistoryLength",
122 /* Do the original action */
124 /* If no part of this touches the sambaPassword, then we don't
125 * need to make any changes. For password changes/set there should
126 * be a 'delete' or a 'modify' on this attribute. */
127 if ((attribute = ldb_msg_find_element(msg, "sambaPassword")) == NULL ) {
128 return ldb_next_request(module, req);
131 mem_ctx = talloc_new(module);
133 return LDB_ERR_OPERATIONS_ERROR;
136 if (req->operation == LDB_REQ_MODIFY) {
137 search_request = talloc(mem_ctx, struct ldb_request);
138 if (!search_request) {
139 talloc_free(mem_ctx);
140 return LDB_ERR_OPERATIONS_ERROR;
143 /* Look up the old ntPwdHash and lmPwdHash values, so
144 * we can later place these into the password
147 search_request->operation = LDB_REQ_SEARCH;
148 search_request->op.search.base = dn;
149 search_request->op.search.scope = LDB_SCOPE_BASE;
150 search_request->op.search.tree = ldb_parse_tree(module->ldb, NULL);
151 search_request->op.search.attrs = old_user_attrs;
152 search_request->controls = NULL;
154 old_ret = ldb_next_request(module, search_request);
157 /* we can't change things untill we copy it */
158 msg2 = ldb_msg_copy_shallow(mem_ctx, msg);
160 /* look again, this time at the copied attribute */
161 if (!msg2 || (attribute = ldb_msg_find_element(msg2, "sambaPassword")) == NULL ) {
162 talloc_free(mem_ctx);
163 /* Gah? where did it go? Oh well... */
164 return LDB_ERR_OPERATIONS_ERROR;
167 /* Wipe out the sambaPassword attribute set, we will handle it in
168 * the second modify. We might not want it written to disk */
170 if (req->operation == LDB_REQ_ADD) {
171 if (attribute->num_values > 1) {
172 ldb_set_errstring(module->ldb,
173 talloc_asprintf(mem_ctx, "sambaPassword_handle: "
174 "attempted set of multiple sambaPassword attributes on %s rejected",
175 ldb_dn_linearize(mem_ctx, dn)));
176 talloc_free(mem_ctx);
177 return LDB_ERR_CONSTRAINT_VIOLATION;
180 if (attribute->num_values == 1) {
181 sambaPassword = (const char *)attribute->values[0].data;
182 ldb_msg_remove_attr(msg2, "sambaPassword");
184 } else if (((attribute->flags & LDB_FLAG_MOD_MASK) == LDB_FLAG_MOD_ADD)
185 || ((attribute->flags & LDB_FLAG_MOD_MASK) == LDB_FLAG_MOD_REPLACE)) {
186 if (attribute->num_values > 1) {
187 ldb_set_errstring(module->ldb,
188 talloc_asprintf(mem_ctx, "sambaPassword_handle: "
189 "attempted set of multiple sambaPassword attributes on %s rejected",
190 ldb_dn_linearize(mem_ctx, dn)));
191 talloc_free(mem_ctx);
192 return LDB_ERR_CONSTRAINT_VIOLATION;
195 if (attribute->num_values == 1) {
196 sambaPassword = (const char *)attribute->values[0].data;
197 ldb_msg_remove_attr(msg2, "sambaPassword");
201 modified_orig_request = talloc(mem_ctx, struct ldb_request);
202 if (!modified_orig_request) {
203 talloc_free(mem_ctx);
204 return LDB_ERR_OPERATIONS_ERROR;
207 *modified_orig_request = *req;
208 switch (modified_orig_request->operation) {
210 modified_orig_request->op.add.message = msg2;
213 modified_orig_request->op.mod.message = msg2;
217 /* Send the (modified) request of the original caller down to the database */
218 ret = ldb_next_request(module, modified_orig_request);
220 talloc_free(mem_ctx);
224 /* While we do the search first (for the old password hashes),
225 * we don't want to override any error that the modify may
226 * have returned. Now check the error */
227 if (req->operation == LDB_REQ_MODIFY) {
229 talloc_free(mem_ctx);
233 /* Find out the old passwords details of the user */
234 old_res = search_request->op.search.res;
235 talloc_steal(mem_ctx, old_res);
236 talloc_free(search_request);
238 if (old_res->count != 1) {
239 ldb_set_errstring(module->ldb,
240 talloc_asprintf(mem_ctx, "password_hash_handle: "
241 "(pre) search for %s found %d != 1 objects, for entry we just modified",
242 ldb_dn_linearize(mem_ctx, dn),
244 /* What happend? The above add/modify worked... */
245 talloc_free(mem_ctx);
246 return LDB_ERR_NO_SUCH_OBJECT;
249 lmOldHash = samdb_result_hash(mem_ctx, old_res->msgs[0], "lmPwdHash");
250 ntOldHash = samdb_result_hash(mem_ctx, old_res->msgs[0], "ntPwdHash");
253 /* Start finding out details we need for the second modify.
254 * We do this after the first add/modify because other modules
255 * will have filled in the templates, and we may have had
256 * things like the username (affecting the salt) changed along
257 * with the password. */
259 /* Now find out what is on the entry after the above add/modify */
260 search_request = talloc(mem_ctx, struct ldb_request);
261 if (!search_request) {
262 talloc_free(mem_ctx);
263 return LDB_ERR_OPERATIONS_ERROR;
266 search_request->operation = LDB_REQ_SEARCH;
267 search_request->op.search.base = dn;
268 search_request->op.search.scope = LDB_SCOPE_BASE;
269 search_request->op.search.tree = ldb_parse_tree(module->ldb, NULL);
270 search_request->op.search.attrs = user_attrs;
271 search_request->controls = NULL;
273 ret = ldb_next_request(module, search_request);
275 talloc_free(mem_ctx);
279 /* Find out the full details of the user */
280 res = search_request->op.search.res;
281 talloc_steal(mem_ctx, res);
282 talloc_free(search_request);
284 if (res->count != 1) {
285 ldb_set_errstring(module->ldb,
286 talloc_asprintf(mem_ctx, "password_hash_handle: "
287 "search for %s found %d != 1 objects, for entry we just added/modified",
288 ldb_dn_linearize(mem_ctx, dn),
290 /* What happend? The above add/modify worked... */
291 talloc_free(mem_ctx);
292 return LDB_ERR_NO_SUCH_OBJECT;
295 userAccountControl = samdb_result_uint(res->msgs[0], "userAccountControl", 0);
296 sambaLMPwdHistory_len = samdb_result_hashes(mem_ctx, res->msgs[0],
297 "sambaLMPwdHistory", &sambaLMPwdHistory);
298 sambaNTPwdHistory_len = samdb_result_hashes(mem_ctx, res->msgs[0],
299 "sambaNTPwdHistory", &sambaNTPwdHistory);
300 ntPwdHash = samdb_result_hash(mem_ctx, res->msgs[0], "ntPwdHash");
301 kvno = samdb_result_uint(res->msgs[0], "msDS-KeyVersionNumber", 0);
303 domain_sid = samdb_result_sid_prefix(mem_ctx, res->msgs[0], "objectSid");
306 objectclasses = ldb_msg_find_element(res->msgs[0], "objectClass");
307 person_val = data_blob_string_const("person");
309 if (!objectclasses || !ldb_msg_find_val(objectclasses, &person_val)) {
310 /* Not a 'person', so the rest of this doesn't make
311 * sense. How we got a sambaPassword this far I don't
313 ldb_set_errstring(module->ldb,
314 talloc_asprintf(mem_ctx, "password_hash_handle: "
315 "attempted set of sambaPassword on non-'person' object %s rejected",
316 ldb_dn_linearize(mem_ctx, dn)));
317 talloc_free(mem_ctx);
318 return LDB_ERR_CONSTRAINT_VIOLATION;
321 computer_val = data_blob_string_const("computer");
323 if (ldb_msg_find_val(objectclasses, &computer_val)) {
329 domain_expression = talloc_asprintf(mem_ctx, "(&(objectSid=%s)(objectClass=domain))",
330 ldap_encode_ndr_dom_sid(mem_ctx, domain_sid));
332 /* Find the user's domain, then find out the domain password
334 ret = ldb_search(module->ldb, NULL, LDB_SCOPE_SUBTREE, domain_expression,
335 domain_attrs, &dom_res);
337 talloc_free(mem_ctx);
341 if (dom_res->count != 1) {
342 /* What happend? The user we are modifying must be odd... */
343 ldb_set_errstring(module->ldb,
344 talloc_asprintf(mem_ctx, "password_hash_handle: "
345 "search for domain %s found %d != 1 objects",
346 dom_sid_string(mem_ctx, domain_sid),
348 talloc_free(mem_ctx);
349 return LDB_ERR_NO_SUCH_OBJECT;
352 pwdProperties = samdb_result_uint(dom_res->msgs[0], "pwdProperties", 0);
353 pwdHistoryLength = samdb_result_uint(dom_res->msgs[0], "pwdHistoryLength", 0);
354 dnsDomain = ldb_msg_find_string(dom_res->msgs[0], "dnsDomain", NULL);
355 realm = strupper_talloc(mem_ctx, dnsDomain);
357 /* Some operations below require kerberos contexts */
358 if (smb_krb5_init_context(mem_ctx, &smb_krb5_context) != 0) {
359 talloc_free(mem_ctx);
360 return LDB_ERR_OPERATIONS_ERROR;
363 /* Prepare the modifications to set all the hash/key types */
364 modify_msg = ldb_msg_new(req);
365 modify_msg->dn = talloc_reference(modify_msg, dn);
367 #define CHECK_RET(x) \
370 if (check_ret != LDB_SUCCESS) { \
371 talloc_free(mem_ctx); \
376 /* Setup krb5Key (we want to either delete an existing value,
377 * or replace with a new one). Both the unicode and NT hash
378 * only branches append keys to this multivalued entry. */
379 CHECK_RET(ldb_msg_add_empty(modify_msg, "krb5Key", LDB_FLAG_MOD_REPLACE));
381 /* Yay, we can compute new password hashes from the unicode
384 Principal *salt_principal;
385 const char *user_principal_name = ldb_msg_find_string(res->msgs[0], "userPrincipalName", NULL);
390 /* compute the new nt and lm hashes */
391 if (E_deshash(sambaPassword, local_lmNewHash.hash)) {
392 lmPwdHash = &local_lmNewHash;
396 E_md4hash(sambaPassword, local_ntNewHash.hash);
397 ntPwdHash = &local_ntNewHash;
398 CHECK_RET(ldb_msg_add_empty(modify_msg, "ntPwdHash",
399 LDB_FLAG_MOD_REPLACE));
400 CHECK_RET(samdb_msg_add_hash(module->ldb, req,
401 modify_msg, "ntPwdHash",
403 CHECK_RET(ldb_msg_add_empty(modify_msg, "lmPwdHash",
404 LDB_FLAG_MOD_REPLACE));
406 CHECK_RET(samdb_msg_add_hash(module->ldb, req,
407 modify_msg, "lmPwdHash",
411 /* Many, many thanks to lukeh@padl.com for this
412 * algorithm, described in his Nov 10 2004 mail to
413 * samba-technical@samba.org */
416 /* Determine a salting principal */
417 char *samAccountName = talloc_strdup(mem_ctx, ldb_msg_find_string(res->msgs[0], "samAccountName", NULL));
419 if (!samAccountName) {
420 ldb_set_errstring(module->ldb,
421 talloc_asprintf(mem_ctx, "password_hash_handle: "
422 "generation of new kerberos keys failed: %s is a computer without a samAccountName",
423 ldb_dn_linearize(mem_ctx, dn)));
424 talloc_free(mem_ctx);
425 return LDB_ERR_OPERATIONS_ERROR;
427 if (samAccountName[strlen(samAccountName)-1] == '$') {
428 samAccountName[strlen(samAccountName)-1] = '\0';
430 saltbody = talloc_asprintf(mem_ctx, "%s.%s", samAccountName, dnsDomain);
432 krb5_ret = krb5_make_principal(smb_krb5_context->krb5_context, &salt_principal, realm, "host", saltbody, NULL);
433 } else if (user_principal_name) {
435 user_principal_name = talloc_strdup(mem_ctx, user_principal_name);
436 if (!user_principal_name) {
437 talloc_free(mem_ctx);
438 return LDB_ERR_OPERATIONS_ERROR;
440 p = strchr(user_principal_name, '@');
444 krb5_ret = krb5_make_principal(smb_krb5_context->krb5_context, &salt_principal, realm, user_principal_name, NULL);
447 const char *samAccountName = ldb_msg_find_string(res->msgs[0], "samAccountName", NULL);
448 if (!samAccountName) {
449 ldb_set_errstring(module->ldb,
450 talloc_asprintf(mem_ctx, "password_hash_handle: "
451 "generation of new kerberos keys failed: %s has no samAccountName",
452 ldb_dn_linearize(mem_ctx, dn)));
453 talloc_free(mem_ctx);
454 return LDB_ERR_OPERATIONS_ERROR;
456 krb5_ret = krb5_make_principal(smb_krb5_context->krb5_context, &salt_principal, realm, samAccountName, NULL);
461 ldb_set_errstring(module->ldb,
462 talloc_asprintf(mem_ctx, "password_hash_handle: "
463 "generation of a saltking principal failed: %s",
464 smb_get_krb5_error_message(smb_krb5_context->krb5_context,
465 krb5_ret, mem_ctx)));
466 talloc_free(mem_ctx);
467 return LDB_ERR_OPERATIONS_ERROR;
470 /* TODO: We may wish to control the encryption types chosen in future */
471 krb5_ret = hdb_generate_key_set_password(smb_krb5_context->krb5_context,
472 salt_principal, sambaPassword, &keys, &num_keys);
473 krb5_free_principal(smb_krb5_context->krb5_context, salt_principal);
476 ldb_set_errstring(module->ldb,
477 talloc_asprintf(mem_ctx, "password_hash_handle: "
478 "generation of new kerberos keys failed: %s",
479 smb_get_krb5_error_message(smb_krb5_context->krb5_context,
480 krb5_ret, mem_ctx)));
481 talloc_free(mem_ctx);
482 return LDB_ERR_OPERATIONS_ERROR;
485 /* Walking all the key types generated, transform each
486 * key into an ASN.1 blob
488 for (i=0; i < num_keys; i++) {
494 if (keys[i].key.keytype == ENCTYPE_ARCFOUR_HMAC) {
495 /* We might end up doing this below:
496 * This ensures we get the unicode
497 * conversion right. This should also
498 * be fixed in the Heimdal libs */
501 ASN1_MALLOC_ENCODE(Key, buf, buf_size, &keys[i], &len, krb5_ret);
503 return LDB_ERR_OPERATIONS_ERROR;
506 val.data = talloc_memdup(req, buf, len);
509 if (!val.data || krb5_ret) {
510 hdb_free_keys (smb_krb5_context->krb5_context, num_keys, keys);
511 talloc_free(mem_ctx);
512 return LDB_ERR_OPERATIONS_ERROR;
514 ret = ldb_msg_add_value(modify_msg, "krb5Key", &val);
515 if (ret != LDB_SUCCESS) {
516 hdb_free_keys (smb_krb5_context->krb5_context, num_keys, keys);
517 talloc_free(mem_ctx);
522 hdb_free_keys (smb_krb5_context->krb5_context, num_keys, keys);
525 /* Possibly kill off the cleartext or store it */
526 CHECK_RET(ldb_msg_add_empty(modify_msg, "sambaPassword", LDB_FLAG_MOD_REPLACE));
528 if (sambaPassword && (pwdProperties & DOMAIN_PASSWORD_STORE_CLEARTEXT) &&
529 (userAccountControl & UF_ENCRYPTED_TEXT_PASSWORD_ALLOWED)) {
530 CHECK_RET(ldb_msg_add_string(modify_msg, "sambaPassword", sambaPassword));
533 /* Even if we didn't get a sambaPassword, we can still setup
534 * krb5Key from the NT hash.
536 * This is an append, so it works with the 'continue' in the
537 * unicode loop above, to use Samba's NT hash function, which
538 * is more correct than Heimdal's
548 key.salt = NULL; /* No salt for this enc type */
550 krb5_ret = krb5_keyblock_init(smb_krb5_context->krb5_context,
551 ENCTYPE_ARCFOUR_HMAC,
552 ntPwdHash->hash, sizeof(ntPwdHash->hash),
555 return LDB_ERR_OPERATIONS_ERROR;
557 ASN1_MALLOC_ENCODE(Key, buf, buf_size, &key, &len, krb5_ret);
559 return LDB_ERR_OPERATIONS_ERROR;
561 krb5_free_keyblock_contents(smb_krb5_context->krb5_context,
564 val.data = talloc_memdup(req, buf, len);
567 if (!val.data || ret) {
568 return LDB_ERR_OPERATIONS_ERROR;
570 CHECK_RET(ldb_msg_add_value(modify_msg, "krb5Key", &val));
573 /* If the original caller did anything with pwdLastSet then skip this. It could be an incoming samsync */
574 attribute = ldb_msg_find_element(msg, "pwdLastSet");
575 if (attribute == NULL) {
576 /* Update the password last set time */
577 unix_to_nt_time(&now_nt, now);
578 CHECK_RET(ldb_msg_add_empty(modify_msg, "pwdLastSet", LDB_FLAG_MOD_REPLACE));
579 CHECK_RET(samdb_msg_add_uint64(module->ldb, mem_ctx, modify_msg, "pwdLastSet", now_nt));
582 /* If the original caller did anything with "msDS-KeyVersionNumber" then skip this. It could be an incoming samsync */
583 attribute = ldb_msg_find_element(msg, "msDS-KeyVersionNumber");
584 if (attribute == NULL) {
586 CHECK_RET(ldb_msg_add_empty(modify_msg, "msDS-KeyVersionNumber",
587 LDB_FLAG_MOD_REPLACE));
588 CHECK_RET(samdb_msg_add_uint(module->ldb, mem_ctx, modify_msg, "msDS-KeyVersionNumber", kvno + 1));
590 /* While we should be in a transaction, go one extra
591 * step in the dance for an 'atomic' increment. This
592 * may be of value against remote LDAP servers. (Note
593 * however that Mulitmaster replication stil offers no
596 struct ldb_val old_kvno, new_kvno;
597 old_kvno.data = (uint8_t *)talloc_asprintf(mem_ctx, "%u", kvno);
598 if (!old_kvno.data) {
601 old_kvno.length = strlen((char *)old_kvno.data);
603 new_kvno.data = (uint8_t *)talloc_asprintf(mem_ctx, "%u", kvno + 1);
604 if (!new_kvno.data) {
607 new_kvno.length = strlen((char *)new_kvno.data);
609 CHECK_RET(ldb_msg_add_empty(modify_msg, "msDS-KeyVersionNumber",
610 LDB_FLAG_MOD_DELETE));
611 CHECK_RET(ldb_msg_add_empty(modify_msg, "msDS-KeyVersionNumber",
613 modify_msg->elements[modify_msg->num_elements - 2].num_values = 1;
614 modify_msg->elements[modify_msg->num_elements - 2].values = &old_kvno;
615 modify_msg->elements[modify_msg->num_elements - 1].num_values = 1;
616 modify_msg->elements[modify_msg->num_elements - 1].values = &new_kvno;
620 CHECK_RET(ldb_msg_add_empty(modify_msg, "sambaLMPwdHistory",
621 LDB_FLAG_MOD_REPLACE));
622 CHECK_RET(ldb_msg_add_empty(modify_msg, "sambaNTPwdHistory",
623 LDB_FLAG_MOD_REPLACE));
625 /* If we have something to put into the history, or an old
626 * history element to expire, update the history */
627 if (pwdHistoryLength > 0 &&
628 ((sambaNTPwdHistory_len > 0) || (sambaLMPwdHistory_len > 0)
629 || lmOldHash || ntOldHash)) {
630 /* store the password history */
631 new_sambaLMPwdHistory = talloc_array(mem_ctx, struct samr_Password,
633 if (!new_sambaLMPwdHistory) {
634 return LDB_ERR_OPERATIONS_ERROR;
636 new_sambaNTPwdHistory = talloc_array(mem_ctx, struct samr_Password,
638 if (!new_sambaNTPwdHistory) {
639 return LDB_ERR_OPERATIONS_ERROR;
641 for (i=0;i<MIN(pwdHistoryLength-1, sambaLMPwdHistory_len);i++) {
642 new_sambaLMPwdHistory[i+1] = sambaLMPwdHistory[i];
644 for (i=0;i<MIN(pwdHistoryLength-1, sambaNTPwdHistory_len);i++) {
645 new_sambaNTPwdHistory[i+1] = sambaNTPwdHistory[i];
648 /* Don't store 'long' passwords in the LM history,
649 but make sure to 'expire' one password off the other end */
651 new_sambaLMPwdHistory[0] = *lmOldHash;
653 ZERO_STRUCT(new_sambaLMPwdHistory[0]);
655 sambaLMPwdHistory_len = MIN(sambaLMPwdHistory_len + 1, pwdHistoryLength);
657 /* Likewise, we might not have an old NT password (lm
658 * only password change function on previous change) */
660 new_sambaNTPwdHistory[0] = *ntOldHash;
662 ZERO_STRUCT(new_sambaNTPwdHistory[0]);
664 sambaNTPwdHistory_len = MIN(sambaNTPwdHistory_len + 1, pwdHistoryLength);
666 CHECK_RET(samdb_msg_add_hashes(module->ldb, mem_ctx, modify_msg,
668 new_sambaLMPwdHistory,
669 sambaLMPwdHistory_len));
671 CHECK_RET(samdb_msg_add_hashes(module->ldb, mem_ctx, modify_msg,
673 new_sambaNTPwdHistory,
674 sambaNTPwdHistory_len));
677 /* Too much code above, we should check we got it close to reasonable */
678 CHECK_RET(ldb_msg_sanity_check(modify_msg));
680 modify_request = talloc(mem_ctx, struct ldb_request);
681 if (!modify_request) {
682 talloc_free(mem_ctx);
683 return LDB_ERR_OPERATIONS_ERROR;
686 modify_request->operation = LDB_REQ_MODIFY;
687 modify_request->op.mod.message = modify_msg;
688 modify_request->controls = NULL;
690 ret = ldb_next_request(module, modify_request);
692 talloc_free(mem_ctx);
696 /* add_record: do things with the sambaPassword attribute */
697 static int password_hash_add(struct ldb_module *module, struct ldb_request *req)
699 const struct ldb_message *msg = req->op.add.message;
701 ldb_debug(module->ldb, LDB_DEBUG_TRACE, "password_hash_add_record\n");
703 if (ldb_dn_is_special(msg->dn)) { /* do not manipulate our control entries */
704 return ldb_next_request(module, req);
707 return password_hash_handle(module, req, msg);
710 /* modify_record: do things with the sambaPassword attribute */
711 static int password_hash_modify(struct ldb_module *module, struct ldb_request *req)
713 const struct ldb_message *msg = req->op.mod.message;
715 ldb_debug(module->ldb, LDB_DEBUG_TRACE, "password_hash_modify_record\n");
717 if (ldb_dn_is_special(msg->dn)) { /* do not manipulate our control entries */
718 return ldb_next_request(module, req);
721 return password_hash_handle(module, req, msg);
724 static int password_hash_request(struct ldb_module *module, struct ldb_request *req)
726 switch (req->operation) {
729 return password_hash_add(module, req);
732 return password_hash_modify(module, req);
735 return ldb_next_request(module, req);
740 static const struct ldb_module_ops password_hash_ops = {
741 .name = "password_hash",
742 .request = password_hash_request
746 int password_hash_module_init(void)
748 return ldb_register_module(&password_hash_ops);