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
;
184 size_t converted_size
= 0;
187 return NT_STATUS_INVALID_PARAMETER
;
190 if (r
->in
.hash
== NULL
) {
191 return NT_STATUS_INVALID_PARAMETER
;
194 /* this call can only work with lanman auth */
195 if (!lpcfg_lanman_auth(dce_call
->conn
->dce_ctx
->lp_ctx
)) {
196 return NT_STATUS_WRONG_PASSWORD
;
199 /* Connect to a SAMDB with system privileges for fetching the old pw
201 sam_ctx
= samdb_connect(mem_ctx
, dce_call
->event_ctx
,
202 dce_call
->conn
->dce_ctx
->lp_ctx
,
203 system_session(dce_call
->conn
->dce_ctx
->lp_ctx
), 0);
204 if (sam_ctx
== NULL
) {
205 return NT_STATUS_INVALID_SYSTEM_SERVICE
;
208 /* we need the users dn and the domain dn (derived from the
209 user SID). We also need the current lm password hash in
210 order to decrypt the incoming password */
211 ret
= gendb_search(sam_ctx
,
212 mem_ctx
, NULL
, &res
, attrs
,
213 "(&(sAMAccountName=%s)(objectclass=user))",
214 r
->in
.account
->string
);
216 /* Don't give the game away: (don't allow anonymous users to prove the existance of usernames) */
217 return NT_STATUS_WRONG_PASSWORD
;
220 user_dn
= res
[0]->dn
;
222 status
= samdb_result_passwords(mem_ctx
, dce_call
->conn
->dce_ctx
->lp_ctx
,
223 res
[0], &lm_pwd
, NULL
);
224 if (!NT_STATUS_IS_OK(status
) || !lm_pwd
) {
225 return NT_STATUS_WRONG_PASSWORD
;
228 /* decrypt the password we have been given */
229 lm_pwd_blob
= data_blob(lm_pwd
->hash
, sizeof(lm_pwd
->hash
));
230 arcfour_crypt_blob(pwbuf
->data
, 516, &lm_pwd_blob
);
231 data_blob_free(&lm_pwd_blob
);
233 if (!extract_pw_from_buffer(mem_ctx
, pwbuf
->data
, &new_password
)) {
234 DEBUG(3,("samr: failed to decode password buffer\n"));
235 return NT_STATUS_WRONG_PASSWORD
;
238 if (!convert_string_talloc_handle(mem_ctx
, lpcfg_iconv_handle(dce_call
->conn
->dce_ctx
->lp_ctx
),
240 (const char *)new_password
.data
,
242 (void **)&new_pass
, &converted_size
)) {
243 DEBUG(3,("samr: failed to convert incoming password buffer to unix charset\n"));
244 return NT_STATUS_WRONG_PASSWORD
;
247 if (!convert_string_talloc_handle(mem_ctx
, lpcfg_iconv_handle(dce_call
->conn
->dce_ctx
->lp_ctx
),
249 (const char *)new_password
.data
,
251 (void **)&new_unicode_password
.data
, &unicode_pw_len
)) {
252 DEBUG(3,("samr: failed to convert incoming password buffer to UTF16 charset\n"));
253 return NT_STATUS_WRONG_PASSWORD
;
255 new_unicode_password
.length
= unicode_pw_len
;
257 E_deshash(new_pass
, new_lm_hash
);
258 E_old_pw_hash(new_lm_hash
, lm_pwd
->hash
, lm_verifier
.hash
);
259 if (memcmp(lm_verifier
.hash
, r
->in
.hash
->hash
, 16) != 0) {
260 return NT_STATUS_WRONG_PASSWORD
;
263 /* Connect to a SAMDB with user privileges for the password change */
264 sam_ctx
= samdb_connect(mem_ctx
, dce_call
->event_ctx
,
265 dce_call
->conn
->dce_ctx
->lp_ctx
,
266 dce_call
->conn
->auth_state
.session_info
, 0);
267 if (sam_ctx
== NULL
) {
268 return NT_STATUS_INVALID_SYSTEM_SERVICE
;
271 /* Start transaction */
272 ret
= ldb_transaction_start(sam_ctx
);
273 if (ret
!= LDB_SUCCESS
) {
274 DEBUG(1, ("Failed to start transaction: %s\n", ldb_errstring(sam_ctx
)));
275 return NT_STATUS_TRANSACTION_ABORTED
;
278 /* Performs the password modification. We pass the old hashes read out
279 * from the database since they were already checked against the user-
281 status
= samdb_set_password(sam_ctx
, mem_ctx
,
283 &new_unicode_password
,
285 lm_pwd
, NULL
, /* this is a user password change */
288 if (!NT_STATUS_IS_OK(status
)) {
289 ldb_transaction_cancel(sam_ctx
);
293 /* And this confirms it in a transaction commit */
294 ret
= ldb_transaction_commit(sam_ctx
);
295 if (ret
!= LDB_SUCCESS
) {
296 DEBUG(1,("Failed to commit transaction to change password on %s: %s\n",
297 ldb_dn_get_linearized(user_dn
),
298 ldb_errstring(sam_ctx
)));
299 return NT_STATUS_TRANSACTION_ABORTED
;
307 samr_ChangePasswordUser3
309 NTSTATUS
dcesrv_samr_ChangePasswordUser3(struct dcesrv_call_state
*dce_call
,
311 struct samr_ChangePasswordUser3
*r
)
314 DATA_BLOB new_password
;
315 struct ldb_context
*sam_ctx
= NULL
;
316 struct ldb_dn
*user_dn
;
318 struct ldb_message
**res
;
319 const char * const attrs
[] = { "unicodePwd", "dBCSPwd", NULL
};
320 struct samr_Password
*nt_pwd
, *lm_pwd
;
321 DATA_BLOB nt_pwd_blob
;
322 struct samr_DomInfo1
*dominfo
= NULL
;
323 struct userPwdChangeFailureInformation
*reject
= NULL
;
324 enum samPwdChangeReason reason
= SAM_PWD_CHANGE_NO_ERROR
;
325 uint8_t new_nt_hash
[16], new_lm_hash
[16];
326 struct samr_Password nt_verifier
, lm_verifier
;
328 *r
->out
.dominfo
= NULL
;
329 *r
->out
.reject
= NULL
;
331 if (r
->in
.nt_password
== NULL
||
332 r
->in
.nt_verifier
== NULL
) {
333 return NT_STATUS_INVALID_PARAMETER
;
336 /* Connect to a SAMDB with system privileges for fetching the old pw
338 sam_ctx
= samdb_connect(mem_ctx
, dce_call
->event_ctx
,
339 dce_call
->conn
->dce_ctx
->lp_ctx
,
340 system_session(dce_call
->conn
->dce_ctx
->lp_ctx
), 0);
341 if (sam_ctx
== NULL
) {
342 return NT_STATUS_INVALID_SYSTEM_SERVICE
;
345 /* we need the users dn and the domain dn (derived from the
346 user SID). We also need the current lm and nt password hashes
347 in order to decrypt the incoming passwords */
348 ret
= gendb_search(sam_ctx
,
349 mem_ctx
, NULL
, &res
, attrs
,
350 "(&(sAMAccountName=%s)(objectclass=user))",
351 r
->in
.account
->string
);
353 /* Don't give the game away: (don't allow anonymous users to prove the existance of usernames) */
354 status
= NT_STATUS_WRONG_PASSWORD
;
358 user_dn
= res
[0]->dn
;
360 status
= samdb_result_passwords(mem_ctx
, dce_call
->conn
->dce_ctx
->lp_ctx
,
361 res
[0], &lm_pwd
, &nt_pwd
);
362 if (!NT_STATUS_IS_OK(status
) ) {
367 status
= NT_STATUS_WRONG_PASSWORD
;
371 /* decrypt the password we have been given */
372 nt_pwd_blob
= data_blob(nt_pwd
->hash
, sizeof(nt_pwd
->hash
));
373 arcfour_crypt_blob(r
->in
.nt_password
->data
, 516, &nt_pwd_blob
);
374 data_blob_free(&nt_pwd_blob
);
376 if (!extract_pw_from_buffer(mem_ctx
, r
->in
.nt_password
->data
, &new_password
)) {
377 DEBUG(3,("samr: failed to decode password buffer\n"));
378 status
= NT_STATUS_WRONG_PASSWORD
;
382 if (r
->in
.nt_verifier
== NULL
) {
383 status
= NT_STATUS_WRONG_PASSWORD
;
387 /* check NT verifier */
388 mdfour(new_nt_hash
, new_password
.data
, new_password
.length
);
390 E_old_pw_hash(new_nt_hash
, nt_pwd
->hash
, nt_verifier
.hash
);
391 if (memcmp(nt_verifier
.hash
, r
->in
.nt_verifier
->hash
, 16) != 0) {
392 status
= NT_STATUS_WRONG_PASSWORD
;
396 /* check LM verifier (really not needed as we just checked the
397 * much stronger NT hash, but the RPC-SAMR test checks for
399 if (lm_pwd
&& r
->in
.lm_verifier
!= NULL
) {
401 size_t converted_size
= 0;
403 if (!convert_string_talloc_handle(mem_ctx
, lpcfg_iconv_handle(dce_call
->conn
->dce_ctx
->lp_ctx
),
405 (const char *)new_password
.data
,
407 (void **)&new_pass
, &converted_size
)) {
408 E_deshash(new_pass
, new_lm_hash
);
409 E_old_pw_hash(new_nt_hash
, lm_pwd
->hash
, lm_verifier
.hash
);
410 if (memcmp(lm_verifier
.hash
, r
->in
.lm_verifier
->hash
, 16) != 0) {
411 status
= NT_STATUS_WRONG_PASSWORD
;
417 /* Connect to a SAMDB with user privileges for the password change */
418 sam_ctx
= samdb_connect(mem_ctx
, dce_call
->event_ctx
,
419 dce_call
->conn
->dce_ctx
->lp_ctx
,
420 dce_call
->conn
->auth_state
.session_info
, 0);
421 if (sam_ctx
== NULL
) {
422 return NT_STATUS_INVALID_SYSTEM_SERVICE
;
425 ret
= ldb_transaction_start(sam_ctx
);
426 if (ret
!= LDB_SUCCESS
) {
427 DEBUG(1, ("Failed to start transaction: %s\n", ldb_errstring(sam_ctx
)));
428 return NT_STATUS_TRANSACTION_ABORTED
;
431 /* Performs the password modification. We pass the old hashes read out
432 * from the database since they were already checked against the user-
434 status
= samdb_set_password(sam_ctx
, mem_ctx
,
438 lm_pwd
, nt_pwd
, /* this is a user password change */
442 if (!NT_STATUS_IS_OK(status
)) {
443 ldb_transaction_cancel(sam_ctx
);
447 /* And this confirms it in a transaction commit */
448 ret
= ldb_transaction_commit(sam_ctx
);
449 if (ret
!= LDB_SUCCESS
) {
450 DEBUG(1,("Failed to commit transaction to change password on %s: %s\n",
451 ldb_dn_get_linearized(user_dn
),
452 ldb_errstring(sam_ctx
)));
453 status
= NT_STATUS_TRANSACTION_ABORTED
;
460 reject
= talloc_zero(mem_ctx
, struct userPwdChangeFailureInformation
);
461 if (reject
!= NULL
) {
462 reject
->extendedFailureReason
= reason
;
464 *r
->out
.reject
= reject
;
467 *r
->out
.dominfo
= dominfo
;
474 samr_ChangePasswordUser2
476 easy - just a subset of samr_ChangePasswordUser3
478 NTSTATUS
dcesrv_samr_ChangePasswordUser2(struct dcesrv_call_state
*dce_call
,
480 struct samr_ChangePasswordUser2
*r
)
482 struct samr_ChangePasswordUser3 r2
;
483 struct samr_DomInfo1
*dominfo
= NULL
;
484 struct userPwdChangeFailureInformation
*reject
= NULL
;
486 r2
.in
.server
= r
->in
.server
;
487 r2
.in
.account
= r
->in
.account
;
488 r2
.in
.nt_password
= r
->in
.nt_password
;
489 r2
.in
.nt_verifier
= r
->in
.nt_verifier
;
490 r2
.in
.lm_change
= r
->in
.lm_change
;
491 r2
.in
.lm_password
= r
->in
.lm_password
;
492 r2
.in
.lm_verifier
= r
->in
.lm_verifier
;
493 r2
.in
.password3
= NULL
;
494 r2
.out
.dominfo
= &dominfo
;
495 r2
.out
.reject
= &reject
;
497 return dcesrv_samr_ChangePasswordUser3(dce_call
, mem_ctx
, &r2
);
502 set password via a samr_CryptPassword buffer
504 NTSTATUS
samr_set_password(struct dcesrv_call_state
*dce_call
,
505 struct ldb_context
*sam_ctx
,
506 struct ldb_dn
*account_dn
, struct ldb_dn
*domain_dn
,
508 struct samr_CryptPassword
*pwbuf
)
511 DATA_BLOB new_password
;
512 DATA_BLOB session_key
= data_blob(NULL
, 0);
514 nt_status
= dcesrv_fetch_session_key(dce_call
->conn
, &session_key
);
515 if (!NT_STATUS_IS_OK(nt_status
)) {
519 arcfour_crypt_blob(pwbuf
->data
, 516, &session_key
);
521 if (!extract_pw_from_buffer(mem_ctx
, pwbuf
->data
, &new_password
)) {
522 DEBUG(3,("samr: failed to decode password buffer\n"));
523 return NT_STATUS_WRONG_PASSWORD
;
526 /* set the password - samdb needs to know both the domain and user DNs,
527 so the domain password policy can be used */
528 return samdb_set_password(sam_ctx
, mem_ctx
,
529 account_dn
, domain_dn
,
532 NULL
, NULL
, /* This is a password set, not change */
538 set password via a samr_CryptPasswordEx buffer
540 NTSTATUS
samr_set_password_ex(struct dcesrv_call_state
*dce_call
,
541 struct ldb_context
*sam_ctx
,
542 struct ldb_dn
*account_dn
,
543 struct ldb_dn
*domain_dn
,
545 struct samr_CryptPasswordEx
*pwbuf
)
548 DATA_BLOB new_password
;
549 DATA_BLOB co_session_key
;
550 DATA_BLOB session_key
= data_blob(NULL
, 0);
551 struct MD5Context ctx
;
553 nt_status
= dcesrv_fetch_session_key(dce_call
->conn
, &session_key
);
554 if (!NT_STATUS_IS_OK(nt_status
)) {
558 co_session_key
= data_blob_talloc(mem_ctx
, NULL
, 16);
559 if (!co_session_key
.data
) {
560 return NT_STATUS_NO_MEMORY
;
564 MD5Update(&ctx
, &pwbuf
->data
[516], 16);
565 MD5Update(&ctx
, session_key
.data
, session_key
.length
);
566 MD5Final(co_session_key
.data
, &ctx
);
568 arcfour_crypt_blob(pwbuf
->data
, 516, &co_session_key
);
570 if (!extract_pw_from_buffer(mem_ctx
, pwbuf
->data
, &new_password
)) {
571 DEBUG(3,("samr: failed to decode password buffer\n"));
572 return NT_STATUS_WRONG_PASSWORD
;
575 /* set the password - samdb needs to know both the domain and user DNs,
576 so the domain password policy can be used */
577 return samdb_set_password(sam_ctx
, mem_ctx
,
578 account_dn
, domain_dn
,
581 NULL
, NULL
, /* This is a password set, not change */
586 set password via encrypted NT and LM hash buffers
588 NTSTATUS
samr_set_password_buffers(struct dcesrv_call_state
*dce_call
,
589 struct ldb_context
*sam_ctx
,
590 struct ldb_dn
*account_dn
,
591 struct ldb_dn
*domain_dn
,
593 const uint8_t *lm_pwd_hash
,
594 const uint8_t *nt_pwd_hash
)
596 struct samr_Password
*d_lm_pwd_hash
= NULL
, *d_nt_pwd_hash
= NULL
;
597 DATA_BLOB session_key
= data_blob(NULL
, 0);
599 NTSTATUS nt_status
= NT_STATUS_OK
;
601 nt_status
= dcesrv_fetch_session_key(dce_call
->conn
, &session_key
);
602 if (!NT_STATUS_IS_OK(nt_status
)) {
606 if (lm_pwd_hash
!= NULL
) {
607 in
= data_blob_const(lm_pwd_hash
, 16);
608 out
= data_blob_talloc_zero(mem_ctx
, 16);
610 sess_crypt_blob(&out
, &in
, &session_key
, false);
612 d_lm_pwd_hash
= (struct samr_Password
*) out
.data
;
614 if (nt_pwd_hash
!= NULL
) {
615 in
= data_blob_const(nt_pwd_hash
, 16);
616 out
= data_blob_talloc_zero(mem_ctx
, 16);
618 sess_crypt_blob(&out
, &in
, &session_key
, false);
620 d_nt_pwd_hash
= (struct samr_Password
*) out
.data
;
623 if ((d_lm_pwd_hash
!= NULL
) || (d_nt_pwd_hash
!= NULL
)) {
624 nt_status
= samdb_set_password(sam_ctx
, mem_ctx
, account_dn
,
626 d_lm_pwd_hash
, d_nt_pwd_hash
,
627 NULL
, NULL
, /* this is a password set */