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