s3:auth Change winbindd -> auth interface to more standard structures
[samba.git] / source3 / winbindd / winbindd_pam.c
1 /*
2    Unix SMB/CIFS implementation.
3
4    Winbind daemon - pam auth funcions
5
6    Copyright (C) Andrew Tridgell 2000
7    Copyright (C) Tim Potter 2001
8    Copyright (C) Andrew Bartlett 2001-2002
9    Copyright (C) Guenther Deschner 2005
10
11    This program is free software; you can redistribute it and/or modify
12    it under the terms of the GNU General Public License as published by
13    the Free Software Foundation; either version 3 of the License, or
14    (at your option) any later version.
15
16    This program is distributed in the hope that it will be useful,
17    but WITHOUT ANY WARRANTY; without even the implied warranty of
18    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19    GNU General Public License for more details.
20
21    You should have received a copy of the GNU General Public License
22    along with this program.  If not, see <http://www.gnu.org/licenses/>.
23 */
24
25 #include "includes.h"
26 #include "winbindd.h"
27 #include "../libcli/auth/libcli_auth.h"
28 #include "../librpc/gen_ndr/cli_samr.h"
29 #include "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         NTSTATUS status;
1137
1138         status = make_user_info(&user_info, user, user, domain, domain,
1139                                 global_myname(), lm_resp, nt_resp, NULL, NULL,
1140                                 NULL, AUTH_PASSWORD_RESPONSE);
1141         if (!NT_STATUS_IS_OK(status)) {
1142                 DEBUG(10, ("make_user_info failed: %s\n", nt_errstr(status)));
1143                 return status;
1144         }
1145
1146         /* We don't want any more mapping of the username */
1147         user_info->mapped_state = True;
1148
1149         status = check_sam_security_info3(challenge, talloc_tos(), user_info,
1150                                           pinfo3);
1151         free_user_info(&user_info);
1152         DEBUG(10, ("Authenticated user %s\\%s successfully\n", domain, user));
1153         return NT_STATUS_OK;
1154 }
1155
1156 typedef NTSTATUS (*netlogon_fn_t)(struct rpc_pipe_client *cli,
1157                                   TALLOC_CTX *mem_ctx,
1158                                   uint32 logon_parameters,
1159                                   const char *server,
1160                                   const char *username,
1161                                   const char *domain,
1162                                   const char *workstation,
1163                                   const uint8 chal[8],
1164                                   DATA_BLOB lm_response,
1165                                   DATA_BLOB nt_response,
1166                                   struct netr_SamInfo3 **info3);
1167
1168 static NTSTATUS winbindd_dual_pam_auth_samlogon(struct winbindd_domain *domain,
1169                                                 struct winbindd_cli_state *state,
1170                                                 struct netr_SamInfo3 **info3)
1171 {
1172
1173         struct rpc_pipe_client *netlogon_pipe;
1174         uchar chal[8];
1175         DATA_BLOB lm_resp;
1176         DATA_BLOB nt_resp;
1177         int attempts = 0;
1178         unsigned char local_lm_response[24];
1179         unsigned char local_nt_response[24];
1180         fstring name_domain, name_user;
1181         bool retry;
1182         NTSTATUS result;
1183         struct netr_SamInfo3 *my_info3 = NULL;
1184
1185         *info3 = NULL;
1186
1187         DEBUG(10,("winbindd_dual_pam_auth_samlogon\n"));
1188
1189         /* Parse domain and username */
1190
1191         parse_domain_user(state->request->data.auth.user, name_domain, name_user);
1192
1193         /* do password magic */
1194
1195         generate_random_buffer(chal, sizeof(chal));
1196
1197         if (lp_client_ntlmv2_auth()) {
1198                 DATA_BLOB server_chal;
1199                 DATA_BLOB names_blob;
1200                 DATA_BLOB nt_response;
1201                 DATA_BLOB lm_response;
1202                 server_chal = data_blob_talloc(state->mem_ctx, chal, 8);
1203
1204                 /* note that the 'workgroup' here is a best guess - we don't know
1205                    the server's domain at this point.  The 'server name' is also
1206                    dodgy...
1207                 */
1208                 names_blob = NTLMv2_generate_names_blob(state->mem_ctx, global_myname(), lp_workgroup());
1209
1210                 if (!SMBNTLMv2encrypt(NULL, name_user, name_domain,
1211                                       state->request->data.auth.pass,
1212                                       &server_chal,
1213                                       &names_blob,
1214                                       &lm_response, &nt_response, NULL, NULL)) {
1215                         data_blob_free(&names_blob);
1216                         data_blob_free(&server_chal);
1217                         DEBUG(0, ("winbindd_pam_auth: SMBNTLMv2encrypt() failed!\n"));
1218                         result = NT_STATUS_NO_MEMORY;
1219                         goto done;
1220                 }
1221                 data_blob_free(&names_blob);
1222                 data_blob_free(&server_chal);
1223                 lm_resp = data_blob_talloc(state->mem_ctx, lm_response.data,
1224                                            lm_response.length);
1225                 nt_resp = data_blob_talloc(state->mem_ctx, nt_response.data,
1226                                            nt_response.length);
1227                 data_blob_free(&lm_response);
1228                 data_blob_free(&nt_response);
1229
1230         } else {
1231                 if (lp_client_lanman_auth()
1232                     && SMBencrypt(state->request->data.auth.pass,
1233                                   chal,
1234                                   local_lm_response)) {
1235                         lm_resp = data_blob_talloc(state->mem_ctx,
1236                                                    local_lm_response,
1237                                                    sizeof(local_lm_response));
1238                 } else {
1239                         lm_resp = data_blob_null;
1240                 }
1241                 SMBNTencrypt(state->request->data.auth.pass,
1242                              chal,
1243                              local_nt_response);
1244
1245                 nt_resp = data_blob_talloc(state->mem_ctx,
1246                                            local_nt_response,
1247                                            sizeof(local_nt_response));
1248         }
1249
1250         if (strequal(name_domain, get_global_sam_name())) {
1251                 DATA_BLOB chal_blob = data_blob_const(chal, sizeof(chal));
1252
1253                 result = winbindd_dual_auth_passdb(
1254                         state->mem_ctx, name_domain, name_user,
1255                         &chal_blob, &lm_resp, &nt_resp, info3);
1256                 goto done;
1257         }
1258
1259         /* check authentication loop */
1260
1261         do {
1262                 netlogon_fn_t logon_fn;
1263
1264                 ZERO_STRUCTP(my_info3);
1265                 retry = false;
1266
1267                 result = cm_connect_netlogon(domain, &netlogon_pipe);
1268
1269                 if (!NT_STATUS_IS_OK(result)) {
1270                         DEBUG(3, ("could not open handle to NETLOGON pipe\n"));
1271                         goto done;
1272                 }
1273
1274                 /* It is really important to try SamLogonEx here,
1275                  * because in a clustered environment, we want to use
1276                  * one machine account from multiple physical
1277                  * computers.
1278                  *
1279                  * With a normal SamLogon call, we must keep the
1280                  * credentials chain updated and intact between all
1281                  * users of the machine account (which would imply
1282                  * cross-node communication for every NTLM logon).
1283                  *
1284                  * (The credentials chain is not per NETLOGON pipe
1285                  * connection, but globally on the server/client pair
1286                  * by machine name).
1287                  *
1288                  * When using SamLogonEx, the credentials are not
1289                  * supplied, but the session key is implied by the
1290                  * wrapping SamLogon context.
1291                  *
1292                  *  -- abartlet 21 April 2008
1293                  */
1294
1295                 logon_fn = domain->can_do_samlogon_ex
1296                         ? rpccli_netlogon_sam_network_logon_ex
1297                         : rpccli_netlogon_sam_network_logon;
1298
1299                 result = logon_fn(netlogon_pipe,
1300                                   state->mem_ctx,
1301                                   0,
1302                                   domain->dcname,         /* server name */
1303                                   name_user,              /* user name */
1304                                   name_domain,            /* target domain */
1305                                   global_myname(),        /* workstation */
1306                                   chal,
1307                                   lm_resp,
1308                                   nt_resp,
1309                                   &my_info3);
1310                 attempts += 1;
1311
1312                 if ((NT_STATUS_V(result) == DCERPC_FAULT_OP_RNG_ERROR)
1313                     && domain->can_do_samlogon_ex) {
1314                         DEBUG(3, ("Got a DC that can not do NetSamLogonEx, "
1315                                   "retrying with NetSamLogon\n"));
1316                         domain->can_do_samlogon_ex = false;
1317                         retry = true;
1318                         continue;
1319                 }
1320
1321                 /* We have to try a second time as cm_connect_netlogon
1322                    might not yet have noticed that the DC has killed
1323                    our connection. */
1324
1325                 if (!rpccli_is_connected(netlogon_pipe)) {
1326                         retry = true;
1327                         continue;
1328                 }
1329
1330                 /* if we get access denied, a possible cause was that we had
1331                    and open connection to the DC, but someone changed our
1332                    machine account password out from underneath us using 'net
1333                    rpc changetrustpw' */
1334
1335                 if ( NT_STATUS_EQUAL(result, NT_STATUS_ACCESS_DENIED) ) {
1336                         DEBUG(3,("winbindd_pam_auth: sam_logon returned "
1337                                  "ACCESS_DENIED.  Maybe the trust account "
1338                                 "password was changed and we didn't know it. "
1339                                  "Killing connections to domain %s\n",
1340                                 name_domain));
1341                         invalidate_cm_connection(&domain->conn);
1342                         retry = true;
1343                 }
1344
1345         } while ( (attempts < 2) && retry );
1346
1347         /* handle the case where a NT4 DC does not fill in the acct_flags in
1348          * the samlogon reply info3. When accurate info3 is required by the
1349          * caller, we look up the account flags ourselve - gd */
1350
1351         if ((state->request->flags & WBFLAG_PAM_INFO3_TEXT) &&
1352             NT_STATUS_IS_OK(result) && (my_info3->base.acct_flags == 0)) {
1353
1354                 struct rpc_pipe_client *samr_pipe;
1355                 struct policy_handle samr_domain_handle, user_pol;
1356                 union samr_UserInfo *info = NULL;
1357                 NTSTATUS status_tmp;
1358                 uint32 acct_flags;
1359
1360                 status_tmp = cm_connect_sam(domain, state->mem_ctx,
1361                                             &samr_pipe, &samr_domain_handle);
1362
1363                 if (!NT_STATUS_IS_OK(status_tmp)) {
1364                         DEBUG(3, ("could not open handle to SAMR pipe: %s\n",
1365                                 nt_errstr(status_tmp)));
1366                         goto done;
1367                 }
1368
1369                 status_tmp = rpccli_samr_OpenUser(samr_pipe, state->mem_ctx,
1370                                                   &samr_domain_handle,
1371                                                   MAXIMUM_ALLOWED_ACCESS,
1372                                                   my_info3->base.rid,
1373                                                   &user_pol);
1374
1375                 if (!NT_STATUS_IS_OK(status_tmp)) {
1376                         DEBUG(3, ("could not open user handle on SAMR pipe: %s\n",
1377                                 nt_errstr(status_tmp)));
1378                         goto done;
1379                 }
1380
1381                 status_tmp = rpccli_samr_QueryUserInfo(samr_pipe, state->mem_ctx,
1382                                                        &user_pol,
1383                                                        16,
1384                                                        &info);
1385
1386                 if (!NT_STATUS_IS_OK(status_tmp)) {
1387                         DEBUG(3, ("could not query user info on SAMR pipe: %s\n",
1388                                 nt_errstr(status_tmp)));
1389                         rpccli_samr_Close(samr_pipe, state->mem_ctx, &user_pol);
1390                         goto done;
1391                 }
1392
1393                 acct_flags = info->info16.acct_flags;
1394
1395                 if (acct_flags == 0) {
1396                         rpccli_samr_Close(samr_pipe, state->mem_ctx, &user_pol);
1397                         goto done;
1398                 }
1399
1400                 my_info3->base.acct_flags = acct_flags;
1401
1402                 DEBUG(10,("successfully retrieved acct_flags 0x%x\n", acct_flags));
1403
1404                 rpccli_samr_Close(samr_pipe, state->mem_ctx, &user_pol);
1405         }
1406
1407         *info3 = my_info3;
1408 done:
1409         return result;
1410 }
1411
1412 enum winbindd_result winbindd_dual_pam_auth(struct winbindd_domain *domain,
1413                                             struct winbindd_cli_state *state)
1414 {
1415         NTSTATUS result = NT_STATUS_LOGON_FAILURE;
1416         NTSTATUS krb5_result = NT_STATUS_OK;
1417         fstring name_domain, name_user;
1418         char *mapped_user;
1419         fstring domain_user;
1420         struct netr_SamInfo3 *info3 = NULL;
1421         NTSTATUS name_map_status = NT_STATUS_UNSUCCESSFUL;
1422
1423         /* Ensure null termination */
1424         state->request->data.auth.user[sizeof(state->request->data.auth.user)-1]='\0';
1425
1426         /* Ensure null termination */
1427         state->request->data.auth.pass[sizeof(state->request->data.auth.pass)-1]='\0';
1428
1429         DEBUG(3, ("[%5lu]: dual pam auth %s\n", (unsigned long)state->pid,
1430                   state->request->data.auth.user));
1431
1432         if (!check_request_flags(state->request->flags)) {
1433                 result = NT_STATUS_INVALID_PARAMETER_MIX;
1434                 goto done;
1435         }
1436
1437         /* Parse domain and username */
1438
1439         name_map_status = normalize_name_unmap(state->mem_ctx,
1440                                                state->request->data.auth.user,
1441                                                &mapped_user);
1442
1443         /* If the name normalization didnt' actually do anything,
1444            just use the original name */
1445
1446         if (!NT_STATUS_IS_OK(name_map_status) &&
1447             !NT_STATUS_EQUAL(name_map_status, NT_STATUS_FILE_RENAMED))
1448         {
1449                 mapped_user = state->request->data.auth.user;
1450         }
1451
1452         parse_domain_user(mapped_user, name_domain, name_user);
1453
1454         if ( mapped_user != state->request->data.auth.user ) {
1455                 fstr_sprintf( domain_user, "%s%c%s", name_domain,
1456                         *lp_winbind_separator(),
1457                         name_user );
1458                 safe_strcpy( state->request->data.auth.user, domain_user,
1459                              sizeof(state->request->data.auth.user)-1 );
1460         }
1461
1462         if (domain->online == false) {
1463                 result = NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND;
1464                 if (domain->startup) {
1465                         /* Logons are very important to users. If we're offline and
1466                            we get a request within the first 30 seconds of startup,
1467                            try very hard to find a DC and go online. */
1468
1469                         DEBUG(10,("winbindd_dual_pam_auth: domain: %s offline and auth "
1470                                 "request in startup mode.\n", domain->name ));
1471
1472                         winbindd_flush_negative_conn_cache(domain);
1473                         result = init_dc_connection(domain);
1474                 }
1475         }
1476
1477         DEBUG(10,("winbindd_dual_pam_auth: domain: %s last was %s\n", domain->name, domain->online ? "online":"offline"));
1478
1479         /* Check for Kerberos authentication */
1480         if (domain->online && (state->request->flags & WBFLAG_PAM_KRB5)) {
1481
1482                 result = winbindd_dual_pam_auth_kerberos(domain, state, &info3);
1483                 /* save for later */
1484                 krb5_result = result;
1485
1486
1487                 if (NT_STATUS_IS_OK(result)) {
1488                         DEBUG(10,("winbindd_dual_pam_auth_kerberos succeeded\n"));
1489                         goto process_result;
1490                 } else {
1491                         DEBUG(10,("winbindd_dual_pam_auth_kerberos failed: %s\n", nt_errstr(result)));
1492                 }
1493
1494                 if (NT_STATUS_EQUAL(result, NT_STATUS_NO_LOGON_SERVERS) ||
1495                     NT_STATUS_EQUAL(result, NT_STATUS_IO_TIMEOUT) ||
1496                     NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
1497                         DEBUG(10,("winbindd_dual_pam_auth_kerberos setting domain to offline\n"));
1498                         set_domain_offline( domain );
1499                         goto cached_logon;
1500                 }
1501
1502                 /* there are quite some NT_STATUS errors where there is no
1503                  * point in retrying with a samlogon, we explictly have to take
1504                  * care not to increase the bad logon counter on the DC */
1505
1506                 if (NT_STATUS_EQUAL(result, NT_STATUS_ACCOUNT_DISABLED) ||
1507                     NT_STATUS_EQUAL(result, NT_STATUS_ACCOUNT_EXPIRED) ||
1508                     NT_STATUS_EQUAL(result, NT_STATUS_ACCOUNT_LOCKED_OUT) ||
1509                     NT_STATUS_EQUAL(result, NT_STATUS_INVALID_LOGON_HOURS) ||
1510                     NT_STATUS_EQUAL(result, NT_STATUS_INVALID_WORKSTATION) ||
1511                     NT_STATUS_EQUAL(result, NT_STATUS_LOGON_FAILURE) ||
1512                     NT_STATUS_EQUAL(result, NT_STATUS_NO_SUCH_USER) ||
1513                     NT_STATUS_EQUAL(result, NT_STATUS_PASSWORD_EXPIRED) ||
1514                     NT_STATUS_EQUAL(result, NT_STATUS_PASSWORD_MUST_CHANGE) ||
1515                     NT_STATUS_EQUAL(result, NT_STATUS_WRONG_PASSWORD)) {
1516                         goto done;
1517                 }
1518
1519                 if (state->request->flags & WBFLAG_PAM_FALLBACK_AFTER_KRB5) {
1520                         DEBUG(3,("falling back to samlogon\n"));
1521                         goto sam_logon;
1522                 } else {
1523                         goto cached_logon;
1524                 }
1525         }
1526
1527 sam_logon:
1528         /* Check for Samlogon authentication */
1529         if (domain->online) {
1530                 result = winbindd_dual_pam_auth_samlogon(domain, state, &info3);
1531
1532                 if (NT_STATUS_IS_OK(result)) {
1533                         DEBUG(10,("winbindd_dual_pam_auth_samlogon succeeded\n"));
1534                         /* add the Krb5 err if we have one */
1535                         if ( NT_STATUS_EQUAL(krb5_result, NT_STATUS_TIME_DIFFERENCE_AT_DC ) ) {
1536                                 info3->base.user_flags |= LOGON_KRB5_FAIL_CLOCK_SKEW;
1537                         }
1538                         goto process_result;
1539                 }
1540
1541                 DEBUG(10,("winbindd_dual_pam_auth_samlogon failed: %s\n",
1542                           nt_errstr(result)));
1543
1544                 if (NT_STATUS_EQUAL(result, NT_STATUS_NO_LOGON_SERVERS) ||
1545                     NT_STATUS_EQUAL(result, NT_STATUS_IO_TIMEOUT) ||
1546                     NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND))
1547                 {
1548                         DEBUG(10,("winbindd_dual_pam_auth_samlogon setting domain to offline\n"));
1549                         set_domain_offline( domain );
1550                         goto cached_logon;
1551                 }
1552
1553                         if (domain->online) {
1554                                 /* We're still online - fail. */
1555                                 goto done;
1556                         }
1557         }
1558
1559 cached_logon:
1560         /* Check for Cached logons */
1561         if (!domain->online && (state->request->flags & WBFLAG_PAM_CACHED_LOGIN) &&
1562             lp_winbind_offline_logon()) {
1563
1564                 result = winbindd_dual_pam_auth_cached(domain, state, &info3);
1565
1566                 if (NT_STATUS_IS_OK(result)) {
1567                         DEBUG(10,("winbindd_dual_pam_auth_cached succeeded\n"));
1568                         goto process_result;
1569                 } else {
1570                         DEBUG(10,("winbindd_dual_pam_auth_cached failed: %s\n", nt_errstr(result)));
1571                         goto done;
1572                 }
1573         }
1574
1575 process_result:
1576
1577         if (NT_STATUS_IS_OK(result)) {
1578
1579                 struct dom_sid user_sid;
1580
1581                 /* In all codepaths where result == NT_STATUS_OK info3 must have
1582                    been initialized. */
1583                 if (!info3) {
1584                         result = NT_STATUS_INTERNAL_ERROR;
1585                         goto done;
1586                 }
1587
1588                 wcache_invalidate_samlogon(find_domain_from_name(name_domain), info3);
1589                 netsamlogon_cache_store(name_user, info3);
1590
1591                 /* save name_to_sid info as early as possible (only if
1592                    this is our primary domain so we don't invalidate
1593                    the cache entry by storing the seq_num for the wrong
1594                    domain). */
1595                 if ( domain->primary ) {
1596                         sid_compose(&user_sid, info3->base.domain_sid,
1597                                     info3->base.rid);
1598                         cache_name2sid(domain, name_domain, name_user,
1599                                        SID_NAME_USER, &user_sid);
1600                 }
1601
1602                 /* Check if the user is in the right group */
1603
1604                 result = check_info3_in_group(
1605                         info3,
1606                         state->request->data.auth.require_membership_of_sid);
1607                 if (!NT_STATUS_IS_OK(result)) {
1608                         DEBUG(3, ("User %s is not in the required group (%s), so plaintext authentication is rejected\n",
1609                                   state->request->data.auth.user,
1610                                   state->request->data.auth.require_membership_of_sid));
1611                         goto done;
1612                 }
1613
1614                 result = append_auth_data(state, info3, name_domain,
1615                                           name_user);
1616                 if (!NT_STATUS_IS_OK(result)) {
1617                         goto done;
1618                 }
1619
1620                 if ((state->request->flags & WBFLAG_PAM_CACHED_LOGIN)) {
1621
1622                         if (lp_winbind_offline_logon()) {
1623                                 result = winbindd_store_creds(domain,
1624                                                       state->mem_ctx,
1625                                                       state->request->data.auth.user,
1626                                                       state->request->data.auth.pass,
1627                                                       info3, NULL);
1628                         }
1629                 }
1630
1631
1632                 if (state->request->flags & WBFLAG_PAM_GET_PWD_POLICY) {
1633                         struct winbindd_domain *our_domain = find_our_domain();
1634
1635                         /* This is not entirely correct I believe, but it is
1636                            consistent.  Only apply the password policy settings
1637                            too warn users for our own domain.  Cannot obtain these
1638                            from trusted DCs all the  time so don't do it at all.
1639                            -- jerry */
1640
1641                         result = NT_STATUS_NOT_SUPPORTED;
1642                         if (our_domain == domain ) {
1643                                 result = fillup_password_policy(our_domain, state);
1644                         }
1645
1646                         if (!NT_STATUS_IS_OK(result)
1647                             && !NT_STATUS_EQUAL(result, NT_STATUS_NOT_SUPPORTED) )
1648                         {
1649                                 DEBUG(10,("Failed to get password policies for domain %s: %s\n",
1650                                           domain->name, nt_errstr(result)));
1651                                 goto done;
1652                         }
1653                 }
1654
1655                 result = NT_STATUS_OK;
1656         }
1657
1658 done:
1659         /* give us a more useful (more correct?) error code */
1660         if ((NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND) ||
1661             (NT_STATUS_EQUAL(result, NT_STATUS_UNSUCCESSFUL)))) {
1662                 result = NT_STATUS_NO_LOGON_SERVERS;
1663         }
1664
1665         set_auth_errors(state->response, result);
1666
1667         DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2, ("Plain-text authentication for user %s returned %s (PAM: %d)\n",
1668               state->request->data.auth.user,
1669               state->response->data.auth.nt_status_string,
1670               state->response->data.auth.pam_error));
1671
1672         return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
1673 }
1674
1675 enum winbindd_result winbindd_dual_pam_auth_crap(struct winbindd_domain *domain,
1676                                                  struct winbindd_cli_state *state)
1677 {
1678         NTSTATUS result;
1679         struct netr_SamInfo3 *info3 = NULL;
1680         struct rpc_pipe_client *netlogon_pipe;
1681         const char *name_user = NULL;
1682         const char *name_domain = NULL;
1683         const char *workstation;
1684         int attempts = 0;
1685         bool retry;
1686
1687         DATA_BLOB lm_resp, nt_resp;
1688
1689         /* This is child-only, so no check for privileged access is needed
1690            anymore */
1691
1692         /* Ensure null termination */
1693         state->request->data.auth_crap.user[sizeof(state->request->data.auth_crap.user)-1]=0;
1694         state->request->data.auth_crap.domain[sizeof(state->request->data.auth_crap.domain)-1]=0;
1695
1696         if (!check_request_flags(state->request->flags)) {
1697                 result = NT_STATUS_INVALID_PARAMETER_MIX;
1698                 goto done;
1699         }
1700
1701         name_user = state->request->data.auth_crap.user;
1702
1703         if (*state->request->data.auth_crap.domain) {
1704                 name_domain = state->request->data.auth_crap.domain;
1705         } else if (lp_winbind_use_default_domain()) {
1706                 name_domain = lp_workgroup();
1707         } else {
1708                 DEBUG(5,("no domain specified with username (%s) - failing auth\n",
1709                          name_user));
1710                 result = NT_STATUS_NO_SUCH_USER;
1711                 goto done;
1712         }
1713
1714         DEBUG(3, ("[%5lu]: pam auth crap domain: %s user: %s\n", (unsigned long)state->pid,
1715                   name_domain, name_user));
1716
1717         if (*state->request->data.auth_crap.workstation) {
1718                 workstation = state->request->data.auth_crap.workstation;
1719         } else {
1720                 workstation = global_myname();
1721         }
1722
1723         if (state->request->data.auth_crap.lm_resp_len > sizeof(state->request->data.auth_crap.lm_resp)
1724                 || state->request->data.auth_crap.nt_resp_len > sizeof(state->request->data.auth_crap.nt_resp)) {
1725                 if (!(state->request->flags & WBFLAG_BIG_NTLMV2_BLOB) ||
1726                      state->request->extra_len != state->request->data.auth_crap.nt_resp_len) {
1727                         DEBUG(0, ("winbindd_pam_auth_crap: invalid password length %u/%u\n",
1728                                   state->request->data.auth_crap.lm_resp_len,
1729                                   state->request->data.auth_crap.nt_resp_len));
1730                         result = NT_STATUS_INVALID_PARAMETER;
1731                         goto done;
1732                 }
1733         }
1734
1735         lm_resp = data_blob_talloc(state->mem_ctx, state->request->data.auth_crap.lm_resp,
1736                                         state->request->data.auth_crap.lm_resp_len);
1737
1738         if (state->request->flags & WBFLAG_BIG_NTLMV2_BLOB) {
1739                 nt_resp = data_blob_talloc(state->mem_ctx,
1740                                            state->request->extra_data.data,
1741                                            state->request->data.auth_crap.nt_resp_len);
1742         } else {
1743                 nt_resp = data_blob_talloc(state->mem_ctx,
1744                                            state->request->data.auth_crap.nt_resp,
1745                                            state->request->data.auth_crap.nt_resp_len);
1746         }
1747
1748         if (strequal(name_domain, get_global_sam_name())) {
1749                 DATA_BLOB chal_blob = data_blob_const(
1750                         state->request->data.auth_crap.chal,
1751                         sizeof(state->request->data.auth_crap.chal));
1752
1753                 result = winbindd_dual_auth_passdb(
1754                         state->mem_ctx, name_domain, name_user,
1755                         &chal_blob, &lm_resp, &nt_resp, &info3);
1756                 goto process_result;
1757         }
1758
1759         do {
1760                 netlogon_fn_t logon_fn;
1761
1762                 retry = false;
1763
1764                 netlogon_pipe = NULL;
1765                 result = cm_connect_netlogon(domain, &netlogon_pipe);
1766
1767                 if (!NT_STATUS_IS_OK(result)) {
1768                         DEBUG(3, ("could not open handle to NETLOGON pipe (error: %s)\n",
1769                                   nt_errstr(result)));
1770                         goto done;
1771                 }
1772
1773                 logon_fn = domain->can_do_samlogon_ex
1774                         ? rpccli_netlogon_sam_network_logon_ex
1775                         : rpccli_netlogon_sam_network_logon;
1776
1777                 result = logon_fn(netlogon_pipe,
1778                                   state->mem_ctx,
1779                                   state->request->data.auth_crap.logon_parameters,
1780                                   domain->dcname,
1781                                   name_user,
1782                                   name_domain,
1783                                   /* Bug #3248 - found by Stefan Burkei. */
1784                                   workstation, /* We carefully set this above so use it... */
1785                                   state->request->data.auth_crap.chal,
1786                                   lm_resp,
1787                                   nt_resp,
1788                                   &info3);
1789
1790                 if ((NT_STATUS_V(result) == DCERPC_FAULT_OP_RNG_ERROR)
1791                     && domain->can_do_samlogon_ex) {
1792                         DEBUG(3, ("Got a DC that can not do NetSamLogonEx, "
1793                                   "retrying with NetSamLogon\n"));
1794                         domain->can_do_samlogon_ex = false;
1795                         retry = true;
1796                         continue;
1797                 }
1798
1799                 attempts += 1;
1800
1801                 /* We have to try a second time as cm_connect_netlogon
1802                    might not yet have noticed that the DC has killed
1803                    our connection. */
1804
1805                 if (!rpccli_is_connected(netlogon_pipe)) {
1806                         retry = true;
1807                         continue;
1808                 }
1809
1810                 /* if we get access denied, a possible cause was that we had and open
1811                    connection to the DC, but someone changed our machine account password
1812                    out from underneath us using 'net rpc changetrustpw' */
1813
1814                 if ( NT_STATUS_EQUAL(result, NT_STATUS_ACCESS_DENIED) ) {
1815                         DEBUG(3,("winbindd_pam_auth: sam_logon returned "
1816                                  "ACCESS_DENIED.  Maybe the trust account "
1817                                 "password was changed and we didn't know it. "
1818                                  "Killing connections to domain %s\n",
1819                                 name_domain));
1820                         invalidate_cm_connection(&domain->conn);
1821                         retry = true;
1822                 }
1823
1824         } while ( (attempts < 2) && retry );
1825
1826 process_result:
1827
1828         if (NT_STATUS_IS_OK(result)) {
1829
1830                 wcache_invalidate_samlogon(find_domain_from_name(name_domain), info3);
1831                 netsamlogon_cache_store(name_user, info3);
1832
1833                 /* Check if the user is in the right group */
1834
1835                 result = check_info3_in_group(
1836                         info3,
1837                         state->request->data.auth_crap.require_membership_of_sid);
1838                 if (!NT_STATUS_IS_OK(result)) {
1839                         DEBUG(3, ("User %s is not in the required group (%s), so "
1840                                   "crap authentication is rejected\n",
1841                                   state->request->data.auth_crap.user,
1842                                   state->request->data.auth_crap.require_membership_of_sid));
1843                         goto done;
1844                 }
1845
1846                 result = append_auth_data(state, info3, name_domain,
1847                                           name_user);
1848                 if (!NT_STATUS_IS_OK(result)) {
1849                         goto done;
1850                 }
1851         }
1852
1853 done:
1854
1855         /* give us a more useful (more correct?) error code */
1856         if ((NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND) ||
1857             (NT_STATUS_EQUAL(result, NT_STATUS_UNSUCCESSFUL)))) {
1858                 result = NT_STATUS_NO_LOGON_SERVERS;
1859         }
1860
1861         if (state->request->flags & WBFLAG_PAM_NT_STATUS_SQUASH) {
1862                 result = nt_status_squash(result);
1863         }
1864
1865         set_auth_errors(state->response, result);
1866
1867         DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2,
1868               ("NTLM CRAP authentication for user [%s]\\[%s] returned %s (PAM: %d)\n",
1869                name_domain,
1870                name_user,
1871                state->response->data.auth.nt_status_string,
1872                state->response->data.auth.pam_error));
1873
1874         return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
1875 }
1876
1877 enum winbindd_result winbindd_dual_pam_chauthtok(struct winbindd_domain *contact_domain,
1878                                                  struct winbindd_cli_state *state)
1879 {
1880         char *oldpass;
1881         char *newpass = NULL;
1882         struct policy_handle dom_pol;
1883         struct rpc_pipe_client *cli = NULL;
1884         bool got_info = false;
1885         struct samr_DomInfo1 *info = NULL;
1886         struct userPwdChangeFailureInformation *reject = NULL;
1887         NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
1888         fstring domain, user;
1889
1890         ZERO_STRUCT(dom_pol);
1891
1892         DEBUG(3, ("[%5lu]: dual pam chauthtok %s\n", (unsigned long)state->pid,
1893                   state->request->data.auth.user));
1894
1895         if (!parse_domain_user(state->request->data.chauthtok.user, domain, user)) {
1896                 goto done;
1897         }
1898
1899         /* Change password */
1900
1901         oldpass = state->request->data.chauthtok.oldpass;
1902         newpass = state->request->data.chauthtok.newpass;
1903
1904         /* Initialize reject reason */
1905         state->response->data.auth.reject_reason = Undefined;
1906
1907         /* Get sam handle */
1908
1909         result = cm_connect_sam(contact_domain, state->mem_ctx, &cli,
1910                                 &dom_pol);
1911         if (!NT_STATUS_IS_OK(result)) {
1912                 DEBUG(1, ("could not get SAM handle on DC for %s\n", domain));
1913                 goto done;
1914         }
1915
1916         result = rpccli_samr_chgpasswd_user3(cli, state->mem_ctx,
1917                                              user,
1918                                              newpass,
1919                                              oldpass,
1920                                              &info,
1921                                              &reject);
1922
1923         /* Windows 2003 returns NT_STATUS_PASSWORD_RESTRICTION */
1924
1925         if (NT_STATUS_EQUAL(result, NT_STATUS_PASSWORD_RESTRICTION) ) {
1926
1927                 fill_in_password_policy(state->response, info);
1928
1929                 state->response->data.auth.reject_reason =
1930                         reject->extendedFailureReason;
1931
1932                 got_info = true;
1933         }
1934
1935         /* atm the pidl generated rpccli_samr_ChangePasswordUser3 function will
1936          * return with NT_STATUS_BUFFER_TOO_SMALL for w2k dcs as w2k just
1937          * returns with 4byte error code (NT_STATUS_NOT_SUPPORTED) which is too
1938          * short to comply with the samr_ChangePasswordUser3 idl - gd */
1939
1940         /* only fallback when the chgpasswd_user3 call is not supported */
1941         if ((NT_STATUS_EQUAL(result, NT_STATUS(DCERPC_FAULT_OP_RNG_ERROR))) ||
1942                    (NT_STATUS_EQUAL(result, NT_STATUS_NOT_SUPPORTED)) ||
1943                    (NT_STATUS_EQUAL(result, NT_STATUS_BUFFER_TOO_SMALL)) ||
1944                    (NT_STATUS_EQUAL(result, NT_STATUS_NOT_IMPLEMENTED))) {
1945
1946                 DEBUG(10,("Password change with chgpasswd_user3 failed with: %s, retrying chgpasswd_user2\n",
1947                         nt_errstr(result)));
1948
1949                 result = rpccli_samr_chgpasswd_user2(cli, state->mem_ctx, user, newpass, oldpass);
1950
1951                 /* Windows 2000 returns NT_STATUS_ACCOUNT_RESTRICTION.
1952                    Map to the same status code as Windows 2003. */
1953
1954                 if ( NT_STATUS_EQUAL(NT_STATUS_ACCOUNT_RESTRICTION, result ) ) {
1955                         result = NT_STATUS_PASSWORD_RESTRICTION;
1956                 }
1957         }
1958
1959 done:
1960
1961         if (NT_STATUS_IS_OK(result) && (state->request->flags & WBFLAG_PAM_CACHED_LOGIN)) {
1962                 if (lp_winbind_offline_logon()) {
1963                         result = winbindd_update_creds_by_name(contact_domain,
1964                                                          state->mem_ctx, user,
1965                                                          newpass);
1966                         /* Again, this happens when we login from gdm or xdm
1967                          * and the password expires, *BUT* cached crendentials
1968                          * doesn't exist. winbindd_update_creds_by_name()
1969                          * returns NT_STATUS_NO_SUCH_USER.
1970                          * This is not a failure.
1971                          * --- BoYang
1972                          * */
1973                         if (NT_STATUS_EQUAL(result, NT_STATUS_NO_SUCH_USER)) {
1974                                 result = NT_STATUS_OK;
1975                         }
1976
1977                         if (!NT_STATUS_IS_OK(result)) {
1978                                 DEBUG(10,("Failed to store creds: %s\n", nt_errstr(result)));
1979                                 goto process_result;
1980                         }
1981                 }
1982         }
1983
1984         if (!NT_STATUS_IS_OK(result) && !got_info && contact_domain) {
1985
1986                 NTSTATUS policy_ret;
1987
1988                 policy_ret = fillup_password_policy(contact_domain, state);
1989
1990                 /* failure of this is non critical, it will just provide no
1991                  * additional information to the client why the change has
1992                  * failed - Guenther */
1993
1994                 if (!NT_STATUS_IS_OK(policy_ret)) {
1995                         DEBUG(10,("Failed to get password policies: %s\n", nt_errstr(policy_ret)));
1996                         goto process_result;
1997                 }
1998         }
1999
2000 process_result:
2001
2002         if (strequal(contact_domain->name, get_global_sam_name())) {
2003                 /* FIXME: internal rpc pipe does not cache handles yet */
2004                 if (cli) {
2005                         if (is_valid_policy_hnd(&dom_pol)) {
2006                                 rpccli_samr_Close(cli, state->mem_ctx, &dom_pol);
2007                         }
2008                         TALLOC_FREE(cli);
2009                 }
2010         }
2011
2012         set_auth_errors(state->response, result);
2013
2014         DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2,
2015               ("Password change for user [%s]\\[%s] returned %s (PAM: %d)\n",
2016                domain,
2017                user,
2018                state->response->data.auth.nt_status_string,
2019                state->response->data.auth.pam_error));
2020
2021         return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
2022 }
2023
2024 enum winbindd_result winbindd_dual_pam_logoff(struct winbindd_domain *domain,
2025                                               struct winbindd_cli_state *state)
2026 {
2027         NTSTATUS result = NT_STATUS_NOT_SUPPORTED;
2028
2029         DEBUG(3, ("[%5lu]: pam dual logoff %s\n", (unsigned long)state->pid,
2030                 state->request->data.logoff.user));
2031
2032         if (!(state->request->flags & WBFLAG_PAM_KRB5)) {
2033                 result = NT_STATUS_OK;
2034                 goto process_result;
2035         }
2036
2037         if (state->request->data.logoff.krb5ccname[0] == '\0') {
2038                 result = NT_STATUS_OK;
2039                 goto process_result;
2040         }
2041
2042 #ifdef HAVE_KRB5
2043
2044         if (state->request->data.logoff.uid < 0) {
2045                 DEBUG(0,("winbindd_pam_logoff: invalid uid\n"));
2046                 goto process_result;
2047         }
2048
2049         /* what we need here is to find the corresponding krb5 ccache name *we*
2050          * created for a given username and destroy it */
2051
2052         if (!ccache_entry_exists(state->request->data.logoff.user)) {
2053                 result = NT_STATUS_OK;
2054                 DEBUG(10,("winbindd_pam_logoff: no entry found.\n"));
2055                 goto process_result;
2056         }
2057
2058         if (!ccache_entry_identical(state->request->data.logoff.user,
2059                                         state->request->data.logoff.uid,
2060                                         state->request->data.logoff.krb5ccname)) {
2061                 DEBUG(0,("winbindd_pam_logoff: cached entry differs.\n"));
2062                 goto process_result;
2063         }
2064
2065         result = remove_ccache(state->request->data.logoff.user);
2066         if (!NT_STATUS_IS_OK(result)) {
2067                 DEBUG(0,("winbindd_pam_logoff: failed to remove ccache: %s\n",
2068                         nt_errstr(result)));
2069                 goto process_result;
2070         }
2071
2072 #else
2073         result = NT_STATUS_NOT_SUPPORTED;
2074 #endif
2075
2076 process_result:
2077
2078
2079         set_auth_errors(state->response, result);
2080
2081         return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
2082 }
2083
2084 /* Change user password with auth crap*/
2085
2086 enum winbindd_result winbindd_dual_pam_chng_pswd_auth_crap(struct winbindd_domain *domainSt, struct winbindd_cli_state *state)
2087 {
2088         NTSTATUS result;
2089         DATA_BLOB new_nt_password;
2090         DATA_BLOB old_nt_hash_enc;
2091         DATA_BLOB new_lm_password;
2092         DATA_BLOB old_lm_hash_enc;
2093         fstring  domain,user;
2094         struct policy_handle dom_pol;
2095         struct winbindd_domain *contact_domain = domainSt;
2096         struct rpc_pipe_client *cli = NULL;
2097
2098         ZERO_STRUCT(dom_pol);
2099
2100         /* Ensure null termination */
2101         state->request->data.chng_pswd_auth_crap.user[
2102                 sizeof(state->request->data.chng_pswd_auth_crap.user)-1]=0;
2103         state->request->data.chng_pswd_auth_crap.domain[
2104                 sizeof(state->request->data.chng_pswd_auth_crap.domain)-1]=0;
2105         *domain = 0;
2106         *user = 0;
2107
2108         DEBUG(3, ("[%5lu]: pam change pswd auth crap domain: %s user: %s\n",
2109                   (unsigned long)state->pid,
2110                   state->request->data.chng_pswd_auth_crap.domain,
2111                   state->request->data.chng_pswd_auth_crap.user));
2112
2113         if (lp_winbind_offline_logon()) {
2114                 DEBUG(0,("Refusing password change as winbind offline logons are enabled. "));
2115                 DEBUGADD(0,("Changing passwords here would risk inconsistent logons\n"));
2116                 result = NT_STATUS_ACCESS_DENIED;
2117                 goto done;
2118         }
2119
2120         if (*state->request->data.chng_pswd_auth_crap.domain) {
2121                 fstrcpy(domain,state->request->data.chng_pswd_auth_crap.domain);
2122         } else {
2123                 parse_domain_user(state->request->data.chng_pswd_auth_crap.user,
2124                                   domain, user);
2125
2126                 if(!*domain) {
2127                         DEBUG(3,("no domain specified with username (%s) - "
2128                                  "failing auth\n",
2129                                  state->request->data.chng_pswd_auth_crap.user));
2130                         result = NT_STATUS_NO_SUCH_USER;
2131                         goto done;
2132                 }
2133         }
2134
2135         if (!*domain && lp_winbind_use_default_domain()) {
2136                 fstrcpy(domain,(char *)lp_workgroup());
2137         }
2138
2139         if(!*user) {
2140                 fstrcpy(user, state->request->data.chng_pswd_auth_crap.user);
2141         }
2142
2143         DEBUG(3, ("[%5lu]: pam auth crap domain: %s user: %s\n",
2144                   (unsigned long)state->pid, domain, user));
2145
2146         /* Change password */
2147         new_nt_password = data_blob_const(
2148                 state->request->data.chng_pswd_auth_crap.new_nt_pswd,
2149                 state->request->data.chng_pswd_auth_crap.new_nt_pswd_len);
2150
2151         old_nt_hash_enc = data_blob_const(
2152                 state->request->data.chng_pswd_auth_crap.old_nt_hash_enc,
2153                 state->request->data.chng_pswd_auth_crap.old_nt_hash_enc_len);
2154
2155         if(state->request->data.chng_pswd_auth_crap.new_lm_pswd_len > 0)        {
2156                 new_lm_password = data_blob_const(
2157                         state->request->data.chng_pswd_auth_crap.new_lm_pswd,
2158                         state->request->data.chng_pswd_auth_crap.new_lm_pswd_len);
2159
2160                 old_lm_hash_enc = data_blob_const(
2161                         state->request->data.chng_pswd_auth_crap.old_lm_hash_enc,
2162                         state->request->data.chng_pswd_auth_crap.old_lm_hash_enc_len);
2163         } else {
2164                 new_lm_password.length = 0;
2165                 old_lm_hash_enc.length = 0;
2166         }
2167
2168         /* Get sam handle */
2169
2170         result = cm_connect_sam(contact_domain, state->mem_ctx, &cli, &dom_pol);
2171         if (!NT_STATUS_IS_OK(result)) {
2172                 DEBUG(1, ("could not get SAM handle on DC for %s\n", domain));
2173                 goto done;
2174         }
2175
2176         result = rpccli_samr_chng_pswd_auth_crap(
2177                 cli, state->mem_ctx, user, new_nt_password, old_nt_hash_enc,
2178                 new_lm_password, old_lm_hash_enc);
2179
2180  done:
2181
2182         if (strequal(contact_domain->name, get_global_sam_name())) {
2183                 /* FIXME: internal rpc pipe does not cache handles yet */
2184                 if (cli) {
2185                         if (is_valid_policy_hnd(&dom_pol)) {
2186                                 rpccli_samr_Close(cli, state->mem_ctx, &dom_pol);
2187                         }
2188                         TALLOC_FREE(cli);
2189                 }
2190         }
2191
2192         set_auth_errors(state->response, result);
2193
2194         DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2,
2195               ("Password change for user [%s]\\[%s] returned %s (PAM: %d)\n",
2196                domain, user,
2197                state->response->data.auth.nt_status_string,
2198                state->response->data.auth.pam_error));
2199
2200         return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
2201 }