fba713d40bd0f53e09e37e8f2d8ed434b09c328a
[abartlet/samba.git/.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         uint32_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->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(name_domain,
842                                 name_user,
843                                 &sid,
844                                 &type)) {
845                 DEBUG(10,("winbindd_dual_pam_auth_cached: no such user in the cache\n"));
846                 return NT_STATUS_NO_SUCH_USER;
847         }
848
849         if (type != SID_NAME_USER) {
850                 DEBUG(10,("winbindd_dual_pam_auth_cached: not a user (%s)\n", sid_type_lookup(type)));
851                 return NT_STATUS_LOGON_FAILURE;
852         }
853
854         result = winbindd_get_creds(domain,
855                                     state->mem_ctx,
856                                     &sid,
857                                     &my_info3,
858                                     &cached_nt_pass,
859                                     &cached_salt);
860         if (!NT_STATUS_IS_OK(result)) {
861                 DEBUG(10,("winbindd_dual_pam_auth_cached: failed to get creds: %s\n", nt_errstr(result)));
862                 return result;
863         }
864
865         *info3 = my_info3;
866
867         E_md4hash(state->request->data.auth.pass, new_nt_pass);
868
869         dump_data_pw("new_nt_pass", new_nt_pass, NT_HASH_LEN);
870         dump_data_pw("cached_nt_pass", cached_nt_pass, NT_HASH_LEN);
871         if (cached_salt) {
872                 dump_data_pw("cached_salt", cached_salt, NT_HASH_LEN);
873         }
874
875         if (cached_salt) {
876                 /* In this case we didn't store the nt_hash itself,
877                    but the MD5 combination of salt + nt_hash. */
878                 uchar salted_hash[NT_HASH_LEN];
879                 E_md5hash(cached_salt, new_nt_pass, salted_hash);
880
881                 password_good = (memcmp(cached_nt_pass, salted_hash,
882                                         NT_HASH_LEN) == 0);
883         } else {
884                 /* Old cached cred - direct store of nt_hash (bad bad bad !). */
885                 password_good = (memcmp(cached_nt_pass, new_nt_pass,
886                                         NT_HASH_LEN) == 0);
887         }
888
889         if (password_good) {
890
891                 /* User *DOES* know the password, update logon_time and reset
892                  * bad_pw_count */
893
894                 my_info3->base.user_flags |= NETLOGON_CACHED_ACCOUNT;
895
896                 if (my_info3->base.acct_flags & ACB_AUTOLOCK) {
897                         return NT_STATUS_ACCOUNT_LOCKED_OUT;
898                 }
899
900                 if (my_info3->base.acct_flags & ACB_DISABLED) {
901                         return NT_STATUS_ACCOUNT_DISABLED;
902                 }
903
904                 if (my_info3->base.acct_flags & ACB_WSTRUST) {
905                         return NT_STATUS_NOLOGON_WORKSTATION_TRUST_ACCOUNT;
906                 }
907
908                 if (my_info3->base.acct_flags & ACB_SVRTRUST) {
909                         return NT_STATUS_NOLOGON_SERVER_TRUST_ACCOUNT;
910                 }
911
912                 if (my_info3->base.acct_flags & ACB_DOMTRUST) {
913                         return NT_STATUS_NOLOGON_INTERDOMAIN_TRUST_ACCOUNT;
914                 }
915
916                 if (!(my_info3->base.acct_flags & ACB_NORMAL)) {
917                         DEBUG(0,("winbindd_dual_pam_auth_cached: whats wrong with that one?: 0x%08x\n",
918                                 my_info3->base.acct_flags));
919                         return NT_STATUS_LOGON_FAILURE;
920                 }
921
922                 kickoff_time = nt_time_to_unix(my_info3->base.acct_expiry);
923                 if (kickoff_time != 0 && time(NULL) > kickoff_time) {
924                         return NT_STATUS_ACCOUNT_EXPIRED;
925                 }
926
927                 must_change_time = nt_time_to_unix(my_info3->base.force_password_change);
928                 if (must_change_time != 0 && must_change_time < time(NULL)) {
929                         /* we allow grace logons when the password has expired */
930                         my_info3->base.user_flags |= NETLOGON_GRACE_LOGON;
931                         /* return NT_STATUS_PASSWORD_EXPIRED; */
932                         goto success;
933                 }
934
935 #ifdef HAVE_KRB5
936                 if ((state->request->flags & WBFLAG_PAM_KRB5) &&
937                     ((tdc_domain = wcache_tdc_fetch_domain(state->mem_ctx, name_domain)) != NULL) &&
938                     ((tdc_domain->trust_type & NETR_TRUST_TYPE_UPLEVEL) ||
939                     /* used to cope with the case winbindd starting without network. */
940                     !strequal(tdc_domain->domain_name, tdc_domain->dns_name))) {
941
942                         uid_t uid = -1;
943                         const char *cc = NULL;
944                         char *realm = NULL;
945                         const char *principal_s = NULL;
946                         const char *service = NULL;
947                         bool internal_ccache = false;
948
949                         uid = get_uid_from_state(state);
950                         if (uid == -1) {
951                                 DEBUG(0,("winbindd_dual_pam_auth_cached: invalid uid\n"));
952                                 return NT_STATUS_INVALID_PARAMETER;
953                         }
954
955                         cc = generate_krb5_ccache(state->mem_ctx,
956                                                 state->request->data.auth.krb5_cc_type,
957                                                 state->request->data.auth.uid,
958                                                 &internal_ccache);
959                         if (cc == NULL) {
960                                 return NT_STATUS_NO_MEMORY;
961                         }
962
963                         realm = domain->alt_name;
964                         strupper_m(realm);
965
966                         principal_s = talloc_asprintf(state->mem_ctx, "%s@%s", name_user, realm);
967                         if (principal_s == NULL) {
968                                 return NT_STATUS_NO_MEMORY;
969                         }
970
971                         service = talloc_asprintf(state->mem_ctx, "%s/%s@%s", KRB5_TGS_NAME, realm, realm);
972                         if (service == NULL) {
973                                 return NT_STATUS_NO_MEMORY;
974                         }
975
976                         if (!internal_ccache) {
977
978                                 setup_return_cc_name(state, cc);
979
980                                 result = add_ccache_to_list(principal_s,
981                                                             cc,
982                                                             service,
983                                                             state->request->data.auth.user,
984                                                             domain->alt_name,
985                                                             uid,
986                                                             time(NULL),
987                                                             time(NULL) + lp_winbind_cache_time(),
988                                                             time(NULL) + WINBINDD_PAM_AUTH_KRB5_RENEW_TIME,
989                                                             true);
990
991                                 if (!NT_STATUS_IS_OK(result)) {
992                                         DEBUG(10,("winbindd_dual_pam_auth_cached: failed "
993                                                 "to add ccache to list: %s\n",
994                                                 nt_errstr(result)));
995                                 }
996                         }
997                 }
998 #endif /* HAVE_KRB5 */
999  success:
1000                 /* FIXME: we possibly should handle logon hours as well (does xp when
1001                  * offline?) see auth/auth_sam.c:sam_account_ok for details */
1002
1003                 unix_to_nt_time(&my_info3->base.last_logon, time(NULL));
1004                 my_info3->base.bad_password_count = 0;
1005
1006                 result = winbindd_update_creds_by_info3(domain,
1007                                                         state->request->data.auth.user,
1008                                                         state->request->data.auth.pass,
1009                                                         my_info3);
1010                 if (!NT_STATUS_IS_OK(result)) {
1011                         DEBUG(1,("winbindd_dual_pam_auth_cached: failed to update creds: %s\n",
1012                                 nt_errstr(result)));
1013                         return result;
1014                 }
1015
1016                 return NT_STATUS_OK;
1017
1018         }
1019
1020         /* User does *NOT* know the correct password, modify info3 accordingly */
1021
1022         /* failure of this is not critical */
1023         result = get_max_bad_attempts_from_lockout_policy(domain, state->mem_ctx, &max_allowed_bad_attempts);
1024         if (!NT_STATUS_IS_OK(result)) {
1025                 DEBUG(10,("winbindd_dual_pam_auth_cached: failed to get max_allowed_bad_attempts. "
1026                           "Won't be able to honour account lockout policies\n"));
1027         }
1028
1029         /* increase counter */
1030         my_info3->base.bad_password_count++;
1031
1032         if (max_allowed_bad_attempts == 0) {
1033                 goto failed;
1034         }
1035
1036         /* lockout user */
1037         if (my_info3->base.bad_password_count >= max_allowed_bad_attempts) {
1038
1039                 uint32 password_properties;
1040
1041                 result = get_pwd_properties(domain, state->mem_ctx, &password_properties);
1042                 if (!NT_STATUS_IS_OK(result)) {
1043                         DEBUG(10,("winbindd_dual_pam_auth_cached: failed to get password properties.\n"));
1044                 }
1045
1046                 if ((my_info3->base.rid != DOMAIN_RID_ADMINISTRATOR) ||
1047                     (password_properties & DOMAIN_PASSWORD_LOCKOUT_ADMINS)) {
1048                         my_info3->base.acct_flags |= ACB_AUTOLOCK;
1049                 }
1050         }
1051
1052 failed:
1053         result = winbindd_update_creds_by_info3(domain,
1054                                                 state->request->data.auth.user,
1055                                                 NULL,
1056                                                 my_info3);
1057
1058         if (!NT_STATUS_IS_OK(result)) {
1059                 DEBUG(0,("winbindd_dual_pam_auth_cached: failed to update creds %s\n",
1060                         nt_errstr(result)));
1061         }
1062
1063         return NT_STATUS_LOGON_FAILURE;
1064 }
1065
1066 static NTSTATUS winbindd_dual_pam_auth_kerberos(struct winbindd_domain *domain,
1067                                                 struct winbindd_cli_state *state,
1068                                                 struct netr_SamInfo3 **info3)
1069 {
1070         struct winbindd_domain *contact_domain;
1071         fstring name_domain, name_user;
1072         NTSTATUS result;
1073
1074         DEBUG(10,("winbindd_dual_pam_auth_kerberos\n"));
1075
1076         /* Parse domain and username */
1077
1078         parse_domain_user(state->request->data.auth.user, name_domain, name_user);
1079
1080         /* what domain should we contact? */
1081
1082         if ( IS_DC ) {
1083                 if (!(contact_domain = find_domain_from_name(name_domain))) {
1084                         DEBUG(3, ("Authentication for domain for [%s] -> [%s]\\[%s] failed as %s is not a trusted domain\n",
1085                                   state->request->data.auth.user, name_domain, name_user, name_domain));
1086                         result = NT_STATUS_NO_SUCH_USER;
1087                         goto done;
1088                 }
1089
1090         } else {
1091                 if (is_myname(name_domain)) {
1092                         DEBUG(3, ("Authentication for domain %s (local domain to this server) not supported at this stage\n", name_domain));
1093                         result =  NT_STATUS_NO_SUCH_USER;
1094                         goto done;
1095                 }
1096
1097                 contact_domain = find_domain_from_name(name_domain);
1098                 if (contact_domain == NULL) {
1099                         DEBUG(3, ("Authentication for domain for [%s] -> [%s]\\[%s] failed as %s is not a trusted domain\n",
1100                                   state->request->data.auth.user, name_domain, name_user, name_domain));
1101
1102                         contact_domain = find_our_domain();
1103                 }
1104         }
1105
1106         if (contact_domain->initialized &&
1107             contact_domain->active_directory) {
1108                 goto try_login;
1109         }
1110
1111         if (!contact_domain->initialized) {
1112                 init_dc_connection(contact_domain);
1113         }
1114
1115         if (!contact_domain->active_directory) {
1116                 DEBUG(3,("krb5 auth requested but domain is not Active Directory\n"));
1117                 return NT_STATUS_INVALID_LOGON_TYPE;
1118         }
1119 try_login:
1120         result = winbindd_raw_kerberos_login(contact_domain, state, info3);
1121 done:
1122         return result;
1123 }
1124
1125 static NTSTATUS winbindd_dual_auth_passdb(TALLOC_CTX *mem_ctx,
1126                                           const char *domain, const char *user,
1127                                           const DATA_BLOB *challenge,
1128                                           const DATA_BLOB *lm_resp,
1129                                           const DATA_BLOB *nt_resp,
1130                                           struct netr_SamInfo3 **pinfo3)
1131 {
1132         struct auth_usersupplied_info *user_info = NULL;
1133         NTSTATUS status;
1134
1135         status = make_user_info(&user_info, user, user, domain, domain,
1136                                 global_myname(), lm_resp, nt_resp, NULL, NULL,
1137                                 NULL, AUTH_PASSWORD_RESPONSE);
1138         if (!NT_STATUS_IS_OK(status)) {
1139                 DEBUG(10, ("make_user_info failed: %s\n", nt_errstr(status)));
1140                 return status;
1141         }
1142
1143         /* We don't want any more mapping of the username */
1144         user_info->mapped_state = True;
1145
1146         status = check_sam_security_info3(challenge, talloc_tos(), user_info,
1147                                           pinfo3);
1148         free_user_info(&user_info);
1149         DEBUG(10, ("Authenticated user %s\\%s successfully\n", domain, user));
1150         return NT_STATUS_OK;
1151 }
1152
1153 typedef NTSTATUS (*netlogon_fn_t)(struct rpc_pipe_client *cli,
1154                                   TALLOC_CTX *mem_ctx,
1155                                   uint32 logon_parameters,
1156                                   const char *server,
1157                                   const char *username,
1158                                   const char *domain,
1159                                   const char *workstation,
1160                                   const uint8 chal[8],
1161                                   DATA_BLOB lm_response,
1162                                   DATA_BLOB nt_response,
1163                                   struct netr_SamInfo3 **info3);
1164
1165 static NTSTATUS winbindd_dual_pam_auth_samlogon(struct winbindd_domain *domain,
1166                                                 struct winbindd_cli_state *state,
1167                                                 struct netr_SamInfo3 **info3)
1168 {
1169
1170         struct rpc_pipe_client *netlogon_pipe;
1171         uchar chal[8];
1172         DATA_BLOB lm_resp;
1173         DATA_BLOB nt_resp;
1174         int attempts = 0;
1175         unsigned char local_lm_response[24];
1176         unsigned char local_nt_response[24];
1177         fstring name_domain, name_user;
1178         bool retry;
1179         NTSTATUS result;
1180         struct netr_SamInfo3 *my_info3 = NULL;
1181
1182         *info3 = NULL;
1183
1184         DEBUG(10,("winbindd_dual_pam_auth_samlogon\n"));
1185
1186         /* Parse domain and username */
1187
1188         parse_domain_user(state->request->data.auth.user, name_domain, name_user);
1189
1190         /* do password magic */
1191
1192         generate_random_buffer(chal, sizeof(chal));
1193
1194         if (lp_client_ntlmv2_auth()) {
1195                 DATA_BLOB server_chal;
1196                 DATA_BLOB names_blob;
1197                 DATA_BLOB nt_response;
1198                 DATA_BLOB lm_response;
1199                 server_chal = data_blob_talloc(state->mem_ctx, chal, 8);
1200
1201                 /* note that the 'workgroup' here is a best guess - we don't know
1202                    the server's domain at this point.  The 'server name' is also
1203                    dodgy...
1204                 */
1205                 names_blob = NTLMv2_generate_names_blob(state->mem_ctx, global_myname(), lp_workgroup());
1206
1207                 if (!SMBNTLMv2encrypt(NULL, name_user, name_domain,
1208                                       state->request->data.auth.pass,
1209                                       &server_chal,
1210                                       &names_blob,
1211                                       &lm_response, &nt_response, NULL, NULL)) {
1212                         data_blob_free(&names_blob);
1213                         data_blob_free(&server_chal);
1214                         DEBUG(0, ("winbindd_pam_auth: SMBNTLMv2encrypt() failed!\n"));
1215                         result = NT_STATUS_NO_MEMORY;
1216                         goto done;
1217                 }
1218                 data_blob_free(&names_blob);
1219                 data_blob_free(&server_chal);
1220                 lm_resp = data_blob_talloc(state->mem_ctx, lm_response.data,
1221                                            lm_response.length);
1222                 nt_resp = data_blob_talloc(state->mem_ctx, nt_response.data,
1223                                            nt_response.length);
1224                 data_blob_free(&lm_response);
1225                 data_blob_free(&nt_response);
1226
1227         } else {
1228                 if (lp_client_lanman_auth()
1229                     && SMBencrypt(state->request->data.auth.pass,
1230                                   chal,
1231                                   local_lm_response)) {
1232                         lm_resp = data_blob_talloc(state->mem_ctx,
1233                                                    local_lm_response,
1234                                                    sizeof(local_lm_response));
1235                 } else {
1236                         lm_resp = data_blob_null;
1237                 }
1238                 SMBNTencrypt(state->request->data.auth.pass,
1239                              chal,
1240                              local_nt_response);
1241
1242                 nt_resp = data_blob_talloc(state->mem_ctx,
1243                                            local_nt_response,
1244                                            sizeof(local_nt_response));
1245         }
1246
1247         if (strequal(name_domain, get_global_sam_name())) {
1248                 DATA_BLOB chal_blob = data_blob_const(chal, sizeof(chal));
1249
1250                 result = winbindd_dual_auth_passdb(
1251                         state->mem_ctx, name_domain, name_user,
1252                         &chal_blob, &lm_resp, &nt_resp, info3);
1253                 goto done;
1254         }
1255
1256         /* check authentication loop */
1257
1258         do {
1259                 netlogon_fn_t logon_fn;
1260
1261                 ZERO_STRUCTP(my_info3);
1262                 retry = false;
1263
1264                 result = cm_connect_netlogon(domain, &netlogon_pipe);
1265
1266                 if (!NT_STATUS_IS_OK(result)) {
1267                         DEBUG(3, ("could not open handle to NETLOGON pipe\n"));
1268                         goto done;
1269                 }
1270
1271                 /* It is really important to try SamLogonEx here,
1272                  * because in a clustered environment, we want to use
1273                  * one machine account from multiple physical
1274                  * computers.
1275                  *
1276                  * With a normal SamLogon call, we must keep the
1277                  * credentials chain updated and intact between all
1278                  * users of the machine account (which would imply
1279                  * cross-node communication for every NTLM logon).
1280                  *
1281                  * (The credentials chain is not per NETLOGON pipe
1282                  * connection, but globally on the server/client pair
1283                  * by machine name).
1284                  *
1285                  * When using SamLogonEx, the credentials are not
1286                  * supplied, but the session key is implied by the
1287                  * wrapping SamLogon context.
1288                  *
1289                  *  -- abartlet 21 April 2008
1290                  */
1291
1292                 logon_fn = domain->can_do_samlogon_ex
1293                         ? rpccli_netlogon_sam_network_logon_ex
1294                         : rpccli_netlogon_sam_network_logon;
1295
1296                 result = logon_fn(netlogon_pipe,
1297                                   state->mem_ctx,
1298                                   0,
1299                                   domain->dcname,         /* server name */
1300                                   name_user,              /* user name */
1301                                   name_domain,            /* target domain */
1302                                   global_myname(),        /* workstation */
1303                                   chal,
1304                                   lm_resp,
1305                                   nt_resp,
1306                                   &my_info3);
1307                 attempts += 1;
1308
1309                 if ((NT_STATUS_V(result) == DCERPC_FAULT_OP_RNG_ERROR)
1310                     && domain->can_do_samlogon_ex) {
1311                         DEBUG(3, ("Got a DC that can not do NetSamLogonEx, "
1312                                   "retrying with NetSamLogon\n"));
1313                         domain->can_do_samlogon_ex = false;
1314                         retry = true;
1315                         continue;
1316                 }
1317
1318                 /* We have to try a second time as cm_connect_netlogon
1319                    might not yet have noticed that the DC has killed
1320                    our connection. */
1321
1322                 if (!rpccli_is_connected(netlogon_pipe)) {
1323                         retry = true;
1324                         continue;
1325                 }
1326
1327                 /* if we get access denied, a possible cause was that we had
1328                    and open connection to the DC, but someone changed our
1329                    machine account password out from underneath us using 'net
1330                    rpc changetrustpw' */
1331
1332                 if ( NT_STATUS_EQUAL(result, NT_STATUS_ACCESS_DENIED) ) {
1333                         DEBUG(3,("winbindd_pam_auth: sam_logon returned "
1334                                  "ACCESS_DENIED.  Maybe the trust account "
1335                                 "password was changed and we didn't know it. "
1336                                  "Killing connections to domain %s\n",
1337                                 name_domain));
1338                         invalidate_cm_connection(&domain->conn);
1339                         retry = true;
1340                 }
1341
1342         } while ( (attempts < 2) && retry );
1343
1344         /* handle the case where a NT4 DC does not fill in the acct_flags in
1345          * the samlogon reply info3. When accurate info3 is required by the
1346          * caller, we look up the account flags ourselve - gd */
1347
1348         if ((state->request->flags & WBFLAG_PAM_INFO3_TEXT) &&
1349             NT_STATUS_IS_OK(result) && (my_info3->base.acct_flags == 0)) {
1350
1351                 struct rpc_pipe_client *samr_pipe;
1352                 struct policy_handle samr_domain_handle, user_pol;
1353                 union samr_UserInfo *info = NULL;
1354                 NTSTATUS status_tmp;
1355                 uint32 acct_flags;
1356
1357                 status_tmp = cm_connect_sam(domain, state->mem_ctx,
1358                                             &samr_pipe, &samr_domain_handle);
1359
1360                 if (!NT_STATUS_IS_OK(status_tmp)) {
1361                         DEBUG(3, ("could not open handle to SAMR pipe: %s\n",
1362                                 nt_errstr(status_tmp)));
1363                         goto done;
1364                 }
1365
1366                 status_tmp = rpccli_samr_OpenUser(samr_pipe, state->mem_ctx,
1367                                                   &samr_domain_handle,
1368                                                   MAXIMUM_ALLOWED_ACCESS,
1369                                                   my_info3->base.rid,
1370                                                   &user_pol);
1371
1372                 if (!NT_STATUS_IS_OK(status_tmp)) {
1373                         DEBUG(3, ("could not open user handle on SAMR pipe: %s\n",
1374                                 nt_errstr(status_tmp)));
1375                         goto done;
1376                 }
1377
1378                 status_tmp = rpccli_samr_QueryUserInfo(samr_pipe, state->mem_ctx,
1379                                                        &user_pol,
1380                                                        16,
1381                                                        &info);
1382
1383                 if (!NT_STATUS_IS_OK(status_tmp)) {
1384                         DEBUG(3, ("could not query user info on SAMR pipe: %s\n",
1385                                 nt_errstr(status_tmp)));
1386                         rpccli_samr_Close(samr_pipe, state->mem_ctx, &user_pol);
1387                         goto done;
1388                 }
1389
1390                 acct_flags = info->info16.acct_flags;
1391
1392                 if (acct_flags == 0) {
1393                         rpccli_samr_Close(samr_pipe, state->mem_ctx, &user_pol);
1394                         goto done;
1395                 }
1396
1397                 my_info3->base.acct_flags = acct_flags;
1398
1399                 DEBUG(10,("successfully retrieved acct_flags 0x%x\n", acct_flags));
1400
1401                 rpccli_samr_Close(samr_pipe, state->mem_ctx, &user_pol);
1402         }
1403
1404         *info3 = my_info3;
1405 done:
1406         return result;
1407 }
1408
1409 enum winbindd_result winbindd_dual_pam_auth(struct winbindd_domain *domain,
1410                                             struct winbindd_cli_state *state)
1411 {
1412         NTSTATUS result = NT_STATUS_LOGON_FAILURE;
1413         NTSTATUS krb5_result = NT_STATUS_OK;
1414         fstring name_domain, name_user;
1415         char *mapped_user;
1416         fstring domain_user;
1417         struct netr_SamInfo3 *info3 = NULL;
1418         NTSTATUS name_map_status = NT_STATUS_UNSUCCESSFUL;
1419
1420         /* Ensure null termination */
1421         state->request->data.auth.user[sizeof(state->request->data.auth.user)-1]='\0';
1422
1423         /* Ensure null termination */
1424         state->request->data.auth.pass[sizeof(state->request->data.auth.pass)-1]='\0';
1425
1426         DEBUG(3, ("[%5lu]: dual pam auth %s\n", (unsigned long)state->pid,
1427                   state->request->data.auth.user));
1428
1429         /* Parse domain and username */
1430
1431         name_map_status = normalize_name_unmap(state->mem_ctx,
1432                                                state->request->data.auth.user,
1433                                                &mapped_user);
1434
1435         /* If the name normalization didnt' actually do anything,
1436            just use the original name */
1437
1438         if (!NT_STATUS_IS_OK(name_map_status) &&
1439             !NT_STATUS_EQUAL(name_map_status, NT_STATUS_FILE_RENAMED))
1440         {
1441                 mapped_user = state->request->data.auth.user;
1442         }
1443
1444         parse_domain_user(mapped_user, name_domain, name_user);
1445
1446         if ( mapped_user != state->request->data.auth.user ) {
1447                 fstr_sprintf( domain_user, "%s%c%s", name_domain,
1448                         *lp_winbind_separator(),
1449                         name_user );
1450                 safe_strcpy( state->request->data.auth.user, domain_user,
1451                              sizeof(state->request->data.auth.user)-1 );
1452         }
1453
1454         if (!domain->online) {
1455                 result = NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND;
1456                 if (domain->startup) {
1457                         /* Logons are very important to users. If we're offline and
1458                            we get a request within the first 30 seconds of startup,
1459                            try very hard to find a DC and go online. */
1460
1461                         DEBUG(10,("winbindd_dual_pam_auth: domain: %s offline and auth "
1462                                 "request in startup mode.\n", domain->name ));
1463
1464                         winbindd_flush_negative_conn_cache(domain);
1465                         result = init_dc_connection(domain);
1466                 }
1467         }
1468
1469         DEBUG(10,("winbindd_dual_pam_auth: domain: %s last was %s\n", domain->name, domain->online ? "online":"offline"));
1470
1471         /* Check for Kerberos authentication */
1472         if (domain->online && (state->request->flags & WBFLAG_PAM_KRB5)) {
1473
1474                 result = winbindd_dual_pam_auth_kerberos(domain, state, &info3);
1475                 /* save for later */
1476                 krb5_result = result;
1477
1478
1479                 if (NT_STATUS_IS_OK(result)) {
1480                         DEBUG(10,("winbindd_dual_pam_auth_kerberos succeeded\n"));
1481                         goto process_result;
1482                 } else {
1483                         DEBUG(10,("winbindd_dual_pam_auth_kerberos failed: %s\n", nt_errstr(result)));
1484                 }
1485
1486                 if (NT_STATUS_EQUAL(result, NT_STATUS_NO_LOGON_SERVERS) ||
1487                     NT_STATUS_EQUAL(result, NT_STATUS_IO_TIMEOUT) ||
1488                     NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
1489                         DEBUG(10,("winbindd_dual_pam_auth_kerberos setting domain to offline\n"));
1490                         set_domain_offline( domain );
1491                         goto cached_logon;
1492                 }
1493
1494                 /* there are quite some NT_STATUS errors where there is no
1495                  * point in retrying with a samlogon, we explictly have to take
1496                  * care not to increase the bad logon counter on the DC */
1497
1498                 if (NT_STATUS_EQUAL(result, NT_STATUS_ACCOUNT_DISABLED) ||
1499                     NT_STATUS_EQUAL(result, NT_STATUS_ACCOUNT_EXPIRED) ||
1500                     NT_STATUS_EQUAL(result, NT_STATUS_ACCOUNT_LOCKED_OUT) ||
1501                     NT_STATUS_EQUAL(result, NT_STATUS_INVALID_LOGON_HOURS) ||
1502                     NT_STATUS_EQUAL(result, NT_STATUS_INVALID_WORKSTATION) ||
1503                     NT_STATUS_EQUAL(result, NT_STATUS_LOGON_FAILURE) ||
1504                     NT_STATUS_EQUAL(result, NT_STATUS_NO_SUCH_USER) ||
1505                     NT_STATUS_EQUAL(result, NT_STATUS_PASSWORD_EXPIRED) ||
1506                     NT_STATUS_EQUAL(result, NT_STATUS_PASSWORD_MUST_CHANGE) ||
1507                     NT_STATUS_EQUAL(result, NT_STATUS_WRONG_PASSWORD)) {
1508                         goto done;
1509                 }
1510
1511                 if (state->request->flags & WBFLAG_PAM_FALLBACK_AFTER_KRB5) {
1512                         DEBUG(3,("falling back to samlogon\n"));
1513                         goto sam_logon;
1514                 } else {
1515                         goto cached_logon;
1516                 }
1517         }
1518
1519 sam_logon:
1520         /* Check for Samlogon authentication */
1521         if (domain->online) {
1522                 result = winbindd_dual_pam_auth_samlogon(domain, state, &info3);
1523
1524                 if (NT_STATUS_IS_OK(result)) {
1525                         DEBUG(10,("winbindd_dual_pam_auth_samlogon succeeded\n"));
1526                         /* add the Krb5 err if we have one */
1527                         if ( NT_STATUS_EQUAL(krb5_result, NT_STATUS_TIME_DIFFERENCE_AT_DC ) ) {
1528                                 info3->base.user_flags |= LOGON_KRB5_FAIL_CLOCK_SKEW;
1529                         }
1530                         goto process_result;
1531                 }
1532
1533                 DEBUG(10,("winbindd_dual_pam_auth_samlogon failed: %s\n",
1534                           nt_errstr(result)));
1535
1536                 if (NT_STATUS_EQUAL(result, NT_STATUS_NO_LOGON_SERVERS) ||
1537                     NT_STATUS_EQUAL(result, NT_STATUS_IO_TIMEOUT) ||
1538                     NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND))
1539                 {
1540                         DEBUG(10,("winbindd_dual_pam_auth_samlogon setting domain to offline\n"));
1541                         set_domain_offline( domain );
1542                         goto cached_logon;
1543                 }
1544
1545                         if (domain->online) {
1546                                 /* We're still online - fail. */
1547                                 goto done;
1548                         }
1549         }
1550
1551 cached_logon:
1552         /* Check for Cached logons */
1553         if (!domain->online && (state->request->flags & WBFLAG_PAM_CACHED_LOGIN) &&
1554             lp_winbind_offline_logon()) {
1555
1556                 result = winbindd_dual_pam_auth_cached(domain, state, &info3);
1557
1558                 if (NT_STATUS_IS_OK(result)) {
1559                         DEBUG(10,("winbindd_dual_pam_auth_cached succeeded\n"));
1560                         goto process_result;
1561                 } else {
1562                         DEBUG(10,("winbindd_dual_pam_auth_cached failed: %s\n", nt_errstr(result)));
1563                         goto done;
1564                 }
1565         }
1566
1567 process_result:
1568
1569         if (NT_STATUS_IS_OK(result)) {
1570
1571                 struct dom_sid user_sid;
1572
1573                 /* In all codepaths where result == NT_STATUS_OK info3 must have
1574                    been initialized. */
1575                 if (!info3) {
1576                         result = NT_STATUS_INTERNAL_ERROR;
1577                         goto done;
1578                 }
1579
1580                 wcache_invalidate_samlogon(find_domain_from_name(name_domain), info3);
1581                 netsamlogon_cache_store(name_user, info3);
1582
1583                 /* save name_to_sid info as early as possible (only if
1584                    this is our primary domain so we don't invalidate
1585                    the cache entry by storing the seq_num for the wrong
1586                    domain). */
1587                 if ( domain->primary ) {
1588                         sid_compose(&user_sid, info3->base.domain_sid,
1589                                     info3->base.rid);
1590                         cache_name2sid(domain, name_domain, name_user,
1591                                        SID_NAME_USER, &user_sid);
1592                 }
1593
1594                 /* Check if the user is in the right group */
1595
1596                 result = check_info3_in_group(
1597                         info3,
1598                         state->request->data.auth.require_membership_of_sid);
1599                 if (!NT_STATUS_IS_OK(result)) {
1600                         DEBUG(3, ("User %s is not in the required group (%s), so plaintext authentication is rejected\n",
1601                                   state->request->data.auth.user,
1602                                   state->request->data.auth.require_membership_of_sid));
1603                         goto done;
1604                 }
1605
1606                 result = append_auth_data(state, info3, name_domain,
1607                                           name_user);
1608                 if (!NT_STATUS_IS_OK(result)) {
1609                         goto done;
1610                 }
1611
1612                 if ((state->request->flags & WBFLAG_PAM_CACHED_LOGIN)
1613                     && lp_winbind_offline_logon()) {
1614
1615                         result = winbindd_store_creds(domain,
1616                                                       state->request->data.auth.user,
1617                                                       state->request->data.auth.pass,
1618                                                       info3);
1619                 }
1620
1621                 if (state->request->flags & WBFLAG_PAM_GET_PWD_POLICY) {
1622                         struct winbindd_domain *our_domain = find_our_domain();
1623
1624                         /* This is not entirely correct I believe, but it is
1625                            consistent.  Only apply the password policy settings
1626                            too warn users for our own domain.  Cannot obtain these
1627                            from trusted DCs all the  time so don't do it at all.
1628                            -- jerry */
1629
1630                         result = NT_STATUS_NOT_SUPPORTED;
1631                         if (our_domain == domain ) {
1632                                 result = fillup_password_policy(our_domain, state);
1633                         }
1634
1635                         if (!NT_STATUS_IS_OK(result)
1636                             && !NT_STATUS_EQUAL(result, NT_STATUS_NOT_SUPPORTED) )
1637                         {
1638                                 DEBUG(10,("Failed to get password policies for domain %s: %s\n",
1639                                           domain->name, nt_errstr(result)));
1640                                 goto done;
1641                         }
1642                 }
1643
1644                 result = NT_STATUS_OK;
1645         }
1646
1647 done:
1648         /* give us a more useful (more correct?) error code */
1649         if ((NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND) ||
1650             (NT_STATUS_EQUAL(result, NT_STATUS_UNSUCCESSFUL)))) {
1651                 result = NT_STATUS_NO_LOGON_SERVERS;
1652         }
1653
1654         set_auth_errors(state->response, result);
1655
1656         DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2, ("Plain-text authentication for user %s returned %s (PAM: %d)\n",
1657               state->request->data.auth.user,
1658               state->response->data.auth.nt_status_string,
1659               state->response->data.auth.pam_error));
1660
1661         return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
1662 }
1663
1664 enum winbindd_result winbindd_dual_pam_auth_crap(struct winbindd_domain *domain,
1665                                                  struct winbindd_cli_state *state)
1666 {
1667         NTSTATUS result;
1668         struct netr_SamInfo3 *info3 = NULL;
1669         struct rpc_pipe_client *netlogon_pipe;
1670         const char *name_user = NULL;
1671         const char *name_domain = NULL;
1672         const char *workstation;
1673         int attempts = 0;
1674         bool retry;
1675
1676         DATA_BLOB lm_resp, nt_resp;
1677
1678         /* This is child-only, so no check for privileged access is needed
1679            anymore */
1680
1681         /* Ensure null termination */
1682         state->request->data.auth_crap.user[sizeof(state->request->data.auth_crap.user)-1]=0;
1683         state->request->data.auth_crap.domain[sizeof(state->request->data.auth_crap.domain)-1]=0;
1684
1685         name_user = state->request->data.auth_crap.user;
1686         name_domain = state->request->data.auth_crap.domain;
1687         workstation = state->request->data.auth_crap.workstation;
1688
1689         DEBUG(3, ("[%5lu]: pam auth crap domain: %s user: %s\n", (unsigned long)state->pid,
1690                   name_domain, name_user));
1691
1692         if (state->request->data.auth_crap.lm_resp_len > sizeof(state->request->data.auth_crap.lm_resp)
1693                 || state->request->data.auth_crap.nt_resp_len > sizeof(state->request->data.auth_crap.nt_resp)) {
1694                 if (!(state->request->flags & WBFLAG_BIG_NTLMV2_BLOB) ||
1695                      state->request->extra_len != state->request->data.auth_crap.nt_resp_len) {
1696                         DEBUG(0, ("winbindd_pam_auth_crap: invalid password length %u/%u\n",
1697                                   state->request->data.auth_crap.lm_resp_len,
1698                                   state->request->data.auth_crap.nt_resp_len));
1699                         result = NT_STATUS_INVALID_PARAMETER;
1700                         goto done;
1701                 }
1702         }
1703
1704         lm_resp = data_blob_talloc(state->mem_ctx, state->request->data.auth_crap.lm_resp,
1705                                         state->request->data.auth_crap.lm_resp_len);
1706
1707         if (state->request->flags & WBFLAG_BIG_NTLMV2_BLOB) {
1708                 nt_resp = data_blob_talloc(state->mem_ctx,
1709                                            state->request->extra_data.data,
1710                                            state->request->data.auth_crap.nt_resp_len);
1711         } else {
1712                 nt_resp = data_blob_talloc(state->mem_ctx,
1713                                            state->request->data.auth_crap.nt_resp,
1714                                            state->request->data.auth_crap.nt_resp_len);
1715         }
1716
1717         if (strequal(name_domain, get_global_sam_name())) {
1718                 DATA_BLOB chal_blob = data_blob_const(
1719                         state->request->data.auth_crap.chal,
1720                         sizeof(state->request->data.auth_crap.chal));
1721
1722                 result = winbindd_dual_auth_passdb(
1723                         state->mem_ctx, name_domain, name_user,
1724                         &chal_blob, &lm_resp, &nt_resp, &info3);
1725                 goto process_result;
1726         }
1727
1728         do {
1729                 netlogon_fn_t logon_fn;
1730
1731                 retry = false;
1732
1733                 netlogon_pipe = NULL;
1734                 result = cm_connect_netlogon(domain, &netlogon_pipe);
1735
1736                 if (!NT_STATUS_IS_OK(result)) {
1737                         DEBUG(3, ("could not open handle to NETLOGON pipe (error: %s)\n",
1738                                   nt_errstr(result)));
1739                         goto done;
1740                 }
1741
1742                 logon_fn = domain->can_do_samlogon_ex
1743                         ? rpccli_netlogon_sam_network_logon_ex
1744                         : rpccli_netlogon_sam_network_logon;
1745
1746                 result = logon_fn(netlogon_pipe,
1747                                   state->mem_ctx,
1748                                   state->request->data.auth_crap.logon_parameters,
1749                                   domain->dcname,
1750                                   name_user,
1751                                   name_domain,
1752                                   /* Bug #3248 - found by Stefan Burkei. */
1753                                   workstation, /* We carefully set this above so use it... */
1754                                   state->request->data.auth_crap.chal,
1755                                   lm_resp,
1756                                   nt_resp,
1757                                   &info3);
1758
1759                 if ((NT_STATUS_V(result) == DCERPC_FAULT_OP_RNG_ERROR)
1760                     && domain->can_do_samlogon_ex) {
1761                         DEBUG(3, ("Got a DC that can not do NetSamLogonEx, "
1762                                   "retrying with NetSamLogon\n"));
1763                         domain->can_do_samlogon_ex = false;
1764                         retry = true;
1765                         continue;
1766                 }
1767
1768                 attempts += 1;
1769
1770                 /* We have to try a second time as cm_connect_netlogon
1771                    might not yet have noticed that the DC has killed
1772                    our connection. */
1773
1774                 if (!rpccli_is_connected(netlogon_pipe)) {
1775                         retry = true;
1776                         continue;
1777                 }
1778
1779                 /* if we get access denied, a possible cause was that we had and open
1780                    connection to the DC, but someone changed our machine account password
1781                    out from underneath us using 'net rpc changetrustpw' */
1782
1783                 if ( NT_STATUS_EQUAL(result, NT_STATUS_ACCESS_DENIED) ) {
1784                         DEBUG(3,("winbindd_pam_auth: sam_logon returned "
1785                                  "ACCESS_DENIED.  Maybe the trust account "
1786                                 "password was changed and we didn't know it. "
1787                                  "Killing connections to domain %s\n",
1788                                 name_domain));
1789                         invalidate_cm_connection(&domain->conn);
1790                         retry = true;
1791                 }
1792
1793         } while ( (attempts < 2) && retry );
1794
1795 process_result:
1796
1797         if (NT_STATUS_IS_OK(result)) {
1798
1799                 wcache_invalidate_samlogon(find_domain_from_name(name_domain), info3);
1800                 netsamlogon_cache_store(name_user, info3);
1801
1802                 /* Check if the user is in the right group */
1803
1804                 result = check_info3_in_group(
1805                         info3,
1806                         state->request->data.auth_crap.require_membership_of_sid);
1807                 if (!NT_STATUS_IS_OK(result)) {
1808                         DEBUG(3, ("User %s is not in the required group (%s), so "
1809                                   "crap authentication is rejected\n",
1810                                   state->request->data.auth_crap.user,
1811                                   state->request->data.auth_crap.require_membership_of_sid));
1812                         goto done;
1813                 }
1814
1815                 result = append_auth_data(state, info3, name_domain,
1816                                           name_user);
1817                 if (!NT_STATUS_IS_OK(result)) {
1818                         goto done;
1819                 }
1820         }
1821
1822 done:
1823
1824         /* give us a more useful (more correct?) error code */
1825         if ((NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND) ||
1826             (NT_STATUS_EQUAL(result, NT_STATUS_UNSUCCESSFUL)))) {
1827                 result = NT_STATUS_NO_LOGON_SERVERS;
1828         }
1829
1830         if (state->request->flags & WBFLAG_PAM_NT_STATUS_SQUASH) {
1831                 result = nt_status_squash(result);
1832         }
1833
1834         set_auth_errors(state->response, result);
1835
1836         DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2,
1837               ("NTLM CRAP authentication for user [%s]\\[%s] returned %s (PAM: %d)\n",
1838                name_domain,
1839                name_user,
1840                state->response->data.auth.nt_status_string,
1841                state->response->data.auth.pam_error));
1842
1843         return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
1844 }
1845
1846 enum winbindd_result winbindd_dual_pam_chauthtok(struct winbindd_domain *contact_domain,
1847                                                  struct winbindd_cli_state *state)
1848 {
1849         char *oldpass;
1850         char *newpass = NULL;
1851         struct policy_handle dom_pol;
1852         struct rpc_pipe_client *cli = NULL;
1853         bool got_info = false;
1854         struct samr_DomInfo1 *info = NULL;
1855         struct userPwdChangeFailureInformation *reject = NULL;
1856         NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
1857         fstring domain, user;
1858
1859         ZERO_STRUCT(dom_pol);
1860
1861         DEBUG(3, ("[%5lu]: dual pam chauthtok %s\n", (unsigned long)state->pid,
1862                   state->request->data.auth.user));
1863
1864         if (!parse_domain_user(state->request->data.chauthtok.user, domain, user)) {
1865                 goto done;
1866         }
1867
1868         /* Change password */
1869
1870         oldpass = state->request->data.chauthtok.oldpass;
1871         newpass = state->request->data.chauthtok.newpass;
1872
1873         /* Initialize reject reason */
1874         state->response->data.auth.reject_reason = Undefined;
1875
1876         /* Get sam handle */
1877
1878         result = cm_connect_sam(contact_domain, state->mem_ctx, &cli,
1879                                 &dom_pol);
1880         if (!NT_STATUS_IS_OK(result)) {
1881                 DEBUG(1, ("could not get SAM handle on DC for %s\n", domain));
1882                 goto done;
1883         }
1884
1885         result = rpccli_samr_chgpasswd_user3(cli, state->mem_ctx,
1886                                              user,
1887                                              newpass,
1888                                              oldpass,
1889                                              &info,
1890                                              &reject);
1891
1892         /* Windows 2003 returns NT_STATUS_PASSWORD_RESTRICTION */
1893
1894         if (NT_STATUS_EQUAL(result, NT_STATUS_PASSWORD_RESTRICTION) ) {
1895
1896                 fill_in_password_policy(state->response, info);
1897
1898                 state->response->data.auth.reject_reason =
1899                         reject->extendedFailureReason;
1900
1901                 got_info = true;
1902         }
1903
1904         /* atm the pidl generated rpccli_samr_ChangePasswordUser3 function will
1905          * return with NT_STATUS_BUFFER_TOO_SMALL for w2k dcs as w2k just
1906          * returns with 4byte error code (NT_STATUS_NOT_SUPPORTED) which is too
1907          * short to comply with the samr_ChangePasswordUser3 idl - gd */
1908
1909         /* only fallback when the chgpasswd_user3 call is not supported */
1910         if ((NT_STATUS_EQUAL(result, NT_STATUS(DCERPC_FAULT_OP_RNG_ERROR))) ||
1911                    (NT_STATUS_EQUAL(result, NT_STATUS_NOT_SUPPORTED)) ||
1912                    (NT_STATUS_EQUAL(result, NT_STATUS_BUFFER_TOO_SMALL)) ||
1913                    (NT_STATUS_EQUAL(result, NT_STATUS_NOT_IMPLEMENTED))) {
1914
1915                 DEBUG(10,("Password change with chgpasswd_user3 failed with: %s, retrying chgpasswd_user2\n",
1916                         nt_errstr(result)));
1917
1918                 result = rpccli_samr_chgpasswd_user2(cli, state->mem_ctx, user, newpass, oldpass);
1919
1920                 /* Windows 2000 returns NT_STATUS_ACCOUNT_RESTRICTION.
1921                    Map to the same status code as Windows 2003. */
1922
1923                 if ( NT_STATUS_EQUAL(NT_STATUS_ACCOUNT_RESTRICTION, result ) ) {
1924                         result = NT_STATUS_PASSWORD_RESTRICTION;
1925                 }
1926         }
1927
1928 done:
1929
1930         if (NT_STATUS_IS_OK(result) && (state->request->flags & WBFLAG_PAM_CACHED_LOGIN)) {
1931                 if (lp_winbind_offline_logon()) {
1932                         result = winbindd_update_creds_by_name(contact_domain,
1933                                                                user, newpass);
1934                         /* Again, this happens when we login from gdm or xdm
1935                          * and the password expires, *BUT* cached crendentials
1936                          * doesn't exist. winbindd_update_creds_by_name()
1937                          * returns NT_STATUS_NO_SUCH_USER.
1938                          * This is not a failure.
1939                          * --- BoYang
1940                          * */
1941                         if (NT_STATUS_EQUAL(result, NT_STATUS_NO_SUCH_USER)) {
1942                                 result = NT_STATUS_OK;
1943                         }
1944
1945                         if (!NT_STATUS_IS_OK(result)) {
1946                                 DEBUG(10,("Failed to store creds: %s\n", nt_errstr(result)));
1947                                 goto process_result;
1948                         }
1949                 }
1950         }
1951
1952         if (!NT_STATUS_IS_OK(result) && !got_info && contact_domain) {
1953
1954                 NTSTATUS policy_ret;
1955
1956                 policy_ret = fillup_password_policy(contact_domain, state);
1957
1958                 /* failure of this is non critical, it will just provide no
1959                  * additional information to the client why the change has
1960                  * failed - Guenther */
1961
1962                 if (!NT_STATUS_IS_OK(policy_ret)) {
1963                         DEBUG(10,("Failed to get password policies: %s\n", nt_errstr(policy_ret)));
1964                         goto process_result;
1965                 }
1966         }
1967
1968 process_result:
1969
1970         if (strequal(contact_domain->name, get_global_sam_name())) {
1971                 /* FIXME: internal rpc pipe does not cache handles yet */
1972                 if (cli) {
1973                         if (is_valid_policy_hnd(&dom_pol)) {
1974                                 rpccli_samr_Close(cli, state->mem_ctx, &dom_pol);
1975                         }
1976                         TALLOC_FREE(cli);
1977                 }
1978         }
1979
1980         set_auth_errors(state->response, result);
1981
1982         DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2,
1983               ("Password change for user [%s]\\[%s] returned %s (PAM: %d)\n",
1984                domain,
1985                user,
1986                state->response->data.auth.nt_status_string,
1987                state->response->data.auth.pam_error));
1988
1989         return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
1990 }
1991
1992 enum winbindd_result winbindd_dual_pam_logoff(struct winbindd_domain *domain,
1993                                               struct winbindd_cli_state *state)
1994 {
1995         NTSTATUS result = NT_STATUS_NOT_SUPPORTED;
1996
1997         DEBUG(3, ("[%5lu]: pam dual logoff %s\n", (unsigned long)state->pid,
1998                 state->request->data.logoff.user));
1999
2000         if (!(state->request->flags & WBFLAG_PAM_KRB5)) {
2001                 result = NT_STATUS_OK;
2002                 goto process_result;
2003         }
2004
2005         if (state->request->data.logoff.krb5ccname[0] == '\0') {
2006                 result = NT_STATUS_OK;
2007                 goto process_result;
2008         }
2009
2010 #ifdef HAVE_KRB5
2011
2012         if (state->request->data.logoff.uid < 0) {
2013                 DEBUG(0,("winbindd_pam_logoff: invalid uid\n"));
2014                 goto process_result;
2015         }
2016
2017         /* what we need here is to find the corresponding krb5 ccache name *we*
2018          * created for a given username and destroy it */
2019
2020         if (!ccache_entry_exists(state->request->data.logoff.user)) {
2021                 result = NT_STATUS_OK;
2022                 DEBUG(10,("winbindd_pam_logoff: no entry found.\n"));
2023                 goto process_result;
2024         }
2025
2026         if (!ccache_entry_identical(state->request->data.logoff.user,
2027                                         state->request->data.logoff.uid,
2028                                         state->request->data.logoff.krb5ccname)) {
2029                 DEBUG(0,("winbindd_pam_logoff: cached entry differs.\n"));
2030                 goto process_result;
2031         }
2032
2033         result = remove_ccache(state->request->data.logoff.user);
2034         if (!NT_STATUS_IS_OK(result)) {
2035                 DEBUG(0,("winbindd_pam_logoff: failed to remove ccache: %s\n",
2036                         nt_errstr(result)));
2037                 goto process_result;
2038         }
2039
2040 #else
2041         result = NT_STATUS_NOT_SUPPORTED;
2042 #endif
2043
2044 process_result:
2045
2046
2047         set_auth_errors(state->response, result);
2048
2049         return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
2050 }
2051
2052 /* Change user password with auth crap*/
2053
2054 enum winbindd_result winbindd_dual_pam_chng_pswd_auth_crap(struct winbindd_domain *domainSt, struct winbindd_cli_state *state)
2055 {
2056         NTSTATUS result;
2057         DATA_BLOB new_nt_password;
2058         DATA_BLOB old_nt_hash_enc;
2059         DATA_BLOB new_lm_password;
2060         DATA_BLOB old_lm_hash_enc;
2061         fstring  domain,user;
2062         struct policy_handle dom_pol;
2063         struct winbindd_domain *contact_domain = domainSt;
2064         struct rpc_pipe_client *cli = NULL;
2065
2066         ZERO_STRUCT(dom_pol);
2067
2068         /* Ensure null termination */
2069         state->request->data.chng_pswd_auth_crap.user[
2070                 sizeof(state->request->data.chng_pswd_auth_crap.user)-1]=0;
2071         state->request->data.chng_pswd_auth_crap.domain[
2072                 sizeof(state->request->data.chng_pswd_auth_crap.domain)-1]=0;
2073         *domain = 0;
2074         *user = 0;
2075
2076         DEBUG(3, ("[%5lu]: pam change pswd auth crap domain: %s user: %s\n",
2077                   (unsigned long)state->pid,
2078                   state->request->data.chng_pswd_auth_crap.domain,
2079                   state->request->data.chng_pswd_auth_crap.user));
2080
2081         if (lp_winbind_offline_logon()) {
2082                 DEBUG(0,("Refusing password change as winbind offline logons are enabled. "));
2083                 DEBUGADD(0,("Changing passwords here would risk inconsistent logons\n"));
2084                 result = NT_STATUS_ACCESS_DENIED;
2085                 goto done;
2086         }
2087
2088         if (*state->request->data.chng_pswd_auth_crap.domain) {
2089                 fstrcpy(domain,state->request->data.chng_pswd_auth_crap.domain);
2090         } else {
2091                 parse_domain_user(state->request->data.chng_pswd_auth_crap.user,
2092                                   domain, user);
2093
2094                 if(!*domain) {
2095                         DEBUG(3,("no domain specified with username (%s) - "
2096                                  "failing auth\n",
2097                                  state->request->data.chng_pswd_auth_crap.user));
2098                         result = NT_STATUS_NO_SUCH_USER;
2099                         goto done;
2100                 }
2101         }
2102
2103         if (!*domain && lp_winbind_use_default_domain()) {
2104                 fstrcpy(domain,(char *)lp_workgroup());
2105         }
2106
2107         if(!*user) {
2108                 fstrcpy(user, state->request->data.chng_pswd_auth_crap.user);
2109         }
2110
2111         DEBUG(3, ("[%5lu]: pam auth crap domain: %s user: %s\n",
2112                   (unsigned long)state->pid, domain, user));
2113
2114         /* Change password */
2115         new_nt_password = data_blob_const(
2116                 state->request->data.chng_pswd_auth_crap.new_nt_pswd,
2117                 state->request->data.chng_pswd_auth_crap.new_nt_pswd_len);
2118
2119         old_nt_hash_enc = data_blob_const(
2120                 state->request->data.chng_pswd_auth_crap.old_nt_hash_enc,
2121                 state->request->data.chng_pswd_auth_crap.old_nt_hash_enc_len);
2122
2123         if(state->request->data.chng_pswd_auth_crap.new_lm_pswd_len > 0)        {
2124                 new_lm_password = data_blob_const(
2125                         state->request->data.chng_pswd_auth_crap.new_lm_pswd,
2126                         state->request->data.chng_pswd_auth_crap.new_lm_pswd_len);
2127
2128                 old_lm_hash_enc = data_blob_const(
2129                         state->request->data.chng_pswd_auth_crap.old_lm_hash_enc,
2130                         state->request->data.chng_pswd_auth_crap.old_lm_hash_enc_len);
2131         } else {
2132                 new_lm_password.length = 0;
2133                 old_lm_hash_enc.length = 0;
2134         }
2135
2136         /* Get sam handle */
2137
2138         result = cm_connect_sam(contact_domain, state->mem_ctx, &cli, &dom_pol);
2139         if (!NT_STATUS_IS_OK(result)) {
2140                 DEBUG(1, ("could not get SAM handle on DC for %s\n", domain));
2141                 goto done;
2142         }
2143
2144         result = rpccli_samr_chng_pswd_auth_crap(
2145                 cli, state->mem_ctx, user, new_nt_password, old_nt_hash_enc,
2146                 new_lm_password, old_lm_hash_enc);
2147
2148  done:
2149
2150         if (strequal(contact_domain->name, get_global_sam_name())) {
2151                 /* FIXME: internal rpc pipe does not cache handles yet */
2152                 if (cli) {
2153                         if (is_valid_policy_hnd(&dom_pol)) {
2154                                 rpccli_samr_Close(cli, state->mem_ctx, &dom_pol);
2155                         }
2156                         TALLOC_FREE(cli);
2157                 }
2158         }
2159
2160         set_auth_errors(state->response, result);
2161
2162         DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2,
2163               ("Password change for user [%s]\\[%s] returned %s (PAM: %d)\n",
2164                domain, user,
2165                state->response->data.auth.nt_status_string,
2166                state->response->data.auth.pam_error));
2167
2168         return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
2169 }