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