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