r15240: Correctly disallow unauthorized access when logging on with the
[jra/samba/.git] / source3 / 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         state->response.extra_data.data = SMB_MALLOC(size);
101         if (!state->response.extra_data.data) {
102                 prs_mem_free(&ps);
103                 return NT_STATUS_NO_MEMORY;
104         }
105         memset( state->response.extra_data.data, '\0', size );
106         prs_copy_all_data_out(state->response.extra_data.data, &ps);
107         state->response.length += size;
108         prs_mem_free(&ps);
109         return NT_STATUS_OK;
110 }
111
112 static NTSTATUS check_info3_in_group(TALLOC_CTX *mem_ctx, 
113                                      NET_USER_INFO_3 *info3,
114                                      const char *group_sid) 
115 {
116         DOM_SID require_membership_of_sid;
117         DOM_SID *all_sids;
118         size_t num_all_sids = (2 + info3->num_groups2 + info3->num_other_sids);
119         size_t i, j = 0;
120
121         /* Parse the 'required group' SID */
122         
123         if (!group_sid || !group_sid[0]) {
124                 /* NO sid supplied, all users may access */
125                 return NT_STATUS_OK;
126         }
127         
128         if (!string_to_sid(&require_membership_of_sid, group_sid)) {
129                 DEBUG(0, ("check_info3_in_group: could not parse %s as a SID!", 
130                           group_sid));
131
132                 return NT_STATUS_INVALID_PARAMETER;
133         }
134
135         all_sids = TALLOC_ARRAY(mem_ctx, DOM_SID, num_all_sids);
136         if (!all_sids)
137                 return NT_STATUS_NO_MEMORY;
138
139         /* and create (by appending rids) the 'domain' sids */
140         
141         sid_copy(&all_sids[0], &(info3->dom_sid.sid));
142         
143         if (!sid_append_rid(&all_sids[0], info3->user_rid)) {
144                 DEBUG(3,("could not append user's primary RID 0x%x\n",
145                          info3->user_rid));                     
146                 
147                 return NT_STATUS_INVALID_PARAMETER;
148         }
149         j++;
150
151         sid_copy(&all_sids[1], &(info3->dom_sid.sid));
152                 
153         if (!sid_append_rid(&all_sids[1], info3->group_rid)) {
154                 DEBUG(3,("could not append additional group rid 0x%x\n",
155                          info3->group_rid));                    
156                 
157                 return NT_STATUS_INVALID_PARAMETER;
158         }
159         j++;    
160
161         for (i = 0; i < info3->num_groups2; i++) {
162         
163                 sid_copy(&all_sids[j], &(info3->dom_sid.sid));
164                 
165                 if (!sid_append_rid(&all_sids[j], info3->gids[i].g_rid)) {
166                         DEBUG(3,("could not append additional group rid 0x%x\n",
167                                 info3->gids[i].g_rid));                 
168                                 
169                         return NT_STATUS_INVALID_PARAMETER;
170                 }
171                 j++;
172         }
173
174         /* Copy 'other' sids.  We need to do sid filtering here to
175            prevent possible elevation of privileges.  See:
176
177            http://www.microsoft.com/windows2000/techinfo/administration/security/sidfilter.asp
178          */
179
180         for (i = 0; i < info3->num_other_sids; i++) {
181                 sid_copy(&all_sids[info3->num_groups2 + i + 2],
182                          &info3->other_sids[i].sid);
183                 j++;
184         }
185
186         for (i = 0; i < j; i++) {
187                 fstring sid1, sid2;
188                 DEBUG(10, ("User has SID: %s\n", 
189                            sid_to_string(sid1, &all_sids[i])));
190                 if (sid_equal(&require_membership_of_sid, &all_sids[i])) {
191                         DEBUG(10, ("SID %s matches %s - user permitted to authenticate!\n", 
192                                    sid_to_string(sid1, &require_membership_of_sid), sid_to_string(sid2, &all_sids[i])));
193                         return NT_STATUS_OK;
194                 }
195         }
196         
197         /* Do not distinguish this error from a wrong username/pw */
198
199         return NT_STATUS_LOGON_FAILURE;
200 }
201
202 static struct winbindd_domain *find_auth_domain(struct winbindd_cli_state *state, 
203                                                 const char *domain_name)
204 {
205         struct winbindd_domain *domain;
206
207         if (IS_DC) {
208                 domain = find_domain_from_name_noinit(domain_name);
209                 if (domain == NULL) {
210                         DEBUG(3, ("Authentication for domain [%s] refused"
211                                   "as it is not a trusted domain\n", 
212                                   domain_name));
213                 }
214                 return domain;
215         }
216
217         if (is_myname(domain_name)) {
218                 DEBUG(3, ("Authentication for domain %s (local domain "
219                           "to this server) not supported at this "
220                           "stage\n", domain_name));
221                 return NULL;
222         }
223
224         return find_our_domain();
225 }
226
227 static void set_auth_errors(struct winbindd_response *resp, NTSTATUS result)
228 {
229         resp->data.auth.nt_status = NT_STATUS_V(result);
230         fstrcpy(resp->data.auth.nt_status_string, nt_errstr(result));
231
232         /* we might have given a more useful error above */
233         if (*resp->data.auth.error_string == '\0') 
234                 fstrcpy(resp->data.auth.error_string,
235                         get_friendly_nt_error_msg(result));
236         resp->data.auth.pam_error = nt_status_to_pam(result);
237 }
238
239 static NTSTATUS fillup_password_policy(struct winbindd_domain *domain,
240                                        struct winbindd_cli_state *state)
241 {
242         struct winbindd_methods *methods;
243         NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
244         SAM_UNK_INFO_1 password_policy;
245
246         methods = domain->methods;
247
248         status = methods->password_policy(domain, state->mem_ctx, &password_policy);
249         if (NT_STATUS_IS_ERR(status)) {
250                 return status;
251         }
252
253         state->response.data.auth.policy.min_length_password =
254                 password_policy.min_length_password;
255         state->response.data.auth.policy.password_history =
256                 password_policy.password_history;
257         state->response.data.auth.policy.password_properties =
258                 password_policy.password_properties;
259         state->response.data.auth.policy.expire =
260                 nt_time_to_unix_abs(&(password_policy.expire));
261         state->response.data.auth.policy.min_passwordage = 
262                 nt_time_to_unix_abs(&(password_policy.min_passwordage));
263
264         return NT_STATUS_OK;
265 }
266
267 static NTSTATUS get_max_bad_attempts_from_lockout_policy(struct winbindd_domain *domain, 
268                                                          TALLOC_CTX *mem_ctx, 
269                                                          uint16 *max_allowed_bad_attempts)
270 {
271         struct winbindd_methods *methods;
272         NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
273         SAM_UNK_INFO_12 lockout_policy;
274
275         *max_allowed_bad_attempts = 0;
276
277         methods = domain->methods;
278
279         status = methods->lockout_policy(domain, mem_ctx, &lockout_policy);
280         if (NT_STATUS_IS_ERR(status)) {
281                 return status;
282         }
283
284         *max_allowed_bad_attempts = lockout_policy.bad_attempt_lockout;
285
286         return NT_STATUS_OK;
287 }
288
289 static NTSTATUS get_pwd_properties(struct winbindd_domain *domain, 
290                                    TALLOC_CTX *mem_ctx, 
291                                    uint32 *password_properties)
292 {
293         struct winbindd_methods *methods;
294         NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
295         SAM_UNK_INFO_1 password_policy;
296
297         *password_properties = 0;
298
299         methods = domain->methods;
300
301         status = methods->password_policy(domain, mem_ctx, &password_policy);
302         if (NT_STATUS_IS_ERR(status)) {
303                 return status;
304         }
305
306         *password_properties = password_policy.password_properties;
307
308         return NT_STATUS_OK;
309 }
310
311 static const char *generate_krb5_ccache(TALLOC_CTX *mem_ctx, 
312                                         const char *type,
313                                         uid_t uid,
314                                         BOOL *internal_ccache)
315 {
316         /* accept FILE and WRFILE as krb5_cc_type from the client and then
317          * build the full ccname string based on the user's uid here -
318          * Guenther*/
319
320         const char *gen_cc = NULL;
321
322         *internal_ccache = True;
323
324         if (uid == -1) {
325                 goto memory_ccache;
326         }
327
328         if (!type || type[0] == '\0') {
329                 goto memory_ccache;
330         }
331
332         if (strequal(type, "FILE")) {
333                 gen_cc = talloc_asprintf(mem_ctx, "FILE:/tmp/krb5cc_%d", uid);
334         } else if (strequal(type, "WRFILE")) {
335                 gen_cc = talloc_asprintf(mem_ctx, "WRFILE:/tmp/krb5cc_%d", uid);
336         } else {
337                 DEBUG(10,("we don't allow to set a %s type ccache\n", type));
338                 goto memory_ccache;
339         }
340
341         *internal_ccache = False;
342         goto done;
343
344   memory_ccache:
345         gen_cc = talloc_strdup(mem_ctx, "MEMORY:winbindd_pam_ccache");
346
347   done:
348         if (gen_cc == NULL) {
349                 DEBUG(0,("out of memory\n"));
350                 return NULL;
351         }
352
353         DEBUG(10,("using ccache: %s %s\n", gen_cc, *internal_ccache ? "(internal)":""));
354
355         return gen_cc;
356 }
357
358 static uid_t get_uid_from_state(struct winbindd_cli_state *state)
359 {
360         uid_t uid = -1;
361
362         uid = state->request.data.auth.uid;
363
364         if (uid < 0) {
365                 DEBUG(1,("invalid uid: '%d'\n", uid));
366                 return -1;
367         }
368         return uid;
369 }
370
371 static void setup_return_cc_name(struct winbindd_cli_state *state, const char *cc)
372 {
373         const char *type = state->request.data.auth.krb5_cc_type;
374
375         state->response.data.auth.krb5ccname[0] = '\0';
376
377         if (type[0] == '\0') {
378                 return;
379         }
380
381         if (!strequal(type, "FILE") &&
382             !strequal(type, "WRFILE")) {
383                 DEBUG(10,("won't return krbccname for a %s type ccache\n", 
384                         type));
385                 return;
386         }
387         
388         fstrcpy(state->response.data.auth.krb5ccname, cc);
389 }
390
391 /**********************************************************************
392  Authenticate a user with a clear text password using Kerberos and fill up
393  ccache if required
394  **********************************************************************/
395 static NTSTATUS winbindd_raw_kerberos_login(struct winbindd_domain *domain,
396                                             struct winbindd_cli_state *state,
397                                             NET_USER_INFO_3 **info3)
398 {
399 #ifdef HAVE_KRB5
400         NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
401         krb5_error_code krb5_ret;
402         DATA_BLOB tkt, session_key_krb5;
403         DATA_BLOB ap_rep, session_key;
404         PAC_DATA *pac_data = NULL;
405         PAC_LOGON_INFO *logon_info = NULL;
406         char *client_princ = NULL;
407         char *client_princ_out = NULL;
408         char *local_service = NULL;
409         const char *cc = NULL;
410         const char *principal_s = NULL;
411         const char *service = NULL;
412         char *realm = NULL;
413         fstring name_domain, name_user;
414         time_t ticket_lifetime = 0;
415         time_t renewal_until = 0;
416         uid_t uid = -1;
417         ADS_STRUCT *ads;
418         time_t time_offset = 0;
419         BOOL internal_ccache = True;
420
421         ZERO_STRUCT(session_key);
422         ZERO_STRUCT(session_key_krb5);
423         ZERO_STRUCT(tkt);
424         ZERO_STRUCT(ap_rep);
425
426         ZERO_STRUCTP(info3);
427
428         *info3 = NULL;
429         
430         /* 1st step: 
431          * prepare a krb5_cc_cache string for the user */
432
433         uid = get_uid_from_state(state);
434         if (uid == -1) {
435                 DEBUG(0,("no valid uid\n"));
436         }
437
438         cc = generate_krb5_ccache(state->mem_ctx,
439                                   state->request.data.auth.krb5_cc_type,
440                                   state->request.data.auth.uid, 
441                                   &internal_ccache);
442         if (cc == NULL) {
443                 return NT_STATUS_NO_MEMORY;
444         }
445
446
447         /* 2nd step: 
448          * get kerberos properties */
449         
450         if (domain->private_data) {
451                 ads = (ADS_STRUCT *)domain->private_data;
452                 time_offset = ads->auth.time_offset; 
453         }
454
455
456         /* 3rd step: 
457          * do kerberos auth and setup ccache as the user */
458
459         parse_domain_user(state->request.data.auth.user, name_domain, name_user);
460
461         realm = domain->alt_name;
462         strupper_m(realm);
463         
464         principal_s = talloc_asprintf(state->mem_ctx, "%s@%s", name_user, realm); 
465         if (principal_s == NULL) {
466                 return NT_STATUS_NO_MEMORY;
467         }
468
469         service = talloc_asprintf(state->mem_ctx, "%s/%s@%s", KRB5_TGS_NAME, realm, realm);
470         if (service == NULL) {
471                 return NT_STATUS_NO_MEMORY;
472         }
473
474         /* if this is a user ccache, we need to act as the user to let the krb5
475          * library handle the chown, etc. */
476
477         /************************ NON-ROOT **********************/
478
479         if (!internal_ccache) {
480
481                 seteuid(uid);
482                 DEBUG(10,("winbindd_raw_kerberos_login: uid is %d\n", uid));
483         }
484
485         krb5_ret = kerberos_kinit_password_ext(principal_s, 
486                                                state->request.data.auth.pass, 
487                                                time_offset, 
488                                                &ticket_lifetime,
489                                                &renewal_until,
490                                                cc, 
491                                                True,
492                                                True,
493                                                WINBINDD_PAM_AUTH_KRB5_RENEW_TIME);
494
495         if (krb5_ret) {
496                 DEBUG(1,("winbindd_raw_kerberos_login: kinit failed for '%s' with: %s (%d)\n", 
497                         principal_s, error_message(krb5_ret), krb5_ret));
498                 result = krb5_to_nt_status(krb5_ret);
499                 goto failed;
500         }
501
502         /* does http_timestring use heimdals libroken strftime?? - Guenther */
503         DEBUG(10,("got TGT for %s in %s (valid until: %s (%d), renewable till: %s (%d))\n", 
504                 principal_s, cc, 
505                 http_timestring(ticket_lifetime), (int)ticket_lifetime, 
506                 http_timestring(renewal_until), (int)renewal_until));
507
508         client_princ = talloc_strdup(state->mem_ctx, global_myname());
509         if (client_princ == NULL) {
510                 result = NT_STATUS_NO_MEMORY;
511                 goto failed;
512         }
513         strlower_m(client_princ);
514
515         local_service = talloc_asprintf(state->mem_ctx, "HOST/%s@%s", client_princ, lp_realm());
516         if (local_service == NULL) {
517                 DEBUG(0,("winbindd_raw_kerberos_login: out of memory\n"));
518                 result = NT_STATUS_NO_MEMORY;
519                 goto failed;
520         }
521
522         krb5_ret = cli_krb5_get_ticket(local_service, 
523                                        time_offset, 
524                                        &tkt, 
525                                        &session_key_krb5, 
526                                        0, 
527                                        cc);
528         if (krb5_ret) {
529                 DEBUG(1,("winbindd_raw_kerberos_login: failed to get ticket for %s: %s\n", 
530                         local_service, error_message(krb5_ret)));
531                 result = krb5_to_nt_status(krb5_ret);
532                 goto failed;
533         }
534
535         if (!internal_ccache) {
536                 seteuid(0);
537         }
538
539         /************************ NON-ROOT **********************/
540
541         result = ads_verify_ticket(state->mem_ctx, 
542                                    lp_realm(), 
543                                    &tkt, 
544                                    &client_princ_out, 
545                                    &pac_data, 
546                                    &ap_rep, 
547                                    &session_key);       
548         if (!NT_STATUS_IS_OK(result)) {
549                 DEBUG(0,("winbindd_raw_kerberos_login: ads_verify_ticket failed: %s\n", 
550                         nt_errstr(result)));
551                 goto failed;
552         }
553
554         DEBUG(10,("winbindd_raw_kerberos_login: winbindd validated ticket of %s\n", 
555                 local_service));
556
557         if (!pac_data) {
558                 DEBUG(3,("winbindd_raw_kerberos_login: no pac data\n"));
559                 result = NT_STATUS_INVALID_PARAMETER;
560                 goto failed;
561         }
562                         
563         logon_info = get_logon_info_from_pac(pac_data);
564         if (logon_info == NULL) {
565                 DEBUG(1,("winbindd_raw_kerberos_login: no logon info\n"));
566                 result = NT_STATUS_INVALID_PARAMETER;
567                 goto failed;
568         }
569
570
571         /* last step: 
572          * put results together */
573
574         *info3 = &logon_info->info3;
575
576         /* if we had a user's ccache then return that string for the pam
577          * environment */
578
579         if (!internal_ccache) {
580                 
581                 setup_return_cc_name(state, cc);
582
583                 result = add_ccache_to_list(principal_s,
584                                             cc,
585                                             service,
586                                             state->request.data.auth.user,
587                                             NULL,
588                                             state->request.data.auth.pass,
589                                             uid,
590                                             time(NULL),
591                                             ticket_lifetime,
592                                             renewal_until, 
593                                             lp_winbind_refresh_tickets());
594
595                 if (!NT_STATUS_IS_OK(result)) {
596                         DEBUG(10,("winbindd_raw_kerberos_login: failed to add ccache to list: %s\n", 
597                                 nt_errstr(result)));
598                 }
599         }
600
601         result = NT_STATUS_OK;
602
603         goto done;
604
605 failed:
606
607         /* we could have created a new credential cache with a valid tgt in it
608          * but we werent able to get or verify the service ticket for this
609          * local host and therefor didn't get the PAC, we need to remove that
610          * cache entirely now */
611
612         krb5_ret = ads_kdestroy(cc);
613         if (krb5_ret) {
614                 DEBUG(0,("winbindd_raw_kerberos_login: "
615                          "could not destroy krb5 credential cache: "
616                          "%s\n", error_message(krb5_ret)));
617         }
618
619 done:
620         data_blob_free(&session_key);
621         data_blob_free(&session_key_krb5);
622         data_blob_free(&ap_rep);
623         data_blob_free(&tkt);
624
625         SAFE_FREE(client_princ_out);
626
627         if (!internal_ccache) {
628                 seteuid(0);
629         }
630
631         return result;
632 #else 
633         return NT_STATUS_NOT_SUPPORTED;
634 #endif /* HAVE_KRB5 */
635 }
636
637 void winbindd_pam_auth(struct winbindd_cli_state *state)
638 {
639         struct winbindd_domain *domain;
640         fstring name_domain, name_user;
641
642         /* Ensure null termination */
643         state->request.data.auth.user
644                 [sizeof(state->request.data.auth.user)-1]='\0';
645
646         /* Ensure null termination */
647         state->request.data.auth.pass
648                 [sizeof(state->request.data.auth.pass)-1]='\0';
649
650         DEBUG(3, ("[%5lu]: pam auth %s\n", (unsigned long)state->pid,
651                   state->request.data.auth.user));
652
653         /* Parse domain and username */
654         
655         if (!parse_domain_user(state->request.data.auth.user,
656                                name_domain, name_user)) {
657                 set_auth_errors(&state->response, NT_STATUS_NO_SUCH_USER);
658                 DEBUG(5, ("Plain text authentication for %s returned %s "
659                           "(PAM: %d)\n",
660                           state->request.data.auth.user, 
661                           state->response.data.auth.nt_status_string,
662                           state->response.data.auth.pam_error));
663                 request_error(state);
664                 return;
665         }
666
667         domain = find_auth_domain(state, name_domain);
668
669         if (domain == NULL) {
670                 set_auth_errors(&state->response, NT_STATUS_NO_SUCH_USER);
671                 DEBUG(5, ("Plain text authentication for %s returned %s "
672                           "(PAM: %d)\n",
673                           state->request.data.auth.user, 
674                           state->response.data.auth.nt_status_string,
675                           state->response.data.auth.pam_error));
676                 request_error(state);
677                 return;
678         }
679
680         sendto_domain(state, domain);
681 }
682
683 NTSTATUS winbindd_dual_pam_auth_cached(struct winbindd_domain *domain,
684                                        struct winbindd_cli_state *state,
685                                        NET_USER_INFO_3 **info3)
686 {
687         NTSTATUS result = NT_STATUS_LOGON_FAILURE;
688         uint16 max_allowed_bad_attempts; 
689         fstring name_domain, name_user;
690         DOM_SID sid;
691         enum SID_NAME_USE type;
692         uchar new_nt_pass[NT_HASH_LEN];
693         const uint8 *cached_nt_pass;
694         NET_USER_INFO_3 *my_info3;
695         time_t kickoff_time, must_change_time;
696
697         *info3 = NULL;
698
699         ZERO_STRUCTP(info3);
700
701         DEBUG(10,("winbindd_dual_pam_auth_cached\n"));
702
703         /* Parse domain and username */
704         
705         parse_domain_user(state->request.data.auth.user, name_domain, name_user);
706
707
708         if (!lookup_cached_name(state->mem_ctx,
709                                 name_domain,
710                                 name_user,
711                                 &sid,
712                                 &type)) {
713                 DEBUG(10,("winbindd_dual_pam_auth_cached: no such user in the cache\n"));
714                 return NT_STATUS_NO_SUCH_USER;
715         }
716
717         if (type != SID_NAME_USER) {
718                 DEBUG(10,("winbindd_dual_pam_auth_cached: not a user (%s)\n", sid_type_lookup(type)));
719                 return NT_STATUS_LOGON_FAILURE;
720         }
721
722         result = winbindd_get_creds(domain, 
723                                     state->mem_ctx, 
724                                     &sid, 
725                                     &my_info3, 
726                                     &cached_nt_pass);
727         if (!NT_STATUS_IS_OK(result)) {
728                 DEBUG(10,("winbindd_dual_pam_auth_cached: failed to get creds: %s\n", nt_errstr(result)));
729                 return result;
730         }
731
732         *info3 = my_info3;
733
734         E_md4hash(state->request.data.auth.pass, new_nt_pass);
735
736 #if DEBUG_PASSWORD
737         dump_data(100, (const char *)new_nt_pass, NT_HASH_LEN);
738         dump_data(100, (const char *)cached_nt_pass, NT_HASH_LEN);
739 #endif
740
741         if (!memcmp(cached_nt_pass, new_nt_pass, NT_HASH_LEN)) {
742
743                 /* User *DOES* know the password, update logon_time and reset
744                  * bad_pw_count */
745         
746                 my_info3->user_flgs |= LOGON_CACHED_ACCOUNT;
747         
748                 if (my_info3->acct_flags & ACB_AUTOLOCK) {
749                         return NT_STATUS_ACCOUNT_LOCKED_OUT;
750                 }
751         
752                 if (my_info3->acct_flags & ACB_DISABLED) {
753                         return NT_STATUS_ACCOUNT_DISABLED;
754                 }
755         
756                 if (my_info3->acct_flags & ACB_WSTRUST) {
757                         return NT_STATUS_NOLOGON_WORKSTATION_TRUST_ACCOUNT;
758                 }
759         
760                 if (my_info3->acct_flags & ACB_SVRTRUST) {
761                         return NT_STATUS_NOLOGON_SERVER_TRUST_ACCOUNT;
762                 }
763         
764                 if (my_info3->acct_flags & ACB_DOMTRUST) {
765                         return NT_STATUS_NOLOGON_INTERDOMAIN_TRUST_ACCOUNT;
766                 }
767
768                 /* The info3 acct_flags in NT4's samlogon reply don't have
769                  * ACB_NORMAL set. */
770 #if 0
771                 if (!(my_info3->acct_flags & ACB_NORMAL)) {
772                         DEBUG(10,("winbindd_dual_pam_auth_cached: whats wrong with that one?: 0x%08x\n", 
773                                 my_info3->acct_flags));
774                         return NT_STATUS_LOGON_FAILURE;
775                 }
776 #endif
777                 kickoff_time = nt_time_to_unix(&my_info3->kickoff_time);
778                 if (kickoff_time != 0 && time(NULL) > kickoff_time) {
779                         return NT_STATUS_ACCOUNT_EXPIRED;
780                 }
781
782                 must_change_time = nt_time_to_unix(&my_info3->pass_must_change_time);
783                 if (must_change_time != 0 && must_change_time < time(NULL)) {
784                         return NT_STATUS_PASSWORD_EXPIRED;
785                 }
786         
787                 /* FIXME: we possibly should handle logon hours as well (does xp when
788                  * offline?) see auth/auth_sam.c:sam_account_ok for details */
789
790                 unix_to_nt_time(&my_info3->logon_time, time(NULL));
791                 my_info3->bad_pw_count = 0;
792
793                 result = winbindd_update_creds_by_info3(domain,
794                                                         state->mem_ctx,
795                                                         state->request.data.auth.user,
796                                                         state->request.data.auth.pass,
797                                                         my_info3);
798                 if (!NT_STATUS_IS_OK(result)) {
799                         DEBUG(1,("failed to update creds: %s\n", nt_errstr(result)));
800                         return result;
801                 }
802
803                 return NT_STATUS_OK;
804
805         }
806
807         /* User does *NOT* know the correct password, modify info3 accordingly */
808
809         /* failure of this is not critical */
810         result = get_max_bad_attempts_from_lockout_policy(domain, state->mem_ctx, &max_allowed_bad_attempts);
811         if (!NT_STATUS_IS_OK(result)) {
812                 DEBUG(10,("winbindd_dual_pam_auth_cached: failed to get max_allowed_bad_attempts. "
813                           "Won't be able to honour account lockout policies\n"));
814         }
815
816         /* increase counter */
817         my_info3->bad_pw_count++;
818
819         if (max_allowed_bad_attempts == 0) {
820                 goto failed;
821         }
822
823         /* lockout user */
824         if (my_info3->bad_pw_count >= max_allowed_bad_attempts) {
825
826                 uint32 password_properties;
827
828                 result = get_pwd_properties(domain, state->mem_ctx, &password_properties);
829                 if (!NT_STATUS_IS_OK(result)) {
830                         DEBUG(10,("winbindd_dual_pam_auth_cached: failed to get password properties.\n"));
831                 }
832
833                 if ((my_info3->user_rid != DOMAIN_USER_RID_ADMIN) || 
834                     (password_properties & DOMAIN_LOCKOUT_ADMINS)) {
835                         my_info3->acct_flags |= ACB_AUTOLOCK;
836                 }
837         }
838
839 failed:
840         result = winbindd_update_creds_by_info3(domain,
841                                                 state->mem_ctx,
842                                                 state->request.data.auth.user,
843                                                 NULL,
844                                                 my_info3);
845
846         if (!NT_STATUS_IS_OK(result)) {
847                 DEBUG(0,("winbindd_dual_pam_auth_cached: failed to update creds %s\n", 
848                         nt_errstr(result)));
849         }
850
851         return NT_STATUS_LOGON_FAILURE;
852 }
853
854 NTSTATUS winbindd_dual_pam_auth_kerberos(struct winbindd_domain *domain,
855                                          struct winbindd_cli_state *state, 
856                                          NET_USER_INFO_3 **info3)
857 {
858         struct winbindd_domain *contact_domain;
859         fstring name_domain, name_user;
860         NTSTATUS result;
861
862         DEBUG(10,("winbindd_dual_pam_auth_kerberos\n"));
863         
864         /* Parse domain and username */
865         
866         parse_domain_user(state->request.data.auth.user, name_domain, name_user);
867
868         /* what domain should we contact? */
869         
870         if ( IS_DC ) {
871                 if (!(contact_domain = find_domain_from_name(name_domain))) {
872                         DEBUG(3, ("Authentication for domain for [%s] -> [%s]\\[%s] failed as %s is not a trusted domain\n", 
873                                   state->request.data.auth.user, name_domain, name_user, name_domain)); 
874                         result = NT_STATUS_NO_SUCH_USER;
875                         goto done;
876                 }
877                 
878         } else {
879                 if (is_myname(name_domain)) {
880                         DEBUG(3, ("Authentication for domain %s (local domain to this server) not supported at this stage\n", name_domain));
881                         result =  NT_STATUS_NO_SUCH_USER;
882                         goto done;
883                 }
884                 
885                 contact_domain = find_domain_from_name(name_domain);
886                 if (contact_domain == NULL) {
887                         DEBUG(3, ("Authentication for domain for [%s] -> [%s]\\[%s] failed as %s is not a trusted domain\n", 
888                                   state->request.data.auth.user, name_domain, name_user, name_domain)); 
889
890                         contact_domain = find_our_domain();
891                 }
892         }
893
894         set_dc_type_and_flags(contact_domain);
895
896         if (!contact_domain->active_directory) {
897                 DEBUG(3,("krb5 auth requested but domain is not Active Directory\n"));
898                 return NT_STATUS_INVALID_LOGON_TYPE;
899         }
900
901         result = winbindd_raw_kerberos_login(contact_domain, state, info3);
902 done:
903         return result;
904 }
905
906 NTSTATUS winbindd_dual_pam_auth_samlogon(struct winbindd_domain *domain,
907                                          struct winbindd_cli_state *state,
908                                          NET_USER_INFO_3 **info3)
909 {
910
911         struct rpc_pipe_client *netlogon_pipe;
912         uchar chal[8];
913         DATA_BLOB lm_resp;
914         DATA_BLOB nt_resp;
915         int attempts = 0;
916         unsigned char local_lm_response[24];
917         unsigned char local_nt_response[24];
918         struct winbindd_domain *contact_domain;
919         fstring name_domain, name_user;
920         BOOL retry;
921         NTSTATUS result;
922         NET_USER_INFO_3 *my_info3;
923
924         ZERO_STRUCTP(info3);
925
926         *info3 = NULL;
927
928         my_info3 = TALLOC_ZERO_P(state->mem_ctx, NET_USER_INFO_3);
929         if (my_info3 == NULL) {
930                 return NT_STATUS_NO_MEMORY;
931         }
932
933
934         DEBUG(10,("winbindd_dual_pam_auth_samlogon\n"));
935         
936         /* Parse domain and username */
937         
938         parse_domain_user(state->request.data.auth.user, name_domain, name_user);
939
940         /* do password magic */
941         
942
943         generate_random_buffer(chal, 8);
944         if (lp_client_ntlmv2_auth()) {
945                 DATA_BLOB server_chal;
946                 DATA_BLOB names_blob;
947                 DATA_BLOB nt_response;
948                 DATA_BLOB lm_response;
949                 server_chal = data_blob_talloc(state->mem_ctx, chal, 8); 
950                 
951                 /* note that the 'workgroup' here is a best guess - we don't know
952                    the server's domain at this point.  The 'server name' is also
953                    dodgy... 
954                 */
955                 names_blob = NTLMv2_generate_names_blob(global_myname(), lp_workgroup());
956                 
957                 if (!SMBNTLMv2encrypt(name_user, name_domain, 
958                                       state->request.data.auth.pass, 
959                                       &server_chal, 
960                                       &names_blob,
961                                       &lm_response, &nt_response, NULL)) {
962                         data_blob_free(&names_blob);
963                         data_blob_free(&server_chal);
964                         DEBUG(0, ("winbindd_pam_auth: SMBNTLMv2encrypt() failed!\n"));
965                         result = NT_STATUS_NO_MEMORY;
966                         goto done;
967                 }
968                 data_blob_free(&names_blob);
969                 data_blob_free(&server_chal);
970                 lm_resp = data_blob_talloc(state->mem_ctx, lm_response.data,
971                                            lm_response.length);
972                 nt_resp = data_blob_talloc(state->mem_ctx, nt_response.data,
973                                            nt_response.length);
974                 data_blob_free(&lm_response);
975                 data_blob_free(&nt_response);
976
977         } else {
978                 if (lp_client_lanman_auth() 
979                     && SMBencrypt(state->request.data.auth.pass, 
980                                   chal, 
981                                   local_lm_response)) {
982                         lm_resp = data_blob_talloc(state->mem_ctx, 
983                                                    local_lm_response, 
984                                                    sizeof(local_lm_response));
985                 } else {
986                         lm_resp = data_blob(NULL, 0);
987                 }
988                 SMBNTencrypt(state->request.data.auth.pass, 
989                              chal,
990                              local_nt_response);
991
992                 nt_resp = data_blob_talloc(state->mem_ctx, 
993                                            local_nt_response, 
994                                            sizeof(local_nt_response));
995         }
996         
997         /* what domain should we contact? */
998         
999         if ( IS_DC ) {
1000                 if (!(contact_domain = find_domain_from_name(name_domain))) {
1001                         DEBUG(3, ("Authentication for domain for [%s] -> [%s]\\[%s] failed as %s is not a trusted domain\n", 
1002                                   state->request.data.auth.user, name_domain, name_user, name_domain)); 
1003                         result = NT_STATUS_NO_SUCH_USER;
1004                         goto done;
1005                 }
1006                 
1007         } else {
1008                 if (is_myname(name_domain)) {
1009                         DEBUG(3, ("Authentication for domain %s (local domain to this server) not supported at this stage\n", name_domain));
1010                         result =  NT_STATUS_NO_SUCH_USER;
1011                         goto done;
1012                 }
1013
1014                 contact_domain = find_our_domain();
1015         }
1016
1017         /* check authentication loop */
1018
1019         do {
1020
1021                 ZERO_STRUCTP(my_info3);
1022                 retry = False;
1023
1024                 result = cm_connect_netlogon(contact_domain, &netlogon_pipe);
1025
1026                 if (!NT_STATUS_IS_OK(result)) {
1027                         DEBUG(3, ("could not open handle to NETLOGON pipe\n"));
1028                         goto done;
1029                 }
1030
1031                 result = rpccli_netlogon_sam_network_logon(netlogon_pipe,
1032                                                            state->mem_ctx,
1033                                                            0,
1034                                                            contact_domain->dcname, /* server name */
1035                                                            name_user,              /* user name */
1036                                                            name_domain,            /* target domain */
1037                                                            global_myname(),        /* workstation */
1038                                                            chal,
1039                                                            lm_resp,
1040                                                            nt_resp,
1041                                                            my_info3);
1042                 attempts += 1;
1043
1044                 /* We have to try a second time as cm_connect_netlogon
1045                    might not yet have noticed that the DC has killed
1046                    our connection. */
1047
1048                 if (NT_STATUS_EQUAL(result, NT_STATUS_UNSUCCESSFUL)) {
1049                         retry = True;
1050                         continue;
1051                 }
1052                 
1053                 /* if we get access denied, a possible cause was that we had
1054                    and open connection to the DC, but someone changed our
1055                    machine account password out from underneath us using 'net
1056                    rpc changetrustpw' */
1057                    
1058                 if ( NT_STATUS_EQUAL(result, NT_STATUS_ACCESS_DENIED) ) {
1059                         DEBUG(3,("winbindd_pam_auth: sam_logon returned "
1060                                  "ACCESS_DENIED.  Maybe the trust account "
1061                                 "password was changed and we didn't know it. "
1062                                  "Killing connections to domain %s\n",
1063                                 name_domain));
1064                         invalidate_cm_connection(&contact_domain->conn);
1065                         retry = True;
1066                 } 
1067                 
1068         } while ( (attempts < 2) && retry );
1069
1070         *info3 = my_info3;
1071 done:
1072         return result;
1073 }
1074
1075 enum winbindd_result winbindd_dual_pam_auth(struct winbindd_domain *domain,
1076                                             struct winbindd_cli_state *state) 
1077 {
1078         NTSTATUS result = NT_STATUS_LOGON_FAILURE;
1079         fstring name_domain, name_user;
1080         NET_USER_INFO_3 *info3 = NULL;
1081         
1082         /* Ensure null termination */
1083         state->request.data.auth.user[sizeof(state->request.data.auth.user)-1]='\0';
1084
1085         /* Ensure null termination */
1086         state->request.data.auth.pass[sizeof(state->request.data.auth.pass)-1]='\0';
1087
1088         DEBUG(3, ("[%5lu]: dual pam auth %s\n", (unsigned long)state->pid,
1089                   state->request.data.auth.user));
1090
1091         /* Parse domain and username */
1092         
1093         parse_domain_user(state->request.data.auth.user, name_domain, name_user);
1094
1095         DEBUG(10,("winbindd_dual_pam_auth: domain: %s last was %s\n", domain->name, domain->online ? "online":"offline"));
1096
1097         /* Check for Kerberos authentication */
1098         if (domain->online && (state->request.flags & WBFLAG_PAM_KRB5)) {
1099         
1100                 result = winbindd_dual_pam_auth_kerberos(domain, state, &info3);
1101
1102                 if (NT_STATUS_IS_OK(result)) {
1103                         DEBUG(10,("winbindd_dual_pam_auth_kerberos succeeded\n"));
1104                         goto process_result;
1105                 } else {
1106                         DEBUG(10,("winbindd_dual_pam_auth_kerberos failed: %s\n", nt_errstr(result)));
1107                 }
1108
1109                 if (NT_STATUS_EQUAL(result, NT_STATUS_NO_LOGON_SERVERS) ||
1110                     NT_STATUS_EQUAL(result, NT_STATUS_IO_TIMEOUT) ||
1111                     NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
1112                         DEBUG(10,("winbindd_dual_pam_auth_kerberos setting domain to offline\n"));
1113                         domain->online = False;
1114                 }
1115
1116                 /* there are quite some NT_STATUS errors where there is no
1117                  * point in retrying with a samlogon, we explictly have to take
1118                  * care not to increase the bad logon counter on the DC */
1119
1120                 if (NT_STATUS_EQUAL(result, NT_STATUS_ACCOUNT_DISABLED) ||
1121                     NT_STATUS_EQUAL(result, NT_STATUS_ACCOUNT_EXPIRED) ||
1122                     NT_STATUS_EQUAL(result, NT_STATUS_ACCOUNT_LOCKED_OUT) ||
1123                     NT_STATUS_EQUAL(result, NT_STATUS_INVALID_LOGON_HOURS) ||
1124                     NT_STATUS_EQUAL(result, NT_STATUS_INVALID_WORKSTATION) ||
1125                     NT_STATUS_EQUAL(result, NT_STATUS_LOGON_FAILURE) ||
1126                     NT_STATUS_EQUAL(result, NT_STATUS_NO_SUCH_USER) ||
1127                     NT_STATUS_EQUAL(result, NT_STATUS_PASSWORD_EXPIRED) ||
1128                     NT_STATUS_EQUAL(result, NT_STATUS_PASSWORD_MUST_CHANGE) ||
1129                     NT_STATUS_EQUAL(result, NT_STATUS_WRONG_PASSWORD)) {
1130                         goto process_result;
1131                 }
1132                 
1133                 if (state->request.flags & WBFLAG_PAM_FALLBACK_AFTER_KRB5) {
1134                         DEBUG(3,("falling back to samlogon\n"));
1135                         goto sam_logon;
1136                 } else {
1137                         goto cached_logon;
1138                 }
1139         }
1140
1141 sam_logon:
1142         /* Check for Samlogon authentication */
1143         if (domain->online) {
1144                 result = winbindd_dual_pam_auth_samlogon(domain, state, &info3);
1145         
1146                 if (NT_STATUS_IS_OK(result)) {
1147                         DEBUG(10,("winbindd_dual_pam_auth_samlogon succeeded\n"));
1148                         goto process_result;
1149                 } else {
1150                         DEBUG(10,("winbindd_dual_pam_auth_samlogon failed: %s\n", nt_errstr(result)));
1151                         if (domain->online) {
1152                                 /* We're still online - fail. */
1153                                 goto done;
1154                         }
1155                         /* Else drop through and see if we can check offline.... */
1156                 }
1157         }
1158
1159 cached_logon:
1160         /* Check for Cached logons */
1161         if (!domain->online && (state->request.flags & WBFLAG_PAM_CACHED_LOGIN) && 
1162             lp_winbind_offline_logon()) {
1163         
1164                 result = winbindd_dual_pam_auth_cached(domain, state, &info3);
1165
1166                 if (NT_STATUS_IS_OK(result)) {
1167                         DEBUG(10,("winbindd_dual_pam_auth_cached succeeded\n"));
1168                         goto process_result;
1169                 } else {
1170                         DEBUG(10,("winbindd_dual_pam_auth_cached failed: %s\n", nt_errstr(result)));
1171                         goto done;
1172                 }
1173         }
1174
1175 process_result:
1176
1177         if (NT_STATUS_IS_OK(result)) {
1178         
1179                 DOM_SID user_sid;
1180
1181                 /* In all codepaths were result == NT_STATUS_OK info3 must have
1182                    been initialized. */
1183                 if (!info3) {
1184                         result = NT_STATUS_INTERNAL_ERROR;
1185                         goto done;
1186                 }
1187
1188                 netsamlogon_cache_store(name_user, info3);
1189                 wcache_invalidate_samlogon(find_domain_from_name(name_domain), info3);
1190
1191                 /* save name_to_sid info as early as possible */
1192                 sid_compose(&user_sid, &info3->dom_sid.sid, info3->user_rid);
1193                 cache_name2sid(domain, name_domain, name_user, SID_NAME_USER, &user_sid);
1194                 
1195                 /* Check if the user is in the right group */
1196
1197                 if (!NT_STATUS_IS_OK(result = check_info3_in_group(state->mem_ctx, info3,
1198                                         state->request.data.auth.require_membership_of_sid))) {
1199                         DEBUG(3, ("User %s is not in the required group (%s), so plaintext authentication is rejected\n",
1200                                   state->request.data.auth.user, 
1201                                   state->request.data.auth.require_membership_of_sid));
1202                         goto done;
1203                 }
1204
1205                 if (state->request.flags & WBFLAG_PAM_INFO3_NDR) {
1206                         result = append_info3_as_ndr(state->mem_ctx, state, info3);
1207                         if (!NT_STATUS_IS_OK(result)) {
1208                                 DEBUG(10,("Failed to append INFO3 (NDR): %s\n", nt_errstr(result)));
1209                                 goto done;
1210                         }
1211                 }
1212
1213                 if (state->request.flags & WBFLAG_PAM_INFO3_TEXT) {
1214                         result = append_info3_as_txt(state->mem_ctx, state, info3);
1215                         if (!NT_STATUS_IS_OK(result)) {
1216                                 DEBUG(10,("Failed to append INFO3 (TXT): %s\n", nt_errstr(result)));
1217                                 goto done;
1218                         }
1219
1220                 }
1221
1222                 if ((state->request.flags & WBFLAG_PAM_CACHED_LOGIN) &&
1223                     lp_winbind_offline_logon()) {
1224
1225                         result = winbindd_store_creds(domain,
1226                                                       state->mem_ctx,
1227                                                       state->request.data.auth.user,
1228                                                       state->request.data.auth.pass,
1229                                                       info3, NULL);
1230                         if (!NT_STATUS_IS_OK(result)) {
1231                                 DEBUG(10,("Failed to store creds: %s\n", nt_errstr(result)));
1232                                 goto done;
1233                         }
1234
1235                 }
1236
1237                 /* this is required to provide password expiry warning */ 
1238                 if (state->request.flags & WBFLAG_PAM_GET_PWD_POLICY) {
1239                         result = fillup_password_policy(domain, state);
1240
1241                         if (!NT_STATUS_IS_OK(result)) {
1242                                 DEBUG(10,("Failed to get password policies: %s\n", nt_errstr(result)));
1243                                 goto done;
1244                         }
1245                 }
1246         
1247         } 
1248
1249 done:
1250         /* give us a more useful (more correct?) error code */
1251         if ((NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND) ||
1252             (NT_STATUS_EQUAL(result, NT_STATUS_UNSUCCESSFUL)))) {
1253                 result = NT_STATUS_NO_LOGON_SERVERS;
1254         }
1255         
1256         state->response.data.auth.nt_status = NT_STATUS_V(result);
1257         fstrcpy(state->response.data.auth.nt_status_string, nt_errstr(result));
1258
1259         /* we might have given a more useful error above */
1260         if (!*state->response.data.auth.error_string) 
1261                 fstrcpy(state->response.data.auth.error_string, get_friendly_nt_error_msg(result));
1262         state->response.data.auth.pam_error = nt_status_to_pam(result);
1263
1264         DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2, ("Plain-text authentication for user %s returned %s (PAM: %d)\n", 
1265               state->request.data.auth.user, 
1266               state->response.data.auth.nt_status_string,
1267               state->response.data.auth.pam_error));          
1268
1269         if ( NT_STATUS_IS_OK(result) &&
1270              (state->request.flags & WBFLAG_PAM_AFS_TOKEN) ) {
1271
1272                 char *afsname = talloc_strdup(state->mem_ctx,
1273                                               lp_afs_username_map());
1274                 char *cell;
1275
1276                 if (afsname == NULL) {
1277                         goto no_token;
1278                 }
1279
1280                 afsname = talloc_string_sub(state->mem_ctx,
1281                                             lp_afs_username_map(),
1282                                             "%D", name_domain);
1283                 afsname = talloc_string_sub(state->mem_ctx, afsname,
1284                                             "%u", name_user);
1285                 afsname = talloc_string_sub(state->mem_ctx, afsname,
1286                                             "%U", name_user);
1287
1288                 {
1289                         DOM_SID user_sid;
1290                         fstring sidstr;
1291
1292                         sid_copy(&user_sid, &info3->dom_sid.sid);
1293                         sid_append_rid(&user_sid, info3->user_rid);
1294                         sid_to_string(sidstr, &user_sid);
1295                         afsname = talloc_string_sub(state->mem_ctx, afsname,
1296                                                     "%s", sidstr);
1297                 }
1298
1299                 if (afsname == NULL) {
1300                         goto no_token;
1301                 }
1302
1303                 strlower_m(afsname);
1304
1305                 DEBUG(10, ("Generating token for user %s\n", afsname));
1306
1307                 cell = strchr(afsname, '@');
1308
1309                 if (cell == NULL) {
1310                         goto no_token;
1311                 }
1312
1313                 *cell = '\0';
1314                 cell += 1;
1315
1316                 /* Append an AFS token string */
1317                 state->response.extra_data.data =
1318                         afs_createtoken_str(afsname, cell);
1319
1320                 if (state->response.extra_data.data != NULL)
1321                         state->response.length +=
1322                                 strlen(state->response.extra_data.data)+1;
1323
1324         no_token:
1325                 TALLOC_FREE(afsname);
1326         }
1327
1328         return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
1329 }
1330
1331
1332 /**********************************************************************
1333  Challenge Response Authentication Protocol 
1334 **********************************************************************/
1335
1336 void winbindd_pam_auth_crap(struct winbindd_cli_state *state)
1337 {
1338         struct winbindd_domain *domain = NULL;
1339         const char *domain_name = NULL;
1340         NTSTATUS result;
1341
1342         if (!state->privileged) {
1343                 char *error_string = NULL;
1344                 DEBUG(2, ("winbindd_pam_auth_crap: non-privileged access "
1345                           "denied.  !\n"));
1346                 DEBUGADD(2, ("winbindd_pam_auth_crap: Ensure permissions "
1347                              "on %s are set correctly.\n",
1348                              get_winbind_priv_pipe_dir()));
1349                 /* send a better message than ACCESS_DENIED */
1350                 error_string = talloc_asprintf(state->mem_ctx,
1351                                                "winbind client not authorized "
1352                                                "to use winbindd_pam_auth_crap."
1353                                                " Ensure permissions on %s "
1354                                                "are set correctly.",
1355                                                get_winbind_priv_pipe_dir());
1356                 fstrcpy(state->response.data.auth.error_string, error_string);
1357                 result = NT_STATUS_ACCESS_DENIED;
1358                 goto done;
1359         }
1360
1361         /* Ensure null termination */
1362         state->request.data.auth_crap.user
1363                 [sizeof(state->request.data.auth_crap.user)-1]=0;
1364         state->request.data.auth_crap.domain
1365                 [sizeof(state->request.data.auth_crap.domain)-1]=0;
1366
1367         DEBUG(3, ("[%5lu]: pam auth crap domain: [%s] user: %s\n",
1368                   (unsigned long)state->pid,
1369                   state->request.data.auth_crap.domain,
1370                   state->request.data.auth_crap.user));
1371
1372         if (*state->request.data.auth_crap.domain != '\0') {
1373                 domain_name = state->request.data.auth_crap.domain;
1374         } else if (lp_winbind_use_default_domain()) {
1375                 domain_name = lp_workgroup();
1376         }
1377
1378         if (domain_name != NULL)
1379                 domain = find_auth_domain(state, domain_name);
1380
1381         if (domain != NULL) {
1382                 sendto_domain(state, domain);
1383                 return;
1384         }
1385
1386         result = NT_STATUS_NO_SUCH_USER;
1387
1388  done:
1389         set_auth_errors(&state->response, result);
1390         DEBUG(5, ("CRAP authentication for %s\\%s returned %s (PAM: %d)\n",
1391                   state->request.data.auth_crap.domain,
1392                   state->request.data.auth_crap.user, 
1393                   state->response.data.auth.nt_status_string,
1394                   state->response.data.auth.pam_error));
1395         request_error(state);
1396         return;
1397 }
1398
1399
1400 enum winbindd_result winbindd_dual_pam_auth_crap(struct winbindd_domain *domain,
1401                                                  struct winbindd_cli_state *state) 
1402 {
1403         NTSTATUS result;
1404         NET_USER_INFO_3 info3;
1405         struct rpc_pipe_client *netlogon_pipe;
1406         const char *name_user = NULL;
1407         const char *name_domain = NULL;
1408         const char *workstation;
1409         struct winbindd_domain *contact_domain;
1410         int attempts = 0;
1411         BOOL retry;
1412
1413         DATA_BLOB lm_resp, nt_resp;
1414
1415         /* This is child-only, so no check for privileged access is needed
1416            anymore */
1417
1418         /* Ensure null termination */
1419         state->request.data.auth_crap.user[sizeof(state->request.data.auth_crap.user)-1]=0;
1420         state->request.data.auth_crap.domain[sizeof(state->request.data.auth_crap.domain)-1]=0;
1421
1422         name_user = state->request.data.auth_crap.user;
1423
1424         if (*state->request.data.auth_crap.domain) {
1425                 name_domain = state->request.data.auth_crap.domain;
1426         } else if (lp_winbind_use_default_domain()) {
1427                 name_domain = lp_workgroup();
1428         } else {
1429                 DEBUG(5,("no domain specified with username (%s) - failing auth\n", 
1430                          name_user));
1431                 result = NT_STATUS_NO_SUCH_USER;
1432                 goto done;
1433         }
1434
1435         DEBUG(3, ("[%5lu]: pam auth crap domain: %s user: %s\n", (unsigned long)state->pid,
1436                   name_domain, name_user));
1437            
1438         if (*state->request.data.auth_crap.workstation) {
1439                 workstation = state->request.data.auth_crap.workstation;
1440         } else {
1441                 workstation = global_myname();
1442         }
1443
1444         if (state->request.data.auth_crap.lm_resp_len > sizeof(state->request.data.auth_crap.lm_resp)
1445                 || state->request.data.auth_crap.nt_resp_len > sizeof(state->request.data.auth_crap.nt_resp)) {
1446                 DEBUG(0, ("winbindd_pam_auth_crap: invalid password length %u/%u\n", 
1447                           state->request.data.auth_crap.lm_resp_len, 
1448                           state->request.data.auth_crap.nt_resp_len));
1449                 result = NT_STATUS_INVALID_PARAMETER;
1450                 goto done;
1451         }
1452
1453         lm_resp = data_blob_talloc(state->mem_ctx, state->request.data.auth_crap.lm_resp,
1454                                         state->request.data.auth_crap.lm_resp_len);
1455         nt_resp = data_blob_talloc(state->mem_ctx, state->request.data.auth_crap.nt_resp,
1456                                         state->request.data.auth_crap.nt_resp_len);
1457
1458         /* what domain should we contact? */
1459         
1460         if ( IS_DC ) {
1461                 if (!(contact_domain = find_domain_from_name(name_domain))) {
1462                         DEBUG(3, ("Authentication for domain for [%s] -> [%s]\\[%s] failed as %s is not a trusted domain\n", 
1463                                   state->request.data.auth_crap.user, name_domain, name_user, name_domain)); 
1464                         result = NT_STATUS_NO_SUCH_USER;
1465                         goto done;
1466                 }
1467         } else {
1468                 if (is_myname(name_domain)) {
1469                         DEBUG(3, ("Authentication for domain %s (local domain to this server) not supported at this stage\n", name_domain));
1470                         result =  NT_STATUS_NO_SUCH_USER;
1471                         goto done;
1472                 }
1473                 contact_domain = find_our_domain();
1474         }
1475
1476         do {
1477                 ZERO_STRUCT(info3);
1478                 retry = False;
1479
1480                 netlogon_pipe = NULL;
1481                 result = cm_connect_netlogon(contact_domain, &netlogon_pipe);
1482
1483                 if (!NT_STATUS_IS_OK(result)) {
1484                         DEBUG(3, ("could not open handle to NETLOGON pipe (error: %s)\n",
1485                                   nt_errstr(result)));
1486                         goto done;
1487                 }
1488
1489                 result = rpccli_netlogon_sam_network_logon(netlogon_pipe,
1490                                                            state->mem_ctx,
1491                                                            state->request.data.auth_crap.logon_parameters,
1492                                                            contact_domain->dcname,
1493                                                            name_user,
1494                                                            name_domain, 
1495                                                                         /* Bug #3248 - found by Stefan Burkei. */
1496                                                            workstation, /* We carefully set this above so use it... */
1497                                                            state->request.data.auth_crap.chal,
1498                                                            lm_resp,
1499                                                            nt_resp,
1500                                                            &info3);
1501
1502                 attempts += 1;
1503
1504                 /* We have to try a second time as cm_connect_netlogon
1505                    might not yet have noticed that the DC has killed
1506                    our connection. */
1507
1508                 if (NT_STATUS_EQUAL(result, NT_STATUS_UNSUCCESSFUL)) {
1509                         retry = True;
1510                         continue;
1511                 }
1512
1513                 /* if we get access denied, a possible cause was that we had and open
1514                    connection to the DC, but someone changed our machine account password
1515                    out from underneath us using 'net rpc changetrustpw' */
1516                    
1517                 if ( NT_STATUS_EQUAL(result, NT_STATUS_ACCESS_DENIED) ) {
1518                         DEBUG(3,("winbindd_pam_auth: sam_logon returned "
1519                                  "ACCESS_DENIED.  Maybe the trust account "
1520                                 "password was changed and we didn't know it. "
1521                                  "Killing connections to domain %s\n",
1522                                 name_domain));
1523                         invalidate_cm_connection(&contact_domain->conn);
1524                         retry = True;
1525                 } 
1526
1527         } while ( (attempts < 2) && retry );
1528
1529         if (NT_STATUS_IS_OK(result)) {
1530
1531                 netsamlogon_cache_store(name_user, &info3);
1532                 wcache_invalidate_samlogon(find_domain_from_name(name_domain), &info3);
1533
1534                 /* Check if the user is in the right group */
1535
1536                 if (!NT_STATUS_IS_OK(result = check_info3_in_group(state->mem_ctx, &info3,
1537                                                         state->request.data.auth_crap.require_membership_of_sid))) {
1538                         DEBUG(3, ("User %s is not in the required group (%s), so plaintext authentication is rejected\n",
1539                                   state->request.data.auth_crap.user, 
1540                                   state->request.data.auth_crap.require_membership_of_sid));
1541                         goto done;
1542                 }
1543
1544                 if (state->request.flags & WBFLAG_PAM_INFO3_NDR) {
1545                         result = append_info3_as_ndr(state->mem_ctx, state, &info3);
1546                 } else if (state->request.flags & WBFLAG_PAM_UNIX_NAME) {
1547                         /* ntlm_auth should return the unix username, per 
1548                            'winbind use default domain' settings and the like */
1549
1550                         fstring username_out;
1551                         const char *nt_username, *nt_domain;
1552                         if (!(nt_username = unistr2_tdup(state->mem_ctx, &(info3.uni_user_name)))) {
1553                                 /* If the server didn't give us one, just use the one we sent them */
1554                                 nt_username = name_user;
1555                         }
1556
1557                         if (!(nt_domain = unistr2_tdup(state->mem_ctx, &(info3.uni_logon_dom)))) {
1558                                 /* If the server didn't give us one, just use the one we sent them */
1559                                 nt_domain = name_domain;
1560                         }
1561
1562                         fill_domain_username(username_out, nt_domain, nt_username, True);
1563
1564                         DEBUG(5, ("Setting unix username to [%s]\n", username_out));
1565
1566                         state->response.extra_data.data = SMB_STRDUP(username_out);
1567                         if (!state->response.extra_data.data) {
1568                                 result = NT_STATUS_NO_MEMORY;
1569                                 goto done;
1570                         }
1571                         state->response.length +=  strlen(state->response.extra_data.data)+1;
1572                 }
1573                 
1574                 if (state->request.flags & WBFLAG_PAM_USER_SESSION_KEY) {
1575                         memcpy(state->response.data.auth.user_session_key, info3.user_sess_key,
1576                                         sizeof(state->response.data.auth.user_session_key) /* 16 */);
1577                 }
1578                 if (state->request.flags & WBFLAG_PAM_LMKEY) {
1579                         memcpy(state->response.data.auth.first_8_lm_hash, info3.lm_sess_key,
1580                                         sizeof(state->response.data.auth.first_8_lm_hash) /* 8 */);
1581                 }
1582         }
1583
1584 done:
1585
1586         /* give us a more useful (more correct?) error code */
1587         if ((NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND) ||
1588             (NT_STATUS_EQUAL(result, NT_STATUS_UNSUCCESSFUL)))) {
1589                 result = NT_STATUS_NO_LOGON_SERVERS;
1590         }
1591
1592         if (state->request.flags & WBFLAG_PAM_NT_STATUS_SQUASH) {
1593                 result = nt_status_squash(result);
1594         }
1595
1596         state->response.data.auth.nt_status = NT_STATUS_V(result);
1597         fstrcpy(state->response.data.auth.nt_status_string, nt_errstr(result));
1598
1599         /* we might have given a more useful error above */
1600         if (!*state->response.data.auth.error_string) {
1601                 fstrcpy(state->response.data.auth.error_string, get_friendly_nt_error_msg(result));
1602         }
1603         state->response.data.auth.pam_error = nt_status_to_pam(result);
1604
1605         DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2, 
1606               ("NTLM CRAP authentication for user [%s]\\[%s] returned %s (PAM: %d)\n", 
1607                name_domain,
1608                name_user,
1609                state->response.data.auth.nt_status_string,
1610                state->response.data.auth.pam_error));         
1611
1612         return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
1613 }
1614
1615 /* Change a user password */
1616
1617 void winbindd_pam_chauthtok(struct winbindd_cli_state *state)
1618 {
1619         NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
1620         char *oldpass;
1621         char *newpass = NULL;
1622         fstring domain, user;
1623         POLICY_HND dom_pol;
1624         struct winbindd_domain *contact_domain;
1625         struct rpc_pipe_client *cli;
1626         BOOL got_info = False;
1627         SAM_UNK_INFO_1 info;
1628         SAMR_CHANGE_REJECT reject;
1629
1630         DEBUG(3, ("[%5lu]: pam chauthtok %s\n", (unsigned long)state->pid,
1631                 state->request.data.chauthtok.user));
1632
1633         /* Setup crap */
1634
1635         parse_domain_user(state->request.data.chauthtok.user, domain, user);
1636
1637         contact_domain = find_domain_from_name(domain);
1638         if (!contact_domain) {
1639                 DEBUG(3, ("Cannot change password for [%s] -> [%s]\\[%s] as %s is not a trusted domain\n", 
1640                           state->request.data.chauthtok.user, domain, user, domain)); 
1641                 result = NT_STATUS_NO_SUCH_USER;
1642                 goto done;
1643         }
1644
1645         /* Change password */
1646
1647         oldpass = state->request.data.chauthtok.oldpass;
1648         newpass = state->request.data.chauthtok.newpass;
1649
1650         /* Get sam handle */
1651
1652         result = cm_connect_sam(contact_domain, state->mem_ctx, &cli,
1653                                 &dom_pol);
1654         if (!NT_STATUS_IS_OK(result)) {
1655                 DEBUG(1, ("could not get SAM handle on DC for %s\n", domain));
1656                 goto done;
1657         }
1658
1659         result = rpccli_samr_chgpasswd3(cli, state->mem_ctx, user, newpass, oldpass, &info, &reject);
1660
1661         /* FIXME: need to check for other error codes ? */
1662         if (NT_STATUS_EQUAL(result, NT_STATUS_PASSWORD_RESTRICTION)) {
1663
1664                 state->response.data.auth.policy.min_length_password = 
1665                         info.min_length_password;
1666                 state->response.data.auth.policy.password_history = 
1667                         info.password_history;
1668                 state->response.data.auth.policy.password_properties = 
1669                         info.password_properties;
1670                 state->response.data.auth.policy.expire = 
1671                         nt_time_to_unix_abs(&info.expire);
1672                 state->response.data.auth.policy.min_passwordage = 
1673                         nt_time_to_unix_abs(&info.min_passwordage);
1674
1675                 state->response.data.auth.reject_reason = 
1676                         reject.reject_reason;
1677
1678                 got_info = True;
1679
1680         /* only fallback when the chgpasswd3 call is not supported */
1681         } else if ((NT_STATUS_EQUAL(result, NT_STATUS(DCERPC_FAULT_OP_RNG_ERROR))) ||
1682                    (NT_STATUS_EQUAL(result, NT_STATUS_NOT_SUPPORTED)) ||
1683                    (NT_STATUS_EQUAL(result, NT_STATUS_NOT_IMPLEMENTED))) {
1684
1685                 DEBUG(10,("Password change with chgpasswd3 failed with: %s, retrying chgpasswd_user\n", 
1686                         nt_errstr(result)));
1687                 
1688                 state->response.data.auth.reject_reason = 0;
1689
1690                 result = rpccli_samr_chgpasswd_user(cli, state->mem_ctx, user, newpass, oldpass);
1691         }
1692
1693 done: 
1694         if (NT_STATUS_IS_OK(result) && (state->request.flags & WBFLAG_PAM_CACHED_LOGIN) &&
1695             lp_winbind_offline_logon()) {
1696
1697                 NTSTATUS cred_ret;
1698                 
1699                 cred_ret = winbindd_update_creds_by_name(contact_domain,
1700                                                          state->mem_ctx, user,
1701                                                          newpass);
1702                 if (!NT_STATUS_IS_OK(cred_ret)) {
1703                         DEBUG(10,("Failed to store creds: %s\n", nt_errstr(cred_ret)));
1704                         goto process_result; /* FIXME: hm, risking inconsistant cache ? */
1705                 }
1706         }               
1707
1708         if (!NT_STATUS_IS_OK(result) && !got_info && contact_domain) {
1709
1710                 NTSTATUS policy_ret;
1711                 
1712                 policy_ret = fillup_password_policy(contact_domain, state);
1713
1714                 /* failure of this is non critical, it will just provide no
1715                  * additional information to the client why the change has
1716                  * failed - Guenther */
1717
1718                 if (!NT_STATUS_IS_OK(policy_ret)) {
1719                         DEBUG(10,("Failed to get password policies: %s\n", nt_errstr(policy_ret)));
1720                         goto process_result;
1721                 }
1722         }
1723
1724 process_result:
1725
1726         state->response.data.auth.nt_status = NT_STATUS_V(result);
1727         fstrcpy(state->response.data.auth.nt_status_string, nt_errstr(result));
1728         fstrcpy(state->response.data.auth.error_string, get_friendly_nt_error_msg(result));
1729         state->response.data.auth.pam_error = nt_status_to_pam(result);
1730
1731         DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2, 
1732               ("Password change for user [%s]\\[%s] returned %s (PAM: %d)\n", 
1733                domain,
1734                user,
1735                state->response.data.auth.nt_status_string,
1736                state->response.data.auth.pam_error));         
1737
1738         if (NT_STATUS_IS_OK(result)) {
1739                 request_ok(state);
1740         } else {
1741                 request_error(state);
1742         }
1743 }
1744
1745 void winbindd_pam_logoff(struct winbindd_cli_state *state)
1746 {
1747         struct winbindd_domain *domain;
1748         fstring name_domain, user;
1749         
1750         DEBUG(3, ("[%5lu]: pam logoff %s\n", (unsigned long)state->pid,
1751                 state->request.data.logoff.user));
1752
1753         /* Ensure null termination */
1754         state->request.data.logoff.user
1755                 [sizeof(state->request.data.logoff.user)-1]='\0';
1756
1757         state->request.data.logoff.krb5ccname
1758                 [sizeof(state->request.data.logoff.krb5ccname)-1]='\0';
1759
1760         parse_domain_user(state->request.data.logoff.user, name_domain, user);
1761
1762         domain = find_auth_domain(state, name_domain);
1763
1764         if (domain == NULL) {
1765                 set_auth_errors(&state->response, NT_STATUS_NO_SUCH_USER);
1766                 DEBUG(5, ("Pam Logoff for %s returned %s "
1767                           "(PAM: %d)\n",
1768                           state->request.data.auth.user, 
1769                           state->response.data.auth.nt_status_string,
1770                           state->response.data.auth.pam_error));
1771                 request_error(state);
1772                 return;
1773         }
1774
1775         sendto_domain(state, domain);
1776 }
1777
1778 enum winbindd_result winbindd_dual_pam_logoff(struct winbindd_domain *domain,
1779                                               struct winbindd_cli_state *state) 
1780 {
1781         NTSTATUS result = NT_STATUS_NOT_SUPPORTED;
1782         struct WINBINDD_CCACHE_ENTRY *entry;
1783         int ret;
1784
1785         DEBUG(3, ("[%5lu]: pam dual logoff %s\n", (unsigned long)state->pid,
1786                 state->request.data.logoff.user));
1787
1788         if (!(state->request.flags & WBFLAG_PAM_KRB5)) {
1789                 result = NT_STATUS_OK;
1790                 goto process_result;
1791         }
1792
1793 #ifdef HAVE_KRB5
1794         
1795         /* what we need here is to find the corresponding krb5 ccache name *we*
1796          * created for a given username and destroy it (as the user who created it) */
1797         
1798         entry = get_ccache_by_username(state->request.data.logoff.user);
1799         if (entry == NULL) {
1800                 DEBUG(10,("winbindd_pam_logoff: could not get ccname for user %s\n", 
1801                         state->request.data.logoff.user));
1802                 goto process_result;
1803         }
1804
1805         DEBUG(10,("winbindd_pam_logoff: found ccache [%s]\n", entry->ccname));
1806
1807         if (entry->uid < 0 || state->request.data.logoff.uid < 0) {
1808                 DEBUG(0,("winbindd_pam_logoff: invalid uid\n"));
1809                 goto process_result;
1810         }
1811
1812         if (entry->uid != state->request.data.logoff.uid) {
1813                 DEBUG(0,("winbindd_pam_logoff: uid's differ: %d != %d\n", 
1814                         entry->uid, state->request.data.logoff.uid));
1815                 goto process_result;
1816         }
1817
1818         if (!strcsequal(entry->ccname, state->request.data.logoff.krb5ccname)) {
1819                 DEBUG(0,("winbindd_pam_logoff: krb5ccnames differ: (daemon) %s != (client) %s\n", 
1820                         entry->ccname, state->request.data.logoff.krb5ccname));
1821                 goto process_result;
1822         }
1823
1824         ret = ads_kdestroy(entry->ccname);
1825
1826         if (ret) {
1827                 DEBUG(0,("winbindd_pam_logoff: failed to destroy user ccache %s with: %s\n", 
1828                         entry->ccname, error_message(ret)));
1829         } else {
1830                 DEBUG(10,("winbindd_pam_logoff: successfully destroyed ccache %s for user %s\n", 
1831                         entry->ccname, state->request.data.logoff.user));
1832                 remove_ccache_by_ccname(entry->ccname);
1833         }
1834
1835         result = krb5_to_nt_status(ret);
1836 #else
1837         result = NT_STATUS_NOT_SUPPORTED;
1838 #endif
1839
1840 process_result:
1841         state->response.data.auth.nt_status = NT_STATUS_V(result);
1842         fstrcpy(state->response.data.auth.nt_status_string, nt_errstr(result));
1843         fstrcpy(state->response.data.auth.error_string, get_friendly_nt_error_msg(result));
1844         state->response.data.auth.pam_error = nt_status_to_pam(result);
1845
1846         return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
1847 }
1848