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