1 /* Copyright (C) 2020 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  * \file
20  *
21  * \author Jeff Lucovsky <jeff@lucovsky.org>
22  *
23  * Implements the dotprefix transformation
24  */
25 
26 #include "suricata-common.h"
27 
28 #include "detect.h"
29 #include "detect-engine.h"
30 #include "detect-engine-prefilter.h"
31 #include "detect-parse.h"
32 #include "detect-transform-dotprefix.h"
33 
34 #include "util-unittest.h"
35 #include "util-print.h"
36 #include "util-memrchr.h"
37 #include "util-memcpy.h"
38 
39 static int DetectTransformDotPrefixSetup (DetectEngineCtx *, Signature *, const char *);
40 #ifdef UNITTESTS
41 static void DetectTransformDotPrefixRegisterTests(void);
42 #endif
43 static void TransformDotPrefix(InspectionBuffer *buffer, void *options);
44 
DetectTransformDotPrefixRegister(void)45 void DetectTransformDotPrefixRegister(void)
46 {
47     sigmatch_table[DETECT_TRANSFORM_DOTPREFIX].name = "dotprefix";
48     sigmatch_table[DETECT_TRANSFORM_DOTPREFIX].desc =
49         "modify buffer to extract the dotprefix";
50     sigmatch_table[DETECT_TRANSFORM_DOTPREFIX].url =
51         "/rules/transforms.html#dotprefix";
52     sigmatch_table[DETECT_TRANSFORM_DOTPREFIX].Transform = TransformDotPrefix;
53     sigmatch_table[DETECT_TRANSFORM_DOTPREFIX].Setup = DetectTransformDotPrefixSetup;
54 #ifdef UNITTESTS
55     sigmatch_table[DETECT_TRANSFORM_DOTPREFIX].RegisterTests =
56         DetectTransformDotPrefixRegisterTests;
57 #endif
58     sigmatch_table[DETECT_TRANSFORM_DOTPREFIX].flags |= SIGMATCH_NOOPT;
59 }
60 
61 /**
62  *  \internal
63  *  \brief Extract the dotprefix, if any, the last pattern match, either content or uricontent
64  *  \param det_ctx detection engine ctx
65  *  \param s signature
66  *  \param nullstr should be null
67  *  \retval 0 ok
68  *  \retval -1 failure
69  */
DetectTransformDotPrefixSetup(DetectEngineCtx * de_ctx,Signature * s,const char * nullstr)70 static int DetectTransformDotPrefixSetup (DetectEngineCtx *de_ctx, Signature *s, const char *nullstr)
71 {
72     SCEnter();
73     int r = DetectSignatureAddTransform(s, DETECT_TRANSFORM_DOTPREFIX, NULL);
74     SCReturnInt(r);
75 }
76 
77 /**
78  * \brief Return the dotprefix, if any, in the last pattern match.
79  *
80  * Input values are modified by prefixing with a ".".
81  *
82  * Rule: "alert dns any any -> any any (dns_query; dotprefix; content:".google.com"; sid:1;)"
83  * 1. hello.google.com --> match
84  * 2. hey.agoogle.com --> no match
85  * 3. agoogle.com --> no match
86  * 4. something.google.com.au --> match
87  * 5. google.com --> match
88  *
89  * To match on the dotprefix only:
90  * Rule: "alert dns any any -> any any (dns_query; dotprefix; content:".google.com"; endswith; sid:1;)"
91  *
92  * 1. hello.google.com --> match
93  * 2. hey.agoogle.com --> no match
94  * 3. agoogle.com --> no match
95  * 4. something.google.com.au --> no match
96  * 5. google.com --> match
97  *
98  * To match on a TLD:
99  * Rule: "alert dns any any -> any any (dns_query; dotprefix; content:".co.uk"; endswith; sid:1;)"
100  *
101  * 1. hello.google.com --> no match
102  * 2. hey.agoogle.com --> no match
103  * 3. agoogle.com --> no match
104  * 4. something.google.co.uk --> match
105  * 5. google.com --> no match
106  */
TransformDotPrefix(InspectionBuffer * buffer,void * options)107 static void TransformDotPrefix(InspectionBuffer *buffer, void *options)
108 {
109     const size_t input_len = buffer->inspect_len;
110 
111     if (input_len) {
112         uint8_t output[input_len + 1]; // For the leading '.'
113 
114         output[0] = '.';
115         memcpy(&output[1], buffer->inspect, input_len);
116         InspectionBufferCopy(buffer, output, input_len + 1);
117     }
118 }
119 
120 #ifdef UNITTESTS
DetectTransformDotPrefixTest01(void)121 static int DetectTransformDotPrefixTest01(void)
122 {
123     const uint8_t *input = (const uint8_t *)"example.com";
124     uint32_t input_len = strlen((char *)input);
125 
126     const char *result = ".example.com";
127     uint32_t result_len = strlen((char *)result);
128 
129     InspectionBuffer buffer;
130     InspectionBufferInit(&buffer, input_len);
131     InspectionBufferSetup(NULL, -1, &buffer, input, input_len);
132     PrintRawDataFp(stdout, buffer.inspect, buffer.inspect_len);
133     TransformDotPrefix(&buffer, NULL);
134     PrintRawDataFp(stdout, buffer.inspect, buffer.inspect_len);
135     FAIL_IF_NOT(buffer.inspect_len == result_len);
136     FAIL_IF_NOT(strncmp(result, (const char *)buffer.inspect, result_len) == 0);
137     InspectionBufferFree(&buffer);
138     PASS;
139 }
140 
DetectTransformDotPrefixTest02(void)141 static int DetectTransformDotPrefixTest02(void)
142 {
143     const uint8_t *input = (const uint8_t *)"hello.example.com";
144     uint32_t input_len = strlen((char *)input);
145 
146     const char *result = ".hello.example.com";
147     uint32_t result_len = strlen((char *)result);
148 
149     InspectionBuffer buffer;
150     InspectionBufferInit(&buffer, input_len);
151     InspectionBufferSetup(NULL, -1, &buffer, input, input_len);
152     PrintRawDataFp(stdout, buffer.inspect, buffer.inspect_len);
153     TransformDotPrefix(&buffer, NULL);
154     PrintRawDataFp(stdout, buffer.inspect, buffer.inspect_len);
155     FAIL_IF_NOT(buffer.inspect_len == result_len);
156     FAIL_IF_NOT(strncmp(result, (const char *)buffer.inspect, result_len) == 0);
157     InspectionBufferFree(&buffer);
158     PASS;
159 }
160 
DetectTransformDotPrefixTest03(void)161 static int DetectTransformDotPrefixTest03(void)
162 {
163     const char rule[] = "alert dns any any -> any any (dns.query; dotprefix; content:\".google.com\"; sid:1;)";
164     ThreadVars th_v;
165     DetectEngineThreadCtx *det_ctx = NULL;
166     memset(&th_v, 0, sizeof(th_v));
167 
168     DetectEngineCtx *de_ctx = DetectEngineCtxInit();
169     FAIL_IF_NULL(de_ctx);
170     Signature *s = DetectEngineAppendSig(de_ctx, rule);
171     FAIL_IF_NULL(s);
172     SigGroupBuild(de_ctx);
173     DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
174     DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx);
175     DetectEngineCtxFree(de_ctx);
176     PASS;
177 }
178 
DetectTransformDotPrefixRegisterTests(void)179 static void DetectTransformDotPrefixRegisterTests(void)
180 {
181     UtRegisterTest("DetectTransformDotPrefixTest01", DetectTransformDotPrefixTest01);
182     UtRegisterTest("DetectTransformDotPrefixTest02", DetectTransformDotPrefixTest02);
183     UtRegisterTest("DetectTransformDotPrefixTest03", DetectTransformDotPrefixTest03);
184 }
185 #endif
186