LCOV - code coverage report
Current view: top level - providers/krb5 - krb5_common.c (source / functions) Hit Total Coverage
Test: .coverage.total Lines: 11 541 2.0 %
Date: 2015-10-19 Functions: 1 20 5.0 %

          Line data    Source code
       1             : /*
       2             :     SSSD
       3             : 
       4             :     Kerberos Provider Common Functions
       5             : 
       6             :     Authors:
       7             :         Sumit Bose <sbose@redhat.com>
       8             : 
       9             :     Copyright (C) 2008-2009 Red Hat
      10             : 
      11             :     This program is free software; you can redistribute it and/or modify
      12             :     it under the terms of the GNU General Public License as published by
      13             :     the Free Software Foundation; either version 3 of the License, or
      14             :     (at your option) any later version.
      15             : 
      16             :     This program is distributed in the hope that it will be useful,
      17             :     but WITHOUT ANY WARRANTY; without even the implied warranty of
      18             :     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      19             :     GNU General Public License for more details.
      20             : 
      21             :     You should have received a copy of the GNU General Public License
      22             :     along with this program.  If not, see <http://www.gnu.org/licenses/>.
      23             : */
      24             : #include <sys/types.h>
      25             : #include <sys/stat.h>
      26             : #include <unistd.h>
      27             : #include <netdb.h>
      28             : #include <arpa/inet.h>
      29             : #include <ctype.h>
      30             : 
      31             : #include "providers/dp_backend.h"
      32             : #include "providers/krb5/krb5_common.h"
      33             : #include "providers/krb5/krb5_opts.h"
      34             : #include "providers/krb5/krb5_utils.h"
      35             : 
      36             : #ifdef HAVE_KRB5_CC_COLLECTION
      37             : /* krb5 profile functions */
      38             : #include <profile.h>
      39             : #endif
      40             : 
      41           0 : errno_t check_and_export_lifetime(struct dp_option *opts, const int opt_id,
      42             :                                   const char *env_name)
      43             : {
      44             :     int ret;
      45             :     char *str;
      46             :     krb5_deltat lifetime;
      47           0 :     bool free_str = false;
      48             : 
      49           0 :     str = dp_opt_get_string(opts, opt_id);
      50           0 :     if (str == NULL || *str == '\0') {
      51           0 :         DEBUG(SSSDBG_FUNC_DATA, "No lifetime configured.\n");
      52           0 :         return EOK;
      53             :     }
      54             : 
      55           0 :     if (isdigit(str[strlen(str)-1])) {
      56           0 :         str = talloc_asprintf(opts, "%ss", str);
      57           0 :         if (str == NULL) {
      58           0 :             DEBUG(SSSDBG_CRIT_FAILURE, "talloc_asprintf failed\n");
      59           0 :             return ENOMEM;
      60             :         }
      61           0 :         free_str = true;
      62             : 
      63           0 :         ret = dp_opt_set_string(opts, opt_id, str);
      64           0 :         if (ret != EOK) {
      65           0 :             DEBUG(SSSDBG_CRIT_FAILURE, "dp_opt_set_string failed\n");
      66           0 :             goto done;
      67             :         }
      68             :     }
      69             : 
      70           0 :     ret = krb5_string_to_deltat(str, &lifetime);
      71           0 :     if (ret != 0) {
      72           0 :         DEBUG(SSSDBG_CRIT_FAILURE, "Invalid value [%s] for a lifetime.\n", str);
      73           0 :         ret = EINVAL;
      74           0 :         goto done;
      75             :     }
      76             : 
      77           0 :     ret = setenv(env_name, str, 1);
      78           0 :     if (ret != EOK) {
      79           0 :         ret = errno;
      80           0 :         DEBUG(SSSDBG_OP_FAILURE, "setenv [%s] failed.\n", env_name);
      81           0 :         goto done;
      82             :     }
      83             : 
      84           0 :     ret = EOK;
      85             : 
      86             : done:
      87           0 :     if (free_str) {
      88           0 :         talloc_free(str);
      89             :     }
      90             : 
      91           0 :     return ret;
      92             : }
      93             : 
      94             : #ifdef HAVE_KRB5_CC_COLLECTION
      95             : /* source default_ccache_name from krb5.conf */
      96           0 : static errno_t sss_get_system_ccname_template(TALLOC_CTX *mem_ctx,
      97             :                                               char **ccname)
      98             : {
      99             :     krb5_context ctx;
     100             :     profile_t p;
     101           0 :     char *value = NULL;
     102             :     long ret;
     103             : 
     104           0 :     *ccname = NULL;
     105             : 
     106           0 :     ret = krb5_init_context(&ctx);
     107           0 :     if (ret) return ret;
     108             : 
     109           0 :     ret = krb5_get_profile(ctx, &p);
     110           0 :     if (ret) goto done;
     111             : 
     112           0 :     ret = profile_get_string(p, "libdefaults", "default_ccache_name",
     113             :                              NULL, NULL, &value);
     114           0 :     if (ret) goto done;
     115             : 
     116           0 :     if (!value) {
     117           0 :         ret = ERR_NOT_FOUND;
     118           0 :         goto done;
     119             :     }
     120             : 
     121           0 :     *ccname = talloc_strdup(mem_ctx, value);
     122           0 :     if (*ccname == NULL) {
     123           0 :         ret = ENOMEM;
     124           0 :         goto done;
     125             :     }
     126             : 
     127           0 :     ret = EOK;
     128             : 
     129             : done:
     130           0 :     krb5_free_context(ctx);
     131           0 :     free(value);
     132           0 :     return ret;
     133             : }
     134             : #else
     135             : static errno_t sss_get_system_ccname_template(TALLOC_CTX *mem_ctx,
     136             :                                               char **ccname)
     137             : {
     138             :     DEBUG(SSSDBG_CONF_SETTINGS,
     139             :           "Your kerberos library does not support the default_ccache_name "
     140             :            "option or the profile library. Please use krb5_ccname_template "
     141             :            "in sssd.conf if you want to change the default\n");
     142             :     *ccname = NULL;
     143             :     return ERR_NOT_FOUND;
     144             : }
     145             : #endif
     146             : 
     147           0 : static void sss_check_cc_template(const char *cc_template)
     148             : {
     149             :     size_t template_len;
     150             : 
     151           0 :     template_len = strlen(cc_template);
     152           0 :     if (template_len >= 6 &&
     153           0 :         strcmp(cc_template + (template_len - 6), "XXXXXX") != 0) {
     154           0 :         DEBUG(SSSDBG_CONF_SETTINGS, "ccache file name template [%s] doesn't "
     155             :                    "contain randomizing characters (XXXXXX), file might not "
     156             :                    "be rewritable\n", cc_template);
     157             :     }
     158           0 : }
     159             : 
     160           0 : errno_t check_and_export_options(struct dp_option *opts,
     161             :                                  struct sss_domain_info *dom,
     162             :                                  struct krb5_ctx *krb5_ctx)
     163             : {
     164           0 :     TALLOC_CTX *tmp_ctx = NULL;
     165             :     int ret;
     166             :     const char *realm;
     167             :     const char *dummy;
     168             :     char *use_fast_str;
     169             :     char *fast_principal;
     170             :     char *ccname;
     171             : 
     172           0 :     tmp_ctx = talloc_new(NULL);
     173           0 :     if (!tmp_ctx) {
     174           0 :         ret = ENOMEM;
     175           0 :         goto done;
     176             :     }
     177             : 
     178           0 :     realm = dp_opt_get_cstring(opts, KRB5_REALM);
     179           0 :     if (realm == NULL) {
     180           0 :         ret = dp_opt_set_string(opts, KRB5_REALM, dom->name);
     181           0 :         if (ret != EOK) {
     182           0 :             DEBUG(SSSDBG_CRIT_FAILURE, "dp_opt_set_string failed.\n");
     183           0 :             goto done;
     184             :         }
     185           0 :         realm = dom->name;
     186             :     }
     187             : 
     188           0 :     ret = setenv(SSSD_KRB5_REALM, realm, 1);
     189           0 :     if (ret != EOK) {
     190           0 :         DEBUG(SSSDBG_OP_FAILURE,
     191             :               "setenv %s failed, authentication might fail.\n",
     192             :                   SSSD_KRB5_REALM);
     193             :     }
     194             : 
     195           0 :     ret = check_and_export_lifetime(opts, KRB5_RENEWABLE_LIFETIME,
     196             :                                     SSSD_KRB5_RENEWABLE_LIFETIME);
     197           0 :     if (ret != EOK) {
     198           0 :         DEBUG(SSSDBG_CRIT_FAILURE,
     199             :               "Failed to check value of krb5_renewable_lifetime. [%d][%s]\n",
     200             :                   ret, strerror(ret));
     201           0 :         goto done;
     202             :     }
     203             : 
     204           0 :     ret = check_and_export_lifetime(opts, KRB5_LIFETIME,
     205             :                                     SSSD_KRB5_LIFETIME);
     206           0 :     if (ret != EOK) {
     207           0 :         DEBUG(SSSDBG_CRIT_FAILURE,
     208             :               "Failed to check value of krb5_lifetime. [%d][%s]\n",
     209             :                   ret, strerror(ret));
     210           0 :         goto done;
     211             :     }
     212             : 
     213             : 
     214           0 :     use_fast_str = dp_opt_get_string(opts, KRB5_USE_FAST);
     215           0 :     if (use_fast_str != NULL) {
     216           0 :         ret = check_fast(use_fast_str, &krb5_ctx->use_fast);
     217           0 :         if (ret != EOK) {
     218           0 :             DEBUG(SSSDBG_CRIT_FAILURE, "check_fast failed.\n");
     219           0 :             goto done;
     220             :         }
     221             : 
     222           0 :         if (krb5_ctx->use_fast) {
     223           0 :             ret = setenv(SSSD_KRB5_USE_FAST, use_fast_str, 1);
     224           0 :             if (ret != EOK) {
     225           0 :                 DEBUG(SSSDBG_OP_FAILURE,
     226             :                       "setenv [%s] failed.\n", SSSD_KRB5_USE_FAST);
     227             :             } else {
     228           0 :                 fast_principal = dp_opt_get_string(opts, KRB5_FAST_PRINCIPAL);
     229           0 :                 if (fast_principal != NULL) {
     230           0 :                     ret = setenv(SSSD_KRB5_FAST_PRINCIPAL, fast_principal, 1);
     231           0 :                     if (ret != EOK) {
     232           0 :                         DEBUG(SSSDBG_OP_FAILURE,
     233             :                               "setenv [%s] failed.\n", SSSD_KRB5_FAST_PRINCIPAL);
     234             :                     }
     235             :                 }
     236             :             }
     237             :         }
     238             :     }
     239             : 
     240             :     /* In contrast to MIT KDCs AD does not automatically canonicalize the
     241             :      * enterprise principal in an AS request but requires the canonicalize
     242             :      * flags to be set. To be on the safe side we always enable
     243             :      * canonicalization if enterprise principals are used. */
     244           0 :     if (dp_opt_get_bool(opts, KRB5_CANONICALIZE)
     245           0 :             || dp_opt_get_bool(opts, KRB5_USE_ENTERPRISE_PRINCIPAL)) {
     246           0 :         ret = setenv(SSSD_KRB5_CANONICALIZE, "true", 1);
     247             :     } else {
     248           0 :         ret = setenv(SSSD_KRB5_CANONICALIZE, "false", 1);
     249             :     }
     250           0 :     if (ret != EOK) {
     251           0 :         DEBUG(SSSDBG_OP_FAILURE,
     252             :               "setenv [%s] failed.\n", SSSD_KRB5_CANONICALIZE);
     253             :     }
     254             : 
     255           0 :     dummy = dp_opt_get_cstring(opts, KRB5_KDC);
     256           0 :     if (dummy == NULL) {
     257           0 :         DEBUG(SSSDBG_CONF_SETTINGS, "No KDC explicitly configured, using defaults.\n");
     258             :     }
     259             : 
     260           0 :     dummy = dp_opt_get_cstring(opts, KRB5_KPASSWD);
     261           0 :     if (dummy == NULL) {
     262           0 :         DEBUG(SSSDBG_CONF_SETTINGS, "No kpasswd server explicitly configured, "
     263             :                                      "using the KDC or defaults.\n");
     264             :     }
     265             : 
     266           0 :     ccname = dp_opt_get_string(opts, KRB5_CCNAME_TMPL);
     267           0 :     if (ccname != NULL) {
     268           0 :         DEBUG(SSSDBG_CONF_SETTINGS,
     269             :               "The credential ccache name template has been explicitly set "
     270             :                "in sssd.conf, it is recommended to set default_ccache_name "
     271             :                "in krb5.conf instead so that a system default is used\n");
     272           0 :         ccname = talloc_strdup(tmp_ctx, ccname);
     273           0 :         if (!ccname) {
     274           0 :             ret = ENOMEM;
     275           0 :             goto done;
     276             :         }
     277             :     } else {
     278           0 :         ret = sss_get_system_ccname_template(tmp_ctx, &ccname);
     279           0 :         if (ret && ret != ERR_NOT_FOUND) {
     280           0 :             goto done;
     281             :         }
     282           0 :         if (ret == ERR_NOT_FOUND) {
     283             :             /* Use fallback default */
     284           0 :             ccname = talloc_strdup(tmp_ctx, DEFAULT_CCNAME_TEMPLATE);
     285           0 :             if (!ccname) {
     286           0 :                 ret = ENOMEM;
     287           0 :                 goto done;
     288             :             }
     289             :         }
     290             : 
     291             :         /* set back in opts */
     292           0 :         ret = dp_opt_set_string(opts, KRB5_CCNAME_TMPL, ccname);
     293           0 :         if (ret != EOK) {
     294           0 :             DEBUG(SSSDBG_CRIT_FAILURE, "dp_opt_set_string failed.\n");
     295           0 :             goto done;
     296             :         }
     297             :     }
     298             : 
     299           0 :     if ((ccname[0] == '/') || (strncmp(ccname, "FILE:", 5) == 0)) {
     300           0 :         DEBUG(SSSDBG_CONF_SETTINGS, "ccache is of type FILE\n");
     301             :         /* warn if the file type (which is usally created in a sticky bit
     302             :          * laden directory) does not have randomizing chracters */
     303           0 :         sss_check_cc_template(ccname);
     304             : 
     305           0 :         if (ccname[0] == '/') {
     306             :             /* /path/to/cc  prepend FILE: */
     307           0 :             DEBUG(SSSDBG_CONF_SETTINGS, "The ccname template was "
     308             :               "missing an explicit type, but is an absolute "
     309             :               "path specifier. Assuming FILE:\n");
     310             : 
     311           0 :             ccname = talloc_asprintf(tmp_ctx, "FILE:%s", ccname);
     312           0 :             if (!ccname) {
     313           0 :                 ret = ENOMEM;
     314           0 :                 goto done;
     315             :             }
     316             : 
     317           0 :             ret = dp_opt_set_string(opts, KRB5_CCNAME_TMPL, ccname);
     318           0 :             if (ret != EOK) {
     319           0 :                 DEBUG(SSSDBG_CRIT_FAILURE, "dp_opt_set_string failed.\n");
     320           0 :                 goto done;
     321             :             }
     322             :         }
     323             :     }
     324             : 
     325           0 :     ret = EOK;
     326             : 
     327             : done:
     328           0 :     talloc_free(tmp_ctx);
     329           0 :     return ret;
     330             : }
     331             : 
     332           0 : errno_t krb5_try_kdcip(struct confdb_ctx *cdb, const char *conf_path,
     333             :                        struct dp_option *opts, int opt_id)
     334             : {
     335           0 :     char *krb5_servers = NULL;
     336             :     errno_t ret;
     337             : 
     338           0 :     krb5_servers = dp_opt_get_string(opts, opt_id);
     339           0 :     if (krb5_servers == NULL) {
     340           0 :         DEBUG(SSSDBG_CONF_SETTINGS,
     341             :               "No KDC found in configuration, trying legacy option\n");
     342           0 :         ret = confdb_get_string(cdb, NULL, conf_path,
     343             :                                 "krb5_kdcip", NULL, &krb5_servers);
     344           0 :         if (ret != EOK) {
     345           0 :             DEBUG(SSSDBG_CRIT_FAILURE, "confdb_get_string failed.\n");
     346           0 :             return ret;
     347             :         }
     348             : 
     349           0 :         if (krb5_servers != NULL)
     350             :         {
     351           0 :             ret = dp_opt_set_string(opts, opt_id, krb5_servers);
     352           0 :             if (ret != EOK) {
     353           0 :                 DEBUG(SSSDBG_CRIT_FAILURE, "dp_opt_set_string failed.\n");
     354           0 :                 talloc_free(krb5_servers);
     355           0 :                 return ret;
     356             :             }
     357             : 
     358           0 :             DEBUG(SSSDBG_CONF_SETTINGS,
     359             :                   "Set krb5 server [%s] based on legacy krb5_kdcip option\n",
     360             :                    krb5_servers);
     361           0 :             DEBUG(SSSDBG_FATAL_FAILURE,
     362             :                   "Your configuration uses the deprecated option "
     363             :                    "'krb5_kdcip' to specify the KDC. Please change the "
     364             :                    "configuration to use the 'krb5_server' option "
     365             :                    "instead.\n");
     366           0 :             talloc_free(krb5_servers);
     367             :         }
     368             :     }
     369             : 
     370           0 :     return EOK;
     371             : }
     372             : 
     373           0 : errno_t krb5_get_options(TALLOC_CTX *memctx, struct confdb_ctx *cdb,
     374             :                          const char *conf_path, struct dp_option **_opts)
     375             : {
     376             :     int ret;
     377             :     struct dp_option *opts;
     378             : 
     379           0 :     opts = talloc_zero(memctx, struct dp_option);
     380           0 :     if (opts == NULL) {
     381           0 :         DEBUG(SSSDBG_CRIT_FAILURE, "talloc_zero failed.\n");
     382           0 :         return ENOMEM;
     383             :     }
     384             : 
     385           0 :     ret = dp_get_options(opts, cdb, conf_path, default_krb5_opts,
     386             :                          KRB5_OPTS, &opts);
     387           0 :     if (ret != EOK) {
     388           0 :         DEBUG(SSSDBG_CRIT_FAILURE, "dp_get_options failed.\n");
     389           0 :         goto done;
     390             :     }
     391             : 
     392             :     /* If there is no KDC, try the deprecated krb5_kdcip option, too */
     393             :     /* FIXME - this can be removed in a future version */
     394           0 :     ret = krb5_try_kdcip(cdb, conf_path, opts, KRB5_KDC);
     395           0 :     if (ret != EOK) {
     396           0 :         DEBUG(SSSDBG_CRIT_FAILURE, "sss_krb5_try_kdcip failed.\n");
     397           0 :         goto done;
     398             :     }
     399             : 
     400           0 :     *_opts = opts;
     401           0 :     ret = EOK;
     402             : 
     403             : done:
     404           0 :     if (ret != EOK) {
     405           0 :         talloc_zfree(opts);
     406             :     }
     407             : 
     408           0 :     return ret;
     409             : }
     410             : 
     411           0 : errno_t write_krb5info_file(const char *realm, const char *server,
     412             :                             const char *service)
     413             : {
     414             :     int ret;
     415           0 :     int fd = -1;
     416           0 :     char *tmp_name = NULL;
     417           0 :     char *krb5info_name = NULL;
     418           0 :     TALLOC_CTX *tmp_ctx = NULL;
     419           0 :     const char *name_tmpl = NULL;
     420             :     size_t server_len;
     421             :     ssize_t written;
     422             : 
     423           0 :     if (realm == NULL || *realm == '\0' || server == NULL || *server == '\0' ||
     424           0 :         service == NULL || *service == '\0') {
     425           0 :         DEBUG(SSSDBG_CRIT_FAILURE,
     426             :               "Missing or empty realm, server or service.\n");
     427           0 :         return EINVAL;
     428             :     }
     429             : 
     430           0 :     if (sss_krb5_realm_has_proxy(realm)) {
     431           0 :         DEBUG(SSSDBG_CONF_SETTINGS,
     432             :               "KDC Proxy available for realm [%s], no kdcinfo file created.\n",
     433             :               realm);
     434           0 :         return EOK;
     435             :     }
     436             : 
     437           0 :     if (strcmp(service, SSS_KRB5KDC_FO_SRV) == 0) {
     438           0 :         name_tmpl = KDCINFO_TMPL;
     439           0 :     } else if (strcmp(service, SSS_KRB5KPASSWD_FO_SRV) == 0) {
     440           0 :         name_tmpl = KPASSWDINFO_TMPL;
     441             :     } else {
     442           0 :         DEBUG(SSSDBG_CRIT_FAILURE, "Unsupported service [%s].\n", service);
     443           0 :         return EINVAL;
     444             :     }
     445             : 
     446           0 :     server_len = strlen(server);
     447             : 
     448           0 :     tmp_ctx = talloc_new(NULL);
     449           0 :     if (tmp_ctx == NULL) {
     450           0 :         DEBUG(SSSDBG_CRIT_FAILURE, "talloc_new failed.\n");
     451           0 :         return ENOMEM;
     452             :     }
     453             : 
     454           0 :     tmp_name = talloc_asprintf(tmp_ctx, PUBCONF_PATH"/.krb5info_dummy_XXXXXX");
     455           0 :     if (tmp_name == NULL) {
     456           0 :         DEBUG(SSSDBG_CRIT_FAILURE, "talloc_asprintf failed.\n");
     457           0 :         ret = ENOMEM;
     458           0 :         goto done;
     459             :     }
     460             : 
     461           0 :     krb5info_name = talloc_asprintf(tmp_ctx, name_tmpl, realm);
     462           0 :     if (krb5info_name == NULL) {
     463           0 :         DEBUG(SSSDBG_CRIT_FAILURE, "talloc_asprintf failed.\n");
     464           0 :         ret = ENOMEM;
     465           0 :         goto done;
     466             :     }
     467             : 
     468           0 :     fd = sss_unique_file(tmp_ctx, tmp_name, &ret);
     469           0 :     if (fd == -1) {
     470           0 :         DEBUG(SSSDBG_CRIT_FAILURE,
     471             :               "sss_unique_file failed [%d][%s].\n", ret, strerror(ret));
     472           0 :         goto done;
     473             :     }
     474             : 
     475           0 :     errno = 0;
     476           0 :     written = sss_atomic_write_s(fd, discard_const(server), server_len);
     477           0 :     if (written == -1) {
     478           0 :         ret = errno;
     479           0 :         DEBUG(SSSDBG_CRIT_FAILURE,
     480             :               "write failed [%d][%s].\n", ret, strerror(ret));
     481           0 :         goto done;
     482             :     }
     483             : 
     484           0 :     if (written != server_len) {
     485           0 :         DEBUG(SSSDBG_CRIT_FAILURE,
     486             :               "Write error, wrote [%zd] bytes, expected [%zu]\n",
     487             :                written, server_len);
     488           0 :         ret = EIO;
     489           0 :         goto done;
     490             :     }
     491             : 
     492           0 :     ret = fchmod(fd, S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH);
     493           0 :     if (ret == -1) {
     494           0 :         ret = errno;
     495           0 :         DEBUG(SSSDBG_CRIT_FAILURE,
     496             :               "fchmod failed [%d][%s].\n", ret, strerror(ret));
     497           0 :         goto done;
     498             :     }
     499             : 
     500           0 :     ret = close(fd);
     501           0 :     fd = -1;
     502           0 :     if (ret == -1) {
     503           0 :         ret = errno;
     504           0 :         DEBUG(SSSDBG_CRIT_FAILURE,
     505             :               "close failed [%d][%s].\n", ret, strerror(ret));
     506           0 :         goto done;
     507             :     }
     508             : 
     509           0 :     ret = rename(tmp_name, krb5info_name);
     510           0 :     if (ret == -1) {
     511           0 :         ret = errno;
     512           0 :         DEBUG(SSSDBG_CRIT_FAILURE,
     513             :               "rename failed [%d][%s].\n", ret, strerror(ret));
     514           0 :         goto done;
     515             :     }
     516             : 
     517           0 :     ret = EOK;
     518             : done:
     519           0 :     if (fd != -1) {
     520           0 :         close(fd);
     521             :     }
     522             : 
     523           0 :     talloc_free(tmp_ctx);
     524           0 :     return ret;
     525             : }
     526             : 
     527           0 : static void krb5_resolve_callback(void *private_data, struct fo_server *server)
     528             : {
     529             :     struct krb5_service *krb5_service;
     530             :     struct resolv_hostent *srvaddr;
     531             :     char *address;
     532             :     char *safe_address;
     533             :     int ret;
     534           0 :     TALLOC_CTX *tmp_ctx = NULL;
     535             : 
     536           0 :     tmp_ctx = talloc_new(NULL);
     537           0 :     if (tmp_ctx == NULL) {
     538           0 :         DEBUG(SSSDBG_CRIT_FAILURE, "talloc_new failed\n");
     539           0 :         return;
     540             :     }
     541             : 
     542           0 :     krb5_service = talloc_get_type(private_data, struct krb5_service);
     543           0 :     if (!krb5_service) {
     544           0 :         DEBUG(SSSDBG_CRIT_FAILURE, "FATAL: Bad private_data\n");
     545           0 :         talloc_free(tmp_ctx);
     546           0 :         return;
     547             :     }
     548             : 
     549           0 :     srvaddr = fo_get_server_hostent(server);
     550           0 :     if (!srvaddr) {
     551           0 :         DEBUG(SSSDBG_CRIT_FAILURE,
     552             :               "FATAL: No hostent available for server (%s)\n",
     553             :                   fo_get_server_str_name(server));
     554           0 :         talloc_free(tmp_ctx);
     555           0 :         return;
     556             :     }
     557             : 
     558           0 :     address = resolv_get_string_address(tmp_ctx, srvaddr);
     559           0 :     if (address == NULL) {
     560           0 :         DEBUG(SSSDBG_CRIT_FAILURE, "resolv_get_string_address failed.\n");
     561           0 :         talloc_free(tmp_ctx);
     562           0 :         return;
     563             :     }
     564             : 
     565           0 :     safe_address = sss_escape_ip_address(tmp_ctx,
     566             :                                          srvaddr->family,
     567             :                                          address);
     568           0 :     if (safe_address == NULL) {
     569           0 :         DEBUG(SSSDBG_CRIT_FAILURE, "sss_escape_ip_address failed.\n");
     570           0 :         talloc_free(tmp_ctx);
     571           0 :         return;
     572             :     }
     573             : 
     574           0 :     if (krb5_service->write_kdcinfo) {
     575           0 :         safe_address = talloc_asprintf_append(safe_address, ":%d",
     576             :                                             fo_get_server_port(server));
     577           0 :         if (safe_address == NULL) {
     578           0 :             DEBUG(SSSDBG_CRIT_FAILURE, "talloc_asprintf_append failed.\n");
     579           0 :             talloc_free(tmp_ctx);
     580           0 :             return;
     581             :         }
     582             : 
     583           0 :         ret = write_krb5info_file(krb5_service->realm, safe_address,
     584           0 :                                   krb5_service->name);
     585           0 :         if (ret != EOK) {
     586           0 :             DEBUG(SSSDBG_OP_FAILURE,
     587             :                   "write_krb5info_file failed, authentication might fail.\n");
     588             :         }
     589             :     }
     590             : 
     591           0 :     talloc_free(tmp_ctx);
     592           0 :     return;
     593             : }
     594             : 
     595           0 : static errno_t _krb5_servers_init(struct be_ctx *ctx,
     596             :                                   struct krb5_service *service,
     597             :                                   const char *service_name,
     598             :                                   const char *servers,
     599             :                                   bool primary)
     600             : {
     601             :     TALLOC_CTX *tmp_ctx;
     602           0 :     char **list = NULL;
     603           0 :     errno_t ret = 0;
     604             :     int i;
     605             :     char *port_str;
     606             :     long port;
     607             :     char *server_spec;
     608             :     char *endptr;
     609             :     struct servent *servent;
     610             : 
     611           0 :     tmp_ctx = talloc_new(NULL);
     612           0 :     if (!tmp_ctx) {
     613           0 :         return ENOMEM;
     614             :     }
     615             : 
     616           0 :     ret = split_on_separator(tmp_ctx, servers, ',', true, true, &list, NULL);
     617           0 :     if (ret != EOK) {
     618           0 :         DEBUG(SSSDBG_CRIT_FAILURE, "Failed to parse server list!\n");
     619           0 :         goto done;
     620             :     }
     621             : 
     622           0 :     for (i = 0; list[i]; i++) {
     623           0 :         talloc_steal(service, list[i]);
     624           0 :         server_spec = talloc_strdup(service, list[i]);
     625           0 :         if (!server_spec) {
     626           0 :             ret = ENOMEM;
     627           0 :             goto done;
     628             :         }
     629             : 
     630           0 :         if (be_fo_is_srv_identifier(server_spec)) {
     631           0 :             if (!primary) {
     632           0 :                 DEBUG(SSSDBG_MINOR_FAILURE,
     633             :                       "Failed to add server [%s] to failover service: "
     634             :                        "SRV resolution only allowed for primary servers!\n",
     635             :                        list[i]);
     636           0 :                 continue;
     637             :             }
     638             : 
     639           0 :             ret = be_fo_add_srv_server(ctx, service_name, service_name, NULL,
     640             :                                        BE_FO_PROTO_UDP, true, NULL);
     641           0 :             if (ret) {
     642           0 :                 DEBUG(SSSDBG_FATAL_FAILURE, "Failed to add server\n");
     643           0 :                 goto done;
     644             :             }
     645             : 
     646           0 :             DEBUG(SSSDBG_TRACE_FUNC, "Added service lookup\n");
     647           0 :             continue;
     648             :         }
     649             : 
     650             :         /* Do not try to get port number if last character is ']' */
     651           0 :         if (server_spec[strlen(server_spec) - 1] != ']') {
     652           0 :             port_str = strrchr(server_spec, ':');
     653             :         } else {
     654           0 :             port_str = NULL;
     655             :         }
     656             : 
     657           0 :         if (port_str == NULL) {
     658           0 :             port = 0;
     659             :         } else {
     660           0 :             *port_str = '\0';
     661           0 :             ++port_str;
     662           0 :             if (isdigit(*port_str)) {
     663           0 :                 errno = 0;
     664           0 :                 port = strtol(port_str, &endptr, 10);
     665           0 :                 if (errno != 0) {
     666           0 :                     ret = errno;
     667           0 :                     DEBUG(SSSDBG_CRIT_FAILURE, "strtol failed on [%s]: [%d][%s].\n", port_str,
     668             :                               ret, strerror(ret));
     669           0 :                     goto done;
     670             :                 }
     671           0 :                 if (*endptr != '\0') {
     672           0 :                     DEBUG(SSSDBG_CRIT_FAILURE, "Found additional characters [%s] in port number "
     673             :                               "[%s].\n", endptr, port_str);
     674           0 :                     ret = EINVAL;
     675           0 :                     goto done;
     676             :                 }
     677             : 
     678           0 :                 if (port < 1 || port > 65535) {
     679           0 :                     DEBUG(SSSDBG_CRIT_FAILURE, "Illegal port number [%ld].\n", port);
     680           0 :                     ret = EINVAL;
     681           0 :                     goto done;
     682             :                 }
     683           0 :             } else if (isalpha(*port_str)) {
     684           0 :                 servent = getservbyname(port_str, NULL);
     685           0 :                 if (servent == NULL) {
     686           0 :                     DEBUG(SSSDBG_CRIT_FAILURE, "getservbyname cannot find service [%s].\n",
     687             :                               port_str);
     688           0 :                     ret = EINVAL;
     689           0 :                     goto done;
     690             :                 }
     691             : 
     692           0 :                 port = servent->s_port;
     693             :             } else {
     694           0 :                 DEBUG(SSSDBG_CRIT_FAILURE, "Unsupported port specifier in [%s].\n", list[i]);
     695           0 :                 ret = EINVAL;
     696           0 :                 goto done;
     697             :             }
     698             :         }
     699             : 
     700             :         /* It could be ipv6 address in square brackets. Remove
     701             :          * the brackets if needed. */
     702           0 :         ret = remove_ipv6_brackets(server_spec);
     703           0 :         if (ret != EOK) {
     704           0 :             goto done;
     705             :         }
     706             : 
     707           0 :         ret = be_fo_add_server(ctx, service_name, server_spec, (int) port,
     708           0 :                                list[i], primary);
     709           0 :         if (ret && ret != EEXIST) {
     710           0 :             DEBUG(SSSDBG_FATAL_FAILURE, "Failed to add server\n");
     711           0 :             goto done;
     712             :         }
     713             : 
     714           0 :         DEBUG(SSSDBG_TRACE_FUNC, "Added Server %s\n", list[i]);
     715             :     }
     716             : 
     717             : done:
     718           0 :     talloc_free(tmp_ctx);
     719           0 :     return ret;
     720             : }
     721             : 
     722             : static inline errno_t
     723           0 : krb5_primary_servers_init(struct be_ctx *ctx, struct krb5_service *service,
     724             :                           const char *service_name, const char *servers)
     725             : {
     726           0 :     return _krb5_servers_init(ctx, service, service_name, servers, true);
     727             : }
     728             : 
     729             : static inline errno_t
     730           0 : krb5_backup_servers_init(struct be_ctx *ctx, struct krb5_service *service,
     731             :                          const char *service_name, const char *servers)
     732             : {
     733           0 :     return _krb5_servers_init(ctx, service, service_name, servers, false);
     734             : }
     735             : 
     736           0 : static int krb5_user_data_cmp(void *ud1, void *ud2)
     737             : {
     738           0 :     return strcasecmp((char*) ud1, (char*) ud2);
     739             : }
     740             : 
     741           0 : int krb5_service_init(TALLOC_CTX *memctx, struct be_ctx *ctx,
     742             :                       const char *service_name,
     743             :                       const char *primary_servers,
     744             :                       const char *backup_servers,
     745             :                       const char *realm,
     746             :                       bool use_kdcinfo,
     747             :                       struct krb5_service **_service)
     748             : {
     749             :     TALLOC_CTX *tmp_ctx;
     750             :     struct krb5_service *service;
     751             :     int ret;
     752             : 
     753           0 :     tmp_ctx = talloc_new(memctx);
     754           0 :     if (!tmp_ctx) {
     755           0 :         return ENOMEM;
     756             :     }
     757             : 
     758           0 :     service = talloc_zero(tmp_ctx, struct krb5_service);
     759           0 :     if (!service) {
     760           0 :         ret = ENOMEM;
     761           0 :         goto done;
     762             :     }
     763             : 
     764           0 :     ret = be_fo_add_service(ctx, service_name, krb5_user_data_cmp);
     765           0 :     if (ret != EOK) {
     766           0 :         DEBUG(SSSDBG_CRIT_FAILURE, "Failed to create failover service!\n");
     767           0 :         goto done;
     768             :     }
     769             : 
     770           0 :     service->name = talloc_strdup(service, service_name);
     771           0 :     if (!service->name) {
     772           0 :         ret = ENOMEM;
     773           0 :         goto done;
     774             :     }
     775             : 
     776           0 :     service->realm = talloc_strdup(service, realm);
     777           0 :     if (!service->realm) {
     778           0 :         ret = ENOMEM;
     779           0 :         goto done;
     780             :     }
     781             : 
     782           0 :     service->write_kdcinfo = use_kdcinfo;
     783             : 
     784           0 :     if (!primary_servers) {
     785           0 :         DEBUG(SSSDBG_CONF_SETTINGS,
     786             :               "No primary servers defined, using service discovery\n");
     787           0 :         primary_servers = BE_SRV_IDENTIFIER;
     788             :     }
     789             : 
     790           0 :     ret = krb5_primary_servers_init(ctx, service, service_name, primary_servers);
     791           0 :     if (ret != EOK) {
     792           0 :         goto done;
     793             :     }
     794             : 
     795           0 :     if (backup_servers) {
     796           0 :         ret = krb5_backup_servers_init(ctx, service, service_name,
     797             :                                        backup_servers);
     798           0 :         if (ret != EOK) {
     799           0 :             goto done;
     800             :         }
     801             :     }
     802             : 
     803           0 :     ret = be_fo_service_add_callback(memctx, ctx, service_name,
     804             :                                      krb5_resolve_callback, service);
     805           0 :     if (ret != EOK) {
     806           0 :         DEBUG(SSSDBG_CRIT_FAILURE, "Failed to add failover callback!\n");
     807           0 :         goto done;
     808             :     }
     809             : 
     810           0 :     ret = EOK;
     811             : 
     812             : done:
     813           0 :     if (ret == EOK) {
     814           0 :         *_service = talloc_steal(memctx, service);
     815             :     }
     816           0 :     talloc_zfree(tmp_ctx);
     817           0 :     return ret;
     818             : }
     819             : 
     820             : 
     821           0 : errno_t remove_krb5_info_files(TALLOC_CTX *mem_ctx, const char *realm)
     822             : {
     823             :     int ret;
     824             :     errno_t err;
     825             :     char *file;
     826             : 
     827           0 :     file = talloc_asprintf(mem_ctx, KDCINFO_TMPL, realm);
     828           0 :     if(file == NULL) {
     829           0 :         DEBUG(SSSDBG_CRIT_FAILURE, "talloc_asprintf failed.\n");
     830           0 :         return ENOMEM;
     831             :     }
     832             : 
     833           0 :     errno = 0;
     834           0 :     ret = unlink(file);
     835           0 :     if (ret == -1) {
     836           0 :         err = errno;
     837           0 :         DEBUG(SSSDBG_FUNC_DATA, "Could not remove [%s], [%d][%s]\n", file,
     838             :                   err, strerror(err));
     839             :     }
     840             : 
     841           0 :     file = talloc_asprintf(mem_ctx, KPASSWDINFO_TMPL, realm);
     842           0 :     if(file == NULL) {
     843           0 :         DEBUG(SSSDBG_CRIT_FAILURE, "talloc_asprintf failed.\n");
     844           0 :         return ENOMEM;
     845             :     }
     846             : 
     847           0 :     errno = 0;
     848           0 :     ret = unlink(file);
     849           0 :     if (ret == -1) {
     850           0 :         err = errno;
     851           0 :         DEBUG(SSSDBG_FUNC_DATA, "Could not remove [%s], [%d][%s]\n", file,
     852             :                   err, strerror(err));
     853             :     }
     854             : 
     855           0 :     return EOK;
     856             : }
     857             : 
     858           0 : void remove_krb5_info_files_callback(void *pvt)
     859             : {
     860             :     int ret;
     861           0 :     TALLOC_CTX *tmp_ctx = NULL;
     862           0 :     struct remove_info_files_ctx *ctx = talloc_get_type(pvt,
     863             :                                                   struct remove_info_files_ctx);
     864             : 
     865           0 :     ret = be_fo_run_callbacks_at_next_request(ctx->be_ctx,
     866             :                                               ctx->kdc_service_name);
     867           0 :     if (ret != EOK) {
     868           0 :         DEBUG(SSSDBG_CRIT_FAILURE,
     869             :               "be_fo_run_callbacks_at_next_request failed, "
     870             :                   "krb5 info files will not be removed, because "
     871             :                   "it is unclear if they will be recreated properly.\n");
     872           0 :         return;
     873             :     }
     874           0 :     if (ctx->kpasswd_service_name != NULL) {
     875           0 :         ret = be_fo_run_callbacks_at_next_request(ctx->be_ctx,
     876             :                                             ctx->kpasswd_service_name);
     877           0 :         if (ret != EOK) {
     878           0 :             DEBUG(SSSDBG_CRIT_FAILURE,
     879             :                   "be_fo_run_callbacks_at_next_request failed, "
     880             :                       "krb5 info files will not be removed, because "
     881             :                       "it is unclear if they will be recreated properly.\n");
     882           0 :             return;
     883             :         }
     884             :     }
     885             : 
     886           0 :     tmp_ctx = talloc_new(NULL);
     887           0 :     if (tmp_ctx == NULL) {
     888           0 :         DEBUG(SSSDBG_CRIT_FAILURE,
     889             :               "talloc_new failed, cannot remove krb5 info files.\n");
     890           0 :         return;
     891             :     }
     892             : 
     893           0 :     ret = remove_krb5_info_files(tmp_ctx, ctx->realm);
     894           0 :     if (ret != EOK) {
     895           0 :         DEBUG(SSSDBG_CRIT_FAILURE, "remove_krb5_info_files failed.\n");
     896             :     }
     897             : 
     898           0 :     talloc_zfree(tmp_ctx);
     899             : }
     900             : 
     901           0 : void krb5_finalize(struct tevent_context *ev,
     902             :                    struct tevent_signal *se,
     903             :                    int signum,
     904             :                    int count,
     905             :                    void *siginfo,
     906             :                    void *private_data)
     907             : {
     908           0 :     char *realm = (char *)private_data;
     909             :     int ret;
     910             : 
     911           0 :     ret = remove_krb5_info_files(se, realm);
     912           0 :     if (ret != EOK) {
     913           0 :         DEBUG(SSSDBG_CRIT_FAILURE, "remove_krb5_info_files failed.\n");
     914             :     }
     915             : 
     916           0 :     orderly_shutdown(0);
     917           0 : }
     918             : 
     919           0 : errno_t krb5_install_offline_callback(struct be_ctx *be_ctx,
     920             :                                       struct krb5_ctx *krb5_ctx)
     921             : {
     922             :     int ret;
     923             :     struct remove_info_files_ctx *ctx;
     924             :     const char *krb5_realm;
     925             : 
     926           0 :     if (krb5_ctx->service == NULL || krb5_ctx->service->name == NULL) {
     927           0 :         DEBUG(SSSDBG_CRIT_FAILURE, "Missing KDC service name!\n");
     928           0 :         return EINVAL;
     929             :     }
     930             : 
     931           0 :     ctx = talloc_zero(krb5_ctx, struct remove_info_files_ctx);
     932           0 :     if (ctx == NULL) {
     933           0 :         DEBUG(SSSDBG_CRIT_FAILURE, "talloc_zfree failed.\n");
     934           0 :         return ENOMEM;
     935             :     }
     936             : 
     937           0 :     krb5_realm = dp_opt_get_cstring(krb5_ctx->opts, KRB5_REALM);
     938           0 :     if (krb5_realm == NULL) {
     939           0 :         DEBUG(SSSDBG_CRIT_FAILURE, "Missing krb5_realm option!\n");
     940           0 :         ret = EINVAL;
     941           0 :         goto done;
     942             :     }
     943             : 
     944           0 :     ctx->realm = talloc_strdup(ctx, krb5_realm);
     945           0 :     if (ctx->realm == NULL) {
     946           0 :         DEBUG(SSSDBG_CRIT_FAILURE, "talloc_strdup failed!\n");
     947           0 :         ret = ENOMEM;
     948           0 :         goto done;
     949             :     }
     950             : 
     951           0 :     ctx->be_ctx = be_ctx;
     952           0 :     ctx->kdc_service_name = krb5_ctx->service->name;
     953           0 :     if (krb5_ctx->kpasswd_service == NULL) {
     954           0 :         ctx->kpasswd_service_name =NULL;
     955             :     } else {
     956           0 :         ctx->kpasswd_service_name = krb5_ctx->kpasswd_service->name;
     957             :     }
     958             : 
     959           0 :     ret = be_add_offline_cb(ctx, be_ctx, remove_krb5_info_files_callback, ctx,
     960             :                             NULL);
     961           0 :     if (ret != EOK) {
     962           0 :         DEBUG(SSSDBG_CRIT_FAILURE, "be_add_offline_cb failed.\n");
     963           0 :         goto done;
     964             :     }
     965             : 
     966           0 :     ret = EOK;
     967             : 
     968             : done:
     969           0 :     if (ret != EOK) {
     970           0 :         talloc_zfree(ctx);
     971             :     }
     972             : 
     973           0 :     return ret;
     974             : }
     975             : 
     976           0 : errno_t krb5_install_sigterm_handler(struct tevent_context *ev,
     977             :                                      struct krb5_ctx *krb5_ctx)
     978             : {
     979             :     const char *krb5_realm;
     980             :     char *sig_realm;
     981             :     struct tevent_signal *sige;
     982             : 
     983           0 :     BlockSignals(false, SIGTERM);
     984             : 
     985           0 :     krb5_realm = dp_opt_get_cstring(krb5_ctx->opts, KRB5_REALM);
     986           0 :     if (krb5_realm == NULL) {
     987           0 :         DEBUG(SSSDBG_CRIT_FAILURE, "Missing krb5_realm option!\n");
     988           0 :         return EINVAL;
     989             :     }
     990             : 
     991           0 :     sig_realm = talloc_strdup(krb5_ctx, krb5_realm);
     992           0 :     if (sig_realm == NULL) {
     993           0 :         DEBUG(SSSDBG_CRIT_FAILURE, "talloc_strdup failed!\n");
     994           0 :         return ENOMEM;
     995             :     }
     996             : 
     997           0 :     sige = tevent_add_signal(ev, krb5_ctx, SIGTERM, SA_SIGINFO, krb5_finalize,
     998             :                              sig_realm);
     999           0 :     if (sige == NULL) {
    1000           0 :         DEBUG(SSSDBG_CRIT_FAILURE, "tevent_add_signal failed.\n");
    1001           0 :         talloc_free(sig_realm);
    1002           0 :         return ENOMEM;
    1003             :     }
    1004           0 :     talloc_steal(sige, sig_realm);
    1005             : 
    1006           0 :     return EOK;
    1007             : }
    1008             : 
    1009           0 : errno_t krb5_get_simple_upn(TALLOC_CTX *mem_ctx, struct krb5_ctx *krb5_ctx,
    1010             :                             struct sss_domain_info *dom, const char *username,
    1011             :                             const char *user_dom, char **_upn)
    1012             : {
    1013           0 :     const char *realm = NULL;
    1014           0 :     char *uc_dom = NULL;
    1015             :     char *upn;
    1016             :     char *name;
    1017           0 :     TALLOC_CTX *tmp_ctx = NULL;
    1018             :     errno_t ret;
    1019             : 
    1020           0 :     tmp_ctx = talloc_new(NULL);
    1021           0 :     if (tmp_ctx == NULL) {
    1022           0 :         DEBUG(SSSDBG_CRIT_FAILURE, "talloc_new failed.\n");
    1023           0 :         return ENOMEM;
    1024             :     }
    1025             : 
    1026           0 :     if (user_dom != NULL && dom->name != NULL &&
    1027           0 :         strcasecmp(dom->name, user_dom) != 0) {
    1028           0 :         uc_dom = get_uppercase_realm(tmp_ctx, user_dom);
    1029           0 :         if (uc_dom == NULL) {
    1030           0 :             DEBUG(SSSDBG_OP_FAILURE, "get_uppercase_realm failed.\n");
    1031           0 :             ret = ENOMEM;
    1032           0 :             goto done;
    1033             :         }
    1034             :     } else {
    1035           0 :         realm = dp_opt_get_cstring(krb5_ctx->opts, KRB5_REALM);
    1036           0 :         if (realm == NULL) {
    1037           0 :             DEBUG(SSSDBG_OP_FAILURE, "Missing Kerberos realm.\n");
    1038           0 :             ret = ENOMEM;
    1039           0 :             goto done;
    1040             :         }
    1041             :     }
    1042             : 
    1043             :     /* Subdomains already have a fully qualified name, which contains
    1044             :      * the domain name. We need to replace it with the realm name
    1045             :      */
    1046           0 :     ret = sss_parse_name(tmp_ctx, dom->names, username, NULL, &name);
    1047           0 :     if (ret != EOK) {
    1048           0 :         DEBUG(SSSDBG_OP_FAILURE,
    1049             :               "Could not parse [%s] into name and "
    1050             :                "domain components, login might fail\n", username);
    1051           0 :         name = discard_const(username);
    1052             :     }
    1053             : 
    1054             :     /* NOTE: this is a hack, works only in some environments */
    1055           0 :     upn = talloc_asprintf(tmp_ctx, "%s@%s",  name,
    1056             :                                              realm != NULL ? realm : uc_dom);
    1057           0 :     if (upn == NULL) {
    1058           0 :         DEBUG(SSSDBG_CRIT_FAILURE, "talloc_asprintf failed.\n");
    1059           0 :         ret = ENOMEM;
    1060           0 :         goto done;
    1061             :     }
    1062             : 
    1063           0 :     DEBUG(SSSDBG_TRACE_ALL, "Using simple UPN [%s].\n", upn);
    1064             : 
    1065           0 :     *_upn = talloc_steal(mem_ctx, upn);
    1066           0 :     ret = EOK;
    1067             : done:
    1068           0 :     talloc_free(tmp_ctx);
    1069           0 :     return ret;
    1070             : }
    1071             : 
    1072          10 : errno_t compare_principal_realm(const char *upn, const char *realm,
    1073             :                                 bool *different_realm)
    1074             : {
    1075             :     char *at_sign;
    1076             : 
    1077          17 :     if (upn == NULL || realm == NULL || different_realm == NULL ||
    1078          13 :         *upn == '\0' || *realm == '\0') {
    1079           5 :         return EINVAL;
    1080             :     }
    1081             : 
    1082           5 :     at_sign = strchr(upn, '@');
    1083             : 
    1084           5 :     if (at_sign == NULL) {
    1085           2 :         return EINVAL;
    1086             :     }
    1087             : 
    1088           3 :     if (strcmp(realm, at_sign + 1) == 0) {
    1089           1 :         *different_realm = false;
    1090             :     } else {
    1091           2 :         *different_realm = true;
    1092             :     }
    1093             : 
    1094           3 :     return EOK;
    1095             : }

Generated by: LCOV version 1.10