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