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 2 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, write to the Free Software
21 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
25 #include "rpc_server/dcerpc_server.h"
26 #include "rpc_server/common/common.h"
27 #include "rpc_server/samr/dcesrv_samr.h"
28 #include "librpc/gen_ndr/ndr_security.h"
29 #include "system/time.h"
30 #include "lib/crypto/crypto.h"
32 #include "libcli/ldap/ldap.h"
33 #include "dsdb/samdb/samdb.h"
34 #include "auth/auth.h"
35 #include "rpc_server/samr/proto.h"
36 #include "libcli/auth/libcli_auth.h"
40 samr_ChangePasswordUser
42 NTSTATUS samr_ChangePasswordUser(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
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[] = { "lmPwdHash", "ntPwdHash" , 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;
67 if (!r->in.cross1_present || !r->in.nt_cross) {
68 return NT_STATUS_NT_CROSS_ENCRYPTION_REQUIRED;
70 if (!r->in.cross2_present || !r->in.lm_cross) {
71 return NT_STATUS_LM_CROSS_ENCRYPTION_REQUIRED;
74 /* To change a password we need to open as system */
75 sam_ctx = samdb_connect(mem_ctx, system_session(mem_ctx));
76 if (sam_ctx == NULL) {
77 return NT_STATUS_INVALID_SYSTEM_SERVICE;
80 ret = ldb_transaction_start(sam_ctx);
82 DEBUG(1, ("Failed to start transaction: %s\n", ldb_errstring(sam_ctx)));
83 return NT_STATUS_TRANSACTION_ABORTED;
86 /* fetch the old hashes */
87 ret = gendb_search_dn(sam_ctx, mem_ctx,
88 a_state->account_dn, &res, attrs);
90 ldb_transaction_cancel(sam_ctx);
91 return NT_STATUS_WRONG_PASSWORD;
95 status = samdb_result_passwords(mem_ctx, msg, &lm_pwd, &nt_pwd);
96 if (!NT_STATUS_IS_OK(status) || !lm_pwd || !nt_pwd) {
97 ldb_transaction_cancel(sam_ctx);
98 return NT_STATUS_WRONG_PASSWORD;
101 /* decrypt and check the new lm hash */
102 D_P16(lm_pwd->hash, r->in.new_lm_crypted->hash, new_lmPwdHash.hash);
103 D_P16(new_lmPwdHash.hash, r->in.old_lm_crypted->hash, checkHash.hash);
104 if (memcmp(checkHash.hash, lm_pwd, 16) != 0) {
105 ldb_transaction_cancel(sam_ctx);
106 return NT_STATUS_WRONG_PASSWORD;
109 /* decrypt and check the new nt hash */
110 D_P16(nt_pwd->hash, r->in.new_nt_crypted->hash, new_ntPwdHash.hash);
111 D_P16(new_ntPwdHash.hash, r->in.old_nt_crypted->hash, checkHash.hash);
112 if (memcmp(checkHash.hash, nt_pwd, 16) != 0) {
113 ldb_transaction_cancel(sam_ctx);
114 return NT_STATUS_WRONG_PASSWORD;
117 /* check the nt cross hash */
118 D_P16(lm_pwd->hash, r->in.nt_cross->hash, checkHash.hash);
119 if (memcmp(checkHash.hash, new_ntPwdHash.hash, 16) != 0) {
120 ldb_transaction_cancel(sam_ctx);
121 return NT_STATUS_WRONG_PASSWORD;
124 /* check the lm cross hash */
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;
131 msg = ldb_msg_new(mem_ctx);
133 ldb_transaction_cancel(sam_ctx);
134 return NT_STATUS_NO_MEMORY;
137 msg->dn = ldb_dn_copy(msg, a_state->account_dn);
139 ldb_transaction_cancel(sam_ctx);
140 return NT_STATUS_NO_MEMORY;
143 /* set the password on the user DN specified. This may fail
144 * due to password policies */
145 status = samdb_set_password(sam_ctx, mem_ctx,
146 a_state->account_dn, a_state->domain_state->domain_dn,
147 msg, NULL, &new_lmPwdHash, &new_ntPwdHash,
148 True, /* this is a user password change */
149 True, /* run restriction tests */
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_linearize(mem_ctx, 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_linearize(mem_ctx, a_state->account_dn),
173 ldb_errstring(sam_ctx)));
174 return NT_STATUS_TRANSACTION_ABORTED;
181 samr_OemChangePasswordUser2
183 NTSTATUS samr_OemChangePasswordUser2(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
184 struct samr_OemChangePasswordUser2 *r)
188 uint32_t new_pass_len;
189 struct samr_CryptPassword *pwbuf = r->in.password;
190 struct ldb_context *sam_ctx;
191 const struct ldb_dn *user_dn;
193 struct ldb_message **res, *mod;
194 const char * const attrs[] = { "objectSid", "lmPwdHash", 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;
201 return NT_STATUS_WRONG_PASSWORD;
204 /* To change a password we need to open as system */
205 sam_ctx = samdb_connect(mem_ctx, system_session(mem_ctx));
206 if (sam_ctx == NULL) {
207 return NT_STATUS_INVALID_SYSTEM_SERVICE;
210 ret = ldb_transaction_start(sam_ctx);
212 DEBUG(1, ("Failed to start transaction: %s\n", ldb_errstring(sam_ctx)));
213 return NT_STATUS_TRANSACTION_ABORTED;
216 /* we need the users dn and the domain dn (derived from the
217 user SID). We also need the current lm password hash in
218 order to decrypt the incoming password */
219 ret = gendb_search(sam_ctx,
220 mem_ctx, NULL, &res, attrs,
221 "(&(sAMAccountName=%s)(objectclass=user))",
222 r->in.account->string);
224 ldb_transaction_cancel(sam_ctx);
225 /* Don't give the game away: (don't allow anonymous users to prove the existance of usernames) */
226 return NT_STATUS_WRONG_PASSWORD;
229 user_dn = res[0]->dn;
231 status = samdb_result_passwords(mem_ctx, res[0], &lm_pwd, NULL);
232 if (!NT_STATUS_IS_OK(status) || !lm_pwd) {
233 ldb_transaction_cancel(sam_ctx);
234 return NT_STATUS_WRONG_PASSWORD;
237 /* decrypt the password we have been given */
238 lm_pwd_blob = data_blob(lm_pwd->hash, sizeof(lm_pwd->hash));
239 arcfour_crypt_blob(pwbuf->data, 516, &lm_pwd_blob);
240 data_blob_free(&lm_pwd_blob);
242 if (!decode_pw_buffer(pwbuf->data, new_pass, sizeof(new_pass),
243 &new_pass_len, STR_ASCII)) {
244 ldb_transaction_cancel(sam_ctx);
245 DEBUG(3,("samr: failed to decode password buffer\n"));
246 return NT_STATUS_WRONG_PASSWORD;
249 /* check LM verifier */
250 if (lm_pwd == NULL || r->in.hash == NULL) {
251 ldb_transaction_cancel(sam_ctx);
252 return NT_STATUS_WRONG_PASSWORD;
255 E_deshash(new_pass, new_lm_hash);
256 E_old_pw_hash(new_lm_hash, lm_pwd->hash, lm_verifier.hash);
257 if (memcmp(lm_verifier.hash, r->in.hash->hash, 16) != 0) {
258 ldb_transaction_cancel(sam_ctx);
259 return NT_STATUS_WRONG_PASSWORD;
262 mod = ldb_msg_new(mem_ctx);
264 ldb_transaction_cancel(sam_ctx);
265 return NT_STATUS_NO_MEMORY;
268 mod->dn = ldb_dn_copy(mod, user_dn);
270 ldb_transaction_cancel(sam_ctx);
271 return NT_STATUS_NO_MEMORY;
274 /* set the password on the user DN specified. This may fail
275 * due to password policies */
276 status = samdb_set_password(sam_ctx, mem_ctx,
280 True, /* this is a user password change */
281 True, /* run restriction tests */
284 if (!NT_STATUS_IS_OK(status)) {
285 ldb_transaction_cancel(sam_ctx);
289 /* The above call only setup the modifications, this actually
290 * makes the write to the database. */
291 ret = samdb_replace(sam_ctx, mem_ctx, mod);
293 DEBUG(2,("Failed to modify record to change password on %s: %s\n",
294 ldb_dn_linearize(mem_ctx, user_dn),
295 ldb_errstring(sam_ctx)));
296 ldb_transaction_cancel(sam_ctx);
297 return NT_STATUS_INTERNAL_DB_CORRUPTION;
300 /* And this confirms it in a transaction commit */
301 ret = ldb_transaction_commit(sam_ctx);
303 DEBUG(1,("Failed to commit transaction to change password on %s: %s\n",
304 ldb_dn_linearize(mem_ctx, user_dn),
305 ldb_errstring(sam_ctx)));
306 return NT_STATUS_TRANSACTION_ABORTED;
314 samr_ChangePasswordUser3
316 NTSTATUS samr_ChangePasswordUser3(struct dcesrv_call_state *dce_call,
318 struct samr_ChangePasswordUser3 *r)
322 uint32_t new_pass_len;
323 struct ldb_context *sam_ctx = NULL;
324 const struct ldb_dn *user_dn;
326 struct ldb_message **res, *mod;
327 const char * const attrs[] = { "ntPwdHash", "lmPwdHash", NULL };
328 struct samr_Password *nt_pwd, *lm_pwd;
329 DATA_BLOB nt_pwd_blob;
330 struct samr_DomInfo1 *dominfo = NULL;
331 struct samr_ChangeReject *reject = NULL;
332 enum samr_RejectReason reason = SAMR_REJECT_OTHER;
333 uint8_t new_nt_hash[16], new_lm_hash[16];
334 struct samr_Password nt_verifier, lm_verifier;
338 if (r->in.nt_password == NULL ||
339 r->in.nt_verifier == NULL) {
340 return NT_STATUS_INVALID_PARAMETER;
343 /* To change a password we need to open as system */
344 sam_ctx = samdb_connect(mem_ctx, system_session(mem_ctx));
345 if (sam_ctx == NULL) {
346 return NT_STATUS_INVALID_SYSTEM_SERVICE;
349 ret = ldb_transaction_start(sam_ctx);
351 talloc_free(sam_ctx);
352 DEBUG(1, ("Failed to start transaction: %s\n", ldb_errstring(sam_ctx)));
353 return NT_STATUS_TRANSACTION_ABORTED;
356 /* we need the users dn and the domain dn (derived from the
357 user SID). We also need the current lm and nt password hashes
358 in order to decrypt the incoming passwords */
359 ret = gendb_search(sam_ctx,
360 mem_ctx, NULL, &res, attrs,
361 "(&(sAMAccountName=%s)(objectclass=user))",
362 r->in.account->string);
364 /* Don't give the game away: (don't allow anonymous users to prove the existance of usernames) */
365 status = NT_STATUS_WRONG_PASSWORD;
369 user_dn = res[0]->dn;
371 status = samdb_result_passwords(mem_ctx, res[0], &lm_pwd, &nt_pwd);
372 if (!NT_STATUS_IS_OK(status) ) {
377 status = NT_STATUS_WRONG_PASSWORD;
381 /* decrypt the password we have been given */
382 nt_pwd_blob = data_blob(nt_pwd->hash, sizeof(nt_pwd->hash));
383 arcfour_crypt_blob(r->in.nt_password->data, 516, &nt_pwd_blob);
384 data_blob_free(&nt_pwd_blob);
386 if (!decode_pw_buffer(r->in.nt_password->data, new_pass, sizeof(new_pass),
387 &new_pass_len, STR_UNICODE)) {
388 DEBUG(3,("samr: failed to decode password buffer\n"));
389 status = NT_STATUS_WRONG_PASSWORD;
393 if (r->in.nt_verifier == NULL) {
394 status = NT_STATUS_WRONG_PASSWORD;
398 /* check NT verifier */
399 E_md4hash(new_pass, new_nt_hash);
400 E_old_pw_hash(new_nt_hash, nt_pwd->hash, nt_verifier.hash);
401 if (memcmp(nt_verifier.hash, r->in.nt_verifier->hash, 16) != 0) {
402 status = NT_STATUS_WRONG_PASSWORD;
406 /* check LM verifier */
407 if (lm_pwd && r->in.lm_verifier != NULL) {
408 E_deshash(new_pass, new_lm_hash);
409 E_old_pw_hash(new_nt_hash, lm_pwd->hash, lm_verifier.hash);
410 if (memcmp(lm_verifier.hash, r->in.lm_verifier->hash, 16) != 0) {
411 status = NT_STATUS_WRONG_PASSWORD;
417 mod = ldb_msg_new(mem_ctx);
419 return NT_STATUS_NO_MEMORY;
422 mod->dn = ldb_dn_copy(mod, user_dn);
424 status = NT_STATUS_NO_MEMORY;
428 /* set the password on the user DN specified. This may fail
429 * due to password policies */
430 status = samdb_set_password(sam_ctx, mem_ctx,
434 True, /* this is a user password change */
435 True, /* run restriction tests */
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_linearize(mem_ctx, 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_linearize(mem_ctx, 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 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 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 const struct ldb_dn *account_dn, const 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 */
544 True, /* run restriction tests */
550 set password via a samr_CryptPasswordEx buffer
551 this will in the 'msg' with modify operations that will update the user
552 password when applied
554 NTSTATUS samr_set_password_ex(struct dcesrv_call_state *dce_call,
555 struct ldb_context *sam_ctx,
556 const struct ldb_dn *account_dn, const struct ldb_dn *domain_dn,
558 struct ldb_message *msg,
559 struct samr_CryptPasswordEx *pwbuf)
563 uint32_t new_pass_len;
564 DATA_BLOB co_session_key;
565 DATA_BLOB session_key = data_blob(NULL, 0);
566 struct MD5Context ctx;
568 nt_status = dcesrv_fetch_session_key(dce_call->conn, &session_key);
569 if (!NT_STATUS_IS_OK(nt_status)) {
573 co_session_key = data_blob_talloc(mem_ctx, NULL, 16);
574 if (!co_session_key.data) {
575 return NT_STATUS_NO_MEMORY;
579 MD5Update(&ctx, &pwbuf->data[516], 16);
580 MD5Update(&ctx, session_key.data, session_key.length);
581 MD5Final(co_session_key.data, &ctx);
583 arcfour_crypt_blob(pwbuf->data, 516, &co_session_key);
585 if (!decode_pw_buffer(pwbuf->data, new_pass, sizeof(new_pass),
586 &new_pass_len, STR_UNICODE)) {
587 DEBUG(3,("samr: failed to decode password buffer\n"));
588 return NT_STATUS_WRONG_PASSWORD;
591 /* set the password - samdb needs to know both the domain and user DNs,
592 so the domain password policy can be used */
593 return samdb_set_password(sam_ctx, mem_ctx,
594 account_dn, domain_dn,
597 False, /* This is a password set, not change */
598 True, /* run restriction tests */