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, Version 1.0 only 6 * (the "License"). You may not use this file except in compliance 7 * with the License. 8 * 9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10 * or http://www.opensolaris.org/os/licensing. 11 * See the License for the specific language governing permissions 12 * and limitations under the License. 13 * 14 * When distributing Covered Code, include this CDDL HEADER in each 15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16 * If applicable, add the following below this CDDL HEADER, with the 17 * fields enclosed by brackets "[]" replaced with your own identifying 18 * information: Portions Copyright [yyyy] [name of copyright owner] 19 * 20 * CDDL HEADER END 21 */ 22 /* 23 * Copyright 2004 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 #pragma ident "%Z%%M% %I% %E% SMI" 28 29 #include <sys/note.h> 30 #include "dh_gssapi.h" 31 32 /* 33 * This module contains the implementation of the gssapi context support 34 * routines for the Diffie-Hellman mechanism. 35 * 36 * The GSS routines that are supported by this module are: 37 * gss_context_time 38 * gss_delete_sec_context 39 * gss_inquire_context 40 * gss_wrap_size_limit 41 * 42 * The following routines are not supported for the Diffie-Hellman 43 * Mechanism at this time. 44 * gss_export_sec_context 45 * gss_import_sec_context 46 * 47 * The following routine is not supported since it is obsolete in version 2 48 * of the GSS-API. 49 * gss_process_context_token. 50 * 51 * Note that support for gss_init_sec_context and gss_accept_sec_context is 52 * found in context_establish.c 53 */ 54 55 OM_uint32 56 __dh_gss_context_time(void *ctx, /* Mechanism context (not used) */ 57 OM_uint32 * minor, /* GSS minor status */ 58 gss_ctx_id_t context, /* GSS context handle */ 59 OM_uint32* time_remaining /* Time remaining */) 60 61 { 62 _NOTE(ARGUNUSED(ctx)) 63 /* Context is a dh context */ 64 dh_gss_context_t cntx = (dh_gss_context_t)context; 65 time_t now = time(0); 66 67 if (minor == 0) 68 return (GSS_S_CALL_INACCESSIBLE_WRITE); 69 70 if (time_remaining == 0) 71 return (GSS_S_CALL_INACCESSIBLE_WRITE); 72 73 /* Validate context */ 74 if ((*minor = __dh_validate_context(cntx)) != DH_SUCCESS) 75 return (GSS_S_NO_CONTEXT); 76 77 /* See if it is always valid */ 78 if (cntx->expire == (time_t)GSS_C_INDEFINITE) { 79 *time_remaining = GSS_C_INDEFINITE; 80 return (GSS_S_COMPLETE); 81 } 82 83 /* Calculate the remainning time */ 84 *time_remaining = (now < cntx->expire) ? cntx->expire - now : 0; 85 86 /* Return expired if there is no time left */ 87 return (*time_remaining ? GSS_S_COMPLETE : GSS_S_CONTEXT_EXPIRED); 88 } 89 90 /* 91 * Delete a Diffie-Hellman context that is pointed to by context. 92 * On a successfull return *context will be NULL. 93 */ 94 95 OM_uint32 96 __dh_gss_delete_sec_context(void *ctx, /* Mechanism context */ 97 OM_uint32 *minor, /* Mechanism status */ 98 gss_ctx_id_t *context, /* GSS context */ 99 gss_buffer_t token /* GSS token */) 100 { 101 _NOTE(ARGUNUSED(ctx)) 102 103 dh_gss_context_t cntx; 104 105 if (context == 0) 106 return (GSS_S_CALL_INACCESSIBLE_READ | 107 GSS_S_CALL_INACCESSIBLE_WRITE); 108 109 /* context is a Diffie-Hellman context */ 110 cntx = (dh_gss_context_t)*context; 111 112 if (minor == 0) 113 return (GSS_S_CALL_INACCESSIBLE_WRITE); 114 115 /* 116 * If token then set the length to zero value to zero to indicate 117 * We indicat a null token since we don't need to send a token to 118 * the other side. 119 */ 120 121 if (token) { 122 token->length = 0; 123 token->value = NULL; 124 } 125 126 /* Deleting a null context is OK */ 127 if (cntx == NULL) 128 return (GSS_S_COMPLETE); 129 130 /* Validate the context */ 131 if ((*minor = __dh_validate_context(cntx)) != DH_SUCCESS) 132 return (GSS_S_NO_CONTEXT); 133 134 /* Zero out the session keys! */ 135 memset(cntx->keys, 0, cntx->no_keys * sizeof (des_block)); 136 137 /* Unregister the context */ 138 *minor = __dh_remove_context(cntx); 139 140 /* Free storage */ 141 __dh_destroy_seq_hist(cntx); 142 free(cntx->remote); 143 free(cntx->local); 144 Free(cntx->keys); 145 Free(cntx); 146 147 /* Set context to NULL */ 148 *context = NULL; 149 150 return (GSS_S_COMPLETE); 151 } 152 153 154 /* 155 * Diffie-Hellman mechanism currently does not support exporting and importing 156 * gss contexts. 157 */ 158 159 OM_uint32 160 /*ARGSUSED*/ 161 __dh_gss_export_sec_context(void *ctx, OM_uint32 *minor, 162 gss_ctx_id_t *context, gss_buffer_t token) 163 { 164 return (GSS_S_UNAVAILABLE); 165 } 166 167 OM_uint32 168 /*ARGSUSED*/ 169 __dh_gss_import_sec_context(void * ctx, OM_uint32 *minor, 170 gss_buffer_t token, gss_ctx_id_t *context) 171 { 172 return (GSS_S_UNAVAILABLE); 173 } 174 175 /* 176 * Get the state of a Diffie-Hellman context 177 */ 178 179 OM_uint32 180 __dh_gss_inquire_context(void *ctx, /* Mechanism context */ 181 OM_uint32 *minor, /* Mechanism status */ 182 gss_ctx_id_t context, /* GSS context */ 183 gss_name_t *initiator, /* Name of initiator */ 184 gss_name_t *acceptor, /* Name of acceptor */ 185 OM_uint32 *time_rec, /* Amount of time left */ 186 gss_OID *mech, /* return OID of mechanism */ 187 OM_uint32 *flags_rec, /* flags set on context */ 188 int *local, /* True if we're the initiator */ 189 int *open /* True if the context is established */) 190 { 191 dh_gss_context_t cntx; 192 OM_uint32 stat = GSS_S_COMPLETE; 193 OM_uint32 t; 194 195 /* context is a Diffie-Hellman */ 196 cntx = (dh_gss_context_t)context; 197 198 /* Validate the context */ 199 if ((*minor = __dh_validate_context(cntx)) != DH_SUCCESS) 200 return (GSS_S_NO_CONTEXT); 201 202 /* If the caller wants the mechanism OID set *mech to if we can */ 203 if (mech) { 204 if (ctx == 0) { 205 *mech = GSS_C_NO_OID; 206 return (GSS_S_CALL_INACCESSIBLE_READ); 207 } 208 else 209 *mech = ((dh_context_t)ctx)->mech; 210 } 211 212 /* set t to be the time left on the context */ 213 if (cntx->expire == GSS_C_INDEFINITE) 214 t = GSS_C_INDEFINITE; 215 else { 216 time_t now = time(0); 217 t = now > cntx->expire ? 0 : (OM_uint32)(cntx->expire - now); 218 } 219 220 /* If the caller wants the initiator set *initiator to it. */ 221 if (initiator) { 222 dh_principal p = cntx->initiate ? cntx->local : cntx->remote; 223 *initiator = (gss_name_t)strdup(p); 224 } 225 226 /* If the callers wants the acceptor set *acceptor to it. */ 227 if (acceptor) { 228 dh_principal p = cntx->initiate ? cntx->remote : cntx->local; 229 *acceptor = (gss_name_t)strdup(p); 230 } 231 232 /* If the caller wants the time remaining set *time_rec to t */ 233 if (time_rec) 234 *time_rec = t; 235 236 237 /* Return the flags in flags_rec if set */ 238 if (flags_rec) 239 *flags_rec = cntx->flags; 240 241 /* ditto for local */ 242 if (local) 243 *local = cntx->initiate; 244 245 /* ditto for open */ 246 if (open) 247 *open = (cntx->state == ESTABLISHED); 248 249 250 /* return GSS_S_CONTEXT_EXPIRED if no time is left on the context */ 251 return ((t == 0 ? GSS_S_CONTEXT_EXPIRED : GSS_S_COMPLETE) | stat); 252 } 253 254 /* 255 * __dh_gss_process_context_token. 256 * This routine is not implemented. It is depricated in version 2. 257 */ 258 259 OM_uint32 260 /*ARGSUSED*/ 261 __dh_gss_process_context_token(void *ctx, OM_uint32 *minor, 262 gss_ctx_id_t context, gss_buffer_t token) 263 { 264 return (GSS_S_UNAVAILABLE); 265 } 266 267 /* 268 * This implements the gss_wrap_size_limit entry point for Diffie-Hellman 269 * mechanism. See RFC 2078 for details. The idea here is for a context, 270 * qop, whether confidentiality is specified, and an output size, return 271 * the maximum input size that will fit in the given output size. Typically 272 * the output size would be the MTU of the higher level protocol using the 273 * GSS-API. 274 */ 275 276 OM_uint32 277 __dh_gss_wrap_size_limit(void *ctx, /* Mechanism context (not used) */ 278 OM_uint32 *minor, /* Mechanism status */ 279 gss_ctx_id_t context, /* GSS context handle */ 280 int conf_req, /* True if confidentiality is wanted */ 281 gss_qop_t qop_req, /* Requested QOP */ 282 OM_uint32 output_size, /* The maximum ouput size */ 283 OM_uint32 *input_size /* Input size returned */) 284 { 285 _NOTE(ARGUNUSED(ctx)) 286 OM_uint32 major, stat = GSS_S_COMPLETE; 287 unsigned int msgsize, sigsize, pad = 1, size; 288 dh_token_desc token; 289 dh_wrap_t wrap = &token.ver.dh_version_u.body.dh_token_body_desc_u.seal; 290 OM_uint32 left; 291 292 if (input_size == 0) 293 stat = GSS_S_CALL_INACCESSIBLE_WRITE; 294 295 /* We check for valid unexpired context by calling gss_context_time. */ 296 if ((major = stat | __dh_gss_context_time(ctx, minor, context, &left)) 297 != GSS_S_COMPLETE) 298 return (major | stat); 299 300 /* Find the signature size for this qop. */ 301 if ((*minor = __get_sig_size(qop_req, &sigsize)) != DH_SUCCESS) 302 return (GSS_S_BAD_QOP | stat); 303 304 /* Just return if we can't give the caller what he ask for. */ 305 if (stat) 306 return (stat); 307 308 /* 309 * If we requested confidentiality, get the cipher pad for the 310 * requested qop. Since we can't support privacy the cipher pad 311 * is always 1. 312 */ 313 if (conf_req) 314 pad = 1; 315 316 /* 317 * Set up an empty wrap token to calculate header and signature 318 * overhead. 319 */ 320 321 token.ver.verno = DH_PROTO_VERSION; 322 token.ver.dh_version_u.body.type = DH_WRAP; 323 wrap->mic.qop = qop_req; 324 wrap->mic.seqnum = 0; 325 wrap->mic.client_flag = 0; 326 wrap->body.body_len = 0; 327 wrap->body.body_val = 0; 328 token.verifier.dh_signature_len = sigsize; 329 token.verifier.dh_signature_val = 0; 330 331 /* This is the size of an empy wrap token */ 332 size = xdr_sizeof((xdrproc_t)xdr_dh_token_desc, (void *)&token); 333 334 /* This is the amount of space left to put our message. */ 335 msgsize = (output_size > size) ? output_size - size : 0; 336 337 /* XDR needs to pad to a four byte boundry */ 338 msgsize = (msgsize / 4) * 4; 339 340 /* We need to pad to pad bytes for encryption (=1 if conf_req = 0) */ 341 msgsize = (msgsize / pad) * pad; 342 343 /* 344 * The serialization of the inner message includes 345 * the original length. 346 */ 347 348 msgsize = (msgsize > sizeof (uint_t)) ? msgsize - sizeof (uint_t) : 0; 349 350 /* 351 * We now have the space for the inner wrap message, which is also 352 * XDR encoded and is padded to a four byte boundry. 353 */ 354 355 msgsize = (msgsize / 4) * 4; 356 357 *input_size = msgsize; 358 359 return (GSS_S_COMPLETE); 360 } 361