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