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"
33 #include "auth/auth_sam.h"
36 samr_ChangePasswordUser
38 So old it is just not worth implementing
39 because it does not supply a plaintext and so we can't do password
40 complexity checking and cannot update all the other password hashes.
43 NTSTATUS
dcesrv_samr_ChangePasswordUser(struct dcesrv_call_state
*dce_call
,
45 struct samr_ChangePasswordUser
*r
)
47 return NT_STATUS_NOT_IMPLEMENTED
;
51 samr_OemChangePasswordUser2
53 NTSTATUS
dcesrv_samr_OemChangePasswordUser2(struct dcesrv_call_state
*dce_call
,
55 struct samr_OemChangePasswordUser2
*r
)
58 DATA_BLOB new_password
, new_unicode_password
;
60 struct samr_CryptPassword
*pwbuf
= r
->in
.password
;
61 struct ldb_context
*sam_ctx
;
62 struct ldb_dn
*user_dn
;
64 struct ldb_message
**res
;
65 const char * const attrs
[] = { "objectSid", "dBCSPwd",
67 "msDS-User-Account-Control-Computed",
68 "badPwdCount", "badPasswordTime",
70 struct samr_Password
*lm_pwd
;
71 DATA_BLOB lm_pwd_blob
;
72 uint8_t new_lm_hash
[16];
73 struct samr_Password lm_verifier
;
74 size_t unicode_pw_len
;
75 size_t converted_size
= 0;
78 return NT_STATUS_INVALID_PARAMETER
;
81 if (r
->in
.hash
== NULL
) {
82 return NT_STATUS_INVALID_PARAMETER
;
85 /* this call can only work with lanman auth */
86 if (!lpcfg_lanman_auth(dce_call
->conn
->dce_ctx
->lp_ctx
)) {
87 return NT_STATUS_WRONG_PASSWORD
;
90 /* Connect to a SAMDB with system privileges for fetching the old pw
92 sam_ctx
= samdb_connect(mem_ctx
, dce_call
->event_ctx
,
93 dce_call
->conn
->dce_ctx
->lp_ctx
,
94 system_session(dce_call
->conn
->dce_ctx
->lp_ctx
), 0);
95 if (sam_ctx
== NULL
) {
96 return NT_STATUS_INVALID_SYSTEM_SERVICE
;
99 /* we need the users dn and the domain dn (derived from the
100 user SID). We also need the current lm password hash in
101 order to decrypt the incoming password */
102 ret
= gendb_search(sam_ctx
,
103 mem_ctx
, NULL
, &res
, attrs
,
104 "(&(sAMAccountName=%s)(objectclass=user))",
105 ldb_binary_encode_string(mem_ctx
, r
->in
.account
->string
));
107 /* Don't give the game away: (don't allow anonymous users to prove the existance of usernames) */
108 return NT_STATUS_WRONG_PASSWORD
;
111 user_dn
= res
[0]->dn
;
113 status
= samdb_result_passwords(mem_ctx
, dce_call
->conn
->dce_ctx
->lp_ctx
,
114 res
[0], &lm_pwd
, NULL
);
115 if (!NT_STATUS_IS_OK(status
)) {
117 } else if (!lm_pwd
) {
118 return NT_STATUS_WRONG_PASSWORD
;
121 /* decrypt the password we have been given */
122 lm_pwd_blob
= data_blob(lm_pwd
->hash
, sizeof(lm_pwd
->hash
));
123 arcfour_crypt_blob(pwbuf
->data
, 516, &lm_pwd_blob
);
124 data_blob_free(&lm_pwd_blob
);
126 if (!extract_pw_from_buffer(mem_ctx
, pwbuf
->data
, &new_password
)) {
127 DEBUG(3,("samr: failed to decode password buffer\n"));
128 authsam_update_bad_pwd_count(sam_ctx
, res
[0], ldb_get_default_basedn(sam_ctx
));
129 return NT_STATUS_WRONG_PASSWORD
;
132 if (!convert_string_talloc_handle(mem_ctx
, lpcfg_iconv_handle(dce_call
->conn
->dce_ctx
->lp_ctx
),
134 (const char *)new_password
.data
,
136 (void **)&new_pass
, &converted_size
)) {
137 DEBUG(3,("samr: failed to convert incoming password buffer to unix charset\n"));
138 authsam_update_bad_pwd_count(sam_ctx
, res
[0], ldb_get_default_basedn(sam_ctx
));
139 return NT_STATUS_WRONG_PASSWORD
;
142 if (!convert_string_talloc_handle(mem_ctx
, lpcfg_iconv_handle(dce_call
->conn
->dce_ctx
->lp_ctx
),
144 (const char *)new_password
.data
,
146 (void **)&new_unicode_password
.data
, &unicode_pw_len
)) {
147 DEBUG(3,("samr: failed to convert incoming password buffer to UTF16 charset\n"));
148 authsam_update_bad_pwd_count(sam_ctx
, res
[0], ldb_get_default_basedn(sam_ctx
));
149 return NT_STATUS_WRONG_PASSWORD
;
151 new_unicode_password
.length
= unicode_pw_len
;
153 E_deshash(new_pass
, new_lm_hash
);
154 E_old_pw_hash(new_lm_hash
, lm_pwd
->hash
, lm_verifier
.hash
);
155 if (memcmp(lm_verifier
.hash
, r
->in
.hash
->hash
, 16) != 0) {
156 authsam_update_bad_pwd_count(sam_ctx
, res
[0], ldb_get_default_basedn(sam_ctx
));
157 return NT_STATUS_WRONG_PASSWORD
;
160 /* Connect to a SAMDB with user privileges for the password change */
161 sam_ctx
= samdb_connect(mem_ctx
, dce_call
->event_ctx
,
162 dce_call
->conn
->dce_ctx
->lp_ctx
,
163 dce_call
->conn
->auth_state
.session_info
, 0);
164 if (sam_ctx
== NULL
) {
165 return NT_STATUS_INVALID_SYSTEM_SERVICE
;
168 /* Start transaction */
169 ret
= ldb_transaction_start(sam_ctx
);
170 if (ret
!= LDB_SUCCESS
) {
171 DEBUG(1, ("Failed to start transaction: %s\n", ldb_errstring(sam_ctx
)));
172 return NT_STATUS_TRANSACTION_ABORTED
;
175 /* Performs the password modification. We pass the old hashes read out
176 * from the database since they were already checked against the user-
178 status
= samdb_set_password(sam_ctx
, mem_ctx
,
180 &new_unicode_password
,
182 lm_pwd
, NULL
, /* this is a user password change */
185 if (!NT_STATUS_IS_OK(status
)) {
186 ldb_transaction_cancel(sam_ctx
);
190 /* And this confirms it in a transaction commit */
191 ret
= ldb_transaction_commit(sam_ctx
);
192 if (ret
!= LDB_SUCCESS
) {
193 DEBUG(1,("Failed to commit transaction to change password on %s: %s\n",
194 ldb_dn_get_linearized(user_dn
),
195 ldb_errstring(sam_ctx
)));
196 return NT_STATUS_TRANSACTION_ABORTED
;
204 samr_ChangePasswordUser3
206 NTSTATUS
dcesrv_samr_ChangePasswordUser3(struct dcesrv_call_state
*dce_call
,
208 struct samr_ChangePasswordUser3
*r
)
211 DATA_BLOB new_password
;
212 struct ldb_context
*sam_ctx
= NULL
;
213 struct ldb_dn
*user_dn
= NULL
;
215 struct ldb_message
**res
;
216 const char * const attrs
[] = { "unicodePwd", "dBCSPwd",
217 "userAccountControl",
218 "msDS-User-Account-Control-Computed",
219 "badPwdCount", "badPasswordTime",
221 struct samr_Password
*nt_pwd
, *lm_pwd
;
222 DATA_BLOB nt_pwd_blob
;
223 struct samr_DomInfo1
*dominfo
= NULL
;
224 struct userPwdChangeFailureInformation
*reject
= NULL
;
225 enum samPwdChangeReason reason
= SAM_PWD_CHANGE_NO_ERROR
;
226 uint8_t new_nt_hash
[16], new_lm_hash
[16];
227 struct samr_Password nt_verifier
, lm_verifier
;
229 *r
->out
.dominfo
= NULL
;
230 *r
->out
.reject
= NULL
;
232 if (r
->in
.nt_password
== NULL
||
233 r
->in
.nt_verifier
== NULL
) {
234 return NT_STATUS_INVALID_PARAMETER
;
237 /* Connect to a SAMDB with system privileges for fetching the old pw
239 sam_ctx
= samdb_connect(mem_ctx
, dce_call
->event_ctx
,
240 dce_call
->conn
->dce_ctx
->lp_ctx
,
241 system_session(dce_call
->conn
->dce_ctx
->lp_ctx
), 0);
242 if (sam_ctx
== NULL
) {
243 return NT_STATUS_INVALID_SYSTEM_SERVICE
;
246 /* we need the users dn and the domain dn (derived from the
247 user SID). We also need the current lm and nt password hashes
248 in order to decrypt the incoming passwords */
249 ret
= gendb_search(sam_ctx
,
250 mem_ctx
, NULL
, &res
, attrs
,
251 "(&(sAMAccountName=%s)(objectclass=user))",
252 ldb_binary_encode_string(mem_ctx
, r
->in
.account
->string
));
254 /* Don't give the game away: (don't allow anonymous users to prove the existance of usernames) */
255 status
= NT_STATUS_WRONG_PASSWORD
;
259 user_dn
= res
[0]->dn
;
261 status
= samdb_result_passwords(mem_ctx
, dce_call
->conn
->dce_ctx
->lp_ctx
,
262 res
[0], &lm_pwd
, &nt_pwd
);
263 if (!NT_STATUS_IS_OK(status
) ) {
268 status
= NT_STATUS_WRONG_PASSWORD
;
272 /* decrypt the password we have been given */
273 nt_pwd_blob
= data_blob(nt_pwd
->hash
, sizeof(nt_pwd
->hash
));
274 arcfour_crypt_blob(r
->in
.nt_password
->data
, 516, &nt_pwd_blob
);
275 data_blob_free(&nt_pwd_blob
);
277 if (!extract_pw_from_buffer(mem_ctx
, r
->in
.nt_password
->data
, &new_password
)) {
278 DEBUG(3,("samr: failed to decode password buffer\n"));
279 status
= NT_STATUS_WRONG_PASSWORD
;
283 if (r
->in
.nt_verifier
== NULL
) {
284 status
= NT_STATUS_WRONG_PASSWORD
;
288 /* check NT verifier */
289 mdfour(new_nt_hash
, new_password
.data
, new_password
.length
);
291 E_old_pw_hash(new_nt_hash
, nt_pwd
->hash
, nt_verifier
.hash
);
292 if (memcmp(nt_verifier
.hash
, r
->in
.nt_verifier
->hash
, 16) != 0) {
293 status
= NT_STATUS_WRONG_PASSWORD
;
297 /* check LM verifier (really not needed as we just checked the
298 * much stronger NT hash, but the RPC-SAMR test checks for
300 if (lm_pwd
&& r
->in
.lm_verifier
!= NULL
) {
302 size_t converted_size
= 0;
304 if (!convert_string_talloc_handle(mem_ctx
, lpcfg_iconv_handle(dce_call
->conn
->dce_ctx
->lp_ctx
),
306 (const char *)new_password
.data
,
308 (void **)&new_pass
, &converted_size
)) {
309 E_deshash(new_pass
, new_lm_hash
);
310 E_old_pw_hash(new_nt_hash
, lm_pwd
->hash
, lm_verifier
.hash
);
311 if (memcmp(lm_verifier
.hash
, r
->in
.lm_verifier
->hash
, 16) != 0) {
312 status
= NT_STATUS_WRONG_PASSWORD
;
318 /* Connect to a SAMDB with user privileges for the password change */
319 sam_ctx
= samdb_connect(mem_ctx
, dce_call
->event_ctx
,
320 dce_call
->conn
->dce_ctx
->lp_ctx
,
321 dce_call
->conn
->auth_state
.session_info
, 0);
322 if (sam_ctx
== NULL
) {
323 return NT_STATUS_INVALID_SYSTEM_SERVICE
;
326 ret
= ldb_transaction_start(sam_ctx
);
327 if (ret
!= LDB_SUCCESS
) {
328 DEBUG(1, ("Failed to start transaction: %s\n", ldb_errstring(sam_ctx
)));
329 return NT_STATUS_TRANSACTION_ABORTED
;
332 /* Performs the password modification. We pass the old hashes read out
333 * from the database since they were already checked against the user-
335 status
= samdb_set_password(sam_ctx
, mem_ctx
,
339 lm_pwd
, nt_pwd
, /* this is a user password change */
343 if (!NT_STATUS_IS_OK(status
)) {
344 ldb_transaction_cancel(sam_ctx
);
348 /* And this confirms it in a transaction commit */
349 ret
= ldb_transaction_commit(sam_ctx
);
350 if (ret
!= LDB_SUCCESS
) {
351 DEBUG(1,("Failed to commit transaction to change password on %s: %s\n",
352 ldb_dn_get_linearized(user_dn
),
353 ldb_errstring(sam_ctx
)));
354 status
= NT_STATUS_TRANSACTION_ABORTED
;
361 /* Only update the badPwdCount if we found the user */
362 if (user_dn
!= NULL
&& NT_STATUS_EQUAL(status
, NT_STATUS_WRONG_PASSWORD
)) {
363 authsam_update_bad_pwd_count(sam_ctx
, res
[0], ldb_get_default_basedn(sam_ctx
));
366 reject
= talloc_zero(mem_ctx
, struct userPwdChangeFailureInformation
);
367 if (reject
!= NULL
) {
368 reject
->extendedFailureReason
= reason
;
370 *r
->out
.reject
= reject
;
373 *r
->out
.dominfo
= dominfo
;
380 samr_ChangePasswordUser2
382 easy - just a subset of samr_ChangePasswordUser3
384 NTSTATUS
dcesrv_samr_ChangePasswordUser2(struct dcesrv_call_state
*dce_call
,
386 struct samr_ChangePasswordUser2
*r
)
388 struct samr_ChangePasswordUser3 r2
;
389 struct samr_DomInfo1
*dominfo
= NULL
;
390 struct userPwdChangeFailureInformation
*reject
= NULL
;
392 r2
.in
.server
= r
->in
.server
;
393 r2
.in
.account
= r
->in
.account
;
394 r2
.in
.nt_password
= r
->in
.nt_password
;
395 r2
.in
.nt_verifier
= r
->in
.nt_verifier
;
396 r2
.in
.lm_change
= r
->in
.lm_change
;
397 r2
.in
.lm_password
= r
->in
.lm_password
;
398 r2
.in
.lm_verifier
= r
->in
.lm_verifier
;
399 r2
.in
.password3
= NULL
;
400 r2
.out
.dominfo
= &dominfo
;
401 r2
.out
.reject
= &reject
;
403 return dcesrv_samr_ChangePasswordUser3(dce_call
, mem_ctx
, &r2
);
408 set password via a samr_CryptPassword buffer
410 NTSTATUS
samr_set_password(struct dcesrv_call_state
*dce_call
,
411 struct ldb_context
*sam_ctx
,
412 struct ldb_dn
*account_dn
, struct ldb_dn
*domain_dn
,
414 struct samr_CryptPassword
*pwbuf
)
417 DATA_BLOB new_password
;
418 DATA_BLOB session_key
= data_blob(NULL
, 0);
420 nt_status
= dcesrv_fetch_session_key(dce_call
->conn
, &session_key
);
421 if (!NT_STATUS_IS_OK(nt_status
)) {
425 arcfour_crypt_blob(pwbuf
->data
, 516, &session_key
);
427 if (!extract_pw_from_buffer(mem_ctx
, pwbuf
->data
, &new_password
)) {
428 DEBUG(3,("samr: failed to decode password buffer\n"));
429 return NT_STATUS_WRONG_PASSWORD
;
432 /* set the password - samdb needs to know both the domain and user DNs,
433 so the domain password policy can be used */
434 return samdb_set_password(sam_ctx
, mem_ctx
,
435 account_dn
, domain_dn
,
438 NULL
, NULL
, /* This is a password set, not change */
444 set password via a samr_CryptPasswordEx buffer
446 NTSTATUS
samr_set_password_ex(struct dcesrv_call_state
*dce_call
,
447 struct ldb_context
*sam_ctx
,
448 struct ldb_dn
*account_dn
,
449 struct ldb_dn
*domain_dn
,
451 struct samr_CryptPasswordEx
*pwbuf
)
454 DATA_BLOB new_password
;
455 DATA_BLOB co_session_key
;
456 DATA_BLOB session_key
= data_blob(NULL
, 0);
459 nt_status
= dcesrv_fetch_session_key(dce_call
->conn
, &session_key
);
460 if (!NT_STATUS_IS_OK(nt_status
)) {
464 co_session_key
= data_blob_talloc(mem_ctx
, NULL
, 16);
465 if (!co_session_key
.data
) {
466 return NT_STATUS_NO_MEMORY
;
470 MD5Update(&ctx
, &pwbuf
->data
[516], 16);
471 MD5Update(&ctx
, session_key
.data
, session_key
.length
);
472 MD5Final(co_session_key
.data
, &ctx
);
474 arcfour_crypt_blob(pwbuf
->data
, 516, &co_session_key
);
476 if (!extract_pw_from_buffer(mem_ctx
, pwbuf
->data
, &new_password
)) {
477 DEBUG(3,("samr: failed to decode password buffer\n"));
478 return NT_STATUS_WRONG_PASSWORD
;
481 /* set the password - samdb needs to know both the domain and user DNs,
482 so the domain password policy can be used */
483 return samdb_set_password(sam_ctx
, mem_ctx
,
484 account_dn
, domain_dn
,
487 NULL
, NULL
, /* This is a password set, not change */
492 set password via encrypted NT and LM hash buffers
494 NTSTATUS
samr_set_password_buffers(struct dcesrv_call_state
*dce_call
,
495 struct ldb_context
*sam_ctx
,
496 struct ldb_dn
*account_dn
,
497 struct ldb_dn
*domain_dn
,
499 const uint8_t *lm_pwd_hash
,
500 const uint8_t *nt_pwd_hash
)
502 struct samr_Password
*d_lm_pwd_hash
= NULL
, *d_nt_pwd_hash
= NULL
;
503 DATA_BLOB session_key
= data_blob(NULL
, 0);
505 NTSTATUS nt_status
= NT_STATUS_OK
;
507 nt_status
= dcesrv_fetch_session_key(dce_call
->conn
, &session_key
);
508 if (!NT_STATUS_IS_OK(nt_status
)) {
512 if (lm_pwd_hash
!= NULL
) {
513 in
= data_blob_const(lm_pwd_hash
, 16);
514 out
= data_blob_talloc_zero(mem_ctx
, 16);
516 sess_crypt_blob(&out
, &in
, &session_key
, false);
518 d_lm_pwd_hash
= (struct samr_Password
*) out
.data
;
520 if (nt_pwd_hash
!= NULL
) {
521 in
= data_blob_const(nt_pwd_hash
, 16);
522 out
= data_blob_talloc_zero(mem_ctx
, 16);
524 sess_crypt_blob(&out
, &in
, &session_key
, false);
526 d_nt_pwd_hash
= (struct samr_Password
*) out
.data
;
529 if ((d_lm_pwd_hash
!= NULL
) || (d_nt_pwd_hash
!= NULL
)) {
530 nt_status
= samdb_set_password(sam_ctx
, mem_ctx
, account_dn
,
532 d_lm_pwd_hash
, d_nt_pwd_hash
,
533 NULL
, NULL
, /* this is a password set */