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