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

          Line data    Source code
       1             : /*
       2             :     SSSD
       3             : 
       4             :     Kerberos 5 Backend Module -- tgt_req and changepw child
       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 <sys/types.h>
      26             : #include <unistd.h>
      27             : #include <sys/stat.h>
      28             : #include <popt.h>
      29             : 
      30             : #include <security/pam_modules.h>
      31             : 
      32             : #include "util/util.h"
      33             : #include "util/sss_krb5.h"
      34             : #include "util/user_info_msg.h"
      35             : #include "util/child_common.h"
      36             : #include "util/find_uid.h"
      37             : #include "src/util/util_errors.h"
      38             : #include "providers/dp_backend.h"
      39             : #include "providers/krb5/krb5_auth.h"
      40             : #include "providers/krb5/krb5_utils.h"
      41             : #include "sss_cli.h"
      42             : 
      43             : #define SSSD_KRB5_CHANGEPW_PRINCIPAL "kadmin/changepw"
      44             : 
      45             : enum k5c_fast_opt {
      46             :     K5C_FAST_NEVER,
      47             :     K5C_FAST_TRY,
      48             :     K5C_FAST_DEMAND,
      49             : };
      50             : 
      51             : struct krb5_req {
      52             :     krb5_context ctx;
      53             :     krb5_principal princ;
      54             :     char* name;
      55             :     krb5_creds *creds;
      56             :     bool otp;
      57             :     char *otp_vendor;
      58             :     char *otp_token_id;
      59             :     char *otp_challenge;
      60             :     krb5_get_init_creds_opt *options;
      61             : 
      62             :     struct pam_data *pd;
      63             : 
      64             :     char *realm;
      65             :     char *ccname;
      66             :     char *keytab;
      67             :     bool validate;
      68             :     bool send_pac;
      69             :     bool use_enterprise_princ;
      70             :     char *fast_ccname;
      71             : 
      72             :     const char *upn;
      73             :     uid_t uid;
      74             :     gid_t gid;
      75             : 
      76             :     char *old_ccname;
      77             :     bool old_cc_valid;
      78             :     bool old_cc_active;
      79             :     enum k5c_fast_opt fast_val;
      80             : 
      81             :     uid_t fast_uid;
      82             :     gid_t fast_gid;
      83             : };
      84             : 
      85             : static krb5_context krb5_error_ctx;
      86             : #define KRB5_CHILD_DEBUG(level, error) KRB5_DEBUG(level, krb5_error_ctx, error)
      87             : 
      88           0 : static krb5_error_code set_lifetime_options(krb5_get_init_creds_opt *options)
      89             : {
      90             :     char *lifetime_str;
      91             :     krb5_error_code kerr;
      92             :     krb5_deltat lifetime;
      93             : 
      94           0 :     lifetime_str = getenv(SSSD_KRB5_RENEWABLE_LIFETIME);
      95           0 :     if (lifetime_str == NULL) {
      96           0 :         DEBUG(SSSDBG_CONF_SETTINGS, "Cannot read [%s] from environment.\n",
      97             :               SSSD_KRB5_RENEWABLE_LIFETIME);
      98             : 
      99             :         /* Unset option flag to make sure defaults from krb5.conf are used. */
     100           0 :         options->flags &= ~(KRB5_GET_INIT_CREDS_OPT_RENEW_LIFE);
     101             :     } else {
     102           0 :         kerr = krb5_string_to_deltat(lifetime_str, &lifetime);
     103           0 :         if (kerr != 0) {
     104           0 :             DEBUG(SSSDBG_CRIT_FAILURE,
     105             :                   "krb5_string_to_deltat failed for [%s].\n",
     106             :                       lifetime_str);
     107           0 :             KRB5_CHILD_DEBUG(SSSDBG_CRIT_FAILURE, kerr);
     108           0 :             return kerr;
     109             :         }
     110           0 :         DEBUG(SSSDBG_CONF_SETTINGS, "%s is set to [%s]\n",
     111             :               SSSD_KRB5_RENEWABLE_LIFETIME, lifetime_str);
     112           0 :         krb5_get_init_creds_opt_set_renew_life(options, lifetime);
     113             :     }
     114             : 
     115           0 :     lifetime_str = getenv(SSSD_KRB5_LIFETIME);
     116           0 :     if (lifetime_str == NULL) {
     117           0 :         DEBUG(SSSDBG_CONF_SETTINGS, "Cannot read [%s] from environment.\n",
     118             :               SSSD_KRB5_LIFETIME);
     119             : 
     120             :         /* Unset option flag to make sure defaults from krb5.conf are used. */
     121           0 :         options->flags &= ~(KRB5_GET_INIT_CREDS_OPT_TKT_LIFE);
     122             :     } else {
     123           0 :         kerr = krb5_string_to_deltat(lifetime_str, &lifetime);
     124           0 :         if (kerr != 0) {
     125           0 :             DEBUG(SSSDBG_CRIT_FAILURE,
     126             :                   "krb5_string_to_deltat failed for [%s].\n",
     127             :                       lifetime_str);
     128           0 :             KRB5_CHILD_DEBUG(SSSDBG_CRIT_FAILURE, kerr);
     129           0 :             return kerr;
     130             :         }
     131           0 :         DEBUG(SSSDBG_CONF_SETTINGS,
     132             :               "%s is set to [%s]\n", SSSD_KRB5_LIFETIME, lifetime_str);
     133           0 :         krb5_get_init_creds_opt_set_tkt_life(options, lifetime);
     134             :     }
     135             : 
     136           0 :     return 0;
     137             : }
     138             : 
     139           0 : static void set_canonicalize_option(krb5_get_init_creds_opt *opts)
     140             : {
     141           0 :     int canonicalize = 0;
     142             :     char *tmp_str;
     143             : 
     144           0 :     tmp_str = getenv(SSSD_KRB5_CANONICALIZE);
     145           0 :     if (tmp_str != NULL && strcasecmp(tmp_str, "true") == 0) {
     146           0 :         canonicalize = 1;
     147             :     }
     148           0 :     DEBUG(SSSDBG_CONF_SETTINGS, "%s is set to [%s]\n",
     149             :           SSSD_KRB5_CANONICALIZE, tmp_str ? tmp_str : "not set");
     150           0 :     sss_krb5_get_init_creds_opt_set_canonicalize(opts, canonicalize);
     151           0 : }
     152             : 
     153           0 : static void set_changepw_options(krb5_get_init_creds_opt *options)
     154             : {
     155           0 :     sss_krb5_get_init_creds_opt_set_canonicalize(options, 0);
     156           0 :     krb5_get_init_creds_opt_set_forwardable(options, 0);
     157           0 :     krb5_get_init_creds_opt_set_proxiable(options, 0);
     158           0 :     krb5_get_init_creds_opt_set_renew_life(options, 0);
     159           0 :     krb5_get_init_creds_opt_set_tkt_life(options, 5*60);
     160           0 : }
     161             : 
     162           0 : static void revert_changepw_options(krb5_get_init_creds_opt *options)
     163             : {
     164             :     krb5_error_code kerr;
     165             : 
     166           0 :     set_canonicalize_option(options);
     167             : 
     168             :     /* Currently we do not set forwardable and proxiable explicitly, the flags
     169             :      * must be removed so that libkrb5 can take the defaults from krb5.conf */
     170           0 :     options->flags &= ~(KRB5_GET_INIT_CREDS_OPT_FORWARDABLE);
     171           0 :     options->flags &= ~(KRB5_GET_INIT_CREDS_OPT_PROXIABLE);
     172             : 
     173           0 :     kerr = set_lifetime_options(options);
     174           0 :     if (kerr != 0) {
     175           0 :         DEBUG(SSSDBG_OP_FAILURE, ("set_lifetime_options failed.\n"));
     176             :     }
     177           0 : }
     178             : 
     179             : 
     180           0 : static errno_t sss_send_pac(krb5_authdata **pac_authdata)
     181             : {
     182             :     struct sss_cli_req_data sss_data;
     183             :     int ret;
     184             :     int errnop;
     185             : 
     186           0 :     sss_data.len = pac_authdata[0]->length;
     187           0 :     sss_data.data = pac_authdata[0]->contents;
     188             : 
     189           0 :     ret = sss_pac_make_request(SSS_PAC_ADD_PAC_USER, &sss_data,
     190             :                                NULL, NULL, &errnop);
     191           0 :     if (ret != NSS_STATUS_SUCCESS || errnop != 0) {
     192           0 :         DEBUG(SSSDBG_OP_FAILURE, "sss_pac_make_request failed [%d][%d].\n",
     193             :                                   ret, errnop);
     194           0 :         return EIO;
     195             :     }
     196             : 
     197           0 :     return EOK;
     198             : }
     199             : 
     200           0 : static void sss_krb5_expire_callback_func(krb5_context context, void *data,
     201             :                                           krb5_timestamp password_expiration,
     202             :                                           krb5_timestamp account_expiration,
     203             :                                           krb5_boolean is_last_req)
     204             : {
     205             :     int ret;
     206             :     uint32_t *blob;
     207             :     long exp_time;
     208           0 :     struct krb5_req *kr = talloc_get_type(data, struct krb5_req);
     209             : 
     210           0 :     if (password_expiration == 0) {
     211           0 :         return;
     212             :     }
     213             : 
     214           0 :     exp_time = password_expiration - time(NULL);
     215           0 :     if (exp_time < 0 || exp_time > UINT32_MAX) {
     216           0 :         DEBUG(SSSDBG_CRIT_FAILURE, "Time to expire out of range.\n");
     217           0 :         return;
     218             :     }
     219           0 :     DEBUG(SSSDBG_TRACE_INTERNAL, "exp_time: [%ld]\n", exp_time);
     220             : 
     221           0 :     blob = talloc_array(kr->pd, uint32_t, 2);
     222           0 :     if (blob == NULL) {
     223           0 :         DEBUG(SSSDBG_CRIT_FAILURE, "talloc_size failed.\n");
     224           0 :         return;
     225             :     }
     226             : 
     227           0 :     blob[0] = SSS_PAM_USER_INFO_EXPIRE_WARN;
     228           0 :     blob[1] = (uint32_t) exp_time;
     229             : 
     230           0 :     ret = pam_add_response(kr->pd, SSS_PAM_USER_INFO, 2 * sizeof(uint32_t),
     231             :                            (uint8_t *) blob);
     232           0 :     if (ret != EOK) {
     233           0 :         DEBUG(SSSDBG_CRIT_FAILURE, "pam_add_response failed.\n");
     234             :     }
     235             : 
     236           0 :     return;
     237             : }
     238             : 
     239             : #ifdef HAVE_KRB5_GET_INIT_CREDS_OPT_SET_RESPONDER
     240             : /*
     241             :  * TODO: These features generally would requires a significant refactoring
     242             :  * of SSSD and MIT krb5 doesn't support them anyway. They are listed here
     243             :  * simply as a reminder of things that might become future feature potential.
     244             :  *
     245             :  *   1. tokeninfo selection
     246             :  *   2. challenge
     247             :  *   3. discreet token/pin prompting
     248             :  *   4. interactive otp format correction
     249             :  *   5. nextOTP
     250             :  *
     251             :  */
     252             : typedef int (*checker)(int c);
     253             : 
     254           0 : static inline checker pick_checker(int format)
     255             : {
     256           0 :     switch (format) {
     257             :     case KRB5_RESPONDER_OTP_FORMAT_DECIMAL:
     258           0 :         return isdigit;
     259             :     case KRB5_RESPONDER_OTP_FORMAT_HEXADECIMAL:
     260           0 :         return isxdigit;
     261             :     case KRB5_RESPONDER_OTP_FORMAT_ALPHANUMERIC:
     262           0 :         return isalnum;
     263             :     }
     264             : 
     265           0 :     return NULL;
     266             : }
     267             : 
     268           0 : static int token_pin_destructor(char *mem)
     269             : {
     270           0 :     safezero(mem, strlen(mem));
     271           0 :     return 0;
     272             : }
     273             : 
     274           0 : static krb5_error_code tokeninfo_matches_2fa(TALLOC_CTX *mem_ctx,
     275             :                                          const krb5_responder_otp_tokeninfo *ti,
     276             :                                          const char *fa1, size_t fa1_len,
     277             :                                          const char *fa2, size_t fa2_len,
     278             :                                          char **out_token, char **out_pin)
     279             : {
     280           0 :     char *token = NULL, *pin = NULL;
     281           0 :     checker check = NULL;
     282             :     int i;
     283             : 
     284           0 :     if (ti->flags & KRB5_RESPONDER_OTP_FLAGS_NEXTOTP) {
     285           0 :         return ENOTSUP;
     286             :     }
     287             : 
     288           0 :     if (ti->challenge != NULL) {
     289           0 :         return ENOTSUP;
     290             :     }
     291             : 
     292             :     /* This is a non-sensical value. */
     293           0 :     if (ti->length == 0) {
     294           0 :         return EPROTO;
     295             :     }
     296             : 
     297           0 :     if (ti->flags & KRB5_RESPONDER_OTP_FLAGS_COLLECT_TOKEN) {
     298           0 :         if (ti->length > 0 && ti->length != fa2_len) {
     299           0 :             DEBUG(SSSDBG_CRIT_FAILURE,
     300             :                   "Expected [%d] and given [%zu] token size "
     301             :                   "do not match.\n", ti->length, fa2_len);
     302           0 :             return EMSGSIZE;
     303             :         }
     304             : 
     305           0 :         if (ti->flags & KRB5_RESPONDER_OTP_FLAGS_COLLECT_PIN) {
     306           0 :             if (ti->flags & KRB5_RESPONDER_OTP_FLAGS_SEPARATE_PIN) {
     307             : 
     308           0 :                 pin = talloc_strndup(mem_ctx, fa1, fa1_len);
     309           0 :                 if (pin == NULL) {
     310           0 :                     talloc_free(token);
     311           0 :                     return ENOMEM;
     312             :                 }
     313           0 :                 talloc_set_destructor(pin, token_pin_destructor);
     314             : 
     315           0 :                 token = talloc_strndup(mem_ctx, fa2, fa2_len);
     316           0 :                 if (token == NULL) {
     317           0 :                     return ENOMEM;
     318             :                 }
     319           0 :                 talloc_set_destructor(token, token_pin_destructor);
     320             : 
     321           0 :                 check = pick_checker(ti->format);
     322             :             }
     323             :         } else {
     324           0 :             token = talloc_asprintf(mem_ctx, "%s%s", fa1, fa2);
     325           0 :             if (token == NULL) {
     326           0 :                 return ENOMEM;
     327             :             }
     328           0 :             talloc_set_destructor(token, token_pin_destructor);
     329             : 
     330           0 :             check = pick_checker(ti->format);
     331             :         }
     332             :     } else {
     333             :         /* Assuming PIN only required */
     334           0 :         pin = talloc_strndup(mem_ctx, fa1, fa1_len);
     335           0 :         if (pin == NULL) {
     336           0 :             return ENOMEM;
     337             :         }
     338           0 :         talloc_set_destructor(pin, token_pin_destructor);
     339             :     }
     340             : 
     341             :     /* If check is set, we need to verify the contents of the token. */
     342           0 :     for (i = 0; check != NULL && token[i] != '\0'; i++) {
     343           0 :         if (!check(token[i])) {
     344           0 :             talloc_free(token);
     345           0 :             talloc_free(pin);
     346           0 :             return EBADMSG;
     347             :         }
     348             :     }
     349             : 
     350           0 :     *out_token = token;
     351           0 :     *out_pin = pin;
     352           0 :     return 0;
     353             : }
     354           0 : static krb5_error_code tokeninfo_matches_pwd(TALLOC_CTX *mem_ctx,
     355             :                                          const krb5_responder_otp_tokeninfo *ti,
     356             :                                          const char *pwd, size_t len,
     357             :                                          char **out_token, char **out_pin)
     358             : {
     359           0 :     char *token = NULL, *pin = NULL;
     360           0 :     checker check = NULL;
     361             :     int i;
     362             : 
     363             : 
     364           0 :     if (ti->flags & KRB5_RESPONDER_OTP_FLAGS_NEXTOTP) {
     365           0 :         return ENOTSUP;
     366             :     }
     367             : 
     368           0 :     if (ti->challenge != NULL) {
     369           0 :         return ENOTSUP;
     370             :     }
     371             : 
     372             :     /* This is a non-sensical value. */
     373           0 :     if (ti->length == 0) {
     374           0 :         return EPROTO;
     375             :     }
     376             : 
     377           0 :     if (ti->flags & KRB5_RESPONDER_OTP_FLAGS_COLLECT_TOKEN) {
     378             :         /* ASSUMPTION: authtok has one of the following formats:
     379             :          *   1. TokenValue
     380             :          *   2. PIN+TokenValue
     381             :          */
     382           0 :         token = talloc_strndup(mem_ctx, pwd, len);
     383           0 :         if (token == NULL) {
     384           0 :             return ENOMEM;
     385             :         }
     386           0 :         talloc_set_destructor(token, token_pin_destructor);
     387             : 
     388           0 :         if (ti->flags & KRB5_RESPONDER_OTP_FLAGS_COLLECT_PIN) {
     389             :             /* If the server desires a separate pin, we will split it.
     390             :              * ASSUMPTION: Format of authtok is PIN+TokenValue. */
     391           0 :             if (ti->flags & KRB5_RESPONDER_OTP_FLAGS_SEPARATE_PIN) {
     392           0 :                 if (ti->length < 1) {
     393           0 :                     talloc_free(token);
     394           0 :                     return ENOTSUP;
     395             :                 }
     396             : 
     397           0 :                 if (ti->length >= len) {
     398           0 :                     talloc_free(token);
     399           0 :                     return EMSGSIZE;
     400             :                 }
     401             : 
     402             :                 /* Copy the PIN from the front of the value. */
     403           0 :                 pin = talloc_strndup(NULL, pwd, len - ti->length);
     404           0 :                 if (pin == NULL) {
     405           0 :                     talloc_free(token);
     406           0 :                     return ENOMEM;
     407             :                 }
     408           0 :                 talloc_set_destructor(pin, token_pin_destructor);
     409             : 
     410             :                 /* Remove the PIN from the front of the token value. */
     411           0 :                 memmove(token, token + len - ti->length, ti->length + 1);
     412             : 
     413           0 :                 check = pick_checker(ti->format);
     414             :             } else {
     415           0 :                 if (ti->length > 0 && ti->length > len) {
     416           0 :                     talloc_free(token);
     417           0 :                     return EMSGSIZE;
     418             :                 }
     419             :             }
     420             :         } else {
     421           0 :             if (ti->length > 0 && ti->length != len) {
     422           0 :                 talloc_free(token);
     423           0 :                 return EMSGSIZE;
     424             :             }
     425             : 
     426           0 :             check = pick_checker(ti->format);
     427             :         }
     428             :     } else {
     429           0 :         pin = talloc_strndup(mem_ctx, pwd, len);
     430           0 :         if (pin == NULL) {
     431           0 :             return ENOMEM;
     432             :         }
     433           0 :         talloc_set_destructor(pin, token_pin_destructor);
     434             :     }
     435             : 
     436             :     /* If check is set, we need to verify the contents of the token. */
     437           0 :     for (i = 0; check != NULL && token[i] != '\0'; i++) {
     438           0 :         if (!check(token[i])) {
     439           0 :             talloc_free(token);
     440           0 :             talloc_free(pin);
     441           0 :             return EBADMSG;
     442             :         }
     443             :     }
     444             : 
     445           0 :     *out_token = token;
     446           0 :     *out_pin = pin;
     447           0 :     return 0;
     448             : }
     449             : 
     450           0 : static krb5_error_code tokeninfo_matches(TALLOC_CTX *mem_ctx,
     451             :                                          const krb5_responder_otp_tokeninfo *ti,
     452             :                                          struct sss_auth_token *auth_tok,
     453             :                                          char **out_token, char **out_pin)
     454             : {
     455             :     int ret;
     456             :     const char *pwd;
     457             :     size_t len;
     458             :     const char *fa2;
     459             :     size_t fa2_len;
     460             : 
     461           0 :     switch (sss_authtok_get_type(auth_tok)) {
     462             :     case SSS_AUTHTOK_TYPE_PASSWORD:
     463           0 :         ret = sss_authtok_get_password(auth_tok, &pwd, &len);
     464           0 :         if (ret != EOK) {
     465           0 :             DEBUG(SSSDBG_OP_FAILURE, "sss_authtok_get_password failed.\n");
     466           0 :             return ret;
     467             :         }
     468             : 
     469           0 :         return tokeninfo_matches_pwd(mem_ctx, ti, pwd, len, out_token, out_pin);
     470             :         break;
     471             :     case SSS_AUTHTOK_TYPE_2FA:
     472           0 :         ret = sss_authtok_get_2fa(auth_tok, &pwd, &len, &fa2, &fa2_len);
     473           0 :         if (ret != EOK) {
     474           0 :             DEBUG(SSSDBG_OP_FAILURE, "sss_authtok_get_2fa failed.\n");
     475           0 :             return ret;
     476             :         }
     477             : 
     478           0 :         return tokeninfo_matches_2fa(mem_ctx, ti, pwd, len, fa2, fa2_len,
     479             :                                      out_token, out_pin);
     480             :         break;
     481             :     default:
     482           0 :         DEBUG(SSSDBG_CRIT_FAILURE, "Unsupported authtok type.\n");
     483             :     }
     484             : 
     485           0 :     return EINVAL;
     486             : }
     487             : 
     488           0 : static krb5_error_code answer_otp(krb5_context ctx,
     489             :                                   struct krb5_req *kr,
     490             :                                   krb5_responder_context rctx)
     491             : {
     492             :     krb5_responder_otp_challenge *chl;
     493           0 :     char *token = NULL, *pin = NULL;
     494             :     krb5_error_code ret;
     495             :     size_t i;
     496             : 
     497           0 :     ret = krb5_responder_otp_get_challenge(ctx, rctx, &chl);
     498           0 :     if (ret != EOK || chl == NULL) {
     499             :         /* Either an error, or nothing to do. */
     500           0 :         return ret;
     501             :     }
     502             : 
     503           0 :     if (chl->tokeninfo == NULL || chl->tokeninfo[0] == NULL) {
     504             :         /* No tokeninfos? Absurd! */
     505           0 :         ret = EINVAL;
     506           0 :         goto done;
     507             :     }
     508             : 
     509           0 :     kr->otp = true;
     510             : 
     511           0 :     if (kr->pd->cmd == SSS_PAM_PREAUTH) {
     512           0 :         for (i = 0; chl->tokeninfo[i] != NULL; i++) {
     513           0 :             DEBUG(SSSDBG_TRACE_ALL, "[%zu] Vendor [%s].\n",
     514             :                                     i, chl->tokeninfo[i]->vendor);
     515           0 :             DEBUG(SSSDBG_TRACE_ALL, "[%zu] Token-ID [%s].\n",
     516             :                                     i, chl->tokeninfo[i]->token_id);
     517           0 :             DEBUG(SSSDBG_TRACE_ALL, "[%zu] Challenge [%s].\n",
     518             :                                     i, chl->tokeninfo[i]->challenge);
     519           0 :             DEBUG(SSSDBG_TRACE_ALL, "[%zu] Flags [%d].\n",
     520             :                                     i, chl->tokeninfo[i]->flags);
     521             :         }
     522             : 
     523           0 :         if (chl->tokeninfo[0]->vendor != NULL) {
     524           0 :             kr->otp_vendor = talloc_strdup(kr, chl->tokeninfo[0]->vendor);
     525             :         }
     526           0 :         if (chl->tokeninfo[0]->token_id != NULL) {
     527           0 :             kr->otp_token_id = talloc_strdup(kr, chl->tokeninfo[0]->token_id);
     528             :         }
     529           0 :         if (chl->tokeninfo[0]->challenge != NULL) {
     530           0 :             kr->otp_challenge = talloc_strdup(kr, chl->tokeninfo[0]->challenge);
     531             :         }
     532             :         /* Allocation errors are ignored on purpose */
     533             : 
     534           0 :         DEBUG(SSSDBG_TRACE_INTERNAL, "Exit answer_otp during pre-auth.\n");
     535           0 :         return EAGAIN;
     536             :     }
     537             : 
     538             :     /* Find the first supported tokeninfo which matches our authtoken. */
     539           0 :     for (i = 0; chl->tokeninfo[i] != NULL; i++) {
     540           0 :         ret = tokeninfo_matches(kr, chl->tokeninfo[i], kr->pd->authtok,
     541             :                                 &token, &pin);
     542           0 :         if (ret == EOK) {
     543           0 :             break;
     544             :         }
     545             : 
     546           0 :         switch (ret) {
     547             :         case EBADMSG:
     548             :         case EMSGSIZE:
     549             :         case ENOTSUP:
     550             :         case EPROTO:
     551           0 :             break;
     552             :         default:
     553           0 :             goto done;
     554             :         }
     555             :     }
     556           0 :     if (chl->tokeninfo[i] == NULL) {
     557           0 :         DEBUG(SSSDBG_CRIT_FAILURE,
     558             :               "No tokeninfos found which match our credentials.\n");
     559           0 :         ret = EOK;
     560           0 :         goto done;
     561             :     }
     562             : 
     563           0 :     if (chl->tokeninfo[i]->flags & KRB5_RESPONDER_OTP_FLAGS_COLLECT_TOKEN) {
     564             :         /* Don't let SSSD cache the OTP authtok since it is single-use. */
     565           0 :         ret = pam_add_response(kr->pd, SSS_OTP, 0, NULL);
     566           0 :         if (ret != EOK) {
     567           0 :             DEBUG(SSSDBG_CRIT_FAILURE, "pam_add_response failed.\n");
     568           0 :             goto done;
     569             :         }
     570             :     }
     571             : 
     572             :     /* Respond with the appropriate answer. */
     573           0 :     ret = krb5_responder_otp_set_answer(ctx, rctx, i, token, pin);
     574             : done:
     575           0 :     talloc_free(token);
     576           0 :     talloc_free(pin);
     577           0 :     krb5_responder_otp_challenge_free(ctx, rctx, chl);
     578           0 :     return ret;
     579             : }
     580             : 
     581           0 : static krb5_error_code sss_krb5_responder(krb5_context ctx,
     582             :                                           void *data,
     583             :                                           krb5_responder_context rctx)
     584             : {
     585           0 :     struct krb5_req *kr = talloc_get_type(data, struct krb5_req);
     586             : 
     587           0 :     if (kr == NULL) {
     588           0 :         return EINVAL;
     589             :     }
     590             : 
     591           0 :     return answer_otp(ctx, kr, rctx);
     592             : }
     593             : #endif
     594             : 
     595           0 : static krb5_error_code sss_krb5_prompter(krb5_context context, void *data,
     596             :                                          const char *name, const char *banner,
     597             :                                          int num_prompts, krb5_prompt prompts[])
     598             : {
     599             :     int ret;
     600           0 :     struct krb5_req *kr = talloc_get_type(data, struct krb5_req);
     601             : 
     602           0 :     if (num_prompts != 0) {
     603           0 :         DEBUG(SSSDBG_CRIT_FAILURE, "Cannot handle password prompts.\n");
     604           0 :         return KRB5_LIBOS_CANTREADPWD;
     605             :     }
     606             : 
     607           0 :     if (banner == NULL || *banner == '\0') {
     608           0 :         DEBUG(SSSDBG_FUNC_DATA,
     609             :               "Prompter called with empty banner, nothing to do.\n");
     610           0 :         return EOK;
     611             :     }
     612             : 
     613           0 :     DEBUG(SSSDBG_FUNC_DATA, "Prompter called with [%s].\n", banner);
     614             : 
     615           0 :     ret = pam_add_response(kr->pd, SSS_PAM_TEXT_MSG, strlen(banner)+1,
     616             :                            (const uint8_t *) banner);
     617           0 :     if (ret != EOK) {
     618           0 :         DEBUG(SSSDBG_CRIT_FAILURE, "pam_add_response failed.\n");
     619             :     }
     620             : 
     621           0 :     return EOK;
     622             : }
     623             : 
     624             : 
     625           0 : static krb5_error_code create_empty_cred(krb5_context ctx, krb5_principal princ,
     626             :                                          krb5_creds **_cred)
     627             : {
     628             :     krb5_error_code kerr;
     629           0 :     krb5_creds *cred = NULL;
     630             :     krb5_data *krb5_realm;
     631             : 
     632           0 :     cred = calloc(sizeof(krb5_creds), 1);
     633           0 :     if (cred == NULL) {
     634           0 :         DEBUG(SSSDBG_CRIT_FAILURE, "calloc failed.\n");
     635           0 :         return ENOMEM;
     636             :     }
     637             : 
     638           0 :     kerr = krb5_copy_principal(ctx, princ, &cred->client);
     639           0 :     if (kerr != 0) {
     640           0 :         DEBUG(SSSDBG_CRIT_FAILURE, "krb5_copy_principal failed.\n");
     641           0 :         goto done;
     642             :     }
     643             : 
     644           0 :     krb5_realm = krb5_princ_realm(ctx, princ);
     645             : 
     646           0 :     kerr = krb5_build_principal_ext(ctx, &cred->server,
     647           0 :                                     krb5_realm->length, krb5_realm->data,
     648             :                                     KRB5_TGS_NAME_SIZE, KRB5_TGS_NAME,
     649             :                                     krb5_realm->length, krb5_realm->data, 0);
     650           0 :     if (kerr != 0) {
     651           0 :         DEBUG(SSSDBG_CRIT_FAILURE, "krb5_build_principal_ext failed.\n");
     652           0 :         goto done;
     653             :     }
     654             : 
     655           0 :     DEBUG(SSSDBG_TRACE_INTERNAL, "Created empty krb5_creds.\n");
     656             : 
     657             : done:
     658           0 :     if (kerr != 0) {
     659           0 :         krb5_free_cred_contents(ctx, cred);
     660           0 :         free(cred);
     661             :     } else {
     662           0 :         *_cred = cred;
     663             :     }
     664             : 
     665           0 :     return kerr;
     666             : }
     667             : 
     668             : 
     669           0 : static errno_t handle_randomized(char *in)
     670             : {
     671             :     size_t ccname_len;
     672           0 :     char *ccname = NULL;
     673             :     int ret;
     674             : 
     675             :     /* We only treat the FILE type case in a special way due to the history
     676             :      * of storing FILE type ccache in /tmp and associated security issues */
     677           0 :     if (in[0] == '/') {
     678           0 :         ccname = in;
     679           0 :     } else if (strncmp(in, "FILE:", 5) == 0) {
     680           0 :         ccname = in + 5;
     681             :     } else {
     682           0 :         return EOK;
     683             :     }
     684             : 
     685           0 :     ccname_len = strlen(ccname);
     686           0 :     if (ccname_len >= 6 && strcmp(ccname + (ccname_len - 6), "XXXXXX") == 0) {
     687             :         /* NOTE: this call is only used to create a unique name, as later
     688             :         * krb5_cc_initialize() will unlink and recreate the file.
     689             :         * This is ok because this part of the code is called with
     690             :         * privileges already dropped when handling user ccache, or the ccache
     691             :         * is stored in a private directory. So we do not have huge issues if
     692             :         * something races, we mostly care only about not accidentally use
     693             :         * an existing name and thus failing in the process of saving the
     694             :         * cache. Malicious races can only be avoided by libkrb5 itself. */
     695           0 :         ret = sss_unique_filename(NULL, ccname);
     696           0 :         if (ret != EOK) {
     697           0 :             DEBUG(SSSDBG_CRIT_FAILURE,
     698             :                     "mkstemp(\"%s\") failed [%d]: %s!\n",
     699             :                     ccname, ret, strerror(ret));
     700           0 :             return ret;
     701             :         }
     702             :     }
     703             : 
     704           0 :     return EOK;
     705             : }
     706             : 
     707             : /* NOTE: callers rely on 'name' being *changed* if it needs to be randomized,
     708             :  * as they will then send the name back to the new name via the return call
     709             :  * k5c_attach_ccname_msg(). Callers will send in a copy of the name if they
     710             :  * do not care for changes. */
     711           0 : static krb5_error_code create_ccache(char *ccname, krb5_creds *creds)
     712             : {
     713           0 :     krb5_context kctx = NULL;
     714           0 :     krb5_ccache kcc = NULL;
     715             :     const char *type;
     716             :     krb5_error_code kerr;
     717             : #ifdef HAVE_KRB5_CC_COLLECTION
     718             :     krb5_ccache cckcc;
     719           0 :     bool switch_to_cc = false;
     720             : #endif
     721             : 
     722             :     /* Set a restrictive umask, just in case we end up creating any file */
     723           0 :     umask(SSS_DFL_X_UMASK);
     724             : 
     725             :     /* we create a new context here as the main process one may have been
     726             :      * opened as root and contain possibly references (even open handles ?)
     727             :      * to resources we do not have or do not want to have access to */
     728           0 :     kerr = krb5_init_context(&kctx);
     729           0 :     if (kerr) {
     730           0 :         KRB5_CHILD_DEBUG(SSSDBG_CRIT_FAILURE, kerr);
     731           0 :         return ERR_INTERNAL;
     732             :     }
     733             : 
     734           0 :     kerr = handle_randomized(ccname);
     735           0 :     if (kerr) {
     736           0 :         DEBUG(SSSDBG_CRIT_FAILURE, "handle_randomized failed: %d\n", kerr);
     737           0 :         goto done;
     738             :     }
     739             : 
     740           0 :     kerr = krb5_cc_resolve(kctx, ccname, &kcc);
     741           0 :     if (kerr) {
     742           0 :         KRB5_CHILD_DEBUG(SSSDBG_CRIT_FAILURE, kerr);
     743           0 :         goto done;
     744             :     }
     745             : 
     746           0 :     type = krb5_cc_get_type(kctx, kcc);
     747           0 :     DEBUG(SSSDBG_TRACE_ALL, "Initializing ccache of type [%s]\n", type);
     748             : 
     749             : #ifdef HAVE_KRB5_CC_COLLECTION
     750           0 :     if (krb5_cc_support_switch(kctx, type)) {
     751           0 :         DEBUG(SSSDBG_TRACE_ALL, "CC supports switch\n");
     752           0 :         kerr = krb5_cc_set_default_name(kctx, ccname);
     753           0 :         if (kerr) {
     754           0 :             DEBUG(SSSDBG_TRACE_ALL, "Cannot set default name!\n");
     755           0 :             KRB5_CHILD_DEBUG(SSSDBG_CRIT_FAILURE, kerr);
     756           0 :             goto done;
     757             :         }
     758             : 
     759           0 :         kerr = krb5_cc_cache_match(kctx, creds->client, &cckcc);
     760           0 :         if (kerr == KRB5_CC_NOTFOUND) {
     761           0 :             DEBUG(SSSDBG_TRACE_ALL, "Match not found\n");
     762           0 :             kerr = krb5_cc_new_unique(kctx, type, NULL, &cckcc);
     763           0 :             switch_to_cc = true;
     764             :         }
     765           0 :         if (kerr) {
     766           0 :             DEBUG(SSSDBG_TRACE_ALL, "krb5_cc_cache_match failed\n");
     767           0 :             KRB5_CHILD_DEBUG(SSSDBG_CRIT_FAILURE, kerr);
     768           0 :             goto done;
     769             :         }
     770           0 :         krb5_cc_close(kctx, kcc);
     771           0 :         kcc = cckcc;
     772             :     }
     773             : #endif
     774             : 
     775           0 :     kerr = krb5_cc_initialize(kctx, kcc, creds->client);
     776           0 :     if (kerr) {
     777           0 :         DEBUG(SSSDBG_TRACE_ALL, "krb5_cc_initialize failed\n");
     778           0 :         KRB5_CHILD_DEBUG(SSSDBG_CRIT_FAILURE, kerr);
     779           0 :         goto done;
     780             :     }
     781             : 
     782           0 :     kerr = krb5_cc_store_cred(kctx, kcc, creds);
     783           0 :     if (kerr) {
     784           0 :         DEBUG(SSSDBG_TRACE_ALL, "krb5_cc_store_cred failed\n");
     785           0 :         KRB5_CHILD_DEBUG(SSSDBG_CRIT_FAILURE, kerr);
     786           0 :         goto done;
     787             :     }
     788             : 
     789             : #ifdef HAVE_KRB5_CC_COLLECTION
     790           0 :     if (switch_to_cc) {
     791           0 :         DEBUG(SSSDBG_TRACE_ALL, "switch_to_cc\n");
     792           0 :         kerr = krb5_cc_switch(kctx, kcc);
     793           0 :         if (kerr) {
     794           0 :             DEBUG(SSSDBG_TRACE_ALL, "krb5_cc_switch\n");
     795           0 :             KRB5_CHILD_DEBUG(SSSDBG_CRIT_FAILURE, kerr);
     796           0 :             goto done;
     797             :         }
     798             :     }
     799             : #endif
     800             : 
     801           0 :     DEBUG(SSSDBG_TRACE_ALL, "returning: %d\n", kerr);
     802             : done:
     803           0 :     if (kcc) {
     804             :         /* FIXME: should we krb5_cc_destroy in case of error ? */
     805           0 :         krb5_cc_close(kctx, kcc);
     806             :     }
     807           0 :     return kerr;
     808             : }
     809             : 
     810           0 : static errno_t pack_response_packet(TALLOC_CTX *mem_ctx, errno_t error,
     811             :                                     struct response_data *resp_list,
     812             :                                     uint8_t **_buf, size_t *_len)
     813             : {
     814             :     uint8_t *buf;
     815           0 :     size_t size = 0;
     816           0 :     size_t p = 0;
     817             :     struct response_data *pdr;
     818             : 
     819             :     /* A buffer with the following structure must be created:
     820             :      * int32_t status of the request (required)
     821             :      * message (zero or more)
     822             :      *
     823             :      * A message consists of:
     824             :      * int32_t type of the message
     825             :      * int32_t length of the following data
     826             :      * uint8_t[len] data
     827             :      */
     828             : 
     829           0 :     size = sizeof(int32_t);
     830             : 
     831           0 :     for (pdr = resp_list; pdr != NULL; pdr = pdr->next) {
     832           0 :         size += 2*sizeof(int32_t) + pdr->len;
     833             :     }
     834             : 
     835           0 :     buf = talloc_array(mem_ctx, uint8_t, size);
     836           0 :     if (!buf) {
     837           0 :         DEBUG(SSSDBG_CRIT_FAILURE, "Insufficient memory to create message.\n");
     838           0 :         return ENOMEM;
     839             :     }
     840             : 
     841           0 :     SAFEALIGN_SET_INT32(&buf[p], error, &p);
     842             : 
     843           0 :     for (pdr = resp_list; pdr != NULL; pdr = pdr->next) {
     844           0 :         SAFEALIGN_SET_INT32(&buf[p], pdr->type, &p);
     845           0 :         SAFEALIGN_SET_INT32(&buf[p], pdr->len, &p);
     846           0 :         safealign_memcpy(&buf[p], pdr->data, pdr->len, &p);
     847             :     }
     848             : 
     849           0 :     DEBUG(SSSDBG_TRACE_INTERNAL, "response packet size: [%zu]\n", p);
     850             : 
     851           0 :     *_buf = buf;
     852           0 :     *_len = p;
     853           0 :     return EOK;
     854             : }
     855             : 
     856           0 : static errno_t k5c_attach_otp_info_msg(struct krb5_req *kr)
     857             : {
     858           0 :     uint8_t *msg = NULL;
     859             :     size_t msg_len;
     860             :     int ret;
     861           0 :     size_t vendor_len = 0;
     862           0 :     size_t token_id_len = 0;
     863           0 :     size_t challenge_len = 0;
     864           0 :     size_t idx = 0;
     865             : 
     866           0 :     msg_len = 3;
     867           0 :     if (kr->otp_vendor != NULL) {
     868           0 :         vendor_len = strlen(kr->otp_vendor);
     869           0 :         msg_len += vendor_len;
     870             :     }
     871             : 
     872           0 :     if (kr->otp_token_id != NULL) {
     873           0 :         token_id_len = strlen(kr->otp_token_id);
     874           0 :         msg_len += token_id_len;
     875             :     }
     876             : 
     877           0 :     if (kr->otp_challenge != NULL) {
     878           0 :         challenge_len = strlen(kr->otp_challenge);
     879           0 :         msg_len += challenge_len;
     880             :     }
     881             : 
     882           0 :     msg = talloc_zero_size(kr, msg_len);
     883           0 :     if (msg == NULL) {
     884           0 :         DEBUG(SSSDBG_OP_FAILURE, "talloc_size failed.\n");
     885           0 :         return ENOMEM;
     886             :     }
     887             : 
     888           0 :     if (kr->otp_vendor != NULL) {
     889           0 :         memcpy(msg, kr->otp_vendor, vendor_len);
     890             :     }
     891           0 :     idx += vendor_len +1;
     892             : 
     893           0 :     if (kr->otp_token_id != NULL) {
     894           0 :         memcpy(msg + idx, kr->otp_token_id, token_id_len);
     895             :     }
     896           0 :     idx += token_id_len +1;
     897             : 
     898           0 :     if (kr->otp_challenge != NULL) {
     899           0 :         memcpy(msg + idx, kr->otp_challenge, challenge_len);
     900             :     }
     901             : 
     902           0 :     ret = pam_add_response(kr->pd, SSS_PAM_OTP_INFO, msg_len, msg);
     903           0 :     talloc_zfree(msg);
     904             : 
     905           0 :     return ret;
     906             : }
     907             : 
     908           0 : static errno_t k5c_attach_ccname_msg(struct krb5_req *kr)
     909             : {
     910           0 :     char *msg = NULL;
     911             :     int ret;
     912             : 
     913           0 :     if (kr->ccname == NULL) {
     914           0 :         DEBUG(SSSDBG_CRIT_FAILURE, "Error obtaining ccname.\n");
     915           0 :         return ERR_INTERNAL;
     916             :     }
     917             : 
     918           0 :     msg = talloc_asprintf(kr, "%s=%s",CCACHE_ENV_NAME, kr->ccname);
     919           0 :     if (msg == NULL) {
     920           0 :         DEBUG(SSSDBG_CRIT_FAILURE, "talloc_asprintf failed.\n");
     921           0 :         return ENOMEM;
     922             :     }
     923             : 
     924           0 :     ret = pam_add_response(kr->pd, SSS_PAM_ENV_ITEM,
     925           0 :                            strlen(msg) + 1, (uint8_t *)msg);
     926           0 :     talloc_zfree(msg);
     927             : 
     928           0 :     return ret;
     929             : }
     930             : 
     931           0 : static errno_t k5c_send_data(struct krb5_req *kr, int fd, errno_t error)
     932             : {
     933             :     ssize_t written;
     934             :     uint8_t *buf;
     935             :     size_t len;
     936             :     int ret;
     937             : 
     938           0 :     DEBUG(SSSDBG_FUNC_DATA, "Received error code %d\n", error);
     939             : 
     940           0 :     ret = pack_response_packet(kr, error, kr->pd->resp_list, &buf, &len);
     941           0 :     if (ret != EOK) {
     942           0 :         DEBUG(SSSDBG_CRIT_FAILURE, "pack_response_packet failed.\n");
     943           0 :         return ret;
     944             :     }
     945             : 
     946           0 :     errno = 0;
     947           0 :     written = sss_atomic_write_s(fd, buf, len);
     948           0 :     if (written == -1) {
     949           0 :         ret = errno;
     950           0 :         DEBUG(SSSDBG_CRIT_FAILURE,
     951             :               "write failed [%d][%s].\n", ret, strerror(ret));
     952           0 :         return ret;
     953             :     }
     954             : 
     955           0 :     if (written != len) {
     956           0 :         DEBUG(SSSDBG_CRIT_FAILURE,
     957             :               "Write error, wrote [%zu] bytes, expected [%zu]\n",
     958             :                written, len);
     959           0 :         return EOK;
     960             :     }
     961             : 
     962           0 :     DEBUG(SSSDBG_TRACE_ALL, "Response sent.\n");
     963             : 
     964           0 :     return EOK;
     965             : }
     966             : 
     967           0 : static errno_t add_ticket_times_and_upn_to_response(struct krb5_req *kr)
     968             : {
     969             :     int ret;
     970             :     int64_t t[4];
     971             :     krb5_error_code kerr;
     972           0 :     char *upn = NULL;
     973           0 :     unsigned int upn_len = 0;
     974             : 
     975           0 :     t[0] = (int64_t) kr->creds->times.authtime;
     976           0 :     t[1] = (int64_t) kr->creds->times.starttime;
     977           0 :     t[2] = (int64_t) kr->creds->times.endtime;
     978           0 :     t[3] = (int64_t) kr->creds->times.renew_till;
     979             : 
     980           0 :     ret = pam_add_response(kr->pd, SSS_KRB5_INFO_TGT_LIFETIME,
     981             :                            4*sizeof(int64_t), (uint8_t *) t);
     982           0 :     if (ret != EOK) {
     983           0 :         DEBUG(SSSDBG_CRIT_FAILURE, "pack_response_packet failed.\n");
     984           0 :         goto done;
     985             :     }
     986             : 
     987           0 :     kerr = krb5_unparse_name_ext(kr->ctx, kr->creds->client, &upn, &upn_len);
     988           0 :     if (kerr != 0) {
     989           0 :         DEBUG(SSSDBG_OP_FAILURE, "krb5_unparse_name failed.\n");
     990           0 :         goto done;
     991             :     }
     992             : 
     993           0 :     ret = pam_add_response(kr->pd, SSS_KRB5_INFO_UPN, upn_len,
     994             :                            (uint8_t *) upn);
     995           0 :     krb5_free_unparsed_name(kr->ctx, upn);
     996           0 :     if (ret != EOK) {
     997           0 :         DEBUG(SSSDBG_CRIT_FAILURE, "pack_response_packet failed.\n");
     998           0 :         goto done;
     999             :     }
    1000             : 
    1001             : done:
    1002           0 :     return ret;
    1003             : }
    1004             : 
    1005           0 : static krb5_error_code validate_tgt(struct krb5_req *kr)
    1006             : {
    1007             :     krb5_error_code kerr;
    1008             :     krb5_error_code kt_err;
    1009           0 :     char *principal = NULL;
    1010             :     krb5_keytab keytab;
    1011             :     krb5_kt_cursor cursor;
    1012             :     krb5_keytab_entry entry;
    1013             :     krb5_verify_init_creds_opt opt;
    1014           0 :     krb5_principal validation_princ = NULL;
    1015           0 :     bool realm_entry_found = false;
    1016           0 :     krb5_ccache validation_ccache = NULL;
    1017           0 :     krb5_authdata **pac_authdata = NULL;
    1018             : 
    1019           0 :     memset(&keytab, 0, sizeof(keytab));
    1020           0 :     kerr = krb5_kt_resolve(kr->ctx, kr->keytab, &keytab);
    1021           0 :     if (kerr != 0) {
    1022           0 :         DEBUG(SSSDBG_CRIT_FAILURE, "error resolving keytab [%s], " \
    1023             :                                     "not verifying TGT.\n", kr->keytab);
    1024           0 :         return kerr;
    1025             :     }
    1026             : 
    1027           0 :     memset(&cursor, 0, sizeof(cursor));
    1028           0 :     kerr = krb5_kt_start_seq_get(kr->ctx, keytab, &cursor);
    1029           0 :     if (kerr != 0) {
    1030           0 :         DEBUG(SSSDBG_CRIT_FAILURE, "error reading keytab [%s], " \
    1031             :                                     "not verifying TGT.\n", kr->keytab);
    1032           0 :         return kerr;
    1033             :     }
    1034             : 
    1035             :     /* We look for the first entry from our realm or take the last one */
    1036           0 :     memset(&entry, 0, sizeof(entry));
    1037           0 :     while ((kt_err = krb5_kt_next_entry(kr->ctx, keytab, &entry, &cursor)) == 0) {
    1038           0 :         if (validation_princ != NULL) {
    1039           0 :             krb5_free_principal(kr->ctx, validation_princ);
    1040           0 :             validation_princ = NULL;
    1041             :         }
    1042           0 :         kerr = krb5_copy_principal(kr->ctx, entry.principal,
    1043             :                                    &validation_princ);
    1044           0 :         if (kerr != 0) {
    1045           0 :             DEBUG(SSSDBG_CRIT_FAILURE, "krb5_copy_principal failed.\n");
    1046           0 :             goto done;
    1047             :         }
    1048             : 
    1049           0 :         kerr = sss_krb5_free_keytab_entry_contents(kr->ctx, &entry);
    1050           0 :         if (kerr != 0) {
    1051           0 :             DEBUG(SSSDBG_MINOR_FAILURE, "Failed to free keytab entry.\n");
    1052             :         }
    1053           0 :         memset(&entry, 0, sizeof(entry));
    1054             : 
    1055           0 :         if (krb5_realm_compare(kr->ctx, validation_princ, kr->creds->client)) {
    1056           0 :             DEBUG(SSSDBG_TRACE_INTERNAL,
    1057             :                   "Found keytab entry with the realm of the credential.\n");
    1058           0 :             realm_entry_found = true;
    1059           0 :             break;
    1060             :         }
    1061             :     }
    1062             : 
    1063           0 :     if (!realm_entry_found) {
    1064           0 :         DEBUG(SSSDBG_TRACE_INTERNAL,
    1065             :                 "Keytab entry with the realm of the credential not found "
    1066             :                  "in keytab. Using the last entry.\n");
    1067             :     }
    1068             : 
    1069             :     /* Close the keytab here.  Even though we're using cursors, the file
    1070             :      * handle is stored in the krb5_keytab structure, and it gets
    1071             :      * overwritten when the verify_init_creds() call below creates its own
    1072             :      * cursor, creating a leak. */
    1073           0 :     kerr = krb5_kt_end_seq_get(kr->ctx, keytab, &cursor);
    1074           0 :     if (kerr != 0) {
    1075           0 :         DEBUG(SSSDBG_CRIT_FAILURE, "krb5_kt_end_seq_get failed, " \
    1076             :                                     "not verifying TGT.\n");
    1077           0 :         goto done;
    1078             :     }
    1079             : 
    1080             :     /* check if we got any errors from krb5_kt_next_entry */
    1081           0 :     if (kt_err != 0 && kt_err != KRB5_KT_END) {
    1082           0 :         DEBUG(SSSDBG_CRIT_FAILURE, "error reading keytab [%s], " \
    1083             :                                     "not verifying TGT.\n", kr->keytab);
    1084           0 :         goto done;
    1085             :     }
    1086             : 
    1087             :     /* Get the principal to which the key belongs, for logging purposes. */
    1088           0 :     principal = NULL;
    1089           0 :     kerr = krb5_unparse_name(kr->ctx, validation_princ, &principal);
    1090           0 :     if (kerr != 0) {
    1091           0 :         DEBUG(SSSDBG_CRIT_FAILURE, "internal error parsing principal name, "
    1092             :                                     "not verifying TGT.\n");
    1093           0 :         KRB5_CHILD_DEBUG(SSSDBG_CRIT_FAILURE, kerr);
    1094           0 :         goto done;
    1095             :     }
    1096             : 
    1097             : 
    1098           0 :     krb5_verify_init_creds_opt_init(&opt);
    1099           0 :     kerr = krb5_verify_init_creds(kr->ctx, kr->creds, validation_princ, keytab,
    1100             :                                   &validation_ccache, &opt);
    1101             : 
    1102           0 :     if (kerr == 0) {
    1103           0 :         DEBUG(SSSDBG_TRACE_FUNC, "TGT verified using key for [%s].\n",
    1104             :                                   principal);
    1105             :     } else {
    1106           0 :         DEBUG(SSSDBG_CRIT_FAILURE ,"TGT failed verification using key " \
    1107             :                                     "for [%s].\n", principal);
    1108           0 :         goto done;
    1109             :     }
    1110             : 
    1111             :     /* Try to find and send the PAC to the PAC responder.
    1112             :      * Failures are not critical. */
    1113           0 :     if (kr->send_pac) {
    1114           0 :         kerr = sss_extract_pac(kr->ctx, validation_ccache, validation_princ,
    1115           0 :                                kr->creds->client, keytab, &pac_authdata);
    1116           0 :         if (kerr != 0) {
    1117           0 :             DEBUG(SSSDBG_OP_FAILURE, "sss_extract_and_send_pac failed, group " \
    1118             :                                       "membership for user with principal [%s] " \
    1119             :                                       "might not be correct.\n", kr->name);
    1120           0 :             kerr = 0;
    1121           0 :             goto done;
    1122             :         }
    1123             : 
    1124           0 :         kerr = sss_send_pac(pac_authdata);
    1125           0 :         krb5_free_authdata(kr->ctx, pac_authdata);
    1126           0 :         if (kerr != 0) {
    1127           0 :             DEBUG(SSSDBG_OP_FAILURE, "sss_send_pac failed, group " \
    1128             :                                       "membership for user with principal [%s] " \
    1129             :                                       "might not be correct.\n", kr->name);
    1130           0 :             kerr = 0;
    1131             :         }
    1132             :     }
    1133             : 
    1134             : done:
    1135           0 :     if (validation_ccache != NULL) {
    1136           0 :         krb5_cc_destroy(kr->ctx, validation_ccache);
    1137             :     }
    1138             : 
    1139           0 :     if (krb5_kt_close(kr->ctx, keytab) != 0) {
    1140           0 :         DEBUG(SSSDBG_MINOR_FAILURE, "krb5_kt_close failed\n");
    1141             :     }
    1142           0 :     if (validation_princ != NULL) {
    1143           0 :         krb5_free_principal(kr->ctx, validation_princ);
    1144             :     }
    1145           0 :     if (principal != NULL) {
    1146           0 :         sss_krb5_free_unparsed_name(kr->ctx, principal);
    1147             :     }
    1148             : 
    1149           0 :     return kerr;
    1150             : 
    1151             : }
    1152             : 
    1153           0 : static krb5_error_code get_and_save_tgt_with_keytab(krb5_context ctx,
    1154             :                                                     krb5_principal princ,
    1155             :                                                     krb5_keytab keytab,
    1156             :                                                     char *ccname)
    1157             : {
    1158           0 :     krb5_error_code kerr = 0;
    1159             :     krb5_creds creds;
    1160             :     krb5_get_init_creds_opt options;
    1161             : 
    1162           0 :     memset(&creds, 0, sizeof(creds));
    1163           0 :     memset(&options, 0, sizeof(options));
    1164             : 
    1165           0 :     krb5_get_init_creds_opt_set_address_list(&options, NULL);
    1166           0 :     krb5_get_init_creds_opt_set_forwardable(&options, 0);
    1167           0 :     krb5_get_init_creds_opt_set_proxiable(&options, 0);
    1168           0 :     set_canonicalize_option(&options);
    1169             : 
    1170           0 :     kerr = krb5_get_init_creds_keytab(ctx, &creds, princ, keytab, 0, NULL,
    1171             :                                       &options);
    1172           0 :     if (kerr != 0) {
    1173           0 :         KRB5_CHILD_DEBUG(SSSDBG_CRIT_FAILURE, kerr);
    1174           0 :         return kerr;
    1175             :     }
    1176             : 
    1177             :     /* Use the updated principal in the creds in case canonicalized */
    1178           0 :     kerr = create_ccache(ccname, &creds);
    1179           0 :     if (kerr != 0) {
    1180           0 :         goto done;
    1181             :     }
    1182           0 :     kerr = 0;
    1183             : 
    1184             : done:
    1185           0 :     krb5_free_cred_contents(ctx, &creds);
    1186             : 
    1187           0 :     return kerr;
    1188             : 
    1189             : }
    1190             : 
    1191           0 : static krb5_error_code get_and_save_tgt(struct krb5_req *kr,
    1192             :                                         const char *password)
    1193             : {
    1194             :     const char *realm_name;
    1195             :     int realm_length;
    1196             :     krb5_error_code kerr;
    1197             :     char *cc_name;
    1198             : 
    1199           0 :     kerr = sss_krb5_get_init_creds_opt_set_expire_callback(kr->ctx, kr->options,
    1200             :                                                   sss_krb5_expire_callback_func,
    1201             :                                                   kr);
    1202           0 :     if (kerr != 0) {
    1203           0 :         KRB5_CHILD_DEBUG(SSSDBG_CRIT_FAILURE, kerr);
    1204           0 :         DEBUG(SSSDBG_CRIT_FAILURE,
    1205             :               "Failed to set expire callback, continue without.\n");
    1206             :     }
    1207             : 
    1208           0 :     sss_krb5_princ_realm(kr->ctx, kr->princ, &realm_name, &realm_length);
    1209           0 :     if (realm_length == 0) {
    1210           0 :         DEBUG(SSSDBG_CRIT_FAILURE, "sss_krb5_princ_realm failed.\n");
    1211           0 :         return KRB5KRB_ERR_GENERIC;
    1212             :     }
    1213             : 
    1214           0 :     DEBUG(SSSDBG_TRACE_FUNC,
    1215             :           "Attempting kinit for realm [%s]\n",realm_name);
    1216           0 :     kerr = krb5_get_init_creds_password(kr->ctx, kr->creds, kr->princ,
    1217             :                                         discard_const(password),
    1218             :                                         sss_krb5_prompter, kr, 0,
    1219             :                                         NULL, kr->options);
    1220           0 :     if (kr->pd->cmd == SSS_PAM_PREAUTH) {
    1221             :         /* Any errors are ignored during pre-auth, only data is collected to
    1222             :          * be send back to the client.*/
    1223           0 :         DEBUG(SSSDBG_TRACE_FUNC,
    1224             :               "krb5_get_init_creds_password returned [%d} during pre-auth.\n",
    1225             :               kerr);
    1226           0 :         return 0;
    1227             :     } else {
    1228           0 :         if (kerr != 0) {
    1229           0 :             KRB5_CHILD_DEBUG(SSSDBG_CRIT_FAILURE, kerr);
    1230           0 :             return kerr;
    1231             :         }
    1232             :     }
    1233             : 
    1234           0 :     if (kr->validate) {
    1235           0 :         kerr = validate_tgt(kr);
    1236           0 :         if (kerr != 0) {
    1237           0 :             KRB5_CHILD_DEBUG(SSSDBG_CRIT_FAILURE, kerr);
    1238           0 :             return kerr;
    1239             :         }
    1240             : 
    1241             :     } else {
    1242           0 :         DEBUG(SSSDBG_CONF_SETTINGS, "TGT validation is disabled.\n");
    1243             :     }
    1244             : 
    1245             :     /* If kr->ccname is cache collection (DIR:/...), we want to work
    1246             :      * directly with file ccache (DIR::/...), but cache collection
    1247             :      * should be returned back to back end.
    1248             :      */
    1249           0 :     cc_name = sss_get_ccache_name_for_principal(kr->pd, kr->ctx,
    1250           0 :                                                 kr->creds->client,
    1251           0 :                                                 kr->ccname);
    1252           0 :     if (cc_name == NULL) {
    1253           0 :         cc_name = kr->ccname;
    1254             :     }
    1255             : 
    1256             :     /* Use the updated principal in the creds in case canonicalized */
    1257           0 :     kerr = create_ccache(cc_name, kr->creds);
    1258           0 :     if (kerr != 0) {
    1259           0 :         goto done;
    1260             :     }
    1261             : 
    1262             :     /* Successfull authentication! Check if ccache contains the
    1263             :      * right principal...
    1264             :      */
    1265           0 :     kerr = sss_krb5_check_ccache_princ(kr->ctx, kr->ccname, kr->creds->client);
    1266           0 :     if (kerr) {
    1267           0 :         DEBUG(SSSDBG_CRIT_FAILURE,
    1268             :               "No ccache for %s in %s?\n", kr->upn, kr->ccname);
    1269           0 :         goto done;
    1270             :     }
    1271             : 
    1272           0 :     kerr = safe_remove_old_ccache_file(kr->old_ccname, kr->ccname,
    1273             :                                        kr->uid, kr->gid);
    1274           0 :     if (kerr != EOK) {
    1275           0 :         DEBUG(SSSDBG_MINOR_FAILURE,
    1276             :               "Failed to remove old ccache file [%s], "
    1277             :               "please remove it manually.\n", kr->old_ccname);
    1278             :     }
    1279             : 
    1280           0 :     kerr = add_ticket_times_and_upn_to_response(kr);
    1281           0 :     if (kerr != 0) {
    1282           0 :         DEBUG(SSSDBG_CRIT_FAILURE,
    1283             :               "add_ticket_times_and_upn_to_response failed.\n");
    1284             :     }
    1285             : 
    1286           0 :     kerr = 0;
    1287             : 
    1288             : done:
    1289           0 :     krb5_free_cred_contents(kr->ctx, kr->creds);
    1290             : 
    1291           0 :     return kerr;
    1292             : 
    1293             : }
    1294             : 
    1295           0 : static errno_t map_krb5_error(krb5_error_code kerr)
    1296             : {
    1297           0 :     if (kerr != 0) {
    1298           0 :         KRB5_CHILD_DEBUG(SSSDBG_CRIT_FAILURE, kerr);
    1299             :     }
    1300             : 
    1301           0 :     switch (kerr) {
    1302             :     case 0:
    1303           0 :         return ERR_OK;
    1304             : 
    1305             :     case KRB5_LIBOS_CANTREADPWD:
    1306           0 :         return ERR_NO_CREDS;
    1307             : 
    1308             :     case KRB5_KDCREP_SKEW:
    1309             :     case KRB5KRB_AP_ERR_SKEW:
    1310             :     case KRB5_KDC_UNREACH:
    1311             :     case KRB5_REALM_CANT_RESOLVE:
    1312           0 :         return ERR_NETWORK_IO;
    1313             : 
    1314             :     case KRB5KDC_ERR_CLIENT_REVOKED:
    1315           0 :         return ERR_ACCOUNT_EXPIRED;
    1316             : 
    1317             :     case KRB5KDC_ERR_KEY_EXP:
    1318           0 :         return ERR_CREDS_EXPIRED;
    1319             : 
    1320             :     case KRB5KRB_AP_ERR_BAD_INTEGRITY:
    1321           0 :         return ERR_AUTH_FAILED;
    1322             : 
    1323             :     /* ERR_CREDS_INVALID is used to indicate to the IPA provider that trying
    1324             :      * password migration would make sense. All Kerberos error codes which can
    1325             :      * be seen while migrating LDAP users to IPA should be added here. */
    1326             :     case KRB5_PROG_ETYPE_NOSUPP:
    1327             :     case KRB5_PREAUTH_FAILED:
    1328             :     case KRB5KDC_ERR_PREAUTH_FAILED:
    1329           0 :         return ERR_CREDS_INVALID;
    1330             : 
    1331             :     /* Please do not remove KRB5KRB_ERR_GENERIC here, it is a _generic_ error
    1332             :      * code and we cannot make any assumptions about the reason for the error.
    1333             :      * As a consequence we cannot return a different error code than a generic
    1334             :      * one which unfortunately might result in a unspecific system error
    1335             :      * message to the user.
    1336             :      *
    1337             :      * If there are cases where libkrb5 calls return KRB5KRB_ERR_GENERIC where
    1338             :      * SSSD should behave differently this has to be detected by different
    1339             :      * means, e.g. by evaluation error messages, and then the error code
    1340             :      * should be changed to a more suitable KRB5* error code or immediately to
    1341             :      * a SSSD ERR_* error code to avoid the default handling here. */
    1342             :     case KRB5KRB_ERR_GENERIC:
    1343             :     default:
    1344           0 :         return ERR_INTERNAL;
    1345             :     }
    1346             : }
    1347             : 
    1348           0 : static errno_t changepw_child(struct krb5_req *kr, bool prelim)
    1349             : {
    1350             :     int ret;
    1351           0 :     krb5_error_code kerr = 0;
    1352           0 :     const char *password = NULL;
    1353           0 :     const char *newpassword = NULL;
    1354           0 :     int result_code = -1;
    1355             :     krb5_data result_code_string;
    1356             :     krb5_data result_string;
    1357           0 :     char *user_error_message = NULL;
    1358             :     size_t user_resp_len;
    1359             :     uint8_t *user_resp;
    1360           0 :     krb5_prompter_fct prompter = NULL;
    1361             :     const char *realm_name;
    1362             :     int realm_length;
    1363             :     size_t msg_len;
    1364             :     uint8_t *msg;
    1365             :     uint32_t user_info_type;
    1366             : 
    1367           0 :     DEBUG(SSSDBG_TRACE_LIBS, "Password change operation\n");
    1368             : 
    1369           0 :     ret = sss_authtok_get_password(kr->pd->authtok, &password, NULL);
    1370           0 :     if (ret != EOK) {
    1371           0 :         DEBUG(SSSDBG_CRIT_FAILURE,
    1372             :               "Failed to fetch current password [%d] %s.\n",
    1373             :                   ret, strerror(ret));
    1374           0 :         return ERR_NO_CREDS;
    1375             :     }
    1376             : 
    1377           0 :     if (!prelim) {
    1378             :         /* We do not need a password expiration warning here. */
    1379           0 :         prompter = sss_krb5_prompter;
    1380             :     }
    1381             : 
    1382           0 :     set_changepw_options(kr->options);
    1383           0 :     sss_krb5_princ_realm(kr->ctx, kr->princ, &realm_name, &realm_length);
    1384           0 :     if (realm_length == 0) {
    1385           0 :         DEBUG(SSSDBG_CRIT_FAILURE, "sss_krb5_princ_realm failed.\n");
    1386           0 :         return ERR_INTERNAL;
    1387             :     }
    1388             : 
    1389           0 :     DEBUG(SSSDBG_TRACE_FUNC,
    1390             :           "Attempting kinit for realm [%s]\n",realm_name);
    1391           0 :     kerr = krb5_get_init_creds_password(kr->ctx, kr->creds, kr->princ,
    1392             :                                         discard_const(password),
    1393             :                                         prompter, kr, 0,
    1394             :                                         SSSD_KRB5_CHANGEPW_PRINCIPAL,
    1395             :                                         kr->options);
    1396           0 :     DEBUG(SSSDBG_TRACE_INTERNAL,
    1397             :           "chpass is%s using OTP\n", kr->otp ? "" : " not");
    1398           0 :     if (kerr != 0) {
    1399           0 :         ret = pack_user_info_chpass_error(kr->pd, "Old password not accepted.",
    1400             :                                           &msg_len, &msg);
    1401           0 :         if (ret != EOK) {
    1402           0 :             DEBUG(SSSDBG_CRIT_FAILURE,
    1403             :                   "pack_user_info_chpass_error failed.\n");
    1404             :         } else {
    1405           0 :             ret = pam_add_response(kr->pd, SSS_PAM_USER_INFO, msg_len,
    1406             :                                    msg);
    1407           0 :             if (ret != EOK) {
    1408           0 :                 DEBUG(SSSDBG_CRIT_FAILURE,
    1409             :                       "pam_add_response failed.\n");
    1410             :             }
    1411             :         }
    1412           0 :         return kerr;
    1413             :     }
    1414             : 
    1415           0 :     sss_authtok_set_empty(kr->pd->authtok);
    1416             : 
    1417           0 :     if (prelim) {
    1418           0 :         DEBUG(SSSDBG_TRACE_LIBS,
    1419             :               "Initial authentication for change password operation "
    1420             :                "successful.\n");
    1421           0 :         krb5_free_cred_contents(kr->ctx, kr->creds);
    1422           0 :         return EOK;
    1423             :     }
    1424             : 
    1425           0 :     ret = sss_authtok_get_password(kr->pd->newauthtok, &newpassword, NULL);
    1426           0 :     if (ret != EOK) {
    1427           0 :         DEBUG(SSSDBG_CRIT_FAILURE, "Failed to fetch new password [%d] %s.\n",
    1428             :                   ret, strerror(ret));
    1429           0 :         return ERR_NO_CREDS;
    1430             :     }
    1431             : 
    1432           0 :     memset(&result_code_string, 0, sizeof(krb5_data));
    1433           0 :     memset(&result_string, 0, sizeof(krb5_data));
    1434           0 :     kerr = krb5_change_password(kr->ctx, kr->creds,
    1435             :                                 discard_const(newpassword), &result_code,
    1436             :                                 &result_code_string, &result_string);
    1437             : 
    1438           0 :     if (kerr == KRB5_KDC_UNREACH) {
    1439           0 :         return ERR_NETWORK_IO;
    1440             :     }
    1441             : 
    1442           0 :     if (kerr != 0 || result_code != 0) {
    1443           0 :         if (kerr != 0) {
    1444           0 :             KRB5_CHILD_DEBUG(SSSDBG_CRIT_FAILURE, kerr);
    1445             :         }
    1446             : 
    1447           0 :         if (result_code_string.length > 0) {
    1448           0 :             DEBUG(SSSDBG_CRIT_FAILURE,
    1449             :                   "krb5_change_password failed [%d][%.*s].\n", result_code,
    1450             :                       result_code_string.length, result_code_string.data);
    1451           0 :             user_error_message = talloc_strndup(kr->pd, result_code_string.data,
    1452           0 :                                                 result_code_string.length);
    1453           0 :             if (user_error_message == NULL) {
    1454           0 :                 DEBUG(SSSDBG_CRIT_FAILURE, "talloc_strndup failed.\n");
    1455             :             }
    1456             :         }
    1457             : 
    1458           0 :         if (result_string.length > 0 && result_string.data[0] != '\0') {
    1459           0 :             DEBUG(SSSDBG_CRIT_FAILURE,
    1460             :                   "krb5_change_password failed [%d][%.*s].\n", result_code,
    1461             :                       result_string.length, result_string.data);
    1462           0 :             talloc_free(user_error_message);
    1463           0 :             user_error_message = talloc_strndup(kr->pd, result_string.data,
    1464           0 :                                                 result_string.length);
    1465           0 :             if (user_error_message == NULL) {
    1466           0 :                 DEBUG(SSSDBG_CRIT_FAILURE, "talloc_strndup failed.\n");
    1467             :             }
    1468           0 :         } else if (result_code == KRB5_KPASSWD_SOFTERROR) {
    1469           0 :             user_error_message = talloc_strdup(kr->pd, "Please make sure the "
    1470             :                                  "password meets the complexity constraints.");
    1471           0 :             if (user_error_message == NULL) {
    1472           0 :                 DEBUG(SSSDBG_CRIT_FAILURE, "talloc_strndup failed.\n");
    1473             :             }
    1474             :         }
    1475             : 
    1476           0 :         if (user_error_message != NULL) {
    1477           0 :             ret = pack_user_info_chpass_error(kr->pd, user_error_message,
    1478             :                                               &user_resp_len, &user_resp);
    1479           0 :             if (ret != EOK) {
    1480           0 :                 DEBUG(SSSDBG_CRIT_FAILURE,
    1481             :                       "pack_user_info_chpass_error failed.\n");
    1482             :             } else {
    1483           0 :                 ret = pam_add_response(kr->pd, SSS_PAM_USER_INFO, user_resp_len,
    1484             :                                        user_resp);
    1485           0 :                 if (ret != EOK) {
    1486           0 :                     DEBUG(SSSDBG_CRIT_FAILURE,
    1487             :                           "pack_response_packet failed.\n");
    1488             :                 }
    1489             :             }
    1490             :         }
    1491             : 
    1492           0 :         return ERR_CHPASS_FAILED;
    1493             :     }
    1494             : 
    1495           0 :     krb5_free_cred_contents(kr->ctx, kr->creds);
    1496             : 
    1497           0 :     if (kr->otp == true) {
    1498           0 :         user_info_type = SSS_PAM_USER_INFO_OTP_CHPASS;
    1499           0 :         ret = pam_add_response(kr->pd, SSS_PAM_USER_INFO, sizeof(uint32_t),
    1500             :                                (const uint8_t *) &user_info_type);
    1501           0 :         if (ret != EOK) {
    1502           0 :             DEBUG(SSSDBG_CRIT_FAILURE, "pam_add_response failed.\n");
    1503             :             /* Not fatal */
    1504             :         }
    1505             : 
    1506           0 :         sss_authtok_set_empty(kr->pd->newauthtok);
    1507           0 :         return map_krb5_error(kerr);
    1508             :     }
    1509             : 
    1510             :     /* We changed some of the gic options for the password change, now we have
    1511             :      * to change them back to get a fresh TGT. */
    1512           0 :     revert_changepw_options(kr->options);
    1513             : 
    1514           0 :     kerr = get_and_save_tgt(kr, newpassword);
    1515             : 
    1516           0 :     sss_authtok_set_empty(kr->pd->newauthtok);
    1517             : 
    1518           0 :     if (kerr == 0) {
    1519           0 :         kerr = k5c_attach_ccname_msg(kr);
    1520             :     }
    1521           0 :     return map_krb5_error(kerr);
    1522             : }
    1523             : 
    1524           0 : static errno_t tgt_req_child(struct krb5_req *kr)
    1525             : {
    1526           0 :     const char *password = NULL;
    1527             :     krb5_error_code kerr;
    1528             :     int ret;
    1529             : 
    1530           0 :     DEBUG(SSSDBG_TRACE_LIBS, "Attempting to get a TGT\n");
    1531             : 
    1532             :     /* No password is needed for pre-auth, or if we have 2FA */
    1533           0 :     if (kr->pd->cmd != SSS_PAM_PREAUTH
    1534           0 :             && sss_authtok_get_type(kr->pd->authtok) != SSS_AUTHTOK_TYPE_2FA) {
    1535           0 :         ret = sss_authtok_get_password(kr->pd->authtok, &password, NULL);
    1536           0 :         switch (ret) {
    1537             :         case EOK:
    1538           0 :             break;
    1539             : 
    1540             :         case EACCES:
    1541           0 :             DEBUG(SSSDBG_OP_FAILURE, "Invalid authtok type\n");
    1542           0 :             return ERR_INVALID_CRED_TYPE;
    1543             :             break;
    1544             : 
    1545             :         default:
    1546           0 :             DEBUG(SSSDBG_OP_FAILURE, "No credentials available\n");
    1547           0 :             return ERR_NO_CREDS;
    1548             :             break;
    1549             :         }
    1550             :     }
    1551             : 
    1552           0 :     kerr = get_and_save_tgt(kr, password);
    1553             : 
    1554           0 :     if (kerr != KRB5KDC_ERR_KEY_EXP) {
    1555           0 :         if (kr->pd->cmd == SSS_PAM_PREAUTH) {
    1556             :             /* add OTP tokeninfo messge if available */
    1557           0 :             if (kr->otp) {
    1558           0 :                 kerr = k5c_attach_otp_info_msg(kr);
    1559             :             }
    1560             :         } else {
    1561           0 :             if (kerr == 0) {
    1562           0 :                 kerr = k5c_attach_ccname_msg(kr);
    1563             :             }
    1564             :         }
    1565           0 :         ret = map_krb5_error(kerr);
    1566           0 :         goto done;
    1567             :     }
    1568             : 
    1569             :     /* If the password is expired the KDC will always return
    1570             :        KRB5KDC_ERR_KEY_EXP regardless if the supplied password is correct or
    1571             :        not. In general the password can still be used to get a changepw ticket.
    1572             :        So we validate the password by trying to get a changepw ticket. */
    1573           0 :     DEBUG(SSSDBG_TRACE_LIBS, "Password was expired\n");
    1574           0 :     kerr = sss_krb5_get_init_creds_opt_set_expire_callback(kr->ctx,
    1575             :                                                            kr->options,
    1576             :                                                            NULL, NULL);
    1577           0 :     if (kerr != 0) {
    1578           0 :         KRB5_CHILD_DEBUG(SSSDBG_CRIT_FAILURE, kerr);
    1579           0 :         DEBUG(SSSDBG_CRIT_FAILURE,
    1580             :               "Failed to unset expire callback, continue ...\n");
    1581             :     }
    1582             : 
    1583           0 :     set_changepw_options(kr->options);
    1584           0 :     kerr = krb5_get_init_creds_password(kr->ctx, kr->creds, kr->princ,
    1585             :                                         discard_const(password),
    1586             :                                         sss_krb5_prompter, kr, 0,
    1587             :                                         SSSD_KRB5_CHANGEPW_PRINCIPAL,
    1588             :                                         kr->options);
    1589             : 
    1590           0 :     krb5_free_cred_contents(kr->ctx, kr->creds);
    1591           0 :     if (kerr == 0) {
    1592           0 :         ret = ERR_CREDS_EXPIRED;
    1593             : 
    1594             :         /* If the password is expired we can safely remove the ccache from the
    1595             :          * cache and disk if it is not actively used anymore. This will allow
    1596             :          * to create a new random ccache if sshd with privilege separation is
    1597             :          * used. */
    1598           0 :         if (kr->old_cc_active == false && kr->old_ccname) {
    1599           0 :             ret = safe_remove_old_ccache_file(kr->old_ccname, NULL,
    1600             :                     kr->uid, kr->gid);
    1601           0 :             if (ret != EOK) {
    1602           0 :                 DEBUG(SSSDBG_CRIT_FAILURE,
    1603             :                         "Failed to remove old ccache file [%s], "
    1604             :                         "please remove it manually.\n", kr->old_ccname);
    1605             :             }
    1606           0 :             ret = ERR_CREDS_EXPIRED_CCACHE;
    1607             :         }
    1608             :     } else {
    1609           0 :         ret = map_krb5_error(kerr);
    1610             :     }
    1611             : 
    1612             : done:
    1613           0 :     sss_authtok_set_empty(kr->pd->authtok);
    1614           0 :     return ret;
    1615             : }
    1616             : 
    1617           0 : static errno_t kuserok_child(struct krb5_req *kr)
    1618             : {
    1619             :     krb5_boolean access_allowed;
    1620             :     krb5_error_code kerr;
    1621             : 
    1622           0 :     DEBUG(SSSDBG_TRACE_LIBS, "Verifying if principal can log in as user\n");
    1623             : 
    1624             :     /* krb5_kuserok tries to verify that kr->pd->user is a locally known
    1625             :      * account, so we have to unset _SSS_LOOPS to make getpwnam() work. */
    1626           0 :     if (unsetenv("_SSS_LOOPS") != 0) {
    1627           0 :         DEBUG(SSSDBG_CRIT_FAILURE, "Failed to unset _SSS_LOOPS, "
    1628             :                   "krb5_kuserok will most certainly fail.\n");
    1629             :     }
    1630             : 
    1631           0 :     kerr = krb5_set_default_realm(kr->ctx, kr->realm);
    1632           0 :     if (kerr != 0) {
    1633           0 :         DEBUG(SSSDBG_CRIT_FAILURE, "krb5_set_default_realm failed, "
    1634             :                   "krb5_kuserok may fail.\n");
    1635             :     }
    1636             : 
    1637           0 :     access_allowed = krb5_kuserok(kr->ctx, kr->princ, kr->pd->user);
    1638           0 :     DEBUG(SSSDBG_TRACE_LIBS,
    1639             :           "Access was %s\n", access_allowed ? "allowed" : "denied");
    1640             : 
    1641           0 :     if (access_allowed) {
    1642           0 :         return EOK;
    1643             :     }
    1644             : 
    1645           0 :     return ERR_AUTH_DENIED;
    1646             : }
    1647             : 
    1648           0 : static errno_t renew_tgt_child(struct krb5_req *kr)
    1649             : {
    1650             :     const char *ccname;
    1651           0 :     krb5_ccache ccache = NULL;
    1652             :     krb5_error_code kerr;
    1653             :     int ret;
    1654             : 
    1655           0 :     DEBUG(SSSDBG_TRACE_LIBS, "Renewing a ticket\n");
    1656             : 
    1657           0 :     ret = sss_authtok_get_ccfile(kr->pd->authtok, &ccname, NULL);
    1658           0 :     if (ret != EOK) {
    1659           0 :         DEBUG(SSSDBG_OP_FAILURE,
    1660             :               "Unsupported authtok type for TGT renewal [%d].\n",
    1661             :                sss_authtok_get_type(kr->pd->authtok));
    1662           0 :         return ERR_INVALID_CRED_TYPE;
    1663             :     }
    1664             : 
    1665           0 :     kerr = krb5_cc_resolve(kr->ctx, ccname, &ccache);
    1666           0 :     if (kerr != 0) {
    1667           0 :         KRB5_CHILD_DEBUG(SSSDBG_CRIT_FAILURE, kerr);
    1668           0 :         goto done;
    1669             :     }
    1670             : 
    1671           0 :     kerr = krb5_get_renewed_creds(kr->ctx, kr->creds, kr->princ, ccache, NULL);
    1672           0 :     if (kerr != 0) {
    1673           0 :         goto done;
    1674             :     }
    1675             : 
    1676           0 :     if (kr->validate) {
    1677           0 :         kerr = validate_tgt(kr);
    1678           0 :         if (kerr != 0) {
    1679           0 :             KRB5_CHILD_DEBUG(SSSDBG_CRIT_FAILURE, kerr);
    1680           0 :             goto done;
    1681             :         }
    1682             : 
    1683             :     } else {
    1684           0 :         DEBUG(SSSDBG_CONF_SETTINGS, "TGT validation is disabled.\n");
    1685             :     }
    1686             : 
    1687           0 :     kerr = krb5_cc_initialize(kr->ctx, ccache, kr->princ);
    1688           0 :     if (kerr != 0) {
    1689           0 :         KRB5_CHILD_DEBUG(SSSDBG_CRIT_FAILURE, kerr);
    1690           0 :         goto done;
    1691             :     }
    1692             : 
    1693           0 :     kerr = krb5_cc_store_cred(kr->ctx, ccache, kr->creds);
    1694           0 :     if (kerr != 0) {
    1695           0 :         KRB5_CHILD_DEBUG(SSSDBG_CRIT_FAILURE, kerr);
    1696           0 :         goto done;
    1697             :     }
    1698             : 
    1699           0 :     kerr = add_ticket_times_and_upn_to_response(kr);
    1700           0 :     if (kerr != 0) {
    1701           0 :         DEBUG(SSSDBG_CRIT_FAILURE,
    1702             :               "add_ticket_times_and_upn_to_response failed.\n");
    1703             :     }
    1704             : 
    1705           0 :     kerr = k5c_attach_ccname_msg(kr);
    1706             : 
    1707             : done:
    1708           0 :     krb5_free_cred_contents(kr->ctx, kr->creds);
    1709             : 
    1710           0 :     if (ccache != NULL) {
    1711           0 :         krb5_cc_close(kr->ctx, ccache);
    1712             :     }
    1713             : 
    1714           0 :     return map_krb5_error(kerr);
    1715             : }
    1716             : 
    1717           0 : static errno_t create_empty_ccache(struct krb5_req *kr)
    1718             : {
    1719           0 :     krb5_creds *creds = NULL;
    1720             :     krb5_error_code kerr;
    1721             : 
    1722           0 :     if (kr->old_cc_valid == false) {
    1723           0 :         DEBUG(SSSDBG_TRACE_LIBS, "Creating empty ccache\n");
    1724           0 :         kerr = create_empty_cred(kr->ctx, kr->princ, &creds);
    1725           0 :         if (kerr == 0) {
    1726           0 :             kerr = create_ccache(kr->ccname, creds);
    1727             :         }
    1728             :     } else {
    1729           0 :         DEBUG(SSSDBG_TRACE_LIBS, "Existing ccache still valid, reusing\n");
    1730           0 :         kerr = 0;
    1731             :     }
    1732             : 
    1733           0 :     if (kerr == 0) {
    1734           0 :         kerr = k5c_attach_ccname_msg(kr);
    1735             :     }
    1736             : 
    1737           0 :     krb5_free_creds(kr->ctx, creds);
    1738             : 
    1739           0 :     return map_krb5_error(kerr);
    1740             : }
    1741             : 
    1742           0 : static errno_t unpack_authtok(struct sss_auth_token *tok,
    1743             :                               uint8_t *buf, size_t size, size_t *p)
    1744             : {
    1745             :     uint32_t auth_token_type;
    1746             :     uint32_t auth_token_length;
    1747           0 :     errno_t ret = EOK;
    1748             : 
    1749           0 :     SAFEALIGN_COPY_UINT32_CHECK(&auth_token_type, buf + *p, size, p);
    1750           0 :     SAFEALIGN_COPY_UINT32_CHECK(&auth_token_length, buf + *p, size, p);
    1751           0 :     if ((*p + auth_token_length) > size) {
    1752           0 :         return EINVAL;
    1753             :     }
    1754           0 :     switch (auth_token_type) {
    1755             :     case SSS_AUTHTOK_TYPE_EMPTY:
    1756           0 :         sss_authtok_set_empty(tok);
    1757           0 :         break;
    1758             :     case SSS_AUTHTOK_TYPE_PASSWORD:
    1759           0 :         ret = sss_authtok_set_password(tok, (char *)(buf + *p), 0);
    1760           0 :         break;
    1761             :     case SSS_AUTHTOK_TYPE_CCFILE:
    1762           0 :         ret = sss_authtok_set_ccfile(tok, (char *)(buf + *p), 0);
    1763           0 :         break;
    1764             :     case SSS_AUTHTOK_TYPE_2FA:
    1765           0 :         ret = sss_authtok_set(tok, SSS_AUTHTOK_TYPE_2FA, (buf + *p),
    1766             :                               auth_token_length);
    1767           0 :         break;
    1768             :     default:
    1769           0 :         return EINVAL;
    1770             :     }
    1771             : 
    1772           0 :     if (ret == EOK) {
    1773           0 :         *p += auth_token_length;
    1774             :     }
    1775           0 :     return ret;
    1776             : }
    1777             : 
    1778           0 : static errno_t unpack_buffer(uint8_t *buf, size_t size,
    1779             :                              struct krb5_req *kr, uint32_t *offline)
    1780             : {
    1781           0 :     size_t p = 0;
    1782             :     uint32_t len;
    1783             :     uint32_t validate;
    1784             :     uint32_t send_pac;
    1785             :     uint32_t use_enterprise_princ;
    1786             :     struct pam_data *pd;
    1787             :     errno_t ret;
    1788             : 
    1789           0 :     DEBUG(SSSDBG_TRACE_LIBS, "total buffer size: [%zu]\n", size);
    1790             : 
    1791           0 :     if (!offline || !kr) return EINVAL;
    1792             : 
    1793           0 :     pd = create_pam_data(kr);
    1794           0 :     if (pd == NULL) {
    1795           0 :         DEBUG(SSSDBG_CRIT_FAILURE, "talloc_zero failed.\n");
    1796           0 :         return ENOMEM;
    1797             :     }
    1798           0 :     kr->pd = pd;
    1799             : 
    1800           0 :     SAFEALIGN_COPY_UINT32_CHECK(&pd->cmd, buf + p, size, &p);
    1801           0 :     SAFEALIGN_COPY_UINT32_CHECK(&kr->uid, buf + p, size, &p);
    1802           0 :     SAFEALIGN_COPY_UINT32_CHECK(&kr->gid, buf + p, size, &p);
    1803           0 :     SAFEALIGN_COPY_UINT32_CHECK(&validate, buf + p, size, &p);
    1804           0 :     kr->validate = (validate == 0) ? false : true;
    1805           0 :     SAFEALIGN_COPY_UINT32_CHECK(offline, buf + p, size, &p);
    1806           0 :     SAFEALIGN_COPY_UINT32_CHECK(&send_pac, buf + p, size, &p);
    1807           0 :     kr->send_pac = (send_pac == 0) ? false : true;
    1808           0 :     SAFEALIGN_COPY_UINT32_CHECK(&use_enterprise_princ, buf + p, size, &p);
    1809           0 :     kr->use_enterprise_princ = (use_enterprise_princ == 0) ? false : true;
    1810           0 :     SAFEALIGN_COPY_UINT32_CHECK(&len, buf + p, size, &p);
    1811           0 :     if (len > size - p) return EINVAL;
    1812           0 :     kr->upn = talloc_strndup(pd, (char *)(buf + p), len);
    1813           0 :     if (kr->upn == NULL) return ENOMEM;
    1814           0 :     p += len;
    1815             : 
    1816           0 :     DEBUG(SSSDBG_CONF_SETTINGS,
    1817             :           "cmd [%d] uid [%llu] gid [%llu] validate [%s] "
    1818             :            "enterprise principal [%s] offline [%s] UPN [%s]\n",
    1819             :            pd->cmd, (unsigned long long) kr->uid,
    1820             :            (unsigned long long) kr->gid, kr->validate ? "true" : "false",
    1821             :            kr->use_enterprise_princ ? "true" : "false",
    1822             :            *offline ? "true" : "false", kr->upn ? kr->upn : "none");
    1823             : 
    1824           0 :     if (pd->cmd == SSS_PAM_AUTHENTICATE ||
    1825           0 :         pd->cmd == SSS_CMD_RENEW ||
    1826           0 :         pd->cmd == SSS_PAM_CHAUTHTOK_PRELIM || pd->cmd == SSS_PAM_CHAUTHTOK) {
    1827           0 :         SAFEALIGN_COPY_UINT32_CHECK(&len, buf + p, size, &p);
    1828           0 :         if (len > size - p) return EINVAL;
    1829           0 :         kr->ccname = talloc_strndup(pd, (char *)(buf + p), len);
    1830           0 :         if (kr->ccname == NULL) return ENOMEM;
    1831           0 :         p += len;
    1832             : 
    1833           0 :         SAFEALIGN_COPY_UINT32_CHECK(&len, buf + p, size, &p);
    1834           0 :         if (len > size - p) return EINVAL;
    1835             : 
    1836           0 :         if (len > 0) {
    1837           0 :             kr->old_ccname = talloc_strndup(pd, (char *)(buf + p), len);
    1838           0 :             if (kr->old_ccname == NULL) return ENOMEM;
    1839           0 :             p += len;
    1840             :         } else {
    1841           0 :             DEBUG(SSSDBG_TRACE_INTERNAL, "No old ccache\n");
    1842             :         }
    1843             : 
    1844           0 :         SAFEALIGN_COPY_UINT32_CHECK(&len, buf + p, size, &p);
    1845           0 :         if (len > size - p) return EINVAL;
    1846           0 :         kr->keytab = talloc_strndup(pd, (char *)(buf + p), len);
    1847           0 :         if (kr->keytab == NULL) return ENOMEM;
    1848           0 :         p += len;
    1849             : 
    1850           0 :         ret = unpack_authtok(pd->authtok, buf, size, &p);
    1851           0 :         if (ret) {
    1852           0 :             return ret;
    1853             :         }
    1854             : 
    1855           0 :         DEBUG(SSSDBG_CONF_SETTINGS,
    1856             :               "ccname: [%s] old_ccname: [%s] keytab: [%s]\n",
    1857             :               kr->ccname,
    1858             :               kr->old_ccname ? kr->old_ccname : "not set",
    1859             :               kr->keytab);
    1860             :     } else {
    1861           0 :         kr->ccname = NULL;
    1862           0 :         kr->old_ccname = NULL;
    1863           0 :         kr->keytab = NULL;
    1864           0 :         sss_authtok_set_empty(pd->authtok);
    1865             :     }
    1866             : 
    1867           0 :     if (pd->cmd == SSS_PAM_CHAUTHTOK) {
    1868           0 :         ret = unpack_authtok(pd->newauthtok, buf, size, &p);
    1869           0 :         if (ret) {
    1870           0 :             return ret;
    1871             :         }
    1872             :     } else {
    1873           0 :         sss_authtok_set_empty(pd->newauthtok);
    1874             :     }
    1875             : 
    1876           0 :     if (pd->cmd == SSS_PAM_ACCT_MGMT) {
    1877           0 :         SAFEALIGN_COPY_UINT32_CHECK(&len, buf + p, size, &p);
    1878           0 :         if (len > size - p) return EINVAL;
    1879           0 :         pd->user = talloc_strndup(pd, (char *)(buf + p), len);
    1880           0 :         if (pd->user == NULL) return ENOMEM;
    1881           0 :         p += len;
    1882           0 :         DEBUG(SSSDBG_CONF_SETTINGS, "user: [%s]\n", pd->user);
    1883             :     } else {
    1884           0 :         pd->user = NULL;
    1885             :     }
    1886             : 
    1887           0 :     return EOK;
    1888             : }
    1889             : 
    1890           0 : static int krb5_cleanup(struct krb5_req *kr)
    1891             : {
    1892           0 :     if (kr == NULL) return EOK;
    1893             : 
    1894           0 :     if (kr->options != NULL) {
    1895           0 :         sss_krb5_get_init_creds_opt_free(kr->ctx, kr->options);
    1896             :     }
    1897             : 
    1898           0 :     if (kr->creds != NULL) {
    1899           0 :         krb5_free_cred_contents(kr->ctx, kr->creds);
    1900           0 :         krb5_free_creds(kr->ctx, kr->creds);
    1901             :     }
    1902           0 :     if (kr->name != NULL)
    1903           0 :         sss_krb5_free_unparsed_name(kr->ctx, kr->name);
    1904           0 :     if (kr->princ != NULL)
    1905           0 :         krb5_free_principal(kr->ctx, kr->princ);
    1906           0 :     if (kr->ctx != NULL)
    1907           0 :         krb5_free_context(kr->ctx);
    1908             : 
    1909           0 :     memset(kr, 0, sizeof(struct krb5_req));
    1910             : 
    1911           0 :     return EOK;
    1912             : }
    1913             : 
    1914           0 : static krb5_error_code get_tgt_times(krb5_context ctx, const char *ccname,
    1915             :                                      krb5_principal server_principal,
    1916             :                                      krb5_principal client_principal,
    1917             :                                      sss_krb5_ticket_times *tgtt)
    1918             : {
    1919             :     krb5_error_code krberr;
    1920           0 :     krb5_ccache ccache = NULL;
    1921             :     krb5_creds mcred;
    1922             :     krb5_creds cred;
    1923             : 
    1924           0 :     krberr = krb5_cc_resolve(ctx, ccname, &ccache);
    1925           0 :     if (krberr != 0) {
    1926           0 :         DEBUG(SSSDBG_CRIT_FAILURE, "krb5_cc_resolve failed.\n");
    1927           0 :         KRB5_CHILD_DEBUG(SSSDBG_CRIT_FAILURE, krberr);
    1928           0 :         goto done;
    1929             :     }
    1930             : 
    1931           0 :     memset(&mcred, 0, sizeof(mcred));
    1932           0 :     memset(&cred, 0, sizeof(mcred));
    1933             : 
    1934           0 :     mcred.server = server_principal;
    1935           0 :     mcred.client = client_principal;
    1936             : 
    1937           0 :     krberr = krb5_cc_retrieve_cred(ctx, ccache, 0, &mcred, &cred);
    1938           0 :     if (krberr == KRB5_FCC_NOFILE) {
    1939           0 :         DEBUG(SSSDBG_TRACE_LIBS, "FAST ccache must be recreated\n");
    1940           0 :     } else if (krberr != 0) {
    1941           0 :         DEBUG(SSSDBG_CRIT_FAILURE, "krb5_cc_retrieve_cred failed\n");
    1942           0 :         KRB5_CHILD_DEBUG(SSSDBG_CRIT_FAILURE, krberr);
    1943           0 :         krberr = 0;
    1944           0 :         goto done;
    1945             :     }
    1946             : 
    1947           0 :     tgtt->authtime = cred.times.authtime;
    1948           0 :     tgtt->starttime = cred.times.starttime;
    1949           0 :     tgtt->endtime = cred.times.endtime;
    1950           0 :     tgtt->renew_till = cred.times.renew_till;
    1951             : 
    1952           0 :     krb5_free_cred_contents(ctx, &cred);
    1953             : 
    1954           0 :     krberr = 0;
    1955             : 
    1956             : done:
    1957           0 :     if (ccache != NULL) {
    1958           0 :         krb5_cc_close(ctx, ccache);
    1959             :     }
    1960             : 
    1961           0 :     return krberr;
    1962             : }
    1963             : 
    1964           0 : static krb5_error_code check_fast_ccache(TALLOC_CTX *mem_ctx,
    1965             :                                          krb5_context ctx,
    1966             :                                          uid_t fast_uid,
    1967             :                                          gid_t fast_gid,
    1968             :                                          const char *primary,
    1969             :                                          const char *realm,
    1970             :                                          const char *keytab_name,
    1971             :                                          char **fast_ccname)
    1972             : {
    1973           0 :     TALLOC_CTX *tmp_ctx = NULL;
    1974             :     krb5_error_code kerr;
    1975             :     char *ccname;
    1976             :     char *server_name;
    1977             :     sss_krb5_ticket_times tgtt;
    1978           0 :     krb5_keytab keytab = NULL;
    1979           0 :     krb5_principal client_princ = NULL;
    1980           0 :     krb5_principal server_princ = NULL;
    1981             :     pid_t fchild_pid;
    1982             :     int status;
    1983             : 
    1984           0 :     tmp_ctx = talloc_new(NULL);
    1985           0 :     if (tmp_ctx == NULL) {
    1986           0 :         DEBUG(SSSDBG_CRIT_FAILURE, "talloc_new failed.\n");
    1987           0 :         return ENOMEM;
    1988             :     }
    1989             : 
    1990           0 :     ccname = talloc_asprintf(tmp_ctx, "FILE:%s/fast_ccache_%s", DB_PATH, realm);
    1991           0 :     if (ccname == NULL) {
    1992           0 :         DEBUG(SSSDBG_CRIT_FAILURE, "talloc_asprintf failed.\n");
    1993           0 :         kerr = ENOMEM;
    1994           0 :         goto done;
    1995             :     }
    1996             : 
    1997           0 :     if (keytab_name != NULL) {
    1998           0 :         kerr = krb5_kt_resolve(ctx, keytab_name, &keytab);
    1999             :     } else {
    2000           0 :         kerr = krb5_kt_default(ctx, &keytab);
    2001             :     }
    2002           0 :     if (kerr) {
    2003           0 :         DEBUG(SSSDBG_FATAL_FAILURE,
    2004             :               "Failed to read keytab file [%s]: %s\n",
    2005             :                KEYTAB_CLEAN_NAME,
    2006             :                sss_krb5_get_error_message(ctx, kerr));
    2007           0 :         goto done;
    2008             :     }
    2009             : 
    2010           0 :     kerr = find_principal_in_keytab(ctx, keytab, primary, realm, &client_princ);
    2011           0 :     if (kerr != 0) {
    2012           0 :         DEBUG(SSSDBG_MINOR_FAILURE,
    2013             :               "find_principal_in_keytab failed for principal %s@%s.\n",
    2014             :                primary, realm);
    2015           0 :         goto done;
    2016             :     }
    2017             : 
    2018           0 :     server_name = talloc_asprintf(tmp_ctx, "krbtgt/%s@%s", realm, realm);
    2019           0 :     if (server_name == NULL) {
    2020           0 :         DEBUG(SSSDBG_CRIT_FAILURE, "talloc_asprintf failed.\n");
    2021           0 :         kerr = ENOMEM;
    2022           0 :         goto done;
    2023             :     }
    2024             : 
    2025           0 :     kerr = krb5_parse_name(ctx, server_name, &server_princ);
    2026           0 :     if (kerr != 0) {
    2027           0 :         DEBUG(SSSDBG_CRIT_FAILURE, "krb5_parse_name failed.\n");
    2028           0 :         goto done;
    2029             :     }
    2030             : 
    2031           0 :     memset(&tgtt, 0, sizeof(tgtt));
    2032           0 :     kerr = get_tgt_times(ctx, ccname, server_princ, client_princ, &tgtt);
    2033           0 :     if (kerr == 0) {
    2034           0 :         if (tgtt.endtime > time(NULL)) {
    2035           0 :             DEBUG(SSSDBG_FUNC_DATA, "FAST TGT is still valid.\n");
    2036           0 :             goto done;
    2037             :         }
    2038             :     }
    2039             : 
    2040             :     /* Need to recreate the FAST ccache */
    2041           0 :     fchild_pid = fork();
    2042           0 :     switch (fchild_pid) {
    2043             :         case -1:
    2044           0 :             DEBUG(SSSDBG_CRIT_FAILURE, "fork failed\n");
    2045           0 :             kerr = EIO;
    2046           0 :             goto done;
    2047             :         case 0:
    2048             :             /* Child */
    2049           0 :             debug_prg_name = talloc_asprintf(NULL, "[sssd[krb5_child[%d]]]", getpid());
    2050           0 :             if (debug_prg_name == NULL) {
    2051           0 :                 debug_prg_name = "[sssd[krb5_child]]";
    2052           0 :                 DEBUG(SSSDBG_CRIT_FAILURE, "talloc_asprintf failed.\n");
    2053             :                 /* Try to carry on */
    2054             :             }
    2055             : 
    2056           0 :             kerr = become_user(fast_uid, fast_gid);
    2057           0 :             if (kerr != 0) {
    2058           0 :                 DEBUG(SSSDBG_CRIT_FAILURE, "become_user failed: %d\n", kerr);
    2059           0 :                 exit(1);
    2060             :             }
    2061           0 :             DEBUG(SSSDBG_TRACE_INTERNAL,
    2062             :                   "Running as [%"SPRIuid"][%"SPRIgid"].\n", geteuid(), getegid());
    2063             : 
    2064           0 :             kerr = get_and_save_tgt_with_keytab(ctx, client_princ,
    2065             :                                                 keytab, ccname);
    2066           0 :             if (kerr != 0) {
    2067           0 :                 DEBUG(SSSDBG_CRIT_FAILURE,
    2068             :                       "get_and_save_tgt_with_keytab failed: %d\n", kerr);
    2069           0 :                 exit(2);
    2070             :             }
    2071           0 :             exit(0);
    2072             :         default:
    2073             :             /* Parent */
    2074             :             do {
    2075           0 :                 errno = 0;
    2076           0 :                 kerr = waitpid(fchild_pid, &status, 0);
    2077           0 :             } while (kerr == -1 && errno == EINTR);
    2078             : 
    2079           0 :             if (kerr > 0) {
    2080           0 :                 if (WIFEXITED(status)) {
    2081           0 :                     kerr = WEXITSTATUS(status);
    2082             :                     /* Don't blindly fail if the child fails, but check
    2083             :                      * the ccache again */
    2084           0 :                     if (kerr != 0) {
    2085           0 :                         DEBUG(SSSDBG_MINOR_FAILURE,
    2086             :                               "Creating FAST ccache failed, krb5_child will "
    2087             :                               "likely fail!\n");
    2088             :                     }
    2089             :                 } else {
    2090           0 :                     DEBUG(SSSDBG_CRIT_FAILURE,
    2091             :                           "krb5_child subprocess %d terminated unexpectedly\n",
    2092             :                           fchild_pid);
    2093             :                 }
    2094             :             } else {
    2095           0 :                 DEBUG(SSSDBG_CRIT_FAILURE,
    2096             :                       "Failed to wait for child %d\n", fchild_pid);
    2097             :                 /* Let the code re-check the TGT times and fail if we
    2098             :                  * can't find the updated principal */
    2099             :             }
    2100             :     }
    2101             : 
    2102             :     /* Check the ccache times again. Should be updated ... */
    2103           0 :     memset(&tgtt, 0, sizeof(tgtt));
    2104           0 :     kerr = get_tgt_times(ctx, ccname, server_princ, client_princ, &tgtt);
    2105           0 :     if (kerr != 0) {
    2106           0 :         DEBUG(SSSDBG_OP_FAILURE, "get_tgt_times() failed\n");
    2107           0 :         goto done;
    2108             :     }
    2109             : 
    2110           0 :     if (tgtt.endtime < time(NULL)) {
    2111           0 :         DEBUG(SSSDBG_OP_FAILURE,
    2112             :               "FAST TGT was renewed but is already expired, please check that "
    2113             :               "time is synchronized with server.\n");
    2114           0 :         kerr = ERR_CREDS_EXPIRED;
    2115           0 :         goto done;
    2116             :     }
    2117           0 :     DEBUG(SSSDBG_FUNC_DATA, "FAST TGT was successfully recreated!\n");
    2118             : 
    2119             : done:
    2120           0 :     if (client_princ != NULL) {
    2121           0 :         krb5_free_principal(ctx, client_princ);
    2122             :     }
    2123           0 :     if (server_princ != NULL) {
    2124           0 :         krb5_free_principal(ctx, server_princ);
    2125             :     }
    2126             : 
    2127           0 :     if (kerr == 0) {
    2128           0 :         *fast_ccname = talloc_steal(mem_ctx, ccname);
    2129             :     }
    2130           0 :     talloc_free(tmp_ctx);
    2131             : 
    2132           0 :     if (keytab != NULL) {
    2133           0 :         krb5_kt_close(ctx, keytab);
    2134             :     }
    2135             : 
    2136           0 :     return kerr;
    2137             : }
    2138             : 
    2139           0 : static errno_t k5c_recv_data(struct krb5_req *kr, int fd, uint32_t *offline)
    2140             : {
    2141             :     uint8_t buf[IN_BUF_SIZE];
    2142             :     ssize_t len;
    2143             :     errno_t ret;
    2144             : 
    2145           0 :     errno = 0;
    2146           0 :     len = sss_atomic_read_s(fd, buf, IN_BUF_SIZE);
    2147           0 :     if (len == -1) {
    2148           0 :         ret = errno;
    2149           0 :         ret = (ret == 0) ? EINVAL: ret;
    2150           0 :         DEBUG(SSSDBG_CRIT_FAILURE,
    2151             :               "read failed [%d][%s].\n", ret, strerror(ret));
    2152           0 :         return ret;
    2153             :     }
    2154             : 
    2155           0 :     ret = unpack_buffer(buf, len, kr, offline);
    2156           0 :     if (ret != EOK) {
    2157           0 :         DEBUG(SSSDBG_CRIT_FAILURE, "unpack_buffer failed.\n");
    2158             :     }
    2159             : 
    2160           0 :     return ret;
    2161             : }
    2162             : 
    2163           0 : static int k5c_setup_fast(struct krb5_req *kr, bool demand)
    2164             : {
    2165             :     krb5_principal fast_princ_struct;
    2166             :     krb5_data *realm_data;
    2167             :     char *fast_principal_realm;
    2168             :     char *fast_principal;
    2169             :     krb5_error_code kerr;
    2170             :     char *tmp_str;
    2171             :     char *new_ccname;
    2172             : 
    2173           0 :     tmp_str = getenv(SSSD_KRB5_FAST_PRINCIPAL);
    2174           0 :     if (tmp_str) {
    2175           0 :         DEBUG(SSSDBG_CONF_SETTINGS, "%s is set to [%s]\n",
    2176             :                                      SSSD_KRB5_FAST_PRINCIPAL, tmp_str);
    2177           0 :         kerr = krb5_parse_name(kr->ctx, tmp_str, &fast_princ_struct);
    2178           0 :         if (kerr) {
    2179           0 :             DEBUG(SSSDBG_CRIT_FAILURE, "krb5_parse_name failed.\n");
    2180           0 :             return kerr;
    2181             :         }
    2182           0 :         kerr = sss_krb5_unparse_name_flags(kr->ctx, fast_princ_struct,
    2183             :                                        KRB5_PRINCIPAL_UNPARSE_NO_REALM,
    2184             :                                        &tmp_str);
    2185           0 :         if (kerr) {
    2186           0 :             DEBUG(SSSDBG_CRIT_FAILURE, "sss_krb5_unparse_name_flags failed.\n");
    2187           0 :             return kerr;
    2188             :         }
    2189           0 :         fast_principal = talloc_strdup(kr, tmp_str);
    2190           0 :         if (!fast_principal) {
    2191           0 :             DEBUG(SSSDBG_CRIT_FAILURE, "talloc_strdup failed.\n");
    2192           0 :             return KRB5KRB_ERR_GENERIC;
    2193             :         }
    2194           0 :         free(tmp_str);
    2195           0 :         realm_data = krb5_princ_realm(kr->ctx, fast_princ_struct);
    2196           0 :         fast_principal_realm = talloc_asprintf(kr, "%.*s", realm_data->length, realm_data->data);
    2197           0 :         if (!fast_principal_realm) {
    2198           0 :             DEBUG(SSSDBG_CRIT_FAILURE, "talloc_asprintf failed.\n");
    2199           0 :             return ENOMEM;
    2200             :         }
    2201             :     } else {
    2202           0 :         fast_principal_realm = kr->realm;
    2203           0 :         fast_principal = NULL;
    2204             :     }
    2205             : 
    2206           0 :     kerr = check_fast_ccache(kr, kr->ctx, kr->fast_uid, kr->fast_gid,
    2207             :                              fast_principal, fast_principal_realm,
    2208           0 :                              kr->keytab, &kr->fast_ccname);
    2209           0 :     if (kerr != 0) {
    2210           0 :         DEBUG(SSSDBG_CRIT_FAILURE, "check_fast_ccache failed.\n");
    2211           0 :         KRB5_CHILD_DEBUG(SSSDBG_CRIT_FAILURE, kerr);
    2212           0 :         return kerr;
    2213             :     }
    2214             : 
    2215           0 :     kerr = copy_ccache_into_memory(kr, kr->ctx, kr->fast_ccname, &new_ccname);
    2216           0 :     if (kerr != 0) {
    2217           0 :         DEBUG(SSSDBG_CRIT_FAILURE, "copy_ccache_into_memory failed.\n");
    2218           0 :         return kerr;
    2219             :     }
    2220             : 
    2221           0 :     talloc_free(kr->fast_ccname);
    2222           0 :     kr->fast_ccname = new_ccname;
    2223             : 
    2224           0 :     kerr = sss_krb5_get_init_creds_opt_set_fast_ccache_name(kr->ctx,
    2225             :                                                             kr->options,
    2226           0 :                                                             kr->fast_ccname);
    2227           0 :     if (kerr != 0) {
    2228           0 :         DEBUG(SSSDBG_CRIT_FAILURE,
    2229             :               "sss_krb5_get_init_creds_opt_set_fast_ccache_name "
    2230             :                   "failed.\n");
    2231           0 :         KRB5_CHILD_DEBUG(SSSDBG_CRIT_FAILURE, kerr);
    2232           0 :         return kerr;
    2233             :     }
    2234             : 
    2235           0 :     if (demand) {
    2236           0 :         kerr = sss_krb5_get_init_creds_opt_set_fast_flags(kr->ctx,
    2237             :                                                 kr->options,
    2238             :                                                 SSS_KRB5_FAST_REQUIRED);
    2239           0 :         if (kerr != 0) {
    2240           0 :             DEBUG(SSSDBG_CRIT_FAILURE,
    2241             :                   "sss_krb5_get_init_creds_opt_set_fast_flags "
    2242             :                       "failed.\n");
    2243           0 :             KRB5_CHILD_DEBUG(SSSDBG_CRIT_FAILURE, kerr);
    2244           0 :             return kerr;
    2245             :         }
    2246             :     }
    2247             : 
    2248           0 :     return EOK;
    2249             : }
    2250             : 
    2251           0 : static errno_t check_use_fast(enum k5c_fast_opt *_fast_val)
    2252             : {
    2253             :     char *use_fast_str;
    2254             :     enum k5c_fast_opt fast_val;
    2255             : 
    2256           0 :     use_fast_str = getenv(SSSD_KRB5_USE_FAST);
    2257           0 :     if (use_fast_str == NULL || strcasecmp(use_fast_str, "never") == 0) {
    2258           0 :         DEBUG(SSSDBG_CONF_SETTINGS, "Not using FAST.\n");
    2259           0 :         fast_val = K5C_FAST_NEVER;
    2260           0 :     } else if (strcasecmp(use_fast_str, "try") == 0) {
    2261           0 :         fast_val = K5C_FAST_TRY;
    2262           0 :     } else if (strcasecmp(use_fast_str, "demand") == 0) {
    2263           0 :         fast_val = K5C_FAST_DEMAND;
    2264             :     } else {
    2265           0 :         DEBUG(SSSDBG_CRIT_FAILURE,
    2266             :                 "Unsupported value [%s] for krb5_use_fast.\n",
    2267             :                 use_fast_str);
    2268           0 :         return EINVAL;
    2269             :     }
    2270             : 
    2271           0 :     *_fast_val = fast_val;
    2272           0 :     return EOK;
    2273             : }
    2274             : 
    2275           0 : static errno_t old_ccache_valid(struct krb5_req *kr, bool *_valid)
    2276             : {
    2277             :     errno_t ret;
    2278             :     bool valid;
    2279             : 
    2280           0 :     valid = false;
    2281             : 
    2282           0 :     ret = sss_krb5_cc_verify_ccache(kr->old_ccname,
    2283             :                                     kr->uid, kr->gid,
    2284           0 :                                     kr->realm, kr->upn);
    2285           0 :     switch (ret) {
    2286             :         case ERR_NOT_FOUND:
    2287             :         case ENOENT:
    2288           0 :             DEBUG(SSSDBG_TRACE_FUNC,
    2289             :                   "Saved ccache %s doesn't exist, ignoring\n", kr->old_ccname);
    2290           0 :             break;
    2291             :         case EINVAL:
    2292             :             /* cache found but no tgt or expired */
    2293             :         case EOK:
    2294           0 :             valid = true;
    2295           0 :             break;
    2296             :         default:
    2297           0 :             DEBUG(SSSDBG_OP_FAILURE,
    2298             :                   "Cannot check if saved ccache %s is valid\n",
    2299             :                    kr->old_ccname);
    2300           0 :             return ret;
    2301             :     }
    2302             : 
    2303           0 :     *_valid = valid;
    2304           0 :     return EOK;
    2305             : }
    2306             : 
    2307           0 : static int k5c_check_old_ccache(struct krb5_req *kr)
    2308             : {
    2309             :     errno_t ret;
    2310             : 
    2311           0 :     if (kr->old_ccname) {
    2312           0 :         ret = old_ccache_valid(kr, &kr->old_cc_valid);
    2313           0 :         if (ret != EOK) {
    2314           0 :             DEBUG(SSSDBG_OP_FAILURE, "old_ccache_valid failed.\n");
    2315           0 :             return ret;
    2316             :         }
    2317             : 
    2318           0 :         ret = check_if_uid_is_active(kr->uid, &kr->old_cc_active);
    2319           0 :         if (ret != EOK) {
    2320           0 :             DEBUG(SSSDBG_OP_FAILURE, "check_if_uid_is_active failed.\n");
    2321           0 :             return ret;
    2322             :         }
    2323             : 
    2324           0 :         DEBUG(SSSDBG_TRACE_ALL,
    2325             :                 "Ccache_file is [%s] and is %s active and TGT is %s valid.\n",
    2326             :                 kr->old_ccname ? kr->old_ccname : "not set",
    2327             :                 kr->old_cc_active ? "" : "not",
    2328             :                 kr->old_cc_valid ? "" : "not");
    2329             :     }
    2330             : 
    2331           0 :     return EOK;
    2332             : }
    2333             : 
    2334           0 : static int k5c_precreate_ccache(struct krb5_req *kr, uint32_t offline)
    2335             : {
    2336             :     errno_t ret;
    2337             : 
    2338             :     /* The ccache file should be (re)created if one of the following conditions
    2339             :      * is true:
    2340             :      * - it doesn't exist (kr->old_ccname == NULL)
    2341             :      * - the backend is online and the current ccache file is not used, i.e
    2342             :      * the related user is currently not logged in and it is not a renewal
    2343             :      * request
    2344             :      * (offline && !kr->old_cc_active && kr->pd->cmd != SSS_CMD_RENEW)
    2345             :      * - the backend is offline and the current cache file not used and
    2346             :      * it does not contain a valid tgt
    2347             :      * (offline && !kr->old_cc_active && !kr->valid_tgt)
    2348             :      */
    2349           0 :     if (kr->old_ccname == NULL ||
    2350           0 :             (offline && !kr->old_cc_active && !kr->old_cc_valid) ||
    2351           0 :             (!offline && !kr->old_cc_active && kr->pd->cmd != SSS_CMD_RENEW)) {
    2352           0 :         DEBUG(SSSDBG_TRACE_ALL, "Recreating ccache\n");
    2353             : 
    2354           0 :         ret = sss_krb5_precreate_ccache(kr->ccname, kr->uid, kr->gid);
    2355           0 :         if (ret != EOK) {
    2356           0 :             DEBUG(SSSDBG_OP_FAILURE, "ccache creation failed.\n");
    2357           0 :             return ret;
    2358             :         }
    2359             :     } else {
    2360             :         /* We can reuse the old ccache */
    2361           0 :         kr->ccname = kr->old_ccname;
    2362             :     }
    2363             : 
    2364           0 :     return EOK;
    2365             : }
    2366             : 
    2367           0 : static int k5c_ccache_setup(struct krb5_req *kr, uint32_t offline)
    2368             : {
    2369             :     errno_t ret;
    2370             : 
    2371           0 :     if (kr->pd->cmd == SSS_PAM_ACCT_MGMT) {
    2372           0 :         return EOK;
    2373             :     }
    2374             : 
    2375           0 :     ret = k5c_check_old_ccache(kr);
    2376           0 :     if (ret != 0) {
    2377           0 :         DEBUG(SSSDBG_CRIT_FAILURE, "Cannot check old ccache [%s]: [%d][%s]. " \
    2378             :                                    "Assuming old cache is invalid " \
    2379             :                                    "and not used.\n",
    2380             :                                    kr->old_ccname, ret, sss_strerror(ret));
    2381             :     }
    2382             : 
    2383             :     /* Pre-creating the ccache must be done as root, otherwise we can't mkdir
    2384             :      * some of the DIR: cache components. One example is /run/user/$UID because
    2385             :      * logind doesn't create the directory until the session phase, whereas
    2386             :      * we need the directory during the auth phase already
    2387             :      */
    2388           0 :     ret = k5c_precreate_ccache(kr, offline);
    2389           0 :     if (ret != 0) {
    2390           0 :         DEBUG(SSSDBG_OP_FAILURE, "Cannot precreate ccache\n");
    2391           0 :         return ret;
    2392             :     }
    2393             : 
    2394           0 :     return EOK;
    2395             : }
    2396             : 
    2397           0 : static int k5c_setup(struct krb5_req *kr, uint32_t offline)
    2398             : {
    2399             :     krb5_error_code kerr;
    2400             :     int parse_flags;
    2401             : 
    2402           0 :     if (offline || (kr->fast_val == K5C_FAST_NEVER && kr->validate == false)) {
    2403             :         /* If krb5_child was started as setuid, but we don't need to
    2404             :          * perform either validation or FAST, just drop privileges to
    2405             :          * the user who is logging in. The same applies to the offline case
    2406             :          * the user who is logging in. The same applies to the offline case.
    2407             :          */
    2408           0 :         kerr = become_user(kr->uid, kr->gid);
    2409           0 :         if (kerr != 0) {
    2410           0 :             DEBUG(SSSDBG_CRIT_FAILURE, "become_user failed.\n");
    2411           0 :             return kerr;
    2412             :         }
    2413             :     }
    2414           0 :     DEBUG(SSSDBG_TRACE_INTERNAL,
    2415             :           "Running as [%"SPRIuid"][%"SPRIgid"].\n", geteuid(), getegid());
    2416             : 
    2417             :     /* Set the global error context */
    2418           0 :     krb5_error_ctx = kr->ctx;
    2419             : 
    2420           0 :     if (debug_level & SSSDBG_TRACE_ALL) {
    2421           0 :         kerr = sss_child_set_krb5_tracing(kr->ctx);
    2422           0 :         if (kerr != 0) {
    2423           0 :             KRB5_CHILD_DEBUG(SSSDBG_MINOR_FAILURE, kerr);
    2424           0 :             return EIO;
    2425             :         }
    2426             :     }
    2427             : 
    2428             :     /* Enterprise principals require that a default realm is available. To
    2429             :      * make SSSD more robust in the case that the default realm option is
    2430             :      * missing in krb5.conf or to allow SSSD to work with multiple unconnected
    2431             :      * realms (e.g. AD domains without trust between them) the default realm
    2432             :      * will be set explicitly. */
    2433           0 :     if (kr->use_enterprise_princ) {
    2434           0 :         kerr = krb5_set_default_realm(kr->ctx, kr->realm);
    2435           0 :         if (kerr != 0) {
    2436           0 :             DEBUG(SSSDBG_CRIT_FAILURE, "krb5_set_default_realm failed.\n");
    2437             :         }
    2438             :     }
    2439             : 
    2440           0 :     parse_flags = kr->use_enterprise_princ ? KRB5_PRINCIPAL_PARSE_ENTERPRISE : 0;
    2441           0 :     kerr = sss_krb5_parse_name_flags(kr->ctx, kr->upn, parse_flags, &kr->princ);
    2442           0 :     if (kerr != 0) {
    2443           0 :         KRB5_CHILD_DEBUG(SSSDBG_CRIT_FAILURE, kerr);
    2444           0 :         return kerr;
    2445             :     }
    2446             : 
    2447           0 :     kerr = krb5_unparse_name(kr->ctx, kr->princ, &kr->name);
    2448           0 :     if (kerr != 0) {
    2449           0 :         KRB5_CHILD_DEBUG(SSSDBG_CRIT_FAILURE, kerr);
    2450           0 :         return kerr;
    2451             :     }
    2452             : 
    2453           0 :     kr->creds = calloc(1, sizeof(krb5_creds));
    2454           0 :     if (kr->creds == NULL) {
    2455           0 :         DEBUG(SSSDBG_CRIT_FAILURE, "talloc_zero failed.\n");
    2456           0 :         return ENOMEM;
    2457             :     }
    2458             : 
    2459             : #ifdef HAVE_KRB5_GET_INIT_CREDS_OPT_SET_RESPONDER
    2460           0 :     kerr = krb5_get_init_creds_opt_set_responder(kr->ctx, kr->options,
    2461             :                                                  sss_krb5_responder, kr);
    2462           0 :     if (kerr != 0) {
    2463           0 :         KRB5_CHILD_DEBUG(SSSDBG_CRIT_FAILURE, kerr);
    2464           0 :         return kerr;
    2465             :     }
    2466             : #endif
    2467             : 
    2468             : #ifdef HAVE_KRB5_GET_INIT_CREDS_OPT_SET_CHANGE_PASSWORD_PROMPT
    2469             :     /* A prompter is used to catch messages about when a password will
    2470             :      * expired. The library shall not use the prompter to ask for a new password
    2471             :      * but shall return KRB5KDC_ERR_KEY_EXP. */
    2472           0 :     krb5_get_init_creds_opt_set_change_password_prompt(kr->options, 0);
    2473             : #endif
    2474             : 
    2475           0 :     kerr = set_lifetime_options(kr->options);
    2476           0 :     if (kerr != 0) {
    2477           0 :         DEBUG(SSSDBG_OP_FAILURE, "set_lifetime_options failed.\n");
    2478           0 :         return kerr;
    2479             :     }
    2480             : 
    2481           0 :     if (!offline) {
    2482           0 :         set_canonicalize_option(kr->options);
    2483             :     }
    2484             : 
    2485             : /* TODO: set options, e.g.
    2486             :  *  krb5_get_init_creds_opt_set_forwardable
    2487             :  *  krb5_get_init_creds_opt_set_proxiable
    2488             :  *  krb5_get_init_creds_opt_set_etype_list
    2489             :  *  krb5_get_init_creds_opt_set_address_list
    2490             :  *  krb5_get_init_creds_opt_set_preauth_list
    2491             :  *  krb5_get_init_creds_opt_set_salt
    2492             :  *  krb5_get_init_creds_opt_set_change_password_prompt
    2493             :  *  krb5_get_init_creds_opt_set_pa
    2494             :  */
    2495             : 
    2496           0 :     return kerr;
    2497             : }
    2498             : 
    2499           0 : static krb5_error_code privileged_krb5_setup(struct krb5_req *kr,
    2500             :                                              uint32_t offline)
    2501             : {
    2502             :     krb5_error_code kerr;
    2503             :     int ret;
    2504             :     char *mem_keytab;
    2505             : 
    2506           0 :     kr->realm = getenv(SSSD_KRB5_REALM);
    2507           0 :     if (kr->realm == NULL) {
    2508           0 :         DEBUG(SSSDBG_MINOR_FAILURE,
    2509             :               "Cannot read [%s] from environment.\n", SSSD_KRB5_REALM);
    2510             :     }
    2511             : 
    2512           0 :     kerr = krb5_init_context(&kr->ctx);
    2513           0 :     if (kerr != 0) {
    2514           0 :         KRB5_CHILD_DEBUG(SSSDBG_CRIT_FAILURE, kerr);
    2515           0 :         return kerr;
    2516             :     }
    2517             : 
    2518           0 :     kerr = sss_krb5_get_init_creds_opt_alloc(kr->ctx, &kr->options);
    2519           0 :     if (kerr != 0) {
    2520           0 :         KRB5_CHILD_DEBUG(SSSDBG_CRIT_FAILURE, kerr);
    2521           0 :         return kerr;
    2522             :     }
    2523             : 
    2524           0 :     ret = check_use_fast(&kr->fast_val);
    2525           0 :     if (ret != EOK) {
    2526           0 :         DEBUG(SSSDBG_CRIT_FAILURE, "check_use_fast failed.\n");
    2527           0 :         return ret;;
    2528             :     }
    2529             : 
    2530             :     /* For ccache types FILE: and DIR: we might need to create some directory
    2531             :      * components as root. Cache files are not needed during preauth. */
    2532           0 :     if (kr->pd->cmd != SSS_PAM_PREAUTH) {
    2533           0 :         ret = k5c_ccache_setup(kr, offline);
    2534           0 :         if (ret != EOK) {
    2535           0 :             DEBUG(SSSDBG_CRIT_FAILURE, "k5c_ccache_setup failed.\n");
    2536           0 :             return ret;
    2537             :         }
    2538             :     }
    2539             : 
    2540           0 :     if (!(offline ||
    2541           0 :             (kr->fast_val == K5C_FAST_NEVER && kr->validate == false))) {
    2542           0 :         kerr = copy_keytab_into_memory(kr, kr->ctx, kr->keytab, &mem_keytab,
    2543             :                                        NULL);
    2544           0 :         if (kerr != 0) {
    2545           0 :             DEBUG(SSSDBG_OP_FAILURE, "copy_keytab_into_memory failed.\n");
    2546           0 :             return kerr;
    2547             :         }
    2548             : 
    2549           0 :         talloc_free(kr->keytab);
    2550           0 :         kr->keytab = mem_keytab;
    2551             : 
    2552           0 :         if (kr->fast_val != K5C_FAST_NEVER) {
    2553           0 :             kerr = k5c_setup_fast(kr, kr->fast_val == K5C_FAST_DEMAND);
    2554           0 :             if (kerr != EOK) {
    2555           0 :                 DEBUG(SSSDBG_OP_FAILURE, "Cannot set up FAST\n");
    2556           0 :                 return kerr;
    2557             :             }
    2558             :         }
    2559             :     }
    2560             : 
    2561           0 :     if (kr->send_pac) {
    2562           0 :         ret = sss_pac_check_and_open();
    2563           0 :         if (ret != EOK) {
    2564           0 :             DEBUG(SSSDBG_MINOR_FAILURE, "Cannot open the PAC responder socket\n");
    2565             :             /* Not fatal */
    2566             :         }
    2567             :     }
    2568             : 
    2569           0 :     return 0;
    2570             : }
    2571             : 
    2572           0 : int main(int argc, const char *argv[])
    2573             : {
    2574           0 :     struct krb5_req *kr = NULL;
    2575             :     uint32_t offline;
    2576             :     int opt;
    2577             :     poptContext pc;
    2578           0 :     int debug_fd = -1;
    2579             :     errno_t ret;
    2580             :     krb5_error_code kerr;
    2581             :     uid_t fast_uid;
    2582             :     gid_t fast_gid;
    2583             : 
    2584           0 :     struct poptOption long_options[] = {
    2585             :         POPT_AUTOHELP
    2586             :         {"debug-level", 'd', POPT_ARG_INT, &debug_level, 0,
    2587           0 :          _("Debug level"), NULL},
    2588             :         {"debug-timestamps", 0, POPT_ARG_INT, &debug_timestamps, 0,
    2589           0 :          _("Add debug timestamps"), NULL},
    2590             :         {"debug-microseconds", 0, POPT_ARG_INT, &debug_microseconds, 0,
    2591           0 :          _("Show timestamps with microseconds"), NULL},
    2592             :         {"debug-fd", 0, POPT_ARG_INT, &debug_fd, 0,
    2593           0 :          _("An open file descriptor for the debug logs"), NULL},
    2594             :         {"debug-to-stderr", 0, POPT_ARG_NONE | POPT_ARGFLAG_DOC_HIDDEN,
    2595             :          &debug_to_stderr, 0,
    2596           0 :          _("Send the debug output to stderr directly."), NULL },
    2597             :         {"fast-ccache-uid", 0, POPT_ARG_INT, &fast_uid, 0,
    2598           0 :           _("The user to create FAST ccache as"), NULL},
    2599             :         {"fast-ccache-gid", 0, POPT_ARG_INT, &fast_gid, 0,
    2600           0 :           _("The group to create FAST ccache as"), NULL},
    2601             :         POPT_TABLEEND
    2602             :     };
    2603             : 
    2604             :     /* Set debug level to invalid value so we can decide if -d 0 was used. */
    2605           0 :     debug_level = SSSDBG_INVALID;
    2606             : 
    2607           0 :     pc = poptGetContext(argv[0], argc, argv, long_options, 0);
    2608           0 :     while((opt = poptGetNextOpt(pc)) != -1) {
    2609             :         switch(opt) {
    2610             :         default:
    2611           0 :         fprintf(stderr, "\nInvalid option %s: %s\n\n",
    2612             :                   poptBadOption(pc, 0), poptStrerror(opt));
    2613           0 :             poptPrintUsage(pc, stderr, 0);
    2614           0 :             _exit(-1);
    2615             :         }
    2616             :     }
    2617             : 
    2618           0 :     poptFreeContext(pc);
    2619             : 
    2620           0 :     DEBUG_INIT(debug_level);
    2621             : 
    2622           0 :     debug_prg_name = talloc_asprintf(NULL, "[sssd[krb5_child[%d]]]", getpid());
    2623           0 :     if (!debug_prg_name) {
    2624           0 :         debug_prg_name = "[sssd[krb5_child]]";
    2625           0 :         DEBUG(SSSDBG_CRIT_FAILURE, "talloc_asprintf failed.\n");
    2626           0 :         ret = ENOMEM;
    2627           0 :         goto done;
    2628             :     }
    2629             : 
    2630           0 :     if (debug_fd != -1) {
    2631           0 :         ret = set_debug_file_from_fd(debug_fd);
    2632           0 :         if (ret != EOK) {
    2633           0 :             DEBUG(SSSDBG_CRIT_FAILURE, "set_debug_file_from_fd failed.\n");
    2634             :         }
    2635             :     }
    2636             : 
    2637           0 :     DEBUG(SSSDBG_TRACE_FUNC, "krb5_child started.\n");
    2638             : 
    2639           0 :     kr = talloc_zero(NULL, struct krb5_req);
    2640           0 :     if (kr == NULL) {
    2641           0 :         DEBUG(SSSDBG_CRIT_FAILURE, "talloc failed.\n");
    2642           0 :         ret = ENOMEM;
    2643           0 :         goto done;
    2644             :     }
    2645           0 :     talloc_steal(kr, debug_prg_name);
    2646             : 
    2647           0 :     kr->fast_uid = fast_uid;
    2648           0 :     kr->fast_gid = fast_gid;
    2649             : 
    2650           0 :     ret = k5c_recv_data(kr, STDIN_FILENO, &offline);
    2651           0 :     if (ret != EOK) {
    2652           0 :         goto done;
    2653             :     }
    2654             : 
    2655           0 :     close(STDIN_FILENO);
    2656             : 
    2657           0 :     kerr = privileged_krb5_setup(kr, offline);
    2658           0 :     if (kerr != 0) {
    2659           0 :         DEBUG(SSSDBG_CRIT_FAILURE, "privileged_krb5_setup failed.\n");
    2660           0 :         ret = EFAULT;
    2661           0 :         goto done;
    2662             :     }
    2663             : 
    2664           0 :     kerr = become_user(kr->uid, kr->gid);
    2665           0 :     if (kerr != 0) {
    2666           0 :         DEBUG(SSSDBG_CRIT_FAILURE, "become_user failed.\n");
    2667           0 :         ret = EFAULT;
    2668           0 :         goto done;
    2669             :     }
    2670             : 
    2671           0 :     DEBUG(SSSDBG_TRACE_INTERNAL,
    2672             :           "Running as [%"SPRIuid"][%"SPRIgid"].\n", geteuid(), getegid());
    2673             : 
    2674           0 :     ret = k5c_setup(kr, offline);
    2675           0 :     if (ret != EOK) {
    2676           0 :         DEBUG(SSSDBG_CRIT_FAILURE, "krb5_child_setup failed.\n");
    2677           0 :         goto done;
    2678             :     }
    2679             : 
    2680           0 :     switch(kr->pd->cmd) {
    2681             :     case SSS_PAM_AUTHENTICATE:
    2682             :         /* If we are offline, we need to create an empty ccache file */
    2683           0 :         if (offline) {
    2684           0 :             DEBUG(SSSDBG_TRACE_FUNC, "Will perform offline auth\n");
    2685           0 :             ret = create_empty_ccache(kr);
    2686             :         } else {
    2687           0 :             DEBUG(SSSDBG_TRACE_FUNC, "Will perform online auth\n");
    2688           0 :             ret = tgt_req_child(kr);
    2689             :         }
    2690           0 :         break;
    2691             :     case SSS_PAM_CHAUTHTOK:
    2692           0 :         DEBUG(SSSDBG_TRACE_FUNC, "Will perform password change\n");
    2693           0 :         ret = changepw_child(kr, false);
    2694           0 :         break;
    2695             :     case SSS_PAM_CHAUTHTOK_PRELIM:
    2696           0 :         DEBUG(SSSDBG_TRACE_FUNC, "Will perform password change checks\n");
    2697           0 :         ret = changepw_child(kr, true);
    2698           0 :         break;
    2699             :     case SSS_PAM_ACCT_MGMT:
    2700           0 :         DEBUG(SSSDBG_TRACE_FUNC, "Will perform account management\n");
    2701           0 :         ret = kuserok_child(kr);
    2702           0 :         break;
    2703             :     case SSS_CMD_RENEW:
    2704           0 :         if (offline) {
    2705           0 :             DEBUG(SSSDBG_CRIT_FAILURE, "Cannot renew TGT while offline\n");
    2706           0 :             ret = KRB5_KDC_UNREACH;
    2707           0 :             goto done;
    2708             :         }
    2709           0 :         DEBUG(SSSDBG_TRACE_FUNC, "Will perform ticket renewal\n");
    2710           0 :         ret = renew_tgt_child(kr);
    2711           0 :         break;
    2712             :     case SSS_PAM_PREAUTH:
    2713           0 :         DEBUG(SSSDBG_TRACE_FUNC, "Will perform pre-auth\n");
    2714           0 :         ret = tgt_req_child(kr);
    2715           0 :         break;
    2716             :     default:
    2717           0 :         DEBUG(SSSDBG_CRIT_FAILURE,
    2718             :               "PAM command [%d] not supported.\n", kr->pd->cmd);
    2719           0 :         ret = EINVAL;
    2720           0 :         goto done;
    2721             :     }
    2722             : 
    2723           0 :     ret = k5c_send_data(kr, STDOUT_FILENO, ret);
    2724           0 :     if (ret != EOK) {
    2725           0 :         DEBUG(SSSDBG_CRIT_FAILURE, "Failed to send reply\n");
    2726             :     }
    2727             : 
    2728             : done:
    2729           0 :     if (ret == EOK) {
    2730           0 :         DEBUG(SSSDBG_TRACE_FUNC, "krb5_child completed successfully\n");
    2731           0 :         ret = 0;
    2732             :     } else {
    2733           0 :         DEBUG(SSSDBG_CRIT_FAILURE, "krb5_child failed!\n");
    2734           0 :         ret = -1;
    2735             :     }
    2736           0 :     krb5_cleanup(kr);
    2737           0 :     talloc_free(kr);
    2738           0 :     exit(ret);
    2739             : }

Generated by: LCOV version 1.10