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