s3: winbind: Prevent null ptr access by returning error if no creds available
[metze/samba/wip.git] / source3 / winbindd / 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 3 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, see <http://www.gnu.org/licenses/>.
23 */
24
25 #include "includes.h"
26 #include "winbindd.h"
27 #include "../libcli/auth/libcli_auth.h"
28 #include "../librpc/gen_ndr/ndr_samr_c.h"
29 #include "rpc_client/cli_pipe.h"
30 #include "rpc_client/cli_samr.h"
31 #include "../librpc/gen_ndr/ndr_netlogon.h"
32 #include "rpc_client/cli_netlogon.h"
33 #include "smb_krb5.h"
34 #include "../lib/crypto/arcfour.h"
35 #include "../libcli/security/security.h"
36 #include "ads.h"
37 #include "../librpc/gen_ndr/krb5pac.h"
38 #include "passdb/machine_sid.h"
39 #include "auth.h"
40 #include "../lib/tsocket/tsocket.h"
41 #include "auth/kerberos/pac_utils.h"
42 #include "auth/gensec/gensec.h"
43 #include "librpc/crypto/gse_krb5.h"
44 #include "lib/afs/afs_funcs.h"
45
46 #undef DBGC_CLASS
47 #define DBGC_CLASS DBGC_WINBIND
48
49 #define LOGON_KRB5_FAIL_CLOCK_SKEW      0x02000000
50
51 static NTSTATUS append_info3_as_txt(TALLOC_CTX *mem_ctx,
52                                     struct winbindd_response *resp,
53                                     struct netr_SamInfo3 *info3)
54 {
55         char *ex;
56         uint32_t i;
57
58         resp->data.auth.info3.logon_time =
59                 nt_time_to_unix(info3->base.logon_time);
60         resp->data.auth.info3.logoff_time =
61                 nt_time_to_unix(info3->base.logoff_time);
62         resp->data.auth.info3.kickoff_time =
63                 nt_time_to_unix(info3->base.kickoff_time);
64         resp->data.auth.info3.pass_last_set_time =
65                 nt_time_to_unix(info3->base.last_password_change);
66         resp->data.auth.info3.pass_can_change_time =
67                 nt_time_to_unix(info3->base.allow_password_change);
68         resp->data.auth.info3.pass_must_change_time =
69                 nt_time_to_unix(info3->base.force_password_change);
70
71         resp->data.auth.info3.logon_count = info3->base.logon_count;
72         resp->data.auth.info3.bad_pw_count = info3->base.bad_password_count;
73
74         resp->data.auth.info3.user_rid = info3->base.rid;
75         resp->data.auth.info3.group_rid = info3->base.primary_gid;
76         sid_to_fstring(resp->data.auth.info3.dom_sid, info3->base.domain_sid);
77
78         resp->data.auth.info3.num_groups = info3->base.groups.count;
79         resp->data.auth.info3.user_flgs = info3->base.user_flags;
80
81         resp->data.auth.info3.acct_flags = info3->base.acct_flags;
82         resp->data.auth.info3.num_other_sids = info3->sidcount;
83
84         fstrcpy(resp->data.auth.info3.user_name,
85                 info3->base.account_name.string);
86         fstrcpy(resp->data.auth.info3.full_name,
87                 info3->base.full_name.string);
88         fstrcpy(resp->data.auth.info3.logon_script,
89                 info3->base.logon_script.string);
90         fstrcpy(resp->data.auth.info3.profile_path,
91                 info3->base.profile_path.string);
92         fstrcpy(resp->data.auth.info3.home_dir,
93                 info3->base.home_directory.string);
94         fstrcpy(resp->data.auth.info3.dir_drive,
95                 info3->base.home_drive.string);
96
97         fstrcpy(resp->data.auth.info3.logon_srv,
98                 info3->base.logon_server.string);
99         fstrcpy(resp->data.auth.info3.logon_dom,
100                 info3->base.logon_domain.string);
101
102         ex = talloc_strdup(mem_ctx, "");
103         NT_STATUS_HAVE_NO_MEMORY(ex);
104
105         for (i=0; i < info3->base.groups.count; i++) {
106                 ex = talloc_asprintf_append_buffer(ex, "0x%08X:0x%08X\n",
107                                                    info3->base.groups.rids[i].rid,
108                                                    info3->base.groups.rids[i].attributes);
109                 NT_STATUS_HAVE_NO_MEMORY(ex);
110         }
111
112         for (i=0; i < info3->sidcount; i++) {
113                 char *sid;
114
115                 sid = dom_sid_string(mem_ctx, info3->sids[i].sid);
116                 NT_STATUS_HAVE_NO_MEMORY(sid);
117
118                 ex = talloc_asprintf_append_buffer(ex, "%s:0x%08X\n",
119                                                    sid,
120                                                    info3->sids[i].attributes);
121                 NT_STATUS_HAVE_NO_MEMORY(ex);
122
123                 talloc_free(sid);
124         }
125
126         resp->extra_data.data = ex;
127         resp->length += talloc_get_size(ex);
128
129         return NT_STATUS_OK;
130 }
131
132 static NTSTATUS append_info3_as_ndr(TALLOC_CTX *mem_ctx,
133                                     struct winbindd_response *resp,
134                                     struct netr_SamInfo3 *info3)
135 {
136         DATA_BLOB blob;
137         enum ndr_err_code ndr_err;
138
139         ndr_err = ndr_push_struct_blob(&blob, mem_ctx, info3,
140                                        (ndr_push_flags_fn_t)ndr_push_netr_SamInfo3);
141         if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
142                 DEBUG(0,("append_info3_as_ndr: failed to append\n"));
143                 return ndr_map_error2ntstatus(ndr_err);
144         }
145
146         resp->extra_data.data = blob.data;
147         resp->length += blob.length;
148
149         return NT_STATUS_OK;
150 }
151
152 static NTSTATUS append_unix_username(TALLOC_CTX *mem_ctx,
153                                      struct winbindd_response *resp,
154                                      const struct netr_SamInfo3 *info3,
155                                      const char *name_domain,
156                                      const char *name_user)
157 {
158         /* We've been asked to return the unix username, per
159            'winbind use default domain' settings and the like */
160
161         const char *nt_username, *nt_domain;
162
163         nt_domain = talloc_strdup(mem_ctx, info3->base.logon_domain.string);
164         if (!nt_domain) {
165                 /* If the server didn't give us one, just use the one
166                  * we sent them */
167                 nt_domain = name_domain;
168         }
169
170         nt_username = talloc_strdup(mem_ctx, info3->base.account_name.string);
171         if (!nt_username) {
172                 /* If the server didn't give us one, just use the one
173                  * we sent them */
174                 nt_username = name_user;
175         }
176
177         fill_domain_username(resp->data.auth.unix_username,
178                              nt_domain, nt_username, true);
179
180         DEBUG(5, ("Setting unix username to [%s]\n",
181                   resp->data.auth.unix_username));
182
183         return NT_STATUS_OK;
184 }
185
186 static NTSTATUS append_afs_token(TALLOC_CTX *mem_ctx,
187                                  struct winbindd_response *resp,
188                                  const struct netr_SamInfo3 *info3,
189                                  const char *name_domain,
190                                  const char *name_user)
191 {
192         char *afsname = NULL;
193         char *cell;
194         char *token;
195
196         afsname = talloc_strdup(mem_ctx, lp_afs_username_map());
197         if (afsname == NULL) {
198                 return NT_STATUS_NO_MEMORY;
199         }
200
201         afsname = talloc_string_sub(mem_ctx,
202                                     lp_afs_username_map(),
203                                     "%D", name_domain);
204         afsname = talloc_string_sub(mem_ctx, afsname,
205                                     "%u", name_user);
206         afsname = talloc_string_sub(mem_ctx, afsname,
207                                     "%U", name_user);
208
209         {
210                 struct dom_sid user_sid;
211                 fstring sidstr;
212
213                 sid_compose(&user_sid, info3->base.domain_sid,
214                             info3->base.rid);
215                 sid_to_fstring(sidstr, &user_sid);
216                 afsname = talloc_string_sub(mem_ctx, afsname,
217                                             "%s", sidstr);
218         }
219
220         if (afsname == NULL) {
221                 return NT_STATUS_NO_MEMORY;
222         }
223
224         if (!strlower_m(afsname)) {
225                 return NT_STATUS_INVALID_PARAMETER;
226         }
227
228         DEBUG(10, ("Generating token for user %s\n", afsname));
229
230         cell = strchr(afsname, '@');
231
232         if (cell == NULL) {
233                 return NT_STATUS_NO_MEMORY;
234         }
235
236         *cell = '\0';
237         cell += 1;
238
239         token = afs_createtoken_str(afsname, cell);
240         if (token == NULL) {
241                 return NT_STATUS_OK;
242         }
243         resp->extra_data.data = talloc_strdup(mem_ctx, token);
244         if (resp->extra_data.data == NULL) {
245                 return NT_STATUS_NO_MEMORY;
246         }
247         resp->length += strlen((const char *)resp->extra_data.data)+1;
248
249         return NT_STATUS_OK;
250 }
251
252 static NTSTATUS check_info3_in_group(struct netr_SamInfo3 *info3,
253                                      const char *group_sid)
254 /**
255  * Check whether a user belongs to a group or list of groups.
256  *
257  * @param mem_ctx talloc memory context.
258  * @param info3 user information, including group membership info.
259  * @param group_sid One or more groups , separated by commas.
260  *
261  * @return NT_STATUS_OK on success,
262  *    NT_STATUS_LOGON_FAILURE if the user does not belong,
263  *    or other NT_STATUS_IS_ERR(status) for other kinds of failure.
264  */
265 {
266         struct dom_sid *require_membership_of_sid;
267         uint32_t num_require_membership_of_sid;
268         char *req_sid;
269         const char *p;
270         struct dom_sid sid;
271         size_t i;
272         struct security_token *token;
273         TALLOC_CTX *frame = talloc_stackframe();
274         NTSTATUS status;
275
276         /* Parse the 'required group' SID */
277
278         if (!group_sid || !group_sid[0]) {
279                 /* NO sid supplied, all users may access */
280                 TALLOC_FREE(frame);
281                 return NT_STATUS_OK;
282         }
283
284         token = talloc_zero(talloc_tos(), struct security_token);
285         if (token == NULL) {
286                 DEBUG(0, ("talloc failed\n"));
287                 TALLOC_FREE(frame);
288                 return NT_STATUS_NO_MEMORY;
289         }
290
291         num_require_membership_of_sid = 0;
292         require_membership_of_sid = NULL;
293
294         p = group_sid;
295
296         while (next_token_talloc(talloc_tos(), &p, &req_sid, ",")) {
297                 if (!string_to_sid(&sid, req_sid)) {
298                         DEBUG(0, ("check_info3_in_group: could not parse %s "
299                                   "as a SID!", req_sid));
300                         TALLOC_FREE(frame);
301                         return NT_STATUS_INVALID_PARAMETER;
302                 }
303
304                 status = add_sid_to_array(talloc_tos(), &sid,
305                                           &require_membership_of_sid,
306                                           &num_require_membership_of_sid);
307                 if (!NT_STATUS_IS_OK(status)) {
308                         DEBUG(0, ("add_sid_to_array failed\n"));
309                         TALLOC_FREE(frame);
310                         return status;
311                 }
312         }
313
314         status = sid_array_from_info3(talloc_tos(), info3,
315                                       &token->sids,
316                                       &token->num_sids,
317                                       true);
318         if (!NT_STATUS_IS_OK(status)) {
319                 TALLOC_FREE(frame);
320                 return status;
321         }
322
323         if (!NT_STATUS_IS_OK(status = add_aliases(get_global_sam_sid(),
324                                                   token))
325             || !NT_STATUS_IS_OK(status = add_aliases(&global_sid_Builtin,
326                                                      token))) {
327                 DEBUG(3, ("could not add aliases: %s\n",
328                           nt_errstr(status)));
329                 TALLOC_FREE(frame);
330                 return status;
331         }
332
333         security_token_debug(DBGC_CLASS, 10, token);
334
335         for (i=0; i<num_require_membership_of_sid; i++) {
336                 DEBUG(10, ("Checking SID %s\n", sid_string_dbg(
337                                    &require_membership_of_sid[i])));
338                 if (nt_token_check_sid(&require_membership_of_sid[i],
339                                        token)) {
340                         DEBUG(10, ("Access ok\n"));
341                         TALLOC_FREE(frame);
342                         return NT_STATUS_OK;
343                 }
344         }
345
346         /* Do not distinguish this error from a wrong username/pw */
347
348         TALLOC_FREE(frame);
349         return NT_STATUS_LOGON_FAILURE;
350 }
351
352 struct winbindd_domain *find_auth_domain(uint8_t flags,
353                                          const char *domain_name)
354 {
355         struct winbindd_domain *domain;
356
357         if (IS_DC) {
358                 domain = find_domain_from_name_noinit(domain_name);
359                 if (domain == NULL) {
360                         DEBUG(3, ("Authentication for domain [%s] refused "
361                                   "as it is not a trusted domain\n",
362                                   domain_name));
363                 }
364                 return domain;
365         }
366
367         if (strequal(domain_name, get_global_sam_name())) {
368                 return find_domain_from_name_noinit(domain_name);
369         }
370
371         /* we can auth against trusted domains */
372         if (flags & WBFLAG_PAM_CONTACT_TRUSTDOM) {
373                 domain = find_domain_from_name_noinit(domain_name);
374                 if (domain == NULL) {
375                         DEBUG(3, ("Authentication for domain [%s] skipped "
376                                   "as it is not a trusted domain\n",
377                                   domain_name));
378                 } else {
379                         return domain;
380                 }
381         }
382
383         return find_our_domain();
384 }
385
386 static void fill_in_password_policy(struct winbindd_response *r,
387                                     const struct samr_DomInfo1 *p)
388 {
389         r->data.auth.policy.min_length_password =
390                 p->min_password_length;
391         r->data.auth.policy.password_history =
392                 p->password_history_length;
393         r->data.auth.policy.password_properties =
394                 p->password_properties;
395         r->data.auth.policy.expire      =
396                 nt_time_to_unix_abs((const NTTIME *)&(p->max_password_age));
397         r->data.auth.policy.min_passwordage =
398                 nt_time_to_unix_abs((const NTTIME *)&(p->min_password_age));
399 }
400
401 static NTSTATUS fillup_password_policy(struct winbindd_domain *domain,
402                                        struct winbindd_response *response)
403 {
404         TALLOC_CTX *frame = talloc_stackframe();
405         struct winbindd_methods *methods;
406         NTSTATUS status;
407         struct samr_DomInfo1 password_policy;
408
409         if ( !winbindd_can_contact_domain( domain ) ) {
410                 DEBUG(5,("fillup_password_policy: No inbound trust to "
411                          "contact domain %s\n", domain->name));
412                 status = NT_STATUS_NOT_SUPPORTED;
413                 goto done;
414         }
415
416         methods = domain->methods;
417
418         status = methods->password_policy(domain, talloc_tos(), &password_policy);
419         if (NT_STATUS_IS_ERR(status)) {
420                 goto done;
421         }
422
423         fill_in_password_policy(response, &password_policy);
424
425 done:
426         TALLOC_FREE(frame);
427         return NT_STATUS_OK;
428 }
429
430 static NTSTATUS get_max_bad_attempts_from_lockout_policy(struct winbindd_domain *domain,
431                                                          TALLOC_CTX *mem_ctx,
432                                                          uint16_t *lockout_threshold)
433 {
434         struct winbindd_methods *methods;
435         NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
436         struct samr_DomInfo12 lockout_policy;
437
438         *lockout_threshold = 0;
439
440         methods = domain->methods;
441
442         status = methods->lockout_policy(domain, mem_ctx, &lockout_policy);
443         if (NT_STATUS_IS_ERR(status)) {
444                 return status;
445         }
446
447         *lockout_threshold = lockout_policy.lockout_threshold;
448
449         return NT_STATUS_OK;
450 }
451
452 static NTSTATUS get_pwd_properties(struct winbindd_domain *domain,
453                                    TALLOC_CTX *mem_ctx,
454                                    uint32_t *password_properties)
455 {
456         struct winbindd_methods *methods;
457         NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
458         struct samr_DomInfo1 password_policy;
459
460         *password_properties = 0;
461
462         methods = domain->methods;
463
464         status = methods->password_policy(domain, mem_ctx, &password_policy);
465         if (NT_STATUS_IS_ERR(status)) {
466                 return status;
467         }
468
469         *password_properties = password_policy.password_properties;
470
471         return NT_STATUS_OK;
472 }
473
474 #ifdef HAVE_KRB5
475
476 static const char *generate_krb5_ccache(TALLOC_CTX *mem_ctx,
477                                         const char *type,
478                                         uid_t uid,
479                                         const char **user_ccache_file)
480 {
481         /* accept FILE and WRFILE as krb5_cc_type from the client and then
482          * build the full ccname string based on the user's uid here -
483          * Guenther*/
484
485         const char *gen_cc = NULL;
486
487         if (uid != -1) {
488                 if (strequal(type, "FILE")) {
489                         gen_cc = talloc_asprintf(
490                                 mem_ctx, "FILE:/tmp/krb5cc_%d", uid);
491                 }
492                 if (strequal(type, "WRFILE")) {
493                         gen_cc = talloc_asprintf(
494                                 mem_ctx, "WRFILE:/tmp/krb5cc_%d", uid);
495                 }
496                 if (strequal(type, "KEYRING")) {
497                         gen_cc = talloc_asprintf(
498                                 mem_ctx, "KEYRING:persistent:%d", uid);
499                 }
500
501                 if (strnequal(type, "FILE:/", 6) ||
502                     strnequal(type, "WRFILE:/", 8) ||
503                     strnequal(type, "DIR:/", 5)) {
504
505                         /* we allow only one "%u" substitution */
506
507                         char *p;
508
509                         p = strchr(type, '%');
510                         if (p != NULL) {
511
512                                 p++;
513
514                                 if (p != NULL && *p == 'u' && strchr(p, '%') == NULL) {
515                                         char uid_str[sizeof("18446744073709551615")];
516
517                                         snprintf(uid_str, sizeof(uid_str), "%u", uid);
518
519                                         gen_cc = talloc_string_sub2(mem_ctx,
520                                                         type,
521                                                         "%u",
522                                                         uid_str,
523                                                         /* remove_unsafe_characters */
524                                                         false,
525                                                         /* replace_once */
526                                                         true,
527                                                         /* allow_trailing_dollar */
528                                                         false);
529                                 }
530                         }
531                 }
532         }
533
534         *user_ccache_file = gen_cc;
535
536         if (gen_cc == NULL) {
537                 gen_cc = talloc_strdup(mem_ctx, "MEMORY:winbindd_pam_ccache");
538         }
539         if (gen_cc == NULL) {
540                 DEBUG(0,("out of memory\n"));
541                 return NULL;
542         }
543
544         DEBUG(10, ("using ccache: %s%s\n", gen_cc,
545                    (*user_ccache_file == NULL) ? " (internal)":""));
546
547         return gen_cc;
548 }
549
550 #endif
551
552 uid_t get_uid_from_request(struct winbindd_request *request)
553 {
554         uid_t uid;
555
556         uid = request->data.auth.uid;
557
558         if (uid < 0) {
559                 DEBUG(1,("invalid uid: '%u'\n", (unsigned int)uid));
560                 return -1;
561         }
562         return uid;
563 }
564
565 /**********************************************************************
566  Authenticate a user with a clear text password using Kerberos and fill up
567  ccache if required
568  **********************************************************************/
569
570 static NTSTATUS winbindd_raw_kerberos_login(TALLOC_CTX *mem_ctx,
571                                             struct winbindd_domain *domain,
572                                             const char *user,
573                                             const char *pass,
574                                             const char *krb5_cc_type,
575                                             uid_t uid,
576                                             struct netr_SamInfo3 **info3,
577                                             fstring krb5ccname)
578 {
579 #ifdef HAVE_KRB5
580         NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
581         krb5_error_code krb5_ret;
582         const char *cc = NULL;
583         const char *principal_s = NULL;
584         const char *service = NULL;
585         char *realm = NULL;
586         fstring name_domain, name_user;
587         time_t ticket_lifetime = 0;
588         time_t renewal_until = 0;
589         ADS_STRUCT *ads;
590         time_t time_offset = 0;
591         const char *user_ccache_file;
592         struct PAC_LOGON_INFO *logon_info = NULL;
593         struct PAC_DATA *pac_data = NULL;
594         struct PAC_DATA_CTR *pac_data_ctr = NULL;
595         const char *local_service;
596         int i;
597         struct netr_SamInfo3 *info3_copy = NULL;
598
599         *info3 = NULL;
600
601         if (domain->alt_name == NULL) {
602                 return NT_STATUS_INVALID_PARAMETER;
603         }
604
605         /* 1st step:
606          * prepare a krb5_cc_cache string for the user */
607
608         if (uid == -1) {
609                 DEBUG(0,("no valid uid\n"));
610         }
611
612         cc = generate_krb5_ccache(mem_ctx,
613                                   krb5_cc_type,
614                                   uid,
615                                   &user_ccache_file);
616         if (cc == NULL) {
617                 return NT_STATUS_NO_MEMORY;
618         }
619
620
621         /* 2nd step:
622          * get kerberos properties */
623
624         if (domain->private_data) {
625                 ads = (ADS_STRUCT *)domain->private_data;
626                 time_offset = ads->auth.time_offset;
627         }
628
629
630         /* 3rd step:
631          * do kerberos auth and setup ccache as the user */
632
633         parse_domain_user(user, name_domain, name_user);
634
635         realm = talloc_strdup(mem_ctx, domain->alt_name);
636         if (realm == NULL) {
637                 return NT_STATUS_NO_MEMORY;
638         }
639
640         if (!strupper_m(realm)) {
641                 return NT_STATUS_INVALID_PARAMETER;
642         }
643
644         principal_s = talloc_asprintf(mem_ctx, "%s@%s", name_user, realm);
645         if (principal_s == NULL) {
646                 return NT_STATUS_NO_MEMORY;
647         }
648
649         service = talloc_asprintf(mem_ctx, "%s/%s@%s", KRB5_TGS_NAME, realm, realm);
650         if (service == NULL) {
651                 return NT_STATUS_NO_MEMORY;
652         }
653
654         local_service = talloc_asprintf(mem_ctx, "%s$@%s",
655                                         lp_netbios_name(), lp_realm());
656         if (local_service == NULL) {
657                 return NT_STATUS_NO_MEMORY;
658         }
659
660
661         /* if this is a user ccache, we need to act as the user to let the krb5
662          * library handle the chown, etc. */
663
664         /************************ ENTERING NON-ROOT **********************/
665
666         if (user_ccache_file != NULL) {
667                 set_effective_uid(uid);
668                 DEBUG(10,("winbindd_raw_kerberos_login: uid is %d\n", uid));
669         }
670
671         result = kerberos_return_pac(mem_ctx,
672                                      principal_s,
673                                      pass,
674                                      time_offset,
675                                      &ticket_lifetime,
676                                      &renewal_until,
677                                      cc,
678                                      true,
679                                      true,
680                                      WINBINDD_PAM_AUTH_KRB5_RENEW_TIME,
681                                      NULL,
682                                      local_service,
683                                      &pac_data_ctr);
684         if (user_ccache_file != NULL) {
685                 gain_root_privilege();
686         }
687
688         /************************ RETURNED TO ROOT **********************/
689
690         if (!NT_STATUS_IS_OK(result)) {
691                 goto failed;
692         }
693
694         if (pac_data_ctr == NULL) {
695                 goto failed;
696         }
697
698         pac_data = pac_data_ctr->pac_data;
699         if (pac_data == NULL) {
700                 goto failed;
701         }
702
703         for (i=0; i < pac_data->num_buffers; i++) {
704
705                 if (pac_data->buffers[i].type != PAC_TYPE_LOGON_INFO) {
706                         continue;
707                 }
708
709                 logon_info = pac_data->buffers[i].info->logon_info.info;
710                 if (!logon_info) {
711                         return NT_STATUS_INVALID_PARAMETER;
712                 }
713
714                 break;
715         }
716
717         if (logon_info == NULL) {
718                 DEBUG(10,("Missing logon_info in ticket of %s\n",
719                         principal_s));
720                 return NT_STATUS_INVALID_PARAMETER;
721         }
722
723         DEBUG(10,("winbindd_raw_kerberos_login: winbindd validated ticket of %s\n",
724                 principal_s));
725
726         result = create_info3_from_pac_logon_info(mem_ctx, logon_info, &info3_copy);
727         if (!NT_STATUS_IS_OK(result)) {
728                 goto failed;
729         }
730
731         /* if we had a user's ccache then return that string for the pam
732          * environment */
733
734         if (user_ccache_file != NULL) {
735
736                 fstrcpy(krb5ccname, user_ccache_file);
737
738                 result = add_ccache_to_list(principal_s,
739                                             cc,
740                                             service,
741                                             user,
742                                             pass,
743                                             realm,
744                                             uid,
745                                             time(NULL),
746                                             ticket_lifetime,
747                                             renewal_until,
748                                             false);
749
750                 if (!NT_STATUS_IS_OK(result)) {
751                         DEBUG(10,("winbindd_raw_kerberos_login: failed to add ccache to list: %s\n",
752                                 nt_errstr(result)));
753                 }
754         } else {
755
756                 /* need to delete the memory cred cache, it is not used anymore */
757
758                 krb5_ret = ads_kdestroy(cc);
759                 if (krb5_ret) {
760                         DEBUG(3,("winbindd_raw_kerberos_login: "
761                                  "could not destroy krb5 credential cache: "
762                                  "%s\n", error_message(krb5_ret)));
763                 }
764
765         }
766         *info3 = info3_copy;
767         return NT_STATUS_OK;
768
769 failed:
770         /*
771          * Do not delete an existing valid credential cache, if the user
772          * e.g. enters a wrong password
773          */
774         if ((strequal(krb5_cc_type, "FILE") || strequal(krb5_cc_type, "WRFILE"))
775             && user_ccache_file != NULL) {
776                 return result;
777         }
778
779         /* we could have created a new credential cache with a valid tgt in it
780          * but we werent able to get or verify the service ticket for this
781          * local host and therefor didn't get the PAC, we need to remove that
782          * cache entirely now */
783
784         krb5_ret = ads_kdestroy(cc);
785         if (krb5_ret) {
786                 DEBUG(3,("winbindd_raw_kerberos_login: "
787                          "could not destroy krb5 credential cache: "
788                          "%s\n", error_message(krb5_ret)));
789         }
790
791         if (!NT_STATUS_IS_OK(remove_ccache(user))) {
792                 DEBUG(3,("winbindd_raw_kerberos_login: "
793                           "could not remove ccache for user %s\n",
794                         user));
795         }
796
797         return result;
798 #else
799         return NT_STATUS_NOT_SUPPORTED;
800 #endif /* HAVE_KRB5 */
801 }
802
803 /****************************************************************
804 ****************************************************************/
805
806 bool check_request_flags(uint32_t flags)
807 {
808         uint32_t flags_edata = WBFLAG_PAM_AFS_TOKEN |
809                                WBFLAG_PAM_INFO3_TEXT |
810                                WBFLAG_PAM_INFO3_NDR;
811
812         if ( ( (flags & flags_edata) == WBFLAG_PAM_AFS_TOKEN) ||
813              ( (flags & flags_edata) == WBFLAG_PAM_INFO3_NDR) ||
814              ( (flags & flags_edata) == WBFLAG_PAM_INFO3_TEXT)||
815               !(flags & flags_edata) ) {
816                 return true;
817         }
818
819         DEBUG(1, ("check_request_flags: invalid request flags[0x%08X]\n",
820                   flags));
821
822         return false;
823 }
824
825 /****************************************************************
826 ****************************************************************/
827
828 NTSTATUS append_auth_data(TALLOC_CTX *mem_ctx,
829                           struct winbindd_response *resp,
830                           uint32_t request_flags,
831                           struct netr_SamInfo3 *info3,
832                           const char *name_domain,
833                           const char *name_user)
834 {
835         NTSTATUS result;
836
837         if (request_flags & WBFLAG_PAM_USER_SESSION_KEY) {
838                 memcpy(resp->data.auth.user_session_key,
839                        info3->base.key.key,
840                        sizeof(resp->data.auth.user_session_key)
841                        /* 16 */);
842         }
843
844         if (request_flags & WBFLAG_PAM_LMKEY) {
845                 memcpy(resp->data.auth.first_8_lm_hash,
846                        info3->base.LMSessKey.key,
847                        sizeof(resp->data.auth.first_8_lm_hash)
848                        /* 8 */);
849         }
850
851         if (request_flags & WBFLAG_PAM_UNIX_NAME) {
852                 result = append_unix_username(mem_ctx, resp,
853                                               info3, name_domain, name_user);
854                 if (!NT_STATUS_IS_OK(result)) {
855                         DEBUG(10,("Failed to append Unix Username: %s\n",
856                                 nt_errstr(result)));
857                         return result;
858                 }
859         }
860
861         /* currently, anything from here on potentially overwrites extra_data. */
862
863         if (request_flags & WBFLAG_PAM_INFO3_NDR) {
864                 result = append_info3_as_ndr(mem_ctx, resp, info3);
865                 if (!NT_STATUS_IS_OK(result)) {
866                         DEBUG(10,("Failed to append INFO3 (NDR): %s\n",
867                                 nt_errstr(result)));
868                         return result;
869                 }
870         }
871
872         if (request_flags & WBFLAG_PAM_INFO3_TEXT) {
873                 result = append_info3_as_txt(mem_ctx, resp, info3);
874                 if (!NT_STATUS_IS_OK(result)) {
875                         DEBUG(10,("Failed to append INFO3 (TXT): %s\n",
876                                 nt_errstr(result)));
877                         return result;
878                 }
879         }
880
881         if (request_flags & WBFLAG_PAM_AFS_TOKEN) {
882                 result = append_afs_token(mem_ctx, resp,
883                                           info3, name_domain, name_user);
884                 if (!NT_STATUS_IS_OK(result)) {
885                         DEBUG(10,("Failed to append AFS token: %s\n",
886                                 nt_errstr(result)));
887                         return result;
888                 }
889         }
890
891         return NT_STATUS_OK;
892 }
893
894 static NTSTATUS winbindd_dual_pam_auth_cached(struct winbindd_domain *domain,
895                                               struct winbindd_cli_state *state,
896                                               struct netr_SamInfo3 **info3)
897 {
898         NTSTATUS result = NT_STATUS_LOGON_FAILURE;
899         uint16_t max_allowed_bad_attempts;
900         fstring name_domain, name_user;
901         struct dom_sid sid;
902         enum lsa_SidType type;
903         uchar new_nt_pass[NT_HASH_LEN];
904         const uint8_t *cached_nt_pass;
905         const uint8_t *cached_salt;
906         struct netr_SamInfo3 *my_info3;
907         time_t kickoff_time, must_change_time;
908         bool password_good = false;
909 #ifdef HAVE_KRB5
910         struct winbindd_tdc_domain *tdc_domain = NULL;
911 #endif
912
913         *info3 = NULL;
914
915         ZERO_STRUCTP(info3);
916
917         DEBUG(10,("winbindd_dual_pam_auth_cached\n"));
918
919         /* Parse domain and username */
920
921         parse_domain_user(state->request->data.auth.user, name_domain, name_user);
922
923
924         if (!lookup_cached_name(name_domain,
925                                 name_user,
926                                 &sid,
927                                 &type)) {
928                 DEBUG(10,("winbindd_dual_pam_auth_cached: no such user in the cache\n"));
929                 return NT_STATUS_NO_SUCH_USER;
930         }
931
932         if (type != SID_NAME_USER) {
933                 DEBUG(10,("winbindd_dual_pam_auth_cached: not a user (%s)\n", sid_type_lookup(type)));
934                 return NT_STATUS_LOGON_FAILURE;
935         }
936
937         result = winbindd_get_creds(domain,
938                                     state->mem_ctx,
939                                     &sid,
940                                     &my_info3,
941                                     &cached_nt_pass,
942                                     &cached_salt);
943         if (!NT_STATUS_IS_OK(result)) {
944                 DEBUG(10,("winbindd_dual_pam_auth_cached: failed to get creds: %s\n", nt_errstr(result)));
945                 return result;
946         }
947
948         *info3 = my_info3;
949
950         E_md4hash(state->request->data.auth.pass, new_nt_pass);
951
952         dump_data_pw("new_nt_pass", new_nt_pass, NT_HASH_LEN);
953         dump_data_pw("cached_nt_pass", cached_nt_pass, NT_HASH_LEN);
954         if (cached_salt) {
955                 dump_data_pw("cached_salt", cached_salt, NT_HASH_LEN);
956         }
957
958         if (cached_salt) {
959                 /* In this case we didn't store the nt_hash itself,
960                    but the MD5 combination of salt + nt_hash. */
961                 uchar salted_hash[NT_HASH_LEN];
962                 E_md5hash(cached_salt, new_nt_pass, salted_hash);
963
964                 password_good = (memcmp(cached_nt_pass, salted_hash,
965                                         NT_HASH_LEN) == 0);
966         } else {
967                 /* Old cached cred - direct store of nt_hash (bad bad bad !). */
968                 password_good = (memcmp(cached_nt_pass, new_nt_pass,
969                                         NT_HASH_LEN) == 0);
970         }
971
972         if (password_good) {
973
974                 /* User *DOES* know the password, update logon_time and reset
975                  * bad_pw_count */
976
977                 my_info3->base.user_flags |= NETLOGON_CACHED_ACCOUNT;
978
979                 if (my_info3->base.acct_flags & ACB_AUTOLOCK) {
980                         return NT_STATUS_ACCOUNT_LOCKED_OUT;
981                 }
982
983                 if (my_info3->base.acct_flags & ACB_DISABLED) {
984                         return NT_STATUS_ACCOUNT_DISABLED;
985                 }
986
987                 if (my_info3->base.acct_flags & ACB_WSTRUST) {
988                         return NT_STATUS_NOLOGON_WORKSTATION_TRUST_ACCOUNT;
989                 }
990
991                 if (my_info3->base.acct_flags & ACB_SVRTRUST) {
992                         return NT_STATUS_NOLOGON_SERVER_TRUST_ACCOUNT;
993                 }
994
995                 if (my_info3->base.acct_flags & ACB_DOMTRUST) {
996                         return NT_STATUS_NOLOGON_INTERDOMAIN_TRUST_ACCOUNT;
997                 }
998
999                 if (!(my_info3->base.acct_flags & ACB_NORMAL)) {
1000                         DEBUG(0,("winbindd_dual_pam_auth_cached: whats wrong with that one?: 0x%08x\n",
1001                                 my_info3->base.acct_flags));
1002                         return NT_STATUS_LOGON_FAILURE;
1003                 }
1004
1005                 kickoff_time = nt_time_to_unix(my_info3->base.kickoff_time);
1006                 if (kickoff_time != 0 && time(NULL) > kickoff_time) {
1007                         return NT_STATUS_ACCOUNT_EXPIRED;
1008                 }
1009
1010                 must_change_time = nt_time_to_unix(my_info3->base.force_password_change);
1011                 if (must_change_time != 0 && must_change_time < time(NULL)) {
1012                         /* we allow grace logons when the password has expired */
1013                         my_info3->base.user_flags |= NETLOGON_GRACE_LOGON;
1014                         /* return NT_STATUS_PASSWORD_EXPIRED; */
1015                         goto success;
1016                 }
1017
1018 #ifdef HAVE_KRB5
1019                 if ((state->request->flags & WBFLAG_PAM_KRB5) &&
1020                     ((tdc_domain = wcache_tdc_fetch_domain(state->mem_ctx, name_domain)) != NULL) &&
1021                     ((tdc_domain->trust_type & LSA_TRUST_TYPE_UPLEVEL) ||
1022                     /* used to cope with the case winbindd starting without network. */
1023                     !strequal(tdc_domain->domain_name, tdc_domain->dns_name))) {
1024
1025                         uid_t uid = -1;
1026                         const char *cc = NULL;
1027                         char *realm = NULL;
1028                         const char *principal_s = NULL;
1029                         const char *service = NULL;
1030                         const char *user_ccache_file;
1031
1032                         if (domain->alt_name == NULL) {
1033                                 return NT_STATUS_INVALID_PARAMETER;
1034                         }
1035
1036                         uid = get_uid_from_request(state->request);
1037                         if (uid == -1) {
1038                                 DEBUG(0,("winbindd_dual_pam_auth_cached: invalid uid\n"));
1039                                 return NT_STATUS_INVALID_PARAMETER;
1040                         }
1041
1042                         cc = generate_krb5_ccache(state->mem_ctx,
1043                                                 state->request->data.auth.krb5_cc_type,
1044                                                 state->request->data.auth.uid,
1045                                                 &user_ccache_file);
1046                         if (cc == NULL) {
1047                                 return NT_STATUS_NO_MEMORY;
1048                         }
1049
1050                         realm = talloc_strdup(state->mem_ctx, domain->alt_name);
1051                         if (realm == NULL) {
1052                                 return NT_STATUS_NO_MEMORY;
1053                         }
1054
1055                         if (!strupper_m(realm)) {
1056                                 return NT_STATUS_INVALID_PARAMETER;
1057                         }
1058
1059                         principal_s = talloc_asprintf(state->mem_ctx, "%s@%s", name_user, realm);
1060                         if (principal_s == NULL) {
1061                                 return NT_STATUS_NO_MEMORY;
1062                         }
1063
1064                         service = talloc_asprintf(state->mem_ctx, "%s/%s@%s", KRB5_TGS_NAME, realm, realm);
1065                         if (service == NULL) {
1066                                 return NT_STATUS_NO_MEMORY;
1067                         }
1068
1069                         if (user_ccache_file != NULL) {
1070
1071                                 fstrcpy(state->response->data.auth.krb5ccname,
1072                                         user_ccache_file);
1073
1074                                 result = add_ccache_to_list(principal_s,
1075                                                             cc,
1076                                                             service,
1077                                                             state->request->data.auth.user,
1078                                                             state->request->data.auth.pass,
1079                                                             realm,
1080                                                             uid,
1081                                                             time(NULL),
1082                                                             time(NULL) + lp_winbind_cache_time(),
1083                                                             time(NULL) + WINBINDD_PAM_AUTH_KRB5_RENEW_TIME,
1084                                                             true);
1085
1086                                 if (!NT_STATUS_IS_OK(result)) {
1087                                         DEBUG(10,("winbindd_dual_pam_auth_cached: failed "
1088                                                 "to add ccache to list: %s\n",
1089                                                 nt_errstr(result)));
1090                                 }
1091                         }
1092                 }
1093 #endif /* HAVE_KRB5 */
1094  success:
1095                 /* FIXME: we possibly should handle logon hours as well (does xp when
1096                  * offline?) see auth/auth_sam.c:sam_account_ok for details */
1097
1098                 unix_to_nt_time(&my_info3->base.logon_time, time(NULL));
1099                 my_info3->base.bad_password_count = 0;
1100
1101                 result = winbindd_update_creds_by_info3(domain,
1102                                                         state->request->data.auth.user,
1103                                                         state->request->data.auth.pass,
1104                                                         my_info3);
1105                 if (!NT_STATUS_IS_OK(result)) {
1106                         DEBUG(1,("winbindd_dual_pam_auth_cached: failed to update creds: %s\n",
1107                                 nt_errstr(result)));
1108                         return result;
1109                 }
1110
1111                 return NT_STATUS_OK;
1112
1113         }
1114
1115         /* User does *NOT* know the correct password, modify info3 accordingly, but only if online */
1116         if (domain->online == false) {
1117                 goto failed;
1118         }
1119
1120         /* failure of this is not critical */
1121         result = get_max_bad_attempts_from_lockout_policy(domain, state->mem_ctx, &max_allowed_bad_attempts);
1122         if (!NT_STATUS_IS_OK(result)) {
1123                 DEBUG(10,("winbindd_dual_pam_auth_cached: failed to get max_allowed_bad_attempts. "
1124                           "Won't be able to honour account lockout policies\n"));
1125         }
1126
1127         /* increase counter */
1128         my_info3->base.bad_password_count++;
1129
1130         if (max_allowed_bad_attempts == 0) {
1131                 goto failed;
1132         }
1133
1134         /* lockout user */
1135         if (my_info3->base.bad_password_count >= max_allowed_bad_attempts) {
1136
1137                 uint32_t password_properties;
1138
1139                 result = get_pwd_properties(domain, state->mem_ctx, &password_properties);
1140                 if (!NT_STATUS_IS_OK(result)) {
1141                         DEBUG(10,("winbindd_dual_pam_auth_cached: failed to get password properties.\n"));
1142                 }
1143
1144                 if ((my_info3->base.rid != DOMAIN_RID_ADMINISTRATOR) ||
1145                     (password_properties & DOMAIN_PASSWORD_LOCKOUT_ADMINS)) {
1146                         my_info3->base.acct_flags |= ACB_AUTOLOCK;
1147                 }
1148         }
1149
1150 failed:
1151         result = winbindd_update_creds_by_info3(domain,
1152                                                 state->request->data.auth.user,
1153                                                 NULL,
1154                                                 my_info3);
1155
1156         if (!NT_STATUS_IS_OK(result)) {
1157                 DEBUG(0,("winbindd_dual_pam_auth_cached: failed to update creds %s\n",
1158                         nt_errstr(result)));
1159         }
1160
1161         return NT_STATUS_LOGON_FAILURE;
1162 }
1163
1164 static NTSTATUS winbindd_dual_pam_auth_kerberos(struct winbindd_domain *domain,
1165                                                 struct winbindd_cli_state *state,
1166                                                 struct netr_SamInfo3 **info3)
1167 {
1168         struct winbindd_domain *contact_domain;
1169         fstring name_domain, name_user;
1170         NTSTATUS result;
1171
1172         DEBUG(10,("winbindd_dual_pam_auth_kerberos\n"));
1173
1174         /* Parse domain and username */
1175
1176         parse_domain_user(state->request->data.auth.user, name_domain, name_user);
1177
1178         /* what domain should we contact? */
1179
1180         if ( IS_DC ) {
1181                 if (!(contact_domain = find_domain_from_name(name_domain))) {
1182                         DEBUG(3, ("Authentication for domain for [%s] -> [%s]\\[%s] failed as %s is not a trusted domain\n",
1183                                   state->request->data.auth.user, name_domain, name_user, name_domain));
1184                         result = NT_STATUS_NO_SUCH_USER;
1185                         goto done;
1186                 }
1187
1188         } else {
1189                 if (is_myname(name_domain)) {
1190                         DEBUG(3, ("Authentication for domain %s (local domain to this server) not supported at this stage\n", name_domain));
1191                         result =  NT_STATUS_NO_SUCH_USER;
1192                         goto done;
1193                 }
1194
1195                 contact_domain = find_domain_from_name(name_domain);
1196                 if (contact_domain == NULL) {
1197                         DEBUG(3, ("Authentication for domain for [%s] -> [%s]\\[%s] failed as %s is not a trusted domain\n",
1198                                   state->request->data.auth.user, name_domain, name_user, name_domain));
1199
1200                         result =  NT_STATUS_NO_SUCH_USER;
1201                         goto done;
1202                 }
1203         }
1204
1205         if (contact_domain->initialized &&
1206             contact_domain->active_directory) {
1207                 goto try_login;
1208         }
1209
1210         if (!contact_domain->initialized) {
1211                 init_dc_connection(contact_domain, false);
1212         }
1213
1214         if (!contact_domain->active_directory) {
1215                 DEBUG(3,("krb5 auth requested but domain is not Active Directory\n"));
1216                 return NT_STATUS_INVALID_LOGON_TYPE;
1217         }
1218 try_login:
1219         result = winbindd_raw_kerberos_login(
1220                 state->mem_ctx, contact_domain,
1221                 state->request->data.auth.user,
1222                 state->request->data.auth.pass,
1223                 state->request->data.auth.krb5_cc_type,
1224                 get_uid_from_request(state->request),
1225                 info3, state->response->data.auth.krb5ccname);
1226 done:
1227         return result;
1228 }
1229
1230 static NTSTATUS winbindd_dual_auth_passdb(TALLOC_CTX *mem_ctx,
1231                                           uint32_t logon_parameters,
1232                                           const char *domain, const char *user,
1233                                           const DATA_BLOB *challenge,
1234                                           const DATA_BLOB *lm_resp,
1235                                           const DATA_BLOB *nt_resp,
1236                                           struct netr_SamInfo3 **pinfo3)
1237 {
1238         struct auth_context *auth_context;
1239         struct auth_serversupplied_info *server_info;
1240         struct auth_usersupplied_info *user_info = NULL;
1241         struct tsocket_address *local;
1242         struct netr_SamInfo3 *info3;
1243         NTSTATUS status;
1244         int rc;
1245         TALLOC_CTX *frame = talloc_stackframe();
1246
1247         rc = tsocket_address_inet_from_strings(frame,
1248                                                "ip",
1249                                                "127.0.0.1",
1250                                                0,
1251                                                &local);
1252         if (rc < 0) {
1253                 TALLOC_FREE(frame);
1254                 return NT_STATUS_NO_MEMORY;
1255         }
1256         status = make_user_info(frame, &user_info, user, user, domain, domain,
1257                                 lp_netbios_name(), local, lm_resp, nt_resp, NULL, NULL,
1258                                 NULL, AUTH_PASSWORD_RESPONSE);
1259         if (!NT_STATUS_IS_OK(status)) {
1260                 DEBUG(10, ("make_user_info failed: %s\n", nt_errstr(status)));
1261                 TALLOC_FREE(frame);
1262                 return status;
1263         }
1264
1265         user_info->logon_parameters = logon_parameters;
1266
1267         /* We don't want any more mapping of the username */
1268         user_info->mapped_state = True;
1269
1270         /* We don't want to come back to winbindd or to do PAM account checks */
1271         user_info->flags |= USER_INFO_LOCAL_SAM_ONLY | USER_INFO_INFO3_AND_NO_AUTHZ;
1272
1273         status = make_auth_context_fixed(frame, &auth_context, challenge->data);
1274
1275         if (!NT_STATUS_IS_OK(status)) {
1276                 DEBUG(0, ("Failed to test authentication with check_sam_security_info3: %s\n", nt_errstr(status)));
1277                 TALLOC_FREE(frame);
1278                 return status;
1279         }
1280
1281         status = auth_check_ntlm_password(mem_ctx,
1282                                           auth_context,
1283                                           user_info,
1284                                           &server_info);
1285
1286         if (!NT_STATUS_IS_OK(status)) {
1287                 TALLOC_FREE(frame);
1288                 return status;
1289         }
1290
1291         info3 = talloc_zero(mem_ctx, struct netr_SamInfo3);
1292         if (info3 == NULL) {
1293                 TALLOC_FREE(frame);
1294                 return NT_STATUS_NO_MEMORY;
1295         }
1296
1297         status = serverinfo_to_SamInfo3(server_info, info3);
1298         if (!NT_STATUS_IS_OK(status)) {
1299                 TALLOC_FREE(frame);
1300                 TALLOC_FREE(info3);
1301                 DEBUG(0, ("serverinfo_to_SamInfo3 failed: %s\n",
1302                           nt_errstr(status)));
1303                 return status;
1304         }
1305
1306         *pinfo3 = info3;
1307         DEBUG(10, ("Authenticaticating user %s\\%s returned %s\n", domain,
1308                    user, nt_errstr(status)));
1309         TALLOC_FREE(frame);
1310         return status;
1311 }
1312
1313 static NTSTATUS winbind_samlogon_retry_loop(struct winbindd_domain *domain,
1314                                             TALLOC_CTX *mem_ctx,
1315                                             uint32_t logon_parameters,
1316                                             const char *username,
1317                                             const char *password,
1318                                             const char *domainname,
1319                                             const char *workstation,
1320                                             const uint8_t chal[8],
1321                                             DATA_BLOB lm_response,
1322                                             DATA_BLOB nt_response,
1323                                             bool interactive,
1324                                             struct netr_SamInfo3 **info3)
1325 {
1326         int attempts = 0;
1327         int netr_attempts = 0;
1328         bool retry = false;
1329         NTSTATUS result;
1330
1331         do {
1332                 struct rpc_pipe_client *netlogon_pipe;
1333                 uint8_t authoritative = 0;
1334                 uint32_t flags = 0;
1335
1336                 ZERO_STRUCTP(info3);
1337                 retry = false;
1338
1339                 result = cm_connect_netlogon(domain, &netlogon_pipe);
1340
1341                 if (!NT_STATUS_IS_OK(result)) {
1342                         DEBUG(3,("Could not open handle to NETLOGON pipe "
1343                                  "(error: %s, attempts: %d)\n",
1344                                   nt_errstr(result), netr_attempts));
1345
1346                         /* After the first retry always close the connection */
1347                         if (netr_attempts > 0) {
1348                                 DEBUG(3, ("This is again a problem for this "
1349                                           "particular call, forcing the close "
1350                                           "of this connection\n"));
1351                                 invalidate_cm_connection(domain);
1352                         }
1353
1354                         /* After the second retry failover to the next DC */
1355                         if (netr_attempts > 1) {
1356                                 /*
1357                                  * If the netlogon server is not reachable then
1358                                  * it is possible that the DC is rebuilding
1359                                  * sysvol and shutdown netlogon for that time.
1360                                  * We should failover to the next dc.
1361                                  */
1362                                 DEBUG(3, ("This is the third problem for this "
1363                                           "particular call, adding DC to the "
1364                                           "negative cache list\n"));
1365                                 add_failed_connection_entry(domain->name,
1366                                                             domain->dcname,
1367                                                             result);
1368                                 saf_delete(domain->name);
1369                         }
1370
1371                         /* Only allow 3 retries */
1372                         if (netr_attempts < 3) {
1373                                 DEBUG(3, ("The connection to netlogon "
1374                                           "failed, retrying\n"));
1375                                 netr_attempts++;
1376                                 retry = true;
1377                                 continue;
1378                         }
1379                         return result;
1380                 }
1381                 netr_attempts = 0;
1382                 if (domain->conn.netlogon_creds == NULL) {
1383                         DBG_NOTICE("No security credentials available for "
1384                                   "domain [%s]\n", domainname);
1385                         result = NT_STATUS_CANT_ACCESS_DOMAIN_INFO;
1386                 } else if (interactive && username != NULL && password != NULL) {
1387                         result = rpccli_netlogon_password_logon(domain->conn.netlogon_creds,
1388                                                                 netlogon_pipe->binding_handle,
1389                                                                 mem_ctx,
1390                                                                 logon_parameters,
1391                                                                 domainname,
1392                                                                 username,
1393                                                                 password,
1394                                                                 workstation,
1395                                                                 NetlogonInteractiveInformation,
1396                                                                 info3);
1397                 } else {
1398                         result = rpccli_netlogon_network_logon(domain->conn.netlogon_creds,
1399                                                         netlogon_pipe->binding_handle,
1400                                                         mem_ctx,
1401                                                         logon_parameters,
1402                                                         username,
1403                                                         domainname,
1404                                                         workstation,
1405                                                         chal,
1406                                                         lm_response,
1407                                                         nt_response,
1408                                                         &authoritative,
1409                                                         &flags,
1410                                                         info3);
1411                 }
1412
1413                 /*
1414                  * we increment this after the "feature negotiation"
1415                  * for can_do_samlogon_ex and can_do_validation6
1416                  */
1417                 attempts += 1;
1418
1419                 /* We have to try a second time as cm_connect_netlogon
1420                    might not yet have noticed that the DC has killed
1421                    our connection. */
1422
1423                 if (!rpccli_is_connected(netlogon_pipe)) {
1424                         retry = true;
1425                         continue;
1426                 }
1427
1428                 /* if we get access denied, a possible cause was that we had
1429                    and open connection to the DC, but someone changed our
1430                    machine account password out from underneath us using 'net
1431                    rpc changetrustpw' */
1432
1433                 if ( NT_STATUS_EQUAL(result, NT_STATUS_ACCESS_DENIED) ) {
1434                         DEBUG(1,("winbind_samlogon_retry_loop: sam_logon returned "
1435                                  "ACCESS_DENIED.  Maybe the DC has Restrict "
1436                                  "NTLM set or the trust account "
1437                                 "password was changed and we didn't know it. "
1438                                  "Killing connections to domain %s\n",
1439                                 domainname));
1440                         invalidate_cm_connection(domain);
1441                         retry = true;
1442                 }
1443
1444                 if (NT_STATUS_EQUAL(result, NT_STATUS_RPC_PROCNUM_OUT_OF_RANGE)) {
1445                         /*
1446                          * Got DCERPC_FAULT_OP_RNG_ERROR for SamLogon
1447                          * (no Ex). This happens against old Samba
1448                          * DCs, if LogonSamLogonEx() fails with an error
1449                          * e.g. NT_STATUS_NO_SUCH_USER or NT_STATUS_WRONG_PASSWORD.
1450                          *
1451                          * The server will log something like this:
1452                          * api_net_sam_logon_ex: Failed to marshall NET_R_SAM_LOGON_EX.
1453                          *
1454                          * This sets the whole connection into a fault_state mode
1455                          * and all following request get NT_STATUS_RPC_PROCNUM_OUT_OF_RANGE.
1456                          *
1457                          * This also happens to our retry with LogonSamLogonWithFlags()
1458                          * and LogonSamLogon().
1459                          *
1460                          * In order to recover from this situation, we need to
1461                          * drop the connection.
1462                          */
1463                         invalidate_cm_connection(domain);
1464                         result = NT_STATUS_LOGON_FAILURE;
1465                         break;
1466                 }
1467
1468         } while ( (attempts < 2) && retry );
1469
1470         if (NT_STATUS_EQUAL(result, NT_STATUS_IO_TIMEOUT)) {
1471                 DEBUG(3,("winbind_samlogon_retry_loop: sam_network_logon(ex) "
1472                                 "returned NT_STATUS_IO_TIMEOUT after the retry."
1473                                 "Killing connections to domain %s\n",
1474                         domainname));
1475                 invalidate_cm_connection(domain);
1476         }
1477         return result;
1478 }
1479
1480 static NTSTATUS winbindd_dual_pam_auth_samlogon(TALLOC_CTX *mem_ctx,
1481                                                 struct winbindd_domain *domain,
1482                                                 const char *user,
1483                                                 const char *pass,
1484                                                 uint32_t request_flags,
1485                                                 struct netr_SamInfo3 **info3)
1486 {
1487
1488         uchar chal[8];
1489         DATA_BLOB lm_resp;
1490         DATA_BLOB nt_resp;
1491         unsigned char local_nt_response[24];
1492         fstring name_domain, name_user;
1493         NTSTATUS result;
1494         struct netr_SamInfo3 *my_info3 = NULL;
1495
1496         *info3 = NULL;
1497
1498         DEBUG(10,("winbindd_dual_pam_auth_samlogon\n"));
1499
1500         /* Parse domain and username */
1501
1502         parse_domain_user(user, name_domain, name_user);
1503
1504         /* do password magic */
1505
1506         generate_random_buffer(chal, sizeof(chal));
1507
1508         if (lp_client_ntlmv2_auth()) {
1509                 DATA_BLOB server_chal;
1510                 DATA_BLOB names_blob;
1511                 server_chal = data_blob_const(chal, 8);
1512
1513                 /* note that the 'workgroup' here is for the local
1514                    machine.  The 'server name' must match the
1515                    'workstation' passed to the actual SamLogon call.
1516                 */
1517                 names_blob = NTLMv2_generate_names_blob(
1518                         mem_ctx, lp_netbios_name(), lp_workgroup());
1519
1520                 if (!SMBNTLMv2encrypt(mem_ctx, name_user, name_domain,
1521                                       pass,
1522                                       &server_chal,
1523                                       &names_blob,
1524                                       &lm_resp, &nt_resp, NULL, NULL)) {
1525                         data_blob_free(&names_blob);
1526                         DEBUG(0, ("winbindd_pam_auth: SMBNTLMv2encrypt() failed!\n"));
1527                         result = NT_STATUS_NO_MEMORY;
1528                         goto done;
1529                 }
1530                 data_blob_free(&names_blob);
1531         } else {
1532                 lm_resp = data_blob_null;
1533                 SMBNTencrypt(pass, chal, local_nt_response);
1534
1535                 nt_resp = data_blob_talloc(mem_ctx, local_nt_response,
1536                                            sizeof(local_nt_response));
1537         }
1538
1539         if (strequal(name_domain, get_global_sam_name())) {
1540                 DATA_BLOB chal_blob = data_blob_const(chal, sizeof(chal));
1541
1542                 result = winbindd_dual_auth_passdb(
1543                         mem_ctx, 0, name_domain, name_user,
1544                         &chal_blob, &lm_resp, &nt_resp, info3);
1545
1546                 /* 
1547                  * We need to try the remote NETLOGON server if this is NOT_IMPLEMENTED 
1548                  */
1549                 if (!NT_STATUS_EQUAL(result, NT_STATUS_NOT_IMPLEMENTED)) {
1550                         goto done;
1551                 }
1552         }
1553
1554         /* check authentication loop */
1555
1556         result = winbind_samlogon_retry_loop(domain,
1557                                              mem_ctx,
1558                                              0,
1559                                              name_user,
1560                                              pass,
1561                                              name_domain,
1562                                              lp_netbios_name(),
1563                                              chal,
1564                                              lm_resp,
1565                                              nt_resp,
1566                                              true, /* interactive */
1567                                              &my_info3);
1568         if (!NT_STATUS_IS_OK(result)) {
1569                 goto done;
1570         }
1571
1572         /* handle the case where a NT4 DC does not fill in the acct_flags in
1573          * the samlogon reply info3. When accurate info3 is required by the
1574          * caller, we look up the account flags ourselve - gd */
1575
1576         if ((request_flags & WBFLAG_PAM_INFO3_TEXT) &&
1577             NT_STATUS_IS_OK(result) && (my_info3->base.acct_flags == 0)) {
1578
1579                 struct rpc_pipe_client *samr_pipe;
1580                 struct policy_handle samr_domain_handle, user_pol;
1581                 union samr_UserInfo *info = NULL;
1582                 NTSTATUS status_tmp, result_tmp;
1583                 uint32_t acct_flags;
1584                 struct dcerpc_binding_handle *b;
1585
1586                 status_tmp = cm_connect_sam(domain, mem_ctx, false,
1587                                             &samr_pipe, &samr_domain_handle);
1588
1589                 if (!NT_STATUS_IS_OK(status_tmp)) {
1590                         DEBUG(3, ("could not open handle to SAMR pipe: %s\n",
1591                                 nt_errstr(status_tmp)));
1592                         goto done;
1593                 }
1594
1595                 b = samr_pipe->binding_handle;
1596
1597                 status_tmp = dcerpc_samr_OpenUser(b, mem_ctx,
1598                                                   &samr_domain_handle,
1599                                                   MAXIMUM_ALLOWED_ACCESS,
1600                                                   my_info3->base.rid,
1601                                                   &user_pol,
1602                                                   &result_tmp);
1603
1604                 if (!NT_STATUS_IS_OK(status_tmp)) {
1605                         DEBUG(3, ("could not open user handle on SAMR pipe: %s\n",
1606                                 nt_errstr(status_tmp)));
1607                         goto done;
1608                 }
1609                 if (!NT_STATUS_IS_OK(result_tmp)) {
1610                         DEBUG(3, ("could not open user handle on SAMR pipe: %s\n",
1611                                 nt_errstr(result_tmp)));
1612                         goto done;
1613                 }
1614
1615                 status_tmp = dcerpc_samr_QueryUserInfo(b, mem_ctx,
1616                                                        &user_pol,
1617                                                        16,
1618                                                        &info,
1619                                                        &result_tmp);
1620
1621                 if (!NT_STATUS_IS_OK(status_tmp)) {
1622                         DEBUG(3, ("could not query user info on SAMR pipe: %s\n",
1623                                 nt_errstr(status_tmp)));
1624                         dcerpc_samr_Close(b, mem_ctx, &user_pol, &result_tmp);
1625                         goto done;
1626                 }
1627                 if (!NT_STATUS_IS_OK(result_tmp)) {
1628                         DEBUG(3, ("could not query user info on SAMR pipe: %s\n",
1629                                 nt_errstr(result_tmp)));
1630                         dcerpc_samr_Close(b, mem_ctx, &user_pol, &result_tmp);
1631                         goto done;
1632                 }
1633
1634                 acct_flags = info->info16.acct_flags;
1635
1636                 if (acct_flags == 0) {
1637                         dcerpc_samr_Close(b, mem_ctx, &user_pol, &result_tmp);
1638                         goto done;
1639                 }
1640
1641                 my_info3->base.acct_flags = acct_flags;
1642
1643                 DEBUG(10,("successfully retrieved acct_flags 0x%x\n", acct_flags));
1644
1645                 dcerpc_samr_Close(b, mem_ctx, &user_pol, &result_tmp);
1646         }
1647
1648         *info3 = my_info3;
1649 done:
1650         return result;
1651 }
1652
1653 enum winbindd_result winbindd_dual_pam_auth(struct winbindd_domain *domain,
1654                                             struct winbindd_cli_state *state)
1655 {
1656         NTSTATUS result = NT_STATUS_LOGON_FAILURE;
1657         NTSTATUS krb5_result = NT_STATUS_OK;
1658         fstring name_domain, name_user;
1659         char *mapped_user;
1660         fstring domain_user;
1661         struct netr_SamInfo3 *info3 = NULL;
1662         NTSTATUS name_map_status = NT_STATUS_UNSUCCESSFUL;
1663
1664         /* Ensure null termination */
1665         state->request->data.auth.user[sizeof(state->request->data.auth.user)-1]='\0';
1666
1667         /* Ensure null termination */
1668         state->request->data.auth.pass[sizeof(state->request->data.auth.pass)-1]='\0';
1669
1670         DEBUG(3, ("[%5lu]: dual pam auth %s\n", (unsigned long)state->pid,
1671                   state->request->data.auth.user));
1672
1673         /* Parse domain and username */
1674
1675         name_map_status = normalize_name_unmap(state->mem_ctx,
1676                                                state->request->data.auth.user,
1677                                                &mapped_user);
1678
1679         /* If the name normalization didnt' actually do anything,
1680            just use the original name */
1681
1682         if (!NT_STATUS_IS_OK(name_map_status) &&
1683             !NT_STATUS_EQUAL(name_map_status, NT_STATUS_FILE_RENAMED))
1684         {
1685                 mapped_user = state->request->data.auth.user;
1686         }
1687
1688         parse_domain_user(mapped_user, name_domain, name_user);
1689
1690         if ( mapped_user != state->request->data.auth.user ) {
1691                 fstr_sprintf( domain_user, "%s%c%s", name_domain,
1692                         *lp_winbind_separator(),
1693                         name_user );
1694                 strlcpy( state->request->data.auth.user, domain_user,
1695                              sizeof(state->request->data.auth.user));
1696         }
1697
1698         if (!domain->online) {
1699                 result = NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND;
1700                 if (domain->startup) {
1701                         /* Logons are very important to users. If we're offline and
1702                            we get a request within the first 30 seconds of startup,
1703                            try very hard to find a DC and go online. */
1704
1705                         DEBUG(10,("winbindd_dual_pam_auth: domain: %s offline and auth "
1706                                 "request in startup mode.\n", domain->name ));
1707
1708                         winbindd_flush_negative_conn_cache(domain);
1709                         result = init_dc_connection(domain, false);
1710                 }
1711         }
1712
1713         DEBUG(10,("winbindd_dual_pam_auth: domain: %s last was %s\n", domain->name, domain->online ? "online":"offline"));
1714
1715         /* Check for Kerberos authentication */
1716         if (domain->online && (state->request->flags & WBFLAG_PAM_KRB5)) {
1717
1718                 result = winbindd_dual_pam_auth_kerberos(domain, state, &info3);
1719                 /* save for later */
1720                 krb5_result = result;
1721
1722
1723                 if (NT_STATUS_IS_OK(result)) {
1724                         DEBUG(10,("winbindd_dual_pam_auth_kerberos succeeded\n"));
1725                         goto process_result;
1726                 } else {
1727                         DEBUG(10,("winbindd_dual_pam_auth_kerberos failed: %s\n", nt_errstr(result)));
1728                 }
1729
1730                 if (NT_STATUS_EQUAL(result, NT_STATUS_NO_LOGON_SERVERS) ||
1731                     NT_STATUS_EQUAL(result, NT_STATUS_IO_TIMEOUT) ||
1732                     NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
1733                         DEBUG(10,("winbindd_dual_pam_auth_kerberos setting domain to offline\n"));
1734                         set_domain_offline( domain );
1735                         goto cached_logon;
1736                 }
1737
1738                 /* there are quite some NT_STATUS errors where there is no
1739                  * point in retrying with a samlogon, we explictly have to take
1740                  * care not to increase the bad logon counter on the DC */
1741
1742                 if (NT_STATUS_EQUAL(result, NT_STATUS_ACCOUNT_DISABLED) ||
1743                     NT_STATUS_EQUAL(result, NT_STATUS_ACCOUNT_EXPIRED) ||
1744                     NT_STATUS_EQUAL(result, NT_STATUS_ACCOUNT_LOCKED_OUT) ||
1745                     NT_STATUS_EQUAL(result, NT_STATUS_INVALID_LOGON_HOURS) ||
1746                     NT_STATUS_EQUAL(result, NT_STATUS_INVALID_WORKSTATION) ||
1747                     NT_STATUS_EQUAL(result, NT_STATUS_LOGON_FAILURE) ||
1748                     NT_STATUS_EQUAL(result, NT_STATUS_NO_SUCH_USER) ||
1749                     NT_STATUS_EQUAL(result, NT_STATUS_PASSWORD_EXPIRED) ||
1750                     NT_STATUS_EQUAL(result, NT_STATUS_PASSWORD_MUST_CHANGE) ||
1751                     NT_STATUS_EQUAL(result, NT_STATUS_WRONG_PASSWORD)) {
1752                         goto done;
1753                 }
1754
1755                 if (state->request->flags & WBFLAG_PAM_FALLBACK_AFTER_KRB5) {
1756                         DEBUG(3,("falling back to samlogon\n"));
1757                         goto sam_logon;
1758                 } else {
1759                         goto cached_logon;
1760                 }
1761         }
1762
1763 sam_logon:
1764         /* Check for Samlogon authentication */
1765         if (domain->online) {
1766                 result = winbindd_dual_pam_auth_samlogon(
1767                         state->mem_ctx, domain,
1768                         state->request->data.auth.user,
1769                         state->request->data.auth.pass,
1770                         state->request->flags,
1771                         &info3);
1772
1773                 if (NT_STATUS_IS_OK(result)) {
1774                         DEBUG(10,("winbindd_dual_pam_auth_samlogon succeeded\n"));
1775                         /* add the Krb5 err if we have one */
1776                         if ( NT_STATUS_EQUAL(krb5_result, NT_STATUS_TIME_DIFFERENCE_AT_DC ) ) {
1777                                 info3->base.user_flags |= LOGON_KRB5_FAIL_CLOCK_SKEW;
1778                         }
1779                         goto process_result;
1780                 }
1781
1782                 DEBUG(10,("winbindd_dual_pam_auth_samlogon failed: %s\n",
1783                           nt_errstr(result)));
1784
1785                 if (NT_STATUS_EQUAL(result, NT_STATUS_NO_LOGON_SERVERS) ||
1786                     NT_STATUS_EQUAL(result, NT_STATUS_IO_TIMEOUT) ||
1787                     NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND))
1788                 {
1789                         DEBUG(10,("winbindd_dual_pam_auth_samlogon setting domain to offline\n"));
1790                         set_domain_offline( domain );
1791                         goto cached_logon;
1792                 }
1793
1794                         if (domain->online) {
1795                                 /* We're still online - fail. */
1796                                 goto done;
1797                         }
1798         }
1799
1800 cached_logon:
1801         /* Check for Cached logons */
1802         if (!domain->online && (state->request->flags & WBFLAG_PAM_CACHED_LOGIN) &&
1803             lp_winbind_offline_logon()) {
1804
1805                 result = winbindd_dual_pam_auth_cached(domain, state, &info3);
1806
1807                 if (NT_STATUS_IS_OK(result)) {
1808                         DEBUG(10,("winbindd_dual_pam_auth_cached succeeded\n"));
1809                         goto process_result;
1810                 } else {
1811                         DEBUG(10,("winbindd_dual_pam_auth_cached failed: %s\n", nt_errstr(result)));
1812                         goto done;
1813                 }
1814         }
1815
1816 process_result:
1817
1818         if (NT_STATUS_IS_OK(result)) {
1819
1820                 struct dom_sid user_sid;
1821
1822                 /* In all codepaths where result == NT_STATUS_OK info3 must have
1823                    been initialized. */
1824                 if (!info3) {
1825                         result = NT_STATUS_INTERNAL_ERROR;
1826                         goto done;
1827                 }
1828
1829                 sid_compose(&user_sid, info3->base.domain_sid,
1830                             info3->base.rid);
1831
1832                 if (info3->base.full_name.string == NULL) {
1833                         struct netr_SamInfo3 *cached_info3;
1834
1835                         cached_info3 = netsamlogon_cache_get(state->mem_ctx,
1836                                                              &user_sid);
1837                         if (cached_info3 != NULL &&
1838                             cached_info3->base.full_name.string != NULL) {
1839                                 info3->base.full_name.string =
1840                                         talloc_strdup(info3,
1841                                                       cached_info3->base.full_name.string);
1842                         } else {
1843
1844                                 /* this might fail so we dont check the return code */
1845                                 wcache_query_user_fullname(domain,
1846                                                 info3,
1847                                                 &user_sid,
1848                                                 &info3->base.full_name.string);
1849                         }
1850                 }
1851
1852                 wcache_invalidate_samlogon(find_domain_from_name(name_domain),
1853                                            &user_sid);
1854                 netsamlogon_cache_store(name_user, info3);
1855
1856                 /* save name_to_sid info as early as possible (only if
1857                    this is our primary domain so we don't invalidate
1858                    the cache entry by storing the seq_num for the wrong
1859                    domain). */
1860                 if ( domain->primary ) {
1861                         cache_name2sid(domain, name_domain, name_user,
1862                                        SID_NAME_USER, &user_sid);
1863                 }
1864
1865                 /* Check if the user is in the right group */
1866
1867                 result = check_info3_in_group(
1868                         info3,
1869                         state->request->data.auth.require_membership_of_sid);
1870                 if (!NT_STATUS_IS_OK(result)) {
1871                         DEBUG(3, ("User %s is not in the required group (%s), so plaintext authentication is rejected\n",
1872                                   state->request->data.auth.user,
1873                                   state->request->data.auth.require_membership_of_sid));
1874                         goto done;
1875                 }
1876
1877                 result = append_auth_data(state->mem_ctx, state->response,
1878                                           state->request->flags, info3,
1879                                           name_domain, name_user);
1880                 if (!NT_STATUS_IS_OK(result)) {
1881                         goto done;
1882                 }
1883
1884                 if ((state->request->flags & WBFLAG_PAM_CACHED_LOGIN)
1885                     && lp_winbind_offline_logon()) {
1886
1887                         result = winbindd_store_creds(domain,
1888                                                       state->request->data.auth.user,
1889                                                       state->request->data.auth.pass,
1890                                                       info3);
1891                 }
1892
1893                 if (state->request->flags & WBFLAG_PAM_GET_PWD_POLICY) {
1894                         struct winbindd_domain *our_domain = find_our_domain();
1895
1896                         /* This is not entirely correct I believe, but it is
1897                            consistent.  Only apply the password policy settings
1898                            too warn users for our own domain.  Cannot obtain these
1899                            from trusted DCs all the  time so don't do it at all.
1900                            -- jerry */
1901
1902                         result = NT_STATUS_NOT_SUPPORTED;
1903                         if (our_domain == domain ) {
1904                                 result = fillup_password_policy(
1905                                         our_domain, state->response);
1906                         }
1907
1908                         if (!NT_STATUS_IS_OK(result)
1909                             && !NT_STATUS_EQUAL(result, NT_STATUS_NOT_SUPPORTED) )
1910                         {
1911                                 DEBUG(10,("Failed to get password policies for domain %s: %s\n",
1912                                           domain->name, nt_errstr(result)));
1913                                 goto done;
1914                         }
1915                 }
1916
1917                 result = NT_STATUS_OK;
1918         }
1919
1920 done:
1921         /* give us a more useful (more correct?) error code */
1922         if ((NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND) ||
1923             (NT_STATUS_EQUAL(result, NT_STATUS_UNSUCCESSFUL)))) {
1924                 result = NT_STATUS_NO_LOGON_SERVERS;
1925         }
1926
1927         set_auth_errors(state->response, result);
1928
1929         DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2, ("Plain-text authentication for user %s returned %s (PAM: %d)\n",
1930               state->request->data.auth.user,
1931               state->response->data.auth.nt_status_string,
1932               state->response->data.auth.pam_error));
1933
1934         return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
1935 }
1936
1937 NTSTATUS winbind_dual_SamLogon(struct winbindd_domain *domain,
1938                                TALLOC_CTX *mem_ctx,
1939                                uint32_t logon_parameters,
1940                                const char *name_user,
1941                                const char *name_domain,
1942                                const char *workstation,
1943                                const uint8_t chal[8],
1944                                DATA_BLOB lm_response,
1945                                DATA_BLOB nt_response,
1946                                struct netr_SamInfo3 **info3)
1947 {
1948         NTSTATUS result;
1949
1950         if (strequal(name_domain, get_global_sam_name())) {
1951                 DATA_BLOB chal_blob = data_blob_const(
1952                         chal, 8);
1953
1954                 result = winbindd_dual_auth_passdb(
1955                         mem_ctx,
1956                         logon_parameters,
1957                         name_domain, name_user,
1958                         &chal_blob, &lm_response, &nt_response, info3);
1959
1960                 /* 
1961                  * We need to try the remote NETLOGON server if this is NOT_IMPLEMENTED 
1962                  */
1963                 if (!NT_STATUS_EQUAL(result, NT_STATUS_NOT_IMPLEMENTED)) {
1964                         goto process_result;
1965                 }
1966         }
1967
1968         result = winbind_samlogon_retry_loop(domain,
1969                                              mem_ctx,
1970                                              logon_parameters,
1971                                              name_user,
1972                                              NULL, /* password */
1973                                              name_domain,
1974                                              /* Bug #3248 - found by Stefan Burkei. */
1975                                              workstation, /* We carefully set this above so use it... */
1976                                              chal,
1977                                              lm_response,
1978                                              nt_response,
1979                                              false, /* interactive */
1980                                              info3);
1981         if (!NT_STATUS_IS_OK(result)) {
1982                 goto done;
1983         }
1984
1985 process_result:
1986
1987         if (NT_STATUS_IS_OK(result)) {
1988                 struct dom_sid user_sid;
1989
1990                 sid_compose(&user_sid, (*info3)->base.domain_sid,
1991                             (*info3)->base.rid);
1992
1993                 if ((*info3)->base.full_name.string == NULL) {
1994                         struct netr_SamInfo3 *cached_info3;
1995
1996                         cached_info3 = netsamlogon_cache_get(mem_ctx,
1997                                                              &user_sid);
1998                         if (cached_info3 != NULL &&
1999                             cached_info3->base.full_name.string != NULL) {
2000                                 (*info3)->base.full_name.string =
2001                                         talloc_strdup(*info3,
2002                                                       cached_info3->base.full_name.string);
2003                         } else {
2004
2005                                 /* this might fail so we dont check the return code */
2006                                 wcache_query_user_fullname(domain,
2007                                                 *info3,
2008                                                 &user_sid,
2009                                                 &(*info3)->base.full_name.string);
2010                         }
2011                 }
2012
2013                 wcache_invalidate_samlogon(find_domain_from_name(name_domain),
2014                                            &user_sid);
2015                 netsamlogon_cache_store(name_user, *info3);
2016         }
2017
2018 done:
2019
2020         /* give us a more useful (more correct?) error code */
2021         if ((NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND) ||
2022             (NT_STATUS_EQUAL(result, NT_STATUS_UNSUCCESSFUL)))) {
2023                 result = NT_STATUS_NO_LOGON_SERVERS;
2024         }
2025
2026         DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2,
2027               ("NTLM CRAP authentication for user [%s]\\[%s] returned %s\n",
2028                name_domain,
2029                name_user,
2030                nt_errstr(result)));
2031
2032         return result;
2033 }
2034
2035 enum winbindd_result winbindd_dual_pam_auth_crap(struct winbindd_domain *domain,
2036                                                  struct winbindd_cli_state *state)
2037 {
2038         NTSTATUS result;
2039         struct netr_SamInfo3 *info3 = NULL;
2040         const char *name_user = NULL;
2041         const char *name_domain = NULL;
2042         const char *workstation;
2043
2044         DATA_BLOB lm_resp, nt_resp;
2045
2046         /* This is child-only, so no check for privileged access is needed
2047            anymore */
2048
2049         /* Ensure null termination */
2050         state->request->data.auth_crap.user[sizeof(state->request->data.auth_crap.user)-1]=0;
2051         state->request->data.auth_crap.domain[sizeof(state->request->data.auth_crap.domain)-1]=0;
2052
2053         name_user = state->request->data.auth_crap.user;
2054         name_domain = state->request->data.auth_crap.domain;
2055         workstation = state->request->data.auth_crap.workstation;
2056
2057         DEBUG(3, ("[%5lu]: pam auth crap domain: %s user: %s\n", (unsigned long)state->pid,
2058                   name_domain, name_user));
2059
2060         if (state->request->data.auth_crap.lm_resp_len > sizeof(state->request->data.auth_crap.lm_resp)
2061                 || state->request->data.auth_crap.nt_resp_len > sizeof(state->request->data.auth_crap.nt_resp)) {
2062                 if (!(state->request->flags & WBFLAG_BIG_NTLMV2_BLOB) ||
2063                      state->request->extra_len != state->request->data.auth_crap.nt_resp_len) {
2064                         DEBUG(0, ("winbindd_pam_auth_crap: invalid password length %u/%u\n",
2065                                   state->request->data.auth_crap.lm_resp_len,
2066                                   state->request->data.auth_crap.nt_resp_len));
2067                         result = NT_STATUS_INVALID_PARAMETER;
2068                         goto done;
2069                 }
2070         }
2071
2072         lm_resp = data_blob_talloc(state->mem_ctx, state->request->data.auth_crap.lm_resp,
2073                                         state->request->data.auth_crap.lm_resp_len);
2074
2075         if (state->request->flags & WBFLAG_BIG_NTLMV2_BLOB) {
2076                 nt_resp = data_blob_talloc(state->mem_ctx,
2077                                            state->request->extra_data.data,
2078                                            state->request->data.auth_crap.nt_resp_len);
2079         } else {
2080                 nt_resp = data_blob_talloc(state->mem_ctx,
2081                                            state->request->data.auth_crap.nt_resp,
2082                                            state->request->data.auth_crap.nt_resp_len);
2083         }
2084
2085         result = winbind_dual_SamLogon(domain,
2086                                        state->mem_ctx,
2087                                        state->request->data.auth_crap.logon_parameters,
2088                                        name_user,
2089                                        name_domain,
2090                                        /* Bug #3248 - found by Stefan Burkei. */
2091                                        workstation, /* We carefully set this above so use it... */
2092                                        state->request->data.auth_crap.chal,
2093                                        lm_resp,
2094                                        nt_resp,
2095                                        &info3);
2096         if (!NT_STATUS_IS_OK(result)) {
2097                 goto done;
2098         }
2099
2100         if (NT_STATUS_IS_OK(result)) {
2101                 /* Check if the user is in the right group */
2102
2103                 result = check_info3_in_group(
2104                         info3,
2105                         state->request->data.auth_crap.require_membership_of_sid);
2106                 if (!NT_STATUS_IS_OK(result)) {
2107                         DEBUG(3, ("User %s is not in the required group (%s), so "
2108                                   "crap authentication is rejected\n",
2109                                   state->request->data.auth_crap.user,
2110                                   state->request->data.auth_crap.require_membership_of_sid));
2111                         goto done;
2112                 }
2113
2114                 result = append_auth_data(state->mem_ctx, state->response,
2115                                           state->request->flags, info3,
2116                                           name_domain, name_user);
2117                 if (!NT_STATUS_IS_OK(result)) {
2118                         goto done;
2119                 }
2120         }
2121
2122 done:
2123
2124         if (state->request->flags & WBFLAG_PAM_NT_STATUS_SQUASH) {
2125                 result = nt_status_squash(result);
2126         }
2127
2128         set_auth_errors(state->response, result);
2129
2130         return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
2131 }
2132
2133 enum winbindd_result winbindd_dual_pam_chauthtok(struct winbindd_domain *contact_domain,
2134                                                  struct winbindd_cli_state *state)
2135 {
2136         char *oldpass;
2137         char *newpass = NULL;
2138         struct policy_handle dom_pol;
2139         struct rpc_pipe_client *cli = NULL;
2140         bool got_info = false;
2141         struct samr_DomInfo1 *info = NULL;
2142         struct userPwdChangeFailureInformation *reject = NULL;
2143         NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
2144         fstring domain, user;
2145         struct dcerpc_binding_handle *b = NULL;
2146
2147         ZERO_STRUCT(dom_pol);
2148
2149         DEBUG(3, ("[%5lu]: dual pam chauthtok %s\n", (unsigned long)state->pid,
2150                   state->request->data.auth.user));
2151
2152         if (!parse_domain_user(state->request->data.chauthtok.user, domain, user)) {
2153                 goto done;
2154         }
2155
2156         /* Change password */
2157
2158         oldpass = state->request->data.chauthtok.oldpass;
2159         newpass = state->request->data.chauthtok.newpass;
2160
2161         /* Initialize reject reason */
2162         state->response->data.auth.reject_reason = Undefined;
2163
2164         /* Get sam handle */
2165
2166         result = cm_connect_sam(contact_domain, state->mem_ctx, true, &cli,
2167                                 &dom_pol);
2168         if (!NT_STATUS_IS_OK(result)) {
2169                 DEBUG(1, ("could not get SAM handle on DC for %s\n", domain));
2170                 goto done;
2171         }
2172
2173         b = cli->binding_handle;
2174
2175         result = rpccli_samr_chgpasswd_user3(cli, state->mem_ctx,
2176                                              user,
2177                                              newpass,
2178                                              oldpass,
2179                                              &info,
2180                                              &reject);
2181
2182         /* Windows 2003 returns NT_STATUS_PASSWORD_RESTRICTION */
2183
2184         if (NT_STATUS_EQUAL(result, NT_STATUS_PASSWORD_RESTRICTION) ) {
2185
2186                 fill_in_password_policy(state->response, info);
2187
2188                 state->response->data.auth.reject_reason =
2189                         reject->extendedFailureReason;
2190
2191                 got_info = true;
2192         }
2193
2194         /* atm the pidl generated rpccli_samr_ChangePasswordUser3 function will
2195          * return with NT_STATUS_BUFFER_TOO_SMALL for w2k dcs as w2k just
2196          * returns with 4byte error code (NT_STATUS_NOT_SUPPORTED) which is too
2197          * short to comply with the samr_ChangePasswordUser3 idl - gd */
2198
2199         /* only fallback when the chgpasswd_user3 call is not supported */
2200         if (NT_STATUS_EQUAL(result, NT_STATUS_RPC_PROCNUM_OUT_OF_RANGE) ||
2201             NT_STATUS_EQUAL(result, NT_STATUS_NOT_SUPPORTED) ||
2202             NT_STATUS_EQUAL(result, NT_STATUS_BUFFER_TOO_SMALL) ||
2203             NT_STATUS_EQUAL(result, NT_STATUS_NOT_IMPLEMENTED)) {
2204
2205                 DEBUG(10,("Password change with chgpasswd_user3 failed with: %s, retrying chgpasswd_user2\n",
2206                         nt_errstr(result)));
2207
2208                 result = rpccli_samr_chgpasswd_user2(cli, state->mem_ctx, user, newpass, oldpass);
2209
2210                 /* Windows 2000 returns NT_STATUS_ACCOUNT_RESTRICTION.
2211                    Map to the same status code as Windows 2003. */
2212
2213                 if ( NT_STATUS_EQUAL(NT_STATUS_ACCOUNT_RESTRICTION, result ) ) {
2214                         result = NT_STATUS_PASSWORD_RESTRICTION;
2215                 }
2216         }
2217
2218 done:
2219
2220         if (NT_STATUS_IS_OK(result)
2221             && (state->request->flags & WBFLAG_PAM_CACHED_LOGIN)
2222             && lp_winbind_offline_logon()) {
2223                 result = winbindd_update_creds_by_name(contact_domain, user,
2224                                                        newpass);
2225                 /* Again, this happens when we login from gdm or xdm
2226                  * and the password expires, *BUT* cached crendentials
2227                  * doesn't exist. winbindd_update_creds_by_name()
2228                  * returns NT_STATUS_NO_SUCH_USER.
2229                  * This is not a failure.
2230                  * --- BoYang
2231                  * */
2232                 if (NT_STATUS_EQUAL(result, NT_STATUS_NO_SUCH_USER)) {
2233                         result = NT_STATUS_OK;
2234                 }
2235
2236                 if (!NT_STATUS_IS_OK(result)) {
2237                         DEBUG(10, ("Failed to store creds: %s\n",
2238                                    nt_errstr(result)));
2239                         goto process_result;
2240                 }
2241         }
2242
2243         if (!NT_STATUS_IS_OK(result) && !got_info && contact_domain) {
2244
2245                 NTSTATUS policy_ret;
2246
2247                 policy_ret = fillup_password_policy(
2248                         contact_domain, state->response);
2249
2250                 /* failure of this is non critical, it will just provide no
2251                  * additional information to the client why the change has
2252                  * failed - Guenther */
2253
2254                 if (!NT_STATUS_IS_OK(policy_ret)) {
2255                         DEBUG(10,("Failed to get password policies: %s\n", nt_errstr(policy_ret)));
2256                         goto process_result;
2257                 }
2258         }
2259
2260 process_result:
2261
2262         if (strequal(contact_domain->name, get_global_sam_name())) {
2263                 /* FIXME: internal rpc pipe does not cache handles yet */
2264                 if (b) {
2265                         if (is_valid_policy_hnd(&dom_pol)) {
2266                                 NTSTATUS _result;
2267                                 dcerpc_samr_Close(b, state->mem_ctx, &dom_pol, &_result);
2268                         }
2269                         TALLOC_FREE(cli);
2270                 }
2271         }
2272
2273         set_auth_errors(state->response, result);
2274
2275         DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2,
2276               ("Password change for user [%s]\\[%s] returned %s (PAM: %d)\n",
2277                domain,
2278                user,
2279                state->response->data.auth.nt_status_string,
2280                state->response->data.auth.pam_error));
2281
2282         return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
2283 }
2284
2285 enum winbindd_result winbindd_dual_pam_logoff(struct winbindd_domain *domain,
2286                                               struct winbindd_cli_state *state)
2287 {
2288         NTSTATUS result = NT_STATUS_NOT_SUPPORTED;
2289
2290         DEBUG(3, ("[%5lu]: pam dual logoff %s\n", (unsigned long)state->pid,
2291                 state->request->data.logoff.user));
2292
2293         if (!(state->request->flags & WBFLAG_PAM_KRB5)) {
2294                 result = NT_STATUS_OK;
2295                 goto process_result;
2296         }
2297
2298         if (state->request->data.logoff.krb5ccname[0] == '\0') {
2299                 result = NT_STATUS_OK;
2300                 goto process_result;
2301         }
2302
2303 #ifdef HAVE_KRB5
2304
2305         if (state->request->data.logoff.uid < 0) {
2306                 DEBUG(0,("winbindd_pam_logoff: invalid uid\n"));
2307                 goto process_result;
2308         }
2309
2310         /* what we need here is to find the corresponding krb5 ccache name *we*
2311          * created for a given username and destroy it */
2312
2313         if (!ccache_entry_exists(state->request->data.logoff.user)) {
2314                 result = NT_STATUS_OK;
2315                 DEBUG(10,("winbindd_pam_logoff: no entry found.\n"));
2316                 goto process_result;
2317         }
2318
2319         if (!ccache_entry_identical(state->request->data.logoff.user,
2320                                         state->request->data.logoff.uid,
2321                                         state->request->data.logoff.krb5ccname)) {
2322                 DEBUG(0,("winbindd_pam_logoff: cached entry differs.\n"));
2323                 goto process_result;
2324         }
2325
2326         result = remove_ccache(state->request->data.logoff.user);
2327         if (!NT_STATUS_IS_OK(result)) {
2328                 DEBUG(0,("winbindd_pam_logoff: failed to remove ccache: %s\n",
2329                         nt_errstr(result)));
2330                 goto process_result;
2331         }
2332
2333         /*
2334          * Remove any mlock'ed memory creds in the child
2335          * we might be using for krb5 ticket renewal.
2336          */
2337
2338         winbindd_delete_memory_creds(state->request->data.logoff.user);
2339
2340 #else
2341         result = NT_STATUS_NOT_SUPPORTED;
2342 #endif
2343
2344 process_result:
2345
2346
2347         set_auth_errors(state->response, result);
2348
2349         return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
2350 }
2351
2352 /* Change user password with auth crap*/
2353
2354 enum winbindd_result winbindd_dual_pam_chng_pswd_auth_crap(struct winbindd_domain *domainSt, struct winbindd_cli_state *state)
2355 {
2356         NTSTATUS result;
2357         DATA_BLOB new_nt_password;
2358         DATA_BLOB old_nt_hash_enc;
2359         DATA_BLOB new_lm_password;
2360         DATA_BLOB old_lm_hash_enc;
2361         fstring  domain,user;
2362         struct policy_handle dom_pol;
2363         struct winbindd_domain *contact_domain = domainSt;
2364         struct rpc_pipe_client *cli = NULL;
2365         struct dcerpc_binding_handle *b = NULL;
2366
2367         ZERO_STRUCT(dom_pol);
2368
2369         /* Ensure null termination */
2370         state->request->data.chng_pswd_auth_crap.user[
2371                 sizeof(state->request->data.chng_pswd_auth_crap.user)-1]=0;
2372         state->request->data.chng_pswd_auth_crap.domain[
2373                 sizeof(state->request->data.chng_pswd_auth_crap.domain)-1]=0;
2374         *domain = 0;
2375         *user = 0;
2376
2377         DEBUG(3, ("[%5lu]: pam change pswd auth crap domain: %s user: %s\n",
2378                   (unsigned long)state->pid,
2379                   state->request->data.chng_pswd_auth_crap.domain,
2380                   state->request->data.chng_pswd_auth_crap.user));
2381
2382         if (lp_winbind_offline_logon()) {
2383                 DEBUG(0,("Refusing password change as winbind offline logons are enabled. "));
2384                 DEBUGADD(0,("Changing passwords here would risk inconsistent logons\n"));
2385                 result = NT_STATUS_ACCESS_DENIED;
2386                 goto done;
2387         }
2388
2389         if (*state->request->data.chng_pswd_auth_crap.domain) {
2390                 fstrcpy(domain,state->request->data.chng_pswd_auth_crap.domain);
2391         } else {
2392                 parse_domain_user(state->request->data.chng_pswd_auth_crap.user,
2393                                   domain, user);
2394
2395                 if(!*domain) {
2396                         DEBUG(3,("no domain specified with username (%s) - "
2397                                  "failing auth\n",
2398                                  state->request->data.chng_pswd_auth_crap.user));
2399                         result = NT_STATUS_NO_SUCH_USER;
2400                         goto done;
2401                 }
2402         }
2403
2404         if (!*domain && lp_winbind_use_default_domain()) {
2405                 fstrcpy(domain,lp_workgroup());
2406         }
2407
2408         if(!*user) {
2409                 fstrcpy(user, state->request->data.chng_pswd_auth_crap.user);
2410         }
2411
2412         DEBUG(3, ("[%5lu]: pam auth crap domain: %s user: %s\n",
2413                   (unsigned long)state->pid, domain, user));
2414
2415         /* Change password */
2416         new_nt_password = data_blob_const(
2417                 state->request->data.chng_pswd_auth_crap.new_nt_pswd,
2418                 state->request->data.chng_pswd_auth_crap.new_nt_pswd_len);
2419
2420         old_nt_hash_enc = data_blob_const(
2421                 state->request->data.chng_pswd_auth_crap.old_nt_hash_enc,
2422                 state->request->data.chng_pswd_auth_crap.old_nt_hash_enc_len);
2423
2424         if(state->request->data.chng_pswd_auth_crap.new_lm_pswd_len > 0)        {
2425                 new_lm_password = data_blob_const(
2426                         state->request->data.chng_pswd_auth_crap.new_lm_pswd,
2427                         state->request->data.chng_pswd_auth_crap.new_lm_pswd_len);
2428
2429                 old_lm_hash_enc = data_blob_const(
2430                         state->request->data.chng_pswd_auth_crap.old_lm_hash_enc,
2431                         state->request->data.chng_pswd_auth_crap.old_lm_hash_enc_len);
2432         } else {
2433                 new_lm_password = data_blob_null;
2434                 old_lm_hash_enc = data_blob_null;
2435         }
2436
2437         /* Get sam handle */
2438
2439         result = cm_connect_sam(contact_domain, state->mem_ctx, true, &cli, &dom_pol);
2440         if (!NT_STATUS_IS_OK(result)) {
2441                 DEBUG(1, ("could not get SAM handle on DC for %s\n", domain));
2442                 goto done;
2443         }
2444
2445         b = cli->binding_handle;
2446
2447         result = rpccli_samr_chng_pswd_auth_crap(
2448                 cli, state->mem_ctx, user, new_nt_password, old_nt_hash_enc,
2449                 new_lm_password, old_lm_hash_enc);
2450
2451  done:
2452
2453         if (strequal(contact_domain->name, get_global_sam_name())) {
2454                 /* FIXME: internal rpc pipe does not cache handles yet */
2455                 if (b) {
2456                         if (is_valid_policy_hnd(&dom_pol)) {
2457                                 NTSTATUS _result;
2458                                 dcerpc_samr_Close(b, state->mem_ctx, &dom_pol, &_result);
2459                         }
2460                         TALLOC_FREE(cli);
2461                 }
2462         }
2463
2464         set_auth_errors(state->response, result);
2465
2466         DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2,
2467               ("Password change for user [%s]\\[%s] returned %s (PAM: %d)\n",
2468                domain, user,
2469                state->response->data.auth.nt_status_string,
2470                state->response->data.auth.pam_error));
2471
2472         return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
2473 }
2474
2475 #ifdef HAVE_KRB5
2476 static NTSTATUS extract_pac_vrfy_sigs(TALLOC_CTX *mem_ctx, DATA_BLOB pac_blob,
2477                                       struct PAC_LOGON_INFO **logon_info)
2478 {
2479         krb5_context krbctx = NULL;
2480         krb5_error_code k5ret;
2481         krb5_keytab keytab;
2482         krb5_kt_cursor cursor;
2483         krb5_keytab_entry entry;
2484         NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
2485
2486         ZERO_STRUCT(entry);
2487         ZERO_STRUCT(cursor);
2488
2489         k5ret = krb5_init_context(&krbctx);
2490         if (k5ret) {
2491                 DEBUG(1, ("Failed to initialize kerberos context: %s\n",
2492                           error_message(k5ret)));
2493                 status = krb5_to_nt_status(k5ret);
2494                 goto out;
2495         }
2496
2497         k5ret =  gse_krb5_get_server_keytab(krbctx, &keytab);
2498         if (k5ret) {
2499                 DEBUG(1, ("Failed to get keytab: %s\n",
2500                           error_message(k5ret)));
2501                 status = krb5_to_nt_status(k5ret);
2502                 goto out_free;
2503         }
2504
2505         k5ret = krb5_kt_start_seq_get(krbctx, keytab, &cursor);
2506         if (k5ret) {
2507                 DEBUG(1, ("Failed to start seq: %s\n",
2508                           error_message(k5ret)));
2509                 status = krb5_to_nt_status(k5ret);
2510                 goto out_keytab;
2511         }
2512
2513         k5ret = krb5_kt_next_entry(krbctx, keytab, &entry, &cursor);
2514         while (k5ret == 0) {
2515                 status = kerberos_pac_logon_info(mem_ctx, pac_blob,
2516                                                  krbctx, NULL,
2517                                                  KRB5_KT_KEY(&entry), NULL, 0,
2518                                                  logon_info);
2519                 if (NT_STATUS_IS_OK(status)) {
2520                         break;
2521                 }
2522                 k5ret = smb_krb5_kt_free_entry(krbctx, &entry);
2523                 k5ret = krb5_kt_next_entry(krbctx, keytab, &entry, &cursor);
2524         }
2525
2526         k5ret = krb5_kt_end_seq_get(krbctx, keytab, &cursor);
2527         if (k5ret) {
2528                 DEBUG(1, ("Failed to end seq: %s\n",
2529                           error_message(k5ret)));
2530         }
2531 out_keytab:
2532         k5ret = krb5_kt_close(krbctx, keytab);
2533         if (k5ret) {
2534                 DEBUG(1, ("Failed to close keytab: %s\n",
2535                           error_message(k5ret)));
2536         }
2537 out_free:
2538         krb5_free_context(krbctx);
2539 out:
2540         return status;
2541 }
2542
2543 NTSTATUS winbindd_pam_auth_pac_send(struct winbindd_cli_state *state,
2544                                     struct netr_SamInfo3 **info3)
2545 {
2546         struct winbindd_request *req = state->request;
2547         DATA_BLOB pac_blob;
2548         struct PAC_LOGON_INFO *logon_info = NULL;
2549         struct netr_SamInfo3 *info3_copy = NULL;
2550         NTSTATUS result;
2551
2552         pac_blob = data_blob_const(req->extra_data.data, req->extra_len);
2553         result = extract_pac_vrfy_sigs(state->mem_ctx, pac_blob, &logon_info);
2554         if (!NT_STATUS_IS_OK(result) &&
2555             !NT_STATUS_EQUAL(result, NT_STATUS_ACCESS_DENIED)) {
2556                 DEBUG(1, ("Error during PAC signature verification: %s\n",
2557                           nt_errstr(result)));
2558                 return result;
2559         }
2560
2561         if (logon_info) {
2562                 /* Signature verification succeeded, trust the PAC */
2563                 result = create_info3_from_pac_logon_info(state->mem_ctx,
2564                                                         logon_info,
2565                                                         &info3_copy);
2566                 if (!NT_STATUS_IS_OK(result)) {
2567                         return result;
2568                 }
2569                 netsamlogon_cache_store(NULL, info3_copy);
2570
2571         } else {
2572                 /* Try without signature verification */
2573                 result = kerberos_pac_logon_info(state->mem_ctx, pac_blob, NULL,
2574                                                  NULL, NULL, NULL, 0,
2575                                                  &logon_info);
2576                 if (!NT_STATUS_IS_OK(result)) {
2577                         DEBUG(10, ("Could not extract PAC: %s\n",
2578                                    nt_errstr(result)));
2579                         return result;
2580                 }
2581                 if (logon_info) {
2582                         /*
2583                          * Don't strictly need to copy here,
2584                          * but it makes it explicit we're
2585                          * returning a copy talloc'ed off
2586                          * the state->mem_ctx.
2587                          */
2588                         info3_copy = copy_netr_SamInfo3(state->mem_ctx,
2589                                         &logon_info->info3);
2590                         if (info3_copy == NULL) {
2591                                 return NT_STATUS_NO_MEMORY;
2592                         }
2593                 }
2594         }
2595
2596         *info3 = info3_copy;
2597
2598         return NT_STATUS_OK;
2599 }
2600 #else /* HAVE_KRB5 */
2601 NTSTATUS winbindd_pam_auth_pac_send(struct winbindd_cli_state *state,
2602                                     struct netr_SamInfo3 **info3)
2603 {
2604         return NT_STATUS_NO_SUCH_USER;
2605 }
2606 #endif /* HAVE_KRB5 */