1   /*
   2    * Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
   3    * Use is subject to license terms.
   4    */
   5   
   6 | #pragma ident        "@(#)g_imp_name.c        1.27        04/09/08 SMI"
   6 | #pragma ident        "@(#)g_imp_name.c        1.26        04/02/23 SMI"
   7   
   8   /*
   9    *  glue routine gss_import_name
  10    *
  11    */
  12   
  13   #include <mechglueP.h>
  14   #include <stdio.h>
  15   #ifdef HAVE_STDLIB_H
  16   #include <stdlib.h>
  17   #endif
  18   #include <string.h>
  19   #include <errno.h>
  20   
  21   extern int
  22   get_der_length(unsigned char **, unsigned int, unsigned int *);
  23   
  24   /* local function to import GSS_C_EXPORT_NAME names */
  25   static OM_uint32 importExportName(OM_uint32 *, gss_union_name_t);
  26   
  27   
  28   OM_uint32
  29   gss_import_name(minor_status,
  30                   input_name_buffer,
  31                   input_name_type,
  32                   output_name)
  33   
  34   OM_uint32 *minor_status;
  35   const gss_buffer_t input_name_buffer;
  36   const gss_OID input_name_type;
  37   gss_name_t *output_name;
  38   {
  39           gss_union_name_t union_name;
  41 -         gss_OID mech;
  40           OM_uint32 major_status = GSS_S_FAILURE, tmp;
  41   
  43 |         gss_initialize();
  42 |         /* check output parameters */
  43 +         if (!minor_status)
  44 +                 return (GSS_S_CALL_INACCESSIBLE_WRITE);
  45 -         if (minor_status)
  45   
  46           *minor_status = 0;
  47   
  48 |         /* if output_name is NULL, simply return */
  48 |         if (GSS_EMPTY_BUFFER(input_name_buffer))
  49 +                 return (GSS_S_CALL_INACCESSIBLE_READ);
  50   
  51           if (output_name == NULL)
  51 |                 return (GSS_S_COMPLETE);
  52 |                 return (GSS_S_CALL_INACCESSIBLE_WRITE);
  53   
  54           *output_name = 0;
  55   
  56           /*
  57            * First create the union name struct that will hold the external
  58            * name and the name type.
  59 - 
  59            */
  60           union_name = (gss_union_name_t)malloc(sizeof (gss_union_name_desc));
  61 |         if (!union_name) {
  62 |                 *minor_status = ENOMEM;
  63 |                 goto allocation_failure;
  61 |         if (!union_name)
  62 |                 return (GSS_S_FAILURE);
  63 | 
  64 -         }
  64           union_name->mech_type = 0;
  65           union_name->mech_name = 0;
  66           union_name->name_type = 0;
  67           union_name->external_name = 0;
  68   
  69           /*
  70            * All we do here is record the external name and name_type.
  71            * When the name is actually used, the underlying gss_import_name()
  73 |          * is called for the appropriate mechanism.  Note that the name type
  74 |          * is assumed to be constant, so only a pointer to it is stored in
  75 |          * union_name
  72 |          * is called for the appropriate mechanism.  The exception to this
  73 |          * rule is when the name of GSS_C_NT_EXPORT_NAME type.  If that is
  74 |          * the case, then we make it MN in this call.
  75            */
  77 |     union_name->external_name =
  78 |         (gss_buffer_t) malloc(sizeof(gss_buffer_desc));
  79 |     if (!union_name->external_name) {
  80 |             *minor_status = ENOMEM;
  81 |             goto allocation_failure;
  76 |         major_status = __gss_create_copy_buffer(input_name_buffer,
  77 |                                         &union_name->external_name, 0);
  78 |         if (major_status != GSS_S_COMPLETE) {
  79 |                 free(union_name);
  80 |                 return (major_status);
  81           }
  82   
  84 |     union_name->external_name->length = input_name_buffer->length;
  85 |     /* we malloc length+1 to stick a NULL on the end, just in case */
  86 |     /* Note that this NULL is not included in ->length for a reason! */
  83 |         if (input_name_type != GSS_C_NULL_OID) {
  84 |                 major_status = generic_gss_copy_oid(minor_status,
  85 |                                                 input_name_type,
  87 -     union_name->external_name->value =
  88 -         (void  *) malloc(input_name_buffer->length+1);
  89 -     if (!union_name->external_name->value) {
  90 -         *minor_status = ENOMEM;
  91 -         goto allocation_failure;
  92 -     }
  93 - 
  94 -     memcpy(union_name->external_name->value, input_name_buffer->value,
  95 -            input_name_buffer->length);
  96 -  
  97 -     /* add NULL to end of external_name->value, just in case... */
  98 -     ((char *)union_name->external_name->value)
  99 -                                 [input_name_buffer->length] = '\0';
 100 -  
 101 -     major_status = generic_gss_copy_oid(minor_status, input_name_type,
  86                                                   &union_name->name_type);
  87                   if (major_status != GSS_S_COMPLETE)
  88                           goto allocation_failure;
  89 +         }
  90   
  91           /*
 107 |      * See if this is a mechanism-specific name.  If so, let's import
 108 |      * it now so we can get any error messages, and to avoid trouble
 109 |      * later...
  92 |          * In MIT Distribution the mechanism is determined from the nametype;
  93 |          * This is not a good idea - first mechanism that supports a given
  94 |          * name type is picked up; later on the caller can request a
  95 +          * different mechanism. So we don't determine the mechanism here. Now
  96 +          * the user level and kernel level import_name routine looks similar
  97 +          * except the kernel routine makes a copy of the nametype structure. We
  98 +          * do however make this an MN for names of GSS_C_NT_EXPORT_NAME type.
  99            */
 111 |     mech = gss_find_mechanism_from_name_type(input_name_type);
 112 |     if (mech) {
 113 |         major_status = generic_gss_copy_oid(minor_status, mech,
 100 |         if (input_name_type != GSS_C_NULL_OID &&
 101 |             g_OID_equal(input_name_type, GSS_C_NT_EXPORT_NAME)) {
 102 |                 major_status = importExportName(minor_status, union_name);
 114 -                                             &union_name->mech_type);
 103                   if (major_status != GSS_S_COMPLETE)
 117 -  
 118 -         major_status = __gss_import_internal_name(minor_status, mech,
 119 -                                                   union_name,
 120 -                                                   &union_name->mech_name);
 121 -         if (major_status)
 122 -             goto allocation_failure;
 104                           goto allocation_failure;
 105           }
 106   
 126 -  
 107           *output_name = (gss_name_t)union_name;
 108           return (GSS_S_COMPLETE);
 109   
 110   allocation_failure:
 111           if (union_name) {
 112                   if (union_name->external_name) {
 113                           if (union_name->external_name->value)
 114                                   free(union_name->external_name->value);
 115                           free(union_name->external_name);
 116                   }
 117                   if (union_name->name_type)
 118                           (void) generic_gss_release_oid(&tmp,
 119                                                       &union_name->name_type);
 120                   if (union_name->mech_name)
 121                           (void) __gss_release_internal_name(minor_status,
 122                                                   union_name->mech_type,
 123                                                   &union_name->mech_name);
 124                   if (union_name->mech_type)
 125                           (void) generic_gss_release_oid(&tmp,
 126                                                       &union_name->mech_type);
 127                   free(union_name);
 128           }
 129           return (major_status);
 130   }
 131   
 132 + 
 133 + /*
 134 +  * GSS export name constants
 135 +  */
 136 + static const char *expNameTokId = "\x04\x01";
 137 + static const int expNameTokIdLen = 2;
 138 + static const int mechOidLenLen = 2;
 139 + static const int nameTypeLenLen = 2;
 140 + 
 141 + static OM_uint32
 142 + importExportName(minor, unionName)
 143 + OM_uint32 *minor;
 144 + gss_union_name_t unionName;
 145 + {
 146 +         gss_OID_desc mechOid;
 147 +         gss_buffer_desc expName;
 148 +         unsigned char *buf;
 149 +         gss_mechanism mech;
 150 +         OM_uint32 major, mechOidLen, nameLen, curLength;
 151 +         unsigned int bytes;
 152 + 
 153 +         expName.value = unionName->external_name->value;
 154 +         expName.length = unionName->external_name->length;
 155 + 
 156 +         curLength = expNameTokIdLen + mechOidLenLen;
 157 +         if (expName.length < curLength)
 158 +                 return (GSS_S_DEFECTIVE_TOKEN);
 159 + 
 160 +         buf = (unsigned char *)expName.value;
 161 +         if (memcmp(expNameTokId, buf, expNameTokIdLen) != 0)
 162 +                 return (GSS_S_DEFECTIVE_TOKEN);
 163 + 
 164 +         buf += expNameTokIdLen;
 165 + 
 166 +         /* extract the mechanism oid length */
 167 +         mechOidLen = (*buf++ << 8);
 168 +         mechOidLen |= (*buf++);
 169 +         curLength += mechOidLen;
 170 +         if (expName.length < curLength)
 171 +                 return (GSS_S_DEFECTIVE_TOKEN);
 172 +         /*
 173 +          * The mechOid itself is encoded in DER format, OID Tag (0x06)
 174 +          * length and the value of mech_OID
 175 +         */
 176 +         if (*buf++ != 0x06)
 177 +                 return (GSS_S_DEFECTIVE_TOKEN);
 178 + 
 179 +         /*
 180 +          * mechoid Length is encoded twice; once in 2 bytes as
 181 +          * explained in RFC2743 (under mechanism independent exported
 182 +          * name object format) and once using DER encoding
 183 +          *
 184 +          * We verify both lengths.
 185 +          */
 186 + 
 187 +         mechOid.length = get_der_length(&buf,
 188 +                                 (expName.length - curLength), &bytes);
 189 +         mechOid.elements = (void *)buf;
 190 + 
 191 +         /*
 192 +          * 'bytes' is the length of the DER length, '1' is for the DER
 193 +          * tag for OID
 194 +          */
 195 +         if ((bytes + mechOid.length + 1) != mechOidLen)
 196 +                 return (GSS_S_DEFECTIVE_TOKEN);
 197 + 
 198 +         buf += mechOid.length;
 199 +         if ((mech = __gss_get_mechanism(&mechOid)) == NULL)
 200 +                 return (GSS_S_BAD_MECH);
 201 + 
 202 +         if (mech->gss_import_name == NULL)
 203 +                 return (GSS_S_UNAVAILABLE);
 204 + 
 205 +         /*
 206 +          * we must now determine if we should unwrap the name ourselves
 207 +          * or make the mechanism do it - we should only unwrap it
 208 +          * if we create it; so if mech->gss_export_name == NULL, we must
 209 +          * have created it.
 210 +          */
 211 +         if (mech->gss_export_name) {
 212 +                 if ((major = mech->gss_import_name(mech->context, minor,
 213 +                                 &expName, (gss_OID)GSS_C_NT_EXPORT_NAME,
 214 +                                 &unionName->mech_name)) != GSS_S_COMPLETE ||
 215 +                         (major = generic_gss_copy_oid(minor, &mechOid,
 216 +                                         &unionName->mech_type)) !=
 217 +                                 GSS_S_COMPLETE) {
 218 +                         return (major);
 219 +                 }
 220 +                 return (major);
 221 +         }
 222 +         /*
 223 +          * we must have exported the name - so we now need to reconstruct it
 224 +          * and call the mechanism to create it
 225 +          *
 226 +          * WARNING:        Older versions of __gss_export_internal_name() did
 227 +          *                not export names correctly, but now it does.  In
 228 +          *                order to stay compatible with existing exported
 229 +          *                names we must support names exported the broken
 230 +          *                way.
 231 +          *
 232 +          * Specifically, __gss_export_internal_name() used to include
 233 +          * the name type OID in the encoding of the exported MN.
 234 +          * Additionally, the Kerberos V mech used to make display names
 235 +          * that included a null terminator which was counted in the
 236 +          * display name gss_buffer_desc.
 237 +          */
 238 +         curLength += 4;                /* 4 bytes for name len */
 239 +         if (expName.length < curLength)
 240 +                 return (GSS_S_DEFECTIVE_TOKEN);
 241 + 
 242 +         /* next 4 bytes in the name are the name length */
 243 +         nameLen = (*buf++) << 24;
 244 +         nameLen |= (*buf++ << 16);
 245 +         nameLen |= (*buf++ << 8);
 246 +         nameLen |= (*buf++);
 247 + 
 248 +         /*
 249 +          * we use < here because bad code in rpcsec_gss rounds up exported
 250 +          * name token lengths and pads with nulls, otherwise != would be
 251 +          * appropriate
 252 +          */
 253 +         curLength += nameLen;   /* this is the total length */
 254 +         if (expName.length < curLength)
 255 +                 return (GSS_S_DEFECTIVE_TOKEN);
 256 + 
 257 +         /*
 258 +          * We detect broken exported names here: they always start with
 259 +          * a two-octet network-byte order OID length, which is always
 260 +          * less than 256 bytes, so the first octet of the length is
 261 +          * always '\0', which is not allowed in GSS-API display names
 262 +          * (or never occurs in them anyways).  Of course, the OID
 263 +          * shouldn't be there, but it is.  After the OID (sans DER tag
 264 +          * and length) there's the name itself, though null-terminated;
 265 +          * this null terminator should also not be there, but it is.
 266 +          */
 267 +         if (nameLen > 0 && *buf == '\0') {
 268 +                 OM_uint32 nameTypeLen;
 269 +                 /* next two bytes are the name oid */
 270 +                 if (nameLen < nameTypeLenLen)
 271 +                         return (GSS_S_DEFECTIVE_TOKEN);
 272 + 
 273 +                 nameLen -= nameTypeLenLen;
 274 + 
 275 +                 nameTypeLen = (*buf++) << 8;
 276 +                 nameTypeLen |= (*buf++);
 277 + 
 278 +                 if (nameLen < nameTypeLen)
 279 +                         return (GSS_S_DEFECTIVE_TOKEN);
 280 + 
 281 +                 buf += nameTypeLen;
 282 +                 nameLen -= nameTypeLen;
 283 + 
 284 +                 /*
 285 +                  * adjust for expected null terminator that should
 286 +                  * really not be there
 287 +                  */
 288 +                 if (nameLen > 0 && *(buf + nameLen - 1) == '\0')
 289 +                         nameLen--;
 290 +         }
 291 + 
 292 +         /*
 293 +          * Can a name be null?  Let the mech decide.
 294 +          *
 295 +          * NOTE: We use GSS_C_NULL_OID as the name type when importing
 296 +          *         the unwrapped name.  Presumably the exported name had,
 297 +          *         prior to being exported been obtained in such a way
 298 +          *         that it has been properly perpared ("canonicalized," in
 299 +          *         GSS-API terms) accroding to some name type; we cannot
 300 +          *         tell what that name type was now, but the name should
 301 +          *         need no further preparation other than the lowest
 302 +          *         common denominator afforded by the mech to names
 303 +          *         imported with GSS_C_NULL_OID.  For the Kerberos V mech
 304 +          *         this means doing less busywork too (particularly once
 305 +          *         IDN is thrown in with Kerberos V extensions).
 306 +          */
 307 +         expName.length = nameLen;
 308 +         expName.value = nameLen ? (void *)buf : NULL;
 309 +         major = mech->gss_import_name(mech->context, minor, &expName,
 310 +                             GSS_C_NULL_OID, &unionName->mech_name);
 311 +         if (major != GSS_S_COMPLETE)
 312 +                 return (major);
 313 + 
 314 +         return (generic_gss_copy_oid(minor, &mechOid, &unionName->mech_type));
 315 + } /* importExportName */