1 /*
   2  * CDDL HEADER START
   3  *
   4  * The contents of this file are subject to the terms of the
   5  * Common Development and Distribution License (the "License").
   6  * You may not use this file except in compliance with the License.
   7  *
   8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
   9  * or http://www.opensolaris.org/os/licensing.
  10  * See the License for the specific language governing permissions
  11  * and limitations under the License.
  12  *
  13  * When distributing Covered Code, include this CDDL HEADER in each
  14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
  15  * If applicable, add the following below this CDDL HEADER, with the
  16  * fields enclosed by brackets "[]" replaced with your own identifying
  17  * information: Portions Copyright [yyyy] [name of copyright owner]
  18  *
  19  * CDDL HEADER END
  20  */
  21 
  22 /*
  23  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
  24  * Use is subject to license terms.
  25  */
  26 
  27 /*
  28  *  glue routine for gss_accept_sec_context
  29  */
  30 
  31 #include <mechglueP.h>
  32 #ifdef HAVE_STDLIB_H
  33 #include <stdlib.h>
  34 #endif
  35 #include <string.h>
  36 #include <errno.h>
  37 
  38 OM_uint32
  39 gss_accept_sec_context(minor_status,
  40                         context_handle,
  41                         verifier_cred_handle,
  42                         input_token_buffer,
  43                         input_chan_bindings,
  44                         src_name,
  45                         mech_type,
  46                         output_token,
  47                         ret_flags,
  48                         time_rec,
  49                         d_cred)
  50 
  51 OM_uint32                       *minor_status;
  52 gss_ctx_id_t                    *context_handle;
  53 const gss_cred_id_t             verifier_cred_handle;
  54 const gss_buffer_t              input_token_buffer;
  55 const gss_channel_bindings_t    input_chan_bindings;
  56 gss_name_t                      *src_name;
  57 gss_OID                         *mech_type;
  58 gss_buffer_t                    output_token;
  59 OM_uint32                       *ret_flags;
  60 OM_uint32                       *time_rec;
  61 gss_cred_id_t                   *d_cred; /* delegated cred handle */
  62 
  63 {
  64         OM_uint32               status, temp_status, t_minstat;
  65         gss_union_ctx_id_t      union_ctx_id;
  66         gss_union_cred_t        union_cred;
  67         gss_cred_id_t   input_cred_handle = GSS_C_NO_CREDENTIAL;
  68         gss_cred_id_t   tmp_d_cred = GSS_C_NO_CREDENTIAL;
  69         gss_name_t              internal_name = GSS_C_NO_NAME;
  70         gss_name_t              tmp_src_name = GSS_C_NO_NAME;
  71         gss_OID_desc    token_mech_type_desc;
  72         gss_OID         token_mech_type = &token_mech_type_desc;
  73         gss_OID         actual_mech = GSS_C_NO_OID;
  74         OM_uint32       flags;
  75         gss_mechanism   mech;
  76 
  77         /* check parameters first */
  78         if (minor_status == NULL)
  79                 return (GSS_S_CALL_INACCESSIBLE_WRITE);
  80         *minor_status = 0;
  81 
  82         if (context_handle == NULL || output_token == NULL)
  83                 return (GSS_S_CALL_INACCESSIBLE_WRITE);
  84 
  85         /* clear optional fields */
  86         output_token->value = NULL;
  87         output_token->length = 0;
  88         if (src_name)
  89                 *src_name = NULL;
  90 
  91         if (mech_type)
  92                 *mech_type = NULL;
  93 
  94         if (d_cred)
  95                 *d_cred = NULL;
  96         /*
  97          * if context_handle is GSS_C_NO_CONTEXT, allocate a union context
  98          * descriptor to hold the mech type information as well as the
  99          * underlying mechanism context handle. Otherwise, cast the
 100          * value of *context_handle to the union context variable.
 101          */
 102 
 103         if (*context_handle == GSS_C_NO_CONTEXT) {
 104 
 105                 if (GSS_EMPTY_BUFFER(input_token_buffer))
 106                         return (GSS_S_CALL_INACCESSIBLE_READ);
 107 
 108                 /* Get the token mech type */
 109                 status = __gss_get_mech_type(token_mech_type,
 110                                         input_token_buffer);
 111 
 112                 if (status)
 113                         return (status);
 114 
 115                 status = GSS_S_FAILURE;
 116                 union_ctx_id = (gss_union_ctx_id_t)
 117                         malloc(sizeof (gss_union_ctx_id_desc));
 118                 if (!union_ctx_id)
 119                         return (GSS_S_FAILURE);
 120 
 121                 union_ctx_id->internal_ctx_id = GSS_C_NO_CONTEXT;
 122                 status = generic_gss_copy_oid(&t_minstat,
 123                                         token_mech_type,
 124                                         &union_ctx_id->mech_type);
 125                 if (status != GSS_S_COMPLETE) {
 126                         free(union_ctx_id);
 127                         return (status);
 128                 }
 129 
 130                 /* set the new context handle to caller's data */
 131                 *context_handle = (gss_ctx_id_t)union_ctx_id;
 132         } else {
 133                 union_ctx_id = (gss_union_ctx_id_t)*context_handle;
 134                 token_mech_type = union_ctx_id->mech_type;
 135         }
 136 
 137         /*
 138          * get the appropriate cred handle from the union cred struct.
 139          * defaults to GSS_C_NO_CREDENTIAL if there is no cred, which will
 140          * use the default credential.
 141          */
 142         union_cred = (gss_union_cred_t)verifier_cred_handle;
 143         input_cred_handle = __gss_get_mechanism_cred(union_cred,
 144                                                 token_mech_type);
 145 
 146         /*
 147          * now select the approprate underlying mechanism routine and
 148          * call it.
 149          */
 150 
 151         mech = __gss_get_mechanism(token_mech_type);
 152         if (mech && mech->gss_accept_sec_context) {
 153                 status = mech->gss_accept_sec_context(
 154                                         mech->context,
 155                                         minor_status,
 156                                         &union_ctx_id->internal_ctx_id,
 157                                         input_cred_handle,
 158                                         input_token_buffer,
 159                                         input_chan_bindings,
 160                                         &internal_name,
 161                                         &actual_mech,
 162                                         output_token,
 163                                         &flags,
 164                                         time_rec,
 165                                         d_cred ? &tmp_d_cred : NULL);
 166 
 167                 /* If there's more work to do, keep going... */
 168                 if (status == GSS_S_CONTINUE_NEEDED)
 169                         return (GSS_S_CONTINUE_NEEDED);
 170 
 171                 /* if the call failed, return with failure */
 172                 if (status != GSS_S_COMPLETE)
 173                         goto error_out;
 174 
 175                 if (mech_type != NULL)
 176                         *mech_type = actual_mech;
 177 
 178                 /*
 179                  * if src_name is non-NULL,
 180                  * convert internal_name into a union name equivalent
 181                  * First call the mechanism specific display_name()
 182                  * then call gss_import_name() to create
 183                  * the union name struct cast to src_name
 184                  */
 185                 if (internal_name != NULL) {
 186                         temp_status = __gss_convert_name_to_union_name(
 187                                 &t_minstat, mech,
 188                                 internal_name, &tmp_src_name);
 189                         if (temp_status != GSS_S_COMPLETE) {
 190                                 *minor_status = t_minstat;
 191                                 if (output_token->length)
 192                                         (void) gss_release_buffer(
 193                                                 &t_minstat,
 194                                                 output_token);
 195                                 if (internal_name != GSS_C_NO_NAME)
 196                                         mech->gss_release_name(
 197                                                 mech->context,
 198                                                 &t_minstat,
 199                                                 &internal_name);
 200                                 return (temp_status);
 201                         }
 202                         if (src_name != NULL) {
 203                                 *src_name = tmp_src_name;
 204                         }
 205                 } else if (src_name != NULL) {
 206                         *src_name = GSS_C_NO_NAME;
 207                 }
 208 
 209                 /* Ensure we're returning correct creds format */
 210                 if ((flags & GSS_C_DELEG_FLAG) &&
 211                     tmp_d_cred != GSS_C_NO_CREDENTIAL) {
 212                         /*
 213                          * If we got back an OID different from the original
 214                          * token OID, assume the delegated_cred is already
 215                          * a proper union_cred and just return it.  Don't
 216                          * try to re-wrap it.  This is for SPNEGO or other
 217                          * pseudo-mechanisms.
 218                          */
 219                         if (actual_mech != GSS_C_NO_OID &&
 220                             token_mech_type != GSS_C_NO_OID &&
 221                             !g_OID_equal(actual_mech, token_mech_type)) {
 222                                 *d_cred = tmp_d_cred;
 223                         } else {
 224                                 gss_union_cred_t d_u_cred = NULL;
 225 
 226                                 d_u_cred = malloc(sizeof (gss_union_cred_desc));
 227                                 if (d_u_cred == NULL) {
 228                                         status = GSS_S_FAILURE;
 229                                         goto error_out;
 230                                 }
 231                                 (void) memset(d_u_cred, 0,
 232                                             sizeof (gss_union_cred_desc));
 233 
 234                                 d_u_cred->count = 1;
 235 
 236                                 status = generic_gss_copy_oid(
 237                                         &t_minstat,
 238                                         actual_mech,
 239                                         &d_u_cred->mechs_array);
 240 
 241                                 if (status != GSS_S_COMPLETE) {
 242                                         free(d_u_cred);
 243                                         goto error_out;
 244                                 }
 245 
 246                                 d_u_cred->cred_array = malloc(
 247                                                 sizeof (gss_cred_id_t));
 248                                 if (d_u_cred->cred_array != NULL) {
 249                                         d_u_cred->cred_array[0] = tmp_d_cred;
 250                                 } else {
 251                                         free(d_u_cred);
 252                                         status = GSS_S_FAILURE;
 253                                         goto error_out;
 254                                 }
 255 
 256                                 if (status != GSS_S_COMPLETE) {
 257                                         free(d_u_cred->cred_array);
 258                                         free(d_u_cred);
 259                                         goto error_out;
 260                                 }
 261 
 262                                 internal_name = GSS_C_NO_NAME;
 263 
 264                                 d_u_cred->auxinfo.creation_time = time(0);
 265                                 d_u_cred->auxinfo.time_rec = 0;
 266 
 267                                 if (mech->gss_inquire_cred) {
 268                                         status = mech->gss_inquire_cred(
 269                                                 mech->context,
 270                                                 minor_status,
 271                                                 tmp_d_cred,
 272                                                 &internal_name,
 273                                                 &d_u_cred->auxinfo.time_rec,
 274                                                 &d_u_cred->auxinfo.cred_usage,
 275                                                 NULL);
 276                                 }
 277 
 278                                 if (internal_name != NULL) {
 279                                         temp_status =
 280                                             __gss_convert_name_to_union_name(
 281                                                 &t_minstat, mech,
 282                                                 internal_name, &tmp_src_name);
 283                                         if (temp_status != GSS_S_COMPLETE) {
 284                                                 *minor_status = t_minstat;
 285                                                 if (output_token->length)
 286                                                     (void) gss_release_buffer(
 287                                                                 &t_minstat,
 288                                                                 output_token);
 289                                                 free(d_u_cred->cred_array);
 290                                                 free(d_u_cred);
 291                                                 return (temp_status);
 292                                         }
 293                                 }
 294 
 295                                 if (tmp_src_name != NULL) {
 296                                         status = gss_display_name(
 297                                                 &t_minstat,
 298                                                 tmp_src_name,
 299                                                 &d_u_cred->auxinfo.name,
 300                                                 &d_u_cred->auxinfo.name_type);
 301                                 }
 302 
 303                                 *d_cred = (gss_cred_id_t)d_u_cred;
 304                         }
 305                 }
 306                 if (ret_flags != NULL) {
 307                         *ret_flags = flags;
 308                 }
 309 
 310                 if (src_name == NULL && tmp_src_name != NULL)
 311                         (void) gss_release_name(&t_minstat,
 312                                         &tmp_src_name);
 313                 return  (status);
 314         } else {
 315 
 316                 status = GSS_S_BAD_MECH;
 317         }
 318 
 319 error_out:
 320         if (union_ctx_id) {
 321                 if (union_ctx_id->mech_type) {
 322                         if (union_ctx_id->mech_type->elements)
 323                                 free(union_ctx_id->mech_type->elements);
 324                         free(union_ctx_id->mech_type);
 325                 }
 326                 free(union_ctx_id);
 327                 *context_handle = GSS_C_NO_CONTEXT;
 328         }
 329 
 330         if (src_name)
 331                 *src_name = GSS_C_NO_NAME;
 332 
 333         if (tmp_src_name != GSS_C_NO_NAME)
 334                 (void) gss_release_buffer(&t_minstat,
 335                         (gss_buffer_t)tmp_src_name);
 336 
 337         return (status);
 338 }