2 Unix SMB/CIFS implementation.
4 samr server password set/change handling
6 Copyright (C) Andrew Tridgell 2004
7 Copyright (C) Andrew Bartlett <abartlet@samba.org> 2005
9 This program is free software; you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation; either version 3 of the License, or
12 (at your option) any later version.
14 This program is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License for more details.
19 You should have received a copy of the GNU General Public License
20 along with this program. If not, see <http://www.gnu.org/licenses/>.
24 #include "rpc_server/dcerpc_server.h"
25 #include "rpc_server/common/common.h"
26 #include "rpc_server/samr/dcesrv_samr.h"
27 #include "system/time.h"
28 #include "lib/crypto/crypto.h"
29 #include "dsdb/common/flags.h"
30 #include "libcli/ldap/ldap.h"
31 #include "dsdb/samdb/samdb.h"
32 #include "auth/auth.h"
33 #include "rpc_server/samr/proto.h"
34 #include "libcli/auth/libcli_auth.h"
35 #include "util/util_ldb.h"
36 #include "param/param.h"
39 samr_ChangePasswordUser
41 NTSTATUS dcesrv_samr_ChangePasswordUser(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
42 struct samr_ChangePasswordUser *r)
44 struct dcesrv_handle *h;
45 struct samr_account_state *a_state;
46 struct ldb_context *sam_ctx;
47 struct ldb_message **res, *msg;
49 struct samr_Password new_lmPwdHash, new_ntPwdHash, checkHash;
50 struct samr_Password *lm_pwd, *nt_pwd;
51 NTSTATUS status = NT_STATUS_OK;
52 const char * const attrs[] = { "dBCSPwd", "unicodePwd" , NULL };
54 DCESRV_PULL_HANDLE(h, r->in.user_handle, SAMR_HANDLE_USER);
58 /* basic sanity checking on parameters. Do this before any database ops */
59 if (!r->in.lm_present || !r->in.nt_present ||
60 !r->in.old_lm_crypted || !r->in.new_lm_crypted ||
61 !r->in.old_nt_crypted || !r->in.new_nt_crypted) {
62 /* we should really handle a change with lm not
64 return NT_STATUS_INVALID_PARAMETER_MIX;
67 /* To change a password we need to open as system */
68 sam_ctx = samdb_connect(mem_ctx, global_loadparm, system_session(mem_ctx, global_loadparm));
69 if (sam_ctx == NULL) {
70 return NT_STATUS_INVALID_SYSTEM_SERVICE;
73 ret = ldb_transaction_start(sam_ctx);
75 DEBUG(1, ("Failed to start transaction: %s\n", ldb_errstring(sam_ctx)));
76 return NT_STATUS_TRANSACTION_ABORTED;
79 /* fetch the old hashes */
80 ret = gendb_search_dn(sam_ctx, mem_ctx,
81 a_state->account_dn, &res, attrs);
83 ldb_transaction_cancel(sam_ctx);
84 return NT_STATUS_WRONG_PASSWORD;
88 status = samdb_result_passwords(mem_ctx, msg, &lm_pwd, &nt_pwd);
89 if (!NT_STATUS_IS_OK(status) || !lm_pwd || !nt_pwd) {
90 ldb_transaction_cancel(sam_ctx);
91 return NT_STATUS_WRONG_PASSWORD;
94 /* decrypt and check the new lm hash */
95 D_P16(lm_pwd->hash, r->in.new_lm_crypted->hash, new_lmPwdHash.hash);
96 D_P16(new_lmPwdHash.hash, r->in.old_lm_crypted->hash, checkHash.hash);
97 if (memcmp(checkHash.hash, lm_pwd, 16) != 0) {
98 ldb_transaction_cancel(sam_ctx);
99 return NT_STATUS_WRONG_PASSWORD;
102 /* decrypt and check the new nt hash */
103 D_P16(nt_pwd->hash, r->in.new_nt_crypted->hash, new_ntPwdHash.hash);
104 D_P16(new_ntPwdHash.hash, r->in.old_nt_crypted->hash, checkHash.hash);
105 if (memcmp(checkHash.hash, nt_pwd, 16) != 0) {
106 ldb_transaction_cancel(sam_ctx);
107 return NT_STATUS_WRONG_PASSWORD;
110 /* The NT Cross is not required by Win2k3 R2, but if present
111 check the nt cross hash */
112 if (r->in.cross1_present && r->in.nt_cross) {
113 D_P16(lm_pwd->hash, r->in.nt_cross->hash, checkHash.hash);
114 if (memcmp(checkHash.hash, new_ntPwdHash.hash, 16) != 0) {
115 ldb_transaction_cancel(sam_ctx);
116 return NT_STATUS_WRONG_PASSWORD;
120 /* The LM Cross is not required by Win2k3 R2, but if present
121 check the lm cross hash */
122 if (r->in.cross2_present && r->in.lm_cross) {
123 D_P16(nt_pwd->hash, r->in.lm_cross->hash, checkHash.hash);
124 if (memcmp(checkHash.hash, new_lmPwdHash.hash, 16) != 0) {
125 ldb_transaction_cancel(sam_ctx);
126 return NT_STATUS_WRONG_PASSWORD;
130 msg = ldb_msg_new(mem_ctx);
132 ldb_transaction_cancel(sam_ctx);
133 return NT_STATUS_NO_MEMORY;
136 msg->dn = ldb_dn_copy(msg, a_state->account_dn);
138 ldb_transaction_cancel(sam_ctx);
139 return NT_STATUS_NO_MEMORY;
142 /* setup password modify mods on the user DN specified. This may fail
143 * due to password policies. */
144 status = samdb_set_password(sam_ctx, mem_ctx,
145 a_state->account_dn, a_state->domain_state->domain_dn,
146 msg, NULL, &new_lmPwdHash, &new_ntPwdHash,
147 true, /* this is a user password change */
150 if (!NT_STATUS_IS_OK(status)) {
151 ldb_transaction_cancel(sam_ctx);
155 /* The above call only setup the modifications, this actually
156 * makes the write to the database. */
157 ret = samdb_replace(sam_ctx, mem_ctx, msg);
159 DEBUG(2,("Failed to modify record to change password on %s: %s\n",
160 ldb_dn_get_linearized(a_state->account_dn),
161 ldb_errstring(sam_ctx)));
162 ldb_transaction_cancel(sam_ctx);
163 return NT_STATUS_INTERNAL_DB_CORRUPTION;
166 /* And this confirms it in a transaction commit */
167 ret = ldb_transaction_commit(sam_ctx);
169 DEBUG(1,("Failed to commit transaction to change password on %s: %s\n",
170 ldb_dn_get_linearized(a_state->account_dn),
171 ldb_errstring(sam_ctx)));
172 return NT_STATUS_TRANSACTION_ABORTED;
179 samr_OemChangePasswordUser2
181 NTSTATUS dcesrv_samr_OemChangePasswordUser2(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
182 struct samr_OemChangePasswordUser2 *r)
186 uint32_t new_pass_len;
187 struct samr_CryptPassword *pwbuf = r->in.password;
188 struct ldb_context *sam_ctx;
189 struct ldb_dn *user_dn;
191 struct ldb_message **res, *mod;
192 const char * const attrs[] = { "objectSid", "dBCSPwd", NULL };
193 struct samr_Password *lm_pwd;
194 DATA_BLOB lm_pwd_blob;
195 uint8_t new_lm_hash[16];
196 struct samr_Password lm_verifier;
199 return NT_STATUS_INVALID_PARAMETER;
202 if (r->in.hash == NULL) {
203 return NT_STATUS_INVALID_PARAMETER;
206 /* To change a password we need to open as system */
207 sam_ctx = samdb_connect(mem_ctx, global_loadparm, system_session(mem_ctx, global_loadparm));
208 if (sam_ctx == NULL) {
209 return NT_STATUS_INVALID_SYSTEM_SERVICE;
212 ret = ldb_transaction_start(sam_ctx);
214 DEBUG(1, ("Failed to start transaction: %s\n", ldb_errstring(sam_ctx)));
215 return NT_STATUS_TRANSACTION_ABORTED;
218 /* we need the users dn and the domain dn (derived from the
219 user SID). We also need the current lm password hash in
220 order to decrypt the incoming password */
221 ret = gendb_search(sam_ctx,
222 mem_ctx, NULL, &res, attrs,
223 "(&(sAMAccountName=%s)(objectclass=user))",
224 r->in.account->string);
226 ldb_transaction_cancel(sam_ctx);
227 /* Don't give the game away: (don't allow anonymous users to prove the existance of usernames) */
228 return NT_STATUS_WRONG_PASSWORD;
231 user_dn = res[0]->dn;
233 status = samdb_result_passwords(mem_ctx, res[0], &lm_pwd, NULL);
234 if (!NT_STATUS_IS_OK(status) || !lm_pwd) {
235 ldb_transaction_cancel(sam_ctx);
236 return NT_STATUS_WRONG_PASSWORD;
239 /* decrypt the password we have been given */
240 lm_pwd_blob = data_blob(lm_pwd->hash, sizeof(lm_pwd->hash));
241 arcfour_crypt_blob(pwbuf->data, 516, &lm_pwd_blob);
242 data_blob_free(&lm_pwd_blob);
244 if (!decode_pw_buffer(pwbuf->data, new_pass, sizeof(new_pass),
245 &new_pass_len, STR_ASCII)) {
246 ldb_transaction_cancel(sam_ctx);
247 DEBUG(3,("samr: failed to decode password buffer\n"));
248 return NT_STATUS_WRONG_PASSWORD;
251 /* check LM verifier */
252 if (lm_pwd == NULL) {
253 ldb_transaction_cancel(sam_ctx);
254 return NT_STATUS_WRONG_PASSWORD;
257 E_deshash(new_pass, new_lm_hash);
258 E_old_pw_hash(new_lm_hash, lm_pwd->hash, lm_verifier.hash);
259 if (memcmp(lm_verifier.hash, r->in.hash->hash, 16) != 0) {
260 ldb_transaction_cancel(sam_ctx);
261 return NT_STATUS_WRONG_PASSWORD;
264 mod = ldb_msg_new(mem_ctx);
266 ldb_transaction_cancel(sam_ctx);
267 return NT_STATUS_NO_MEMORY;
270 mod->dn = ldb_dn_copy(mod, user_dn);
272 ldb_transaction_cancel(sam_ctx);
273 return NT_STATUS_NO_MEMORY;
276 /* set the password on the user DN specified. This may fail
277 * due to password policies */
278 status = samdb_set_password(sam_ctx, mem_ctx,
282 true, /* this is a user password change */
285 if (!NT_STATUS_IS_OK(status)) {
286 ldb_transaction_cancel(sam_ctx);
290 /* The above call only setup the modifications, this actually
291 * makes the write to the database. */
292 ret = samdb_replace(sam_ctx, mem_ctx, mod);
294 DEBUG(2,("Failed to modify record to change password on %s: %s\n",
295 ldb_dn_get_linearized(user_dn),
296 ldb_errstring(sam_ctx)));
297 ldb_transaction_cancel(sam_ctx);
298 return NT_STATUS_INTERNAL_DB_CORRUPTION;
301 /* And this confirms it in a transaction commit */
302 ret = ldb_transaction_commit(sam_ctx);
304 DEBUG(1,("Failed to commit transaction to change password on %s: %s\n",
305 ldb_dn_get_linearized(user_dn),
306 ldb_errstring(sam_ctx)));
307 return NT_STATUS_TRANSACTION_ABORTED;
315 samr_ChangePasswordUser3
317 NTSTATUS dcesrv_samr_ChangePasswordUser3(struct dcesrv_call_state *dce_call,
319 struct samr_ChangePasswordUser3 *r)
323 uint32_t new_pass_len;
324 struct ldb_context *sam_ctx = NULL;
325 struct ldb_dn *user_dn;
327 struct ldb_message **res, *mod;
328 const char * const attrs[] = { "unicodePwd", "dBCSPwd", NULL };
329 struct samr_Password *nt_pwd, *lm_pwd;
330 DATA_BLOB nt_pwd_blob;
331 struct samr_DomInfo1 *dominfo = NULL;
332 struct samr_ChangeReject *reject = NULL;
333 enum samr_RejectReason reason = SAMR_REJECT_OTHER;
334 uint8_t new_nt_hash[16], new_lm_hash[16];
335 struct samr_Password nt_verifier, lm_verifier;
339 if (r->in.nt_password == NULL ||
340 r->in.nt_verifier == NULL) {
341 return NT_STATUS_INVALID_PARAMETER;
344 /* To change a password we need to open as system */
345 sam_ctx = samdb_connect(mem_ctx, global_loadparm, system_session(mem_ctx, global_loadparm));
346 if (sam_ctx == NULL) {
347 return NT_STATUS_INVALID_SYSTEM_SERVICE;
350 ret = ldb_transaction_start(sam_ctx);
352 talloc_free(sam_ctx);
353 DEBUG(1, ("Failed to start transaction: %s\n", ldb_errstring(sam_ctx)));
354 return NT_STATUS_TRANSACTION_ABORTED;
357 /* we need the users dn and the domain dn (derived from the
358 user SID). We also need the current lm and nt password hashes
359 in order to decrypt the incoming passwords */
360 ret = gendb_search(sam_ctx,
361 mem_ctx, NULL, &res, attrs,
362 "(&(sAMAccountName=%s)(objectclass=user))",
363 r->in.account->string);
365 /* Don't give the game away: (don't allow anonymous users to prove the existance of usernames) */
366 status = NT_STATUS_WRONG_PASSWORD;
370 user_dn = res[0]->dn;
372 status = samdb_result_passwords(mem_ctx, res[0], &lm_pwd, &nt_pwd);
373 if (!NT_STATUS_IS_OK(status) ) {
378 status = NT_STATUS_WRONG_PASSWORD;
382 /* decrypt the password we have been given */
383 nt_pwd_blob = data_blob(nt_pwd->hash, sizeof(nt_pwd->hash));
384 arcfour_crypt_blob(r->in.nt_password->data, 516, &nt_pwd_blob);
385 data_blob_free(&nt_pwd_blob);
387 if (!decode_pw_buffer(r->in.nt_password->data, new_pass, sizeof(new_pass),
388 &new_pass_len, STR_UNICODE)) {
389 DEBUG(3,("samr: failed to decode password buffer\n"));
390 status = NT_STATUS_WRONG_PASSWORD;
394 if (r->in.nt_verifier == NULL) {
395 status = NT_STATUS_WRONG_PASSWORD;
399 /* check NT verifier */
400 E_md4hash(new_pass, new_nt_hash);
401 E_old_pw_hash(new_nt_hash, nt_pwd->hash, nt_verifier.hash);
402 if (memcmp(nt_verifier.hash, r->in.nt_verifier->hash, 16) != 0) {
403 status = NT_STATUS_WRONG_PASSWORD;
407 /* check LM verifier */
408 if (lm_pwd && r->in.lm_verifier != NULL) {
409 E_deshash(new_pass, new_lm_hash);
410 E_old_pw_hash(new_nt_hash, lm_pwd->hash, lm_verifier.hash);
411 if (memcmp(lm_verifier.hash, r->in.lm_verifier->hash, 16) != 0) {
412 status = NT_STATUS_WRONG_PASSWORD;
418 mod = ldb_msg_new(mem_ctx);
420 return NT_STATUS_NO_MEMORY;
423 mod->dn = ldb_dn_copy(mod, user_dn);
425 status = NT_STATUS_NO_MEMORY;
429 /* set the password on the user DN specified. This may fail
430 * due to password policies */
431 status = samdb_set_password(sam_ctx, mem_ctx,
435 true, /* this is a user password change */
438 if (!NT_STATUS_IS_OK(status)) {
442 /* The above call only setup the modifications, this actually
443 * makes the write to the database. */
444 ret = samdb_replace(sam_ctx, mem_ctx, mod);
446 DEBUG(2,("samdb_replace failed to change password for %s: %s\n",
447 ldb_dn_get_linearized(user_dn),
448 ldb_errstring(sam_ctx)));
449 status = NT_STATUS_UNSUCCESSFUL;
453 /* And this confirms it in a transaction commit */
454 ret = ldb_transaction_commit(sam_ctx);
456 DEBUG(1,("Failed to commit transaction to change password on %s: %s\n",
457 ldb_dn_get_linearized(user_dn),
458 ldb_errstring(sam_ctx)));
459 status = NT_STATUS_TRANSACTION_ABORTED;
466 ldb_transaction_cancel(sam_ctx);
467 talloc_free(sam_ctx);
469 reject = talloc(mem_ctx, struct samr_ChangeReject);
470 r->out.dominfo = dominfo;
471 r->out.reject = reject;
473 if (reject == NULL) {
476 ZERO_STRUCTP(reject);
478 reject->reason = reason;
485 samr_ChangePasswordUser2
487 easy - just a subset of samr_ChangePasswordUser3
489 NTSTATUS dcesrv_samr_ChangePasswordUser2(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
490 struct samr_ChangePasswordUser2 *r)
492 struct samr_ChangePasswordUser3 r2;
494 r2.in.server = r->in.server;
495 r2.in.account = r->in.account;
496 r2.in.nt_password = r->in.nt_password;
497 r2.in.nt_verifier = r->in.nt_verifier;
498 r2.in.lm_change = r->in.lm_change;
499 r2.in.lm_password = r->in.lm_password;
500 r2.in.lm_verifier = r->in.lm_verifier;
501 r2.in.password3 = NULL;
503 return dcesrv_samr_ChangePasswordUser3(dce_call, mem_ctx, &r2);
508 set password via a samr_CryptPassword buffer
509 this will in the 'msg' with modify operations that will update the user
510 password when applied
512 NTSTATUS samr_set_password(struct dcesrv_call_state *dce_call,
514 struct ldb_dn *account_dn, struct ldb_dn *domain_dn,
516 struct ldb_message *msg,
517 struct samr_CryptPassword *pwbuf)
521 uint32_t new_pass_len;
522 DATA_BLOB session_key = data_blob(NULL, 0);
524 nt_status = dcesrv_fetch_session_key(dce_call->conn, &session_key);
525 if (!NT_STATUS_IS_OK(nt_status)) {
529 arcfour_crypt_blob(pwbuf->data, 516, &session_key);
531 if (!decode_pw_buffer(pwbuf->data, new_pass, sizeof(new_pass),
532 &new_pass_len, STR_UNICODE)) {
533 DEBUG(3,("samr: failed to decode password buffer\n"));
534 return NT_STATUS_WRONG_PASSWORD;
537 /* set the password - samdb needs to know both the domain and user DNs,
538 so the domain password policy can be used */
539 return samdb_set_password(sam_ctx, mem_ctx,
540 account_dn, domain_dn,
543 false, /* This is a password set, not change */
549 set password via a samr_CryptPasswordEx buffer
550 this will in the 'msg' with modify operations that will update the user
551 password when applied
553 NTSTATUS samr_set_password_ex(struct dcesrv_call_state *dce_call,
554 struct ldb_context *sam_ctx,
555 struct ldb_dn *account_dn, struct ldb_dn *domain_dn,
557 struct ldb_message *msg,
558 struct samr_CryptPasswordEx *pwbuf)
562 uint32_t new_pass_len;
563 DATA_BLOB co_session_key;
564 DATA_BLOB session_key = data_blob(NULL, 0);
565 struct MD5Context ctx;
567 nt_status = dcesrv_fetch_session_key(dce_call->conn, &session_key);
568 if (!NT_STATUS_IS_OK(nt_status)) {
572 co_session_key = data_blob_talloc(mem_ctx, NULL, 16);
573 if (!co_session_key.data) {
574 return NT_STATUS_NO_MEMORY;
578 MD5Update(&ctx, &pwbuf->data[516], 16);
579 MD5Update(&ctx, session_key.data, session_key.length);
580 MD5Final(co_session_key.data, &ctx);
582 arcfour_crypt_blob(pwbuf->data, 516, &co_session_key);
584 if (!decode_pw_buffer(pwbuf->data, new_pass, sizeof(new_pass),
585 &new_pass_len, STR_UNICODE)) {
586 DEBUG(3,("samr: failed to decode password buffer\n"));
587 return NT_STATUS_WRONG_PASSWORD;
590 /* set the password - samdb needs to know both the domain and user DNs,
591 so the domain password policy can be used */
592 return samdb_set_password(sam_ctx, mem_ctx,
593 account_dn, domain_dn,
596 false, /* This is a password set, not change */