1*0Sstevel@tonic-gate /*
2*0Sstevel@tonic-gate  * Cryptographic attack detector for ssh - source code
3*0Sstevel@tonic-gate  *
4*0Sstevel@tonic-gate  * Copyright (c) 1998 CORE SDI S.A., Buenos Aires, Argentina.
5*0Sstevel@tonic-gate  *
6*0Sstevel@tonic-gate  * All rights reserved. Redistribution and use in source and binary
7*0Sstevel@tonic-gate  * forms, with or without modification, are permitted provided that
8*0Sstevel@tonic-gate  * this copyright notice is retained.
9*0Sstevel@tonic-gate  *
10*0Sstevel@tonic-gate  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
11*0Sstevel@tonic-gate  * WARRANTIES ARE DISCLAIMED. IN NO EVENT SHALL CORE SDI S.A. BE
12*0Sstevel@tonic-gate  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY OR
13*0Sstevel@tonic-gate  * CONSEQUENTIAL DAMAGES RESULTING FROM THE USE OR MISUSE OF THIS
14*0Sstevel@tonic-gate  * SOFTWARE.
15*0Sstevel@tonic-gate  *
16*0Sstevel@tonic-gate  * Ariel Futoransky <futo@core-sdi.com>
17*0Sstevel@tonic-gate  * <http://www.core-sdi.com>
18*0Sstevel@tonic-gate  */
19*0Sstevel@tonic-gate 
20*0Sstevel@tonic-gate #include "includes.h"
21*0Sstevel@tonic-gate RCSID("$OpenBSD: deattack.c,v 1.18 2002/03/04 17:27:39 stevesk Exp $");
22*0Sstevel@tonic-gate 
23*0Sstevel@tonic-gate #pragma ident	"%Z%%M%	%I%	%E% SMI"
24*0Sstevel@tonic-gate 
25*0Sstevel@tonic-gate #include "deattack.h"
26*0Sstevel@tonic-gate #include "log.h"
27*0Sstevel@tonic-gate #include "crc32.h"
28*0Sstevel@tonic-gate #include "getput.h"
29*0Sstevel@tonic-gate #include "xmalloc.h"
30*0Sstevel@tonic-gate #include "deattack.h"
31*0Sstevel@tonic-gate 
32*0Sstevel@tonic-gate /* SSH Constants */
33*0Sstevel@tonic-gate #define SSH_MAXBLOCKS	(32 * 1024)
34*0Sstevel@tonic-gate #define SSH_BLOCKSIZE	(8)
35*0Sstevel@tonic-gate 
36*0Sstevel@tonic-gate /* Hashing constants */
37*0Sstevel@tonic-gate #define HASH_MINSIZE	(8 * 1024)
38*0Sstevel@tonic-gate #define HASH_ENTRYSIZE	(2)
39*0Sstevel@tonic-gate #define HASH_FACTOR(x)	((x)*3/2)
40*0Sstevel@tonic-gate #define HASH_UNUSEDCHAR	(0xff)
41*0Sstevel@tonic-gate #define HASH_UNUSED	(0xffff)
42*0Sstevel@tonic-gate #define HASH_IV		(0xfffe)
43*0Sstevel@tonic-gate 
44*0Sstevel@tonic-gate #define HASH_MINBLOCKS	(7*SSH_BLOCKSIZE)
45*0Sstevel@tonic-gate 
46*0Sstevel@tonic-gate 
47*0Sstevel@tonic-gate /* Hash function (Input keys are cipher results) */
48*0Sstevel@tonic-gate #define HASH(x)		GET_32BIT(x)
49*0Sstevel@tonic-gate 
50*0Sstevel@tonic-gate #define CMP(a, b)	(memcmp(a, b, SSH_BLOCKSIZE))
51*0Sstevel@tonic-gate 
52*0Sstevel@tonic-gate static void
53*0Sstevel@tonic-gate crc_update(u_int32_t *a, u_int32_t b)
54*0Sstevel@tonic-gate {
55*0Sstevel@tonic-gate 	b ^= *a;
56*0Sstevel@tonic-gate 	*a = ssh_crc32((u_char *) &b, sizeof(b));
57*0Sstevel@tonic-gate }
58*0Sstevel@tonic-gate 
59*0Sstevel@tonic-gate /* detect if a block is used in a particular pattern */
60*0Sstevel@tonic-gate static int
61*0Sstevel@tonic-gate check_crc(u_char *S, u_char *buf, u_int32_t len,
62*0Sstevel@tonic-gate 	  u_char *IV)
63*0Sstevel@tonic-gate {
64*0Sstevel@tonic-gate 	u_int32_t crc;
65*0Sstevel@tonic-gate 	u_char *c;
66*0Sstevel@tonic-gate 
67*0Sstevel@tonic-gate 	crc = 0;
68*0Sstevel@tonic-gate 	if (IV && !CMP(S, IV)) {
69*0Sstevel@tonic-gate 		crc_update(&crc, 1);
70*0Sstevel@tonic-gate 		crc_update(&crc, 0);
71*0Sstevel@tonic-gate 	}
72*0Sstevel@tonic-gate 	for (c = buf; c < buf + len; c += SSH_BLOCKSIZE) {
73*0Sstevel@tonic-gate 		if (!CMP(S, c)) {
74*0Sstevel@tonic-gate 			crc_update(&crc, 1);
75*0Sstevel@tonic-gate 			crc_update(&crc, 0);
76*0Sstevel@tonic-gate 		} else {
77*0Sstevel@tonic-gate 			crc_update(&crc, 0);
78*0Sstevel@tonic-gate 			crc_update(&crc, 0);
79*0Sstevel@tonic-gate 		}
80*0Sstevel@tonic-gate 	}
81*0Sstevel@tonic-gate 	return (crc == 0);
82*0Sstevel@tonic-gate }
83*0Sstevel@tonic-gate 
84*0Sstevel@tonic-gate 
85*0Sstevel@tonic-gate /* Detect a crc32 compensation attack on a packet */
86*0Sstevel@tonic-gate int
87*0Sstevel@tonic-gate detect_attack(u_char *buf, u_int32_t len, u_char *IV)
88*0Sstevel@tonic-gate {
89*0Sstevel@tonic-gate 	static u_int16_t *h = (u_int16_t *) NULL;
90*0Sstevel@tonic-gate 	static u_int32_t n = HASH_MINSIZE / HASH_ENTRYSIZE;
91*0Sstevel@tonic-gate 	u_int32_t i, j;
92*0Sstevel@tonic-gate 	u_int32_t l;
93*0Sstevel@tonic-gate 	u_char *c;
94*0Sstevel@tonic-gate 	u_char *d;
95*0Sstevel@tonic-gate 
96*0Sstevel@tonic-gate 	if (len > (SSH_MAXBLOCKS * SSH_BLOCKSIZE) ||
97*0Sstevel@tonic-gate 	    len % SSH_BLOCKSIZE != 0) {
98*0Sstevel@tonic-gate 		fatal("detect_attack: bad length %d", len);
99*0Sstevel@tonic-gate 	}
100*0Sstevel@tonic-gate 	for (l = n; l < HASH_FACTOR(len / SSH_BLOCKSIZE); l = l << 2)
101*0Sstevel@tonic-gate 		;
102*0Sstevel@tonic-gate 
103*0Sstevel@tonic-gate 	if (h == NULL) {
104*0Sstevel@tonic-gate 		debug("Installing crc compensation attack detector.");
105*0Sstevel@tonic-gate 		n = l;
106*0Sstevel@tonic-gate 		h = (u_int16_t *) xmalloc(n * HASH_ENTRYSIZE);
107*0Sstevel@tonic-gate 	} else {
108*0Sstevel@tonic-gate 		if (l > n) {
109*0Sstevel@tonic-gate 			n = l;
110*0Sstevel@tonic-gate 			h = (u_int16_t *) xrealloc(h, n * HASH_ENTRYSIZE);
111*0Sstevel@tonic-gate 		}
112*0Sstevel@tonic-gate 	}
113*0Sstevel@tonic-gate 
114*0Sstevel@tonic-gate 	if (len <= HASH_MINBLOCKS) {
115*0Sstevel@tonic-gate 		for (c = buf; c < buf + len; c += SSH_BLOCKSIZE) {
116*0Sstevel@tonic-gate 			if (IV && (!CMP(c, IV))) {
117*0Sstevel@tonic-gate 				if ((check_crc(c, buf, len, IV)))
118*0Sstevel@tonic-gate 					return (DEATTACK_DETECTED);
119*0Sstevel@tonic-gate 				else
120*0Sstevel@tonic-gate 					break;
121*0Sstevel@tonic-gate 			}
122*0Sstevel@tonic-gate 			for (d = buf; d < c; d += SSH_BLOCKSIZE) {
123*0Sstevel@tonic-gate 				if (!CMP(c, d)) {
124*0Sstevel@tonic-gate 					if ((check_crc(c, buf, len, IV)))
125*0Sstevel@tonic-gate 						return (DEATTACK_DETECTED);
126*0Sstevel@tonic-gate 					else
127*0Sstevel@tonic-gate 						break;
128*0Sstevel@tonic-gate 				}
129*0Sstevel@tonic-gate 			}
130*0Sstevel@tonic-gate 		}
131*0Sstevel@tonic-gate 		return (DEATTACK_OK);
132*0Sstevel@tonic-gate 	}
133*0Sstevel@tonic-gate 	memset(h, HASH_UNUSEDCHAR, n * HASH_ENTRYSIZE);
134*0Sstevel@tonic-gate 
135*0Sstevel@tonic-gate 	if (IV)
136*0Sstevel@tonic-gate 		h[HASH(IV) & (n - 1)] = HASH_IV;
137*0Sstevel@tonic-gate 
138*0Sstevel@tonic-gate 	for (c = buf, j = 0; c < (buf + len); c += SSH_BLOCKSIZE, j++) {
139*0Sstevel@tonic-gate 		for (i = HASH(c) & (n - 1); h[i] != HASH_UNUSED;
140*0Sstevel@tonic-gate 		    i = (i + 1) & (n - 1)) {
141*0Sstevel@tonic-gate 			if (h[i] == HASH_IV) {
142*0Sstevel@tonic-gate 				if (!CMP(c, IV)) {
143*0Sstevel@tonic-gate 					if (check_crc(c, buf, len, IV))
144*0Sstevel@tonic-gate 						return (DEATTACK_DETECTED);
145*0Sstevel@tonic-gate 					else
146*0Sstevel@tonic-gate 						break;
147*0Sstevel@tonic-gate 				}
148*0Sstevel@tonic-gate 			} else if (!CMP(c, buf + h[i] * SSH_BLOCKSIZE)) {
149*0Sstevel@tonic-gate 				if (check_crc(c, buf, len, IV))
150*0Sstevel@tonic-gate 					return (DEATTACK_DETECTED);
151*0Sstevel@tonic-gate 				else
152*0Sstevel@tonic-gate 					break;
153*0Sstevel@tonic-gate 			}
154*0Sstevel@tonic-gate 		}
155*0Sstevel@tonic-gate 		h[i] = j;
156*0Sstevel@tonic-gate 	}
157*0Sstevel@tonic-gate 	return (DEATTACK_OK);
158*0Sstevel@tonic-gate }
159