2 Unix SMB/CIFS implementation.
4 Winbind daemon - pam auth funcions
6 Copyright (C) Andrew Tridgell 2000
7 Copyright (C) Tim Potter 2001
8 Copyright (C) Andrew Bartlett 2001-2002
10 This program is free software; you can redistribute it and/or modify
11 it under the terms of the GNU General Public License as published by
12 the Free Software Foundation; either version 2 of the License, or
13 (at your option) any later version.
15 This program is distributed in the hope that it will be useful,
16 but WITHOUT ANY WARRANTY; without even the implied warranty of
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 GNU General Public License for more details.
20 You should have received a copy of the GNU General Public License
21 along with this program; if not, write to the Free Software
22 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
28 #define DBGC_CLASS DBGC_WINBIND
31 static NTSTATUS
append_info3_as_ndr(TALLOC_CTX
*mem_ctx
,
32 struct winbindd_cli_state
*state
,
33 NET_USER_INFO_3
*info3
)
37 if (!prs_init(&ps
, 256 /* Random, non-zero number */, mem_ctx
, MARSHALL
)) {
38 return NT_STATUS_NO_MEMORY
;
40 if (!net_io_user_info3("", info3
, &ps
, 1, 3)) {
42 return NT_STATUS_UNSUCCESSFUL
;
45 size
= prs_data_size(&ps
);
46 state
->response
.extra_data
= malloc(size
);
47 if (!state
->response
.extra_data
) {
49 return NT_STATUS_NO_MEMORY
;
51 memset( state
->response
.extra_data
, '\0', size
);
52 prs_copy_all_data_out(state
->response
.extra_data
, &ps
);
53 state
->response
.length
+= size
;
58 static NTSTATUS
check_info3_in_group(TALLOC_CTX
*mem_ctx
,
59 NET_USER_INFO_3
*info3
,
60 const char *group_sid
)
62 DOM_SID required_membership_sid
;
64 size_t num_all_sids
= (2 + info3
->num_groups2
+ info3
->num_other_sids
);
67 /* Parse the 'required group' SID */
69 if (!group_sid
|| !group_sid
[0]) {
70 /* NO sid supplied, all users may access */
74 if (!string_to_sid(&required_membership_sid
, group_sid
)) {
75 DEBUG(0, ("check_info3_in_group: could not parse %s as a SID!",
78 return NT_STATUS_INVALID_PARAMETER
;
81 all_sids
= talloc(mem_ctx
, sizeof(DOM_SID
) * num_all_sids
);
83 return NT_STATUS_NO_MEMORY
;
85 /* and create (by appending rids) the 'domain' sids */
87 sid_copy(&all_sids
[0], &(info3
->dom_sid
.sid
));
89 if (!sid_append_rid(&all_sids
[0], info3
->user_rid
)) {
90 DEBUG(3,("could not append user's primary RID 0x%x\n",
93 return NT_STATUS_INVALID_PARAMETER
;
97 sid_copy(&all_sids
[1], &(info3
->dom_sid
.sid
));
99 if (!sid_append_rid(&all_sids
[1], info3
->group_rid
)) {
100 DEBUG(3,("could not append additional group rid 0x%x\n",
103 return NT_STATUS_INVALID_PARAMETER
;
107 for (i
= 0; i
< info3
->num_groups2
; i
++) {
109 sid_copy(&all_sids
[j
], &(info3
->dom_sid
.sid
));
111 if (!sid_append_rid(&all_sids
[j
], info3
->gids
[j
].g_rid
)) {
112 DEBUG(3,("could not append additional group rid 0x%x\n",
113 info3
->gids
[j
].g_rid
));
115 return NT_STATUS_INVALID_PARAMETER
;
120 /* Copy 'other' sids. We need to do sid filtering here to
121 prevent possible elevation of privileges. See:
123 http://www.microsoft.com/windows2000/techinfo/administration/security/sidfilter.asp
126 for (i
= 0; i
< info3
->num_other_sids
; j
++) {
127 sid_copy(&all_sids
[info3
->num_groups2
+ i
+ 2],
128 &info3
->other_sids
[j
].sid
);
132 for (i
= 0; i
< j
; i
++) {
134 DEBUG(10, ("User has SID: %s\n",
135 sid_to_string(sid1
, &all_sids
[i
])));
136 if (sid_equal(&required_membership_sid
, &all_sids
[i
])) {
137 DEBUG(10, ("SID %s matches %s - user permitted to authenticate!\n",
138 sid_to_string(sid1
, &required_membership_sid
), sid_to_string(sid2
, &all_sids
[i
])));
143 /* Do not distinguish this error from a wrong username/pw */
145 return NT_STATUS_LOGON_FAILURE
;
148 /**********************************************************************
149 Authenticate a user with a clear text password
150 **********************************************************************/
152 enum winbindd_result
winbindd_pam_auth(struct winbindd_cli_state
*state
)
155 fstring name_domain
, name_user
;
156 unsigned char trust_passwd
[16];
157 time_t last_change_time
;
158 uint32 sec_channel_type
;
159 NET_USER_INFO_3 info3
;
160 struct cli_state
*cli
= NULL
;
162 TALLOC_CTX
*mem_ctx
= NULL
;
167 unsigned char local_lm_response
[24];
168 unsigned char local_nt_response
[24];
169 struct winbindd_domain
*contact_domain
;
172 /* Ensure null termination */
173 state
->request
.data
.auth
.user
[sizeof(state
->request
.data
.auth
.user
)-1]='\0';
175 /* Ensure null termination */
176 state
->request
.data
.auth
.pass
[sizeof(state
->request
.data
.auth
.pass
)-1]='\0';
178 DEBUG(3, ("[%5lu]: pam auth %s\n", (unsigned long)state
->pid
,
179 state
->request
.data
.auth
.user
));
181 if (!(mem_ctx
= talloc_init("winbind pam auth for %s", state
->request
.data
.auth
.user
))) {
182 DEBUG(0, ("winbindd_pam_auth: could not talloc_init()!\n"));
183 result
= NT_STATUS_NO_MEMORY
;
187 /* Parse domain and username */
189 parse_domain_user(state
->request
.data
.auth
.user
, name_domain
, name_user
);
191 /* do password magic */
193 generate_random_buffer(chal
, 8, False
);
194 SMBencrypt(state
->request
.data
.auth
.pass
, chal
, local_lm_response
);
196 SMBNTencrypt(state
->request
.data
.auth
.pass
, chal
, local_nt_response
);
198 lm_resp
= data_blob_talloc(mem_ctx
, local_lm_response
, sizeof(local_lm_response
));
199 nt_resp
= data_blob_talloc(mem_ctx
, local_nt_response
, sizeof(local_nt_response
));
201 /* what domain should we contact? */
204 if (!(contact_domain
= find_domain_from_name(name_domain
))) {
205 DEBUG(3, ("Authentication for domain for [%s] -> [%s]\\[%s] failed as %s is not a trusted domain\n",
206 state
->request
.data
.auth
.user
, name_domain
, name_user
, name_domain
));
207 result
= NT_STATUS_NO_SUCH_USER
;
212 if (is_myname(name_domain
)) {
213 DEBUG(3, ("Authentication for domain %s (local domain to this server) not supported at this stage\n", name_domain
));
214 result
= NT_STATUS_NO_SUCH_USER
;
218 if (!(contact_domain
= find_our_domain())) {
219 DEBUG(1, ("Authentication for [%s] -> [%s]\\[%s] in our domain failed - we can't find our domain!\n",
220 state
->request
.data
.auth
.user
, name_domain
, name_user
));
221 result
= NT_STATUS_NO_SUCH_USER
;
226 if ( !get_trust_pw(contact_domain
->name
, trust_passwd
, &last_change_time
, &sec_channel_type
) ) {
227 result
= NT_STATUS_CANT_ACCESS_DOMAIN_INFO
;
231 /* check authentication loop */
235 ZERO_STRUCT(ret_creds
);
238 /* Don't shut this down - it belongs to the connection cache code */
239 result
= cm_get_netlogon_cli(contact_domain
, trust_passwd
,
240 sec_channel_type
, False
, &cli
);
242 if (!NT_STATUS_IS_OK(result
)) {
243 DEBUG(3, ("could not open handle to NETLOGON pipe\n"));
247 result
= cli_netlogon_sam_network_logon(cli
, mem_ctx
,
249 name_user
, name_domain
,
250 global_myname(), chal
,
255 /* We have to try a second time as cm_get_netlogon_cli
256 might not yet have noticed that the DC has killed
259 if ( cli
->fd
== -1 ) {
264 /* if we get access denied, a possible cuase was that we had and open
265 connection to the DC, but someone changed our machine account password
266 out from underneath us using 'net rpc changetrustpw' */
268 if ( NT_STATUS_V(result
) == NT_STATUS_V(NT_STATUS_ACCESS_DENIED
) ) {
269 DEBUG(3,("winbindd_pam_auth: sam_logon returned ACCESS_DENIED. Maybe the trust account "
270 "password was changed and we didn't know it. Killing connections to domain %s\n",
277 } while ( (attempts
< 2) && retry
);
280 /* We might have come out of the loop above with cli == NULL,
281 so don't dereference that. */
282 clnt_deal_with_creds(cli
->sess_key
, &(cli
->clnt_cred
), &ret_creds
);
285 if (NT_STATUS_IS_OK(result
)) {
286 netsamlogon_cache_store( cli
->mem_ctx
, &info3
);
287 wcache_invalidate_samlogon(find_domain_from_name(name_domain
), &info3
);
289 /* Check if the user is in the right group */
291 if (!NT_STATUS_IS_OK(result
= check_info3_in_group(mem_ctx
, &info3
, state
->request
.data
.auth
.required_membership_sid
))) {
292 DEBUG(3, ("User %s is not in the required group (%s), so plaintext authentication is rejected\n",
293 state
->request
.data
.auth
.user
,
294 state
->request
.data
.auth
.required_membership_sid
));
299 /* give us a more useful (more correct?) error code */
300 if ((NT_STATUS_EQUAL(result
, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND
) || (NT_STATUS_EQUAL(result
, NT_STATUS_UNSUCCESSFUL
)))) {
301 result
= NT_STATUS_NO_LOGON_SERVERS
;
304 state
->response
.data
.auth
.nt_status
= NT_STATUS_V(result
);
305 fstrcpy(state
->response
.data
.auth
.nt_status_string
, nt_errstr(result
));
307 /* we might have given a more useful error above */
308 if (!*state
->response
.data
.auth
.error_string
)
309 fstrcpy(state
->response
.data
.auth
.error_string
, get_friendly_nt_error_msg(result
));
310 state
->response
.data
.auth
.pam_error
= nt_status_to_pam(result
);
312 DEBUG(NT_STATUS_IS_OK(result
) ? 5 : 2, ("Plain-text authentication for user %s returned %s (PAM: %d)\n",
313 state
->request
.data
.auth
.user
,
314 state
->response
.data
.auth
.nt_status_string
,
315 state
->response
.data
.auth
.pam_error
));
317 if ( NT_STATUS_IS_OK(result
) &&
318 (state
->request
.flags
& WBFLAG_PAM_AFS_TOKEN
) ) {
320 char *afsname
= strdup(lp_afs_username_map());
323 if (afsname
== NULL
) goto no_token
;
325 afsname
= realloc_string_sub(afsname
, "%D", name_domain
);
326 afsname
= realloc_string_sub(afsname
, "%u", name_user
);
327 afsname
= realloc_string_sub(afsname
, "%U", name_user
);
329 if (afsname
== NULL
) goto no_token
;
333 cell
= strchr(afsname
, '@');
335 if (cell
== NULL
) goto no_token
;
340 /* Append an AFS token string */
341 state
->response
.extra_data
=
342 afs_createtoken_str(afsname
, cell
);
344 if (state
->response
.extra_data
!= NULL
)
345 state
->response
.length
+=
346 strlen(state
->response
.extra_data
)+1;
353 talloc_destroy(mem_ctx
);
355 return NT_STATUS_IS_OK(result
) ? WINBINDD_OK
: WINBINDD_ERROR
;
358 /**********************************************************************
359 Challenge Response Authentication Protocol
360 **********************************************************************/
362 enum winbindd_result
winbindd_pam_auth_crap(struct winbindd_cli_state
*state
)
365 unsigned char trust_passwd
[16];
366 time_t last_change_time
;
367 uint32 sec_channel_type
;
368 NET_USER_INFO_3 info3
;
369 struct cli_state
*cli
= NULL
;
370 TALLOC_CTX
*mem_ctx
= NULL
;
371 char *name_user
= NULL
;
372 const char *name_domain
= NULL
;
373 const char *workstation
;
374 struct winbindd_domain
*contact_domain
;
379 DATA_BLOB lm_resp
, nt_resp
;
381 if (!state
->privileged
) {
382 char *error_string
= NULL
;
383 DEBUG(2, ("winbindd_pam_auth_crap: non-privileged access denied. !\n"));
384 DEBUGADD(2, ("winbindd_pam_auth_crap: Ensure permissions on %s are set correctly.\n",
385 get_winbind_priv_pipe_dir()));
386 /* send a better message than ACCESS_DENIED */
387 asprintf(&error_string
, "winbind client not authorized to use winbindd_pam_auth_crap. Ensure permissions on %s are set correctly.",
388 get_winbind_priv_pipe_dir());
389 push_utf8_fstring(state
->response
.data
.auth
.error_string
, error_string
);
390 SAFE_FREE(error_string
);
391 result
= NT_STATUS_ACCESS_DENIED
;
395 /* Ensure null termination */
396 state
->request
.data
.auth_crap
.user
[sizeof(state
->request
.data
.auth_crap
.user
)-1]=0;
397 state
->request
.data
.auth_crap
.domain
[sizeof(state
->request
.data
.auth_crap
.domain
)-1]=0;
399 if (!(mem_ctx
= talloc_init("winbind pam auth crap for (utf8) %s", state
->request
.data
.auth_crap
.user
))) {
400 DEBUG(0, ("winbindd_pam_auth_crap: could not talloc_init()!\n"));
401 result
= NT_STATUS_NO_MEMORY
;
405 if (pull_utf8_talloc(mem_ctx
, &name_user
, state
->request
.data
.auth_crap
.user
) == (size_t)-1) {
406 DEBUG(0, ("winbindd_pam_auth_crap: pull_utf8_talloc failed!\n"));
407 result
= NT_STATUS_UNSUCCESSFUL
;
411 if (*state
->request
.data
.auth_crap
.domain
) {
413 if (pull_utf8_talloc(mem_ctx
, &dom
, state
->request
.data
.auth_crap
.domain
) == (size_t)-1) {
414 DEBUG(0, ("winbindd_pam_auth_crap: pull_utf8_talloc failed!\n"));
415 result
= NT_STATUS_UNSUCCESSFUL
;
419 } else if (lp_winbind_use_default_domain()) {
420 name_domain
= lp_workgroup();
422 DEBUG(5,("no domain specified with username (%s) - failing auth\n",
424 result
= NT_STATUS_NO_SUCH_USER
;
428 DEBUG(3, ("[%5lu]: pam auth crap domain: %s user: %s\n", (unsigned long)state
->pid
,
429 name_domain
, name_user
));
431 if (*state
->request
.data
.auth_crap
.workstation
) {
433 if (pull_utf8_talloc(mem_ctx
, &wrk
, state
->request
.data
.auth_crap
.workstation
) == (size_t)-1) {
434 DEBUG(0, ("winbindd_pam_auth_crap: pull_utf8_talloc failed!\n"));
435 result
= NT_STATUS_UNSUCCESSFUL
;
440 workstation
= global_myname();
443 if (state
->request
.data
.auth_crap
.lm_resp_len
> sizeof(state
->request
.data
.auth_crap
.lm_resp
)
444 || state
->request
.data
.auth_crap
.nt_resp_len
> sizeof(state
->request
.data
.auth_crap
.nt_resp
)) {
445 DEBUG(0, ("winbindd_pam_auth_crap: invalid password length %u/%u\n",
446 state
->request
.data
.auth_crap
.lm_resp_len
,
447 state
->request
.data
.auth_crap
.nt_resp_len
));
448 result
= NT_STATUS_INVALID_PARAMETER
;
452 lm_resp
= data_blob_talloc(mem_ctx
, state
->request
.data
.auth_crap
.lm_resp
, state
->request
.data
.auth_crap
.lm_resp_len
);
453 nt_resp
= data_blob_talloc(mem_ctx
, state
->request
.data
.auth_crap
.nt_resp
, state
->request
.data
.auth_crap
.nt_resp_len
);
456 /* what domain should we contact? */
459 if (!(contact_domain
= find_domain_from_name(name_domain
))) {
460 DEBUG(3, ("Authentication for domain for [%s] -> [%s]\\[%s] failed as %s is not a trusted domain\n",
461 state
->request
.data
.auth_crap
.user
, name_domain
, name_user
, name_domain
));
462 result
= NT_STATUS_NO_SUCH_USER
;
467 if (is_myname(name_domain
)) {
468 DEBUG(3, ("Authentication for domain %s (local domain to this server) not supported at this stage\n", name_domain
));
469 result
= NT_STATUS_NO_SUCH_USER
;
473 if (!(contact_domain
= find_our_domain())) {
474 DEBUG(1, ("Authenticatoin for [%s] -> [%s]\\[%s] in our domain failed - we can't find our domain!\n",
475 state
->request
.data
.auth_crap
.user
, name_domain
, name_user
));
476 result
= NT_STATUS_NO_SUCH_USER
;
481 if ( !get_trust_pw(contact_domain
->name
, trust_passwd
, &last_change_time
, &sec_channel_type
) ) {
482 result
= NT_STATUS_CANT_ACCESS_DOMAIN_INFO
;
488 ZERO_STRUCT(ret_creds
);
491 /* Don't shut this down - it belongs to the connection cache code */
492 result
= cm_get_netlogon_cli(contact_domain
, trust_passwd
, sec_channel_type
, False
, &cli
);
494 if (!NT_STATUS_IS_OK(result
)) {
495 DEBUG(3, ("could not open handle to NETLOGON pipe (error: %s)\n",
500 result
= cli_netlogon_sam_network_logon(cli
, mem_ctx
,
502 name_user
, name_domain
,
504 state
->request
.data
.auth_crap
.chal
,
510 /* We have to try a second time as cm_get_netlogon_cli
511 might not yet have noticed that the DC has killed
514 if ( cli
->fd
== -1 ) {
519 /* if we get access denied, a possible cause was that we had and open
520 connection to the DC, but someone changed our machine account password
521 out from underneath us using 'net rpc changetrustpw' */
523 if ( NT_STATUS_V(result
) == NT_STATUS_V(NT_STATUS_ACCESS_DENIED
) ) {
524 DEBUG(3,("winbindd_pam_auth_crap: sam_logon returned ACCESS_DENIED. Maybe the trust account "
525 "password was changed and we didn't know it. Killing connections to domain %s\n",
526 contact_domain
->name
));
532 } while ( (attempts
< 2) && retry
);
535 /* We might have come out of the loop above with cli == NULL,
536 so don't dereference that. */
537 clnt_deal_with_creds(cli
->sess_key
, &(cli
->clnt_cred
), &ret_creds
);
540 if (NT_STATUS_IS_OK(result
)) {
541 netsamlogon_cache_store( cli
->mem_ctx
, &info3
);
542 wcache_invalidate_samlogon(find_domain_from_name(name_domain
), &info3
);
544 if (!NT_STATUS_IS_OK(result
= check_info3_in_group(mem_ctx
, &info3
, state
->request
.data
.auth_crap
.required_membership_sid
))) {
545 DEBUG(3, ("User %s is not in the required group (%s), so plaintext authentication is rejected\n",
546 state
->request
.data
.auth_crap
.user
,
547 state
->request
.data
.auth_crap
.required_membership_sid
));
551 if (state
->request
.flags
& WBFLAG_PAM_INFO3_NDR
) {
552 result
= append_info3_as_ndr(mem_ctx
, state
, &info3
);
553 } else if (state
->request
.flags
& WBFLAG_PAM_UNIX_NAME
) {
554 /* ntlm_auth should return the unix username, per
555 'winbind use default domain' settings and the like */
557 fstring username_out
;
558 const char *nt_username
, *nt_domain
;
559 if (!(nt_username
= unistr2_tdup(mem_ctx
, &(info3
.uni_user_name
)))) {
560 /* If the server didn't give us one, just use the one we sent them */
561 nt_username
= name_user
;
564 if (!(nt_domain
= unistr2_tdup(mem_ctx
, &(info3
.uni_logon_dom
)))) {
565 /* If the server didn't give us one, just use the one we sent them */
566 nt_domain
= name_domain
;
569 fill_domain_username(username_out
, nt_domain
, nt_username
);
571 DEBUG(5, ("Setting unix username to [%s]\n", username_out
));
573 /* this interface is in UTF8 */
574 if (push_utf8_allocate((char **)&state
->response
.extra_data
, username_out
) == -1) {
575 result
= NT_STATUS_NO_MEMORY
;
578 state
->response
.length
+= strlen(state
->response
.extra_data
)+1;
581 if (state
->request
.flags
& WBFLAG_PAM_USER_SESSION_KEY
) {
582 memcpy(state
->response
.data
.auth
.user_session_key
, info3
.user_sess_key
, sizeof(state
->response
.data
.auth
.user_session_key
) /* 16 */);
584 if (state
->request
.flags
& WBFLAG_PAM_LMKEY
) {
585 memcpy(state
->response
.data
.auth
.first_8_lm_hash
, info3
.padding
, sizeof(state
->response
.data
.auth
.first_8_lm_hash
) /* 8 */);
590 /* give us a more useful (more correct?) error code */
591 if ((NT_STATUS_EQUAL(result
, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND
) || (NT_STATUS_EQUAL(result
, NT_STATUS_UNSUCCESSFUL
)))) {
592 result
= NT_STATUS_NO_LOGON_SERVERS
;
595 if (state
->request
.flags
& WBFLAG_PAM_NT_STATUS_SQUASH
) {
596 result
= nt_status_squash(result
);
599 state
->response
.data
.auth
.nt_status
= NT_STATUS_V(result
);
600 push_utf8_fstring(state
->response
.data
.auth
.nt_status_string
, nt_errstr(result
));
602 /* we might have given a more useful error above */
603 if (!*state
->response
.data
.auth
.error_string
)
604 push_utf8_fstring(state
->response
.data
.auth
.error_string
, get_friendly_nt_error_msg(result
));
605 state
->response
.data
.auth
.pam_error
= nt_status_to_pam(result
);
607 DEBUG(NT_STATUS_IS_OK(result
) ? 5 : 2,
608 ("NTLM CRAP authentication for user [%s]\\[%s] returned %s (PAM: %d)\n",
611 state
->response
.data
.auth
.nt_status_string
,
612 state
->response
.data
.auth
.pam_error
));
615 talloc_destroy(mem_ctx
);
617 return NT_STATUS_IS_OK(result
) ? WINBINDD_OK
: WINBINDD_ERROR
;
620 /* Change a user password */
622 enum winbindd_result
winbindd_pam_chauthtok(struct winbindd_cli_state
*state
)
625 char *oldpass
, *newpass
;
626 fstring domain
, user
;
629 struct winbindd_domain
*contact_domain
;
631 DEBUG(3, ("[%5lu]: pam chauthtok %s\n", (unsigned long)state
->pid
,
632 state
->request
.data
.chauthtok
.user
));
634 if (!(mem_ctx
= talloc_init("winbind password change for (utf8) %s",
635 state
->request
.data
.chauthtok
.user
))) {
636 DEBUG(0, ("winbindd_pam_auth_crap: could not talloc_init()!\n"));
637 result
= NT_STATUS_NO_MEMORY
;
644 return WINBINDD_ERROR
;
646 parse_domain_user(state
->request
.data
.chauthtok
.user
, domain
, user
);
648 if (!(contact_domain
= find_domain_from_name(domain
))) {
649 DEBUG(3, ("Cannot change password for [%s] -> [%s]\\[%s] as %s is not a trusted domain\n",
650 state
->request
.data
.chauthtok
.user
, domain
, user
, domain
));
651 result
= NT_STATUS_NO_SUCH_USER
;
655 /* Change password */
657 oldpass
= state
->request
.data
.chauthtok
.oldpass
;
658 newpass
= state
->request
.data
.chauthtok
.newpass
;
662 if (!NT_STATUS_IS_OK(result
= cm_get_sam_handle(contact_domain
, &hnd
)) ) {
663 DEBUG(1, ("could not get SAM handle on DC for %s\n", domain
));
667 result
= cli_samr_chgpasswd_user(hnd
->cli
, mem_ctx
, user
, newpass
, oldpass
);
670 state
->response
.data
.auth
.nt_status
= NT_STATUS_V(result
);
671 fstrcpy(state
->response
.data
.auth
.nt_status_string
, nt_errstr(result
));
672 fstrcpy(state
->response
.data
.auth
.error_string
, nt_errstr(result
));
673 state
->response
.data
.auth
.pam_error
= nt_status_to_pam(result
);
675 DEBUG(NT_STATUS_IS_OK(result
) ? 5 : 2,
676 ("Password change for user [%s]\\[%s] returned %s (PAM: %d)\n",
679 state
->response
.data
.auth
.nt_status_string
,
680 state
->response
.data
.auth
.pam_error
));
683 talloc_destroy(mem_ctx
);
685 return NT_STATUS_IS_OK(result
) ? WINBINDD_OK
: WINBINDD_ERROR
;