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