LCOV - code coverage report
Current view: top level - responder/pam - pamsrv_cmd.c (source / functions) Hit Total Coverage
Test: .coverage.total Lines: 366 741 49.4 %
Date: 2015-10-19 Functions: 30 39 76.9 %

          Line data    Source code
       1             : /*
       2             :    SSSD
       3             : 
       4             :    PAM Responder
       5             : 
       6             :    Copyright (C) Simo Sorce <ssorce@redhat.com>   2009
       7             :    Copyright (C) Sumit Bose <sbose@redhat.com>    2009
       8             : 
       9             :    This program is free software; you can redistribute it and/or modify
      10             :    it under the terms of the GNU General Public License as published by
      11             :    the Free Software Foundation; either version 3 of the License, or
      12             :    (at your option) any later version.
      13             : 
      14             :    This program is distributed in the hope that it will be useful,
      15             :    but WITHOUT ANY WARRANTY; without even the implied warranty of
      16             :    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      17             :    GNU General Public License for more details.
      18             : 
      19             :    You should have received a copy of the GNU General Public License
      20             :    along with this program.  If not, see <http://www.gnu.org/licenses/>.
      21             : */
      22             : 
      23             : #include <time.h>
      24             : #include "util/util.h"
      25             : #include "util/auth_utils.h"
      26             : #include "db/sysdb.h"
      27             : #include "confdb/confdb.h"
      28             : #include "responder/common/responder_packet.h"
      29             : #include "responder/common/responder.h"
      30             : #include "responder/common/negcache.h"
      31             : #include "providers/data_provider.h"
      32             : #include "responder/pam/pamsrv.h"
      33             : #include "responder/pam/pam_helpers.h"
      34             : #include "responder/common/responder_cache_req.h"
      35             : #include "db/sysdb.h"
      36             : 
      37             : #define DEFAULT_PAM_VERBOSITY PAM_VERBOSITY_IMPORTANT
      38             : 
      39             : static errno_t
      40             : pam_null_last_online_auth_with_curr_token(struct sss_domain_info *domain,
      41             :                                           const char *username);
      42             : static errno_t
      43             : pam_get_last_online_auth_with_curr_token(struct sss_domain_info *domain,
      44             :                                          const char *name,
      45             :                                          uint64_t *_value);
      46             : 
      47             : static void pam_reply(struct pam_auth_req *preq);
      48             : 
      49          27 : static bool is_domain_requested(struct pam_data *pd, const char *domain_name)
      50             : {
      51             :     int i;
      52             : 
      53             :     /* If none specific domains got requested via pam, all domains are allowed.
      54             :      * Which mimics the default/original behaviour.
      55             :      */
      56          27 :     if (!pd->requested_domains) {
      57          27 :         return true;
      58             :     }
      59             : 
      60           0 :     for (i = 0; pd->requested_domains[i]; i++) {
      61           0 :         if (strcasecmp(domain_name, pd->requested_domains[i])) {
      62           0 :             continue;
      63             :         }
      64             : 
      65           0 :         return true;
      66             :     }
      67             : 
      68           0 :     return false;
      69             : }
      70             : 
      71          27 : static int pd_set_primary_name(const struct ldb_message *msg,struct pam_data *pd)
      72             : {
      73             :     const char *name;
      74             : 
      75          27 :     name = ldb_msg_find_attr_as_string(msg, SYSDB_NAME, NULL);
      76          27 :     if (!name) {
      77           0 :         DEBUG(SSSDBG_CRIT_FAILURE, "A user with no name?\n");
      78           0 :         return EIO;
      79             :     }
      80             : 
      81          27 :     if (strcmp(pd->user, name)) {
      82           0 :         DEBUG(SSSDBG_TRACE_FUNC, "User's primary name is %s\n", name);
      83           0 :         talloc_free(pd->user);
      84           0 :         pd->user = talloc_strdup(pd, name);
      85           0 :         if (!pd->user) return ENOMEM;
      86             :     }
      87             : 
      88          27 :     return EOK;
      89             : }
      90             : 
      91             : 
      92             : /*=Save-Last-Login-State===================================================*/
      93             : 
      94           2 : static errno_t set_last_login(struct pam_auth_req *preq)
      95             : {
      96             :     struct sysdb_attrs *attrs;
      97             :     errno_t ret;
      98             : 
      99           2 :     attrs = sysdb_new_attrs(preq);
     100           2 :     if (!attrs) {
     101           0 :         ret = ENOMEM;
     102           0 :         goto fail;
     103             :     }
     104             : 
     105           2 :     ret = sysdb_attrs_add_time_t(attrs, SYSDB_LAST_ONLINE_AUTH, time(NULL));
     106           2 :     if (ret != EOK) {
     107           0 :         goto fail;
     108             :     }
     109             : 
     110           2 :     ret = sysdb_attrs_add_time_t(attrs,
     111             :                                  SYSDB_LAST_ONLINE_AUTH_WITH_CURR_TOKEN,
     112             :                                  time(NULL));
     113           2 :     if (ret != EOK) {
     114           0 :         goto fail;
     115             :     }
     116             : 
     117           2 :     ret = sysdb_attrs_add_time_t(attrs, SYSDB_LAST_LOGIN, time(NULL));
     118           2 :     if (ret != EOK) {
     119           0 :         goto fail;
     120             :     }
     121             : 
     122           2 :     ret = sysdb_set_user_attr(preq->domain, preq->pd->user, attrs,
     123             :                               SYSDB_MOD_REP);
     124           2 :     if (ret != EOK) {
     125           0 :         DEBUG(SSSDBG_OP_FAILURE, "set_last_login failed.\n");
     126           0 :         preq->pd->pam_status = PAM_SYSTEM_ERR;
     127           0 :         goto fail;
     128             :     } else {
     129           2 :         preq->pd->last_auth_saved = true;
     130             :     }
     131           2 :     preq->callback(preq);
     132             : 
     133           2 :     return EOK;
     134             : 
     135             : fail:
     136           0 :     return ret;
     137             : }
     138             : 
     139          31 : static errno_t filter_responses(struct confdb_ctx *cdb,
     140             :                                 struct response_data *resp_list)
     141             : {
     142             :     int ret;
     143             :     struct response_data *resp;
     144             :     uint32_t user_info_type;
     145             :     int64_t expire_date;
     146             :     int pam_verbosity;
     147             : 
     148          31 :     ret = confdb_get_int(cdb, CONFDB_PAM_CONF_ENTRY,
     149             :                          CONFDB_PAM_VERBOSITY, DEFAULT_PAM_VERBOSITY,
     150             :                          &pam_verbosity);
     151          31 :     if (ret != EOK) {
     152           0 :         DEBUG(SSSDBG_CRIT_FAILURE,
     153             :               "Failed to read PAM verbosity, not fatal.\n");
     154           0 :         pam_verbosity = DEFAULT_PAM_VERBOSITY;
     155             :     }
     156             : 
     157          31 :     resp = resp_list;
     158          71 :     while(resp != NULL) {
     159           9 :         if (resp->type == SSS_PAM_USER_INFO) {
     160           7 :             if (resp->len < sizeof(uint32_t)) {
     161           0 :                 DEBUG(SSSDBG_CRIT_FAILURE, "User info entry is too short.\n");
     162           0 :                 return EINVAL;
     163             :             }
     164             : 
     165           7 :             if (pam_verbosity == PAM_VERBOSITY_NO_MESSAGES) {
     166           0 :                 resp->do_not_send_to_client = true;
     167           0 :                 resp = resp->next;
     168           0 :                 continue;
     169             :             }
     170             : 
     171           7 :             memcpy(&user_info_type, resp->data, sizeof(uint32_t));
     172             : 
     173           7 :             resp->do_not_send_to_client = false;
     174           7 :             switch (user_info_type) {
     175             :                 case SSS_PAM_USER_INFO_OFFLINE_AUTH:
     176           5 :                     if (resp->len != sizeof(uint32_t) + sizeof(int64_t)) {
     177           0 :                         DEBUG(SSSDBG_CRIT_FAILURE,
     178             :                               "User info offline auth entry is "
     179             :                                   "too short.\n");
     180           0 :                         return EINVAL;
     181             :                     }
     182           5 :                     memcpy(&expire_date, resp->data + sizeof(uint32_t),
     183             :                            sizeof(int64_t));
     184          10 :                     if ((expire_date == 0 &&
     185           5 :                          pam_verbosity < PAM_VERBOSITY_INFO) ||
     186           0 :                         (expire_date > 0 &&
     187           0 :                          pam_verbosity < PAM_VERBOSITY_IMPORTANT)) {
     188           5 :                         resp->do_not_send_to_client = true;
     189             :                     }
     190             : 
     191           5 :                     break;
     192             :                 default:
     193           2 :                     DEBUG(SSSDBG_TRACE_LIBS,
     194             :                           "User info type [%d] not filtered.\n",
     195             :                            user_info_type);
     196             :             }
     197           2 :         } else if (resp->type & SSS_SERVER_INFO) {
     198           0 :             resp->do_not_send_to_client = true;
     199             :         }
     200             : 
     201           9 :         resp = resp->next;
     202             :     }
     203             : 
     204          31 :     return EOK;
     205             : }
     206             : 
     207           0 : static void pam_reply_delay(struct tevent_context *ev, struct tevent_timer *te,
     208             :                             struct timeval tv, void *pvt)
     209             : {
     210             :     struct pam_auth_req *preq;
     211             : 
     212           0 :     DEBUG(SSSDBG_CONF_SETTINGS, "pam_reply_delay get called.\n");
     213             : 
     214           0 :     preq = talloc_get_type(pvt, struct pam_auth_req);
     215             : 
     216           0 :     pam_reply(preq);
     217           0 : }
     218             : 
     219          12 : static errno_t get_password_for_cache_auth(struct sss_auth_token *authtok,
     220             :                                            const char **password)
     221             : {
     222             :     int ret;
     223             :     size_t pw_len;
     224             :     const char *fa2;
     225             :     size_t fa2_len;
     226             : 
     227          12 :     switch (sss_authtok_get_type(authtok)) {
     228             :     case SSS_AUTHTOK_TYPE_PASSWORD:
     229           8 :         ret = sss_authtok_get_password(authtok, password, NULL);
     230           8 :         break;
     231             :     case SSS_AUTHTOK_TYPE_2FA:
     232           4 :         ret = sss_authtok_get_2fa(authtok, password, &pw_len, &fa2, &fa2_len);
     233           4 :         break;
     234             :     default:
     235           0 :         DEBUG(SSSDBG_FATAL_FAILURE, "Unsupported auth token type [%d].\n",
     236             :               sss_authtok_get_type(authtok));
     237           0 :         ret = EINVAL;
     238             :     }
     239          12 :     if (ret != EOK) {
     240           0 :         DEBUG(SSSDBG_FATAL_FAILURE, "Failed to get password.\n");
     241           0 :         return ret;
     242             :     }
     243             : 
     244          12 :     return EOK;
     245             : }
     246             : 
     247          31 : static errno_t add_warning_about_expiration(struct pam_data *pd,
     248             :                                             struct confdb_ctx *cdb)
     249             : {
     250             :     char *pam_account_expired_message;
     251             :     int pam_verbosity;
     252             :     errno_t ret;
     253             : 
     254          31 :     ret = confdb_get_int(cdb, CONFDB_PAM_CONF_ENTRY,
     255             :                          CONFDB_PAM_VERBOSITY, DEFAULT_PAM_VERBOSITY,
     256             :                          &pam_verbosity);
     257          31 :     if (ret != EOK) {
     258           0 :         DEBUG(SSSDBG_CRIT_FAILURE,
     259             :               "Failed to read PAM verbosity, not fatal.\n");
     260           0 :         pam_verbosity = DEFAULT_PAM_VERBOSITY;
     261             :     }
     262             : 
     263          31 :     if (pd->pam_status != PAM_ACCT_EXPIRED ||
     264           0 :         ((pd->service == NULL || strcasecmp(pd->service, "sshd") != 0) ||
     265           0 :          pam_verbosity < PAM_VERBOSITY_INFO)) {
     266          31 :         return EOK;
     267             :     }
     268             : 
     269           0 :     ret = confdb_get_string(cdb, pd, CONFDB_PAM_CONF_ENTRY,
     270             :                             CONFDB_PAM_ACCOUNT_EXPIRED_MESSAGE, "",
     271             :                             &pam_account_expired_message);
     272           0 :     if (ret != EOK) {
     273           0 :         DEBUG(SSSDBG_MINOR_FAILURE,
     274             :                 "Failed to get expiration message: %d:[%s].\n",
     275             :                 ret, sss_strerror(ret));
     276           0 :         goto done;
     277             :     }
     278             : 
     279           0 :     ret = pamsrv_exp_warn(pd, pam_verbosity, pam_account_expired_message);
     280           0 :     if (ret != EOK) {
     281           0 :         DEBUG(SSSDBG_MINOR_FAILURE,
     282             :                 "Failed to add password expiration warning: %d: %s\n",
     283             :                 ret, sss_strerror(ret));
     284           0 :         goto done;
     285             :     }
     286             : 
     287           0 :     ret = EOK;
     288             : 
     289             : done:
     290           0 :     return ret;
     291             : }
     292             : 
     293             : 
     294             : static int pam_forwarder(struct cli_ctx *cctx, int pam_cmd);
     295             : static void pam_handle_cached_login(struct pam_auth_req *preq, int ret,
     296             :                                     time_t expire_date, time_t delayed_until, bool cached_auth);
     297             : 
     298          45 : static void pam_reply(struct pam_auth_req *preq)
     299             : {
     300             :     struct cli_ctx *cctx;
     301             :     int ret;
     302             :     struct timeval tv;
     303             :     struct tevent_timer *te;
     304             :     struct pam_data *pd;
     305             :     struct pam_ctx *pctx;
     306             :     uint32_t user_info_type;
     307          45 :     time_t exp_date = -1;
     308          45 :     time_t delay_until = -1;
     309             : 
     310          45 :     pd = preq->pd;
     311          45 :     cctx = preq->cctx;
     312          45 :     pctx = talloc_get_type(preq->cctx->rctx->pvt_ctx, struct pam_ctx);
     313             : 
     314          45 :     DEBUG(SSSDBG_FUNC_DATA,
     315             :           "pam_reply called with result [%d]: %s.\n",
     316             :           pd->pam_status, pam_strerror(NULL, pd->pam_status));
     317          45 :     if (pd->pam_status == PAM_AUTHINFO_UNAVAIL || preq->use_cached_auth) {
     318             : 
     319          14 :         switch(pd->cmd) {
     320             :         case SSS_PAM_AUTHENTICATE:
     321          24 :             if ((preq->domain != NULL) &&
     322          24 :                 (preq->domain->cache_credentials == true) &&
     323          12 :                 (pd->offline_auth == false)) {
     324          12 :                 const char *password = NULL;
     325             :                 bool use_cached_auth;
     326             : 
     327             :                 /* backup value of preq->use_cached_auth*/
     328          12 :                 use_cached_auth = preq->use_cached_auth;
     329             :                 /* set to false to avoid entering this branch when pam_reply()
     330             :                  * is recursively called from pam_handle_cached_login() */
     331          12 :                 preq->use_cached_auth = false;
     332             : 
     333             :                 /* do auth with offline credentials */
     334          12 :                 pd->offline_auth = true;
     335             : 
     336          12 :                 if (preq->domain->sysdb == NULL) {
     337           0 :                     DEBUG(SSSDBG_FATAL_FAILURE,
     338             :                           "Fatal: Sysdb CTX not found for domain"
     339             :                               " [%s]!\n", preq->domain->name);
     340           0 :                     goto done;
     341             :                 }
     342             : 
     343          12 :                 ret = get_password_for_cache_auth(pd->authtok, &password);
     344          12 :                 if (ret != EOK) {
     345           0 :                     DEBUG(SSSDBG_FATAL_FAILURE,
     346             :                           "get_password_and_type_for_cache_auth failed.\n");
     347           0 :                     goto done;
     348             :                 }
     349             : 
     350          24 :                 ret = sysdb_cache_auth(preq->domain,
     351          12 :                                        pd->user, password,
     352          12 :                                        pctx->rctx->cdb, false,
     353             :                                        &exp_date, &delay_until);
     354             : 
     355          12 :                 pam_handle_cached_login(preq, ret, exp_date, delay_until,
     356             :                                         use_cached_auth);
     357          12 :                 return;
     358             :             }
     359           0 :             break;
     360             :         case SSS_PAM_CHAUTHTOK_PRELIM:
     361             :         case SSS_PAM_CHAUTHTOK:
     362           2 :             DEBUG(SSSDBG_FUNC_DATA,
     363             :                   "Password change not possible while offline.\n");
     364           2 :             pd->pam_status = PAM_AUTHTOK_ERR;
     365           2 :             user_info_type = SSS_PAM_USER_INFO_OFFLINE_CHPASS;
     366           2 :             ret = pam_add_response(pd, SSS_PAM_USER_INFO, sizeof(uint32_t),
     367             :                                    (const uint8_t *) &user_info_type);
     368           2 :             if (ret != EOK) {
     369           0 :                 DEBUG(SSSDBG_CRIT_FAILURE, "pam_add_response failed.\n");
     370           0 :                 goto done;
     371             :             }
     372           2 :             break;
     373             : /* TODO: we need the pam session cookie here to make sure that cached
     374             :  * authentication was successful */
     375             :         case SSS_PAM_SETCRED:
     376             :         case SSS_PAM_ACCT_MGMT:
     377             :         case SSS_PAM_OPEN_SESSION:
     378             :         case SSS_PAM_CLOSE_SESSION:
     379           0 :             DEBUG(SSSDBG_OP_FAILURE,
     380             :                   "Assuming offline authentication setting status for "
     381             :                       "pam call %d to PAM_SUCCESS.\n", pd->cmd);
     382           0 :             pd->pam_status = PAM_SUCCESS;
     383           0 :             break;
     384             :         default:
     385           0 :             DEBUG(SSSDBG_CRIT_FAILURE, "Unknown PAM call [%d].\n", pd->cmd);
     386           0 :             pd->pam_status = PAM_MODULE_UNKNOWN;
     387             :         }
     388             :     }
     389             : 
     390          33 :     if (pd->pam_status == PAM_SUCCESS && pd->cmd == SSS_PAM_CHAUTHTOK) {
     391           1 :         ret = pam_null_last_online_auth_with_curr_token(preq->domain,
     392           1 :                                                         pd->user);
     393           1 :         if (ret != EOK) {
     394           0 :             DEBUG(SSSDBG_CRIT_FAILURE,
     395             :                   "sysdb_null_last_online_auth_with_curr_token failed: "
     396             :                   "%s [%d].\n", sss_strerror(ret), ret);
     397           0 :             goto done;
     398             :         }
     399             :     }
     400             : 
     401          33 :     if (pd->response_delay > 0) {
     402           0 :         ret = gettimeofday(&tv, NULL);
     403           0 :         if (ret != EOK) {
     404           0 :             DEBUG(SSSDBG_CRIT_FAILURE, "gettimeofday failed [%d][%s].\n",
     405             :                   errno, strerror(errno));
     406           0 :             goto done;
     407             :         }
     408           0 :         tv.tv_sec += pd->response_delay;
     409           0 :         tv.tv_usec = 0;
     410           0 :         pd->response_delay = 0;
     411             : 
     412           0 :         te = tevent_add_timer(cctx->ev, cctx, tv, pam_reply_delay, preq);
     413           0 :         if (te == NULL) {
     414           0 :             DEBUG(SSSDBG_CRIT_FAILURE,
     415             :                   "Failed to add event pam_reply_delay.\n");
     416           0 :             goto done;
     417             :         }
     418             : 
     419           0 :         return;
     420             :     }
     421             : 
     422             :     /* If this was a successful login, save the lastLogin time */
     423          49 :     if (pd->cmd == SSS_PAM_AUTHENTICATE &&
     424          25 :         pd->pam_status == PAM_SUCCESS &&
     425          18 :         preq->domain->cache_credentials &&
     426          13 :         !pd->offline_auth &&
     427           6 :         !pd->last_auth_saved &&
     428           4 :         NEED_CHECK_PROVIDER(preq->domain->provider)) {
     429           2 :         ret = set_last_login(preq);
     430           2 :         if (ret != EOK) {
     431           0 :             goto done;
     432             :         }
     433           2 :         return;
     434             :     }
     435             : 
     436          31 :     ret = add_warning_about_expiration(pd, pctx->rctx->cdb);
     437          31 :     if (ret != EOK) {
     438           0 :         DEBUG(SSSDBG_MINOR_FAILURE, "warn_about_expiration failed: %d:[%s]\n",
     439             :               ret, sss_strerror(ret));
     440           0 :         goto done;
     441             :     }
     442             : 
     443          31 :     ret = filter_responses(pctx->rctx->cdb, pd->resp_list);
     444          31 :     if (ret != EOK) {
     445           0 :         DEBUG(SSSDBG_CRIT_FAILURE, "filter_responses failed, not fatal.\n");
     446             :     }
     447             : 
     448          31 :     if (pd->domain != NULL) {
     449          28 :         ret = pam_add_response(pd, SSS_PAM_DOMAIN_NAME, strlen(pd->domain)+1,
     450          28 :                                (uint8_t *) pd->domain);
     451          28 :         if (ret != EOK) {
     452           0 :             DEBUG(SSSDBG_CRIT_FAILURE, "pam_add_response failed.\n");
     453           0 :             goto done;
     454             :         }
     455             :     }
     456             : 
     457          31 :     ret = pamsrv_reply_packet(cctx->creq, pd, sss_packet_get_cmd(cctx->creq->in),
     458          31 :                               &cctx->creq->out);
     459          31 :     if (ret != EOK) {
     460           0 :         goto done;
     461             :     }
     462             : 
     463             : done:
     464          31 :     sss_cmd_done(cctx, preq);
     465             : }
     466             : 
     467             : static void pam_dom_forwarder(struct pam_auth_req *preq);
     468             : 
     469          12 : static void pam_handle_cached_login(struct pam_auth_req *preq, int ret,
     470             :                                     time_t expire_date, time_t delayed_until,
     471             :                                     bool use_cached_auth)
     472             : {
     473             :     uint32_t resp_type;
     474             :     size_t resp_len;
     475             :     uint8_t *resp;
     476             :     int64_t dummy;
     477             : 
     478          12 :     preq->pd->pam_status = cached_login_pam_status(ret);
     479             : 
     480          12 :     switch (preq->pd->pam_status) {
     481             :         case PAM_SUCCESS:
     482           5 :             resp_type = SSS_PAM_USER_INFO_OFFLINE_AUTH;
     483           5 :             resp_len = sizeof(uint32_t) + sizeof(int64_t);
     484           5 :             resp = talloc_size(preq->pd, resp_len);
     485           5 :             if (resp == NULL) {
     486           0 :                 DEBUG(SSSDBG_CRIT_FAILURE,
     487             :                       "talloc_size failed, cannot prepare user info.\n");
     488             :             } else {
     489           5 :                 memcpy(resp, &resp_type, sizeof(uint32_t));
     490           5 :                 dummy = (int64_t) expire_date;
     491           5 :                 memcpy(resp+sizeof(uint32_t), &dummy, sizeof(int64_t));
     492           5 :                 ret = pam_add_response(preq->pd, SSS_PAM_USER_INFO, resp_len,
     493             :                                        (const uint8_t *) resp);
     494           5 :                 if (ret != EOK) {
     495           0 :                     DEBUG(SSSDBG_CRIT_FAILURE, "pam_add_response failed.\n");
     496             :                 }
     497             :             }
     498           5 :             break;
     499             :         case PAM_PERM_DENIED:
     500           1 :             if (delayed_until >= 0) {
     501           0 :                 resp_type = SSS_PAM_USER_INFO_OFFLINE_AUTH_DELAYED;
     502           0 :                 resp_len = sizeof(uint32_t) + sizeof(int64_t);
     503           0 :                 resp = talloc_size(preq->pd, resp_len);
     504           0 :                 if (resp == NULL) {
     505           0 :                     DEBUG(SSSDBG_CRIT_FAILURE,
     506             :                           "talloc_size failed, cannot prepare user info.\n");
     507             :                 } else {
     508           0 :                     memcpy(resp, &resp_type, sizeof(uint32_t));
     509           0 :                     dummy = (int64_t) delayed_until;
     510           0 :                     memcpy(resp+sizeof(uint32_t), &dummy, sizeof(int64_t));
     511           0 :                     ret = pam_add_response(preq->pd, SSS_PAM_USER_INFO, resp_len,
     512             :                                            (const uint8_t *) resp);
     513           0 :                     if (ret != EOK) {
     514           0 :                         DEBUG(SSSDBG_CRIT_FAILURE,
     515             :                               "pam_add_response failed.\n");
     516             :                     }
     517             :                 }
     518             :             }
     519           1 :             break;
     520             :         case PAM_AUTH_ERR:
     521             :             /* Was this attempt to authenticate from cache? */
     522           6 :             if (use_cached_auth) {
     523             :                 /* Don't try cached authentication again, try online check. */
     524           0 :                 DEBUG(SSSDBG_FUNC_DATA,
     525             :                       "Cached authentication failed for: %s\n",
     526             :                       preq->pd->user);
     527           0 :                 preq->cached_auth_failed = true;
     528           0 :                 pam_dom_forwarder(preq);
     529           0 :                 return;
     530             :             }
     531           6 :             break;
     532             :         default:
     533           0 :             DEBUG(SSSDBG_TRACE_LIBS,
     534             :                   "cached login returned: %d\n", preq->pd->pam_status);
     535             :     }
     536             : 
     537          12 :     pam_reply(preq);
     538          12 :     return;
     539             : }
     540             : 
     541             : static void pam_forwarder_cb(struct tevent_req *req);
     542             : static void pam_forwarder_cert_cb(struct tevent_req *req);
     543             : static void pam_check_user_dp_callback(uint16_t err_maj, uint32_t err_min,
     544             :                                        const char *err_msg, void *ptr);
     545             : static int pam_check_user_search(struct pam_auth_req *preq);
     546             : static int pam_check_user_done(struct pam_auth_req *preq, int ret);
     547             : 
     548             : /* TODO: we should probably return some sort of cookie that is set in the
     549             :  * PAM_ENVIRONMENT, so that we can save performing some calls and cache
     550             :  * data. */
     551             : 
     552             : 
     553          31 : static int pam_auth_req_destructor(struct pam_auth_req *preq)
     554             : {
     555          31 :     if (preq && preq->dpreq_spy) {
     556             :         /* If there is still a request pending, tell the spy
     557             :          * the client is going away
     558             :          */
     559           0 :         preq->dpreq_spy->preq = NULL;
     560             :     }
     561          31 :     return 0;
     562             : }
     563             : 
     564          31 : static bool is_uid_trusted(uint32_t uid,
     565             :                            size_t trusted_uids_count,
     566             :                            uid_t *trusted_uids)
     567             : {
     568             :     size_t i;
     569             : 
     570             :     /* root is always trusted */
     571          31 :     if (uid == 0) {
     572          31 :         return true;
     573             :     }
     574             : 
     575             :     /* All uids are allowed */
     576           0 :     if (trusted_uids_count == 0) {
     577           0 :         return true;
     578             :     }
     579             : 
     580           0 :     for(i = 0; i < trusted_uids_count; i++) {
     581           0 :         if (trusted_uids[i] == uid) {
     582           0 :             return true;
     583             :         }
     584             :     }
     585             : 
     586           0 :     return false;
     587             : }
     588             : 
     589           0 : static bool is_domain_public(char *name,
     590             :                              char **public_dom_names,
     591             :                              size_t public_dom_names_count)
     592             : {
     593             :     size_t i;
     594             : 
     595           0 :     for(i=0; i < public_dom_names_count; i++) {
     596           0 :         if (strcasecmp(name, public_dom_names[i]) == 0) {
     597           0 :             return true;
     598             :         }
     599             :     }
     600           0 :     return false;
     601             : }
     602             : 
     603           8 : static errno_t check_cert(TALLOC_CTX *mctx,
     604             :                           struct tevent_context *ev,
     605             :                           struct pam_ctx *pctx,
     606             :                           struct pam_auth_req *preq,
     607             :                           struct pam_data *pd)
     608             : {
     609             :     int p11_child_timeout;
     610           8 :     const int P11_CHILD_TIMEOUT_DEFAULT = 10;
     611             :     errno_t ret;
     612             :     struct tevent_req *req;
     613             : 
     614           8 :     ret = confdb_get_int(pctx->rctx->cdb, CONFDB_PAM_CONF_ENTRY,
     615             :                          CONFDB_PAM_P11_CHILD_TIMEOUT,
     616             :                          P11_CHILD_TIMEOUT_DEFAULT,
     617             :                          &p11_child_timeout);
     618           8 :     if (ret != EOK) {
     619           0 :         DEBUG(SSSDBG_CRIT_FAILURE,
     620             :               "Failed to read p11_child_timeout from confdb: [%d]: %s\n",
     621             :               ret, sss_strerror(ret));
     622           0 :         return ret;
     623             :     }
     624             : 
     625          16 :     req = pam_check_cert_send(mctx, ev, pctx->p11_child_debug_fd,
     626           8 :                               pctx->nss_db, p11_child_timeout, pd);
     627           8 :     if (req == NULL) {
     628           0 :         DEBUG(SSSDBG_OP_FAILURE, "pam_check_cert_send failed.\n");
     629           0 :         return ENOMEM;
     630             :     }
     631             : 
     632           8 :     tevent_req_set_callback(req, pam_forwarder_cert_cb, preq);
     633           8 :     return EAGAIN;
     634             : }
     635             : 
     636          31 : static int pam_forwarder(struct cli_ctx *cctx, int pam_cmd)
     637             : {
     638             :     struct sss_domain_info *dom;
     639             :     struct pam_auth_req *preq;
     640             :     struct pam_data *pd;
     641             :     int ret;
     642             :     errno_t ncret;
     643          31 :     struct pam_ctx *pctx =
     644          31 :             talloc_get_type(cctx->rctx->pvt_ctx, struct pam_ctx);
     645             :     struct tevent_req *req;
     646             : 
     647          31 :     preq = talloc_zero(cctx, struct pam_auth_req);
     648          31 :     if (!preq) {
     649           0 :         return ENOMEM;
     650             :     }
     651          31 :     talloc_set_destructor(preq, pam_auth_req_destructor);
     652          31 :     preq->cctx = cctx;
     653             : 
     654          31 :     preq->pd = create_pam_data(preq);
     655          31 :     if (!preq->pd) {
     656           0 :         talloc_free(preq);
     657           0 :         return ENOMEM;
     658             :     }
     659          31 :     pd = preq->pd;
     660             : 
     661          31 :     preq->is_uid_trusted = is_uid_trusted(cctx->client_euid,
     662             :                                           pctx->trusted_uids_count,
     663             :                                           pctx->trusted_uids);
     664             : 
     665          31 :     if (!preq->is_uid_trusted) {
     666           0 :         DEBUG(SSSDBG_MINOR_FAILURE, "uid %"PRIu32" is not trusted.\n",
     667             :               cctx->client_euid);
     668             :     }
     669             : 
     670             : 
     671          31 :     pd->cmd = pam_cmd;
     672          31 :     pd->priv = cctx->priv;
     673             : 
     674          31 :     ret = pam_forwarder_parse_data(cctx, pd);
     675          31 :     if (ret == EAGAIN) {
     676           0 :         req = sss_dp_get_domains_send(cctx->rctx, cctx->rctx, true, pd->domain);
     677           0 :         if (req == NULL) {
     678           0 :             ret = ENOMEM;
     679             :         } else {
     680           0 :             tevent_req_set_callback(req, pam_forwarder_cb, preq);
     681           0 :             ret = EAGAIN;
     682             :         }
     683           0 :         goto done;
     684          31 :     } else if (ret != EOK) {
     685           1 :         goto done;
     686             :     }
     687             : 
     688          30 :     if (pd->user != NULL) {
     689             :         /* now check user is valid */
     690          27 :         if (pd->domain) {
     691           0 :             preq->domain = responder_get_domain(cctx->rctx, pd->domain);
     692           0 :             if (!preq->domain) {
     693           0 :                 ret = ENOENT;
     694           0 :                 goto done;
     695             :             }
     696             : 
     697           0 :             ncret = sss_ncache_check_user(pctx->ncache, pctx->neg_timeout,
     698           0 :                                           preq->domain, pd->user);
     699           0 :             if (ncret == EEXIST) {
     700             :                 /* User found in the negative cache */
     701           0 :                 ret = ENOENT;
     702           0 :                 goto done;
     703             :             }
     704             :         } else {
     705          54 :             for (dom = preq->cctx->rctx->domains;
     706             :                  dom;
     707           0 :                  dom = get_next_domain(dom, false)) {
     708          27 :                 if (dom->fqnames) continue;
     709             : 
     710          27 :                 ncret = sss_ncache_check_user(pctx->ncache, pctx->neg_timeout,
     711          27 :                                               dom, pd->user);
     712          27 :                 if (ncret == ENOENT) {
     713             :                     /* User not found in the negative cache
     714             :                      * Proceed with PAM actions
     715             :                      */
     716          27 :                     break;
     717             :                 }
     718             : 
     719             :                 /* Try the next domain */
     720           0 :                 DEBUG(SSSDBG_TRACE_FUNC,
     721             :                       "User [%s@%s] filtered out (negative cache). "
     722             :                        "Trying next domain.\n", pd->user, dom->name);
     723             :             }
     724             : 
     725          27 :             if (!dom) {
     726           0 :                 ret = ENOENT;
     727           0 :                 goto done;
     728             :             }
     729          27 :             preq->domain = dom;
     730             :         }
     731             :     }
     732             : 
     733             : 
     734          30 :     if (may_do_cert_auth(pctx, pd)) {
     735           8 :         ret = check_cert(cctx, cctx->ev, pctx, preq, pd);
     736             :         /* Finish here */
     737           8 :         goto done;
     738             :     }
     739             : 
     740             : 
     741          22 :     if (preq->domain->provider == NULL) {
     742           0 :         DEBUG(SSSDBG_CRIT_FAILURE,
     743             :               "Domain [%s] has no auth provider.\n", preq->domain->name);
     744           0 :         ret = EINVAL;
     745           0 :         goto done;
     746             :     }
     747             : 
     748          22 :     preq->check_provider = NEED_CHECK_PROVIDER(preq->domain->provider);
     749             : 
     750          22 :     ret = pam_check_user_search(preq);
     751          22 :     if (ret == EOK) {
     752          22 :         pam_dom_forwarder(preq);
     753             :     }
     754             : 
     755             : done:
     756          31 :     return pam_check_user_done(preq, ret);
     757             : }
     758             : 
     759             : static void pam_forwarder_lookup_by_cert_done(struct tevent_req *req);
     760           8 : static void pam_forwarder_cert_cb(struct tevent_req *req)
     761             : {
     762           8 :     struct pam_auth_req *preq = tevent_req_callback_data(req,
     763             :                                                          struct pam_auth_req);
     764           8 :     struct cli_ctx *cctx = preq->cctx;
     765             :     struct pam_data *pd;
     766           8 :     errno_t ret = EOK;
     767             :     char *cert;
     768           8 :     struct pam_ctx *pctx =
     769           8 :             talloc_get_type(preq->cctx->rctx->pvt_ctx, struct pam_ctx);
     770             : 
     771           8 :     ret = pam_check_cert_recv(req, preq, &cert, &preq->token_name);
     772           8 :     talloc_free(req);
     773           8 :     if (ret != EOK) {
     774           0 :         DEBUG(SSSDBG_OP_FAILURE, "get_cert request failed.\n");
     775           0 :         goto done;
     776             :     }
     777             : 
     778           8 :     pd = preq->pd;
     779             : 
     780           8 :     if (cert == NULL) {
     781           2 :         if (pd->logon_name == NULL) {
     782           1 :             DEBUG(SSSDBG_CRIT_FAILURE,
     783             :                   "No certificate found and no logon name given, " \
     784             :                   "authentication not possible.\n");;
     785           1 :             ret = ENOENT;
     786             :         } else {
     787           1 :             if (pd->cmd == SSS_PAM_AUTHENTICATE) {
     788           0 :                 DEBUG(SSSDBG_CRIT_FAILURE,
     789             :                       "No certificate returned, authentication failed.\n");
     790           0 :                 ret = ENOENT;
     791             :             } else {
     792           1 :                 ret = pam_check_user_search(preq);
     793           1 :                 if (ret == EOK) {
     794           1 :                     pam_dom_forwarder(preq);
     795             :                 }
     796             :             }
     797             : 
     798             :         }
     799           2 :         goto done;
     800             :     }
     801             : 
     802             : 
     803           6 :     req = cache_req_user_by_cert_send(preq, cctx->ev, cctx->rctx,
     804             :                                       pctx->ncache, pctx->neg_timeout,
     805             :                                       0, NULL, cert);
     806           6 :     if (req == NULL) {
     807           0 :         DEBUG(SSSDBG_OP_FAILURE, "cache_req_user_by_cert_send failed.\n");
     808           0 :         ret = ENOMEM;
     809           0 :         goto done;
     810             :     }
     811           6 :     tevent_req_set_callback(req, pam_forwarder_lookup_by_cert_done, preq);
     812           6 :     return;
     813             : 
     814             : done:
     815           2 :     pam_check_user_done(preq, ret);
     816             : }
     817             : 
     818           6 : static void pam_forwarder_lookup_by_cert_done(struct tevent_req *req)
     819             : {
     820             :     int ret;
     821             :     struct ldb_result *res;
     822             :     struct sss_domain_info *domain;
     823           6 :     struct pam_auth_req *preq = tevent_req_callback_data(req,
     824             :                                                          struct pam_auth_req);
     825             :     const char *cert_user;
     826             : 
     827             : 
     828           6 :     ret = cache_req_user_by_cert_recv(preq, req, &res, &domain, NULL);
     829           6 :     talloc_zfree(req);
     830           6 :     if (ret != EOK && ret != ENOENT) {
     831           0 :         DEBUG(SSSDBG_OP_FAILURE, "cache_req_user_by_cert request failed.\n");
     832           0 :         goto done;
     833             :     }
     834             : 
     835           6 :     if (ret == EOK && res->count > 1) {
     836           0 :         DEBUG(SSSDBG_CRIT_FAILURE,
     837             :               "Search by certificate returned more than one result.\n");
     838           0 :         ret = EINVAL;
     839           0 :         goto done;
     840             :     }
     841             : 
     842           6 :     if (ret == EOK) {
     843           4 :         if (preq->domain == NULL) {
     844           1 :             preq->domain = domain;
     845             :         }
     846             : 
     847           4 :         preq->cert_user_obj = talloc_steal(preq, res->msgs[0]);
     848             : 
     849           4 :         if (preq->pd->logon_name == NULL) {
     850           1 :             cert_user = ldb_msg_find_attr_as_string(preq->cert_user_obj,
     851             :                                                     SYSDB_NAME, NULL);
     852           1 :             if (cert_user == NULL) {
     853           0 :                 DEBUG(SSSDBG_CRIT_FAILURE,
     854             :                       "Certificate user object has not name.\n");
     855           0 :                 ret = ENOENT;
     856           0 :                 goto done;
     857             :             }
     858             : 
     859           1 :             DEBUG(SSSDBG_FUNC_DATA, "Found certificate user [%s].\n",
     860             :                                     cert_user);
     861             : 
     862           1 :             ret = add_pam_cert_response(preq->pd, cert_user, preq->token_name);
     863           1 :             if (ret != EOK) {
     864           0 :                 DEBUG(SSSDBG_OP_FAILURE, "add_pam_cert_response failed.\n");
     865             :             }
     866             : 
     867           1 :             preq->pd->domain = talloc_strdup(preq->pd, domain->name);
     868           1 :             if (preq->pd->domain == NULL) {
     869           0 :                 DEBUG(SSSDBG_OP_FAILURE, "talloc_strdup failed.\n");
     870           0 :                 ret = ENOMEM;
     871           0 :                 goto done;
     872             :             }
     873           1 :             preq->pd->pam_status = PAM_SUCCESS;
     874           1 :             pam_reply(preq);
     875           1 :             return;
     876             :         }
     877             :     } else {
     878           2 :         if (preq->pd->logon_name == NULL) {
     879           1 :             DEBUG(SSSDBG_CRIT_FAILURE,
     880             :                   "Missing logon name and no certificate user found.\n");
     881           1 :             ret = ENOENT;
     882           1 :             goto done;
     883             :         }
     884             :     }
     885             : 
     886           4 :     ret = pam_check_user_search(preq);
     887           4 :     if (ret == EOK) {
     888           4 :         pam_dom_forwarder(preq);
     889             :     }
     890             : 
     891             : done:
     892           5 :     pam_check_user_done(preq, ret);
     893             : }
     894             : 
     895           0 : static void pam_forwarder_cb(struct tevent_req *req)
     896             : {
     897           0 :     struct pam_auth_req *preq = tevent_req_callback_data(req,
     898             :                                                          struct pam_auth_req);
     899           0 :     struct cli_ctx *cctx = preq->cctx;
     900             :     struct pam_data *pd;
     901           0 :     errno_t ret = EOK;
     902           0 :     struct pam_ctx *pctx =
     903           0 :             talloc_get_type(preq->cctx->rctx->pvt_ctx, struct pam_ctx);
     904             : 
     905           0 :     ret = sss_dp_get_domains_recv(req);
     906           0 :     talloc_free(req);
     907           0 :     if (ret != EOK) {
     908           0 :         goto done;
     909             :     }
     910             : 
     911           0 :     pd = preq->pd;
     912             : 
     913           0 :     ret = pam_forwarder_parse_data(cctx, pd);
     914           0 :     if (ret == EAGAIN) {
     915           0 :         if (strchr(preq->pd->logon_name, '@') == NULL) {
     916           0 :             goto done;
     917             :         }
     918             :         /* Assuming Kerberos principal */
     919           0 :         preq->domain = preq->cctx->rctx->domains;
     920           0 :         preq->check_provider = NEED_CHECK_PROVIDER(preq->domain->provider);
     921           0 :         preq->pd->user = talloc_strdup(preq->pd, preq->pd->logon_name);
     922           0 :         if (preq->pd->user == NULL) {
     923           0 :             DEBUG(SSSDBG_OP_FAILURE, "talloc_strdup failed.\n");
     924           0 :             ret = ENOMEM;
     925           0 :             goto done;
     926             :         }
     927           0 :         preq->pd->name_is_upn = true;
     928           0 :         preq->pd->domain = NULL;
     929           0 :     } else if (ret != EOK) {
     930           0 :         ret = EINVAL;
     931           0 :         goto done;
     932             :     }
     933             : 
     934           0 :     if (preq->pd->domain) {
     935           0 :         preq->domain = responder_get_domain(cctx->rctx, preq->pd->domain);
     936           0 :         if (preq->domain == NULL) {
     937           0 :             ret = ENOENT;
     938           0 :             goto done;
     939             :         }
     940             :     }
     941             : 
     942           0 :     if (may_do_cert_auth(pctx, pd)) {
     943           0 :         ret = check_cert(cctx, cctx->ev, pctx, preq, pd);
     944             :         /* Finish here */
     945           0 :         goto done;
     946             :     }
     947             : 
     948           0 :     ret = pam_check_user_search(preq);
     949           0 :     if (ret == EOK) {
     950           0 :         pam_dom_forwarder(preq);
     951             :     }
     952             : 
     953             : done:
     954           0 :     pam_check_user_done(preq, ret);
     955           0 : }
     956             : 
     957             : static void pam_dp_send_acct_req_done(struct tevent_req *req);
     958          27 : static int pam_check_user_search(struct pam_auth_req *preq)
     959             : {
     960          27 :     struct sss_domain_info *dom = preq->domain;
     961          27 :     char *name = NULL;
     962             :     time_t cacheExpire;
     963             :     int ret;
     964             :     struct tevent_req *dpreq;
     965             :     struct dp_callback_ctx *cb_ctx;
     966          27 :     struct pam_ctx *pctx =
     967          27 :             talloc_get_type(preq->cctx->rctx->pvt_ctx, struct pam_ctx);
     968             :     static const char *user_attrs[] = SYSDB_PW_ATTRS;
     969             :     struct ldb_message *msg;
     970             :     struct ldb_result *res;
     971             : 
     972          27 :     while (dom) {
     973             :        /* if it is a domainless search, skip domains that require fully
     974             :         * qualified names instead */
     975          54 :         while (dom && !preq->pd->domain && !preq->pd->name_is_upn
     976          27 :                && dom->fqnames) {
     977           0 :             dom = get_next_domain(dom, false);
     978             :         }
     979             : 
     980          27 :         if (!dom) break;
     981             : 
     982          27 :         if (dom != preq->domain) {
     983             :             /* make sure we reset the check_provider flag when we check
     984             :              * a new domain */
     985           0 :             preq->check_provider = NEED_CHECK_PROVIDER(dom->provider);
     986             :         }
     987             : 
     988             :         /* make sure to update the preq if we changed domain */
     989          27 :         preq->domain = dom;
     990             : 
     991          27 :         talloc_free(name);
     992          27 :         name = sss_get_cased_name(preq, preq->pd->user,
     993          27 :                                   dom->case_sensitive);
     994          27 :         if (!name) {
     995           0 :             return ENOMEM;
     996             :         }
     997             : 
     998          27 :         name = sss_reverse_replace_space(preq, name,
     999          27 :                                          pctx->rctx->override_space);
    1000          27 :         if (name == NULL) {
    1001           0 :             DEBUG(SSSDBG_CRIT_FAILURE,
    1002             :                   "sss_reverse_replace_space failed\n");
    1003           0 :             return ENOMEM;
    1004             :         }
    1005             : 
    1006             :         /* Refresh the user's cache entry on any PAM query
    1007             :          * We put a timeout in the client context so that we limit
    1008             :          * the number of updates within a reasonable timeout
    1009             :          */
    1010          27 :         if (preq->check_provider) {
    1011          22 :             ret = pam_initgr_check_timeout(pctx->id_table,
    1012          22 :                                            preq->pd->logon_name);
    1013          22 :             if (ret != EOK
    1014           0 :                     && ret != ENOENT) {
    1015           0 :                 DEBUG(SSSDBG_OP_FAILURE,
    1016             :                       "Could not look up initgroup timout\n");
    1017           0 :                 return EIO;
    1018          22 :             } else if (ret == ENOENT) {
    1019             :                 /* Call provider first */
    1020           0 :                 break;
    1021             :             }
    1022             :             /* Entry is still valid, get it from the sysdb */
    1023             :         }
    1024             : 
    1025          27 :         DEBUG(SSSDBG_CONF_SETTINGS,
    1026             :               "Requesting info for [%s@%s]\n", name, dom->name);
    1027             : 
    1028          27 :         if (dom->sysdb == NULL) {
    1029           0 :             DEBUG(SSSDBG_FATAL_FAILURE,
    1030             :                   "Fatal: Sysdb CTX not found for this domain!\n");
    1031           0 :             preq->pd->pam_status = PAM_SYSTEM_ERR;
    1032           0 :             return EFAULT;
    1033             :         }
    1034             : 
    1035          27 :         if (preq->pd->name_is_upn) {
    1036           0 :             ret = sysdb_search_user_by_upn(preq, dom, name, user_attrs, &msg);
    1037             :         } else {
    1038          27 :             ret = sysdb_getpwnam_with_views(preq, dom, name, &res);
    1039          27 :             if (res->count > 1) {
    1040           0 :                 DEBUG(SSSDBG_FATAL_FAILURE,
    1041             :                       "getpwnam call returned more than one result !?!\n");
    1042           0 :                 sss_log(SSS_LOG_ERR,
    1043             :                         "More users have the same name [%s@%s] in SSSD cache. "
    1044             :                         "SSSD will not work correctly.\n",
    1045             :                         name, dom->name);
    1046           0 :                 return ENOENT;
    1047          27 :             } else if (res->count == 0) {
    1048           0 :                 ret = ENOENT;
    1049             :             } else {
    1050          27 :                 msg = res->msgs[0];
    1051             :             }
    1052             :         }
    1053          27 :         if (ret != EOK && ret != ENOENT) {
    1054           0 :             DEBUG(SSSDBG_CRIT_FAILURE,
    1055             :                   "Failed to make request to our cache!\n");
    1056           0 :             return EIO;
    1057             :         }
    1058             : 
    1059          27 :         if (ret == ENOENT) {
    1060           0 :             if (preq->check_provider == false) {
    1061             :                 /* set negative cache only if not result of cache check */
    1062           0 :                 ret = sss_ncache_set_user(pctx->ncache, false, dom, name);
    1063           0 :                 if (ret != EOK) {
    1064             :                     /* Should not be fatal, just slower next time */
    1065           0 :                     DEBUG(SSSDBG_MINOR_FAILURE,
    1066             :                            "Cannot set ncache for [%s@%s]\n", name,
    1067             :                             dom->name);
    1068             :                 }
    1069             :             }
    1070             : 
    1071             :             /* if a multidomain search, try with next */
    1072           0 :             if (!preq->pd->domain) {
    1073           0 :                 dom = get_next_domain(dom, false);
    1074           0 :                 continue;
    1075             :             }
    1076             : 
    1077           0 :             DEBUG(SSSDBG_OP_FAILURE, "No results for getpwnam call\n");
    1078             : 
    1079             :             /* TODO: store negative cache ? */
    1080             : 
    1081           0 :             return ENOENT;
    1082             :         }
    1083             : 
    1084             :         /* One result found */
    1085             : 
    1086             :         /* if we need to check the remote account go on */
    1087          27 :         if (preq->check_provider) {
    1088          22 :             cacheExpire = ldb_msg_find_attr_as_uint64(msg,
    1089             :                                                       SYSDB_CACHE_EXPIRE, 0);
    1090          22 :             if (cacheExpire < time(NULL)) {
    1091           0 :                 break;
    1092             :             }
    1093             :         }
    1094             : 
    1095          27 :         DEBUG(SSSDBG_TRACE_FUNC,
    1096             :               "Returning info for user [%s@%s]\n", name, dom->name);
    1097             : 
    1098             :         /* We might have searched by alias. Pass on the primary name */
    1099          27 :         ret = pd_set_primary_name(msg, preq->pd);
    1100          27 :         if (ret != EOK) {
    1101           0 :             DEBUG(SSSDBG_CRIT_FAILURE, "Could not canonicalize username\n");
    1102           0 :             return ret;
    1103             :         }
    1104             : 
    1105          27 :         return EOK;
    1106             :     }
    1107             : 
    1108           0 :     if (!dom) {
    1109             :         /* Ensure that we don't try to check a provider without a domain,
    1110             :          * since this will cause a NULL-dereference below.
    1111             :          */
    1112           0 :         preq->check_provider = false;
    1113             :     }
    1114             : 
    1115           0 :     if (preq->check_provider) {
    1116             : 
    1117             :         /* dont loop forever :-) */
    1118           0 :         preq->check_provider = false;
    1119             : 
    1120           0 :         dpreq = sss_dp_get_account_send(preq, preq->cctx->rctx,
    1121             :                               dom, false, SSS_DP_INITGROUPS, name, 0,
    1122           0 :                               preq->pd->name_is_upn ? EXTRA_NAME_IS_UPN : NULL);
    1123           0 :         if (!dpreq) {
    1124           0 :             DEBUG(SSSDBG_CRIT_FAILURE,
    1125             :                   "Out of memory sending data provider request\n");
    1126           0 :             return ENOMEM;
    1127             :         }
    1128             : 
    1129           0 :         cb_ctx = talloc_zero(preq, struct dp_callback_ctx);
    1130           0 :         if(!cb_ctx) {
    1131           0 :             talloc_zfree(dpreq);
    1132           0 :             return ENOMEM;
    1133             :         }
    1134             : 
    1135           0 :         cb_ctx->callback = pam_check_user_dp_callback;
    1136           0 :         cb_ctx->ptr = preq;
    1137           0 :         cb_ctx->cctx = preq->cctx;
    1138           0 :         cb_ctx->mem_ctx = preq;
    1139             : 
    1140           0 :         tevent_req_set_callback(dpreq, pam_dp_send_acct_req_done, cb_ctx);
    1141             : 
    1142             :         /* tell caller we are in an async call */
    1143           0 :         return EAGAIN;
    1144             :     }
    1145             : 
    1146           0 :     DEBUG(SSSDBG_MINOR_FAILURE,
    1147             :           "No matching domain found for [%s], fail!\n", preq->pd->user);
    1148           0 :     return ENOENT;
    1149             : }
    1150             : 
    1151           0 : static void pam_dp_send_acct_req_done(struct tevent_req *req)
    1152             : {
    1153           0 :     struct dp_callback_ctx *cb_ctx =
    1154           0 :             tevent_req_callback_data(req, struct dp_callback_ctx);
    1155             : 
    1156             :     errno_t ret;
    1157             :     dbus_uint16_t err_maj;
    1158             :     dbus_uint32_t err_min;
    1159             :     char *err_msg;
    1160             : 
    1161           0 :     ret = sss_dp_get_account_recv(cb_ctx->mem_ctx, req,
    1162             :                                   &err_maj, &err_min,
    1163             :                                   &err_msg);
    1164           0 :     talloc_zfree(req);
    1165           0 :     if (ret != EOK) {
    1166           0 :         DEBUG(SSSDBG_CRIT_FAILURE,
    1167             :               "Fatal error, killing connection!\n");
    1168           0 :         talloc_free(cb_ctx->cctx);
    1169           0 :         return;
    1170             :     }
    1171             : 
    1172           0 :     cb_ctx->callback(err_maj, err_min, err_msg, cb_ctx->ptr);
    1173             : }
    1174             : 
    1175          38 : static int pam_check_user_done(struct pam_auth_req *preq, int ret)
    1176             : {
    1177          38 :     switch (ret) {
    1178             :     case EOK:
    1179          27 :         break;
    1180             : 
    1181             :     case EAGAIN:
    1182             :         /* performing async request, just return */
    1183           8 :         break;
    1184             : 
    1185             :     case ENOENT:
    1186           2 :         preq->pd->pam_status = PAM_USER_UNKNOWN;
    1187           2 :         pam_reply(preq);
    1188           2 :         break;
    1189             : 
    1190             :     case ERR_NO_CREDS:
    1191           1 :         preq->pd->pam_status = PAM_CRED_INSUFFICIENT;
    1192           1 :         pam_reply(preq);
    1193           1 :         break;
    1194             : 
    1195             :     default:
    1196           0 :         preq->pd->pam_status = PAM_SYSTEM_ERR;
    1197           0 :         pam_reply(preq);
    1198           0 :         break;
    1199             :     }
    1200             : 
    1201          38 :     return EOK;
    1202             : }
    1203             : 
    1204           0 : static void pam_check_user_dp_callback(uint16_t err_maj, uint32_t err_min,
    1205             :                                        const char *err_msg, void *ptr)
    1206             : {
    1207           0 :     struct pam_auth_req *preq = talloc_get_type(ptr, struct pam_auth_req);
    1208             :     int ret;
    1209           0 :     struct pam_ctx *pctx =
    1210           0 :             talloc_get_type(preq->cctx->rctx->pvt_ctx, struct pam_ctx);
    1211             : 
    1212           0 :     if (err_maj) {
    1213           0 :         DEBUG(SSSDBG_OP_FAILURE,
    1214             :               "Unable to get information from Data Provider\n"
    1215             :                   "Error: %u, %u, %s\n",
    1216             :                   (unsigned int)err_maj, (unsigned int)err_min, err_msg);
    1217             :     }
    1218             : 
    1219           0 :     ret = pam_check_user_search(preq);
    1220           0 :     if (ret == EOK) {
    1221             :         /* Make sure we don't go to the ID provider too often */
    1222           0 :         ret = pam_initgr_cache_set(pctx->rctx->ev, pctx->id_table,
    1223           0 :                                    preq->pd->logon_name, pctx->id_timeout);
    1224           0 :         if (ret != EOK) {
    1225           0 :             DEBUG(SSSDBG_OP_FAILURE,
    1226             :                   "Could not save initgr timestamp. "
    1227             :                    "Proceeding with PAM actions\n");
    1228             :             /* This is non-fatal, we'll just end up going to the
    1229             :              * data provider again next time.
    1230             :              */
    1231             :         }
    1232             : 
    1233           0 :         pam_dom_forwarder(preq);
    1234             :     }
    1235             : 
    1236           0 :     ret = pam_check_user_done(preq, ret);
    1237             : 
    1238           0 :     if (ret) {
    1239           0 :         preq->pd->pam_status = PAM_SYSTEM_ERR;
    1240           0 :         pam_reply(preq);
    1241             :     }
    1242           0 : }
    1243             : 
    1244           0 : static errno_t pam_is_last_online_login_fresh(struct sss_domain_info *domain,
    1245             :                                               const char* user,
    1246             :                                               struct confdb_ctx *cdb,
    1247             :                                               int cached_auth_timeout,
    1248             :                                               bool *_result)
    1249             : {
    1250             :     errno_t ret;
    1251             :     bool result;
    1252             :     uint64_t last_login;
    1253             : 
    1254           0 :     ret = pam_get_last_online_auth_with_curr_token(domain, user, &last_login);
    1255           0 :     if (ret != EOK) {
    1256           0 :         DEBUG(SSSDBG_MINOR_FAILURE,
    1257             :               "sysdb_get_last_online_auth_with_curr_token failed: %s:[%d]\n",
    1258             :               sss_strerror(ret), ret);
    1259           0 :         goto done;
    1260             :     }
    1261             : 
    1262           0 :     result = time(NULL) < (last_login + cached_auth_timeout);
    1263           0 :     ret = EOK;
    1264             : 
    1265             : done:
    1266           0 :     if (ret == EOK) {
    1267           0 :         *_result = result;
    1268             :     }
    1269           0 :     return ret;
    1270             : }
    1271             : 
    1272           0 : static bool pam_is_cmd_cachable(int cmd)
    1273             : {
    1274             :     bool is_cachable;
    1275             : 
    1276           0 :     switch(cmd) {
    1277             :     case SSS_PAM_AUTHENTICATE:
    1278           0 :         is_cachable = true;
    1279           0 :         break;
    1280             :     default:
    1281           0 :         is_cachable = false;
    1282             :     }
    1283             : 
    1284           0 :     return is_cachable;
    1285             : }
    1286             : 
    1287           0 : static bool pam_is_authtok_cachable(struct sss_auth_token *authtok)
    1288             : {
    1289             :     enum sss_authtok_type type;
    1290           0 :     bool cachable = false;
    1291             : 
    1292           0 :     type = sss_authtok_get_type(authtok);
    1293           0 :     if (type == SSS_AUTHTOK_TYPE_PASSWORD) {
    1294           0 :         cachable = true;
    1295             :     } else {
    1296           0 :         DEBUG(SSSDBG_TRACE_LIBS, "Authentication token can't be cached\n");
    1297             :     }
    1298             : 
    1299           0 :     return cachable;
    1300             : }
    1301             : 
    1302          27 : static bool pam_can_user_cache_auth(struct confdb_ctx *cdb,
    1303             :                                     struct sss_domain_info *domain,
    1304             :                                     int pam_cmd,
    1305             :                                     struct sss_auth_token *authtok,
    1306             :                                     const char* user,
    1307             :                                     bool cached_auth_failed)
    1308             : {
    1309             :     errno_t ret;
    1310          27 :     bool result = false;
    1311             : 
    1312          27 :     if (!cached_auth_failed /* don't try cached auth again */
    1313          27 :             && domain->cache_credentials
    1314          27 :             && domain->cached_auth_timeout > 0
    1315           0 :             && pam_is_authtok_cachable(authtok)
    1316           0 :             && pam_is_cmd_cachable(pam_cmd)) {
    1317             : 
    1318           0 :         ret = pam_is_last_online_login_fresh(domain, user, cdb,
    1319           0 :                                              domain->cached_auth_timeout,
    1320             :                                              &result);
    1321           0 :         if (ret != EOK) {
    1322             :             /* non-critical, consider fail as 'non-fresh value' */
    1323           0 :             DEBUG(SSSDBG_MINOR_FAILURE,
    1324             :                   "pam_is_last_online_login_fresh failed: %s:[%d]\n",
    1325             :                   sss_strerror(ret), ret);
    1326             :         }
    1327             :     }
    1328             : 
    1329          27 :     return result;
    1330             : }
    1331             : 
    1332          27 : static void pam_dom_forwarder(struct pam_auth_req *preq)
    1333             : {
    1334             :     int ret;
    1335          27 :     struct pam_ctx *pctx =
    1336          27 :             talloc_get_type(preq->cctx->rctx->pvt_ctx, struct pam_ctx);
    1337             :     const char *cert_user;
    1338             : 
    1339          27 :     if (!preq->pd->domain) {
    1340          27 :         preq->pd->domain = preq->domain->name;
    1341             :     }
    1342             : 
    1343             :     /* Untrusted users can access only public domains. */
    1344          27 :     if (!preq->is_uid_trusted &&
    1345           0 :             !is_domain_public(preq->pd->domain, pctx->public_domains,
    1346           0 :                             pctx->public_domains_count)) {
    1347           0 :         DEBUG(SSSDBG_MINOR_FAILURE,
    1348             :                 "Untrusted user %"PRIu32" cannot access non-public domain %s.\n",
    1349             :                 preq->cctx->client_euid, preq->pd->domain);
    1350           0 :         preq->pd->pam_status = PAM_PERM_DENIED;
    1351           0 :         pam_reply(preq);
    1352           0 :         return;
    1353             :     }
    1354             : 
    1355             :     /* skip this domain if not requested and the user is trusted
    1356             :      * as untrusted users can't request a domain */
    1357          54 :     if (preq->is_uid_trusted &&
    1358          27 :             !is_domain_requested(preq->pd, preq->pd->domain)) {
    1359           0 :         preq->pd->pam_status = PAM_USER_UNKNOWN;
    1360           0 :         pam_reply(preq);
    1361           0 :         return;
    1362             :     }
    1363             : 
    1364          81 :     if (pam_can_user_cache_auth(pctx->rctx->cdb,
    1365             :                                 preq->domain,
    1366          27 :                                 preq->pd->cmd,
    1367          27 :                                 preq->pd->authtok,
    1368          27 :                                 preq->pd->user,
    1369          27 :                                 preq->cached_auth_failed)) {
    1370           0 :         preq->use_cached_auth = true;
    1371           0 :         pam_reply(preq);
    1372           0 :         return;
    1373             :     }
    1374             : 
    1375          27 :     if (may_do_cert_auth(pctx, preq->pd) && preq->cert_user_obj != NULL) {
    1376             :         /* Check if user matches certificate user */
    1377           3 :         cert_user = ldb_msg_find_attr_as_string(preq->cert_user_obj, SYSDB_NAME,
    1378             :                                                 NULL);
    1379           3 :         if (cert_user == NULL) {
    1380           0 :             DEBUG(SSSDBG_CRIT_FAILURE,
    1381             :                   "Certificate user object has not name.\n");
    1382           0 :             preq->pd->pam_status = PAM_USER_UNKNOWN;
    1383           0 :             pam_reply(preq);
    1384           0 :             return;
    1385             :         }
    1386             : 
    1387             :         /* pam_check_user_search() calls pd_set_primary_name() is the search
    1388             :          * was successful, so pd->user contains the canonical name as well */
    1389           3 :         if (strcmp(cert_user, preq->pd->user) == 0) {
    1390             : 
    1391           2 :             preq->pd->pam_status = PAM_SUCCESS;
    1392             : 
    1393           2 :             if (preq->pd->cmd == SSS_PAM_PREAUTH) {
    1394           1 :                 ret = add_pam_cert_response(preq->pd, cert_user,
    1395           1 :                                             preq->token_name);
    1396           1 :                 if (ret != EOK) {
    1397           0 :                     DEBUG(SSSDBG_OP_FAILURE, "add_pam_cert_response failed.\n");
    1398           0 :                     preq->pd->pam_status = PAM_AUTHINFO_UNAVAIL;
    1399             :                 }
    1400             :             }
    1401             : 
    1402           2 :             preq->callback = pam_reply;
    1403           2 :             pam_reply(preq);
    1404           2 :             return;
    1405             :         } else {
    1406           1 :             if (preq->pd->cmd == SSS_PAM_PREAUTH) {
    1407           1 :                 DEBUG(SSSDBG_TRACE_FUNC,
    1408             :                       "User and certificate user do not match, " \
    1409             :                       "continue with other authentication methods.\n");
    1410             :             } else {
    1411           0 :                 DEBUG(SSSDBG_CRIT_FAILURE,
    1412             :                       "User and certificate user do not match.\n");
    1413           0 :                 preq->pd->pam_status = PAM_AUTH_ERR;
    1414           0 :                 pam_reply(preq);
    1415           0 :                 return;
    1416             :             }
    1417             :         }
    1418             :     }
    1419             : 
    1420          25 :     if (!NEED_CHECK_PROVIDER(preq->domain->provider) ) {
    1421           0 :         preq->callback = pam_reply;
    1422           0 :         ret = LOCAL_pam_handler(preq);
    1423             :     }
    1424             :     else {
    1425          25 :         preq->callback = pam_reply;
    1426          25 :         ret = pam_dp_send_req(preq, SSS_CLI_SOCKET_TIMEOUT/2);
    1427          25 :         DEBUG(SSSDBG_CONF_SETTINGS, "pam_dp_send_req returned %d\n", ret);
    1428             :     }
    1429             : 
    1430          25 :     if (ret != EOK) {
    1431           0 :         preq->pd->pam_status = PAM_SYSTEM_ERR;
    1432           0 :         pam_reply(preq);
    1433             :     }
    1434             : }
    1435             : 
    1436          14 : static int pam_cmd_authenticate(struct cli_ctx *cctx) {
    1437          14 :     DEBUG(SSSDBG_CONF_SETTINGS, "entering pam_cmd_authenticate\n");
    1438          14 :     return pam_forwarder(cctx, SSS_PAM_AUTHENTICATE);
    1439             : }
    1440             : 
    1441           1 : static int pam_cmd_setcred(struct cli_ctx *cctx) {
    1442           1 :     DEBUG(SSSDBG_CONF_SETTINGS, "entering pam_cmd_setcred\n");
    1443           1 :     return pam_forwarder(cctx, SSS_PAM_SETCRED);
    1444             : }
    1445             : 
    1446           1 : static int pam_cmd_acct_mgmt(struct cli_ctx *cctx) {
    1447           1 :     DEBUG(SSSDBG_CONF_SETTINGS, "entering pam_cmd_acct_mgmt\n");
    1448           1 :     return pam_forwarder(cctx, SSS_PAM_ACCT_MGMT);
    1449             : }
    1450             : 
    1451           1 : static int pam_cmd_open_session(struct cli_ctx *cctx) {
    1452           1 :     DEBUG(SSSDBG_CONF_SETTINGS, "entering pam_cmd_open_session\n");
    1453           1 :     return pam_forwarder(cctx, SSS_PAM_OPEN_SESSION);
    1454             : }
    1455             : 
    1456           1 : static int pam_cmd_close_session(struct cli_ctx *cctx) {
    1457           1 :     DEBUG(SSSDBG_CONF_SETTINGS, "entering pam_cmd_close_session\n");
    1458           1 :     return pam_forwarder(cctx, SSS_PAM_CLOSE_SESSION);
    1459             : }
    1460             : 
    1461           2 : static int pam_cmd_chauthtok(struct cli_ctx *cctx) {
    1462           2 :     DEBUG(SSSDBG_CONF_SETTINGS, "entering pam_cmd_chauthtok\n");
    1463           2 :     return pam_forwarder(cctx, SSS_PAM_CHAUTHTOK);
    1464             : }
    1465             : 
    1466           2 : static int pam_cmd_chauthtok_prelim(struct cli_ctx *cctx) {
    1467           2 :     DEBUG(SSSDBG_CONF_SETTINGS, "entering pam_cmd_chauthtok_prelim\n");
    1468           2 :     return pam_forwarder(cctx, SSS_PAM_CHAUTHTOK_PRELIM);
    1469             : }
    1470             : 
    1471           9 : static int pam_cmd_preauth(struct cli_ctx *cctx)
    1472             : {
    1473           9 :     DEBUG(SSSDBG_CONF_SETTINGS, "entering pam_cmd_preauth\n");
    1474           9 :     return pam_forwarder(cctx, SSS_PAM_PREAUTH);
    1475             : }
    1476             : 
    1477          31 : struct cli_protocol_version *register_cli_protocol_version(void)
    1478             : {
    1479             :     static struct cli_protocol_version pam_cli_protocol_version[] = {
    1480             :         {3, "2009-09-14", "make cli_pid mandatory"},
    1481             :         {2, "2009-05-12", "new format <type><size><data>"},
    1482             :         {1, "2008-09-05", "initial version, \\0 terminated strings"},
    1483             :         {0, NULL, NULL}
    1484             :     };
    1485             : 
    1486          31 :     return pam_cli_protocol_version;
    1487             : }
    1488             : 
    1489          31 : struct sss_cmd_table *get_pam_cmds(void)
    1490             : {
    1491             :     static struct sss_cmd_table sss_cmds[] = {
    1492             :         {SSS_GET_VERSION, sss_cmd_get_version},
    1493             :         {SSS_PAM_AUTHENTICATE, pam_cmd_authenticate},
    1494             :         {SSS_PAM_SETCRED, pam_cmd_setcred},
    1495             :         {SSS_PAM_ACCT_MGMT, pam_cmd_acct_mgmt},
    1496             :         {SSS_PAM_OPEN_SESSION, pam_cmd_open_session},
    1497             :         {SSS_PAM_CLOSE_SESSION, pam_cmd_close_session},
    1498             :         {SSS_PAM_CHAUTHTOK, pam_cmd_chauthtok},
    1499             :         {SSS_PAM_CHAUTHTOK_PRELIM, pam_cmd_chauthtok_prelim},
    1500             :         {SSS_PAM_PREAUTH, pam_cmd_preauth},
    1501             :         {SSS_CLI_NULL, NULL}
    1502             :     };
    1503             : 
    1504          31 :     return sss_cmds;
    1505             : }
    1506             : 
    1507             : static errno_t
    1508           1 : pam_set_last_online_auth_with_curr_token(struct sss_domain_info *domain,
    1509             :                                          const char *username,
    1510             :                                          uint64_t value)
    1511             : {
    1512             :     TALLOC_CTX *tmp_ctx;
    1513             :     struct sysdb_attrs *attrs;
    1514             :     int ret;
    1515             : 
    1516           1 :     tmp_ctx = talloc_new(NULL);
    1517           1 :     if (tmp_ctx == NULL) {
    1518           0 :         ret = ENOMEM;
    1519           0 :         goto done;
    1520             :     }
    1521             : 
    1522           1 :     attrs = sysdb_new_attrs(tmp_ctx);
    1523           1 :     if (attrs == NULL) {
    1524           0 :         ret = ENOMEM;
    1525           0 :         goto done;
    1526             :     }
    1527             : 
    1528           1 :     ret = sysdb_attrs_add_time_t(attrs,
    1529             :                                  SYSDB_LAST_ONLINE_AUTH_WITH_CURR_TOKEN,
    1530             :                                  value);
    1531           1 :     if (ret != EOK) { goto done; }
    1532             : 
    1533           1 :     ret = sysdb_set_user_attr(domain, username, attrs, SYSDB_MOD_REP);
    1534           1 :     if (ret != EOK) { goto done; }
    1535             : 
    1536             : done:
    1537           1 :     if (ret != EOK) {
    1538           0 :         DEBUG(SSSDBG_TRACE_FUNC, "Error: %d (%s)\n", ret, sss_strerror(ret));
    1539             :     }
    1540             : 
    1541           1 :     talloc_zfree(tmp_ctx);
    1542           1 :     return ret;
    1543             : }
    1544             : 
    1545             : static errno_t
    1546           1 : pam_null_last_online_auth_with_curr_token(struct sss_domain_info *domain,
    1547             :                                           const char *username)
    1548             : {
    1549           1 :     return pam_set_last_online_auth_with_curr_token(domain, username, 0);
    1550             : }
    1551             : 
    1552             : static errno_t
    1553           0 : pam_get_last_online_auth_with_curr_token(struct sss_domain_info *domain,
    1554             :                                          const char *name,
    1555             :                                          uint64_t *_value)
    1556             : {
    1557           0 :     TALLOC_CTX *tmp_ctx = NULL;
    1558           0 :     const char *attrs[] = { SYSDB_LAST_ONLINE_AUTH_WITH_CURR_TOKEN, NULL };
    1559             :     struct ldb_message *ldb_msg;
    1560             :     uint64_t value;
    1561             :     errno_t ret;
    1562             : 
    1563           0 :     if (name == NULL || *name == '\0') {
    1564           0 :         DEBUG(SSSDBG_CRIT_FAILURE, "Missing user name.\n");
    1565           0 :         ret = EINVAL;
    1566           0 :         goto done;
    1567             :     }
    1568             : 
    1569           0 :     if (domain->sysdb == NULL) {
    1570           0 :         DEBUG(SSSDBG_CRIT_FAILURE, "Missing sysdb db context.\n");
    1571           0 :         ret = EINVAL;
    1572           0 :         goto done;
    1573             :     }
    1574             : 
    1575           0 :     tmp_ctx = talloc_new(NULL);
    1576           0 :     if (tmp_ctx == NULL) {
    1577           0 :         ret = ENOMEM;
    1578           0 :         goto done;
    1579             :     }
    1580             : 
    1581           0 :     ret = sysdb_search_user_by_name(tmp_ctx, domain, name, attrs, &ldb_msg);
    1582           0 :     if (ret != EOK) {
    1583           0 :         DEBUG(SSSDBG_CRIT_FAILURE,
    1584             :               "sysdb_search_user_by_name failed [%d][%s].\n",
    1585             :               ret, strerror(ret));
    1586           0 :         goto done;
    1587             :     }
    1588             : 
    1589             :     /* Check offline_auth_cache_timeout */
    1590           0 :     value = ldb_msg_find_attr_as_uint64(ldb_msg,
    1591             :                                         SYSDB_LAST_ONLINE_AUTH_WITH_CURR_TOKEN,
    1592             :                                         0);
    1593           0 :     ret = EOK;
    1594             : 
    1595             : done:
    1596           0 :     if (ret == EOK) {
    1597           0 :         *_value = value;
    1598             :     }
    1599             : 
    1600           0 :     talloc_free(tmp_ctx);
    1601           0 :     return ret;
    1602             : }

Generated by: LCOV version 1.10