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