1 /* Copyright (C) 2018-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 Pierre Chifflier <chifflier@wzdftpd.net>
22 */
23
24 #include "suricata-common.h"
25 #include "util-unittest.h"
26 #include "util-byte.h"
27
28 #include "detect-parse.h"
29 #include "detect-engine.h"
30
31 #include "detect-krb5-errcode.h"
32
33 #include "app-layer-krb5.h"
34 #include "rust.h"
35
36 /**
37 * \brief Regex for parsing our keyword options
38 */
39 #define PARSE_REGEX "^\\s*([A-z0-9\\.]+|\"[A-z0-9_\\.]+\")\\s*$"
40 static DetectParseRegex parse_regex;
41
42 /* Prototypes of functions registered in DetectKrb5ErrCodeRegister below */
43 static int DetectKrb5ErrCodeMatch (DetectEngineThreadCtx *, Flow *,
44 uint8_t, void *, void *, const Signature *,
45 const SigMatchCtx *);
46 static int DetectKrb5ErrCodeSetup (DetectEngineCtx *, Signature *, const char *);
47 static void DetectKrb5ErrCodeFree (DetectEngineCtx *, void *);
48 #ifdef UNITTESTS
49 static void DetectKrb5ErrCodeRegisterTests (void);
50 #endif
51
52 static int DetectEngineInspectKRB5Generic(ThreadVars *tv,
53 DetectEngineCtx *de_ctx, DetectEngineThreadCtx *det_ctx,
54 const Signature *s, const SigMatchData *smd,
55 Flow *f, uint8_t flags, void *alstate,
56 void *txv, uint64_t tx_id);
57
58 static int g_krb5_err_code_list_id = 0;
59
60 /**
61 * \brief Registration function for krb5_err_code: keyword
62 *
63 * This function is called once in the 'lifetime' of the engine.
64 */
DetectKrb5ErrCodeRegister(void)65 void DetectKrb5ErrCodeRegister(void)
66 {
67 sigmatch_table[DETECT_AL_KRB5_ERRCODE].name = "krb5_err_code";
68 sigmatch_table[DETECT_AL_KRB5_ERRCODE].desc = "match Kerberos 5 error code";
69 sigmatch_table[DETECT_AL_KRB5_ERRCODE].url = "/rules/kerberos-keywords.html#krb5-err-code";
70 sigmatch_table[DETECT_AL_KRB5_ERRCODE].Match = NULL;
71 sigmatch_table[DETECT_AL_KRB5_ERRCODE].AppLayerTxMatch = DetectKrb5ErrCodeMatch;
72 sigmatch_table[DETECT_AL_KRB5_ERRCODE].Setup = DetectKrb5ErrCodeSetup;
73 sigmatch_table[DETECT_AL_KRB5_ERRCODE].Free = DetectKrb5ErrCodeFree;
74 #ifdef UNITTESTS
75 sigmatch_table[DETECT_AL_KRB5_ERRCODE].RegisterTests = DetectKrb5ErrCodeRegisterTests;
76 #endif
77 DetectAppLayerInspectEngineRegister("krb5_err_code",
78 ALPROTO_KRB5, SIG_FLAG_TOSERVER, 0,
79 DetectEngineInspectKRB5Generic);
80
81 DetectAppLayerInspectEngineRegister("krb5_err_code",
82 ALPROTO_KRB5, SIG_FLAG_TOCLIENT, 0,
83 DetectEngineInspectKRB5Generic);
84
85 /* set up the PCRE for keyword parsing */
86 DetectSetupParseRegexes(PARSE_REGEX, &parse_regex);
87
88 g_krb5_err_code_list_id = DetectBufferTypeRegister("krb5_err_code");
89 SCLogDebug("g_krb5_err_code_list_id %d", g_krb5_err_code_list_id);
90 }
91
DetectEngineInspectKRB5Generic(ThreadVars * tv,DetectEngineCtx * de_ctx,DetectEngineThreadCtx * det_ctx,const Signature * s,const SigMatchData * smd,Flow * f,uint8_t flags,void * alstate,void * txv,uint64_t tx_id)92 static int DetectEngineInspectKRB5Generic(ThreadVars *tv,
93 DetectEngineCtx *de_ctx, DetectEngineThreadCtx *det_ctx,
94 const Signature *s, const SigMatchData *smd,
95 Flow *f, uint8_t flags, void *alstate,
96 void *txv, uint64_t tx_id)
97 {
98 return DetectEngineInspectGenericList(tv, de_ctx, det_ctx, s, smd,
99 f, flags, alstate, txv, tx_id);
100 }
101
102 /**
103 * \brief This function is used to match KRB5 rule option on a packet
104 *
105 * \param t pointer to thread vars
106 * \param det_ctx pointer to the pattern matcher thread
107 * \param p pointer to the current packet
108 * \param m pointer to the sigmatch with context that we will cast into DetectKrb5Data
109 *
110 * \retval 0 no match
111 * \retval 1 match
112 */
DetectKrb5ErrCodeMatch(DetectEngineThreadCtx * det_ctx,Flow * f,uint8_t flags,void * state,void * txv,const Signature * s,const SigMatchCtx * ctx)113 static int DetectKrb5ErrCodeMatch (DetectEngineThreadCtx *det_ctx,
114 Flow *f, uint8_t flags, void *state,
115 void *txv, const Signature *s,
116 const SigMatchCtx *ctx)
117 {
118 int32_t err_code;
119 int ret;
120 const DetectKrb5ErrCodeData *dd = (const DetectKrb5ErrCodeData *)ctx;
121
122 SCEnter();
123
124 ret = rs_krb5_tx_get_errcode(txv, &err_code);
125 if (ret != 0)
126 SCReturnInt(0);
127
128 if (dd->err_code == err_code)
129 SCReturnInt(1);
130
131 SCReturnInt(0);
132 }
133
134 /**
135 * \brief This function is used to parse options passed via krb5_errcode: keyword
136 *
137 * \param krb5str Pointer to the user provided krb5_err_code options
138 *
139 * \retval krb5d pointer to DetectKrb5Data on success
140 * \retval NULL on failure
141 */
DetectKrb5ErrCodeParse(const char * krb5str)142 static DetectKrb5ErrCodeData *DetectKrb5ErrCodeParse (const char *krb5str)
143 {
144 DetectKrb5ErrCodeData *krb5d = NULL;
145 char arg1[4] = "";
146 int ret = 0, res = 0;
147 int ov[MAX_SUBSTRINGS];
148
149 ret = DetectParsePcreExec(&parse_regex, krb5str, 0, 0, ov, MAX_SUBSTRINGS);
150 if (ret != 2) {
151 SCLogError(SC_ERR_PCRE_MATCH, "parse error, ret %" PRId32 "", ret);
152 goto error;
153 }
154
155 res = pcre_copy_substring((char *) krb5str, ov, MAX_SUBSTRINGS, 1, arg1, sizeof(arg1));
156 if (res < 0) {
157 SCLogError(SC_ERR_PCRE_GET_SUBSTRING, "pcre_copy_substring failed");
158 goto error;
159 }
160
161 krb5d = SCMalloc(sizeof (DetectKrb5ErrCodeData));
162 if (unlikely(krb5d == NULL))
163 goto error;
164 if (StringParseInt32(&krb5d->err_code, 10, 0,
165 (const char *)arg1) < 0) {
166 goto error;
167 }
168 return krb5d;
169
170 error:
171 if (krb5d)
172 SCFree(krb5d);
173 return NULL;
174 }
175
176 /**
177 * \brief parse the options from the 'krb5_err_code' keyword in the rule into
178 * the Signature data structure.
179 *
180 * \param de_ctx pointer to the Detection Engine Context
181 * \param s pointer to the Current Signature
182 * \param krb5str pointer to the user provided options
183 *
184 * \retval 0 on Success
185 * \retval -1 on Failure
186 */
DetectKrb5ErrCodeSetup(DetectEngineCtx * de_ctx,Signature * s,const char * krb5str)187 static int DetectKrb5ErrCodeSetup (DetectEngineCtx *de_ctx, Signature *s, const char *krb5str)
188 {
189 DetectKrb5ErrCodeData *krb5d = NULL;
190 SigMatch *sm = NULL;
191
192 if (DetectSignatureSetAppProto(s, ALPROTO_KRB5) != 0)
193 return -1;
194
195 krb5d = DetectKrb5ErrCodeParse(krb5str);
196 if (krb5d == NULL)
197 goto error;
198
199 sm = SigMatchAlloc();
200 if (sm == NULL)
201 goto error;
202
203 sm->type = DETECT_AL_KRB5_ERRCODE;
204 sm->ctx = (void *)krb5d;
205
206 SigMatchAppendSMToList(s, sm, g_krb5_err_code_list_id);
207
208 return 0;
209
210 error:
211 if (krb5d != NULL)
212 DetectKrb5ErrCodeFree(de_ctx, krb5d);
213 if (sm != NULL)
214 SCFree(sm);
215 return -1;
216 }
217
218 /**
219 * \brief this function will free memory associated with DetectKrb5Data
220 *
221 * \param ptr pointer to DetectKrb5Data
222 */
DetectKrb5ErrCodeFree(DetectEngineCtx * de_ctx,void * ptr)223 static void DetectKrb5ErrCodeFree(DetectEngineCtx *de_ctx, void *ptr) {
224 DetectKrb5ErrCodeData *krb5d = (DetectKrb5ErrCodeData *)ptr;
225
226 SCFree(krb5d);
227 }
228
229 #ifdef UNITTESTS
230 /**
231 * \test description of the test
232 */
233
DetectKrb5ErrCodeParseTest01(void)234 static int DetectKrb5ErrCodeParseTest01 (void)
235 {
236 DetectKrb5ErrCodeData *krb5d = DetectKrb5ErrCodeParse("10");
237 FAIL_IF_NULL(krb5d);
238 FAIL_IF(!(krb5d->err_code == 10));
239 DetectKrb5ErrCodeFree(NULL, krb5d);
240 PASS;
241 }
242
DetectKrb5ErrCodeSignatureTest01(void)243 static int DetectKrb5ErrCodeSignatureTest01 (void)
244 {
245 DetectEngineCtx *de_ctx = DetectEngineCtxInit();
246 FAIL_IF_NULL(de_ctx);
247
248 Signature *sig = DetectEngineAppendSig(de_ctx, "alert krb5 any any -> any any (krb5_err_code:10; sid:1; rev:1;)");
249 FAIL_IF_NULL(sig);
250
251 DetectEngineCtxFree(de_ctx);
252 PASS;
253 }
254
255 /**
256 * \brief this function registers unit tests for DetectKrb5ErrCode
257 */
DetectKrb5ErrCodeRegisterTests(void)258 static void DetectKrb5ErrCodeRegisterTests(void)
259 {
260 UtRegisterTest("DetectKrb5ErrCodeParseTest01", DetectKrb5ErrCodeParseTest01);
261 UtRegisterTest("DetectKrb5ErrCodeSignatureTest01",
262 DetectKrb5ErrCodeSignatureTest01);
263 }
264 #endif /* UNITTESTS */