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,
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, msg, &lm_pwd, &nt_pwd);
90 if (!NT_STATUS_IS_OK(status) || !lm_pwd || !nt_pwd) {
91 ldb_transaction_cancel(sam_ctx);
92 return NT_STATUS_WRONG_PASSWORD;
95 /* decrypt and check the new lm hash */
96 D_P16(lm_pwd->hash, r->in.new_lm_crypted->hash, new_lmPwdHash.hash);
97 D_P16(new_lmPwdHash.hash, r->in.old_lm_crypted->hash, checkHash.hash);
98 if (memcmp(checkHash.hash, lm_pwd, 16) != 0) {
99 ldb_transaction_cancel(sam_ctx);
100 return NT_STATUS_WRONG_PASSWORD;
103 /* decrypt and check the new nt hash */
104 D_P16(nt_pwd->hash, r->in.new_nt_crypted->hash, new_ntPwdHash.hash);
105 D_P16(new_ntPwdHash.hash, r->in.old_nt_crypted->hash, checkHash.hash);
106 if (memcmp(checkHash.hash, nt_pwd, 16) != 0) {
107 ldb_transaction_cancel(sam_ctx);
108 return NT_STATUS_WRONG_PASSWORD;
111 /* The NT Cross is not required by Win2k3 R2, but if present
112 check the nt cross hash */
113 if (r->in.cross1_present && r->in.nt_cross) {
114 D_P16(lm_pwd->hash, r->in.nt_cross->hash, checkHash.hash);
115 if (memcmp(checkHash.hash, new_ntPwdHash.hash, 16) != 0) {
116 ldb_transaction_cancel(sam_ctx);
117 return NT_STATUS_WRONG_PASSWORD;
121 /* The LM Cross is not required by Win2k3 R2, but if present
122 check the lm cross hash */
123 if (r->in.cross2_present && r->in.lm_cross) {
124 D_P16(nt_pwd->hash, r->in.lm_cross->hash, checkHash.hash);
125 if (memcmp(checkHash.hash, new_lmPwdHash.hash, 16) != 0) {
126 ldb_transaction_cancel(sam_ctx);
127 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 /* setup password modify mods 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 */
151 if (!NT_STATUS_IS_OK(status)) {
152 ldb_transaction_cancel(sam_ctx);
156 /* The above call only setup the modifications, this actually
157 * makes the write to the database. */
158 ret = samdb_replace(sam_ctx, mem_ctx, msg);
160 DEBUG(2,("Failed to modify record to change password on %s: %s\n",
161 ldb_dn_get_linearized(a_state->account_dn),
162 ldb_errstring(sam_ctx)));
163 ldb_transaction_cancel(sam_ctx);
164 return NT_STATUS_INTERNAL_DB_CORRUPTION;
167 /* And this confirms it in a transaction commit */
168 ret = ldb_transaction_commit(sam_ctx);
170 DEBUG(1,("Failed to commit transaction to change password on %s: %s\n",
171 ldb_dn_get_linearized(a_state->account_dn),
172 ldb_errstring(sam_ctx)));
173 return NT_STATUS_TRANSACTION_ABORTED;
180 samr_OemChangePasswordUser2
182 NTSTATUS dcesrv_samr_OemChangePasswordUser2(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
183 struct samr_OemChangePasswordUser2 *r)
187 uint32_t new_pass_len;
188 struct samr_CryptPassword *pwbuf = r->in.password;
189 struct ldb_context *sam_ctx;
190 struct ldb_dn *user_dn;
192 struct ldb_message **res, *mod;
193 const char * const attrs[] = { "objectSid", "dBCSPwd", NULL };
194 struct samr_Password *lm_pwd;
195 DATA_BLOB lm_pwd_blob;
196 uint8_t new_lm_hash[16];
197 struct samr_Password lm_verifier;
200 return NT_STATUS_INVALID_PARAMETER;
203 if (r->in.hash == NULL) {
204 return NT_STATUS_INVALID_PARAMETER;
207 /* To change a password we need to open as system */
208 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));
209 if (sam_ctx == NULL) {
210 return NT_STATUS_INVALID_SYSTEM_SERVICE;
213 ret = ldb_transaction_start(sam_ctx);
215 DEBUG(1, ("Failed to start transaction: %s\n", ldb_errstring(sam_ctx)));
216 return NT_STATUS_TRANSACTION_ABORTED;
219 /* we need the users dn and the domain dn (derived from the
220 user SID). We also need the current lm password hash in
221 order to decrypt the incoming password */
222 ret = gendb_search(sam_ctx,
223 mem_ctx, NULL, &res, attrs,
224 "(&(sAMAccountName=%s)(objectclass=user))",
225 r->in.account->string);
227 ldb_transaction_cancel(sam_ctx);
228 /* Don't give the game away: (don't allow anonymous users to prove the existance of usernames) */
229 return NT_STATUS_WRONG_PASSWORD;
232 user_dn = res[0]->dn;
234 status = samdb_result_passwords(mem_ctx, res[0], &lm_pwd, NULL);
235 if (!NT_STATUS_IS_OK(status) || !lm_pwd) {
236 ldb_transaction_cancel(sam_ctx);
237 return NT_STATUS_WRONG_PASSWORD;
240 /* decrypt the password we have been given */
241 lm_pwd_blob = data_blob(lm_pwd->hash, sizeof(lm_pwd->hash));
242 arcfour_crypt_blob(pwbuf->data, 516, &lm_pwd_blob);
243 data_blob_free(&lm_pwd_blob);
245 if (!decode_pw_buffer(pwbuf->data, new_pass, sizeof(new_pass),
246 &new_pass_len, STR_ASCII)) {
247 ldb_transaction_cancel(sam_ctx);
248 DEBUG(3,("samr: failed to decode password buffer\n"));
249 return NT_STATUS_WRONG_PASSWORD;
252 /* check LM verifier */
253 if (lm_pwd == NULL) {
254 ldb_transaction_cancel(sam_ctx);
255 return NT_STATUS_WRONG_PASSWORD;
258 E_deshash(new_pass, new_lm_hash);
259 E_old_pw_hash(new_lm_hash, lm_pwd->hash, lm_verifier.hash);
260 if (memcmp(lm_verifier.hash, r->in.hash->hash, 16) != 0) {
261 ldb_transaction_cancel(sam_ctx);
262 return NT_STATUS_WRONG_PASSWORD;
265 mod = ldb_msg_new(mem_ctx);
267 ldb_transaction_cancel(sam_ctx);
268 return NT_STATUS_NO_MEMORY;
271 mod->dn = ldb_dn_copy(mod, user_dn);
273 ldb_transaction_cancel(sam_ctx);
274 return NT_STATUS_NO_MEMORY;
277 /* set the password on the user DN specified. This may fail
278 * due to password policies */
279 status = samdb_set_password(sam_ctx, mem_ctx,
283 true, /* this is a user password change */
286 if (!NT_STATUS_IS_OK(status)) {
287 ldb_transaction_cancel(sam_ctx);
291 /* The above call only setup the modifications, this actually
292 * makes the write to the database. */
293 ret = samdb_replace(sam_ctx, mem_ctx, mod);
295 DEBUG(2,("Failed to modify record to change password on %s: %s\n",
296 ldb_dn_get_linearized(user_dn),
297 ldb_errstring(sam_ctx)));
298 ldb_transaction_cancel(sam_ctx);
299 return NT_STATUS_INTERNAL_DB_CORRUPTION;
302 /* And this confirms it in a transaction commit */
303 ret = ldb_transaction_commit(sam_ctx);
305 DEBUG(1,("Failed to commit transaction to change password on %s: %s\n",
306 ldb_dn_get_linearized(user_dn),
307 ldb_errstring(sam_ctx)));
308 return NT_STATUS_TRANSACTION_ABORTED;
316 samr_ChangePasswordUser3
318 NTSTATUS dcesrv_samr_ChangePasswordUser3(struct dcesrv_call_state *dce_call,
320 struct samr_ChangePasswordUser3 *r)
324 uint32_t new_pass_len;
325 struct ldb_context *sam_ctx = NULL;
326 struct ldb_dn *user_dn;
328 struct ldb_message **res, *mod;
329 const char * const attrs[] = { "unicodePwd", "dBCSPwd", NULL };
330 struct samr_Password *nt_pwd, *lm_pwd;
331 DATA_BLOB nt_pwd_blob;
332 struct samr_DomInfo1 *dominfo = NULL;
333 struct samr_ChangeReject *reject = NULL;
334 enum samr_RejectReason reason = SAMR_REJECT_OTHER;
335 uint8_t new_nt_hash[16], new_lm_hash[16];
336 struct samr_Password nt_verifier, lm_verifier;
340 if (r->in.nt_password == NULL ||
341 r->in.nt_verifier == NULL) {
342 return NT_STATUS_INVALID_PARAMETER;
345 /* To change a password we need to open as system */
346 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));
347 if (sam_ctx == NULL) {
348 return NT_STATUS_INVALID_SYSTEM_SERVICE;
351 ret = ldb_transaction_start(sam_ctx);
353 talloc_free(sam_ctx);
354 DEBUG(1, ("Failed to start transaction: %s\n", ldb_errstring(sam_ctx)));
355 return NT_STATUS_TRANSACTION_ABORTED;
358 /* we need the users dn and the domain dn (derived from the
359 user SID). We also need the current lm and nt password hashes
360 in order to decrypt the incoming passwords */
361 ret = gendb_search(sam_ctx,
362 mem_ctx, NULL, &res, attrs,
363 "(&(sAMAccountName=%s)(objectclass=user))",
364 r->in.account->string);
366 /* Don't give the game away: (don't allow anonymous users to prove the existance of usernames) */
367 status = NT_STATUS_WRONG_PASSWORD;
371 user_dn = res[0]->dn;
373 status = samdb_result_passwords(mem_ctx, res[0], &lm_pwd, &nt_pwd);
374 if (!NT_STATUS_IS_OK(status) ) {
379 status = NT_STATUS_WRONG_PASSWORD;
383 /* decrypt the password we have been given */
384 nt_pwd_blob = data_blob(nt_pwd->hash, sizeof(nt_pwd->hash));
385 arcfour_crypt_blob(r->in.nt_password->data, 516, &nt_pwd_blob);
386 data_blob_free(&nt_pwd_blob);
388 if (!decode_pw_buffer(r->in.nt_password->data, new_pass, sizeof(new_pass),
389 &new_pass_len, STR_UNICODE)) {
390 DEBUG(3,("samr: failed to decode password buffer\n"));
391 status = NT_STATUS_WRONG_PASSWORD;
395 if (r->in.nt_verifier == NULL) {
396 status = NT_STATUS_WRONG_PASSWORD;
400 /* check NT verifier */
401 E_md4hash(new_pass, new_nt_hash);
402 E_old_pw_hash(new_nt_hash, nt_pwd->hash, nt_verifier.hash);
403 if (memcmp(nt_verifier.hash, r->in.nt_verifier->hash, 16) != 0) {
404 status = NT_STATUS_WRONG_PASSWORD;
408 /* check LM verifier */
409 if (lm_pwd && r->in.lm_verifier != NULL) {
410 E_deshash(new_pass, new_lm_hash);
411 E_old_pw_hash(new_nt_hash, lm_pwd->hash, lm_verifier.hash);
412 if (memcmp(lm_verifier.hash, r->in.lm_verifier->hash, 16) != 0) {
413 status = NT_STATUS_WRONG_PASSWORD;
419 mod = ldb_msg_new(mem_ctx);
421 return NT_STATUS_NO_MEMORY;
424 mod->dn = ldb_dn_copy(mod, user_dn);
426 status = NT_STATUS_NO_MEMORY;
430 /* set the password on the user DN specified. This may fail
431 * due to password policies */
432 status = samdb_set_password(sam_ctx, mem_ctx,
436 true, /* this is a user password change */
439 if (!NT_STATUS_IS_OK(status)) {
443 /* The above call only setup the modifications, this actually
444 * makes the write to the database. */
445 ret = samdb_replace(sam_ctx, mem_ctx, mod);
447 DEBUG(2,("samdb_replace failed to change password for %s: %s\n",
448 ldb_dn_get_linearized(user_dn),
449 ldb_errstring(sam_ctx)));
450 status = NT_STATUS_UNSUCCESSFUL;
454 /* And this confirms it in a transaction commit */
455 ret = ldb_transaction_commit(sam_ctx);
457 DEBUG(1,("Failed to commit transaction to change password on %s: %s\n",
458 ldb_dn_get_linearized(user_dn),
459 ldb_errstring(sam_ctx)));
460 status = NT_STATUS_TRANSACTION_ABORTED;
467 ldb_transaction_cancel(sam_ctx);
468 talloc_free(sam_ctx);
470 reject = talloc(mem_ctx, struct samr_ChangeReject);
471 r->out.dominfo = dominfo;
472 r->out.reject = reject;
474 if (reject == NULL) {
477 ZERO_STRUCTP(reject);
479 reject->reason = reason;
486 samr_ChangePasswordUser2
488 easy - just a subset of samr_ChangePasswordUser3
490 NTSTATUS dcesrv_samr_ChangePasswordUser2(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
491 struct samr_ChangePasswordUser2 *r)
493 struct samr_ChangePasswordUser3 r2;
495 r2.in.server = r->in.server;
496 r2.in.account = r->in.account;
497 r2.in.nt_password = r->in.nt_password;
498 r2.in.nt_verifier = r->in.nt_verifier;
499 r2.in.lm_change = r->in.lm_change;
500 r2.in.lm_password = r->in.lm_password;
501 r2.in.lm_verifier = r->in.lm_verifier;
502 r2.in.password3 = NULL;
504 return dcesrv_samr_ChangePasswordUser3(dce_call, mem_ctx, &r2);
509 set password via a samr_CryptPassword buffer
510 this will in the 'msg' with modify operations that will update the user
511 password when applied
513 NTSTATUS samr_set_password(struct dcesrv_call_state *dce_call,
515 struct ldb_dn *account_dn, struct ldb_dn *domain_dn,
517 struct ldb_message *msg,
518 struct samr_CryptPassword *pwbuf)
522 uint32_t new_pass_len;
523 DATA_BLOB session_key = data_blob(NULL, 0);
525 nt_status = dcesrv_fetch_session_key(dce_call->conn, &session_key);
526 if (!NT_STATUS_IS_OK(nt_status)) {
530 arcfour_crypt_blob(pwbuf->data, 516, &session_key);
532 if (!decode_pw_buffer(pwbuf->data, new_pass, sizeof(new_pass),
533 &new_pass_len, STR_UNICODE)) {
534 DEBUG(3,("samr: failed to decode password buffer\n"));
535 return NT_STATUS_WRONG_PASSWORD;
538 /* set the password - samdb needs to know both the domain and user DNs,
539 so the domain password policy can be used */
540 return samdb_set_password(sam_ctx, mem_ctx,
541 account_dn, domain_dn,
544 false, /* This is a password set, not change */
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 struct ldb_dn *account_dn, 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 */