1 /*
2     ettercap -- dissector for iSCSI CHAP authentication -- TCP 860 3260
3 
4     Copyright (C) Dhiru Kholia (dhiru at openwall.com)
5 
6     Tested with,
7 
8     1. Dell EqualLogic target and Open-iSCSI initiator on CentOS 6.2
9     2. LIO Target and Open-iSCSI initiator, both on Arch Linux 2012
10 
11     This program is free software; you can redistribute it and/or modify
12     it under the terms of the GNU General Public License as published by
13     the Free Software Foundation; either version 2 of the License, or
14     (at your option) any later version.
15 
16     This program is distributed in the hope that it will be useful,
17     but WITHOUT ANY WARRANTY; without even the implied warranty of
18     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19     GNU General Public License for more details.
20 
21     You should have received a copy of the GNU General Public License along
22     with this program; if not, write to the Free Software Foundation, Inc.,
23     51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
24 
25 */
26 
27 #include <ec.h>
28 #include <ec_decode.h>
29 #include <ec_dissect.h>
30 #include <ec_session.h>
31 
32 /* globals */
33 
34 struct iscsi_status {
35    u_char status;
36    u_char id; /* CHAP_I */
37    u_char challenge[49]; /* CHAP_C can be 24 bytes long (48 bytes hex-encoded)  */
38 };
39 
40 #define WAIT_RESPONSE   1
41 
42 /* protos */
43 
44 FUNC_DECODER(dissector_iscsi);
45 void iscsi_init(void);
46 
47 /************************************************/
48 
49 /* borrowed from http://dsss.be/w/c:memmem */
_memmem(unsigned char * haystack,int hlen,char * needle,int nlen)50 static unsigned char *_memmem(unsigned char *haystack, int hlen, char *needle, int nlen)
51 {
52    int i, j = 0;
53    if (nlen > hlen)
54       return 0;
55    switch (nlen) { // we have a few specialized compares for certain needle sizes
56    case 0:     // no needle? just give the haystack
57       return haystack;
58    case 1:     // just use memchr for 1-byte needle
59       return memchr(haystack, needle[0], hlen);
60    case 2:     // use 16-bit compares for 2-byte needles
61       for (i = 0; i < hlen - nlen + 1; i++) {
62          if (*(uint16_t *) (haystack + i) == *(uint16_t *) needle) {
63             return haystack + i;
64          }
65       }
66       break;
67    case 4:     // use 32-bit compares for 4-byte needles
68       for (i = 0; i < hlen - nlen + 1; i++) {
69          if (*(uint32_t *) (haystack + i) == *(uint32_t *) needle) {
70             return haystack + i;
71          }
72       }
73       break;
74    default:    // generic compare for any other needle size
75       // walk i through the haystack, matching j as long as needle[j] matches haystack[i]
76       for (i = 0; i < hlen - nlen + 1; i++) {
77          if (haystack[i] == needle[j]) {
78             if (j == nlen - 1) { // end of needle and it all matched?  win.
79                return haystack + i - j;
80             } else { // keep advancing j (and i, implicitly)
81                j++;
82             }
83          } else { // no match, rewind i the length of the failed match (j), and reset j
84             i -= j;
85             j = 0;
86          }
87       }
88    }
89    return NULL;
90 }
91 
92 /*
93  * this function is the initializer.
94  * it adds the entry in the table of registered decoder
95  */
96 
iscsi_init(void)97 void __init iscsi_init(void)
98 {
99    dissect_add("iscsi", APP_LAYER_TCP, 860, dissector_iscsi);
100    dissect_add("iscsi", APP_LAYER_TCP, 3260, dissector_iscsi);
101 }
102 
FUNC_DECODER(dissector_iscsi)103 FUNC_DECODER(dissector_iscsi)
104 {
105    DECLARE_DISP_PTR_END(ptr, end);
106    struct ec_session *s = NULL;
107    void *ident = NULL;
108    char tmp[MAX_ASCII_ADDR_LEN];
109    struct iscsi_status *conn_status;
110 
111    //suppress unused warning
112    (void)end;
113    (void) DECODE_DATA;
114    (void) DECODE_DATALEN;
115    (void) DECODED_LEN;
116 
117    /* Packets coming from the server */
118    if (FROM_SERVER("iscsi", PACKET)) {
119 
120       /* Interesting packets have len >= 4 */
121       if (PACKET->DATA.len < 4)
122          return NULL;
123 
124       dissect_create_ident(&ident, PACKET, DISSECT_CODE(dissector_iscsi));
125 
126       /* if the session does not exist... */
127       if (session_get(&s, ident, DISSECT_IDENT_LEN) == -E_NOTFOUND) {
128          /* search for CHAP ID and Message Challenge */
129          char *i = (char*)_memmem(ptr, PACKET->DATA.len, "CHAP_I", 6);
130          char *c = (char*)_memmem(ptr, PACKET->DATA.len, "CHAP_C", 6);
131          if (i && c) {
132             /* create the new session */
133             dissect_create_session(&s, PACKET, DISSECT_CODE(dissector_iscsi));
134 
135             /* remember the state (used later) */
136             SAFE_CALLOC(s->data, 1, sizeof(struct iscsi_status));
137 
138             conn_status = (struct iscsi_status *) s->data;
139             conn_status->status = WAIT_RESPONSE;
140             conn_status->id = (u_char)atoi(i + 7);
141 
142             /* CHAP_C is always null-terminated */
143             strncpy((char*)conn_status->challenge, c + 9, 48);
144             conn_status->challenge[48] = 0;
145 
146             /* save the session */
147             session_put(s);
148          }
149       }
150    } else { /* Packets coming from the client */
151       dissect_create_ident(&ident, PACKET, DISSECT_CODE(dissector_iscsi));
152 
153       /* Only if we catched the connection from the beginning */
154       if (session_get(&s, ident, DISSECT_IDENT_LEN) == E_SUCCESS) {
155          conn_status = (struct iscsi_status *) s->data;
156          char *n = NULL;
157          char *r = NULL;
158          if (PACKET->DATA.len > 5) {
159             n = (char*)_memmem(ptr, PACKET->DATA.len, "CHAP_N", 6);
160             r = (char*)_memmem(ptr, PACKET->DATA.len, "CHAP_R", 6);
161          }
162 
163          if (conn_status->status == WAIT_RESPONSE && n && r) {
164             char user[65];
165             char response[33];
166             DEBUG_MSG("\tDissector_iscsi RESPONSE");
167             /* CHAP_R and CHAP_N are always null-terminated */
168             /* Even possibly wrong passwords can be useful,
169              * don't look for login result */
170             strncpy(response, r + 9, 32);
171             response[32] = 0;
172             strncpy(user, n + 7, 64);
173             user[64] = 0;
174             DISSECT_MSG("%s-%s-%d:$chap$%d*%s*%s\n", user, ip_addr_ntoa(&PACKET->L3.dst, tmp),
175                             ntohs(PACKET->L4.dst), conn_status->id, conn_status->challenge, response);
176             dissect_wipe_session(PACKET, DISSECT_CODE(dissector_iscsi));
177          }
178       }
179    }
180 
181    SAFE_FREE(ident);
182    return NULL;
183 }
184 
185 // vim:ts=3:expandtab
186