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