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