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
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.
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]
25 * Copyright (c) 1997, by Sun Microsystems, Inc.
26 * All rights reserved.
31 #include "dh_gssapi.h"
34 /* Release the storage for a signature */
36 __free_signature(dh_signature_t sig
)
38 Free(sig
->dh_signature_val
);
39 sig
->dh_signature_val
= NULL
;
40 sig
->dh_signature_len
= 0;
43 /* Release the storage for a gss_buffer */
45 __dh_release_buffer(gss_buffer_t b
)
52 typedef struct cipher_entry
{
53 cipher_proc cipher
; /* Routine to en/decrypt with */
54 unsigned int pad
; /* Padding need for the routine */
55 } cipher_entry
, *cipher_t
;
57 typedef struct verifer_entry
{
58 verifier_proc msg
; /* Routine to calculate the check sum */
59 unsigned int size
; /* Size of check sum */
60 cipher_t signer
; /* Cipher entry to sign the check sum */
61 } verifier_entry
, *verifier_t
;
63 typedef struct QOP_entry
{
64 int export_level
; /* Not currentlyt used */
65 verifier_t verifier
; /* Verifier entry to use for integrity */
69 * Return the length produced by using cipher entry c given the supplied len
72 cipher_pad(cipher_t c
, unsigned int len
)
78 return (((len
+ pad
- 1)/pad
)*pad
);
83 * Des [en/de]crypt buffer, buf of length, len for each key provided using
84 * an CBC initialization vector ivec.
85 * If the mode is encrypt we will use the following pattern if the number
87 * encrypt(buf, k[0]), decrypt(buf, k[1]), encrypt(buf, k[2])
88 * decrypt(buf, k[4]) ... encrypt(buf, k[keynum - 1])
89 * If we have an even number of keys and additional encryption will be
90 * done with the first key, i.e., ecrypt(buf, k[0]);
91 * In each [en/de]cription above we will used the passed in CBC initialization
92 * vector. The new initialization vector will be the vector return from the
95 * In the decryption case we reverse the proccess. Note in this case
96 * the return ivec will be from the first decryption.
100 __desN_crypt(des_block keys
[], int keynum
, char *buf
, unsigned int len
,
101 unsigned int mode
, char *ivec
)
103 /* Get the direction of ciphering */
104 unsigned int m
= mode
& (DES_ENCRYPT
| DES_DECRYPT
);
105 /* Get the remaining flags from mode */
106 unsigned int flags
= mode
& ~(DES_ENCRYPT
| DES_DECRYPT
);
107 des_block svec
, dvec
;
110 /* Do we have at least one key */
112 return (DESERR_BADPARAM
);
114 /* Save the passed in ivec */
115 memcpy(svec
.c
, ivec
, sizeof (des_block
));
117 /* For each key do the appropriate cipher */
118 for (i
= 0; i
< keynum
; i
++) {
119 j
= (mode
& DES_DECRYPT
) ? keynum
- 1 - i
: i
;
120 stat
= cbc_crypt(keys
[j
].c
, buf
, len
, m
| flags
, ivec
);
121 if (mode
& DES_DECRYPT
&& i
== 0)
122 memcpy(dvec
.c
, ivec
, sizeof (des_block
));
124 if (DES_FAILED(stat
))
127 m
= (m
== DES_ENCRYPT
? DES_DECRYPT
: DES_ENCRYPT
);
129 if ((mode
& DES_DECRYPT
) || i
!= keynum
- 1 || i
%2)
130 memcpy(ivec
, svec
.c
, sizeof (des_block
));
134 * If we have an even number of keys then do an extra round of
135 * [en/de]cryption with the first key.
138 stat
= cbc_crypt(keys
[0].c
, buf
, len
, mode
, ivec
);
140 /* If were decrypting ivec is set from first decryption */
141 if (mode
& DES_DECRYPT
)
142 memcpy(ivec
, dvec
.c
, sizeof (des_block
));
149 * DesN crypt packaged for use as a cipher entry
152 __dh_desN_crypt(gss_buffer_t buf
, dh_key_set_t keys
, cipher_mode_t cipher_mode
)
154 int stat
= DESERR_BADPARAM
;
155 int encrypt_flag
= (cipher_mode
== ENCIPHER
);
156 unsigned mode
= (encrypt_flag
? DES_ENCRYPT
: DES_DECRYPT
) | DES_HW
;
159 if (keys
->dh_key_set_len
< 1)
160 return (DH_BADARG_FAILURE
);
163 * We all ways start of with ivec set to zeros. There is no
164 * good way to maintain ivecs since packets could be out of sequence
165 * duplicated or worst of all lost. Under these conditions the
166 * higher level protocol would have to some how resync the ivecs
167 * on both sides and start again. Theres no mechanism for this in
170 memset(&ivec
, 0, sizeof (ivec
));
172 /* Do the encryption/decryption */
173 stat
= __desN_crypt(keys
->dh_key_set_val
, keys
->dh_key_set_len
,
174 (char *)buf
->value
, buf
->length
, mode
, ivec
.c
);
176 if (DES_FAILED(stat
))
177 return (DH_CIPHER_FAILURE
);
183 * Package up plain des cbc crypt for use as a cipher entry.
186 __dh_des_crypt(gss_buffer_t buf
, dh_key_set_t keys
, cipher_mode_t cipher_mode
)
188 int stat
= DESERR_BADPARAM
;
189 int encrypt_flag
= (cipher_mode
== ENCIPHER
);
190 unsigned mode
= (encrypt_flag
? DES_ENCRYPT
: DES_DECRYPT
) | DES_HW
;
193 if (keys
->dh_key_set_len
< 1)
194 return (DH_BADARG_FAILURE
);
196 /* Set the ivec to zeros and then cbc crypt the result */
197 memset(&ivec
, 0, sizeof (ivec
));
198 stat
= cbc_crypt(keys
->dh_key_set_val
[0].c
, (char *)buf
->value
,
199 buf
->length
, mode
, ivec
.c
);
201 if (DES_FAILED(stat
))
202 return (DH_CIPHER_FAILURE
);
208 * MD5_verifier: This is a verifier routine suitable for use in a
209 * verifier entry. It calculates the MD5 check sum over an optional
210 * msg and a token. It signs it using the supplied cipher_proc and stores
211 * the result in signature.
213 * Note signature should already be allocated and be large enough to
214 * hold the signature after its been encrypted. If keys is null, then
215 * we will just return the unencrypted check sum.
218 MD5_verifier(gss_buffer_t tok
, /* The buffer to sign */
219 gss_buffer_t msg
, /* Optional buffer to include */
220 cipher_proc signer
, /* Routine to encrypt the integrity check */
221 dh_key_set_t keys
, /* Optiona keys to be used with the above */
222 dh_signature_t signature
/* The resulting MIC */)
224 MD5_CTX md5_ctx
; /* MD5 context */
225 gss_buffer_desc buf
; /* GSS buffer to hold keys for cipher routine */
227 /* Initialize the MD5 context */
229 /* If we have a message to digest, digest it */
231 MD5Update(&md5_ctx
, (unsigned char *)msg
->value
, msg
->length
);
232 /* Digest the supplied token */
233 MD5Update(&md5_ctx
, (unsigned char *)tok
->value
, tok
->length
);
234 /* Finalize the sum. The MD5 context contains the digets */
237 /* Copy the digest to the signature */
238 memcpy(signature
->dh_signature_val
, (void *)md5_ctx
.digest
, 16);
240 buf
.length
= signature
->dh_signature_len
;
241 buf
.value
= signature
->dh_signature_val
;
243 /* If we have keys encrypt it */
245 return (signer(&buf
, keys
, ENCIPHER
));
252 cipher_entry cipher_tab
[] = {
254 { __dh_desN_crypt
, 8},
259 #define __NO_CRYPT &cipher_tab[0]
260 #define __DES_N_CRYPT &cipher_tab[1]
261 #define __DES_CRYPT &cipher_tab[2]
265 verifier_entry verifier_tab
[] = {
266 { MD5_verifier
, 16, __DES_N_CRYPT
},
267 { MD5_verifier
, 16, __DES_CRYPT
}
272 QOP_entry QOP_table
[] = {
273 { 0, &verifier_tab
[0] },
274 { 0, &verifier_tab
[1] }
277 #define QOP_ENTRIES (sizeof (QOP_table) / sizeof (QOP_entry))
280 * __dh_is_valid_QOP: Return true if qop is valid entry into the QOP
281 * table, else return false.
284 __dh_is_valid_QOP(dh_qop_t qop
)
286 bool_t is_valid
= FALSE
;
288 is_valid
= qop
< QOP_ENTRIES
;
294 * __alloc_sig: Allocate a signature for a given QOP. This takes into
295 * account the size of the signature after padding for the encryption
299 __alloc_sig(dh_qop_t qop
, dh_signature_t sig
)
301 OM_uint32 stat
= DH_VERIFIER_FAILURE
;
304 /* Check that the QOP is valid */
305 if (!__dh_is_valid_QOP(qop
))
306 return (DH_UNKNOWN_QOP
);
308 /* Get the verifier entry from the QOP entry */
309 v
= QOP_table
[qop
].verifier
;
311 /* Calulate the length needed for the signature */
312 sig
->dh_signature_len
= cipher_pad(v
->signer
, v
->size
);
314 /* Allocate the signature */
315 sig
->dh_signature_val
= (void*)New(char, sig
->dh_signature_len
);
316 if (sig
->dh_signature_val
== NULL
) {
317 sig
->dh_signature_len
= 0;
318 return (DH_NOMEM_FAILURE
);
327 * __get_sig_size: Return the total size needed for a signature given a QOP.
330 __get_sig_size(dh_qop_t qop
, unsigned int *size
)
332 /* Check for valid QOP */
333 if (__dh_is_valid_QOP(qop
)) {
334 /* Get the verifier entry */
335 verifier_t v
= QOP_table
[qop
].verifier
;
337 /* Return the size include the padding needed for encryption */
338 *size
= v
? cipher_pad(v
->signer
, v
->size
) : 0;
344 return (DH_UNKNOWN_QOP
);
348 * __mk_sig: Generate a signature using a given qop over a token of a
349 * given length and an optional message. We use the supplied keys to
350 * encrypt the check sum if they are available. The output is place
351 * in a preallocate signature, that was allocated using __alloc_sig.
354 __mk_sig(dh_qop_t qop
, /* The QOP to use */
355 char *tok
, /* The token to sign */
356 long len
, /* The tokens length */
357 gss_buffer_t mesg
, /* An optional message to be included */
358 dh_key_set_t keys
, /* The optional encryption keys */
359 dh_signature_t sig
/* The resulting MIC */)
361 OM_uint32 stat
= DH_VERIFIER_FAILURE
;
364 verifier_entry
*v
; /* Verifier entry */
365 gss_buffer_desc buf
; /* Buffer to package tok */
367 /* Make sure the QOP is valid */
368 if (!__dh_is_valid_QOP(qop
))
369 return (DH_UNKNOWN_QOP
);
371 /* Grab the verifier entry for the qop */
372 v
= QOP_table
[qop
].verifier
;
374 /* Package the token for use in a verifier_proc */
379 * Calculate the signature using the supplied keys. If keys
380 * is null, the the v->signer->cipher routine will not be called
381 * and sig will not be encrypted.
383 stat
= (*v
->msg
)(&buf
, mesg
, v
->signer
->cipher
, keys
, sig
);
389 * __verify_sig: Verify that the supplied signature, sig, is the same
390 * as the token verifier
393 __verify_sig(dh_token_t token
, /* The token to be verified */
394 dh_qop_t qop
, /* The QOP to use */
395 dh_key_set_t keys
, /* The context session keys */
396 dh_signature_t sig
/* The signature from the serialized token */)
398 OM_uint32 stat
= DH_VERIFIER_FAILURE
;
400 cipher_proc cipher
; /* cipher routine to use */
401 gss_buffer_desc buf
; /* Packaging for sig */
404 if (!__dh_is_valid_QOP(qop
))
405 return (DH_UNKNOWN_QOP
);
407 /* Package up the supplied signature */
408 buf
.length
= sig
->dh_signature_len
;
409 buf
.value
= sig
->dh_signature_val
;
411 /* Get the cipher proc to use from the verifier entry for qop */
412 cipher
= QOP_table
[qop
].verifier
->signer
->cipher
;
414 /* Encrypt the check sum using the supplied set of keys */
415 if ((stat
= (*cipher
)(&buf
, keys
, ENCIPHER
)) != DH_SUCCESS
)
418 /* Compare the signatures */
419 if (__cmpsig(sig
, &token
->verifier
))
422 stat
= DH_VERIFIER_MISMATCH
;
428 * __cmpsig: Return true if two signatures are the same, else false.
431 __cmpsig(dh_signature_t s1
, dh_signature_t s2
)
433 return (s1
->dh_signature_len
== s2
->dh_signature_len
&&
434 memcmp(s1
->dh_signature_val
,
435 s2
->dh_signature_val
, s1
->dh_signature_len
) == 0);
439 * wrap_msg_body: Wrap the message pointed to be in into a
440 * message pointed to by out that has ben padded out by pad bytes.
442 * The output message looks like:
443 * out->length = total length of out->value including any padding
444 * out->value points to memory as follows:
445 * +------------+-------------------------+---------|
446 * | in->length | in->value | XDR PAD |
447 * +------------+-------------------------+---------|
448 * 4 bytes in->length bytes 0 - 3
451 wrap_msg_body(gss_buffer_t in
, gss_buffer_t out
)
453 XDR xdrs
; /* xdrs to wrap with */
454 unsigned int len
, out_len
; /* length */
460 /* Make sure the address of len points to a 32 bit word */
461 len
= (unsigned int)in
->length
;
462 if (len
!= in
->length
)
463 return (DH_ENCODE_FAILURE
);
465 size
= ((in
->length
+ sizeof (OM_uint32
) + 3)/4) * 4;
468 return (DH_ENCODE_FAILURE
);
470 /* Allocate the output buffer and set the length */
471 if ((out
->value
= (void *)New(char, len
)) == NULL
)
472 return (DH_NOMEM_FAILURE
);
473 out
->length
= out_len
;
476 /* Create xdr stream to wrap into */
477 xdrmem_create(&xdrs
, out
->value
, out
->length
, XDR_ENCODE
);
479 /* Wrap the bytes in value */
480 if (!xdr_bytes(&xdrs
, (char **)&in
->value
, &len
, len
)) {
481 __dh_release_buffer(out
);
482 return (DH_ENCODE_FAILURE
);
489 * __QOPSeal: Wrap the input message placing the output in output given
490 * a valid QOP. If confidentialiy is requested it is ignored. We can't
491 * support privacy. The return flag will always be zero.
494 __QOPSeal(dh_qop_t qop
, /* The QOP to use */
495 gss_buffer_t input
, /* The buffer to wrap */
496 int conf_req
, /* Do we want privacy ? */
497 dh_key_set_t keys
, /* The session keys */
498 gss_buffer_t output
, /* The wraped message */
499 int *conf_ret
/* Did we encrypt it? */)
501 _NOTE(ARGUNUSED(conf_req
,keys
))
502 OM_uint32 stat
= DH_CIPHER_FAILURE
;
504 *conf_ret
= FALSE
; /* No encryption allowed */
506 /* Check for valid QOP */
507 if (!__dh_is_valid_QOP(qop
))
508 return (DH_UNKNOWN_QOP
);
510 /* Wrap the message */
511 if ((stat
= wrap_msg_body(input
, output
))
519 * unwrap_msg_body: Unwrap the message, that was wrapped from above
522 unwrap_msg_body(gss_buffer_t in
, gss_buffer_t out
)
525 unsigned int len
; /* sizeof (len) == 32bits */
527 /* Create an xdr stream to on wrap in */
528 xdrmem_create(&xdrs
, in
->value
, in
->length
, XDR_DECODE
);
530 /* Unwrap the input into out->value */
531 if (!xdr_bytes(&xdrs
, (char **)&out
->value
, &len
, in
->length
))
532 return (DH_DECODE_FAILURE
);
541 * __QOPUnSeal: Unwrap the input message into output using the supplied QOP.
542 * Note it is the callers responsibility to release the allocated output
543 * buffer. If conf_req is true we return DH_CIPHER_FAILURE since we don't
547 __QOPUnSeal(dh_qop_t qop
, /* The QOP to use */
548 gss_buffer_t input
, /* The message to unwrap */
549 int conf_req
, /* Is the message encrypted */
550 dh_key_set_t keys
, /* The session keys to decrypt if conf_req */
551 gss_buffer_t output
/* The unwraped message */)
553 _NOTE(ARGUNUSED(keys
))
554 OM_uint32 stat
= DH_CIPHER_FAILURE
;
556 /* Check that the qop is valid */
557 if (!__dh_is_valid_QOP(qop
))
558 return (DH_UNKNOWN_QOP
);
560 /* Set output to sane values */
562 output
->value
= NULL
;
564 /* Fail if this is privacy */
566 return (DH_CIPHER_FAILURE
);
568 /* Unwrap the input into the output, return the status */
569 stat
= unwrap_msg_body(input
, output
);