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"
32 #include "rpc_server/samr/proto.h"
35 samr_ChangePasswordUser
37 NTSTATUS dcesrv_samr_ChangePasswordUser(struct dcesrv_call_state *dce_call,
39 struct samr_ChangePasswordUser *r)
41 struct dcesrv_handle *h;
42 struct samr_account_state *a_state;
43 struct ldb_context *sam_ctx;
44 struct ldb_message **res;
46 struct samr_Password new_lmPwdHash, new_ntPwdHash, checkHash;
47 struct samr_Password *lm_pwd, *nt_pwd;
48 NTSTATUS status = NT_STATUS_OK;
49 const char * const attrs[] = { "dBCSPwd", "unicodePwd" , NULL };
51 DCESRV_PULL_HANDLE(h, r->in.user_handle, SAMR_HANDLE_USER);
55 /* basic sanity checking on parameters. Do this before any database ops */
56 if (!r->in.lm_present || !r->in.nt_present ||
57 !r->in.old_lm_crypted || !r->in.new_lm_crypted ||
58 !r->in.old_nt_crypted || !r->in.new_nt_crypted) {
59 /* we should really handle a change with lm not
61 return NT_STATUS_INVALID_PARAMETER_MIX;
64 /* Connect to a SAMDB with system privileges for fetching the old pw
66 sam_ctx = samdb_connect(mem_ctx, dce_call->event_ctx,
67 dce_call->conn->dce_ctx->lp_ctx,
68 system_session(dce_call->conn->dce_ctx->lp_ctx), 0);
69 if (sam_ctx == NULL) {
70 return NT_STATUS_INVALID_SYSTEM_SERVICE;
73 /* fetch the old hashes */
74 ret = gendb_search_dn(sam_ctx, mem_ctx,
75 a_state->account_dn, &res, attrs);
77 return NT_STATUS_WRONG_PASSWORD;
80 status = samdb_result_passwords(mem_ctx,
81 dce_call->conn->dce_ctx->lp_ctx,
82 res[0], &lm_pwd, &nt_pwd);
83 if (!NT_STATUS_IS_OK(status) || !nt_pwd) {
84 return NT_STATUS_WRONG_PASSWORD;
87 /* decrypt and check the new lm hash */
89 D_P16(lm_pwd->hash, r->in.new_lm_crypted->hash, new_lmPwdHash.hash);
90 D_P16(new_lmPwdHash.hash, r->in.old_lm_crypted->hash, checkHash.hash);
91 if (memcmp(checkHash.hash, lm_pwd, 16) != 0) {
92 return NT_STATUS_WRONG_PASSWORD;
96 /* decrypt and check the new nt hash */
97 D_P16(nt_pwd->hash, r->in.new_nt_crypted->hash, new_ntPwdHash.hash);
98 D_P16(new_ntPwdHash.hash, r->in.old_nt_crypted->hash, checkHash.hash);
99 if (memcmp(checkHash.hash, nt_pwd, 16) != 0) {
100 return NT_STATUS_WRONG_PASSWORD;
103 /* The NT Cross is not required by Win2k3 R2, but if present
104 check the nt cross hash */
105 if (r->in.cross1_present && r->in.nt_cross && lm_pwd) {
106 D_P16(lm_pwd->hash, r->in.nt_cross->hash, checkHash.hash);
107 if (memcmp(checkHash.hash, new_ntPwdHash.hash, 16) != 0) {
108 return NT_STATUS_WRONG_PASSWORD;
112 /* The LM Cross is not required by Win2k3 R2, but if present
113 check the lm cross hash */
114 if (r->in.cross2_present && r->in.lm_cross && lm_pwd) {
115 D_P16(nt_pwd->hash, r->in.lm_cross->hash, checkHash.hash);
116 if (memcmp(checkHash.hash, new_lmPwdHash.hash, 16) != 0) {
117 return NT_STATUS_WRONG_PASSWORD;
121 /* Start a SAM with user privileges for the password change */
122 sam_ctx = samdb_connect(mem_ctx, dce_call->event_ctx,
123 dce_call->conn->dce_ctx->lp_ctx,
124 dce_call->conn->auth_state.session_info, 0);
125 if (sam_ctx == NULL) {
126 return NT_STATUS_INVALID_SYSTEM_SERVICE;
129 /* Start transaction */
130 ret = ldb_transaction_start(sam_ctx);
131 if (ret != LDB_SUCCESS) {
132 DEBUG(1, ("Failed to start transaction: %s\n", ldb_errstring(sam_ctx)));
133 return NT_STATUS_TRANSACTION_ABORTED;
136 /* Performs the password modification. We pass the old hashes read out
137 * from the database since they were already checked against the user-
139 status = samdb_set_password(sam_ctx, mem_ctx,
141 a_state->domain_state->domain_dn,
142 NULL, &new_lmPwdHash, &new_ntPwdHash,
143 lm_pwd, nt_pwd, /* this is a user password change */
146 if (!NT_STATUS_IS_OK(status)) {
147 ldb_transaction_cancel(sam_ctx);
151 /* And this confirms it in a transaction commit */
152 ret = ldb_transaction_commit(sam_ctx);
153 if (ret != LDB_SUCCESS) {
154 DEBUG(1,("Failed to commit transaction to change password on %s: %s\n",
155 ldb_dn_get_linearized(a_state->account_dn),
156 ldb_errstring(sam_ctx)));
157 return NT_STATUS_TRANSACTION_ABORTED;
164 samr_OemChangePasswordUser2
166 NTSTATUS dcesrv_samr_OemChangePasswordUser2(struct dcesrv_call_state *dce_call,
168 struct samr_OemChangePasswordUser2 *r)
171 DATA_BLOB new_password, new_unicode_password;
173 struct samr_CryptPassword *pwbuf = r->in.password;
174 struct ldb_context *sam_ctx;
175 struct ldb_dn *user_dn;
177 struct ldb_message **res;
178 const char * const attrs[] = { "objectSid", "dBCSPwd", NULL };
179 struct samr_Password *lm_pwd;
180 DATA_BLOB lm_pwd_blob;
181 uint8_t new_lm_hash[16];
182 struct samr_Password lm_verifier;
183 size_t unicode_pw_len;
186 return NT_STATUS_INVALID_PARAMETER;
189 if (r->in.hash == NULL) {
190 return NT_STATUS_INVALID_PARAMETER;
193 /* this call can only work with lanman auth */
194 if (!lpcfg_lanman_auth(dce_call->conn->dce_ctx->lp_ctx)) {
195 return NT_STATUS_WRONG_PASSWORD;
198 /* Connect to a SAMDB with system privileges for fetching the old pw
200 sam_ctx = samdb_connect(mem_ctx, dce_call->event_ctx,
201 dce_call->conn->dce_ctx->lp_ctx,
202 system_session(dce_call->conn->dce_ctx->lp_ctx), 0);
203 if (sam_ctx == NULL) {
204 return NT_STATUS_INVALID_SYSTEM_SERVICE;
207 /* we need the users dn and the domain dn (derived from the
208 user SID). We also need the current lm password hash in
209 order to decrypt the incoming password */
210 ret = gendb_search(sam_ctx,
211 mem_ctx, NULL, &res, attrs,
212 "(&(sAMAccountName=%s)(objectclass=user))",
213 r->in.account->string);
215 /* Don't give the game away: (don't allow anonymous users to prove the existance of usernames) */
216 return NT_STATUS_WRONG_PASSWORD;
219 user_dn = res[0]->dn;
221 status = samdb_result_passwords(mem_ctx, dce_call->conn->dce_ctx->lp_ctx,
222 res[0], &lm_pwd, NULL);
223 if (!NT_STATUS_IS_OK(status) || !lm_pwd) {
224 return NT_STATUS_WRONG_PASSWORD;
227 /* decrypt the password we have been given */
228 lm_pwd_blob = data_blob(lm_pwd->hash, sizeof(lm_pwd->hash));
229 arcfour_crypt_blob(pwbuf->data, 516, &lm_pwd_blob);
230 data_blob_free(&lm_pwd_blob);
232 if (!extract_pw_from_buffer(mem_ctx, pwbuf->data, &new_password)) {
233 DEBUG(3,("samr: failed to decode password buffer\n"));
234 return NT_STATUS_WRONG_PASSWORD;
237 if (!convert_string_talloc_handle(mem_ctx, lpcfg_iconv_handle(dce_call->conn->dce_ctx->lp_ctx),
239 (const char *)new_password.data,
241 (void **)&new_pass, NULL)) {
242 DEBUG(3,("samr: failed to convert incoming password buffer to unix charset\n"));
243 return NT_STATUS_WRONG_PASSWORD;
246 if (!convert_string_talloc_handle(mem_ctx, lpcfg_iconv_handle(dce_call->conn->dce_ctx->lp_ctx),
248 (const char *)new_password.data,
250 (void **)&new_unicode_password.data, &unicode_pw_len)) {
251 DEBUG(3,("samr: failed to convert incoming password buffer to UTF16 charset\n"));
252 return NT_STATUS_WRONG_PASSWORD;
254 new_unicode_password.length = unicode_pw_len;
256 E_deshash(new_pass, new_lm_hash);
257 E_old_pw_hash(new_lm_hash, lm_pwd->hash, lm_verifier.hash);
258 if (memcmp(lm_verifier.hash, r->in.hash->hash, 16) != 0) {
259 return NT_STATUS_WRONG_PASSWORD;
262 /* Connect to a SAMDB with user privileges for the password change */
263 sam_ctx = samdb_connect(mem_ctx, dce_call->event_ctx,
264 dce_call->conn->dce_ctx->lp_ctx,
265 dce_call->conn->auth_state.session_info, 0);
266 if (sam_ctx == NULL) {
267 return NT_STATUS_INVALID_SYSTEM_SERVICE;
270 /* Start transaction */
271 ret = ldb_transaction_start(sam_ctx);
272 if (ret != LDB_SUCCESS) {
273 DEBUG(1, ("Failed to start transaction: %s\n", ldb_errstring(sam_ctx)));
274 return NT_STATUS_TRANSACTION_ABORTED;
277 /* Performs the password modification. We pass the old hashes read out
278 * from the database since they were already checked against the user-
280 status = samdb_set_password(sam_ctx, mem_ctx,
282 &new_unicode_password,
284 lm_pwd, NULL, /* this is a user password change */
287 if (!NT_STATUS_IS_OK(status)) {
288 ldb_transaction_cancel(sam_ctx);
292 /* And this confirms it in a transaction commit */
293 ret = ldb_transaction_commit(sam_ctx);
294 if (ret != LDB_SUCCESS) {
295 DEBUG(1,("Failed to commit transaction to change password on %s: %s\n",
296 ldb_dn_get_linearized(user_dn),
297 ldb_errstring(sam_ctx)));
298 return NT_STATUS_TRANSACTION_ABORTED;
306 samr_ChangePasswordUser3
308 NTSTATUS dcesrv_samr_ChangePasswordUser3(struct dcesrv_call_state *dce_call,
310 struct samr_ChangePasswordUser3 *r)
313 DATA_BLOB new_password;
314 struct ldb_context *sam_ctx = NULL;
315 struct ldb_dn *user_dn;
317 struct ldb_message **res;
318 const char * const attrs[] = { "unicodePwd", "dBCSPwd", NULL };
319 struct samr_Password *nt_pwd, *lm_pwd;
320 DATA_BLOB nt_pwd_blob;
321 struct samr_DomInfo1 *dominfo = NULL;
322 struct userPwdChangeFailureInformation *reject = NULL;
323 enum samPwdChangeReason reason = SAM_PWD_CHANGE_NO_ERROR;
324 uint8_t new_nt_hash[16], new_lm_hash[16];
325 struct samr_Password nt_verifier, lm_verifier;
327 *r->out.dominfo = NULL;
328 *r->out.reject = NULL;
330 if (r->in.nt_password == NULL ||
331 r->in.nt_verifier == NULL) {
332 return NT_STATUS_INVALID_PARAMETER;
335 /* Connect to a SAMDB with system privileges for fetching the old pw
337 sam_ctx = samdb_connect(mem_ctx, dce_call->event_ctx,
338 dce_call->conn->dce_ctx->lp_ctx,
339 system_session(dce_call->conn->dce_ctx->lp_ctx), 0);
340 if (sam_ctx == NULL) {
341 return NT_STATUS_INVALID_SYSTEM_SERVICE;
344 /* we need the users dn and the domain dn (derived from the
345 user SID). We also need the current lm and nt password hashes
346 in order to decrypt the incoming passwords */
347 ret = gendb_search(sam_ctx,
348 mem_ctx, NULL, &res, attrs,
349 "(&(sAMAccountName=%s)(objectclass=user))",
350 r->in.account->string);
352 /* Don't give the game away: (don't allow anonymous users to prove the existance of usernames) */
353 status = NT_STATUS_WRONG_PASSWORD;
357 user_dn = res[0]->dn;
359 status = samdb_result_passwords(mem_ctx, dce_call->conn->dce_ctx->lp_ctx,
360 res[0], &lm_pwd, &nt_pwd);
361 if (!NT_STATUS_IS_OK(status) ) {
366 status = NT_STATUS_WRONG_PASSWORD;
370 /* decrypt the password we have been given */
371 nt_pwd_blob = data_blob(nt_pwd->hash, sizeof(nt_pwd->hash));
372 arcfour_crypt_blob(r->in.nt_password->data, 516, &nt_pwd_blob);
373 data_blob_free(&nt_pwd_blob);
375 if (!extract_pw_from_buffer(mem_ctx, r->in.nt_password->data, &new_password)) {
376 DEBUG(3,("samr: failed to decode password buffer\n"));
377 status = NT_STATUS_WRONG_PASSWORD;
381 if (r->in.nt_verifier == NULL) {
382 status = NT_STATUS_WRONG_PASSWORD;
386 /* check NT verifier */
387 mdfour(new_nt_hash, new_password.data, new_password.length);
389 E_old_pw_hash(new_nt_hash, nt_pwd->hash, nt_verifier.hash);
390 if (memcmp(nt_verifier.hash, r->in.nt_verifier->hash, 16) != 0) {
391 status = NT_STATUS_WRONG_PASSWORD;
395 /* check LM verifier (really not needed as we just checked the
396 * much stronger NT hash, but the RPC-SAMR test checks for
398 if (lm_pwd && r->in.lm_verifier != NULL) {
400 if (!convert_string_talloc_handle(mem_ctx, lpcfg_iconv_handle(dce_call->conn->dce_ctx->lp_ctx),
402 (const char *)new_password.data,
404 (void **)&new_pass, NULL)) {
405 E_deshash(new_pass, new_lm_hash);
406 E_old_pw_hash(new_nt_hash, lm_pwd->hash, lm_verifier.hash);
407 if (memcmp(lm_verifier.hash, r->in.lm_verifier->hash, 16) != 0) {
408 status = NT_STATUS_WRONG_PASSWORD;
414 /* Connect to a SAMDB with user privileges for the password change */
415 sam_ctx = samdb_connect(mem_ctx, dce_call->event_ctx,
416 dce_call->conn->dce_ctx->lp_ctx,
417 dce_call->conn->auth_state.session_info, 0);
418 if (sam_ctx == NULL) {
419 return NT_STATUS_INVALID_SYSTEM_SERVICE;
422 ret = ldb_transaction_start(sam_ctx);
423 if (ret != LDB_SUCCESS) {
424 DEBUG(1, ("Failed to start transaction: %s\n", ldb_errstring(sam_ctx)));
425 return NT_STATUS_TRANSACTION_ABORTED;
428 /* Performs the password modification. We pass the old hashes read out
429 * from the database since they were already checked against the user-
431 status = samdb_set_password(sam_ctx, mem_ctx,
435 lm_pwd, nt_pwd, /* this is a user password change */
439 if (!NT_STATUS_IS_OK(status)) {
440 ldb_transaction_cancel(sam_ctx);
444 /* And this confirms it in a transaction commit */
445 ret = ldb_transaction_commit(sam_ctx);
446 if (ret != LDB_SUCCESS) {
447 DEBUG(1,("Failed to commit transaction to change password on %s: %s\n",
448 ldb_dn_get_linearized(user_dn),
449 ldb_errstring(sam_ctx)));
450 status = NT_STATUS_TRANSACTION_ABORTED;
457 reject = talloc_zero(mem_ctx, struct userPwdChangeFailureInformation);
458 if (reject != NULL) {
459 reject->extendedFailureReason = reason;
461 *r->out.reject = reject;
464 *r->out.dominfo = dominfo;
471 samr_ChangePasswordUser2
473 easy - just a subset of samr_ChangePasswordUser3
475 NTSTATUS dcesrv_samr_ChangePasswordUser2(struct dcesrv_call_state *dce_call,
477 struct samr_ChangePasswordUser2 *r)
479 struct samr_ChangePasswordUser3 r2;
480 struct samr_DomInfo1 *dominfo = NULL;
481 struct userPwdChangeFailureInformation *reject = NULL;
483 r2.in.server = r->in.server;
484 r2.in.account = r->in.account;
485 r2.in.nt_password = r->in.nt_password;
486 r2.in.nt_verifier = r->in.nt_verifier;
487 r2.in.lm_change = r->in.lm_change;
488 r2.in.lm_password = r->in.lm_password;
489 r2.in.lm_verifier = r->in.lm_verifier;
490 r2.in.password3 = NULL;
491 r2.out.dominfo = &dominfo;
492 r2.out.reject = &reject;
494 return dcesrv_samr_ChangePasswordUser3(dce_call, mem_ctx, &r2);
499 set password via a samr_CryptPassword buffer
501 NTSTATUS samr_set_password(struct dcesrv_call_state *dce_call,
502 struct ldb_context *sam_ctx,
503 struct ldb_dn *account_dn, struct ldb_dn *domain_dn,
505 struct samr_CryptPassword *pwbuf)
508 DATA_BLOB new_password;
509 DATA_BLOB session_key = data_blob(NULL, 0);
511 nt_status = dcesrv_fetch_session_key(dce_call->conn, &session_key);
512 if (!NT_STATUS_IS_OK(nt_status)) {
516 arcfour_crypt_blob(pwbuf->data, 516, &session_key);
518 if (!extract_pw_from_buffer(mem_ctx, pwbuf->data, &new_password)) {
519 DEBUG(3,("samr: failed to decode password buffer\n"));
520 return NT_STATUS_WRONG_PASSWORD;
523 /* set the password - samdb needs to know both the domain and user DNs,
524 so the domain password policy can be used */
525 return samdb_set_password(sam_ctx, mem_ctx,
526 account_dn, domain_dn,
529 NULL, NULL, /* This is a password set, not change */
535 set password via a samr_CryptPasswordEx buffer
537 NTSTATUS samr_set_password_ex(struct dcesrv_call_state *dce_call,
538 struct ldb_context *sam_ctx,
539 struct ldb_dn *account_dn,
540 struct ldb_dn *domain_dn,
542 struct samr_CryptPasswordEx *pwbuf)
545 DATA_BLOB new_password;
546 DATA_BLOB co_session_key;
547 DATA_BLOB session_key = data_blob(NULL, 0);
548 struct MD5Context ctx;
550 nt_status = dcesrv_fetch_session_key(dce_call->conn, &session_key);
551 if (!NT_STATUS_IS_OK(nt_status)) {
555 co_session_key = data_blob_talloc(mem_ctx, NULL, 16);
556 if (!co_session_key.data) {
557 return NT_STATUS_NO_MEMORY;
561 MD5Update(&ctx, &pwbuf->data[516], 16);
562 MD5Update(&ctx, session_key.data, session_key.length);
563 MD5Final(co_session_key.data, &ctx);
565 arcfour_crypt_blob(pwbuf->data, 516, &co_session_key);
567 if (!extract_pw_from_buffer(mem_ctx, pwbuf->data, &new_password)) {
568 DEBUG(3,("samr: failed to decode password buffer\n"));
569 return NT_STATUS_WRONG_PASSWORD;
572 /* set the password - samdb needs to know both the domain and user DNs,
573 so the domain password policy can be used */
574 return samdb_set_password(sam_ctx, mem_ctx,
575 account_dn, domain_dn,
578 NULL, NULL, /* This is a password set, not change */
583 set password via encrypted NT and LM hash buffers
585 NTSTATUS samr_set_password_buffers(struct dcesrv_call_state *dce_call,
586 struct ldb_context *sam_ctx,
587 struct ldb_dn *account_dn,
588 struct ldb_dn *domain_dn,
590 const uint8_t *lm_pwd_hash,
591 const uint8_t *nt_pwd_hash)
593 struct samr_Password *d_lm_pwd_hash = NULL, *d_nt_pwd_hash = NULL;
594 DATA_BLOB session_key = data_blob(NULL, 0);
596 NTSTATUS nt_status = NT_STATUS_OK;
598 nt_status = dcesrv_fetch_session_key(dce_call->conn, &session_key);
599 if (!NT_STATUS_IS_OK(nt_status)) {
603 if (lm_pwd_hash != NULL) {
604 in = data_blob_const(lm_pwd_hash, 16);
605 out = data_blob_talloc_zero(mem_ctx, 16);
607 sess_crypt_blob(&out, &in, &session_key, false);
609 d_lm_pwd_hash = (struct samr_Password *) out.data;
611 if (nt_pwd_hash != NULL) {
612 in = data_blob_const(nt_pwd_hash, 16);
613 out = data_blob_talloc_zero(mem_ctx, 16);
615 sess_crypt_blob(&out, &in, &session_key, false);
617 d_nt_pwd_hash = (struct samr_Password *) out.data;
620 if ((d_lm_pwd_hash != NULL) || (d_nt_pwd_hash != NULL)) {
621 nt_status = samdb_set_password(sam_ctx, mem_ctx, account_dn,
623 d_lm_pwd_hash, d_nt_pwd_hash,
624 NULL, NULL, /* this is a password set */