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