LCOV - code coverage report
Current view: top level - third_party/heimdal/lib/gssapi/krb5 - store_cred.c (source / functions) Hit Total Coverage
Test: coverage report for master 2f515e9b Lines: 68 186 36.6 %
Date: 2024-04-21 15:09:00 Functions: 2 7 28.6 %

          Line data    Source code
       1             : /*
       2             :  * Copyright (c) 2003 Kungliga Tekniska Högskolan
       3             :  * (Royal Institute of Technology, Stockholm, Sweden).
       4             :  * All rights reserved.
       5             :  *
       6             :  * Redistribution and use in source and binary forms, with or without
       7             :  * modification, are permitted provided that the following conditions
       8             :  * are met:
       9             :  *
      10             :  * 1. Redistributions of source code must retain the above copyright
      11             :  *    notice, this list of conditions and the following disclaimer.
      12             :  *
      13             :  * 2. Redistributions in binary form must reproduce the above copyright
      14             :  *    notice, this list of conditions and the following disclaimer in the
      15             :  *    documentation and/or other materials provided with the distribution.
      16             :  *
      17             :  * 3. Neither the name of the Institute nor the names of its contributors
      18             :  *    may be used to endorse or promote products derived from this software
      19             :  *    without specific prior written permission.
      20             :  *
      21             :  * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
      22             :  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
      23             :  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
      24             :  * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
      25             :  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
      26             :  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
      27             :  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
      28             :  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
      29             :  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
      30             :  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
      31             :  * SUCH DAMAGE.
      32             :  */
      33             : 
      34             : #include "gsskrb5_locl.h"
      35             : 
      36             : static int
      37           0 : same_princ(krb5_context context, krb5_ccache id1, krb5_ccache id2)
      38             : {
      39           0 :     krb5_error_code ret;
      40           0 :     krb5_principal p1 = NULL;
      41           0 :     krb5_principal p2 = NULL;
      42           0 :     int same = 0;
      43             : 
      44           0 :     ret = krb5_cc_get_principal(context, id1, &p1);
      45           0 :     if (ret == 0)
      46           0 :         ret = krb5_cc_get_principal(context, id2, &p2);
      47             :     /* If either principal is absent, it's the same for our purposes */
      48           0 :     same = ret ? 1 : krb5_principal_compare(context, p1, p2);
      49           0 :     krb5_free_principal(context, p1);
      50           0 :     krb5_free_principal(context, p2);
      51           0 :     return same;
      52             : }
      53             : 
      54             : static OM_uint32
      55           0 : add_env(OM_uint32 *minor,
      56             :         gss_buffer_set_t *env,
      57             :         const char *var,
      58             :         const char *val)
      59             : {
      60           0 :     OM_uint32 major;
      61           0 :     gss_buffer_desc b;
      62           0 :     char *varval = NULL;
      63             : 
      64           0 :     if (asprintf(&varval, "%s=%s", var, val) == -1 || varval == NULL) {
      65           0 :         *minor = ENOMEM;
      66           0 :         return GSS_S_FAILURE;
      67             :     }
      68             : 
      69           0 :     b.value = varval;
      70           0 :     b.length = strlen(varval) + 1;
      71           0 :     major = gss_add_buffer_set_member(minor, &b, env);
      72           0 :     free(varval);
      73           0 :     return major;
      74             : }
      75             : 
      76             : static OM_uint32
      77           0 : set_proc(OM_uint32 *minor, gss_buffer_set_t env)
      78             : {
      79             :     /*
      80             :      * XXX On systems with setpag(), call setpag().  On WIN32... create a
      81             :      * session, set the access token, ...?
      82             :      */
      83             : #ifndef WIN32
      84           0 :     size_t i;
      85             : 
      86           0 :     for (i = 0; i < env->count; i++)
      87           0 :         putenv(env->elements[i].value);
      88             : #endif
      89           0 :     return GSS_S_COMPLETE;
      90             : }
      91             : 
      92             : /*
      93             :  * A principal is the best principal for a user IFF
      94             :  *
      95             :  *  - it has one component
      96             :  *  - the one component is the same as the user's name
      97             :  *  - the real is the user_realm from configuration
      98             :  */
      99             : static int
     100           0 : principal_is_best_for_user(krb5_context context,
     101             :                            const char *app,
     102             :                            krb5_const_principal p,
     103             :                            const char *user)
     104             : {
     105           0 :     char *default_realm = NULL;
     106           0 :     char *user_realm = NULL;
     107           0 :     int ret;
     108             : 
     109           0 :     (void) krb5_get_default_realm(context, &default_realm);
     110           0 :     krb5_appdefault_string(context, app, NULL, "user_realm", default_realm,
     111             :                            &user_realm);
     112           0 :     ret = user_realm &&
     113           0 :         krb5_principal_get_num_comp(context, p) == 1 &&
     114           0 :         strcmp(user_realm, krb5_principal_get_realm(context, p)) == 0 &&
     115           0 :         (!user ||
     116           0 :          strcmp(user, krb5_principal_get_comp_string(context, p, 0)) == 0);
     117           0 :     free(default_realm);
     118           0 :     free(user_realm);
     119           0 :     return ret;
     120             : }
     121             : 
     122             : static krb5_error_code
     123       71866 : check_destination_tgt_policy(krb5_context context,
     124             :                              const char *appname,
     125             :                              gsskrb5_cred input_cred)
     126             : {
     127         881 :     krb5_error_code ret;
     128       71866 :     krb5_boolean want_dst_tgt = 0;
     129         881 :     krb5_data v;
     130             : 
     131       71866 :     if (input_cred->destination_realm == NULL)
     132             :         /*
     133             :          * Not a delegated credential, so we can't check the destination TGT
     134             :          * policy for the realm of the service -- we don't know the realm of
     135             :          * the service.
     136             :          */
     137       23107 :         return 0;
     138             : 
     139       48759 :     krb5_appdefault_boolean(context, appname, input_cred->destination_realm,
     140             :                             "require_delegate_destination_tgt", FALSE,
     141             :                             &want_dst_tgt);
     142       48759 :     if (!want_dst_tgt)
     143       47878 :         return 0;
     144             : 
     145           0 :     krb5_data_zero(&v);
     146           0 :     ret = krb5_cc_get_config(context, input_cred->ccache, NULL,
     147             :                              "start_realm", &v);
     148           0 :     if (ret == 0 &&
     149           0 :         v.length != strlen(input_cred->destination_realm))
     150           0 :         ret = KRB5_CC_NOTFOUND;
     151           0 :     if (ret == 0 &&
     152           0 :         strncmp(input_cred->destination_realm, v.data, v.length) != 0)
     153           0 :         ret = KRB5_CC_NOTFOUND;
     154           0 :     if (ret)
     155           0 :         krb5_set_error_message(context, ret,
     156             :                                "Delegated TGT is not a destination TGT for "
     157             :                                "realm \"%s\" but for \"%.*s\"",
     158             :                                input_cred->destination_realm,
     159           0 :                                (int)(v.length ? v.length : sizeof("<UNKNOWN>") - 1),
     160           0 :                                v.data ? (const char *)v.data : "<UNKNOWN>");
     161           0 :     krb5_data_free(&v);
     162           0 :     return ret;
     163             : }
     164             : 
     165             : OM_uint32 GSSAPI_CALLCONV
     166       71866 : _gsskrb5_store_cred_into2(OM_uint32         *minor_status,
     167             :                           gss_const_cred_id_t input_cred_handle,
     168             :                           gss_cred_usage_t  cred_usage,
     169             :                           const gss_OID     desired_mech,
     170             :                           OM_uint32         store_cred_flags,
     171             :                           gss_const_key_value_set_t cred_store,
     172             :                           gss_OID_set       *elements_stored,
     173             :                           gss_cred_usage_t  *cred_usage_stored,
     174             :                           gss_buffer_set_t  *envp)
     175             : {
     176         881 :     krb5_context context;
     177         881 :     krb5_error_code ret;
     178         881 :     gsskrb5_cred input_cred;
     179       71866 :     krb5_ccache id = NULL;
     180         881 :     time_t exp_current;
     181         881 :     time_t exp_new;
     182       71866 :     gss_buffer_set_t env = GSS_C_NO_BUFFER_SET;
     183       71866 :     const char *cs_unique_ccache = NULL;
     184       71866 :     const char *cs_ccache_name = NULL;
     185       71866 :     const char *cs_user_name = NULL;
     186       71866 :     const char *cs_app_name = NULL;
     187       71866 :     char *ccache_name = NULL;
     188       71866 :     OM_uint32 major_status = GSS_S_FAILURE;
     189         881 :     OM_uint32 junk;
     190       71866 :     OM_uint32 overwrite_cred = store_cred_flags & GSS_C_STORE_CRED_OVERWRITE;
     191       71866 :     int default_for = 0;
     192             : 
     193       71866 :     *minor_status = 0;
     194             : 
     195             :     /* Sanity check inputs */
     196       71866 :     if (cred_usage != GSS_C_INITIATE) {
     197             :         /* It'd be nice if we could also do accept, writing a keytab */
     198           0 :         *minor_status = GSS_KRB5_S_G_BAD_USAGE;
     199           0 :         return GSS_S_FAILURE;
     200             :     }
     201      143732 :     if (desired_mech != GSS_C_NO_OID &&
     202       71866 :         gss_oid_equal(desired_mech, GSS_KRB5_MECHANISM) == 0)
     203           0 :         return GSS_S_BAD_MECH;
     204       71866 :     if (input_cred_handle == GSS_C_NO_CREDENTIAL)
     205           0 :         return GSS_S_CALL_INACCESSIBLE_READ;
     206       71866 :     input_cred = (gsskrb5_cred)input_cred_handle;
     207             : 
     208             :     /* Sanity check the input_cred */
     209       71866 :     if (input_cred->usage != cred_usage && input_cred->usage != GSS_C_BOTH) {
     210           0 :         *minor_status = GSS_KRB5_S_G_BAD_USAGE;
     211           0 :         return GSS_S_NO_CRED;
     212             :     }
     213       71866 :     if (input_cred->principal == NULL) {
     214           0 :         *minor_status = GSS_KRB5_S_KG_TGT_MISSING;
     215           0 :         return GSS_S_NO_CRED;
     216             :     }
     217             : 
     218             :     /* Extract the ccache name from the store if given */
     219       71866 :     if (cred_store != GSS_C_NO_CRED_STORE) {
     220       71866 :         major_status = __gsskrb5_cred_store_find(minor_status, cred_store,
     221             :                                                  "unique_ccache_type",
     222             :                                                  &cs_unique_ccache);
     223       71866 :         if (GSS_ERROR(major_status))
     224           0 :             return major_status;
     225       71866 :         major_status = __gsskrb5_cred_store_find(minor_status, cred_store,
     226             :                                                  "ccache", &cs_ccache_name);
     227       71866 :         if (GSS_ERROR(major_status))
     228           0 :             return major_status;
     229       71866 :         major_status = __gsskrb5_cred_store_find(minor_status, cred_store,
     230             :                                                  "username", &cs_user_name);
     231       71866 :         if (GSS_ERROR(major_status))
     232           0 :             return major_status;
     233       71866 :         major_status = __gsskrb5_cred_store_find(minor_status, cred_store,
     234             :                                                  "appname", &cs_app_name);
     235       71866 :         if (GSS_ERROR(major_status))
     236           0 :             return major_status;
     237             :     }
     238             : 
     239       71866 :     GSSAPI_KRB5_INIT (&context);
     240         881 :     HEIMDAL_MUTEX_lock(&input_cred->cred_id_mutex);
     241             : 
     242       71866 :     if (cs_ccache_name && strchr(cs_ccache_name, '%')) {
     243           0 :         ret = _krb5_expand_default_cc_name(context, cs_ccache_name,
     244             :                                            &ccache_name);
     245           0 :         if (ret) {
     246           0 :             HEIMDAL_MUTEX_unlock(&input_cred->cred_id_mutex);
     247           0 :             *minor_status = ret;
     248           0 :             return GSS_S_FAILURE;
     249             :         }
     250           0 :         cs_ccache_name = ccache_name;
     251             :     }
     252             : 
     253             :     /* More sanity checking of the input_cred (good to fail early) */
     254       71866 :     ret = krb5_cc_get_lifetime(context, input_cred->ccache, &exp_new);
     255       71866 :     if (ret) {
     256           0 :         HEIMDAL_MUTEX_unlock(&input_cred->cred_id_mutex);
     257           0 :         *minor_status = ret;
     258           0 :         free(ccache_name);
     259           0 :         return GSS_S_NO_CRED;
     260             :     }
     261             : 
     262       71866 :     ret = check_destination_tgt_policy(context, cs_app_name, input_cred);
     263       71866 :     if (ret) {
     264           0 :         HEIMDAL_MUTEX_unlock(&input_cred->cred_id_mutex);
     265           0 :         *minor_status = ret;
     266           0 :         free(ccache_name);
     267           0 :         return GSS_S_NO_CRED;
     268             :     }
     269             : 
     270             :     /*
     271             :      * Find an appropriate ccache, which will be one of:
     272             :      *
     273             :      *  - the one given in the cred_store, if given
     274             :      *  - a new unique one for some ccache type in the cred_store, if given
     275             :      *  - a subsidiary cache named for the principal in the default collection,
     276             :      *    if the principal is the "best principal for the user"
     277             :      *  - the default ccache
     278             :      */
     279       71866 :     if (cs_ccache_name) {
     280       71866 :         ret = krb5_cc_resolve(context, cs_ccache_name, &id);
     281           0 :     } else if (cs_unique_ccache) {
     282           0 :         overwrite_cred = 1;
     283           0 :         ret = krb5_cc_new_unique(context, cs_unique_ccache, NULL, &id);
     284           0 :     } else if (principal_is_best_for_user(context, cs_app_name,
     285           0 :                                           input_cred->principal,
     286             :                                           cs_user_name)) {
     287           0 :         ret = krb5_cc_default(context, &id);
     288           0 :         if (ret == 0 && !same_princ(context, id, input_cred->ccache)) {
     289           0 :             krb5_cc_close(context, id);
     290           0 :             ret = krb5_cc_default_for(context, input_cred->principal, &id);
     291           0 :             default_for = 1;
     292             :         }
     293             :     } else {
     294           0 :         ret = krb5_cc_default_for(context, input_cred->principal, &id);
     295           0 :         default_for = 1;
     296             :     }
     297             : 
     298       71866 :     if (ret || id == NULL) {
     299           0 :         HEIMDAL_MUTEX_unlock(&input_cred->cred_id_mutex);
     300           0 :         *minor_status = ret;
     301           0 :         free(ccache_name);
     302           0 :         return ret == 0 ? GSS_S_NO_CRED : GSS_S_FAILURE;
     303             :     }
     304             : 
     305             :     /*
     306             :      * If we're using a subsidiary ccache for this principal and it has some
     307             :      * other principal's tickets in it -> overwrite.
     308             :      */
     309       71866 :     if (!overwrite_cred && default_for &&
     310           0 :         !same_princ(context, id, input_cred->ccache))
     311           0 :         overwrite_cred = 1;
     312       71866 :     if (!overwrite_cred && same_princ(context, id, input_cred->ccache)) {
     313             :         /*
     314             :          * If current creds are for the same princ as we already had creds for,
     315             :          * and the new creds live longer than the old, overwrite.
     316             :          */
     317           0 :         ret = krb5_cc_get_lifetime(context, id, &exp_current);
     318           0 :         if (ret != 0 || exp_new > exp_current)
     319           0 :             overwrite_cred = 1;
     320             :     }
     321             : 
     322       71866 :     if (overwrite_cred) {
     323       71866 :         ret = krb5_cc_initialize(context, id, input_cred->principal);
     324       71866 :         if (ret == 0)
     325       71866 :             ret = krb5_cc_copy_match_f(context, input_cred->ccache, id, NULL, NULL,
     326             :                                        NULL);
     327             :     }
     328             : 
     329       71866 :     if ((store_cred_flags & GSS_C_STORE_CRED_SET_PROCESS) && envp == NULL)
     330           0 :         envp = &env;
     331       71866 :     if (envp != NULL) {
     332           0 :         char *fullname = NULL;
     333             :         
     334           0 :         if ((ret = krb5_cc_get_full_name(context, id, &fullname)) == 0) {
     335           0 :             major_status = add_env(minor_status, envp, "KRB5CCNAME", fullname);
     336           0 :             free(fullname);
     337           0 :             if (major_status)
     338           0 :                 ret = *minor_status;
     339             :         }
     340             :     }
     341       71866 :     (void) krb5_cc_close(context, id);
     342             : 
     343         881 :     HEIMDAL_MUTEX_unlock(&input_cred->cred_id_mutex);
     344       71866 :     if (ret == 0 && (store_cred_flags & GSS_C_STORE_CRED_SET_PROCESS) &&
     345           0 :         (major_status = set_proc(minor_status, *envp)) != GSS_S_COMPLETE)
     346           0 :         ret = *minor_status;
     347       71866 :     (void) gss_release_buffer_set(&junk, &env);
     348       71866 :     free(ccache_name);
     349       71866 :     *minor_status = ret;
     350       71866 :     return ret ? major_status : GSS_S_COMPLETE;
     351             : }
     352             : 
     353             : OM_uint32 GSSAPI_CALLCONV
     354           0 : _gsskrb5_store_cred_into(OM_uint32         *minor_status,
     355             :                          gss_const_cred_id_t input_cred_handle,
     356             :                          gss_cred_usage_t  cred_usage,
     357             :                          const gss_OID     desired_mech,
     358             :                          OM_uint32         overwrite_cred,
     359             :                          OM_uint32         default_cred,
     360             :                          gss_const_key_value_set_t cred_store,
     361             :                          gss_OID_set       *elements_stored,
     362             :                          gss_cred_usage_t  *cred_usage_stored)
     363             : {
     364           0 :     OM_uint32 store_cred_flags =
     365           0 :         (overwrite_cred ? GSS_C_STORE_CRED_OVERWRITE : 0) |
     366           0 :         (default_cred ? GSS_C_STORE_CRED_DEFAULT : 0);
     367             : 
     368           0 :     return _gsskrb5_store_cred_into2(minor_status, input_cred_handle,
     369             :                                      cred_usage, desired_mech,
     370             :                                      store_cred_flags, cred_store,
     371             :                                      elements_stored, cred_usage_stored, NULL);
     372             : }

Generated by: LCOV version 1.14