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