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