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