Fix the wcache_invalidate_samlogon calls.
[ira/wip.git] / source3 / winbindd / winbindd_pam.c
1 /*
2    Unix SMB/CIFS implementation.
3
4    Winbind daemon - pam auth funcions
5
6    Copyright (C) Andrew Tridgell 2000
7    Copyright (C) Tim Potter 2001
8    Copyright (C) Andrew Bartlett 2001-2002
9    Copyright (C) Guenther Deschner 2005
10
11    This program is free software; you can redistribute it and/or modify
12    it under the terms of the GNU General Public License as published by
13    the Free Software Foundation; either version 3 of the License, or
14    (at your option) any later version.
15
16    This program is distributed in the hope that it will be useful,
17    but WITHOUT ANY WARRANTY; without even the implied warranty of
18    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19    GNU General Public License for more details.
20
21    You should have received a copy of the GNU General Public License
22    along with this program.  If not, see <http://www.gnu.org/licenses/>.
23 */
24
25 #include "includes.h"
26 #include "winbindd.h"
27 #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, 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         NTSTATUS result;
815
816         /* Ensure null termination */
817         state->request.data.auth.user
818                 [sizeof(state->request.data.auth.user)-1]='\0';
819
820         /* Ensure null termination */
821         state->request.data.auth.pass
822                 [sizeof(state->request.data.auth.pass)-1]='\0';
823
824         DEBUG(3, ("[%5lu]: pam auth %s\n", (unsigned long)state->pid,
825                   state->request.data.auth.user));
826
827         if (!check_request_flags(state->request.flags)) {
828                 result = NT_STATUS_INVALID_PARAMETER_MIX;
829                 goto done;
830         }
831
832         /* Parse domain and username */
833
834         ws_name_return( state->request.data.auth.user, WB_REPLACE_CHAR );
835
836         if (!canonicalize_username(state->request.data.auth.user,
837                                name_domain, name_user)) {
838                 result = NT_STATUS_NO_SUCH_USER;
839                 goto done;
840         }
841
842         domain = find_auth_domain(state, name_domain);
843
844         if (domain == NULL) {
845                 result = NT_STATUS_NO_SUCH_USER;
846                 goto done;
847         }
848
849         sendto_domain(state, domain);
850         return;
851  done:
852         set_auth_errors(&state->response, result);
853         DEBUG(5, ("Plain text authentication for %s returned %s "
854                   "(PAM: %d)\n",
855                   state->request.data.auth.user,
856                   state->response.data.auth.nt_status_string,
857                   state->response.data.auth.pam_error));
858         request_error(state);
859 }
860
861 NTSTATUS winbindd_dual_pam_auth_cached(struct winbindd_domain *domain,
862                                        struct winbindd_cli_state *state,
863                                        struct netr_SamInfo3 **info3)
864 {
865         NTSTATUS result = NT_STATUS_LOGON_FAILURE;
866         uint16 max_allowed_bad_attempts;
867         fstring name_domain, name_user;
868         DOM_SID sid;
869         enum lsa_SidType type;
870         uchar new_nt_pass[NT_HASH_LEN];
871         const uint8 *cached_nt_pass;
872         const uint8 *cached_salt;
873         struct netr_SamInfo3 *my_info3;
874         time_t kickoff_time, must_change_time;
875         bool password_good = false;
876 #ifdef HAVE_KRB5
877         struct winbindd_tdc_domain *tdc_domain = NULL;
878 #endif
879
880         *info3 = NULL;
881
882         ZERO_STRUCTP(info3);
883
884         DEBUG(10,("winbindd_dual_pam_auth_cached\n"));
885
886         /* Parse domain and username */
887
888         parse_domain_user(state->request.data.auth.user, name_domain, name_user);
889
890
891         if (!lookup_cached_name(state->mem_ctx,
892                                 name_domain,
893                                 name_user,
894                                 &sid,
895                                 &type)) {
896                 DEBUG(10,("winbindd_dual_pam_auth_cached: no such user in the cache\n"));
897                 return NT_STATUS_NO_SUCH_USER;
898         }
899
900         if (type != SID_NAME_USER) {
901                 DEBUG(10,("winbindd_dual_pam_auth_cached: not a user (%s)\n", sid_type_lookup(type)));
902                 return NT_STATUS_LOGON_FAILURE;
903         }
904
905         result = winbindd_get_creds(domain,
906                                     state->mem_ctx,
907                                     &sid,
908                                     &my_info3,
909                                     &cached_nt_pass,
910                                     &cached_salt);
911         if (!NT_STATUS_IS_OK(result)) {
912                 DEBUG(10,("winbindd_dual_pam_auth_cached: failed to get creds: %s\n", nt_errstr(result)));
913                 return result;
914         }
915
916         *info3 = my_info3;
917
918         E_md4hash(state->request.data.auth.pass, new_nt_pass);
919
920         dump_data_pw("new_nt_pass", new_nt_pass, NT_HASH_LEN);
921         dump_data_pw("cached_nt_pass", cached_nt_pass, NT_HASH_LEN);
922         if (cached_salt) {
923                 dump_data_pw("cached_salt", cached_salt, NT_HASH_LEN);
924         }
925
926         if (cached_salt) {
927                 /* In this case we didn't store the nt_hash itself,
928                    but the MD5 combination of salt + nt_hash. */
929                 uchar salted_hash[NT_HASH_LEN];
930                 E_md5hash(cached_salt, new_nt_pass, salted_hash);
931
932                 password_good = (memcmp(cached_nt_pass, salted_hash, NT_HASH_LEN) == 0) ?
933                         true : false;
934         } else {
935                 /* Old cached cred - direct store of nt_hash (bad bad bad !). */
936                 password_good = (memcmp(cached_nt_pass, new_nt_pass, NT_HASH_LEN) == 0) ?
937                         true : false;
938         }
939
940         if (password_good) {
941
942                 /* User *DOES* know the password, update logon_time and reset
943                  * bad_pw_count */
944
945                 my_info3->base.user_flags |= NETLOGON_CACHED_ACCOUNT;
946
947                 if (my_info3->base.acct_flags & ACB_AUTOLOCK) {
948                         return NT_STATUS_ACCOUNT_LOCKED_OUT;
949                 }
950
951                 if (my_info3->base.acct_flags & ACB_DISABLED) {
952                         return NT_STATUS_ACCOUNT_DISABLED;
953                 }
954
955                 if (my_info3->base.acct_flags & ACB_WSTRUST) {
956                         return NT_STATUS_NOLOGON_WORKSTATION_TRUST_ACCOUNT;
957                 }
958
959                 if (my_info3->base.acct_flags & ACB_SVRTRUST) {
960                         return NT_STATUS_NOLOGON_SERVER_TRUST_ACCOUNT;
961                 }
962
963                 if (my_info3->base.acct_flags & ACB_DOMTRUST) {
964                         return NT_STATUS_NOLOGON_INTERDOMAIN_TRUST_ACCOUNT;
965                 }
966
967                 if (!(my_info3->base.acct_flags & ACB_NORMAL)) {
968                         DEBUG(0,("winbindd_dual_pam_auth_cached: whats wrong with that one?: 0x%08x\n",
969                                 my_info3->base.acct_flags));
970                         return NT_STATUS_LOGON_FAILURE;
971                 }
972
973                 kickoff_time = nt_time_to_unix(my_info3->base.acct_expiry);
974                 if (kickoff_time != 0 && time(NULL) > kickoff_time) {
975                         return NT_STATUS_ACCOUNT_EXPIRED;
976                 }
977
978                 must_change_time = nt_time_to_unix(my_info3->base.force_password_change);
979                 if (must_change_time != 0 && must_change_time < time(NULL)) {
980                         /* we allow grace logons when the password has expired */
981                         my_info3->base.user_flags |= NETLOGON_GRACE_LOGON;
982                         /* return NT_STATUS_PASSWORD_EXPIRED; */
983                         goto success;
984                 }
985
986 #ifdef HAVE_KRB5
987                 if ((state->request.flags & WBFLAG_PAM_KRB5) &&
988                     ((tdc_domain = wcache_tdc_fetch_domain(state->mem_ctx, name_domain)) != NULL) &&
989                     (tdc_domain->trust_type & NETR_TRUST_TYPE_UPLEVEL)) {
990
991                         uid_t uid = -1;
992                         const char *cc = NULL;
993                         char *realm = NULL;
994                         const char *principal_s = NULL;
995                         const char *service = NULL;
996                         bool internal_ccache = false;
997
998                         uid = get_uid_from_state(state);
999                         if (uid == -1) {
1000                                 DEBUG(0,("winbindd_dual_pam_auth_cached: invalid uid\n"));
1001                                 return NT_STATUS_INVALID_PARAMETER;
1002                         }
1003
1004                         cc = generate_krb5_ccache(state->mem_ctx,
1005                                                 state->request.data.auth.krb5_cc_type,
1006                                                 state->request.data.auth.uid,
1007                                                 &internal_ccache);
1008                         if (cc == NULL) {
1009                                 return NT_STATUS_NO_MEMORY;
1010                         }
1011
1012                         realm = domain->alt_name;
1013                         strupper_m(realm);
1014
1015                         principal_s = talloc_asprintf(state->mem_ctx, "%s@%s", name_user, realm);
1016                         if (principal_s == NULL) {
1017                                 return NT_STATUS_NO_MEMORY;
1018                         }
1019
1020                         service = talloc_asprintf(state->mem_ctx, "%s/%s@%s", KRB5_TGS_NAME, realm, realm);
1021                         if (service == NULL) {
1022                                 return NT_STATUS_NO_MEMORY;
1023                         }
1024
1025                         if (!internal_ccache) {
1026
1027                                 setup_return_cc_name(state, cc);
1028
1029                                 result = add_ccache_to_list(principal_s,
1030                                                             cc,
1031                                                             service,
1032                                                             state->request.data.auth.user,
1033                                                             domain->alt_name,
1034                                                             uid,
1035                                                             time(NULL),
1036                                                             time(NULL) + lp_winbind_cache_time(),
1037                                                             time(NULL) + WINBINDD_PAM_AUTH_KRB5_RENEW_TIME,
1038                                                             true);
1039
1040                                 if (!NT_STATUS_IS_OK(result)) {
1041                                         DEBUG(10,("winbindd_dual_pam_auth_cached: failed "
1042                                                 "to add ccache to list: %s\n",
1043                                                 nt_errstr(result)));
1044                                 }
1045                         }
1046                 }
1047 #endif /* HAVE_KRB5 */
1048  success:
1049                 /* FIXME: we possibly should handle logon hours as well (does xp when
1050                  * offline?) see auth/auth_sam.c:sam_account_ok for details */
1051
1052                 unix_to_nt_time(&my_info3->base.last_logon, time(NULL));
1053                 my_info3->base.bad_password_count = 0;
1054
1055                 result = winbindd_update_creds_by_info3(domain,
1056                                                         state->mem_ctx,
1057                                                         state->request.data.auth.user,
1058                                                         state->request.data.auth.pass,
1059                                                         my_info3);
1060                 if (!NT_STATUS_IS_OK(result)) {
1061                         DEBUG(1,("winbindd_dual_pam_auth_cached: failed to update creds: %s\n",
1062                                 nt_errstr(result)));
1063                         return result;
1064                 }
1065
1066                 return NT_STATUS_OK;
1067
1068         }
1069
1070         /* User does *NOT* know the correct password, modify info3 accordingly */
1071
1072         /* failure of this is not critical */
1073         result = get_max_bad_attempts_from_lockout_policy(domain, state->mem_ctx, &max_allowed_bad_attempts);
1074         if (!NT_STATUS_IS_OK(result)) {
1075                 DEBUG(10,("winbindd_dual_pam_auth_cached: failed to get max_allowed_bad_attempts. "
1076                           "Won't be able to honour account lockout policies\n"));
1077         }
1078
1079         /* increase counter */
1080         my_info3->base.bad_password_count++;
1081
1082         if (max_allowed_bad_attempts == 0) {
1083                 goto failed;
1084         }
1085
1086         /* lockout user */
1087         if (my_info3->base.bad_password_count >= max_allowed_bad_attempts) {
1088
1089                 uint32 password_properties;
1090
1091                 result = get_pwd_properties(domain, state->mem_ctx, &password_properties);
1092                 if (!NT_STATUS_IS_OK(result)) {
1093                         DEBUG(10,("winbindd_dual_pam_auth_cached: failed to get password properties.\n"));
1094                 }
1095
1096                 if ((my_info3->base.rid != DOMAIN_USER_RID_ADMIN) ||
1097                     (password_properties & DOMAIN_PASSWORD_LOCKOUT_ADMINS)) {
1098                         my_info3->base.acct_flags |= ACB_AUTOLOCK;
1099                 }
1100         }
1101
1102 failed:
1103         result = winbindd_update_creds_by_info3(domain,
1104                                                 state->mem_ctx,
1105                                                 state->request.data.auth.user,
1106                                                 NULL,
1107                                                 my_info3);
1108
1109         if (!NT_STATUS_IS_OK(result)) {
1110                 DEBUG(0,("winbindd_dual_pam_auth_cached: failed to update creds %s\n",
1111                         nt_errstr(result)));
1112         }
1113
1114         return NT_STATUS_LOGON_FAILURE;
1115 }
1116
1117 NTSTATUS winbindd_dual_pam_auth_kerberos(struct winbindd_domain *domain,
1118                                          struct winbindd_cli_state *state,
1119                                          struct netr_SamInfo3 **info3)
1120 {
1121         struct winbindd_domain *contact_domain;
1122         fstring name_domain, name_user;
1123         NTSTATUS result;
1124
1125         DEBUG(10,("winbindd_dual_pam_auth_kerberos\n"));
1126
1127         /* Parse domain and username */
1128
1129         parse_domain_user(state->request.data.auth.user, name_domain, name_user);
1130
1131         /* what domain should we contact? */
1132
1133         if ( IS_DC ) {
1134                 if (!(contact_domain = find_domain_from_name(name_domain))) {
1135                         DEBUG(3, ("Authentication for domain for [%s] -> [%s]\\[%s] failed as %s is not a trusted domain\n",
1136                                   state->request.data.auth.user, name_domain, name_user, name_domain));
1137                         result = NT_STATUS_NO_SUCH_USER;
1138                         goto done;
1139                 }
1140
1141         } else {
1142                 if (is_myname(name_domain)) {
1143                         DEBUG(3, ("Authentication for domain %s (local domain to this server) not supported at this stage\n", name_domain));
1144                         result =  NT_STATUS_NO_SUCH_USER;
1145                         goto done;
1146                 }
1147
1148                 contact_domain = find_domain_from_name(name_domain);
1149                 if (contact_domain == NULL) {
1150                         DEBUG(3, ("Authentication for domain for [%s] -> [%s]\\[%s] failed as %s is not a trusted domain\n",
1151                                   state->request.data.auth.user, name_domain, name_user, name_domain));
1152
1153                         contact_domain = find_our_domain();
1154                 }
1155         }
1156
1157         if (contact_domain->initialized &&
1158             contact_domain->active_directory) {
1159                 goto try_login;
1160         }
1161
1162         if (!contact_domain->initialized) {
1163                 init_dc_connection(contact_domain);
1164         }
1165
1166         if (!contact_domain->active_directory) {
1167                 DEBUG(3,("krb5 auth requested but domain is not Active Directory\n"));
1168                 return NT_STATUS_INVALID_LOGON_TYPE;
1169         }
1170 try_login:
1171         result = winbindd_raw_kerberos_login(contact_domain, state, info3);
1172 done:
1173         return result;
1174 }
1175
1176 typedef NTSTATUS (*netlogon_fn_t)(struct rpc_pipe_client *cli,
1177                                   TALLOC_CTX *mem_ctx,
1178                                   uint32 logon_parameters,
1179                                   const char *server,
1180                                   const char *username,
1181                                   const char *domain,
1182                                   const char *workstation,
1183                                   const uint8 chal[8],
1184                                   DATA_BLOB lm_response,
1185                                   DATA_BLOB nt_response,
1186                                   struct netr_SamInfo3 **info3);
1187
1188 NTSTATUS winbindd_dual_pam_auth_samlogon(struct winbindd_domain *domain,
1189                                          struct winbindd_cli_state *state,
1190                                          struct netr_SamInfo3 **info3)
1191 {
1192
1193         struct rpc_pipe_client *netlogon_pipe;
1194         uchar chal[8];
1195         DATA_BLOB lm_resp;
1196         DATA_BLOB nt_resp;
1197         int attempts = 0;
1198         unsigned char local_lm_response[24];
1199         unsigned char local_nt_response[24];
1200         struct winbindd_domain *contact_domain;
1201         fstring name_domain, name_user;
1202         bool retry;
1203         NTSTATUS result;
1204         struct netr_SamInfo3 *my_info3 = NULL;
1205
1206         *info3 = NULL;
1207
1208         DEBUG(10,("winbindd_dual_pam_auth_samlogon\n"));
1209
1210         /* Parse domain and username */
1211
1212         parse_domain_user(state->request.data.auth.user, name_domain, name_user);
1213
1214         /* do password magic */
1215
1216
1217         generate_random_buffer(chal, 8);
1218         if (lp_client_ntlmv2_auth()) {
1219                 DATA_BLOB server_chal;
1220                 DATA_BLOB names_blob;
1221                 DATA_BLOB nt_response;
1222                 DATA_BLOB lm_response;
1223                 server_chal = data_blob_talloc(state->mem_ctx, chal, 8);
1224
1225                 /* note that the 'workgroup' here is a best guess - we don't know
1226                    the server's domain at this point.  The 'server name' is also
1227                    dodgy...
1228                 */
1229                 names_blob = NTLMv2_generate_names_blob(global_myname(), lp_workgroup());
1230
1231                 if (!SMBNTLMv2encrypt(name_user, name_domain,
1232                                       state->request.data.auth.pass,
1233                                       &server_chal,
1234                                       &names_blob,
1235                                       &lm_response, &nt_response, NULL)) {
1236                         data_blob_free(&names_blob);
1237                         data_blob_free(&server_chal);
1238                         DEBUG(0, ("winbindd_pam_auth: SMBNTLMv2encrypt() failed!\n"));
1239                         result = NT_STATUS_NO_MEMORY;
1240                         goto done;
1241                 }
1242                 data_blob_free(&names_blob);
1243                 data_blob_free(&server_chal);
1244                 lm_resp = data_blob_talloc(state->mem_ctx, lm_response.data,
1245                                            lm_response.length);
1246                 nt_resp = data_blob_talloc(state->mem_ctx, nt_response.data,
1247                                            nt_response.length);
1248                 data_blob_free(&lm_response);
1249                 data_blob_free(&nt_response);
1250
1251         } else {
1252                 if (lp_client_lanman_auth()
1253                     && SMBencrypt(state->request.data.auth.pass,
1254                                   chal,
1255                                   local_lm_response)) {
1256                         lm_resp = data_blob_talloc(state->mem_ctx,
1257                                                    local_lm_response,
1258                                                    sizeof(local_lm_response));
1259                 } else {
1260                         lm_resp = data_blob_null;
1261                 }
1262                 SMBNTencrypt(state->request.data.auth.pass,
1263                              chal,
1264                              local_nt_response);
1265
1266                 nt_resp = data_blob_talloc(state->mem_ctx,
1267                                            local_nt_response,
1268                                            sizeof(local_nt_response));
1269         }
1270
1271         /* what domain should we contact? */
1272
1273         if ( IS_DC ) {
1274                 if (!(contact_domain = find_domain_from_name(name_domain))) {
1275                         DEBUG(3, ("Authentication for domain for [%s] -> [%s]\\[%s] failed as %s is not a trusted domain\n",
1276                                   state->request.data.auth.user, name_domain, name_user, name_domain));
1277                         result = NT_STATUS_NO_SUCH_USER;
1278                         goto done;
1279                 }
1280
1281         } else {
1282                 if (is_myname(name_domain)) {
1283                         DEBUG(3, ("Authentication for domain %s (local domain to this server) not supported at this stage\n", name_domain));
1284                         result =  NT_STATUS_NO_SUCH_USER;
1285                         goto done;
1286                 }
1287
1288                 contact_domain = find_our_domain();
1289         }
1290
1291         /* check authentication loop */
1292
1293         do {
1294                 netlogon_fn_t logon_fn;
1295
1296                 ZERO_STRUCTP(my_info3);
1297                 retry = false;
1298
1299                 result = cm_connect_netlogon(contact_domain, &netlogon_pipe);
1300
1301                 if (!NT_STATUS_IS_OK(result)) {
1302                         DEBUG(3, ("could not open handle to NETLOGON pipe\n"));
1303                         goto done;
1304                 }
1305
1306                 /* It is really important to try SamLogonEx here,
1307                  * because in a clustered environment, we want to use
1308                  * one machine account from multiple physical
1309                  * computers.
1310                  *
1311                  * With a normal SamLogon call, we must keep the
1312                  * credentials chain updated and intact between all
1313                  * users of the machine account (which would imply
1314                  * cross-node communication for every NTLM logon).
1315                  *
1316                  * (The credentials chain is not per NETLOGON pipe
1317                  * connection, but globally on the server/client pair
1318                  * by machine name).
1319                  *
1320                  * When using SamLogonEx, the credentials are not
1321                  * supplied, but the session key is implied by the
1322                  * wrapping SamLogon context.
1323                  *
1324                  *  -- abartlet 21 April 2008
1325                  */
1326
1327                 logon_fn = contact_domain->can_do_samlogon_ex
1328                         ? rpccli_netlogon_sam_network_logon_ex
1329                         : rpccli_netlogon_sam_network_logon;
1330
1331                 result = logon_fn(netlogon_pipe,
1332                                   state->mem_ctx,
1333                                   0,
1334                                   contact_domain->dcname, /* server name */
1335                                   name_user,              /* user name */
1336                                   name_domain,            /* target domain */
1337                                   global_myname(),        /* workstation */
1338                                   chal,
1339                                   lm_resp,
1340                                   nt_resp,
1341                                   &my_info3);
1342                 attempts += 1;
1343
1344                 if ((NT_STATUS_V(result) == DCERPC_FAULT_OP_RNG_ERROR)
1345                     && contact_domain->can_do_samlogon_ex) {
1346                         DEBUG(3, ("Got a DC that can not do NetSamLogonEx, "
1347                                   "retrying with NetSamLogon\n"));
1348                         contact_domain->can_do_samlogon_ex = false;
1349                         retry = true;
1350                         continue;
1351                 }
1352
1353                 /* We have to try a second time as cm_connect_netlogon
1354                    might not yet have noticed that the DC has killed
1355                    our connection. */
1356
1357                 if (NT_STATUS_EQUAL(result, NT_STATUS_UNSUCCESSFUL)) {
1358                         retry = true;
1359                         continue;
1360                 }
1361
1362                 /* if we get access denied, a possible cause was that we had
1363                    and open connection to the DC, but someone changed our
1364                    machine account password out from underneath us using 'net
1365                    rpc changetrustpw' */
1366
1367                 if ( NT_STATUS_EQUAL(result, NT_STATUS_ACCESS_DENIED) ) {
1368                         DEBUG(3,("winbindd_pam_auth: sam_logon returned "
1369                                  "ACCESS_DENIED.  Maybe the trust account "
1370                                 "password was changed and we didn't know it. "
1371                                  "Killing connections to domain %s\n",
1372                                 name_domain));
1373                         invalidate_cm_connection(&contact_domain->conn);
1374                         retry = true;
1375                 }
1376
1377         } while ( (attempts < 2) && retry );
1378
1379         /* handle the case where a NT4 DC does not fill in the acct_flags in
1380          * the samlogon reply info3. When accurate info3 is required by the
1381          * caller, we look up the account flags ourselve - gd */
1382
1383         if ((state->request.flags & WBFLAG_PAM_INFO3_TEXT) &&
1384             NT_STATUS_IS_OK(result) && (my_info3->base.acct_flags == 0)) {
1385
1386                 struct rpc_pipe_client *samr_pipe;
1387                 POLICY_HND samr_domain_handle, user_pol;
1388                 union samr_UserInfo *info = NULL;
1389                 NTSTATUS status_tmp;
1390                 uint32 acct_flags;
1391
1392                 status_tmp = cm_connect_sam(contact_domain, state->mem_ctx,
1393                                             &samr_pipe, &samr_domain_handle);
1394
1395                 if (!NT_STATUS_IS_OK(status_tmp)) {
1396                         DEBUG(3, ("could not open handle to SAMR pipe: %s\n",
1397                                 nt_errstr(status_tmp)));
1398                         goto done;
1399                 }
1400
1401                 status_tmp = rpccli_samr_OpenUser(samr_pipe, state->mem_ctx,
1402                                                   &samr_domain_handle,
1403                                                   MAXIMUM_ALLOWED_ACCESS,
1404                                                   my_info3->base.rid,
1405                                                   &user_pol);
1406
1407                 if (!NT_STATUS_IS_OK(status_tmp)) {
1408                         DEBUG(3, ("could not open user handle on SAMR pipe: %s\n",
1409                                 nt_errstr(status_tmp)));
1410                         goto done;
1411                 }
1412
1413                 status_tmp = rpccli_samr_QueryUserInfo(samr_pipe, state->mem_ctx,
1414                                                        &user_pol,
1415                                                        16,
1416                                                        &info);
1417
1418                 if (!NT_STATUS_IS_OK(status_tmp)) {
1419                         DEBUG(3, ("could not query user info on SAMR pipe: %s\n",
1420                                 nt_errstr(status_tmp)));
1421                         rpccli_samr_Close(samr_pipe, state->mem_ctx, &user_pol);
1422                         goto done;
1423                 }
1424
1425                 acct_flags = info->info16.acct_flags;
1426
1427                 if (acct_flags == 0) {
1428                         rpccli_samr_Close(samr_pipe, state->mem_ctx, &user_pol);
1429                         goto done;
1430                 }
1431
1432                 my_info3->base.acct_flags = acct_flags;
1433
1434                 DEBUG(10,("successfully retrieved acct_flags 0x%x\n", acct_flags));
1435
1436                 rpccli_samr_Close(samr_pipe, state->mem_ctx, &user_pol);
1437         }
1438
1439         *info3 = my_info3;
1440 done:
1441         return result;
1442 }
1443
1444 enum winbindd_result winbindd_dual_pam_auth(struct winbindd_domain *domain,
1445                                             struct winbindd_cli_state *state)
1446 {
1447         NTSTATUS result = NT_STATUS_LOGON_FAILURE;
1448         NTSTATUS krb5_result = NT_STATUS_OK;
1449         fstring name_domain, name_user;
1450         struct netr_SamInfo3 *info3 = NULL;
1451
1452         /* Ensure null termination */
1453         state->request.data.auth.user[sizeof(state->request.data.auth.user)-1]='\0';
1454
1455         /* Ensure null termination */
1456         state->request.data.auth.pass[sizeof(state->request.data.auth.pass)-1]='\0';
1457
1458         DEBUG(3, ("[%5lu]: dual pam auth %s\n", (unsigned long)state->pid,
1459                   state->request.data.auth.user));
1460
1461         if (!check_request_flags(state->request.flags)) {
1462                 result = NT_STATUS_INVALID_PARAMETER_MIX;
1463                 goto done;
1464         }
1465
1466         /* Parse domain and username */
1467
1468         ws_name_return( state->request.data.auth.user, WB_REPLACE_CHAR );
1469
1470         parse_domain_user(state->request.data.auth.user, name_domain, name_user);
1471
1472         if (domain->online == false) {
1473                 result = NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND;
1474                 if (domain->startup) {
1475                         /* Logons are very important to users. If we're offline and
1476                            we get a request within the first 30 seconds of startup,
1477                            try very hard to find a DC and go online. */
1478
1479                         DEBUG(10,("winbindd_dual_pam_auth: domain: %s offline and auth "
1480                                 "request in startup mode.\n", domain->name ));
1481
1482                         winbindd_flush_negative_conn_cache(domain);
1483                         result = init_dc_connection(domain);
1484                 }
1485         }
1486
1487         DEBUG(10,("winbindd_dual_pam_auth: domain: %s last was %s\n", domain->name, domain->online ? "online":"offline"));
1488
1489         /* Check for Kerberos authentication */
1490         if (domain->online && (state->request.flags & WBFLAG_PAM_KRB5)) {
1491
1492                 result = winbindd_dual_pam_auth_kerberos(domain, state, &info3);
1493                 /* save for later */
1494                 krb5_result = result;
1495
1496
1497                 if (NT_STATUS_IS_OK(result)) {
1498                         DEBUG(10,("winbindd_dual_pam_auth_kerberos succeeded\n"));
1499                         goto process_result;
1500                 } else {
1501                         DEBUG(10,("winbindd_dual_pam_auth_kerberos failed: %s\n", nt_errstr(result)));
1502                 }
1503
1504                 if (NT_STATUS_EQUAL(result, NT_STATUS_NO_LOGON_SERVERS) ||
1505                     NT_STATUS_EQUAL(result, NT_STATUS_IO_TIMEOUT) ||
1506                     NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
1507                         DEBUG(10,("winbindd_dual_pam_auth_kerberos setting domain to offline\n"));
1508                         set_domain_offline( domain );
1509                         goto cached_logon;
1510                 }
1511
1512                 /* there are quite some NT_STATUS errors where there is no
1513                  * point in retrying with a samlogon, we explictly have to take
1514                  * care not to increase the bad logon counter on the DC */
1515
1516                 if (NT_STATUS_EQUAL(result, NT_STATUS_ACCOUNT_DISABLED) ||
1517                     NT_STATUS_EQUAL(result, NT_STATUS_ACCOUNT_EXPIRED) ||
1518                     NT_STATUS_EQUAL(result, NT_STATUS_ACCOUNT_LOCKED_OUT) ||
1519                     NT_STATUS_EQUAL(result, NT_STATUS_INVALID_LOGON_HOURS) ||
1520                     NT_STATUS_EQUAL(result, NT_STATUS_INVALID_WORKSTATION) ||
1521                     NT_STATUS_EQUAL(result, NT_STATUS_LOGON_FAILURE) ||
1522                     NT_STATUS_EQUAL(result, NT_STATUS_NO_SUCH_USER) ||
1523                     NT_STATUS_EQUAL(result, NT_STATUS_PASSWORD_EXPIRED) ||
1524                     NT_STATUS_EQUAL(result, NT_STATUS_PASSWORD_MUST_CHANGE) ||
1525                     NT_STATUS_EQUAL(result, NT_STATUS_WRONG_PASSWORD)) {
1526                         goto process_result;
1527                 }
1528
1529                 if (state->request.flags & WBFLAG_PAM_FALLBACK_AFTER_KRB5) {
1530                         DEBUG(3,("falling back to samlogon\n"));
1531                         goto sam_logon;
1532                 } else {
1533                         goto cached_logon;
1534                 }
1535         }
1536
1537 sam_logon:
1538         /* Check for Samlogon authentication */
1539         if (domain->online) {
1540                 result = winbindd_dual_pam_auth_samlogon(domain, state, &info3);
1541
1542                 if (NT_STATUS_IS_OK(result)) {
1543                         DEBUG(10,("winbindd_dual_pam_auth_samlogon succeeded\n"));
1544                         /* add the Krb5 err if we have one */
1545                         if ( NT_STATUS_EQUAL(krb5_result, NT_STATUS_TIME_DIFFERENCE_AT_DC ) ) {
1546                                 info3->base.user_flags |= LOGON_KRB5_FAIL_CLOCK_SKEW;
1547                         }
1548                         goto process_result;
1549                 }
1550
1551                 DEBUG(10,("winbindd_dual_pam_auth_samlogon failed: %s\n",
1552                           nt_errstr(result)));
1553
1554                 if (NT_STATUS_EQUAL(result, NT_STATUS_NO_LOGON_SERVERS) ||
1555                     NT_STATUS_EQUAL(result, NT_STATUS_IO_TIMEOUT) ||
1556                     NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND))
1557                 {
1558                         DEBUG(10,("winbindd_dual_pam_auth_samlogon setting domain to offline\n"));
1559                         set_domain_offline( domain );
1560                         goto cached_logon;
1561                 }
1562
1563                         if (domain->online) {
1564                                 /* We're still online - fail. */
1565                                 goto done;
1566                         }
1567         }
1568
1569 cached_logon:
1570         /* Check for Cached logons */
1571         if (!domain->online && (state->request.flags & WBFLAG_PAM_CACHED_LOGIN) &&
1572             lp_winbind_offline_logon()) {
1573
1574                 result = winbindd_dual_pam_auth_cached(domain, state, &info3);
1575
1576                 if (NT_STATUS_IS_OK(result)) {
1577                         DEBUG(10,("winbindd_dual_pam_auth_cached succeeded\n"));
1578                         goto process_result;
1579                 } else {
1580                         DEBUG(10,("winbindd_dual_pam_auth_cached failed: %s\n", nt_errstr(result)));
1581                         goto done;
1582                 }
1583         }
1584
1585 process_result:
1586
1587         if (NT_STATUS_IS_OK(result)) {
1588
1589                 DOM_SID user_sid;
1590
1591                 /* In all codepaths where result == NT_STATUS_OK info3 must have
1592                    been initialized. */
1593                 if (!info3) {
1594                         result = NT_STATUS_INTERNAL_ERROR;
1595                         goto done;
1596                 }
1597
1598                 wcache_invalidate_samlogon(find_domain_from_name(name_domain), info3);
1599                 netsamlogon_cache_store(name_user, info3);
1600
1601                 /* save name_to_sid info as early as possible (only if
1602                    this is our primary domain so we don't invalidate
1603                    the cache entry by storing the seq_num for the wrong
1604                    domain). */
1605                 if ( domain->primary ) {
1606                         sid_compose(&user_sid, info3->base.domain_sid,
1607                                     info3->base.rid);
1608                         cache_name2sid(domain, name_domain, name_user,
1609                                        SID_NAME_USER, &user_sid);
1610                 }
1611
1612                 /* Check if the user is in the right group */
1613
1614                 if (!NT_STATUS_IS_OK(result = check_info3_in_group(state->mem_ctx, info3,
1615                                         state->request.data.auth.require_membership_of_sid))) {
1616                         DEBUG(3, ("User %s is not in the required group (%s), so plaintext authentication is rejected\n",
1617                                   state->request.data.auth.user,
1618                                   state->request.data.auth.require_membership_of_sid));
1619                         goto done;
1620                 }
1621
1622                 result = append_data(state, info3, name_domain, name_user);
1623                 if (!NT_STATUS_IS_OK(result)) {
1624                         goto done;
1625                 }
1626
1627                 if ((state->request.flags & WBFLAG_PAM_CACHED_LOGIN)) {
1628
1629                         /* Store in-memory creds for single-signon using ntlm_auth. */
1630                         result = winbindd_add_memory_creds(state->request.data.auth.user,
1631                                                         get_uid_from_state(state),
1632                                                         state->request.data.auth.pass);
1633
1634                         if (!NT_STATUS_IS_OK(result)) {
1635                                 DEBUG(10,("Failed to store memory creds: %s\n", nt_errstr(result)));
1636                                 goto done;
1637                         }
1638
1639                         if (lp_winbind_offline_logon()) {
1640                                 result = winbindd_store_creds(domain,
1641                                                       state->mem_ctx,
1642                                                       state->request.data.auth.user,
1643                                                       state->request.data.auth.pass,
1644                                                       info3, NULL);
1645                                 if (!NT_STATUS_IS_OK(result)) {
1646
1647                                         /* Release refcount. */
1648                                         winbindd_delete_memory_creds(state->request.data.auth.user);
1649
1650                                         DEBUG(10,("Failed to store creds: %s\n", nt_errstr(result)));
1651                                         goto done;
1652                                 }
1653                         }
1654                 }
1655
1656
1657                 if (state->request.flags & WBFLAG_PAM_GET_PWD_POLICY) {
1658                         struct winbindd_domain *our_domain = find_our_domain();
1659
1660                         /* This is not entirely correct I believe, but it is
1661                            consistent.  Only apply the password policy settings
1662                            too warn users for our own domain.  Cannot obtain these
1663                            from trusted DCs all the  time so don't do it at all.
1664                            -- jerry */
1665
1666                         result = NT_STATUS_NOT_SUPPORTED;
1667                         if (our_domain == domain ) {
1668                                 result = fillup_password_policy(our_domain, state);
1669                         }
1670
1671                         if (!NT_STATUS_IS_OK(result)
1672                             && !NT_STATUS_EQUAL(result, NT_STATUS_NOT_SUPPORTED) )
1673                         {
1674                                 DEBUG(10,("Failed to get password policies for domain %s: %s\n",
1675                                           domain->name, nt_errstr(result)));
1676                                 goto done;
1677                         }
1678                 }
1679
1680                 result = NT_STATUS_OK;
1681         }
1682
1683 done:
1684         /* give us a more useful (more correct?) error code */
1685         if ((NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND) ||
1686             (NT_STATUS_EQUAL(result, NT_STATUS_UNSUCCESSFUL)))) {
1687                 result = NT_STATUS_NO_LOGON_SERVERS;
1688         }
1689
1690         set_auth_errors(&state->response, result);
1691
1692         DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2, ("Plain-text authentication for user %s returned %s (PAM: %d)\n",
1693               state->request.data.auth.user,
1694               state->response.data.auth.nt_status_string,
1695               state->response.data.auth.pam_error));
1696
1697         return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
1698 }
1699
1700
1701 /**********************************************************************
1702  Challenge Response Authentication Protocol
1703 **********************************************************************/
1704
1705 void winbindd_pam_auth_crap(struct winbindd_cli_state *state)
1706 {
1707         struct winbindd_domain *domain = NULL;
1708         const char *domain_name = NULL;
1709         NTSTATUS result;
1710
1711         if (!check_request_flags(state->request.flags)) {
1712                 result = NT_STATUS_INVALID_PARAMETER_MIX;
1713                 goto done;
1714         }
1715
1716         if (!state->privileged) {
1717                 char *error_string = NULL;
1718                 DEBUG(2, ("winbindd_pam_auth_crap: non-privileged access "
1719                           "denied.  !\n"));
1720                 DEBUGADD(2, ("winbindd_pam_auth_crap: Ensure permissions "
1721                              "on %s are set correctly.\n",
1722                              get_winbind_priv_pipe_dir()));
1723                 /* send a better message than ACCESS_DENIED */
1724                 error_string = talloc_asprintf(state->mem_ctx,
1725                                                "winbind client not authorized "
1726                                                "to use winbindd_pam_auth_crap."
1727                                                " Ensure permissions on %s "
1728                                                "are set correctly.",
1729                                                get_winbind_priv_pipe_dir());
1730                 fstrcpy(state->response.data.auth.error_string, error_string);
1731                 result = NT_STATUS_ACCESS_DENIED;
1732                 goto done;
1733         }
1734
1735         /* Ensure null termination */
1736         state->request.data.auth_crap.user
1737                 [sizeof(state->request.data.auth_crap.user)-1]=0;
1738         state->request.data.auth_crap.domain
1739                 [sizeof(state->request.data.auth_crap.domain)-1]=0;
1740
1741         DEBUG(3, ("[%5lu]: pam auth crap domain: [%s] user: %s\n",
1742                   (unsigned long)state->pid,
1743                   state->request.data.auth_crap.domain,
1744                   state->request.data.auth_crap.user));
1745
1746         if (*state->request.data.auth_crap.domain != '\0') {
1747                 domain_name = state->request.data.auth_crap.domain;
1748         } else if (lp_winbind_use_default_domain()) {
1749                 domain_name = lp_workgroup();
1750         }
1751
1752         if (domain_name != NULL)
1753                 domain = find_auth_domain(state, domain_name);
1754
1755         if (domain != NULL) {
1756                 sendto_domain(state, domain);
1757                 return;
1758         }
1759
1760         result = NT_STATUS_NO_SUCH_USER;
1761
1762  done:
1763         set_auth_errors(&state->response, result);
1764         DEBUG(5, ("CRAP authentication for %s\\%s returned %s (PAM: %d)\n",
1765                   state->request.data.auth_crap.domain,
1766                   state->request.data.auth_crap.user,
1767                   state->response.data.auth.nt_status_string,
1768                   state->response.data.auth.pam_error));
1769         request_error(state);
1770         return;
1771 }
1772
1773
1774 enum winbindd_result winbindd_dual_pam_auth_crap(struct winbindd_domain *domain,
1775                                                  struct winbindd_cli_state *state)
1776 {
1777         NTSTATUS result;
1778         struct netr_SamInfo3 *info3 = NULL;
1779         struct rpc_pipe_client *netlogon_pipe;
1780         const char *name_user = NULL;
1781         const char *name_domain = NULL;
1782         const char *workstation;
1783         struct winbindd_domain *contact_domain;
1784         int attempts = 0;
1785         bool retry;
1786
1787         DATA_BLOB lm_resp, nt_resp;
1788
1789         /* This is child-only, so no check for privileged access is needed
1790            anymore */
1791
1792         /* Ensure null termination */
1793         state->request.data.auth_crap.user[sizeof(state->request.data.auth_crap.user)-1]=0;
1794         state->request.data.auth_crap.domain[sizeof(state->request.data.auth_crap.domain)-1]=0;
1795
1796         if (!check_request_flags(state->request.flags)) {
1797                 result = NT_STATUS_INVALID_PARAMETER_MIX;
1798                 goto done;
1799         }
1800
1801         name_user = state->request.data.auth_crap.user;
1802
1803         if (*state->request.data.auth_crap.domain) {
1804                 name_domain = state->request.data.auth_crap.domain;
1805         } else if (lp_winbind_use_default_domain()) {
1806                 name_domain = lp_workgroup();
1807         } else {
1808                 DEBUG(5,("no domain specified with username (%s) - failing auth\n",
1809                          name_user));
1810                 result = NT_STATUS_NO_SUCH_USER;
1811                 goto done;
1812         }
1813
1814         DEBUG(3, ("[%5lu]: pam auth crap domain: %s user: %s\n", (unsigned long)state->pid,
1815                   name_domain, name_user));
1816
1817         if (*state->request.data.auth_crap.workstation) {
1818                 workstation = state->request.data.auth_crap.workstation;
1819         } else {
1820                 workstation = global_myname();
1821         }
1822
1823         if (state->request.data.auth_crap.lm_resp_len > sizeof(state->request.data.auth_crap.lm_resp)
1824                 || state->request.data.auth_crap.nt_resp_len > sizeof(state->request.data.auth_crap.nt_resp)) {
1825                 DEBUG(0, ("winbindd_pam_auth_crap: invalid password length %u/%u\n",
1826                           state->request.data.auth_crap.lm_resp_len,
1827                           state->request.data.auth_crap.nt_resp_len));
1828                 result = NT_STATUS_INVALID_PARAMETER;
1829                 goto done;
1830         }
1831
1832         lm_resp = data_blob_talloc(state->mem_ctx, state->request.data.auth_crap.lm_resp,
1833                                         state->request.data.auth_crap.lm_resp_len);
1834         nt_resp = data_blob_talloc(state->mem_ctx, state->request.data.auth_crap.nt_resp,
1835                                         state->request.data.auth_crap.nt_resp_len);
1836
1837         /* what domain should we contact? */
1838
1839         if ( IS_DC ) {
1840                 if (!(contact_domain = find_domain_from_name(name_domain))) {
1841                         DEBUG(3, ("Authentication for domain for [%s] -> [%s]\\[%s] failed as %s is not a trusted domain\n",
1842                                   state->request.data.auth_crap.user, name_domain, name_user, name_domain));
1843                         result = NT_STATUS_NO_SUCH_USER;
1844                         goto done;
1845                 }
1846         } else {
1847                 if (is_myname(name_domain)) {
1848                         DEBUG(3, ("Authentication for domain %s (local domain to this server) not supported at this stage\n", name_domain));
1849                         result =  NT_STATUS_NO_SUCH_USER;
1850                         goto done;
1851                 }
1852                 contact_domain = find_our_domain();
1853         }
1854
1855         do {
1856                 netlogon_fn_t logon_fn;
1857
1858                 retry = false;
1859
1860                 netlogon_pipe = NULL;
1861                 result = cm_connect_netlogon(contact_domain, &netlogon_pipe);
1862
1863                 if (!NT_STATUS_IS_OK(result)) {
1864                         DEBUG(3, ("could not open handle to NETLOGON pipe (error: %s)\n",
1865                                   nt_errstr(result)));
1866                         goto done;
1867                 }
1868
1869                 logon_fn = contact_domain->can_do_samlogon_ex
1870                         ? rpccli_netlogon_sam_network_logon_ex
1871                         : rpccli_netlogon_sam_network_logon;
1872
1873                 result = logon_fn(netlogon_pipe,
1874                                   state->mem_ctx,
1875                                   state->request.data.auth_crap.logon_parameters,
1876                                   contact_domain->dcname,
1877                                   name_user,
1878                                   name_domain,
1879                                   /* Bug #3248 - found by Stefan Burkei. */
1880                                   workstation, /* We carefully set this above so use it... */
1881                                   state->request.data.auth_crap.chal,
1882                                   lm_resp,
1883                                   nt_resp,
1884                                   &info3);
1885
1886                 if ((NT_STATUS_V(result) == DCERPC_FAULT_OP_RNG_ERROR)
1887                     && contact_domain->can_do_samlogon_ex) {
1888                         DEBUG(3, ("Got a DC that can not do NetSamLogonEx, "
1889                                   "retrying with NetSamLogon\n"));
1890                         contact_domain->can_do_samlogon_ex = false;
1891                         retry = true;
1892                         continue;
1893                 }
1894
1895                 attempts += 1;
1896
1897                 /* We have to try a second time as cm_connect_netlogon
1898                    might not yet have noticed that the DC has killed
1899                    our connection. */
1900
1901                 if (NT_STATUS_EQUAL(result, NT_STATUS_UNSUCCESSFUL)) {
1902                         retry = true;
1903                         continue;
1904                 }
1905
1906                 /* if we get access denied, a possible cause was that we had and open
1907                    connection to the DC, but someone changed our machine account password
1908                    out from underneath us using 'net rpc changetrustpw' */
1909
1910                 if ( NT_STATUS_EQUAL(result, NT_STATUS_ACCESS_DENIED) ) {
1911                         DEBUG(3,("winbindd_pam_auth: sam_logon returned "
1912                                  "ACCESS_DENIED.  Maybe the trust account "
1913                                 "password was changed and we didn't know it. "
1914                                  "Killing connections to domain %s\n",
1915                                 name_domain));
1916                         invalidate_cm_connection(&contact_domain->conn);
1917                         retry = true;
1918                 }
1919
1920         } while ( (attempts < 2) && retry );
1921
1922         if (NT_STATUS_IS_OK(result)) {
1923
1924                 wcache_invalidate_samlogon(find_domain_from_name(name_domain), info3);
1925                 netsamlogon_cache_store(name_user, info3);
1926
1927                 /* Check if the user is in the right group */
1928
1929                 if (!NT_STATUS_IS_OK(result = check_info3_in_group(state->mem_ctx, info3,
1930                                                         state->request.data.auth_crap.require_membership_of_sid))) {
1931                         DEBUG(3, ("User %s is not in the required group (%s), so "
1932                                   "crap authentication is rejected\n",
1933                                   state->request.data.auth_crap.user,
1934                                   state->request.data.auth_crap.require_membership_of_sid));
1935                         goto done;
1936                 }
1937
1938                 result = append_data(state, info3, name_domain, name_user);
1939                 if (!NT_STATUS_IS_OK(result)) {
1940                         goto done;
1941                 }
1942         }
1943
1944 done:
1945
1946         /* give us a more useful (more correct?) error code */
1947         if ((NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND) ||
1948             (NT_STATUS_EQUAL(result, NT_STATUS_UNSUCCESSFUL)))) {
1949                 result = NT_STATUS_NO_LOGON_SERVERS;
1950         }
1951
1952         if (state->request.flags & WBFLAG_PAM_NT_STATUS_SQUASH) {
1953                 result = nt_status_squash(result);
1954         }
1955
1956         set_auth_errors(&state->response, result);
1957
1958         DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2,
1959               ("NTLM CRAP authentication for user [%s]\\[%s] returned %s (PAM: %d)\n",
1960                name_domain,
1961                name_user,
1962                state->response.data.auth.nt_status_string,
1963                state->response.data.auth.pam_error));
1964
1965         return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
1966 }
1967
1968 /* Change a user password */
1969
1970 void winbindd_pam_chauthtok(struct winbindd_cli_state *state)
1971 {
1972         fstring domain, user;
1973         struct winbindd_domain *contact_domain;
1974
1975         DEBUG(3, ("[%5lu]: pam chauthtok %s\n", (unsigned long)state->pid,
1976                 state->request.data.chauthtok.user));
1977
1978         /* Setup crap */
1979
1980         ws_name_return( state->request.data.auth.user, WB_REPLACE_CHAR );
1981
1982         if (!canonicalize_username(state->request.data.chauthtok.user, domain, user)) {
1983                 set_auth_errors(&state->response, NT_STATUS_NO_SUCH_USER);
1984                 DEBUG(5, ("winbindd_pam_chauthtok: canonicalize_username %s failed with %s"
1985                           "(PAM: %d)\n",
1986                           state->request.data.auth.user,
1987                           state->response.data.auth.nt_status_string,
1988                           state->response.data.auth.pam_error));
1989                 request_error(state);
1990                 return;
1991         }
1992
1993         contact_domain = find_domain_from_name(domain);
1994         if (!contact_domain) {
1995                 set_auth_errors(&state->response, NT_STATUS_NO_SUCH_USER);
1996                 DEBUG(3, ("Cannot change password for [%s] -> [%s]\\[%s] as %s is not a trusted domain\n",
1997                           state->request.data.chauthtok.user, domain, user, domain));
1998                 request_error(state);
1999                 return;
2000         }
2001
2002         sendto_domain(state, contact_domain);
2003 }
2004
2005 enum winbindd_result winbindd_dual_pam_chauthtok(struct winbindd_domain *contact_domain,
2006                                                  struct winbindd_cli_state *state)
2007 {
2008         char *oldpass;
2009         char *newpass = NULL;
2010         POLICY_HND dom_pol;
2011         struct rpc_pipe_client *cli;
2012         bool got_info = false;
2013         struct samr_DomInfo1 *info = NULL;
2014         struct samr_ChangeReject *reject = NULL;
2015         NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
2016         fstring domain, user;
2017
2018         DEBUG(3, ("[%5lu]: dual pam chauthtok %s\n", (unsigned long)state->pid,
2019                   state->request.data.auth.user));
2020
2021         if (!parse_domain_user(state->request.data.chauthtok.user, domain, user)) {
2022                 goto done;
2023         }
2024
2025         /* Change password */
2026
2027         oldpass = state->request.data.chauthtok.oldpass;
2028         newpass = state->request.data.chauthtok.newpass;
2029
2030         /* Initialize reject reason */
2031         state->response.data.auth.reject_reason = Undefined;
2032
2033         /* Get sam handle */
2034
2035         result = cm_connect_sam(contact_domain, state->mem_ctx, &cli,
2036                                 &dom_pol);
2037         if (!NT_STATUS_IS_OK(result)) {
2038                 DEBUG(1, ("could not get SAM handle on DC for %s\n", domain));
2039                 goto done;
2040         }
2041
2042         result = rpccli_samr_chgpasswd_user3(cli, state->mem_ctx,
2043                                              user,
2044                                              newpass,
2045                                              oldpass,
2046                                              &info,
2047                                              &reject);
2048
2049         /* Windows 2003 returns NT_STATUS_PASSWORD_RESTRICTION */
2050
2051         if (NT_STATUS_EQUAL(result, NT_STATUS_PASSWORD_RESTRICTION) ) {
2052
2053                 fill_in_password_policy(&state->response, info);
2054
2055                 state->response.data.auth.reject_reason =
2056                         reject->reason;
2057
2058                 got_info = true;
2059         }
2060
2061         /* only fallback when the chgpasswd_user3 call is not supported */
2062         if ((NT_STATUS_EQUAL(result, NT_STATUS(DCERPC_FAULT_OP_RNG_ERROR))) ||
2063                    (NT_STATUS_EQUAL(result, NT_STATUS_NOT_SUPPORTED)) ||
2064                    (NT_STATUS_EQUAL(result, NT_STATUS_NOT_IMPLEMENTED))) {
2065
2066                 DEBUG(10,("Password change with chgpasswd_user3 failed with: %s, retrying chgpasswd_user2\n",
2067                         nt_errstr(result)));
2068
2069                 result = rpccli_samr_chgpasswd_user2(cli, state->mem_ctx, user, newpass, oldpass);
2070
2071                 /* Windows 2000 returns NT_STATUS_ACCOUNT_RESTRICTION.
2072                    Map to the same status code as Windows 2003. */
2073
2074                 if ( NT_STATUS_EQUAL(NT_STATUS_ACCOUNT_RESTRICTION, result ) ) {
2075                         result = NT_STATUS_PASSWORD_RESTRICTION;
2076                 }
2077         }
2078
2079 done:
2080
2081         if (NT_STATUS_IS_OK(result) && (state->request.flags & WBFLAG_PAM_CACHED_LOGIN)) {
2082
2083                 /* Update the single sign-on memory creds. */
2084                 result = winbindd_replace_memory_creds(state->request.data.chauthtok.user,
2085                                                         newpass);
2086
2087                 /* When we login from gdm or xdm and password expires,
2088                  * we change password, but there are no memory crendentials
2089                  * So, winbindd_replace_memory_creds() returns
2090                  * NT_STATUS_OBJECT_NAME_NOT_FOUND. This is not a failure.
2091                  * --- BoYang
2092                  * */
2093                 if (NT_STATUS_EQUAL(result, NT_STATUS_OBJECT_NAME_NOT_FOUND)) {
2094                         result = NT_STATUS_OK;
2095                 }
2096
2097                 if (!NT_STATUS_IS_OK(result)) {
2098                         DEBUG(10,("Failed to replace memory creds: %s\n", nt_errstr(result)));
2099                         goto process_result;
2100                 }
2101
2102                 if (lp_winbind_offline_logon()) {
2103                         result = winbindd_update_creds_by_name(contact_domain,
2104                                                          state->mem_ctx, user,
2105                                                          newpass);
2106                         /* Again, this happens when we login from gdm or xdm
2107                          * and the password expires, *BUT* cached crendentials
2108                          * doesn't exist. winbindd_update_creds_by_name()
2109                          * returns NT_STATUS_NO_SUCH_USER.
2110                          * This is not a failure.
2111                          * --- BoYang
2112                          * */
2113                         if (NT_STATUS_EQUAL(result, NT_STATUS_NO_SUCH_USER)) {
2114                                 result = NT_STATUS_OK;
2115                         }
2116
2117                         if (!NT_STATUS_IS_OK(result)) {
2118                                 DEBUG(10,("Failed to store creds: %s\n", nt_errstr(result)));
2119                                 goto process_result;
2120                         }
2121                 }
2122         }
2123
2124         if (!NT_STATUS_IS_OK(result) && !got_info && contact_domain) {
2125
2126                 NTSTATUS policy_ret;
2127
2128                 policy_ret = fillup_password_policy(contact_domain, state);
2129
2130                 /* failure of this is non critical, it will just provide no
2131                  * additional information to the client why the change has
2132                  * failed - Guenther */
2133
2134                 if (!NT_STATUS_IS_OK(policy_ret)) {
2135                         DEBUG(10,("Failed to get password policies: %s\n", nt_errstr(policy_ret)));
2136                         goto process_result;
2137                 }
2138         }
2139
2140 process_result:
2141
2142         set_auth_errors(&state->response, result);
2143
2144         DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2,
2145               ("Password change for user [%s]\\[%s] returned %s (PAM: %d)\n",
2146                domain,
2147                user,
2148                state->response.data.auth.nt_status_string,
2149                state->response.data.auth.pam_error));
2150
2151         return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
2152 }
2153
2154 void winbindd_pam_logoff(struct winbindd_cli_state *state)
2155 {
2156         struct winbindd_domain *domain;
2157         fstring name_domain, user;
2158         uid_t caller_uid = (uid_t)-1;
2159         uid_t request_uid = state->request.data.logoff.uid;
2160
2161         DEBUG(3, ("[%5lu]: pam logoff %s\n", (unsigned long)state->pid,
2162                 state->request.data.logoff.user));
2163
2164         /* Ensure null termination */
2165         state->request.data.logoff.user
2166                 [sizeof(state->request.data.logoff.user)-1]='\0';
2167
2168         state->request.data.logoff.krb5ccname
2169                 [sizeof(state->request.data.logoff.krb5ccname)-1]='\0';
2170
2171         if (request_uid == (gid_t)-1) {
2172                 goto failed;
2173         }
2174
2175         if (!canonicalize_username(state->request.data.logoff.user, name_domain, user)) {
2176                 goto failed;
2177         }
2178
2179         if ((domain = find_auth_domain(state, name_domain)) == NULL) {
2180                 goto failed;
2181         }
2182
2183         if ((sys_getpeereid(state->sock, &caller_uid)) != 0) {
2184                 DEBUG(1,("winbindd_pam_logoff: failed to check peerid: %s\n",
2185                         strerror(errno)));
2186                 goto failed;
2187         }
2188
2189         switch (caller_uid) {
2190                 case -1:
2191                         goto failed;
2192                 case 0:
2193                         /* root must be able to logoff any user - gd */
2194                         state->request.data.logoff.uid = request_uid;
2195                         break;
2196                 default:
2197                         if (caller_uid != request_uid) {
2198                                 DEBUG(1,("winbindd_pam_logoff: caller requested invalid uid\n"));
2199                                 goto failed;
2200                         }
2201                         state->request.data.logoff.uid = caller_uid;
2202                         break;
2203         }
2204
2205         sendto_domain(state, domain);
2206         return;
2207
2208  failed:
2209         set_auth_errors(&state->response, NT_STATUS_NO_SUCH_USER);
2210         DEBUG(5, ("Pam Logoff for %s returned %s "
2211                   "(PAM: %d)\n",
2212                   state->request.data.logoff.user,
2213                   state->response.data.auth.nt_status_string,
2214                   state->response.data.auth.pam_error));
2215         request_error(state);
2216         return;
2217 }
2218
2219 enum winbindd_result winbindd_dual_pam_logoff(struct winbindd_domain *domain,
2220                                               struct winbindd_cli_state *state)
2221 {
2222         NTSTATUS result = NT_STATUS_NOT_SUPPORTED;
2223
2224         DEBUG(3, ("[%5lu]: pam dual logoff %s\n", (unsigned long)state->pid,
2225                 state->request.data.logoff.user));
2226
2227         if (!(state->request.flags & WBFLAG_PAM_KRB5)) {
2228                 result = NT_STATUS_OK;
2229                 goto process_result;
2230         }
2231
2232         if (state->request.data.logoff.krb5ccname[0] == '\0') {
2233                 result = NT_STATUS_OK;
2234                 goto process_result;
2235         }
2236
2237 #ifdef HAVE_KRB5
2238
2239         if (state->request.data.logoff.uid < 0) {
2240                 DEBUG(0,("winbindd_pam_logoff: invalid uid\n"));
2241                 goto process_result;
2242         }
2243
2244         /* what we need here is to find the corresponding krb5 ccache name *we*
2245          * created for a given username and destroy it */
2246
2247         if (!ccache_entry_exists(state->request.data.logoff.user)) {
2248                 result = NT_STATUS_OK;
2249                 DEBUG(10,("winbindd_pam_logoff: no entry found.\n"));
2250                 goto process_result;
2251         }
2252
2253         if (!ccache_entry_identical(state->request.data.logoff.user,
2254                                         state->request.data.logoff.uid,
2255                                         state->request.data.logoff.krb5ccname)) {
2256                 DEBUG(0,("winbindd_pam_logoff: cached entry differs.\n"));
2257                 goto process_result;
2258         }
2259
2260         result = remove_ccache(state->request.data.logoff.user);
2261         if (!NT_STATUS_IS_OK(result)) {
2262                 DEBUG(0,("winbindd_pam_logoff: failed to remove ccache: %s\n",
2263                         nt_errstr(result)));
2264                 goto process_result;
2265         }
2266
2267 #else
2268         result = NT_STATUS_NOT_SUPPORTED;
2269 #endif
2270
2271 process_result:
2272
2273         winbindd_delete_memory_creds(state->request.data.logoff.user);
2274
2275         set_auth_errors(&state->response, result);
2276
2277         return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
2278 }
2279
2280 /* Change user password with auth crap*/
2281
2282 void winbindd_pam_chng_pswd_auth_crap(struct winbindd_cli_state *state)
2283 {
2284         struct winbindd_domain *domain = NULL;
2285         const char *domain_name = NULL;
2286
2287         /* Ensure null termination */
2288         state->request.data.chng_pswd_auth_crap.user[
2289                 sizeof(state->request.data.chng_pswd_auth_crap.user)-1]=0;
2290         state->request.data.chng_pswd_auth_crap.domain[
2291                 sizeof(state->request.data.chng_pswd_auth_crap.domain)-1]=0;
2292
2293         DEBUG(3, ("[%5lu]: pam change pswd auth crap domain: %s user: %s\n",
2294                   (unsigned long)state->pid,
2295                   state->request.data.chng_pswd_auth_crap.domain,
2296                   state->request.data.chng_pswd_auth_crap.user));
2297
2298         if (*state->request.data.chng_pswd_auth_crap.domain != '\0') {
2299                 domain_name = state->request.data.chng_pswd_auth_crap.domain;
2300         } else if (lp_winbind_use_default_domain()) {
2301                 domain_name = lp_workgroup();
2302         }
2303
2304         if (domain_name != NULL)
2305                 domain = find_domain_from_name(domain_name);
2306
2307         if (domain != NULL) {
2308                 DEBUG(7, ("[%5lu]: pam auth crap changing pswd in domain: "
2309                           "%s\n", (unsigned long)state->pid,domain->name));
2310                 sendto_domain(state, domain);
2311                 return;
2312         }
2313
2314         set_auth_errors(&state->response, NT_STATUS_NO_SUCH_USER);
2315         DEBUG(5, ("CRAP change password  for %s\\%s returned %s (PAM: %d)\n",
2316                   state->request.data.chng_pswd_auth_crap.domain,
2317                   state->request.data.chng_pswd_auth_crap.user,
2318                   state->response.data.auth.nt_status_string,
2319                   state->response.data.auth.pam_error));
2320         request_error(state);
2321         return;
2322 }
2323
2324 enum winbindd_result winbindd_dual_pam_chng_pswd_auth_crap(struct winbindd_domain *domainSt, struct winbindd_cli_state *state)
2325 {
2326         NTSTATUS result;
2327         DATA_BLOB new_nt_password;
2328         DATA_BLOB old_nt_hash_enc;
2329         DATA_BLOB new_lm_password;
2330         DATA_BLOB old_lm_hash_enc;
2331         fstring  domain,user;
2332         POLICY_HND dom_pol;
2333         struct winbindd_domain *contact_domain = domainSt;
2334         struct rpc_pipe_client *cli;
2335
2336         /* Ensure null termination */
2337         state->request.data.chng_pswd_auth_crap.user[
2338                 sizeof(state->request.data.chng_pswd_auth_crap.user)-1]=0;
2339         state->request.data.chng_pswd_auth_crap.domain[
2340                 sizeof(state->request.data.chng_pswd_auth_crap.domain)-1]=0;
2341         *domain = 0;
2342         *user = 0;
2343
2344         DEBUG(3, ("[%5lu]: pam change pswd auth crap domain: %s user: %s\n",
2345                   (unsigned long)state->pid,
2346                   state->request.data.chng_pswd_auth_crap.domain,
2347                   state->request.data.chng_pswd_auth_crap.user));
2348
2349         if (lp_winbind_offline_logon()) {
2350                 DEBUG(0,("Refusing password change as winbind offline logons are enabled. "));
2351                 DEBUGADD(0,("Changing passwords here would risk inconsistent logons\n"));
2352                 result = NT_STATUS_ACCESS_DENIED;
2353                 goto done;
2354         }
2355
2356         if (*state->request.data.chng_pswd_auth_crap.domain) {
2357                 fstrcpy(domain,state->request.data.chng_pswd_auth_crap.domain);
2358         } else {
2359                 parse_domain_user(state->request.data.chng_pswd_auth_crap.user,
2360                                   domain, user);
2361
2362                 if(!*domain) {
2363                         DEBUG(3,("no domain specified with username (%s) - "
2364                                  "failing auth\n",
2365                                  state->request.data.chng_pswd_auth_crap.user));
2366                         result = NT_STATUS_NO_SUCH_USER;
2367                         goto done;
2368                 }
2369         }
2370
2371         if (!*domain && lp_winbind_use_default_domain()) {
2372                 fstrcpy(domain,(char *)lp_workgroup());
2373         }
2374
2375         if(!*user) {
2376                 fstrcpy(user, state->request.data.chng_pswd_auth_crap.user);
2377         }
2378
2379         DEBUG(3, ("[%5lu]: pam auth crap domain: %s user: %s\n",
2380                   (unsigned long)state->pid, domain, user));
2381
2382         /* Change password */
2383         new_nt_password = data_blob_talloc(
2384                 state->mem_ctx,
2385                 state->request.data.chng_pswd_auth_crap.new_nt_pswd,
2386                 state->request.data.chng_pswd_auth_crap.new_nt_pswd_len);
2387
2388         old_nt_hash_enc = data_blob_talloc(
2389                 state->mem_ctx,
2390                 state->request.data.chng_pswd_auth_crap.old_nt_hash_enc,
2391                 state->request.data.chng_pswd_auth_crap.old_nt_hash_enc_len);
2392
2393         if(state->request.data.chng_pswd_auth_crap.new_lm_pswd_len > 0) {
2394                 new_lm_password = data_blob_talloc(
2395                         state->mem_ctx,
2396                         state->request.data.chng_pswd_auth_crap.new_lm_pswd,
2397                         state->request.data.chng_pswd_auth_crap.new_lm_pswd_len);
2398
2399                 old_lm_hash_enc = data_blob_talloc(
2400                         state->mem_ctx,
2401                         state->request.data.chng_pswd_auth_crap.old_lm_hash_enc,
2402                         state->request.data.chng_pswd_auth_crap.old_lm_hash_enc_len);
2403         } else {
2404                 new_lm_password.length = 0;
2405                 old_lm_hash_enc.length = 0;
2406         }
2407
2408         /* Get sam handle */
2409
2410         result = cm_connect_sam(contact_domain, state->mem_ctx, &cli, &dom_pol);
2411         if (!NT_STATUS_IS_OK(result)) {
2412                 DEBUG(1, ("could not get SAM handle on DC for %s\n", domain));
2413                 goto done;
2414         }
2415
2416         result = rpccli_samr_chng_pswd_auth_crap(
2417                 cli, state->mem_ctx, user, new_nt_password, old_nt_hash_enc,
2418                 new_lm_password, old_lm_hash_enc);
2419
2420  done:
2421
2422         set_auth_errors(&state->response, result);
2423
2424         DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2,
2425               ("Password change for user [%s]\\[%s] returned %s (PAM: %d)\n",
2426                domain, user,
2427                state->response.data.auth.nt_status_string,
2428                state->response.data.auth.pam_error));
2429
2430         return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
2431 }