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