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