LCOV - code coverage report
Current view: top level - third_party/heimdal/kdc - gss_preauth.c (source / functions) Hit Total Coverage
Test: coverage report for master 2f515e9b Lines: 8 485 1.6 %
Date: 2024-04-21 15:09:00 Functions: 1 21 4.8 %

          Line data    Source code
       1             : /*
       2             :  * Copyright (c) 2021, PADL Software Pty Ltd.
       3             :  * All rights reserved.
       4             :  *
       5             :  * Portions Copyright (c) 2019 Kungliga Tekniska Högskolan
       6             :  *
       7             :  * Redistribution and use in source and binary forms, with or without
       8             :  * modification, are permitted provided that the following conditions
       9             :  * are met:
      10             :  *
      11             :  * 1. Redistributions of source code must retain the above copyright
      12             :  *    notice, this list of conditions and the following disclaimer.
      13             :  *
      14             :  * 2. Redistributions in binary form must reproduce the above copyright
      15             :  *    notice, this list of conditions and the following disclaimer in the
      16             :  *    documentation and/or other materials provided with the distribution.
      17             :  *
      18             :  * 3. Neither the name of PADL Software nor the names of its contributors
      19             :  *    may be used to endorse or promote products derived from this software
      20             :  *    without specific prior written permission.
      21             :  *
      22             :  * THIS SOFTWARE IS PROVIDED BY PADL SOFTWARE AND CONTRIBUTORS ``AS IS'' AND
      23             :  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
      24             :  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
      25             :  * ARE DISCLAIMED.  IN NO EVENT SHALL PADL SOFTWARE OR CONTRIBUTORS BE LIABLE
      26             :  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
      27             :  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
      28             :  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
      29             :  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
      30             :  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
      31             :  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
      32             :  * SUCH DAMAGE.
      33             :  */
      34             : 
      35             : #include "kdc_locl.h"
      36             : 
      37             : #include <gssapi/gssapi.h>
      38             : #include <gssapi_mech.h>
      39             : 
      40             : #include <gss-preauth-protos.h>
      41             : #include <gss-preauth-private.h>
      42             : 
      43             : #include "gss_preauth_authorizer_plugin.h"
      44             : 
      45             : struct gss_client_params {
      46             :     OM_uint32 major, minor;
      47             :     gss_ctx_id_t context_handle;
      48             :     gss_name_t initiator_name;
      49             :     gss_OID mech_type;
      50             :     gss_buffer_desc output_token;
      51             :     OM_uint32 flags;
      52             :     OM_uint32 lifetime;
      53             :     krb5_checksum req_body_checksum;
      54             : };
      55             : 
      56             : static void
      57             : pa_gss_display_status(astgs_request_t r,
      58             :                       OM_uint32 major,
      59             :                       OM_uint32 minor,
      60             :                       gss_client_params *gcp,
      61             :                       const char *msg);
      62             : 
      63             : static void
      64             : pa_gss_display_name(gss_name_t name,
      65             :                     gss_buffer_t namebuf,
      66             :                     gss_const_buffer_t *namebuf_p);
      67             : 
      68             : static void HEIM_CALLCONV
      69             : pa_gss_dealloc_client_params(void *ptr);
      70             : 
      71             : /*
      72             :  * Create a checksum over KDC-REQ-BODY (without the nonce), used to
      73             :  * assert the request is invariant within the preauth conversation.
      74             :  */
      75             : static krb5_error_code
      76           0 : pa_gss_create_req_body_checksum(astgs_request_t r,
      77             :                                 krb5_checksum *checksum)
      78             : {
      79           0 :     krb5_error_code ret;
      80           0 :     KDC_REQ_BODY b = r->req.req_body;
      81           0 :     krb5_data data;
      82           0 :     size_t size;
      83             : 
      84           0 :     b.nonce = 0;
      85             : 
      86           0 :     ASN1_MALLOC_ENCODE(KDC_REQ_BODY, data.data, data.length, &b, &size, ret);
      87           0 :     heim_assert(ret || data.length,
      88             :                 "internal asn1 encoder error");
      89             : 
      90           0 :     ret = krb5_create_checksum(r->context, NULL, 0, CKSUMTYPE_SHA256,
      91             :                                data.data, data.length, checksum);
      92           0 :     krb5_data_free(&data);
      93             : 
      94           0 :     return ret;
      95             : }
      96             : 
      97             : /*
      98             :  * Verify a checksum over KDC-REQ-BODY (without the nonce), used to
      99             :  * assert the request is invariant within the preauth conversation.
     100             :  */
     101             : static krb5_error_code
     102           0 : pa_gss_verify_req_body_checksum(astgs_request_t r,
     103             :                                 krb5_checksum *checksum)
     104             : {
     105           0 :     krb5_error_code ret;
     106           0 :     KDC_REQ_BODY b = r->req.req_body;
     107           0 :     krb5_data data;
     108           0 :     size_t size;
     109             : 
     110           0 :     b.nonce = 0;
     111             : 
     112           0 :     ASN1_MALLOC_ENCODE(KDC_REQ_BODY, data.data, data.length, &b, &size, ret);
     113           0 :     heim_assert(ret || data.length,
     114             :                 "internal asn1 encoder error");
     115             : 
     116           0 :     ret = _kdc_verify_checksum(r->context, NULL, 0, &data, checksum);
     117           0 :     krb5_data_free(&data);
     118             : 
     119           0 :     return ret;
     120             : }
     121             : 
     122             : /*
     123             :  * Decode the FX-COOKIE context state, consisting of the exported
     124             :  * GSS context token concatenated with the checksum of the initial
     125             :  * KDC-REQ-BODY.
     126             :  */
     127             : static krb5_error_code
     128           0 : pa_gss_decode_context_state(astgs_request_t r,
     129             :                             const krb5_data *state,
     130             :                             gss_buffer_t sec_context_token,
     131             :                             krb5_checksum *req_body_checksum)
     132             : {
     133           0 :     krb5_error_code ret;
     134           0 :     krb5_storage *sp;
     135           0 :     size_t cksumsize;
     136           0 :     krb5_data data;
     137           0 :     int32_t cksumtype;
     138             : 
     139           0 :     memset(req_body_checksum, 0, sizeof(*req_body_checksum));
     140           0 :     sec_context_token->length = 0;
     141           0 :     sec_context_token->value = NULL;
     142             : 
     143           0 :     krb5_data_zero(&data);
     144             : 
     145           0 :     sp = krb5_storage_from_readonly_mem(state->data, state->length);
     146           0 :     if (sp == NULL) {
     147           0 :         ret = krb5_enomem(r->context);
     148           0 :         goto out;
     149             :     }
     150             : 
     151           0 :     krb5_storage_set_eof_code(sp, KRB5_BAD_MSIZE);
     152           0 :     krb5_storage_set_byteorder(sp, KRB5_STORAGE_BYTEORDER_PACKED);
     153             : 
     154           0 :     ret = krb5_ret_data(sp, &data);
     155           0 :     if (ret)
     156           0 :         goto out;
     157             : 
     158           0 :     ret = krb5_ret_int32(sp, &cksumtype);
     159           0 :     if (ret)
     160           0 :         goto out;
     161             : 
     162           0 :     req_body_checksum->cksumtype = (CKSUMTYPE)cksumtype;
     163             : 
     164           0 :     if (req_body_checksum->cksumtype == CKSUMTYPE_NONE ||
     165           0 :         krb5_checksum_is_keyed(r->context, req_body_checksum->cksumtype)) {
     166           0 :         ret = KRB5KDC_ERR_SUMTYPE_NOSUPP;
     167           0 :         goto out;
     168             :     }
     169             : 
     170           0 :     ret = krb5_checksumsize(r->context, req_body_checksum->cksumtype,
     171             :                             &cksumsize);
     172           0 :     if (ret)
     173           0 :         goto out;
     174             : 
     175           0 :     req_body_checksum->checksum.data = malloc(cksumsize);
     176           0 :     if (req_body_checksum->checksum.data == NULL) {
     177           0 :         ret = krb5_enomem(r->context);
     178           0 :         goto out;
     179             :     }
     180             : 
     181           0 :     if (krb5_storage_read(sp, req_body_checksum->checksum.data,
     182           0 :                           cksumsize) != cksumsize) {
     183           0 :         ret = KRB5_BAD_MSIZE;
     184           0 :         goto out;
     185             :     }
     186             : 
     187           0 :     req_body_checksum->checksum.length = cksumsize;
     188             : 
     189           0 :     _krb5_gss_data_to_buffer(&data, sec_context_token);
     190             : 
     191           0 : out:
     192           0 :     if (ret) {
     193           0 :         krb5_data_free(&data);
     194           0 :         free_Checksum(req_body_checksum);
     195           0 :         memset(req_body_checksum, 0, sizeof(*req_body_checksum));
     196             :     }
     197           0 :     krb5_storage_free(sp);
     198             : 
     199           0 :     return ret;
     200             : }
     201             : 
     202             : /*
     203             :  * Deserialize a GSS-API security context from the FAST cookie.
     204             :  */
     205             : static krb5_error_code
     206           0 : pa_gss_get_context_state(astgs_request_t r,
     207             :                          gss_client_params *gcp)
     208             : {
     209           0 :     int idx = 0;
     210           0 :     PA_DATA *fast_pa;
     211           0 :     krb5_error_code ret;
     212             : 
     213           0 :     OM_uint32 major, minor;
     214           0 :     gss_buffer_desc sec_context_token;
     215             : 
     216           0 :     fast_pa = krb5_find_padata(r->fast.fast_state.val,
     217             :                                r->fast.fast_state.len,
     218             :                                KRB5_PADATA_GSS, &idx);
     219           0 :     if (fast_pa == NULL)
     220           0 :         return 0;
     221             : 
     222           0 :     ret = pa_gss_decode_context_state(r, &fast_pa->padata_value,
     223             :                                       &sec_context_token,
     224             :                                       &gcp->req_body_checksum);
     225           0 :     if (ret)
     226           0 :         return ret;
     227             : 
     228           0 :     ret = pa_gss_verify_req_body_checksum(r, &gcp->req_body_checksum);
     229           0 :     if (ret) {
     230           0 :         gss_release_buffer(&minor, &sec_context_token);
     231           0 :         return ret;
     232             :     }
     233             : 
     234           0 :     major = gss_import_sec_context(&minor, &sec_context_token,
     235             :                                    &gcp->context_handle);
     236           0 :     if (GSS_ERROR(major)) {
     237           0 :         pa_gss_display_status(r, major, minor, gcp,
     238             :                               "Failed to import GSS pre-authentication context");
     239           0 :         ret = _krb5_gss_map_error(major, minor);
     240             :     } else
     241           0 :         ret = 0;
     242             : 
     243           0 :     gss_release_buffer(&minor, &sec_context_token);
     244             : 
     245           0 :     return ret;
     246             : }
     247             : 
     248             : /*
     249             :  * Encode the FX-COOKIE context state, consisting of the exported
     250             :  * GSS context token concatenated with the checksum of the initial
     251             :  * KDC-REQ-BODY.
     252             :  */
     253             : static krb5_error_code
     254           0 : pa_gss_encode_context_state(astgs_request_t r,
     255             :                             gss_const_buffer_t sec_context_token,
     256             :                             const krb5_checksum *req_body_checksum,
     257             :                             krb5_data *state)
     258             : {
     259           0 :     krb5_error_code ret;
     260           0 :     krb5_storage *sp;
     261           0 :     krb5_data data;
     262             : 
     263           0 :     krb5_data_zero(state);
     264             : 
     265           0 :     sp = krb5_storage_emem();
     266           0 :     if (sp == NULL) {
     267           0 :         ret = krb5_enomem(r->context);
     268           0 :         goto out;
     269             :     }
     270             : 
     271           0 :     krb5_storage_set_byteorder(sp, KRB5_STORAGE_BYTEORDER_PACKED);
     272             : 
     273           0 :     _krb5_gss_buffer_to_data(sec_context_token, &data);
     274             : 
     275           0 :     ret = krb5_store_data(sp, data);
     276           0 :     if (ret)
     277           0 :         goto out;
     278             : 
     279           0 :     ret = krb5_store_int32(sp, (int32_t)req_body_checksum->cksumtype);
     280           0 :     if (ret)
     281           0 :         goto out;
     282             : 
     283           0 :     ret = krb5_store_bytes(sp, req_body_checksum->checksum.data,
     284           0 :                            req_body_checksum->checksum.length);
     285           0 :     if (ret)
     286           0 :         goto out;
     287             : 
     288           0 :     ret = krb5_storage_to_data(sp, state);
     289           0 :     if (ret)
     290           0 :         goto out;
     291             : 
     292           0 : out:
     293           0 :     krb5_storage_free(sp);
     294             : 
     295           0 :     return ret;
     296             : }
     297             : 
     298             : /*
     299             :  * Serialize a GSS-API security context into a FAST cookie.
     300             :  */
     301             : static krb5_error_code
     302           0 : pa_gss_set_context_state(astgs_request_t r,
     303             :                          gss_client_params *gcp)
     304             : {
     305           0 :     krb5_error_code ret;
     306           0 :     PA_DATA *fast_pa;
     307           0 :     int idx = 0;
     308           0 :     krb5_data state;
     309             : 
     310           0 :     OM_uint32 major, minor;
     311           0 :     gss_buffer_desc sec_context_token = GSS_C_EMPTY_BUFFER;
     312             : 
     313             :     /*
     314             :      * On second and subsequent responses, we can recycle the checksum
     315             :      * from the request as it is validated and invariant. This saves
     316             :      * re-encoding the request body again.
     317             :      */
     318           0 :     if (gcp->req_body_checksum.cksumtype == CKSUMTYPE_NONE) {
     319           0 :         ret = pa_gss_create_req_body_checksum(r, &gcp->req_body_checksum);
     320           0 :         if (ret)
     321           0 :             return ret;
     322             :     }
     323             : 
     324           0 :     major = gss_export_sec_context(&minor, &gcp->context_handle,
     325             :                                    &sec_context_token);
     326           0 :     if (GSS_ERROR(major)) {
     327           0 :         pa_gss_display_status(r, major, minor, gcp,
     328             :                               "Failed to export GSS pre-authentication context");
     329           0 :         return _krb5_gss_map_error(major, minor);
     330             :     }
     331             : 
     332           0 :     ret = pa_gss_encode_context_state(r, &sec_context_token,
     333           0 :                                       &gcp->req_body_checksum, &state);
     334           0 :     gss_release_buffer(&minor, &sec_context_token);
     335           0 :     if (ret)
     336           0 :         return ret;
     337             : 
     338           0 :     fast_pa = krb5_find_padata(r->fast.fast_state.val,
     339             :                                r->fast.fast_state.len,
     340             :                                KRB5_PADATA_GSS, &idx);
     341           0 :     if (fast_pa) {
     342           0 :         krb5_data_free(&fast_pa->padata_value);
     343           0 :         fast_pa->padata_value = state;
     344             :     } else {
     345           0 :         ret = krb5_padata_add(r->context, &r->fast.fast_state,
     346             :                               KRB5_PADATA_GSS,
     347             :                               state.data, state.length);
     348           0 :         if (ret)
     349           0 :             krb5_data_free(&state);
     350             :     }
     351             : 
     352           0 :     return ret;
     353             : }
     354             : 
     355             : static krb5_error_code
     356           0 : pa_gss_acquire_acceptor_cred(astgs_request_t r,
     357             :                              gss_client_params *gcp,
     358             :                              gss_cred_id_t *cred)
     359             : {
     360           0 :     krb5_error_code ret;
     361           0 :     krb5_principal tgs_name;
     362             : 
     363           0 :     OM_uint32 major, minor;
     364           0 :     gss_name_t target_name = GSS_C_NO_NAME;
     365           0 :     gss_buffer_desc display_name = GSS_C_EMPTY_BUFFER;
     366           0 :     gss_const_buffer_t display_name_p;
     367             : 
     368           0 :     *cred = GSS_C_NO_CREDENTIAL;
     369             : 
     370           0 :     ret = krb5_make_principal(r->context, &tgs_name, r->req.req_body.realm,
     371             :                               KRB5_TGS_NAME, r->req.req_body.realm, NULL);
     372           0 :     if (ret)
     373           0 :         return ret;
     374             : 
     375           0 :     ret = _krb5_gss_pa_unparse_name(r->context, tgs_name, &target_name);
     376           0 :     krb5_free_principal(r->context, tgs_name);
     377           0 :     if (ret)
     378           0 :         return ret;
     379             : 
     380           0 :     pa_gss_display_name(target_name, &display_name, &display_name_p);
     381             : 
     382           0 :     kdc_log(r->context, r->config, 4,
     383             :             "Acquiring GSS acceptor credential for %.*s",
     384           0 :             (int)display_name_p->length, (char *)display_name_p->value);
     385             : 
     386           0 :     major = gss_acquire_cred(&minor, target_name, GSS_C_INDEFINITE,
     387           0 :                              r->config->gss_mechanisms_allowed,
     388             :                              GSS_C_ACCEPT, cred, NULL, NULL);
     389           0 :     ret = _krb5_gss_map_error(major, minor);
     390             : 
     391           0 :     if (ret)
     392           0 :         pa_gss_display_status(r, major, minor, gcp,
     393             :                               "Failed to acquire GSS acceptor credential");
     394             : 
     395           0 :     gss_release_buffer(&minor, &display_name);
     396           0 :     gss_release_name(&minor, &target_name);
     397             : 
     398           0 :     return ret;
     399             : }
     400             : 
     401             : krb5_error_code
     402           0 : _kdc_gss_rd_padata(astgs_request_t r,
     403             :                    const PA_DATA *pa,
     404             :                    gss_client_params **pgcp,
     405             :                    int *open)
     406             : {
     407           0 :     krb5_error_code ret;
     408             : 
     409           0 :     OM_uint32 minor;
     410           0 :     gss_client_params *gcp = NULL;
     411           0 :     gss_cred_id_t cred = GSS_C_NO_CREDENTIAL;
     412           0 :     gss_buffer_desc input_token = GSS_C_EMPTY_BUFFER;
     413           0 :     struct gss_channel_bindings_struct cb;
     414             : 
     415           0 :     memset(&cb, 0, sizeof(cb));
     416             : 
     417           0 :     *pgcp = NULL;
     418             : 
     419           0 :     if (!r->config->enable_gss_preauth) {
     420           0 :         ret = KRB5KDC_ERR_POLICY;
     421           0 :         goto out;
     422             :     }
     423             : 
     424           0 :     if (pa->padata_value.length == 0) {
     425           0 :         ret = KRB5KDC_ERR_PREAUTH_FAILED;
     426           0 :         goto out;
     427             :     }
     428             : 
     429           0 :     gcp = kdc_object_alloc(sizeof(*gcp), "pa-gss-client-params", pa_gss_dealloc_client_params);
     430           0 :     if (gcp == NULL) {
     431           0 :         ret = krb5_enomem(r->context);
     432           0 :         goto out;
     433             :     }
     434             : 
     435             :     /* errors are fast fail until gss_accept_sec_context() is called */
     436           0 :     gcp->major = GSS_S_NO_CONTEXT;
     437             : 
     438           0 :     ret = pa_gss_get_context_state(r, gcp);
     439           0 :     if (ret)
     440           0 :         goto out;
     441             : 
     442           0 :     ret = pa_gss_acquire_acceptor_cred(r, gcp, &cred);
     443           0 :     if (ret)
     444           0 :         goto out;
     445             : 
     446           0 :     _krb5_gss_data_to_buffer(&pa->padata_value, &input_token);
     447           0 :     _krb5_gss_data_to_buffer(&r->req.req_body._save, &cb.application_data);
     448             : 
     449           0 :     gcp->major = gss_accept_sec_context(&gcp->minor,
     450             :                                         &gcp->context_handle,
     451             :                                         cred,
     452             :                                         &input_token,
     453             :                                         &cb,
     454             :                                         &gcp->initiator_name,
     455             :                                         &gcp->mech_type,
     456           0 :                                         &gcp->output_token,
     457             :                                         &gcp->flags,
     458             :                                         &gcp->lifetime,
     459             :                                         NULL); /* delegated_cred_handle */
     460             : 
     461           0 :     ret = _krb5_gss_map_error(gcp->major, gcp->minor);
     462             : 
     463           0 :     if (GSS_ERROR(gcp->major)) {
     464           0 :         pa_gss_display_status(r, gcp->major, gcp->minor, gcp,
     465             :                               "Failed to accept GSS security context");
     466           0 :     } else if ((gcp->flags & GSS_C_ANON_FLAG) && !_kdc_is_anon_request(&r->req)) {
     467           0 :         kdc_log(r->context, r->config, 2,
     468             :                 "Anonymous GSS pre-authentication request w/o anonymous flag");
     469           0 :         ret = KRB5KDC_ERR_BADOPTION;
     470             :     } else
     471           0 :         *open = (gcp->major == GSS_S_COMPLETE);
     472             : 
     473           0 : out:
     474           0 :     gss_release_cred(&minor, &cred);
     475             : 
     476           0 :     if (gcp && gcp->major != GSS_S_NO_CONTEXT)
     477           0 :         *pgcp = gcp;
     478             :     else
     479           0 :         kdc_object_release(gcp);
     480             : 
     481           0 :     return ret;
     482             : }
     483             : 
     484             : krb5_timestamp
     485           0 : _kdc_gss_endtime(astgs_request_t r,
     486             :                  gss_client_params *gcp)
     487             : {
     488           0 :     krb5_timestamp endtime;
     489             : 
     490           0 :     if (gcp->lifetime == GSS_C_INDEFINITE)
     491           0 :         endtime = 0;
     492             :     else
     493           0 :         endtime = kdc_time + gcp->lifetime;
     494             : 
     495           0 :     kdc_log(r->context, r->config, 10,
     496             :             "GSS pre-authentication endtime is %ld", (long)endtime);
     497             : 
     498           0 :     return endtime;
     499             : }
     500             : 
     501             : struct pa_gss_authorize_plugin_ctx {
     502             :     astgs_request_t r;
     503             :     struct gss_client_params *gcp;
     504             :     krb5_boolean authorized;
     505             :     krb5_principal initiator_princ;
     506             : };
     507             : 
     508             : static krb5_error_code KRB5_LIB_CALL
     509           0 : pa_gss_authorize_cb(krb5_context context,
     510             :                     const void *plug,
     511             :                     void *plugctx,
     512             :                     void *userctx)
     513             : {
     514           0 :     const krb5plugin_gss_preauth_authorizer_ftable *authorizer = plug;
     515           0 :     struct pa_gss_authorize_plugin_ctx *pa_gss_authorize_plugin_ctx = userctx;
     516             : 
     517           0 :     return authorizer->authorize(plugctx,
     518             :                                  pa_gss_authorize_plugin_ctx->r,
     519           0 :                                  pa_gss_authorize_plugin_ctx->gcp->initiator_name,
     520           0 :                                  pa_gss_authorize_plugin_ctx->gcp->mech_type,
     521           0 :                                  pa_gss_authorize_plugin_ctx->gcp->flags,
     522             :                                  &pa_gss_authorize_plugin_ctx->authorized,
     523             :                                  &pa_gss_authorize_plugin_ctx->initiator_princ);
     524             : }
     525             : 
     526             : static const char *plugin_deps[] = {
     527             :     "kdc",
     528             :     "hdb",
     529             :     "gssapi",
     530             :     "krb5",
     531             :     NULL
     532             : };
     533             : 
     534             : static struct heim_plugin_data
     535             : gss_preauth_authorizer_data = {
     536             :     "kdc",
     537             :     KDC_GSS_PREAUTH_AUTHORIZER,
     538             :     KDC_GSS_PREAUTH_AUTHORIZER_VERSION_1,
     539             :     plugin_deps,
     540             :     kdc_get_instance
     541             : };
     542             : 
     543             : static krb5_error_code
     544           0 : pa_gss_authorize_plugin(astgs_request_t r,
     545             :                         struct gss_client_params *gcp,
     546             :                         gss_const_buffer_t display_name,
     547             :                         krb5_boolean *authorized,
     548             :                         krb5_principal *initiator_princ)
     549             : {
     550           0 :     krb5_error_code ret;
     551           0 :     struct pa_gss_authorize_plugin_ctx ctx;
     552             : 
     553           0 :     ctx.r = r;
     554           0 :     ctx.gcp = gcp;
     555           0 :     ctx.authorized = 0;
     556           0 :     ctx.initiator_princ = NULL;
     557             : 
     558           0 :     krb5_clear_error_message(r->context);
     559           0 :     ret = _krb5_plugin_run_f(r->context, &gss_preauth_authorizer_data,
     560             :                              0, &ctx, pa_gss_authorize_cb);
     561             : 
     562           0 :     if (ret != KRB5_PLUGIN_NO_HANDLE) {
     563           0 :         const char *msg = krb5_get_error_message(r->context, ret);
     564             : 
     565           0 :         kdc_log(r->context, r->config, 7,
     566             :                 "GSS authz plugin %sauthorize%s %s initiator %.*s: %s",
     567           0 :                 ctx.authorized ? "" : "did not " ,
     568           0 :                 ctx.authorized ? "d" : "",
     569           0 :                 gss_oid_to_name(gcp->mech_type),
     570           0 :                 (int)display_name->length, (char *)display_name->value,
     571             :                 msg);
     572           0 :         krb5_free_error_message(r->context, msg);
     573             :     }
     574             : 
     575           0 :     *authorized = ctx.authorized;
     576           0 :     *initiator_princ = ctx.initiator_princ;
     577             : 
     578           0 :     return ret;
     579             : }
     580             : 
     581             : static krb5_error_code
     582           0 : pa_gss_authorize_default(astgs_request_t r,
     583             :                          struct gss_client_params *gcp,
     584             :                          gss_const_buffer_t display_name,
     585             :                          krb5_boolean *authorized,
     586             :                          krb5_principal *initiator_princ)
     587             : {
     588           0 :     krb5_error_code ret;
     589           0 :     krb5_principal principal;
     590           0 :     krb5_const_realm realm = r->server->principal->realm;
     591           0 :     int flags = 0, cross_realm_allowed = 0, unauth_anon;
     592             : 
     593             :     /*
     594             :      * gss_cross_realm_mechanisms_allowed is a list of GSS-API mechanisms
     595             :      * that are allowed to map directly to Kerberos principals in any
     596             :      * realm. If the authenticating mechanism is not on the list, then
     597             :      * the initiator will be mapped to an enterprise principal in the
     598             :      * service realm. This is useful to stop synthetic principals in
     599             :      * foreign realms being conflated with true cross-realm principals.
     600             :      */
     601           0 :     if (r->config->gss_cross_realm_mechanisms_allowed) {
     602           0 :         OM_uint32 minor;
     603             : 
     604           0 :         gss_test_oid_set_member(&minor, gcp->mech_type,
     605           0 :                                 r->config->gss_cross_realm_mechanisms_allowed,
     606             :                                 &cross_realm_allowed);
     607             :     }
     608             : 
     609           0 :     kdc_log(r->context, r->config, 10,
     610             :             "Initiator %.*s will be mapped to %s",
     611           0 :             (int)display_name->length, (char *)display_name->value,
     612           0 :             cross_realm_allowed ? "nt-principal" : "nt-enterprise-principal");
     613             : 
     614           0 :     if (!cross_realm_allowed)
     615           0 :         flags |= KRB5_PRINCIPAL_PARSE_ENTERPRISE | KRB5_PRINCIPAL_PARSE_NO_REALM;
     616             : 
     617           0 :     ret = _krb5_gss_pa_parse_name(r->context, gcp->initiator_name,
     618             :                                   flags, &principal);
     619           0 :     if (ret) {
     620           0 :         const char *msg = krb5_get_error_message(r->context, ret);
     621             : 
     622           0 :         kdc_log(r->context, r->config, 2,
     623             :                 "Failed to parse %s initiator name %.*s: %s",
     624           0 :                 gss_oid_to_name(gcp->mech_type),
     625           0 :                 (int)display_name->length, (char *)display_name->value, msg);
     626           0 :         krb5_free_error_message(r->context, msg);
     627             : 
     628           0 :         return ret;
     629             :     }
     630             : 
     631             :     /*
     632             :      * GSS_C_ANON_FLAG indicates the client requested anonymous authentication
     633             :      * (it is validated against the request-anonymous flag).
     634             :      *
     635             :      * _kdc_is_anonymous_pkinit() returns TRUE if the principal contains both
     636             :      * the well known anonymous name and realm.
     637             :      */
     638           0 :     unauth_anon = (gcp->flags & GSS_C_ANON_FLAG) &&
     639           0 :         _kdc_is_anonymous_pkinit(r->context, principal);
     640             : 
     641             :     /*
     642             :      * Always use the anonymous entry created in our HDB, i.e. with the local
     643             :      * realm, for authorizing anonymous requests. This matches PKINIT behavior
     644             :      * as anonymous PKINIT requests include the KDC realm in the request.
     645             :      */
     646           0 :     if (unauth_anon || (flags & KRB5_PRINCIPAL_PARSE_ENTERPRISE)) {
     647           0 :         ret = krb5_principal_set_realm(r->context, principal, realm);
     648           0 :         if (ret) {
     649           0 :             krb5_free_principal(r->context, principal);
     650           0 :             return ret;
     651             :         }
     652             :     }
     653             : 
     654           0 :     if (unauth_anon) {
     655             :         /*
     656             :          * Special case to avoid changing _kdc_as_rep(). If the initiator is
     657             :          * the unauthenticated anonymous principal, r->client_princ also needs
     658             :          * to be set in order to force the AS-REP realm to be set to the well-
     659             :          * known anonymous identity. This is because (unlike anonymous PKINIT)
     660             :          * we only require the anonymous flag, not the anonymous name, in the
     661             :          * client AS-REQ.
     662             :          */
     663           0 :         krb5_principal anon_princ;
     664             : 
     665           0 :         ret = krb5_copy_principal(r->context, principal, &anon_princ);
     666           0 :         if (ret)
     667           0 :             return ret;
     668             : 
     669           0 :         krb5_free_principal(r->context, r->client_princ);
     670           0 :         r->client_princ = anon_princ;
     671             :     }
     672             : 
     673           0 :     *authorized = TRUE;
     674           0 :     *initiator_princ = principal;
     675             : 
     676           0 :     return 0;
     677             : }
     678             : 
     679             : krb5_error_code
     680           0 : _kdc_gss_check_client(astgs_request_t r,
     681             :                       gss_client_params *gcp,
     682             :                       char **client_name)
     683             : {
     684           0 :     krb5_error_code ret;
     685           0 :     krb5_principal initiator_princ = NULL;
     686           0 :     hdb_entry *initiator = NULL;
     687           0 :     krb5_boolean authorized = FALSE;
     688           0 :     HDB *clientdb = r->clientdb;
     689             : 
     690           0 :     OM_uint32 minor;
     691           0 :     gss_buffer_desc display_name = GSS_C_EMPTY_BUFFER;
     692           0 :     gss_const_buffer_t display_name_p;
     693             : 
     694           0 :     *client_name = NULL;
     695             : 
     696           0 :     pa_gss_display_name(gcp->initiator_name, &display_name, &display_name_p);
     697             : 
     698             :     /*
     699             :      * If no plugins handled the authorization request, then all clients
     700             :      * are authorized as the directly corresponding Kerberos principal.
     701             :      */
     702           0 :     ret = pa_gss_authorize_plugin(r, gcp, display_name_p,
     703             :                                   &authorized, &initiator_princ);
     704           0 :     if (ret == KRB5_PLUGIN_NO_HANDLE)
     705           0 :         ret = pa_gss_authorize_default(r, gcp, display_name_p,
     706             :                                        &authorized, &initiator_princ);
     707           0 :     if (ret == 0 && !authorized)
     708           0 :         ret = KRB5_KDC_ERR_CLIENT_NAME_MISMATCH;
     709           0 :     if (ret)
     710           0 :         goto out;
     711             : 
     712           0 :     ret = krb5_unparse_name(r->context, initiator_princ, client_name);
     713           0 :     if (ret)
     714           0 :         goto out;
     715             : 
     716           0 :     kdc_log(r->context, r->config, 4,
     717             :             "Mapped GSS %s initiator %.*s to principal %s",
     718           0 :             gss_oid_to_name(gcp->mech_type),
     719           0 :             (int)display_name_p->length, (char *)display_name_p->value,
     720             :             *client_name);
     721             : 
     722           0 :     ret = _kdc_db_fetch(r->context,
     723             :                         r->config,
     724             :                         initiator_princ,
     725             :                         HDB_F_FOR_AS_REQ | HDB_F_GET_CLIENT |
     726             :                              HDB_F_CANON | HDB_F_SYNTHETIC_OK,
     727             :                         NULL,
     728             :                         &r->clientdb,
     729             :                         &initiator);
     730           0 :     if (ret) {
     731           0 :         const char *msg = krb5_get_error_message(r->context, ret);
     732             : 
     733           0 :         kdc_log(r->context, r->config, 4, "UNKNOWN -- %s: %s",
     734             :                 *client_name, msg);
     735           0 :         krb5_free_error_message(r->context, msg);
     736             : 
     737           0 :         goto out;
     738             :     }
     739             : 
     740             :     /*
     741             :      * If the AS-REQ client name was the well-known federated name, then
     742             :      * replace the client name with the initiator name. Otherwise, the
     743             :      * two principals must match, noting that GSS pre-authentication is
     744             :      * for authentication, not general purpose impersonation.
     745             :      */
     746           0 :     if (krb5_principal_is_federated(r->context, r->client->principal)) {
     747           0 :         initiator->flags.force_canonicalize = 1;
     748             : 
     749           0 :         _kdc_free_ent(r->context, clientdb, r->client);
     750           0 :         r->client = initiator;
     751           0 :         initiator = NULL;
     752           0 :     } else if (!krb5_principal_compare(r->context,
     753           0 :                                        r->client->principal,
     754           0 :                                        initiator->principal)) {
     755           0 :         kdc_log(r->context, r->config, 2,
     756             :                 "GSS %s initiator %.*s does not match principal %s",
     757           0 :                 gss_oid_to_name(gcp->mech_type),
     758           0 :                 (int)display_name_p->length, (char *)display_name_p->value,
     759             :                 r->cname);
     760           0 :         ret = KRB5_KDC_ERR_CLIENT_NAME_MISMATCH;
     761           0 :         goto out;
     762             :     }
     763             : 
     764           0 : out:
     765           0 :     krb5_free_principal(r->context, initiator_princ);
     766           0 :     if (initiator)
     767           0 :         _kdc_free_ent(r->context, r->clientdb, initiator);
     768           0 :     gss_release_buffer(&minor, &display_name);
     769             : 
     770           0 :     return ret;
     771             : }
     772             : 
     773             : krb5_error_code
     774           0 : _kdc_gss_mk_pa_reply(astgs_request_t r,
     775             :                      gss_client_params *gcp)
     776             : {
     777           0 :     krb5_error_code ret;
     778           0 :     const KDC_REQ *req = &r->req;
     779             : 
     780           0 :     if (gcp->major == GSS_S_COMPLETE) {
     781           0 :         krb5_enctype enctype;
     782           0 :         uint32_t kfe = 0;
     783           0 :         krb5_keyblock *reply_key = NULL;
     784             : 
     785           0 :         if (krb5_principal_is_krbtgt(r->context, r->server_princ))
     786           0 :             kfe |= KFE_IS_TGS;
     787             : 
     788           0 :         ret = _kdc_find_etype(r, kfe, req->req_body.etype.val,
     789           0 :                               req->req_body.etype.len, &enctype, NULL, NULL);
     790           0 :         if (ret)
     791           0 :             return ret;
     792             : 
     793           0 :         ret = _krb5_gss_pa_derive_key(r->context, gcp->context_handle,
     794           0 :                                       req->req_body.nonce,
     795             :                                       enctype, &reply_key);
     796           0 :         if (ret) {
     797           0 :             kdc_log(r->context, r->config, 10,
     798             :                     "Failed to derive GSS reply key: %d", ret);
     799           0 :             return ret;
     800             :         }
     801             : 
     802           0 :         krb5_free_keyblock_contents(r->context, &r->reply_key);
     803           0 :         r->reply_key = *reply_key;
     804           0 :         free(reply_key);
     805           0 :     } else if (gcp->major == GSS_S_CONTINUE_NEEDED) {
     806           0 :         ret = pa_gss_set_context_state(r, gcp);
     807           0 :         if (ret)
     808           0 :             return ret;
     809             :     }
     810             : 
     811             :     /* only return padata in error case if we have an error token */
     812           0 :     if (!GSS_ERROR(gcp->major) || gcp->output_token.length) {
     813           0 :         ret = krb5_padata_add(r->context, r->rep.padata, KRB5_PADATA_GSS,
     814             :                               gcp->output_token.value, gcp->output_token.length);
     815           0 :         if (ret)
     816           0 :             return ret;
     817             : 
     818             :         /* token is now owned by r->rep.padata */
     819           0 :         gcp->output_token.length = 0;
     820           0 :         gcp->output_token.value = NULL;
     821             :     }
     822             : 
     823           0 :     if (gcp->major == GSS_S_CONTINUE_NEEDED)
     824           0 :         ret = KRB5_KDC_ERR_MORE_PREAUTH_DATA_REQUIRED;
     825             :     else
     826           0 :         ret = _krb5_gss_map_error(gcp->major, gcp->minor);
     827             : 
     828           0 :     return ret;
     829             : }
     830             : 
     831             : krb5_error_code
     832           0 : _kdc_gss_mk_composite_name_ad(astgs_request_t r,
     833             :                               gss_client_params *gcp)
     834             : {
     835           0 :     krb5_error_code ret;
     836           0 :     krb5_data data;
     837             : 
     838           0 :     OM_uint32 major, minor;
     839           0 :     gss_buffer_desc namebuf = GSS_C_EMPTY_BUFFER;
     840             : 
     841           0 :     if (!r->config->enable_gss_auth_data || (gcp->flags & GSS_C_ANON_FLAG))
     842           0 :         return 0;
     843             : 
     844           0 :     major = gss_export_name_composite(&minor, gcp->initiator_name, &namebuf);
     845           0 :     if (major == GSS_S_COMPLETE) {
     846           0 :         _krb5_gss_buffer_to_data(&namebuf, &data);
     847             : 
     848           0 :         ret = _kdc_tkt_add_if_relevant_ad(r->context, &r->et,
     849             :                                           KRB5_AUTHDATA_GSS_COMPOSITE_NAME,
     850             :                                           &data);
     851           0 :     } else if (major != GSS_S_UNAVAILABLE)
     852           0 :         ret = _krb5_gss_map_error(major, minor);
     853             :     else
     854           0 :         ret = 0;
     855             : 
     856           0 :     gss_release_buffer(&minor, &namebuf);
     857             : 
     858           0 :     return ret;
     859             : }
     860             : 
     861             : static void HEIM_CALLCONV
     862           0 : pa_gss_dealloc_client_params(void *ptr)
     863             : {
     864           0 :     gss_client_params *gcp = ptr;
     865           0 :     OM_uint32 minor;
     866             : 
     867           0 :     if (gcp == NULL)
     868           0 :         return;
     869             : 
     870           0 :     gss_delete_sec_context(&minor, &gcp->context_handle, GSS_C_NO_BUFFER);
     871           0 :     gss_release_name(&minor, &gcp->initiator_name);
     872           0 :     gss_release_buffer(&minor, &gcp->output_token);
     873           0 :     free_Checksum(&gcp->req_body_checksum);
     874           0 :     memset(gcp, 0, sizeof(*gcp));
     875             : }
     876             : 
     877             : krb5_error_code
     878         186 : _kdc_gss_get_mechanism_config(krb5_context context,
     879             :                               const char *section,
     880             :                               const char *key,
     881             :                               gss_OID_set *oidsp)
     882             : {
     883          16 :     krb5_error_code ret;
     884          16 :     char **mechs, **mechp;
     885             : 
     886         186 :     gss_OID_set oids = GSS_C_NO_OID_SET;
     887          16 :     OM_uint32 major, minor;
     888             : 
     889         186 :     mechs = krb5_config_get_strings(context, NULL, section, key, NULL);
     890         186 :     if (mechs == NULL)
     891         170 :         return 0;
     892             : 
     893           0 :     major = gss_create_empty_oid_set(&minor, &oids);
     894           0 :     if (GSS_ERROR(major)) {
     895           0 :         krb5_config_free_strings(mechs);
     896           0 :         return _krb5_gss_map_error(major, minor);
     897             :     }
     898             : 
     899           0 :     for (mechp = mechs; *mechp; mechp++) {
     900           0 :         gss_OID oid = gss_name_to_oid(*mechp);
     901           0 :         if (oid == GSS_C_NO_OID)
     902           0 :             continue;
     903             : 
     904           0 :         major = gss_add_oid_set_member(&minor, oid, &oids);
     905           0 :         if (GSS_ERROR(major))
     906           0 :             break;
     907             :     }
     908             : 
     909           0 :     ret = _krb5_gss_map_error(major, minor);
     910           0 :     if (ret == 0)
     911           0 :         *oidsp = oids;
     912             :     else
     913           0 :         gss_release_oid_set(&minor, &oids);
     914             : 
     915           0 :     krb5_config_free_strings(mechs);
     916             : 
     917           0 :     return ret;
     918             : }
     919             : 
     920             : static void
     921           0 : pa_gss_display_status(astgs_request_t r,
     922             :                       OM_uint32 major,
     923             :                       OM_uint32 minor,
     924             :                       gss_client_params *gcp,
     925             :                       const char *msg)
     926             : {
     927           0 :     krb5_error_code ret = _krb5_gss_map_error(major, minor);
     928           0 :     gss_buffer_desc buf = GSS_C_EMPTY_BUFFER;
     929           0 :     OM_uint32 dmaj, dmin;
     930           0 :     OM_uint32 more = 0;
     931           0 :     char *gmmsg = NULL;
     932           0 :     char *gmsg = NULL;
     933           0 :     char *s = NULL;
     934             : 
     935           0 :     do {
     936           0 :         gss_release_buffer(&dmin, &buf);
     937           0 :         dmaj = gss_display_status(&dmin, major, GSS_C_GSS_CODE, GSS_C_NO_OID,
     938             :                                   &more, &buf);
     939           0 :         if (GSS_ERROR(dmaj) ||
     940           0 :             buf.length >= INT_MAX ||
     941           0 :             asprintf(&s, "%s%s%.*s", gmsg ? gmsg : "", gmsg ? ": " : "",
     942           0 :                      (int)buf.length, (char *)buf.value) == -1 ||
     943           0 :             s == NULL) {
     944           0 :             free(gmsg);
     945           0 :             gmsg = NULL;
     946           0 :             break;
     947             :         }
     948           0 :         gmsg = s;
     949           0 :         s = NULL;
     950           0 :     } while (!GSS_ERROR(dmaj) && more);
     951             : 
     952           0 :     if (gcp->mech_type != GSS_C_NO_OID) {
     953           0 :         do {
     954           0 :             gss_release_buffer(&dmin, &buf);
     955           0 :             dmaj = gss_display_status(&dmin, major, GSS_C_MECH_CODE,
     956             :                                       gcp->mech_type, &more, &buf);
     957           0 :             if (GSS_ERROR(dmaj) ||
     958           0 :                 asprintf(&s, "%s%s%.*s", gmmsg ? gmmsg : "", gmmsg ? ": " : "",
     959           0 :                          (int)buf.length, (char *)buf.value) == -1 ||
     960           0 :                 s == NULL) {
     961           0 :                 free(gmmsg);
     962           0 :                 gmmsg = NULL;
     963           0 :                 break;
     964             :             }
     965           0 :             gmmsg = s;
     966           0 :             s = NULL;
     967           0 :         } while (!GSS_ERROR(dmaj) && more);
     968             :     }
     969             : 
     970           0 :     if (gmsg == NULL)
     971           0 :         krb5_set_error_message(r->context, ENOMEM,
     972             :                                "Error displaying GSS-API status");
     973             :     else
     974           0 :         krb5_set_error_message(r->context, ret, "%s%s%s%s", gmsg,
     975             :                                gmmsg ? " (" : "", gmmsg ? gmmsg : "",
     976             :                                gmmsg ? ")" : "");
     977           0 :     krb5_prepend_error_message(r->context, ret, "%s", msg);
     978             : 
     979           0 :     kdc_log(r->context, r->config, 1,
     980             :             "%s: %s%s%s%s",
     981             :             msg, gmsg, gmmsg ? " (" : "", gmmsg ? gmmsg : "",
     982             :             gmmsg ? ")" : "");
     983             : 
     984           0 :     free(gmmsg);
     985           0 :     free(gmsg);
     986           0 : }
     987             : 
     988             : static const gss_buffer_desc
     989             : gss_pa_unknown_display_name = {
     990             :     sizeof("<unknown name>") - 1,
     991             :     "<unknown name>"
     992             : };
     993             : 
     994             : static void
     995           0 : pa_gss_display_name(gss_name_t name,
     996             :                     gss_buffer_t namebuf,
     997             :                     gss_const_buffer_t *namebuf_p)
     998             : {
     999           0 :     OM_uint32 major, minor;
    1000             : 
    1001           0 :     major = gss_display_name(&minor, name, namebuf, NULL);
    1002           0 :     if (GSS_ERROR(major))
    1003           0 :         *namebuf_p = &gss_pa_unknown_display_name;
    1004             :     else
    1005           0 :         *namebuf_p = namebuf;
    1006           0 : }
    1007             : 
    1008             : static krb5_error_code KRB5_LIB_CALL
    1009           0 : pa_gss_finalize_pac_cb(krb5_context context,
    1010             :                         const void *plug,
    1011             :                         void *plugctx,
    1012             :                         void *userctx)
    1013             : {
    1014           0 :     const krb5plugin_gss_preauth_authorizer_ftable *authorizer = plug;
    1015             : 
    1016           0 :     return authorizer->finalize_pac(plugctx, userctx);
    1017             : }
    1018             : 
    1019             : 
    1020             : krb5_error_code
    1021           0 : _kdc_gss_finalize_pac(astgs_request_t r,
    1022             :                       gss_client_params *gcp)
    1023             : {
    1024           0 :     krb5_error_code ret;
    1025             : 
    1026           0 :     krb5_clear_error_message(r->context);
    1027           0 :     ret = _krb5_plugin_run_f(r->context, &gss_preauth_authorizer_data,
    1028             :                              0, r, pa_gss_finalize_pac_cb);
    1029             : 
    1030           0 :     if (ret == KRB5_PLUGIN_NO_HANDLE)
    1031           0 :         ret = 0;
    1032             : 
    1033           0 :     return ret;
    1034             : }

Generated by: LCOV version 1.14