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