"Fossies" - the Fresh Open Source Software Archive

Member "nss_ldap-265/ldap-init-krb5-cache.c" (6 Nov 2009, 32535 Bytes) of package /linux/privat/old/nss_ldap-265.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.

    1 /* Copyright (C) 2007 Howard Wilkinson
    2    Copyright (C) 2007 Markus Moeller
    3    Copyright (C) 2007 Luke Howard
    4    This file is part of the nss_ldap library
    5    Contributed by Howard Wilkinson <howard@cohtech.com>, 2007.
    6 
    7    Derived from original version by Markus Moeller <huaraz@moeller.plus.com>
    8 
    9    The nss_ldap library is free software; you can redistribute it and/or
   10    modify it under the terms of the GNU Library General Public License as
   11    published by the Free Software Foundation; either version 2 of the
   12    License, or (at your option) any later version.
   13 
   14    The nss_ldap library is distributed in the hope that it will be useful,
   15    but WITHOUT ANY WARRANTY; without even the implied warranty of
   16    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
   17    Library General Public License for more details.
   18 
   19    You should have received a copy of the GNU Library General Public
   20    License along with the nss_ldap library; see the file COPYING.LIB.  If not,
   21    write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
   22    Boston, MA 02111-1307, USA.
   23  */
   24 
   25 static char rcsId[] =
   26   "$Id: ldap-init-krb5-cache.c,v 2.1 2007/10/01 00:12:06 lukeh Exp $";
   27 
   28 /*
   29  * This file implementes the management of the Kerberos Credential Cache
   30  * for SASL connections.
   31  *
   32  * The init function implements a finite state machine which is designed to
   33  * manage the various configuration states that the Kerberos environment
   34  * can be in.
   35  */
   36 
   37 /*
   38  * INIT:    Initial state when first called and also state after a reset.
   39  *      Calls krb5_cache_setup which populates the environment
   40  *       => REFRESH - Try to load some credentials first
   41  * RUNNING: Credentials have been loaded and are current.
   42  *       => EXPIRED - Cached credentials have expired
   43  *       => REFRESH - Cached credentials are expiring
   44  *       => INIT    - escape path for inconsistent data
   45  *              (should never happen)
   46  * RENEW:   Credentials are expiring and have not been externally refreshed
   47  *       => ERROR   - Autorenew is not on so this is an error state
   48  *       => EXPIRED - Cached credentials expired during renewal
   49  *       => RUNNING - Cached credentials have been successfully renewed
   50  * EXPIRED: Credentials have expired acquire new ones.
   51  *      This only works if a keytab is configured and useable.
   52  *       => ERROR   - No available keytab or keytab is not useable
   53  *       => RUNNING - New credentials acquired
   54  * REFRESH: Reload credentials from the configured credential cache
   55  *       => RUNNING - New credentials loaded and not expired
   56  *       => RENEW   - New credentials loaded but are expiring
   57  *              (only happens if autorenew is on)
   58  *       => EXPIRED - New credentials loaded have expired
   59  *              or are expiring and autorenew is off
   60  *       => ERROR   - Inconsistent credentials state
   61  *       => ACQUIRE - Cannot load any usable credentials from a cache
   62  *              Try for a keytab if one if configured
   63  * ACQUIRE: No credentials loaded acquire new ones - (See EXPIRED)
   64  * ERROR:   Call reset and then return failure from this attempt
   65  *       => REFRESH
   66  */
   67 
   68 /*
   69  * The code is written to allow and external program to supply credentials
   70  * in the environment. This program can refresh/renew the credentials
   71  * periodically and we will use these.
   72  * As an alternative this code can renew externally provided credentials
   73  * if necessary - autorenew must be turned on.
   74  * Finally if provided with a keytab this code will acquire credentials
   75  * from a KDC
   76  */
   77 
   78 /*
   79  * 31st July 2007 -  NO ATTEMPT HAS BEEN MADE TO MAKE
   80  *           THIS CODE THREAD SAFE AT THIS TIME
   81  */
   82 
   83 #include "config.h"
   84 #ifdef HAVE_LBER_H
   85 #include <lber.h>
   86 #endif
   87 #ifdef HAVE_LDAP_H
   88 #include <ldap.h>
   89 #endif
   90 #include "ldap-nss.h"
   91 #ifdef CONFIGURE_KRB5_KEYTAB
   92 #include <krb5.h>
   93 #include <unistd.h>
   94 #include <stdlib.h>
   95 #include <libgen.h>
   96 #include <stdio.h>
   97 #include <string.h>
   98 #include <errno.h>
   99 #include <syslog.h>
  100 #include <time.h>
  101 #ifndef HEIMDAL
  102 #include <profile.h>
  103 #endif
  104 #ifdef HEIMDAL
  105 #define error_message(code) krb5_get_err_text(context,code)
  106 #endif
  107 #include <sys/types.h>
  108 #include <assert.h>
  109 #include <gssapi/gssapi.h>
  110 #include <gssapi/gssapi_krb5.h>
  111 
  112 #define MAX_RENEW_TIME "365d"
  113 
  114 #define KT_PATH_MAX 256
  115 
  116 #ifndef HEIMDAL
  117 typedef struct _profile_t *profile_t;
  118 #endif
  119 
  120 /* State machine items */
  121 typedef enum
  122 {
  123   KRB5_CACHE_INIT = 0,      /* First time through or has been reset */
  124   KRB5_CACHE_RUNNING,       /* Valid non-expired credentials loaded */
  125   KRB5_CACHE_RENEW,     /* Valid about to expire credentials loaded */
  126   KRB5_CACHE_EXPIRED,       /* Valid expired credentials loaded */
  127   KRB5_CACHE_REFRESH,       /* No credentials loaded */
  128   KRB5_CACHE_ACQUIRE,       /* Acquire new credentials from KDC */
  129   KRB5_CACHE_ERROR      /* Cannot get any credentials (this time) */
  130 } krb5_cache_state;
  131 
  132 /* Run the state machine from here */
  133 static krb5_cache_state cache_state = KRB5_CACHE_INIT;
  134 
  135 /* Track our Effective UID incase it is changing as we run */
  136 static uid_t __euid = -1;
  137 static uid_t euid = -1;
  138 
  139 static krb5_context context = NULL;
  140 static krb5_creds *creds = NULL;
  141 #ifdef HEIMDAL
  142 static krb5_creds creds2;
  143 #endif
  144 static krb5_principal principal = NULL;
  145 static krb5_ccache cc = NULL;
  146 static krb5_deltat skew = 0;
  147 static char *ccname = NULL;
  148 static char *ktname = NULL;
  149 static char *saslid = NULL;
  150 static int autorenew = 0;
  151 
  152 #define credsOK(__c__) \
  153   ((__c__ != NULL) && ((__c__->times.endtime - time(0)) > (2*skew)))
  154 
  155 #define credsEXPIRING(__c__) \
  156   ((__c__ != NULL) \
  157    && (((__c__->times.endtime - time(0)) <= (2*skew)) \
  158        && ((__c__->times.endtime - time(0)) > skew)))
  159 
  160 #define credsEXPIRED(__c__) \
  161   ((__c__ == NULL) \
  162    || (((__c__->times.renew_till - time(0)) <= (2*skew)) \
  163        || ((__c__->times.endtime - time(0)) <= skew)))
  164 
  165 static int
  166 krb5_cache_reset (ldap_config_t * config)
  167 {
  168   debug ("==> krb5_cache_reset");
  169   if (creds != NULL)
  170     {
  171       free ((void *) creds);
  172       creds = NULL;
  173     }
  174   if (context != NULL)
  175     {
  176       if (principal != NULL)
  177     {
  178       krb5_free_principal (context, principal);
  179       principal = NULL;
  180     }
  181       if (cc != NULL)
  182     {
  183       krb5_cc_close (context, cc);
  184       cc = NULL;
  185     }
  186       krb5_free_context (context);
  187       context = NULL;
  188     }
  189   skew = 0;
  190   autorenew = 0;
  191   if (ccname != NULL)
  192     {
  193       free ((void *) ccname);
  194       ccname = NULL;
  195     }
  196   if (ktname != NULL)
  197     {
  198       free ((void *) ktname);
  199       ktname = NULL;
  200     }
  201   if (saslid != NULL)
  202     {
  203       free ((void *) saslid);
  204       saslid = NULL;
  205     }
  206   cache_state = KRB5_CACHE_INIT;
  207   debug ("<== krb5_cache_reset");
  208   return (0);
  209 }
  210 
  211 static int
  212 krb5_cache_kt_is_accessible (char *__ktname)
  213 {
  214   krb5_error_code code = 0;
  215   krb5_keytab __keytab;
  216 
  217   debug ("==> krb5_cache_kt_is_accessible: ktname %s", __ktname);
  218   assert (context != NULL);
  219   if (!(code = krb5_kt_resolve (context, __ktname, &__keytab)))
  220     {
  221       debug ("==> krb5_cache_kt_is_accessible: resolved ktname %s - %s",
  222          __ktname, krb5_kt_get_type (context, __keytab));
  223       if (strcmp ("FILE", krb5_kt_get_type (context, __keytab)) == 0)
  224     {
  225       debug ("==> krb5_cache_kt_is_accessible: kt type = FILE");
  226       uid_t ruid = getuid ();
  227       gid_t rgid = getgid ();
  228       gid_t egid = getegid ();
  229       char buf[KT_PATH_MAX];
  230       if (ruid != euid)
  231         {
  232           setreuid (euid, ruid);
  233         }
  234       if (rgid != egid)
  235         {
  236           setregid (egid, rgid);
  237         }
  238       krb5_kt_get_name (context, __keytab, buf, KT_PATH_MAX);
  239       debug ("==> krb5_cache_kt_is_accessible: kt_get_name gives %s",
  240          buf);
  241       code = access (buf, R_OK);
  242       if (ruid != euid)
  243         {
  244           setreuid (ruid, euid);
  245         }
  246       if (rgid != rgid)
  247         {
  248           setregid (rgid, egid);
  249         }
  250     }
  251       krb5_kt_close (context, __keytab);
  252     }
  253 
  254   debug ("<== krb5_cache_kt_is_accessible: returns %s(%d)",
  255      error_message (code), (int) code);
  256   return (code == 0);
  257 }
  258 
  259 static char *
  260 krb5_cache_get_ktname (ldap_config_t * config)
  261 {
  262   char *__ktname = NULL;
  263 
  264   debug ("==> krb5_cache_get_ktname");
  265   {
  266     char *rootktname = ((euid == 0 && config->ldc_krb5_rootusekeytab
  267              && config->ldc_rootusesasl
  268              && config->ldc_krb5_rootkeytabname)
  269             ? config->ldc_krb5_rootkeytabname : NULL);
  270     char *userktname = ((((config->ldc_usesasl && config->ldc_krb5_usekeytab)
  271               || (euid == 0 && config->ldc_rootusesasl
  272                   && config->ldc_krb5_rootusekeytab))
  273              && config->ldc_krb5_keytabname)
  274             ? config->ldc_krb5_keytabname : NULL);
  275     char *envktname = ((((config->ldc_usesasl && config->ldc_krb5_usekeytab)
  276              || (euid == 0 && config->ldc_rootusesasl
  277                  && config->ldc_krb5_rootusekeytab))
  278             && getenv ("KRB5_KTNAME"))
  279                ? getenv ("KRB5_KTNAME") : NULL);
  280     char *defktname = NULL;
  281 
  282     if ((config->ldc_usesasl && config->ldc_krb5_usekeytab)
  283     || (euid == 0 && config->ldc_rootusesasl
  284         && config->ldc_krb5_rootusekeytab))
  285       {
  286     char buf[KT_PATH_MAX];
  287     debug ("==> krb5_cache_get_ktname: get default keytab name");
  288     krb5_kt_default_name (context, buf, KT_PATH_MAX);
  289     defktname = strdup (buf);
  290       }
  291 
  292     debug
  293       ("==> krb5_cache_get_ktname: rootktname = %s, userktname = %s, envktname = %s, defktname = %s",
  294        (rootktname) ? rootktname : "NULL", (userktname) ? userktname : "NULL",
  295        (envktname) ? envktname : "NULL", (defktname) ? defktname : "NULL");
  296     __ktname =
  297       ((rootktname
  298     && krb5_cache_kt_is_accessible (rootktname)) ? rootktname
  299        : (userktname
  300       && krb5_cache_kt_is_accessible (userktname)) ? userktname
  301        : (envktname
  302       && krb5_cache_kt_is_accessible (envktname)) ? envktname : (defktname
  303                                      &&
  304                                      krb5_cache_kt_is_accessible
  305                                      (defktname))
  306        ? defktname : NULL);
  307   }
  308   debug ("<== krb5_cache_get_ktname: returns %s",
  309      (__ktname) ? __ktname : "NULL");
  310   return __ktname;
  311 }
  312 
  313 static int
  314 krb5_cache_cc_is_accessible (char *__ccname, int writeable)
  315 {
  316   krb5_error_code code = 0;
  317   krb5_ccache __cc;
  318 
  319   debug ("==> krb5_cache_cc_is_accessible: ccname %s, writeable %d",
  320      __ccname, writeable);
  321   assert (context != NULL);
  322   if (!(code = krb5_cc_resolve (context, __ccname, &__cc)))
  323     {
  324       debug ("==> krb5_cache_cc_is_accessible: resolved ccname %s - %s",
  325          __ccname, krb5_cc_get_type (context, __cc));
  326       if ((strcmp ("FILE", krb5_cc_get_type (context, __cc)) == 0)
  327       || (strcmp ("WRFILE", krb5_cc_get_type (context, __cc)) == 0))
  328     {
  329       int mode = R_OK;
  330       uid_t ruid = getuid ();
  331       gid_t rgid = getgid ();
  332       gid_t egid = getegid ();
  333       if (writeable)
  334         {
  335           mode = mode | W_OK;
  336         }
  337       if (ruid != euid)
  338         {
  339           setreuid (euid, ruid);
  340         }
  341       if (rgid != egid)
  342         {
  343           setregid (egid, rgid);
  344         }
  345       if ((code = access (krb5_cc_get_name (context, __cc), F_OK)))
  346         {
  347           debug
  348         ("==> krb5_cache_cc_is_accessible: cache file not accessible %s(%d)",
  349          strerror (errno), errno);
  350           if (errno == EACCES)
  351         {       /* File does not exist */
  352           if (writeable)
  353             {
  354               /* Check that path exists */
  355               char *x__ccname =
  356             strdup (krb5_cc_get_name (context, cc));
  357               char *x__ccdir = dirname (x__ccname);
  358               code = access (x__ccdir, mode | X_OK);
  359               free ((void *) x__ccname);
  360             }
  361         }
  362         }
  363       else
  364         {
  365           code = access (krb5_cc_get_name (context, __cc), mode);
  366         }
  367       if (ruid != euid)
  368         {
  369           setreuid (ruid, euid);
  370         }
  371       if (rgid != rgid)
  372         {
  373           setregid (rgid, egid);
  374         }
  375     }
  376       krb5_cc_close (context, __cc);
  377     }
  378 
  379   debug ("<== krb5_cache_cc_is_accessible: returns %s(%d)",
  380      error_message (code), code);
  381   return (code == 0);
  382 }
  383 
  384 static char *
  385 krb5_cache_get_ccname (ldap_config_t * config)
  386 {
  387   char *__ccname = NULL;
  388 
  389   debug ("==> krb5_cache_get_ccname");
  390   {
  391     char *rootccname = ((euid == 0
  392              && config->ldc_rootusesasl
  393              && config->ldc_krb5_rootccname)
  394             ? config->ldc_krb5_rootccname : NULL);
  395     char *userccname = (((config->ldc_usesasl
  396               || (euid == 0 && config->ldc_rootusesasl))
  397              && config->ldc_krb5_ccname)
  398             ? config->ldc_krb5_ccname : NULL);
  399     char *envccname = (((config->ldc_usesasl
  400              || (euid == 0 && config->ldc_rootusesasl))
  401             && getenv ("KRB5CCNAME"))
  402                ? getenv ("KRB5CCNAME") : NULL);
  403     char *defccname = (((config->ldc_usesasl
  404              || (euid == 0 && config->ldc_rootusesasl))
  405             && (char *) krb5_cc_default_name (context))
  406                ? (char *) krb5_cc_default_name (context) : NULL);
  407 
  408     int writeable = (autorenew
  409              || config->ldc_krb5_usekeytab
  410              || (euid == 0 && config->ldc_krb5_rootusekeytab));
  411 
  412     debug
  413       ("==> krb5_cache_get_ccname: rootccname = %s, userccname = %s, envccname = %s, defccname = %s",
  414        (rootccname) ? rootccname : "NULL", (userccname) ? userccname : "NULL",
  415        (envccname) ? envccname : "NULL", (defccname) ? defccname : "NULL");
  416     __ccname =
  417       ((rootccname
  418     && krb5_cache_cc_is_accessible (rootccname,
  419                     writeable)) ? rootccname : (userccname
  420                                     &&
  421                                     krb5_cache_cc_is_accessible
  422                                     (userccname,
  423                                      writeable))
  424        ? userccname : (envccname
  425                && krb5_cache_cc_is_accessible (envccname,
  426                                writeable)) ? envccname
  427        : (defccname
  428       && krb5_cache_cc_is_accessible (defccname,
  429                       writeable)) ? defccname : NULL);
  430     if (__ccname == NULL
  431     && (config->ldc_krb5_usekeytab
  432         || (euid == 0 && config->ldc_krb5_rootusekeytab)))
  433       __ccname = "MEMORY:store_creds";
  434   }
  435   debug ("<== krb5_cache_get_ccname: returns ccname = %s",
  436      (__ccname) ? __ccname : "NULL");
  437   return ((__ccname) ? strdup (__ccname) : "NULL");
  438 }
  439 
  440 static char *
  441 krb5_cache_get_saslid (ldap_config_t * config)
  442 {
  443   char *__saslid = ((euid = 0 && config->ldc_rootusesasl
  444              && config->ldc_rootsaslid)
  445             ? config->ldc_rootsaslid
  446             : (config->ldc_usesasl && config->ldc_saslid)
  447             ? config->ldc_saslid : NULL);
  448   if (__saslid == NULL)
  449     {
  450       int retval;
  451       char hostname[HOST_NAME_MAX];
  452 
  453       debug ("==> krb5_cache_get_saslid: get default principal name");
  454       errno = 0;
  455       retval = gethostname (hostname, HOST_NAME_MAX);
  456       if (!retval)
  457     {
  458       hostname[HOST_NAME_MAX] = '\0';
  459       __saslid = malloc (sizeof (hostname) + 6);
  460       strcpy (__saslid, "host/");
  461       strcat (__saslid, hostname);
  462       debug ("==> krb5_cache_get_saslid: set principal name %s",
  463          __saslid);
  464     }
  465       else
  466     {
  467       syslog (LOG_ERR, "nss_ldap: %s while resolving hostname",
  468           strerror (errno));
  469       debug ("==> krb5_cache_get_saslid: %s while resolving hostname",
  470          strerror (errno));
  471     }
  472     }
  473   else
  474     {
  475       __saslid = strdup (__saslid);
  476     }
  477   return __saslid;
  478 }
  479 
  480 static krb5_principal
  481 krb5_cache_get_principal (ldap_config_t * config)
  482 {
  483   krb5_error_code code = 0;
  484   krb5_principal __principal;
  485 
  486   assert (saslid != NULL);
  487   if ((code = krb5_parse_name (context, saslid, &__principal)))
  488     {
  489       syslog (LOG_ERR, "nss_ldap: %s(%d) while parsing principal name %s",
  490           error_message (code), (int) code, saslid);
  491       debug
  492     ("==> krb5_cache_get_principal: %s(%d) while parsing principal name %s",
  493      error_message (code), (int) code, saslid);
  494       return (NULL);
  495     }
  496   return __principal;
  497 }
  498 
  499 /* Set up to manage the credentials cache */
  500 static int
  501 krb5_cache_setup (ldap_config_t * config)
  502 {
  503 
  504   krb5_error_code code = 0;
  505 
  506 #ifndef HEIMDAL
  507   profile_t profile;
  508 #endif
  509 
  510   debug ("==> krb5_cache_setup");
  511   if (context == NULL)
  512     {
  513       if ((code = krb5_init_context (&context)))
  514     {
  515       syslog (LOG_ERR,
  516           "nss_ldap: %s(%d) while initialising Kerberos library",
  517           error_message (code), (int) code);
  518       debug
  519         ("<== krb5_cache_setup: %s(%d) while initialising Kerberos library",
  520          error_message (code), (int) code);
  521       return (code);
  522     }
  523     }
  524 #ifndef HEIMDAL
  525   if ((code = krb5_get_profile (context, &profile)))
  526     {
  527       syslog (LOG_ERR, "nss_ldap: %s(%d) while getting profile",
  528           error_message (code), (int) code);
  529       debug ("<== krb5_cache_setup: %s(%d) while getting profile",
  530          error_message (code), (int) code);
  531       return (code);
  532     }
  533   if ((code = profile_get_integer (profile,
  534                    "libdefaults",
  535                    "clockskew", 0, 5 * 60, &skew)))
  536     {
  537       syslog (LOG_ERR, "nss_ldap: %s(%d) while getting clockskew",
  538           error_message (code), (int) code);
  539       debug ("<== krb5_cache_setup: %s(%d) while getting clockskew",
  540          error_message (code), (int) code);
  541       return (code);
  542     }
  543   profile_release (profile);
  544 #else
  545   skew = context->max_skew;
  546 #endif
  547   ccname = krb5_cache_get_ccname (config);
  548   debug ("==> krb5_cache_setup: credential cache name %s",
  549      ccname ? ccname : "NULL");
  550   cache_state = KRB5_CACHE_REFRESH;
  551   autorenew = (config->ldc_krb5_autorenew
  552            || (euid == 0 && config->ldc_krb5_rootautorenew));
  553   debug ("<== krb5_cache_setup");
  554   return (0);
  555 }
  556 
  557 static void
  558 krb5_cache_setup_creds (ldap_config_t * context)
  559 {
  560   debug ("==> krb5_cache_setup_creds");
  561   if (creds == NULL)
  562     {
  563       creds = malloc (sizeof (*creds));
  564       assert (creds != NULL);
  565     }
  566   memset (creds, 0, sizeof (*creds));
  567   debug ("<== krb5_cache_setup_creds");
  568 }
  569 
  570 /* (Re)load the credentials cache into our local data */
  571 static int
  572 krb5_cache_refresh (ldap_config_t * config)
  573 {
  574 
  575   krb5_error_code code = 0;
  576 
  577   debug ("==> krb5_cache_refresh");
  578   if ((code = krb5_cc_resolve (context, ccname, &cc)))
  579     {
  580       debug ("==> krb5_cache_refresh: cache %s cannot be resolved",
  581          ccname ? ccname : "NULL");
  582     }
  583   else if ((code = krb5_cc_get_principal (context, cc, &principal)))
  584     {
  585       debug ("==> krb5_cache_refresh: cannot get principal from cache %s",
  586          ccname ? ccname : "NULL");
  587     }
  588   else
  589     {
  590       debug ("==> krb5_cache_refresh: found existing cache %s",
  591          ccname ? ccname : "NULL");
  592       /* Use the principal name from the cache rather than preconfigured saslid */
  593       char *principal_name = NULL;
  594       if ((code = krb5_unparse_name (context, principal, &principal_name)))
  595     {
  596       debug
  597         ("==> krb5_cache_refresh: cannot unparse principal from cache %s",
  598          ccname ? ccname : "NULL");
  599     }
  600       else
  601     {
  602       krb5_cc_cursor cursor;
  603 
  604       debug ("==> krb5_cache_refresh: cache %s principal %s",
  605          ccname ? ccname : "NULL",
  606          principal_name ? principal_name : "NULL");
  607       if ((code = krb5_cc_start_seq_get (context, cc, &cursor)))
  608         {
  609           debug
  610         ("==> krb5_cache_refresh: cache %s credentials not usable",
  611          ccname ? ccname : "NULL");
  612         }
  613       else
  614         {
  615           krb5_cache_setup_creds (config);
  616           do
  617         {
  618           if (!
  619               (code =
  620                krb5_cc_next_cred (context, cc, &cursor, creds)))
  621             {
  622               debug ("==> krb5_cache_refresh: retrieved creds");
  623               if (credsOK (creds))
  624             {
  625               debug
  626                 ("==> krb5_cache_refresh: creds are OK --> RUNNING");
  627               /* Reloaded cache is fine */
  628               cache_state = KRB5_CACHE_RUNNING;
  629             }
  630               else
  631             {
  632               if (credsEXPIRING (creds))
  633                 {
  634                   debug
  635                 ("==> krb5_cache_refresh: creds are EXPIRING");
  636                   /* Reloaded cache will expire shortly */
  637                   if (autorenew)
  638                 {
  639                   debug ("==> krb5_cache_refresh: --> RENEW");
  640                   cache_state = KRB5_CACHE_RENEW;
  641                 }
  642                   else
  643                 {
  644                   debug
  645                     ("==> krb5_cache_refresh: --> EXPIRED");
  646                   cache_state = KRB5_CACHE_EXPIRED;
  647                 }
  648                 }
  649               else if (credsEXPIRED (creds))
  650                 {
  651                   debug
  652                 ("==> krb5_cache_refresh: creds have EXPIRED --> EXPIRED");
  653                   /* Reload cache has expired */
  654                   cache_state = KRB5_CACHE_EXPIRED;
  655                 }
  656               else
  657                 {   /* Should never happen */
  658                   debug
  659                 ("==> krb5_cache_refresh: creds in weird state --> ERROR");
  660                   cache_state = KRB5_CACHE_ERROR;
  661                 }
  662               krb5_free_cred_contents (context, creds);
  663             }
  664             }
  665         }
  666           while (!code && cache_state == KRB5_CACHE_REFRESH);
  667           if ((code = krb5_cc_end_seq_get (context, cc, &cursor)))
  668         {
  669           debug
  670             ("==> krb5_cache_refresh: cache %s scan failed to end cleanly",
  671              ccname ? ccname : "NULL");
  672         }
  673         }
  674       krb5_free_unparsed_name (context, principal_name);
  675     }
  676     }
  677 
  678   if (principal != NULL)
  679     {
  680       krb5_free_principal (context, principal);
  681       principal = NULL;
  682     }
  683   if (cc != NULL)
  684     {
  685       if ((code = krb5_cc_close (context, cc)))
  686     {
  687       debug ("==> krb5_cache_refresh: cache %s close failed",
  688          ccname ? ccname : "NULL");
  689     }
  690       cc = NULL;
  691     }
  692   if (cache_state == KRB5_CACHE_REFRESH)
  693     {
  694       debug ("==> krb5_cache_refresh: stuck in refresh --> ACQUIRE");
  695       cache_state = KRB5_CACHE_ACQUIRE; /* Try for a keytab */
  696     }
  697   debug ("<== krb5_cache_refresh");
  698   return (code);
  699 }
  700 
  701 /* Renew an expired credentials cache */
  702 static int
  703 krb5_cache_renew (ldap_config_t * config)
  704 {
  705 
  706   krb5_error_code code = 0;
  707 
  708 #ifdef HEIMDAL
  709   krb5_kdc_flags flags;
  710   krb5_realm *client_realm;
  711 #endif
  712 
  713   debug ("==> krb5_cache_renew");
  714 
  715   if (!autorenew)
  716     {
  717       /*
  718        * We should never get here
  719        * as the refresh code should only enter renew
  720        * if autorenew is true
  721        */
  722       debug
  723     ("==> krb5_cache_renew: renew called with autorenew off --> ERROR");
  724       cache_state = KRB5_CACHE_ERROR;
  725       return (1);
  726     }
  727 
  728   assert (creds != NULL);   /* Refresh or acquire will have set this up */
  729 #ifndef HEIMDAL
  730   /* renew ticket */
  731   /* Overwrites contents of creds no storage allocation happening */
  732   if ((code = krb5_get_renewed_creds (context, creds, principal, cc, NULL)))
  733     {
  734       debug ("==> krb5_cache_renew: failed to renew creds %s(%d)",
  735          error_message (code), (int) code);
  736 #else
  737   /* renew ticket */
  738   flags.i = 0;
  739   flags.b.renewable = flags.b.renew = 1;
  740   if ((code = krb5_cc_get_principal (context, cc, &creds2.client)))
  741     {
  742       syslog (LOG_ERR,
  743           "nss_ldap: %s(%d) while getting principal from credential cache",
  744           error_message (code), (int) code);
  745       debug
  746     ("==> krb5_cache_renew: %s(%d) while getting principal from credentials cache",
  747      error_message (code), (int) code);
  748       cache_state = KRB5_CACHE_REFRESH;
  749       return (code);
  750     }
  751   client_realm = krb5_princ_realm (context, creds2.client);
  752   if ((code = krb5_make_principal (context, &creds2.server, *client_realm,
  753                    KRB5_TGS_NAME, *client_realm, NULL)))
  754     {
  755       syslog (LOG_ERR, "nss_ldap: %s(%d) while getting krbtgt principal",
  756           error_message (code), (int) code);
  757       debug ("==> krb5_cache_renew: %s(%d) while getting krbtgt principal",
  758          error_message (code), (int) code);
  759       cache_state = KRB5_CACHE_REFRESH;
  760       return (code);
  761     }
  762   /* I think there is a potential storage leak here as creds is written to */
  763   /* Need to check Heimdal code to see if it will overwrite or replace memory */
  764   if ((code = krb5_get_kdc_cred (context,
  765                  cc, flags, NULL, NULL, &creds2, &creds)))
  766     {
  767       debug ("==> krb5_cache_renew: failed to get creds from kdc %s(%d)",
  768          error_message (code), (int) code);
  769 #endif
  770       if (code == KRB5KRB_AP_ERR_TKT_EXPIRED)
  771     {
  772       /* this can happen because of clock skew */
  773       debug
  774         ("<== krb5_cache_renew: ticket has expired because of clock skey --> EXPIRED");
  775       cache_state = KRB5_CACHE_EXPIRED;
  776       return (0);
  777     }
  778       else
  779     {
  780       syslog (LOG_ERR, "nss_ldap: %s(%d) while renewing credentials",
  781           error_message (code), (int) code);
  782       debug ("==> krb5_cache_renew: %s(%d) while renewing credentials",
  783          error_message (code), (int) code);
  784       cache_state = KRB5_CACHE_REFRESH;
  785       return (code);
  786     }
  787     }
  788   cache_state = KRB5_CACHE_RUNNING;
  789   debug ("<== krb5_cache_renew: renewed creds --> RUNNING");
  790   return (0);
  791 }
  792 
  793 /* Initialise the credentials cache from a keytab */
  794 static int
  795 krb5_cache_acquire (ldap_config_t * config)
  796 {
  797 
  798   krb5_error_code code = 0;
  799   krb5_keytab keytab = NULL;
  800 
  801   debug ("==> krb5_cache_acquire");
  802   /*
  803    * We have not managed to find any credentials.
  804    * If a keytab is configured then try using that
  805    */
  806   assert (context != NULL);
  807   /* use keytab to fill cache */
  808   if ((ktname == NULL) && ((ktname = krb5_cache_get_ktname (config)) == NULL))
  809     {
  810       debug ("==> krb5_cache_acquire: no usable keytab");
  811       code = 1;
  812     }
  813   else if ((code = krb5_kt_resolve (context, ktname, &keytab)))
  814     {
  815       syslog (LOG_ERR, "nss_ldap: %s(%d) while resolving keytab filename %s",
  816           error_message (code), (int) code, ktname);
  817       debug
  818     ("==> krb5_cache_acquire: %s(%d) while resolving keytab filename %s",
  819      error_message (code), (int) code, ktname);
  820     }
  821   else
  822     {
  823       krb5_get_init_creds_opt options;
  824       krb5_deltat rlife;
  825       if (saslid == NULL)
  826     {
  827       saslid = krb5_cache_get_saslid (config);
  828     }
  829       debug ("==> krb5_cache_acquire: saslid = %s",
  830          (saslid) ? saslid : "NULL");
  831       if (saslid && principal == NULL)
  832     {
  833       principal = krb5_cache_get_principal (config);
  834       if (principal == NULL)
  835         {
  836           debug ("<== krb5_cache_acquire: no valid principal --> ERROR");
  837           cache_state = KRB5_CACHE_ERROR;
  838           return (1);
  839         }
  840     }
  841       krb5_get_init_creds_opt_init (&options);
  842       if ((code = krb5_string_to_deltat (MAX_RENEW_TIME, &rlife))
  843       || (rlife == 0))
  844     {
  845       syslog (LOG_ERR,
  846           "nss_ldap: %s(%d) while setting renew lifetime value to %s",
  847           error_message (code), (int) code, MAX_RENEW_TIME);
  848       debug
  849         ("==> krb5_cache_acquire: %s(%d) while setting renew lifetime value to %s",
  850          error_message (code), (int) code, MAX_RENEW_TIME);
  851       code = (code == 0) ? 1 : code;
  852     }
  853       else
  854     {
  855       krb5_get_init_creds_opt_set_renew_life (&options, rlife);
  856       debug ("==> krb5_cache_acquire: get credentials from keytab");
  857       krb5_cache_setup_creds (config);
  858       if ((code = krb5_get_init_creds_keytab (context,
  859                           creds,
  860                           principal,
  861                           keytab,
  862                           0,
  863                           NULL,
  864                           &options))
  865           && (code != EEXIST))
  866         {
  867           /* Failed to initialise credentials from keytab */
  868           syslog (LOG_ERR,
  869               "nss_ldap: %s(%d) while initialising credentials from keytab",
  870               error_message (code), (int) code);
  871           debug
  872         ("==> krb5_cache_acquire get credentials from keytab failed %s(%d)",
  873          error_message (code), (int) code);
  874           debug
  875         ("==> krb5_cache_acquire try refreshing from credential cache");
  876           if ((code = krb5_cache_refresh (config)))
  877         {
  878           debug
  879             ("==> krb5_cache_acquire: cache credentials not usable");
  880           free ((void *) creds);
  881           creds = NULL;
  882         }
  883           else if (cache_state == KRB5_CACHE_ACQUIRE)
  884         code = 1;
  885         }
  886       else
  887         {
  888           /* We have a set of credentials we now need to save them */
  889           if ((code = krb5_cc_resolve (context, ccname, &cc)))
  890         {
  891           syslog (LOG_ERR,
  892               "nss_ldap: %s(%d) while resolving credential cache",
  893               error_message (code), (int) code);
  894           debug
  895             ("==>krb5_cache_acquire: %s(%d) while resolving credential cache",
  896              error_message (code), (int) code);
  897         }
  898           else if ((code = krb5_cc_initialize (context, cc, principal))
  899                && (code != EEXIST))
  900         {
  901           /* Failed to initialize the cache try to use a default one instead */
  902           syslog (LOG_ERR,
  903               "nss_ldap: %s(%d) while initializing credential cache",
  904               error_message (code), (int) code);
  905           debug
  906             ("==> krb5_cache_acquire: initializing credential cache failed %s(%d)",
  907              error_message (code), (int) code);
  908         }
  909           else
  910         {
  911           debug
  912             ("==> krb5_cache_acquire store credentials in cache file");
  913           if ((code = krb5_cc_store_cred (context, cc, creds)))
  914             {
  915               syslog (LOG_ERR,
  916                   "nss_ldap: %s(%d) while storing credentials",
  917                   error_message (code), (int) code);
  918               debug
  919             ("==> krb5_cache_acquire: %s(%d) while storing credentials",
  920              error_message (code), (int) code);
  921             }
  922           else
  923             {
  924               if (!creds->times.starttime)
  925             creds->times.starttime = creds->times.authtime;
  926               debug ("==> krb5_cache_acquire: got new credentials");
  927               cache_state = KRB5_CACHE_RUNNING;
  928             }
  929           if (cc != NULL)
  930             {
  931               if ((code = krb5_cc_close (context, cc)))
  932             {
  933               debug
  934                 ("==> krb5_cache_acquire: cache %s close failed",
  935                  ccname ? ccname : "NULL");
  936             }
  937             }
  938         }
  939         }
  940     }
  941       if (keytab != NULL)
  942     {
  943       krb5_kt_close (context, keytab);
  944     }
  945     }
  946   if (code)
  947     {
  948       debug ("<== krb5_cache_acquire: --> ERROR");
  949       cache_state = KRB5_CACHE_ERROR;
  950     }
  951   else
  952     {
  953       debug ("<== krb5_cache_acquire");
  954     }
  955   return (code);
  956 }
  957 
  958 int
  959 do_init_krb5_cache (ldap_config_t * config)
  960 {
  961   krb5_error_code code = 0;
  962 
  963   debug ("==> do_init_krb5_cache %s %s %s",
  964      config->ldc_krb5_keytabname ? config->ldc_krb5_keytabname : "NULL",
  965      config->ldc_krb5_ccname ? config->ldc_krb5_ccname : "NULL",
  966      config->ldc_saslid ? config->ldc_saslid : "NULL");
  967 
  968   euid = geteuid ();
  969 
  970   /* Check to see if we are using sasl, if not then return as nothing to do */
  971   if (!(config->ldc_usesasl || (euid == 0 && config->ldc_rootusesasl)))
  972     {
  973       return (1);       /* Should we signal success? */
  974     }
  975 
  976   /* Check to see if we have swapped user since we were last called */
  977   if (__euid != euid)
  978     {               /* Could be first call but clear everything out anyway */
  979       krb5_cache_reset (config);
  980       __euid = euid;
  981     }
  982 
  983 
  984   /* 
  985    * If we do not have any credentials
  986    * or they are expired or they are about to expire
  987    * then try to load a new set from the credentials cache
  988    * - this may have been renewed by
  989    * some other process or thread so check this first.
  990    */
  991 
  992   do
  993     {
  994       switch (cache_state)
  995     {
  996 
  997     case KRB5_CACHE_INIT:
  998       code = krb5_cache_setup (config);
  999       debug
 1000         ("==> do_init_krb5_cache ktname = %s, ccname = %s, saslid = %s, euid = %d",
 1001          ktname ? ktname : "NULL", ccname ? ccname : "NULL",
 1002          saslid ? saslid : "NULL", euid);
 1003       break;
 1004 
 1005     case KRB5_CACHE_RUNNING:
 1006       /*
 1007        * If we have credentials 
 1008        * and they are not expired or about to expire then OK!
 1009        */
 1010       if (credsOK (creds))
 1011         {
 1012           debug ("==> do_init_krb5_cache: return 0");
 1013           return (0);
 1014         }
 1015 
 1016       if (credsEXPIRED (creds))
 1017         {
 1018           cache_state = KRB5_CACHE_EXPIRED;
 1019         }
 1020       else if (credsEXPIRING (creds))
 1021         {
 1022           cache_state = KRB5_CACHE_REFRESH;
 1023         }
 1024       else
 1025         {
 1026           /* Should not get here if things are OK so start again */
 1027           code = krb5_cache_reset (config);
 1028           cache_state = KRB5_CACHE_INIT;
 1029         }
 1030       break;
 1031 
 1032     case KRB5_CACHE_RENEW:
 1033       code = krb5_cache_renew (config);
 1034       break;
 1035 
 1036     case KRB5_CACHE_EXPIRED:
 1037       code = krb5_cache_acquire (config);
 1038       break;
 1039 
 1040     case KRB5_CACHE_REFRESH:
 1041       code = krb5_cache_refresh (config);
 1042       break;
 1043 
 1044     case KRB5_CACHE_ACQUIRE:
 1045       code = krb5_cache_acquire (config);
 1046       break;
 1047 
 1048     case KRB5_CACHE_ERROR:
 1049       /*
 1050        * Can't do anything while in ERROR state.
 1051        * ?? Wait for a few seconds to have passed and try again ??
 1052        * Otherwise how do we break out of this loop
 1053        * - reset will make same sequence happen if this is a hard problem
 1054        */
 1055       code = krb5_cache_reset (config);
 1056       break;
 1057 
 1058     default:
 1059       break;
 1060     }
 1061       if (code)
 1062     {
 1063       debug ("==> do_init_krb5_cache: return %d", (int) code);
 1064       return (code);
 1065     }
 1066     }
 1067   while (1);
 1068 
 1069    /*NOTREACHED*/
 1070     debug ("==> do_init_krb5_cache: reinit ticket loop exit failure");
 1071   return (1);
 1072 }
 1073 
 1074 static char *saveccname = NULL;
 1075 /* This is shared into the environment so be careful */
 1076 #ifdef CONFIGURE_KRB5_CCNAME_ENV
 1077 static char envbuf[256];
 1078 #endif
 1079 
 1080 int
 1081 do_select_krb5_cache (ldap_config_t * config)
 1082 {
 1083   int result = 0;
 1084   if (cache_state != KRB5_CACHE_RUNNING)
 1085     {
 1086       result = do_init_krb5_cache (config);
 1087     }
 1088   if (ccname != NULL)
 1089     {
 1090 #ifdef CONFIGURE_KRB5_CCNAME_ENV
 1091       char tmpbuf[256];
 1092       char *oldccname = getenv ("KRB5CCNAME");
 1093       if (saveccname != NULL)
 1094     {
 1095       free ((void *) saveccname);
 1096       saveccname = NULL;
 1097     }
 1098       if (oldccname != NULL)
 1099     {
 1100       strncpy (tmpbuf, oldccname, sizeof (tmpbuf));
 1101       tmpbuf[sizeof (tmpbuf) - 1] = '\0';
 1102       saveccname = (char *) malloc (strlen (tmpbuf) + 1);
 1103       strcpy (saveccname, tmpbuf);
 1104     }
 1105       snprintf (envbuf, sizeof (envbuf), "KRBCCNAME=%s", ccname);
 1106       putenv (envbuf);
 1107 #elif defined(CONFIGURE_KRB5_CCNAME_GSSAPI)
 1108       OM_uint32 retval = 0;
 1109       if (gss_krb5_ccache_name (&retval,
 1110                 (const char *) ccname,
 1111                 (const char **) &saveccname) !=
 1112       GSS_S_COMPLETE)
 1113     {
 1114       debug
 1115         ("==> do_select_krb5_cache: unable to set default credential cache");
 1116       result = -1;
 1117     }
 1118 #endif
 1119     }
 1120   return result;
 1121 }
 1122 
 1123 int
 1124 do_restore_krb5_cache (ldap_config_t * config)
 1125 {
 1126   int result = 0;
 1127   if (saveccname != NULL)
 1128     {
 1129 #ifdef CONFIGURE_KRB5_CCNAME_ENV
 1130       snprintf (envbuf, sizeof (envbuf), "KRB5CCNAME=%s", saveccname);
 1131       putenv (envbuf);
 1132       free ((void *) saveccname);
 1133 #elif defined(CONFIGURE_KRB5_CCNAME_GSSAPI)
 1134       OM_uint32 retval = 0;
 1135       if (gss_krb5_ccache_name (&retval, (const char *) saveccname, NULL) !=
 1136       GSS_S_COMPLETE)
 1137     {
 1138       debug
 1139         ("==> do_restore_krb5_cache: unable to restore default credential cache");
 1140       result = -1;
 1141     }
 1142 #endif
 1143       saveccname = NULL;
 1144     }
 1145   return result;
 1146 }
 1147 #endif /* CONFIGURE_KRB5_KEYTAB */