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