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