1 /* Copyright (C) 2007-2013 Open Information Security Foundation
2  *
3  * You can copy, redistribute or modify this Program under the terms of
4  * the GNU General Public License version 2 as published by the Free
5  * Software Foundation.
6  *
7  * This program is distributed in the hope that it will be useful,
8  * but WITHOUT ANY WARRANTY; without even the implied warranty of
9  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10  * GNU General Public License for more details.
11  *
12  * You should have received a copy of the GNU General Public License
13  * version 2 along with this program; if not, write to the Free Software
14  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
15  * 02110-1301, USA.
16  */
17 
18 /**
19  * \ingroup decode
20  *
21  * @{
22  */
23 
24 
25 /**
26  * \file
27  *
28  * \author James Riden <jamesr@europe.com>
29  *
30  * PPPOE Decoder
31  */
32 
33 #include "suricata-common.h"
34 
35 #include "packet-queue.h"
36 
37 #include "decode.h"
38 #include "decode-ppp.h"
39 #include "decode-pppoe.h"
40 #include "decode-events.h"
41 
42 #include "flow.h"
43 
44 #include "util-unittest.h"
45 #include "util-debug.h"
46 
47 /**
48  * \brief Main decoding function for PPPOE Discovery packets
49  */
DecodePPPOEDiscovery(ThreadVars * tv,DecodeThreadVars * dtv,Packet * p,const uint8_t * pkt,uint32_t len)50 int DecodePPPOEDiscovery(ThreadVars *tv, DecodeThreadVars *dtv, Packet *p,
51         const uint8_t *pkt, uint32_t len)
52 {
53     StatsIncr(tv, dtv->counter_pppoe);
54 
55     if (len < PPPOE_DISCOVERY_HEADER_MIN_LEN) {
56         ENGINE_SET_INVALID_EVENT(p, PPPOE_PKT_TOO_SMALL);
57         return TM_ECODE_FAILED;
58     }
59 
60     p->pppoedh = (PPPOEDiscoveryHdr *)pkt;
61     if (p->pppoedh == NULL)
62         return TM_ECODE_FAILED;
63 
64     /* parse the PPPOE code */
65     switch (p->pppoedh->pppoe_code)
66     {
67         case  PPPOE_CODE_PADI:
68             break;
69         case  PPPOE_CODE_PADO:
70             break;
71         case  PPPOE_CODE_PADR:
72             break;
73         case PPPOE_CODE_PADS:
74             break;
75         case PPPOE_CODE_PADT:
76             break;
77         default:
78             SCLogDebug("unknown PPPOE code: 0x%0"PRIX8"", p->pppoedh->pppoe_code);
79             ENGINE_SET_INVALID_EVENT(p, PPPOE_WRONG_CODE);
80             return TM_ECODE_OK;
81     }
82 
83     /* parse any tags we have in the packet */
84 
85     uint32_t tag_length = 0;
86     PPPOEDiscoveryTag* pppoedt = (PPPOEDiscoveryTag*) (p->pppoedh +  PPPOE_DISCOVERY_HEADER_MIN_LEN);
87 
88     uint32_t pppoe_length = SCNtohs(p->pppoedh->pppoe_length);
89     uint32_t packet_length = len - PPPOE_DISCOVERY_HEADER_MIN_LEN ;
90 
91     SCLogDebug("pppoe_length %"PRIu32", packet_length %"PRIu32"",
92         pppoe_length, packet_length);
93 
94     if (pppoe_length > packet_length) {
95         SCLogDebug("malformed PPPOE tags");
96         ENGINE_SET_INVALID_EVENT(p, PPPOE_MALFORMED_TAGS);
97         return TM_ECODE_OK;
98     }
99 
100     while (pppoedt < (PPPOEDiscoveryTag*) (pkt + (len - sizeof(PPPOEDiscoveryTag))) && pppoe_length >=4 && packet_length >=4)
101     {
102 #ifdef DEBUG
103         uint16_t tag_type = SCNtohs(pppoedt->pppoe_tag_type);
104 #endif
105         tag_length = SCNtohs(pppoedt->pppoe_tag_length);
106 
107         SCLogDebug ("PPPoE Tag type %x, length %"PRIu32, tag_type, tag_length);
108 
109         if (pppoe_length >= (4 + tag_length)) {
110             pppoe_length -= (4 + tag_length);
111         } else {
112             pppoe_length = 0; // don't want an underflow
113         }
114 
115         if (packet_length >= 4 + tag_length) {
116             packet_length -= (4 + tag_length);
117         } else {
118             packet_length = 0; // don't want an underflow
119         }
120 
121         pppoedt = pppoedt + (4 + tag_length);
122     }
123 
124     return TM_ECODE_OK;
125 }
126 
127 /**
128  * \brief Main decoding function for PPPOE Session packets
129  */
DecodePPPOESession(ThreadVars * tv,DecodeThreadVars * dtv,Packet * p,const uint8_t * pkt,uint32_t len)130 int DecodePPPOESession(ThreadVars *tv, DecodeThreadVars *dtv, Packet *p,
131         const uint8_t *pkt, uint32_t len)
132 {
133     StatsIncr(tv, dtv->counter_pppoe);
134 
135     if (len < PPPOE_SESSION_HEADER_LEN) {
136         ENGINE_SET_INVALID_EVENT(p, PPPOE_PKT_TOO_SMALL);
137         return TM_ECODE_FAILED;
138     }
139 
140     p->pppoesh = (PPPOESessionHdr *)pkt;
141     if (p->pppoesh == NULL)
142         return TM_ECODE_FAILED;
143 
144     SCLogDebug("PPPOE VERSION %" PRIu32 " TYPE %" PRIu32 " CODE %" PRIu32 " SESSIONID %" PRIu32 " LENGTH %" PRIu32 "",
145            PPPOE_SESSION_GET_VERSION(p->pppoesh),  PPPOE_SESSION_GET_TYPE(p->pppoesh),  p->pppoesh->pppoe_code,  SCNtohs(p->pppoesh->session_id),  SCNtohs(p->pppoesh->pppoe_length));
146 
147     /* can't use DecodePPP() here because we only get a single 2-byte word to indicate protocol instead of the full PPP header */
148 
149     if (SCNtohs(p->pppoesh->pppoe_length) > 0) {
150         /* decode contained PPP packet */
151 
152         switch (SCNtohs(p->pppoesh->protocol))
153         {
154             case PPP_VJ_COMP:
155             case PPP_IPX:
156             case PPP_OSI:
157             case PPP_NS:
158             case PPP_DECNET:
159             case PPP_APPLE:
160             case PPP_BRPDU:
161             case PPP_STII:
162             case PPP_VINES:
163             case PPP_HELLO:
164             case PPP_LUXCOM:
165             case PPP_SNS:
166             case PPP_MPLS_UCAST:
167             case PPP_MPLS_MCAST:
168             case PPP_IPCP:
169             case PPP_OSICP:
170             case PPP_NSCP:
171             case PPP_DECNETCP:
172             case PPP_APPLECP:
173             case PPP_IPXCP:
174             case PPP_STIICP:
175             case PPP_VINESCP:
176             case PPP_IPV6CP:
177             case PPP_MPLSCP:
178             case PPP_LCP:
179             case PPP_PAP:
180             case PPP_LQM:
181             case PPP_CHAP:
182                 ENGINE_SET_EVENT(p,PPP_UNSUP_PROTO);
183                 break;
184 
185             case PPP_VJ_UCOMP:
186 
187                 if(len < (PPPOE_SESSION_HEADER_LEN + IPV4_HEADER_LEN))    {
188                     ENGINE_SET_INVALID_EVENT(p, PPPVJU_PKT_TOO_SMALL);
189                     return TM_ECODE_OK;
190                 }
191                 if (unlikely(len > PPPOE_SESSION_HEADER_LEN + USHRT_MAX)) {
192                     return TM_ECODE_FAILED;
193                 }
194 
195                 if(IPV4_GET_RAW_VER((IPV4Hdr *)(pkt + PPPOE_SESSION_HEADER_LEN)) == 4) {
196                     DecodeIPV4(tv, dtv, p, pkt + PPPOE_SESSION_HEADER_LEN, len - PPPOE_SESSION_HEADER_LEN);
197                 }
198                 break;
199 
200             case PPP_IP:
201                 if(len < (PPPOE_SESSION_HEADER_LEN + IPV4_HEADER_LEN))    {
202                     ENGINE_SET_INVALID_EVENT(p, PPPIPV4_PKT_TOO_SMALL);
203                     return TM_ECODE_OK;
204                 }
205                 if (unlikely(len > PPPOE_SESSION_HEADER_LEN + USHRT_MAX)) {
206                     return TM_ECODE_FAILED;
207                 }
208 
209                 DecodeIPV4(tv, dtv, p, pkt + PPPOE_SESSION_HEADER_LEN, len - PPPOE_SESSION_HEADER_LEN);
210                 break;
211 
212             /* PPP IPv6 was not tested */
213             case PPP_IPV6:
214                 if(len < (PPPOE_SESSION_HEADER_LEN + IPV6_HEADER_LEN))    {
215                     ENGINE_SET_INVALID_EVENT(p, PPPIPV6_PKT_TOO_SMALL);
216                     return TM_ECODE_OK;
217                 }
218                 if (unlikely(len > PPPOE_SESSION_HEADER_LEN + USHRT_MAX)) {
219                     return TM_ECODE_FAILED;
220                 }
221 
222                 DecodeIPV6(tv, dtv, p, pkt + PPPOE_SESSION_HEADER_LEN, len - PPPOE_SESSION_HEADER_LEN);
223                 break;
224 
225             default:
226                 SCLogDebug("unknown PPP protocol: %" PRIx32 "",SCNtohs(p->pppoesh->protocol));
227                 ENGINE_SET_INVALID_EVENT(p, PPP_WRONG_TYPE);
228                 return TM_ECODE_OK;
229         }
230     }
231     return TM_ECODE_OK;
232 }
233 
234 #ifdef UNITTESTS
235 /** DecodePPPOEtest01
236  *  \brief Decode malformed PPPOE packet (too short)
237  *  \retval 1 Expected test value
238  */
DecodePPPOEtest01(void)239 static int DecodePPPOEtest01 (void)
240 {
241 
242     uint8_t raw_pppoe[] = { 0x11, 0x00, 0x00, 0x00, 0x00 };
243     Packet *p = PacketGetFromAlloc();
244     if (unlikely(p == NULL))
245         return 0;
246     ThreadVars tv;
247     DecodeThreadVars dtv;
248 
249     memset(&tv, 0, sizeof(ThreadVars));
250     memset(&dtv, 0, sizeof(DecodeThreadVars));
251 
252     DecodePPPOESession(&tv, &dtv, p, raw_pppoe, sizeof(raw_pppoe));
253 
254     if (ENGINE_ISSET_EVENT(p,PPPOE_PKT_TOO_SMALL))  {
255         SCFree(p);
256         return 1;
257     }
258 
259     SCFree(p);
260     return 0;
261 }
262 
263 /** DecodePPPOEtest02
264  *  \brief Valid PPPOE packet - check the invalid ICMP type encapsulated is flagged
265  *  \retval 0 Expected test value
266  */
DecodePPPOEtest02(void)267 static int DecodePPPOEtest02 (void)
268 {
269 
270     uint8_t raw_pppoe[] = {
271         0x11, 0x00, 0x00, 0x01, 0x00, 0x40, 0x00, 0x21,
272         0x45, 0x00, 0x00, 0x3c, 0x05, 0x5c, 0x00, 0x00,
273         0x20, 0x01, 0xff, 0x30, 0xc0, 0xa8, 0x0a, 0x7f,
274         0xc0, 0xa8, 0x0a, 0x65, 0xab, 0xcd, 0x16, 0x5e,
275         0x02, 0x00, 0x37, 0x00, 0x41, 0x42, 0x43, 0x44,
276         0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c,
277         0x4d, 0x4e, 0x4f, 0x50, 0x51, 0x52, 0x53, 0x54,
278         0x55, 0x56, 0x57, 0x41, 0x42, 0x43, 0x44, 0x45,
279         0x46, 0x47, 0x48, 0x49 };
280 
281     Packet *p = PacketGetFromAlloc();
282     if (unlikely(p == NULL))
283         return 0;
284     ThreadVars tv;
285     DecodeThreadVars dtv;
286     int ret = 0;
287 
288     memset(&tv, 0, sizeof(ThreadVars));
289     memset(&dtv, 0, sizeof(DecodeThreadVars));
290 
291     FlowInitConfig(FLOW_QUIET);
292 
293     DecodePPPOESession(&tv, &dtv, p, raw_pppoe, sizeof(raw_pppoe));
294 
295     if(ENGINE_ISSET_EVENT(p,PPPOE_PKT_TOO_SMALL))  {
296         goto end;
297     }
298 
299     // and we insist that the invalid ICMP encapsulated (type 0xab, code 0xcd) is flagged
300 
301     if(! ENGINE_ISSET_EVENT(p,ICMPV4_UNKNOWN_TYPE))  {
302         goto end;
303     }
304 
305     ret = 1;
306 end:
307     FlowShutdown();
308     SCFree(p);
309     return ret;
310 }
311 
312 
313 /** DecodePPPOEtest03
314  *  \brief Valid example PADO packet PPPOE packet taken from RFC2516
315  *  \retval 0 Expected test value
316  */
DecodePPPOEtest03(void)317 static int DecodePPPOEtest03 (void)
318 {
319 
320     /* example PADO packet taken from RFC2516 */
321     uint8_t raw_pppoe[] = {
322         0x11, 0x07, 0x00, 0x00, 0x00, 0x20, 0x01, 0x01,
323         0x00, 0x00, 0x01, 0x02, 0x00, 0x18, 0x47, 0x6f,
324         0x20, 0x52, 0x65, 0x64, 0x42, 0x61, 0x63, 0x6b,
325         0x20, 0x2d, 0x20, 0x65, 0x73, 0x68, 0x73, 0x68,
326         0x65, 0x73, 0x68, 0x6f, 0x6f, 0x74
327     };
328 
329     Packet *p = PacketGetFromAlloc();
330     if (unlikely(p == NULL))
331         return 0;
332     ThreadVars tv;
333     DecodeThreadVars dtv;
334 
335     memset(&tv, 0, sizeof(ThreadVars));
336     memset(&dtv, 0, sizeof(DecodeThreadVars));
337 
338     DecodePPPOEDiscovery(&tv, &dtv, p, raw_pppoe, sizeof(raw_pppoe));
339     if (p->pppoedh == NULL) {
340     SCFree(p);
341     return 0;
342     }
343 
344     SCFree(p);
345     return 1;
346 }
347 
348 /** DecodePPPOEtest04
349  *  \brief Valid example PPPOE packet taken from RFC2516 - but with wrong PPPOE code
350  *  \retval 1 Expected test value
351  */
DecodePPPOEtest04(void)352 static int DecodePPPOEtest04 (void)
353 {
354 
355     /* example PADI packet taken from RFC2516, but with wrong code */
356     uint8_t raw_pppoe[] = {
357         0x11, 0xbb, 0x00, 0x00, 0x00, 0x04, 0x01, 0x01,
358         0x00, 0x00
359     };
360 
361     Packet *p = PacketGetFromAlloc();
362     if (unlikely(p == NULL))
363         return 0;
364     ThreadVars tv;
365     DecodeThreadVars dtv;
366 
367     memset(&tv, 0, sizeof(ThreadVars));
368     memset(&dtv, 0, sizeof(DecodeThreadVars));
369 
370     DecodePPPOEDiscovery(&tv, &dtv, p, raw_pppoe, sizeof(raw_pppoe));
371 
372     if(ENGINE_ISSET_EVENT(p,PPPOE_WRONG_CODE))  {
373         SCFree(p);
374         return 1;
375     }
376 
377     SCFree(p);
378     return 0;
379 }
380 
381 /** DecodePPPOEtest05
382  *  \brief Valid exaple PADO PPPOE packet taken from RFC2516, but too short for given length
383  *  \retval 0 Expected test value
384  */
DecodePPPOEtest05(void)385 static int DecodePPPOEtest05 (void)
386 {
387 
388     /* example PADI packet taken from RFC2516 */
389     uint8_t raw_pppoe[] = {
390         0x11, 0x07, 0x00, 0x00, 0x00, 0x20, 0x01, 0x01,
391         0x00, 0x00, 0x01, 0x02, 0x00, 0x18, 0x47, 0x6f,
392         0x20, 0x52, 0x65, 0x64, 0x42, 0x61, 0x63, 0x6b,
393         0x20, 0x2d, 0x20, 0x65, 0x73, 0x68, 0x73, 0x68
394     };
395 
396     Packet *p = PacketGetFromAlloc();
397     if (unlikely(p == NULL))
398         return 0;
399     ThreadVars tv;
400     DecodeThreadVars dtv;
401 
402     memset(&tv, 0, sizeof(ThreadVars));
403     memset(&dtv, 0, sizeof(DecodeThreadVars));
404 
405     DecodePPPOEDiscovery(&tv, &dtv, p, raw_pppoe, sizeof(raw_pppoe));
406 
407     if(ENGINE_ISSET_EVENT(p,PPPOE_MALFORMED_TAGS))  {
408         SCFree(p);
409         return 1;
410     }
411 
412     SCFree(p);
413     return 0;
414 }
415 
416 /** DecodePPPOEtest06
417  *  \brief Check that the macros work as expected. Type and version are
418  * fields of 4 bits length. So they are sharing the same var and the macros
419  * should extract the first 4 bits for version and the second 4 bits for type
420  *  \retval 1 Expected test value
421  */
DecodePPPOEtest06(void)422 static int DecodePPPOEtest06 (void)
423 {
424 
425     PPPOESessionHdr pppoesh;
426     PPPOEDiscoveryHdr pppoedh;
427     pppoesh.pppoe_version_type = 0xAB;
428     pppoedh.pppoe_version_type = 0xCD;
429 
430     if (PPPOE_SESSION_GET_VERSION(&pppoesh) != 0x0A) {
431         printf("Error, PPPOE macro pppoe_session_get_version failed: ");
432         return 0;
433     }
434     if (PPPOE_SESSION_GET_TYPE(&pppoesh) != 0x0B) {
435         printf("Error, PPPOE macro pppoe_session_get_type failed: ");
436         return 0;
437     }
438     if (PPPOE_DISCOVERY_GET_VERSION(&pppoedh) != 0x0C) {
439         printf("Error, PPPOE macro pppoe_discovery_get_version failed: ");
440         return 0;
441     }
442     if (PPPOE_DISCOVERY_GET_TYPE(&pppoedh) != 0x0D) {
443         printf("Error, PPPOE macro pppoe_discovery_get_type failed: ");
444         return 0;
445     }
446 
447     return 1;
448 }
449 #endif /* UNITTESTS */
450 
451 
452 
453 /**
454  * \brief Registers PPPOE unit tests
455  * \todo More PPPOE tests
456  */
DecodePPPOERegisterTests(void)457 void DecodePPPOERegisterTests(void)
458 {
459 #ifdef UNITTESTS
460     UtRegisterTest("DecodePPPOEtest01", DecodePPPOEtest01);
461     UtRegisterTest("DecodePPPOEtest02", DecodePPPOEtest02);
462     UtRegisterTest("DecodePPPOEtest03", DecodePPPOEtest03);
463     UtRegisterTest("DecodePPPOEtest04", DecodePPPOEtest04);
464     UtRegisterTest("DecodePPPOEtest05", DecodePPPOEtest05);
465     UtRegisterTest("DecodePPPOEtest06", DecodePPPOEtest06);
466 #endif /* UNITTESTS */
467 }
468 
469 /**
470  * @}
471  */
472