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 "../lib/util/util_ldb.h"
36 #include "param/param.h"
39 samr_ChangePasswordUser
41 NTSTATUS dcesrv_samr_ChangePasswordUser(struct dcesrv_call_state *dce_call,
43 struct samr_ChangePasswordUser *r)
45 struct dcesrv_handle *h;
46 struct samr_account_state *a_state;
47 struct ldb_context *sam_ctx;
48 struct ldb_message **res, *msg;
50 struct samr_Password new_lmPwdHash, new_ntPwdHash, checkHash;
51 struct samr_Password *lm_pwd, *nt_pwd;
52 NTSTATUS status = NT_STATUS_OK;
53 const char * const attrs[] = { "dBCSPwd", "unicodePwd" , NULL };
55 DCESRV_PULL_HANDLE(h, r->in.user_handle, SAMR_HANDLE_USER);
59 /* basic sanity checking on parameters. Do this before any database ops */
60 if (!r->in.lm_present || !r->in.nt_present ||
61 !r->in.old_lm_crypted || !r->in.new_lm_crypted ||
62 !r->in.old_nt_crypted || !r->in.new_nt_crypted) {
63 /* we should really handle a change with lm not
65 return NT_STATUS_INVALID_PARAMETER_MIX;
68 /* To change a password we need to open as system */
69 sam_ctx = samdb_connect(mem_ctx, dce_call->event_ctx, dce_call->conn->dce_ctx->lp_ctx, system_session(mem_ctx, dce_call->conn->dce_ctx->lp_ctx));
70 if (sam_ctx == NULL) {
71 return NT_STATUS_INVALID_SYSTEM_SERVICE;
74 ret = ldb_transaction_start(sam_ctx);
76 DEBUG(1, ("Failed to start transaction: %s\n", ldb_errstring(sam_ctx)));
77 return NT_STATUS_TRANSACTION_ABORTED;
80 /* fetch the old hashes */
81 ret = gendb_search_dn(sam_ctx, mem_ctx,
82 a_state->account_dn, &res, attrs);
84 ldb_transaction_cancel(sam_ctx);
85 return NT_STATUS_WRONG_PASSWORD;
89 status = samdb_result_passwords(mem_ctx, dce_call->conn->dce_ctx->lp_ctx,
90 msg, &lm_pwd, &nt_pwd);
91 if (!NT_STATUS_IS_OK(status) || !nt_pwd) {
92 ldb_transaction_cancel(sam_ctx);
93 return NT_STATUS_WRONG_PASSWORD;
96 /* decrypt and check the new lm hash */
98 D_P16(lm_pwd->hash, r->in.new_lm_crypted->hash, new_lmPwdHash.hash);
99 D_P16(new_lmPwdHash.hash, r->in.old_lm_crypted->hash, checkHash.hash);
100 if (memcmp(checkHash.hash, lm_pwd, 16) != 0) {
101 ldb_transaction_cancel(sam_ctx);
102 return NT_STATUS_WRONG_PASSWORD;
106 /* decrypt and check the new nt hash */
107 D_P16(nt_pwd->hash, r->in.new_nt_crypted->hash, new_ntPwdHash.hash);
108 D_P16(new_ntPwdHash.hash, r->in.old_nt_crypted->hash, checkHash.hash);
109 if (memcmp(checkHash.hash, nt_pwd, 16) != 0) {
110 ldb_transaction_cancel(sam_ctx);
111 return NT_STATUS_WRONG_PASSWORD;
114 /* The NT Cross is not required by Win2k3 R2, but if present
115 check the nt cross hash */
116 if (r->in.cross1_present && r->in.nt_cross && lm_pwd) {
117 D_P16(lm_pwd->hash, r->in.nt_cross->hash, checkHash.hash);
118 if (memcmp(checkHash.hash, new_ntPwdHash.hash, 16) != 0) {
119 ldb_transaction_cancel(sam_ctx);
120 return NT_STATUS_WRONG_PASSWORD;
124 /* The LM Cross is not required by Win2k3 R2, but if present
125 check the lm cross hash */
126 if (r->in.cross2_present && r->in.lm_cross && lm_pwd) {
127 D_P16(nt_pwd->hash, r->in.lm_cross->hash, checkHash.hash);
128 if (memcmp(checkHash.hash, new_lmPwdHash.hash, 16) != 0) {
129 ldb_transaction_cancel(sam_ctx);
130 return NT_STATUS_WRONG_PASSWORD;
134 msg = ldb_msg_new(mem_ctx);
136 ldb_transaction_cancel(sam_ctx);
137 return NT_STATUS_NO_MEMORY;
140 msg->dn = ldb_dn_copy(msg, a_state->account_dn);
142 ldb_transaction_cancel(sam_ctx);
143 return NT_STATUS_NO_MEMORY;
146 /* setup password modify mods on the user DN specified. This may fail
147 * due to password policies. */
148 status = samdb_set_password(sam_ctx, mem_ctx,
149 a_state->account_dn, a_state->domain_state->domain_dn,
150 msg, NULL, &new_lmPwdHash, &new_ntPwdHash,
151 true, /* this is a user password change */
154 if (!NT_STATUS_IS_OK(status)) {
155 ldb_transaction_cancel(sam_ctx);
159 /* The above call only setup the modifications, this actually
160 * makes the write to the database. */
161 ret = samdb_replace(sam_ctx, mem_ctx, msg);
163 DEBUG(2,("Failed to modify record to change password on %s: %s\n",
164 ldb_dn_get_linearized(a_state->account_dn),
165 ldb_errstring(sam_ctx)));
166 ldb_transaction_cancel(sam_ctx);
167 return NT_STATUS_INTERNAL_DB_CORRUPTION;
170 /* And this confirms it in a transaction commit */
171 ret = ldb_transaction_commit(sam_ctx);
173 DEBUG(1,("Failed to commit transaction to change password on %s: %s\n",
174 ldb_dn_get_linearized(a_state->account_dn),
175 ldb_errstring(sam_ctx)));
176 return NT_STATUS_TRANSACTION_ABORTED;
183 samr_OemChangePasswordUser2
185 NTSTATUS dcesrv_samr_OemChangePasswordUser2(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
186 struct samr_OemChangePasswordUser2 *r)
189 DATA_BLOB new_password, new_unicode_password;
191 struct samr_CryptPassword *pwbuf = r->in.password;
192 struct ldb_context *sam_ctx;
193 struct ldb_dn *user_dn;
195 struct ldb_message **res, *mod;
196 const char * const attrs[] = { "objectSid", "dBCSPwd", NULL };
197 struct samr_Password *lm_pwd;
198 DATA_BLOB lm_pwd_blob;
199 uint8_t new_lm_hash[16];
200 struct samr_Password lm_verifier;
201 size_t unicode_pw_len;
204 return NT_STATUS_INVALID_PARAMETER;
207 if (r->in.hash == NULL) {
208 return NT_STATUS_INVALID_PARAMETER;
211 /* this call can only work with lanman auth */
212 if (!lp_lanman_auth(dce_call->conn->dce_ctx->lp_ctx)) {
213 return NT_STATUS_NOT_SUPPORTED;
216 /* To change a password we need to open as system */
217 sam_ctx = samdb_connect(mem_ctx, dce_call->event_ctx, dce_call->conn->dce_ctx->lp_ctx, system_session(mem_ctx, dce_call->conn->dce_ctx->lp_ctx));
218 if (sam_ctx == NULL) {
219 return NT_STATUS_INVALID_SYSTEM_SERVICE;
222 ret = ldb_transaction_start(sam_ctx);
224 DEBUG(1, ("Failed to start transaction: %s\n", ldb_errstring(sam_ctx)));
225 return NT_STATUS_TRANSACTION_ABORTED;
228 /* we need the users dn and the domain dn (derived from the
229 user SID). We also need the current lm password hash in
230 order to decrypt the incoming password */
231 ret = gendb_search(sam_ctx,
232 mem_ctx, NULL, &res, attrs,
233 "(&(sAMAccountName=%s)(objectclass=user))",
234 r->in.account->string);
236 ldb_transaction_cancel(sam_ctx);
237 /* Don't give the game away: (don't allow anonymous users to prove the existance of usernames) */
238 return NT_STATUS_WRONG_PASSWORD;
241 user_dn = res[0]->dn;
243 status = samdb_result_passwords(mem_ctx, dce_call->conn->dce_ctx->lp_ctx,
244 res[0], &lm_pwd, NULL);
245 if (!NT_STATUS_IS_OK(status) || !lm_pwd) {
246 ldb_transaction_cancel(sam_ctx);
247 return NT_STATUS_WRONG_PASSWORD;
250 /* decrypt the password we have been given */
251 lm_pwd_blob = data_blob(lm_pwd->hash, sizeof(lm_pwd->hash));
252 arcfour_crypt_blob(pwbuf->data, 516, &lm_pwd_blob);
253 data_blob_free(&lm_pwd_blob);
255 if (!extract_pw_from_buffer(mem_ctx, pwbuf->data, &new_password)) {
256 ldb_transaction_cancel(sam_ctx);
257 DEBUG(3,("samr: failed to decode password buffer\n"));
258 return NT_STATUS_WRONG_PASSWORD;
261 if (!convert_string_talloc_convenience(mem_ctx, lp_iconv_convenience(dce_call->conn->dce_ctx->lp_ctx),
263 (const char *)new_password.data,
265 (void **)&new_pass, NULL, false)) {
266 DEBUG(3,("samr: failed to convert incoming password buffer to unix charset\n"));
267 ldb_transaction_cancel(sam_ctx);
268 return NT_STATUS_WRONG_PASSWORD;
271 if (!convert_string_talloc_convenience(mem_ctx, lp_iconv_convenience(dce_call->conn->dce_ctx->lp_ctx),
273 (const char *)new_password.data,
275 (void **)&new_unicode_password.data, &unicode_pw_len, false)) {
276 DEBUG(3,("samr: failed to convert incoming password buffer to UTF16 charset\n"));
277 ldb_transaction_cancel(sam_ctx);
278 return NT_STATUS_WRONG_PASSWORD;
280 new_unicode_password.length = unicode_pw_len;
282 E_deshash(new_pass, new_lm_hash);
283 E_old_pw_hash(new_lm_hash, lm_pwd->hash, lm_verifier.hash);
284 if (memcmp(lm_verifier.hash, r->in.hash->hash, 16) != 0) {
285 ldb_transaction_cancel(sam_ctx);
286 return NT_STATUS_WRONG_PASSWORD;
289 mod = ldb_msg_new(mem_ctx);
291 ldb_transaction_cancel(sam_ctx);
292 return NT_STATUS_NO_MEMORY;
295 mod->dn = ldb_dn_copy(mod, user_dn);
297 ldb_transaction_cancel(sam_ctx);
298 return NT_STATUS_NO_MEMORY;
301 /* set the password on the user DN specified. This may fail
302 * due to password policies */
303 status = samdb_set_password(sam_ctx, mem_ctx,
305 mod, &new_unicode_password,
307 true, /* this is a user password change */
310 if (!NT_STATUS_IS_OK(status)) {
311 ldb_transaction_cancel(sam_ctx);
315 /* The above call only setup the modifications, this actually
316 * makes the write to the database. */
317 ret = samdb_replace(sam_ctx, mem_ctx, mod);
319 DEBUG(2,("Failed to modify record to change password on %s: %s\n",
320 ldb_dn_get_linearized(user_dn),
321 ldb_errstring(sam_ctx)));
322 ldb_transaction_cancel(sam_ctx);
323 return NT_STATUS_INTERNAL_DB_CORRUPTION;
326 /* And this confirms it in a transaction commit */
327 ret = ldb_transaction_commit(sam_ctx);
329 DEBUG(1,("Failed to commit transaction to change password on %s: %s\n",
330 ldb_dn_get_linearized(user_dn),
331 ldb_errstring(sam_ctx)));
332 return NT_STATUS_TRANSACTION_ABORTED;
340 samr_ChangePasswordUser3
342 NTSTATUS dcesrv_samr_ChangePasswordUser3(struct dcesrv_call_state *dce_call,
344 struct samr_ChangePasswordUser3 *r)
347 DATA_BLOB new_password;
348 struct ldb_context *sam_ctx = NULL;
349 struct ldb_dn *user_dn;
351 struct ldb_message **res, *mod;
352 const char * const attrs[] = { "unicodePwd", "dBCSPwd", NULL };
353 struct samr_Password *nt_pwd, *lm_pwd;
354 DATA_BLOB nt_pwd_blob;
355 struct samr_DomInfo1 *dominfo = NULL;
356 struct samr_ChangeReject *reject = NULL;
357 enum samr_RejectReason reason = SAMR_REJECT_OTHER;
358 uint8_t new_nt_hash[16], new_lm_hash[16];
359 struct samr_Password nt_verifier, lm_verifier;
361 *r->out.dominfo = NULL;
362 *r->out.reject = NULL;
364 if (r->in.nt_password == NULL ||
365 r->in.nt_verifier == NULL) {
366 return NT_STATUS_INVALID_PARAMETER;
369 /* To change a password we need to open as system */
370 sam_ctx = samdb_connect(mem_ctx, dce_call->event_ctx, dce_call->conn->dce_ctx->lp_ctx, system_session(mem_ctx, dce_call->conn->dce_ctx->lp_ctx));
371 if (sam_ctx == NULL) {
372 return NT_STATUS_INVALID_SYSTEM_SERVICE;
375 ret = ldb_transaction_start(sam_ctx);
377 talloc_free(sam_ctx);
378 DEBUG(1, ("Failed to start transaction: %s\n", ldb_errstring(sam_ctx)));
379 return NT_STATUS_TRANSACTION_ABORTED;
382 /* we need the users dn and the domain dn (derived from the
383 user SID). We also need the current lm and nt password hashes
384 in order to decrypt the incoming passwords */
385 ret = gendb_search(sam_ctx,
386 mem_ctx, NULL, &res, attrs,
387 "(&(sAMAccountName=%s)(objectclass=user))",
388 r->in.account->string);
390 /* Don't give the game away: (don't allow anonymous users to prove the existance of usernames) */
391 status = NT_STATUS_WRONG_PASSWORD;
395 user_dn = res[0]->dn;
397 status = samdb_result_passwords(mem_ctx, dce_call->conn->dce_ctx->lp_ctx,
398 res[0], &lm_pwd, &nt_pwd);
399 if (!NT_STATUS_IS_OK(status) ) {
404 status = NT_STATUS_WRONG_PASSWORD;
408 /* decrypt the password we have been given */
409 nt_pwd_blob = data_blob(nt_pwd->hash, sizeof(nt_pwd->hash));
410 arcfour_crypt_blob(r->in.nt_password->data, 516, &nt_pwd_blob);
411 data_blob_free(&nt_pwd_blob);
413 if (!extract_pw_from_buffer(mem_ctx, r->in.nt_password->data, &new_password)) {
414 ldb_transaction_cancel(sam_ctx);
415 DEBUG(3,("samr: failed to decode password buffer\n"));
416 return NT_STATUS_WRONG_PASSWORD;
419 if (r->in.nt_verifier == NULL) {
420 status = NT_STATUS_WRONG_PASSWORD;
424 /* check NT verifier */
425 mdfour(new_nt_hash, new_password.data, new_password.length);
427 E_old_pw_hash(new_nt_hash, nt_pwd->hash, nt_verifier.hash);
428 if (memcmp(nt_verifier.hash, r->in.nt_verifier->hash, 16) != 0) {
429 status = NT_STATUS_WRONG_PASSWORD;
433 /* check LM verifier (really not needed as we just checked the
434 * much stronger NT hash, but the RPC-SAMR test checks for
436 if (lm_pwd && r->in.lm_verifier != NULL) {
438 if (!convert_string_talloc_convenience(mem_ctx, lp_iconv_convenience(dce_call->conn->dce_ctx->lp_ctx),
440 (const char *)new_password.data,
442 (void **)&new_pass, NULL, false)) {
443 E_deshash(new_pass, new_lm_hash);
444 E_old_pw_hash(new_nt_hash, lm_pwd->hash, lm_verifier.hash);
445 if (memcmp(lm_verifier.hash, r->in.lm_verifier->hash, 16) != 0) {
446 status = NT_STATUS_WRONG_PASSWORD;
452 mod = ldb_msg_new(mem_ctx);
454 status = NT_STATUS_NO_MEMORY;
458 mod->dn = ldb_dn_copy(mod, user_dn);
460 status = NT_STATUS_NO_MEMORY;
464 /* set the password on the user DN specified. This may fail
465 * due to password policies */
466 status = samdb_set_password(sam_ctx, mem_ctx,
470 true, /* this is a user password change */
473 if (!NT_STATUS_IS_OK(status)) {
477 /* The above call only setup the modifications, this actually
478 * makes the write to the database. */
479 ret = samdb_replace(sam_ctx, mem_ctx, mod);
481 DEBUG(2,("samdb_replace failed to change password for %s: %s\n",
482 ldb_dn_get_linearized(user_dn),
483 ldb_errstring(sam_ctx)));
484 status = NT_STATUS_UNSUCCESSFUL;
488 /* And this confirms it in a transaction commit */
489 ret = ldb_transaction_commit(sam_ctx);
491 DEBUG(1,("Failed to commit transaction to change password on %s: %s\n",
492 ldb_dn_get_linearized(user_dn),
493 ldb_errstring(sam_ctx)));
494 status = NT_STATUS_TRANSACTION_ABORTED;
501 ldb_transaction_cancel(sam_ctx);
502 talloc_free(sam_ctx);
504 reject = talloc(mem_ctx, struct samr_ChangeReject);
505 *r->out.dominfo = dominfo;
506 *r->out.reject = reject;
508 if (reject == NULL) {
511 ZERO_STRUCTP(reject);
513 reject->reason = reason;
520 samr_ChangePasswordUser2
522 easy - just a subset of samr_ChangePasswordUser3
524 NTSTATUS dcesrv_samr_ChangePasswordUser2(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
525 struct samr_ChangePasswordUser2 *r)
527 struct samr_ChangePasswordUser3 r2;
528 struct samr_DomInfo1 *dominfo = NULL;
529 struct samr_ChangeReject *reject = NULL;
531 r2.in.server = r->in.server;
532 r2.in.account = r->in.account;
533 r2.in.nt_password = r->in.nt_password;
534 r2.in.nt_verifier = r->in.nt_verifier;
535 r2.in.lm_change = r->in.lm_change;
536 r2.in.lm_password = r->in.lm_password;
537 r2.in.lm_verifier = r->in.lm_verifier;
538 r2.in.password3 = NULL;
539 r2.out.dominfo = &dominfo;
540 r2.out.reject = &reject;
542 return dcesrv_samr_ChangePasswordUser3(dce_call, mem_ctx, &r2);
547 set password via a samr_CryptPassword buffer
548 this will in the 'msg' with modify operations that will update the user
549 password when applied
551 NTSTATUS samr_set_password(struct dcesrv_call_state *dce_call,
553 struct ldb_dn *account_dn, struct ldb_dn *domain_dn,
555 struct ldb_message *msg,
556 struct samr_CryptPassword *pwbuf)
559 DATA_BLOB new_password;
560 DATA_BLOB session_key = data_blob(NULL, 0);
562 nt_status = dcesrv_fetch_session_key(dce_call->conn, &session_key);
563 if (!NT_STATUS_IS_OK(nt_status)) {
567 arcfour_crypt_blob(pwbuf->data, 516, &session_key);
569 if (!extract_pw_from_buffer(mem_ctx, pwbuf->data, &new_password)) {
570 DEBUG(3,("samr: failed to decode password buffer\n"));
571 return NT_STATUS_WRONG_PASSWORD;
574 /* set the password - samdb needs to know both the domain and user DNs,
575 so the domain password policy can be used */
576 return samdb_set_password(sam_ctx, mem_ctx,
577 account_dn, domain_dn,
580 false, /* This is a password set, not change */
586 set password via a samr_CryptPasswordEx buffer
587 this will in the 'msg' with modify operations that will update the user
588 password when applied
590 NTSTATUS samr_set_password_ex(struct dcesrv_call_state *dce_call,
591 struct ldb_context *sam_ctx,
592 struct ldb_dn *account_dn, struct ldb_dn *domain_dn,
594 struct ldb_message *msg,
595 struct samr_CryptPasswordEx *pwbuf)
598 DATA_BLOB new_password;
599 DATA_BLOB co_session_key;
600 DATA_BLOB session_key = data_blob(NULL, 0);
601 struct MD5Context ctx;
603 nt_status = dcesrv_fetch_session_key(dce_call->conn, &session_key);
604 if (!NT_STATUS_IS_OK(nt_status)) {
608 co_session_key = data_blob_talloc(mem_ctx, NULL, 16);
609 if (!co_session_key.data) {
610 return NT_STATUS_NO_MEMORY;
614 MD5Update(&ctx, &pwbuf->data[516], 16);
615 MD5Update(&ctx, session_key.data, session_key.length);
616 MD5Final(co_session_key.data, &ctx);
618 arcfour_crypt_blob(pwbuf->data, 516, &co_session_key);
620 if (!extract_pw_from_buffer(mem_ctx, pwbuf->data, &new_password)) {
621 DEBUG(3,("samr: failed to decode password buffer\n"));
622 return NT_STATUS_WRONG_PASSWORD;
625 /* set the password - samdb needs to know both the domain and user DNs,
626 so the domain password policy can be used */
627 return samdb_set_password(sam_ctx, mem_ctx,
628 account_dn, domain_dn,
631 false, /* This is a password set, not change */