Annotation of src/usr.bin/ssh/deattack.c, Revision 1.31
1.31 ! markus 1: /* $OpenBSD: deattack.c,v 1.30 2006/09/16 19:53:37 djm 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.31 ! markus 21: #include <sys/param.h>
1.28 stevesk 22: #include <string.h>
1.29 deraadt 23: #include <stdio.h>
1.31 ! markus 24: #include <stdlib.h>
1.18 stevesk 25:
1.1 dugsong 26: #include "deattack.h"
1.3 markus 27: #include "crc32.h"
1.31 ! markus 28: #include "sshbuf.h"
1.27 djm 29: #include "misc.h"
1.1 dugsong 30:
1.30 djm 31: /*
32: * CRC attack detection has a worst-case behaviour that is O(N^3) over
33: * the number of identical blocks in a packet. This behaviour can be
34: * exploited to create a limited denial of service attack.
35: *
36: * However, because we are dealing with encrypted data, identical
37: * blocks should only occur every 2^35 maximally-sized packets or so.
38: * Consequently, we can detect this DoS by looking for identical blocks
39: * in a packet.
40: *
41: * The parameter below determines how many identical blocks we will
42: * accept in a single packet, trading off between attack detection and
43: * likelihood of terminating a legitimate connection. A value of 32
44: * corresponds to an average of 2^40 messages before an attack is
45: * misdetected
46: */
47: #define MAX_IDENTICAL 32
48:
1.1 dugsong 49: /* SSH Constants */
1.5 markus 50: #define SSH_MAXBLOCKS (32 * 1024)
51: #define SSH_BLOCKSIZE (8)
1.1 dugsong 52:
53: /* Hashing constants */
1.5 markus 54: #define HASH_MINSIZE (8 * 1024)
55: #define HASH_ENTRYSIZE (2)
56: #define HASH_FACTOR(x) ((x)*3/2)
57: #define HASH_UNUSEDCHAR (0xff)
58: #define HASH_UNUSED (0xffff)
1.17 deraadt 59: #define HASH_IV (0xfffe)
1.1 dugsong 60:
1.5 markus 61: #define HASH_MINBLOCKS (7*SSH_BLOCKSIZE)
1.1 dugsong 62:
63:
64: /* Hash function (Input keys are cipher results) */
1.31 ! markus 65: #define HASH(x) PEEK_U32(x)
1.1 dugsong 66:
1.13 deraadt 67: #define CMP(a, b) (memcmp(a, b, SSH_BLOCKSIZE))
1.1 dugsong 68:
1.14 itojun 69: static void
1.5 markus 70: crc_update(u_int32_t *a, u_int32_t b)
1.1 dugsong 71: {
1.5 markus 72: b ^= *a;
1.22 deraadt 73: *a = ssh_crc32((u_char *)&b, sizeof(b));
1.1 dugsong 74: }
75:
1.5 markus 76: /* detect if a block is used in a particular pattern */
1.14 itojun 77: static int
1.31 ! markus 78: check_crc(const u_char *S, const u_char *buf, u_int32_t len)
1.1 dugsong 79: {
1.5 markus 80: u_int32_t crc;
1.31 ! markus 81: const u_char *c;
1.1 dugsong 82:
1.5 markus 83: crc = 0;
84: for (c = buf; c < buf + len; c += SSH_BLOCKSIZE) {
85: if (!CMP(S, c)) {
86: crc_update(&crc, 1);
87: crc_update(&crc, 0);
88: } else {
89: crc_update(&crc, 0);
90: crc_update(&crc, 0);
91: }
92: }
1.31 ! markus 93: return crc == 0;
1.1 dugsong 94: }
95:
1.31 ! markus 96: void
! 97: deattack_init(struct deattack_ctx *dctx)
! 98: {
! 99: bzero(dctx, sizeof(*dctx));
! 100: dctx->n = HASH_MINSIZE / HASH_ENTRYSIZE;
! 101: }
1.1 dugsong 102:
1.5 markus 103: /* Detect a crc32 compensation attack on a packet */
1.1 dugsong 104: int
1.31 ! markus 105: detect_attack(struct deattack_ctx *dctx, const u_char *buf, u_int32_t len)
1.1 dugsong 106: {
1.31 ! markus 107: u_int32_t i, j, l, same;
! 108: u_int16_t *tmp;
! 109: const u_char *c, *d;
1.5 markus 110:
111: if (len > (SSH_MAXBLOCKS * SSH_BLOCKSIZE) ||
1.31 ! markus 112: len % SSH_BLOCKSIZE != 0)
! 113: return DEATTACK_ERROR;
! 114: for (l = dctx->n; l < HASH_FACTOR(len / SSH_BLOCKSIZE); l = l << 2)
1.5 markus 115: ;
1.1 dugsong 116:
1.31 ! markus 117: if (dctx->h == NULL) {
! 118: if ((dctx->h = calloc(l, HASH_ENTRYSIZE)) == NULL)
! 119: return DEATTACK_ERROR;
! 120: dctx->n = l;
1.5 markus 121: } else {
1.31 ! markus 122: if (l > dctx->n) {
! 123: if ((tmp = reallocarray(dctx->h, l, HASH_ENTRYSIZE))
! 124: == NULL) {
! 125: free(dctx->h);
! 126: dctx->h = NULL;
! 127: return DEATTACK_ERROR;
! 128: }
! 129: dctx->h = tmp;
! 130: dctx->n = l;
1.5 markus 131: }
132: }
133:
134: if (len <= HASH_MINBLOCKS) {
135: for (c = buf; c < buf + len; c += SSH_BLOCKSIZE) {
136: for (d = buf; d < c; d += SSH_BLOCKSIZE) {
137: if (!CMP(c, d)) {
1.23 djm 138: if ((check_crc(c, buf, len)))
1.31 ! markus 139: return DEATTACK_DETECTED;
1.5 markus 140: else
141: break;
142: }
143: }
144: }
1.31 ! markus 145: return DEATTACK_OK;
1.5 markus 146: }
1.31 ! markus 147: memset(dctx->h, HASH_UNUSEDCHAR, dctx->n * HASH_ENTRYSIZE);
1.5 markus 148:
1.30 djm 149: for (c = buf, same = j = 0; c < (buf + len); c += SSH_BLOCKSIZE, j++) {
1.31 ! markus 150: for (i = HASH(c) & (dctx->n - 1); dctx->h[i] != HASH_UNUSED;
! 151: i = (i + 1) & (dctx->n - 1)) {
! 152: if (!CMP(c, buf + dctx->h[i] * SSH_BLOCKSIZE)) {
1.30 djm 153: if (++same > MAX_IDENTICAL)
1.31 ! markus 154: return DEATTACK_DOS_DETECTED;
1.23 djm 155: if (check_crc(c, buf, len))
1.31 ! markus 156: return DEATTACK_DETECTED;
1.5 markus 157: else
158: break;
159: }
160: }
1.31 ! markus 161: dctx->h[i] = j;
1.5 markus 162: }
1.31 ! markus 163: return DEATTACK_OK;
1.1 dugsong 164: }