r18271: Big change:
[kai/samba.git] / source3 / nsswitch / winbindd_pam.c
1 /*
2    Unix SMB/CIFS implementation.
3
4    Winbind daemon - pam auth funcions
5
6    Copyright (C) Andrew Tridgell 2000
7    Copyright (C) Tim Potter 2001
8    Copyright (C) Andrew Bartlett 2001-2002
9    Copyright (C) Guenther Deschner 2005
10    
11    This program is free software; you can redistribute it and/or modify
12    it under the terms of the GNU General Public License as published by
13    the Free Software Foundation; either version 2 of the License, or
14    (at your option) any later version.
15    
16    This program is distributed in the hope that it will be useful,
17    but WITHOUT ANY WARRANTY; without even the implied warranty of
18    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19    GNU General Public License for more details.
20    
21    You should have received a copy of the GNU General Public License
22    along with this program; if not, write to the Free Software
23    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
24 */
25
26 #include "includes.h"
27 #include "winbindd.h"
28 #undef DBGC_CLASS
29 #define DBGC_CLASS DBGC_WINBIND
30
31 static NTSTATUS append_info3_as_txt(TALLOC_CTX *mem_ctx, 
32                                     struct winbindd_cli_state *state, 
33                                     NET_USER_INFO_3 *info3) 
34 {
35         fstring str_sid;
36
37         state->response.data.auth.info3.logon_time = 
38                 nt_time_to_unix(info3->logon_time);
39         state->response.data.auth.info3.logoff_time = 
40                 nt_time_to_unix(info3->logoff_time);
41         state->response.data.auth.info3.kickoff_time = 
42                 nt_time_to_unix(info3->kickoff_time);
43         state->response.data.auth.info3.pass_last_set_time = 
44                 nt_time_to_unix(info3->pass_last_set_time);
45         state->response.data.auth.info3.pass_can_change_time = 
46                 nt_time_to_unix(info3->pass_can_change_time);
47         state->response.data.auth.info3.pass_must_change_time = 
48                 nt_time_to_unix(info3->pass_must_change_time);
49
50         state->response.data.auth.info3.logon_count = info3->logon_count;
51         state->response.data.auth.info3.bad_pw_count = info3->bad_pw_count;
52
53         state->response.data.auth.info3.user_rid = info3->user_rid;
54         state->response.data.auth.info3.group_rid = info3->group_rid;
55         sid_to_string(str_sid, &(info3->dom_sid.sid));
56         fstrcpy(state->response.data.auth.info3.dom_sid, str_sid);
57
58         state->response.data.auth.info3.num_groups = info3->num_groups;
59         state->response.data.auth.info3.user_flgs = info3->user_flgs;
60
61         state->response.data.auth.info3.acct_flags = info3->acct_flags;
62         state->response.data.auth.info3.num_other_sids = info3->num_other_sids;
63
64         unistr2_to_ascii(state->response.data.auth.info3.user_name, 
65                 &info3->uni_user_name, -1);
66         unistr2_to_ascii(state->response.data.auth.info3.full_name, 
67                 &info3->uni_full_name, -1);
68         unistr2_to_ascii(state->response.data.auth.info3.logon_script, 
69                 &info3->uni_logon_script, -1);
70         unistr2_to_ascii(state->response.data.auth.info3.profile_path, 
71                 &info3->uni_profile_path, -1);
72         unistr2_to_ascii(state->response.data.auth.info3.home_dir, 
73                 &info3->uni_home_dir, -1);
74         unistr2_to_ascii(state->response.data.auth.info3.dir_drive, 
75                 &info3->uni_dir_drive, -1);
76
77         unistr2_to_ascii(state->response.data.auth.info3.logon_srv, 
78                 &info3->uni_logon_srv, -1);
79         unistr2_to_ascii(state->response.data.auth.info3.logon_dom, 
80                 &info3->uni_logon_dom, -1);
81
82         return NT_STATUS_OK;
83 }
84
85 static NTSTATUS append_info3_as_ndr(TALLOC_CTX *mem_ctx, 
86                                     struct winbindd_cli_state *state, 
87                                     NET_USER_INFO_3 *info3) 
88 {
89         prs_struct ps;
90         uint32 size;
91         if (!prs_init(&ps, 256 /* Random, non-zero number */, mem_ctx, MARSHALL)) {
92                 return NT_STATUS_NO_MEMORY;
93         }
94         if (!net_io_user_info3("", info3, &ps, 1, 3, False)) {
95                 prs_mem_free(&ps);
96                 return NT_STATUS_UNSUCCESSFUL;
97         }
98
99         size = prs_data_size(&ps);
100         SAFE_FREE(state->response.extra_data.data);
101         state->response.extra_data.data = SMB_MALLOC(size);
102         if (!state->response.extra_data.data) {
103                 prs_mem_free(&ps);
104                 return NT_STATUS_NO_MEMORY;
105         }
106         memset( state->response.extra_data.data, '\0', size );
107         prs_copy_all_data_out((char *)state->response.extra_data.data, &ps);
108         state->response.length += size;
109         prs_mem_free(&ps);
110         return NT_STATUS_OK;
111 }
112
113 static NTSTATUS check_info3_in_group(TALLOC_CTX *mem_ctx, 
114                                      NET_USER_INFO_3 *info3,
115                                      const char *group_sid) 
116 {
117         DOM_SID require_membership_of_sid;
118         DOM_SID *all_sids;
119         size_t num_all_sids = (2 + info3->num_groups2 + info3->num_other_sids);
120         size_t i, j = 0;
121
122         /* Parse the 'required group' SID */
123         
124         if (!group_sid || !group_sid[0]) {
125                 /* NO sid supplied, all users may access */
126                 return NT_STATUS_OK;
127         }
128         
129         if (!string_to_sid(&require_membership_of_sid, group_sid)) {
130                 DEBUG(0, ("check_info3_in_group: could not parse %s as a SID!", 
131                           group_sid));
132
133                 return NT_STATUS_INVALID_PARAMETER;
134         }
135
136         all_sids = TALLOC_ARRAY(mem_ctx, DOM_SID, num_all_sids);
137         if (!all_sids)
138                 return NT_STATUS_NO_MEMORY;
139
140         /* and create (by appending rids) the 'domain' sids */
141         
142         sid_copy(&all_sids[0], &(info3->dom_sid.sid));
143         
144         if (!sid_append_rid(&all_sids[0], info3->user_rid)) {
145                 DEBUG(3,("could not append user's primary RID 0x%x\n",
146                          info3->user_rid));                     
147                 
148                 return NT_STATUS_INVALID_PARAMETER;
149         }
150         j++;
151
152         sid_copy(&all_sids[1], &(info3->dom_sid.sid));
153                 
154         if (!sid_append_rid(&all_sids[1], info3->group_rid)) {
155                 DEBUG(3,("could not append additional group rid 0x%x\n",
156                          info3->group_rid));                    
157                 
158                 return NT_STATUS_INVALID_PARAMETER;
159         }
160         j++;    
161
162         for (i = 0; i < info3->num_groups2; i++) {
163         
164                 sid_copy(&all_sids[j], &(info3->dom_sid.sid));
165                 
166                 if (!sid_append_rid(&all_sids[j], info3->gids[i].g_rid)) {
167                         DEBUG(3,("could not append additional group rid 0x%x\n",
168                                 info3->gids[i].g_rid));                 
169                                 
170                         return NT_STATUS_INVALID_PARAMETER;
171                 }
172                 j++;
173         }
174
175         /* Copy 'other' sids.  We need to do sid filtering here to
176            prevent possible elevation of privileges.  See:
177
178            http://www.microsoft.com/windows2000/techinfo/administration/security/sidfilter.asp
179          */
180
181         for (i = 0; i < info3->num_other_sids; i++) {
182                 sid_copy(&all_sids[info3->num_groups2 + i + 2],
183                          &info3->other_sids[i].sid);
184                 j++;
185         }
186
187         for (i = 0; i < j; i++) {
188                 fstring sid1, sid2;
189                 DEBUG(10, ("User has SID: %s\n", 
190                            sid_to_string(sid1, &all_sids[i])));
191                 if (sid_equal(&require_membership_of_sid, &all_sids[i])) {
192                         DEBUG(10, ("SID %s matches %s - user permitted to authenticate!\n", 
193                                    sid_to_string(sid1, &require_membership_of_sid), sid_to_string(sid2, &all_sids[i])));
194                         return NT_STATUS_OK;
195                 }
196         }
197         
198         /* Do not distinguish this error from a wrong username/pw */
199
200         return NT_STATUS_LOGON_FAILURE;
201 }
202
203 struct winbindd_domain *find_auth_domain(struct winbindd_cli_state *state, 
204                                         const char *domain_name)
205 {
206         struct winbindd_domain *domain;
207
208         if (IS_DC) {
209                 domain = find_domain_from_name_noinit(domain_name);
210                 if (domain == NULL) {
211                         DEBUG(3, ("Authentication for domain [%s] refused"
212                                   "as it is not a trusted domain\n", 
213                                   domain_name));
214                 }
215                 return domain;
216         }
217
218         if (is_myname(domain_name)) {
219                 DEBUG(3, ("Authentication for domain %s (local domain "
220                           "to this server) not supported at this "
221                           "stage\n", domain_name));
222                 return NULL;
223         }
224
225         /* we can auth against trusted domains */
226         if (state->request.flags & WBFLAG_PAM_CONTACT_TRUSTDOM) {
227                 domain = find_domain_from_name_noinit(domain_name);
228                 if (domain == NULL) {
229                         DEBUG(3, ("Authentication for domain [%s] skipped " 
230                                   "as it is not a trusted domain\n", 
231                                   domain_name));
232                 } else {
233                         return domain;
234                 } 
235                 }
236
237         return find_our_domain();
238 }
239
240 static void set_auth_errors(struct winbindd_response *resp, NTSTATUS result)
241 {
242         resp->data.auth.nt_status = NT_STATUS_V(result);
243         fstrcpy(resp->data.auth.nt_status_string, nt_errstr(result));
244
245         /* we might have given a more useful error above */
246         if (*resp->data.auth.error_string == '\0') 
247                 fstrcpy(resp->data.auth.error_string,
248                         get_friendly_nt_error_msg(result));
249         resp->data.auth.pam_error = nt_status_to_pam(result);
250 }
251
252 static NTSTATUS fillup_password_policy(struct winbindd_domain *domain,
253                                        struct winbindd_cli_state *state)
254 {
255         struct winbindd_methods *methods;
256         NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
257         SAM_UNK_INFO_1 password_policy;
258
259         methods = domain->methods;
260
261         status = methods->password_policy(domain, state->mem_ctx, &password_policy);
262         if (NT_STATUS_IS_ERR(status)) {
263                 return status;
264         }
265
266         state->response.data.auth.policy.min_length_password =
267                 password_policy.min_length_password;
268         state->response.data.auth.policy.password_history =
269                 password_policy.password_history;
270         state->response.data.auth.policy.password_properties =
271                 password_policy.password_properties;
272         state->response.data.auth.policy.expire =
273                 nt_time_to_unix_abs(&(password_policy.expire));
274         state->response.data.auth.policy.min_passwordage = 
275                 nt_time_to_unix_abs(&(password_policy.min_passwordage));
276
277         return NT_STATUS_OK;
278 }
279
280 static NTSTATUS get_max_bad_attempts_from_lockout_policy(struct winbindd_domain *domain, 
281                                                          TALLOC_CTX *mem_ctx, 
282                                                          uint16 *max_allowed_bad_attempts)
283 {
284         struct winbindd_methods *methods;
285         NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
286         SAM_UNK_INFO_12 lockout_policy;
287
288         *max_allowed_bad_attempts = 0;
289
290         methods = domain->methods;
291
292         status = methods->lockout_policy(domain, mem_ctx, &lockout_policy);
293         if (NT_STATUS_IS_ERR(status)) {
294                 return status;
295         }
296
297         *max_allowed_bad_attempts = lockout_policy.bad_attempt_lockout;
298
299         return NT_STATUS_OK;
300 }
301
302 static NTSTATUS get_pwd_properties(struct winbindd_domain *domain, 
303                                    TALLOC_CTX *mem_ctx, 
304                                    uint32 *password_properties)
305 {
306         struct winbindd_methods *methods;
307         NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
308         SAM_UNK_INFO_1 password_policy;
309
310         *password_properties = 0;
311
312         methods = domain->methods;
313
314         status = methods->password_policy(domain, mem_ctx, &password_policy);
315         if (NT_STATUS_IS_ERR(status)) {
316                 return status;
317         }
318
319         *password_properties = password_policy.password_properties;
320
321         return NT_STATUS_OK;
322 }
323
324 #ifdef HAVE_KRB5
325
326 static const char *generate_krb5_ccache(TALLOC_CTX *mem_ctx, 
327                                         const char *type,
328                                         uid_t uid,
329                                         BOOL *internal_ccache)
330 {
331         /* accept FILE and WRFILE as krb5_cc_type from the client and then
332          * build the full ccname string based on the user's uid here -
333          * Guenther*/
334
335         const char *gen_cc = NULL;
336
337         *internal_ccache = True;
338
339         if (uid == -1) {
340                 goto memory_ccache;
341         }
342
343         if (!type || type[0] == '\0') {
344                 goto memory_ccache;
345         }
346
347         if (strequal(type, "FILE")) {
348                 gen_cc = talloc_asprintf(mem_ctx, "FILE:/tmp/krb5cc_%d", uid);
349         } else if (strequal(type, "WRFILE")) {
350                 gen_cc = talloc_asprintf(mem_ctx, "WRFILE:/tmp/krb5cc_%d", uid);
351         } else {
352                 DEBUG(10,("we don't allow to set a %s type ccache\n", type));
353                 goto memory_ccache;
354         }
355
356         *internal_ccache = False;
357         goto done;
358
359   memory_ccache:
360         gen_cc = talloc_strdup(mem_ctx, "MEMORY:winbindd_pam_ccache");
361
362   done:
363         if (gen_cc == NULL) {
364                 DEBUG(0,("out of memory\n"));
365                 return NULL;
366         }
367
368         DEBUG(10,("using ccache: %s %s\n", gen_cc, *internal_ccache ? "(internal)":""));
369
370         return gen_cc;
371 }
372
373 static void setup_return_cc_name(struct winbindd_cli_state *state, const char *cc)
374 {
375         const char *type = state->request.data.auth.krb5_cc_type;
376
377         state->response.data.auth.krb5ccname[0] = '\0';
378
379         if (type[0] == '\0') {
380                 return;
381         }
382
383         if (!strequal(type, "FILE") &&
384             !strequal(type, "WRFILE")) {
385                 DEBUG(10,("won't return krbccname for a %s type ccache\n", 
386                         type));
387                 return;
388         }
389         
390         fstrcpy(state->response.data.auth.krb5ccname, cc);
391 }
392
393 #endif
394
395 static uid_t get_uid_from_state(struct winbindd_cli_state *state)
396 {
397         uid_t uid = -1;
398
399         uid = state->request.data.auth.uid;
400
401         if (uid < 0) {
402                 DEBUG(1,("invalid uid: '%d'\n", uid));
403                 return -1;
404         }
405         return uid;
406 }
407
408 /**********************************************************************
409  Authenticate a user with a clear text password using Kerberos and fill up
410  ccache if required
411  **********************************************************************/
412
413 static NTSTATUS winbindd_raw_kerberos_login(struct winbindd_domain *domain,
414                                             struct winbindd_cli_state *state,
415                                             NET_USER_INFO_3 **info3)
416 {
417 #ifdef HAVE_KRB5
418         NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
419         krb5_error_code krb5_ret;
420         DATA_BLOB tkt, session_key_krb5;
421         DATA_BLOB ap_rep, session_key;
422         PAC_DATA *pac_data = NULL;
423         PAC_LOGON_INFO *logon_info = NULL;
424         char *client_princ = NULL;
425         char *client_princ_out = NULL;
426         char *local_service = NULL;
427         const char *cc = NULL;
428         const char *principal_s = NULL;
429         const char *service = NULL;
430         char *realm = NULL;
431         fstring name_domain, name_user;
432         time_t ticket_lifetime = 0;
433         time_t renewal_until = 0;
434         uid_t uid = -1;
435         ADS_STRUCT *ads;
436         time_t time_offset = 0;
437         BOOL internal_ccache = True;
438
439         ZERO_STRUCT(session_key);
440         ZERO_STRUCT(session_key_krb5);
441         ZERO_STRUCT(tkt);
442         ZERO_STRUCT(ap_rep);
443
444         ZERO_STRUCTP(info3);
445
446         *info3 = NULL;
447         
448         /* 1st step: 
449          * prepare a krb5_cc_cache string for the user */
450
451         uid = get_uid_from_state(state);
452         if (uid == -1) {
453                 DEBUG(0,("no valid uid\n"));
454         }
455
456         cc = generate_krb5_ccache(state->mem_ctx,
457                                   state->request.data.auth.krb5_cc_type,
458                                   state->request.data.auth.uid, 
459                                   &internal_ccache);
460         if (cc == NULL) {
461                 return NT_STATUS_NO_MEMORY;
462         }
463
464
465         /* 2nd step: 
466          * get kerberos properties */
467         
468         if (domain->private_data) {
469                 ads = (ADS_STRUCT *)domain->private_data;
470                 time_offset = ads->auth.time_offset; 
471         }
472
473
474         /* 3rd step: 
475          * do kerberos auth and setup ccache as the user */
476
477         parse_domain_user(state->request.data.auth.user, name_domain, name_user);
478
479         realm = domain->alt_name;
480         strupper_m(realm);
481         
482         principal_s = talloc_asprintf(state->mem_ctx, "%s@%s", name_user, realm); 
483         if (principal_s == NULL) {
484                 return NT_STATUS_NO_MEMORY;
485         }
486
487         service = talloc_asprintf(state->mem_ctx, "%s/%s@%s", KRB5_TGS_NAME, realm, realm);
488         if (service == NULL) {
489                 return NT_STATUS_NO_MEMORY;
490         }
491
492         /* if this is a user ccache, we need to act as the user to let the krb5
493          * library handle the chown, etc. */
494
495         /************************ NON-ROOT **********************/
496
497         if (!internal_ccache) {
498
499                 set_effective_uid(uid);
500                 DEBUG(10,("winbindd_raw_kerberos_login: uid is %d\n", uid));
501         }
502
503         krb5_ret = kerberos_kinit_password_ext(principal_s, 
504                                                state->request.data.auth.pass, 
505                                                time_offset, 
506                                                &ticket_lifetime,
507                                                &renewal_until,
508                                                cc, 
509                                                True,
510                                                True,
511                                                WINBINDD_PAM_AUTH_KRB5_RENEW_TIME);
512
513         if (krb5_ret) {
514                 DEBUG(1,("winbindd_raw_kerberos_login: kinit failed for '%s' with: %s (%d)\n", 
515                         principal_s, error_message(krb5_ret), krb5_ret));
516                 result = krb5_to_nt_status(krb5_ret);
517                 goto failed;
518         }
519
520         /* does http_timestring use heimdals libroken strftime?? - Guenther */
521         DEBUG(10,("got TGT for %s in %s (valid until: %s (%d), renewable till: %s (%d))\n", 
522                 principal_s, cc, 
523                 http_timestring(ticket_lifetime), (int)ticket_lifetime, 
524                 http_timestring(renewal_until), (int)renewal_until));
525
526         client_princ = talloc_strdup(state->mem_ctx, global_myname());
527         if (client_princ == NULL) {
528                 result = NT_STATUS_NO_MEMORY;
529                 goto failed;
530         }
531         strlower_m(client_princ);
532
533         local_service = talloc_asprintf(state->mem_ctx, "%s$@%s", client_princ, lp_realm());
534         if (local_service == NULL) {
535                 DEBUG(0,("winbindd_raw_kerberos_login: out of memory\n"));
536                 result = NT_STATUS_NO_MEMORY;
537                 goto failed;
538         }
539
540         krb5_ret = cli_krb5_get_ticket(local_service, 
541                                        time_offset, 
542                                        &tkt, 
543                                        &session_key_krb5, 
544                                        0, 
545                                        cc);
546         if (krb5_ret) {
547                 DEBUG(1,("winbindd_raw_kerberos_login: failed to get ticket for %s: %s\n", 
548                         local_service, error_message(krb5_ret)));
549                 result = krb5_to_nt_status(krb5_ret);
550                 goto failed;
551         }
552
553         if (!internal_ccache) {
554                 gain_root_privilege();
555         }
556
557         /************************ NON-ROOT **********************/
558
559         result = ads_verify_ticket(state->mem_ctx, 
560                                    lp_realm(), 
561                                    time_offset,
562                                    &tkt, 
563                                    &client_princ_out, 
564                                    &pac_data, 
565                                    &ap_rep, 
566                                    &session_key);       
567         if (!NT_STATUS_IS_OK(result)) {
568                 DEBUG(0,("winbindd_raw_kerberos_login: ads_verify_ticket failed: %s\n", 
569                         nt_errstr(result)));
570                 goto failed;
571         }
572
573         if (!pac_data) {
574                 DEBUG(3,("winbindd_raw_kerberos_login: no pac data\n"));
575                 result = NT_STATUS_INVALID_PARAMETER;
576                 goto failed;
577         }
578                         
579         logon_info = get_logon_info_from_pac(pac_data);
580         if (logon_info == NULL) {
581                 DEBUG(1,("winbindd_raw_kerberos_login: no logon info\n"));
582                 result = NT_STATUS_INVALID_PARAMETER;
583                 goto failed;
584         }
585
586         DEBUG(10,("winbindd_raw_kerberos_login: winbindd validated ticket of %s\n", 
587                 local_service));
588
589
590         /* last step: 
591          * put results together */
592
593         *info3 = &logon_info->info3;
594
595         /* if we had a user's ccache then return that string for the pam
596          * environment */
597
598         if (!internal_ccache) {
599                 
600                 setup_return_cc_name(state, cc);
601
602                 result = add_ccache_to_list(principal_s,
603                                             cc,
604                                             service,
605                                             state->request.data.auth.user,
606                                             realm,
607                                             uid,
608                                             time(NULL),
609                                             ticket_lifetime,
610                                             renewal_until, 
611                                             lp_winbind_refresh_tickets(),
612                                             False);
613
614                 if (!NT_STATUS_IS_OK(result)) {
615                         DEBUG(10,("winbindd_raw_kerberos_login: failed to add ccache to list: %s\n", 
616                                 nt_errstr(result)));
617                 }
618         }
619
620         result = NT_STATUS_OK;
621
622         goto done;
623
624 failed:
625
626         /* we could have created a new credential cache with a valid tgt in it
627          * but we werent able to get or verify the service ticket for this
628          * local host and therefor didn't get the PAC, we need to remove that
629          * cache entirely now */
630
631         krb5_ret = ads_kdestroy(cc);
632         if (krb5_ret) {
633                 DEBUG(3,("winbindd_raw_kerberos_login: "
634                          "could not destroy krb5 credential cache: "
635                          "%s\n", error_message(krb5_ret)));
636         }
637
638         if (!NT_STATUS_IS_OK(remove_ccache(state->request.data.auth.user))) {
639                 DEBUG(3,("winbindd_raw_kerberos_login: "
640                           "could not remove ccache for user %s\n",
641                         state->request.data.auth.user));
642         }
643
644 done:
645         data_blob_free(&session_key);
646         data_blob_free(&session_key_krb5);
647         data_blob_free(&ap_rep);
648         data_blob_free(&tkt);
649
650         SAFE_FREE(client_princ_out);
651
652         if (!internal_ccache) {
653                 gain_root_privilege();
654         }
655
656         return result;
657 #else 
658         return NT_STATUS_NOT_SUPPORTED;
659 #endif /* HAVE_KRB5 */
660 }
661
662 void winbindd_pam_auth(struct winbindd_cli_state *state)
663 {
664         struct winbindd_domain *domain;
665         fstring name_domain, name_user;
666
667         /* Ensure null termination */
668         state->request.data.auth.user
669                 [sizeof(state->request.data.auth.user)-1]='\0';
670
671         /* Ensure null termination */
672         state->request.data.auth.pass
673                 [sizeof(state->request.data.auth.pass)-1]='\0';
674
675         DEBUG(3, ("[%5lu]: pam auth %s\n", (unsigned long)state->pid,
676                   state->request.data.auth.user));
677
678         /* Parse domain and username */
679         
680         if (!parse_domain_user(state->request.data.auth.user,
681                                name_domain, name_user)) {
682                 set_auth_errors(&state->response, NT_STATUS_NO_SUCH_USER);
683                 DEBUG(5, ("Plain text authentication for %s returned %s "
684                           "(PAM: %d)\n",
685                           state->request.data.auth.user, 
686                           state->response.data.auth.nt_status_string,
687                           state->response.data.auth.pam_error));
688                 request_error(state);
689                 return;
690         }
691
692         domain = find_auth_domain(state, name_domain);
693
694         if (domain == NULL) {
695                 set_auth_errors(&state->response, NT_STATUS_NO_SUCH_USER);
696                 DEBUG(5, ("Plain text authentication for %s returned %s "
697                           "(PAM: %d)\n",
698                           state->request.data.auth.user, 
699                           state->response.data.auth.nt_status_string,
700                           state->response.data.auth.pam_error));
701                 request_error(state);
702                 return;
703         }
704
705         sendto_domain(state, domain);
706 }
707
708 NTSTATUS winbindd_dual_pam_auth_cached(struct winbindd_domain *domain,
709                                        struct winbindd_cli_state *state,
710                                        NET_USER_INFO_3 **info3)
711 {
712         NTSTATUS result = NT_STATUS_LOGON_FAILURE;
713         uint16 max_allowed_bad_attempts; 
714         fstring name_domain, name_user;
715         DOM_SID sid;
716         enum lsa_SidType type;
717         uchar new_nt_pass[NT_HASH_LEN];
718         const uint8 *cached_nt_pass;
719         const uint8 *cached_salt;
720         NET_USER_INFO_3 *my_info3;
721         time_t kickoff_time, must_change_time;
722         BOOL password_good = False;
723
724         *info3 = NULL;
725
726         ZERO_STRUCTP(info3);
727
728         DEBUG(10,("winbindd_dual_pam_auth_cached\n"));
729
730         /* Parse domain and username */
731         
732         parse_domain_user(state->request.data.auth.user, name_domain, name_user);
733
734
735         if (!lookup_cached_name(state->mem_ctx,
736                                 name_domain,
737                                 name_user,
738                                 &sid,
739                                 &type)) {
740                 DEBUG(10,("winbindd_dual_pam_auth_cached: no such user in the cache\n"));
741                 return NT_STATUS_NO_SUCH_USER;
742         }
743
744         if (type != SID_NAME_USER) {
745                 DEBUG(10,("winbindd_dual_pam_auth_cached: not a user (%s)\n", sid_type_lookup(type)));
746                 return NT_STATUS_LOGON_FAILURE;
747         }
748
749         result = winbindd_get_creds(domain, 
750                                     state->mem_ctx, 
751                                     &sid, 
752                                     &my_info3, 
753                                     &cached_nt_pass,
754                                     &cached_salt);
755         if (!NT_STATUS_IS_OK(result)) {
756                 DEBUG(10,("winbindd_dual_pam_auth_cached: failed to get creds: %s\n", nt_errstr(result)));
757                 return result;
758         }
759
760         *info3 = my_info3;
761
762         E_md4hash(state->request.data.auth.pass, new_nt_pass);
763
764 #if DEBUG_PASSWORD
765         dump_data(100, (const char *)new_nt_pass, NT_HASH_LEN);
766         dump_data(100, (const char *)cached_nt_pass, NT_HASH_LEN);
767         if (cached_salt) {
768                 dump_data(100, (const char *)cached_salt, NT_HASH_LEN);
769         }
770 #endif
771
772         if (cached_salt) {
773                 /* In this case we didn't store the nt_hash itself,
774                    but the MD5 combination of salt + nt_hash. */
775                 uchar salted_hash[NT_HASH_LEN];
776                 E_md5hash(cached_salt, new_nt_pass, salted_hash);
777
778                 password_good = (memcmp(cached_nt_pass, salted_hash, NT_HASH_LEN) == 0) ?
779                         True : False;
780         } else {
781                 /* Old cached cred - direct store of nt_hash (bad bad bad !). */
782                 password_good = (memcmp(cached_nt_pass, new_nt_pass, NT_HASH_LEN) == 0) ?
783                         True : False;
784         }
785
786         if (password_good) {
787
788                 /* User *DOES* know the password, update logon_time and reset
789                  * bad_pw_count */
790         
791                 my_info3->user_flgs |= LOGON_CACHED_ACCOUNT;
792         
793                 if (my_info3->acct_flags & ACB_AUTOLOCK) {
794                         return NT_STATUS_ACCOUNT_LOCKED_OUT;
795                 }
796         
797                 if (my_info3->acct_flags & ACB_DISABLED) {
798                         return NT_STATUS_ACCOUNT_DISABLED;
799                 }
800         
801                 if (my_info3->acct_flags & ACB_WSTRUST) {
802                         return NT_STATUS_NOLOGON_WORKSTATION_TRUST_ACCOUNT;
803                 }
804         
805                 if (my_info3->acct_flags & ACB_SVRTRUST) {
806                         return NT_STATUS_NOLOGON_SERVER_TRUST_ACCOUNT;
807                 }
808         
809                 if (my_info3->acct_flags & ACB_DOMTRUST) {
810                         return NT_STATUS_NOLOGON_INTERDOMAIN_TRUST_ACCOUNT;
811                 }
812
813                 /* The info3 acct_flags in NT4's samlogon reply don't have
814                  * ACB_NORMAL set. */
815 #if 0
816                 if (!(my_info3->acct_flags & ACB_NORMAL)) {
817                         DEBUG(10,("winbindd_dual_pam_auth_cached: whats wrong with that one?: 0x%08x\n", 
818                                 my_info3->acct_flags));
819                         return NT_STATUS_LOGON_FAILURE;
820                 }
821 #endif
822                 kickoff_time = nt_time_to_unix(my_info3->kickoff_time);
823                 if (kickoff_time != 0 && time(NULL) > kickoff_time) {
824                         return NT_STATUS_ACCOUNT_EXPIRED;
825                 }
826
827                 must_change_time = nt_time_to_unix(my_info3->pass_must_change_time);
828                 if (must_change_time != 0 && must_change_time < time(NULL)) {
829                         return NT_STATUS_PASSWORD_EXPIRED;
830                 }
831         
832                 /* FIXME: we possibly should handle logon hours as well (does xp when
833                  * offline?) see auth/auth_sam.c:sam_account_ok for details */
834
835                 unix_to_nt_time(&my_info3->logon_time, time(NULL));
836                 my_info3->bad_pw_count = 0;
837
838                 result = winbindd_update_creds_by_info3(domain,
839                                                         state->mem_ctx,
840                                                         state->request.data.auth.user,
841                                                         state->request.data.auth.pass,
842                                                         my_info3);
843                 if (!NT_STATUS_IS_OK(result)) {
844                         DEBUG(1,("winbindd_dual_pam_auth_cached: failed to update creds: %s\n",
845                                 nt_errstr(result)));
846                         return result;
847                 }
848
849 #ifdef HAVE_KRB5
850                 /* FIXME: what else points out that the remote domain is AD ? */
851                 if (!strequal(domain->name, domain->alt_name) &&
852                     (state->request.flags & WBFLAG_PAM_KRB5)) {
853
854                         uid_t uid = -1;
855                         const char *cc = NULL;
856                         char *realm = NULL;
857                         const char *principal_s = NULL;
858                         const char *service = NULL;
859                         BOOL internal_ccache = False;
860
861                         uid = get_uid_from_state(state);
862                         if (uid == -1) {
863                                 DEBUG(0,("winbindd_dual_pam_auth_cached: invalid uid\n"));
864                                 return NT_STATUS_INVALID_PARAMETER;
865                         }
866
867                         cc = generate_krb5_ccache(state->mem_ctx,
868                                                 state->request.data.auth.krb5_cc_type,
869                                                 state->request.data.auth.uid,
870                                                 &internal_ccache);
871                         if (cc == NULL) {
872                                 return NT_STATUS_NO_MEMORY;
873                         }
874
875                         realm = domain->alt_name;
876                         strupper_m(realm);
877
878                         principal_s = talloc_asprintf(state->mem_ctx, "%s@%s", name_user, realm);
879                         if (principal_s == NULL) {
880                                 return NT_STATUS_NO_MEMORY;
881                         }
882
883                         service = talloc_asprintf(state->mem_ctx, "%s/%s@%s", KRB5_TGS_NAME, realm, realm);
884                         if (service == NULL) {
885                                 return NT_STATUS_NO_MEMORY;
886                         }
887
888                         if (!internal_ccache) {
889
890                                 setup_return_cc_name(state, cc);
891
892                                 result = add_ccache_to_list(principal_s,
893                                                             cc,
894                                                             service,
895                                                             state->request.data.auth.user,
896                                                             domain->alt_name,
897                                                             uid,
898                                                             time(NULL),
899                                                             time(NULL) + lp_winbind_cache_time(),
900                                                             time(NULL) + WINBINDD_PAM_AUTH_KRB5_RENEW_TIME,
901                                                             lp_winbind_refresh_tickets(),
902                                                             True);
903
904                                 if (!NT_STATUS_IS_OK(result)) {
905                                         DEBUG(10,("winbindd_dual_pam_auth_cached: failed "
906                                                 "to add ccache to list: %s\n",
907                                                 nt_errstr(result)));
908                                 }
909                         }
910                 }
911 #endif /* HAVE_KRB5 */
912                 return NT_STATUS_OK;
913
914         }
915
916         /* User does *NOT* know the correct password, modify info3 accordingly */
917
918         /* failure of this is not critical */
919         result = get_max_bad_attempts_from_lockout_policy(domain, state->mem_ctx, &max_allowed_bad_attempts);
920         if (!NT_STATUS_IS_OK(result)) {
921                 DEBUG(10,("winbindd_dual_pam_auth_cached: failed to get max_allowed_bad_attempts. "
922                           "Won't be able to honour account lockout policies\n"));
923         }
924
925         /* increase counter */
926         my_info3->bad_pw_count++;
927
928         if (max_allowed_bad_attempts == 0) {
929                 goto failed;
930         }
931
932         /* lockout user */
933         if (my_info3->bad_pw_count >= max_allowed_bad_attempts) {
934
935                 uint32 password_properties;
936
937                 result = get_pwd_properties(domain, state->mem_ctx, &password_properties);
938                 if (!NT_STATUS_IS_OK(result)) {
939                         DEBUG(10,("winbindd_dual_pam_auth_cached: failed to get password properties.\n"));
940                 }
941
942                 if ((my_info3->user_rid != DOMAIN_USER_RID_ADMIN) || 
943                     (password_properties & DOMAIN_LOCKOUT_ADMINS)) {
944                         my_info3->acct_flags |= ACB_AUTOLOCK;
945                 }
946         }
947
948 failed:
949         result = winbindd_update_creds_by_info3(domain,
950                                                 state->mem_ctx,
951                                                 state->request.data.auth.user,
952                                                 NULL,
953                                                 my_info3);
954
955         if (!NT_STATUS_IS_OK(result)) {
956                 DEBUG(0,("winbindd_dual_pam_auth_cached: failed to update creds %s\n", 
957                         nt_errstr(result)));
958         }
959
960         return NT_STATUS_LOGON_FAILURE;
961 }
962
963 NTSTATUS winbindd_dual_pam_auth_kerberos(struct winbindd_domain *domain,
964                                          struct winbindd_cli_state *state, 
965                                          NET_USER_INFO_3 **info3)
966 {
967         struct winbindd_domain *contact_domain;
968         fstring name_domain, name_user;
969         NTSTATUS result;
970
971         DEBUG(10,("winbindd_dual_pam_auth_kerberos\n"));
972         
973         /* Parse domain and username */
974         
975         parse_domain_user(state->request.data.auth.user, name_domain, name_user);
976
977         /* what domain should we contact? */
978         
979         if ( IS_DC ) {
980                 if (!(contact_domain = find_domain_from_name(name_domain))) {
981                         DEBUG(3, ("Authentication for domain for [%s] -> [%s]\\[%s] failed as %s is not a trusted domain\n", 
982                                   state->request.data.auth.user, name_domain, name_user, name_domain)); 
983                         result = NT_STATUS_NO_SUCH_USER;
984                         goto done;
985                 }
986                 
987         } else {
988                 if (is_myname(name_domain)) {
989                         DEBUG(3, ("Authentication for domain %s (local domain to this server) not supported at this stage\n", name_domain));
990                         result =  NT_STATUS_NO_SUCH_USER;
991                         goto done;
992                 }
993                 
994                 contact_domain = find_domain_from_name(name_domain);
995                 if (contact_domain == NULL) {
996                         DEBUG(3, ("Authentication for domain for [%s] -> [%s]\\[%s] failed as %s is not a trusted domain\n", 
997                                   state->request.data.auth.user, name_domain, name_user, name_domain)); 
998
999                         contact_domain = find_our_domain();
1000                 }
1001         }
1002
1003         if (contact_domain->initialized && 
1004             contact_domain->active_directory) {
1005                 goto try_login;
1006         }
1007
1008         if (!contact_domain->initialized) {
1009                 set_dc_type_and_flags(contact_domain);
1010         }
1011
1012         if (!contact_domain->active_directory) {
1013                 DEBUG(3,("krb5 auth requested but domain is not Active Directory\n"));
1014                 return NT_STATUS_INVALID_LOGON_TYPE;
1015         }
1016 try_login:
1017         result = winbindd_raw_kerberos_login(contact_domain, state, info3);
1018 done:
1019         return result;
1020 }
1021
1022 NTSTATUS winbindd_dual_pam_auth_samlogon(struct winbindd_domain *domain,
1023                                          struct winbindd_cli_state *state,
1024                                          NET_USER_INFO_3 **info3)
1025 {
1026
1027         struct rpc_pipe_client *netlogon_pipe;
1028         uchar chal[8];
1029         DATA_BLOB lm_resp;
1030         DATA_BLOB nt_resp;
1031         int attempts = 0;
1032         unsigned char local_lm_response[24];
1033         unsigned char local_nt_response[24];
1034         struct winbindd_domain *contact_domain;
1035         fstring name_domain, name_user;
1036         BOOL retry;
1037         NTSTATUS result;
1038         NET_USER_INFO_3 *my_info3;
1039
1040         ZERO_STRUCTP(info3);
1041
1042         *info3 = NULL;
1043
1044         my_info3 = TALLOC_ZERO_P(state->mem_ctx, NET_USER_INFO_3);
1045         if (my_info3 == NULL) {
1046                 return NT_STATUS_NO_MEMORY;
1047         }
1048
1049
1050         DEBUG(10,("winbindd_dual_pam_auth_samlogon\n"));
1051         
1052         /* Parse domain and username */
1053         
1054         parse_domain_user(state->request.data.auth.user, name_domain, name_user);
1055
1056         /* do password magic */
1057         
1058
1059         generate_random_buffer(chal, 8);
1060         if (lp_client_ntlmv2_auth()) {
1061                 DATA_BLOB server_chal;
1062                 DATA_BLOB names_blob;
1063                 DATA_BLOB nt_response;
1064                 DATA_BLOB lm_response;
1065                 server_chal = data_blob_talloc(state->mem_ctx, chal, 8); 
1066                 
1067                 /* note that the 'workgroup' here is a best guess - we don't know
1068                    the server's domain at this point.  The 'server name' is also
1069                    dodgy... 
1070                 */
1071                 names_blob = NTLMv2_generate_names_blob(global_myname(), lp_workgroup());
1072                 
1073                 if (!SMBNTLMv2encrypt(name_user, name_domain, 
1074                                       state->request.data.auth.pass, 
1075                                       &server_chal, 
1076                                       &names_blob,
1077                                       &lm_response, &nt_response, NULL)) {
1078                         data_blob_free(&names_blob);
1079                         data_blob_free(&server_chal);
1080                         DEBUG(0, ("winbindd_pam_auth: SMBNTLMv2encrypt() failed!\n"));
1081                         result = NT_STATUS_NO_MEMORY;
1082                         goto done;
1083                 }
1084                 data_blob_free(&names_blob);
1085                 data_blob_free(&server_chal);
1086                 lm_resp = data_blob_talloc(state->mem_ctx, lm_response.data,
1087                                            lm_response.length);
1088                 nt_resp = data_blob_talloc(state->mem_ctx, nt_response.data,
1089                                            nt_response.length);
1090                 data_blob_free(&lm_response);
1091                 data_blob_free(&nt_response);
1092
1093         } else {
1094                 if (lp_client_lanman_auth() 
1095                     && SMBencrypt(state->request.data.auth.pass, 
1096                                   chal, 
1097                                   local_lm_response)) {
1098                         lm_resp = data_blob_talloc(state->mem_ctx, 
1099                                                    local_lm_response, 
1100                                                    sizeof(local_lm_response));
1101                 } else {
1102                         lm_resp = data_blob(NULL, 0);
1103                 }
1104                 SMBNTencrypt(state->request.data.auth.pass, 
1105                              chal,
1106                              local_nt_response);
1107
1108                 nt_resp = data_blob_talloc(state->mem_ctx, 
1109                                            local_nt_response, 
1110                                            sizeof(local_nt_response));
1111         }
1112         
1113         /* what domain should we contact? */
1114         
1115         if ( IS_DC ) {
1116                 if (!(contact_domain = find_domain_from_name(name_domain))) {
1117                         DEBUG(3, ("Authentication for domain for [%s] -> [%s]\\[%s] failed as %s is not a trusted domain\n", 
1118                                   state->request.data.auth.user, name_domain, name_user, name_domain)); 
1119                         result = NT_STATUS_NO_SUCH_USER;
1120                         goto done;
1121                 }
1122                 
1123         } else {
1124                 if (is_myname(name_domain)) {
1125                         DEBUG(3, ("Authentication for domain %s (local domain to this server) not supported at this stage\n", name_domain));
1126                         result =  NT_STATUS_NO_SUCH_USER;
1127                         goto done;
1128                 }
1129
1130                 contact_domain = find_our_domain();
1131         }
1132
1133         /* check authentication loop */
1134
1135         do {
1136
1137                 ZERO_STRUCTP(my_info3);
1138                 retry = False;
1139
1140                 result = cm_connect_netlogon(contact_domain, &netlogon_pipe);
1141
1142                 if (!NT_STATUS_IS_OK(result)) {
1143                         DEBUG(3, ("could not open handle to NETLOGON pipe\n"));
1144                         goto done;
1145                 }
1146
1147                 result = rpccli_netlogon_sam_network_logon(netlogon_pipe,
1148                                                            state->mem_ctx,
1149                                                            0,
1150                                                            contact_domain->dcname, /* server name */
1151                                                            name_user,              /* user name */
1152                                                            name_domain,            /* target domain */
1153                                                            global_myname(),        /* workstation */
1154                                                            chal,
1155                                                            lm_resp,
1156                                                            nt_resp,
1157                                                            my_info3);
1158                 attempts += 1;
1159
1160                 /* We have to try a second time as cm_connect_netlogon
1161                    might not yet have noticed that the DC has killed
1162                    our connection. */
1163
1164                 if (NT_STATUS_EQUAL(result, NT_STATUS_UNSUCCESSFUL)) {
1165                         retry = True;
1166                         continue;
1167                 }
1168                 
1169                 /* if we get access denied, a possible cause was that we had
1170                    and open connection to the DC, but someone changed our
1171                    machine account password out from underneath us using 'net
1172                    rpc changetrustpw' */
1173                    
1174                 if ( NT_STATUS_EQUAL(result, NT_STATUS_ACCESS_DENIED) ) {
1175                         DEBUG(3,("winbindd_pam_auth: sam_logon returned "
1176                                  "ACCESS_DENIED.  Maybe the trust account "
1177                                 "password was changed and we didn't know it. "
1178                                  "Killing connections to domain %s\n",
1179                                 name_domain));
1180                         invalidate_cm_connection(&contact_domain->conn);
1181                         retry = True;
1182                 } 
1183                 
1184         } while ( (attempts < 2) && retry );
1185
1186         *info3 = my_info3;
1187 done:
1188         return result;
1189 }
1190
1191 enum winbindd_result winbindd_dual_pam_auth(struct winbindd_domain *domain,
1192                                             struct winbindd_cli_state *state) 
1193 {
1194         NTSTATUS result = NT_STATUS_LOGON_FAILURE;
1195         fstring name_domain, name_user;
1196         NET_USER_INFO_3 *info3 = NULL;
1197         
1198         /* Ensure null termination */
1199         state->request.data.auth.user[sizeof(state->request.data.auth.user)-1]='\0';
1200
1201         /* Ensure null termination */
1202         state->request.data.auth.pass[sizeof(state->request.data.auth.pass)-1]='\0';
1203
1204         DEBUG(3, ("[%5lu]: dual pam auth %s\n", (unsigned long)state->pid,
1205                   state->request.data.auth.user));
1206
1207         /* Parse domain and username */
1208         
1209         parse_domain_user(state->request.data.auth.user, name_domain, name_user);
1210
1211         DEBUG(10,("winbindd_dual_pam_auth: domain: %s last was %s\n", domain->name, domain->online ? "online":"offline"));
1212
1213         /* Check for Kerberos authentication */
1214         if (domain->online && (state->request.flags & WBFLAG_PAM_KRB5)) {
1215         
1216                 result = winbindd_dual_pam_auth_kerberos(domain, state, &info3);
1217
1218                 if (NT_STATUS_IS_OK(result)) {
1219                         DEBUG(10,("winbindd_dual_pam_auth_kerberos succeeded\n"));
1220                         goto process_result;
1221                 } else {
1222                         DEBUG(10,("winbindd_dual_pam_auth_kerberos failed: %s\n", nt_errstr(result)));
1223                 }
1224
1225                 if (NT_STATUS_EQUAL(result, NT_STATUS_NO_LOGON_SERVERS) ||
1226                     NT_STATUS_EQUAL(result, NT_STATUS_IO_TIMEOUT) ||
1227                     NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
1228                         DEBUG(10,("winbindd_dual_pam_auth_kerberos setting domain to offline\n"));
1229                         domain->online = False;
1230                 }
1231
1232                 /* there are quite some NT_STATUS errors where there is no
1233                  * point in retrying with a samlogon, we explictly have to take
1234                  * care not to increase the bad logon counter on the DC */
1235
1236                 if (NT_STATUS_EQUAL(result, NT_STATUS_ACCOUNT_DISABLED) ||
1237                     NT_STATUS_EQUAL(result, NT_STATUS_ACCOUNT_EXPIRED) ||
1238                     NT_STATUS_EQUAL(result, NT_STATUS_ACCOUNT_LOCKED_OUT) ||
1239                     NT_STATUS_EQUAL(result, NT_STATUS_INVALID_LOGON_HOURS) ||
1240                     NT_STATUS_EQUAL(result, NT_STATUS_INVALID_WORKSTATION) ||
1241                     NT_STATUS_EQUAL(result, NT_STATUS_LOGON_FAILURE) ||
1242                     NT_STATUS_EQUAL(result, NT_STATUS_NO_SUCH_USER) ||
1243                     NT_STATUS_EQUAL(result, NT_STATUS_PASSWORD_EXPIRED) ||
1244                     NT_STATUS_EQUAL(result, NT_STATUS_PASSWORD_MUST_CHANGE) ||
1245                     NT_STATUS_EQUAL(result, NT_STATUS_WRONG_PASSWORD)) {
1246                         goto process_result;
1247                 }
1248                 
1249                 if (state->request.flags & WBFLAG_PAM_FALLBACK_AFTER_KRB5) {
1250                         DEBUG(3,("falling back to samlogon\n"));
1251                         goto sam_logon;
1252                 } else {
1253                         goto cached_logon;
1254                 }
1255         }
1256
1257 sam_logon:
1258         /* Check for Samlogon authentication */
1259         if (domain->online) {
1260                 result = winbindd_dual_pam_auth_samlogon(domain, state, &info3);
1261         
1262                 if (NT_STATUS_IS_OK(result)) {
1263                         DEBUG(10,("winbindd_dual_pam_auth_samlogon succeeded\n"));
1264                         goto process_result;
1265                 } else {
1266                         DEBUG(10,("winbindd_dual_pam_auth_samlogon failed: %s\n", nt_errstr(result)));
1267                         if (domain->online) {
1268                                 /* We're still online - fail. */
1269                                 goto done;
1270                         }
1271                         /* Else drop through and see if we can check offline.... */
1272                 }
1273         }
1274
1275 cached_logon:
1276         /* Check for Cached logons */
1277         if (!domain->online && (state->request.flags & WBFLAG_PAM_CACHED_LOGIN) && 
1278             lp_winbind_offline_logon()) {
1279         
1280                 result = winbindd_dual_pam_auth_cached(domain, state, &info3);
1281
1282                 if (NT_STATUS_IS_OK(result)) {
1283                         DEBUG(10,("winbindd_dual_pam_auth_cached succeeded\n"));
1284                         goto process_result;
1285                 } else {
1286                         DEBUG(10,("winbindd_dual_pam_auth_cached failed: %s\n", nt_errstr(result)));
1287                         goto done;
1288                 }
1289         }
1290
1291 process_result:
1292
1293         if (NT_STATUS_IS_OK(result)) {
1294         
1295                 DOM_SID user_sid;
1296
1297                 /* In all codepaths where result == NT_STATUS_OK info3 must have
1298                    been initialized. */
1299                 if (!info3) {
1300                         result = NT_STATUS_INTERNAL_ERROR;
1301                         goto done;
1302                 }
1303
1304                 netsamlogon_cache_store(name_user, info3);
1305                 wcache_invalidate_samlogon(find_domain_from_name(name_domain), info3);
1306
1307                 /* save name_to_sid info as early as possible */
1308                 sid_compose(&user_sid, &info3->dom_sid.sid, info3->user_rid);
1309                 cache_name2sid(domain, name_domain, name_user, SID_NAME_USER, &user_sid);
1310                 
1311                 /* Check if the user is in the right group */
1312
1313                 if (!NT_STATUS_IS_OK(result = check_info3_in_group(state->mem_ctx, info3,
1314                                         state->request.data.auth.require_membership_of_sid))) {
1315                         DEBUG(3, ("User %s is not in the required group (%s), so plaintext authentication is rejected\n",
1316                                   state->request.data.auth.user, 
1317                                   state->request.data.auth.require_membership_of_sid));
1318                         goto done;
1319                 }
1320
1321                 if (state->request.flags & WBFLAG_PAM_INFO3_NDR) {
1322                         result = append_info3_as_ndr(state->mem_ctx, state, info3);
1323                         if (!NT_STATUS_IS_OK(result)) {
1324                                 DEBUG(10,("Failed to append INFO3 (NDR): %s\n", nt_errstr(result)));
1325                                 goto done;
1326                         }
1327                 }
1328
1329                 if (state->request.flags & WBFLAG_PAM_INFO3_TEXT) {
1330                         result = append_info3_as_txt(state->mem_ctx, state, info3);
1331                         if (!NT_STATUS_IS_OK(result)) {
1332                                 DEBUG(10,("Failed to append INFO3 (TXT): %s\n", nt_errstr(result)));
1333                                 goto done;
1334                         }
1335
1336                 }
1337
1338                 if ((state->request.flags & WBFLAG_PAM_CACHED_LOGIN)) {
1339
1340                         /* Store in-memory creds for single-signon using ntlm_auth. */
1341                         result = winbindd_add_memory_creds(state->request.data.auth.user,
1342                                                         get_uid_from_state(state),
1343                                                         state->request.data.auth.pass);
1344
1345                         if (!NT_STATUS_IS_OK(result)) {
1346                                 DEBUG(10,("Failed to store memory creds: %s\n", nt_errstr(result)));
1347                                 goto done;
1348                         }
1349
1350                         if (lp_winbind_offline_logon()) {
1351                                 result = winbindd_store_creds(domain,
1352                                                       state->mem_ctx,
1353                                                       state->request.data.auth.user,
1354                                                       state->request.data.auth.pass,
1355                                                       info3, NULL);
1356                                 if (!NT_STATUS_IS_OK(result)) {
1357
1358                                         /* Release refcount. */
1359                                         winbindd_delete_memory_creds(state->request.data.auth.user);
1360
1361                                         DEBUG(10,("Failed to store creds: %s\n", nt_errstr(result)));
1362                                         goto done;
1363                                 }
1364                         }
1365                 }
1366
1367                 result = fillup_password_policy(domain, state);
1368
1369                 if (!NT_STATUS_IS_OK(result)) {
1370                         DEBUG(10,("Failed to get password policies: %s\n", nt_errstr(result)));
1371                         goto done;
1372                 }
1373
1374                 if (state->request.flags & WBFLAG_PAM_UNIX_NAME) {
1375                         /* We've been asked to return the unix username, per 
1376                            'winbind use default domain' settings and the like */
1377
1378                         fstring username_out;
1379                         const char *nt_username, *nt_domain;
1380
1381                         if (!(nt_username = unistr2_tdup(state->mem_ctx, &info3->uni_user_name))) {
1382                                 /* If the server didn't give us one, just use the one we sent them */
1383                                 nt_username = name_user;
1384                         }
1385
1386                         if (!(nt_domain = unistr2_tdup(state->mem_ctx, &info3->uni_logon_dom))) {
1387                                 /* If the server didn't give us one, just use the one we sent them */
1388                                 nt_domain = name_domain;
1389                         }
1390
1391                         fill_domain_username(username_out, nt_domain, nt_username, True);
1392
1393                         DEBUG(5, ("Setting unix username to [%s]\n", username_out));
1394
1395                         SAFE_FREE(state->response.extra_data.data);
1396                         state->response.extra_data.data = SMB_STRDUP(username_out);
1397                         if (!state->response.extra_data.data) {
1398                                 result = NT_STATUS_NO_MEMORY;
1399                                 goto done;
1400                         }
1401                         state->response.length +=
1402                                 strlen((const char *)state->response.extra_data.data)+1;
1403                 }
1404         }
1405  
1406
1407 done:
1408         /* give us a more useful (more correct?) error code */
1409         if ((NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND) ||
1410             (NT_STATUS_EQUAL(result, NT_STATUS_UNSUCCESSFUL)))) {
1411                 result = NT_STATUS_NO_LOGON_SERVERS;
1412         }
1413         
1414         state->response.data.auth.nt_status = NT_STATUS_V(result);
1415         fstrcpy(state->response.data.auth.nt_status_string, nt_errstr(result));
1416
1417         /* we might have given a more useful error above */
1418         if (!*state->response.data.auth.error_string) 
1419                 fstrcpy(state->response.data.auth.error_string, get_friendly_nt_error_msg(result));
1420         state->response.data.auth.pam_error = nt_status_to_pam(result);
1421
1422         DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2, ("Plain-text authentication for user %s returned %s (PAM: %d)\n", 
1423               state->request.data.auth.user, 
1424               state->response.data.auth.nt_status_string,
1425               state->response.data.auth.pam_error));          
1426
1427         if ( NT_STATUS_IS_OK(result) && info3 &&
1428              (state->request.flags & WBFLAG_PAM_AFS_TOKEN) ) {
1429
1430                 char *afsname = talloc_strdup(state->mem_ctx,
1431                                               lp_afs_username_map());
1432                 char *cell;
1433
1434                 if (afsname == NULL) {
1435                         goto no_token;
1436                 }
1437
1438                 afsname = talloc_string_sub(state->mem_ctx,
1439                                             lp_afs_username_map(),
1440                                             "%D", name_domain);
1441                 afsname = talloc_string_sub(state->mem_ctx, afsname,
1442                                             "%u", name_user);
1443                 afsname = talloc_string_sub(state->mem_ctx, afsname,
1444                                             "%U", name_user);
1445
1446                 {
1447                         DOM_SID user_sid;
1448                         fstring sidstr;
1449
1450                         sid_copy(&user_sid, &info3->dom_sid.sid);
1451                         sid_append_rid(&user_sid, info3->user_rid);
1452                         sid_to_string(sidstr, &user_sid);
1453                         afsname = talloc_string_sub(state->mem_ctx, afsname,
1454                                                     "%s", sidstr);
1455                 }
1456
1457                 if (afsname == NULL) {
1458                         goto no_token;
1459                 }
1460
1461                 strlower_m(afsname);
1462
1463                 DEBUG(10, ("Generating token for user %s\n", afsname));
1464
1465                 cell = strchr(afsname, '@');
1466
1467                 if (cell == NULL) {
1468                         goto no_token;
1469                 }
1470
1471                 *cell = '\0';
1472                 cell += 1;
1473
1474                 /* Append an AFS token string */
1475                 SAFE_FREE(state->response.extra_data.data);
1476                 state->response.extra_data.data =
1477                         afs_createtoken_str(afsname, cell);
1478
1479                 if (state->response.extra_data.data != NULL) {
1480                         state->response.length +=
1481                                 strlen((const char *)state->response.extra_data.data)+1;
1482                 }
1483
1484         no_token:
1485                 TALLOC_FREE(afsname);
1486         }
1487         
1488         return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
1489 }
1490
1491
1492 /**********************************************************************
1493  Challenge Response Authentication Protocol 
1494 **********************************************************************/
1495
1496 void winbindd_pam_auth_crap(struct winbindd_cli_state *state)
1497 {
1498         struct winbindd_domain *domain = NULL;
1499         const char *domain_name = NULL;
1500         NTSTATUS result;
1501
1502         if (!state->privileged) {
1503                 char *error_string = NULL;
1504                 DEBUG(2, ("winbindd_pam_auth_crap: non-privileged access "
1505                           "denied.  !\n"));
1506                 DEBUGADD(2, ("winbindd_pam_auth_crap: Ensure permissions "
1507                              "on %s are set correctly.\n",
1508                              get_winbind_priv_pipe_dir()));
1509                 /* send a better message than ACCESS_DENIED */
1510                 error_string = talloc_asprintf(state->mem_ctx,
1511                                                "winbind client not authorized "
1512                                                "to use winbindd_pam_auth_crap."
1513                                                " Ensure permissions on %s "
1514                                                "are set correctly.",
1515                                                get_winbind_priv_pipe_dir());
1516                 fstrcpy(state->response.data.auth.error_string, error_string);
1517                 result = NT_STATUS_ACCESS_DENIED;
1518                 goto done;
1519         }
1520
1521         /* Ensure null termination */
1522         state->request.data.auth_crap.user
1523                 [sizeof(state->request.data.auth_crap.user)-1]=0;
1524         state->request.data.auth_crap.domain
1525                 [sizeof(state->request.data.auth_crap.domain)-1]=0;
1526
1527         DEBUG(3, ("[%5lu]: pam auth crap domain: [%s] user: %s\n",
1528                   (unsigned long)state->pid,
1529                   state->request.data.auth_crap.domain,
1530                   state->request.data.auth_crap.user));
1531
1532         if (*state->request.data.auth_crap.domain != '\0') {
1533                 domain_name = state->request.data.auth_crap.domain;
1534         } else if (lp_winbind_use_default_domain()) {
1535                 domain_name = lp_workgroup();
1536         }
1537
1538         if (domain_name != NULL)
1539                 domain = find_auth_domain(state, domain_name);
1540
1541         if (domain != NULL) {
1542                 sendto_domain(state, domain);
1543                 return;
1544         }
1545
1546         result = NT_STATUS_NO_SUCH_USER;
1547
1548  done:
1549         set_auth_errors(&state->response, result);
1550         DEBUG(5, ("CRAP authentication for %s\\%s returned %s (PAM: %d)\n",
1551                   state->request.data.auth_crap.domain,
1552                   state->request.data.auth_crap.user, 
1553                   state->response.data.auth.nt_status_string,
1554                   state->response.data.auth.pam_error));
1555         request_error(state);
1556         return;
1557 }
1558
1559
1560 enum winbindd_result winbindd_dual_pam_auth_crap(struct winbindd_domain *domain,
1561                                                  struct winbindd_cli_state *state) 
1562 {
1563         NTSTATUS result;
1564         NET_USER_INFO_3 info3;
1565         struct rpc_pipe_client *netlogon_pipe;
1566         const char *name_user = NULL;
1567         const char *name_domain = NULL;
1568         const char *workstation;
1569         struct winbindd_domain *contact_domain;
1570         int attempts = 0;
1571         BOOL retry;
1572
1573         DATA_BLOB lm_resp, nt_resp;
1574
1575         /* This is child-only, so no check for privileged access is needed
1576            anymore */
1577
1578         /* Ensure null termination */
1579         state->request.data.auth_crap.user[sizeof(state->request.data.auth_crap.user)-1]=0;
1580         state->request.data.auth_crap.domain[sizeof(state->request.data.auth_crap.domain)-1]=0;
1581
1582         name_user = state->request.data.auth_crap.user;
1583
1584         if (*state->request.data.auth_crap.domain) {
1585                 name_domain = state->request.data.auth_crap.domain;
1586         } else if (lp_winbind_use_default_domain()) {
1587                 name_domain = lp_workgroup();
1588         } else {
1589                 DEBUG(5,("no domain specified with username (%s) - failing auth\n", 
1590                          name_user));
1591                 result = NT_STATUS_NO_SUCH_USER;
1592                 goto done;
1593         }
1594
1595         DEBUG(3, ("[%5lu]: pam auth crap domain: %s user: %s\n", (unsigned long)state->pid,
1596                   name_domain, name_user));
1597            
1598         if (*state->request.data.auth_crap.workstation) {
1599                 workstation = state->request.data.auth_crap.workstation;
1600         } else {
1601                 workstation = global_myname();
1602         }
1603
1604         if (state->request.data.auth_crap.lm_resp_len > sizeof(state->request.data.auth_crap.lm_resp)
1605                 || state->request.data.auth_crap.nt_resp_len > sizeof(state->request.data.auth_crap.nt_resp)) {
1606                 DEBUG(0, ("winbindd_pam_auth_crap: invalid password length %u/%u\n", 
1607                           state->request.data.auth_crap.lm_resp_len, 
1608                           state->request.data.auth_crap.nt_resp_len));
1609                 result = NT_STATUS_INVALID_PARAMETER;
1610                 goto done;
1611         }
1612
1613         lm_resp = data_blob_talloc(state->mem_ctx, state->request.data.auth_crap.lm_resp,
1614                                         state->request.data.auth_crap.lm_resp_len);
1615         nt_resp = data_blob_talloc(state->mem_ctx, state->request.data.auth_crap.nt_resp,
1616                                         state->request.data.auth_crap.nt_resp_len);
1617
1618         /* what domain should we contact? */
1619         
1620         if ( IS_DC ) {
1621                 if (!(contact_domain = find_domain_from_name(name_domain))) {
1622                         DEBUG(3, ("Authentication for domain for [%s] -> [%s]\\[%s] failed as %s is not a trusted domain\n", 
1623                                   state->request.data.auth_crap.user, name_domain, name_user, name_domain)); 
1624                         result = NT_STATUS_NO_SUCH_USER;
1625                         goto done;
1626                 }
1627         } else {
1628                 if (is_myname(name_domain)) {
1629                         DEBUG(3, ("Authentication for domain %s (local domain to this server) not supported at this stage\n", name_domain));
1630                         result =  NT_STATUS_NO_SUCH_USER;
1631                         goto done;
1632                 }
1633                 contact_domain = find_our_domain();
1634         }
1635
1636         do {
1637                 ZERO_STRUCT(info3);
1638                 retry = False;
1639
1640                 netlogon_pipe = NULL;
1641                 result = cm_connect_netlogon(contact_domain, &netlogon_pipe);
1642
1643                 if (!NT_STATUS_IS_OK(result)) {
1644                         DEBUG(3, ("could not open handle to NETLOGON pipe (error: %s)\n",
1645                                   nt_errstr(result)));
1646                         goto done;
1647                 }
1648
1649                 result = rpccli_netlogon_sam_network_logon(netlogon_pipe,
1650                                                            state->mem_ctx,
1651                                                            state->request.data.auth_crap.logon_parameters,
1652                                                            contact_domain->dcname,
1653                                                            name_user,
1654                                                            name_domain, 
1655                                                                         /* Bug #3248 - found by Stefan Burkei. */
1656                                                            workstation, /* We carefully set this above so use it... */
1657                                                            state->request.data.auth_crap.chal,
1658                                                            lm_resp,
1659                                                            nt_resp,
1660                                                            &info3);
1661
1662                 attempts += 1;
1663
1664                 /* We have to try a second time as cm_connect_netlogon
1665                    might not yet have noticed that the DC has killed
1666                    our connection. */
1667
1668                 if (NT_STATUS_EQUAL(result, NT_STATUS_UNSUCCESSFUL)) {
1669                         retry = True;
1670                         continue;
1671                 }
1672
1673                 /* if we get access denied, a possible cause was that we had and open
1674                    connection to the DC, but someone changed our machine account password
1675                    out from underneath us using 'net rpc changetrustpw' */
1676                    
1677                 if ( NT_STATUS_EQUAL(result, NT_STATUS_ACCESS_DENIED) ) {
1678                         DEBUG(3,("winbindd_pam_auth: sam_logon returned "
1679                                  "ACCESS_DENIED.  Maybe the trust account "
1680                                 "password was changed and we didn't know it. "
1681                                  "Killing connections to domain %s\n",
1682                                 name_domain));
1683                         invalidate_cm_connection(&contact_domain->conn);
1684                         retry = True;
1685                 } 
1686
1687         } while ( (attempts < 2) && retry );
1688
1689         if (NT_STATUS_IS_OK(result)) {
1690
1691                 netsamlogon_cache_store(name_user, &info3);
1692                 wcache_invalidate_samlogon(find_domain_from_name(name_domain), &info3);
1693
1694                 /* Check if the user is in the right group */
1695
1696                 if (!NT_STATUS_IS_OK(result = check_info3_in_group(state->mem_ctx, &info3,
1697                                                         state->request.data.auth_crap.require_membership_of_sid))) {
1698                         DEBUG(3, ("User %s is not in the required group (%s), so plaintext authentication is rejected\n",
1699                                   state->request.data.auth_crap.user, 
1700                                   state->request.data.auth_crap.require_membership_of_sid));
1701                         goto done;
1702                 }
1703
1704                 if (state->request.flags & WBFLAG_PAM_INFO3_NDR) {
1705                         result = append_info3_as_ndr(state->mem_ctx, state, &info3);
1706                 } else if (state->request.flags & WBFLAG_PAM_UNIX_NAME) {
1707                         /* ntlm_auth should return the unix username, per 
1708                            'winbind use default domain' settings and the like */
1709
1710                         fstring username_out;
1711                         const char *nt_username, *nt_domain;
1712                         if (!(nt_username = unistr2_tdup(state->mem_ctx, &(info3.uni_user_name)))) {
1713                                 /* If the server didn't give us one, just use the one we sent them */
1714                                 nt_username = name_user;
1715                         }
1716
1717                         if (!(nt_domain = unistr2_tdup(state->mem_ctx, &(info3.uni_logon_dom)))) {
1718                                 /* If the server didn't give us one, just use the one we sent them */
1719                                 nt_domain = name_domain;
1720                         }
1721
1722                         fill_domain_username(username_out, nt_domain, nt_username, True);
1723
1724                         DEBUG(5, ("Setting unix username to [%s]\n", username_out));
1725
1726                         SAFE_FREE(state->response.extra_data.data);
1727                         state->response.extra_data.data = SMB_STRDUP(username_out);
1728                         if (!state->response.extra_data.data) {
1729                                 result = NT_STATUS_NO_MEMORY;
1730                                 goto done;
1731                         }
1732                         state->response.length +=
1733                                 strlen((const char *)state->response.extra_data.data)+1;
1734                 }
1735                 
1736                 if (state->request.flags & WBFLAG_PAM_USER_SESSION_KEY) {
1737                         memcpy(state->response.data.auth.user_session_key, info3.user_sess_key,
1738                                         sizeof(state->response.data.auth.user_session_key) /* 16 */);
1739                 }
1740                 if (state->request.flags & WBFLAG_PAM_LMKEY) {
1741                         memcpy(state->response.data.auth.first_8_lm_hash, info3.lm_sess_key,
1742                                         sizeof(state->response.data.auth.first_8_lm_hash) /* 8 */);
1743                 }
1744         }
1745
1746 done:
1747
1748         /* give us a more useful (more correct?) error code */
1749         if ((NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND) ||
1750             (NT_STATUS_EQUAL(result, NT_STATUS_UNSUCCESSFUL)))) {
1751                 result = NT_STATUS_NO_LOGON_SERVERS;
1752         }
1753
1754         if (state->request.flags & WBFLAG_PAM_NT_STATUS_SQUASH) {
1755                 result = nt_status_squash(result);
1756         }
1757
1758         state->response.data.auth.nt_status = NT_STATUS_V(result);
1759         fstrcpy(state->response.data.auth.nt_status_string, nt_errstr(result));
1760
1761         /* we might have given a more useful error above */
1762         if (!*state->response.data.auth.error_string) {
1763                 fstrcpy(state->response.data.auth.error_string, get_friendly_nt_error_msg(result));
1764         }
1765         state->response.data.auth.pam_error = nt_status_to_pam(result);
1766
1767         DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2, 
1768               ("NTLM CRAP authentication for user [%s]\\[%s] returned %s (PAM: %d)\n", 
1769                name_domain,
1770                name_user,
1771                state->response.data.auth.nt_status_string,
1772                state->response.data.auth.pam_error));         
1773
1774         return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
1775 }
1776
1777 /* Change a user password */
1778
1779 void winbindd_pam_chauthtok(struct winbindd_cli_state *state)
1780 {
1781         NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
1782         char *oldpass;
1783         char *newpass = NULL;
1784         fstring domain, user;
1785         POLICY_HND dom_pol;
1786         struct winbindd_domain *contact_domain;
1787         struct rpc_pipe_client *cli;
1788         BOOL got_info = False;
1789         SAM_UNK_INFO_1 info;
1790         SAMR_CHANGE_REJECT reject;
1791
1792         DEBUG(3, ("[%5lu]: pam chauthtok %s\n", (unsigned long)state->pid,
1793                 state->request.data.chauthtok.user));
1794
1795         /* Setup crap */
1796
1797         parse_domain_user(state->request.data.chauthtok.user, domain, user);
1798
1799         contact_domain = find_domain_from_name(domain);
1800         if (!contact_domain) {
1801                 DEBUG(3, ("Cannot change password for [%s] -> [%s]\\[%s] as %s is not a trusted domain\n", 
1802                           state->request.data.chauthtok.user, domain, user, domain)); 
1803                 result = NT_STATUS_NO_SUCH_USER;
1804                 goto done;
1805         }
1806
1807         /* Change password */
1808
1809         oldpass = state->request.data.chauthtok.oldpass;
1810         newpass = state->request.data.chauthtok.newpass;
1811
1812         /* Initialize reject reason */
1813         state->response.data.auth.reject_reason = Undefined;
1814
1815         /* Get sam handle */
1816
1817         result = cm_connect_sam(contact_domain, state->mem_ctx, &cli,
1818                                 &dom_pol);
1819         if (!NT_STATUS_IS_OK(result)) {
1820                 DEBUG(1, ("could not get SAM handle on DC for %s\n", domain));
1821                 goto done;
1822         }
1823
1824         result = rpccli_samr_chgpasswd3(cli, state->mem_ctx, user, newpass, oldpass, &info, &reject);
1825
1826         /* FIXME: need to check for other error codes ? */
1827         if (NT_STATUS_EQUAL(result, NT_STATUS_PASSWORD_RESTRICTION)) {
1828
1829                 state->response.data.auth.policy.min_length_password = 
1830                         info.min_length_password;
1831                 state->response.data.auth.policy.password_history = 
1832                         info.password_history;
1833                 state->response.data.auth.policy.password_properties = 
1834                         info.password_properties;
1835                 state->response.data.auth.policy.expire = 
1836                         nt_time_to_unix_abs(&info.expire);
1837                 state->response.data.auth.policy.min_passwordage = 
1838                         nt_time_to_unix_abs(&info.min_passwordage);
1839
1840                 state->response.data.auth.reject_reason = 
1841                         reject.reject_reason;
1842
1843                 got_info = True;
1844
1845         /* only fallback when the chgpasswd3 call is not supported */
1846         } else if ((NT_STATUS_EQUAL(result, NT_STATUS(DCERPC_FAULT_OP_RNG_ERROR))) ||
1847                    (NT_STATUS_EQUAL(result, NT_STATUS_NOT_SUPPORTED)) ||
1848                    (NT_STATUS_EQUAL(result, NT_STATUS_NOT_IMPLEMENTED))) {
1849
1850                 DEBUG(10,("Password change with chgpasswd3 failed with: %s, retrying chgpasswd_user\n", 
1851                         nt_errstr(result)));
1852                 
1853                 result = rpccli_samr_chgpasswd_user(cli, state->mem_ctx, user, newpass, oldpass);
1854         }
1855
1856 done: 
1857
1858         if (NT_STATUS_IS_OK(result) && (state->request.flags & WBFLAG_PAM_CACHED_LOGIN)) {
1859                 
1860                 /* Update the single sign-on memory creds. */
1861                 result = winbindd_replace_memory_creds(state->request.data.chauthtok.user,
1862                                                         newpass);
1863
1864                 if (!NT_STATUS_IS_OK(result)) {
1865                         DEBUG(10,("Failed to replace memory creds: %s\n", nt_errstr(result)));
1866                         goto process_result;
1867                 }
1868
1869                 if (lp_winbind_offline_logon()) {
1870                         result = winbindd_update_creds_by_name(contact_domain,
1871                                                          state->mem_ctx, user,
1872                                                          newpass);
1873                         if (!NT_STATUS_IS_OK(result)) {
1874                                 DEBUG(10,("Failed to store creds: %s\n", nt_errstr(result)));
1875                                 goto process_result;
1876                         }
1877                 }
1878         }               
1879
1880         if (!NT_STATUS_IS_OK(result) && !got_info && contact_domain) {
1881
1882                 NTSTATUS policy_ret;
1883                 
1884                 policy_ret = fillup_password_policy(contact_domain, state);
1885
1886                 /* failure of this is non critical, it will just provide no
1887                  * additional information to the client why the change has
1888                  * failed - Guenther */
1889
1890                 if (!NT_STATUS_IS_OK(policy_ret)) {
1891                         DEBUG(10,("Failed to get password policies: %s\n", nt_errstr(policy_ret)));
1892                         goto process_result;
1893                 }
1894         }
1895
1896 process_result:
1897
1898         state->response.data.auth.nt_status = NT_STATUS_V(result);
1899         fstrcpy(state->response.data.auth.nt_status_string, nt_errstr(result));
1900         fstrcpy(state->response.data.auth.error_string, get_friendly_nt_error_msg(result));
1901         state->response.data.auth.pam_error = nt_status_to_pam(result);
1902
1903         DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2, 
1904               ("Password change for user [%s]\\[%s] returned %s (PAM: %d)\n", 
1905                domain,
1906                user,
1907                state->response.data.auth.nt_status_string,
1908                state->response.data.auth.pam_error));         
1909
1910         if (NT_STATUS_IS_OK(result)) {
1911                 request_ok(state);
1912         } else {
1913                 request_error(state);
1914         }
1915 }
1916
1917 void winbindd_pam_logoff(struct winbindd_cli_state *state)
1918 {
1919         struct winbindd_domain *domain;
1920         fstring name_domain, user;
1921         
1922         DEBUG(3, ("[%5lu]: pam logoff %s\n", (unsigned long)state->pid,
1923                 state->request.data.logoff.user));
1924
1925         /* Ensure null termination */
1926         state->request.data.logoff.user
1927                 [sizeof(state->request.data.logoff.user)-1]='\0';
1928
1929         state->request.data.logoff.krb5ccname
1930                 [sizeof(state->request.data.logoff.krb5ccname)-1]='\0';
1931
1932         if (!parse_domain_user(state->request.data.logoff.user, name_domain, user)) {
1933                 goto failed;
1934         }
1935
1936         if ((domain = find_auth_domain(state, name_domain)) == NULL) {
1937                 goto failed;
1938         }
1939
1940         sendto_domain(state, domain);
1941         return;
1942
1943  failed:
1944         set_auth_errors(&state->response, NT_STATUS_NO_SUCH_USER);
1945         DEBUG(5, ("Pam Logoff for %s returned %s "
1946                   "(PAM: %d)\n",
1947                   state->request.data.auth.user, 
1948                   state->response.data.auth.nt_status_string,
1949                   state->response.data.auth.pam_error));
1950         request_error(state);
1951         return;
1952 }
1953
1954 enum winbindd_result winbindd_dual_pam_logoff(struct winbindd_domain *domain,
1955                                               struct winbindd_cli_state *state) 
1956 {
1957         NTSTATUS result = NT_STATUS_NOT_SUPPORTED;
1958 #ifdef HAVE_KRB5
1959         int ret;
1960 #endif
1961
1962         DEBUG(3, ("[%5lu]: pam dual logoff %s\n", (unsigned long)state->pid,
1963                 state->request.data.logoff.user));
1964
1965         if (!(state->request.flags & WBFLAG_PAM_KRB5)) {
1966                 result = NT_STATUS_OK;
1967                 goto process_result;
1968         }
1969
1970         if (state->request.data.logoff.krb5ccname[0] == '\0') {
1971                 result = NT_STATUS_OK;
1972                 goto process_result;
1973         }
1974
1975 #ifdef HAVE_KRB5
1976         
1977         if (state->request.data.logoff.uid < 0) {
1978                 DEBUG(0,("winbindd_pam_logoff: invalid uid\n"));
1979                 goto process_result;
1980         }
1981
1982         /* what we need here is to find the corresponding krb5 ccache name *we*
1983          * created for a given username and destroy it (as the user who created it) */
1984         
1985         if (!ccache_entry_identical(state->request.data.logoff.user, 
1986                                         state->request.data.logoff.uid,
1987                                         state->request.data.logoff.krb5ccname)) {
1988                 DEBUG(0,("winbindd_pam_logoff: cached entry differs.\n"));
1989                 goto process_result;
1990         }
1991
1992         ret = ads_kdestroy(state->request.data.logoff.krb5ccname);
1993
1994         if (ret) {
1995                 DEBUG(0,("winbindd_pam_logoff: failed to destroy user ccache %s with: %s\n", 
1996                         state->request.data.logoff.krb5ccname, error_message(ret)));
1997         } else {
1998                 DEBUG(10,("winbindd_pam_logoff: successfully destroyed ccache %s for user %s\n", 
1999                         state->request.data.logoff.krb5ccname, state->request.data.logoff.user));
2000         }
2001
2002         remove_ccache(state->request.data.logoff.user);
2003
2004         result = krb5_to_nt_status(ret);
2005 #else
2006         result = NT_STATUS_NOT_SUPPORTED;
2007 #endif
2008
2009 process_result:
2010
2011         winbindd_delete_memory_creds(state->request.data.logoff.user);
2012
2013         state->response.data.auth.nt_status = NT_STATUS_V(result);
2014         fstrcpy(state->response.data.auth.nt_status_string, nt_errstr(result));
2015         fstrcpy(state->response.data.auth.error_string, get_friendly_nt_error_msg(result));
2016         state->response.data.auth.pam_error = nt_status_to_pam(result);
2017
2018         return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
2019 }
2020
2021 /* Change user password with auth crap*/
2022
2023 void winbindd_pam_chng_pswd_auth_crap(struct winbindd_cli_state *state)
2024 {
2025         struct winbindd_domain *domain = NULL;
2026         const char *domain_name = NULL;
2027
2028         /* Ensure null termination */
2029         state->request.data.chng_pswd_auth_crap.user[
2030                 sizeof(state->request.data.chng_pswd_auth_crap.user)-1]=0;
2031         state->request.data.chng_pswd_auth_crap.domain[
2032                 sizeof(state->request.data.chng_pswd_auth_crap.domain)-1]=0;
2033         
2034         DEBUG(3, ("[%5lu]: pam change pswd auth crap domain: %s user: %s\n",
2035                   (unsigned long)state->pid,
2036                   state->request.data.chng_pswd_auth_crap.domain,
2037                   state->request.data.chng_pswd_auth_crap.user));
2038         
2039         if (*state->request.data.chng_pswd_auth_crap.domain != '\0') {
2040                 domain_name = state->request.data.chng_pswd_auth_crap.domain;
2041         } else if (lp_winbind_use_default_domain()) {
2042                 domain_name = lp_workgroup();
2043         }
2044
2045         if (domain_name != NULL)
2046                 domain = find_domain_from_name(domain_name);
2047
2048         if (domain != NULL) {
2049                 DEBUG(7, ("[%5lu]: pam auth crap changing pswd in domain: "
2050                           "%s\n", (unsigned long)state->pid,domain->name));
2051                 sendto_domain(state, domain);
2052                 return;
2053         }
2054
2055         set_auth_errors(&state->response, NT_STATUS_NO_SUCH_USER);
2056         DEBUG(5, ("CRAP change password  for %s\\%s returned %s (PAM: %d)\n",
2057                   state->request.data.chng_pswd_auth_crap.domain,
2058                   state->request.data.chng_pswd_auth_crap.user, 
2059                   state->response.data.auth.nt_status_string,
2060                   state->response.data.auth.pam_error));
2061         request_error(state);
2062         return;
2063 }
2064
2065 enum winbindd_result winbindd_dual_pam_chng_pswd_auth_crap(struct winbindd_domain *domainSt, struct winbindd_cli_state *state)
2066 {
2067         NTSTATUS result;
2068         DATA_BLOB new_nt_password;
2069         DATA_BLOB old_nt_hash_enc;
2070         DATA_BLOB new_lm_password;
2071         DATA_BLOB old_lm_hash_enc;
2072         fstring  domain,user;
2073         POLICY_HND dom_pol;
2074         struct winbindd_domain *contact_domain = domainSt;
2075         struct rpc_pipe_client *cli;
2076
2077         /* Ensure null termination */
2078         state->request.data.chng_pswd_auth_crap.user[
2079                 sizeof(state->request.data.chng_pswd_auth_crap.user)-1]=0;
2080         state->request.data.chng_pswd_auth_crap.domain[
2081                 sizeof(state->request.data.chng_pswd_auth_crap.domain)-1]=0;
2082         *domain = 0;
2083         *user = 0;
2084         
2085         DEBUG(3, ("[%5lu]: pam change pswd auth crap domain: %s user: %s\n",
2086                   (unsigned long)state->pid,
2087                   state->request.data.chng_pswd_auth_crap.domain,
2088                   state->request.data.chng_pswd_auth_crap.user));
2089         
2090         if (*state->request.data.chng_pswd_auth_crap.domain) {
2091                 fstrcpy(domain,state->request.data.chng_pswd_auth_crap.domain);
2092         } else {
2093                 parse_domain_user(state->request.data.chng_pswd_auth_crap.user,
2094                                   domain, user);
2095
2096                 if(!*domain) {
2097                         DEBUG(3,("no domain specified with username (%s) - "
2098                                  "failing auth\n",
2099                                  state->request.data.chng_pswd_auth_crap.user));
2100                         result = NT_STATUS_NO_SUCH_USER;
2101                         goto done;
2102                 }
2103         }
2104
2105         if (!*domain && lp_winbind_use_default_domain()) {
2106                 fstrcpy(domain,(char *)lp_workgroup());
2107         }
2108
2109         if(!*user) {
2110                 fstrcpy(user, state->request.data.chng_pswd_auth_crap.user);
2111         }
2112
2113         DEBUG(3, ("[%5lu]: pam auth crap domain: %s user: %s\n",
2114                   (unsigned long)state->pid, domain, user));
2115         
2116         /* Change password */
2117         new_nt_password = data_blob_talloc(
2118                 state->mem_ctx,
2119                 state->request.data.chng_pswd_auth_crap.new_nt_pswd,
2120                 state->request.data.chng_pswd_auth_crap.new_nt_pswd_len);
2121
2122         old_nt_hash_enc = data_blob_talloc(
2123                 state->mem_ctx,
2124                 state->request.data.chng_pswd_auth_crap.old_nt_hash_enc,
2125                 state->request.data.chng_pswd_auth_crap.old_nt_hash_enc_len);
2126
2127         if(state->request.data.chng_pswd_auth_crap.new_lm_pswd_len > 0) {
2128                 new_lm_password = data_blob_talloc(
2129                         state->mem_ctx,
2130                         state->request.data.chng_pswd_auth_crap.new_lm_pswd,
2131                         state->request.data.chng_pswd_auth_crap.new_lm_pswd_len);
2132
2133                 old_lm_hash_enc = data_blob_talloc(
2134                         state->mem_ctx,
2135                         state->request.data.chng_pswd_auth_crap.old_lm_hash_enc,
2136                         state->request.data.chng_pswd_auth_crap.old_lm_hash_enc_len);
2137         } else {
2138                 new_lm_password.length = 0;
2139                 old_lm_hash_enc.length = 0;
2140         }
2141
2142         /* Get sam handle */
2143
2144         result = cm_connect_sam(contact_domain, state->mem_ctx, &cli, &dom_pol);
2145         if (!NT_STATUS_IS_OK(result)) {
2146                 DEBUG(1, ("could not get SAM handle on DC for %s\n", domain));
2147                 goto done;
2148         }
2149
2150         result = rpccli_samr_chng_pswd_auth_crap(
2151                 cli, state->mem_ctx, user, new_nt_password, old_nt_hash_enc,
2152                 new_lm_password, old_lm_hash_enc);
2153
2154  done:    
2155         state->response.data.auth.nt_status = NT_STATUS_V(result);
2156         fstrcpy(state->response.data.auth.nt_status_string, nt_errstr(result));
2157         fstrcpy(state->response.data.auth.error_string,
2158                 get_friendly_nt_error_msg(result));
2159         state->response.data.auth.pam_error = nt_status_to_pam(result);
2160
2161         DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2, 
2162               ("Password change for user [%s]\\[%s] returned %s (PAM: %d)\n", 
2163                domain, user,
2164                state->response.data.auth.nt_status_string,
2165                state->response.data.auth.pam_error));         
2166
2167         return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
2168 }