61c8c298f1b615d1823e45ce6266184abf0a81cb
[ira/wip.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 static uid_t get_uid_from_state(struct winbindd_cli_state *state)
526 {
527         uid_t uid;
528
529         uid = state->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 /**********************************************************************
539  Authenticate a user with a clear text password using Kerberos and fill up
540  ccache if required
541  **********************************************************************/
542
543 static NTSTATUS winbindd_raw_kerberos_login(struct winbindd_domain *domain,
544                                             struct winbindd_cli_state *state,
545                                             struct netr_SamInfo3 **info3)
546 {
547 #ifdef HAVE_KRB5
548         NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
549         krb5_error_code krb5_ret;
550         const char *cc = NULL;
551         const char *principal_s = NULL;
552         const char *service = NULL;
553         char *realm = NULL;
554         fstring name_domain, name_user;
555         time_t ticket_lifetime = 0;
556         time_t renewal_until = 0;
557         uid_t uid = -1;
558         ADS_STRUCT *ads;
559         time_t time_offset = 0;
560         bool internal_ccache = true;
561
562         ZERO_STRUCTP(info3);
563
564         *info3 = NULL;
565
566         /* 1st step:
567          * prepare a krb5_cc_cache string for the user */
568
569         uid = get_uid_from_state(state);
570         if (uid == -1) {
571                 DEBUG(0,("no valid uid\n"));
572         }
573
574         cc = generate_krb5_ccache(state->mem_ctx,
575                                   state->request->data.auth.krb5_cc_type,
576                                   state->request->data.auth.uid,
577                                   &internal_ccache);
578         if (cc == NULL) {
579                 return NT_STATUS_NO_MEMORY;
580         }
581
582
583         /* 2nd step:
584          * get kerberos properties */
585
586         if (domain->private_data) {
587                 ads = (ADS_STRUCT *)domain->private_data;
588                 time_offset = ads->auth.time_offset;
589         }
590
591
592         /* 3rd step:
593          * do kerberos auth and setup ccache as the user */
594
595         parse_domain_user(state->request->data.auth.user, name_domain, name_user);
596
597         realm = domain->alt_name;
598         strupper_m(realm);
599
600         principal_s = talloc_asprintf(state->mem_ctx, "%s@%s", name_user, realm);
601         if (principal_s == NULL) {
602                 return NT_STATUS_NO_MEMORY;
603         }
604
605         service = talloc_asprintf(state->mem_ctx, "%s/%s@%s", KRB5_TGS_NAME, realm, realm);
606         if (service == NULL) {
607                 return NT_STATUS_NO_MEMORY;
608         }
609
610         /* if this is a user ccache, we need to act as the user to let the krb5
611          * library handle the chown, etc. */
612
613         /************************ ENTERING NON-ROOT **********************/
614
615         if (!internal_ccache) {
616                 set_effective_uid(uid);
617                 DEBUG(10,("winbindd_raw_kerberos_login: uid is %d\n", uid));
618         }
619
620         result = kerberos_return_info3_from_pac(state->mem_ctx,
621                                                 principal_s,
622                                                 state->request->data.auth.pass,
623                                                 time_offset,
624                                                 &ticket_lifetime,
625                                                 &renewal_until,
626                                                 cc,
627                                                 true,
628                                                 true,
629                                                 WINBINDD_PAM_AUTH_KRB5_RENEW_TIME,
630                                                 NULL,
631                                                 info3);
632         if (!internal_ccache) {
633                 gain_root_privilege();
634         }
635
636         /************************ RETURNED TO ROOT **********************/
637
638         if (!NT_STATUS_IS_OK(result)) {
639                 goto failed;
640         }
641
642         DEBUG(10,("winbindd_raw_kerberos_login: winbindd validated ticket of %s\n",
643                 principal_s));
644
645         /* if we had a user's ccache then return that string for the pam
646          * environment */
647
648         if (!internal_ccache) {
649
650                 setup_return_cc_name(state, cc);
651
652                 result = add_ccache_to_list(principal_s,
653                                             cc,
654                                             service,
655                                             state->request->data.auth.user,
656                                             realm,
657                                             uid,
658                                             time(NULL),
659                                             ticket_lifetime,
660                                             renewal_until,
661                                             false);
662
663                 if (!NT_STATUS_IS_OK(result)) {
664                         DEBUG(10,("winbindd_raw_kerberos_login: failed to add ccache to list: %s\n",
665                                 nt_errstr(result)));
666                 }
667         } else {
668
669                 /* need to delete the memory cred cache, it is not used anymore */
670
671                 krb5_ret = ads_kdestroy(cc);
672                 if (krb5_ret) {
673                         DEBUG(3,("winbindd_raw_kerberos_login: "
674                                  "could not destroy krb5 credential cache: "
675                                  "%s\n", error_message(krb5_ret)));
676                 }
677
678         }
679
680         return NT_STATUS_OK;
681
682 failed:
683
684         /* we could have created a new credential cache with a valid tgt in it
685          * but we werent able to get or verify the service ticket for this
686          * local host and therefor didn't get the PAC, we need to remove that
687          * cache entirely now */
688
689         krb5_ret = ads_kdestroy(cc);
690         if (krb5_ret) {
691                 DEBUG(3,("winbindd_raw_kerberos_login: "
692                          "could not destroy krb5 credential cache: "
693                          "%s\n", error_message(krb5_ret)));
694         }
695
696         if (!NT_STATUS_IS_OK(remove_ccache(state->request->data.auth.user))) {
697                 DEBUG(3,("winbindd_raw_kerberos_login: "
698                           "could not remove ccache for user %s\n",
699                         state->request->data.auth.user));
700         }
701
702         return result;
703 #else
704         return NT_STATUS_NOT_SUPPORTED;
705 #endif /* HAVE_KRB5 */
706 }
707
708 /****************************************************************
709 ****************************************************************/
710
711 bool check_request_flags(uint32_t flags)
712 {
713         uint32_t flags_edata = WBFLAG_PAM_AFS_TOKEN |
714                                WBFLAG_PAM_INFO3_TEXT |
715                                WBFLAG_PAM_INFO3_NDR;
716
717         if ( ( (flags & flags_edata) == WBFLAG_PAM_AFS_TOKEN) ||
718              ( (flags & flags_edata) == WBFLAG_PAM_INFO3_NDR) ||
719              ( (flags & flags_edata) == WBFLAG_PAM_INFO3_TEXT)||
720               !(flags & flags_edata) ) {
721                 return true;
722         }
723
724         DEBUG(1, ("check_request_flags: invalid request flags[0x%08X]\n",
725                   flags));
726
727         return false;
728 }
729
730 /****************************************************************
731 ****************************************************************/
732
733 static NTSTATUS append_auth_data(struct winbindd_cli_state *state,
734                                  struct netr_SamInfo3 *info3,
735                                  const char *name_domain,
736                                  const char *name_user)
737 {
738         NTSTATUS result;
739         uint32_t flags = state->request->flags;
740
741         if (flags & WBFLAG_PAM_USER_SESSION_KEY) {
742                 memcpy(state->response->data.auth.user_session_key,
743                        info3->base.key.key,
744                        sizeof(state->response->data.auth.user_session_key)
745                        /* 16 */);
746         }
747
748         if (flags & WBFLAG_PAM_LMKEY) {
749                 memcpy(state->response->data.auth.first_8_lm_hash,
750                        info3->base.LMSessKey.key,
751                        sizeof(state->response->data.auth.first_8_lm_hash)
752                        /* 8 */);
753         }
754
755         if (flags & WBFLAG_PAM_INFO3_TEXT) {
756                 result = append_info3_as_txt(state->mem_ctx, state, info3);
757                 if (!NT_STATUS_IS_OK(result)) {
758                         DEBUG(10,("Failed to append INFO3 (TXT): %s\n",
759                                 nt_errstr(result)));
760                         return result;
761                 }
762         }
763
764         /* currently, anything from here on potentially overwrites extra_data. */
765
766         if (flags & WBFLAG_PAM_INFO3_NDR) {
767                 result = append_info3_as_ndr(state->mem_ctx, state, info3);
768                 if (!NT_STATUS_IS_OK(result)) {
769                         DEBUG(10,("Failed to append INFO3 (NDR): %s\n",
770                                 nt_errstr(result)));
771                         return result;
772                 }
773         }
774
775         if (flags & WBFLAG_PAM_UNIX_NAME) {
776                 result = append_unix_username(state->mem_ctx, state, info3,
777                                               name_domain, name_user);
778                 if (!NT_STATUS_IS_OK(result)) {
779                         DEBUG(10,("Failed to append Unix Username: %s\n",
780                                 nt_errstr(result)));
781                         return result;
782                 }
783         }
784
785         if (flags & WBFLAG_PAM_AFS_TOKEN) {
786                 result = append_afs_token(state->mem_ctx, state, info3,
787                                           name_domain, name_user);
788                 if (!NT_STATUS_IS_OK(result)) {
789                         DEBUG(10,("Failed to append AFS token: %s\n",
790                                 nt_errstr(result)));
791                         return result;
792                 }
793         }
794
795         return NT_STATUS_OK;
796 }
797
798 static NTSTATUS winbindd_dual_pam_auth_cached(struct winbindd_domain *domain,
799                                               struct winbindd_cli_state *state,
800                                               struct netr_SamInfo3 **info3)
801 {
802         NTSTATUS result = NT_STATUS_LOGON_FAILURE;
803         uint16 max_allowed_bad_attempts;
804         fstring name_domain, name_user;
805         DOM_SID sid;
806         enum lsa_SidType type;
807         uchar new_nt_pass[NT_HASH_LEN];
808         const uint8 *cached_nt_pass;
809         const uint8 *cached_salt;
810         struct netr_SamInfo3 *my_info3;
811         time_t kickoff_time, must_change_time;
812         bool password_good = false;
813 #ifdef HAVE_KRB5
814         struct winbindd_tdc_domain *tdc_domain = NULL;
815 #endif
816
817         *info3 = NULL;
818
819         ZERO_STRUCTP(info3);
820
821         DEBUG(10,("winbindd_dual_pam_auth_cached\n"));
822
823         /* Parse domain and username */
824
825         parse_domain_user(state->request->data.auth.user, name_domain, name_user);
826
827
828         if (!lookup_cached_name(state->mem_ctx,
829                                 name_domain,
830                                 name_user,
831                                 &sid,
832                                 &type)) {
833                 DEBUG(10,("winbindd_dual_pam_auth_cached: no such user in the cache\n"));
834                 return NT_STATUS_NO_SUCH_USER;
835         }
836
837         if (type != SID_NAME_USER) {
838                 DEBUG(10,("winbindd_dual_pam_auth_cached: not a user (%s)\n", sid_type_lookup(type)));
839                 return NT_STATUS_LOGON_FAILURE;
840         }
841
842         result = winbindd_get_creds(domain,
843                                     state->mem_ctx,
844                                     &sid,
845                                     &my_info3,
846                                     &cached_nt_pass,
847                                     &cached_salt);
848         if (!NT_STATUS_IS_OK(result)) {
849                 DEBUG(10,("winbindd_dual_pam_auth_cached: failed to get creds: %s\n", nt_errstr(result)));
850                 return result;
851         }
852
853         *info3 = my_info3;
854
855         E_md4hash(state->request->data.auth.pass, new_nt_pass);
856
857         dump_data_pw("new_nt_pass", new_nt_pass, NT_HASH_LEN);
858         dump_data_pw("cached_nt_pass", cached_nt_pass, NT_HASH_LEN);
859         if (cached_salt) {
860                 dump_data_pw("cached_salt", cached_salt, NT_HASH_LEN);
861         }
862
863         if (cached_salt) {
864                 /* In this case we didn't store the nt_hash itself,
865                    but the MD5 combination of salt + nt_hash. */
866                 uchar salted_hash[NT_HASH_LEN];
867                 E_md5hash(cached_salt, new_nt_pass, salted_hash);
868
869                 password_good = (memcmp(cached_nt_pass, salted_hash,
870                                         NT_HASH_LEN) == 0);
871         } else {
872                 /* Old cached cred - direct store of nt_hash (bad bad bad !). */
873                 password_good = (memcmp(cached_nt_pass, new_nt_pass,
874                                         NT_HASH_LEN) == 0);
875         }
876
877         if (password_good) {
878
879                 /* User *DOES* know the password, update logon_time and reset
880                  * bad_pw_count */
881
882                 my_info3->base.user_flags |= NETLOGON_CACHED_ACCOUNT;
883
884                 if (my_info3->base.acct_flags & ACB_AUTOLOCK) {
885                         return NT_STATUS_ACCOUNT_LOCKED_OUT;
886                 }
887
888                 if (my_info3->base.acct_flags & ACB_DISABLED) {
889                         return NT_STATUS_ACCOUNT_DISABLED;
890                 }
891
892                 if (my_info3->base.acct_flags & ACB_WSTRUST) {
893                         return NT_STATUS_NOLOGON_WORKSTATION_TRUST_ACCOUNT;
894                 }
895
896                 if (my_info3->base.acct_flags & ACB_SVRTRUST) {
897                         return NT_STATUS_NOLOGON_SERVER_TRUST_ACCOUNT;
898                 }
899
900                 if (my_info3->base.acct_flags & ACB_DOMTRUST) {
901                         return NT_STATUS_NOLOGON_INTERDOMAIN_TRUST_ACCOUNT;
902                 }
903
904                 if (!(my_info3->base.acct_flags & ACB_NORMAL)) {
905                         DEBUG(0,("winbindd_dual_pam_auth_cached: whats wrong with that one?: 0x%08x\n",
906                                 my_info3->base.acct_flags));
907                         return NT_STATUS_LOGON_FAILURE;
908                 }
909
910                 kickoff_time = nt_time_to_unix(my_info3->base.acct_expiry);
911                 if (kickoff_time != 0 && time(NULL) > kickoff_time) {
912                         return NT_STATUS_ACCOUNT_EXPIRED;
913                 }
914
915                 must_change_time = nt_time_to_unix(my_info3->base.force_password_change);
916                 if (must_change_time != 0 && must_change_time < time(NULL)) {
917                         /* we allow grace logons when the password has expired */
918                         my_info3->base.user_flags |= NETLOGON_GRACE_LOGON;
919                         /* return NT_STATUS_PASSWORD_EXPIRED; */
920                         goto success;
921                 }
922
923 #ifdef HAVE_KRB5
924                 if ((state->request->flags & WBFLAG_PAM_KRB5) &&
925                     ((tdc_domain = wcache_tdc_fetch_domain(state->mem_ctx, name_domain)) != NULL) &&
926                     ((tdc_domain->trust_type & NETR_TRUST_TYPE_UPLEVEL) ||
927                     /* used to cope with the case winbindd starting without network. */
928                     !strequal(tdc_domain->domain_name, tdc_domain->dns_name))) {
929
930                         uid_t uid = -1;
931                         const char *cc = NULL;
932                         char *realm = NULL;
933                         const char *principal_s = NULL;
934                         const char *service = NULL;
935                         bool internal_ccache = false;
936
937                         uid = get_uid_from_state(state);
938                         if (uid == -1) {
939                                 DEBUG(0,("winbindd_dual_pam_auth_cached: invalid uid\n"));
940                                 return NT_STATUS_INVALID_PARAMETER;
941                         }
942
943                         cc = generate_krb5_ccache(state->mem_ctx,
944                                                 state->request->data.auth.krb5_cc_type,
945                                                 state->request->data.auth.uid,
946                                                 &internal_ccache);
947                         if (cc == NULL) {
948                                 return NT_STATUS_NO_MEMORY;
949                         }
950
951                         realm = domain->alt_name;
952                         strupper_m(realm);
953
954                         principal_s = talloc_asprintf(state->mem_ctx, "%s@%s", name_user, realm);
955                         if (principal_s == NULL) {
956                                 return NT_STATUS_NO_MEMORY;
957                         }
958
959                         service = talloc_asprintf(state->mem_ctx, "%s/%s@%s", KRB5_TGS_NAME, realm, realm);
960                         if (service == NULL) {
961                                 return NT_STATUS_NO_MEMORY;
962                         }
963
964                         if (!internal_ccache) {
965
966                                 setup_return_cc_name(state, cc);
967
968                                 result = add_ccache_to_list(principal_s,
969                                                             cc,
970                                                             service,
971                                                             state->request->data.auth.user,
972                                                             domain->alt_name,
973                                                             uid,
974                                                             time(NULL),
975                                                             time(NULL) + lp_winbind_cache_time(),
976                                                             time(NULL) + WINBINDD_PAM_AUTH_KRB5_RENEW_TIME,
977                                                             true);
978
979                                 if (!NT_STATUS_IS_OK(result)) {
980                                         DEBUG(10,("winbindd_dual_pam_auth_cached: failed "
981                                                 "to add ccache to list: %s\n",
982                                                 nt_errstr(result)));
983                                 }
984                         }
985                 }
986 #endif /* HAVE_KRB5 */
987  success:
988                 /* FIXME: we possibly should handle logon hours as well (does xp when
989                  * offline?) see auth/auth_sam.c:sam_account_ok for details */
990
991                 unix_to_nt_time(&my_info3->base.last_logon, time(NULL));
992                 my_info3->base.bad_password_count = 0;
993
994                 result = winbindd_update_creds_by_info3(domain,
995                                                         state->mem_ctx,
996                                                         state->request->data.auth.user,
997                                                         state->request->data.auth.pass,
998                                                         my_info3);
999                 if (!NT_STATUS_IS_OK(result)) {
1000                         DEBUG(1,("winbindd_dual_pam_auth_cached: failed to update creds: %s\n",
1001                                 nt_errstr(result)));
1002                         return result;
1003                 }
1004
1005                 return NT_STATUS_OK;
1006
1007         }
1008
1009         /* User does *NOT* know the correct password, modify info3 accordingly */
1010
1011         /* failure of this is not critical */
1012         result = get_max_bad_attempts_from_lockout_policy(domain, state->mem_ctx, &max_allowed_bad_attempts);
1013         if (!NT_STATUS_IS_OK(result)) {
1014                 DEBUG(10,("winbindd_dual_pam_auth_cached: failed to get max_allowed_bad_attempts. "
1015                           "Won't be able to honour account lockout policies\n"));
1016         }
1017
1018         /* increase counter */
1019         my_info3->base.bad_password_count++;
1020
1021         if (max_allowed_bad_attempts == 0) {
1022                 goto failed;
1023         }
1024
1025         /* lockout user */
1026         if (my_info3->base.bad_password_count >= max_allowed_bad_attempts) {
1027
1028                 uint32 password_properties;
1029
1030                 result = get_pwd_properties(domain, state->mem_ctx, &password_properties);
1031                 if (!NT_STATUS_IS_OK(result)) {
1032                         DEBUG(10,("winbindd_dual_pam_auth_cached: failed to get password properties.\n"));
1033                 }
1034
1035                 if ((my_info3->base.rid != DOMAIN_USER_RID_ADMIN) ||
1036                     (password_properties & DOMAIN_PASSWORD_LOCKOUT_ADMINS)) {
1037                         my_info3->base.acct_flags |= ACB_AUTOLOCK;
1038                 }
1039         }
1040
1041 failed:
1042         result = winbindd_update_creds_by_info3(domain,
1043                                                 state->mem_ctx,
1044                                                 state->request->data.auth.user,
1045                                                 NULL,
1046                                                 my_info3);
1047
1048         if (!NT_STATUS_IS_OK(result)) {
1049                 DEBUG(0,("winbindd_dual_pam_auth_cached: failed to update creds %s\n",
1050                         nt_errstr(result)));
1051         }
1052
1053         return NT_STATUS_LOGON_FAILURE;
1054 }
1055
1056 static NTSTATUS winbindd_dual_pam_auth_kerberos(struct winbindd_domain *domain,
1057                                                 struct winbindd_cli_state *state,
1058                                                 struct netr_SamInfo3 **info3)
1059 {
1060         struct winbindd_domain *contact_domain;
1061         fstring name_domain, name_user;
1062         NTSTATUS result;
1063
1064         DEBUG(10,("winbindd_dual_pam_auth_kerberos\n"));
1065
1066         /* Parse domain and username */
1067
1068         parse_domain_user(state->request->data.auth.user, name_domain, name_user);
1069
1070         /* what domain should we contact? */
1071
1072         if ( IS_DC ) {
1073                 if (!(contact_domain = find_domain_from_name(name_domain))) {
1074                         DEBUG(3, ("Authentication for domain for [%s] -> [%s]\\[%s] failed as %s is not a trusted domain\n",
1075                                   state->request->data.auth.user, name_domain, name_user, name_domain));
1076                         result = NT_STATUS_NO_SUCH_USER;
1077                         goto done;
1078                 }
1079
1080         } else {
1081                 if (is_myname(name_domain)) {
1082                         DEBUG(3, ("Authentication for domain %s (local domain to this server) not supported at this stage\n", name_domain));
1083                         result =  NT_STATUS_NO_SUCH_USER;
1084                         goto done;
1085                 }
1086
1087                 contact_domain = find_domain_from_name(name_domain);
1088                 if (contact_domain == NULL) {
1089                         DEBUG(3, ("Authentication for domain for [%s] -> [%s]\\[%s] failed as %s is not a trusted domain\n",
1090                                   state->request->data.auth.user, name_domain, name_user, name_domain));
1091
1092                         contact_domain = find_our_domain();
1093                 }
1094         }
1095
1096         if (contact_domain->initialized &&
1097             contact_domain->active_directory) {
1098                 goto try_login;
1099         }
1100
1101         if (!contact_domain->initialized) {
1102                 init_dc_connection(contact_domain);
1103         }
1104
1105         if (!contact_domain->active_directory) {
1106                 DEBUG(3,("krb5 auth requested but domain is not Active Directory\n"));
1107                 return NT_STATUS_INVALID_LOGON_TYPE;
1108         }
1109 try_login:
1110         result = winbindd_raw_kerberos_login(contact_domain, state, info3);
1111 done:
1112         return result;
1113 }
1114
1115 static NTSTATUS winbindd_dual_auth_passdb(TALLOC_CTX *mem_ctx,
1116                                           const char *domain, const char *user,
1117                                           const DATA_BLOB *challenge,
1118                                           const DATA_BLOB *lm_resp,
1119                                           const DATA_BLOB *nt_resp,
1120                                           struct netr_SamInfo3 **pinfo3)
1121 {
1122         struct auth_usersupplied_info *user_info = NULL;
1123         struct auth_serversupplied_info *server_info = NULL;
1124         struct netr_SamInfo3 *info3;
1125         NTSTATUS status;
1126
1127         status = make_user_info(&user_info, user, user, domain, domain,
1128                                 global_myname(), lm_resp, nt_resp, NULL, NULL,
1129                                 NULL, True);
1130         if (!NT_STATUS_IS_OK(status)) {
1131                 DEBUG(10, ("make_user_info failed: %s\n", nt_errstr(status)));
1132                 return status;
1133         }
1134
1135         status = check_sam_security(challenge, talloc_tos(), user_info,
1136                                     &server_info);
1137         free_user_info(&user_info);
1138
1139         if (!NT_STATUS_IS_OK(status)) {
1140                 DEBUG(10, ("check_ntlm_password failed: %s\n",
1141                            nt_errstr(status)));
1142                 return status;
1143         }
1144
1145         info3 = TALLOC_ZERO_P(mem_ctx, struct netr_SamInfo3);
1146         if (info3 == NULL) {
1147                 return NT_STATUS_NO_MEMORY;
1148         }
1149
1150         status = serverinfo_to_SamInfo3(server_info, NULL, 0, info3);
1151         if (!NT_STATUS_IS_OK(status)) {
1152                 DEBUG(10, ("serverinfo_to_SamInfo3 failed: %s\n",
1153                            nt_errstr(status)));
1154                 return status;
1155         }
1156
1157         DEBUG(10, ("Authenticated user %s\\%s successfully\n", domain, user));
1158         *pinfo3 = info3;
1159         return NT_STATUS_OK;
1160 }
1161
1162 typedef NTSTATUS (*netlogon_fn_t)(struct rpc_pipe_client *cli,
1163                                   TALLOC_CTX *mem_ctx,
1164                                   uint32 logon_parameters,
1165                                   const char *server,
1166                                   const char *username,
1167                                   const char *domain,
1168                                   const char *workstation,
1169                                   const uint8 chal[8],
1170                                   DATA_BLOB lm_response,
1171                                   DATA_BLOB nt_response,
1172                                   struct netr_SamInfo3 **info3);
1173
1174 static NTSTATUS winbindd_dual_pam_auth_samlogon(struct winbindd_domain *domain,
1175                                                 struct winbindd_cli_state *state,
1176                                                 struct netr_SamInfo3 **info3)
1177 {
1178
1179         struct rpc_pipe_client *netlogon_pipe;
1180         uchar chal[8];
1181         DATA_BLOB lm_resp;
1182         DATA_BLOB nt_resp;
1183         int attempts = 0;
1184         unsigned char local_lm_response[24];
1185         unsigned char local_nt_response[24];
1186         fstring name_domain, name_user;
1187         bool retry;
1188         NTSTATUS result;
1189         struct netr_SamInfo3 *my_info3 = NULL;
1190
1191         *info3 = NULL;
1192
1193         DEBUG(10,("winbindd_dual_pam_auth_samlogon\n"));
1194
1195         /* Parse domain and username */
1196
1197         parse_domain_user(state->request->data.auth.user, name_domain, name_user);
1198
1199         /* do password magic */
1200
1201         generate_random_buffer(chal, sizeof(chal));
1202
1203         if (lp_client_ntlmv2_auth()) {
1204                 DATA_BLOB server_chal;
1205                 DATA_BLOB names_blob;
1206                 DATA_BLOB nt_response;
1207                 DATA_BLOB lm_response;
1208                 server_chal = data_blob_talloc(state->mem_ctx, chal, 8);
1209
1210                 /* note that the 'workgroup' here is a best guess - we don't know
1211                    the server's domain at this point.  The 'server name' is also
1212                    dodgy...
1213                 */
1214                 names_blob = NTLMv2_generate_names_blob(state->mem_ctx, global_myname(), lp_workgroup());
1215
1216                 if (!SMBNTLMv2encrypt(NULL, name_user, name_domain,
1217                                       state->request->data.auth.pass,
1218                                       &server_chal,
1219                                       &names_blob,
1220                                       &lm_response, &nt_response, NULL, NULL)) {
1221                         data_blob_free(&names_blob);
1222                         data_blob_free(&server_chal);
1223                         DEBUG(0, ("winbindd_pam_auth: SMBNTLMv2encrypt() failed!\n"));
1224                         result = NT_STATUS_NO_MEMORY;
1225                         goto done;
1226                 }
1227                 data_blob_free(&names_blob);
1228                 data_blob_free(&server_chal);
1229                 lm_resp = data_blob_talloc(state->mem_ctx, lm_response.data,
1230                                            lm_response.length);
1231                 nt_resp = data_blob_talloc(state->mem_ctx, nt_response.data,
1232                                            nt_response.length);
1233                 data_blob_free(&lm_response);
1234                 data_blob_free(&nt_response);
1235
1236         } else {
1237                 if (lp_client_lanman_auth()
1238                     && SMBencrypt(state->request->data.auth.pass,
1239                                   chal,
1240                                   local_lm_response)) {
1241                         lm_resp = data_blob_talloc(state->mem_ctx,
1242                                                    local_lm_response,
1243                                                    sizeof(local_lm_response));
1244                 } else {
1245                         lm_resp = data_blob_null;
1246                 }
1247                 SMBNTencrypt(state->request->data.auth.pass,
1248                              chal,
1249                              local_nt_response);
1250
1251                 nt_resp = data_blob_talloc(state->mem_ctx,
1252                                            local_nt_response,
1253                                            sizeof(local_nt_response));
1254         }
1255
1256         if (strequal(name_domain, get_global_sam_name())) {
1257                 DATA_BLOB chal_blob = data_blob_const(chal, sizeof(chal));
1258
1259                 result = winbindd_dual_auth_passdb(
1260                         state->mem_ctx, name_domain, name_user,
1261                         &chal_blob, &lm_resp, &nt_resp, info3);
1262                 goto done;
1263         }
1264
1265         /* check authentication loop */
1266
1267         do {
1268                 netlogon_fn_t logon_fn;
1269
1270                 ZERO_STRUCTP(my_info3);
1271                 retry = false;
1272
1273                 result = cm_connect_netlogon(domain, &netlogon_pipe);
1274
1275                 if (!NT_STATUS_IS_OK(result)) {
1276                         DEBUG(3, ("could not open handle to NETLOGON pipe\n"));
1277                         goto done;
1278                 }
1279
1280                 /* It is really important to try SamLogonEx here,
1281                  * because in a clustered environment, we want to use
1282                  * one machine account from multiple physical
1283                  * computers.
1284                  *
1285                  * With a normal SamLogon call, we must keep the
1286                  * credentials chain updated and intact between all
1287                  * users of the machine account (which would imply
1288                  * cross-node communication for every NTLM logon).
1289                  *
1290                  * (The credentials chain is not per NETLOGON pipe
1291                  * connection, but globally on the server/client pair
1292                  * by machine name).
1293                  *
1294                  * When using SamLogonEx, the credentials are not
1295                  * supplied, but the session key is implied by the
1296                  * wrapping SamLogon context.
1297                  *
1298                  *  -- abartlet 21 April 2008
1299                  */
1300
1301                 logon_fn = domain->can_do_samlogon_ex
1302                         ? rpccli_netlogon_sam_network_logon_ex
1303                         : rpccli_netlogon_sam_network_logon;
1304
1305                 result = logon_fn(netlogon_pipe,
1306                                   state->mem_ctx,
1307                                   0,
1308                                   domain->dcname,         /* server name */
1309                                   name_user,              /* user name */
1310                                   name_domain,            /* target domain */
1311                                   global_myname(),        /* workstation */
1312                                   chal,
1313                                   lm_resp,
1314                                   nt_resp,
1315                                   &my_info3);
1316                 attempts += 1;
1317
1318                 if ((NT_STATUS_V(result) == DCERPC_FAULT_OP_RNG_ERROR)
1319                     && domain->can_do_samlogon_ex) {
1320                         DEBUG(3, ("Got a DC that can not do NetSamLogonEx, "
1321                                   "retrying with NetSamLogon\n"));
1322                         domain->can_do_samlogon_ex = false;
1323                         retry = true;
1324                         continue;
1325                 }
1326
1327                 /* We have to try a second time as cm_connect_netlogon
1328                    might not yet have noticed that the DC has killed
1329                    our connection. */
1330
1331                 if (!rpccli_is_connected(netlogon_pipe)) {
1332                         retry = true;
1333                         continue;
1334                 }
1335
1336                 /* if we get access denied, a possible cause was that we had
1337                    and open connection to the DC, but someone changed our
1338                    machine account password out from underneath us using 'net
1339                    rpc changetrustpw' */
1340
1341                 if ( NT_STATUS_EQUAL(result, NT_STATUS_ACCESS_DENIED) ) {
1342                         DEBUG(3,("winbindd_pam_auth: sam_logon returned "
1343                                  "ACCESS_DENIED.  Maybe the trust account "
1344                                 "password was changed and we didn't know it. "
1345                                  "Killing connections to domain %s\n",
1346                                 name_domain));
1347                         invalidate_cm_connection(&domain->conn);
1348                         retry = true;
1349                 }
1350
1351         } while ( (attempts < 2) && retry );
1352
1353         /* handle the case where a NT4 DC does not fill in the acct_flags in
1354          * the samlogon reply info3. When accurate info3 is required by the
1355          * caller, we look up the account flags ourselve - gd */
1356
1357         if ((state->request->flags & WBFLAG_PAM_INFO3_TEXT) &&
1358             NT_STATUS_IS_OK(result) && (my_info3->base.acct_flags == 0)) {
1359
1360                 struct rpc_pipe_client *samr_pipe;
1361                 struct policy_handle samr_domain_handle, user_pol;
1362                 union samr_UserInfo *info = NULL;
1363                 NTSTATUS status_tmp;
1364                 uint32 acct_flags;
1365
1366                 status_tmp = cm_connect_sam(domain, state->mem_ctx,
1367                                             &samr_pipe, &samr_domain_handle);
1368
1369                 if (!NT_STATUS_IS_OK(status_tmp)) {
1370                         DEBUG(3, ("could not open handle to SAMR pipe: %s\n",
1371                                 nt_errstr(status_tmp)));
1372                         goto done;
1373                 }
1374
1375                 status_tmp = rpccli_samr_OpenUser(samr_pipe, state->mem_ctx,
1376                                                   &samr_domain_handle,
1377                                                   MAXIMUM_ALLOWED_ACCESS,
1378                                                   my_info3->base.rid,
1379                                                   &user_pol);
1380
1381                 if (!NT_STATUS_IS_OK(status_tmp)) {
1382                         DEBUG(3, ("could not open user handle on SAMR pipe: %s\n",
1383                                 nt_errstr(status_tmp)));
1384                         goto done;
1385                 }
1386
1387                 status_tmp = rpccli_samr_QueryUserInfo(samr_pipe, state->mem_ctx,
1388                                                        &user_pol,
1389                                                        16,
1390                                                        &info);
1391
1392                 if (!NT_STATUS_IS_OK(status_tmp)) {
1393                         DEBUG(3, ("could not query user info on SAMR pipe: %s\n",
1394                                 nt_errstr(status_tmp)));
1395                         rpccli_samr_Close(samr_pipe, state->mem_ctx, &user_pol);
1396                         goto done;
1397                 }
1398
1399                 acct_flags = info->info16.acct_flags;
1400
1401                 if (acct_flags == 0) {
1402                         rpccli_samr_Close(samr_pipe, state->mem_ctx, &user_pol);
1403                         goto done;
1404                 }
1405
1406                 my_info3->base.acct_flags = acct_flags;
1407
1408                 DEBUG(10,("successfully retrieved acct_flags 0x%x\n", acct_flags));
1409
1410                 rpccli_samr_Close(samr_pipe, state->mem_ctx, &user_pol);
1411         }
1412
1413         *info3 = my_info3;
1414 done:
1415         return result;
1416 }
1417
1418 enum winbindd_result winbindd_dual_pam_auth(struct winbindd_domain *domain,
1419                                             struct winbindd_cli_state *state)
1420 {
1421         NTSTATUS result = NT_STATUS_LOGON_FAILURE;
1422         NTSTATUS krb5_result = NT_STATUS_OK;
1423         fstring name_domain, name_user;
1424         char *mapped_user;
1425         fstring domain_user;
1426         struct netr_SamInfo3 *info3 = NULL;
1427         NTSTATUS name_map_status = NT_STATUS_UNSUCCESSFUL;
1428
1429         /* Ensure null termination */
1430         state->request->data.auth.user[sizeof(state->request->data.auth.user)-1]='\0';
1431
1432         /* Ensure null termination */
1433         state->request->data.auth.pass[sizeof(state->request->data.auth.pass)-1]='\0';
1434
1435         DEBUG(3, ("[%5lu]: dual pam auth %s\n", (unsigned long)state->pid,
1436                   state->request->data.auth.user));
1437
1438         if (!check_request_flags(state->request->flags)) {
1439                 result = NT_STATUS_INVALID_PARAMETER_MIX;
1440                 goto done;
1441         }
1442
1443         /* Parse domain and username */
1444
1445         name_map_status = normalize_name_unmap(state->mem_ctx,
1446                                                state->request->data.auth.user,
1447                                                &mapped_user);
1448
1449         /* If the name normalization didnt' actually do anything,
1450            just use the original name */
1451
1452         if (!NT_STATUS_IS_OK(name_map_status) &&
1453             !NT_STATUS_EQUAL(name_map_status, NT_STATUS_FILE_RENAMED))
1454         {
1455                 mapped_user = state->request->data.auth.user;
1456         }
1457
1458         parse_domain_user(mapped_user, name_domain, name_user);
1459
1460         if ( mapped_user != state->request->data.auth.user ) {
1461                 fstr_sprintf( domain_user, "%s\\%s", name_domain, name_user );
1462                 safe_strcpy( state->request->data.auth.user, domain_user,
1463                              sizeof(state->request->data.auth.user)-1 );
1464         }
1465
1466         if (domain->online == false) {
1467                 result = NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND;
1468                 if (domain->startup) {
1469                         /* Logons are very important to users. If we're offline and
1470                            we get a request within the first 30 seconds of startup,
1471                            try very hard to find a DC and go online. */
1472
1473                         DEBUG(10,("winbindd_dual_pam_auth: domain: %s offline and auth "
1474                                 "request in startup mode.\n", domain->name ));
1475
1476                         winbindd_flush_negative_conn_cache(domain);
1477                         result = init_dc_connection(domain);
1478                 }
1479         }
1480
1481         DEBUG(10,("winbindd_dual_pam_auth: domain: %s last was %s\n", domain->name, domain->online ? "online":"offline"));
1482
1483         /* Check for Kerberos authentication */
1484         if (domain->online && (state->request->flags & WBFLAG_PAM_KRB5)) {
1485
1486                 result = winbindd_dual_pam_auth_kerberos(domain, state, &info3);
1487                 /* save for later */
1488                 krb5_result = result;
1489
1490
1491                 if (NT_STATUS_IS_OK(result)) {
1492                         DEBUG(10,("winbindd_dual_pam_auth_kerberos succeeded\n"));
1493                         goto process_result;
1494                 } else {
1495                         DEBUG(10,("winbindd_dual_pam_auth_kerberos failed: %s\n", nt_errstr(result)));
1496                 }
1497
1498                 if (NT_STATUS_EQUAL(result, NT_STATUS_NO_LOGON_SERVERS) ||
1499                     NT_STATUS_EQUAL(result, NT_STATUS_IO_TIMEOUT) ||
1500                     NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
1501                         DEBUG(10,("winbindd_dual_pam_auth_kerberos setting domain to offline\n"));
1502                         set_domain_offline( domain );
1503                         goto cached_logon;
1504                 }
1505
1506                 /* there are quite some NT_STATUS errors where there is no
1507                  * point in retrying with a samlogon, we explictly have to take
1508                  * care not to increase the bad logon counter on the DC */
1509
1510                 if (NT_STATUS_EQUAL(result, NT_STATUS_ACCOUNT_DISABLED) ||
1511                     NT_STATUS_EQUAL(result, NT_STATUS_ACCOUNT_EXPIRED) ||
1512                     NT_STATUS_EQUAL(result, NT_STATUS_ACCOUNT_LOCKED_OUT) ||
1513                     NT_STATUS_EQUAL(result, NT_STATUS_INVALID_LOGON_HOURS) ||
1514                     NT_STATUS_EQUAL(result, NT_STATUS_INVALID_WORKSTATION) ||
1515                     NT_STATUS_EQUAL(result, NT_STATUS_LOGON_FAILURE) ||
1516                     NT_STATUS_EQUAL(result, NT_STATUS_NO_SUCH_USER) ||
1517                     NT_STATUS_EQUAL(result, NT_STATUS_PASSWORD_EXPIRED) ||
1518                     NT_STATUS_EQUAL(result, NT_STATUS_PASSWORD_MUST_CHANGE) ||
1519                     NT_STATUS_EQUAL(result, NT_STATUS_WRONG_PASSWORD)) {
1520                         goto done;
1521                 }
1522
1523                 if (state->request->flags & WBFLAG_PAM_FALLBACK_AFTER_KRB5) {
1524                         DEBUG(3,("falling back to samlogon\n"));
1525                         goto sam_logon;
1526                 } else {
1527                         goto cached_logon;
1528                 }
1529         }
1530
1531 sam_logon:
1532         /* Check for Samlogon authentication */
1533         if (domain->online) {
1534                 result = winbindd_dual_pam_auth_samlogon(domain, state, &info3);
1535
1536                 if (NT_STATUS_IS_OK(result)) {
1537                         DEBUG(10,("winbindd_dual_pam_auth_samlogon succeeded\n"));
1538                         /* add the Krb5 err if we have one */
1539                         if ( NT_STATUS_EQUAL(krb5_result, NT_STATUS_TIME_DIFFERENCE_AT_DC ) ) {
1540                                 info3->base.user_flags |= LOGON_KRB5_FAIL_CLOCK_SKEW;
1541                         }
1542                         goto process_result;
1543                 }
1544
1545                 DEBUG(10,("winbindd_dual_pam_auth_samlogon failed: %s\n",
1546                           nt_errstr(result)));
1547
1548                 if (NT_STATUS_EQUAL(result, NT_STATUS_NO_LOGON_SERVERS) ||
1549                     NT_STATUS_EQUAL(result, NT_STATUS_IO_TIMEOUT) ||
1550                     NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND))
1551                 {
1552                         DEBUG(10,("winbindd_dual_pam_auth_samlogon setting domain to offline\n"));
1553                         set_domain_offline( domain );
1554                         goto cached_logon;
1555                 }
1556
1557                         if (domain->online) {
1558                                 /* We're still online - fail. */
1559                                 goto done;
1560                         }
1561         }
1562
1563 cached_logon:
1564         /* Check for Cached logons */
1565         if (!domain->online && (state->request->flags & WBFLAG_PAM_CACHED_LOGIN) &&
1566             lp_winbind_offline_logon()) {
1567
1568                 result = winbindd_dual_pam_auth_cached(domain, state, &info3);
1569
1570                 if (NT_STATUS_IS_OK(result)) {
1571                         DEBUG(10,("winbindd_dual_pam_auth_cached succeeded\n"));
1572                         goto process_result;
1573                 } else {
1574                         DEBUG(10,("winbindd_dual_pam_auth_cached failed: %s\n", nt_errstr(result)));
1575                         goto done;
1576                 }
1577         }
1578
1579 process_result:
1580
1581         if (NT_STATUS_IS_OK(result)) {
1582
1583                 DOM_SID user_sid;
1584
1585                 /* In all codepaths where result == NT_STATUS_OK info3 must have
1586                    been initialized. */
1587                 if (!info3) {
1588                         result = NT_STATUS_INTERNAL_ERROR;
1589                         goto done;
1590                 }
1591
1592                 wcache_invalidate_samlogon(find_domain_from_name(name_domain), info3);
1593                 netsamlogon_cache_store(name_user, info3);
1594
1595                 /* save name_to_sid info as early as possible (only if
1596                    this is our primary domain so we don't invalidate
1597                    the cache entry by storing the seq_num for the wrong
1598                    domain). */
1599                 if ( domain->primary ) {
1600                         sid_compose(&user_sid, info3->base.domain_sid,
1601                                     info3->base.rid);
1602                         cache_name2sid(domain, name_domain, name_user,
1603                                        SID_NAME_USER, &user_sid);
1604                 }
1605
1606                 /* Check if the user is in the right group */
1607
1608                 result = check_info3_in_group(
1609                         info3,
1610                         state->request->data.auth.require_membership_of_sid);
1611                 if (!NT_STATUS_IS_OK(result)) {
1612                         DEBUG(3, ("User %s is not in the required group (%s), so plaintext authentication is rejected\n",
1613                                   state->request->data.auth.user,
1614                                   state->request->data.auth.require_membership_of_sid));
1615                         goto done;
1616                 }
1617
1618                 result = append_auth_data(state, info3, name_domain,
1619                                           name_user);
1620                 if (!NT_STATUS_IS_OK(result)) {
1621                         goto done;
1622                 }
1623
1624                 if ((state->request->flags & WBFLAG_PAM_CACHED_LOGIN)) {
1625
1626                         /* Store in-memory creds for single-signon using ntlm_auth. */
1627                         result = winbindd_add_memory_creds(state->request->data.auth.user,
1628                                                         get_uid_from_state(state),
1629                                                         state->request->data.auth.pass);
1630
1631                         if (!NT_STATUS_IS_OK(result)) {
1632                                 DEBUG(10,("Failed to store memory creds: %s\n", nt_errstr(result)));
1633                                 goto done;
1634                         }
1635
1636                         if (lp_winbind_offline_logon()) {
1637                                 result = winbindd_store_creds(domain,
1638                                                       state->mem_ctx,
1639                                                       state->request->data.auth.user,
1640                                                       state->request->data.auth.pass,
1641                                                       info3, NULL);
1642                                 if (!NT_STATUS_IS_OK(result)) {
1643
1644                                         /* Release refcount. */
1645                                         winbindd_delete_memory_creds(state->request->data.auth.user);
1646
1647                                         DEBUG(10,("Failed to store creds: %s\n", nt_errstr(result)));
1648                                         goto done;
1649                                 }
1650                         }
1651                 }
1652
1653
1654                 if (state->request->flags & WBFLAG_PAM_GET_PWD_POLICY) {
1655                         struct winbindd_domain *our_domain = find_our_domain();
1656
1657                         /* This is not entirely correct I believe, but it is
1658                            consistent.  Only apply the password policy settings
1659                            too warn users for our own domain.  Cannot obtain these
1660                            from trusted DCs all the  time so don't do it at all.
1661                            -- jerry */
1662
1663                         result = NT_STATUS_NOT_SUPPORTED;
1664                         if (our_domain == domain ) {
1665                                 result = fillup_password_policy(our_domain, state);
1666                         }
1667
1668                         if (!NT_STATUS_IS_OK(result)
1669                             && !NT_STATUS_EQUAL(result, NT_STATUS_NOT_SUPPORTED) )
1670                         {
1671                                 DEBUG(10,("Failed to get password policies for domain %s: %s\n",
1672                                           domain->name, nt_errstr(result)));
1673                                 goto done;
1674                         }
1675                 }
1676
1677                 result = NT_STATUS_OK;
1678         }
1679
1680 done:
1681         /* give us a more useful (more correct?) error code */
1682         if ((NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND) ||
1683             (NT_STATUS_EQUAL(result, NT_STATUS_UNSUCCESSFUL)))) {
1684                 result = NT_STATUS_NO_LOGON_SERVERS;
1685         }
1686
1687         set_auth_errors(state->response, result);
1688
1689         DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2, ("Plain-text authentication for user %s returned %s (PAM: %d)\n",
1690               state->request->data.auth.user,
1691               state->response->data.auth.nt_status_string,
1692               state->response->data.auth.pam_error));
1693
1694         return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
1695 }
1696
1697 enum winbindd_result winbindd_dual_pam_auth_crap(struct winbindd_domain *domain,
1698                                                  struct winbindd_cli_state *state)
1699 {
1700         NTSTATUS result;
1701         struct netr_SamInfo3 *info3 = NULL;
1702         struct rpc_pipe_client *netlogon_pipe;
1703         const char *name_user = NULL;
1704         const char *name_domain = NULL;
1705         const char *workstation;
1706         int attempts = 0;
1707         bool retry;
1708
1709         DATA_BLOB lm_resp, nt_resp;
1710
1711         /* This is child-only, so no check for privileged access is needed
1712            anymore */
1713
1714         /* Ensure null termination */
1715         state->request->data.auth_crap.user[sizeof(state->request->data.auth_crap.user)-1]=0;
1716         state->request->data.auth_crap.domain[sizeof(state->request->data.auth_crap.domain)-1]=0;
1717
1718         if (!check_request_flags(state->request->flags)) {
1719                 result = NT_STATUS_INVALID_PARAMETER_MIX;
1720                 goto done;
1721         }
1722
1723         name_user = state->request->data.auth_crap.user;
1724
1725         if (*state->request->data.auth_crap.domain) {
1726                 name_domain = state->request->data.auth_crap.domain;
1727         } else if (lp_winbind_use_default_domain()) {
1728                 name_domain = lp_workgroup();
1729         } else {
1730                 DEBUG(5,("no domain specified with username (%s) - failing auth\n",
1731                          name_user));
1732                 result = NT_STATUS_NO_SUCH_USER;
1733                 goto done;
1734         }
1735
1736         DEBUG(3, ("[%5lu]: pam auth crap domain: %s user: %s\n", (unsigned long)state->pid,
1737                   name_domain, name_user));
1738
1739         if (*state->request->data.auth_crap.workstation) {
1740                 workstation = state->request->data.auth_crap.workstation;
1741         } else {
1742                 workstation = global_myname();
1743         }
1744
1745         if (state->request->data.auth_crap.lm_resp_len > sizeof(state->request->data.auth_crap.lm_resp)
1746                 || state->request->data.auth_crap.nt_resp_len > sizeof(state->request->data.auth_crap.nt_resp)) {
1747                 if (!(state->request->flags & WBFLAG_BIG_NTLMV2_BLOB) ||
1748                      state->request->extra_len != state->request->data.auth_crap.nt_resp_len) {
1749                         DEBUG(0, ("winbindd_pam_auth_crap: invalid password length %u/%u\n",
1750                                   state->request->data.auth_crap.lm_resp_len,
1751                                   state->request->data.auth_crap.nt_resp_len));
1752                         result = NT_STATUS_INVALID_PARAMETER;
1753                         goto done;
1754                 }
1755         }
1756
1757         lm_resp = data_blob_talloc(state->mem_ctx, state->request->data.auth_crap.lm_resp,
1758                                         state->request->data.auth_crap.lm_resp_len);
1759
1760         if (state->request->flags & WBFLAG_BIG_NTLMV2_BLOB) {
1761                 nt_resp = data_blob_talloc(state->mem_ctx,
1762                                            state->request->extra_data.data,
1763                                            state->request->data.auth_crap.nt_resp_len);
1764         } else {
1765                 nt_resp = data_blob_talloc(state->mem_ctx,
1766                                            state->request->data.auth_crap.nt_resp,
1767                                            state->request->data.auth_crap.nt_resp_len);
1768         }
1769
1770         if (strequal(name_domain, get_global_sam_name())) {
1771                 DATA_BLOB chal_blob = data_blob_const(
1772                         state->request->data.auth_crap.chal,
1773                         sizeof(state->request->data.auth_crap.chal));
1774
1775                 result = winbindd_dual_auth_passdb(
1776                         state->mem_ctx, name_domain, name_user,
1777                         &chal_blob, &lm_resp, &nt_resp, &info3);
1778                 goto process_result;
1779         }
1780
1781         do {
1782                 netlogon_fn_t logon_fn;
1783
1784                 retry = false;
1785
1786                 netlogon_pipe = NULL;
1787                 result = cm_connect_netlogon(domain, &netlogon_pipe);
1788
1789                 if (!NT_STATUS_IS_OK(result)) {
1790                         DEBUG(3, ("could not open handle to NETLOGON pipe (error: %s)\n",
1791                                   nt_errstr(result)));
1792                         goto done;
1793                 }
1794
1795                 logon_fn = domain->can_do_samlogon_ex
1796                         ? rpccli_netlogon_sam_network_logon_ex
1797                         : rpccli_netlogon_sam_network_logon;
1798
1799                 result = logon_fn(netlogon_pipe,
1800                                   state->mem_ctx,
1801                                   state->request->data.auth_crap.logon_parameters,
1802                                   domain->dcname,
1803                                   name_user,
1804                                   name_domain,
1805                                   /* Bug #3248 - found by Stefan Burkei. */
1806                                   workstation, /* We carefully set this above so use it... */
1807                                   state->request->data.auth_crap.chal,
1808                                   lm_resp,
1809                                   nt_resp,
1810                                   &info3);
1811
1812                 if ((NT_STATUS_V(result) == DCERPC_FAULT_OP_RNG_ERROR)
1813                     && domain->can_do_samlogon_ex) {
1814                         DEBUG(3, ("Got a DC that can not do NetSamLogonEx, "
1815                                   "retrying with NetSamLogon\n"));
1816                         domain->can_do_samlogon_ex = false;
1817                         retry = true;
1818                         continue;
1819                 }
1820
1821                 attempts += 1;
1822
1823                 /* We have to try a second time as cm_connect_netlogon
1824                    might not yet have noticed that the DC has killed
1825                    our connection. */
1826
1827                 if (!rpccli_is_connected(netlogon_pipe)) {
1828                         retry = true;
1829                         continue;
1830                 }
1831
1832                 /* if we get access denied, a possible cause was that we had and open
1833                    connection to the DC, but someone changed our machine account password
1834                    out from underneath us using 'net rpc changetrustpw' */
1835
1836                 if ( NT_STATUS_EQUAL(result, NT_STATUS_ACCESS_DENIED) ) {
1837                         DEBUG(3,("winbindd_pam_auth: sam_logon returned "
1838                                  "ACCESS_DENIED.  Maybe the trust account "
1839                                 "password was changed and we didn't know it. "
1840                                  "Killing connections to domain %s\n",
1841                                 name_domain));
1842                         invalidate_cm_connection(&domain->conn);
1843                         retry = true;
1844                 }
1845
1846         } while ( (attempts < 2) && retry );
1847
1848 process_result:
1849
1850         if (NT_STATUS_IS_OK(result)) {
1851
1852                 wcache_invalidate_samlogon(find_domain_from_name(name_domain), info3);
1853                 netsamlogon_cache_store(name_user, info3);
1854
1855                 /* Check if the user is in the right group */
1856
1857                 result = check_info3_in_group(
1858                         info3,
1859                         state->request->data.auth_crap.require_membership_of_sid);
1860                 if (!NT_STATUS_IS_OK(result)) {
1861                         DEBUG(3, ("User %s is not in the required group (%s), so "
1862                                   "crap authentication is rejected\n",
1863                                   state->request->data.auth_crap.user,
1864                                   state->request->data.auth_crap.require_membership_of_sid));
1865                         goto done;
1866                 }
1867
1868                 result = append_auth_data(state, info3, name_domain,
1869                                           name_user);
1870                 if (!NT_STATUS_IS_OK(result)) {
1871                         goto done;
1872                 }
1873         }
1874
1875 done:
1876
1877         /* give us a more useful (more correct?) error code */
1878         if ((NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND) ||
1879             (NT_STATUS_EQUAL(result, NT_STATUS_UNSUCCESSFUL)))) {
1880                 result = NT_STATUS_NO_LOGON_SERVERS;
1881         }
1882
1883         if (state->request->flags & WBFLAG_PAM_NT_STATUS_SQUASH) {
1884                 result = nt_status_squash(result);
1885         }
1886
1887         set_auth_errors(state->response, result);
1888
1889         DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2,
1890               ("NTLM CRAP authentication for user [%s]\\[%s] returned %s (PAM: %d)\n",
1891                name_domain,
1892                name_user,
1893                state->response->data.auth.nt_status_string,
1894                state->response->data.auth.pam_error));
1895
1896         return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
1897 }
1898
1899 enum winbindd_result winbindd_dual_pam_chauthtok(struct winbindd_domain *contact_domain,
1900                                                  struct winbindd_cli_state *state)
1901 {
1902         char *oldpass;
1903         char *newpass = NULL;
1904         struct policy_handle dom_pol;
1905         struct rpc_pipe_client *cli;
1906         bool got_info = false;
1907         struct samr_DomInfo1 *info = NULL;
1908         struct userPwdChangeFailureInformation *reject = NULL;
1909         NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
1910         fstring domain, user;
1911
1912         DEBUG(3, ("[%5lu]: dual pam chauthtok %s\n", (unsigned long)state->pid,
1913                   state->request->data.auth.user));
1914
1915         if (!parse_domain_user(state->request->data.chauthtok.user, domain, user)) {
1916                 goto done;
1917         }
1918
1919         /* Change password */
1920
1921         oldpass = state->request->data.chauthtok.oldpass;
1922         newpass = state->request->data.chauthtok.newpass;
1923
1924         /* Initialize reject reason */
1925         state->response->data.auth.reject_reason = Undefined;
1926
1927         /* Get sam handle */
1928
1929         result = cm_connect_sam(contact_domain, state->mem_ctx, &cli,
1930                                 &dom_pol);
1931         if (!NT_STATUS_IS_OK(result)) {
1932                 DEBUG(1, ("could not get SAM handle on DC for %s\n", domain));
1933                 goto done;
1934         }
1935
1936         result = rpccli_samr_chgpasswd_user3(cli, state->mem_ctx,
1937                                              user,
1938                                              newpass,
1939                                              oldpass,
1940                                              &info,
1941                                              &reject);
1942
1943         /* Windows 2003 returns NT_STATUS_PASSWORD_RESTRICTION */
1944
1945         if (NT_STATUS_EQUAL(result, NT_STATUS_PASSWORD_RESTRICTION) ) {
1946
1947                 fill_in_password_policy(state->response, info);
1948
1949                 state->response->data.auth.reject_reason =
1950                         reject->extendedFailureReason;
1951
1952                 got_info = true;
1953         }
1954
1955         /* atm the pidl generated rpccli_samr_ChangePasswordUser3 function will
1956          * return with NT_STATUS_BUFFER_TOO_SMALL for w2k dcs as w2k just
1957          * returns with 4byte error code (NT_STATUS_NOT_SUPPORTED) which is too
1958          * short to comply with the samr_ChangePasswordUser3 idl - gd */
1959
1960         /* only fallback when the chgpasswd_user3 call is not supported */
1961         if ((NT_STATUS_EQUAL(result, NT_STATUS(DCERPC_FAULT_OP_RNG_ERROR))) ||
1962                    (NT_STATUS_EQUAL(result, NT_STATUS_NOT_SUPPORTED)) ||
1963                    (NT_STATUS_EQUAL(result, NT_STATUS_BUFFER_TOO_SMALL)) ||
1964                    (NT_STATUS_EQUAL(result, NT_STATUS_NOT_IMPLEMENTED))) {
1965
1966                 DEBUG(10,("Password change with chgpasswd_user3 failed with: %s, retrying chgpasswd_user2\n",
1967                         nt_errstr(result)));
1968
1969                 result = rpccli_samr_chgpasswd_user2(cli, state->mem_ctx, user, newpass, oldpass);
1970
1971                 /* Windows 2000 returns NT_STATUS_ACCOUNT_RESTRICTION.
1972                    Map to the same status code as Windows 2003. */
1973
1974                 if ( NT_STATUS_EQUAL(NT_STATUS_ACCOUNT_RESTRICTION, result ) ) {
1975                         result = NT_STATUS_PASSWORD_RESTRICTION;
1976                 }
1977         }
1978
1979 done:
1980
1981         if (NT_STATUS_IS_OK(result) && (state->request->flags & WBFLAG_PAM_CACHED_LOGIN)) {
1982
1983                 /* Update the single sign-on memory creds. */
1984                 result = winbindd_replace_memory_creds(state->request->data.chauthtok.user,
1985                                                         newpass);
1986
1987                 /* When we login from gdm or xdm and password expires,
1988                  * we change password, but there are no memory crendentials
1989                  * So, winbindd_replace_memory_creds() returns
1990                  * NT_STATUS_OBJECT_NAME_NOT_FOUND. This is not a failure.
1991                  * --- BoYang
1992                  * */
1993                 if (NT_STATUS_EQUAL(result, NT_STATUS_OBJECT_NAME_NOT_FOUND)) {
1994                         result = NT_STATUS_OK;
1995                 }
1996
1997                 if (!NT_STATUS_IS_OK(result)) {
1998                         DEBUG(10,("Failed to replace memory creds: %s\n", nt_errstr(result)));
1999                         goto process_result;
2000                 }
2001
2002                 if (lp_winbind_offline_logon()) {
2003                         result = winbindd_update_creds_by_name(contact_domain,
2004                                                          state->mem_ctx, user,
2005                                                          newpass);
2006                         /* Again, this happens when we login from gdm or xdm
2007                          * and the password expires, *BUT* cached crendentials
2008                          * doesn't exist. winbindd_update_creds_by_name()
2009                          * returns NT_STATUS_NO_SUCH_USER.
2010                          * This is not a failure.
2011                          * --- BoYang
2012                          * */
2013                         if (NT_STATUS_EQUAL(result, NT_STATUS_NO_SUCH_USER)) {
2014                                 result = NT_STATUS_OK;
2015                         }
2016
2017                         if (!NT_STATUS_IS_OK(result)) {
2018                                 DEBUG(10,("Failed to store creds: %s\n", nt_errstr(result)));
2019                                 goto process_result;
2020                         }
2021                 }
2022         }
2023
2024         if (!NT_STATUS_IS_OK(result) && !got_info && contact_domain) {
2025
2026                 NTSTATUS policy_ret;
2027
2028                 policy_ret = fillup_password_policy(contact_domain, state);
2029
2030                 /* failure of this is non critical, it will just provide no
2031                  * additional information to the client why the change has
2032                  * failed - Guenther */
2033
2034                 if (!NT_STATUS_IS_OK(policy_ret)) {
2035                         DEBUG(10,("Failed to get password policies: %s\n", nt_errstr(policy_ret)));
2036                         goto process_result;
2037                 }
2038         }
2039
2040 process_result:
2041
2042         set_auth_errors(state->response, result);
2043
2044         DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2,
2045               ("Password change for user [%s]\\[%s] returned %s (PAM: %d)\n",
2046                domain,
2047                user,
2048                state->response->data.auth.nt_status_string,
2049                state->response->data.auth.pam_error));
2050
2051         return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
2052 }
2053
2054 void winbindd_pam_logoff(struct winbindd_cli_state *state)
2055 {
2056         struct winbindd_domain *domain;
2057         fstring name_domain, user;
2058         uid_t caller_uid = (uid_t)-1;
2059         uid_t request_uid = state->request->data.logoff.uid;
2060
2061         /* Ensure null termination */
2062         state->request->data.logoff.user
2063                 [sizeof(state->request->data.logoff.user)-1]='\0';
2064
2065         state->request->data.logoff.krb5ccname
2066                 [sizeof(state->request->data.logoff.krb5ccname)-1]='\0';
2067
2068         DEBUG(3, ("[%5lu]: pam logoff %s\n", (unsigned long)state->pid,
2069                 state->request->data.logoff.user));
2070
2071         if (request_uid == (uid_t)-1) {
2072                 goto failed;
2073         }
2074
2075         if (!canonicalize_username(state->request->data.logoff.user, name_domain, user)) {
2076                 goto failed;
2077         }
2078
2079         if ((domain = find_auth_domain(state->request->flags,
2080                                        name_domain)) == NULL) {
2081                 goto failed;
2082         }
2083
2084         if ((sys_getpeereid(state->sock, &caller_uid)) != 0) {
2085                 DEBUG(1,("winbindd_pam_logoff: failed to check peerid: %s\n",
2086                         strerror(errno)));
2087                 goto failed;
2088         }
2089
2090         switch (caller_uid) {
2091                 case -1:
2092                         goto failed;
2093                 case 0:
2094                         /* root must be able to logoff any user - gd */
2095                         state->request->data.logoff.uid = request_uid;
2096                         break;
2097                 default:
2098                         if (caller_uid != request_uid) {
2099                                 DEBUG(1,("winbindd_pam_logoff: caller requested invalid uid\n"));
2100                                 goto failed;
2101                         }
2102                         state->request->data.logoff.uid = caller_uid;
2103                         break;
2104         }
2105
2106         sendto_domain(state, domain);
2107         return;
2108
2109  failed:
2110         set_auth_errors(state->response, NT_STATUS_NO_SUCH_USER);
2111         DEBUG(5, ("Pam Logoff for %s returned %s "
2112                   "(PAM: %d)\n",
2113                   state->request->data.logoff.user,
2114                   state->response->data.auth.nt_status_string,
2115                   state->response->data.auth.pam_error));
2116         request_error(state);
2117         return;
2118 }
2119
2120 enum winbindd_result winbindd_dual_pam_logoff(struct winbindd_domain *domain,
2121                                               struct winbindd_cli_state *state)
2122 {
2123         NTSTATUS result = NT_STATUS_NOT_SUPPORTED;
2124
2125         DEBUG(3, ("[%5lu]: pam dual logoff %s\n", (unsigned long)state->pid,
2126                 state->request->data.logoff.user));
2127
2128         if (!(state->request->flags & WBFLAG_PAM_KRB5)) {
2129                 result = NT_STATUS_OK;
2130                 goto process_result;
2131         }
2132
2133         if (state->request->data.logoff.krb5ccname[0] == '\0') {
2134                 result = NT_STATUS_OK;
2135                 goto process_result;
2136         }
2137
2138 #ifdef HAVE_KRB5
2139
2140         if (state->request->data.logoff.uid < 0) {
2141                 DEBUG(0,("winbindd_pam_logoff: invalid uid\n"));
2142                 goto process_result;
2143         }
2144
2145         /* what we need here is to find the corresponding krb5 ccache name *we*
2146          * created for a given username and destroy it */
2147
2148         if (!ccache_entry_exists(state->request->data.logoff.user)) {
2149                 result = NT_STATUS_OK;
2150                 DEBUG(10,("winbindd_pam_logoff: no entry found.\n"));
2151                 goto process_result;
2152         }
2153
2154         if (!ccache_entry_identical(state->request->data.logoff.user,
2155                                         state->request->data.logoff.uid,
2156                                         state->request->data.logoff.krb5ccname)) {
2157                 DEBUG(0,("winbindd_pam_logoff: cached entry differs.\n"));
2158                 goto process_result;
2159         }
2160
2161         result = remove_ccache(state->request->data.logoff.user);
2162         if (!NT_STATUS_IS_OK(result)) {
2163                 DEBUG(0,("winbindd_pam_logoff: failed to remove ccache: %s\n",
2164                         nt_errstr(result)));
2165                 goto process_result;
2166         }
2167
2168 #else
2169         result = NT_STATUS_NOT_SUPPORTED;
2170 #endif
2171
2172 process_result:
2173
2174         winbindd_delete_memory_creds(state->request->data.logoff.user);
2175
2176         set_auth_errors(state->response, result);
2177
2178         return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
2179 }
2180
2181 /* Change user password with auth crap*/
2182
2183 void winbindd_pam_chng_pswd_auth_crap(struct winbindd_cli_state *state)
2184 {
2185         struct winbindd_domain *domain = NULL;
2186         const char *domain_name = NULL;
2187
2188         /* Ensure null termination */
2189         state->request->data.chng_pswd_auth_crap.user[
2190                 sizeof(state->request->data.chng_pswd_auth_crap.user)-1]=0;
2191         state->request->data.chng_pswd_auth_crap.domain[
2192                 sizeof(state->request->data.chng_pswd_auth_crap.domain)-1]=0;
2193
2194         DEBUG(3, ("[%5lu]: pam change pswd auth crap domain: %s user: %s\n",
2195                   (unsigned long)state->pid,
2196                   state->request->data.chng_pswd_auth_crap.domain,
2197                   state->request->data.chng_pswd_auth_crap.user));
2198
2199         if (*state->request->data.chng_pswd_auth_crap.domain != '\0') {
2200                 domain_name = state->request->data.chng_pswd_auth_crap.domain;
2201         } else if (lp_winbind_use_default_domain()) {
2202                 domain_name = lp_workgroup();
2203         }
2204
2205         if (domain_name != NULL)
2206                 domain = find_domain_from_name(domain_name);
2207
2208         if (domain != NULL) {
2209                 DEBUG(7, ("[%5lu]: pam auth crap changing pswd in domain: "
2210                           "%s\n", (unsigned long)state->pid,domain->name));
2211                 sendto_domain(state, domain);
2212                 return;
2213         }
2214
2215         set_auth_errors(state->response, NT_STATUS_NO_SUCH_USER);
2216         DEBUG(5, ("CRAP change password  for %s\\%s returned %s (PAM: %d)\n",
2217                   state->request->data.chng_pswd_auth_crap.domain,
2218                   state->request->data.chng_pswd_auth_crap.user,
2219                   state->response->data.auth.nt_status_string,
2220                   state->response->data.auth.pam_error));
2221         request_error(state);
2222         return;
2223 }
2224
2225 enum winbindd_result winbindd_dual_pam_chng_pswd_auth_crap(struct winbindd_domain *domainSt, struct winbindd_cli_state *state)
2226 {
2227         NTSTATUS result;
2228         DATA_BLOB new_nt_password;
2229         DATA_BLOB old_nt_hash_enc;
2230         DATA_BLOB new_lm_password;
2231         DATA_BLOB old_lm_hash_enc;
2232         fstring  domain,user;
2233         struct policy_handle dom_pol;
2234         struct winbindd_domain *contact_domain = domainSt;
2235         struct rpc_pipe_client *cli;
2236
2237         /* Ensure null termination */
2238         state->request->data.chng_pswd_auth_crap.user[
2239                 sizeof(state->request->data.chng_pswd_auth_crap.user)-1]=0;
2240         state->request->data.chng_pswd_auth_crap.domain[
2241                 sizeof(state->request->data.chng_pswd_auth_crap.domain)-1]=0;
2242         *domain = 0;
2243         *user = 0;
2244
2245         DEBUG(3, ("[%5lu]: pam change pswd auth crap domain: %s user: %s\n",
2246                   (unsigned long)state->pid,
2247                   state->request->data.chng_pswd_auth_crap.domain,
2248                   state->request->data.chng_pswd_auth_crap.user));
2249
2250         if (lp_winbind_offline_logon()) {
2251                 DEBUG(0,("Refusing password change as winbind offline logons are enabled. "));
2252                 DEBUGADD(0,("Changing passwords here would risk inconsistent logons\n"));
2253                 result = NT_STATUS_ACCESS_DENIED;
2254                 goto done;
2255         }
2256
2257         if (*state->request->data.chng_pswd_auth_crap.domain) {
2258                 fstrcpy(domain,state->request->data.chng_pswd_auth_crap.domain);
2259         } else {
2260                 parse_domain_user(state->request->data.chng_pswd_auth_crap.user,
2261                                   domain, user);
2262
2263                 if(!*domain) {
2264                         DEBUG(3,("no domain specified with username (%s) - "
2265                                  "failing auth\n",
2266                                  state->request->data.chng_pswd_auth_crap.user));
2267                         result = NT_STATUS_NO_SUCH_USER;
2268                         goto done;
2269                 }
2270         }
2271
2272         if (!*domain && lp_winbind_use_default_domain()) {
2273                 fstrcpy(domain,(char *)lp_workgroup());
2274         }
2275
2276         if(!*user) {
2277                 fstrcpy(user, state->request->data.chng_pswd_auth_crap.user);
2278         }
2279
2280         DEBUG(3, ("[%5lu]: pam auth crap domain: %s user: %s\n",
2281                   (unsigned long)state->pid, domain, user));
2282
2283         /* Change password */
2284         new_nt_password = data_blob_talloc(
2285                 state->mem_ctx,
2286                 state->request->data.chng_pswd_auth_crap.new_nt_pswd,
2287                 state->request->data.chng_pswd_auth_crap.new_nt_pswd_len);
2288
2289         old_nt_hash_enc = data_blob_talloc(
2290                 state->mem_ctx,
2291                 state->request->data.chng_pswd_auth_crap.old_nt_hash_enc,
2292                 state->request->data.chng_pswd_auth_crap.old_nt_hash_enc_len);
2293
2294         if(state->request->data.chng_pswd_auth_crap.new_lm_pswd_len > 0)        {
2295                 new_lm_password = data_blob_talloc(
2296                         state->mem_ctx,
2297                         state->request->data.chng_pswd_auth_crap.new_lm_pswd,
2298                         state->request->data.chng_pswd_auth_crap.new_lm_pswd_len);
2299
2300                 old_lm_hash_enc = data_blob_talloc(
2301                         state->mem_ctx,
2302                         state->request->data.chng_pswd_auth_crap.old_lm_hash_enc,
2303                         state->request->data.chng_pswd_auth_crap.old_lm_hash_enc_len);
2304         } else {
2305                 new_lm_password.length = 0;
2306                 old_lm_hash_enc.length = 0;
2307         }
2308
2309         /* Get sam handle */
2310
2311         result = cm_connect_sam(contact_domain, state->mem_ctx, &cli, &dom_pol);
2312         if (!NT_STATUS_IS_OK(result)) {
2313                 DEBUG(1, ("could not get SAM handle on DC for %s\n", domain));
2314                 goto done;
2315         }
2316
2317         result = rpccli_samr_chng_pswd_auth_crap(
2318                 cli, state->mem_ctx, user, new_nt_password, old_nt_hash_enc,
2319                 new_lm_password, old_lm_hash_enc);
2320
2321  done:
2322
2323         set_auth_errors(state->response, result);
2324
2325         DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2,
2326               ("Password change for user [%s]\\[%s] returned %s (PAM: %d)\n",
2327                domain, user,
2328                state->response->data.auth.nt_status_string,
2329                state->response->data.auth.pam_error));
2330
2331         return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
2332 }