Annotation of src/usr.bin/ssh/deattack.c, Revision 1.30
1.30 ! djm 1: /* $OpenBSD: deattack.c,v 1.29 2006/08/03 03:34:42 deraadt Exp $ */
1.1 dugsong 2: /*
1.2 dugsong 3: * Cryptographic attack detector for ssh - source code
4: *
5: * Copyright (c) 1998 CORE SDI S.A., Buenos Aires, Argentina.
6: *
7: * All rights reserved. Redistribution and use in source and binary
8: * forms, with or without modification, are permitted provided that
9: * this copyright notice is retained.
10: *
11: * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
12: * WARRANTIES ARE DISCLAIMED. IN NO EVENT SHALL CORE SDI S.A. BE
13: * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY OR
14: * CONSEQUENTIAL DAMAGES RESULTING FROM THE USE OR MISUSE OF THIS
15: * SOFTWARE.
16: *
17: * Ariel Futoransky <futo@core-sdi.com>
1.6 deraadt 18: * <http://www.core-sdi.com>
19: */
1.1 dugsong 20:
1.29 deraadt 21: #include <sys/types.h>
1.28 stevesk 22:
23: #include <string.h>
1.29 deraadt 24: #include <stdio.h>
25: #include <stdarg.h>
1.18 stevesk 26:
1.29 deraadt 27: #include "xmalloc.h"
1.1 dugsong 28: #include "deattack.h"
1.12 markus 29: #include "log.h"
1.3 markus 30: #include "crc32.h"
1.27 djm 31: #include "misc.h"
1.1 dugsong 32:
1.30 ! djm 33: /*
! 34: * CRC attack detection has a worst-case behaviour that is O(N^3) over
! 35: * the number of identical blocks in a packet. This behaviour can be
! 36: * exploited to create a limited denial of service attack.
! 37: *
! 38: * However, because we are dealing with encrypted data, identical
! 39: * blocks should only occur every 2^35 maximally-sized packets or so.
! 40: * Consequently, we can detect this DoS by looking for identical blocks
! 41: * in a packet.
! 42: *
! 43: * The parameter below determines how many identical blocks we will
! 44: * accept in a single packet, trading off between attack detection and
! 45: * likelihood of terminating a legitimate connection. A value of 32
! 46: * corresponds to an average of 2^40 messages before an attack is
! 47: * misdetected
! 48: */
! 49: #define MAX_IDENTICAL 32
! 50:
1.1 dugsong 51: /* SSH Constants */
1.5 markus 52: #define SSH_MAXBLOCKS (32 * 1024)
53: #define SSH_BLOCKSIZE (8)
1.1 dugsong 54:
55: /* Hashing constants */
1.5 markus 56: #define HASH_MINSIZE (8 * 1024)
57: #define HASH_ENTRYSIZE (2)
58: #define HASH_FACTOR(x) ((x)*3/2)
59: #define HASH_UNUSEDCHAR (0xff)
60: #define HASH_UNUSED (0xffff)
1.17 deraadt 61: #define HASH_IV (0xfffe)
1.1 dugsong 62:
1.5 markus 63: #define HASH_MINBLOCKS (7*SSH_BLOCKSIZE)
1.1 dugsong 64:
65:
66: /* Hash function (Input keys are cipher results) */
1.27 djm 67: #define HASH(x) get_u32(x)
1.1 dugsong 68:
1.13 deraadt 69: #define CMP(a, b) (memcmp(a, b, SSH_BLOCKSIZE))
1.1 dugsong 70:
1.14 itojun 71: static void
1.5 markus 72: crc_update(u_int32_t *a, u_int32_t b)
1.1 dugsong 73: {
1.5 markus 74: b ^= *a;
1.22 deraadt 75: *a = ssh_crc32((u_char *)&b, sizeof(b));
1.1 dugsong 76: }
77:
1.5 markus 78: /* detect if a block is used in a particular pattern */
1.14 itojun 79: static int
1.23 djm 80: check_crc(u_char *S, u_char *buf, u_int32_t len)
1.1 dugsong 81: {
1.5 markus 82: u_int32_t crc;
1.11 markus 83: u_char *c;
1.1 dugsong 84:
1.5 markus 85: crc = 0;
86: for (c = buf; c < buf + len; c += SSH_BLOCKSIZE) {
87: if (!CMP(S, c)) {
88: crc_update(&crc, 1);
89: crc_update(&crc, 0);
90: } else {
91: crc_update(&crc, 0);
92: crc_update(&crc, 0);
93: }
94: }
95: return (crc == 0);
1.1 dugsong 96: }
97:
98:
1.5 markus 99: /* Detect a crc32 compensation attack on a packet */
1.1 dugsong 100: int
1.23 djm 101: detect_attack(u_char *buf, u_int32_t len)
1.1 dugsong 102: {
1.5 markus 103: static u_int16_t *h = (u_int16_t *) NULL;
1.10 markus 104: static u_int32_t n = HASH_MINSIZE / HASH_ENTRYSIZE;
1.15 mpech 105: u_int32_t i, j;
1.30 ! djm 106: u_int32_t l, same;
1.15 mpech 107: u_char *c;
1.11 markus 108: u_char *d;
1.5 markus 109:
110: if (len > (SSH_MAXBLOCKS * SSH_BLOCKSIZE) ||
111: len % SSH_BLOCKSIZE != 0) {
112: fatal("detect_attack: bad length %d", len);
113: }
114: for (l = n; l < HASH_FACTOR(len / SSH_BLOCKSIZE); l = l << 2)
115: ;
1.1 dugsong 116:
1.5 markus 117: if (h == NULL) {
118: debug("Installing crc compensation attack detector.");
1.24 djm 119: h = (u_int16_t *) xcalloc(l, HASH_ENTRYSIZE);
1.5 markus 120: n = l;
121: } else {
122: if (l > n) {
1.25 djm 123: h = (u_int16_t *)xrealloc(h, l, HASH_ENTRYSIZE);
1.5 markus 124: n = l;
125: }
126: }
127:
128: if (len <= HASH_MINBLOCKS) {
129: for (c = buf; c < buf + len; c += SSH_BLOCKSIZE) {
130: for (d = buf; d < c; d += SSH_BLOCKSIZE) {
131: if (!CMP(c, d)) {
1.23 djm 132: if ((check_crc(c, buf, len)))
1.5 markus 133: return (DEATTACK_DETECTED);
134: else
135: break;
136: }
137: }
138: }
139: return (DEATTACK_OK);
140: }
141: memset(h, HASH_UNUSEDCHAR, n * HASH_ENTRYSIZE);
142:
1.30 ! djm 143: for (c = buf, same = j = 0; c < (buf + len); c += SSH_BLOCKSIZE, j++) {
1.5 markus 144: for (i = HASH(c) & (n - 1); h[i] != HASH_UNUSED;
1.17 deraadt 145: i = (i + 1) & (n - 1)) {
1.23 djm 146: if (!CMP(c, buf + h[i] * SSH_BLOCKSIZE)) {
1.30 ! djm 147: if (++same > MAX_IDENTICAL)
! 148: return (DEATTACK_DOS_DETECTED);
1.23 djm 149: if (check_crc(c, buf, len))
1.5 markus 150: return (DEATTACK_DETECTED);
151: else
152: break;
153: }
154: }
155: h[i] = j;
156: }
157: return (DEATTACK_OK);
1.1 dugsong 158: }