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