s3: Replace most calls to sid_append_rid() by sid_compose()
[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 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 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         struct winbindd_domain *contact_domain;
1207         fstring name_domain, name_user;
1208         bool retry;
1209         NTSTATUS result;
1210         struct netr_SamInfo3 *my_info3 = NULL;
1211
1212         *info3 = NULL;
1213
1214         DEBUG(10,("winbindd_dual_pam_auth_samlogon\n"));
1215
1216         /* Parse domain and username */
1217
1218         parse_domain_user(state->request->data.auth.user, name_domain, name_user);
1219
1220         /* do password magic */
1221
1222
1223         generate_random_buffer(chal, 8);
1224         if (lp_client_ntlmv2_auth()) {
1225                 DATA_BLOB server_chal;
1226                 DATA_BLOB names_blob;
1227                 DATA_BLOB nt_response;
1228                 DATA_BLOB lm_response;
1229                 server_chal = data_blob_talloc(state->mem_ctx, chal, 8);
1230
1231                 /* note that the 'workgroup' here is a best guess - we don't know
1232                    the server's domain at this point.  The 'server name' is also
1233                    dodgy...
1234                 */
1235                 names_blob = NTLMv2_generate_names_blob(state->mem_ctx, global_myname(), lp_workgroup());
1236
1237                 if (!SMBNTLMv2encrypt(NULL, name_user, name_domain,
1238                                       state->request->data.auth.pass,
1239                                       &server_chal,
1240                                       &names_blob,
1241                                       &lm_response, &nt_response, NULL, NULL)) {
1242                         data_blob_free(&names_blob);
1243                         data_blob_free(&server_chal);
1244                         DEBUG(0, ("winbindd_pam_auth: SMBNTLMv2encrypt() failed!\n"));
1245                         result = NT_STATUS_NO_MEMORY;
1246                         goto done;
1247                 }
1248                 data_blob_free(&names_blob);
1249                 data_blob_free(&server_chal);
1250                 lm_resp = data_blob_talloc(state->mem_ctx, lm_response.data,
1251                                            lm_response.length);
1252                 nt_resp = data_blob_talloc(state->mem_ctx, nt_response.data,
1253                                            nt_response.length);
1254                 data_blob_free(&lm_response);
1255                 data_blob_free(&nt_response);
1256
1257         } else {
1258                 if (lp_client_lanman_auth()
1259                     && SMBencrypt(state->request->data.auth.pass,
1260                                   chal,
1261                                   local_lm_response)) {
1262                         lm_resp = data_blob_talloc(state->mem_ctx,
1263                                                    local_lm_response,
1264                                                    sizeof(local_lm_response));
1265                 } else {
1266                         lm_resp = data_blob_null;
1267                 }
1268                 SMBNTencrypt(state->request->data.auth.pass,
1269                              chal,
1270                              local_nt_response);
1271
1272                 nt_resp = data_blob_talloc(state->mem_ctx,
1273                                            local_nt_response,
1274                                            sizeof(local_nt_response));
1275         }
1276
1277         /* what domain should we contact? */
1278
1279         if ( IS_DC ) {
1280                 if (!(contact_domain = find_domain_from_name(name_domain))) {
1281                         DEBUG(3, ("Authentication for domain for [%s] -> [%s]\\[%s] failed as %s is not a trusted domain\n",
1282                                   state->request->data.auth.user, name_domain, name_user, name_domain));
1283                         result = NT_STATUS_NO_SUCH_USER;
1284                         goto done;
1285                 }
1286
1287         } else {
1288                 if (is_myname(name_domain)) {
1289                         DEBUG(3, ("Authentication for domain %s (local domain to this server) not supported at this stage\n", name_domain));
1290                         result =  NT_STATUS_NO_SUCH_USER;
1291                         goto done;
1292                 }
1293
1294                 contact_domain = find_our_domain();
1295         }
1296
1297         /* check authentication loop */
1298
1299         do {
1300                 netlogon_fn_t logon_fn;
1301
1302                 ZERO_STRUCTP(my_info3);
1303                 retry = false;
1304
1305                 result = cm_connect_netlogon(contact_domain, &netlogon_pipe);
1306
1307                 if (!NT_STATUS_IS_OK(result)) {
1308                         DEBUG(3, ("could not open handle to NETLOGON pipe\n"));
1309                         goto done;
1310                 }
1311
1312                 /* It is really important to try SamLogonEx here,
1313                  * because in a clustered environment, we want to use
1314                  * one machine account from multiple physical
1315                  * computers.
1316                  *
1317                  * With a normal SamLogon call, we must keep the
1318                  * credentials chain updated and intact between all
1319                  * users of the machine account (which would imply
1320                  * cross-node communication for every NTLM logon).
1321                  *
1322                  * (The credentials chain is not per NETLOGON pipe
1323                  * connection, but globally on the server/client pair
1324                  * by machine name).
1325                  *
1326                  * When using SamLogonEx, the credentials are not
1327                  * supplied, but the session key is implied by the
1328                  * wrapping SamLogon context.
1329                  *
1330                  *  -- abartlet 21 April 2008
1331                  */
1332
1333                 logon_fn = contact_domain->can_do_samlogon_ex
1334                         ? rpccli_netlogon_sam_network_logon_ex
1335                         : rpccli_netlogon_sam_network_logon;
1336
1337                 result = logon_fn(netlogon_pipe,
1338                                   state->mem_ctx,
1339                                   0,
1340                                   contact_domain->dcname, /* server name */
1341                                   name_user,              /* user name */
1342                                   name_domain,            /* target domain */
1343                                   global_myname(),        /* workstation */
1344                                   chal,
1345                                   lm_resp,
1346                                   nt_resp,
1347                                   &my_info3);
1348                 attempts += 1;
1349
1350                 if ((NT_STATUS_V(result) == DCERPC_FAULT_OP_RNG_ERROR)
1351                     && contact_domain->can_do_samlogon_ex) {
1352                         DEBUG(3, ("Got a DC that can not do NetSamLogonEx, "
1353                                   "retrying with NetSamLogon\n"));
1354                         contact_domain->can_do_samlogon_ex = false;
1355                         retry = true;
1356                         continue;
1357                 }
1358
1359                 /* We have to try a second time as cm_connect_netlogon
1360                    might not yet have noticed that the DC has killed
1361                    our connection. */
1362
1363                 if (NT_STATUS_EQUAL(result, NT_STATUS_UNSUCCESSFUL)) {
1364                         retry = true;
1365                         continue;
1366                 }
1367
1368                 /* if we get access denied, a possible cause was that we had
1369                    and open connection to the DC, but someone changed our
1370                    machine account password out from underneath us using 'net
1371                    rpc changetrustpw' */
1372
1373                 if ( NT_STATUS_EQUAL(result, NT_STATUS_ACCESS_DENIED) ) {
1374                         DEBUG(3,("winbindd_pam_auth: sam_logon returned "
1375                                  "ACCESS_DENIED.  Maybe the trust account "
1376                                 "password was changed and we didn't know it. "
1377                                  "Killing connections to domain %s\n",
1378                                 name_domain));
1379                         invalidate_cm_connection(&contact_domain->conn);
1380                         retry = true;
1381                 }
1382
1383         } while ( (attempts < 2) && retry );
1384
1385         /* handle the case where a NT4 DC does not fill in the acct_flags in
1386          * the samlogon reply info3. When accurate info3 is required by the
1387          * caller, we look up the account flags ourselve - gd */
1388
1389         if ((state->request->flags & WBFLAG_PAM_INFO3_TEXT) &&
1390             NT_STATUS_IS_OK(result) && (my_info3->base.acct_flags == 0)) {
1391
1392                 struct rpc_pipe_client *samr_pipe;
1393                 struct policy_handle samr_domain_handle, user_pol;
1394                 union samr_UserInfo *info = NULL;
1395                 NTSTATUS status_tmp;
1396                 uint32 acct_flags;
1397
1398                 status_tmp = cm_connect_sam(contact_domain, state->mem_ctx,
1399                                             &samr_pipe, &samr_domain_handle);
1400
1401                 if (!NT_STATUS_IS_OK(status_tmp)) {
1402                         DEBUG(3, ("could not open handle to SAMR pipe: %s\n",
1403                                 nt_errstr(status_tmp)));
1404                         goto done;
1405                 }
1406
1407                 status_tmp = rpccli_samr_OpenUser(samr_pipe, state->mem_ctx,
1408                                                   &samr_domain_handle,
1409                                                   MAXIMUM_ALLOWED_ACCESS,
1410                                                   my_info3->base.rid,
1411                                                   &user_pol);
1412
1413                 if (!NT_STATUS_IS_OK(status_tmp)) {
1414                         DEBUG(3, ("could not open user handle on SAMR pipe: %s\n",
1415                                 nt_errstr(status_tmp)));
1416                         goto done;
1417                 }
1418
1419                 status_tmp = rpccli_samr_QueryUserInfo(samr_pipe, state->mem_ctx,
1420                                                        &user_pol,
1421                                                        16,
1422                                                        &info);
1423
1424                 if (!NT_STATUS_IS_OK(status_tmp)) {
1425                         DEBUG(3, ("could not query user info on SAMR pipe: %s\n",
1426                                 nt_errstr(status_tmp)));
1427                         rpccli_samr_Close(samr_pipe, state->mem_ctx, &user_pol);
1428                         goto done;
1429                 }
1430
1431                 acct_flags = info->info16.acct_flags;
1432
1433                 if (acct_flags == 0) {
1434                         rpccli_samr_Close(samr_pipe, state->mem_ctx, &user_pol);
1435                         goto done;
1436                 }
1437
1438                 my_info3->base.acct_flags = acct_flags;
1439
1440                 DEBUG(10,("successfully retrieved acct_flags 0x%x\n", acct_flags));
1441
1442                 rpccli_samr_Close(samr_pipe, state->mem_ctx, &user_pol);
1443         }
1444
1445         *info3 = my_info3;
1446 done:
1447         return result;
1448 }
1449
1450 enum winbindd_result winbindd_dual_pam_auth(struct winbindd_domain *domain,
1451                                             struct winbindd_cli_state *state)
1452 {
1453         NTSTATUS result = NT_STATUS_LOGON_FAILURE;
1454         NTSTATUS krb5_result = NT_STATUS_OK;
1455         fstring name_domain, name_user;
1456         char *mapped_user;
1457         fstring domain_user;
1458         struct netr_SamInfo3 *info3 = NULL;
1459         NTSTATUS name_map_status = NT_STATUS_UNSUCCESSFUL;
1460
1461         /* Ensure null termination */
1462         state->request->data.auth.user[sizeof(state->request->data.auth.user)-1]='\0';
1463
1464         /* Ensure null termination */
1465         state->request->data.auth.pass[sizeof(state->request->data.auth.pass)-1]='\0';
1466
1467         DEBUG(3, ("[%5lu]: dual pam auth %s\n", (unsigned long)state->pid,
1468                   state->request->data.auth.user));
1469
1470         if (!check_request_flags(state->request->flags)) {
1471                 result = NT_STATUS_INVALID_PARAMETER_MIX;
1472                 goto done;
1473         }
1474
1475         /* Parse domain and username */
1476
1477         name_map_status = normalize_name_unmap(state->mem_ctx,
1478                                                state->request->data.auth.user,
1479                                                &mapped_user);
1480
1481         /* If the name normalization didnt' actually do anything,
1482            just use the original name */
1483
1484         if (!NT_STATUS_IS_OK(name_map_status) &&
1485             !NT_STATUS_EQUAL(name_map_status, NT_STATUS_FILE_RENAMED))
1486         {
1487                 mapped_user = state->request->data.auth.user;
1488         }
1489
1490         parse_domain_user(mapped_user, name_domain, name_user);
1491
1492         if ( mapped_user != state->request->data.auth.user ) {
1493                 fstr_sprintf( domain_user, "%s\\%s", name_domain, name_user );
1494                 safe_strcpy( state->request->data.auth.user, domain_user,
1495                              sizeof(state->request->data.auth.user)-1 );
1496         }
1497
1498         if (domain->online == false) {
1499                 result = NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND;
1500                 if (domain->startup) {
1501                         /* Logons are very important to users. If we're offline and
1502                            we get a request within the first 30 seconds of startup,
1503                            try very hard to find a DC and go online. */
1504
1505                         DEBUG(10,("winbindd_dual_pam_auth: domain: %s offline and auth "
1506                                 "request in startup mode.\n", domain->name ));
1507
1508                         winbindd_flush_negative_conn_cache(domain);
1509                         result = init_dc_connection(domain);
1510                 }
1511         }
1512
1513         DEBUG(10,("winbindd_dual_pam_auth: domain: %s last was %s\n", domain->name, domain->online ? "online":"offline"));
1514
1515         /* Check for Kerberos authentication */
1516         if (domain->online && (state->request->flags & WBFLAG_PAM_KRB5)) {
1517
1518                 result = winbindd_dual_pam_auth_kerberos(domain, state, &info3);
1519                 /* save for later */
1520                 krb5_result = result;
1521
1522
1523                 if (NT_STATUS_IS_OK(result)) {
1524                         DEBUG(10,("winbindd_dual_pam_auth_kerberos succeeded\n"));
1525                         goto process_result;
1526                 } else {
1527                         DEBUG(10,("winbindd_dual_pam_auth_kerberos failed: %s\n", nt_errstr(result)));
1528                 }
1529
1530                 if (NT_STATUS_EQUAL(result, NT_STATUS_NO_LOGON_SERVERS) ||
1531                     NT_STATUS_EQUAL(result, NT_STATUS_IO_TIMEOUT) ||
1532                     NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
1533                         DEBUG(10,("winbindd_dual_pam_auth_kerberos setting domain to offline\n"));
1534                         set_domain_offline( domain );
1535                         goto cached_logon;
1536                 }
1537
1538                 /* there are quite some NT_STATUS errors where there is no
1539                  * point in retrying with a samlogon, we explictly have to take
1540                  * care not to increase the bad logon counter on the DC */
1541
1542                 if (NT_STATUS_EQUAL(result, NT_STATUS_ACCOUNT_DISABLED) ||
1543                     NT_STATUS_EQUAL(result, NT_STATUS_ACCOUNT_EXPIRED) ||
1544                     NT_STATUS_EQUAL(result, NT_STATUS_ACCOUNT_LOCKED_OUT) ||
1545                     NT_STATUS_EQUAL(result, NT_STATUS_INVALID_LOGON_HOURS) ||
1546                     NT_STATUS_EQUAL(result, NT_STATUS_INVALID_WORKSTATION) ||
1547                     NT_STATUS_EQUAL(result, NT_STATUS_LOGON_FAILURE) ||
1548                     NT_STATUS_EQUAL(result, NT_STATUS_NO_SUCH_USER) ||
1549                     NT_STATUS_EQUAL(result, NT_STATUS_PASSWORD_EXPIRED) ||
1550                     NT_STATUS_EQUAL(result, NT_STATUS_PASSWORD_MUST_CHANGE) ||
1551                     NT_STATUS_EQUAL(result, NT_STATUS_WRONG_PASSWORD)) {
1552                         goto process_result;
1553                 }
1554
1555                 if (state->request->flags & WBFLAG_PAM_FALLBACK_AFTER_KRB5) {
1556                         DEBUG(3,("falling back to samlogon\n"));
1557                         goto sam_logon;
1558                 } else {
1559                         goto cached_logon;
1560                 }
1561         }
1562
1563 sam_logon:
1564         /* Check for Samlogon authentication */
1565         if (domain->online) {
1566                 result = winbindd_dual_pam_auth_samlogon(domain, state, &info3);
1567
1568                 if (NT_STATUS_IS_OK(result)) {
1569                         DEBUG(10,("winbindd_dual_pam_auth_samlogon succeeded\n"));
1570                         /* add the Krb5 err if we have one */
1571                         if ( NT_STATUS_EQUAL(krb5_result, NT_STATUS_TIME_DIFFERENCE_AT_DC ) ) {
1572                                 info3->base.user_flags |= LOGON_KRB5_FAIL_CLOCK_SKEW;
1573                         }
1574                         goto process_result;
1575                 }
1576
1577                 DEBUG(10,("winbindd_dual_pam_auth_samlogon failed: %s\n",
1578                           nt_errstr(result)));
1579
1580                 if (NT_STATUS_EQUAL(result, NT_STATUS_NO_LOGON_SERVERS) ||
1581                     NT_STATUS_EQUAL(result, NT_STATUS_IO_TIMEOUT) ||
1582                     NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND))
1583                 {
1584                         DEBUG(10,("winbindd_dual_pam_auth_samlogon setting domain to offline\n"));
1585                         set_domain_offline( domain );
1586                         goto cached_logon;
1587                 }
1588
1589                         if (domain->online) {
1590                                 /* We're still online - fail. */
1591                                 goto done;
1592                         }
1593         }
1594
1595 cached_logon:
1596         /* Check for Cached logons */
1597         if (!domain->online && (state->request->flags & WBFLAG_PAM_CACHED_LOGIN) &&
1598             lp_winbind_offline_logon()) {
1599
1600                 result = winbindd_dual_pam_auth_cached(domain, state, &info3);
1601
1602                 if (NT_STATUS_IS_OK(result)) {
1603                         DEBUG(10,("winbindd_dual_pam_auth_cached succeeded\n"));
1604                         goto process_result;
1605                 } else {
1606                         DEBUG(10,("winbindd_dual_pam_auth_cached failed: %s\n", nt_errstr(result)));
1607                         goto done;
1608                 }
1609         }
1610
1611 process_result:
1612
1613         if (NT_STATUS_IS_OK(result)) {
1614
1615                 DOM_SID user_sid;
1616
1617                 /* In all codepaths where result == NT_STATUS_OK info3 must have
1618                    been initialized. */
1619                 if (!info3) {
1620                         result = NT_STATUS_INTERNAL_ERROR;
1621                         goto done;
1622                 }
1623
1624                 wcache_invalidate_samlogon(find_domain_from_name(name_domain), info3);
1625                 netsamlogon_cache_store(name_user, info3);
1626
1627                 /* save name_to_sid info as early as possible (only if
1628                    this is our primary domain so we don't invalidate
1629                    the cache entry by storing the seq_num for the wrong
1630                    domain). */
1631                 if ( domain->primary ) {
1632                         sid_compose(&user_sid, info3->base.domain_sid,
1633                                     info3->base.rid);
1634                         cache_name2sid(domain, name_domain, name_user,
1635                                        SID_NAME_USER, &user_sid);
1636                 }
1637
1638                 /* Check if the user is in the right group */
1639
1640                 result = check_info3_in_group(
1641                         info3,
1642                         state->request->data.auth.require_membership_of_sid);
1643                 if (!NT_STATUS_IS_OK(result)) {
1644                         DEBUG(3, ("User %s is not in the required group (%s), so plaintext authentication is rejected\n",
1645                                   state->request->data.auth.user,
1646                                   state->request->data.auth.require_membership_of_sid));
1647                         goto done;
1648                 }
1649
1650                 result = append_auth_data(state, info3, name_domain,
1651                                           name_user);
1652                 if (!NT_STATUS_IS_OK(result)) {
1653                         goto done;
1654                 }
1655
1656                 if ((state->request->flags & WBFLAG_PAM_CACHED_LOGIN)) {
1657
1658                         /* Store in-memory creds for single-signon using ntlm_auth. */
1659                         result = winbindd_add_memory_creds(state->request->data.auth.user,
1660                                                         get_uid_from_state(state),
1661                                                         state->request->data.auth.pass);
1662
1663                         if (!NT_STATUS_IS_OK(result)) {
1664                                 DEBUG(10,("Failed to store memory creds: %s\n", nt_errstr(result)));
1665                                 goto done;
1666                         }
1667
1668                         if (lp_winbind_offline_logon()) {
1669                                 result = winbindd_store_creds(domain,
1670                                                       state->mem_ctx,
1671                                                       state->request->data.auth.user,
1672                                                       state->request->data.auth.pass,
1673                                                       info3, NULL);
1674                                 if (!NT_STATUS_IS_OK(result)) {
1675
1676                                         /* Release refcount. */
1677                                         winbindd_delete_memory_creds(state->request->data.auth.user);
1678
1679                                         DEBUG(10,("Failed to store creds: %s\n", nt_errstr(result)));
1680                                         goto done;
1681                                 }
1682                         }
1683                 }
1684
1685
1686                 if (state->request->flags & WBFLAG_PAM_GET_PWD_POLICY) {
1687                         struct winbindd_domain *our_domain = find_our_domain();
1688
1689                         /* This is not entirely correct I believe, but it is
1690                            consistent.  Only apply the password policy settings
1691                            too warn users for our own domain.  Cannot obtain these
1692                            from trusted DCs all the  time so don't do it at all.
1693                            -- jerry */
1694
1695                         result = NT_STATUS_NOT_SUPPORTED;
1696                         if (our_domain == domain ) {
1697                                 result = fillup_password_policy(our_domain, state);
1698                         }
1699
1700                         if (!NT_STATUS_IS_OK(result)
1701                             && !NT_STATUS_EQUAL(result, NT_STATUS_NOT_SUPPORTED) )
1702                         {
1703                                 DEBUG(10,("Failed to get password policies for domain %s: %s\n",
1704                                           domain->name, nt_errstr(result)));
1705                                 goto done;
1706                         }
1707                 }
1708
1709                 result = NT_STATUS_OK;
1710         }
1711
1712 done:
1713         /* give us a more useful (more correct?) error code */
1714         if ((NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND) ||
1715             (NT_STATUS_EQUAL(result, NT_STATUS_UNSUCCESSFUL)))) {
1716                 result = NT_STATUS_NO_LOGON_SERVERS;
1717         }
1718
1719         set_auth_errors(state->response, result);
1720
1721         DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2, ("Plain-text authentication for user %s returned %s (PAM: %d)\n",
1722               state->request->data.auth.user,
1723               state->response->data.auth.nt_status_string,
1724               state->response->data.auth.pam_error));
1725
1726         return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
1727 }
1728
1729
1730 /**********************************************************************
1731  Challenge Response Authentication Protocol
1732 **********************************************************************/
1733
1734 void winbindd_pam_auth_crap(struct winbindd_cli_state *state)
1735 {
1736         struct winbindd_domain *domain = NULL;
1737         const char *domain_name = NULL;
1738         NTSTATUS result;
1739
1740         if (!check_request_flags(state->request->flags)) {
1741                 result = NT_STATUS_INVALID_PARAMETER_MIX;
1742                 goto done;
1743         }
1744
1745         if (!state->privileged) {
1746                 DEBUG(2, ("winbindd_pam_auth_crap: non-privileged access "
1747                           "denied.  !\n"));
1748                 DEBUGADD(2, ("winbindd_pam_auth_crap: Ensure permissions "
1749                              "on %s are set correctly.\n",
1750                              get_winbind_priv_pipe_dir()));
1751                 /* send a better message than ACCESS_DENIED */
1752                 fstr_sprintf(state->response->data.auth.error_string,
1753                              "winbind client not authorized to use "
1754                              "winbindd_pam_auth_crap. Ensure permissions on "
1755                              "%s are set correctly.",
1756                              get_winbind_priv_pipe_dir());
1757                 result = NT_STATUS_ACCESS_DENIED;
1758                 goto done;
1759         }
1760
1761         /* Ensure null termination */
1762         state->request->data.auth_crap.user
1763                 [sizeof(state->request->data.auth_crap.user)-1]=0;
1764         state->request->data.auth_crap.domain
1765                 [sizeof(state->request->data.auth_crap.domain)-1]=0;
1766
1767         DEBUG(3, ("[%5lu]: pam auth crap domain: [%s] user: %s\n",
1768                   (unsigned long)state->pid,
1769                   state->request->data.auth_crap.domain,
1770                   state->request->data.auth_crap.user));
1771
1772         if (*state->request->data.auth_crap.domain != '\0') {
1773                 domain_name = state->request->data.auth_crap.domain;
1774         } else if (lp_winbind_use_default_domain()) {
1775                 domain_name = lp_workgroup();
1776         }
1777
1778         if (domain_name != NULL)
1779                 domain = find_auth_domain(state->request->flags, domain_name);
1780
1781         if (domain != NULL) {
1782                 sendto_domain(state, domain);
1783                 return;
1784         }
1785
1786         result = NT_STATUS_NO_SUCH_USER;
1787
1788  done:
1789         set_auth_errors(state->response, result);
1790         DEBUG(5, ("CRAP authentication for %s\\%s returned %s (PAM: %d)\n",
1791                   state->request->data.auth_crap.domain,
1792                   state->request->data.auth_crap.user,
1793                   state->response->data.auth.nt_status_string,
1794                   state->response->data.auth.pam_error));
1795         request_error(state);
1796         return;
1797 }
1798
1799
1800 enum winbindd_result winbindd_dual_pam_auth_crap(struct winbindd_domain *domain,
1801                                                  struct winbindd_cli_state *state)
1802 {
1803         NTSTATUS result;
1804         struct netr_SamInfo3 *info3 = NULL;
1805         struct rpc_pipe_client *netlogon_pipe;
1806         const char *name_user = NULL;
1807         const char *name_domain = NULL;
1808         const char *workstation;
1809         struct winbindd_domain *contact_domain;
1810         int attempts = 0;
1811         bool retry;
1812
1813         DATA_BLOB lm_resp, nt_resp;
1814
1815         /* This is child-only, so no check for privileged access is needed
1816            anymore */
1817
1818         /* Ensure null termination */
1819         state->request->data.auth_crap.user[sizeof(state->request->data.auth_crap.user)-1]=0;
1820         state->request->data.auth_crap.domain[sizeof(state->request->data.auth_crap.domain)-1]=0;
1821
1822         if (!check_request_flags(state->request->flags)) {
1823                 result = NT_STATUS_INVALID_PARAMETER_MIX;
1824                 goto done;
1825         }
1826
1827         name_user = state->request->data.auth_crap.user;
1828
1829         if (*state->request->data.auth_crap.domain) {
1830                 name_domain = state->request->data.auth_crap.domain;
1831         } else if (lp_winbind_use_default_domain()) {
1832                 name_domain = lp_workgroup();
1833         } else {
1834                 DEBUG(5,("no domain specified with username (%s) - failing auth\n",
1835                          name_user));
1836                 result = NT_STATUS_NO_SUCH_USER;
1837                 goto done;
1838         }
1839
1840         DEBUG(3, ("[%5lu]: pam auth crap domain: %s user: %s\n", (unsigned long)state->pid,
1841                   name_domain, name_user));
1842
1843         if (*state->request->data.auth_crap.workstation) {
1844                 workstation = state->request->data.auth_crap.workstation;
1845         } else {
1846                 workstation = global_myname();
1847         }
1848
1849         if (state->request->data.auth_crap.lm_resp_len > sizeof(state->request->data.auth_crap.lm_resp)
1850                 || state->request->data.auth_crap.nt_resp_len > sizeof(state->request->data.auth_crap.nt_resp)) {
1851                 if (!(state->request->flags & WBFLAG_BIG_NTLMV2_BLOB) ||
1852                      state->request->extra_len != state->request->data.auth_crap.nt_resp_len) {
1853                         DEBUG(0, ("winbindd_pam_auth_crap: invalid password length %u/%u\n",
1854                                   state->request->data.auth_crap.lm_resp_len,
1855                                   state->request->data.auth_crap.nt_resp_len));
1856                         result = NT_STATUS_INVALID_PARAMETER;
1857                         goto done;
1858                 }
1859         }
1860
1861         lm_resp = data_blob_talloc(state->mem_ctx, state->request->data.auth_crap.lm_resp,
1862                                         state->request->data.auth_crap.lm_resp_len);
1863
1864         if (state->request->flags & WBFLAG_BIG_NTLMV2_BLOB) {
1865                 nt_resp = data_blob_talloc(state->mem_ctx,
1866                                            state->request->extra_data.data,
1867                                            state->request->data.auth_crap.nt_resp_len);
1868         } else {
1869                 nt_resp = data_blob_talloc(state->mem_ctx,
1870                                            state->request->data.auth_crap.nt_resp,
1871                                            state->request->data.auth_crap.nt_resp_len);
1872         }
1873
1874         /* what domain should we contact? */
1875
1876         if ( IS_DC ) {
1877                 if (!(contact_domain = find_domain_from_name(name_domain))) {
1878                         DEBUG(3, ("Authentication for domain for [%s] -> [%s]\\[%s] failed as %s is not a trusted domain\n",
1879                                   state->request->data.auth_crap.user, name_domain, name_user, name_domain));
1880                         result = NT_STATUS_NO_SUCH_USER;
1881                         goto done;
1882                 }
1883         } else {
1884                 if (is_myname(name_domain)) {
1885                         DEBUG(3, ("Authentication for domain %s (local domain to this server) not supported at this stage\n", name_domain));
1886                         result =  NT_STATUS_NO_SUCH_USER;
1887                         goto done;
1888                 }
1889                 contact_domain = find_our_domain();
1890         }
1891
1892         do {
1893                 netlogon_fn_t logon_fn;
1894
1895                 retry = false;
1896
1897                 netlogon_pipe = NULL;
1898                 result = cm_connect_netlogon(contact_domain, &netlogon_pipe);
1899
1900                 if (!NT_STATUS_IS_OK(result)) {
1901                         DEBUG(3, ("could not open handle to NETLOGON pipe (error: %s)\n",
1902                                   nt_errstr(result)));
1903                         goto done;
1904                 }
1905
1906                 logon_fn = contact_domain->can_do_samlogon_ex
1907                         ? rpccli_netlogon_sam_network_logon_ex
1908                         : rpccli_netlogon_sam_network_logon;
1909
1910                 result = logon_fn(netlogon_pipe,
1911                                   state->mem_ctx,
1912                                   state->request->data.auth_crap.logon_parameters,
1913                                   contact_domain->dcname,
1914                                   name_user,
1915                                   name_domain,
1916                                   /* Bug #3248 - found by Stefan Burkei. */
1917                                   workstation, /* We carefully set this above so use it... */
1918                                   state->request->data.auth_crap.chal,
1919                                   lm_resp,
1920                                   nt_resp,
1921                                   &info3);
1922
1923                 if ((NT_STATUS_V(result) == DCERPC_FAULT_OP_RNG_ERROR)
1924                     && contact_domain->can_do_samlogon_ex) {
1925                         DEBUG(3, ("Got a DC that can not do NetSamLogonEx, "
1926                                   "retrying with NetSamLogon\n"));
1927                         contact_domain->can_do_samlogon_ex = false;
1928                         retry = true;
1929                         continue;
1930                 }
1931
1932                 attempts += 1;
1933
1934                 /* We have to try a second time as cm_connect_netlogon
1935                    might not yet have noticed that the DC has killed
1936                    our connection. */
1937
1938                 if (NT_STATUS_EQUAL(result, NT_STATUS_UNSUCCESSFUL)) {
1939                         retry = true;
1940                         continue;
1941                 }
1942
1943                 /* if we get access denied, a possible cause was that we had and open
1944                    connection to the DC, but someone changed our machine account password
1945                    out from underneath us using 'net rpc changetrustpw' */
1946
1947                 if ( NT_STATUS_EQUAL(result, NT_STATUS_ACCESS_DENIED) ) {
1948                         DEBUG(3,("winbindd_pam_auth: sam_logon returned "
1949                                  "ACCESS_DENIED.  Maybe the trust account "
1950                                 "password was changed and we didn't know it. "
1951                                  "Killing connections to domain %s\n",
1952                                 name_domain));
1953                         invalidate_cm_connection(&contact_domain->conn);
1954                         retry = true;
1955                 }
1956
1957         } while ( (attempts < 2) && retry );
1958
1959         if (NT_STATUS_IS_OK(result)) {
1960
1961                 wcache_invalidate_samlogon(find_domain_from_name(name_domain), info3);
1962                 netsamlogon_cache_store(name_user, info3);
1963
1964                 /* Check if the user is in the right group */
1965
1966                 result = check_info3_in_group(
1967                         info3,
1968                         state->request->data.auth_crap.require_membership_of_sid);
1969                 if (!NT_STATUS_IS_OK(result)) {
1970                         DEBUG(3, ("User %s is not in the required group (%s), so "
1971                                   "crap authentication is rejected\n",
1972                                   state->request->data.auth_crap.user,
1973                                   state->request->data.auth_crap.require_membership_of_sid));
1974                         goto done;
1975                 }
1976
1977                 result = append_auth_data(state, info3, name_domain,
1978                                           name_user);
1979                 if (!NT_STATUS_IS_OK(result)) {
1980                         goto done;
1981                 }
1982         }
1983
1984 done:
1985
1986         /* give us a more useful (more correct?) error code */
1987         if ((NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND) ||
1988             (NT_STATUS_EQUAL(result, NT_STATUS_UNSUCCESSFUL)))) {
1989                 result = NT_STATUS_NO_LOGON_SERVERS;
1990         }
1991
1992         if (state->request->flags & WBFLAG_PAM_NT_STATUS_SQUASH) {
1993                 result = nt_status_squash(result);
1994         }
1995
1996         set_auth_errors(state->response, result);
1997
1998         DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2,
1999               ("NTLM CRAP authentication for user [%s]\\[%s] returned %s (PAM: %d)\n",
2000                name_domain,
2001                name_user,
2002                state->response->data.auth.nt_status_string,
2003                state->response->data.auth.pam_error));
2004
2005         return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
2006 }
2007
2008 /* Change a user password */
2009
2010 void winbindd_pam_chauthtok(struct winbindd_cli_state *state)
2011 {
2012         fstring domain, user;
2013         char *mapped_user;
2014         struct winbindd_domain *contact_domain;
2015         NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
2016
2017         DEBUG(3, ("[%5lu]: pam chauthtok %s\n", (unsigned long)state->pid,
2018                 state->request->data.chauthtok.user));
2019
2020         /* Setup crap */
2021
2022         nt_status = normalize_name_unmap(state->mem_ctx,
2023                                          state->request->data.chauthtok.user,
2024                                          &mapped_user);
2025
2026         /* Update the chauthtok name if we did any mapping */
2027
2028         if (NT_STATUS_IS_OK(nt_status) ||
2029             NT_STATUS_EQUAL(nt_status, NT_STATUS_FILE_RENAMED))
2030         {
2031                 fstrcpy(state->request->data.chauthtok.user, mapped_user);
2032         }
2033
2034         /* Must pass in state->...chauthtok.user because
2035            canonicalize_username() assumes an fstring().  Since
2036            we have already copied it (if necessary), this is ok. */
2037
2038         if (!canonicalize_username(state->request->data.chauthtok.user, domain, user)) {
2039                 set_auth_errors(state->response, NT_STATUS_NO_SUCH_USER);
2040                 DEBUG(5, ("winbindd_pam_chauthtok: canonicalize_username %s failed with %s"
2041                           "(PAM: %d)\n",
2042                           state->request->data.auth.user,
2043                           state->response->data.auth.nt_status_string,
2044                           state->response->data.auth.pam_error));
2045                 request_error(state);
2046                 return;
2047         }
2048
2049         contact_domain = find_domain_from_name(domain);
2050         if (!contact_domain) {
2051                 set_auth_errors(state->response, NT_STATUS_NO_SUCH_USER);
2052                 DEBUG(3, ("Cannot change password for [%s] -> [%s]\\[%s] as %s is not a trusted domain\n",
2053                           state->request->data.chauthtok.user, domain, user, domain));
2054                 request_error(state);
2055                 return;
2056         }
2057
2058         sendto_domain(state, contact_domain);
2059 }
2060
2061 enum winbindd_result winbindd_dual_pam_chauthtok(struct winbindd_domain *contact_domain,
2062                                                  struct winbindd_cli_state *state)
2063 {
2064         char *oldpass;
2065         char *newpass = NULL;
2066         struct policy_handle dom_pol;
2067         struct rpc_pipe_client *cli;
2068         bool got_info = false;
2069         struct samr_DomInfo1 *info = NULL;
2070         struct userPwdChangeFailureInformation *reject = NULL;
2071         NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
2072         fstring domain, user;
2073
2074         DEBUG(3, ("[%5lu]: dual pam chauthtok %s\n", (unsigned long)state->pid,
2075                   state->request->data.auth.user));
2076
2077         if (!parse_domain_user(state->request->data.chauthtok.user, domain, user)) {
2078                 goto done;
2079         }
2080
2081         /* Change password */
2082
2083         oldpass = state->request->data.chauthtok.oldpass;
2084         newpass = state->request->data.chauthtok.newpass;
2085
2086         /* Initialize reject reason */
2087         state->response->data.auth.reject_reason = Undefined;
2088
2089         /* Get sam handle */
2090
2091         result = cm_connect_sam(contact_domain, state->mem_ctx, &cli,
2092                                 &dom_pol);
2093         if (!NT_STATUS_IS_OK(result)) {
2094                 DEBUG(1, ("could not get SAM handle on DC for %s\n", domain));
2095                 goto done;
2096         }
2097
2098         result = rpccli_samr_chgpasswd_user3(cli, state->mem_ctx,
2099                                              user,
2100                                              newpass,
2101                                              oldpass,
2102                                              &info,
2103                                              &reject);
2104
2105         /* Windows 2003 returns NT_STATUS_PASSWORD_RESTRICTION */
2106
2107         if (NT_STATUS_EQUAL(result, NT_STATUS_PASSWORD_RESTRICTION) ) {
2108
2109                 fill_in_password_policy(state->response, info);
2110
2111                 state->response->data.auth.reject_reason =
2112                         reject->extendedFailureReason;
2113
2114                 got_info = true;
2115         }
2116
2117         /* atm the pidl generated rpccli_samr_ChangePasswordUser3 function will
2118          * return with NT_STATUS_BUFFER_TOO_SMALL for w2k dcs as w2k just
2119          * returns with 4byte error code (NT_STATUS_NOT_SUPPORTED) which is too
2120          * short to comply with the samr_ChangePasswordUser3 idl - gd */
2121
2122         /* only fallback when the chgpasswd_user3 call is not supported */
2123         if ((NT_STATUS_EQUAL(result, NT_STATUS(DCERPC_FAULT_OP_RNG_ERROR))) ||
2124                    (NT_STATUS_EQUAL(result, NT_STATUS_NOT_SUPPORTED)) ||
2125                    (NT_STATUS_EQUAL(result, NT_STATUS_BUFFER_TOO_SMALL)) ||
2126                    (NT_STATUS_EQUAL(result, NT_STATUS_NOT_IMPLEMENTED))) {
2127
2128                 DEBUG(10,("Password change with chgpasswd_user3 failed with: %s, retrying chgpasswd_user2\n",
2129                         nt_errstr(result)));
2130
2131                 result = rpccli_samr_chgpasswd_user2(cli, state->mem_ctx, user, newpass, oldpass);
2132
2133                 /* Windows 2000 returns NT_STATUS_ACCOUNT_RESTRICTION.
2134                    Map to the same status code as Windows 2003. */
2135
2136                 if ( NT_STATUS_EQUAL(NT_STATUS_ACCOUNT_RESTRICTION, result ) ) {
2137                         result = NT_STATUS_PASSWORD_RESTRICTION;
2138                 }
2139         }
2140
2141 done:
2142
2143         if (NT_STATUS_IS_OK(result) && (state->request->flags & WBFLAG_PAM_CACHED_LOGIN)) {
2144
2145                 /* Update the single sign-on memory creds. */
2146                 result = winbindd_replace_memory_creds(state->request->data.chauthtok.user,
2147                                                         newpass);
2148
2149                 /* When we login from gdm or xdm and password expires,
2150                  * we change password, but there are no memory crendentials
2151                  * So, winbindd_replace_memory_creds() returns
2152                  * NT_STATUS_OBJECT_NAME_NOT_FOUND. This is not a failure.
2153                  * --- BoYang
2154                  * */
2155                 if (NT_STATUS_EQUAL(result, NT_STATUS_OBJECT_NAME_NOT_FOUND)) {
2156                         result = NT_STATUS_OK;
2157                 }
2158
2159                 if (!NT_STATUS_IS_OK(result)) {
2160                         DEBUG(10,("Failed to replace memory creds: %s\n", nt_errstr(result)));
2161                         goto process_result;
2162                 }
2163
2164                 if (lp_winbind_offline_logon()) {
2165                         result = winbindd_update_creds_by_name(contact_domain,
2166                                                          state->mem_ctx, user,
2167                                                          newpass);
2168                         /* Again, this happens when we login from gdm or xdm
2169                          * and the password expires, *BUT* cached crendentials
2170                          * doesn't exist. winbindd_update_creds_by_name()
2171                          * returns NT_STATUS_NO_SUCH_USER.
2172                          * This is not a failure.
2173                          * --- BoYang
2174                          * */
2175                         if (NT_STATUS_EQUAL(result, NT_STATUS_NO_SUCH_USER)) {
2176                                 result = NT_STATUS_OK;
2177                         }
2178
2179                         if (!NT_STATUS_IS_OK(result)) {
2180                                 DEBUG(10,("Failed to store creds: %s\n", nt_errstr(result)));
2181                                 goto process_result;
2182                         }
2183                 }
2184         }
2185
2186         if (!NT_STATUS_IS_OK(result) && !got_info && contact_domain) {
2187
2188                 NTSTATUS policy_ret;
2189
2190                 policy_ret = fillup_password_policy(contact_domain, state);
2191
2192                 /* failure of this is non critical, it will just provide no
2193                  * additional information to the client why the change has
2194                  * failed - Guenther */
2195
2196                 if (!NT_STATUS_IS_OK(policy_ret)) {
2197                         DEBUG(10,("Failed to get password policies: %s\n", nt_errstr(policy_ret)));
2198                         goto process_result;
2199                 }
2200         }
2201
2202 process_result:
2203
2204         set_auth_errors(state->response, result);
2205
2206         DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2,
2207               ("Password change for user [%s]\\[%s] returned %s (PAM: %d)\n",
2208                domain,
2209                user,
2210                state->response->data.auth.nt_status_string,
2211                state->response->data.auth.pam_error));
2212
2213         return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
2214 }
2215
2216 void winbindd_pam_logoff(struct winbindd_cli_state *state)
2217 {
2218         struct winbindd_domain *domain;
2219         fstring name_domain, user;
2220         uid_t caller_uid = (uid_t)-1;
2221         uid_t request_uid = state->request->data.logoff.uid;
2222
2223         DEBUG(3, ("[%5lu]: pam logoff %s\n", (unsigned long)state->pid,
2224                 state->request->data.logoff.user));
2225
2226         /* Ensure null termination */
2227         state->request->data.logoff.user
2228                 [sizeof(state->request->data.logoff.user)-1]='\0';
2229
2230         state->request->data.logoff.krb5ccname
2231                 [sizeof(state->request->data.logoff.krb5ccname)-1]='\0';
2232
2233         if (request_uid == (gid_t)-1) {
2234                 goto failed;
2235         }
2236
2237         if (!canonicalize_username(state->request->data.logoff.user, name_domain, user)) {
2238                 goto failed;
2239         }
2240
2241         if ((domain = find_auth_domain(state->request->flags,
2242                                        name_domain)) == NULL) {
2243                 goto failed;
2244         }
2245
2246         if ((sys_getpeereid(state->sock, &caller_uid)) != 0) {
2247                 DEBUG(1,("winbindd_pam_logoff: failed to check peerid: %s\n",
2248                         strerror(errno)));
2249                 goto failed;
2250         }
2251
2252         switch (caller_uid) {
2253                 case -1:
2254                         goto failed;
2255                 case 0:
2256                         /* root must be able to logoff any user - gd */
2257                         state->request->data.logoff.uid = request_uid;
2258                         break;
2259                 default:
2260                         if (caller_uid != request_uid) {
2261                                 DEBUG(1,("winbindd_pam_logoff: caller requested invalid uid\n"));
2262                                 goto failed;
2263                         }
2264                         state->request->data.logoff.uid = caller_uid;
2265                         break;
2266         }
2267
2268         sendto_domain(state, domain);
2269         return;
2270
2271  failed:
2272         set_auth_errors(state->response, NT_STATUS_NO_SUCH_USER);
2273         DEBUG(5, ("Pam Logoff for %s returned %s "
2274                   "(PAM: %d)\n",
2275                   state->request->data.logoff.user,
2276                   state->response->data.auth.nt_status_string,
2277                   state->response->data.auth.pam_error));
2278         request_error(state);
2279         return;
2280 }
2281
2282 enum winbindd_result winbindd_dual_pam_logoff(struct winbindd_domain *domain,
2283                                               struct winbindd_cli_state *state)
2284 {
2285         NTSTATUS result = NT_STATUS_NOT_SUPPORTED;
2286
2287         DEBUG(3, ("[%5lu]: pam dual logoff %s\n", (unsigned long)state->pid,
2288                 state->request->data.logoff.user));
2289
2290         if (!(state->request->flags & WBFLAG_PAM_KRB5)) {
2291                 result = NT_STATUS_OK;
2292                 goto process_result;
2293         }
2294
2295         if (state->request->data.logoff.krb5ccname[0] == '\0') {
2296                 result = NT_STATUS_OK;
2297                 goto process_result;
2298         }
2299
2300 #ifdef HAVE_KRB5
2301
2302         if (state->request->data.logoff.uid < 0) {
2303                 DEBUG(0,("winbindd_pam_logoff: invalid uid\n"));
2304                 goto process_result;
2305         }
2306
2307         /* what we need here is to find the corresponding krb5 ccache name *we*
2308          * created for a given username and destroy it */
2309
2310         if (!ccache_entry_exists(state->request->data.logoff.user)) {
2311                 result = NT_STATUS_OK;
2312                 DEBUG(10,("winbindd_pam_logoff: no entry found.\n"));
2313                 goto process_result;
2314         }
2315
2316         if (!ccache_entry_identical(state->request->data.logoff.user,
2317                                         state->request->data.logoff.uid,
2318                                         state->request->data.logoff.krb5ccname)) {
2319                 DEBUG(0,("winbindd_pam_logoff: cached entry differs.\n"));
2320                 goto process_result;
2321         }
2322
2323         result = remove_ccache(state->request->data.logoff.user);
2324         if (!NT_STATUS_IS_OK(result)) {
2325                 DEBUG(0,("winbindd_pam_logoff: failed to remove ccache: %s\n",
2326                         nt_errstr(result)));
2327                 goto process_result;
2328         }
2329
2330 #else
2331         result = NT_STATUS_NOT_SUPPORTED;
2332 #endif
2333
2334 process_result:
2335
2336         winbindd_delete_memory_creds(state->request->data.logoff.user);
2337
2338         set_auth_errors(state->response, result);
2339
2340         return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
2341 }
2342
2343 /* Change user password with auth crap*/
2344
2345 void winbindd_pam_chng_pswd_auth_crap(struct winbindd_cli_state *state)
2346 {
2347         struct winbindd_domain *domain = NULL;
2348         const char *domain_name = NULL;
2349
2350         /* Ensure null termination */
2351         state->request->data.chng_pswd_auth_crap.user[
2352                 sizeof(state->request->data.chng_pswd_auth_crap.user)-1]=0;
2353         state->request->data.chng_pswd_auth_crap.domain[
2354                 sizeof(state->request->data.chng_pswd_auth_crap.domain)-1]=0;
2355
2356         DEBUG(3, ("[%5lu]: pam change pswd auth crap domain: %s user: %s\n",
2357                   (unsigned long)state->pid,
2358                   state->request->data.chng_pswd_auth_crap.domain,
2359                   state->request->data.chng_pswd_auth_crap.user));
2360
2361         if (*state->request->data.chng_pswd_auth_crap.domain != '\0') {
2362                 domain_name = state->request->data.chng_pswd_auth_crap.domain;
2363         } else if (lp_winbind_use_default_domain()) {
2364                 domain_name = lp_workgroup();
2365         }
2366
2367         if (domain_name != NULL)
2368                 domain = find_domain_from_name(domain_name);
2369
2370         if (domain != NULL) {
2371                 DEBUG(7, ("[%5lu]: pam auth crap changing pswd in domain: "
2372                           "%s\n", (unsigned long)state->pid,domain->name));
2373                 sendto_domain(state, domain);
2374                 return;
2375         }
2376
2377         set_auth_errors(state->response, NT_STATUS_NO_SUCH_USER);
2378         DEBUG(5, ("CRAP change password  for %s\\%s returned %s (PAM: %d)\n",
2379                   state->request->data.chng_pswd_auth_crap.domain,
2380                   state->request->data.chng_pswd_auth_crap.user,
2381                   state->response->data.auth.nt_status_string,
2382                   state->response->data.auth.pam_error));
2383         request_error(state);
2384         return;
2385 }
2386
2387 enum winbindd_result winbindd_dual_pam_chng_pswd_auth_crap(struct winbindd_domain *domainSt, struct winbindd_cli_state *state)
2388 {
2389         NTSTATUS result;
2390         DATA_BLOB new_nt_password;
2391         DATA_BLOB old_nt_hash_enc;
2392         DATA_BLOB new_lm_password;
2393         DATA_BLOB old_lm_hash_enc;
2394         fstring  domain,user;
2395         struct policy_handle dom_pol;
2396         struct winbindd_domain *contact_domain = domainSt;
2397         struct rpc_pipe_client *cli;
2398
2399         /* Ensure null termination */
2400         state->request->data.chng_pswd_auth_crap.user[
2401                 sizeof(state->request->data.chng_pswd_auth_crap.user)-1]=0;
2402         state->request->data.chng_pswd_auth_crap.domain[
2403                 sizeof(state->request->data.chng_pswd_auth_crap.domain)-1]=0;
2404         *domain = 0;
2405         *user = 0;
2406
2407         DEBUG(3, ("[%5lu]: pam change pswd auth crap domain: %s user: %s\n",
2408                   (unsigned long)state->pid,
2409                   state->request->data.chng_pswd_auth_crap.domain,
2410                   state->request->data.chng_pswd_auth_crap.user));
2411
2412         if (lp_winbind_offline_logon()) {
2413                 DEBUG(0,("Refusing password change as winbind offline logons are enabled. "));
2414                 DEBUGADD(0,("Changing passwords here would risk inconsistent logons\n"));
2415                 result = NT_STATUS_ACCESS_DENIED;
2416                 goto done;
2417         }
2418
2419         if (*state->request->data.chng_pswd_auth_crap.domain) {
2420                 fstrcpy(domain,state->request->data.chng_pswd_auth_crap.domain);
2421         } else {
2422                 parse_domain_user(state->request->data.chng_pswd_auth_crap.user,
2423                                   domain, user);
2424
2425                 if(!*domain) {
2426                         DEBUG(3,("no domain specified with username (%s) - "
2427                                  "failing auth\n",
2428                                  state->request->data.chng_pswd_auth_crap.user));
2429                         result = NT_STATUS_NO_SUCH_USER;
2430                         goto done;
2431                 }
2432         }
2433
2434         if (!*domain && lp_winbind_use_default_domain()) {
2435                 fstrcpy(domain,(char *)lp_workgroup());
2436         }
2437
2438         if(!*user) {
2439                 fstrcpy(user, state->request->data.chng_pswd_auth_crap.user);
2440         }
2441
2442         DEBUG(3, ("[%5lu]: pam auth crap domain: %s user: %s\n",
2443                   (unsigned long)state->pid, domain, user));
2444
2445         /* Change password */
2446         new_nt_password = data_blob_talloc(
2447                 state->mem_ctx,
2448                 state->request->data.chng_pswd_auth_crap.new_nt_pswd,
2449                 state->request->data.chng_pswd_auth_crap.new_nt_pswd_len);
2450
2451         old_nt_hash_enc = data_blob_talloc(
2452                 state->mem_ctx,
2453                 state->request->data.chng_pswd_auth_crap.old_nt_hash_enc,
2454                 state->request->data.chng_pswd_auth_crap.old_nt_hash_enc_len);
2455
2456         if(state->request->data.chng_pswd_auth_crap.new_lm_pswd_len > 0)        {
2457                 new_lm_password = data_blob_talloc(
2458                         state->mem_ctx,
2459                         state->request->data.chng_pswd_auth_crap.new_lm_pswd,
2460                         state->request->data.chng_pswd_auth_crap.new_lm_pswd_len);
2461
2462                 old_lm_hash_enc = data_blob_talloc(
2463                         state->mem_ctx,
2464                         state->request->data.chng_pswd_auth_crap.old_lm_hash_enc,
2465                         state->request->data.chng_pswd_auth_crap.old_lm_hash_enc_len);
2466         } else {
2467                 new_lm_password.length = 0;
2468                 old_lm_hash_enc.length = 0;
2469         }
2470
2471         /* Get sam handle */
2472
2473         result = cm_connect_sam(contact_domain, state->mem_ctx, &cli, &dom_pol);
2474         if (!NT_STATUS_IS_OK(result)) {
2475                 DEBUG(1, ("could not get SAM handle on DC for %s\n", domain));
2476                 goto done;
2477         }
2478
2479         result = rpccli_samr_chng_pswd_auth_crap(
2480                 cli, state->mem_ctx, user, new_nt_password, old_nt_hash_enc,
2481                 new_lm_password, old_lm_hash_enc);
2482
2483  done:
2484
2485         set_auth_errors(state->response, result);
2486
2487         DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2,
2488               ("Password change for user [%s]\\[%s] returned %s (PAM: %d)\n",
2489                domain, user,
2490                state->response->data.auth.nt_status_string,
2491                state->response->data.auth.pam_error));
2492
2493         return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
2494 }