Line data Source code
1 : /*
2 : SSSD
3 :
4 : Kerberos 5 Backend Module
5 :
6 : Authors:
7 : Sumit Bose <sbose@redhat.com>
8 :
9 : Copyright (C) 2009-2010 Red Hat
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 <errno.h>
26 : #include <sys/time.h>
27 :
28 : #include <sys/types.h>
29 : #include <sys/wait.h>
30 : #include <pwd.h>
31 : #include <sys/stat.h>
32 :
33 : #include <security/pam_modules.h>
34 :
35 : #include "util/util.h"
36 : #include "util/find_uid.h"
37 : #include "util/auth_utils.h"
38 : #include "db/sysdb.h"
39 : #include "util/sss_utf8.h"
40 : #include "util/child_common.h"
41 : #include "providers/krb5/krb5_auth.h"
42 : #include "providers/krb5/krb5_utils.h"
43 : #include "providers/krb5/krb5_ccache.h"
44 :
45 0 : static int krb5_mod_ccname(TALLOC_CTX *mem_ctx,
46 : struct sysdb_ctx *sysdb,
47 : struct sss_domain_info *domain,
48 : const char *name,
49 : const char *ccname,
50 : int mod_op)
51 : {
52 : TALLOC_CTX *tmpctx;
53 : struct sysdb_attrs *attrs;
54 : int ret;
55 : errno_t sret;
56 0 : bool in_transaction = false;
57 :
58 0 : if (name == NULL || ccname == NULL) {
59 0 : DEBUG(SSSDBG_CRIT_FAILURE, "Missing user or ccache name.\n");
60 0 : return EINVAL;
61 : }
62 :
63 0 : if (mod_op != SYSDB_MOD_REP && mod_op != SYSDB_MOD_DEL) {
64 0 : DEBUG(SSSDBG_CRIT_FAILURE, "Unsupported operation [%d].\n", mod_op);
65 0 : return EINVAL;
66 : }
67 :
68 0 : DEBUG(SSSDBG_TRACE_ALL, "%s ccname [%s] for user [%s].\n",
69 : mod_op == SYSDB_MOD_REP ? "Save" : "Delete", ccname, name);
70 :
71 0 : tmpctx = talloc_new(mem_ctx);
72 0 : if (!tmpctx) {
73 0 : return ENOMEM;
74 : }
75 :
76 0 : attrs = sysdb_new_attrs(tmpctx);
77 0 : if (!attrs) {
78 0 : ret = ENOMEM;
79 0 : goto done;
80 : }
81 :
82 0 : ret = sysdb_attrs_add_string(attrs, SYSDB_CCACHE_FILE, ccname);
83 0 : if (ret != EOK) {
84 0 : DEBUG(SSSDBG_CRIT_FAILURE, "sysdb_attrs_add_string failed.\n");
85 0 : goto done;
86 : }
87 :
88 0 : ret = sysdb_transaction_start(sysdb);
89 0 : if (ret != EOK) {
90 0 : DEBUG(SSSDBG_CRIT_FAILURE,
91 : "Error %d starting transaction (%s)\n", ret, strerror(ret));
92 0 : goto done;
93 : }
94 0 : in_transaction = true;
95 :
96 0 : ret = sysdb_set_user_attr(domain, name, attrs, mod_op);
97 0 : if (ret != EOK) {
98 0 : DEBUG(SSSDBG_TRACE_FUNC, "Error: %d (%s)\n", ret, strerror(ret));
99 0 : goto done;
100 : }
101 :
102 0 : ret = sysdb_transaction_commit(sysdb);
103 0 : if (ret != EOK) {
104 0 : DEBUG(SSSDBG_CRIT_FAILURE, "Failed to commit transaction!\n");
105 0 : goto done;
106 : }
107 0 : in_transaction = false;
108 :
109 : done:
110 0 : if (in_transaction) {
111 0 : sret = sysdb_transaction_cancel(sysdb);
112 0 : if (sret != EOK) {
113 0 : DEBUG(SSSDBG_CRIT_FAILURE, "Failed to cancel transaction\n");
114 : }
115 : }
116 0 : talloc_zfree(tmpctx);
117 0 : return ret;
118 : }
119 :
120 0 : static int krb5_save_ccname(TALLOC_CTX *mem_ctx,
121 : struct sysdb_ctx *sysdb,
122 : struct sss_domain_info *domain,
123 : const char *name,
124 : const char *ccname)
125 : {
126 0 : return krb5_mod_ccname(mem_ctx, sysdb, domain, name, ccname,
127 : SYSDB_MOD_REP);
128 : }
129 :
130 0 : static int krb5_delete_ccname(TALLOC_CTX *mem_ctx,
131 : struct sysdb_ctx *sysdb,
132 : struct sss_domain_info *domain,
133 : const char *name,
134 : const char *ccname)
135 : {
136 0 : return krb5_mod_ccname(mem_ctx, sysdb, domain, name, ccname,
137 : SYSDB_MOD_DEL);
138 : }
139 :
140 0 : static int krb5_cleanup(void *ptr)
141 : {
142 0 : struct krb5child_req *kr = talloc_get_type(ptr, struct krb5child_req);
143 :
144 0 : if (kr == NULL) return EOK;
145 :
146 0 : memset(kr, 0, sizeof(struct krb5child_req));
147 :
148 0 : return EOK;
149 : }
150 :
151 : static errno_t
152 0 : get_krb_primary(struct map_id_name_to_krb_primary *name_to_primary,
153 : char *id_prov_name, bool cs, const char **_krb_primary)
154 : {
155 : errno_t ret;
156 0 : int i = 0;
157 :
158 0 : while(name_to_primary != NULL &&
159 0 : name_to_primary[i].id_name != NULL &&
160 0 : name_to_primary[i].krb_primary != NULL) {
161 :
162 0 : if (sss_string_equal(cs, name_to_primary[i].id_name, id_prov_name)) {
163 0 : *_krb_primary = name_to_primary[i].krb_primary;
164 0 : ret = EOK;
165 0 : goto done;
166 : }
167 0 : i++;
168 : }
169 :
170 : /* Handle also the case of name_to_primary being NULL */
171 0 : ret = ENOENT;
172 :
173 : done:
174 0 : return ret;
175 : }
176 :
177 0 : errno_t krb5_setup(TALLOC_CTX *mem_ctx, struct pam_data *pd,
178 : struct krb5_ctx *krb5_ctx, bool cs,
179 : struct krb5child_req **_krb5_req)
180 : {
181 : struct krb5child_req *kr;
182 : const char *mapped_name;
183 : TALLOC_CTX *tmp_ctx;
184 : errno_t ret;
185 :
186 0 : tmp_ctx = talloc_new(NULL);
187 0 : if (tmp_ctx == NULL) {
188 0 : return ENOMEM;
189 : }
190 :
191 0 : kr = talloc_zero(tmp_ctx, struct krb5child_req);
192 0 : if (kr == NULL) {
193 0 : DEBUG(SSSDBG_CRIT_FAILURE, "talloc failed.\n");
194 0 : ret = ENOMEM;
195 0 : goto done;
196 : }
197 0 : kr->is_offline = false;
198 0 : talloc_set_destructor((TALLOC_CTX *) kr, krb5_cleanup);
199 :
200 0 : kr->pd = pd;
201 0 : kr->krb5_ctx = krb5_ctx;
202 :
203 0 : ret = get_krb_primary(krb5_ctx->name_to_primary,
204 : pd->user, cs, &mapped_name);
205 0 : if (ret == EOK) {
206 0 : DEBUG(SSSDBG_TRACE_FUNC, "Setting mapped name to: %s\n", mapped_name);
207 0 : kr->user = mapped_name;
208 0 : } else if (ret == ENOENT) {
209 0 : DEBUG(SSSDBG_TRACE_ALL, "No mapping for: %s\n", pd->user);
210 0 : kr->user = pd->user;
211 : } else {
212 0 : DEBUG(SSSDBG_CRIT_FAILURE, "get_krb_primary failed - %s:[%d]\n",
213 : sss_strerror(ret), ret);
214 0 : goto done;
215 : }
216 :
217 0 : ret = EOK;
218 :
219 : done:
220 0 : if (ret == EOK) {
221 0 : *_krb5_req = talloc_steal(mem_ctx, kr);
222 : }
223 0 : talloc_free(tmp_ctx);
224 0 : return ret;
225 : }
226 :
227 :
228 0 : static void krb5_auth_cache_creds(struct krb5_ctx *krb5_ctx,
229 : struct sss_domain_info *domain,
230 : struct confdb_ctx *cdb,
231 : struct pam_data *pd, uid_t uid,
232 : int *pam_status, int *dp_err)
233 : {
234 0 : const char *password = NULL;
235 : errno_t ret;
236 :
237 0 : if (sss_authtok_get_type(pd->authtok) != SSS_AUTHTOK_TYPE_PASSWORD) {
238 0 : DEBUG(SSSDBG_MINOR_FAILURE,
239 : "Delayed authentication is only available for password "
240 : "authentication (single factor).\n");
241 0 : return;
242 : }
243 :
244 0 : ret = sss_authtok_get_password(pd->authtok, &password, NULL);
245 0 : if (ret != EOK) {
246 0 : DEBUG(SSSDBG_FATAL_FAILURE,
247 : "Failed to get password [%d] %s\n", ret, strerror(ret));
248 0 : *pam_status = PAM_SYSTEM_ERR;
249 0 : *dp_err = DP_ERR_OK;
250 0 : return;
251 : }
252 :
253 0 : ret = sysdb_cache_auth(domain, pd->user,
254 : password, cdb, true, NULL, NULL);
255 0 : if (ret != EOK) {
256 0 : DEBUG(SSSDBG_CRIT_FAILURE, "Offline authentication failed\n");
257 0 : *pam_status = cached_login_pam_status(ret);
258 0 : *dp_err = DP_ERR_OK;
259 0 : return;
260 : }
261 :
262 0 : ret = add_user_to_delayed_online_authentication(krb5_ctx, pd, uid);
263 0 : if (ret != EOK) {
264 : /* This error is not fatal */
265 0 : DEBUG(SSSDBG_CRIT_FAILURE,
266 : "add_user_to_delayed_online_authentication failed.\n");
267 : }
268 0 : *pam_status = PAM_AUTHINFO_UNAVAIL;
269 0 : *dp_err = DP_ERR_OFFLINE;
270 : }
271 :
272 0 : static errno_t krb5_auth_prepare_ccache_name(struct krb5child_req *kr,
273 : struct ldb_message *user_msg,
274 : struct be_ctx *be_ctx)
275 : {
276 : const char *ccname_template;
277 :
278 0 : ccname_template = dp_opt_get_cstring(kr->krb5_ctx->opts, KRB5_CCNAME_TMPL);
279 :
280 0 : kr->ccname = expand_ccname_template(kr, kr, ccname_template,
281 0 : kr->krb5_ctx->illegal_path_re, true,
282 0 : be_ctx->domain->case_sensitive);
283 0 : if (kr->ccname == NULL) {
284 0 : DEBUG(SSSDBG_CRIT_FAILURE, "expand_ccname_template failed.\n");
285 0 : return ENOMEM;
286 : }
287 :
288 0 : kr->old_ccname = ldb_msg_find_attr_as_string(user_msg,
289 : SYSDB_CCACHE_FILE, NULL);
290 0 : if (kr->old_ccname == NULL) {
291 0 : DEBUG(SSSDBG_TRACE_LIBS,
292 : "No ccache file for user [%s] found.\n", kr->pd->user);
293 : }
294 :
295 0 : return EOK;
296 : }
297 :
298 0 : static void krb5_auth_store_creds(struct sss_domain_info *domain,
299 : struct pam_data *pd)
300 : {
301 0 : const char *password = NULL;
302 : const char *fa2;
303 : size_t password_len;
304 0 : size_t fa2_len = 0;
305 0 : int ret = EOK;
306 :
307 0 : switch(pd->cmd) {
308 : case SSS_CMD_RENEW:
309 : /* The authtok is set to the credential cache
310 : * during renewal. We don't want to save this
311 : * as the cached password.
312 : */
313 0 : break;
314 : case SSS_PAM_PREAUTH:
315 : /* There are no credentials available during pre-authentication,
316 : * nothing to do. */
317 0 : break;
318 : case SSS_PAM_AUTHENTICATE:
319 : case SSS_PAM_CHAUTHTOK_PRELIM:
320 0 : if (sss_authtok_get_type(pd->authtok) == SSS_AUTHTOK_TYPE_2FA) {
321 0 : ret = sss_authtok_get_2fa(pd->authtok, &password, &password_len,
322 : &fa2, &fa2_len);
323 0 : if (ret == EOK && password_len <
324 0 : domain->cache_credentials_min_ff_length) {
325 0 : DEBUG(SSSDBG_FATAL_FAILURE,
326 : "First factor is too short to be cache, "
327 : "minimum length is [%u].\n",
328 : domain->cache_credentials_min_ff_length);
329 0 : ret = EINVAL;
330 : }
331 : } else {
332 0 : ret = sss_authtok_get_password(pd->authtok, &password, NULL);
333 : }
334 0 : break;
335 : case SSS_PAM_CHAUTHTOK:
336 0 : ret = sss_authtok_get_password(pd->newauthtok, &password, NULL);
337 0 : break;
338 : default:
339 0 : DEBUG(SSSDBG_FATAL_FAILURE,
340 : "unsupported PAM command [%d].\n", pd->cmd);
341 : }
342 :
343 0 : if (ret != EOK) {
344 0 : DEBUG(SSSDBG_FATAL_FAILURE,
345 : "Failed to get password [%d] %s\n", ret, strerror(ret));
346 : /* password caching failures are not fatal errors */
347 0 : return;
348 : }
349 :
350 0 : if (password == NULL) {
351 0 : if (pd->cmd != SSS_CMD_RENEW && pd->cmd != SSS_PAM_PREAUTH) {
352 0 : DEBUG(SSSDBG_FATAL_FAILURE,
353 : "password not available, offline auth may not work.\n");
354 : /* password caching failures are not fatal errors */
355 : }
356 0 : return;
357 : }
358 :
359 0 : ret = sysdb_cache_password_ex(domain, pd->user, password,
360 : sss_authtok_get_type(pd->authtok), fa2_len);
361 0 : if (ret) {
362 0 : DEBUG(SSSDBG_OP_FAILURE,
363 : "Failed to cache password, offline auth may not work."
364 : " (%d)[%s]!?\n", ret, strerror(ret));
365 : /* password caching failures are not fatal errors */
366 : }
367 : }
368 :
369 0 : static bool is_otp_enabled(struct ldb_message *user_msg)
370 : {
371 : struct ldb_message_element *el;
372 : size_t i;
373 :
374 0 : el = ldb_msg_find_element(user_msg, SYSDB_AUTH_TYPE);
375 0 : if (el == NULL) {
376 0 : return false;
377 : }
378 :
379 0 : for (i = 0; i < el->num_values; i++) {
380 0 : if (strcmp((const char * )el->values[i].data, "otp") == 0) {
381 0 : return true;
382 : }
383 : }
384 :
385 0 : return false;
386 : }
387 :
388 : /* krb5_auth request */
389 :
390 : struct krb5_auth_state {
391 : struct tevent_context *ev;
392 : struct be_ctx *be_ctx;
393 : struct pam_data *pd;
394 : struct sysdb_ctx *sysdb;
395 : struct sss_domain_info *domain;
396 : struct krb5_ctx *krb5_ctx;
397 : struct krb5child_req *kr;
398 :
399 : bool search_kpasswd;
400 :
401 : int pam_status;
402 : int dp_err;
403 : };
404 :
405 : static void krb5_auth_resolve_done(struct tevent_req *subreq);
406 : static void krb5_auth_done(struct tevent_req *subreq);
407 :
408 0 : struct tevent_req *krb5_auth_send(TALLOC_CTX *mem_ctx,
409 : struct tevent_context *ev,
410 : struct be_ctx *be_ctx,
411 : struct pam_data *pd,
412 : struct krb5_ctx *krb5_ctx)
413 : {
414 : const char **attrs;
415 : struct krb5_auth_state *state;
416 : struct ldb_result *res;
417 0 : struct krb5child_req *kr = NULL;
418 : const char *realm;
419 : struct tevent_req *req;
420 : struct tevent_req *subreq;
421 : enum sss_authtok_type authtok_type;
422 : int ret;
423 : bool otp;
424 :
425 0 : req = tevent_req_create(mem_ctx, &state, struct krb5_auth_state);
426 0 : if (req == NULL) {
427 0 : DEBUG(SSSDBG_CRIT_FAILURE, "tevent_req_create failed.\n");
428 0 : return NULL;
429 : }
430 :
431 0 : state->ev = ev;
432 0 : state->be_ctx = be_ctx;
433 0 : state->pd = pd;
434 0 : state->krb5_ctx = krb5_ctx;
435 0 : state->kr = NULL;
436 0 : state->pam_status = PAM_SYSTEM_ERR;
437 0 : state->dp_err = DP_ERR_FATAL;
438 :
439 0 : ret = get_domain_or_subdomain(be_ctx, pd->domain, &state->domain);
440 0 : if (ret != EOK) {
441 0 : DEBUG(SSSDBG_OP_FAILURE, "get_domain_or_subdomain failed.\n");
442 0 : goto done;
443 : }
444 :
445 0 : state->sysdb = state->domain->sysdb;
446 :
447 0 : authtok_type = sss_authtok_get_type(pd->authtok);
448 :
449 0 : switch (pd->cmd) {
450 : case SSS_PAM_AUTHENTICATE:
451 : case SSS_PAM_CHAUTHTOK:
452 0 : if (authtok_type != SSS_AUTHTOK_TYPE_PASSWORD
453 0 : && authtok_type != SSS_AUTHTOK_TYPE_2FA) {
454 : /* handle empty password gracefully */
455 0 : if (authtok_type == SSS_AUTHTOK_TYPE_EMPTY) {
456 0 : DEBUG(SSSDBG_CRIT_FAILURE,
457 : "Illegal zero-length authtok for user [%s]\n",
458 : pd->user);
459 0 : state->pam_status = PAM_AUTH_ERR;
460 0 : state->dp_err = DP_ERR_OK;
461 0 : ret = EOK;
462 0 : goto done;
463 : }
464 :
465 0 : DEBUG(SSSDBG_CRIT_FAILURE,
466 : "Wrong authtok type for user [%s]. " \
467 : "Expected [%d], got [%d]\n", pd->user,
468 : SSS_AUTHTOK_TYPE_PASSWORD,
469 : authtok_type);
470 0 : state->pam_status = PAM_SYSTEM_ERR;
471 0 : state->dp_err = DP_ERR_FATAL;
472 0 : ret = EINVAL;
473 0 : goto done;
474 : }
475 0 : break;
476 : case SSS_PAM_CHAUTHTOK_PRELIM:
477 0 : if (pd->priv == 1 &&
478 : authtok_type != SSS_AUTHTOK_TYPE_PASSWORD) {
479 0 : DEBUG(SSSDBG_MINOR_FAILURE,
480 : "Password reset by root is not supported.\n");
481 0 : state->pam_status = PAM_PERM_DENIED;
482 0 : state->dp_err = DP_ERR_OK;
483 0 : ret = EOK;
484 0 : goto done;
485 : }
486 0 : break;
487 : case SSS_CMD_RENEW:
488 0 : if (authtok_type != SSS_AUTHTOK_TYPE_CCFILE) {
489 0 : DEBUG(SSSDBG_CRIT_FAILURE,
490 : "Wrong authtok type for user [%s]. " \
491 : "Expected [%d], got [%d]\n", pd->user,
492 : SSS_AUTHTOK_TYPE_CCFILE,
493 : authtok_type);
494 0 : state->pam_status = PAM_SYSTEM_ERR;
495 0 : state->dp_err = DP_ERR_FATAL;
496 0 : ret = EINVAL;
497 0 : goto done;
498 : }
499 0 : break;
500 : case SSS_PAM_PREAUTH:
501 0 : break;
502 : default:
503 0 : DEBUG(SSSDBG_CONF_SETTINGS, "Unexpected pam task %d.\n", pd->cmd);
504 0 : state->pam_status = PAM_SYSTEM_ERR;
505 0 : state->dp_err = DP_ERR_FATAL;
506 0 : ret = EINVAL;
507 0 : goto done;
508 : }
509 :
510 0 : if (be_is_offline(be_ctx) &&
511 0 : (pd->cmd == SSS_PAM_CHAUTHTOK || pd->cmd == SSS_PAM_CHAUTHTOK_PRELIM ||
512 0 : pd->cmd == SSS_CMD_RENEW)) {
513 0 : DEBUG(SSSDBG_TRACE_ALL,
514 : "Password changes and ticket renewal are not possible "
515 : "while offline.\n");
516 0 : state->pam_status = PAM_AUTHINFO_UNAVAIL;
517 0 : state->dp_err = DP_ERR_OFFLINE;
518 0 : ret = EOK;
519 0 : goto done;
520 : }
521 :
522 0 : attrs = talloc_array(state, const char *, 8);
523 0 : if (attrs == NULL) {
524 0 : ret = ENOMEM;
525 0 : goto done;
526 : }
527 :
528 0 : attrs[0] = SYSDB_UPN;
529 0 : attrs[1] = SYSDB_HOMEDIR;
530 0 : attrs[2] = SYSDB_CCACHE_FILE;
531 0 : attrs[3] = SYSDB_UIDNUM;
532 0 : attrs[4] = SYSDB_GIDNUM;
533 0 : attrs[5] = SYSDB_CANONICAL_UPN;
534 0 : attrs[6] = SYSDB_AUTH_TYPE;
535 0 : attrs[7] = NULL;
536 :
537 0 : ret = krb5_setup(state, pd, krb5_ctx, state->domain->case_sensitive,
538 0 : &state->kr);
539 0 : if (ret != EOK) {
540 0 : DEBUG(SSSDBG_CRIT_FAILURE, "krb5_setup failed.\n");
541 0 : goto done;
542 : }
543 0 : kr = state->kr;
544 :
545 0 : ret = sysdb_get_user_attr_with_views(state, state->domain, state->pd->user,
546 : attrs, &res);
547 0 : if (ret) {
548 0 : DEBUG(SSSDBG_FUNC_DATA,
549 : "sysdb search for upn of user [%s] failed.\n", pd->user);
550 0 : state->pam_status = PAM_SYSTEM_ERR;
551 0 : state->dp_err = DP_ERR_OK;
552 0 : goto done;
553 : }
554 :
555 0 : realm = dp_opt_get_cstring(krb5_ctx->opts, KRB5_REALM);
556 0 : if (realm == NULL) {
557 0 : DEBUG(SSSDBG_CRIT_FAILURE, "Missing Kerberos realm.\n");
558 0 : ret = ENOENT;
559 0 : goto done;
560 : }
561 :
562 0 : switch (res->count) {
563 : case 0:
564 0 : DEBUG(SSSDBG_FUNC_DATA,
565 : "No attributes for user [%s] found.\n", pd->user);
566 0 : ret = ENOENT;
567 0 : goto done;
568 : break;
569 :
570 : case 1:
571 0 : ret = find_or_guess_upn(state, res->msgs[0], krb5_ctx, be_ctx->domain,
572 0 : kr->user, pd->domain, &kr->upn);
573 0 : if (ret != EOK) {
574 0 : DEBUG(SSSDBG_OP_FAILURE, "find_or_guess_upn failed.\n");
575 0 : goto done;
576 : }
577 :
578 0 : ret = compare_principal_realm(kr->upn, realm,
579 : &kr->upn_from_different_realm);
580 0 : if (ret != 0) {
581 0 : DEBUG(SSSDBG_OP_FAILURE, "compare_principal_realm failed.\n");
582 0 : goto done;
583 : }
584 :
585 0 : kr->homedir = sss_view_ldb_msg_find_attr_as_string(state->domain,
586 0 : res->msgs[0],
587 : SYSDB_HOMEDIR,
588 : NULL);
589 0 : if (kr->homedir == NULL) {
590 0 : DEBUG(SSSDBG_CONF_SETTINGS,
591 : "Home directory for user [%s] not known.\n", pd->user);
592 : }
593 :
594 0 : kr->uid = sss_view_ldb_msg_find_attr_as_uint64(state->domain,
595 0 : res->msgs[0],
596 : SYSDB_UIDNUM, 0);
597 0 : if (kr->uid == 0) {
598 0 : DEBUG(SSSDBG_CONF_SETTINGS,
599 : "UID for user [%s] not known.\n", pd->user);
600 0 : ret = ENOENT;
601 0 : goto done;
602 : }
603 :
604 0 : kr->gid = sss_view_ldb_msg_find_attr_as_uint64(state->domain,
605 0 : res->msgs[0],
606 : SYSDB_GIDNUM, 0);
607 0 : if (kr->gid == 0) {
608 0 : DEBUG(SSSDBG_CONF_SETTINGS,
609 : "GID for user [%s] not known.\n", pd->user);
610 0 : ret = ENOENT;
611 0 : goto done;
612 : }
613 :
614 0 : ret = krb5_auth_prepare_ccache_name(kr, res->msgs[0], state->be_ctx);
615 0 : if (ret != EOK) {
616 0 : DEBUG(SSSDBG_CRIT_FAILURE, "Cannot prepare ccache names!\n");
617 0 : goto done;
618 : }
619 0 : break;
620 :
621 : default:
622 0 : DEBUG(SSSDBG_CRIT_FAILURE,
623 : "User search for (%s) returned > 1 results!\n", pd->user);
624 0 : ret = EINVAL;
625 0 : goto done;
626 : break;
627 : }
628 :
629 0 : otp = is_otp_enabled(res->msgs[0]);
630 0 : if (pd->cmd == SSS_PAM_CHAUTHTOK_PRELIM && otp == true) {
631 : /* To avoid consuming the OTP */
632 0 : DEBUG(SSSDBG_TRACE_FUNC,
633 : "Skipping password checks for OTP-enabled user\n");
634 0 : state->pam_status = PAM_SUCCESS;
635 0 : state->dp_err = DP_ERR_OK;
636 0 : ret = EOK;
637 0 : goto done;
638 : }
639 :
640 0 : kr->srv = NULL;
641 0 : kr->kpasswd_srv = NULL;
642 :
643 0 : state->search_kpasswd = false;
644 0 : subreq = be_resolve_server_send(state, state->ev, state->be_ctx,
645 0 : state->krb5_ctx->service->name,
646 0 : state->kr->srv == NULL ? true : false);
647 0 : if (!subreq) {
648 0 : DEBUG(SSSDBG_CRIT_FAILURE, "Failed resolver request.\n");
649 0 : ret = EIO;
650 0 : goto done;
651 : }
652 0 : tevent_req_set_callback(subreq, krb5_auth_resolve_done, req);
653 :
654 0 : return req;
655 :
656 : done:
657 0 : if (ret == EOK) {
658 0 : tevent_req_done(req);
659 : } else {
660 0 : tevent_req_error(req, ret);
661 : }
662 0 : tevent_req_post(req, state->ev);
663 0 : return req;
664 : }
665 :
666 0 : static void krb5_auth_resolve_done(struct tevent_req *subreq)
667 : {
668 0 : struct tevent_req *req = tevent_req_callback_data(subreq, struct tevent_req);
669 0 : struct krb5_auth_state *state = tevent_req_data(req, struct krb5_auth_state);
670 0 : struct krb5child_req *kr = state->kr;
671 : int ret;
672 :
673 0 : if (!state->search_kpasswd) {
674 0 : ret = be_resolve_server_recv(subreq, kr, &kr->srv);
675 : } else {
676 0 : ret = be_resolve_server_recv(subreq, kr, &kr->kpasswd_srv);
677 : }
678 0 : talloc_zfree(subreq);
679 :
680 0 : if (state->search_kpasswd) {
681 0 : if ((ret != EOK) &&
682 0 : (kr->pd->cmd == SSS_PAM_CHAUTHTOK ||
683 0 : kr->pd->cmd == SSS_PAM_CHAUTHTOK_PRELIM)) {
684 : /* all kpasswd servers have been tried and none was found good,
685 : * but the kdc seems ok. Password changes are not possible but
686 : * authentication is. We return an PAM error here, but do not
687 : * mark the backend offline. */
688 0 : state->pam_status = PAM_AUTHTOK_LOCK_BUSY;
689 0 : state->dp_err = DP_ERR_OK;
690 0 : ret = EOK;
691 0 : goto done;
692 : }
693 : } else {
694 0 : if (ret != EOK) {
695 : /* all servers have been tried and none
696 : * was found good, setting offline,
697 : * but we still have to call the child to setup
698 : * the ccache file if we are performing auth */
699 0 : be_mark_dom_offline(state->domain, state->be_ctx);
700 0 : kr->is_offline = true;
701 :
702 0 : if (kr->pd->cmd == SSS_PAM_CHAUTHTOK ||
703 0 : kr->pd->cmd == SSS_PAM_CHAUTHTOK_PRELIM) {
704 0 : DEBUG(SSSDBG_TRACE_FUNC,
705 : "No KDC suitable for password change is available\n");
706 0 : state->pam_status = PAM_AUTHTOK_LOCK_BUSY;
707 0 : state->dp_err = DP_ERR_OK;
708 0 : ret = EOK;
709 0 : goto done;
710 : }
711 : } else {
712 0 : if (kr->krb5_ctx->kpasswd_service != NULL) {
713 0 : state->search_kpasswd = true;
714 0 : subreq = be_resolve_server_send(state,
715 : state->ev, state->be_ctx,
716 0 : state->krb5_ctx->kpasswd_service->name,
717 0 : kr->kpasswd_srv == NULL ? true : false);
718 0 : if (subreq == NULL) {
719 0 : DEBUG(SSSDBG_CRIT_FAILURE, "Resolver request failed.\n");
720 0 : ret = EIO;
721 0 : goto done;
722 : }
723 0 : tevent_req_set_callback(subreq, krb5_auth_resolve_done, req);
724 0 : return;
725 : }
726 : }
727 : }
728 :
729 0 : if (!kr->is_offline) {
730 0 : kr->is_offline = be_is_offline(state->be_ctx);
731 : }
732 :
733 0 : if (!kr->is_offline
734 0 : && sss_domain_get_state(state->domain) == DOM_INACTIVE) {
735 0 : DEBUG(SSSDBG_TRACE_INTERNAL,
736 : "Subdomain %s is inactive, will proceed offline\n",
737 : state->domain->name);
738 0 : kr->is_offline = true;
739 : }
740 :
741 0 : if (kr->is_offline
742 0 : && sss_krb5_realm_has_proxy(dp_opt_get_cstring(kr->krb5_ctx->opts,
743 : KRB5_REALM))) {
744 0 : DEBUG(SSSDBG_TRACE_FUNC,
745 : "Resetting offline status, KDC proxy is in use\n");
746 0 : kr->is_offline = false;
747 : }
748 :
749 0 : subreq = handle_child_send(state, state->ev, kr);
750 0 : if (subreq == NULL) {
751 0 : DEBUG(SSSDBG_CRIT_FAILURE, "handle_child_send failed.\n");
752 0 : ret = ENOMEM;
753 0 : goto done;
754 : }
755 0 : tevent_req_set_callback(subreq, krb5_auth_done, req);
756 0 : return;
757 :
758 : done:
759 0 : if (ret == EOK) {
760 0 : tevent_req_done(req);
761 : } else {
762 0 : tevent_req_error(req, ret);
763 : }
764 : }
765 :
766 0 : static void krb5_auth_done(struct tevent_req *subreq)
767 : {
768 0 : struct tevent_req *req = tevent_req_callback_data(subreq, struct tevent_req);
769 0 : struct krb5_auth_state *state = tevent_req_data(req, struct krb5_auth_state);
770 0 : struct krb5child_req *kr = state->kr;
771 0 : struct pam_data *pd = state->pd;
772 : int ret;
773 0 : uint8_t *buf = NULL;
774 0 : ssize_t len = -1;
775 : struct krb5_child_response *res;
776 : struct fo_server *search_srv;
777 : krb5_deltat renew_interval_delta;
778 : char *renew_interval_str;
779 0 : time_t renew_interval_time = 0;
780 : bool use_enterprise_principal;
781 :
782 0 : ret = handle_child_recv(subreq, pd, &buf, &len);
783 0 : talloc_zfree(subreq);
784 0 : if (ret == ETIMEDOUT) {
785 :
786 0 : DEBUG(SSSDBG_CRIT_FAILURE, "child timed out!\n");
787 :
788 0 : switch (pd->cmd) {
789 : case SSS_PAM_AUTHENTICATE:
790 : case SSS_CMD_RENEW:
791 0 : state->search_kpasswd = false;
792 0 : search_srv = kr->srv;
793 0 : break;
794 : case SSS_PAM_CHAUTHTOK:
795 : case SSS_PAM_CHAUTHTOK_PRELIM:
796 0 : if (state->kr->kpasswd_srv) {
797 0 : state->search_kpasswd = true;
798 0 : search_srv = kr->kpasswd_srv;
799 0 : break;
800 : } else {
801 0 : state->search_kpasswd = false;
802 0 : search_srv = kr->srv;
803 0 : break;
804 : }
805 : case SSS_PAM_PREAUTH:
806 0 : state->pam_status = PAM_CRED_UNAVAIL;
807 0 : state->dp_err = DP_ERR_OK;
808 0 : ret = EOK;
809 0 : goto done;
810 : default:
811 0 : DEBUG(SSSDBG_CRIT_FAILURE, "Unexpected PAM task\n");
812 0 : ret = EINVAL;
813 0 : goto done;
814 : }
815 :
816 0 : be_fo_set_port_status(state->be_ctx, state->krb5_ctx->service->name,
817 : search_srv, PORT_NOT_WORKING);
818 0 : subreq = be_resolve_server_send(state, state->ev, state->be_ctx,
819 0 : state->krb5_ctx->service->name,
820 : search_srv == NULL ? true : false);
821 0 : if (subreq == NULL) {
822 0 : DEBUG(SSSDBG_CRIT_FAILURE, "Failed resolved request.\n");
823 0 : ret = ENOMEM;
824 0 : goto done;
825 : }
826 0 : tevent_req_set_callback(subreq, krb5_auth_resolve_done, req);
827 0 : return;
828 :
829 0 : } else if (ret != EOK) {
830 :
831 0 : DEBUG(SSSDBG_CRIT_FAILURE,
832 : "child failed (%d [%s])\n", ret, strerror(ret));
833 0 : goto done;
834 : }
835 :
836 : /* EOK */
837 :
838 0 : ret = parse_krb5_child_response(state, buf, len, pd,
839 0 : state->be_ctx->domain->pwd_expiration_warning,
840 : &res);
841 0 : if (ret) {
842 0 : DEBUG(SSSDBG_OP_FAILURE, "Could not parse child response [%d]: %s\n",
843 : ret, strerror(ret));
844 0 : goto done;
845 : }
846 :
847 0 : if (res->ccname) {
848 0 : kr->ccname = talloc_strdup(kr, res->ccname);
849 0 : if (!kr->ccname) {
850 0 : ret = ENOMEM;
851 0 : goto done;
852 : }
853 : }
854 :
855 0 : use_enterprise_principal = dp_opt_get_bool(kr->krb5_ctx->opts,
856 : KRB5_USE_ENTERPRISE_PRINCIPAL);
857 :
858 : /* Check if the cases of our upn are correct and update it if needed.
859 : * Fail if the upn differs by more than just the case for non-enterprise
860 : * principals. */
861 0 : if (res->correct_upn != NULL &&
862 0 : strcmp(kr->upn, res->correct_upn) != 0) {
863 0 : if (strcasecmp(kr->upn, res->correct_upn) == 0 ||
864 : use_enterprise_principal == true) {
865 0 : talloc_free(kr->upn);
866 0 : kr->upn = talloc_strdup(kr, res->correct_upn);
867 0 : if (kr->upn == NULL) {
868 0 : DEBUG(SSSDBG_OP_FAILURE, "talloc_strdup failed.\n");
869 0 : ret = ENOMEM;
870 0 : goto done;
871 : }
872 :
873 0 : ret = check_if_cached_upn_needs_update(state->sysdb, state->domain,
874 0 : pd->user, res->correct_upn);
875 0 : if (ret != EOK) {
876 0 : DEBUG(SSSDBG_OP_FAILURE,
877 : "check_if_cached_upn_needs_update failed.\n");
878 0 : goto done;
879 : }
880 : } else {
881 0 : DEBUG(SSSDBG_CRIT_FAILURE, "UPN used in the request [%s] and " \
882 : "returned UPN [%s] differ by more " \
883 : "than just the case.\n",
884 : kr->upn, res->correct_upn);
885 0 : ret = EINVAL;
886 0 : goto done;
887 : }
888 : }
889 :
890 : /* If the child request failed, but did not return an offline error code,
891 : * return with the status */
892 0 : switch (res->msg_status) {
893 : case ERR_OK:
894 : /* If the child request was successful and we run the first pass of the
895 : * change password request just return success. */
896 0 : if (pd->cmd == SSS_PAM_CHAUTHTOK_PRELIM) {
897 0 : state->pam_status = PAM_SUCCESS;
898 0 : state->dp_err = DP_ERR_OK;
899 0 : ret = EOK;
900 0 : goto done;
901 : }
902 0 : break;
903 :
904 : case ERR_NETWORK_IO:
905 0 : if (kr->kpasswd_srv != NULL &&
906 0 : (pd->cmd == SSS_PAM_CHAUTHTOK ||
907 0 : pd->cmd == SSS_PAM_CHAUTHTOK_PRELIM)) {
908 : /* if using a dedicated kpasswd server for a chpass operation... */
909 :
910 0 : be_fo_set_port_status(state->be_ctx,
911 : state->krb5_ctx->kpasswd_service->name,
912 : kr->kpasswd_srv, PORT_NOT_WORKING);
913 : /* ..try to resolve next kpasswd server */
914 0 : state->search_kpasswd = true;
915 0 : subreq = be_resolve_server_send(state, state->ev, state->be_ctx,
916 0 : state->krb5_ctx->kpasswd_service->name,
917 0 : state->kr->kpasswd_srv == NULL ? true : false);
918 0 : if (subreq == NULL) {
919 0 : DEBUG(SSSDBG_CRIT_FAILURE, "Resolver request failed.\n");
920 0 : ret = ENOMEM;
921 0 : goto done;
922 : }
923 0 : tevent_req_set_callback(subreq, krb5_auth_resolve_done, req);
924 0 : return;
925 0 : } else if (kr->srv != NULL) {
926 : /* failed to use the KDC... */
927 0 : be_fo_set_port_status(state->be_ctx,
928 : state->krb5_ctx->service->name,
929 : kr->srv, PORT_NOT_WORKING);
930 : /* ..try to resolve next KDC */
931 0 : state->search_kpasswd = false;
932 0 : subreq = be_resolve_server_send(state, state->ev, state->be_ctx,
933 0 : state->krb5_ctx->service->name,
934 0 : kr->srv == NULL ? true : false);
935 0 : if (subreq == NULL) {
936 0 : DEBUG(SSSDBG_CRIT_FAILURE, "Resolver request failed.\n");
937 0 : ret = ENOMEM;
938 0 : goto done;
939 : }
940 0 : tevent_req_set_callback(subreq, krb5_auth_resolve_done, req);
941 0 : return;
942 : }
943 0 : break;
944 :
945 : case ERR_CREDS_EXPIRED_CCACHE:
946 0 : ret = krb5_delete_ccname(state, state->sysdb, state->domain,
947 0 : pd->user, kr->old_ccname);
948 0 : if (ret != EOK) {
949 0 : DEBUG(SSSDBG_CRIT_FAILURE, "krb5_delete_ccname failed.\n");
950 : }
951 : /* FALLTHROUGH */
952 :
953 : case ERR_CREDS_EXPIRED:
954 : /* If the password is expired we can safely remove the ccache from the
955 : * cache and disk if it is not actively used anymore. This will allow
956 : * to create a new random ccache if sshd with privilege separation is
957 : * used. */
958 0 : if (pd->cmd == SSS_PAM_AUTHENTICATE && !kr->active_ccache) {
959 0 : if (kr->old_ccname != NULL) {
960 0 : ret = krb5_delete_ccname(state, state->sysdb, state->domain,
961 0 : pd->user, kr->old_ccname);
962 0 : if (ret != EOK) {
963 0 : DEBUG(SSSDBG_CRIT_FAILURE, "krb5_delete_ccname failed.\n");
964 : }
965 : }
966 : }
967 :
968 0 : state->pam_status = PAM_NEW_AUTHTOK_REQD;
969 0 : state->dp_err = DP_ERR_OK;
970 0 : ret = EOK;
971 0 : goto done;
972 :
973 : case ERR_CREDS_INVALID:
974 0 : state->pam_status = PAM_CRED_ERR;
975 0 : state->dp_err = DP_ERR_OK;
976 0 : ret = EOK;
977 0 : goto done;
978 :
979 : case ERR_ACCOUNT_EXPIRED:
980 0 : state->pam_status = PAM_ACCT_EXPIRED;
981 0 : state->dp_err = DP_ERR_OK;
982 0 : ret = EOK;
983 0 : goto done;
984 :
985 : case ERR_ACCOUNT_LOCKED:
986 0 : state->pam_status = PAM_PERM_DENIED;
987 0 : state->dp_err = DP_ERR_OK;
988 0 : ret = EOK;
989 0 : goto done;
990 :
991 : case ERR_NO_CREDS:
992 0 : state->pam_status = PAM_CRED_UNAVAIL;
993 0 : state->dp_err = DP_ERR_OK;
994 0 : ret = EOK;
995 0 : goto done;
996 :
997 : case ERR_AUTH_FAILED:
998 0 : state->pam_status = PAM_AUTH_ERR;
999 0 : state->dp_err = DP_ERR_OK;
1000 0 : ret = EOK;
1001 0 : goto done;
1002 :
1003 : case ERR_CHPASS_FAILED:
1004 0 : state->pam_status = PAM_AUTHTOK_ERR;
1005 0 : state->dp_err = DP_ERR_OK;
1006 0 : ret = EOK;
1007 0 : goto done;
1008 :
1009 : default:
1010 0 : state->pam_status = PAM_SYSTEM_ERR;
1011 0 : state->dp_err = DP_ERR_OK;
1012 0 : ret = EOK;
1013 0 : goto done;
1014 : }
1015 :
1016 0 : if (kr->kpasswd_srv != NULL &&
1017 0 : (pd->cmd == SSS_PAM_CHAUTHTOK ||
1018 0 : pd->cmd == SSS_PAM_CHAUTHTOK_PRELIM)) {
1019 : /* found a dedicated kpasswd server for a chpass operation */
1020 0 : be_fo_set_port_status(state->be_ctx,
1021 : state->krb5_ctx->service->name,
1022 : kr->kpasswd_srv, PORT_WORKING);
1023 0 : } else if (kr->srv != NULL) {
1024 : /* found a KDC */
1025 0 : be_fo_set_port_status(state->be_ctx, state->krb5_ctx->service->name,
1026 : kr->srv, PORT_WORKING);
1027 : }
1028 :
1029 : /* Now only a successful authentication or password change is left.
1030 : *
1031 : * We expect that one of the messages in the received buffer contains
1032 : * the name of the credential cache file. */
1033 0 : if (kr->ccname == NULL) {
1034 0 : DEBUG(SSSDBG_CRIT_FAILURE, "Missing ccache name in child response.\n");
1035 0 : ret = EINVAL;
1036 0 : goto done;
1037 : }
1038 :
1039 0 : ret = krb5_save_ccname(state, state->sysdb, state->domain,
1040 0 : pd->user, kr->ccname);
1041 0 : if (ret) {
1042 0 : DEBUG(SSSDBG_CRIT_FAILURE, "krb5_save_ccname failed.\n");
1043 0 : goto done;
1044 : }
1045 0 : renew_interval_str = dp_opt_get_string(kr->krb5_ctx->opts,
1046 : KRB5_RENEW_INTERVAL);
1047 0 : if (renew_interval_str != NULL) {
1048 0 : ret = krb5_string_to_deltat(renew_interval_str, &renew_interval_delta);
1049 0 : if (ret != EOK) {
1050 0 : DEBUG(SSSDBG_MINOR_FAILURE,
1051 : "Reading krb5_renew_interval failed.\n");
1052 0 : renew_interval_delta = 0;
1053 : }
1054 0 : renew_interval_time = renew_interval_delta;
1055 : }
1056 0 : if (res->msg_status == ERR_OK && renew_interval_time > 0 &&
1057 0 : (pd->cmd == SSS_PAM_AUTHENTICATE ||
1058 0 : pd->cmd == SSS_CMD_RENEW ||
1059 0 : pd->cmd == SSS_PAM_CHAUTHTOK) &&
1060 0 : (res->tgtt.renew_till > res->tgtt.endtime) &&
1061 0 : (kr->ccname != NULL)) {
1062 0 : DEBUG(SSSDBG_TRACE_LIBS,
1063 : "Adding [%s] for automatic renewal.\n", kr->ccname);
1064 0 : ret = add_tgt_to_renew_table(kr->krb5_ctx, kr->ccname, &(res->tgtt),
1065 0 : pd, kr->upn);
1066 0 : if (ret != EOK) {
1067 0 : DEBUG(SSSDBG_CRIT_FAILURE, "add_tgt_to_renew_table failed, "
1068 : "automatic renewal not possible.\n");
1069 : }
1070 : }
1071 :
1072 0 : if (kr->is_offline) {
1073 0 : if (dp_opt_get_bool(kr->krb5_ctx->opts,
1074 : KRB5_STORE_PASSWORD_IF_OFFLINE)) {
1075 0 : krb5_auth_cache_creds(state->kr->krb5_ctx,
1076 : state->domain,
1077 0 : state->be_ctx->cdb,
1078 0 : state->pd, state->kr->uid,
1079 : &state->pam_status, &state->dp_err);
1080 : } else {
1081 0 : DEBUG(SSSDBG_CONF_SETTINGS,
1082 : "Backend is marked offline, retry later!\n");
1083 0 : state->pam_status = PAM_AUTHINFO_UNAVAIL;
1084 0 : state->dp_err = DP_ERR_OFFLINE;
1085 : }
1086 0 : ret = EOK;
1087 0 : goto done;
1088 : }
1089 :
1090 0 : if (state->be_ctx->domain->cache_credentials == TRUE
1091 0 : && (!res->otp
1092 0 : || (res->otp && sss_authtok_get_type(pd->authtok) ==
1093 : SSS_AUTHTOK_TYPE_2FA))) {
1094 0 : krb5_auth_store_creds(state->domain, pd);
1095 : }
1096 :
1097 : /* The SSS_OTP message will prevent pam_sss from putting the entered
1098 : * password on the PAM stack for other modules to use. This is not needed
1099 : * when both factors were entered separately because here the first factor
1100 : * (long term password) can be passed to the other modules. */
1101 0 : if (res->otp == true && pd->cmd == SSS_PAM_AUTHENTICATE
1102 0 : && sss_authtok_get_type(pd->authtok) != SSS_AUTHTOK_TYPE_2FA) {
1103 0 : uint32_t otp_flag = 1;
1104 0 : ret = pam_add_response(pd, SSS_OTP, sizeof(uint32_t),
1105 : (const uint8_t *) &otp_flag);
1106 0 : if (ret != EOK) {
1107 0 : DEBUG(SSSDBG_CRIT_FAILURE,
1108 : "pam_add_response failed: %d (%s).\n",
1109 : ret, sss_strerror(ret));
1110 0 : state->pam_status = PAM_SYSTEM_ERR;
1111 0 : state->dp_err = DP_ERR_OK;
1112 0 : goto done;
1113 : }
1114 : }
1115 :
1116 0 : state->pam_status = PAM_SUCCESS;
1117 0 : state->dp_err = DP_ERR_OK;
1118 0 : ret = EOK;
1119 :
1120 : done:
1121 0 : if (ret == EOK) {
1122 0 : tevent_req_done(req);
1123 : } else {
1124 0 : tevent_req_error(req, ret);
1125 : }
1126 :
1127 : }
1128 :
1129 0 : int krb5_auth_recv(struct tevent_req *req, int *pam_status, int *dp_err)
1130 : {
1131 0 : struct krb5_auth_state *state = tevent_req_data(req, struct krb5_auth_state);
1132 0 : *pam_status = state->pam_status;
1133 0 : *dp_err = state->dp_err;
1134 :
1135 0 : TEVENT_REQ_RETURN_ON_ERROR(req);
1136 :
1137 0 : return EOK;
1138 : }
1139 :
1140 : struct krb5_pam_handler_state {
1141 : struct pam_data *pd;
1142 : };
1143 :
1144 : static void krb5_pam_handler_auth_done(struct tevent_req *subreq);
1145 : static void krb5_pam_handler_access_done(struct tevent_req *subreq);
1146 :
1147 : struct tevent_req *
1148 0 : krb5_pam_handler_send(TALLOC_CTX *mem_ctx,
1149 : struct krb5_ctx *krb5_ctx,
1150 : struct pam_data *pd,
1151 : struct dp_req_params *params)
1152 : {
1153 : struct krb5_pam_handler_state *state;
1154 : struct tevent_req *subreq;
1155 : struct tevent_req *req;
1156 :
1157 0 : req = tevent_req_create(mem_ctx, &state,
1158 : struct krb5_pam_handler_state);
1159 0 : if (req == NULL) {
1160 0 : DEBUG(SSSDBG_CRIT_FAILURE, "tevent_req_create() failed\n");
1161 0 : return NULL;
1162 : }
1163 :
1164 0 : state->pd = pd;
1165 :
1166 0 : switch (pd->cmd) {
1167 : case SSS_PAM_AUTHENTICATE:
1168 : case SSS_CMD_RENEW:
1169 : case SSS_PAM_CHAUTHTOK_PRELIM:
1170 : case SSS_PAM_CHAUTHTOK:
1171 0 : subreq = krb5_auth_queue_send(state, params->ev, params->be_ctx,
1172 : pd, krb5_ctx);
1173 0 : if (subreq == NULL) {
1174 0 : DEBUG(SSSDBG_CRIT_FAILURE, "krb5_auth_send failed.\n");
1175 0 : pd->pam_status = PAM_SYSTEM_ERR;
1176 0 : goto immediately;
1177 : }
1178 :
1179 0 : tevent_req_set_callback(subreq, krb5_pam_handler_auth_done, req);
1180 0 : break;
1181 : case SSS_PAM_ACCT_MGMT:
1182 0 : subreq = krb5_access_send(state, params->ev, params->be_ctx,
1183 : pd, krb5_ctx);
1184 0 : if (subreq == NULL) {
1185 0 : DEBUG(SSSDBG_CRIT_FAILURE, "krb5_access_send failed.\n");
1186 0 : pd->pam_status = PAM_SYSTEM_ERR;
1187 0 : goto immediately;
1188 : }
1189 :
1190 0 : tevent_req_set_callback(subreq, krb5_pam_handler_access_done, req);
1191 0 : break;
1192 : case SSS_PAM_SETCRED:
1193 : case SSS_PAM_OPEN_SESSION:
1194 : case SSS_PAM_CLOSE_SESSION:
1195 0 : pd->pam_status = PAM_SUCCESS;
1196 0 : goto immediately;
1197 : break;
1198 : default:
1199 0 : DEBUG(SSSDBG_CONF_SETTINGS,
1200 : "krb5 does not handles pam task %d.\n", pd->cmd);
1201 0 : pd->pam_status = PAM_MODULE_UNKNOWN;
1202 0 : goto immediately;
1203 : }
1204 :
1205 0 : return req;
1206 :
1207 : immediately:
1208 : /* TODO For backward compatibility we always return EOK to DP now. */
1209 0 : tevent_req_done(req);
1210 0 : tevent_req_post(req, params->ev);
1211 :
1212 0 : return req;
1213 : }
1214 :
1215 0 : static void krb5_pam_handler_auth_done(struct tevent_req *subreq)
1216 : {
1217 : struct krb5_pam_handler_state *state;
1218 : struct tevent_req *req;
1219 : errno_t ret;
1220 :
1221 0 : req = tevent_req_callback_data(subreq, struct tevent_req);
1222 0 : state = tevent_req_data(req, struct krb5_pam_handler_state);
1223 :
1224 0 : ret = krb5_auth_queue_recv(subreq, &state->pd->pam_status, NULL);
1225 0 : talloc_zfree(subreq);
1226 0 : if (ret != EOK) {
1227 0 : state->pd->pam_status = PAM_SYSTEM_ERR;
1228 : }
1229 :
1230 : /* TODO For backward compatibility we always return EOK to DP now. */
1231 0 : tevent_req_done(req);
1232 0 : }
1233 :
1234 0 : static void krb5_pam_handler_access_done(struct tevent_req *subreq)
1235 : {
1236 : struct krb5_pam_handler_state *state;
1237 : struct tevent_req *req;
1238 : bool access_allowed;
1239 : errno_t ret;
1240 :
1241 0 : req = tevent_req_callback_data(subreq, struct tevent_req);
1242 0 : state = tevent_req_data(req, struct krb5_pam_handler_state);
1243 :
1244 0 : ret = krb5_access_recv(subreq, &access_allowed);
1245 0 : talloc_zfree(subreq);
1246 0 : if (ret != EOK) {
1247 0 : state->pd->pam_status = PAM_SYSTEM_ERR;
1248 : }
1249 :
1250 :
1251 0 : DEBUG(SSSDBG_TRACE_LIBS, "Access %s for user [%s].\n",
1252 : access_allowed ? "allowed" : "denied", state->pd->user);
1253 0 : state->pd->pam_status = access_allowed ? PAM_SUCCESS : PAM_PERM_DENIED;
1254 :
1255 : /* TODO For backward compatibility we always return EOK to DP now. */
1256 0 : tevent_req_done(req);
1257 0 : }
1258 :
1259 : errno_t
1260 0 : krb5_pam_handler_recv(TALLOC_CTX *mem_ctx,
1261 : struct tevent_req *req,
1262 : struct pam_data **_data)
1263 : {
1264 0 : struct krb5_pam_handler_state *state = NULL;
1265 :
1266 0 : state = tevent_req_data(req, struct krb5_pam_handler_state);
1267 :
1268 0 : TEVENT_REQ_RETURN_ON_ERROR(req);
1269 :
1270 0 : *_data = talloc_steal(mem_ctx, state->pd);
1271 :
1272 0 : return EOK;
1273 : }
|