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"
33 samr_ChangePasswordUser
35 NTSTATUS
dcesrv_samr_ChangePasswordUser(struct dcesrv_call_state
*dce_call
,
37 struct samr_ChangePasswordUser
*r
)
39 struct dcesrv_handle
*h
;
40 struct samr_account_state
*a_state
;
41 struct ldb_context
*sam_ctx
;
42 struct ldb_message
**res
;
44 struct samr_Password new_lmPwdHash
, new_ntPwdHash
, checkHash
;
45 struct samr_Password
*lm_pwd
, *nt_pwd
;
46 NTSTATUS status
= NT_STATUS_OK
;
47 const char * const attrs
[] = { "dBCSPwd", "unicodePwd" , NULL
};
49 DCESRV_PULL_HANDLE(h
, r
->in
.user_handle
, SAMR_HANDLE_USER
);
53 /* basic sanity checking on parameters. Do this before any database ops */
54 if (!r
->in
.lm_present
|| !r
->in
.nt_present
||
55 !r
->in
.old_lm_crypted
|| !r
->in
.new_lm_crypted
||
56 !r
->in
.old_nt_crypted
|| !r
->in
.new_nt_crypted
) {
57 /* we should really handle a change with lm not
59 return NT_STATUS_INVALID_PARAMETER_MIX
;
62 /* Connect to a SAMDB with system privileges for fetching the old pw
64 sam_ctx
= samdb_connect(mem_ctx
, dce_call
->event_ctx
,
65 dce_call
->conn
->dce_ctx
->lp_ctx
,
66 system_session(dce_call
->conn
->dce_ctx
->lp_ctx
), 0);
67 if (sam_ctx
== NULL
) {
68 return NT_STATUS_INVALID_SYSTEM_SERVICE
;
71 /* fetch the old hashes */
72 ret
= gendb_search_dn(sam_ctx
, mem_ctx
,
73 a_state
->account_dn
, &res
, attrs
);
75 return NT_STATUS_WRONG_PASSWORD
;
78 status
= samdb_result_passwords(mem_ctx
,
79 dce_call
->conn
->dce_ctx
->lp_ctx
,
80 res
[0], &lm_pwd
, &nt_pwd
);
81 if (!NT_STATUS_IS_OK(status
) || !nt_pwd
) {
82 return NT_STATUS_WRONG_PASSWORD
;
85 /* decrypt and check the new lm hash */
87 D_P16(lm_pwd
->hash
, r
->in
.new_lm_crypted
->hash
, new_lmPwdHash
.hash
);
88 D_P16(new_lmPwdHash
.hash
, r
->in
.old_lm_crypted
->hash
, checkHash
.hash
);
89 if (memcmp(checkHash
.hash
, lm_pwd
, 16) != 0) {
90 return NT_STATUS_WRONG_PASSWORD
;
94 /* decrypt and check the new nt hash */
95 D_P16(nt_pwd
->hash
, r
->in
.new_nt_crypted
->hash
, new_ntPwdHash
.hash
);
96 D_P16(new_ntPwdHash
.hash
, r
->in
.old_nt_crypted
->hash
, checkHash
.hash
);
97 if (memcmp(checkHash
.hash
, nt_pwd
, 16) != 0) {
98 return NT_STATUS_WRONG_PASSWORD
;
101 /* The NT Cross is not required by Win2k3 R2, but if present
102 check the nt cross hash */
103 if (r
->in
.cross1_present
&& r
->in
.nt_cross
&& lm_pwd
) {
104 D_P16(lm_pwd
->hash
, r
->in
.nt_cross
->hash
, checkHash
.hash
);
105 if (memcmp(checkHash
.hash
, new_ntPwdHash
.hash
, 16) != 0) {
106 return NT_STATUS_WRONG_PASSWORD
;
110 /* The LM Cross is not required by Win2k3 R2, but if present
111 check the lm cross hash */
112 if (r
->in
.cross2_present
&& r
->in
.lm_cross
&& lm_pwd
) {
113 D_P16(nt_pwd
->hash
, r
->in
.lm_cross
->hash
, checkHash
.hash
);
114 if (memcmp(checkHash
.hash
, new_lmPwdHash
.hash
, 16) != 0) {
115 return NT_STATUS_WRONG_PASSWORD
;
119 /* Start a SAM with user privileges for the password change */
120 sam_ctx
= samdb_connect(mem_ctx
, dce_call
->event_ctx
,
121 dce_call
->conn
->dce_ctx
->lp_ctx
,
122 dce_call
->conn
->auth_state
.session_info
, 0);
123 if (sam_ctx
== NULL
) {
124 return NT_STATUS_INVALID_SYSTEM_SERVICE
;
127 /* Start transaction */
128 ret
= ldb_transaction_start(sam_ctx
);
129 if (ret
!= LDB_SUCCESS
) {
130 DEBUG(1, ("Failed to start transaction: %s\n", ldb_errstring(sam_ctx
)));
131 return NT_STATUS_TRANSACTION_ABORTED
;
134 /* Performs the password modification. We pass the old hashes read out
135 * from the database since they were already checked against the user-
137 status
= samdb_set_password(sam_ctx
, mem_ctx
,
139 a_state
->domain_state
->domain_dn
,
140 NULL
, &new_lmPwdHash
, &new_ntPwdHash
,
141 lm_pwd
, nt_pwd
, /* this is a user password change */
144 if (!NT_STATUS_IS_OK(status
)) {
145 ldb_transaction_cancel(sam_ctx
);
149 /* And this confirms it in a transaction commit */
150 ret
= ldb_transaction_commit(sam_ctx
);
151 if (ret
!= LDB_SUCCESS
) {
152 DEBUG(1,("Failed to commit transaction to change password on %s: %s\n",
153 ldb_dn_get_linearized(a_state
->account_dn
),
154 ldb_errstring(sam_ctx
)));
155 return NT_STATUS_TRANSACTION_ABORTED
;
162 samr_OemChangePasswordUser2
164 NTSTATUS
dcesrv_samr_OemChangePasswordUser2(struct dcesrv_call_state
*dce_call
,
166 struct samr_OemChangePasswordUser2
*r
)
169 DATA_BLOB new_password
, new_unicode_password
;
171 struct samr_CryptPassword
*pwbuf
= r
->in
.password
;
172 struct ldb_context
*sam_ctx
;
173 struct ldb_dn
*user_dn
;
175 struct ldb_message
**res
;
176 const char * const attrs
[] = { "objectSid", "dBCSPwd", NULL
};
177 struct samr_Password
*lm_pwd
;
178 DATA_BLOB lm_pwd_blob
;
179 uint8_t new_lm_hash
[16];
180 struct samr_Password lm_verifier
;
181 size_t unicode_pw_len
;
184 return NT_STATUS_INVALID_PARAMETER
;
187 if (r
->in
.hash
== NULL
) {
188 return NT_STATUS_INVALID_PARAMETER
;
191 /* this call can only work with lanman auth */
192 if (!lpcfg_lanman_auth(dce_call
->conn
->dce_ctx
->lp_ctx
)) {
193 return NT_STATUS_WRONG_PASSWORD
;
196 /* Connect to a SAMDB with system privileges for fetching the old pw
198 sam_ctx
= samdb_connect(mem_ctx
, dce_call
->event_ctx
,
199 dce_call
->conn
->dce_ctx
->lp_ctx
,
200 system_session(dce_call
->conn
->dce_ctx
->lp_ctx
), 0);
201 if (sam_ctx
== NULL
) {
202 return NT_STATUS_INVALID_SYSTEM_SERVICE
;
205 /* we need the users dn and the domain dn (derived from the
206 user SID). We also need the current lm password hash in
207 order to decrypt the incoming password */
208 ret
= gendb_search(sam_ctx
,
209 mem_ctx
, NULL
, &res
, attrs
,
210 "(&(sAMAccountName=%s)(objectclass=user))",
211 r
->in
.account
->string
);
213 /* Don't give the game away: (don't allow anonymous users to prove the existance of usernames) */
214 return NT_STATUS_WRONG_PASSWORD
;
217 user_dn
= res
[0]->dn
;
219 status
= samdb_result_passwords(mem_ctx
, dce_call
->conn
->dce_ctx
->lp_ctx
,
220 res
[0], &lm_pwd
, NULL
);
221 if (!NT_STATUS_IS_OK(status
) || !lm_pwd
) {
222 return NT_STATUS_WRONG_PASSWORD
;
225 /* decrypt the password we have been given */
226 lm_pwd_blob
= data_blob(lm_pwd
->hash
, sizeof(lm_pwd
->hash
));
227 arcfour_crypt_blob(pwbuf
->data
, 516, &lm_pwd_blob
);
228 data_blob_free(&lm_pwd_blob
);
230 if (!extract_pw_from_buffer(mem_ctx
, pwbuf
->data
, &new_password
)) {
231 DEBUG(3,("samr: failed to decode password buffer\n"));
232 return NT_STATUS_WRONG_PASSWORD
;
235 if (!convert_string_talloc_convenience(mem_ctx
, lpcfg_iconv_convenience(dce_call
->conn
->dce_ctx
->lp_ctx
),
237 (const char *)new_password
.data
,
239 (void **)&new_pass
, NULL
, false)) {
240 DEBUG(3,("samr: failed to convert incoming password buffer to unix charset\n"));
241 return NT_STATUS_WRONG_PASSWORD
;
244 if (!convert_string_talloc_convenience(mem_ctx
, lpcfg_iconv_convenience(dce_call
->conn
->dce_ctx
->lp_ctx
),
246 (const char *)new_password
.data
,
248 (void **)&new_unicode_password
.data
, &unicode_pw_len
, false)) {
249 DEBUG(3,("samr: failed to convert incoming password buffer to UTF16 charset\n"));
250 return NT_STATUS_WRONG_PASSWORD
;
252 new_unicode_password
.length
= unicode_pw_len
;
254 E_deshash(new_pass
, new_lm_hash
);
255 E_old_pw_hash(new_lm_hash
, lm_pwd
->hash
, lm_verifier
.hash
);
256 if (memcmp(lm_verifier
.hash
, r
->in
.hash
->hash
, 16) != 0) {
257 return NT_STATUS_WRONG_PASSWORD
;
260 /* Connect to a SAMDB with user privileges for the password change */
261 sam_ctx
= samdb_connect(mem_ctx
, dce_call
->event_ctx
,
262 dce_call
->conn
->dce_ctx
->lp_ctx
,
263 dce_call
->conn
->auth_state
.session_info
, 0);
264 if (sam_ctx
== NULL
) {
265 return NT_STATUS_INVALID_SYSTEM_SERVICE
;
268 /* Start transaction */
269 ret
= ldb_transaction_start(sam_ctx
);
270 if (ret
!= LDB_SUCCESS
) {
271 DEBUG(1, ("Failed to start transaction: %s\n", ldb_errstring(sam_ctx
)));
272 return NT_STATUS_TRANSACTION_ABORTED
;
275 /* Performs the password modification. We pass the old hashes read out
276 * from the database since they were already checked against the user-
278 status
= samdb_set_password(sam_ctx
, mem_ctx
,
280 &new_unicode_password
,
282 lm_pwd
, NULL
, /* this is a user password change */
285 if (!NT_STATUS_IS_OK(status
)) {
286 ldb_transaction_cancel(sam_ctx
);
290 /* And this confirms it in a transaction commit */
291 ret
= ldb_transaction_commit(sam_ctx
);
292 if (ret
!= LDB_SUCCESS
) {
293 DEBUG(1,("Failed to commit transaction to change password on %s: %s\n",
294 ldb_dn_get_linearized(user_dn
),
295 ldb_errstring(sam_ctx
)));
296 return NT_STATUS_TRANSACTION_ABORTED
;
304 samr_ChangePasswordUser3
306 NTSTATUS
dcesrv_samr_ChangePasswordUser3(struct dcesrv_call_state
*dce_call
,
308 struct samr_ChangePasswordUser3
*r
)
311 DATA_BLOB new_password
;
312 struct ldb_context
*sam_ctx
= NULL
;
313 struct ldb_dn
*user_dn
;
315 struct ldb_message
**res
;
316 const char * const attrs
[] = { "unicodePwd", "dBCSPwd", NULL
};
317 struct samr_Password
*nt_pwd
, *lm_pwd
;
318 DATA_BLOB nt_pwd_blob
;
319 struct samr_DomInfo1
*dominfo
= NULL
;
320 struct userPwdChangeFailureInformation
*reject
= NULL
;
321 enum samPwdChangeReason reason
= SAM_PWD_CHANGE_NO_ERROR
;
322 uint8_t new_nt_hash
[16], new_lm_hash
[16];
323 struct samr_Password nt_verifier
, lm_verifier
;
325 *r
->out
.dominfo
= NULL
;
326 *r
->out
.reject
= NULL
;
328 if (r
->in
.nt_password
== NULL
||
329 r
->in
.nt_verifier
== NULL
) {
330 return NT_STATUS_INVALID_PARAMETER
;
333 /* Connect to a SAMDB with system privileges for fetching the old pw
335 sam_ctx
= samdb_connect(mem_ctx
, dce_call
->event_ctx
,
336 dce_call
->conn
->dce_ctx
->lp_ctx
,
337 system_session(dce_call
->conn
->dce_ctx
->lp_ctx
), 0);
338 if (sam_ctx
== NULL
) {
339 return NT_STATUS_INVALID_SYSTEM_SERVICE
;
342 /* we need the users dn and the domain dn (derived from the
343 user SID). We also need the current lm and nt password hashes
344 in order to decrypt the incoming passwords */
345 ret
= gendb_search(sam_ctx
,
346 mem_ctx
, NULL
, &res
, attrs
,
347 "(&(sAMAccountName=%s)(objectclass=user))",
348 r
->in
.account
->string
);
350 /* Don't give the game away: (don't allow anonymous users to prove the existance of usernames) */
351 status
= NT_STATUS_WRONG_PASSWORD
;
355 user_dn
= res
[0]->dn
;
357 status
= samdb_result_passwords(mem_ctx
, dce_call
->conn
->dce_ctx
->lp_ctx
,
358 res
[0], &lm_pwd
, &nt_pwd
);
359 if (!NT_STATUS_IS_OK(status
) ) {
364 status
= NT_STATUS_WRONG_PASSWORD
;
368 /* decrypt the password we have been given */
369 nt_pwd_blob
= data_blob(nt_pwd
->hash
, sizeof(nt_pwd
->hash
));
370 arcfour_crypt_blob(r
->in
.nt_password
->data
, 516, &nt_pwd_blob
);
371 data_blob_free(&nt_pwd_blob
);
373 if (!extract_pw_from_buffer(mem_ctx
, r
->in
.nt_password
->data
, &new_password
)) {
374 DEBUG(3,("samr: failed to decode password buffer\n"));
375 status
= NT_STATUS_WRONG_PASSWORD
;
379 if (r
->in
.nt_verifier
== NULL
) {
380 status
= NT_STATUS_WRONG_PASSWORD
;
384 /* check NT verifier */
385 mdfour(new_nt_hash
, new_password
.data
, new_password
.length
);
387 E_old_pw_hash(new_nt_hash
, nt_pwd
->hash
, nt_verifier
.hash
);
388 if (memcmp(nt_verifier
.hash
, r
->in
.nt_verifier
->hash
, 16) != 0) {
389 status
= NT_STATUS_WRONG_PASSWORD
;
393 /* check LM verifier (really not needed as we just checked the
394 * much stronger NT hash, but the RPC-SAMR test checks for
396 if (lm_pwd
&& r
->in
.lm_verifier
!= NULL
) {
398 if (!convert_string_talloc_convenience(mem_ctx
, lpcfg_iconv_convenience(dce_call
->conn
->dce_ctx
->lp_ctx
),
400 (const char *)new_password
.data
,
402 (void **)&new_pass
, NULL
, false)) {
403 E_deshash(new_pass
, new_lm_hash
);
404 E_old_pw_hash(new_nt_hash
, lm_pwd
->hash
, lm_verifier
.hash
);
405 if (memcmp(lm_verifier
.hash
, r
->in
.lm_verifier
->hash
, 16) != 0) {
406 status
= NT_STATUS_WRONG_PASSWORD
;
412 /* Connect to a SAMDB with user privileges for the password change */
413 sam_ctx
= samdb_connect(mem_ctx
, dce_call
->event_ctx
,
414 dce_call
->conn
->dce_ctx
->lp_ctx
,
415 dce_call
->conn
->auth_state
.session_info
, 0);
416 if (sam_ctx
== NULL
) {
417 return NT_STATUS_INVALID_SYSTEM_SERVICE
;
420 ret
= ldb_transaction_start(sam_ctx
);
421 if (ret
!= LDB_SUCCESS
) {
422 DEBUG(1, ("Failed to start transaction: %s\n", ldb_errstring(sam_ctx
)));
423 return NT_STATUS_TRANSACTION_ABORTED
;
426 /* Performs the password modification. We pass the old hashes read out
427 * from the database since they were already checked against the user-
429 status
= samdb_set_password(sam_ctx
, mem_ctx
,
433 lm_pwd
, nt_pwd
, /* this is a user password change */
437 if (!NT_STATUS_IS_OK(status
)) {
438 ldb_transaction_cancel(sam_ctx
);
442 /* And this confirms it in a transaction commit */
443 ret
= ldb_transaction_commit(sam_ctx
);
444 if (ret
!= LDB_SUCCESS
) {
445 DEBUG(1,("Failed to commit transaction to change password on %s: %s\n",
446 ldb_dn_get_linearized(user_dn
),
447 ldb_errstring(sam_ctx
)));
448 status
= NT_STATUS_TRANSACTION_ABORTED
;
455 reject
= talloc_zero(mem_ctx
, struct userPwdChangeFailureInformation
);
456 if (reject
!= NULL
) {
457 reject
->extendedFailureReason
= reason
;
459 *r
->out
.reject
= reject
;
462 *r
->out
.dominfo
= dominfo
;
469 samr_ChangePasswordUser2
471 easy - just a subset of samr_ChangePasswordUser3
473 NTSTATUS
dcesrv_samr_ChangePasswordUser2(struct dcesrv_call_state
*dce_call
,
475 struct samr_ChangePasswordUser2
*r
)
477 struct samr_ChangePasswordUser3 r2
;
478 struct samr_DomInfo1
*dominfo
= NULL
;
479 struct userPwdChangeFailureInformation
*reject
= NULL
;
481 r2
.in
.server
= r
->in
.server
;
482 r2
.in
.account
= r
->in
.account
;
483 r2
.in
.nt_password
= r
->in
.nt_password
;
484 r2
.in
.nt_verifier
= r
->in
.nt_verifier
;
485 r2
.in
.lm_change
= r
->in
.lm_change
;
486 r2
.in
.lm_password
= r
->in
.lm_password
;
487 r2
.in
.lm_verifier
= r
->in
.lm_verifier
;
488 r2
.in
.password3
= NULL
;
489 r2
.out
.dominfo
= &dominfo
;
490 r2
.out
.reject
= &reject
;
492 return dcesrv_samr_ChangePasswordUser3(dce_call
, mem_ctx
, &r2
);
497 set password via a samr_CryptPassword buffer
499 NTSTATUS
samr_set_password(struct dcesrv_call_state
*dce_call
,
500 struct ldb_context
*sam_ctx
,
501 struct ldb_dn
*account_dn
, struct ldb_dn
*domain_dn
,
503 struct samr_CryptPassword
*pwbuf
)
506 DATA_BLOB new_password
;
507 DATA_BLOB session_key
= data_blob(NULL
, 0);
509 nt_status
= dcesrv_fetch_session_key(dce_call
->conn
, &session_key
);
510 if (!NT_STATUS_IS_OK(nt_status
)) {
514 arcfour_crypt_blob(pwbuf
->data
, 516, &session_key
);
516 if (!extract_pw_from_buffer(mem_ctx
, pwbuf
->data
, &new_password
)) {
517 DEBUG(3,("samr: failed to decode password buffer\n"));
518 return NT_STATUS_WRONG_PASSWORD
;
521 /* set the password - samdb needs to know both the domain and user DNs,
522 so the domain password policy can be used */
523 return samdb_set_password(sam_ctx
, mem_ctx
,
524 account_dn
, domain_dn
,
527 NULL
, NULL
, /* This is a password set, not change */
533 set password via a samr_CryptPasswordEx buffer
535 NTSTATUS
samr_set_password_ex(struct dcesrv_call_state
*dce_call
,
536 struct ldb_context
*sam_ctx
,
537 struct ldb_dn
*account_dn
,
538 struct ldb_dn
*domain_dn
,
540 struct samr_CryptPasswordEx
*pwbuf
)
543 DATA_BLOB new_password
;
544 DATA_BLOB co_session_key
;
545 DATA_BLOB session_key
= data_blob(NULL
, 0);
546 struct MD5Context ctx
;
548 nt_status
= dcesrv_fetch_session_key(dce_call
->conn
, &session_key
);
549 if (!NT_STATUS_IS_OK(nt_status
)) {
553 co_session_key
= data_blob_talloc(mem_ctx
, NULL
, 16);
554 if (!co_session_key
.data
) {
555 return NT_STATUS_NO_MEMORY
;
559 MD5Update(&ctx
, &pwbuf
->data
[516], 16);
560 MD5Update(&ctx
, session_key
.data
, session_key
.length
);
561 MD5Final(co_session_key
.data
, &ctx
);
563 arcfour_crypt_blob(pwbuf
->data
, 516, &co_session_key
);
565 if (!extract_pw_from_buffer(mem_ctx
, pwbuf
->data
, &new_password
)) {
566 DEBUG(3,("samr: failed to decode password buffer\n"));
567 return NT_STATUS_WRONG_PASSWORD
;
570 /* set the password - samdb needs to know both the domain and user DNs,
571 so the domain password policy can be used */
572 return samdb_set_password(sam_ctx
, mem_ctx
,
573 account_dn
, domain_dn
,
576 NULL
, NULL
, /* This is a password set, not change */
581 set password via encrypted NT and LM hash buffers
583 NTSTATUS
samr_set_password_buffers(struct dcesrv_call_state
*dce_call
,
584 struct ldb_context
*sam_ctx
,
585 struct ldb_dn
*account_dn
,
586 struct ldb_dn
*domain_dn
,
588 const uint8_t *lm_pwd_hash
,
589 const uint8_t *nt_pwd_hash
)
591 struct samr_Password
*d_lm_pwd_hash
= NULL
, *d_nt_pwd_hash
= NULL
;
592 DATA_BLOB session_key
= data_blob(NULL
, 0);
594 NTSTATUS nt_status
= NT_STATUS_OK
;
596 nt_status
= dcesrv_fetch_session_key(dce_call
->conn
, &session_key
);
597 if (!NT_STATUS_IS_OK(nt_status
)) {
601 if (lm_pwd_hash
!= NULL
) {
602 in
= data_blob_const(lm_pwd_hash
, 16);
603 out
= data_blob_talloc_zero(mem_ctx
, 16);
605 sess_crypt_blob(&out
, &in
, &session_key
, false);
607 d_lm_pwd_hash
= (struct samr_Password
*) out
.data
;
609 if (nt_pwd_hash
!= NULL
) {
610 in
= data_blob_const(nt_pwd_hash
, 16);
611 out
= data_blob_talloc_zero(mem_ctx
, 16);
613 sess_crypt_blob(&out
, &in
, &session_key
, false);
615 d_nt_pwd_hash
= (struct samr_Password
*) out
.data
;
618 if ((d_lm_pwd_hash
!= NULL
) || (d_nt_pwd_hash
!= NULL
)) {
619 nt_status
= samdb_set_password(sam_ctx
, mem_ctx
, account_dn
,
621 d_lm_pwd_hash
, d_nt_pwd_hash
,
622 NULL
, NULL
, /* this is a password set */