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