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