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 ssize_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(mem_ctx, lp_iconv_convenience(dce_call->conn->dce_ctx->lp_ctx),
256 (const char *)new_password.data,
258 (void **)&new_pass) == -1) {
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 unicode_pw_len = convert_string_talloc(mem_ctx, lp_iconv_convenience(dce_call->conn->dce_ctx->lp_ctx),
266 (const char *)new_password.data,
268 (void **)&new_unicode_password.data);
269 if (unicode_pw_len == -1) {
270 DEBUG(3,("samr: failed to convert incoming password buffer to UTF16 charset\n"));
271 ldb_transaction_cancel(sam_ctx);
272 return NT_STATUS_WRONG_PASSWORD;
274 new_unicode_password.length = unicode_pw_len;
276 E_deshash(new_pass, new_lm_hash);
277 E_old_pw_hash(new_lm_hash, lm_pwd->hash, lm_verifier.hash);
278 if (memcmp(lm_verifier.hash, r->in.hash->hash, 16) != 0) {
279 ldb_transaction_cancel(sam_ctx);
280 return NT_STATUS_WRONG_PASSWORD;
283 mod = ldb_msg_new(mem_ctx);
285 ldb_transaction_cancel(sam_ctx);
286 return NT_STATUS_NO_MEMORY;
289 mod->dn = ldb_dn_copy(mod, user_dn);
291 ldb_transaction_cancel(sam_ctx);
292 return NT_STATUS_NO_MEMORY;
295 /* set the password on the user DN specified. This may fail
296 * due to password policies */
297 status = samdb_set_password(sam_ctx, mem_ctx,
299 mod, &new_unicode_password,
301 true, /* this is a user password change */
304 if (!NT_STATUS_IS_OK(status)) {
305 ldb_transaction_cancel(sam_ctx);
309 /* The above call only setup the modifications, this actually
310 * makes the write to the database. */
311 ret = samdb_replace(sam_ctx, mem_ctx, mod);
313 DEBUG(2,("Failed to modify record to change password on %s: %s\n",
314 ldb_dn_get_linearized(user_dn),
315 ldb_errstring(sam_ctx)));
316 ldb_transaction_cancel(sam_ctx);
317 return NT_STATUS_INTERNAL_DB_CORRUPTION;
320 /* And this confirms it in a transaction commit */
321 ret = ldb_transaction_commit(sam_ctx);
323 DEBUG(1,("Failed to commit transaction to change password on %s: %s\n",
324 ldb_dn_get_linearized(user_dn),
325 ldb_errstring(sam_ctx)));
326 return NT_STATUS_TRANSACTION_ABORTED;
334 samr_ChangePasswordUser3
336 NTSTATUS dcesrv_samr_ChangePasswordUser3(struct dcesrv_call_state *dce_call,
338 struct samr_ChangePasswordUser3 *r)
341 DATA_BLOB new_password;
342 struct ldb_context *sam_ctx = NULL;
343 struct ldb_dn *user_dn;
345 struct ldb_message **res, *mod;
346 const char * const attrs[] = { "unicodePwd", "dBCSPwd", NULL };
347 struct samr_Password *nt_pwd, *lm_pwd;
348 DATA_BLOB nt_pwd_blob;
349 struct samr_DomInfo1 *dominfo = NULL;
350 struct samr_ChangeReject *reject = NULL;
351 enum samr_RejectReason reason = SAMR_REJECT_OTHER;
352 uint8_t new_nt_hash[16], new_lm_hash[16];
353 struct samr_Password nt_verifier, lm_verifier;
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(mem_ctx, lp_iconv_convenience(dce_call->conn->dce_ctx->lp_ctx),
433 (const char *)new_password.data,
435 (void **)&new_pass) != -1) {
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;
522 r2.in.server = r->in.server;
523 r2.in.account = r->in.account;
524 r2.in.nt_password = r->in.nt_password;
525 r2.in.nt_verifier = r->in.nt_verifier;
526 r2.in.lm_change = r->in.lm_change;
527 r2.in.lm_password = r->in.lm_password;
528 r2.in.lm_verifier = r->in.lm_verifier;
529 r2.in.password3 = NULL;
531 return dcesrv_samr_ChangePasswordUser3(dce_call, mem_ctx, &r2);
536 set password via a samr_CryptPassword buffer
537 this will in the 'msg' with modify operations that will update the user
538 password when applied
540 NTSTATUS samr_set_password(struct dcesrv_call_state *dce_call,
542 struct ldb_dn *account_dn, struct ldb_dn *domain_dn,
544 struct ldb_message *msg,
545 struct samr_CryptPassword *pwbuf)
548 DATA_BLOB new_password;
549 DATA_BLOB session_key = data_blob(NULL, 0);
551 nt_status = dcesrv_fetch_session_key(dce_call->conn, &session_key);
552 if (!NT_STATUS_IS_OK(nt_status)) {
556 arcfour_crypt_blob(pwbuf->data, 516, &session_key);
558 if (!extract_pw_from_buffer(mem_ctx, pwbuf->data, &new_password)) {
559 DEBUG(3,("samr: failed to decode password buffer\n"));
560 return NT_STATUS_WRONG_PASSWORD;
563 /* set the password - samdb needs to know both the domain and user DNs,
564 so the domain password policy can be used */
565 return samdb_set_password(sam_ctx, mem_ctx,
566 account_dn, domain_dn,
569 false, /* This is a password set, not change */
575 set password via a samr_CryptPasswordEx buffer
576 this will in the 'msg' with modify operations that will update the user
577 password when applied
579 NTSTATUS samr_set_password_ex(struct dcesrv_call_state *dce_call,
580 struct ldb_context *sam_ctx,
581 struct ldb_dn *account_dn, struct ldb_dn *domain_dn,
583 struct ldb_message *msg,
584 struct samr_CryptPasswordEx *pwbuf)
587 DATA_BLOB new_password;
588 DATA_BLOB co_session_key;
589 DATA_BLOB session_key = data_blob(NULL, 0);
590 struct MD5Context ctx;
592 nt_status = dcesrv_fetch_session_key(dce_call->conn, &session_key);
593 if (!NT_STATUS_IS_OK(nt_status)) {
597 co_session_key = data_blob_talloc(mem_ctx, NULL, 16);
598 if (!co_session_key.data) {
599 return NT_STATUS_NO_MEMORY;
603 MD5Update(&ctx, &pwbuf->data[516], 16);
604 MD5Update(&ctx, session_key.data, session_key.length);
605 MD5Final(co_session_key.data, &ctx);
607 arcfour_crypt_blob(pwbuf->data, 516, &co_session_key);
609 if (!extract_pw_from_buffer(mem_ctx, pwbuf->data, &new_password)) {
610 DEBUG(3,("samr: failed to decode password buffer\n"));
611 return NT_STATUS_WRONG_PASSWORD;
614 /* set the password - samdb needs to know both the domain and user DNs,
615 so the domain password policy can be used */
616 return samdb_set_password(sam_ctx, mem_ctx,
617 account_dn, domain_dn,
620 false, /* This is a password set, not change */