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 * MICwrap.c 24 * 25 * Copyright 2004 Sun Microsystems, Inc. All rights reserved. 26 * Use is subject to license terms. 27 * 28 */ 29 30 #include <sys/note.h> 31 #include "dh_gssapi.h" 32 #include "crypto.h" 33 34 /* 35 * This module implements the GSS-API entry points gss_sign, 36 * gss_verify, gss_seal, and gss_unseal. 37 */ 38 39 /* 40 * __dh_gss_sign: Sign (Caluculate a check sum as specified by the qop 41 * and encrypt it with a cipher also determined by the qop using the context 42 * session keys). the message with the given qop and return 43 * a Diffie-Hellman DH_MIC token pointed to by token. 44 */ 45 46 OM_uint32 47 __dh_gss_sign(void *ctx, /* Per mechanism context (not used) */ 48 OM_uint32 *minor, /* Mechanism status */ 49 gss_ctx_id_t context, /* GSS context */ 50 int qop_req, /* Requested qop */ 51 gss_buffer_t message, /* Input message */ 52 gss_buffer_t token /* output token */) 53 { 54 _NOTE(ARGUNUSED(ctx)) 55 /* context is a Diffie-Hellman context */ 56 dh_gss_context_t cntx = (dh_gss_context_t)context; 57 dh_token_desc tok; 58 /* grap a pointer to the mic part of the token */ 59 dh_mic_t mic = &tok.ver.dh_version_u.body.dh_token_body_desc_u.sign; 60 dh_key_set keys; 61 62 /* 63 * Make sure we can return the mechanism status an the token 64 * containning the MIC 65 */ 66 if (minor == 0 || token == GSS_C_NO_BUFFER) 67 return (GSS_S_CALL_INACCESSIBLE_WRITE); 68 69 /* Make sure the context is valid */ 70 if ((*minor = __dh_validate_context(cntx)) != DH_SUCCESS) 71 return (GSS_S_NO_CONTEXT); 72 73 /* that it is established, */ 74 if (cntx->state != ESTABLISHED) 75 return (GSS_S_NO_CONTEXT); 76 77 /* and that it has not expired */ 78 if (cntx->expire != GSS_C_INDEFINITE && cntx->expire < time(0)) 79 return (GSS_S_CONTEXT_EXPIRED); 80 81 /* Package the context session keys in a key_set for __make_token */ 82 keys.dh_key_set_len = cntx->no_keys; 83 keys.dh_key_set_val = cntx->keys; 84 85 /* Set the token version number and type */ 86 tok.ver.verno = cntx->proto_version; 87 tok.ver.dh_version_u.body.type = DH_MIC; 88 89 /* Set the token qop, seq_number and client flag */ 90 mic->qop = qop_req; 91 92 mic->seqnum = __dh_next_seqno(cntx); 93 94 mic->client_flag = cntx->initiate; 95 96 /* 97 * Build the the output token from the message the diffie-hellman 98 * non serialized tok and the context keys. 99 */ 100 if ((*minor = __make_token(token, message, &tok, &keys)) 101 != DH_SUCCESS) { 102 return (GSS_S_FAILURE); 103 } 104 105 return (GSS_S_COMPLETE); 106 } 107 108 109 /* 110 * __dh_gss_verify: calculate the signature of the message and compare 111 * it to the signature represented by the DH_MIC token supplied. If the 112 * major return value is GSS_S_COMPLETE, then *qop will be the qop that 113 * was used in token. 114 */ 115 116 OM_uint32 117 __dh_gss_verify(void *ctx, /* Per mechanism context (not used) */ 118 OM_uint32 *minor, /* Mechanism status */ 119 gss_ctx_id_t context, /* GSS context */ 120 gss_buffer_t message, /* The message */ 121 gss_buffer_t token, /* The DH_MIC message token */ 122 int *qop /* qop used */) 123 { 124 _NOTE(ARGUNUSED(ctx)) 125 /* context is a Diffie-Hellman context */ 126 dh_gss_context_t cntx = (dh_gss_context_t)context; 127 dh_token_desc tok; 128 /* Grab the mic of the token */ 129 dh_mic_t mic = &tok.ver.dh_version_u.body.dh_token_body_desc_u.sign; 130 dh_key_set keys; 131 OM_uint32 stat; 132 133 if (minor == 0) 134 return (GSS_S_CALL_INACCESSIBLE_WRITE); 135 136 /* Validate the context */ 137 if ((*minor = __dh_validate_context(cntx)) != DH_SUCCESS) 138 return (GSS_S_NO_CONTEXT); 139 140 /* Check that the context is established */ 141 if (cntx->state != ESTABLISHED) 142 return (GSS_S_NO_CONTEXT); 143 144 /* and that it has not expired */ 145 if (cntx->expire != GSS_C_INDEFINITE && cntx->expire < time(0)) 146 return (GSS_S_CONTEXT_EXPIRED); 147 148 /* Package up the context session keys in to a key set */ 149 keys.dh_key_set_len = cntx->no_keys; 150 keys.dh_key_set_val = cntx->keys; 151 152 /* Deserialize token into tok using messaget and keys */ 153 if ((*minor = __get_token(token, message, 154 &tok, &keys)) != DH_SUCCESS) { 155 switch (*minor) { 156 case DH_DECODE_FAILURE: 157 return (GSS_S_DEFECTIVE_TOKEN); 158 case DH_VERIFIER_MISMATCH: 159 return (GSS_S_BAD_SIG); 160 default: 161 return (GSS_S_FAILURE); 162 } 163 } 164 165 /* Check that the tok version is supported */ 166 if (tok.ver.verno != cntx->proto_version || 167 tok.ver.dh_version_u.body.type != DH_MIC) { 168 xdr_free(xdr_dh_token_desc, (char *)&tok); 169 return (GSS_S_DEFECTIVE_TOKEN); 170 } 171 172 /* Set the return qop */ 173 if (qop != NULL) 174 *qop = mic->qop; 175 176 /* Sequence & Replay detection here */ 177 stat = __dh_seq_detection(cntx, mic->seqnum); 178 179 /* free the deserialize token tok */ 180 xdr_free(xdr_dh_token_desc, (char *)&tok); 181 182 /* 183 * If client flag is the same as the initiator flag, we're talking 184 * to our selves or we're being spoofed. We return 185 * GSS_S_DUPLICATE_TOKEN since its the best return code in the 186 * supplementry group. 187 */ 188 189 if (mic->client_flag == cntx->initiate) 190 stat |= GSS_S_DUPLICATE_TOKEN; 191 192 return (stat); 193 } 194 195 196 /* 197 * __dh_gss_seal: Seal a message, i.e, it wraps or embeds a supplied message 198 * in a DH_WRAP token to be delivered to the other side. A message check 199 * over the whole message is include and is selected base on the supplied 200 * qop. If the qop supports privacy and confidentiality was requested, then 201 * the embedded message will be encrypted. A return flag will be set if 202 * the message was encrypted. 203 * 204 * NOTE: IN THE CURRENT PRODUCT NO QOP CAN SUPPORT PRIVACY. THE *conf_state 205 * FLAG WILL ALWAYS BE ZERO. 206 */ 207 208 OM_uint32 209 __dh_gss_seal(void * ctx, /* Per mechanism context */ 210 OM_uint32 *minor, /* Mechanism status */ 211 gss_ctx_id_t context, /* GSS context */ 212 int conf_req, /* True to request privacy */ 213 int qop_req, /* Use the requested qop */ 214 gss_buffer_t input, /* Input message to wrap */ 215 int *conf_state, /* True if message was encrypted */ 216 gss_buffer_t output /* Contains the ouputed DH_WRAP token*/) 217 { 218 _NOTE(ARGUNUSED(ctx)) 219 /* context is a Diffie-Hellman context */ 220 dh_gss_context_t cntx = (dh_gss_context_t)context; 221 dh_token_desc tok; 222 /* Get a pointer to the wrap protion of the token */ 223 dh_wrap_t wrap = &tok.ver.dh_version_u.body.dh_token_body_desc_u.seal; 224 dh_key_set keys; 225 gss_buffer_desc body; 226 227 if (minor == 0) 228 return (GSS_S_CALL_INACCESSIBLE_WRITE); 229 230 /* See if the context is valid */ 231 if ((*minor = __dh_validate_context(cntx)) != DH_SUCCESS) 232 return (GSS_S_NO_CONTEXT); 233 234 /* that it is established, */ 235 if (cntx->state != ESTABLISHED) 236 return (GSS_S_NO_CONTEXT); 237 238 /* and that it has not expired */ 239 if (cntx->expire != GSS_C_INDEFINITE && cntx->expire < time(0)) 240 return (GSS_S_CONTEXT_EXPIRED); 241 242 /* Package the session keys in a key_set */ 243 keys.dh_key_set_len = cntx->no_keys; 244 keys.dh_key_set_val = cntx->keys; 245 246 /* Set the version and token type */ 247 tok.ver.verno = cntx->proto_version; 248 tok.ver.dh_version_u.body.type = DH_WRAP; 249 250 /* Set the qop, initiate flag, and sequence number */ 251 wrap->mic.qop = qop_req; 252 wrap->mic.client_flag = cntx->initiate; 253 wrap->mic.seqnum = __dh_next_seqno(cntx); 254 255 /* 256 * Wrap the supplied message and encrypted if it is requested 257 * and allowed. The qop will have to have an associated cipher 258 * routine. NOTE: BECAUSE OF EXPORT CONTROLS, THE MECHANISM 259 * CURRENTLY WILL NOT DO ENCRYPTION AND conf_stat WILL ALWAY BE SET 260 * TO FALSE. 261 */ 262 if ((*minor = __QOPSeal(wrap->mic.qop, input, conf_req, 263 &keys, &body, conf_state)) != DH_SUCCESS) { 264 __free_signature(&tok.verifier); 265 return (GSS_S_FAILURE); 266 } 267 268 /* The body now contains the wrapped orignal message */ 269 wrap->body.body_len = body.length; 270 wrap->body.body_val = (char *)body.value; 271 272 /* 273 * Tell the other side if encrypted. 274 * SEE NOTE ABOVE. THIS WILL ALWAYS BE FALSE. 275 */ 276 if (conf_state) 277 wrap->conf_flag = *conf_state; 278 else 279 wrap->conf_flag = FALSE; 280 281 /* Serialize the token tok into output using the session keys */ 282 if ((*minor = __make_token(output, NULL, &tok, &keys)) != DH_SUCCESS) { 283 __dh_release_buffer(&body); 284 return (GSS_S_FAILURE); 285 } 286 /* We're done with the wrapped body */ 287 __dh_release_buffer(&body); 288 289 return (GSS_S_COMPLETE); 290 } 291 292 /* 293 * __dh_gss_unseal: Unwrap a supplied DH_WRAP token extracting the orginal 294 * message, qop_used, and whether privacy was used. 295 * 296 * NOTE: BECAUSE OF EXPORT CONTROLS, NO QOP IN THE MECHANISM SUPPORTS 297 * PRIVACY. *conf_state WILL ALWAY BE FALSE. 298 */ 299 300 OM_uint32 301 __dh_gss_unseal(void *ctx, /* Per mechanism context (not used) */ 302 OM_uint32 *minor, /* Mechanism status */ 303 gss_ctx_id_t context, /* GSS context handle */ 304 gss_buffer_t input, /* Wrapped Diffie-Hellman token */ 305 gss_buffer_t output, /* The unwrapped message */ 306 int *conf_state, /* True if the message was encrypted */ 307 int *qop_used /* QOP used in token */) 308 { 309 _NOTE(ARGUNUSED(ctx)) 310 /* context is a Diffie-Hellman context */ 311 dh_gss_context_t cntx = (dh_gss_context_t)context; 312 dh_token_desc tok; 313 /* Grap the wrap portion of the above token */ 314 dh_wrap_t wrap = &tok.ver.dh_version_u.body.dh_token_body_desc_u.seal; 315 dh_key_set keys; 316 gss_buffer_desc message; 317 OM_uint32 stat; 318 319 if (minor == 0 || conf_state == 0 || output == GSS_C_NO_BUFFER) 320 return (GSS_S_CALL_INACCESSIBLE_WRITE); 321 322 /* Validate context, */ 323 if ((*minor = __dh_validate_context(cntx)) != DH_SUCCESS) 324 return (GSS_S_NO_CONTEXT); 325 326 /* check if it is established, */ 327 if (cntx->state != ESTABLISHED) 328 return (GSS_S_NO_CONTEXT); 329 330 /* and that it has not expired */ 331 if (cntx->expire != GSS_C_INDEFINITE && cntx->expire < time(0)) 332 return (GSS_S_CONTEXT_EXPIRED); 333 334 /* Package up the session keys in to a key_set */ 335 keys.dh_key_set_len = cntx->no_keys; 336 keys.dh_key_set_val = cntx->keys; 337 338 /* Deserialize the input in to tok using keys */ 339 if ((*minor = __get_token(input, NULL, &tok, &keys)) != DH_SUCCESS) { 340 switch (*minor) { 341 case DH_DECODE_FAILURE: 342 case DH_UNKNOWN_QOP: 343 return (GSS_S_DEFECTIVE_TOKEN); 344 case DH_VERIFIER_MISMATCH: 345 return (GSS_S_BAD_SIG); 346 default: 347 return (GSS_S_FAILURE); 348 } 349 } 350 351 /* Set the qop_used and confidentiality state */ 352 if (qop_used != NULL) 353 *qop_used = wrap->mic.qop; 354 *conf_state = wrap->conf_flag; 355 356 /* See if this is a version that we can support */ 357 if (tok.ver.verno != cntx->proto_version || 358 tok.ver.dh_version_u.body.type != DH_WRAP) { 359 xdr_free(xdr_dh_token_desc, (char *)&tok); 360 return (GSS_S_DEFECTIVE_TOKEN); 361 } 362 363 /* Put the unwrapped body in to a gss_buffer */ 364 message.length = wrap->body.body_len; 365 message.value = wrap->body.body_val; 366 367 /* 368 * Unwrap the message putting the result in output. We use the 369 * qop from the token, the session keys, and set *conf_state if 370 * encryption was used. 371 * 372 * NOTE: THIS MECHANISM DOES NOT SUPPORT ENCRYPTION. *conf_state 373 * WILL ALWAY BE FALSE. 374 */ 375 if ((*minor = __QOPUnSeal(wrap->mic.qop, &message, 376 *conf_state, &keys, output)) 377 != DH_SUCCESS) { 378 xdr_free(xdr_dh_token_desc, (char *)&tok); 379 return (*minor == DH_UNKNOWN_QOP ? 380 GSS_S_DEFECTIVE_TOKEN : GSS_S_FAILURE); 381 } 382 383 /* Sequence & Replay detection here */ 384 stat = __dh_seq_detection(cntx, wrap->mic.seqnum); 385 386 /* 387 * If client flag is the same as the initiator flag, we're talking 388 * to our selves or we're being spoofed. We return 389 * GSS_S_DUPLICATE_TOKEN since its the best return code in the 390 * supplementry group. 391 */ 392 393 if (wrap->mic.client_flag == cntx->initiate) 394 stat |= GSS_S_DUPLICATE_TOKEN; 395 396 /* Were done with the deserialize token, tok */ 397 xdr_free(xdr_dh_token_desc, (char *)&tok); 398 399 return (stat); 400 } 401