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