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