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