[BACK]Return to deattack.c CVS log [TXT][DIR] Up to [local] / src / usr.bin / ssh

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: }