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