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