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/samr/dcesrv_samr.h"
26 #include "system/time.h"
27 #include "../lib/crypto/crypto.h"
28 #include "dsdb/samdb/samdb.h"
29 #include "auth/auth.h"
30 #include "libcli/auth/libcli_auth.h"
31 #include "../lib/util/util_ldb.h"
34 samr_ChangePasswordUser
36 NTSTATUS dcesrv_samr_ChangePasswordUser(struct dcesrv_call_state *dce_call,
38 struct samr_ChangePasswordUser *r)
40 struct dcesrv_handle *h;
41 struct samr_account_state *a_state;
42 struct ldb_context *sam_ctx;
43 struct ldb_message **res, *msg;
45 struct samr_Password new_lmPwdHash, new_ntPwdHash, checkHash;
46 struct samr_Password *lm_pwd, *nt_pwd;
47 NTSTATUS status = NT_STATUS_OK;
48 const char * const attrs[] = { "dBCSPwd", "unicodePwd" , NULL };
50 DCESRV_PULL_HANDLE(h, r->in.user_handle, SAMR_HANDLE_USER);
54 /* basic sanity checking on parameters. Do this before any database ops */
55 if (!r->in.lm_present || !r->in.nt_present ||
56 !r->in.old_lm_crypted || !r->in.new_lm_crypted ||
57 !r->in.old_nt_crypted || !r->in.new_nt_crypted) {
58 /* we should really handle a change with lm not
60 return NT_STATUS_INVALID_PARAMETER_MIX;
63 /* To change a password we need to open as system */
64 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));
65 if (sam_ctx == NULL) {
66 return NT_STATUS_INVALID_SYSTEM_SERVICE;
69 ret = ldb_transaction_start(sam_ctx);
71 DEBUG(1, ("Failed to start transaction: %s\n", ldb_errstring(sam_ctx)));
72 return NT_STATUS_TRANSACTION_ABORTED;
75 /* fetch the old hashes */
76 ret = gendb_search_dn(sam_ctx, mem_ctx,
77 a_state->account_dn, &res, attrs);
79 ldb_transaction_cancel(sam_ctx);
80 return NT_STATUS_WRONG_PASSWORD;
84 status = samdb_result_passwords(mem_ctx, dce_call->conn->dce_ctx->lp_ctx,
85 msg, &lm_pwd, &nt_pwd);
86 if (!NT_STATUS_IS_OK(status) || !nt_pwd) {
87 ldb_transaction_cancel(sam_ctx);
88 return NT_STATUS_WRONG_PASSWORD;
91 /* decrypt and check the new lm hash */
93 D_P16(lm_pwd->hash, r->in.new_lm_crypted->hash, new_lmPwdHash.hash);
94 D_P16(new_lmPwdHash.hash, r->in.old_lm_crypted->hash, checkHash.hash);
95 if (memcmp(checkHash.hash, lm_pwd, 16) != 0) {
96 ldb_transaction_cancel(sam_ctx);
97 return NT_STATUS_WRONG_PASSWORD;
101 /* decrypt and check the new nt hash */
102 D_P16(nt_pwd->hash, r->in.new_nt_crypted->hash, new_ntPwdHash.hash);
103 D_P16(new_ntPwdHash.hash, r->in.old_nt_crypted->hash, checkHash.hash);
104 if (memcmp(checkHash.hash, nt_pwd, 16) != 0) {
105 ldb_transaction_cancel(sam_ctx);
106 return NT_STATUS_WRONG_PASSWORD;
109 /* The NT Cross is not required by Win2k3 R2, but if present
110 check the nt cross hash */
111 if (r->in.cross1_present && r->in.nt_cross && lm_pwd) {
112 D_P16(lm_pwd->hash, r->in.nt_cross->hash, checkHash.hash);
113 if (memcmp(checkHash.hash, new_ntPwdHash.hash, 16) != 0) {
114 ldb_transaction_cancel(sam_ctx);
115 return NT_STATUS_WRONG_PASSWORD;
119 /* The LM Cross is not required by Win2k3 R2, but if present
120 check the lm cross hash */
121 if (r->in.cross2_present && r->in.lm_cross && lm_pwd) {
122 D_P16(nt_pwd->hash, r->in.lm_cross->hash, checkHash.hash);
123 if (memcmp(checkHash.hash, new_lmPwdHash.hash, 16) != 0) {
124 ldb_transaction_cancel(sam_ctx);
125 return NT_STATUS_WRONG_PASSWORD;
129 msg = ldb_msg_new(mem_ctx);
131 ldb_transaction_cancel(sam_ctx);
132 return NT_STATUS_NO_MEMORY;
135 msg->dn = ldb_dn_copy(msg, a_state->account_dn);
137 ldb_transaction_cancel(sam_ctx);
138 return NT_STATUS_NO_MEMORY;
141 /* setup password modify mods on the user DN specified. This may fail
142 * due to password policies. */
143 status = samdb_set_password(sam_ctx, mem_ctx,
144 a_state->account_dn, a_state->domain_state->domain_dn,
145 msg, NULL, &new_lmPwdHash, &new_ntPwdHash,
146 true, /* this is a user password change */
149 if (!NT_STATUS_IS_OK(status)) {
150 ldb_transaction_cancel(sam_ctx);
154 /* The above call only setup the modifications, this actually
155 * makes the write to the database. */
156 ret = samdb_replace(sam_ctx, mem_ctx, msg);
158 DEBUG(2,("Failed to modify record to change password on %s: %s\n",
159 ldb_dn_get_linearized(a_state->account_dn),
160 ldb_errstring(sam_ctx)));
161 ldb_transaction_cancel(sam_ctx);
162 return NT_STATUS_INTERNAL_DB_CORRUPTION;
165 /* And this confirms it in a transaction commit */
166 ret = ldb_transaction_commit(sam_ctx);
168 DEBUG(1,("Failed to commit transaction to change password on %s: %s\n",
169 ldb_dn_get_linearized(a_state->account_dn),
170 ldb_errstring(sam_ctx)));
171 return NT_STATUS_TRANSACTION_ABORTED;
178 samr_OemChangePasswordUser2
180 NTSTATUS dcesrv_samr_OemChangePasswordUser2(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
181 struct samr_OemChangePasswordUser2 *r)
184 DATA_BLOB new_password, new_unicode_password;
186 struct samr_CryptPassword *pwbuf = r->in.password;
187 struct ldb_context *sam_ctx;
188 struct ldb_dn *user_dn;
190 struct ldb_message **res, *mod;
191 const char * const attrs[] = { "objectSid", "dBCSPwd", NULL };
192 struct samr_Password *lm_pwd;
193 DATA_BLOB lm_pwd_blob;
194 uint8_t new_lm_hash[16];
195 struct samr_Password lm_verifier;
196 size_t unicode_pw_len;
199 return NT_STATUS_INVALID_PARAMETER;
202 if (r->in.hash == NULL) {
203 return NT_STATUS_INVALID_PARAMETER;
206 /* this call can only work with lanman auth */
207 if (!lp_lanman_auth(dce_call->conn->dce_ctx->lp_ctx)) {
208 return NT_STATUS_NOT_SUPPORTED;
211 /* To change a password we need to open as system */
212 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));
213 if (sam_ctx == NULL) {
214 return NT_STATUS_INVALID_SYSTEM_SERVICE;
217 ret = ldb_transaction_start(sam_ctx);
219 DEBUG(1, ("Failed to start transaction: %s\n", ldb_errstring(sam_ctx)));
220 return NT_STATUS_TRANSACTION_ABORTED;
223 /* we need the users dn and the domain dn (derived from the
224 user SID). We also need the current lm password hash in
225 order to decrypt the incoming password */
226 ret = gendb_search(sam_ctx,
227 mem_ctx, NULL, &res, attrs,
228 "(&(sAMAccountName=%s)(objectclass=user))",
229 r->in.account->string);
231 ldb_transaction_cancel(sam_ctx);
232 /* Don't give the game away: (don't allow anonymous users to prove the existance of usernames) */
233 return NT_STATUS_WRONG_PASSWORD;
236 user_dn = res[0]->dn;
238 status = samdb_result_passwords(mem_ctx, dce_call->conn->dce_ctx->lp_ctx,
239 res[0], &lm_pwd, NULL);
240 if (!NT_STATUS_IS_OK(status) || !lm_pwd) {
241 ldb_transaction_cancel(sam_ctx);
242 return NT_STATUS_WRONG_PASSWORD;
245 /* decrypt the password we have been given */
246 lm_pwd_blob = data_blob(lm_pwd->hash, sizeof(lm_pwd->hash));
247 arcfour_crypt_blob(pwbuf->data, 516, &lm_pwd_blob);
248 data_blob_free(&lm_pwd_blob);
250 if (!extract_pw_from_buffer(mem_ctx, pwbuf->data, &new_password)) {
251 ldb_transaction_cancel(sam_ctx);
252 DEBUG(3,("samr: failed to decode password buffer\n"));
253 return NT_STATUS_WRONG_PASSWORD;
256 if (!convert_string_talloc_convenience(mem_ctx, lp_iconv_convenience(dce_call->conn->dce_ctx->lp_ctx),
258 (const char *)new_password.data,
260 (void **)&new_pass, NULL, false)) {
261 DEBUG(3,("samr: failed to convert incoming password buffer to unix charset\n"));
262 ldb_transaction_cancel(sam_ctx);
263 return NT_STATUS_WRONG_PASSWORD;
266 if (!convert_string_talloc_convenience(mem_ctx, lp_iconv_convenience(dce_call->conn->dce_ctx->lp_ctx),
268 (const char *)new_password.data,
270 (void **)&new_unicode_password.data, &unicode_pw_len, false)) {
271 DEBUG(3,("samr: failed to convert incoming password buffer to UTF16 charset\n"));
272 ldb_transaction_cancel(sam_ctx);
273 return NT_STATUS_WRONG_PASSWORD;
275 new_unicode_password.length = unicode_pw_len;
277 E_deshash(new_pass, new_lm_hash);
278 E_old_pw_hash(new_lm_hash, lm_pwd->hash, lm_verifier.hash);
279 if (memcmp(lm_verifier.hash, r->in.hash->hash, 16) != 0) {
280 ldb_transaction_cancel(sam_ctx);
281 return NT_STATUS_WRONG_PASSWORD;
284 mod = ldb_msg_new(mem_ctx);
286 ldb_transaction_cancel(sam_ctx);
287 return NT_STATUS_NO_MEMORY;
290 mod->dn = ldb_dn_copy(mod, user_dn);
292 ldb_transaction_cancel(sam_ctx);
293 return NT_STATUS_NO_MEMORY;
296 /* set the password on the user DN specified. This may fail
297 * due to password policies */
298 status = samdb_set_password(sam_ctx, mem_ctx,
300 mod, &new_unicode_password,
302 true, /* this is a user password change */
305 if (!NT_STATUS_IS_OK(status)) {
306 ldb_transaction_cancel(sam_ctx);
310 /* The above call only setup the modifications, this actually
311 * makes the write to the database. */
312 ret = samdb_replace(sam_ctx, mem_ctx, mod);
314 DEBUG(2,("Failed to modify record to change password on %s: %s\n",
315 ldb_dn_get_linearized(user_dn),
316 ldb_errstring(sam_ctx)));
317 ldb_transaction_cancel(sam_ctx);
318 return NT_STATUS_INTERNAL_DB_CORRUPTION;
321 /* And this confirms it in a transaction commit */
322 ret = ldb_transaction_commit(sam_ctx);
324 DEBUG(1,("Failed to commit transaction to change password on %s: %s\n",
325 ldb_dn_get_linearized(user_dn),
326 ldb_errstring(sam_ctx)));
327 return NT_STATUS_TRANSACTION_ABORTED;
335 samr_ChangePasswordUser3
337 NTSTATUS dcesrv_samr_ChangePasswordUser3(struct dcesrv_call_state *dce_call,
339 struct samr_ChangePasswordUser3 *r)
342 DATA_BLOB new_password;
343 struct ldb_context *sam_ctx = NULL;
344 struct ldb_dn *user_dn;
346 struct ldb_message **res, *mod;
347 const char * const attrs[] = { "unicodePwd", "dBCSPwd", NULL };
348 struct samr_Password *nt_pwd, *lm_pwd;
349 DATA_BLOB nt_pwd_blob;
350 struct samr_DomInfo1 *dominfo = NULL;
351 struct samr_ChangeReject *reject = NULL;
352 enum samr_RejectReason reason = SAMR_REJECT_OTHER;
353 uint8_t new_nt_hash[16], new_lm_hash[16];
354 struct samr_Password nt_verifier, lm_verifier;
356 *r->out.dominfo = NULL;
357 *r->out.reject = NULL;
359 if (r->in.nt_password == NULL ||
360 r->in.nt_verifier == NULL) {
361 return NT_STATUS_INVALID_PARAMETER;
364 /* To change a password we need to open as system */
365 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));
366 if (sam_ctx == NULL) {
367 return NT_STATUS_INVALID_SYSTEM_SERVICE;
370 ret = ldb_transaction_start(sam_ctx);
372 talloc_free(sam_ctx);
373 DEBUG(1, ("Failed to start transaction: %s\n", ldb_errstring(sam_ctx)));
374 return NT_STATUS_TRANSACTION_ABORTED;
377 /* we need the users dn and the domain dn (derived from the
378 user SID). We also need the current lm and nt password hashes
379 in order to decrypt the incoming passwords */
380 ret = gendb_search(sam_ctx,
381 mem_ctx, NULL, &res, attrs,
382 "(&(sAMAccountName=%s)(objectclass=user))",
383 r->in.account->string);
385 /* Don't give the game away: (don't allow anonymous users to prove the existance of usernames) */
386 status = NT_STATUS_WRONG_PASSWORD;
390 user_dn = res[0]->dn;
392 status = samdb_result_passwords(mem_ctx, dce_call->conn->dce_ctx->lp_ctx,
393 res[0], &lm_pwd, &nt_pwd);
394 if (!NT_STATUS_IS_OK(status) ) {
399 status = NT_STATUS_WRONG_PASSWORD;
403 /* decrypt the password we have been given */
404 nt_pwd_blob = data_blob(nt_pwd->hash, sizeof(nt_pwd->hash));
405 arcfour_crypt_blob(r->in.nt_password->data, 516, &nt_pwd_blob);
406 data_blob_free(&nt_pwd_blob);
408 if (!extract_pw_from_buffer(mem_ctx, r->in.nt_password->data, &new_password)) {
409 ldb_transaction_cancel(sam_ctx);
410 DEBUG(3,("samr: failed to decode password buffer\n"));
411 return NT_STATUS_WRONG_PASSWORD;
414 if (r->in.nt_verifier == NULL) {
415 status = NT_STATUS_WRONG_PASSWORD;
419 /* check NT verifier */
420 mdfour(new_nt_hash, new_password.data, new_password.length);
422 E_old_pw_hash(new_nt_hash, nt_pwd->hash, nt_verifier.hash);
423 if (memcmp(nt_verifier.hash, r->in.nt_verifier->hash, 16) != 0) {
424 status = NT_STATUS_WRONG_PASSWORD;
428 /* check LM verifier (really not needed as we just checked the
429 * much stronger NT hash, but the RPC-SAMR test checks for
431 if (lm_pwd && r->in.lm_verifier != NULL) {
433 if (!convert_string_talloc_convenience(mem_ctx, lp_iconv_convenience(dce_call->conn->dce_ctx->lp_ctx),
435 (const char *)new_password.data,
437 (void **)&new_pass, NULL, false)) {
438 E_deshash(new_pass, new_lm_hash);
439 E_old_pw_hash(new_nt_hash, lm_pwd->hash, lm_verifier.hash);
440 if (memcmp(lm_verifier.hash, r->in.lm_verifier->hash, 16) != 0) {
441 status = NT_STATUS_WRONG_PASSWORD;
447 mod = ldb_msg_new(mem_ctx);
449 status = NT_STATUS_NO_MEMORY;
453 mod->dn = ldb_dn_copy(mod, user_dn);
455 status = NT_STATUS_NO_MEMORY;
459 /* set the password on the user DN specified. This may fail
460 * due to password policies */
461 status = samdb_set_password(sam_ctx, mem_ctx,
465 true, /* this is a user password change */
468 if (!NT_STATUS_IS_OK(status)) {
472 /* The above call only setup the modifications, this actually
473 * makes the write to the database. */
474 ret = samdb_replace(sam_ctx, mem_ctx, mod);
476 DEBUG(2,("samdb_replace failed to change password for %s: %s\n",
477 ldb_dn_get_linearized(user_dn),
478 ldb_errstring(sam_ctx)));
479 status = NT_STATUS_UNSUCCESSFUL;
483 /* And this confirms it in a transaction commit */
484 ret = ldb_transaction_commit(sam_ctx);
486 DEBUG(1,("Failed to commit transaction to change password on %s: %s\n",
487 ldb_dn_get_linearized(user_dn),
488 ldb_errstring(sam_ctx)));
489 status = NT_STATUS_TRANSACTION_ABORTED;
496 ldb_transaction_cancel(sam_ctx);
497 talloc_free(sam_ctx);
499 reject = talloc(mem_ctx, struct samr_ChangeReject);
500 *r->out.dominfo = dominfo;
501 *r->out.reject = reject;
503 if (reject == NULL) {
506 ZERO_STRUCTP(reject);
508 reject->reason = reason;
515 samr_ChangePasswordUser2
517 easy - just a subset of samr_ChangePasswordUser3
519 NTSTATUS dcesrv_samr_ChangePasswordUser2(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
520 struct samr_ChangePasswordUser2 *r)
522 struct samr_ChangePasswordUser3 r2;
523 struct samr_DomInfo1 *dominfo = NULL;
524 struct samr_ChangeReject *reject = NULL;
526 r2.in.server = r->in.server;
527 r2.in.account = r->in.account;
528 r2.in.nt_password = r->in.nt_password;
529 r2.in.nt_verifier = r->in.nt_verifier;
530 r2.in.lm_change = r->in.lm_change;
531 r2.in.lm_password = r->in.lm_password;
532 r2.in.lm_verifier = r->in.lm_verifier;
533 r2.in.password3 = NULL;
534 r2.out.dominfo = &dominfo;
535 r2.out.reject = &reject;
537 return dcesrv_samr_ChangePasswordUser3(dce_call, mem_ctx, &r2);
542 set password via a samr_CryptPassword buffer
543 this will in the 'msg' with modify operations that will update the user
544 password when applied
546 NTSTATUS samr_set_password(struct dcesrv_call_state *dce_call,
548 struct ldb_dn *account_dn, struct ldb_dn *domain_dn,
550 struct ldb_message *msg,
551 struct samr_CryptPassword *pwbuf)
554 DATA_BLOB new_password;
555 DATA_BLOB session_key = data_blob(NULL, 0);
557 nt_status = dcesrv_fetch_session_key(dce_call->conn, &session_key);
558 if (!NT_STATUS_IS_OK(nt_status)) {
562 arcfour_crypt_blob(pwbuf->data, 516, &session_key);
564 if (!extract_pw_from_buffer(mem_ctx, pwbuf->data, &new_password)) {
565 DEBUG(3,("samr: failed to decode password buffer\n"));
566 return NT_STATUS_WRONG_PASSWORD;
569 /* set the password - samdb needs to know both the domain and user DNs,
570 so the domain password policy can be used */
571 return samdb_set_password(sam_ctx, mem_ctx,
572 account_dn, domain_dn,
575 false, /* This is a password set, not change */
581 set password via a samr_CryptPasswordEx buffer
582 this will in the 'msg' with modify operations that will update the user
583 password when applied
585 NTSTATUS samr_set_password_ex(struct dcesrv_call_state *dce_call,
586 struct ldb_context *sam_ctx,
587 struct ldb_dn *account_dn, struct ldb_dn *domain_dn,
589 struct ldb_message *msg,
590 struct samr_CryptPasswordEx *pwbuf)
593 DATA_BLOB new_password;
594 DATA_BLOB co_session_key;
595 DATA_BLOB session_key = data_blob(NULL, 0);
596 struct MD5Context ctx;
598 nt_status = dcesrv_fetch_session_key(dce_call->conn, &session_key);
599 if (!NT_STATUS_IS_OK(nt_status)) {
603 co_session_key = data_blob_talloc(mem_ctx, NULL, 16);
604 if (!co_session_key.data) {
605 return NT_STATUS_NO_MEMORY;
609 MD5Update(&ctx, &pwbuf->data[516], 16);
610 MD5Update(&ctx, session_key.data, session_key.length);
611 MD5Final(co_session_key.data, &ctx);
613 arcfour_crypt_blob(pwbuf->data, 516, &co_session_key);
615 if (!extract_pw_from_buffer(mem_ctx, pwbuf->data, &new_password)) {
616 DEBUG(3,("samr: failed to decode password buffer\n"));
617 return NT_STATUS_WRONG_PASSWORD;
620 /* set the password - samdb needs to know both the domain and user DNs,
621 so the domain password policy can be used */
622 return samdb_set_password(sam_ctx, mem_ctx,
623 account_dn, domain_dn,
626 false, /* This is a password set, not change */