A small improvement to the DEBUG message when pass-through authentication
[samba.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
1383                 if (interactive && username != NULL && password != NULL) {
1384                         result = rpccli_netlogon_password_logon(domain->conn.netlogon_creds,
1385                                                                 netlogon_pipe->binding_handle,
1386                                                                 mem_ctx,
1387                                                                 logon_parameters,
1388                                                                 domainname,
1389                                                                 username,
1390                                                                 password,
1391                                                                 workstation,
1392                                                                 NetlogonInteractiveInformation,
1393                                                                 info3);
1394                 } else {
1395                         result = rpccli_netlogon_network_logon(domain->conn.netlogon_creds,
1396                                                         netlogon_pipe->binding_handle,
1397                                                         mem_ctx,
1398                                                         logon_parameters,
1399                                                         username,
1400                                                         domainname,
1401                                                         workstation,
1402                                                         chal,
1403                                                         lm_response,
1404                                                         nt_response,
1405                                                         &authoritative,
1406                                                         &flags,
1407                                                         info3);
1408                 }
1409
1410                 /*
1411                  * we increment this after the "feature negotiation"
1412                  * for can_do_samlogon_ex and can_do_validation6
1413                  */
1414                 attempts += 1;
1415
1416                 /* We have to try a second time as cm_connect_netlogon
1417                    might not yet have noticed that the DC has killed
1418                    our connection. */
1419
1420                 if (!rpccli_is_connected(netlogon_pipe)) {
1421                         retry = true;
1422                         continue;
1423                 }
1424
1425                 /* if we get access denied, a possible cause was that we had
1426                    and open connection to the DC, but someone changed our
1427                    machine account password out from underneath us using 'net
1428                    rpc changetrustpw' */
1429
1430                 if ( NT_STATUS_EQUAL(result, NT_STATUS_ACCESS_DENIED) ) {
1431                         DEBUG(1,("winbind_samlogon_retry_loop: sam_logon returned "
1432                                  "ACCESS_DENIED.  Maybe the DC has Restrict "
1433                                  "NTLM set or the trust account "
1434                                 "password was changed and we didn't know it. "
1435                                  "Killing connections to domain %s\n",
1436                                 domainname));
1437                         invalidate_cm_connection(domain);
1438                         retry = true;
1439                 }
1440
1441                 if (NT_STATUS_EQUAL(result, NT_STATUS_RPC_PROCNUM_OUT_OF_RANGE)) {
1442                         /*
1443                          * Got DCERPC_FAULT_OP_RNG_ERROR for SamLogon
1444                          * (no Ex). This happens against old Samba
1445                          * DCs, if LogonSamLogonEx() fails with an error
1446                          * e.g. NT_STATUS_NO_SUCH_USER or NT_STATUS_WRONG_PASSWORD.
1447                          *
1448                          * The server will log something like this:
1449                          * api_net_sam_logon_ex: Failed to marshall NET_R_SAM_LOGON_EX.
1450                          *
1451                          * This sets the whole connection into a fault_state mode
1452                          * and all following request get NT_STATUS_RPC_PROCNUM_OUT_OF_RANGE.
1453                          *
1454                          * This also happens to our retry with LogonSamLogonWithFlags()
1455                          * and LogonSamLogon().
1456                          *
1457                          * In order to recover from this situation, we need to
1458                          * drop the connection.
1459                          */
1460                         invalidate_cm_connection(domain);
1461                         result = NT_STATUS_LOGON_FAILURE;
1462                         break;
1463                 }
1464
1465         } while ( (attempts < 2) && retry );
1466
1467         if (NT_STATUS_EQUAL(result, NT_STATUS_IO_TIMEOUT)) {
1468                 DEBUG(3,("winbind_samlogon_retry_loop: sam_network_logon(ex) "
1469                                 "returned NT_STATUS_IO_TIMEOUT after the retry."
1470                                 "Killing connections to domain %s\n",
1471                         domainname));
1472                 invalidate_cm_connection(domain);
1473         }
1474         return result;
1475 }
1476
1477 static NTSTATUS winbindd_dual_pam_auth_samlogon(TALLOC_CTX *mem_ctx,
1478                                                 struct winbindd_domain *domain,
1479                                                 const char *user,
1480                                                 const char *pass,
1481                                                 uint32_t request_flags,
1482                                                 struct netr_SamInfo3 **info3)
1483 {
1484
1485         uchar chal[8];
1486         DATA_BLOB lm_resp;
1487         DATA_BLOB nt_resp;
1488         unsigned char local_nt_response[24];
1489         fstring name_domain, name_user;
1490         NTSTATUS result;
1491         struct netr_SamInfo3 *my_info3 = NULL;
1492
1493         *info3 = NULL;
1494
1495         DEBUG(10,("winbindd_dual_pam_auth_samlogon\n"));
1496
1497         /* Parse domain and username */
1498
1499         parse_domain_user(user, name_domain, name_user);
1500
1501         /* do password magic */
1502
1503         generate_random_buffer(chal, sizeof(chal));
1504
1505         if (lp_client_ntlmv2_auth()) {
1506                 DATA_BLOB server_chal;
1507                 DATA_BLOB names_blob;
1508                 server_chal = data_blob_const(chal, 8);
1509
1510                 /* note that the 'workgroup' here is for the local
1511                    machine.  The 'server name' must match the
1512                    'workstation' passed to the actual SamLogon call.
1513                 */
1514                 names_blob = NTLMv2_generate_names_blob(
1515                         mem_ctx, lp_netbios_name(), lp_workgroup());
1516
1517                 if (!SMBNTLMv2encrypt(mem_ctx, name_user, name_domain,
1518                                       pass,
1519                                       &server_chal,
1520                                       &names_blob,
1521                                       &lm_resp, &nt_resp, NULL, NULL)) {
1522                         data_blob_free(&names_blob);
1523                         DEBUG(0, ("winbindd_pam_auth: SMBNTLMv2encrypt() failed!\n"));
1524                         result = NT_STATUS_NO_MEMORY;
1525                         goto done;
1526                 }
1527                 data_blob_free(&names_blob);
1528         } else {
1529                 lm_resp = data_blob_null;
1530                 SMBNTencrypt(pass, chal, local_nt_response);
1531
1532                 nt_resp = data_blob_talloc(mem_ctx, local_nt_response,
1533                                            sizeof(local_nt_response));
1534         }
1535
1536         if (strequal(name_domain, get_global_sam_name())) {
1537                 DATA_BLOB chal_blob = data_blob_const(chal, sizeof(chal));
1538
1539                 result = winbindd_dual_auth_passdb(
1540                         mem_ctx, 0, name_domain, name_user,
1541                         &chal_blob, &lm_resp, &nt_resp, info3);
1542
1543                 /* 
1544                  * We need to try the remote NETLOGON server if this is NOT_IMPLEMENTED 
1545                  */
1546                 if (!NT_STATUS_EQUAL(result, NT_STATUS_NOT_IMPLEMENTED)) {
1547                         goto done;
1548                 }
1549         }
1550
1551         /* check authentication loop */
1552
1553         result = winbind_samlogon_retry_loop(domain,
1554                                              mem_ctx,
1555                                              0,
1556                                              name_user,
1557                                              pass,
1558                                              name_domain,
1559                                              lp_netbios_name(),
1560                                              chal,
1561                                              lm_resp,
1562                                              nt_resp,
1563                                              true, /* interactive */
1564                                              &my_info3);
1565         if (!NT_STATUS_IS_OK(result)) {
1566                 goto done;
1567         }
1568
1569         /* handle the case where a NT4 DC does not fill in the acct_flags in
1570          * the samlogon reply info3. When accurate info3 is required by the
1571          * caller, we look up the account flags ourselve - gd */
1572
1573         if ((request_flags & WBFLAG_PAM_INFO3_TEXT) &&
1574             NT_STATUS_IS_OK(result) && (my_info3->base.acct_flags == 0)) {
1575
1576                 struct rpc_pipe_client *samr_pipe;
1577                 struct policy_handle samr_domain_handle, user_pol;
1578                 union samr_UserInfo *info = NULL;
1579                 NTSTATUS status_tmp, result_tmp;
1580                 uint32_t acct_flags;
1581                 struct dcerpc_binding_handle *b;
1582
1583                 status_tmp = cm_connect_sam(domain, mem_ctx, false,
1584                                             &samr_pipe, &samr_domain_handle);
1585
1586                 if (!NT_STATUS_IS_OK(status_tmp)) {
1587                         DEBUG(3, ("could not open handle to SAMR pipe: %s\n",
1588                                 nt_errstr(status_tmp)));
1589                         goto done;
1590                 }
1591
1592                 b = samr_pipe->binding_handle;
1593
1594                 status_tmp = dcerpc_samr_OpenUser(b, mem_ctx,
1595                                                   &samr_domain_handle,
1596                                                   MAXIMUM_ALLOWED_ACCESS,
1597                                                   my_info3->base.rid,
1598                                                   &user_pol,
1599                                                   &result_tmp);
1600
1601                 if (!NT_STATUS_IS_OK(status_tmp)) {
1602                         DEBUG(3, ("could not open user handle on SAMR pipe: %s\n",
1603                                 nt_errstr(status_tmp)));
1604                         goto done;
1605                 }
1606                 if (!NT_STATUS_IS_OK(result_tmp)) {
1607                         DEBUG(3, ("could not open user handle on SAMR pipe: %s\n",
1608                                 nt_errstr(result_tmp)));
1609                         goto done;
1610                 }
1611
1612                 status_tmp = dcerpc_samr_QueryUserInfo(b, mem_ctx,
1613                                                        &user_pol,
1614                                                        16,
1615                                                        &info,
1616                                                        &result_tmp);
1617
1618                 if (!NT_STATUS_IS_OK(status_tmp)) {
1619                         DEBUG(3, ("could not query user info on SAMR pipe: %s\n",
1620                                 nt_errstr(status_tmp)));
1621                         dcerpc_samr_Close(b, mem_ctx, &user_pol, &result_tmp);
1622                         goto done;
1623                 }
1624                 if (!NT_STATUS_IS_OK(result_tmp)) {
1625                         DEBUG(3, ("could not query user info on SAMR pipe: %s\n",
1626                                 nt_errstr(result_tmp)));
1627                         dcerpc_samr_Close(b, mem_ctx, &user_pol, &result_tmp);
1628                         goto done;
1629                 }
1630
1631                 acct_flags = info->info16.acct_flags;
1632
1633                 if (acct_flags == 0) {
1634                         dcerpc_samr_Close(b, mem_ctx, &user_pol, &result_tmp);
1635                         goto done;
1636                 }
1637
1638                 my_info3->base.acct_flags = acct_flags;
1639
1640                 DEBUG(10,("successfully retrieved acct_flags 0x%x\n", acct_flags));
1641
1642                 dcerpc_samr_Close(b, mem_ctx, &user_pol, &result_tmp);
1643         }
1644
1645         *info3 = my_info3;
1646 done:
1647         return result;
1648 }
1649
1650 enum winbindd_result winbindd_dual_pam_auth(struct winbindd_domain *domain,
1651                                             struct winbindd_cli_state *state)
1652 {
1653         NTSTATUS result = NT_STATUS_LOGON_FAILURE;
1654         NTSTATUS krb5_result = NT_STATUS_OK;
1655         fstring name_domain, name_user;
1656         char *mapped_user;
1657         fstring domain_user;
1658         struct netr_SamInfo3 *info3 = NULL;
1659         NTSTATUS name_map_status = NT_STATUS_UNSUCCESSFUL;
1660
1661         /* Ensure null termination */
1662         state->request->data.auth.user[sizeof(state->request->data.auth.user)-1]='\0';
1663
1664         /* Ensure null termination */
1665         state->request->data.auth.pass[sizeof(state->request->data.auth.pass)-1]='\0';
1666
1667         DEBUG(3, ("[%5lu]: dual pam auth %s\n", (unsigned long)state->pid,
1668                   state->request->data.auth.user));
1669
1670         /* Parse domain and username */
1671
1672         name_map_status = normalize_name_unmap(state->mem_ctx,
1673                                                state->request->data.auth.user,
1674                                                &mapped_user);
1675
1676         /* If the name normalization didnt' actually do anything,
1677            just use the original name */
1678
1679         if (!NT_STATUS_IS_OK(name_map_status) &&
1680             !NT_STATUS_EQUAL(name_map_status, NT_STATUS_FILE_RENAMED))
1681         {
1682                 mapped_user = state->request->data.auth.user;
1683         }
1684
1685         parse_domain_user(mapped_user, name_domain, name_user);
1686
1687         if ( mapped_user != state->request->data.auth.user ) {
1688                 fstr_sprintf( domain_user, "%s%c%s", name_domain,
1689                         *lp_winbind_separator(),
1690                         name_user );
1691                 strlcpy( state->request->data.auth.user, domain_user,
1692                              sizeof(state->request->data.auth.user));
1693         }
1694
1695         if (!domain->online) {
1696                 result = NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND;
1697                 if (domain->startup) {
1698                         /* Logons are very important to users. If we're offline and
1699                            we get a request within the first 30 seconds of startup,
1700                            try very hard to find a DC and go online. */
1701
1702                         DEBUG(10,("winbindd_dual_pam_auth: domain: %s offline and auth "
1703                                 "request in startup mode.\n", domain->name ));
1704
1705                         winbindd_flush_negative_conn_cache(domain);
1706                         result = init_dc_connection(domain, false);
1707                 }
1708         }
1709
1710         DEBUG(10,("winbindd_dual_pam_auth: domain: %s last was %s\n", domain->name, domain->online ? "online":"offline"));
1711
1712         /* Check for Kerberos authentication */
1713         if (domain->online && (state->request->flags & WBFLAG_PAM_KRB5)) {
1714
1715                 result = winbindd_dual_pam_auth_kerberos(domain, state, &info3);
1716                 /* save for later */
1717                 krb5_result = result;
1718
1719
1720                 if (NT_STATUS_IS_OK(result)) {
1721                         DEBUG(10,("winbindd_dual_pam_auth_kerberos succeeded\n"));
1722                         goto process_result;
1723                 } else {
1724                         DEBUG(10,("winbindd_dual_pam_auth_kerberos failed: %s\n", nt_errstr(result)));
1725                 }
1726
1727                 if (NT_STATUS_EQUAL(result, NT_STATUS_NO_LOGON_SERVERS) ||
1728                     NT_STATUS_EQUAL(result, NT_STATUS_IO_TIMEOUT) ||
1729                     NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
1730                         DEBUG(10,("winbindd_dual_pam_auth_kerberos setting domain to offline\n"));
1731                         set_domain_offline( domain );
1732                         goto cached_logon;
1733                 }
1734
1735                 /* there are quite some NT_STATUS errors where there is no
1736                  * point in retrying with a samlogon, we explictly have to take
1737                  * care not to increase the bad logon counter on the DC */
1738
1739                 if (NT_STATUS_EQUAL(result, NT_STATUS_ACCOUNT_DISABLED) ||
1740                     NT_STATUS_EQUAL(result, NT_STATUS_ACCOUNT_EXPIRED) ||
1741                     NT_STATUS_EQUAL(result, NT_STATUS_ACCOUNT_LOCKED_OUT) ||
1742                     NT_STATUS_EQUAL(result, NT_STATUS_INVALID_LOGON_HOURS) ||
1743                     NT_STATUS_EQUAL(result, NT_STATUS_INVALID_WORKSTATION) ||
1744                     NT_STATUS_EQUAL(result, NT_STATUS_LOGON_FAILURE) ||
1745                     NT_STATUS_EQUAL(result, NT_STATUS_NO_SUCH_USER) ||
1746                     NT_STATUS_EQUAL(result, NT_STATUS_PASSWORD_EXPIRED) ||
1747                     NT_STATUS_EQUAL(result, NT_STATUS_PASSWORD_MUST_CHANGE) ||
1748                     NT_STATUS_EQUAL(result, NT_STATUS_WRONG_PASSWORD)) {
1749                         goto done;
1750                 }
1751
1752                 if (state->request->flags & WBFLAG_PAM_FALLBACK_AFTER_KRB5) {
1753                         DEBUG(3,("falling back to samlogon\n"));
1754                         goto sam_logon;
1755                 } else {
1756                         goto cached_logon;
1757                 }
1758         }
1759
1760 sam_logon:
1761         /* Check for Samlogon authentication */
1762         if (domain->online) {
1763                 result = winbindd_dual_pam_auth_samlogon(
1764                         state->mem_ctx, domain,
1765                         state->request->data.auth.user,
1766                         state->request->data.auth.pass,
1767                         state->request->flags,
1768                         &info3);
1769
1770                 if (NT_STATUS_IS_OK(result)) {
1771                         DEBUG(10,("winbindd_dual_pam_auth_samlogon succeeded\n"));
1772                         /* add the Krb5 err if we have one */
1773                         if ( NT_STATUS_EQUAL(krb5_result, NT_STATUS_TIME_DIFFERENCE_AT_DC ) ) {
1774                                 info3->base.user_flags |= LOGON_KRB5_FAIL_CLOCK_SKEW;
1775                         }
1776                         goto process_result;
1777                 }
1778
1779                 DEBUG(10,("winbindd_dual_pam_auth_samlogon failed: %s\n",
1780                           nt_errstr(result)));
1781
1782                 if (NT_STATUS_EQUAL(result, NT_STATUS_NO_LOGON_SERVERS) ||
1783                     NT_STATUS_EQUAL(result, NT_STATUS_IO_TIMEOUT) ||
1784                     NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND))
1785                 {
1786                         DEBUG(10,("winbindd_dual_pam_auth_samlogon setting domain to offline\n"));
1787                         set_domain_offline( domain );
1788                         goto cached_logon;
1789                 }
1790
1791                         if (domain->online) {
1792                                 /* We're still online - fail. */
1793                                 goto done;
1794                         }
1795         }
1796
1797 cached_logon:
1798         /* Check for Cached logons */
1799         if (!domain->online && (state->request->flags & WBFLAG_PAM_CACHED_LOGIN) &&
1800             lp_winbind_offline_logon()) {
1801
1802                 result = winbindd_dual_pam_auth_cached(domain, state, &info3);
1803
1804                 if (NT_STATUS_IS_OK(result)) {
1805                         DEBUG(10,("winbindd_dual_pam_auth_cached succeeded\n"));
1806                         goto process_result;
1807                 } else {
1808                         DEBUG(10,("winbindd_dual_pam_auth_cached failed: %s\n", nt_errstr(result)));
1809                         goto done;
1810                 }
1811         }
1812
1813 process_result:
1814
1815         if (NT_STATUS_IS_OK(result)) {
1816
1817                 struct dom_sid user_sid;
1818
1819                 /* In all codepaths where result == NT_STATUS_OK info3 must have
1820                    been initialized. */
1821                 if (!info3) {
1822                         result = NT_STATUS_INTERNAL_ERROR;
1823                         goto done;
1824                 }
1825
1826                 sid_compose(&user_sid, info3->base.domain_sid,
1827                             info3->base.rid);
1828
1829                 if (info3->base.full_name.string == NULL) {
1830                         struct netr_SamInfo3 *cached_info3;
1831
1832                         cached_info3 = netsamlogon_cache_get(state->mem_ctx,
1833                                                              &user_sid);
1834                         if (cached_info3 != NULL &&
1835                             cached_info3->base.full_name.string != NULL) {
1836                                 info3->base.full_name.string =
1837                                         talloc_strdup(info3,
1838                                                       cached_info3->base.full_name.string);
1839                         } else {
1840
1841                                 /* this might fail so we dont check the return code */
1842                                 wcache_query_user_fullname(domain,
1843                                                 info3,
1844                                                 &user_sid,
1845                                                 &info3->base.full_name.string);
1846                         }
1847                 }
1848
1849                 wcache_invalidate_samlogon(find_domain_from_name(name_domain),
1850                                            &user_sid);
1851                 netsamlogon_cache_store(name_user, info3);
1852
1853                 /* save name_to_sid info as early as possible (only if
1854                    this is our primary domain so we don't invalidate
1855                    the cache entry by storing the seq_num for the wrong
1856                    domain). */
1857                 if ( domain->primary ) {
1858                         cache_name2sid(domain, name_domain, name_user,
1859                                        SID_NAME_USER, &user_sid);
1860                 }
1861
1862                 /* Check if the user is in the right group */
1863
1864                 result = check_info3_in_group(
1865                         info3,
1866                         state->request->data.auth.require_membership_of_sid);
1867                 if (!NT_STATUS_IS_OK(result)) {
1868                         DEBUG(3, ("User %s is not in the required group (%s), so plaintext authentication is rejected\n",
1869                                   state->request->data.auth.user,
1870                                   state->request->data.auth.require_membership_of_sid));
1871                         goto done;
1872                 }
1873
1874                 result = append_auth_data(state->mem_ctx, state->response,
1875                                           state->request->flags, info3,
1876                                           name_domain, name_user);
1877                 if (!NT_STATUS_IS_OK(result)) {
1878                         goto done;
1879                 }
1880
1881                 if ((state->request->flags & WBFLAG_PAM_CACHED_LOGIN)
1882                     && lp_winbind_offline_logon()) {
1883
1884                         result = winbindd_store_creds(domain,
1885                                                       state->request->data.auth.user,
1886                                                       state->request->data.auth.pass,
1887                                                       info3);
1888                 }
1889
1890                 if (state->request->flags & WBFLAG_PAM_GET_PWD_POLICY) {
1891                         struct winbindd_domain *our_domain = find_our_domain();
1892
1893                         /* This is not entirely correct I believe, but it is
1894                            consistent.  Only apply the password policy settings
1895                            too warn users for our own domain.  Cannot obtain these
1896                            from trusted DCs all the  time so don't do it at all.
1897                            -- jerry */
1898
1899                         result = NT_STATUS_NOT_SUPPORTED;
1900                         if (our_domain == domain ) {
1901                                 result = fillup_password_policy(
1902                                         our_domain, state->response);
1903                         }
1904
1905                         if (!NT_STATUS_IS_OK(result)
1906                             && !NT_STATUS_EQUAL(result, NT_STATUS_NOT_SUPPORTED) )
1907                         {
1908                                 DEBUG(10,("Failed to get password policies for domain %s: %s\n",
1909                                           domain->name, nt_errstr(result)));
1910                                 goto done;
1911                         }
1912                 }
1913
1914                 result = NT_STATUS_OK;
1915         }
1916
1917 done:
1918         /* give us a more useful (more correct?) error code */
1919         if ((NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND) ||
1920             (NT_STATUS_EQUAL(result, NT_STATUS_UNSUCCESSFUL)))) {
1921                 result = NT_STATUS_NO_LOGON_SERVERS;
1922         }
1923
1924         set_auth_errors(state->response, result);
1925
1926         DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2, ("Plain-text authentication for user %s returned %s (PAM: %d)\n",
1927               state->request->data.auth.user,
1928               state->response->data.auth.nt_status_string,
1929               state->response->data.auth.pam_error));
1930
1931         return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
1932 }
1933
1934 NTSTATUS winbind_dual_SamLogon(struct winbindd_domain *domain,
1935                                TALLOC_CTX *mem_ctx,
1936                                uint32_t logon_parameters,
1937                                const char *name_user,
1938                                const char *name_domain,
1939                                const char *workstation,
1940                                const uint8_t chal[8],
1941                                DATA_BLOB lm_response,
1942                                DATA_BLOB nt_response,
1943                                struct netr_SamInfo3 **info3)
1944 {
1945         NTSTATUS result;
1946
1947         if (strequal(name_domain, get_global_sam_name())) {
1948                 DATA_BLOB chal_blob = data_blob_const(
1949                         chal, 8);
1950
1951                 result = winbindd_dual_auth_passdb(
1952                         mem_ctx,
1953                         logon_parameters,
1954                         name_domain, name_user,
1955                         &chal_blob, &lm_response, &nt_response, info3);
1956
1957                 /* 
1958                  * We need to try the remote NETLOGON server if this is NOT_IMPLEMENTED 
1959                  */
1960                 if (!NT_STATUS_EQUAL(result, NT_STATUS_NOT_IMPLEMENTED)) {
1961                         goto process_result;
1962                 }
1963         }
1964
1965         result = winbind_samlogon_retry_loop(domain,
1966                                              mem_ctx,
1967                                              logon_parameters,
1968                                              name_user,
1969                                              NULL, /* password */
1970                                              name_domain,
1971                                              /* Bug #3248 - found by Stefan Burkei. */
1972                                              workstation, /* We carefully set this above so use it... */
1973                                              chal,
1974                                              lm_response,
1975                                              nt_response,
1976                                              false, /* interactive */
1977                                              info3);
1978         if (!NT_STATUS_IS_OK(result)) {
1979                 goto done;
1980         }
1981
1982 process_result:
1983
1984         if (NT_STATUS_IS_OK(result)) {
1985                 struct dom_sid user_sid;
1986
1987                 sid_compose(&user_sid, (*info3)->base.domain_sid,
1988                             (*info3)->base.rid);
1989
1990                 if ((*info3)->base.full_name.string == NULL) {
1991                         struct netr_SamInfo3 *cached_info3;
1992
1993                         cached_info3 = netsamlogon_cache_get(mem_ctx,
1994                                                              &user_sid);
1995                         if (cached_info3 != NULL &&
1996                             cached_info3->base.full_name.string != NULL) {
1997                                 (*info3)->base.full_name.string =
1998                                         talloc_strdup(*info3,
1999                                                       cached_info3->base.full_name.string);
2000                         } else {
2001
2002                                 /* this might fail so we dont check the return code */
2003                                 wcache_query_user_fullname(domain,
2004                                                 *info3,
2005                                                 &user_sid,
2006                                                 &(*info3)->base.full_name.string);
2007                         }
2008                 }
2009
2010                 wcache_invalidate_samlogon(find_domain_from_name(name_domain),
2011                                            &user_sid);
2012                 netsamlogon_cache_store(name_user, *info3);
2013         }
2014
2015 done:
2016
2017         /* give us a more useful (more correct?) error code */
2018         if ((NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND) ||
2019             (NT_STATUS_EQUAL(result, NT_STATUS_UNSUCCESSFUL)))) {
2020                 result = NT_STATUS_NO_LOGON_SERVERS;
2021         }
2022
2023         DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2,
2024               ("NTLM CRAP authentication for user [%s]\\[%s] returned %s\n",
2025                name_domain,
2026                name_user,
2027                nt_errstr(result)));
2028
2029         return result;
2030 }
2031
2032 enum winbindd_result winbindd_dual_pam_auth_crap(struct winbindd_domain *domain,
2033                                                  struct winbindd_cli_state *state)
2034 {
2035         NTSTATUS result;
2036         struct netr_SamInfo3 *info3 = NULL;
2037         const char *name_user = NULL;
2038         const char *name_domain = NULL;
2039         const char *workstation;
2040
2041         DATA_BLOB lm_resp, nt_resp;
2042
2043         /* This is child-only, so no check for privileged access is needed
2044            anymore */
2045
2046         /* Ensure null termination */
2047         state->request->data.auth_crap.user[sizeof(state->request->data.auth_crap.user)-1]=0;
2048         state->request->data.auth_crap.domain[sizeof(state->request->data.auth_crap.domain)-1]=0;
2049
2050         name_user = state->request->data.auth_crap.user;
2051         name_domain = state->request->data.auth_crap.domain;
2052         workstation = state->request->data.auth_crap.workstation;
2053
2054         DEBUG(3, ("[%5lu]: pam auth crap domain: %s user: %s\n", (unsigned long)state->pid,
2055                   name_domain, name_user));
2056
2057         if (state->request->data.auth_crap.lm_resp_len > sizeof(state->request->data.auth_crap.lm_resp)
2058                 || state->request->data.auth_crap.nt_resp_len > sizeof(state->request->data.auth_crap.nt_resp)) {
2059                 if (!(state->request->flags & WBFLAG_BIG_NTLMV2_BLOB) ||
2060                      state->request->extra_len != state->request->data.auth_crap.nt_resp_len) {
2061                         DEBUG(0, ("winbindd_pam_auth_crap: invalid password length %u/%u\n",
2062                                   state->request->data.auth_crap.lm_resp_len,
2063                                   state->request->data.auth_crap.nt_resp_len));
2064                         result = NT_STATUS_INVALID_PARAMETER;
2065                         goto done;
2066                 }
2067         }
2068
2069         lm_resp = data_blob_talloc(state->mem_ctx, state->request->data.auth_crap.lm_resp,
2070                                         state->request->data.auth_crap.lm_resp_len);
2071
2072         if (state->request->flags & WBFLAG_BIG_NTLMV2_BLOB) {
2073                 nt_resp = data_blob_talloc(state->mem_ctx,
2074                                            state->request->extra_data.data,
2075                                            state->request->data.auth_crap.nt_resp_len);
2076         } else {
2077                 nt_resp = data_blob_talloc(state->mem_ctx,
2078                                            state->request->data.auth_crap.nt_resp,
2079                                            state->request->data.auth_crap.nt_resp_len);
2080         }
2081
2082         result = winbind_dual_SamLogon(domain,
2083                                        state->mem_ctx,
2084                                        state->request->data.auth_crap.logon_parameters,
2085                                        name_user,
2086                                        name_domain,
2087                                        /* Bug #3248 - found by Stefan Burkei. */
2088                                        workstation, /* We carefully set this above so use it... */
2089                                        state->request->data.auth_crap.chal,
2090                                        lm_resp,
2091                                        nt_resp,
2092                                        &info3);
2093         if (!NT_STATUS_IS_OK(result)) {
2094                 goto done;
2095         }
2096
2097         if (NT_STATUS_IS_OK(result)) {
2098                 /* Check if the user is in the right group */
2099
2100                 result = check_info3_in_group(
2101                         info3,
2102                         state->request->data.auth_crap.require_membership_of_sid);
2103                 if (!NT_STATUS_IS_OK(result)) {
2104                         DEBUG(3, ("User %s is not in the required group (%s), so "
2105                                   "crap authentication is rejected\n",
2106                                   state->request->data.auth_crap.user,
2107                                   state->request->data.auth_crap.require_membership_of_sid));
2108                         goto done;
2109                 }
2110
2111                 result = append_auth_data(state->mem_ctx, state->response,
2112                                           state->request->flags, info3,
2113                                           name_domain, name_user);
2114                 if (!NT_STATUS_IS_OK(result)) {
2115                         goto done;
2116                 }
2117         }
2118
2119 done:
2120
2121         if (state->request->flags & WBFLAG_PAM_NT_STATUS_SQUASH) {
2122                 result = nt_status_squash(result);
2123         }
2124
2125         set_auth_errors(state->response, result);
2126
2127         return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
2128 }
2129
2130 enum winbindd_result winbindd_dual_pam_chauthtok(struct winbindd_domain *contact_domain,
2131                                                  struct winbindd_cli_state *state)
2132 {
2133         char *oldpass;
2134         char *newpass = NULL;
2135         struct policy_handle dom_pol;
2136         struct rpc_pipe_client *cli = NULL;
2137         bool got_info = false;
2138         struct samr_DomInfo1 *info = NULL;
2139         struct userPwdChangeFailureInformation *reject = NULL;
2140         NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
2141         fstring domain, user;
2142         struct dcerpc_binding_handle *b = NULL;
2143
2144         ZERO_STRUCT(dom_pol);
2145
2146         DEBUG(3, ("[%5lu]: dual pam chauthtok %s\n", (unsigned long)state->pid,
2147                   state->request->data.auth.user));
2148
2149         if (!parse_domain_user(state->request->data.chauthtok.user, domain, user)) {
2150                 goto done;
2151         }
2152
2153         /* Change password */
2154
2155         oldpass = state->request->data.chauthtok.oldpass;
2156         newpass = state->request->data.chauthtok.newpass;
2157
2158         /* Initialize reject reason */
2159         state->response->data.auth.reject_reason = Undefined;
2160
2161         /* Get sam handle */
2162
2163         result = cm_connect_sam(contact_domain, state->mem_ctx, true, &cli,
2164                                 &dom_pol);
2165         if (!NT_STATUS_IS_OK(result)) {
2166                 DEBUG(1, ("could not get SAM handle on DC for %s\n", domain));
2167                 goto done;
2168         }
2169
2170         b = cli->binding_handle;
2171
2172         result = rpccli_samr_chgpasswd_user3(cli, state->mem_ctx,
2173                                              user,
2174                                              newpass,
2175                                              oldpass,
2176                                              &info,
2177                                              &reject);
2178
2179         /* Windows 2003 returns NT_STATUS_PASSWORD_RESTRICTION */
2180
2181         if (NT_STATUS_EQUAL(result, NT_STATUS_PASSWORD_RESTRICTION) ) {
2182
2183                 fill_in_password_policy(state->response, info);
2184
2185                 state->response->data.auth.reject_reason =
2186                         reject->extendedFailureReason;
2187
2188                 got_info = true;
2189         }
2190
2191         /* atm the pidl generated rpccli_samr_ChangePasswordUser3 function will
2192          * return with NT_STATUS_BUFFER_TOO_SMALL for w2k dcs as w2k just
2193          * returns with 4byte error code (NT_STATUS_NOT_SUPPORTED) which is too
2194          * short to comply with the samr_ChangePasswordUser3 idl - gd */
2195
2196         /* only fallback when the chgpasswd_user3 call is not supported */
2197         if (NT_STATUS_EQUAL(result, NT_STATUS_RPC_PROCNUM_OUT_OF_RANGE) ||
2198             NT_STATUS_EQUAL(result, NT_STATUS_NOT_SUPPORTED) ||
2199             NT_STATUS_EQUAL(result, NT_STATUS_BUFFER_TOO_SMALL) ||
2200             NT_STATUS_EQUAL(result, NT_STATUS_NOT_IMPLEMENTED)) {
2201
2202                 DEBUG(10,("Password change with chgpasswd_user3 failed with: %s, retrying chgpasswd_user2\n",
2203                         nt_errstr(result)));
2204
2205                 result = rpccli_samr_chgpasswd_user2(cli, state->mem_ctx, user, newpass, oldpass);
2206
2207                 /* Windows 2000 returns NT_STATUS_ACCOUNT_RESTRICTION.
2208                    Map to the same status code as Windows 2003. */
2209
2210                 if ( NT_STATUS_EQUAL(NT_STATUS_ACCOUNT_RESTRICTION, result ) ) {
2211                         result = NT_STATUS_PASSWORD_RESTRICTION;
2212                 }
2213         }
2214
2215 done:
2216
2217         if (NT_STATUS_IS_OK(result)
2218             && (state->request->flags & WBFLAG_PAM_CACHED_LOGIN)
2219             && lp_winbind_offline_logon()) {
2220                 result = winbindd_update_creds_by_name(contact_domain, user,
2221                                                        newpass);
2222                 /* Again, this happens when we login from gdm or xdm
2223                  * and the password expires, *BUT* cached crendentials
2224                  * doesn't exist. winbindd_update_creds_by_name()
2225                  * returns NT_STATUS_NO_SUCH_USER.
2226                  * This is not a failure.
2227                  * --- BoYang
2228                  * */
2229                 if (NT_STATUS_EQUAL(result, NT_STATUS_NO_SUCH_USER)) {
2230                         result = NT_STATUS_OK;
2231                 }
2232
2233                 if (!NT_STATUS_IS_OK(result)) {
2234                         DEBUG(10, ("Failed to store creds: %s\n",
2235                                    nt_errstr(result)));
2236                         goto process_result;
2237                 }
2238         }
2239
2240         if (!NT_STATUS_IS_OK(result) && !got_info && contact_domain) {
2241
2242                 NTSTATUS policy_ret;
2243
2244                 policy_ret = fillup_password_policy(
2245                         contact_domain, state->response);
2246
2247                 /* failure of this is non critical, it will just provide no
2248                  * additional information to the client why the change has
2249                  * failed - Guenther */
2250
2251                 if (!NT_STATUS_IS_OK(policy_ret)) {
2252                         DEBUG(10,("Failed to get password policies: %s\n", nt_errstr(policy_ret)));
2253                         goto process_result;
2254                 }
2255         }
2256
2257 process_result:
2258
2259         if (strequal(contact_domain->name, get_global_sam_name())) {
2260                 /* FIXME: internal rpc pipe does not cache handles yet */
2261                 if (b) {
2262                         if (is_valid_policy_hnd(&dom_pol)) {
2263                                 NTSTATUS _result;
2264                                 dcerpc_samr_Close(b, state->mem_ctx, &dom_pol, &_result);
2265                         }
2266                         TALLOC_FREE(cli);
2267                 }
2268         }
2269
2270         set_auth_errors(state->response, result);
2271
2272         DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2,
2273               ("Password change for user [%s]\\[%s] returned %s (PAM: %d)\n",
2274                domain,
2275                user,
2276                state->response->data.auth.nt_status_string,
2277                state->response->data.auth.pam_error));
2278
2279         return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
2280 }
2281
2282 enum winbindd_result winbindd_dual_pam_logoff(struct winbindd_domain *domain,
2283                                               struct winbindd_cli_state *state)
2284 {
2285         NTSTATUS result = NT_STATUS_NOT_SUPPORTED;
2286
2287         DEBUG(3, ("[%5lu]: pam dual logoff %s\n", (unsigned long)state->pid,
2288                 state->request->data.logoff.user));
2289
2290         if (!(state->request->flags & WBFLAG_PAM_KRB5)) {
2291                 result = NT_STATUS_OK;
2292                 goto process_result;
2293         }
2294
2295         if (state->request->data.logoff.krb5ccname[0] == '\0') {
2296                 result = NT_STATUS_OK;
2297                 goto process_result;
2298         }
2299
2300 #ifdef HAVE_KRB5
2301
2302         if (state->request->data.logoff.uid < 0) {
2303                 DEBUG(0,("winbindd_pam_logoff: invalid uid\n"));
2304                 goto process_result;
2305         }
2306
2307         /* what we need here is to find the corresponding krb5 ccache name *we*
2308          * created for a given username and destroy it */
2309
2310         if (!ccache_entry_exists(state->request->data.logoff.user)) {
2311                 result = NT_STATUS_OK;
2312                 DEBUG(10,("winbindd_pam_logoff: no entry found.\n"));
2313                 goto process_result;
2314         }
2315
2316         if (!ccache_entry_identical(state->request->data.logoff.user,
2317                                         state->request->data.logoff.uid,
2318                                         state->request->data.logoff.krb5ccname)) {
2319                 DEBUG(0,("winbindd_pam_logoff: cached entry differs.\n"));
2320                 goto process_result;
2321         }
2322
2323         result = remove_ccache(state->request->data.logoff.user);
2324         if (!NT_STATUS_IS_OK(result)) {
2325                 DEBUG(0,("winbindd_pam_logoff: failed to remove ccache: %s\n",
2326                         nt_errstr(result)));
2327                 goto process_result;
2328         }
2329
2330         /*
2331          * Remove any mlock'ed memory creds in the child
2332          * we might be using for krb5 ticket renewal.
2333          */
2334
2335         winbindd_delete_memory_creds(state->request->data.logoff.user);
2336
2337 #else
2338         result = NT_STATUS_NOT_SUPPORTED;
2339 #endif
2340
2341 process_result:
2342
2343
2344         set_auth_errors(state->response, result);
2345
2346         return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
2347 }
2348
2349 /* Change user password with auth crap*/
2350
2351 enum winbindd_result winbindd_dual_pam_chng_pswd_auth_crap(struct winbindd_domain *domainSt, struct winbindd_cli_state *state)
2352 {
2353         NTSTATUS result;
2354         DATA_BLOB new_nt_password;
2355         DATA_BLOB old_nt_hash_enc;
2356         DATA_BLOB new_lm_password;
2357         DATA_BLOB old_lm_hash_enc;
2358         fstring  domain,user;
2359         struct policy_handle dom_pol;
2360         struct winbindd_domain *contact_domain = domainSt;
2361         struct rpc_pipe_client *cli = NULL;
2362         struct dcerpc_binding_handle *b = NULL;
2363
2364         ZERO_STRUCT(dom_pol);
2365
2366         /* Ensure null termination */
2367         state->request->data.chng_pswd_auth_crap.user[
2368                 sizeof(state->request->data.chng_pswd_auth_crap.user)-1]=0;
2369         state->request->data.chng_pswd_auth_crap.domain[
2370                 sizeof(state->request->data.chng_pswd_auth_crap.domain)-1]=0;
2371         *domain = 0;
2372         *user = 0;
2373
2374         DEBUG(3, ("[%5lu]: pam change pswd auth crap domain: %s user: %s\n",
2375                   (unsigned long)state->pid,
2376                   state->request->data.chng_pswd_auth_crap.domain,
2377                   state->request->data.chng_pswd_auth_crap.user));
2378
2379         if (lp_winbind_offline_logon()) {
2380                 DEBUG(0,("Refusing password change as winbind offline logons are enabled. "));
2381                 DEBUGADD(0,("Changing passwords here would risk inconsistent logons\n"));
2382                 result = NT_STATUS_ACCESS_DENIED;
2383                 goto done;
2384         }
2385
2386         if (*state->request->data.chng_pswd_auth_crap.domain) {
2387                 fstrcpy(domain,state->request->data.chng_pswd_auth_crap.domain);
2388         } else {
2389                 parse_domain_user(state->request->data.chng_pswd_auth_crap.user,
2390                                   domain, user);
2391
2392                 if(!*domain) {
2393                         DEBUG(3,("no domain specified with username (%s) - "
2394                                  "failing auth\n",
2395                                  state->request->data.chng_pswd_auth_crap.user));
2396                         result = NT_STATUS_NO_SUCH_USER;
2397                         goto done;
2398                 }
2399         }
2400
2401         if (!*domain && lp_winbind_use_default_domain()) {
2402                 fstrcpy(domain,lp_workgroup());
2403         }
2404
2405         if(!*user) {
2406                 fstrcpy(user, state->request->data.chng_pswd_auth_crap.user);
2407         }
2408
2409         DEBUG(3, ("[%5lu]: pam auth crap domain: %s user: %s\n",
2410                   (unsigned long)state->pid, domain, user));
2411
2412         /* Change password */
2413         new_nt_password = data_blob_const(
2414                 state->request->data.chng_pswd_auth_crap.new_nt_pswd,
2415                 state->request->data.chng_pswd_auth_crap.new_nt_pswd_len);
2416
2417         old_nt_hash_enc = data_blob_const(
2418                 state->request->data.chng_pswd_auth_crap.old_nt_hash_enc,
2419                 state->request->data.chng_pswd_auth_crap.old_nt_hash_enc_len);
2420
2421         if(state->request->data.chng_pswd_auth_crap.new_lm_pswd_len > 0)        {
2422                 new_lm_password = data_blob_const(
2423                         state->request->data.chng_pswd_auth_crap.new_lm_pswd,
2424                         state->request->data.chng_pswd_auth_crap.new_lm_pswd_len);
2425
2426                 old_lm_hash_enc = data_blob_const(
2427                         state->request->data.chng_pswd_auth_crap.old_lm_hash_enc,
2428                         state->request->data.chng_pswd_auth_crap.old_lm_hash_enc_len);
2429         } else {
2430                 new_lm_password = data_blob_null;
2431                 old_lm_hash_enc = data_blob_null;
2432         }
2433
2434         /* Get sam handle */
2435
2436         result = cm_connect_sam(contact_domain, state->mem_ctx, true, &cli, &dom_pol);
2437         if (!NT_STATUS_IS_OK(result)) {
2438                 DEBUG(1, ("could not get SAM handle on DC for %s\n", domain));
2439                 goto done;
2440         }
2441
2442         b = cli->binding_handle;
2443
2444         result = rpccli_samr_chng_pswd_auth_crap(
2445                 cli, state->mem_ctx, user, new_nt_password, old_nt_hash_enc,
2446                 new_lm_password, old_lm_hash_enc);
2447
2448  done:
2449
2450         if (strequal(contact_domain->name, get_global_sam_name())) {
2451                 /* FIXME: internal rpc pipe does not cache handles yet */
2452                 if (b) {
2453                         if (is_valid_policy_hnd(&dom_pol)) {
2454                                 NTSTATUS _result;
2455                                 dcerpc_samr_Close(b, state->mem_ctx, &dom_pol, &_result);
2456                         }
2457                         TALLOC_FREE(cli);
2458                 }
2459         }
2460
2461         set_auth_errors(state->response, result);
2462
2463         DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2,
2464               ("Password change for user [%s]\\[%s] returned %s (PAM: %d)\n",
2465                domain, user,
2466                state->response->data.auth.nt_status_string,
2467                state->response->data.auth.pam_error));
2468
2469         return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
2470 }
2471
2472 #ifdef HAVE_KRB5
2473 static NTSTATUS extract_pac_vrfy_sigs(TALLOC_CTX *mem_ctx, DATA_BLOB pac_blob,
2474                                       struct PAC_LOGON_INFO **logon_info)
2475 {
2476         krb5_context krbctx = NULL;
2477         krb5_error_code k5ret;
2478         krb5_keytab keytab;
2479         krb5_kt_cursor cursor;
2480         krb5_keytab_entry entry;
2481         NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
2482
2483         ZERO_STRUCT(entry);
2484         ZERO_STRUCT(cursor);
2485
2486         k5ret = krb5_init_context(&krbctx);
2487         if (k5ret) {
2488                 DEBUG(1, ("Failed to initialize kerberos context: %s\n",
2489                           error_message(k5ret)));
2490                 status = krb5_to_nt_status(k5ret);
2491                 goto out;
2492         }
2493
2494         k5ret =  gse_krb5_get_server_keytab(krbctx, &keytab);
2495         if (k5ret) {
2496                 DEBUG(1, ("Failed to get keytab: %s\n",
2497                           error_message(k5ret)));
2498                 status = krb5_to_nt_status(k5ret);
2499                 goto out_free;
2500         }
2501
2502         k5ret = krb5_kt_start_seq_get(krbctx, keytab, &cursor);
2503         if (k5ret) {
2504                 DEBUG(1, ("Failed to start seq: %s\n",
2505                           error_message(k5ret)));
2506                 status = krb5_to_nt_status(k5ret);
2507                 goto out_keytab;
2508         }
2509
2510         k5ret = krb5_kt_next_entry(krbctx, keytab, &entry, &cursor);
2511         while (k5ret == 0) {
2512                 status = kerberos_pac_logon_info(mem_ctx, pac_blob,
2513                                                  krbctx, NULL,
2514                                                  KRB5_KT_KEY(&entry), NULL, 0,
2515                                                  logon_info);
2516                 if (NT_STATUS_IS_OK(status)) {
2517                         break;
2518                 }
2519                 k5ret = smb_krb5_kt_free_entry(krbctx, &entry);
2520                 k5ret = krb5_kt_next_entry(krbctx, keytab, &entry, &cursor);
2521         }
2522
2523         k5ret = krb5_kt_end_seq_get(krbctx, keytab, &cursor);
2524         if (k5ret) {
2525                 DEBUG(1, ("Failed to end seq: %s\n",
2526                           error_message(k5ret)));
2527         }
2528 out_keytab:
2529         k5ret = krb5_kt_close(krbctx, keytab);
2530         if (k5ret) {
2531                 DEBUG(1, ("Failed to close keytab: %s\n",
2532                           error_message(k5ret)));
2533         }
2534 out_free:
2535         krb5_free_context(krbctx);
2536 out:
2537         return status;
2538 }
2539
2540 NTSTATUS winbindd_pam_auth_pac_send(struct winbindd_cli_state *state,
2541                                     struct netr_SamInfo3 **info3)
2542 {
2543         struct winbindd_request *req = state->request;
2544         DATA_BLOB pac_blob;
2545         struct PAC_LOGON_INFO *logon_info = NULL;
2546         struct netr_SamInfo3 *info3_copy = NULL;
2547         NTSTATUS result;
2548
2549         pac_blob = data_blob_const(req->extra_data.data, req->extra_len);
2550         result = extract_pac_vrfy_sigs(state->mem_ctx, pac_blob, &logon_info);
2551         if (!NT_STATUS_IS_OK(result) &&
2552             !NT_STATUS_EQUAL(result, NT_STATUS_ACCESS_DENIED)) {
2553                 DEBUG(1, ("Error during PAC signature verification: %s\n",
2554                           nt_errstr(result)));
2555                 return result;
2556         }
2557
2558         if (logon_info) {
2559                 /* Signature verification succeeded, trust the PAC */
2560                 result = create_info3_from_pac_logon_info(state->mem_ctx,
2561                                                         logon_info,
2562                                                         &info3_copy);
2563                 if (!NT_STATUS_IS_OK(result)) {
2564                         return result;
2565                 }
2566                 netsamlogon_cache_store(NULL, info3_copy);
2567
2568         } else {
2569                 /* Try without signature verification */
2570                 result = kerberos_pac_logon_info(state->mem_ctx, pac_blob, NULL,
2571                                                  NULL, NULL, NULL, 0,
2572                                                  &logon_info);
2573                 if (!NT_STATUS_IS_OK(result)) {
2574                         DEBUG(10, ("Could not extract PAC: %s\n",
2575                                    nt_errstr(result)));
2576                         return result;
2577                 }
2578                 if (logon_info) {
2579                         /*
2580                          * Don't strictly need to copy here,
2581                          * but it makes it explicit we're
2582                          * returning a copy talloc'ed off
2583                          * the state->mem_ctx.
2584                          */
2585                         info3_copy = copy_netr_SamInfo3(state->mem_ctx,
2586                                         &logon_info->info3);
2587                         if (info3_copy == NULL) {
2588                                 return NT_STATUS_NO_MEMORY;
2589                         }
2590                 }
2591         }
2592
2593         *info3 = info3_copy;
2594
2595         return NT_STATUS_OK;
2596 }
2597 #else /* HAVE_KRB5 */
2598 NTSTATUS winbindd_pam_auth_pac_send(struct winbindd_cli_state *state,
2599                                     struct netr_SamInfo3 **info3)
2600 {
2601         return NT_STATUS_NO_SUCH_USER;
2602 }
2603 #endif /* HAVE_KRB5 */