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