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