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  *	crypto.c
24  *
25  *	Copyright (c) 1997, by Sun Microsystems, Inc.
26  *	All rights reserved.
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 /* Release the storage for a signature */
37 void
38 __free_signature(dh_signature_t sig)
39 {
40 	Free(sig->dh_signature_val);
41 	sig->dh_signature_val = NULL;
42 	sig->dh_signature_len = 0;
43 }
44 
45 /* Release the storage for a gss_buffer */
46 void
47 __dh_release_buffer(gss_buffer_t b)
48 {
49 	Free(b->value);
50 	b->length = 0;
51 	b->value = NULL;
52 }
53 
54 typedef struct cipher_entry {
55 	cipher_proc cipher;	/* Routine to en/decrypt with */
56 	unsigned int pad;	/* Padding need for the routine */
57 } cipher_entry, *cipher_t;
58 
59 typedef struct verifer_entry {
60 	verifier_proc msg;	/* Routine to calculate the check sum */
61 	unsigned int size;	/* Size of check sum */
62 	cipher_t signer;	/* Cipher entry to sign the check sum */
63 } verifier_entry, *verifier_t;
64 
65 typedef struct QOP_entry {
66 	int export_level;	/* Not currentlyt used */
67 	verifier_t verifier;	/* Verifier entry to use for integrity */
68 } QOP_entry;
69 
70 /*
71  * Return the length produced by using cipher entry c given the supplied len
72  */
73 static unsigned int
74 cipher_pad(cipher_t c, unsigned int len)
75 {
76 	unsigned int pad;
77 
78 	pad = c ? c->pad : 1;
79 
80 	return (((len + pad - 1)/pad)*pad);
81 }
82 
83 
84 /* EXPORT DELETE START */
85 
86 /*
87  * Des [en/de]crypt buffer, buf of length, len for each key provided using
88  * an CBC initialization vector ivec.
89  * If the mode is encrypt we will use the following pattern if the number
90  * of keys is odd
91  * encrypt(buf, k[0]), decrypt(buf, k[1]), encrypt(buf, k[2])
92  *	decrypt(buf, k[4]) ... encrypt(buf, k[keynum - 1])
93  * If we have an even number of keys and additional encryption will be
94  * done with the first key, i.e., ecrypt(buf, k[0]);
95  * In each [en/de]cription above we will used the passed in CBC initialization
96  * vector. The new initialization vector will be the vector return from the
97  * last encryption.
98  *
99  * In the decryption case we reverse the proccess. Note in this case
100  * the return ivec will be from the first decryption.
101  */
102 
103 static int
104 __desN_crypt(des_block keys[], int keynum, char *buf, unsigned int len,
105     unsigned int mode, char *ivec)
106 {
107 	/* Get the direction of ciphering */
108 	unsigned int m = mode & (DES_ENCRYPT | DES_DECRYPT);
109 	/* Get the remaining flags from mode */
110 	unsigned int flags = mode & ~(DES_ENCRYPT | DES_DECRYPT);
111 	des_block svec, dvec;
112 	int i, j, stat;
113 
114 	/* Do we have at least one key */
115 	if (keynum < 1)
116 		return (DESERR_BADPARAM);
117 
118 	/* Save the passed in ivec */
119 	memcpy(svec.c, ivec, sizeof (des_block));
120 
121 	/* For  each key do the appropriate cipher */
122 	for (i = 0; i < keynum; i++) {
123 		j = (mode & DES_DECRYPT) ? keynum - 1 - i : i;
124 		stat = cbc_crypt(keys[j].c, buf, len, m | flags, ivec);
125 		if (mode & DES_DECRYPT && i == 0)
126 			memcpy(dvec.c, ivec, sizeof (des_block));
127 
128 		if (DES_FAILED(stat))
129 			return (stat);
130 
131 		m = (m == DES_ENCRYPT ? DES_DECRYPT : DES_ENCRYPT);
132 
133 		if ((mode & DES_DECRYPT) || i != keynum - 1 || i%2)
134 			memcpy(ivec, svec.c, sizeof (des_block));
135 	}
136 
137 	/*
138 	 * If we have an even number of keys then do an extra round of
139 	 * [en/de]cryption with the first key.
140 	 */
141 	if (keynum % 2 == 0)
142 		stat = cbc_crypt(keys[0].c, buf, len, mode, ivec);
143 
144 	/* If were decrypting ivec is set from first decryption */
145 	if (mode & DES_DECRYPT)
146 		memcpy(ivec, dvec.c, sizeof (des_block));
147 
148 	return (stat);
149 }
150 
151 /* EXPORT DELETE END */
152 
153 
154 /*
155  * DesN crypt packaged for use as a cipher entry
156  */
157 static OM_uint32
158 __dh_desN_crypt(gss_buffer_t buf, dh_key_set_t keys, cipher_mode_t cipher_mode)
159 {
160 	int stat = DESERR_BADPARAM;
161 /* EXPORT DELETE START */
162 	int encrypt_flag = (cipher_mode == ENCIPHER);
163 	unsigned mode = (encrypt_flag ? DES_ENCRYPT : DES_DECRYPT) | DES_HW;
164 	des_block ivec;
165 
166 	if (keys->dh_key_set_len < 1)
167 		return (DH_BADARG_FAILURE);
168 
169 	/*
170 	 * We all ways start of with ivec set to zeros. There is no
171 	 * good way to maintain ivecs since packets could be out of sequence
172 	 * duplicated or worst of all lost. Under these conditions the
173 	 * higher level protocol would have to some how resync the ivecs
174 	 * on both sides and start again. Theres no mechanism for this in
175 	 * GSS.
176 	 */
177 	memset(&ivec, 0, sizeof (ivec));
178 
179 	/* Do the encryption/decryption */
180 	stat = __desN_crypt(keys->dh_key_set_val, keys->dh_key_set_len,
181 			    (char *)buf->value, buf->length, mode, ivec.c);
182 /* EXPORT DELETE END */
183 
184 	if (DES_FAILED(stat))
185 		return (DH_CIPHER_FAILURE);
186 
187 	return (DH_SUCCESS);
188 }
189 
190 /*
191  * Package up plain des cbc crypt for use as a cipher entry.
192  */
193 static OM_uint32
194 __dh_des_crypt(gss_buffer_t buf, dh_key_set_t keys, cipher_mode_t cipher_mode)
195 {
196 	int stat = DESERR_BADPARAM;
197 /* EXPORT DELETE START */
198 	int encrypt_flag = (cipher_mode == ENCIPHER);
199 	unsigned mode = (encrypt_flag ? DES_ENCRYPT : DES_DECRYPT) | DES_HW;
200 	des_block ivec;
201 
202 	if (keys->dh_key_set_len < 1)
203 		return (DH_BADARG_FAILURE);
204 
205 	/*  Set the ivec to zeros and then cbc crypt the result */
206 	memset(&ivec, 0, sizeof (ivec));
207 	stat = cbc_crypt(keys->dh_key_set_val[0].c, (char *)buf->value,
208 			buf->length, mode, ivec.c);
209 /* EXPORT DELETE END */
210 
211 	if (DES_FAILED(stat))
212 		return (DH_CIPHER_FAILURE);
213 
214 	return (DH_SUCCESS);
215 }
216 
217 /*
218  * MD5_verifier: This is a verifier routine suitable for use in a
219  * verifier entry. It calculates the MD5 check sum over an optional
220  * msg and a token. It signs it using the supplied cipher_proc and stores
221  * the result in signature.
222  *
223  * Note signature should already be allocated and be large enough to
224  * hold the signature after its been encrypted. If keys is null, then
225  * we will just return the unencrypted check sum.
226  */
227 static OM_uint32
228 MD5_verifier(gss_buffer_t tok, /* The buffer to sign */
229 	    gss_buffer_t msg, /* Optional buffer to include */
230 	    cipher_proc signer, /* Routine to encrypt the integrity check */
231 	    dh_key_set_t keys, /* Optiona keys to be used with the above */
232 	    dh_signature_t signature /* The resulting MIC */)
233 {
234 	MD5_CTX md5_ctx;	/* MD5 context */
235 	gss_buffer_desc buf;	/* GSS buffer to hold keys for cipher routine */
236 
237 	/* Initialize the MD5 context */
238 	MD5Init(&md5_ctx);
239 	/* If we have a message to digest, digest it */
240 	if (msg)
241 	    MD5Update(&md5_ctx, (unsigned char *)msg->value, msg->length);
242 	/* Digest the supplied token */
243 	MD5Update(&md5_ctx, (unsigned char *)tok->value, tok->length);
244 	/* Finalize the sum. The MD5 context contains the digets */
245 	MD5Final(&md5_ctx);
246 
247 	/* Copy the digest to the signature */
248 	memcpy(signature->dh_signature_val, (void *)md5_ctx.digest, 16);
249 
250 	buf.length = signature->dh_signature_len;
251 	buf.value = signature->dh_signature_val;
252 
253 	/* If we have keys encrypt it */
254 	if (keys != NULL)
255 		return (signer(&buf, keys, ENCIPHER));
256 
257 	return (DH_SUCCESS);
258 }
259 
260 /* Cipher table */
261 static
262 cipher_entry cipher_tab[] = {
263 	{ NULL, 1},
264 	{ __dh_desN_crypt, 8},
265 	{ __dh_des_crypt, 8}
266 };
267 
268 
269 #define	__NO_CRYPT	&cipher_tab[0]
270 #define	__DES_N_CRYPT	&cipher_tab[1]
271 #define	__DES_CRYPT	&cipher_tab[2]
272 
273 /* Verifier table */
274 static
275 verifier_entry verifier_tab[] = {
276 	{ MD5_verifier, 16, __DES_N_CRYPT },
277 	{ MD5_verifier, 16, __DES_CRYPT }
278 };
279 
280 /* QOP table */
281 static
282 QOP_entry QOP_table[] = {
283 	{ 0, &verifier_tab[0] },
284 	{ 0, &verifier_tab[1] }
285 };
286 
287 #define	QOP_ENTRIES (sizeof (QOP_table) / sizeof (QOP_entry))
288 
289 /*
290  * __dh_is_valid_QOP: Return true if qop is valid entry into the QOP
291  * table, else return false.
292  */
293 bool_t
294 __dh_is_valid_QOP(dh_qop_t qop)
295 {
296 	bool_t is_valid = FALSE;
297 
298 	is_valid = qop < QOP_ENTRIES;
299 
300 	return (is_valid);
301 }
302 
303 /*
304  * __alloc_sig: Allocate a signature for a given QOP. This takes into
305  * account the size of the signature after padding for the encryption
306  * routine.
307  */
308 OM_uint32
309 __alloc_sig(dh_qop_t qop, dh_signature_t sig)
310 {
311 	OM_uint32 stat = DH_VERIFIER_FAILURE;
312 	verifier_entry *v;
313 
314 	/* Check that the QOP is valid */
315 	if (!__dh_is_valid_QOP(qop))
316 		return (DH_UNKNOWN_QOP);
317 
318 	/* Get the verifier entry from the QOP entry */
319 	v = QOP_table[qop].verifier;
320 
321 	/* Calulate the length needed for the signature */
322 	sig->dh_signature_len = cipher_pad(v->signer, v->size);
323 
324 	/* Allocate the signature */
325 	sig->dh_signature_val = (void*)New(char, sig->dh_signature_len);
326 	if (sig->dh_signature_val == NULL) {
327 		sig->dh_signature_len = 0;
328 		return (DH_NOMEM_FAILURE);
329 	}
330 
331 	stat = DH_SUCCESS;
332 
333 	return (stat);
334 }
335 
336 /*
337  * __get_sig_size: Return the total size needed for a signature given a QOP.
338  */
339 OM_uint32
340 __get_sig_size(dh_qop_t qop, unsigned int *size)
341 {
342 	/* Check for valid QOP */
343 	if (__dh_is_valid_QOP(qop)) {
344 		/* Get the verifier entry */
345 		verifier_t v = QOP_table[qop].verifier;
346 
347 		/* Return the size include the padding needed for encryption */
348 		*size = v ? cipher_pad(v->signer, v->size) : 0;
349 
350 		return (DH_SUCCESS);
351 	}
352 	*size = 0;
353 
354 	return (DH_UNKNOWN_QOP);
355 }
356 
357 /*
358  * __mk_sig: Generate a signature using a given qop over a token of a
359  * given length and an optional message. We use the supplied keys to
360  * encrypt the check sum if they are available. The output is place
361  * in a preallocate signature, that was allocated using __alloc_sig.
362  */
363 OM_uint32
364 __mk_sig(dh_qop_t qop, /* The QOP to use */
365 	char *tok, /* The token to sign */
366 	long len, /* The tokens length */
367 	gss_buffer_t mesg,	/* An optional message to be included */
368 	dh_key_set_t keys, /* The optional encryption keys */
369 	dh_signature_t sig /* The resulting MIC */)
370 {
371 	OM_uint32 stat = DH_VERIFIER_FAILURE;
372 
373 
374 	verifier_entry *v;	/* Verifier entry */
375 	gss_buffer_desc buf;	/* Buffer to package tok */
376 
377 	/* Make sure the QOP is valid */
378 	if (!__dh_is_valid_QOP(qop))
379 		return (DH_UNKNOWN_QOP);
380 
381 	/* Grab the verifier entry for the qop */
382 	v = QOP_table[qop].verifier;
383 
384 	/* Package the token for use in a verifier_proc */
385 	buf.length = len;
386 	buf.value = tok;
387 
388 	/*
389 	 * Calculate the signature using the supplied keys. If keys
390 	 * is null, the the v->signer->cipher routine will not be called
391 	 * and sig will not be encrypted.
392 	 */
393 	stat = (*v->msg)(&buf, mesg, v->signer->cipher, keys, sig);
394 
395 	return (stat);
396 }
397 
398 /*
399  * __verify_sig: Verify that the supplied signature, sig, is the same
400  * as the token verifier
401  */
402 OM_uint32
403 __verify_sig(dh_token_t token, /* The token to be verified */
404 	    dh_qop_t qop, /* The QOP to use */
405 	    dh_key_set_t keys, /* The context session keys */
406 	    dh_signature_t sig /* The signature from the serialized token */)
407 {
408 	OM_uint32 stat = DH_VERIFIER_FAILURE;
409 
410 	cipher_proc cipher;	/* cipher routine to use */
411 	gss_buffer_desc buf;	/* Packaging for sig */
412 
413 	/* Check the QOP */
414 	if (!__dh_is_valid_QOP(qop))
415 		return (DH_UNKNOWN_QOP);
416 
417 	/* Package up the supplied signature */
418 	buf.length = sig->dh_signature_len;
419 	buf.value = sig->dh_signature_val;
420 
421 	/* Get the cipher proc to use from the verifier entry for qop */
422 	cipher = QOP_table[qop].verifier->signer->cipher;
423 
424 	/* Encrypt the check sum using the supplied set of keys */
425 	if ((stat = (*cipher)(&buf, keys, ENCIPHER)) != DH_SUCCESS)
426 		return (stat);
427 
428 	/* Compare the signatures */
429 	if (__cmpsig(sig, &token->verifier))
430 		return (DH_SUCCESS);
431 
432 	stat = DH_VERIFIER_MISMATCH;
433 
434 	return (stat);
435 }
436 
437 /*
438  * __cmpsig: Return true if two signatures are the same, else false.
439  */
440 bool_t
441 __cmpsig(dh_signature_t s1, dh_signature_t s2)
442 {
443 	return (s1->dh_signature_len == s2->dh_signature_len &&
444 	    memcmp(s1->dh_signature_val,
445 		s2->dh_signature_val, s1->dh_signature_len) == 0);
446 }
447 
448 /*
449  * wrap_msg_body: Wrap the message pointed to be in into a
450  * message pointed to by out that has ben padded out by pad bytes.
451  *
452  * The output message looks like:
453  * out->length = total length of out->value including any padding
454  * out->value points to memory as follows:
455  * +------------+-------------------------+---------|
456  * | in->length | in->value               | XDR PAD |
457  * +------------+-------------------------+---------|
458  *    4 bytes      in->length bytes         0 - 3
459  */
460 static OM_uint32
461 wrap_msg_body(gss_buffer_t in, gss_buffer_t out)
462 {
463 	XDR xdrs;			/* xdrs to wrap with */
464 	unsigned int len, out_len;	/* length  */
465 	size_t size;
466 
467 	out->length = 0;
468 	out->value = 0;
469 
470 	/* Make sure the address of len points to a 32 bit word */
471 	len = (unsigned int)in->length;
472 	if (len != in->length)
473 		return (DH_ENCODE_FAILURE);
474 
475 	size = ((in->length + sizeof (OM_uint32) + 3)/4) * 4;
476 	out_len = size;
477 	if (out_len != size)
478 		return (DH_ENCODE_FAILURE);
479 
480 	/* Allocate the output buffer and set the length */
481 	if ((out->value = (void *)New(char, len)) == NULL)
482 		return (DH_NOMEM_FAILURE);
483 	out->length = out_len;
484 
485 
486 	/* Create xdr stream to wrap into */
487 	xdrmem_create(&xdrs, out->value, out->length, XDR_ENCODE);
488 
489 	/* Wrap the bytes in value */
490 	if (!xdr_bytes(&xdrs, (char **)&in->value, &len, len)) {
491 		__dh_release_buffer(out);
492 		return (DH_ENCODE_FAILURE);
493 	}
494 
495 	return (DH_SUCCESS);
496 }
497 
498 /*
499  * __QOPSeal: Wrap the input message placing the output in output given
500  * a valid QOP. If confidentialiy is requested it is ignored. We can't
501  * support privacy. The return flag will always be zero.
502  */
503 OM_uint32
504 __QOPSeal(dh_qop_t qop, /* The QOP to use */
505 	gss_buffer_t input, /* The buffer to wrap */
506 	int conf_req, /* Do we want privacy ? */
507 	dh_key_set_t keys, /* The session keys */
508 	gss_buffer_t output, /* The wraped message */
509 	int *conf_ret /* Did we encrypt it? */)
510 {
511 _NOTE(ARGUNUSED(conf_req,keys))
512 	OM_uint32 stat = DH_CIPHER_FAILURE;
513 
514 	*conf_ret = FALSE;	/* No encryption allowed */
515 
516 	/* Check for valid QOP */
517 	if (!__dh_is_valid_QOP(qop))
518 		return (DH_UNKNOWN_QOP);
519 
520 	/* Wrap the message */
521 	if ((stat = wrap_msg_body(input, output))
522 	    != DH_SUCCESS)
523 		return (stat);
524 
525 	return (stat);
526 }
527 
528 /*
529  * unwrap_msg_body: Unwrap the message, that was wrapped from above
530  */
531 static OM_uint32
532 unwrap_msg_body(gss_buffer_t in, gss_buffer_t out)
533 {
534 	XDR xdrs;
535 	unsigned int len;	/* sizeof (len) == 32bits */
536 
537 	/* Create an xdr stream to on wrap in */
538 	xdrmem_create(&xdrs, in->value, in->length, XDR_DECODE);
539 
540 	/* Unwrap the input into out->value */
541 	if (!xdr_bytes(&xdrs, (char **)&out->value, &len, in->length))
542 		return (DH_DECODE_FAILURE);
543 
544 	/* set the length */
545 	out->length = len;
546 
547 	return (DH_SUCCESS);
548 }
549 
550 /*
551  * __QOPUnSeal: Unwrap the input message into output using the supplied QOP.
552  * Note it is the callers responsibility to release the allocated output
553  * buffer. If conf_req is true we return DH_CIPHER_FAILURE since we don't
554  * support privacy.
555  */
556 OM_uint32
557 __QOPUnSeal(dh_qop_t qop, /* The QOP to use */
558 	    gss_buffer_t input, /* The message to unwrap */
559 	    int conf_req, /* Is the message encrypted */
560 	    dh_key_set_t keys, /* The session keys to decrypt if conf_req */
561 	    gss_buffer_t output /* The unwraped message */)
562 {
563 _NOTE(ARGUNUSED(keys))
564 	OM_uint32 stat = DH_CIPHER_FAILURE;
565 
566 	/* Check that the qop is valid */
567 	if (!__dh_is_valid_QOP(qop))
568 		return (DH_UNKNOWN_QOP);
569 
570 	/* Set output to sane values */
571 	output->length = 0;
572 	output->value = NULL;
573 
574 	/* Fail if this is privacy */
575 	if (conf_req)
576 		return (DH_CIPHER_FAILURE);
577 
578 	/* Unwrap the input into the output, return the status */
579 	stat = unwrap_msg_body(input, output);
580 
581 	return (stat);
582 }
583