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