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) || !lm_pwd || !nt_pwd) {
92 ldb_transaction_cancel(sam_ctx);
93 return NT_STATUS_WRONG_PASSWORD;
96 /* decrypt and check the new lm hash */
97 D_P16(lm_pwd->hash, r->in.new_lm_crypted->hash, new_lmPwdHash.hash);
98 D_P16(new_lmPwdHash.hash, r->in.old_lm_crypted->hash, checkHash.hash);
99 if (memcmp(checkHash.hash, lm_pwd, 16) != 0) {
100 ldb_transaction_cancel(sam_ctx);
101 return NT_STATUS_WRONG_PASSWORD;
104 /* decrypt and check the new nt hash */
105 D_P16(nt_pwd->hash, r->in.new_nt_crypted->hash, new_ntPwdHash.hash);
106 D_P16(new_ntPwdHash.hash, r->in.old_nt_crypted->hash, checkHash.hash);
107 if (memcmp(checkHash.hash, nt_pwd, 16) != 0) {
108 ldb_transaction_cancel(sam_ctx);
109 return NT_STATUS_WRONG_PASSWORD;
112 /* The NT Cross is not required by Win2k3 R2, but if present
113 check the nt cross hash */
114 if (r->in.cross1_present && r->in.nt_cross) {
115 D_P16(lm_pwd->hash, r->in.nt_cross->hash, checkHash.hash);
116 if (memcmp(checkHash.hash, new_ntPwdHash.hash, 16) != 0) {
117 ldb_transaction_cancel(sam_ctx);
118 return NT_STATUS_WRONG_PASSWORD;
122 /* The LM Cross is not required by Win2k3 R2, but if present
123 check the lm cross hash */
124 if (r->in.cross2_present && r->in.lm_cross) {
125 D_P16(nt_pwd->hash, r->in.lm_cross->hash, checkHash.hash);
126 if (memcmp(checkHash.hash, new_lmPwdHash.hash, 16) != 0) {
127 ldb_transaction_cancel(sam_ctx);
128 return NT_STATUS_WRONG_PASSWORD;
132 msg = ldb_msg_new(mem_ctx);
134 ldb_transaction_cancel(sam_ctx);
135 return NT_STATUS_NO_MEMORY;
138 msg->dn = ldb_dn_copy(msg, a_state->account_dn);
140 ldb_transaction_cancel(sam_ctx);
141 return NT_STATUS_NO_MEMORY;
144 /* setup password modify mods on the user DN specified. This may fail
145 * due to password policies. */
146 status = samdb_set_password(sam_ctx, mem_ctx,
147 a_state->account_dn, a_state->domain_state->domain_dn,
148 msg, NULL, &new_lmPwdHash, &new_ntPwdHash,
149 true, /* this is a user password change */
152 if (!NT_STATUS_IS_OK(status)) {
153 ldb_transaction_cancel(sam_ctx);
157 /* The above call only setup the modifications, this actually
158 * makes the write to the database. */
159 ret = samdb_replace(sam_ctx, mem_ctx, msg);
161 DEBUG(2,("Failed to modify record to change password on %s: %s\n",
162 ldb_dn_get_linearized(a_state->account_dn),
163 ldb_errstring(sam_ctx)));
164 ldb_transaction_cancel(sam_ctx);
165 return NT_STATUS_INTERNAL_DB_CORRUPTION;
168 /* And this confirms it in a transaction commit */
169 ret = ldb_transaction_commit(sam_ctx);
171 DEBUG(1,("Failed to commit transaction to change password on %s: %s\n",
172 ldb_dn_get_linearized(a_state->account_dn),
173 ldb_errstring(sam_ctx)));
174 return NT_STATUS_TRANSACTION_ABORTED;
181 samr_OemChangePasswordUser2
183 NTSTATUS dcesrv_samr_OemChangePasswordUser2(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
184 struct samr_OemChangePasswordUser2 *r)
187 DATA_BLOB new_password, new_unicode_password;
189 struct samr_CryptPassword *pwbuf = r->in.password;
190 struct ldb_context *sam_ctx;
191 struct ldb_dn *user_dn;
193 struct ldb_message **res, *mod;
194 const char * const attrs[] = { "objectSid", "dBCSPwd", NULL };
195 struct samr_Password *lm_pwd;
196 DATA_BLOB lm_pwd_blob;
197 uint8_t new_lm_hash[16];
198 struct samr_Password lm_verifier;
199 size_t unicode_pw_len;
202 return NT_STATUS_INVALID_PARAMETER;
205 if (r->in.hash == NULL) {
206 return NT_STATUS_INVALID_PARAMETER;
209 /* To change a password we need to open as system */
210 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));
211 if (sam_ctx == NULL) {
212 return NT_STATUS_INVALID_SYSTEM_SERVICE;
215 ret = ldb_transaction_start(sam_ctx);
217 DEBUG(1, ("Failed to start transaction: %s\n", ldb_errstring(sam_ctx)));
218 return NT_STATUS_TRANSACTION_ABORTED;
221 /* we need the users dn and the domain dn (derived from the
222 user SID). We also need the current lm password hash in
223 order to decrypt the incoming password */
224 ret = gendb_search(sam_ctx,
225 mem_ctx, NULL, &res, attrs,
226 "(&(sAMAccountName=%s)(objectclass=user))",
227 r->in.account->string);
229 ldb_transaction_cancel(sam_ctx);
230 /* Don't give the game away: (don't allow anonymous users to prove the existance of usernames) */
231 return NT_STATUS_WRONG_PASSWORD;
234 user_dn = res[0]->dn;
236 status = samdb_result_passwords(mem_ctx, dce_call->conn->dce_ctx->lp_ctx,
237 res[0], &lm_pwd, NULL);
238 if (!NT_STATUS_IS_OK(status) || !lm_pwd) {
239 ldb_transaction_cancel(sam_ctx);
240 return NT_STATUS_WRONG_PASSWORD;
243 /* decrypt the password we have been given */
244 lm_pwd_blob = data_blob(lm_pwd->hash, sizeof(lm_pwd->hash));
245 arcfour_crypt_blob(pwbuf->data, 516, &lm_pwd_blob);
246 data_blob_free(&lm_pwd_blob);
248 if (!extract_pw_from_buffer(mem_ctx, pwbuf->data, &new_password)) {
249 ldb_transaction_cancel(sam_ctx);
250 DEBUG(3,("samr: failed to decode password buffer\n"));
251 return NT_STATUS_WRONG_PASSWORD;
254 if (!convert_string_talloc_convenience(mem_ctx, lp_iconv_convenience(dce_call->conn->dce_ctx->lp_ctx),
256 (const char *)new_password.data,
258 (void **)&new_pass, NULL, false)) {
259 DEBUG(3,("samr: failed to convert incoming password buffer to unix charset\n"));
260 ldb_transaction_cancel(sam_ctx);
261 return NT_STATUS_WRONG_PASSWORD;
264 if (!convert_string_talloc_convenience(mem_ctx, lp_iconv_convenience(dce_call->conn->dce_ctx->lp_ctx),
266 (const char *)new_password.data,
268 (void **)&new_unicode_password.data, &unicode_pw_len, false)) {
269 DEBUG(3,("samr: failed to convert incoming password buffer to UTF16 charset\n"));
270 ldb_transaction_cancel(sam_ctx);
271 return NT_STATUS_WRONG_PASSWORD;
273 new_unicode_password.length = unicode_pw_len;
275 E_deshash(new_pass, new_lm_hash);
276 E_old_pw_hash(new_lm_hash, lm_pwd->hash, lm_verifier.hash);
277 if (memcmp(lm_verifier.hash, r->in.hash->hash, 16) != 0) {
278 ldb_transaction_cancel(sam_ctx);
279 return NT_STATUS_WRONG_PASSWORD;
282 mod = ldb_msg_new(mem_ctx);
284 ldb_transaction_cancel(sam_ctx);
285 return NT_STATUS_NO_MEMORY;
288 mod->dn = ldb_dn_copy(mod, user_dn);
290 ldb_transaction_cancel(sam_ctx);
291 return NT_STATUS_NO_MEMORY;
294 /* set the password on the user DN specified. This may fail
295 * due to password policies */
296 status = samdb_set_password(sam_ctx, mem_ctx,
298 mod, &new_unicode_password,
300 true, /* this is a user password change */
303 if (!NT_STATUS_IS_OK(status)) {
304 ldb_transaction_cancel(sam_ctx);
308 /* The above call only setup the modifications, this actually
309 * makes the write to the database. */
310 ret = samdb_replace(sam_ctx, mem_ctx, mod);
312 DEBUG(2,("Failed to modify record to change password on %s: %s\n",
313 ldb_dn_get_linearized(user_dn),
314 ldb_errstring(sam_ctx)));
315 ldb_transaction_cancel(sam_ctx);
316 return NT_STATUS_INTERNAL_DB_CORRUPTION;
319 /* And this confirms it in a transaction commit */
320 ret = ldb_transaction_commit(sam_ctx);
322 DEBUG(1,("Failed to commit transaction to change password on %s: %s\n",
323 ldb_dn_get_linearized(user_dn),
324 ldb_errstring(sam_ctx)));
325 return NT_STATUS_TRANSACTION_ABORTED;
333 samr_ChangePasswordUser3
335 NTSTATUS dcesrv_samr_ChangePasswordUser3(struct dcesrv_call_state *dce_call,
337 struct samr_ChangePasswordUser3 *r)
340 DATA_BLOB new_password;
341 struct ldb_context *sam_ctx = NULL;
342 struct ldb_dn *user_dn;
344 struct ldb_message **res, *mod;
345 const char * const attrs[] = { "unicodePwd", "dBCSPwd", NULL };
346 struct samr_Password *nt_pwd, *lm_pwd;
347 DATA_BLOB nt_pwd_blob;
348 struct samr_DomInfo1 *dominfo = NULL;
349 struct samr_ChangeReject *reject = NULL;
350 enum samr_RejectReason reason = SAMR_REJECT_OTHER;
351 uint8_t new_nt_hash[16], new_lm_hash[16];
352 struct samr_Password nt_verifier, lm_verifier;
354 *r->out.dominfo = NULL;
355 *r->out.reject = NULL;
357 if (r->in.nt_password == NULL ||
358 r->in.nt_verifier == NULL) {
359 return NT_STATUS_INVALID_PARAMETER;
362 /* To change a password we need to open as system */
363 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));
364 if (sam_ctx == NULL) {
365 return NT_STATUS_INVALID_SYSTEM_SERVICE;
368 ret = ldb_transaction_start(sam_ctx);
370 talloc_free(sam_ctx);
371 DEBUG(1, ("Failed to start transaction: %s\n", ldb_errstring(sam_ctx)));
372 return NT_STATUS_TRANSACTION_ABORTED;
375 /* we need the users dn and the domain dn (derived from the
376 user SID). We also need the current lm and nt password hashes
377 in order to decrypt the incoming passwords */
378 ret = gendb_search(sam_ctx,
379 mem_ctx, NULL, &res, attrs,
380 "(&(sAMAccountName=%s)(objectclass=user))",
381 r->in.account->string);
383 /* Don't give the game away: (don't allow anonymous users to prove the existance of usernames) */
384 status = NT_STATUS_WRONG_PASSWORD;
388 user_dn = res[0]->dn;
390 status = samdb_result_passwords(mem_ctx, dce_call->conn->dce_ctx->lp_ctx,
391 res[0], &lm_pwd, &nt_pwd);
392 if (!NT_STATUS_IS_OK(status) ) {
397 status = NT_STATUS_WRONG_PASSWORD;
401 /* decrypt the password we have been given */
402 nt_pwd_blob = data_blob(nt_pwd->hash, sizeof(nt_pwd->hash));
403 arcfour_crypt_blob(r->in.nt_password->data, 516, &nt_pwd_blob);
404 data_blob_free(&nt_pwd_blob);
406 if (!extract_pw_from_buffer(mem_ctx, r->in.nt_password->data, &new_password)) {
407 ldb_transaction_cancel(sam_ctx);
408 DEBUG(3,("samr: failed to decode password buffer\n"));
409 return NT_STATUS_WRONG_PASSWORD;
412 if (r->in.nt_verifier == NULL) {
413 status = NT_STATUS_WRONG_PASSWORD;
417 /* check NT verifier */
418 mdfour(new_nt_hash, new_password.data, new_password.length);
420 E_old_pw_hash(new_nt_hash, nt_pwd->hash, nt_verifier.hash);
421 if (memcmp(nt_verifier.hash, r->in.nt_verifier->hash, 16) != 0) {
422 status = NT_STATUS_WRONG_PASSWORD;
426 /* check LM verifier (really not needed as we just checked the
427 * much stronger NT hash, but the RPC-SAMR test checks for
429 if (lm_pwd && r->in.lm_verifier != NULL) {
431 if (!convert_string_talloc_convenience(mem_ctx, lp_iconv_convenience(dce_call->conn->dce_ctx->lp_ctx),
433 (const char *)new_password.data,
435 (void **)&new_pass, NULL, false)) {
436 E_deshash(new_pass, new_lm_hash);
437 E_old_pw_hash(new_nt_hash, lm_pwd->hash, lm_verifier.hash);
438 if (memcmp(lm_verifier.hash, r->in.lm_verifier->hash, 16) != 0) {
439 status = NT_STATUS_WRONG_PASSWORD;
445 mod = ldb_msg_new(mem_ctx);
447 status = NT_STATUS_NO_MEMORY;
451 mod->dn = ldb_dn_copy(mod, user_dn);
453 status = NT_STATUS_NO_MEMORY;
457 /* set the password on the user DN specified. This may fail
458 * due to password policies */
459 status = samdb_set_password(sam_ctx, mem_ctx,
463 true, /* this is a user password change */
466 if (!NT_STATUS_IS_OK(status)) {
470 /* The above call only setup the modifications, this actually
471 * makes the write to the database. */
472 ret = samdb_replace(sam_ctx, mem_ctx, mod);
474 DEBUG(2,("samdb_replace failed to change password for %s: %s\n",
475 ldb_dn_get_linearized(user_dn),
476 ldb_errstring(sam_ctx)));
477 status = NT_STATUS_UNSUCCESSFUL;
481 /* And this confirms it in a transaction commit */
482 ret = ldb_transaction_commit(sam_ctx);
484 DEBUG(1,("Failed to commit transaction to change password on %s: %s\n",
485 ldb_dn_get_linearized(user_dn),
486 ldb_errstring(sam_ctx)));
487 status = NT_STATUS_TRANSACTION_ABORTED;
494 ldb_transaction_cancel(sam_ctx);
495 talloc_free(sam_ctx);
497 reject = talloc(mem_ctx, struct samr_ChangeReject);
498 *r->out.dominfo = dominfo;
499 *r->out.reject = reject;
501 if (reject == NULL) {
504 ZERO_STRUCTP(reject);
506 reject->reason = reason;
513 samr_ChangePasswordUser2
515 easy - just a subset of samr_ChangePasswordUser3
517 NTSTATUS dcesrv_samr_ChangePasswordUser2(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
518 struct samr_ChangePasswordUser2 *r)
520 struct samr_ChangePasswordUser3 r2;
521 struct samr_DomInfo1 *dominfo = NULL;
522 struct samr_ChangeReject *reject = NULL;
524 r2.in.server = r->in.server;
525 r2.in.account = r->in.account;
526 r2.in.nt_password = r->in.nt_password;
527 r2.in.nt_verifier = r->in.nt_verifier;
528 r2.in.lm_change = r->in.lm_change;
529 r2.in.lm_password = r->in.lm_password;
530 r2.in.lm_verifier = r->in.lm_verifier;
531 r2.in.password3 = NULL;
532 r2.out.dominfo = &dominfo;
533 r2.out.reject = &reject;
535 return dcesrv_samr_ChangePasswordUser3(dce_call, mem_ctx, &r2);
540 set password via a samr_CryptPassword buffer
541 this will in the 'msg' with modify operations that will update the user
542 password when applied
544 NTSTATUS samr_set_password(struct dcesrv_call_state *dce_call,
546 struct ldb_dn *account_dn, struct ldb_dn *domain_dn,
548 struct ldb_message *msg,
549 struct samr_CryptPassword *pwbuf)
552 DATA_BLOB new_password;
553 DATA_BLOB session_key = data_blob(NULL, 0);
555 nt_status = dcesrv_fetch_session_key(dce_call->conn, &session_key);
556 if (!NT_STATUS_IS_OK(nt_status)) {
560 arcfour_crypt_blob(pwbuf->data, 516, &session_key);
562 if (!extract_pw_from_buffer(mem_ctx, pwbuf->data, &new_password)) {
563 DEBUG(3,("samr: failed to decode password buffer\n"));
564 return NT_STATUS_WRONG_PASSWORD;
567 /* set the password - samdb needs to know both the domain and user DNs,
568 so the domain password policy can be used */
569 return samdb_set_password(sam_ctx, mem_ctx,
570 account_dn, domain_dn,
573 false, /* This is a password set, not change */
579 set password via a samr_CryptPasswordEx buffer
580 this will in the 'msg' with modify operations that will update the user
581 password when applied
583 NTSTATUS samr_set_password_ex(struct dcesrv_call_state *dce_call,
584 struct ldb_context *sam_ctx,
585 struct ldb_dn *account_dn, struct ldb_dn *domain_dn,
587 struct ldb_message *msg,
588 struct samr_CryptPasswordEx *pwbuf)
591 DATA_BLOB new_password;
592 DATA_BLOB co_session_key;
593 DATA_BLOB session_key = data_blob(NULL, 0);
594 struct MD5Context ctx;
596 nt_status = dcesrv_fetch_session_key(dce_call->conn, &session_key);
597 if (!NT_STATUS_IS_OK(nt_status)) {
601 co_session_key = data_blob_talloc(mem_ctx, NULL, 16);
602 if (!co_session_key.data) {
603 return NT_STATUS_NO_MEMORY;
607 MD5Update(&ctx, &pwbuf->data[516], 16);
608 MD5Update(&ctx, session_key.data, session_key.length);
609 MD5Final(co_session_key.data, &ctx);
611 arcfour_crypt_blob(pwbuf->data, 516, &co_session_key);
613 if (!extract_pw_from_buffer(mem_ctx, pwbuf->data, &new_password)) {
614 DEBUG(3,("samr: failed to decode password buffer\n"));
615 return NT_STATUS_WRONG_PASSWORD;
618 /* set the password - samdb needs to know both the domain and user DNs,
619 so the domain password policy can be used */
620 return samdb_set_password(sam_ctx, mem_ctx,
621 account_dn, domain_dn,
624 false, /* This is a password set, not change */