"Fossies" - the Fresh Open Source Software Archive

Member "sssd-2.4.2/src/responder/pam/pamsrv_gssapi.c" (19 Feb 2021, 30891 Bytes) of package /linux/misc/sssd-2.4.2.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "pamsrv_gssapi.c" see the Fossies "Dox" file reference documentation and the latest Fossies "Diffs" side-by-side code changes report: 2.4.1_vs_2.4.2.

    1 /*
    2     Authors:
    3         Pavel Březina <pbrezina@redhat.com>
    4 
    5     Copyright (C) 2020 Red Hat
    6 
    7     This program is free software; you can redistribute it and/or modify
    8     it under the terms of the GNU General Public License as published by
    9     the Free Software Foundation; either version 3 of the License, or
   10     (at your option) any later version.
   11 
   12     This program is distributed in the hope that it will be useful,
   13     but WITHOUT ANY WARRANTY; without even the implied warranty of
   14     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   15     GNU General Public License for more details.
   16 
   17     You should have received a copy of the GNU General Public License
   18     along with this program.  If not, see <http://www.gnu.org/licenses/>.
   19 */
   20 
   21 #include <errno.h>
   22 #include <gssapi.h>
   23 #include <gssapi/gssapi_ext.h>
   24 #include <gssapi/gssapi_krb5.h>
   25 #include <stdint.h>
   26 #include <stdlib.h>
   27 #include <string.h>
   28 #include <talloc.h>
   29 #include <ldb.h>
   30 
   31 #include "confdb/confdb.h"
   32 #include "db/sysdb.h"
   33 #include "responder/common/responder_packet.h"
   34 #include "responder/common/responder.h"
   35 #include "responder/common/cache_req/cache_req.h"
   36 #include "responder/pam/pamsrv.h"
   37 #include "sss_client/sss_cli.h"
   38 #include "util/util.h"
   39 #include "util/sss_utf8.h"
   40 
   41 static errno_t read_str(size_t body_len,
   42                         uint8_t *body,
   43                         size_t *pctr,
   44                         const char **_str)
   45 {
   46     size_t i;
   47 
   48     for (i = *pctr; i < body_len && body[i] != 0; i++) {
   49         /* counting */
   50     }
   51 
   52     if (i >= body_len) {
   53         return EINVAL;
   54     }
   55 
   56     if (!sss_utf8_check(&body[*pctr], i - *pctr)) {
   57         DEBUG(SSSDBG_CRIT_FAILURE, "Body is not UTF-8 string!\n");
   58         return EINVAL;
   59     }
   60 
   61     *_str = (const char *)&body[*pctr];
   62     *pctr = i + 1;
   63 
   64     return EOK;
   65 }
   66 
   67 static bool pam_gssapi_should_check_upn(struct pam_ctx *pam_ctx,
   68                                         struct sss_domain_info *domain)
   69 {
   70     if (domain->gssapi_check_upn != NULL) {
   71         if (strcasecmp(domain->gssapi_check_upn, "true") == 0) {
   72             return true;
   73         }
   74 
   75         if (strcasecmp(domain->gssapi_check_upn, "false") == 0) {
   76             return false;
   77         }
   78 
   79         DEBUG(SSSDBG_MINOR_FAILURE, "Invalid value for %s: %s\n",
   80               CONFDB_PAM_GSSAPI_CHECK_UPN, domain->gssapi_check_upn);
   81         return false;
   82     }
   83 
   84     return pam_ctx->gssapi_check_upn;
   85 }
   86 
   87 static int pam_gssapi_check_indicators(TALLOC_CTX *mem_ctx,
   88                                        const char *pam_service,
   89                                        char **gssapi_indicators_map,
   90                                        char **indicators)
   91 {
   92     char *authind = NULL;
   93     size_t pam_len = strlen(pam_service);
   94     char **map = gssapi_indicators_map;
   95     char **result = NULL;
   96     int res;
   97 
   98     authind = talloc_strdup(mem_ctx, "");
   99     if (authind == NULL) {
  100         return ENOMEM;
  101     }
  102 
  103     for (int i = 0; map[i]; i++) {
  104         if (map[i][0] == '-') {
  105             DEBUG(SSSDBG_TRACE_FUNC,
  106                   "Indicators aren't used for [%s]\n",
  107                   pam_service);
  108             talloc_free(authind);
  109             return EOK;
  110         }
  111         if (!strchr(map[i], ':')) {
  112             authind = talloc_asprintf_append(authind, "%s ", map[i]);
  113             if (authind == NULL) {
  114                 /* Since we allocate on pam_ctx, caller will free it */
  115                 return ENOMEM;
  116             }
  117             continue;
  118         }
  119 
  120         res = strncmp(map[i], pam_service, pam_len);
  121         if (res == 0) {
  122             if (strlen(map[i]) > pam_len) {
  123                 if (map[i][pam_len] != ':') {
  124                     /* different PAM service, skip it */
  125                     continue;
  126                 }
  127 
  128                 if (map[i][pam_len + 1] == '-') {
  129                     DEBUG(SSSDBG_TRACE_FUNC,
  130                         "Indicators aren't used for [%s]\n",
  131                         pam_service);
  132                     talloc_free(authind);
  133                     return EOK;
  134                 }
  135 
  136                 authind = talloc_asprintf_append(authind, "%s ",
  137                                                  map[i] + (pam_len + 1));
  138                 if (authind == NULL) {
  139                     /* Since we allocate on pam_ctx, caller will free it */
  140                     return ENOMEM;
  141                 }
  142             } else {
  143                 DEBUG(SSSDBG_MINOR_FAILURE, "Invalid value for %s: [%s]\n",
  144                       CONFDB_PAM_GSSAPI_INDICATORS_MAP, map[i]);
  145                 talloc_free(authind);
  146                 return EINVAL;
  147             }
  148         }
  149     }
  150 
  151     res = ENOENT;
  152     map = NULL;
  153 
  154     if (authind[0] == '\0') {
  155         /* empty list of per-service indicators -> skip */
  156         goto done;
  157     }
  158 
  159     /* trim a space after the final indicator
  160      * to prevent split_on_separator() to fail */
  161     authind[strlen(authind) - 1] = '\0';
  162 
  163     res = split_on_separator(mem_ctx, authind, ' ', true, true,
  164                              &map, NULL);
  165     if (res != 0) {
  166         DEBUG(SSSDBG_FATAL_FAILURE,
  167             "Cannot parse list of indicators: [%s]\n", authind);
  168         res = EINVAL;
  169         goto done;
  170     }
  171 
  172     res = diff_string_lists(mem_ctx, indicators, map, NULL, NULL, &result);
  173     if (res != 0) {
  174         DEBUG(SSSDBG_FATAL_FAILURE,"Cannot diff lists of indicators\n");
  175         res = EINVAL;
  176         goto done;
  177     }
  178 
  179     if (result && result[0] != NULL) {
  180         for (int i = 0; result[i]; i++) {
  181             DEBUG(SSSDBG_TRACE_FUNC,
  182                   "indicator [%s] is allowed for PAM service [%s]\n",
  183                   result[i], pam_service);
  184         }
  185         res = EOK;
  186         goto done;
  187     }
  188 
  189     res = EPERM;
  190 
  191 done:
  192     talloc_free(result);
  193     talloc_free(authind);
  194     talloc_free(map);
  195     return res;
  196 }
  197 
  198 static bool pam_gssapi_allowed(struct pam_ctx *pam_ctx,
  199                                struct sss_domain_info *domain,
  200                                const char *service)
  201 {
  202     char **list = pam_ctx->gssapi_services;
  203 
  204     if (domain->gssapi_services != NULL) {
  205         list = domain->gssapi_services;
  206     }
  207 
  208     if (strcmp(service, "-") == 0) {
  209         /* Dash is used as a "not set" value to allow to explicitly disable
  210          * gssapi auth for specific domain. Disallow this service to be safe.
  211          */
  212         DEBUG(SSSDBG_TRACE_FUNC, "Dash - was used as a PAM service name. "
  213               "GSSAPI authentication is not allowed.\n");
  214         return false;
  215     }
  216 
  217     return string_in_list(service, list, true);
  218 }
  219 
  220 static char *pam_gssapi_target(TALLOC_CTX *mem_ctx,
  221                                struct sss_domain_info *domain)
  222 {
  223     return talloc_asprintf(mem_ctx, "host@%s", domain->hostname);
  224 }
  225 
  226 static const char *pam_gssapi_get_upn(struct cache_req_result *result)
  227 {
  228     if (result->count == 0) {
  229         return NULL;
  230     }
  231 
  232     /* Canonical UPN should be available if the user has kinited through SSSD.
  233      * Use it as a hint for GSSAPI. Default to empty string so it may be
  234      * more easily transffered over the wire. */
  235     return ldb_msg_find_attr_as_string(result->msgs[0], SYSDB_CANONICAL_UPN, "");
  236 }
  237 
  238 static const char *pam_gssapi_get_name(struct cache_req_result *result)
  239 {
  240     if (result->count == 0) {
  241         return NULL;
  242     }
  243 
  244     /* Return username known to SSSD to make sure we authenticated as the same
  245      * user after GSSAPI handshake. */
  246     return ldb_msg_find_attr_as_string(result->msgs[0], SYSDB_NAME, NULL);
  247 }
  248 
  249 static errno_t pam_gssapi_init_parse(struct cli_protocol *pctx,
  250                                      const char **_service,
  251                                      const char **_username)
  252 {
  253     size_t body_len;
  254     size_t pctr = 0;
  255     uint8_t *body;
  256     errno_t ret;
  257 
  258     sss_packet_get_body(pctx->creq->in, &body, &body_len);
  259     if (body == NULL) {
  260         DEBUG(SSSDBG_CRIT_FAILURE, "Invalid input\n");
  261         return EINVAL;
  262     }
  263 
  264     ret = read_str(body_len, body, &pctr, _service);
  265     if (ret != EOK) {
  266         return ret;
  267     }
  268 
  269     ret = read_str(body_len, body, &pctr, _username);
  270     if (ret != EOK) {
  271         return ret;
  272     }
  273 
  274     return EOK;
  275 }
  276 
  277 static errno_t pam_gssapi_init_reply(struct cli_protocol *pctx,
  278                                      const char *domain,
  279                                      const char *target,
  280                                      const char *upn,
  281                                      const char *username)
  282 {
  283     size_t reply_len;
  284     size_t body_len;
  285     size_t pctr;
  286     uint8_t *body;
  287     errno_t ret;
  288 
  289     ret = sss_packet_new(pctx->creq, 0, sss_packet_get_cmd(pctx->creq->in),
  290                          &pctx->creq->out);
  291     if (ret != EOK) {
  292         DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create a new packet [%d]; %s\n",
  293               ret, sss_strerror(ret));
  294         return ret;
  295     }
  296 
  297     reply_len =  strlen(username) + 1;
  298     reply_len += strlen(domain) + 1;
  299     reply_len += strlen(target) + 1;
  300     reply_len += strlen(upn) + 1;
  301 
  302     ret = sss_packet_grow(pctx->creq->out, reply_len);
  303     if (ret != EOK) {
  304         DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create response: %s\n",
  305               sss_strerror(ret));
  306         return ret;
  307     }
  308 
  309     sss_packet_get_body(pctx->creq->out, &body, &body_len);
  310 
  311     pctr = 0;
  312     SAFEALIGN_SETMEM_STRING(&body[pctr], username, strlen(username) + 1, &pctr);
  313     SAFEALIGN_SETMEM_STRING(&body[pctr], domain, strlen(domain) + 1, &pctr);
  314     SAFEALIGN_SETMEM_STRING(&body[pctr], target, strlen(target) + 1, &pctr);
  315     SAFEALIGN_SETMEM_STRING(&body[pctr], upn, strlen(upn) + 1, &pctr);
  316 
  317     return EOK;
  318 }
  319 
  320 struct gssapi_init_state {
  321     struct cli_ctx *cli_ctx;
  322     const char *username;
  323     const char *service;
  324 };
  325 
  326 static void pam_cmd_gssapi_init_done(struct tevent_req *req);
  327 
  328 int pam_cmd_gssapi_init(struct cli_ctx *cli_ctx)
  329 {
  330     struct gssapi_init_state *state;
  331     struct cli_protocol *pctx;
  332     struct tevent_req *req;
  333     const char *username;
  334     const char *service;
  335     const char *attrs[] = { SYSDB_NAME, SYSDB_CANONICAL_UPN, NULL };
  336     errno_t ret;
  337 
  338     state = talloc_zero(cli_ctx, struct gssapi_init_state);
  339     if (state == NULL) {
  340         return ENOMEM;
  341     }
  342 
  343     pctx = talloc_get_type(cli_ctx->protocol_ctx, struct cli_protocol);
  344 
  345     ret = pam_gssapi_init_parse(pctx, &service, &username);
  346     if (ret != EOK) {
  347         DEBUG(SSSDBG_CRIT_FAILURE, "Unable to parse input [%d]: %s\n",
  348               ret, sss_strerror(ret));
  349         goto done;
  350     }
  351 
  352     state->cli_ctx = cli_ctx;
  353     state->service = service;
  354     state->username = username;
  355 
  356     DEBUG(SSSDBG_TRACE_ALL,
  357           "Requesting GSSAPI authentication of [%s] in service [%s]\n",
  358           username, service);
  359 
  360     req = cache_req_user_by_name_attrs_send(cli_ctx, cli_ctx->ev, cli_ctx->rctx,
  361                                             cli_ctx->rctx->ncache, 0,
  362                                             NULL, username, attrs);
  363     if (req == NULL) {
  364         DEBUG(SSSDBG_CRIT_FAILURE, "Out of memory!\n");
  365         ret = ENOMEM;
  366         goto done;
  367     }
  368 
  369     tevent_req_set_callback(req, pam_cmd_gssapi_init_done, state);
  370 
  371     ret = EOK;
  372 
  373 done:
  374     if (ret != EOK) {
  375         sss_cmd_send_error(cli_ctx, ret);
  376         sss_cmd_done(cli_ctx, NULL);
  377     }
  378 
  379     return EOK;
  380 }
  381 
  382 static void pam_cmd_gssapi_init_done(struct tevent_req *req)
  383 {
  384     struct gssapi_init_state *state;
  385     struct cache_req_result *result;
  386     struct cli_protocol *pctx;
  387     struct pam_ctx *pam_ctx;
  388     const char *username;
  389     const char *upn;
  390     char *target;
  391     errno_t ret;
  392 
  393     state = tevent_req_callback_data(req, struct gssapi_init_state);
  394     pctx = talloc_get_type(state->cli_ctx->protocol_ctx, struct cli_protocol);
  395     pam_ctx = talloc_get_type(state->cli_ctx->rctx->pvt_ctx, struct pam_ctx);
  396 
  397     ret = cache_req_user_by_name_attrs_recv(state, req, &result);
  398     talloc_zfree(req);
  399     if (ret == ENOENT || ret == ERR_DOMAIN_NOT_FOUND) {
  400         ret = ENOENT;
  401         goto done;
  402     } else if (ret != EOK) {
  403         goto done;
  404     }
  405 
  406     if (!pam_gssapi_allowed(pam_ctx, result->domain, state->service)) {
  407         ret = ENOTSUP;
  408         goto done;
  409     }
  410 
  411     username = pam_gssapi_get_name(result);
  412     if (username == NULL) {
  413         /* User with no name? */
  414         ret = ERR_INTERNAL;
  415         goto done;
  416     }
  417 
  418     upn = pam_gssapi_get_upn(result);
  419     if (upn == NULL) {
  420         /* UPN hint may be an empty string, but not NULL. */
  421         ret = ERR_INTERNAL;
  422         goto done;
  423     }
  424 
  425     target = pam_gssapi_target(state, result->domain);
  426     if (target == NULL) {
  427         ret = ENOMEM;
  428         goto done;
  429     }
  430 
  431     DEBUG(SSSDBG_TRACE_FUNC,
  432           "Trying GSSAPI auth: User[%s], Domain[%s], UPN[%s], Target[%s]\n",
  433           username, result->domain->name, upn, target);
  434 
  435     ret = pam_gssapi_init_reply(pctx, result->domain->name, target, upn,
  436                                 username);
  437     if (ret != EOK) {
  438         DEBUG(SSSDBG_CRIT_FAILURE, "Unable to construct reply [%d]: %s\n",
  439               ret, sss_strerror(ret));
  440         goto done;
  441     }
  442 
  443 done:
  444     DEBUG(SSSDBG_TRACE_FUNC, "Returning [%d]: %s\n", ret, sss_strerror(ret));
  445 
  446     if (ret == EOK) {
  447         sss_packet_set_error(pctx->creq->out, EOK);
  448     } else {
  449         sss_cmd_send_error(state->cli_ctx, ret);
  450     }
  451 
  452     sss_cmd_done(state->cli_ctx, state);
  453 }
  454 
  455 static void gssapi_log_status(int type, OM_uint32 status_code)
  456 {
  457     OM_uint32 message_context = 0;
  458     gss_buffer_desc buf;
  459     OM_uint32 minor;
  460 
  461     do {
  462         gss_display_status(&minor, status_code, type, GSS_C_NO_OID,
  463                            &message_context, &buf);
  464         DEBUG(SSSDBG_OP_FAILURE, "GSSAPI: %.*s\n", (int)buf.length,
  465               (char *)buf.value);
  466         gss_release_buffer(&minor, &buf);
  467     } while (message_context != 0);
  468 }
  469 
  470 static void gssapi_log_error(OM_uint32 major, OM_uint32 minor)
  471 {
  472     gssapi_log_status(GSS_C_GSS_CODE, major);
  473     gssapi_log_status(GSS_C_MECH_CODE, minor);
  474 }
  475 
  476 static char *gssapi_get_name(TALLOC_CTX *mem_ctx, gss_name_t gss_name)
  477 {
  478     gss_buffer_desc buf;
  479     OM_uint32 major;
  480     OM_uint32 minor;
  481     char *exported;
  482 
  483     major = gss_display_name(&minor, gss_name, &buf, NULL);
  484     if (major != GSS_S_COMPLETE) {
  485         DEBUG(SSSDBG_CRIT_FAILURE, "Unable to export name\n");
  486         return NULL;
  487     }
  488 
  489     exported = talloc_strndup(mem_ctx, buf.value, buf.length);
  490     gss_release_buffer(&minor, &buf);
  491 
  492     if (exported == NULL) {
  493         DEBUG(SSSDBG_CRIT_FAILURE, "Out of memory!\n");
  494         return NULL;
  495     }
  496 
  497     return exported;
  498 }
  499 
  500 #define AUTH_INDICATORS_TAG "auth-indicators"
  501 
  502 static char **gssapi_get_indicators(TALLOC_CTX *mem_ctx, gss_name_t gss_name)
  503 {
  504     gss_buffer_set_t attrs = GSS_C_NO_BUFFER_SET;
  505     int is_mechname;
  506     OM_uint32 major;
  507     OM_uint32 minor;
  508     gss_buffer_desc value = GSS_C_EMPTY_BUFFER;
  509     gss_buffer_desc display_value = GSS_C_EMPTY_BUFFER;
  510     char *exported = NULL;
  511     char **map = NULL;
  512     int res;
  513 
  514     major = gss_inquire_name(&minor, gss_name, &is_mechname, NULL, &attrs);
  515     if (major != GSS_S_COMPLETE) {
  516         DEBUG(SSSDBG_CRIT_FAILURE, "Unable to inquire name\n");
  517         return NULL;
  518     }
  519 
  520     if (attrs == GSS_C_NO_BUFFER_SET) {
  521         DEBUG(SSSDBG_TRACE_FUNC, "No krb5 attributes in the ticket\n");
  522         return NULL;
  523     }
  524 
  525     exported = talloc_strdup(mem_ctx, "");
  526     if (exported == NULL) {
  527         DEBUG(SSSDBG_CRIT_FAILURE,
  528               "Unable to pre-allocate indicators\n");
  529         goto done;
  530     }
  531 
  532     for (int i = 0; i < attrs->count; i++) {
  533         int authenticated = 0;
  534         int complete = 0;
  535         int more = -1;
  536 
  537         /* skip anything but auth-indicators */
  538         if (strncmp(AUTH_INDICATORS_TAG, attrs->elements[i].value,
  539                     sizeof(AUTH_INDICATORS_TAG) - 1) != 0)
  540             continue;
  541 
  542         /* retrieve all indicators */
  543         while (more != 0) {
  544             value.value = NULL;
  545             display_value.value = NULL;
  546 
  547             major = gss_get_name_attribute(&minor, gss_name,
  548                                             &attrs->elements[i],
  549                                             &authenticated,
  550                                             &complete, &value,
  551                                             &display_value,
  552                                             &more);
  553             if (major != GSS_S_COMPLETE) {
  554                 DEBUG(SSSDBG_CRIT_FAILURE,
  555                         "Unable to retrieve an attribute\n");
  556                 goto done;
  557             }
  558 
  559             if ((value.value != NULL) && authenticated) {
  560                 DEBUG(SSSDBG_TRACE_FUNC,
  561                         "attribute's [%.*s] value [%.*s] authenticated\n",
  562                         (int) attrs->elements[i].length,
  563                         (char*) attrs->elements[i].value,
  564                         (int) value.length,
  565                         (char*) value.value);
  566                 exported = talloc_asprintf_append(exported, "%.*s ",
  567                                                 (int) value.length,
  568                                                 (char*) value.value);
  569             }
  570 
  571             if (exported == NULL) {
  572                 /* Since we allocate on mem_ctx, caller will free
  573                  * the previous version of 'exported' */
  574                 DEBUG(SSSDBG_CRIT_FAILURE,
  575                         "Unable to collect an attribute value\n");
  576                 goto done;
  577             }
  578             (void) gss_release_buffer(&minor, &value);
  579             (void) gss_release_buffer(&minor, &display_value);
  580         }
  581     }
  582 
  583     if (exported[0] != '\0') {
  584         /* trim a space after the final indicator
  585          * to prevent split_on_separator() to fail */
  586         exported[strlen(exported) - 1] = '\0';
  587     } else {
  588         /* empty list */
  589         goto done;
  590     }
  591 
  592     res = split_on_separator(mem_ctx, exported, ' ', true, true,
  593                             &map, NULL);
  594     if (res != 0) {
  595         DEBUG(SSSDBG_FATAL_FAILURE,
  596             "Cannot parse list of indicators: [%s]\n", exported);
  597         goto done;
  598     } else {
  599         DEBUG(SSSDBG_TRACE_FUNC, "authentication indicators: [%s]\n",
  600               exported);
  601     }
  602 
  603 done:
  604     (void) gss_release_buffer(&minor, &value);
  605     (void) gss_release_buffer(&minor, &display_value);
  606     (void) gss_release_buffer_set(&minor, &attrs);
  607 
  608     talloc_free(exported);
  609     return map;
  610 }
  611 
  612 
  613 struct gssapi_state {
  614     struct cli_ctx *cli_ctx;
  615     struct sss_domain_info *domain;
  616     const char *username;
  617 
  618     char *authenticated_upn;
  619     char **auth_indicators;
  620     bool established;
  621     gss_ctx_id_t ctx;
  622 };
  623 
  624 int gssapi_state_destructor(struct gssapi_state *state)
  625 {
  626     OM_uint32 minor;
  627 
  628     gss_delete_sec_context(&minor, &state->ctx, NULL);
  629 
  630     return 0;
  631 }
  632 
  633 static struct gssapi_state *gssapi_get_state(struct cli_ctx *cli_ctx,
  634                                              const char *username,
  635                                              struct sss_domain_info *domain)
  636 {
  637     struct gssapi_state *state;
  638 
  639     state = talloc_get_type(cli_ctx->state_ctx, struct gssapi_state);
  640     if (state != NULL) {
  641         return state;
  642     }
  643 
  644     state = talloc_zero(cli_ctx, struct gssapi_state);
  645     if (state == NULL) {
  646         return NULL;
  647     }
  648 
  649     state->username = talloc_strdup(state, username);
  650     if (state == NULL) {
  651         talloc_free(state);
  652         return NULL;
  653     }
  654 
  655     state->domain = domain;
  656     state->cli_ctx = cli_ctx;
  657     state->ctx = GSS_C_NO_CONTEXT;
  658     talloc_set_destructor(state, gssapi_state_destructor);
  659 
  660     cli_ctx->state_ctx = state;
  661 
  662     return state;
  663 }
  664 
  665 static errno_t gssapi_get_creds(const char *keytab,
  666                                 const char *target,
  667                                 gss_cred_id_t *_creds)
  668 {
  669     gss_key_value_set_desc cstore = {0, NULL};
  670     gss_key_value_element_desc el;
  671     gss_buffer_desc name_buf;
  672     gss_name_t name = GSS_C_NO_NAME;
  673     OM_uint32 major;
  674     OM_uint32 minor;
  675     errno_t ret;
  676 
  677     if (keytab != NULL) {
  678         el.key = "keytab";
  679         el.value = keytab;
  680         cstore.count = 1;
  681         cstore.elements = &el;
  682     }
  683 
  684     if (target != NULL) {
  685         name_buf.value = discard_const(target);
  686         name_buf.length = strlen(target);
  687 
  688         major = gss_import_name(&minor, &name_buf, GSS_C_NT_HOSTBASED_SERVICE,
  689                                 &name);
  690         if (GSS_ERROR(major)) {
  691             DEBUG(SSSDBG_OP_FAILURE, "Could not import name [%s] "
  692                  "[maj:0x%x, min:0x%x]\n", target, major, minor);
  693 
  694             gssapi_log_error(major, minor);
  695 
  696             ret = EIO;
  697             goto done;
  698         }
  699     }
  700 
  701     major = gss_acquire_cred_from(&minor, name, GSS_C_INDEFINITE,
  702                                   GSS_C_NO_OID_SET, GSS_C_ACCEPT, &cstore,
  703                                   _creds, NULL, NULL);
  704     if (GSS_ERROR(major)) {
  705         DEBUG(SSSDBG_OP_FAILURE, "Unable to read credentials from [%s] "
  706               "[maj:0x%x, min:0x%x]\n", keytab ? keytab : "default",
  707               major, minor);
  708 
  709         gssapi_log_error(major, minor);
  710 
  711         ret = EIO;
  712         goto done;
  713     }
  714 
  715     ret = EOK;
  716 
  717 done:
  718     gss_release_name(&minor, &name);
  719 
  720     return ret;
  721 }
  722 
  723 static errno_t
  724 gssapi_handshake(struct gssapi_state *state,
  725                  struct cli_protocol *pctx,
  726                  const char *keytab,
  727                  const char *target,
  728                  uint8_t *gss_data,
  729                  size_t gss_data_len)
  730 {
  731     OM_uint32 flags = GSS_C_MUTUAL_FLAG;
  732     gss_buffer_desc output = GSS_C_EMPTY_BUFFER;
  733     gss_buffer_desc input;
  734     gss_name_t client_name;
  735     gss_cred_id_t creds;
  736     OM_uint32 ret_flags;
  737     gss_OID mech_type;
  738     OM_uint32 major;
  739     OM_uint32 minor;
  740     errno_t ret;
  741 
  742     input.value = gss_data;
  743     input.length = gss_data_len;
  744 
  745     ret = gssapi_get_creds(keytab, target, &creds);
  746     if (ret != EOK) {
  747         return ret;
  748     }
  749 
  750     major = gss_accept_sec_context(&minor, &state->ctx, creds,
  751                                    &input, NULL, &client_name, &mech_type,
  752                                    &output, &ret_flags, NULL, NULL);
  753     if (major == GSS_S_CONTINUE_NEEDED || output.length > 0) {
  754         ret = sss_packet_set_body(pctx->creq->out, output.value, output.length);
  755         if (ret != EOK) {
  756             goto done;
  757         }
  758     }
  759 
  760     if (GSS_ERROR(major)) {
  761         DEBUG(SSSDBG_OP_FAILURE, "Unable to establish GSS context "
  762               "[maj:0x%x, min:0x%x]\n", major, minor);
  763 
  764         gssapi_log_error(major, minor);
  765         ret = EIO;
  766         goto done;
  767     }
  768 
  769     if (major == GSS_S_CONTINUE_NEEDED) {
  770         ret = EOK;
  771         goto done;
  772     } else if (major != GSS_S_COMPLETE) {
  773         DEBUG(SSSDBG_OP_FAILURE, "Unable to establish GSS context, unexpected "
  774               "value: 0x%x\n", major);
  775         ret = EIO;
  776         goto done;
  777     }
  778 
  779     if ((ret_flags & flags) != flags) {
  780         DEBUG(SSSDBG_MINOR_FAILURE,
  781                 "Negotiated context does not support requested flags\n");
  782         state->established = false;
  783         ret = EIO;
  784         goto done;
  785     }
  786 
  787     state->authenticated_upn = gssapi_get_name(state, client_name);
  788     if (state->authenticated_upn == NULL) {
  789         state->established = false;
  790         ret = ENOMEM;
  791         goto done;
  792     }
  793 
  794     DEBUG(SSSDBG_TRACE_FUNC, "Security context established with [%s]\n",
  795           state->authenticated_upn);
  796 
  797     state->auth_indicators = gssapi_get_indicators(state, client_name);
  798 
  799     state->established = true;
  800     ret = EOK;
  801 
  802 done:
  803     gss_release_cred(&minor, &creds);
  804     gss_release_buffer(&minor, &output);
  805 
  806     return ret;
  807 }
  808 
  809 static errno_t pam_cmd_gssapi_sec_ctx_parse(struct cli_protocol *pctx,
  810                                             const char **_pam_service,
  811                                             const char **_username,
  812                                             const char **_domain,
  813                                             uint8_t **_gss_data,
  814                                             size_t *_gss_data_len)
  815 {
  816     size_t body_len;
  817     uint8_t *body;
  818     size_t pctr;
  819     errno_t ret;
  820 
  821     sss_packet_get_body(pctx->creq->in, &body, &body_len);
  822     if (body == NULL) {
  823         DEBUG(SSSDBG_CRIT_FAILURE, "Invalid input\n");
  824         return EINVAL;
  825     }
  826 
  827     pctr = 0;
  828     ret = read_str(body_len, body, &pctr, _pam_service);
  829     if (ret != EOK) {
  830         return ret;
  831     }
  832 
  833     ret = read_str(body_len, body, &pctr, _username);
  834     if (ret != EOK) {
  835         return ret;
  836     }
  837 
  838     ret = read_str(body_len, body, &pctr, _domain);
  839     if (ret != EOK) {
  840         return ret;
  841     }
  842 
  843     *_gss_data = (pctr == body_len) ? NULL : body + pctr;
  844     *_gss_data_len = body_len - pctr;
  845 
  846     return EOK;
  847 }
  848 
  849 static void pam_cmd_gssapi_sec_ctx_done(struct tevent_req *req);
  850 
  851 int
  852 pam_cmd_gssapi_sec_ctx(struct cli_ctx *cli_ctx)
  853 {
  854     struct sss_domain_info *domain;
  855     struct gssapi_state *state;
  856     struct cli_protocol *pctx;
  857     struct pam_ctx *pam_ctx;
  858     struct tevent_req *req;
  859     const char *pam_service;
  860     const char *domain_name;
  861     const char *username;
  862     char *target;
  863     char **indicators_map = NULL;
  864     size_t gss_data_len;
  865     uint8_t *gss_data;
  866     errno_t ret;
  867 
  868     pctx = talloc_get_type(cli_ctx->protocol_ctx, struct cli_protocol);
  869     pam_ctx = talloc_get_type(cli_ctx->rctx->pvt_ctx, struct pam_ctx);
  870 
  871     ret = sss_packet_new(pctx->creq, 0, sss_packet_get_cmd(pctx->creq->in),
  872                          &pctx->creq->out);
  873     if (ret != EOK) {
  874         DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create a new packet [%d]; %s\n",
  875               ret, sss_strerror(ret));
  876         return ret;
  877     }
  878 
  879     ret = pam_cmd_gssapi_sec_ctx_parse(pctx, &pam_service, &username,
  880                                        &domain_name, &gss_data, &gss_data_len);
  881     if (ret != EOK) {
  882         DEBUG(SSSDBG_OP_FAILURE, "Unable to parse input data [%d]: %s\n",
  883               ret, sss_strerror(ret));
  884         goto done;
  885     }
  886 
  887     domain = find_domain_by_name(cli_ctx->rctx->domains, domain_name, false);
  888     if (domain == NULL) {
  889         ret = EINVAL;
  890         goto done;
  891     }
  892 
  893     if (!pam_gssapi_allowed(pam_ctx, domain, pam_service)) {
  894         ret = ENOTSUP;
  895         goto done;
  896     }
  897 
  898     target = pam_gssapi_target(cli_ctx, domain);
  899     if (target == NULL) {
  900         ret = ENOMEM;
  901         goto done;
  902     }
  903 
  904     state = gssapi_get_state(cli_ctx, username, domain);
  905     if (state == NULL) {
  906         ret = ENOMEM;
  907         goto done;
  908     }
  909 
  910     if (strcmp(username, state->username) != 0 || state->domain != domain) {
  911         /* This should not happen, but be paranoid. */
  912         DEBUG(SSSDBG_CRIT_FAILURE, "Different input user then who initiated "
  913               "the request!\n");
  914         ret = EPERM;
  915         goto done;
  916     }
  917 
  918     if (state->established) {
  919         DEBUG(SSSDBG_MINOR_FAILURE,
  920               "Security context is already established\n");
  921         ret = EPERM;
  922         goto done;
  923     }
  924 
  925     ret = gssapi_handshake(state, pctx, domain->krb5_keytab, target, gss_data,
  926                            gss_data_len);
  927     if (ret != EOK || !state->established) {
  928         goto done;
  929     }
  930 
  931     /* Use map for auth-indicators from the domain, if defined and
  932      * fallback to the [pam] section otherwise */
  933     indicators_map = domain->gssapi_indicators_map ?
  934                      domain->gssapi_indicators_map :
  935                      (pam_ctx->gssapi_indicators_map ?
  936                       pam_ctx->gssapi_indicators_map : NULL);
  937     if (indicators_map != NULL) {
  938         ret = pam_gssapi_check_indicators(state,
  939                                           pam_service,
  940                                           indicators_map,
  941                                           state->auth_indicators);
  942         DEBUG(SSSDBG_TRACE_FUNC,
  943               "Check if acquired service ticket has req. indicators: %d\n",
  944               ret);
  945         if ((ret == EPERM) || (ret == ENOMEM) || (ret == EINVAL)) {
  946             /* skip further checks if denied or no memory,
  947              * ENOENT means the check is not applicable */
  948             goto done;
  949         }
  950     }
  951 
  952     if (!pam_gssapi_should_check_upn(pam_ctx, domain)) {
  953         /* We are done. */
  954         goto done;
  955     }
  956 
  957     /* We have established the security context. Now check the the principal
  958      * used for authorization can be associated with the user. We have
  959      * already done initgroups before so we could just search the sysdb
  960      * directly, but use cache req to avoid looking up a possible expired
  961      * object if the handshake took longer. */
  962 
  963     DEBUG(SSSDBG_TRACE_FUNC, "Checking that target user matches UPN\n");
  964 
  965     req = cache_req_user_by_upn_send(cli_ctx, cli_ctx->ev, cli_ctx->rctx,
  966                                      cli_ctx->rctx->ncache, 0,
  967                                      CACHE_REQ_POSIX_DOM,
  968                                      domain->name, state->authenticated_upn);
  969     if (req == NULL) {
  970         DEBUG(SSSDBG_CRIT_FAILURE, "Out of memory!\n");
  971         ret = ENOMEM;
  972         goto done;
  973     }
  974 
  975     tevent_req_set_callback(req, pam_cmd_gssapi_sec_ctx_done, state);
  976 
  977     return EOK;
  978 
  979 done:
  980     DEBUG(SSSDBG_TRACE_FUNC, "Returning [%d]: %s\n", ret, sss_strerror(ret));
  981 
  982     if (ret == EOK) {
  983         sss_packet_set_error(pctx->creq->out, EOK);
  984     } else {
  985         sss_cmd_send_error(cli_ctx, ret);
  986     }
  987 
  988     sss_cmd_done(cli_ctx, NULL);
  989     return EOK;
  990 }
  991 
  992 static void pam_cmd_gssapi_sec_ctx_done(struct tevent_req *req)
  993 {
  994     struct gssapi_state *state;
  995     struct cache_req_result *result;
  996     struct cli_protocol *pctx;
  997     const char *name;
  998     errno_t ret;
  999 
 1000     state = tevent_req_callback_data(req, struct gssapi_state);
 1001     pctx = talloc_get_type(state->cli_ctx->protocol_ctx, struct cli_protocol);
 1002 
 1003     ret = cache_req_user_by_upn_recv(state, req, &result);
 1004     talloc_zfree(req);
 1005     if (ret == ENOENT || ret == ERR_DOMAIN_NOT_FOUND) {
 1006         /* We have no match. Return failure. */
 1007         DEBUG(SSSDBG_TRACE_FUNC, "User with UPN [%s] was not found. "
 1008               "Authentication failed.\n", state->authenticated_upn);
 1009         ret = EACCES;
 1010         goto done;
 1011     } else if (ret != EOK) {
 1012         /* Generic error. Return failure. */
 1013         DEBUG(SSSDBG_CRIT_FAILURE, "Unable to lookup user by UPN [%d]: %s\n",
 1014               ret, sss_strerror(ret));
 1015         goto done;
 1016     }
 1017 
 1018     /* Check that username match. */
 1019     name = ldb_msg_find_attr_as_string(result->msgs[0], SYSDB_NAME, NULL);
 1020     if (name == NULL || strcmp(name, state->username) != 0) {
 1021         DEBUG(SSSDBG_TRACE_FUNC, "UPN [%s] does not match target user [%s]. "
 1022               "Authentication failed.\n", state->authenticated_upn,
 1023               state->username);
 1024         ret = EACCES;
 1025         goto done;
 1026     }
 1027 
 1028     DEBUG(SSSDBG_TRACE_FUNC, "User [%s] match UPN [%s]. Authentication was "
 1029           "successful.\n", state->username, state->authenticated_upn);
 1030 
 1031     ret = EOK;
 1032 
 1033 done:
 1034     DEBUG(SSSDBG_TRACE_FUNC, "Returning [%d]: %s\n", ret, sss_strerror(ret));
 1035 
 1036     if (ret == EOK) {
 1037         sss_packet_set_error(pctx->creq->out, EOK);
 1038     } else {
 1039         sss_cmd_send_error(state->cli_ctx, ret);
 1040     }
 1041 
 1042     sss_cmd_done(state->cli_ctx, state);
 1043 }