66ac61e5c9db96a54290681830b819a09f45d82b
[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                         DEBUG(10,("Failed to get password policies: %s\n", nt_errstr(result)));
1539                         goto done;
1540                 }
1541
1542                 if (state->request.flags & WBFLAG_PAM_UNIX_NAME) {
1543                         /* We've been asked to return the unix username, per 
1544                            'winbind use default domain' settings and the like */
1545
1546                         fstring username_out;
1547                         const char *nt_username, *nt_domain;
1548
1549                         if (!(nt_username = unistr2_tdup(state->mem_ctx, &info3->uni_user_name))) {
1550                                 /* If the server didn't give us one, just use the one we sent them */
1551                                 nt_username = name_user;
1552                         }
1553
1554                         if (!(nt_domain = unistr2_tdup(state->mem_ctx, &info3->uni_logon_dom))) {
1555                                 /* If the server didn't give us one, just use the one we sent them */
1556                                 nt_domain = name_domain;
1557                         }
1558
1559                         fill_domain_username(username_out, nt_domain, nt_username, True);
1560
1561                         DEBUG(5, ("Setting unix username to [%s]\n", username_out));
1562
1563                         SAFE_FREE(state->response.extra_data.data);
1564                         state->response.extra_data.data = SMB_STRDUP(username_out);
1565                         if (!state->response.extra_data.data) {
1566                                 result = NT_STATUS_NO_MEMORY;
1567                                 goto done;
1568                         }
1569                         state->response.length +=
1570                                 strlen((const char *)state->response.extra_data.data)+1;
1571                 }
1572         }
1573  
1574
1575 done:
1576         /* give us a more useful (more correct?) error code */
1577         if ((NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND) ||
1578             (NT_STATUS_EQUAL(result, NT_STATUS_UNSUCCESSFUL)))) {
1579                 result = NT_STATUS_NO_LOGON_SERVERS;
1580         }
1581         
1582         state->response.data.auth.nt_status = NT_STATUS_V(result);
1583         fstrcpy(state->response.data.auth.nt_status_string, nt_errstr(result));
1584
1585         /* we might have given a more useful error above */
1586         if (!*state->response.data.auth.error_string) 
1587                 fstrcpy(state->response.data.auth.error_string, get_friendly_nt_error_msg(result));
1588         state->response.data.auth.pam_error = nt_status_to_pam(result);
1589
1590         DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2, ("Plain-text authentication for user %s returned %s (PAM: %d)\n", 
1591               state->request.data.auth.user, 
1592               state->response.data.auth.nt_status_string,
1593               state->response.data.auth.pam_error));          
1594
1595         if ( NT_STATUS_IS_OK(result) && info3 &&
1596              (state->request.flags & WBFLAG_PAM_AFS_TOKEN) ) {
1597
1598                 char *afsname = talloc_strdup(state->mem_ctx,
1599                                               lp_afs_username_map());
1600                 char *cell;
1601
1602                 if (afsname == NULL) {
1603                         goto no_token;
1604                 }
1605
1606                 afsname = talloc_string_sub(state->mem_ctx,
1607                                             lp_afs_username_map(),
1608                                             "%D", name_domain);
1609                 afsname = talloc_string_sub(state->mem_ctx, afsname,
1610                                             "%u", name_user);
1611                 afsname = talloc_string_sub(state->mem_ctx, afsname,
1612                                             "%U", name_user);
1613
1614                 {
1615                         DOM_SID user_sid;
1616                         fstring sidstr;
1617
1618                         sid_copy(&user_sid, &info3->dom_sid.sid);
1619                         sid_append_rid(&user_sid, info3->user_rid);
1620                         sid_to_string(sidstr, &user_sid);
1621                         afsname = talloc_string_sub(state->mem_ctx, afsname,
1622                                                     "%s", sidstr);
1623                 }
1624
1625                 if (afsname == NULL) {
1626                         goto no_token;
1627                 }
1628
1629                 strlower_m(afsname);
1630
1631                 DEBUG(10, ("Generating token for user %s\n", afsname));
1632
1633                 cell = strchr(afsname, '@');
1634
1635                 if (cell == NULL) {
1636                         goto no_token;
1637                 }
1638
1639                 *cell = '\0';
1640                 cell += 1;
1641
1642                 /* Append an AFS token string */
1643                 SAFE_FREE(state->response.extra_data.data);
1644                 state->response.extra_data.data =
1645                         afs_createtoken_str(afsname, cell);
1646
1647                 if (state->response.extra_data.data != NULL) {
1648                         state->response.length +=
1649                                 strlen((const char *)state->response.extra_data.data)+1;
1650                 }
1651
1652         no_token:
1653                 TALLOC_FREE(afsname);
1654         }
1655         
1656         return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
1657 }
1658
1659
1660 /**********************************************************************
1661  Challenge Response Authentication Protocol 
1662 **********************************************************************/
1663
1664 void winbindd_pam_auth_crap(struct winbindd_cli_state *state)
1665 {
1666         struct winbindd_domain *domain = NULL;
1667         const char *domain_name = NULL;
1668         NTSTATUS result;
1669
1670         if (!state->privileged) {
1671                 char *error_string = NULL;
1672                 DEBUG(2, ("winbindd_pam_auth_crap: non-privileged access "
1673                           "denied.  !\n"));
1674                 DEBUGADD(2, ("winbindd_pam_auth_crap: Ensure permissions "
1675                              "on %s are set correctly.\n",
1676                              get_winbind_priv_pipe_dir()));
1677                 /* send a better message than ACCESS_DENIED */
1678                 error_string = talloc_asprintf(state->mem_ctx,
1679                                                "winbind client not authorized "
1680                                                "to use winbindd_pam_auth_crap."
1681                                                " Ensure permissions on %s "
1682                                                "are set correctly.",
1683                                                get_winbind_priv_pipe_dir());
1684                 fstrcpy(state->response.data.auth.error_string, error_string);
1685                 result = NT_STATUS_ACCESS_DENIED;
1686                 goto done;
1687         }
1688
1689         /* Ensure null termination */
1690         state->request.data.auth_crap.user
1691                 [sizeof(state->request.data.auth_crap.user)-1]=0;
1692         state->request.data.auth_crap.domain
1693                 [sizeof(state->request.data.auth_crap.domain)-1]=0;
1694
1695         DEBUG(3, ("[%5lu]: pam auth crap domain: [%s] user: %s\n",
1696                   (unsigned long)state->pid,
1697                   state->request.data.auth_crap.domain,
1698                   state->request.data.auth_crap.user));
1699
1700         if (*state->request.data.auth_crap.domain != '\0') {
1701                 domain_name = state->request.data.auth_crap.domain;
1702         } else if (lp_winbind_use_default_domain()) {
1703                 domain_name = lp_workgroup();
1704         }
1705
1706         if (domain_name != NULL)
1707                 domain = find_auth_domain(state, domain_name);
1708
1709         if (domain != NULL) {
1710                 sendto_domain(state, domain);
1711                 return;
1712         }
1713
1714         result = NT_STATUS_NO_SUCH_USER;
1715
1716  done:
1717         set_auth_errors(&state->response, result);
1718         DEBUG(5, ("CRAP authentication for %s\\%s returned %s (PAM: %d)\n",
1719                   state->request.data.auth_crap.domain,
1720                   state->request.data.auth_crap.user, 
1721                   state->response.data.auth.nt_status_string,
1722                   state->response.data.auth.pam_error));
1723         request_error(state);
1724         return;
1725 }
1726
1727
1728 enum winbindd_result winbindd_dual_pam_auth_crap(struct winbindd_domain *domain,
1729                                                  struct winbindd_cli_state *state) 
1730 {
1731         NTSTATUS result;
1732         NET_USER_INFO_3 info3;
1733         struct rpc_pipe_client *netlogon_pipe;
1734         const char *name_user = NULL;
1735         const char *name_domain = NULL;
1736         const char *workstation;
1737         struct winbindd_domain *contact_domain;
1738         int attempts = 0;
1739         BOOL retry;
1740
1741         DATA_BLOB lm_resp, nt_resp;
1742
1743         /* This is child-only, so no check for privileged access is needed
1744            anymore */
1745
1746         /* Ensure null termination */
1747         state->request.data.auth_crap.user[sizeof(state->request.data.auth_crap.user)-1]=0;
1748         state->request.data.auth_crap.domain[sizeof(state->request.data.auth_crap.domain)-1]=0;
1749
1750         name_user = state->request.data.auth_crap.user;
1751
1752         if (*state->request.data.auth_crap.domain) {
1753                 name_domain = state->request.data.auth_crap.domain;
1754         } else if (lp_winbind_use_default_domain()) {
1755                 name_domain = lp_workgroup();
1756         } else {
1757                 DEBUG(5,("no domain specified with username (%s) - failing auth\n", 
1758                          name_user));
1759                 result = NT_STATUS_NO_SUCH_USER;
1760                 goto done;
1761         }
1762
1763         DEBUG(3, ("[%5lu]: pam auth crap domain: %s user: %s\n", (unsigned long)state->pid,
1764                   name_domain, name_user));
1765            
1766         if (*state->request.data.auth_crap.workstation) {
1767                 workstation = state->request.data.auth_crap.workstation;
1768         } else {
1769                 workstation = global_myname();
1770         }
1771
1772         if (state->request.data.auth_crap.lm_resp_len > sizeof(state->request.data.auth_crap.lm_resp)
1773                 || state->request.data.auth_crap.nt_resp_len > sizeof(state->request.data.auth_crap.nt_resp)) {
1774                 DEBUG(0, ("winbindd_pam_auth_crap: invalid password length %u/%u\n", 
1775                           state->request.data.auth_crap.lm_resp_len, 
1776                           state->request.data.auth_crap.nt_resp_len));
1777                 result = NT_STATUS_INVALID_PARAMETER;
1778                 goto done;
1779         }
1780
1781         lm_resp = data_blob_talloc(state->mem_ctx, state->request.data.auth_crap.lm_resp,
1782                                         state->request.data.auth_crap.lm_resp_len);
1783         nt_resp = data_blob_talloc(state->mem_ctx, state->request.data.auth_crap.nt_resp,
1784                                         state->request.data.auth_crap.nt_resp_len);
1785
1786         /* what domain should we contact? */
1787         
1788         if ( IS_DC ) {
1789                 if (!(contact_domain = find_domain_from_name(name_domain))) {
1790                         DEBUG(3, ("Authentication for domain for [%s] -> [%s]\\[%s] failed as %s is not a trusted domain\n", 
1791                                   state->request.data.auth_crap.user, name_domain, name_user, name_domain)); 
1792                         result = NT_STATUS_NO_SUCH_USER;
1793                         goto done;
1794                 }
1795         } else {
1796                 if (is_myname(name_domain)) {
1797                         DEBUG(3, ("Authentication for domain %s (local domain to this server) not supported at this stage\n", name_domain));
1798                         result =  NT_STATUS_NO_SUCH_USER;
1799                         goto done;
1800                 }
1801                 contact_domain = find_our_domain();
1802         }
1803
1804         do {
1805                 ZERO_STRUCT(info3);
1806                 retry = False;
1807
1808                 netlogon_pipe = NULL;
1809                 result = cm_connect_netlogon(contact_domain, &netlogon_pipe);
1810
1811                 if (!NT_STATUS_IS_OK(result)) {
1812                         DEBUG(3, ("could not open handle to NETLOGON pipe (error: %s)\n",
1813                                   nt_errstr(result)));
1814                         goto done;
1815                 }
1816
1817                 result = rpccli_netlogon_sam_network_logon(netlogon_pipe,
1818                                                            state->mem_ctx,
1819                                                            state->request.data.auth_crap.logon_parameters,
1820                                                            contact_domain->dcname,
1821                                                            name_user,
1822                                                            name_domain, 
1823                                                                         /* Bug #3248 - found by Stefan Burkei. */
1824                                                            workstation, /* We carefully set this above so use it... */
1825                                                            state->request.data.auth_crap.chal,
1826                                                            lm_resp,
1827                                                            nt_resp,
1828                                                            &info3);
1829
1830                 attempts += 1;
1831
1832                 /* We have to try a second time as cm_connect_netlogon
1833                    might not yet have noticed that the DC has killed
1834                    our connection. */
1835
1836                 if (NT_STATUS_EQUAL(result, NT_STATUS_UNSUCCESSFUL)) {
1837                         retry = True;
1838                         continue;
1839                 }
1840
1841                 /* if we get access denied, a possible cause was that we had and open
1842                    connection to the DC, but someone changed our machine account password
1843                    out from underneath us using 'net rpc changetrustpw' */
1844                    
1845                 if ( NT_STATUS_EQUAL(result, NT_STATUS_ACCESS_DENIED) ) {
1846                         DEBUG(3,("winbindd_pam_auth: sam_logon returned "
1847                                  "ACCESS_DENIED.  Maybe the trust account "
1848                                 "password was changed and we didn't know it. "
1849                                  "Killing connections to domain %s\n",
1850                                 name_domain));
1851                         invalidate_cm_connection(&contact_domain->conn);
1852                         retry = True;
1853                 } 
1854
1855         } while ( (attempts < 2) && retry );
1856
1857         if (NT_STATUS_IS_OK(result)) {
1858
1859                 netsamlogon_cache_store(name_user, &info3);
1860                 wcache_invalidate_samlogon(find_domain_from_name(name_domain), &info3);
1861
1862                 /* Check if the user is in the right group */
1863
1864                 if (!NT_STATUS_IS_OK(result = check_info3_in_group(state->mem_ctx, &info3,
1865                                                         state->request.data.auth_crap.require_membership_of_sid))) {
1866                         DEBUG(3, ("User %s is not in the required group (%s), so plaintext authentication is rejected\n",
1867                                   state->request.data.auth_crap.user, 
1868                                   state->request.data.auth_crap.require_membership_of_sid));
1869                         goto done;
1870                 }
1871
1872                 if (state->request.flags & WBFLAG_PAM_INFO3_NDR) {
1873                         result = append_info3_as_ndr(state->mem_ctx, state, &info3);
1874                 } else if (state->request.flags & WBFLAG_PAM_UNIX_NAME) {
1875                         /* ntlm_auth should return the unix username, per 
1876                            'winbind use default domain' settings and the like */
1877
1878                         fstring username_out;
1879                         const char *nt_username, *nt_domain;
1880                         if (!(nt_username = unistr2_tdup(state->mem_ctx, &(info3.uni_user_name)))) {
1881                                 /* If the server didn't give us one, just use the one we sent them */
1882                                 nt_username = name_user;
1883                         }
1884
1885                         if (!(nt_domain = unistr2_tdup(state->mem_ctx, &(info3.uni_logon_dom)))) {
1886                                 /* If the server didn't give us one, just use the one we sent them */
1887                                 nt_domain = name_domain;
1888                         }
1889
1890                         fill_domain_username(username_out, nt_domain, nt_username, True);
1891
1892                         DEBUG(5, ("Setting unix username to [%s]\n", username_out));
1893
1894                         SAFE_FREE(state->response.extra_data.data);
1895                         state->response.extra_data.data = SMB_STRDUP(username_out);
1896                         if (!state->response.extra_data.data) {
1897                                 result = NT_STATUS_NO_MEMORY;
1898                                 goto done;
1899                         }
1900                         state->response.length +=
1901                                 strlen((const char *)state->response.extra_data.data)+1;
1902                 }
1903                 
1904                 if (state->request.flags & WBFLAG_PAM_USER_SESSION_KEY) {
1905                         memcpy(state->response.data.auth.user_session_key, info3.user_sess_key,
1906                                         sizeof(state->response.data.auth.user_session_key) /* 16 */);
1907                 }
1908                 if (state->request.flags & WBFLAG_PAM_LMKEY) {
1909                         memcpy(state->response.data.auth.first_8_lm_hash, info3.lm_sess_key,
1910                                         sizeof(state->response.data.auth.first_8_lm_hash) /* 8 */);
1911                 }
1912         }
1913
1914 done:
1915
1916         /* give us a more useful (more correct?) error code */
1917         if ((NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND) ||
1918             (NT_STATUS_EQUAL(result, NT_STATUS_UNSUCCESSFUL)))) {
1919                 result = NT_STATUS_NO_LOGON_SERVERS;
1920         }
1921
1922         if (state->request.flags & WBFLAG_PAM_NT_STATUS_SQUASH) {
1923                 result = nt_status_squash(result);
1924         }
1925
1926         state->response.data.auth.nt_status = NT_STATUS_V(result);
1927         fstrcpy(state->response.data.auth.nt_status_string, nt_errstr(result));
1928
1929         /* we might have given a more useful error above */
1930         if (!*state->response.data.auth.error_string) {
1931                 fstrcpy(state->response.data.auth.error_string, get_friendly_nt_error_msg(result));
1932         }
1933         state->response.data.auth.pam_error = nt_status_to_pam(result);
1934
1935         DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2, 
1936               ("NTLM CRAP authentication for user [%s]\\[%s] returned %s (PAM: %d)\n", 
1937                name_domain,
1938                name_user,
1939                state->response.data.auth.nt_status_string,
1940                state->response.data.auth.pam_error));         
1941
1942         return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
1943 }
1944
1945 /* Change a user password */
1946
1947 void winbindd_pam_chauthtok(struct winbindd_cli_state *state)
1948 {
1949         fstring domain, user;
1950         struct winbindd_domain *contact_domain;
1951
1952         DEBUG(3, ("[%5lu]: pam chauthtok %s\n", (unsigned long)state->pid,
1953                 state->request.data.chauthtok.user));
1954
1955         /* Setup crap */
1956
1957         if (!canonicalize_username(state->request.data.chauthtok.user, domain, user)) {
1958                 set_auth_errors(&state->response, NT_STATUS_NO_SUCH_USER);
1959                 DEBUG(5, ("winbindd_pam_chauthtok: canonicalize_username %s failed with %s"
1960                           "(PAM: %d)\n",
1961                           state->request.data.auth.user, 
1962                           state->response.data.auth.nt_status_string,
1963                           state->response.data.auth.pam_error));
1964                 request_error(state);
1965                 return;
1966         }
1967
1968         contact_domain = find_domain_from_name(domain);
1969         if (!contact_domain) {
1970                 set_auth_errors(&state->response, NT_STATUS_NO_SUCH_USER);
1971                 DEBUG(3, ("Cannot change password for [%s] -> [%s]\\[%s] as %s is not a trusted domain\n", 
1972                           state->request.data.chauthtok.user, domain, user, domain)); 
1973                 request_error(state);
1974                 return;
1975         }
1976
1977         sendto_domain(state, contact_domain);
1978 }
1979
1980 enum winbindd_result winbindd_dual_pam_chauthtok(struct winbindd_domain *contact_domain,
1981                                                  struct winbindd_cli_state *state)
1982 {
1983         char *oldpass;
1984         char *newpass = NULL;
1985         POLICY_HND dom_pol;
1986         struct rpc_pipe_client *cli;
1987         BOOL got_info = False;
1988         SAM_UNK_INFO_1 info;
1989         SAMR_CHANGE_REJECT reject;
1990         NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
1991         fstring domain, user;
1992
1993         DEBUG(3, ("[%5lu]: dual pam chauthtok %s\n", (unsigned long)state->pid,
1994                   state->request.data.auth.user));
1995
1996         if (!parse_domain_user(state->request.data.chauthtok.user, domain, user)) {
1997                 goto done;
1998         }
1999
2000         /* Change password */
2001
2002         oldpass = state->request.data.chauthtok.oldpass;
2003         newpass = state->request.data.chauthtok.newpass;
2004
2005         /* Initialize reject reason */
2006         state->response.data.auth.reject_reason = Undefined;
2007
2008         /* Get sam handle */
2009
2010         result = cm_connect_sam(contact_domain, state->mem_ctx, &cli,
2011                                 &dom_pol);
2012         if (!NT_STATUS_IS_OK(result)) {
2013                 DEBUG(1, ("could not get SAM handle on DC for %s\n", domain));
2014                 goto done;
2015         }
2016
2017         result = rpccli_samr_chgpasswd3(cli, state->mem_ctx, user, newpass, oldpass, &info, &reject);
2018
2019         /* Windows 2003 returns NT_STATUS_PASSWORD_RESTRICTION */
2020
2021         if (NT_STATUS_EQUAL(result, NT_STATUS_PASSWORD_RESTRICTION) ) {
2022                 state->response.data.auth.policy.min_length_password = 
2023                         info.min_length_password;
2024                 state->response.data.auth.policy.password_history = 
2025                         info.password_history;
2026                 state->response.data.auth.policy.password_properties = 
2027                         info.password_properties;
2028                 state->response.data.auth.policy.expire = 
2029                         nt_time_to_unix_abs(&info.expire);
2030                 state->response.data.auth.policy.min_passwordage = 
2031                         nt_time_to_unix_abs(&info.min_passwordage);
2032
2033                 state->response.data.auth.reject_reason = 
2034                         reject.reject_reason;
2035
2036                 got_info = True;
2037         }
2038
2039         /* only fallback when the chgpasswd3 call is not supported */
2040         if ((NT_STATUS_EQUAL(result, NT_STATUS(DCERPC_FAULT_OP_RNG_ERROR))) ||
2041                    (NT_STATUS_EQUAL(result, NT_STATUS_NOT_SUPPORTED)) ||
2042                    (NT_STATUS_EQUAL(result, NT_STATUS_NOT_IMPLEMENTED))) {
2043
2044                 DEBUG(10,("Password change with chgpasswd3 failed with: %s, retrying chgpasswd_user\n", 
2045                         nt_errstr(result)));
2046                 
2047                 result = rpccli_samr_chgpasswd_user(cli, state->mem_ctx, user, newpass, oldpass);
2048
2049                 /* Windows 2000 returns NT_STATUS_ACCOUNT_RESTRICTION.
2050                    Map to the same status code as Windows 2003. */
2051
2052                 if ( NT_STATUS_EQUAL(NT_STATUS_ACCOUNT_RESTRICTION, result ) ) {
2053                         result = NT_STATUS_PASSWORD_RESTRICTION;                        
2054                 }
2055         }
2056
2057 done: 
2058
2059         if (NT_STATUS_IS_OK(result) && (state->request.flags & WBFLAG_PAM_CACHED_LOGIN)) {
2060                 
2061                 /* Update the single sign-on memory creds. */
2062                 result = winbindd_replace_memory_creds(state->request.data.chauthtok.user,
2063                                                         newpass);
2064
2065                 if (!NT_STATUS_IS_OK(result)) {
2066                         DEBUG(10,("Failed to replace memory creds: %s\n", nt_errstr(result)));
2067                         goto process_result;
2068                 }
2069
2070                 if (lp_winbind_offline_logon()) {
2071                         result = winbindd_update_creds_by_name(contact_domain,
2072                                                          state->mem_ctx, user,
2073                                                          newpass);
2074                         if (!NT_STATUS_IS_OK(result)) {
2075                                 DEBUG(10,("Failed to store creds: %s\n", nt_errstr(result)));
2076                                 goto process_result;
2077                         }
2078                 }
2079         }               
2080
2081         if (!NT_STATUS_IS_OK(result) && !got_info && contact_domain) {
2082
2083                 NTSTATUS policy_ret;
2084                 
2085                 policy_ret = fillup_password_policy(contact_domain, state);
2086
2087                 /* failure of this is non critical, it will just provide no
2088                  * additional information to the client why the change has
2089                  * failed - Guenther */
2090
2091                 if (!NT_STATUS_IS_OK(policy_ret)) {
2092                         DEBUG(10,("Failed to get password policies: %s\n", nt_errstr(policy_ret)));
2093                         goto process_result;
2094                 }
2095         }
2096
2097 process_result:
2098
2099         state->response.data.auth.nt_status = NT_STATUS_V(result);
2100         fstrcpy(state->response.data.auth.nt_status_string, nt_errstr(result));
2101         fstrcpy(state->response.data.auth.error_string, get_friendly_nt_error_msg(result));
2102         state->response.data.auth.pam_error = nt_status_to_pam(result);
2103
2104         DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2, 
2105               ("Password change for user [%s]\\[%s] returned %s (PAM: %d)\n", 
2106                domain,
2107                user,
2108                state->response.data.auth.nt_status_string,
2109                state->response.data.auth.pam_error));         
2110
2111         return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
2112 }
2113
2114 void winbindd_pam_logoff(struct winbindd_cli_state *state)
2115 {
2116         struct winbindd_domain *domain;
2117         fstring name_domain, user;
2118         uid_t caller_uid = (uid_t)-1;
2119         uid_t request_uid = state->request.data.logoff.uid;
2120
2121         DEBUG(3, ("[%5lu]: pam logoff %s\n", (unsigned long)state->pid,
2122                 state->request.data.logoff.user));
2123
2124         /* Ensure null termination */
2125         state->request.data.logoff.user
2126                 [sizeof(state->request.data.logoff.user)-1]='\0';
2127
2128         state->request.data.logoff.krb5ccname
2129                 [sizeof(state->request.data.logoff.krb5ccname)-1]='\0';
2130
2131         if (request_uid == (gid_t)-1) {
2132                 goto failed;
2133         }
2134
2135         if (!canonicalize_username(state->request.data.logoff.user, name_domain, user)) {
2136                 goto failed;
2137         }
2138
2139         if ((domain = find_auth_domain(state, name_domain)) == NULL) {
2140                 goto failed;
2141         }
2142
2143         if ((sys_getpeereid(state->sock, &caller_uid)) != 0) {
2144                 DEBUG(1,("winbindd_pam_logoff: failed to check peerid: %s\n", 
2145                         strerror(errno)));
2146                 goto failed;
2147         }
2148
2149         switch (caller_uid) {
2150                 case -1:
2151                         goto failed;
2152                 case 0:
2153                         /* root must be able to logoff any user - gd */
2154                         state->request.data.logoff.uid = request_uid;
2155                         break;
2156                 default:
2157                         if (caller_uid != request_uid) {
2158                                 DEBUG(1,("winbindd_pam_logoff: caller requested invalid uid\n"));
2159                                 goto failed;
2160                         }
2161                         state->request.data.logoff.uid = caller_uid;
2162                         break;
2163         }
2164
2165         sendto_domain(state, domain);
2166         return;
2167
2168  failed:
2169         set_auth_errors(&state->response, NT_STATUS_NO_SUCH_USER);
2170         DEBUG(5, ("Pam Logoff for %s returned %s "
2171                   "(PAM: %d)\n",
2172                   state->request.data.logoff.user,
2173                   state->response.data.auth.nt_status_string,
2174                   state->response.data.auth.pam_error));
2175         request_error(state);
2176         return;
2177 }
2178
2179 enum winbindd_result winbindd_dual_pam_logoff(struct winbindd_domain *domain,
2180                                               struct winbindd_cli_state *state) 
2181 {
2182         NTSTATUS result = NT_STATUS_NOT_SUPPORTED;
2183
2184         DEBUG(3, ("[%5lu]: pam dual logoff %s\n", (unsigned long)state->pid,
2185                 state->request.data.logoff.user));
2186
2187         if (!(state->request.flags & WBFLAG_PAM_KRB5)) {
2188                 result = NT_STATUS_OK;
2189                 goto process_result;
2190         }
2191
2192         if (state->request.data.logoff.krb5ccname[0] == '\0') {
2193                 result = NT_STATUS_OK;
2194                 goto process_result;
2195         }
2196
2197 #ifdef HAVE_KRB5
2198         
2199         if (state->request.data.logoff.uid < 0) {
2200                 DEBUG(0,("winbindd_pam_logoff: invalid uid\n"));
2201                 goto process_result;
2202         }
2203
2204         /* what we need here is to find the corresponding krb5 ccache name *we*
2205          * created for a given username and destroy it */
2206
2207         if (!ccache_entry_exists(state->request.data.logoff.user)) {
2208                 result = NT_STATUS_OK;
2209                 DEBUG(10,("winbindd_pam_logoff: no entry found.\n"));
2210                 goto process_result;
2211         }
2212
2213         if (!ccache_entry_identical(state->request.data.logoff.user, 
2214                                         state->request.data.logoff.uid,
2215                                         state->request.data.logoff.krb5ccname)) {
2216                 DEBUG(0,("winbindd_pam_logoff: cached entry differs.\n"));
2217                 goto process_result;
2218         }
2219
2220         result = remove_ccache(state->request.data.logoff.user);
2221         if (!NT_STATUS_IS_OK(result)) {
2222                 DEBUG(0,("winbindd_pam_logoff: failed to remove ccache: %s\n",
2223                         nt_errstr(result)));
2224                 goto process_result;
2225         }
2226
2227 #else
2228         result = NT_STATUS_NOT_SUPPORTED;
2229 #endif
2230
2231 process_result:
2232
2233         winbindd_delete_memory_creds(state->request.data.logoff.user);
2234
2235         state->response.data.auth.nt_status = NT_STATUS_V(result);
2236         fstrcpy(state->response.data.auth.nt_status_string, nt_errstr(result));
2237         fstrcpy(state->response.data.auth.error_string, get_friendly_nt_error_msg(result));
2238         state->response.data.auth.pam_error = nt_status_to_pam(result);
2239
2240         return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
2241 }
2242
2243 /* Change user password with auth crap*/
2244
2245 void winbindd_pam_chng_pswd_auth_crap(struct winbindd_cli_state *state)
2246 {
2247         struct winbindd_domain *domain = NULL;
2248         const char *domain_name = NULL;
2249
2250         /* Ensure null termination */
2251         state->request.data.chng_pswd_auth_crap.user[
2252                 sizeof(state->request.data.chng_pswd_auth_crap.user)-1]=0;
2253         state->request.data.chng_pswd_auth_crap.domain[
2254                 sizeof(state->request.data.chng_pswd_auth_crap.domain)-1]=0;
2255         
2256         DEBUG(3, ("[%5lu]: pam change pswd auth crap domain: %s user: %s\n",
2257                   (unsigned long)state->pid,
2258                   state->request.data.chng_pswd_auth_crap.domain,
2259                   state->request.data.chng_pswd_auth_crap.user));
2260         
2261         if (*state->request.data.chng_pswd_auth_crap.domain != '\0') {
2262                 domain_name = state->request.data.chng_pswd_auth_crap.domain;
2263         } else if (lp_winbind_use_default_domain()) {
2264                 domain_name = lp_workgroup();
2265         }
2266
2267         if (domain_name != NULL)
2268                 domain = find_domain_from_name(domain_name);
2269
2270         if (domain != NULL) {
2271                 DEBUG(7, ("[%5lu]: pam auth crap changing pswd in domain: "
2272                           "%s\n", (unsigned long)state->pid,domain->name));
2273                 sendto_domain(state, domain);
2274                 return;
2275         }
2276
2277         set_auth_errors(&state->response, NT_STATUS_NO_SUCH_USER);
2278         DEBUG(5, ("CRAP change password  for %s\\%s returned %s (PAM: %d)\n",
2279                   state->request.data.chng_pswd_auth_crap.domain,
2280                   state->request.data.chng_pswd_auth_crap.user, 
2281                   state->response.data.auth.nt_status_string,
2282                   state->response.data.auth.pam_error));
2283         request_error(state);
2284         return;
2285 }
2286
2287 enum winbindd_result winbindd_dual_pam_chng_pswd_auth_crap(struct winbindd_domain *domainSt, struct winbindd_cli_state *state)
2288 {
2289         NTSTATUS result;
2290         DATA_BLOB new_nt_password;
2291         DATA_BLOB old_nt_hash_enc;
2292         DATA_BLOB new_lm_password;
2293         DATA_BLOB old_lm_hash_enc;
2294         fstring  domain,user;
2295         POLICY_HND dom_pol;
2296         struct winbindd_domain *contact_domain = domainSt;
2297         struct rpc_pipe_client *cli;
2298
2299         /* Ensure null termination */
2300         state->request.data.chng_pswd_auth_crap.user[
2301                 sizeof(state->request.data.chng_pswd_auth_crap.user)-1]=0;
2302         state->request.data.chng_pswd_auth_crap.domain[
2303                 sizeof(state->request.data.chng_pswd_auth_crap.domain)-1]=0;
2304         *domain = 0;
2305         *user = 0;
2306         
2307         DEBUG(3, ("[%5lu]: pam change pswd auth crap domain: %s user: %s\n",
2308                   (unsigned long)state->pid,
2309                   state->request.data.chng_pswd_auth_crap.domain,
2310                   state->request.data.chng_pswd_auth_crap.user));
2311
2312         if (lp_winbind_offline_logon()) {
2313                 DEBUG(0,("Refusing password change as winbind offline logons are enabled. "));
2314                 DEBUGADD(0,("Changing passwords here would risk inconsistent logons\n"));
2315                 result = NT_STATUS_ACCESS_DENIED;
2316                 goto done;
2317         }
2318
2319         if (*state->request.data.chng_pswd_auth_crap.domain) {
2320                 fstrcpy(domain,state->request.data.chng_pswd_auth_crap.domain);
2321         } else {
2322                 parse_domain_user(state->request.data.chng_pswd_auth_crap.user,
2323                                   domain, user);
2324
2325                 if(!*domain) {
2326                         DEBUG(3,("no domain specified with username (%s) - "
2327                                  "failing auth\n",
2328                                  state->request.data.chng_pswd_auth_crap.user));
2329                         result = NT_STATUS_NO_SUCH_USER;
2330                         goto done;
2331                 }
2332         }
2333
2334         if (!*domain && lp_winbind_use_default_domain()) {
2335                 fstrcpy(domain,(char *)lp_workgroup());
2336         }
2337
2338         if(!*user) {
2339                 fstrcpy(user, state->request.data.chng_pswd_auth_crap.user);
2340         }
2341
2342         DEBUG(3, ("[%5lu]: pam auth crap domain: %s user: %s\n",
2343                   (unsigned long)state->pid, domain, user));
2344         
2345         /* Change password */
2346         new_nt_password = data_blob_talloc(
2347                 state->mem_ctx,
2348                 state->request.data.chng_pswd_auth_crap.new_nt_pswd,
2349                 state->request.data.chng_pswd_auth_crap.new_nt_pswd_len);
2350
2351         old_nt_hash_enc = data_blob_talloc(
2352                 state->mem_ctx,
2353                 state->request.data.chng_pswd_auth_crap.old_nt_hash_enc,
2354                 state->request.data.chng_pswd_auth_crap.old_nt_hash_enc_len);
2355
2356         if(state->request.data.chng_pswd_auth_crap.new_lm_pswd_len > 0) {
2357                 new_lm_password = data_blob_talloc(
2358                         state->mem_ctx,
2359                         state->request.data.chng_pswd_auth_crap.new_lm_pswd,
2360                         state->request.data.chng_pswd_auth_crap.new_lm_pswd_len);
2361
2362                 old_lm_hash_enc = data_blob_talloc(
2363                         state->mem_ctx,
2364                         state->request.data.chng_pswd_auth_crap.old_lm_hash_enc,
2365                         state->request.data.chng_pswd_auth_crap.old_lm_hash_enc_len);
2366         } else {
2367                 new_lm_password.length = 0;
2368                 old_lm_hash_enc.length = 0;
2369         }
2370
2371         /* Get sam handle */
2372
2373         result = cm_connect_sam(contact_domain, state->mem_ctx, &cli, &dom_pol);
2374         if (!NT_STATUS_IS_OK(result)) {
2375                 DEBUG(1, ("could not get SAM handle on DC for %s\n", domain));
2376                 goto done;
2377         }
2378
2379         result = rpccli_samr_chng_pswd_auth_crap(
2380                 cli, state->mem_ctx, user, new_nt_password, old_nt_hash_enc,
2381                 new_lm_password, old_lm_hash_enc);
2382
2383  done:    
2384         state->response.data.auth.nt_status = NT_STATUS_V(result);
2385         fstrcpy(state->response.data.auth.nt_status_string, nt_errstr(result));
2386         fstrcpy(state->response.data.auth.error_string,
2387                 get_friendly_nt_error_msg(result));
2388         state->response.data.auth.pam_error = nt_status_to_pam(result);
2389
2390         DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2, 
2391               ("Password change for user [%s]\\[%s] returned %s (PAM: %d)\n", 
2392                domain, user,
2393                state->response.data.auth.nt_status_string,
2394                state->response.data.auth.pam_error));         
2395
2396         return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
2397 }