8ec4fe4a6a155e66c7b876a95eeb45c30400457e
[metze/samba/wip.git] / source3 / winbindd / winbindd_pam.c
1 /*
2    Unix SMB/CIFS implementation.
3
4    Winbind daemon - pam auth funcions
5
6    Copyright (C) Andrew Tridgell 2000
7    Copyright (C) Tim Potter 2001
8    Copyright (C) Andrew Bartlett 2001-2002
9    Copyright (C) Guenther Deschner 2005
10
11    This program is free software; you can redistribute it and/or modify
12    it under the terms of the GNU General Public License as published by
13    the Free Software Foundation; either version 3 of the License, or
14    (at your option) any later version.
15
16    This program is distributed in the hope that it will be useful,
17    but WITHOUT ANY WARRANTY; without even the implied warranty of
18    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19    GNU General Public License for more details.
20
21    You should have received a copy of the GNU General Public License
22    along with this program.  If not, see <http://www.gnu.org/licenses/>.
23 */
24
25 #include "includes.h"
26 #include "winbindd.h"
27 #include "../libcli/auth/libcli_auth.h"
28 #include "../librpc/gen_ndr/ndr_samr_c.h"
29 #include "rpc_client/cli_pipe.h"
30 #include "rpc_client/cli_samr.h"
31 #include "../librpc/gen_ndr/ndr_netlogon.h"
32 #include "rpc_client/cli_netlogon.h"
33 #include "smb_krb5.h"
34 #include "../lib/crypto/arcfour.h"
35 #include "../libcli/security/security.h"
36 #include "ads.h"
37 #include "../librpc/gen_ndr/krb5pac.h"
38 #include "passdb/machine_sid.h"
39 #include "auth.h"
40 #include "../lib/tsocket/tsocket.h"
41 #include "auth/kerberos/pac_utils.h"
42 #include "auth/gensec/gensec.h"
43 #include "librpc/crypto/gse_krb5.h"
44 #include "lib/afs/afs_funcs.h"
45
46 #undef DBGC_CLASS
47 #define DBGC_CLASS DBGC_WINBIND
48
49 #define LOGON_KRB5_FAIL_CLOCK_SKEW      0x02000000
50
51 static NTSTATUS append_info3_as_txt(TALLOC_CTX *mem_ctx,
52                                     struct winbindd_response *resp,
53                                     struct netr_SamInfo3 *info3)
54 {
55         char *ex;
56         uint32_t i;
57
58         resp->data.auth.info3.logon_time =
59                 nt_time_to_unix(info3->base.logon_time);
60         resp->data.auth.info3.logoff_time =
61                 nt_time_to_unix(info3->base.logoff_time);
62         resp->data.auth.info3.kickoff_time =
63                 nt_time_to_unix(info3->base.kickoff_time);
64         resp->data.auth.info3.pass_last_set_time =
65                 nt_time_to_unix(info3->base.last_password_change);
66         resp->data.auth.info3.pass_can_change_time =
67                 nt_time_to_unix(info3->base.allow_password_change);
68         resp->data.auth.info3.pass_must_change_time =
69                 nt_time_to_unix(info3->base.force_password_change);
70
71         resp->data.auth.info3.logon_count = info3->base.logon_count;
72         resp->data.auth.info3.bad_pw_count = info3->base.bad_password_count;
73
74         resp->data.auth.info3.user_rid = info3->base.rid;
75         resp->data.auth.info3.group_rid = info3->base.primary_gid;
76         sid_to_fstring(resp->data.auth.info3.dom_sid, info3->base.domain_sid);
77
78         resp->data.auth.info3.num_groups = info3->base.groups.count;
79         resp->data.auth.info3.user_flgs = info3->base.user_flags;
80
81         resp->data.auth.info3.acct_flags = info3->base.acct_flags;
82         resp->data.auth.info3.num_other_sids = info3->sidcount;
83
84         fstrcpy(resp->data.auth.info3.user_name,
85                 info3->base.account_name.string);
86         fstrcpy(resp->data.auth.info3.full_name,
87                 info3->base.full_name.string);
88         fstrcpy(resp->data.auth.info3.logon_script,
89                 info3->base.logon_script.string);
90         fstrcpy(resp->data.auth.info3.profile_path,
91                 info3->base.profile_path.string);
92         fstrcpy(resp->data.auth.info3.home_dir,
93                 info3->base.home_directory.string);
94         fstrcpy(resp->data.auth.info3.dir_drive,
95                 info3->base.home_drive.string);
96
97         fstrcpy(resp->data.auth.info3.logon_srv,
98                 info3->base.logon_server.string);
99         fstrcpy(resp->data.auth.info3.logon_dom,
100                 info3->base.logon_domain.string);
101
102         ex = talloc_strdup(mem_ctx, "");
103         NT_STATUS_HAVE_NO_MEMORY(ex);
104
105         for (i=0; i < info3->base.groups.count; i++) {
106                 ex = talloc_asprintf_append_buffer(ex, "0x%08X:0x%08X\n",
107                                                    info3->base.groups.rids[i].rid,
108                                                    info3->base.groups.rids[i].attributes);
109                 NT_STATUS_HAVE_NO_MEMORY(ex);
110         }
111
112         for (i=0; i < info3->sidcount; i++) {
113                 char *sid;
114
115                 sid = dom_sid_string(mem_ctx, info3->sids[i].sid);
116                 NT_STATUS_HAVE_NO_MEMORY(sid);
117
118                 ex = talloc_asprintf_append_buffer(ex, "%s:0x%08X\n",
119                                                    sid,
120                                                    info3->sids[i].attributes);
121                 NT_STATUS_HAVE_NO_MEMORY(ex);
122
123                 talloc_free(sid);
124         }
125
126         resp->extra_data.data = ex;
127         resp->length += talloc_get_size(ex);
128
129         return NT_STATUS_OK;
130 }
131
132 static NTSTATUS append_info3_as_ndr(TALLOC_CTX *mem_ctx,
133                                     struct winbindd_response *resp,
134                                     struct netr_SamInfo3 *info3)
135 {
136         DATA_BLOB blob;
137         enum ndr_err_code ndr_err;
138
139         ndr_err = ndr_push_struct_blob(&blob, mem_ctx, info3,
140                                        (ndr_push_flags_fn_t)ndr_push_netr_SamInfo3);
141         if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
142                 DEBUG(0,("append_info3_as_ndr: failed to append\n"));
143                 return ndr_map_error2ntstatus(ndr_err);
144         }
145
146         resp->extra_data.data = blob.data;
147         resp->length += blob.length;
148
149         return NT_STATUS_OK;
150 }
151
152 static NTSTATUS append_unix_username(TALLOC_CTX *mem_ctx,
153                                      struct winbindd_response *resp,
154                                      const struct netr_SamInfo3 *info3,
155                                      const char *name_domain,
156                                      const char *name_user)
157 {
158         /* We've been asked to return the unix username, per
159            'winbind use default domain' settings and the like */
160
161         const char *nt_username, *nt_domain;
162
163         nt_domain = talloc_strdup(mem_ctx, info3->base.logon_domain.string);
164         if (!nt_domain) {
165                 /* If the server didn't give us one, just use the one
166                  * we sent them */
167                 nt_domain = name_domain;
168         }
169
170         nt_username = talloc_strdup(mem_ctx, info3->base.account_name.string);
171         if (!nt_username) {
172                 /* If the server didn't give us one, just use the one
173                  * we sent them */
174                 nt_username = name_user;
175         }
176
177         fill_domain_username(resp->data.auth.unix_username,
178                              nt_domain, nt_username, true);
179
180         DEBUG(5, ("Setting unix username to [%s]\n",
181                   resp->data.auth.unix_username));
182
183         return NT_STATUS_OK;
184 }
185
186 static NTSTATUS append_afs_token(TALLOC_CTX *mem_ctx,
187                                  struct winbindd_response *resp,
188                                  const struct netr_SamInfo3 *info3,
189                                  const char *name_domain,
190                                  const char *name_user)
191 {
192         char *afsname = NULL;
193         char *cell;
194         char *token;
195
196         afsname = talloc_strdup(mem_ctx, lp_afs_username_map());
197         if (afsname == NULL) {
198                 return NT_STATUS_NO_MEMORY;
199         }
200
201         afsname = talloc_string_sub(mem_ctx,
202                                     lp_afs_username_map(),
203                                     "%D", name_domain);
204         afsname = talloc_string_sub(mem_ctx, afsname,
205                                     "%u", name_user);
206         afsname = talloc_string_sub(mem_ctx, afsname,
207                                     "%U", name_user);
208
209         {
210                 struct dom_sid user_sid;
211                 fstring sidstr;
212
213                 sid_compose(&user_sid, info3->base.domain_sid,
214                             info3->base.rid);
215                 sid_to_fstring(sidstr, &user_sid);
216                 afsname = talloc_string_sub(mem_ctx, afsname,
217                                             "%s", sidstr);
218         }
219
220         if (afsname == NULL) {
221                 return NT_STATUS_NO_MEMORY;
222         }
223
224         if (!strlower_m(afsname)) {
225                 return NT_STATUS_INVALID_PARAMETER;
226         }
227
228         DEBUG(10, ("Generating token for user %s\n", afsname));
229
230         cell = strchr(afsname, '@');
231
232         if (cell == NULL) {
233                 return NT_STATUS_NO_MEMORY;
234         }
235
236         *cell = '\0';
237         cell += 1;
238
239         token = afs_createtoken_str(afsname, cell);
240         if (token == NULL) {
241                 return NT_STATUS_OK;
242         }
243         resp->extra_data.data = talloc_strdup(mem_ctx, token);
244         if (resp->extra_data.data == NULL) {
245                 return NT_STATUS_NO_MEMORY;
246         }
247         resp->length += strlen((const char *)resp->extra_data.data)+1;
248
249         return NT_STATUS_OK;
250 }
251
252 static NTSTATUS check_info3_in_group(struct netr_SamInfo3 *info3,
253                                      const char *group_sid)
254 /**
255  * Check whether a user belongs to a group or list of groups.
256  *
257  * @param mem_ctx talloc memory context.
258  * @param info3 user information, including group membership info.
259  * @param group_sid One or more groups , separated by commas.
260  *
261  * @return NT_STATUS_OK on success,
262  *    NT_STATUS_LOGON_FAILURE if the user does not belong,
263  *    or other NT_STATUS_IS_ERR(status) for other kinds of failure.
264  */
265 {
266         struct dom_sid *require_membership_of_sid;
267         uint32_t num_require_membership_of_sid;
268         char *req_sid;
269         const char *p;
270         struct dom_sid sid;
271         size_t i;
272         struct security_token *token;
273         TALLOC_CTX *frame = talloc_stackframe();
274         NTSTATUS status;
275
276         /* Parse the 'required group' SID */
277
278         if (!group_sid || !group_sid[0]) {
279                 /* NO sid supplied, all users may access */
280                 TALLOC_FREE(frame);
281                 return NT_STATUS_OK;
282         }
283
284         token = talloc_zero(talloc_tos(), struct security_token);
285         if (token == NULL) {
286                 DEBUG(0, ("talloc failed\n"));
287                 TALLOC_FREE(frame);
288                 return NT_STATUS_NO_MEMORY;
289         }
290
291         num_require_membership_of_sid = 0;
292         require_membership_of_sid = NULL;
293
294         p = group_sid;
295
296         while (next_token_talloc(talloc_tos(), &p, &req_sid, ",")) {
297                 if (!string_to_sid(&sid, req_sid)) {
298                         DEBUG(0, ("check_info3_in_group: could not parse %s "
299                                   "as a SID!", req_sid));
300                         TALLOC_FREE(frame);
301                         return NT_STATUS_INVALID_PARAMETER;
302                 }
303
304                 status = add_sid_to_array(talloc_tos(), &sid,
305                                           &require_membership_of_sid,
306                                           &num_require_membership_of_sid);
307                 if (!NT_STATUS_IS_OK(status)) {
308                         DEBUG(0, ("add_sid_to_array failed\n"));
309                         TALLOC_FREE(frame);
310                         return status;
311                 }
312         }
313
314         status = sid_array_from_info3(talloc_tos(), info3,
315                                       &token->sids,
316                                       &token->num_sids,
317                                       true);
318         if (!NT_STATUS_IS_OK(status)) {
319                 TALLOC_FREE(frame);
320                 return status;
321         }
322
323         if (!NT_STATUS_IS_OK(status = add_aliases(get_global_sam_sid(),
324                                                   token))
325             || !NT_STATUS_IS_OK(status = add_aliases(&global_sid_Builtin,
326                                                      token))) {
327                 DEBUG(3, ("could not add aliases: %s\n",
328                           nt_errstr(status)));
329                 TALLOC_FREE(frame);
330                 return status;
331         }
332
333         security_token_debug(DBGC_CLASS, 10, token);
334
335         for (i=0; i<num_require_membership_of_sid; i++) {
336                 DEBUG(10, ("Checking SID %s\n", sid_string_dbg(
337                                    &require_membership_of_sid[i])));
338                 if (nt_token_check_sid(&require_membership_of_sid[i],
339                                        token)) {
340                         DEBUG(10, ("Access ok\n"));
341                         TALLOC_FREE(frame);
342                         return NT_STATUS_OK;
343                 }
344         }
345
346         /* Do not distinguish this error from a wrong username/pw */
347
348         TALLOC_FREE(frame);
349         return NT_STATUS_LOGON_FAILURE;
350 }
351
352 struct winbindd_domain *find_auth_domain(uint8_t flags,
353                                          const char *domain_name)
354 {
355         struct winbindd_domain *domain;
356
357         if (IS_DC) {
358                 domain = find_domain_from_name_noinit(domain_name);
359                 if (domain == NULL) {
360                         DEBUG(3, ("Authentication for domain [%s] refused "
361                                   "as it is not a trusted domain\n",
362                                   domain_name));
363                 }
364                 return domain;
365         }
366
367         if (strequal(domain_name, get_global_sam_name())) {
368                 return find_domain_from_name_noinit(domain_name);
369         }
370
371         /* we can auth against trusted domains */
372         if (flags & WBFLAG_PAM_CONTACT_TRUSTDOM) {
373                 domain = find_domain_from_name_noinit(domain_name);
374                 if (domain == NULL) {
375                         DEBUG(3, ("Authentication for domain [%s] skipped "
376                                   "as it is not a trusted domain\n",
377                                   domain_name));
378                 } else {
379                         return domain;
380                 }
381         }
382
383         return find_our_domain();
384 }
385
386 static void fill_in_password_policy(struct winbindd_response *r,
387                                     const struct samr_DomInfo1 *p)
388 {
389         r->data.auth.policy.min_length_password =
390                 p->min_password_length;
391         r->data.auth.policy.password_history =
392                 p->password_history_length;
393         r->data.auth.policy.password_properties =
394                 p->password_properties;
395         r->data.auth.policy.expire      =
396                 nt_time_to_unix_abs((const NTTIME *)&(p->max_password_age));
397         r->data.auth.policy.min_passwordage =
398                 nt_time_to_unix_abs((const NTTIME *)&(p->min_password_age));
399 }
400
401 static NTSTATUS fillup_password_policy(struct winbindd_domain *domain,
402                                        struct winbindd_response *response)
403 {
404         TALLOC_CTX *frame = talloc_stackframe();
405         struct winbindd_methods *methods;
406         NTSTATUS status;
407         struct samr_DomInfo1 password_policy;
408
409         if ( !winbindd_can_contact_domain( domain ) ) {
410                 DEBUG(5,("fillup_password_policy: No inbound trust to "
411                          "contact domain %s\n", domain->name));
412                 status = NT_STATUS_NOT_SUPPORTED;
413                 goto done;
414         }
415
416         methods = domain->methods;
417
418         status = methods->password_policy(domain, talloc_tos(), &password_policy);
419         if (NT_STATUS_IS_ERR(status)) {
420                 goto done;
421         }
422
423         fill_in_password_policy(response, &password_policy);
424
425 done:
426         TALLOC_FREE(frame);
427         return NT_STATUS_OK;
428 }
429
430 static NTSTATUS get_max_bad_attempts_from_lockout_policy(struct winbindd_domain *domain,
431                                                          TALLOC_CTX *mem_ctx,
432                                                          uint16_t *lockout_threshold)
433 {
434         struct winbindd_methods *methods;
435         NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
436         struct samr_DomInfo12 lockout_policy;
437
438         *lockout_threshold = 0;
439
440         methods = domain->methods;
441
442         status = methods->lockout_policy(domain, mem_ctx, &lockout_policy);
443         if (NT_STATUS_IS_ERR(status)) {
444                 return status;
445         }
446
447         *lockout_threshold = lockout_policy.lockout_threshold;
448
449         return NT_STATUS_OK;
450 }
451
452 static NTSTATUS get_pwd_properties(struct winbindd_domain *domain,
453                                    TALLOC_CTX *mem_ctx,
454                                    uint32_t *password_properties)
455 {
456         struct winbindd_methods *methods;
457         NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
458         struct samr_DomInfo1 password_policy;
459
460         *password_properties = 0;
461
462         methods = domain->methods;
463
464         status = methods->password_policy(domain, mem_ctx, &password_policy);
465         if (NT_STATUS_IS_ERR(status)) {
466                 return status;
467         }
468
469         *password_properties = password_policy.password_properties;
470
471         return NT_STATUS_OK;
472 }
473
474 #ifdef HAVE_KRB5
475
476 static const char *generate_krb5_ccache(TALLOC_CTX *mem_ctx,
477                                         const char *type,
478                                         uid_t uid,
479                                         const char **user_ccache_file)
480 {
481         /* accept FILE and WRFILE as krb5_cc_type from the client and then
482          * build the full ccname string based on the user's uid here -
483          * Guenther*/
484
485         const char *gen_cc = NULL;
486
487         if (uid != -1) {
488                 if (strequal(type, "FILE")) {
489                         gen_cc = talloc_asprintf(
490                                 mem_ctx, "FILE:/tmp/krb5cc_%d", uid);
491                 }
492                 if (strequal(type, "WRFILE")) {
493                         gen_cc = talloc_asprintf(
494                                 mem_ctx, "WRFILE:/tmp/krb5cc_%d", uid);
495                 }
496                 if (strequal(type, "KEYRING")) {
497                         gen_cc = talloc_asprintf(
498                                 mem_ctx, "KEYRING:persistent:%d", uid);
499                 }
500
501                 if (strnequal(type, "FILE:/", 6) ||
502                     strnequal(type, "WRFILE:/", 8) ||
503                     strnequal(type, "DIR:/", 5)) {
504
505                         /* we allow only one "%u" substitution */
506
507                         char *p;
508
509                         p = strchr(type, '%');
510                         if (p != NULL) {
511
512                                 p++;
513
514                                 if (p != NULL && *p == 'u' && strchr(p, '%') == NULL) {
515                                         char uid_str[sizeof("18446744073709551615")];
516
517                                         snprintf(uid_str, sizeof(uid_str), "%u", uid);
518
519                                         gen_cc = talloc_string_sub2(mem_ctx,
520                                                         type,
521                                                         "%u",
522                                                         uid_str,
523                                                         /* remove_unsafe_characters */
524                                                         false,
525                                                         /* replace_once */
526                                                         true,
527                                                         /* allow_trailing_dollar */
528                                                         false);
529                                 }
530                         }
531                 }
532         }
533
534         *user_ccache_file = gen_cc;
535
536         if (gen_cc == NULL) {
537                 gen_cc = talloc_strdup(mem_ctx, "MEMORY:winbindd_pam_ccache");
538         }
539         if (gen_cc == NULL) {
540                 DEBUG(0,("out of memory\n"));
541                 return NULL;
542         }
543
544         DEBUG(10, ("using ccache: %s%s\n", gen_cc,
545                    (*user_ccache_file == NULL) ? " (internal)":""));
546
547         return gen_cc;
548 }
549
550 #endif
551
552 uid_t get_uid_from_request(struct winbindd_request *request)
553 {
554         uid_t uid;
555
556         uid = request->data.auth.uid;
557
558         if (uid == (uid_t)-1) {
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                                           bool interactive,
1237                                           struct netr_SamInfo3 **pinfo3)
1238 {
1239         struct auth_context *auth_context;
1240         struct auth_serversupplied_info *server_info;
1241         struct auth_usersupplied_info *user_info = NULL;
1242         struct tsocket_address *local;
1243         struct netr_SamInfo3 *info3;
1244         NTSTATUS status;
1245         int rc;
1246         TALLOC_CTX *frame = talloc_stackframe();
1247
1248         rc = tsocket_address_inet_from_strings(frame,
1249                                                "ip",
1250                                                "127.0.0.1",
1251                                                0,
1252                                                &local);
1253         if (rc < 0) {
1254                 TALLOC_FREE(frame);
1255                 return NT_STATUS_NO_MEMORY;
1256         }
1257         status = make_user_info(frame, &user_info, user, user, domain, domain,
1258                                 lp_netbios_name(), local, lm_resp, nt_resp, NULL, NULL,
1259                                 NULL, AUTH_PASSWORD_RESPONSE);
1260         if (!NT_STATUS_IS_OK(status)) {
1261                 DEBUG(10, ("make_user_info failed: %s\n", nt_errstr(status)));
1262                 TALLOC_FREE(frame);
1263                 return status;
1264         }
1265
1266         user_info->logon_parameters = logon_parameters;
1267
1268         /* We don't want any more mapping of the username */
1269         user_info->mapped_state = True;
1270
1271         /* We don't want to come back to winbindd or to do PAM account checks */
1272         user_info->flags |= USER_INFO_LOCAL_SAM_ONLY | USER_INFO_INFO3_AND_NO_AUTHZ;
1273
1274         if (interactive) {
1275                 user_info->flags |= USER_INFO_INTERACTIVE_LOGON;
1276         }
1277
1278         status = make_auth_context_fixed(frame, &auth_context, challenge->data);
1279
1280         if (!NT_STATUS_IS_OK(status)) {
1281                 DEBUG(0, ("Failed to test authentication with check_sam_security_info3: %s\n", nt_errstr(status)));
1282                 TALLOC_FREE(frame);
1283                 return status;
1284         }
1285
1286         status = auth_check_ntlm_password(mem_ctx,
1287                                           auth_context,
1288                                           user_info,
1289                                           &server_info);
1290
1291         if (!NT_STATUS_IS_OK(status)) {
1292                 TALLOC_FREE(frame);
1293                 return status;
1294         }
1295
1296         info3 = talloc_zero(mem_ctx, struct netr_SamInfo3);
1297         if (info3 == NULL) {
1298                 TALLOC_FREE(frame);
1299                 return NT_STATUS_NO_MEMORY;
1300         }
1301
1302         status = serverinfo_to_SamInfo3(server_info, info3);
1303         if (!NT_STATUS_IS_OK(status)) {
1304                 TALLOC_FREE(frame);
1305                 TALLOC_FREE(info3);
1306                 DEBUG(0, ("serverinfo_to_SamInfo3 failed: %s\n",
1307                           nt_errstr(status)));
1308                 return status;
1309         }
1310
1311         *pinfo3 = info3;
1312         DEBUG(10, ("Authenticaticating user %s\\%s returned %s\n", domain,
1313                    user, nt_errstr(status)));
1314         TALLOC_FREE(frame);
1315         return status;
1316 }
1317
1318 static NTSTATUS winbind_samlogon_retry_loop(struct winbindd_domain *domain,
1319                                             TALLOC_CTX *mem_ctx,
1320                                             uint32_t logon_parameters,
1321                                             const char *username,
1322                                             const char *password,
1323                                             const char *domainname,
1324                                             const char *workstation,
1325                                             const uint8_t chal[8],
1326                                             DATA_BLOB lm_response,
1327                                             DATA_BLOB nt_response,
1328                                             bool interactive,
1329                                             struct netr_SamInfo3 **info3)
1330 {
1331         int attempts = 0;
1332         int netr_attempts = 0;
1333         bool retry = false;
1334         NTSTATUS result;
1335
1336         do {
1337                 struct rpc_pipe_client *netlogon_pipe;
1338                 uint8_t authoritative = 0;
1339                 uint32_t flags = 0;
1340
1341                 ZERO_STRUCTP(info3);
1342                 retry = false;
1343
1344                 result = cm_connect_netlogon(domain, &netlogon_pipe);
1345
1346                 if (!NT_STATUS_IS_OK(result)) {
1347                         DEBUG(3,("Could not open handle to NETLOGON pipe "
1348                                  "(error: %s, attempts: %d)\n",
1349                                   nt_errstr(result), netr_attempts));
1350
1351                         /* After the first retry always close the connection */
1352                         if (netr_attempts > 0) {
1353                                 DEBUG(3, ("This is again a problem for this "
1354                                           "particular call, forcing the close "
1355                                           "of this connection\n"));
1356                                 invalidate_cm_connection(domain);
1357                         }
1358
1359                         /* After the second retry failover to the next DC */
1360                         if (netr_attempts > 1) {
1361                                 /*
1362                                  * If the netlogon server is not reachable then
1363                                  * it is possible that the DC is rebuilding
1364                                  * sysvol and shutdown netlogon for that time.
1365                                  * We should failover to the next dc.
1366                                  */
1367                                 DEBUG(3, ("This is the third problem for this "
1368                                           "particular call, adding DC to the "
1369                                           "negative cache list\n"));
1370                                 add_failed_connection_entry(domain->name,
1371                                                             domain->dcname,
1372                                                             result);
1373                                 saf_delete(domain->name);
1374                         }
1375
1376                         /* Only allow 3 retries */
1377                         if (netr_attempts < 3) {
1378                                 DEBUG(3, ("The connection to netlogon "
1379                                           "failed, retrying\n"));
1380                                 netr_attempts++;
1381                                 retry = true;
1382                                 continue;
1383                         }
1384                         return result;
1385                 }
1386                 netr_attempts = 0;
1387                 if (domain->conn.netlogon_creds == NULL) {
1388                         DBG_NOTICE("No security credentials available for "
1389                                   "domain [%s]\n", domainname);
1390                         result = NT_STATUS_CANT_ACCESS_DOMAIN_INFO;
1391                 } else if (interactive && username != NULL && password != NULL) {
1392                         result = rpccli_netlogon_password_logon(domain->conn.netlogon_creds,
1393                                                                 netlogon_pipe->binding_handle,
1394                                                                 mem_ctx,
1395                                                                 logon_parameters,
1396                                                                 domainname,
1397                                                                 username,
1398                                                                 password,
1399                                                                 workstation,
1400                                                                 NetlogonInteractiveInformation,
1401                                                                 info3);
1402                 } else {
1403                         result = rpccli_netlogon_network_logon(domain->conn.netlogon_creds,
1404                                                         netlogon_pipe->binding_handle,
1405                                                         mem_ctx,
1406                                                         logon_parameters,
1407                                                         username,
1408                                                         domainname,
1409                                                         workstation,
1410                                                         chal,
1411                                                         lm_response,
1412                                                         nt_response,
1413                                                         &authoritative,
1414                                                         &flags,
1415                                                         info3);
1416                 }
1417
1418                 /*
1419                  * we increment this after the "feature negotiation"
1420                  * for can_do_samlogon_ex and can_do_validation6
1421                  */
1422                 attempts += 1;
1423
1424                 /* We have to try a second time as cm_connect_netlogon
1425                    might not yet have noticed that the DC has killed
1426                    our connection. */
1427
1428                 if (!rpccli_is_connected(netlogon_pipe)) {
1429                         retry = true;
1430                         continue;
1431                 }
1432
1433                 /* if we get access denied, a possible cause was that we had
1434                    and open connection to the DC, but someone changed our
1435                    machine account password out from underneath us using 'net
1436                    rpc changetrustpw' */
1437
1438                 if ( NT_STATUS_EQUAL(result, NT_STATUS_ACCESS_DENIED) ) {
1439                         DEBUG(1,("winbind_samlogon_retry_loop: sam_logon returned "
1440                                  "ACCESS_DENIED.  Maybe the DC has Restrict "
1441                                  "NTLM set or the trust account "
1442                                 "password was changed and we didn't know it. "
1443                                  "Killing connections to domain %s\n",
1444                                 domainname));
1445                         invalidate_cm_connection(domain);
1446                         retry = true;
1447                 }
1448
1449                 if (NT_STATUS_EQUAL(result, NT_STATUS_RPC_PROCNUM_OUT_OF_RANGE)) {
1450                         /*
1451                          * Got DCERPC_FAULT_OP_RNG_ERROR for SamLogon
1452                          * (no Ex). This happens against old Samba
1453                          * DCs, if LogonSamLogonEx() fails with an error
1454                          * e.g. NT_STATUS_NO_SUCH_USER or NT_STATUS_WRONG_PASSWORD.
1455                          *
1456                          * The server will log something like this:
1457                          * api_net_sam_logon_ex: Failed to marshall NET_R_SAM_LOGON_EX.
1458                          *
1459                          * This sets the whole connection into a fault_state mode
1460                          * and all following request get NT_STATUS_RPC_PROCNUM_OUT_OF_RANGE.
1461                          *
1462                          * This also happens to our retry with LogonSamLogonWithFlags()
1463                          * and LogonSamLogon().
1464                          *
1465                          * In order to recover from this situation, we need to
1466                          * drop the connection.
1467                          */
1468                         invalidate_cm_connection(domain);
1469                         result = NT_STATUS_LOGON_FAILURE;
1470                         break;
1471                 }
1472
1473         } while ( (attempts < 2) && retry );
1474
1475         if (NT_STATUS_EQUAL(result, NT_STATUS_IO_TIMEOUT)) {
1476                 DEBUG(3,("winbind_samlogon_retry_loop: sam_network_logon(ex) "
1477                                 "returned NT_STATUS_IO_TIMEOUT after the retry."
1478                                 "Killing connections to domain %s\n",
1479                         domainname));
1480                 invalidate_cm_connection(domain);
1481         }
1482         return result;
1483 }
1484
1485 static NTSTATUS winbindd_dual_pam_auth_samlogon(TALLOC_CTX *mem_ctx,
1486                                                 struct winbindd_domain *domain,
1487                                                 const char *user,
1488                                                 const char *pass,
1489                                                 uint32_t request_flags,
1490                                                 struct netr_SamInfo3 **info3)
1491 {
1492
1493         uchar chal[8];
1494         DATA_BLOB lm_resp;
1495         DATA_BLOB nt_resp;
1496         unsigned char local_nt_response[24];
1497         fstring name_domain, name_user;
1498         NTSTATUS result;
1499         struct netr_SamInfo3 *my_info3 = NULL;
1500
1501         *info3 = NULL;
1502
1503         DEBUG(10,("winbindd_dual_pam_auth_samlogon\n"));
1504
1505         /* Parse domain and username */
1506
1507         parse_domain_user(user, name_domain, name_user);
1508
1509         /* do password magic */
1510
1511         generate_random_buffer(chal, sizeof(chal));
1512
1513         if (lp_client_ntlmv2_auth()) {
1514                 DATA_BLOB server_chal;
1515                 DATA_BLOB names_blob;
1516                 server_chal = data_blob_const(chal, 8);
1517
1518                 /* note that the 'workgroup' here is for the local
1519                    machine.  The 'server name' must match the
1520                    'workstation' passed to the actual SamLogon call.
1521                 */
1522                 names_blob = NTLMv2_generate_names_blob(
1523                         mem_ctx, lp_netbios_name(), lp_workgroup());
1524
1525                 if (!SMBNTLMv2encrypt(mem_ctx, name_user, name_domain,
1526                                       pass,
1527                                       &server_chal,
1528                                       &names_blob,
1529                                       &lm_resp, &nt_resp, NULL, NULL)) {
1530                         data_blob_free(&names_blob);
1531                         DEBUG(0, ("winbindd_pam_auth: SMBNTLMv2encrypt() failed!\n"));
1532                         result = NT_STATUS_NO_MEMORY;
1533                         goto done;
1534                 }
1535                 data_blob_free(&names_blob);
1536         } else {
1537                 lm_resp = data_blob_null;
1538                 SMBNTencrypt(pass, chal, local_nt_response);
1539
1540                 nt_resp = data_blob_talloc(mem_ctx, local_nt_response,
1541                                            sizeof(local_nt_response));
1542         }
1543
1544         if (strequal(name_domain, get_global_sam_name())) {
1545                 DATA_BLOB chal_blob = data_blob_const(chal, sizeof(chal));
1546
1547                 result = winbindd_dual_auth_passdb(
1548                         mem_ctx, 0, name_domain, name_user,
1549                         &chal_blob, &lm_resp, &nt_resp,
1550                         true, /* interactive */
1551                         info3);
1552
1553                 /* 
1554                  * We need to try the remote NETLOGON server if this is NOT_IMPLEMENTED 
1555                  */
1556                 if (!NT_STATUS_EQUAL(result, NT_STATUS_NOT_IMPLEMENTED)) {
1557                         goto done;
1558                 }
1559         }
1560
1561         /* check authentication loop */
1562
1563         result = winbind_samlogon_retry_loop(domain,
1564                                              mem_ctx,
1565                                              0,
1566                                              name_user,
1567                                              pass,
1568                                              name_domain,
1569                                              lp_netbios_name(),
1570                                              chal,
1571                                              lm_resp,
1572                                              nt_resp,
1573                                              true, /* interactive */
1574                                              &my_info3);
1575         if (!NT_STATUS_IS_OK(result)) {
1576                 goto done;
1577         }
1578
1579         /* handle the case where a NT4 DC does not fill in the acct_flags in
1580          * the samlogon reply info3. When accurate info3 is required by the
1581          * caller, we look up the account flags ourselve - gd */
1582
1583         if ((request_flags & WBFLAG_PAM_INFO3_TEXT) &&
1584             NT_STATUS_IS_OK(result) && (my_info3->base.acct_flags == 0)) {
1585
1586                 struct rpc_pipe_client *samr_pipe;
1587                 struct policy_handle samr_domain_handle, user_pol;
1588                 union samr_UserInfo *info = NULL;
1589                 NTSTATUS status_tmp, result_tmp;
1590                 uint32_t acct_flags;
1591                 struct dcerpc_binding_handle *b;
1592
1593                 status_tmp = cm_connect_sam(domain, mem_ctx, false,
1594                                             &samr_pipe, &samr_domain_handle);
1595
1596                 if (!NT_STATUS_IS_OK(status_tmp)) {
1597                         DEBUG(3, ("could not open handle to SAMR pipe: %s\n",
1598                                 nt_errstr(status_tmp)));
1599                         goto done;
1600                 }
1601
1602                 b = samr_pipe->binding_handle;
1603
1604                 status_tmp = dcerpc_samr_OpenUser(b, mem_ctx,
1605                                                   &samr_domain_handle,
1606                                                   MAXIMUM_ALLOWED_ACCESS,
1607                                                   my_info3->base.rid,
1608                                                   &user_pol,
1609                                                   &result_tmp);
1610
1611                 if (!NT_STATUS_IS_OK(status_tmp)) {
1612                         DEBUG(3, ("could not open user handle on SAMR pipe: %s\n",
1613                                 nt_errstr(status_tmp)));
1614                         goto done;
1615                 }
1616                 if (!NT_STATUS_IS_OK(result_tmp)) {
1617                         DEBUG(3, ("could not open user handle on SAMR pipe: %s\n",
1618                                 nt_errstr(result_tmp)));
1619                         goto done;
1620                 }
1621
1622                 status_tmp = dcerpc_samr_QueryUserInfo(b, mem_ctx,
1623                                                        &user_pol,
1624                                                        16,
1625                                                        &info,
1626                                                        &result_tmp);
1627
1628                 if (!NT_STATUS_IS_OK(status_tmp)) {
1629                         DEBUG(3, ("could not query user info on SAMR pipe: %s\n",
1630                                 nt_errstr(status_tmp)));
1631                         dcerpc_samr_Close(b, mem_ctx, &user_pol, &result_tmp);
1632                         goto done;
1633                 }
1634                 if (!NT_STATUS_IS_OK(result_tmp)) {
1635                         DEBUG(3, ("could not query user info on SAMR pipe: %s\n",
1636                                 nt_errstr(result_tmp)));
1637                         dcerpc_samr_Close(b, mem_ctx, &user_pol, &result_tmp);
1638                         goto done;
1639                 }
1640
1641                 acct_flags = info->info16.acct_flags;
1642
1643                 if (acct_flags == 0) {
1644                         dcerpc_samr_Close(b, mem_ctx, &user_pol, &result_tmp);
1645                         goto done;
1646                 }
1647
1648                 my_info3->base.acct_flags = acct_flags;
1649
1650                 DEBUG(10,("successfully retrieved acct_flags 0x%x\n", acct_flags));
1651
1652                 dcerpc_samr_Close(b, mem_ctx, &user_pol, &result_tmp);
1653         }
1654
1655         *info3 = my_info3;
1656 done:
1657         return result;
1658 }
1659
1660 enum winbindd_result winbindd_dual_pam_auth(struct winbindd_domain *domain,
1661                                             struct winbindd_cli_state *state)
1662 {
1663         NTSTATUS result = NT_STATUS_LOGON_FAILURE;
1664         NTSTATUS krb5_result = NT_STATUS_OK;
1665         fstring name_domain, name_user;
1666         char *mapped_user;
1667         fstring domain_user;
1668         struct netr_SamInfo3 *info3 = NULL;
1669         NTSTATUS name_map_status = NT_STATUS_UNSUCCESSFUL;
1670
1671         /* Ensure null termination */
1672         state->request->data.auth.user[sizeof(state->request->data.auth.user)-1]='\0';
1673
1674         /* Ensure null termination */
1675         state->request->data.auth.pass[sizeof(state->request->data.auth.pass)-1]='\0';
1676
1677         DEBUG(3, ("[%5lu]: dual pam auth %s\n", (unsigned long)state->pid,
1678                   state->request->data.auth.user));
1679
1680         /* Parse domain and username */
1681
1682         name_map_status = normalize_name_unmap(state->mem_ctx,
1683                                                state->request->data.auth.user,
1684                                                &mapped_user);
1685
1686         /* If the name normalization didnt' actually do anything,
1687            just use the original name */
1688
1689         if (!NT_STATUS_IS_OK(name_map_status) &&
1690             !NT_STATUS_EQUAL(name_map_status, NT_STATUS_FILE_RENAMED))
1691         {
1692                 mapped_user = state->request->data.auth.user;
1693         }
1694
1695         parse_domain_user(mapped_user, name_domain, name_user);
1696
1697         if ( mapped_user != state->request->data.auth.user ) {
1698                 fstr_sprintf( domain_user, "%s%c%s", name_domain,
1699                         *lp_winbind_separator(),
1700                         name_user );
1701                 strlcpy( state->request->data.auth.user, domain_user,
1702                              sizeof(state->request->data.auth.user));
1703         }
1704
1705         if (!domain->online) {
1706                 result = NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND;
1707                 if (domain->startup) {
1708                         /* Logons are very important to users. If we're offline and
1709                            we get a request within the first 30 seconds of startup,
1710                            try very hard to find a DC and go online. */
1711
1712                         DEBUG(10,("winbindd_dual_pam_auth: domain: %s offline and auth "
1713                                 "request in startup mode.\n", domain->name ));
1714
1715                         winbindd_flush_negative_conn_cache(domain);
1716                         result = init_dc_connection(domain, false);
1717                 }
1718         }
1719
1720         DEBUG(10,("winbindd_dual_pam_auth: domain: %s last was %s\n", domain->name, domain->online ? "online":"offline"));
1721
1722         /* Check for Kerberos authentication */
1723         if (domain->online && (state->request->flags & WBFLAG_PAM_KRB5)) {
1724
1725                 result = winbindd_dual_pam_auth_kerberos(domain, state, &info3);
1726                 /* save for later */
1727                 krb5_result = result;
1728
1729
1730                 if (NT_STATUS_IS_OK(result)) {
1731                         DEBUG(10,("winbindd_dual_pam_auth_kerberos succeeded\n"));
1732                         goto process_result;
1733                 } else {
1734                         DEBUG(10,("winbindd_dual_pam_auth_kerberos failed: %s\n", nt_errstr(result)));
1735                 }
1736
1737                 if (NT_STATUS_EQUAL(result, NT_STATUS_NO_LOGON_SERVERS) ||
1738                     NT_STATUS_EQUAL(result, NT_STATUS_IO_TIMEOUT) ||
1739                     NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
1740                         DEBUG(10,("winbindd_dual_pam_auth_kerberos setting domain to offline\n"));
1741                         set_domain_offline( domain );
1742                         goto cached_logon;
1743                 }
1744
1745                 /* there are quite some NT_STATUS errors where there is no
1746                  * point in retrying with a samlogon, we explictly have to take
1747                  * care not to increase the bad logon counter on the DC */
1748
1749                 if (NT_STATUS_EQUAL(result, NT_STATUS_ACCOUNT_DISABLED) ||
1750                     NT_STATUS_EQUAL(result, NT_STATUS_ACCOUNT_EXPIRED) ||
1751                     NT_STATUS_EQUAL(result, NT_STATUS_ACCOUNT_LOCKED_OUT) ||
1752                     NT_STATUS_EQUAL(result, NT_STATUS_INVALID_LOGON_HOURS) ||
1753                     NT_STATUS_EQUAL(result, NT_STATUS_INVALID_WORKSTATION) ||
1754                     NT_STATUS_EQUAL(result, NT_STATUS_LOGON_FAILURE) ||
1755                     NT_STATUS_EQUAL(result, NT_STATUS_NO_SUCH_USER) ||
1756                     NT_STATUS_EQUAL(result, NT_STATUS_PASSWORD_EXPIRED) ||
1757                     NT_STATUS_EQUAL(result, NT_STATUS_PASSWORD_MUST_CHANGE) ||
1758                     NT_STATUS_EQUAL(result, NT_STATUS_WRONG_PASSWORD)) {
1759                         goto done;
1760                 }
1761
1762                 if (state->request->flags & WBFLAG_PAM_FALLBACK_AFTER_KRB5) {
1763                         DEBUG(3,("falling back to samlogon\n"));
1764                         goto sam_logon;
1765                 } else {
1766                         goto cached_logon;
1767                 }
1768         }
1769
1770 sam_logon:
1771         /* Check for Samlogon authentication */
1772         if (domain->online) {
1773                 result = winbindd_dual_pam_auth_samlogon(
1774                         state->mem_ctx, domain,
1775                         state->request->data.auth.user,
1776                         state->request->data.auth.pass,
1777                         state->request->flags,
1778                         &info3);
1779
1780                 if (NT_STATUS_IS_OK(result)) {
1781                         DEBUG(10,("winbindd_dual_pam_auth_samlogon succeeded\n"));
1782                         /* add the Krb5 err if we have one */
1783                         if ( NT_STATUS_EQUAL(krb5_result, NT_STATUS_TIME_DIFFERENCE_AT_DC ) ) {
1784                                 info3->base.user_flags |= LOGON_KRB5_FAIL_CLOCK_SKEW;
1785                         }
1786                         goto process_result;
1787                 }
1788
1789                 DEBUG(10,("winbindd_dual_pam_auth_samlogon failed: %s\n",
1790                           nt_errstr(result)));
1791
1792                 if (NT_STATUS_EQUAL(result, NT_STATUS_NO_LOGON_SERVERS) ||
1793                     NT_STATUS_EQUAL(result, NT_STATUS_IO_TIMEOUT) ||
1794                     NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND))
1795                 {
1796                         DEBUG(10,("winbindd_dual_pam_auth_samlogon setting domain to offline\n"));
1797                         set_domain_offline( domain );
1798                         goto cached_logon;
1799                 }
1800
1801                         if (domain->online) {
1802                                 /* We're still online - fail. */
1803                                 goto done;
1804                         }
1805         }
1806
1807 cached_logon:
1808         /* Check for Cached logons */
1809         if (!domain->online && (state->request->flags & WBFLAG_PAM_CACHED_LOGIN) &&
1810             lp_winbind_offline_logon()) {
1811
1812                 result = winbindd_dual_pam_auth_cached(domain, state, &info3);
1813
1814                 if (NT_STATUS_IS_OK(result)) {
1815                         DEBUG(10,("winbindd_dual_pam_auth_cached succeeded\n"));
1816                         goto process_result;
1817                 } else {
1818                         DEBUG(10,("winbindd_dual_pam_auth_cached failed: %s\n", nt_errstr(result)));
1819                         goto done;
1820                 }
1821         }
1822
1823 process_result:
1824
1825         if (NT_STATUS_IS_OK(result)) {
1826
1827                 struct dom_sid user_sid;
1828
1829                 /* In all codepaths where result == NT_STATUS_OK info3 must have
1830                    been initialized. */
1831                 if (!info3) {
1832                         result = NT_STATUS_INTERNAL_ERROR;
1833                         goto done;
1834                 }
1835
1836                 sid_compose(&user_sid, info3->base.domain_sid,
1837                             info3->base.rid);
1838
1839                 if (info3->base.full_name.string == NULL) {
1840                         struct netr_SamInfo3 *cached_info3;
1841
1842                         cached_info3 = netsamlogon_cache_get(state->mem_ctx,
1843                                                              &user_sid);
1844                         if (cached_info3 != NULL &&
1845                             cached_info3->base.full_name.string != NULL) {
1846                                 info3->base.full_name.string =
1847                                         talloc_strdup(info3,
1848                                                       cached_info3->base.full_name.string);
1849                         } else {
1850
1851                                 /* this might fail so we don't check the return code */
1852                                 wcache_query_user_fullname(domain,
1853                                                 info3,
1854                                                 &user_sid,
1855                                                 &info3->base.full_name.string);
1856                         }
1857                 }
1858
1859                 wcache_invalidate_samlogon(find_domain_from_name(name_domain),
1860                                            &user_sid);
1861                 netsamlogon_cache_store(name_user, info3);
1862
1863                 /* save name_to_sid info as early as possible (only if
1864                    this is our primary domain so we don't invalidate
1865                    the cache entry by storing the seq_num for the wrong
1866                    domain). */
1867                 if ( domain->primary ) {
1868                         cache_name2sid(domain, name_domain, name_user,
1869                                        SID_NAME_USER, &user_sid);
1870                 }
1871
1872                 /* Check if the user is in the right group */
1873
1874                 result = check_info3_in_group(
1875                         info3,
1876                         state->request->data.auth.require_membership_of_sid);
1877                 if (!NT_STATUS_IS_OK(result)) {
1878                         DEBUG(3, ("User %s is not in the required group (%s), so plaintext authentication is rejected\n",
1879                                   state->request->data.auth.user,
1880                                   state->request->data.auth.require_membership_of_sid));
1881                         goto done;
1882                 }
1883
1884                 result = append_auth_data(state->mem_ctx, state->response,
1885                                           state->request->flags, info3,
1886                                           name_domain, name_user);
1887                 if (!NT_STATUS_IS_OK(result)) {
1888                         goto done;
1889                 }
1890
1891                 if ((state->request->flags & WBFLAG_PAM_CACHED_LOGIN)
1892                     && lp_winbind_offline_logon()) {
1893
1894                         result = winbindd_store_creds(domain,
1895                                                       state->request->data.auth.user,
1896                                                       state->request->data.auth.pass,
1897                                                       info3);
1898                 }
1899
1900                 if (state->request->flags & WBFLAG_PAM_GET_PWD_POLICY) {
1901                         struct winbindd_domain *our_domain = find_our_domain();
1902
1903                         /* This is not entirely correct I believe, but it is
1904                            consistent.  Only apply the password policy settings
1905                            too warn users for our own domain.  Cannot obtain these
1906                            from trusted DCs all the  time so don't do it at all.
1907                            -- jerry */
1908
1909                         result = NT_STATUS_NOT_SUPPORTED;
1910                         if (our_domain == domain ) {
1911                                 result = fillup_password_policy(
1912                                         our_domain, state->response);
1913                         }
1914
1915                         if (!NT_STATUS_IS_OK(result)
1916                             && !NT_STATUS_EQUAL(result, NT_STATUS_NOT_SUPPORTED) )
1917                         {
1918                                 DEBUG(10,("Failed to get password policies for domain %s: %s\n",
1919                                           domain->name, nt_errstr(result)));
1920                                 goto done;
1921                         }
1922                 }
1923
1924                 result = NT_STATUS_OK;
1925         }
1926
1927 done:
1928         /* give us a more useful (more correct?) error code */
1929         if ((NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND) ||
1930             (NT_STATUS_EQUAL(result, NT_STATUS_UNSUCCESSFUL)))) {
1931                 result = NT_STATUS_NO_LOGON_SERVERS;
1932         }
1933
1934         set_auth_errors(state->response, result);
1935
1936         DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2, ("Plain-text authentication for user %s returned %s (PAM: %d)\n",
1937               state->request->data.auth.user,
1938               state->response->data.auth.nt_status_string,
1939               state->response->data.auth.pam_error));
1940
1941         return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
1942 }
1943
1944 NTSTATUS winbind_dual_SamLogon(struct winbindd_domain *domain,
1945                                TALLOC_CTX *mem_ctx,
1946                                uint32_t logon_parameters,
1947                                const char *name_user,
1948                                const char *name_domain,
1949                                const char *workstation,
1950                                const uint8_t chal[8],
1951                                DATA_BLOB lm_response,
1952                                DATA_BLOB nt_response,
1953                                struct netr_SamInfo3 **info3)
1954 {
1955         NTSTATUS result;
1956
1957         if (strequal(name_domain, get_global_sam_name())) {
1958                 DATA_BLOB chal_blob = data_blob_const(
1959                         chal, 8);
1960
1961                 result = winbindd_dual_auth_passdb(
1962                         mem_ctx,
1963                         logon_parameters,
1964                         name_domain, name_user,
1965                         &chal_blob, &lm_response, &nt_response,
1966                         false, /* interactive */
1967                         info3);
1968
1969                 /* 
1970                  * We need to try the remote NETLOGON server if this is NOT_IMPLEMENTED 
1971                  */
1972                 if (!NT_STATUS_EQUAL(result, NT_STATUS_NOT_IMPLEMENTED)) {
1973                         goto process_result;
1974                 }
1975         }
1976
1977         result = winbind_samlogon_retry_loop(domain,
1978                                              mem_ctx,
1979                                              logon_parameters,
1980                                              name_user,
1981                                              NULL, /* password */
1982                                              name_domain,
1983                                              /* Bug #3248 - found by Stefan Burkei. */
1984                                              workstation, /* We carefully set this above so use it... */
1985                                              chal,
1986                                              lm_response,
1987                                              nt_response,
1988                                              false, /* interactive */
1989                                              info3);
1990         if (!NT_STATUS_IS_OK(result)) {
1991                 goto done;
1992         }
1993
1994 process_result:
1995
1996         if (NT_STATUS_IS_OK(result)) {
1997                 struct dom_sid user_sid;
1998
1999                 sid_compose(&user_sid, (*info3)->base.domain_sid,
2000                             (*info3)->base.rid);
2001
2002                 if ((*info3)->base.full_name.string == NULL) {
2003                         struct netr_SamInfo3 *cached_info3;
2004
2005                         cached_info3 = netsamlogon_cache_get(mem_ctx,
2006                                                              &user_sid);
2007                         if (cached_info3 != NULL &&
2008                             cached_info3->base.full_name.string != NULL) {
2009                                 (*info3)->base.full_name.string =
2010                                         talloc_strdup(*info3,
2011                                                       cached_info3->base.full_name.string);
2012                         } else {
2013
2014                                 /* this might fail so we don't check the return code */
2015                                 wcache_query_user_fullname(domain,
2016                                                 *info3,
2017                                                 &user_sid,
2018                                                 &(*info3)->base.full_name.string);
2019                         }
2020                 }
2021
2022                 wcache_invalidate_samlogon(find_domain_from_name(name_domain),
2023                                            &user_sid);
2024                 netsamlogon_cache_store(name_user, *info3);
2025         }
2026
2027 done:
2028
2029         /* give us a more useful (more correct?) error code */
2030         if ((NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND) ||
2031             (NT_STATUS_EQUAL(result, NT_STATUS_UNSUCCESSFUL)))) {
2032                 result = NT_STATUS_NO_LOGON_SERVERS;
2033         }
2034
2035         DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2,
2036               ("NTLM CRAP authentication for user [%s]\\[%s] returned %s\n",
2037                name_domain,
2038                name_user,
2039                nt_errstr(result)));
2040
2041         return result;
2042 }
2043
2044 enum winbindd_result winbindd_dual_pam_auth_crap(struct winbindd_domain *domain,
2045                                                  struct winbindd_cli_state *state)
2046 {
2047         NTSTATUS result;
2048         struct netr_SamInfo3 *info3 = NULL;
2049         const char *name_user = NULL;
2050         const char *name_domain = NULL;
2051         const char *workstation;
2052
2053         DATA_BLOB lm_resp, nt_resp;
2054
2055         /* This is child-only, so no check for privileged access is needed
2056            anymore */
2057
2058         /* Ensure null termination */
2059         state->request->data.auth_crap.user[sizeof(state->request->data.auth_crap.user)-1]=0;
2060         state->request->data.auth_crap.domain[sizeof(state->request->data.auth_crap.domain)-1]=0;
2061
2062         name_user = state->request->data.auth_crap.user;
2063         name_domain = state->request->data.auth_crap.domain;
2064         workstation = state->request->data.auth_crap.workstation;
2065
2066         DEBUG(3, ("[%5lu]: pam auth crap domain: %s user: %s\n", (unsigned long)state->pid,
2067                   name_domain, name_user));
2068
2069         if (state->request->data.auth_crap.lm_resp_len > sizeof(state->request->data.auth_crap.lm_resp)
2070                 || state->request->data.auth_crap.nt_resp_len > sizeof(state->request->data.auth_crap.nt_resp)) {
2071                 if (!(state->request->flags & WBFLAG_BIG_NTLMV2_BLOB) ||
2072                      state->request->extra_len != state->request->data.auth_crap.nt_resp_len) {
2073                         DEBUG(0, ("winbindd_pam_auth_crap: invalid password length %u/%u\n",
2074                                   state->request->data.auth_crap.lm_resp_len,
2075                                   state->request->data.auth_crap.nt_resp_len));
2076                         result = NT_STATUS_INVALID_PARAMETER;
2077                         goto done;
2078                 }
2079         }
2080
2081         lm_resp = data_blob_talloc(state->mem_ctx, state->request->data.auth_crap.lm_resp,
2082                                         state->request->data.auth_crap.lm_resp_len);
2083
2084         if (state->request->flags & WBFLAG_BIG_NTLMV2_BLOB) {
2085                 nt_resp = data_blob_talloc(state->mem_ctx,
2086                                            state->request->extra_data.data,
2087                                            state->request->data.auth_crap.nt_resp_len);
2088         } else {
2089                 nt_resp = data_blob_talloc(state->mem_ctx,
2090                                            state->request->data.auth_crap.nt_resp,
2091                                            state->request->data.auth_crap.nt_resp_len);
2092         }
2093
2094         result = winbind_dual_SamLogon(domain,
2095                                        state->mem_ctx,
2096                                        state->request->data.auth_crap.logon_parameters,
2097                                        name_user,
2098                                        name_domain,
2099                                        /* Bug #3248 - found by Stefan Burkei. */
2100                                        workstation, /* We carefully set this above so use it... */
2101                                        state->request->data.auth_crap.chal,
2102                                        lm_resp,
2103                                        nt_resp,
2104                                        &info3);
2105         if (!NT_STATUS_IS_OK(result)) {
2106                 goto done;
2107         }
2108
2109         if (NT_STATUS_IS_OK(result)) {
2110                 /* Check if the user is in the right group */
2111
2112                 result = check_info3_in_group(
2113                         info3,
2114                         state->request->data.auth_crap.require_membership_of_sid);
2115                 if (!NT_STATUS_IS_OK(result)) {
2116                         DEBUG(3, ("User %s is not in the required group (%s), so "
2117                                   "crap authentication is rejected\n",
2118                                   state->request->data.auth_crap.user,
2119                                   state->request->data.auth_crap.require_membership_of_sid));
2120                         goto done;
2121                 }
2122
2123                 result = append_auth_data(state->mem_ctx, state->response,
2124                                           state->request->flags, info3,
2125                                           name_domain, name_user);
2126                 if (!NT_STATUS_IS_OK(result)) {
2127                         goto done;
2128                 }
2129         }
2130
2131 done:
2132
2133         if (state->request->flags & WBFLAG_PAM_NT_STATUS_SQUASH) {
2134                 result = nt_status_squash(result);
2135         }
2136
2137         set_auth_errors(state->response, result);
2138
2139         return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
2140 }
2141
2142 enum winbindd_result winbindd_dual_pam_chauthtok(struct winbindd_domain *contact_domain,
2143                                                  struct winbindd_cli_state *state)
2144 {
2145         char *oldpass;
2146         char *newpass = NULL;
2147         struct policy_handle dom_pol;
2148         struct rpc_pipe_client *cli = NULL;
2149         bool got_info = false;
2150         struct samr_DomInfo1 *info = NULL;
2151         struct userPwdChangeFailureInformation *reject = NULL;
2152         NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
2153         fstring domain, user;
2154         struct dcerpc_binding_handle *b = NULL;
2155
2156         ZERO_STRUCT(dom_pol);
2157
2158         DEBUG(3, ("[%5lu]: dual pam chauthtok %s\n", (unsigned long)state->pid,
2159                   state->request->data.auth.user));
2160
2161         if (!parse_domain_user(state->request->data.chauthtok.user, domain, user)) {
2162                 goto done;
2163         }
2164
2165         /* Change password */
2166
2167         oldpass = state->request->data.chauthtok.oldpass;
2168         newpass = state->request->data.chauthtok.newpass;
2169
2170         /* Initialize reject reason */
2171         state->response->data.auth.reject_reason = Undefined;
2172
2173         /* Get sam handle */
2174
2175         result = cm_connect_sam(contact_domain, state->mem_ctx, true, &cli,
2176                                 &dom_pol);
2177         if (!NT_STATUS_IS_OK(result)) {
2178                 DEBUG(1, ("could not get SAM handle on DC for %s\n", domain));
2179                 goto done;
2180         }
2181
2182         b = cli->binding_handle;
2183
2184         result = rpccli_samr_chgpasswd_user3(cli, state->mem_ctx,
2185                                              user,
2186                                              newpass,
2187                                              oldpass,
2188                                              &info,
2189                                              &reject);
2190
2191         /* Windows 2003 returns NT_STATUS_PASSWORD_RESTRICTION */
2192
2193         if (NT_STATUS_EQUAL(result, NT_STATUS_PASSWORD_RESTRICTION) ) {
2194
2195                 fill_in_password_policy(state->response, info);
2196
2197                 state->response->data.auth.reject_reason =
2198                         reject->extendedFailureReason;
2199
2200                 got_info = true;
2201         }
2202
2203         /* atm the pidl generated rpccli_samr_ChangePasswordUser3 function will
2204          * return with NT_STATUS_BUFFER_TOO_SMALL for w2k dcs as w2k just
2205          * returns with 4byte error code (NT_STATUS_NOT_SUPPORTED) which is too
2206          * short to comply with the samr_ChangePasswordUser3 idl - gd */
2207
2208         /* only fallback when the chgpasswd_user3 call is not supported */
2209         if (NT_STATUS_EQUAL(result, NT_STATUS_RPC_PROCNUM_OUT_OF_RANGE) ||
2210             NT_STATUS_EQUAL(result, NT_STATUS_NOT_SUPPORTED) ||
2211             NT_STATUS_EQUAL(result, NT_STATUS_BUFFER_TOO_SMALL) ||
2212             NT_STATUS_EQUAL(result, NT_STATUS_NOT_IMPLEMENTED)) {
2213
2214                 DEBUG(10,("Password change with chgpasswd_user3 failed with: %s, retrying chgpasswd_user2\n",
2215                         nt_errstr(result)));
2216
2217                 result = rpccli_samr_chgpasswd_user2(cli, state->mem_ctx, user, newpass, oldpass);
2218
2219                 /* Windows 2000 returns NT_STATUS_ACCOUNT_RESTRICTION.
2220                    Map to the same status code as Windows 2003. */
2221
2222                 if ( NT_STATUS_EQUAL(NT_STATUS_ACCOUNT_RESTRICTION, result ) ) {
2223                         result = NT_STATUS_PASSWORD_RESTRICTION;
2224                 }
2225         }
2226
2227 done:
2228
2229         if (NT_STATUS_IS_OK(result)
2230             && (state->request->flags & WBFLAG_PAM_CACHED_LOGIN)
2231             && lp_winbind_offline_logon()) {
2232                 result = winbindd_update_creds_by_name(contact_domain, user,
2233                                                        newpass);
2234                 /* Again, this happens when we login from gdm or xdm
2235                  * and the password expires, *BUT* cached crendentials
2236                  * doesn't exist. winbindd_update_creds_by_name()
2237                  * returns NT_STATUS_NO_SUCH_USER.
2238                  * This is not a failure.
2239                  * --- BoYang
2240                  * */
2241                 if (NT_STATUS_EQUAL(result, NT_STATUS_NO_SUCH_USER)) {
2242                         result = NT_STATUS_OK;
2243                 }
2244
2245                 if (!NT_STATUS_IS_OK(result)) {
2246                         DEBUG(10, ("Failed to store creds: %s\n",
2247                                    nt_errstr(result)));
2248                         goto process_result;
2249                 }
2250         }
2251
2252         if (!NT_STATUS_IS_OK(result) && !got_info && contact_domain) {
2253
2254                 NTSTATUS policy_ret;
2255
2256                 policy_ret = fillup_password_policy(
2257                         contact_domain, state->response);
2258
2259                 /* failure of this is non critical, it will just provide no
2260                  * additional information to the client why the change has
2261                  * failed - Guenther */
2262
2263                 if (!NT_STATUS_IS_OK(policy_ret)) {
2264                         DEBUG(10,("Failed to get password policies: %s\n", nt_errstr(policy_ret)));
2265                         goto process_result;
2266                 }
2267         }
2268
2269 process_result:
2270
2271         if (strequal(contact_domain->name, get_global_sam_name())) {
2272                 /* FIXME: internal rpc pipe does not cache handles yet */
2273                 if (b) {
2274                         if (is_valid_policy_hnd(&dom_pol)) {
2275                                 NTSTATUS _result;
2276                                 dcerpc_samr_Close(b, state->mem_ctx, &dom_pol, &_result);
2277                         }
2278                         TALLOC_FREE(cli);
2279                 }
2280         }
2281
2282         set_auth_errors(state->response, result);
2283
2284         DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2,
2285               ("Password change for user [%s]\\[%s] returned %s (PAM: %d)\n",
2286                domain,
2287                user,
2288                state->response->data.auth.nt_status_string,
2289                state->response->data.auth.pam_error));
2290
2291         return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
2292 }
2293
2294 enum winbindd_result winbindd_dual_pam_logoff(struct winbindd_domain *domain,
2295                                               struct winbindd_cli_state *state)
2296 {
2297         NTSTATUS result = NT_STATUS_NOT_SUPPORTED;
2298
2299         DEBUG(3, ("[%5lu]: pam dual logoff %s\n", (unsigned long)state->pid,
2300                 state->request->data.logoff.user));
2301
2302         if (!(state->request->flags & WBFLAG_PAM_KRB5)) {
2303                 result = NT_STATUS_OK;
2304                 goto process_result;
2305         }
2306
2307         if (state->request->data.logoff.krb5ccname[0] == '\0') {
2308                 result = NT_STATUS_OK;
2309                 goto process_result;
2310         }
2311
2312 #ifdef HAVE_KRB5
2313
2314         if (state->request->data.logoff.uid == (uid_t)-1) {
2315                 DEBUG(0,("winbindd_pam_logoff: invalid uid\n"));
2316                 goto process_result;
2317         }
2318
2319         /* what we need here is to find the corresponding krb5 ccache name *we*
2320          * created for a given username and destroy it */
2321
2322         if (!ccache_entry_exists(state->request->data.logoff.user)) {
2323                 result = NT_STATUS_OK;
2324                 DEBUG(10,("winbindd_pam_logoff: no entry found.\n"));
2325                 goto process_result;
2326         }
2327
2328         if (!ccache_entry_identical(state->request->data.logoff.user,
2329                                         state->request->data.logoff.uid,
2330                                         state->request->data.logoff.krb5ccname)) {
2331                 DEBUG(0,("winbindd_pam_logoff: cached entry differs.\n"));
2332                 goto process_result;
2333         }
2334
2335         result = remove_ccache(state->request->data.logoff.user);
2336         if (!NT_STATUS_IS_OK(result)) {
2337                 DEBUG(0,("winbindd_pam_logoff: failed to remove ccache: %s\n",
2338                         nt_errstr(result)));
2339                 goto process_result;
2340         }
2341
2342         /*
2343          * Remove any mlock'ed memory creds in the child
2344          * we might be using for krb5 ticket renewal.
2345          */
2346
2347         winbindd_delete_memory_creds(state->request->data.logoff.user);
2348
2349 #else
2350         result = NT_STATUS_NOT_SUPPORTED;
2351 #endif
2352
2353 process_result:
2354
2355
2356         set_auth_errors(state->response, result);
2357
2358         return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
2359 }
2360
2361 /* Change user password with auth crap*/
2362
2363 enum winbindd_result winbindd_dual_pam_chng_pswd_auth_crap(struct winbindd_domain *domainSt, struct winbindd_cli_state *state)
2364 {
2365         NTSTATUS result;
2366         DATA_BLOB new_nt_password;
2367         DATA_BLOB old_nt_hash_enc;
2368         DATA_BLOB new_lm_password;
2369         DATA_BLOB old_lm_hash_enc;
2370         fstring  domain,user;
2371         struct policy_handle dom_pol;
2372         struct winbindd_domain *contact_domain = domainSt;
2373         struct rpc_pipe_client *cli = NULL;
2374         struct dcerpc_binding_handle *b = NULL;
2375
2376         ZERO_STRUCT(dom_pol);
2377
2378         /* Ensure null termination */
2379         state->request->data.chng_pswd_auth_crap.user[
2380                 sizeof(state->request->data.chng_pswd_auth_crap.user)-1]=0;
2381         state->request->data.chng_pswd_auth_crap.domain[
2382                 sizeof(state->request->data.chng_pswd_auth_crap.domain)-1]=0;
2383         *domain = 0;
2384         *user = 0;
2385
2386         DEBUG(3, ("[%5lu]: pam change pswd auth crap domain: %s user: %s\n",
2387                   (unsigned long)state->pid,
2388                   state->request->data.chng_pswd_auth_crap.domain,
2389                   state->request->data.chng_pswd_auth_crap.user));
2390
2391         if (lp_winbind_offline_logon()) {
2392                 DEBUG(0,("Refusing password change as winbind offline logons are enabled. "));
2393                 DEBUGADD(0,("Changing passwords here would risk inconsistent logons\n"));
2394                 result = NT_STATUS_ACCESS_DENIED;
2395                 goto done;
2396         }
2397
2398         if (*state->request->data.chng_pswd_auth_crap.domain) {
2399                 fstrcpy(domain,state->request->data.chng_pswd_auth_crap.domain);
2400         } else {
2401                 parse_domain_user(state->request->data.chng_pswd_auth_crap.user,
2402                                   domain, user);
2403
2404                 if(!*domain) {
2405                         DEBUG(3,("no domain specified with username (%s) - "
2406                                  "failing auth\n",
2407                                  state->request->data.chng_pswd_auth_crap.user));
2408                         result = NT_STATUS_NO_SUCH_USER;
2409                         goto done;
2410                 }
2411         }
2412
2413         if (!*domain && lp_winbind_use_default_domain()) {
2414                 fstrcpy(domain,lp_workgroup());
2415         }
2416
2417         if(!*user) {
2418                 fstrcpy(user, state->request->data.chng_pswd_auth_crap.user);
2419         }
2420
2421         DEBUG(3, ("[%5lu]: pam auth crap domain: %s user: %s\n",
2422                   (unsigned long)state->pid, domain, user));
2423
2424         /* Change password */
2425         new_nt_password = data_blob_const(
2426                 state->request->data.chng_pswd_auth_crap.new_nt_pswd,
2427                 state->request->data.chng_pswd_auth_crap.new_nt_pswd_len);
2428
2429         old_nt_hash_enc = data_blob_const(
2430                 state->request->data.chng_pswd_auth_crap.old_nt_hash_enc,
2431                 state->request->data.chng_pswd_auth_crap.old_nt_hash_enc_len);
2432
2433         if(state->request->data.chng_pswd_auth_crap.new_lm_pswd_len > 0)        {
2434                 new_lm_password = data_blob_const(
2435                         state->request->data.chng_pswd_auth_crap.new_lm_pswd,
2436                         state->request->data.chng_pswd_auth_crap.new_lm_pswd_len);
2437
2438                 old_lm_hash_enc = data_blob_const(
2439                         state->request->data.chng_pswd_auth_crap.old_lm_hash_enc,
2440                         state->request->data.chng_pswd_auth_crap.old_lm_hash_enc_len);
2441         } else {
2442                 new_lm_password = data_blob_null;
2443                 old_lm_hash_enc = data_blob_null;
2444         }
2445
2446         /* Get sam handle */
2447
2448         result = cm_connect_sam(contact_domain, state->mem_ctx, true, &cli, &dom_pol);
2449         if (!NT_STATUS_IS_OK(result)) {
2450                 DEBUG(1, ("could not get SAM handle on DC for %s\n", domain));
2451                 goto done;
2452         }
2453
2454         b = cli->binding_handle;
2455
2456         result = rpccli_samr_chng_pswd_auth_crap(
2457                 cli, state->mem_ctx, user, new_nt_password, old_nt_hash_enc,
2458                 new_lm_password, old_lm_hash_enc);
2459
2460  done:
2461
2462         if (strequal(contact_domain->name, get_global_sam_name())) {
2463                 /* FIXME: internal rpc pipe does not cache handles yet */
2464                 if (b) {
2465                         if (is_valid_policy_hnd(&dom_pol)) {
2466                                 NTSTATUS _result;
2467                                 dcerpc_samr_Close(b, state->mem_ctx, &dom_pol, &_result);
2468                         }
2469                         TALLOC_FREE(cli);
2470                 }
2471         }
2472
2473         set_auth_errors(state->response, result);
2474
2475         DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2,
2476               ("Password change for user [%s]\\[%s] returned %s (PAM: %d)\n",
2477                domain, user,
2478                state->response->data.auth.nt_status_string,
2479                state->response->data.auth.pam_error));
2480
2481         return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
2482 }
2483
2484 #ifdef HAVE_KRB5
2485 static NTSTATUS extract_pac_vrfy_sigs(TALLOC_CTX *mem_ctx, DATA_BLOB pac_blob,
2486                                       struct PAC_LOGON_INFO **logon_info)
2487 {
2488         krb5_context krbctx = NULL;
2489         krb5_error_code k5ret;
2490         krb5_keytab keytab;
2491         krb5_kt_cursor cursor;
2492         krb5_keytab_entry entry;
2493         NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
2494
2495         ZERO_STRUCT(entry);
2496         ZERO_STRUCT(cursor);
2497
2498         k5ret = krb5_init_context(&krbctx);
2499         if (k5ret) {
2500                 DEBUG(1, ("Failed to initialize kerberos context: %s\n",
2501                           error_message(k5ret)));
2502                 status = krb5_to_nt_status(k5ret);
2503                 goto out;
2504         }
2505
2506         k5ret =  gse_krb5_get_server_keytab(krbctx, &keytab);
2507         if (k5ret) {
2508                 DEBUG(1, ("Failed to get keytab: %s\n",
2509                           error_message(k5ret)));
2510                 status = krb5_to_nt_status(k5ret);
2511                 goto out_free;
2512         }
2513
2514         k5ret = krb5_kt_start_seq_get(krbctx, keytab, &cursor);
2515         if (k5ret) {
2516                 DEBUG(1, ("Failed to start seq: %s\n",
2517                           error_message(k5ret)));
2518                 status = krb5_to_nt_status(k5ret);
2519                 goto out_keytab;
2520         }
2521
2522         k5ret = krb5_kt_next_entry(krbctx, keytab, &entry, &cursor);
2523         while (k5ret == 0) {
2524                 status = kerberos_pac_logon_info(mem_ctx, pac_blob,
2525                                                  krbctx, NULL,
2526                                                  KRB5_KT_KEY(&entry), NULL, 0,
2527                                                  logon_info);
2528                 if (NT_STATUS_IS_OK(status)) {
2529                         break;
2530                 }
2531                 k5ret = smb_krb5_kt_free_entry(krbctx, &entry);
2532                 k5ret = krb5_kt_next_entry(krbctx, keytab, &entry, &cursor);
2533         }
2534
2535         k5ret = krb5_kt_end_seq_get(krbctx, keytab, &cursor);
2536         if (k5ret) {
2537                 DEBUG(1, ("Failed to end seq: %s\n",
2538                           error_message(k5ret)));
2539         }
2540 out_keytab:
2541         k5ret = krb5_kt_close(krbctx, keytab);
2542         if (k5ret) {
2543                 DEBUG(1, ("Failed to close keytab: %s\n",
2544                           error_message(k5ret)));
2545         }
2546 out_free:
2547         krb5_free_context(krbctx);
2548 out:
2549         return status;
2550 }
2551
2552 NTSTATUS winbindd_pam_auth_pac_send(struct winbindd_cli_state *state,
2553                                     struct netr_SamInfo3 **info3)
2554 {
2555         struct winbindd_request *req = state->request;
2556         DATA_BLOB pac_blob;
2557         struct PAC_LOGON_INFO *logon_info = NULL;
2558         struct netr_SamInfo3 *info3_copy = NULL;
2559         NTSTATUS result;
2560
2561         pac_blob = data_blob_const(req->extra_data.data, req->extra_len);
2562         result = extract_pac_vrfy_sigs(state->mem_ctx, pac_blob, &logon_info);
2563         if (!NT_STATUS_IS_OK(result) &&
2564             !NT_STATUS_EQUAL(result, NT_STATUS_ACCESS_DENIED)) {
2565                 DEBUG(1, ("Error during PAC signature verification: %s\n",
2566                           nt_errstr(result)));
2567                 return result;
2568         }
2569
2570         if (logon_info) {
2571                 /* Signature verification succeeded, trust the PAC */
2572                 result = create_info3_from_pac_logon_info(state->mem_ctx,
2573                                                         logon_info,
2574                                                         &info3_copy);
2575                 if (!NT_STATUS_IS_OK(result)) {
2576                         return result;
2577                 }
2578                 netsamlogon_cache_store(NULL, info3_copy);
2579
2580         } else {
2581                 /* Try without signature verification */
2582                 result = kerberos_pac_logon_info(state->mem_ctx, pac_blob, NULL,
2583                                                  NULL, NULL, NULL, 0,
2584                                                  &logon_info);
2585                 if (!NT_STATUS_IS_OK(result)) {
2586                         DEBUG(10, ("Could not extract PAC: %s\n",
2587                                    nt_errstr(result)));
2588                         return result;
2589                 }
2590                 if (logon_info) {
2591                         /*
2592                          * Don't strictly need to copy here,
2593                          * but it makes it explicit we're
2594                          * returning a copy talloc'ed off
2595                          * the state->mem_ctx.
2596                          */
2597                         info3_copy = copy_netr_SamInfo3(state->mem_ctx,
2598                                         &logon_info->info3);
2599                         if (info3_copy == NULL) {
2600                                 return NT_STATUS_NO_MEMORY;
2601                         }
2602                 }
2603         }
2604
2605         *info3 = info3_copy;
2606
2607         return NT_STATUS_OK;
2608 }
2609 #else /* HAVE_KRB5 */
2610 NTSTATUS winbindd_pam_auth_pac_send(struct winbindd_cli_state *state,
2611                                     struct netr_SamInfo3 **info3)
2612 {
2613         return NT_STATUS_NO_SUCH_USER;
2614 }
2615 #endif /* HAVE_KRB5 */