LCOV - code coverage report
Current view: top level - providers/krb5 - krb5_auth.c (source / functions) Hit Total Coverage
Test: .coverage.total Lines: 0 638 0.0 %
Date: 2015-10-19 Functions: 0 18 0.0 %

          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 : }

Generated by: LCOV version 1.10