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