Imported Upstream version 1.0.2
Pierre Chifflier
13 years ago
2913 | 2913 | |
2914 | 2914 | # Define the identity of the package. |
2915 | 2915 | PACKAGE=suricata |
2916 | VERSION=1.0.1 | |
2916 | VERSION=1.0.2 | |
2917 | 2917 | |
2918 | 2918 | |
2919 | 2919 | cat >>confdefs.h <<_ACEOF |
2 | 2 | AC_INIT(configure.in) |
3 | 3 | |
4 | 4 | AM_CONFIG_HEADER(config.h) |
5 | AM_INIT_AUTOMAKE(suricata, 1.0.1) | |
5 | AM_INIT_AUTOMAKE(suricata, 1.0.2) | |
6 | 6 | |
7 | 7 | AC_LANG_C |
8 | 8 | AC_PROG_CC_C99 |
114 | 114 | detect-http-header.c detect-http-header.h \ |
115 | 115 | detect-http-uri.c detect-http-uri.h \ |
116 | 116 | detect-tls-version.c detect-tls-version.h \ |
117 | detect-ssh-proto-version.c detect-ssh-proto-version.h \ | |
118 | detect-ssh-software-version.c detect-ssh-software-version.h \ | |
117 | 119 | detect-icmp-id.c detect-icmp-id.h \ |
118 | 120 | detect-icmp-seq.c detect-icmp-seq.h \ |
119 | 121 | detect-dce-iface.c detect-dce-iface.h \ |
205 | 207 | app-layer-dcerpc-udp.c app-layer-dcerpc-udp.h \ |
206 | 208 | app-layer-ftp.c app-layer-ftp.h \ |
207 | 209 | app-layer-ssl.c app-layer-ssl.h \ |
210 | app-layer-ssh.c app-layer-ssh.h \ | |
208 | 211 | defrag.c defrag.h \ |
209 | 212 | output.c output.h \ |
210 | 213 | win32-misc.c win32-misc.h \ |
105 | 105 | detect-icode.$(OBJEXT) detect-http-cookie.$(OBJEXT) \ |
106 | 106 | detect-http-method.$(OBJEXT) detect-http-header.$(OBJEXT) \ |
107 | 107 | detect-http-uri.$(OBJEXT) detect-tls-version.$(OBJEXT) \ |
108 | detect-icmp-id.$(OBJEXT) detect-icmp-seq.$(OBJEXT) \ | |
109 | detect-dce-iface.$(OBJEXT) detect-dce-opnum.$(OBJEXT) \ | |
110 | detect-dce-stub-data.$(OBJEXT) detect-urilen.$(OBJEXT) \ | |
111 | detect-detection-filter.$(OBJEXT) \ | |
108 | detect-ssh-proto-version.$(OBJEXT) \ | |
109 | detect-ssh-software-version.$(OBJEXT) detect-icmp-id.$(OBJEXT) \ | |
110 | detect-icmp-seq.$(OBJEXT) detect-dce-iface.$(OBJEXT) \ | |
111 | detect-dce-opnum.$(OBJEXT) detect-dce-stub-data.$(OBJEXT) \ | |
112 | detect-urilen.$(OBJEXT) detect-detection-filter.$(OBJEXT) \ | |
112 | 113 | detect-http-client-body.$(OBJEXT) detect-asn1.$(OBJEXT) \ |
113 | 114 | util-print.$(OBJEXT) util-fmemopen.$(OBJEXT) \ |
114 | 115 | util-cpu.$(OBJEXT) util-pidfile.$(OBJEXT) util-mpm.$(OBJEXT) \ |
148 | 149 | app-layer-tls.$(OBJEXT) app-layer-smb.$(OBJEXT) \ |
149 | 150 | app-layer-smb2.$(OBJEXT) app-layer-dcerpc.$(OBJEXT) \ |
150 | 151 | app-layer-dcerpc-udp.$(OBJEXT) app-layer-ftp.$(OBJEXT) \ |
151 | app-layer-ssl.$(OBJEXT) defrag.$(OBJEXT) output.$(OBJEXT) \ | |
152 | win32-misc.$(OBJEXT) win32-service.$(OBJEXT) \ | |
153 | util-action.$(OBJEXT) util-profiling.$(OBJEXT) \ | |
154 | cuda-packet-batcher.$(OBJEXT) | |
152 | app-layer-ssl.$(OBJEXT) app-layer-ssh.$(OBJEXT) \ | |
153 | defrag.$(OBJEXT) output.$(OBJEXT) win32-misc.$(OBJEXT) \ | |
154 | win32-service.$(OBJEXT) util-action.$(OBJEXT) \ | |
155 | util-profiling.$(OBJEXT) cuda-packet-batcher.$(OBJEXT) | |
155 | 156 | suricata_OBJECTS = $(am_suricata_OBJECTS) |
156 | 157 | @BUILD_LIBHTP_TRUE@suricata_DEPENDENCIES = \ |
157 | 158 | @BUILD_LIBHTP_TRUE@ $(top_builddir)/libhtp/htp/libhtp.la |
417 | 418 | detect-http-header.c detect-http-header.h \ |
418 | 419 | detect-http-uri.c detect-http-uri.h \ |
419 | 420 | detect-tls-version.c detect-tls-version.h \ |
421 | detect-ssh-proto-version.c detect-ssh-proto-version.h \ | |
422 | detect-ssh-software-version.c detect-ssh-software-version.h \ | |
420 | 423 | detect-icmp-id.c detect-icmp-id.h \ |
421 | 424 | detect-icmp-seq.c detect-icmp-seq.h \ |
422 | 425 | detect-dce-iface.c detect-dce-iface.h \ |
508 | 511 | app-layer-dcerpc-udp.c app-layer-dcerpc-udp.h \ |
509 | 512 | app-layer-ftp.c app-layer-ftp.h \ |
510 | 513 | app-layer-ssl.c app-layer-ssl.h \ |
514 | app-layer-ssh.c app-layer-ssh.h \ | |
511 | 515 | defrag.c defrag.h \ |
512 | 516 | output.c output.h \ |
513 | 517 | win32-misc.c win32-misc.h \ |
625 | 629 | @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/app-layer-parser.Po@am__quote@ |
626 | 630 | @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/app-layer-smb.Po@am__quote@ |
627 | 631 | @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/app-layer-smb2.Po@am__quote@ |
632 | @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/app-layer-ssh.Po@am__quote@ | |
628 | 633 | @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/app-layer-ssl.Po@am__quote@ |
629 | 634 | @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/app-layer-tls.Po@am__quote@ |
630 | 635 | @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/app-layer.Po@am__quote@ |
720 | 725 | @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/detect-sameip.Po@am__quote@ |
721 | 726 | @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/detect-seq.Po@am__quote@ |
722 | 727 | @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/detect-sid.Po@am__quote@ |
728 | @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/detect-ssh-proto-version.Po@am__quote@ | |
729 | @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/detect-ssh-software-version.Po@am__quote@ | |
723 | 730 | @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/detect-stream_size.Po@am__quote@ |
724 | 731 | @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/detect-tag.Po@am__quote@ |
725 | 732 | @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/detect-threshold.Po@am__quote@ |
104 | 104 | * \param ctx the contex |
105 | 105 | * \param co the content match |
106 | 106 | * \param proto the proto id |
107 | * \initonly | |
107 | 108 | */ |
108 | 109 | static void AlpProtoAddSignature(AlpProtoDetectCtx *ctx, DetectContentData *co, uint16_t ip_proto, uint16_t proto) { |
109 | 110 | AlpProtoSignature *s = SCMalloc(sizeof(AlpProtoSignature)); |
110 | 111 | if (s == NULL) { |
111 | return; | |
112 | SCLogError(SC_ERR_FATAL, "Error allocating memory. Signature not loaded. Not enough memory so.. exiting.."); | |
113 | exit(EXIT_FAILURE); | |
112 | 114 | } |
113 | 115 | memset(s, 0x00, sizeof(AlpProtoSignature)); |
114 | 116 |
64 | 64 | char inputlower[5]; |
65 | 65 | |
66 | 66 | if (input_len >= 4) { |
67 | memcpy(inputlower,input,5); | |
67 | memcpy(inputlower,input,4); | |
68 | 68 | int i = 0; |
69 | 69 | for (; i < 4; i++) |
70 | 70 | inputlower[i] = tolower(inputlower[i]); |
72 | 72 | if (memcmp(inputlower, "port", 4) == 0) { |
73 | 73 | fstate->command = FTP_COMMAND_PORT; |
74 | 74 | } |
75 | ||
75 | 76 | /* else { |
76 | 77 | * Add the ftp commands you need here |
77 | 78 | * } |
43 | 43 | #include "app-layer-htp.h" |
44 | 44 | |
45 | 45 | #include "util-spm.h" |
46 | #include "util-unittest.h" | |
47 | 46 | #include "util-debug.h" |
48 | 47 | #include "app-layer-htp.h" |
49 | 48 | #include "util-time.h" |
50 | 49 | #include <htp/htp.h> |
50 | ||
51 | #include "util-unittest.h" | |
52 | #include "util-unittest-helper.h" | |
53 | #include "flow-util.h" | |
54 | ||
55 | #include "detect-engine.h" | |
56 | #include "detect-engine-state.h" | |
57 | #include "detect-parse.h" | |
51 | 58 | |
52 | 59 | #include "conf.h" |
53 | 60 | |
145 | 152 | |
146 | 153 | memset(s, 0x00, sizeof(HtpState)); |
147 | 154 | |
148 | s->body.nchunks = 0; | |
149 | s->body.operation = HTP_BODY_NONE; | |
150 | s->body.pcre_flags = HTP_PCRE_NONE; | |
151 | ||
152 | 155 | #ifdef DEBUG |
153 | 156 | SCMutexLock(&htp_state_mem_lock); |
154 | 157 | htp_state_memcnt++; |
175 | 178 | |
176 | 179 | HtpState *s = (HtpState *)state; |
177 | 180 | |
181 | /* Unset the body inspection */ | |
182 | s->flags &=~ HTP_FLAG_NEW_BODY_SET; | |
183 | ||
178 | 184 | /* free the connection parser memory used by HTP library */ |
179 | if (s != NULL) { | |
180 | if (s->connp != NULL) { | |
181 | htp_connp_destroy_all(s->connp); | |
182 | } | |
185 | if (s != NULL && s->connp != NULL) { | |
186 | size_t i; | |
183 | 187 | /* free the list of body chunks */ |
184 | if (s->body.nchunks > 0) { | |
185 | HtpBodyFree(&s->body); | |
186 | } | |
188 | if (s->connp->conn != NULL) { | |
189 | for (i = 0; i < list_size(s->connp->conn->transactions); i++) { | |
190 | htp_tx_t *tx = (htp_tx_t *)list_get(s->connp->conn->transactions, i); | |
191 | if (tx != NULL) { | |
192 | SCHtpTxUserData *htud = (SCHtpTxUserData *) htp_tx_get_user_data(tx); | |
193 | if (htud != NULL) { | |
194 | HtpBodyFree(&htud->body); | |
195 | SCFree(htud); | |
196 | } | |
197 | htp_tx_set_user_data(tx, NULL); | |
198 | } | |
199 | } | |
200 | } | |
201 | htp_connp_destroy_all(s->connp); | |
187 | 202 | } |
188 | 203 | |
189 | 204 | SCFree(s); |
243 | 258 | /** |
244 | 259 | * \brief Sets a flag that informs the HTP app layer that some module in the |
245 | 260 | * engine needs the http request body data. |
261 | * \initonly | |
246 | 262 | */ |
247 | 263 | void AppLayerHtpEnableRequestBodyCallback(void) |
248 | 264 | { |
668 | 684 | "%"PRIu32"", hstate, d, d->data, (uint32_t)d->len); |
669 | 685 | |
670 | 686 | //PrintRawDataFp(stdout, d->data, d->len); |
671 | ||
672 | /* If it has been inspected by pcre and there's no match, | |
673 | * remove this chunks */ | |
674 | if ( !(hstate->body.pcre_flags & HTP_PCRE_HAS_MATCH) && | |
675 | (hstate->body.pcre_flags & HTP_PCRE_DONE)) | |
676 | { | |
677 | HtpBodyFree(&hstate->body); | |
678 | } | |
679 | ||
680 | /* If its a new operation, remove the old data */ | |
681 | if (hstate->body.operation == HTP_BODY_RESPONSE) { | |
682 | HtpBodyFree(&hstate->body); | |
683 | hstate->body.pcre_flags = HTP_PCRE_NONE; | |
684 | } | |
685 | hstate->body.operation = HTP_BODY_REQUEST; | |
686 | ||
687 | ||
688 | HtpBodyAppendChunk(&hstate->body, (uint8_t*)d->data, (uint32_t)d->len); | |
689 | hstate->body.pcre_flags = HTP_PCRE_NONE; | |
687 | SCHtpTxUserData *htud = (SCHtpTxUserData *) htp_tx_get_user_data(d->tx); | |
688 | if (htud == NULL) { | |
689 | htud = SCMalloc(sizeof(SCHtpTxUserData)); | |
690 | if (htud == NULL) { | |
691 | SCLogError(SC_ERR_MEM_ALLOC, "Error allocating memory"); | |
692 | SCReturnInt(HOOK_OK); | |
693 | } | |
694 | memset(htud, 0, sizeof(SCHtpTxUserData)); | |
695 | htud->body.operation = HTP_BODY_NONE; | |
696 | htud->body.pcre_flags = HTP_PCRE_NONE; | |
697 | ||
698 | /* Set the user data for handling body chunks on this transaction */ | |
699 | htp_tx_set_user_data(d->tx, htud); | |
700 | } | |
701 | ||
702 | htud->body.operation = HTP_BODY_REQUEST; | |
703 | ||
704 | HtpBodyAppendChunk(&htud->body, (uint8_t*)d->data, (uint32_t)d->len); | |
705 | htud->body.pcre_flags = HTP_PCRE_NONE; | |
690 | 706 | if (SCLogDebugEnabled()) { |
691 | HtpBodyPrint(&hstate->body); | |
707 | HtpBodyPrint(&htud->body); | |
692 | 708 | } |
693 | 709 | |
694 | 710 | /* set the new chunk flag */ |
765 | 781 | SCReturnInt(HOOK_ERROR); |
766 | 782 | } |
767 | 783 | |
768 | /* Free data when we have a response */ | |
769 | if (hstate->body.nchunks > 0) | |
770 | HtpBodyFree(&hstate->body); | |
771 | ||
772 | hstate->body.operation = HTP_BODY_RESPONSE; | |
773 | hstate->body.pcre_flags = HTP_PCRE_NONE; | |
784 | /* Unset the body inspection (if any) */ | |
785 | hstate->flags &=~ HTP_FLAG_NEW_BODY_SET; | |
774 | 786 | |
775 | 787 | /* remove obsolete transactions */ |
776 | 788 | size_t idx; |
778 | 790 | htp_tx_t *tx = list_get(hstate->connp->conn->transactions, idx); |
779 | 791 | if (tx == NULL) |
780 | 792 | continue; |
793 | ||
794 | /* This will remove obsolete body chunks */ | |
795 | SCHtpTxUserData *htud = (SCHtpTxUserData *) htp_tx_get_user_data(tx); | |
796 | if (htud != NULL) { | |
797 | HtpBodyFree(&htud->body); | |
798 | htp_tx_set_user_data(tx, NULL); | |
799 | SCFree(htud); | |
800 | } | |
781 | 801 | |
782 | 802 | htp_tx_destroy(tx); |
783 | 803 | } |
1806 | 1826 | { |
1807 | 1827 | int result = 1; |
1808 | 1828 | Flow f; |
1829 | FLOW_INITIALIZE(&f); | |
1809 | 1830 | uint8_t httpbuf1[] = "POST / HTTP/1.0\r\nUser-Agent: Victor/1.0\r\n\r\nPost" |
1810 | 1831 | " Data is c0oL!"; |
1811 | 1832 | uint32_t httplen1 = sizeof(httpbuf1) - 1; /* minus the \0 */ |
1907 | 1928 | StreamTcpFreeConfig(TRUE); |
1908 | 1929 | if (htp_state != NULL) |
1909 | 1930 | HTPStateFree(htp_state); |
1931 | FLOW_DESTROY(&f); | |
1910 | 1932 | return result; |
1911 | 1933 | } |
1934 | ||
1912 | 1935 | #endif /* UNITTESTS */ |
1913 | 1936 | |
1914 | 1937 | /** |
81 | 81 | any pcre (so we can free() without waiting) */ |
82 | 82 | } HtpBody; |
83 | 83 | |
84 | /** Now the Body Chunks will be stored per transaction, at | |
85 | * the tx user data */ | |
86 | typedef struct SCHtpTxUserData_ { | |
87 | HtpBody body; /**< Body of the request (if any) */ | |
88 | } SCHtpTxUserData; | |
89 | ||
84 | 90 | typedef struct HtpState_ { |
85 | 91 | |
86 | 92 | htp_connp_t *connp; /**< Connection parser structure for |
87 | 93 | each connection */ |
88 | HtpBody body; /**< Body of the request (if any) */ | |
89 | 94 | // size_t new_in_tx_index; /**< Index to indicate that after this we have |
90 | 95 | // new requests to log */ |
91 | 96 | uint8_t flags; |
0 | /* Copyright (C) 2007-2010 Open Information Security Foundation | |
1 | * | |
2 | * You can copy, redistribute or modify this Program under the terms of | |
3 | * the GNU General Public License version 2 as published by the Free | |
4 | * Software Foundation. | |
5 | * | |
6 | * This program is distributed in the hope that it will be useful, | |
7 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
8 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
9 | * GNU General Public License for more details. | |
10 | * | |
11 | * You should have received a copy of the GNU General Public License | |
12 | * version 2 along with this program; if not, write to the Free Software | |
13 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA | |
14 | * 02110-1301, USA. | |
15 | */ | |
16 | ||
17 | /** | |
18 | * \file | |
19 | * | |
20 | * \author Pablo Rincon <pablo.rincon.crespo@gmail.com> | |
21 | * | |
22 | * App-layer parser for SSH protocol | |
23 | * | |
24 | */ | |
25 | ||
26 | #include "suricata-common.h" | |
27 | #include "debug.h" | |
28 | #include "decode.h" | |
29 | #include "threads.h" | |
30 | ||
31 | #include "util-print.h" | |
32 | #include "util-pool.h" | |
33 | ||
34 | #include "stream-tcp-private.h" | |
35 | #include "stream-tcp-reassemble.h" | |
36 | #include "stream-tcp.h" | |
37 | #include "stream.h" | |
38 | ||
39 | #include "app-layer-protos.h" | |
40 | #include "app-layer-parser.h" | |
41 | #include "app-layer-ssh.h" | |
42 | ||
43 | #include "conf.h" | |
44 | ||
45 | #include "util-spm.h" | |
46 | #include "util-unittest.h" | |
47 | #include "util-debug.h" | |
48 | #include "flow-private.h" | |
49 | ||
50 | #include "util-byte.h" | |
51 | ||
52 | /** | |
53 | * \brief Function to parse the SSH version string of the server | |
54 | * | |
55 | * \param ssh_state Pointer the state in which the value to be stored | |
56 | * \param pstate Application layer tarser state for this session | |
57 | * \param input Pointer the received input data | |
58 | * \param input_len Length in bytes of the received data | |
59 | * \param output Pointer to the list of parsed output elements | |
60 | */ | |
61 | static int SSHParseServerVersion(Flow *f, void *ssh_state, AppLayerParserState *pstate, | |
62 | uint8_t *input, uint32_t input_len, | |
63 | AppLayerParserResult *output) { | |
64 | uint8_t *line_ptr = input; | |
65 | uint32_t line_len = input_len; | |
66 | uint32_t offset = 0; | |
67 | ||
68 | SshState *state = (SshState *)ssh_state; | |
69 | ||
70 | while (input_len > 0) { | |
71 | offset = 0; | |
72 | ||
73 | if (pstate->store_len > 0){ | |
74 | const uint8_t delim[] = { 0x0a, }; | |
75 | int r = AlpParseFieldByDelimiter(output, pstate, | |
76 | SSH_FIELD_SERVER_VER_STATE_LINE, delim, sizeof(delim), | |
77 | input, input_len, &offset); | |
78 | ||
79 | if (r == 0) | |
80 | SCReturnInt(0); | |
81 | ||
82 | /* process the result elements */ | |
83 | AppLayerParserResultElmt *e = output->head; | |
84 | line_ptr = NULL; | |
85 | line_len = 0; | |
86 | for (; e != NULL; e = e->next) { | |
87 | SCLogDebug("e %p e->name_idx %" PRIu32 ", e->data_ptr %p, e->data_len " | |
88 | "%" PRIu32, e, e->name_idx, | |
89 | e->data_ptr, e->data_len); | |
90 | ||
91 | /* no parser defined for this field. */ | |
92 | if (e->name_idx != SSH_FIELD_SERVER_VER_STATE_LINE) { | |
93 | continue; | |
94 | } | |
95 | ||
96 | line_ptr = e->data_ptr; | |
97 | line_len = e->data_len; | |
98 | } | |
99 | ||
100 | /* Update for the next round */ | |
101 | input_len -= offset; | |
102 | input += offset; | |
103 | ||
104 | if (line_ptr == NULL) | |
105 | continue; | |
106 | } else { | |
107 | const uint8_t delim[] = { 0x0a, }; | |
108 | int r = AlpParseFieldByDelimiter(output, pstate, | |
109 | SSH_FIELD_SERVER_VER_STATE_LINE, delim, sizeof(delim), | |
110 | input, input_len, &offset); | |
111 | ||
112 | if (r == 0) | |
113 | SCReturnInt(0); | |
114 | ||
115 | /* Temporal pointer / len for the current line */ | |
116 | line_ptr = input; | |
117 | line_len = offset; | |
118 | ||
119 | /* Update for the next round */ | |
120 | input_len -= offset; | |
121 | input += offset; | |
122 | } | |
123 | ||
124 | //printf("INPUT: \n"); | |
125 | //PrintRawDataFp(stdout, line_ptr, line_len); | |
126 | ||
127 | if (line_len < 5) { | |
128 | SCLogDebug("This is not the version line we are searching for (probably a banner or informational messages)"); | |
129 | continue; | |
130 | } | |
131 | ||
132 | /* is it the version line? */ | |
133 | if (memcmp("SSH-", line_ptr, 4) == 0) { | |
134 | if (line_len > 255) { | |
135 | SCLogDebug("Invalid version string, it should be less than 255 characters including <CR><NL>"); | |
136 | SCReturnInt(-1); | |
137 | } | |
138 | ||
139 | /* ok, we have found the version line/string, skip it and parse proto version */ | |
140 | line_ptr += 4; | |
141 | line_len -= 4; | |
142 | } else { | |
143 | SCLogDebug("This is not the version line we are searching for (probably a banner or informational messages)"); | |
144 | continue; | |
145 | } | |
146 | ||
147 | uint8_t *proto_end = BasicSearch(line_ptr, line_len, (uint8_t*)"-", 1); | |
148 | if (proto_end == NULL) { | |
149 | /* Strings starting with SSH- are not allowed | |
150 | * if they are not the real version string */ | |
151 | SCLogDebug("Invalid Version String for SSH (invalid usage of SSH- prefix)"); | |
152 | SCReturnInt(-1); | |
153 | } | |
154 | ||
155 | uint64_t proto_ver_len = (uint64_t)(proto_end - line_ptr); | |
156 | state->server_proto_version = SCMalloc(proto_ver_len + 1); | |
157 | if (state->server_proto_version == NULL) { | |
158 | SCLogError(SC_ERR_MEM_ALLOC, "Error allocating memory"); | |
159 | SCReturnInt(-1); | |
160 | } | |
161 | memcpy(state->server_proto_version, line_ptr, proto_ver_len); | |
162 | state->server_proto_version[proto_ver_len] = '\0'; | |
163 | ||
164 | /* Now lets parse the software & version */ | |
165 | line_ptr += proto_ver_len + 1; | |
166 | line_len -= proto_ver_len + 1; | |
167 | if (line_len < 1) { | |
168 | SCLogDebug("No software version specified (weird)"); | |
169 | state->flags |= SSH_FLAG_CLIENT_VERSION_PARSED; | |
170 | /* Return the remaining length */ | |
171 | SCReturnInt(input_len); | |
172 | } | |
173 | ||
174 | uint8_t *sw_end = BasicSearch(line_ptr, line_len, (uint8_t*)" ", 1); | |
175 | if (sw_end == NULL) { | |
176 | sw_end = BasicSearch(line_ptr, line_len, (uint8_t*)"\r", 1); | |
177 | if (sw_end == NULL) { | |
178 | sw_end = line_ptr + line_len; | |
179 | } | |
180 | } | |
181 | ||
182 | uint64_t sw_ver_len = (uint64_t)(sw_end - line_ptr); | |
183 | state->server_software_version = SCMalloc(sw_ver_len + 1); | |
184 | if (state->server_software_version == NULL) { | |
185 | SCLogError(SC_ERR_MEM_ALLOC, "Error allocating memory"); | |
186 | SCReturnInt(-1); | |
187 | } | |
188 | memcpy(state->server_software_version, line_ptr, sw_ver_len); | |
189 | state->server_software_version[sw_ver_len] = '\0'; | |
190 | if (state->server_software_version[sw_ver_len - 1] == 0x0d) | |
191 | state->server_software_version[sw_ver_len - 1] = '\0'; | |
192 | ||
193 | state->flags |= SSH_FLAG_SERVER_VERSION_PARSED; | |
194 | /* Return the remaining length */ | |
195 | SCReturnInt(input_len); | |
196 | } | |
197 | ||
198 | SCReturnInt(0); | |
199 | } | |
200 | ||
201 | /** | |
202 | * \brief Function to parse the SSH field in packet received from the server | |
203 | * | |
204 | * \param ssh_state Pointer the state in which the value to be stored | |
205 | * \param pstate Application layer tarser state for this session | |
206 | * \param input Pointer the received input data | |
207 | * \param input_len Length in bytes of the received data | |
208 | * \param output Pointer to the list of parsed output elements | |
209 | */ | |
210 | static int SSHParseServerRecord(Flow *f, void *ssh_state, AppLayerParserState *pstate, | |
211 | uint8_t *input, uint32_t input_len, | |
212 | AppLayerParserResult *output) | |
213 | { | |
214 | SshState *state = (SshState *)ssh_state; | |
215 | if (state->flags & SSH_FLAG_PARSER_DONE) { | |
216 | SCReturnInt(0); | |
217 | } | |
218 | ||
219 | SCEnter(); | |
220 | int ret = 0; | |
221 | ||
222 | SCLogDebug("ssh_state %p, pstate %p, input %p,input_len %" PRIu32 "", | |
223 | ssh_state, pstate, input, input_len); | |
224 | //PrintRawDataFp(stdout, input,input_len); | |
225 | ||
226 | if (pstate == NULL) | |
227 | SCReturnInt(-1); | |
228 | ||
229 | if ( !(state->flags & SSH_FLAG_SERVER_VERSION_PARSED)) { | |
230 | ret = SSHParseServerVersion(f, ssh_state, pstate, input, input_len, output); | |
231 | if (ret < 0) { | |
232 | if (ret <= -1) { | |
233 | SCLogDebug("Invalid version string"); | |
234 | SCReturnInt(-1); | |
235 | } | |
236 | SCLogDebug("Version string not parsed yet"); | |
237 | pstate->parse_field = 0; | |
238 | SCReturnInt(ret); | |
239 | } else if (state->flags & SSH_FLAG_SERVER_VERSION_PARSED) { | |
240 | SCLogDebug("Version string parsed"); | |
241 | input += input_len - ret; | |
242 | input_len -= (input_len - ret); | |
243 | pstate->parse_field = 1; | |
244 | ret = 1; | |
245 | if (input_len == 0) | |
246 | SCReturnInt(ret); | |
247 | } else { | |
248 | SCLogDebug("Version string not parsed yet"); | |
249 | pstate->parse_field = 0; | |
250 | SCReturnInt(ret); | |
251 | } | |
252 | } else { | |
253 | SCLogDebug("Version string already parsed"); | |
254 | } | |
255 | ||
256 | uint16_t max_fields = 4; | |
257 | int16_t u = 0; | |
258 | uint32_t offset = 0; | |
259 | ||
260 | //PrintRawDataFp(stdout, input,input_len); | |
261 | ||
262 | if (pstate == NULL) | |
263 | SCReturnInt(-1); | |
264 | ||
265 | for (u = pstate->parse_field; u < max_fields; u++) { | |
266 | SCLogDebug("u %" PRIu32 "", u); | |
267 | ||
268 | switch(u % 4) { | |
269 | case 0: | |
270 | { | |
271 | continue; | |
272 | } | |
273 | case 1: /* TLS CONTENT TYPE */ | |
274 | { | |
275 | uint8_t *data = input + offset; | |
276 | uint32_t data_len = input_len - offset; | |
277 | ||
278 | int r = AlpParseFieldBySize(output, pstate, | |
279 | SSH_FIELD_SERVER_PKT_LENGTH, | |
280 | /* single byte field */4, data, | |
281 | data_len, &offset); | |
282 | SCLogDebug("r = %" PRId32 "", r); | |
283 | ||
284 | if (r == 0) { | |
285 | pstate->parse_field = 1; | |
286 | SCReturnInt(0); | |
287 | } else if (r == -1) { | |
288 | SCLogError(SC_ERR_ALPARSER, "AlpParseFieldBySize failed, " | |
289 | "r %d", r); | |
290 | SCReturnInt(-1); | |
291 | } | |
292 | ||
293 | uint32_t pkt_len = 0; | |
294 | int ret = ByteExtractUint32(&pkt_len, BYTE_BIG_ENDIAN, | |
295 | output->tail->data_len, output->tail->data_ptr); | |
296 | if (ret != 4) { | |
297 | SCReturnInt(-1); | |
298 | } | |
299 | state->srv_hdr.pkt_len = pkt_len; | |
300 | SCLogDebug("pkt len: %"PRIu32, pkt_len); | |
301 | ||
302 | break; | |
303 | } | |
304 | case 2: /* TLS VERSION */ | |
305 | { | |
306 | uint8_t *data = input + offset; | |
307 | uint32_t data_len = input_len - offset; | |
308 | ||
309 | int r = AlpParseFieldBySize(output, pstate, | |
310 | SSH_FIELD_SERVER_PADDING_LENGTH, | |
311 | /* 2 byte field */1, data, data_len, | |
312 | &offset); | |
313 | if (r == 0) { | |
314 | pstate->parse_field = 2; | |
315 | SCReturnInt(0); | |
316 | } else if (r == -1) { | |
317 | SCLogError(SC_ERR_ALPARSER, "AlpParseFieldBySize failed, " | |
318 | "r %d", r); | |
319 | SCReturnInt(-1); | |
320 | } | |
321 | uint8_t padding_len = 0; | |
322 | if (output->tail->data_len == 1) { | |
323 | padding_len = (uint8_t) *output->tail->data_ptr; | |
324 | SCLogDebug("padding len: %"PRIu8, padding_len); | |
325 | } | |
326 | state->srv_hdr.padding_len = padding_len; | |
327 | ||
328 | break; | |
329 | } | |
330 | case 3: /* SSH_PAYLOAD */ | |
331 | { | |
332 | uint8_t *data = input + offset; | |
333 | uint32_t data_len = input_len - offset; | |
334 | ||
335 | /* we add a -1 to the pkt len since the padding length is already parsed */ | |
336 | int r = AlpParseFieldBySize(output, pstate, SSH_FIELD_SERVER_PAYLOAD, | |
337 | state->srv_hdr.pkt_len - 1, data, data_len, | |
338 | &offset); | |
339 | SCLogDebug("AlpParseFieldBySize returned r %d, offset %"PRIu32, | |
340 | r, offset); | |
341 | if (r == 0) { | |
342 | pstate->parse_field = 3; | |
343 | SCReturnInt(0); | |
344 | } else if (r == -1) { | |
345 | SCLogError(SC_ERR_ALPARSER, "AlpParseFieldBySize failed, " | |
346 | "r %d", r); | |
347 | SCReturnInt(-1); | |
348 | } | |
349 | ||
350 | uint8_t msg_code = 0; | |
351 | if (output->tail->data_len >= 1) { | |
352 | msg_code = (uint8_t) *output->tail->data_ptr; | |
353 | SCLogDebug("msg code: %"PRIu8, msg_code); | |
354 | } | |
355 | state->srv_hdr.msg_code = msg_code; | |
356 | ||
357 | if (state->srv_hdr.msg_code == SSH_MSG_NEWKEYS) { | |
358 | /* We are not going to inspect any packet more | |
359 | * as the data is now encrypted */ | |
360 | SCLogDebug("SSH parser done (the rest of the communication is encrypted)"); | |
361 | state->flags |= SSH_FLAG_PARSER_DONE; | |
362 | pstate->flags |= APP_LAYER_PARSER_DONE; | |
363 | pstate->flags |= APP_LAYER_PARSER_NO_INSPECTION; | |
364 | pstate->flags |= APP_LAYER_PARSER_NO_REASSEMBLY; | |
365 | pstate->parse_field = 1; | |
366 | SCReturnInt(1); | |
367 | } | |
368 | ||
369 | pstate->parse_field = 1; | |
370 | ret = 1; | |
371 | ||
372 | /* If we have remaining data, continue processing */ | |
373 | if ((int)input_len - (int)offset > 0) { | |
374 | u = 0; | |
375 | } | |
376 | break; | |
377 | } | |
378 | } | |
379 | ||
380 | } | |
381 | ||
382 | SCReturnInt(ret); | |
383 | } | |
384 | ||
385 | /** | |
386 | * \brief Function to parse the SSH version string of the client | |
387 | * | |
388 | * \param ssh_state Pointer the state in which the value to be stored | |
389 | * \param pstate Application layer tarser state for this session | |
390 | * \param input Pointer the received input data | |
391 | * \param input_len Length in bytes of the received data | |
392 | * \param output Pointer to the list of parsed output elements | |
393 | */ | |
394 | static int SSHParseClientVersion(Flow *f, void *ssh_state, AppLayerParserState *pstate, | |
395 | uint8_t *input, uint32_t input_len, | |
396 | AppLayerParserResult *output) { | |
397 | uint8_t *line_ptr = input; | |
398 | uint32_t line_len = input_len; | |
399 | uint32_t offset = 0; | |
400 | ||
401 | SshState *state = (SshState *)ssh_state; | |
402 | ||
403 | while (input_len > 0) { | |
404 | offset = 0; | |
405 | ||
406 | ||
407 | if (pstate->store_len > 0){ | |
408 | const uint8_t delim[] = { 0x0a, }; | |
409 | int r = AlpParseFieldByDelimiter(output, pstate, | |
410 | SSH_FIELD_CLIENT_VER_STATE_LINE, delim, sizeof(delim), | |
411 | input, input_len, &offset); | |
412 | ||
413 | if (r == 0) | |
414 | SCReturnInt(0); | |
415 | ||
416 | /* process the result elements */ | |
417 | AppLayerParserResultElmt *e = output->head; | |
418 | line_ptr = NULL; | |
419 | line_len = 0; | |
420 | for (; e != NULL; e = e->next) { | |
421 | SCLogDebug("e %p e->name_idx %" PRIu32 ", e->data_ptr %p, e->data_len " | |
422 | "%" PRIu32, e, e->name_idx, | |
423 | e->data_ptr, e->data_len); | |
424 | ||
425 | /* no parser defined for this field. */ | |
426 | if (e->name_idx != SSH_FIELD_CLIENT_VER_STATE_LINE) { | |
427 | continue; | |
428 | } | |
429 | ||
430 | line_ptr = e->data_ptr; | |
431 | line_len = e->data_len; | |
432 | } | |
433 | ||
434 | /* Update for the next round */ | |
435 | input_len -= offset; | |
436 | input += offset; | |
437 | ||
438 | if (line_ptr == NULL) | |
439 | continue; | |
440 | } else { | |
441 | const uint8_t delim[] = { 0x0a, }; | |
442 | int r = AlpParseFieldByDelimiter(output, pstate, | |
443 | SSH_FIELD_CLIENT_VER_STATE_LINE, delim, sizeof(delim), | |
444 | input, input_len, &offset); | |
445 | ||
446 | if (r == 0) | |
447 | SCReturnInt(0); | |
448 | ||
449 | /* Temporal pointer / len for the current line */ | |
450 | line_ptr = input; | |
451 | line_len = offset; | |
452 | ||
453 | /* Update for the next round */ | |
454 | input_len -= offset; | |
455 | input += offset; | |
456 | } | |
457 | ||
458 | //PrintRawDataFp(stdout, line_ptr, line_len); | |
459 | ||
460 | if (line_len < 5) { | |
461 | SCLogDebug("This is not the version line we are searching for (probably a banner or informational messages)"); | |
462 | continue; | |
463 | } | |
464 | ||
465 | /* is it the version line? */ | |
466 | if (memcmp("SSH-", line_ptr, 4) == 0) { | |
467 | if (line_len > 255) { | |
468 | SCLogDebug("Invalid version string, it should be less than 255 characters including <CR><NL>"); | |
469 | SCReturnInt(-1); | |
470 | } | |
471 | ||
472 | /* ok, we have found the version line/string, skip it and parse proto version */ | |
473 | line_ptr += 4; | |
474 | line_len -= 4; | |
475 | } else { | |
476 | SCLogDebug("This is not the version line we are searching for (probably a banner or informational messages)"); | |
477 | continue; | |
478 | } | |
479 | ||
480 | uint8_t *proto_end = BasicSearch(line_ptr, line_len, (uint8_t*)"-", 1); | |
481 | if (proto_end == NULL) { | |
482 | /* Strings starting with SSH- are not allowed | |
483 | * if they are not the real version string */ | |
484 | SCLogDebug("Invalid Version String for SSH (invalid usage of SSH- prefix)"); | |
485 | SCReturnInt(-1); | |
486 | } | |
487 | ||
488 | uint64_t proto_ver_len = (uint64_t)(proto_end - line_ptr); | |
489 | state->client_proto_version = SCMalloc(proto_ver_len + 1); | |
490 | if (state->client_proto_version == NULL) { | |
491 | SCLogError(SC_ERR_MEM_ALLOC, "Error allocating memory"); | |
492 | SCReturnInt(-1); | |
493 | } | |
494 | memcpy(state->client_proto_version, line_ptr, proto_ver_len); | |
495 | state->client_proto_version[proto_ver_len] = '\0'; | |
496 | ||
497 | /* Now lets parse the software & version */ | |
498 | line_ptr += proto_ver_len + 1; | |
499 | line_len -= proto_ver_len + 1; | |
500 | if (line_len < 1) { | |
501 | SCLogDebug("No software version specified (weird)"); | |
502 | state->flags |= SSH_FLAG_CLIENT_VERSION_PARSED; | |
503 | /* Return the remaining length */ | |
504 | SCReturnInt(input_len); | |
505 | } | |
506 | ||
507 | uint8_t *sw_end = BasicSearch(line_ptr, line_len, (uint8_t*)" ", 1); | |
508 | if (sw_end == NULL) { | |
509 | sw_end = BasicSearch(line_ptr, line_len, (uint8_t*)"\r", 1); | |
510 | if (sw_end == NULL) { | |
511 | sw_end = line_ptr + line_len; | |
512 | } | |
513 | } | |
514 | ||
515 | uint64_t sw_ver_len = (uint64_t)(sw_end - line_ptr); | |
516 | state->client_software_version = SCMalloc(sw_ver_len + 1); | |
517 | if (state->client_software_version == NULL) { | |
518 | SCLogError(SC_ERR_MEM_ALLOC, "Error allocating memory"); | |
519 | SCReturnInt(-1); | |
520 | } | |
521 | memcpy(state->client_software_version, line_ptr, sw_ver_len); | |
522 | state->client_software_version[sw_ver_len] = '\0'; | |
523 | if (state->client_software_version[sw_ver_len - 1] == 0x0d) | |
524 | state->client_software_version[sw_ver_len - 1] = '\0'; | |
525 | ||
526 | state->flags |= SSH_FLAG_CLIENT_VERSION_PARSED; | |
527 | /* Return the remaining length */ | |
528 | SCReturnInt(input_len); | |
529 | } | |
530 | ||
531 | SCReturnInt(0); | |
532 | } | |
533 | ||
534 | /** | |
535 | * \brief Function to parse the SSH field in packet received from the client | |
536 | * | |
537 | * \param ssh_state Pointer the state in which the value to be stored | |
538 | * \param pstate Application layer tarser state for this session | |
539 | * \param input Pointer the received input data | |
540 | * \param input_len Length in bytes of the received data | |
541 | * \param output Pointer to the list of parsed output elements | |
542 | */ | |
543 | static int SSHParseClientRecord(Flow *f, void *ssh_state, AppLayerParserState *pstate, | |
544 | uint8_t *input, uint32_t input_len, | |
545 | AppLayerParserResult *output) | |
546 | { | |
547 | SshState *state = (SshState *)ssh_state; | |
548 | if (state->flags & SSH_FLAG_PARSER_DONE) { | |
549 | SCReturnInt(0); | |
550 | } | |
551 | ||
552 | SCEnter(); | |
553 | int ret = 0; | |
554 | ||
555 | SCLogDebug("ssh_state %p, pstate %p, input %p,input_len %" PRIu32 "", | |
556 | ssh_state, pstate, input, input_len); | |
557 | //PrintRawDataFp(stdout, input,input_len); | |
558 | ||
559 | if (pstate == NULL) | |
560 | SCReturnInt(-1); | |
561 | ||
562 | if ( !(state->flags & SSH_FLAG_CLIENT_VERSION_PARSED)) { | |
563 | ret = SSHParseClientVersion(f, ssh_state, pstate, input, input_len, output); | |
564 | if (ret < 0) { | |
565 | if (ret <= -1) { | |
566 | SCLogDebug("Invalid version string"); | |
567 | SCReturnInt(-1); | |
568 | } | |
569 | SCLogDebug("Version string not parsed yet"); | |
570 | pstate->parse_field = 0; | |
571 | SCReturnInt(0); | |
572 | } else if (state->flags & SSH_FLAG_CLIENT_VERSION_PARSED) { | |
573 | SCLogDebug("Version string parsed"); | |
574 | input += input_len - ret; | |
575 | input_len -= (input_len - ret); | |
576 | pstate->parse_field = 1; | |
577 | ret = 1; | |
578 | } else { | |
579 | SCLogDebug("Version string not parsed yet"); | |
580 | pstate->parse_field = 0; | |
581 | SCReturnInt(0); | |
582 | } | |
583 | } else { | |
584 | SCLogDebug("Version string already parsed"); | |
585 | } | |
586 | ||
587 | uint16_t max_fields = 4; | |
588 | int16_t u = 0; | |
589 | uint32_t offset = 0; | |
590 | ||
591 | //printf("INPUT: \n"); | |
592 | //PrintRawDataFp(stdout, input,input_len); | |
593 | ||
594 | if (pstate == NULL) | |
595 | SCReturnInt(-1); | |
596 | ||
597 | for (u = pstate->parse_field; u < max_fields; u++) { | |
598 | SCLogDebug("u %" PRIu32 "", u); | |
599 | ||
600 | switch(u % 4) { | |
601 | case 0: | |
602 | { | |
603 | continue; | |
604 | } | |
605 | case 1: /* TLS CONTENT TYPE */ | |
606 | { | |
607 | uint8_t *data = input + offset; | |
608 | uint32_t data_len = input_len - offset; | |
609 | ||
610 | int r = AlpParseFieldBySize(output, pstate, | |
611 | SSH_FIELD_CLIENT_PKT_LENGTH, | |
612 | /* single byte field */4, data, | |
613 | data_len, &offset); | |
614 | SCLogDebug("r = %" PRId32 "", r); | |
615 | ||
616 | if (r == 0) { | |
617 | pstate->parse_field = 1; | |
618 | SCReturnInt(0); | |
619 | } else if (r == -1) { | |
620 | SCLogError(SC_ERR_ALPARSER, "AlpParseFieldBySize failed, " | |
621 | "r %d", r); | |
622 | SCReturnInt(-1); | |
623 | } | |
624 | ||
625 | uint32_t pkt_len = 0; | |
626 | int ret = ByteExtractUint32(&pkt_len, BYTE_BIG_ENDIAN, | |
627 | output->tail->data_len, output->tail->data_ptr); | |
628 | if (ret != 4) { | |
629 | SCReturnInt(-1); | |
630 | } | |
631 | state->cli_hdr.pkt_len = pkt_len; | |
632 | SCLogDebug("pkt len: %"PRIu32"\n", pkt_len); | |
633 | ||
634 | break; | |
635 | } | |
636 | case 2: /* TLS VERSION */ | |
637 | { | |
638 | uint8_t *data = input + offset; | |
639 | uint32_t data_len = input_len - offset; | |
640 | ||
641 | int r = AlpParseFieldBySize(output, pstate, | |
642 | SSH_FIELD_CLIENT_PADDING_LENGTH, | |
643 | /* 2 byte field */1, data, data_len, | |
644 | &offset); | |
645 | if (r == 0) { | |
646 | pstate->parse_field = 2; | |
647 | SCReturnInt(0); | |
648 | } else if (r == -1) { | |
649 | SCLogError(SC_ERR_ALPARSER, "AlpParseFieldBySize failed, " | |
650 | "r %d", r); | |
651 | SCReturnInt(-1); | |
652 | } | |
653 | uint8_t padding_len = 0; | |
654 | if (output->tail->data_len == 1) { | |
655 | padding_len = (uint8_t) *output->tail->data_ptr; | |
656 | SCLogDebug("padding len: %"PRIu8, padding_len); | |
657 | } | |
658 | state->cli_hdr.padding_len = padding_len; | |
659 | ||
660 | break; | |
661 | } | |
662 | case 3: /* SSH_PAYLOAD */ | |
663 | { | |
664 | uint8_t *data = input + offset; | |
665 | uint32_t data_len = input_len - offset; | |
666 | ||
667 | /* we add a -1 to the pkt len since the padding length is already parsed */ | |
668 | int r = AlpParseFieldBySize(output, pstate, SSH_FIELD_CLIENT_PAYLOAD, | |
669 | /* 1 byte field */ state->cli_hdr.pkt_len - 1, data, data_len, | |
670 | &offset); | |
671 | SCLogDebug("AlpParseFieldBySize returned r %d, offset %"PRIu32, | |
672 | r, offset); | |
673 | if (r == 0) { | |
674 | pstate->parse_field = 3; | |
675 | SCReturnInt(0); | |
676 | } else if (r == -1) { | |
677 | SCLogError(SC_ERR_ALPARSER, "AlpParseFieldBySize failed, " | |
678 | "r %d", r); | |
679 | SCReturnInt(-1); | |
680 | } | |
681 | ||
682 | uint8_t msg_code = 0; | |
683 | if (output->tail->data_len >= 1) { | |
684 | msg_code = (uint8_t) *output->tail->data_ptr; | |
685 | SCLogDebug("msg code: %"PRIu8, msg_code); | |
686 | } | |
687 | ||
688 | state->cli_hdr.msg_code = msg_code; | |
689 | if (state->cli_hdr.msg_code == SSH_MSG_NEWKEYS) { | |
690 | /* We are not going to inspect any packet more | |
691 | * as the data is now encrypted */ | |
692 | SCLogDebug("SSH parser done (the rest of the communication is encrypted)"); | |
693 | state->flags |= SSH_FLAG_PARSER_DONE; | |
694 | pstate->flags |= APP_LAYER_PARSER_DONE; | |
695 | pstate->flags |= APP_LAYER_PARSER_NO_INSPECTION; | |
696 | pstate->flags |= APP_LAYER_PARSER_NO_REASSEMBLY; | |
697 | pstate->parse_field = 1; | |
698 | SCReturnInt(1); | |
699 | } | |
700 | ||
701 | pstate->parse_field = 1; | |
702 | ret = 1; | |
703 | ||
704 | /* If we have remaining data, continue processing */ | |
705 | if (input_len - offset > 0) { | |
706 | u = 0; | |
707 | } | |
708 | ||
709 | break; | |
710 | } | |
711 | } | |
712 | ||
713 | } | |
714 | ||
715 | SCReturnInt(ret); | |
716 | } | |
717 | ||
718 | /** \brief Function to allocates the SSH state memory | |
719 | */ | |
720 | static void *SSHStateAlloc(void) | |
721 | { | |
722 | void *s = SCMalloc(sizeof(SshState)); | |
723 | if (s == NULL) | |
724 | return NULL; | |
725 | ||
726 | memset(s, 0, sizeof(SshState)); | |
727 | return s; | |
728 | } | |
729 | ||
730 | /** \brief Function to free the SSH state memory | |
731 | */ | |
732 | static void SSHStateFree(void *state) | |
733 | { | |
734 | SshState *s = (SshState *)state; | |
735 | if (s->client_proto_version != NULL) | |
736 | SCFree(s->client_proto_version); | |
737 | if (s->client_software_version != NULL) | |
738 | SCFree(s->client_software_version); | |
739 | if (s->server_proto_version != NULL) | |
740 | SCFree(s->server_proto_version); | |
741 | if (s->server_software_version != NULL) | |
742 | SCFree(s->server_software_version); | |
743 | ||
744 | SCFree(s); | |
745 | } | |
746 | ||
747 | /** \brief Function to register the SSH protocol parsers and other functions | |
748 | */ | |
749 | void RegisterSSHParsers(void) | |
750 | { | |
751 | AppLayerRegisterProto("ssh", ALPROTO_SSH, STREAM_TOCLIENT, | |
752 | SSHParseServerRecord); | |
753 | AppLayerRegisterProto("ssh", ALPROTO_SSH, STREAM_TOSERVER, | |
754 | SSHParseClientRecord); | |
755 | ||
756 | AppLayerRegisterStateFuncs(ALPROTO_SSH, SSHStateAlloc, SSHStateFree); | |
757 | ||
758 | } | |
759 | ||
760 | /* UNITTESTS */ | |
761 | #ifdef UNITTESTS | |
762 | ||
763 | /** \test Send a version string in one chunk (client version str). */ | |
764 | static int SSHParserTest01(void) { | |
765 | int result = 0; | |
766 | Flow f; | |
767 | uint8_t sshbuf[] = "SSH-2.0-MySSHClient-0.5.1\n"; | |
768 | uint32_t sshlen = sizeof(sshbuf) - 1; | |
769 | TcpSession ssn; | |
770 | ||
771 | memset(&f, 0, sizeof(f)); | |
772 | memset(&ssn, 0, sizeof(ssn)); | |
773 | f.protoctx = (void *)&ssn; | |
774 | ||
775 | StreamTcpInitConfig(TRUE); | |
776 | FlowL7DataPtrInit(&f); | |
777 | ||
778 | int r = AppLayerParse(&f, ALPROTO_SSH, STREAM_TOSERVER|STREAM_EOF, sshbuf, sshlen); | |
779 | if (r != 0) { | |
780 | printf("toclient chunk 1 returned %" PRId32 ", expected 0: ", r); | |
781 | result = 0; | |
782 | goto end; | |
783 | } | |
784 | ||
785 | SshState *ssh_state = f.aldata[AlpGetStateIdx(ALPROTO_SSH)]; | |
786 | if (ssh_state == NULL) { | |
787 | printf("no ssh state: "); | |
788 | result = 0; | |
789 | goto end; | |
790 | } | |
791 | ||
792 | if ( !(ssh_state->flags & SSH_FLAG_CLIENT_VERSION_PARSED)) { | |
793 | printf("Client version string not parsed: "); | |
794 | result = 0; | |
795 | goto end; | |
796 | } | |
797 | ||
798 | if (ssh_state->client_software_version == NULL) { | |
799 | printf("Client version string not parsed: "); | |
800 | result = 0; | |
801 | goto end; | |
802 | } | |
803 | ||
804 | if (ssh_state->client_proto_version == NULL) { | |
805 | printf("Client version string not parsed: "); | |
806 | result = 0; | |
807 | goto end; | |
808 | } | |
809 | ||
810 | if (strncmp((char*)ssh_state->client_software_version, "MySSHClient-0.5.1", strlen("MySSHClient-0.5.1")) != 0) { | |
811 | printf("Client version string not parsed correctly: "); | |
812 | result = 0; | |
813 | goto end; | |
814 | } | |
815 | ||
816 | if (strncmp((char*)ssh_state->client_proto_version, "2.0", strlen("2.0")) != 0) { | |
817 | printf("Client version string not parsed correctly: "); | |
818 | result = 0; | |
819 | goto end; | |
820 | } | |
821 | ||
822 | result = 1; | |
823 | ||
824 | end: | |
825 | FlowL7DataPtrFree(&f); | |
826 | StreamTcpFreeConfig(TRUE); | |
827 | return result; | |
828 | } | |
829 | ||
830 | /** \test Send a version string in one chunk but multiple lines and comments. | |
831 | * (client version str) | |
832 | */ | |
833 | static int SSHParserTest02(void) { | |
834 | int result = 0; | |
835 | Flow f; | |
836 | uint8_t sshbuf[] = "lalala\n lal al al\nSSH-2.0-MySSHClient-0.5.1 some comments...\n"; | |
837 | uint32_t sshlen = sizeof(sshbuf) - 1; | |
838 | TcpSession ssn; | |
839 | ||
840 | memset(&f, 0, sizeof(f)); | |
841 | memset(&ssn, 0, sizeof(ssn)); | |
842 | f.protoctx = (void *)&ssn; | |
843 | ||
844 | StreamTcpInitConfig(TRUE); | |
845 | FlowL7DataPtrInit(&f); | |
846 | ||
847 | int r = AppLayerParse(&f, ALPROTO_SSH, STREAM_TOSERVER|STREAM_EOF, sshbuf, sshlen); | |
848 | if (r != 0) { | |
849 | printf("toclient chunk 1 returned %" PRId32 ", expected 0: ", r); | |
850 | result = 0; | |
851 | goto end; | |
852 | } | |
853 | ||
854 | SshState *ssh_state = f.aldata[AlpGetStateIdx(ALPROTO_SSH)]; | |
855 | if (ssh_state == NULL) { | |
856 | printf("no ssh state: "); | |
857 | result = 0; | |
858 | goto end; | |
859 | } | |
860 | ||
861 | if ( !(ssh_state->flags & SSH_FLAG_CLIENT_VERSION_PARSED)) { | |
862 | printf("Client version string not parsed: "); | |
863 | result = 0; | |
864 | goto end; | |
865 | } | |
866 | ||
867 | if (ssh_state->client_software_version == NULL) { | |
868 | printf("Client version string not parsed: "); | |
869 | result = 0; | |
870 | goto end; | |
871 | } | |
872 | ||
873 | if (ssh_state->client_proto_version == NULL) { | |
874 | printf("Client version string not parsed: "); | |
875 | result = 0; | |
876 | goto end; | |
877 | } | |
878 | ||
879 | if (strncmp((char*)ssh_state->client_software_version, "MySSHClient-0.5.1", strlen("MySSHClient-0.5.1")) != 0) { | |
880 | printf("Client version string not parsed correctly: "); | |
881 | result = 0; | |
882 | goto end; | |
883 | } | |
884 | ||
885 | if (strncmp((char*)ssh_state->client_proto_version, "2.0", strlen("2.0")) != 0) { | |
886 | printf("Client version string not parsed correctly: "); | |
887 | result = 0; | |
888 | goto end; | |
889 | } | |
890 | ||
891 | result = 1; | |
892 | ||
893 | end: | |
894 | FlowL7DataPtrFree(&f); | |
895 | StreamTcpFreeConfig(TRUE); | |
896 | return result; | |
897 | } | |
898 | ||
899 | /** \test Send a invalid version string in one chunk but multiple lines and comments. | |
900 | * (client version str) | |
901 | */ | |
902 | static int SSHParserTest03(void) { | |
903 | int result = 0; | |
904 | Flow f; | |
905 | uint8_t sshbuf[] = "lalala\n lal al al\nSSH-2.0 some comments...\n"; | |
906 | uint32_t sshlen = sizeof(sshbuf) - 1; | |
907 | TcpSession ssn; | |
908 | ||
909 | memset(&f, 0, sizeof(f)); | |
910 | memset(&ssn, 0, sizeof(ssn)); | |
911 | f.protoctx = (void *)&ssn; | |
912 | ||
913 | StreamTcpInitConfig(TRUE); | |
914 | FlowL7DataPtrInit(&f); | |
915 | ||
916 | int r = AppLayerParse(&f, ALPROTO_SSH, STREAM_TOSERVER|STREAM_EOF, sshbuf, sshlen); | |
917 | if (r == 0) { | |
918 | printf("toclient chunk 1 returned %" PRId32 ", expected != 0: ", r); | |
919 | result = 0; | |
920 | goto end; | |
921 | } | |
922 | /* Ok, it returned an error. Let's make sure we didn't parse the string at all */ | |
923 | ||
924 | SshState *ssh_state = f.aldata[AlpGetStateIdx(ALPROTO_SSH)]; | |
925 | if (ssh_state == NULL) { | |
926 | printf("no ssh state: "); | |
927 | result = 0; | |
928 | goto end; | |
929 | } | |
930 | ||
931 | if (ssh_state->flags & SSH_FLAG_CLIENT_VERSION_PARSED) { | |
932 | printf("Client version string parsed? It's not a valid string: "); | |
933 | result = 0; | |
934 | goto end; | |
935 | } | |
936 | ||
937 | if (ssh_state->client_proto_version != NULL) { | |
938 | result = 0; | |
939 | goto end; | |
940 | } | |
941 | ||
942 | if (ssh_state->client_software_version != NULL) { | |
943 | result = 0; | |
944 | goto end; | |
945 | } | |
946 | ||
947 | result = 1; | |
948 | ||
949 | end: | |
950 | FlowL7DataPtrFree(&f); | |
951 | StreamTcpFreeConfig(TRUE); | |
952 | return result; | |
953 | } | |
954 | ||
955 | /** \test Send a version string in one chunk (server version str). */ | |
956 | static int SSHParserTest04(void) { | |
957 | int result = 0; | |
958 | Flow f; | |
959 | uint8_t sshbuf[] = "SSH-2.0-MySSHClient-0.5.1\n"; | |
960 | uint32_t sshlen = sizeof(sshbuf) - 1; | |
961 | TcpSession ssn; | |
962 | ||
963 | memset(&f, 0, sizeof(f)); | |
964 | memset(&ssn, 0, sizeof(ssn)); | |
965 | f.protoctx = (void *)&ssn; | |
966 | ||
967 | StreamTcpInitConfig(TRUE); | |
968 | FlowL7DataPtrInit(&f); | |
969 | ||
970 | int r = AppLayerParse(&f, ALPROTO_SSH, STREAM_TOCLIENT|STREAM_EOF, sshbuf, sshlen); | |
971 | if (r != 0) { | |
972 | printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r); | |
973 | result = 0; | |
974 | goto end; | |
975 | } | |
976 | ||
977 | SshState *ssh_state = f.aldata[AlpGetStateIdx(ALPROTO_SSH)]; | |
978 | if (ssh_state == NULL) { | |
979 | printf("no ssh state: "); | |
980 | result = 0; | |
981 | goto end; | |
982 | } | |
983 | ||
984 | if ( !(ssh_state->flags & SSH_FLAG_SERVER_VERSION_PARSED)) { | |
985 | printf("Client version string not parsed: "); | |
986 | result = 0; | |
987 | goto end; | |
988 | } | |
989 | ||
990 | if (ssh_state->server_software_version == NULL) { | |
991 | printf("Client version string not parsed: "); | |
992 | result = 0; | |
993 | goto end; | |
994 | } | |
995 | ||
996 | if (ssh_state->server_proto_version == NULL) { | |
997 | printf("Client version string not parsed: "); | |
998 | result = 0; | |
999 | goto end; | |
1000 | } | |
1001 | ||
1002 | if (strncmp((char*)ssh_state->server_software_version, "MySSHClient-0.5.1", strlen("MySSHClient-0.5.1")) != 0) { | |
1003 | printf("Client version string not parsed correctly: "); | |
1004 | result = 0; | |
1005 | goto end; | |
1006 | } | |
1007 | ||
1008 | if (strncmp((char*)ssh_state->server_proto_version, "2.0", strlen("2.0")) != 0) { | |
1009 | printf("Client version string not parsed correctly: "); | |
1010 | result = 0; | |
1011 | goto end; | |
1012 | } | |
1013 | ||
1014 | result = 1; | |
1015 | ||
1016 | end: | |
1017 | FlowL7DataPtrFree(&f); | |
1018 | StreamTcpFreeConfig(TRUE); | |
1019 | return result; | |
1020 | } | |
1021 | ||
1022 | /** \test Send a version string in one chunk but multiple lines and comments. | |
1023 | * (server version str) | |
1024 | */ | |
1025 | static int SSHParserTest05(void) { | |
1026 | int result = 0; | |
1027 | Flow f; | |
1028 | uint8_t sshbuf[] = "lalala\n lal al al\nSSH-2.0-MySSHClient-0.5.1 some comments...\n"; | |
1029 | uint32_t sshlen = sizeof(sshbuf) - 1; | |
1030 | TcpSession ssn; | |
1031 | ||
1032 | memset(&f, 0, sizeof(f)); | |
1033 | memset(&ssn, 0, sizeof(ssn)); | |
1034 | f.protoctx = (void *)&ssn; | |
1035 | ||
1036 | StreamTcpInitConfig(TRUE); | |
1037 | FlowL7DataPtrInit(&f); | |
1038 | ||
1039 | int r = AppLayerParse(&f, ALPROTO_SSH, STREAM_TOCLIENT|STREAM_EOF, sshbuf, sshlen); | |
1040 | if (r != 0) { | |
1041 | printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r); | |
1042 | result = 0; | |
1043 | goto end; | |
1044 | } | |
1045 | ||
1046 | SshState *ssh_state = f.aldata[AlpGetStateIdx(ALPROTO_SSH)]; | |
1047 | if (ssh_state == NULL) { | |
1048 | printf("no ssh state: "); | |
1049 | result = 0; | |
1050 | goto end; | |
1051 | } | |
1052 | ||
1053 | if ( !(ssh_state->flags & SSH_FLAG_SERVER_VERSION_PARSED)) { | |
1054 | printf("Client version string not parsed: "); | |
1055 | result = 0; | |
1056 | goto end; | |
1057 | } | |
1058 | ||
1059 | if (ssh_state->server_software_version == NULL) { | |
1060 | printf("Client version string not parsed: "); | |
1061 | result = 0; | |
1062 | goto end; | |
1063 | } | |
1064 | ||
1065 | if (ssh_state->server_proto_version == NULL) { | |
1066 | printf("Client version string not parsed: "); | |
1067 | result = 0; | |
1068 | goto end; | |
1069 | } | |
1070 | ||
1071 | if (strncmp((char*)ssh_state->server_software_version, "MySSHClient-0.5.1", strlen("MySSHClient-0.5.1")) != 0) { | |
1072 | printf("Client version string not parsed correctly: "); | |
1073 | result = 0; | |
1074 | goto end; | |
1075 | } | |
1076 | ||
1077 | if (strncmp((char*)ssh_state->server_proto_version, "2.0", strlen("2.0")) != 0) { | |
1078 | printf("Client version string not parsed correctly: "); | |
1079 | result = 0; | |
1080 | goto end; | |
1081 | } | |
1082 | ||
1083 | result = 1; | |
1084 | ||
1085 | end: | |
1086 | FlowL7DataPtrFree(&f); | |
1087 | StreamTcpFreeConfig(TRUE); | |
1088 | return result; | |
1089 | } | |
1090 | ||
1091 | /** \test Send a invalid version string in one chunk but multiple lines and comments. | |
1092 | * (server version str) | |
1093 | */ | |
1094 | static int SSHParserTest06(void) { | |
1095 | int result = 0; | |
1096 | Flow f; | |
1097 | uint8_t sshbuf[] = "lalala\n lal al al\nSSH-2.0 some comments...\n"; | |
1098 | uint32_t sshlen = sizeof(sshbuf) - 1; | |
1099 | TcpSession ssn; | |
1100 | ||
1101 | memset(&f, 0, sizeof(f)); | |
1102 | memset(&ssn, 0, sizeof(ssn)); | |
1103 | f.protoctx = (void *)&ssn; | |
1104 | ||
1105 | StreamTcpInitConfig(TRUE); | |
1106 | FlowL7DataPtrInit(&f); | |
1107 | ||
1108 | int r = AppLayerParse(&f, ALPROTO_SSH, STREAM_TOCLIENT|STREAM_EOF, sshbuf, sshlen); | |
1109 | if (r == 0) { | |
1110 | printf("toserver chunk 1 returned %" PRId32 ", expected != 0: ", r); | |
1111 | result = 0; | |
1112 | goto end; | |
1113 | } | |
1114 | /* Ok, it returned an error. Let's make sure we didn't parse the string at all */ | |
1115 | ||
1116 | SshState *ssh_state = f.aldata[AlpGetStateIdx(ALPROTO_SSH)]; | |
1117 | if (ssh_state == NULL) { | |
1118 | printf("no ssh state: "); | |
1119 | result = 0; | |
1120 | goto end; | |
1121 | } | |
1122 | ||
1123 | if (ssh_state->flags & SSH_FLAG_SERVER_VERSION_PARSED) { | |
1124 | printf("Client version string parsed? It's not a valid string: "); | |
1125 | result = 0; | |
1126 | goto end; | |
1127 | } | |
1128 | ||
1129 | if (ssh_state->server_proto_version != NULL) { | |
1130 | result = 0; | |
1131 | goto end; | |
1132 | } | |
1133 | ||
1134 | if (ssh_state->server_software_version != NULL) { | |
1135 | result = 0; | |
1136 | goto end; | |
1137 | } | |
1138 | ||
1139 | result = 1; | |
1140 | ||
1141 | end: | |
1142 | FlowL7DataPtrFree(&f); | |
1143 | StreamTcpFreeConfig(TRUE); | |
1144 | return result; | |
1145 | } | |
1146 | ||
1147 | static int SSHParserTest07(void) { | |
1148 | int result = 0; | |
1149 | Flow f; | |
1150 | uint8_t sshbuf1[] = "SSH-2."; | |
1151 | uint32_t sshlen1 = sizeof(sshbuf1) - 1; | |
1152 | uint8_t sshbuf2[] = { "0-MySSHClient-0.5.1\r\n"}; | |
1153 | uint32_t sshlen2 = sizeof(sshbuf2) - 1; | |
1154 | TcpSession ssn; | |
1155 | ||
1156 | memset(&f, 0, sizeof(f)); | |
1157 | memset(&ssn, 0, sizeof(ssn)); | |
1158 | f.protoctx = (void *)&ssn; | |
1159 | ||
1160 | StreamTcpInitConfig(TRUE); | |
1161 | FlowL7DataPtrInit(&f); | |
1162 | ||
1163 | int r = AppLayerParse(&f, ALPROTO_SSH, STREAM_TOSERVER, sshbuf1, sshlen1); | |
1164 | if (r != 0) { | |
1165 | printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r); | |
1166 | goto end; | |
1167 | } | |
1168 | ||
1169 | r = AppLayerParse(&f, ALPROTO_SSH, STREAM_TOSERVER, sshbuf2, sshlen2); | |
1170 | if (r != 0) { | |
1171 | printf("toserver chunk 2 returned %" PRId32 ", expected 0: ", r); | |
1172 | goto end; | |
1173 | } | |
1174 | ||
1175 | SshState *ssh_state = f.aldata[AlpGetStateIdx(ALPROTO_SSH)]; | |
1176 | if (ssh_state == NULL) { | |
1177 | printf("no ssh state: "); | |
1178 | goto end; | |
1179 | } | |
1180 | ||
1181 | if ( !(ssh_state->flags & SSH_FLAG_CLIENT_VERSION_PARSED)) { | |
1182 | printf("Client version string not parsed: "); | |
1183 | goto end; | |
1184 | } | |
1185 | ||
1186 | if (ssh_state->client_software_version == NULL) { | |
1187 | printf("Client version string not parsed: "); | |
1188 | goto end; | |
1189 | } | |
1190 | ||
1191 | if (ssh_state->client_proto_version == NULL) { | |
1192 | printf("Client version string not parsed: "); | |
1193 | goto end; | |
1194 | } | |
1195 | ||
1196 | if (strncmp((char*)ssh_state->client_software_version, "MySSHClient-0.5.1", strlen("MySSHClient-0.5.1")) != 0) { | |
1197 | printf("Client version string not parsed correctly: "); | |
1198 | goto end; | |
1199 | } | |
1200 | ||
1201 | if (strncmp((char*)ssh_state->client_proto_version, "2.0", strlen("2.0")) != 0) { | |
1202 | printf("Client version string not parsed correctly: "); | |
1203 | goto end; | |
1204 | } | |
1205 | ||
1206 | result = 1; | |
1207 | ||
1208 | end: | |
1209 | FlowL7DataPtrFree(&f); | |
1210 | StreamTcpFreeConfig(TRUE); | |
1211 | return result; | |
1212 | } | |
1213 | ||
1214 | /** \test Send a version banner in three chunks. */ | |
1215 | static int SSHParserTest08(void) { | |
1216 | int result = 0; | |
1217 | Flow f; | |
1218 | uint8_t sshbuf1[] = "Welcome to this ssh server\nSSH-"; | |
1219 | uint32_t sshlen1 = sizeof(sshbuf1) - 1; | |
1220 | uint8_t sshbuf2[] = "2."; | |
1221 | uint32_t sshlen2 = sizeof(sshbuf2) - 1; | |
1222 | uint8_t sshbuf3[] = { "0-MySSHClient-0.5.1\r\n"}; | |
1223 | uint32_t sshlen3 = sizeof(sshbuf3) - 1; | |
1224 | TcpSession ssn; | |
1225 | ||
1226 | memset(&f, 0, sizeof(f)); | |
1227 | memset(&ssn, 0, sizeof(ssn)); | |
1228 | f.protoctx = (void *)&ssn; | |
1229 | ||
1230 | StreamTcpInitConfig(TRUE); | |
1231 | FlowL7DataPtrInit(&f); | |
1232 | ||
1233 | int r = AppLayerParse(&f, ALPROTO_SSH, STREAM_TOSERVER, sshbuf1, sshlen1); | |
1234 | if (r != 0) { | |
1235 | printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r); | |
1236 | goto end; | |
1237 | } | |
1238 | ||
1239 | r = AppLayerParse(&f, ALPROTO_SSH, STREAM_TOSERVER, sshbuf2, sshlen2); | |
1240 | if (r != 0) { | |
1241 | printf("toserver chunk 2 returned %" PRId32 ", expected 0: ", r); | |
1242 | goto end; | |
1243 | } | |
1244 | ||
1245 | r = AppLayerParse(&f, ALPROTO_SSH, STREAM_TOSERVER, sshbuf3, sshlen3); | |
1246 | if (r != 0) { | |
1247 | printf("toserver chunk 3 returned %" PRId32 ", expected 0: ", r); | |
1248 | goto end; | |
1249 | } | |
1250 | ||
1251 | SshState *ssh_state = f.aldata[AlpGetStateIdx(ALPROTO_SSH)]; | |
1252 | if (ssh_state == NULL) { | |
1253 | printf("no ssh state: "); | |
1254 | goto end; | |
1255 | } | |
1256 | ||
1257 | if ( !(ssh_state->flags & SSH_FLAG_CLIENT_VERSION_PARSED)) { | |
1258 | printf("Client version string not parsed: "); | |
1259 | goto end; | |
1260 | } | |
1261 | ||
1262 | if (ssh_state->client_software_version == NULL) { | |
1263 | printf("Client version string not parsed: "); | |
1264 | goto end; | |
1265 | } | |
1266 | ||
1267 | if (ssh_state->client_proto_version == NULL) { | |
1268 | printf("Client version string not parsed: "); | |
1269 | goto end; | |
1270 | } | |
1271 | ||
1272 | if (strncmp((char*)ssh_state->client_software_version, "MySSHClient-0.5.1", strlen("MySSHClient-0.5.1")) != 0) { | |
1273 | printf("Client version string not parsed correctly: "); | |
1274 | goto end; | |
1275 | } | |
1276 | ||
1277 | if (strncmp((char*)ssh_state->client_proto_version, "2.0", strlen("2.0")) != 0) { | |
1278 | printf("Client version string not parsed correctly: "); | |
1279 | goto end; | |
1280 | } | |
1281 | ||
1282 | result = 1; | |
1283 | end: | |
1284 | FlowL7DataPtrFree(&f); | |
1285 | StreamTcpFreeConfig(TRUE); | |
1286 | return result; | |
1287 | } | |
1288 | ||
1289 | static int SSHParserTest09(void) { | |
1290 | int result = 0; | |
1291 | Flow f; | |
1292 | uint8_t sshbuf1[] = "SSH-2."; | |
1293 | uint32_t sshlen1 = sizeof(sshbuf1) - 1; | |
1294 | uint8_t sshbuf2[] = { "0-MySSHClient-0.5.1\r\n"}; | |
1295 | uint32_t sshlen2 = sizeof(sshbuf2) - 1; | |
1296 | TcpSession ssn; | |
1297 | ||
1298 | memset(&f, 0, sizeof(f)); | |
1299 | memset(&ssn, 0, sizeof(ssn)); | |
1300 | f.protoctx = (void *)&ssn; | |
1301 | ||
1302 | StreamTcpInitConfig(TRUE); | |
1303 | FlowL7DataPtrInit(&f); | |
1304 | ||
1305 | int r = AppLayerParse(&f, ALPROTO_SSH, STREAM_TOCLIENT, sshbuf1, sshlen1); | |
1306 | if (r != 0) { | |
1307 | printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r); | |
1308 | goto end; | |
1309 | } | |
1310 | ||
1311 | r = AppLayerParse(&f, ALPROTO_SSH, STREAM_TOCLIENT, sshbuf2, sshlen2); | |
1312 | if (r != 0) { | |
1313 | printf("toserver chunk 2 returned %" PRId32 ", expected 0: ", r); | |
1314 | goto end; | |
1315 | } | |
1316 | ||
1317 | SshState *ssh_state = f.aldata[AlpGetStateIdx(ALPROTO_SSH)]; | |
1318 | if (ssh_state == NULL) { | |
1319 | printf("no ssh state: "); | |
1320 | goto end; | |
1321 | } | |
1322 | ||
1323 | if ( !(ssh_state->flags & SSH_FLAG_SERVER_VERSION_PARSED)) { | |
1324 | printf("Client version string not parsed: "); | |
1325 | goto end; | |
1326 | } | |
1327 | ||
1328 | if (ssh_state->server_software_version == NULL) { | |
1329 | printf("Client version string not parsed: "); | |
1330 | goto end; | |
1331 | } | |
1332 | ||
1333 | if (ssh_state->server_proto_version == NULL) { | |
1334 | printf("Client version string not parsed: "); | |
1335 | goto end; | |
1336 | } | |
1337 | ||
1338 | if (strncmp((char*)ssh_state->server_software_version, "MySSHClient-0.5.1", strlen("MySSHClient-0.5.1")) != 0) { | |
1339 | printf("Client version string not parsed correctly: "); | |
1340 | goto end; | |
1341 | } | |
1342 | ||
1343 | if (strncmp((char*)ssh_state->server_proto_version, "2.0", strlen("2.0")) != 0) { | |
1344 | printf("Client version string not parsed correctly: "); | |
1345 | goto end; | |
1346 | } | |
1347 | ||
1348 | result = 1; | |
1349 | ||
1350 | end: | |
1351 | FlowL7DataPtrFree(&f); | |
1352 | StreamTcpFreeConfig(TRUE); | |
1353 | return result; | |
1354 | } | |
1355 | ||
1356 | /** \test Send a version banner in three chunks. */ | |
1357 | static int SSHParserTest10(void) { | |
1358 | int result = 0; | |
1359 | Flow f; | |
1360 | uint8_t sshbuf1[] = "Welcome to this ssh server\nSSH-"; | |
1361 | uint32_t sshlen1 = sizeof(sshbuf1) - 1; | |
1362 | uint8_t sshbuf2[] = "2."; | |
1363 | uint32_t sshlen2 = sizeof(sshbuf2) - 1; | |
1364 | uint8_t sshbuf3[] = { "0-MySSHClient-0.5.1\r\n"}; | |
1365 | uint32_t sshlen3 = sizeof(sshbuf3) - 1; | |
1366 | TcpSession ssn; | |
1367 | ||
1368 | memset(&f, 0, sizeof(f)); | |
1369 | memset(&ssn, 0, sizeof(ssn)); | |
1370 | f.protoctx = (void *)&ssn; | |
1371 | ||
1372 | StreamTcpInitConfig(TRUE); | |
1373 | FlowL7DataPtrInit(&f); | |
1374 | ||
1375 | int r = AppLayerParse(&f, ALPROTO_SSH, STREAM_TOCLIENT, sshbuf1, sshlen1); | |
1376 | if (r != 0) { | |
1377 | printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r); | |
1378 | goto end; | |
1379 | } | |
1380 | ||
1381 | r = AppLayerParse(&f, ALPROTO_SSH, STREAM_TOCLIENT, sshbuf2, sshlen2); | |
1382 | if (r != 0) { | |
1383 | printf("toserver chunk 2 returned %" PRId32 ", expected 0: ", r); | |
1384 | goto end; | |
1385 | } | |
1386 | ||
1387 | r = AppLayerParse(&f, ALPROTO_SSH, STREAM_TOCLIENT, sshbuf3, sshlen3); | |
1388 | if (r != 0) { | |
1389 | printf("toserver chunk 3 returned %" PRId32 ", expected 0: ", r); | |
1390 | goto end; | |
1391 | } | |
1392 | ||
1393 | SshState *ssh_state = f.aldata[AlpGetStateIdx(ALPROTO_SSH)]; | |
1394 | if (ssh_state == NULL) { | |
1395 | printf("no ssh state: "); | |
1396 | goto end; | |
1397 | } | |
1398 | ||
1399 | if ( !(ssh_state->flags & SSH_FLAG_SERVER_VERSION_PARSED)) { | |
1400 | printf("Client version string not parsed: "); | |
1401 | goto end; | |
1402 | } | |
1403 | ||
1404 | if (ssh_state->server_software_version == NULL) { | |
1405 | printf("Client version string not parsed: "); | |
1406 | goto end; | |
1407 | } | |
1408 | ||
1409 | if (ssh_state->server_proto_version == NULL) { | |
1410 | printf("Client version string not parsed: "); | |
1411 | goto end; | |
1412 | } | |
1413 | ||
1414 | if (strncmp((char*)ssh_state->server_software_version, "MySSHClient-0.5.1", strlen("MySSHClient-0.5.1")) != 0) { | |
1415 | printf("Client version string not parsed correctly: "); | |
1416 | goto end; | |
1417 | } | |
1418 | ||
1419 | if (strncmp((char*)ssh_state->server_proto_version, "2.0", strlen("2.0")) != 0) { | |
1420 | printf("Client version string not parsed correctly: "); | |
1421 | goto end; | |
1422 | } | |
1423 | ||
1424 | result = 1; | |
1425 | end: | |
1426 | FlowL7DataPtrFree(&f); | |
1427 | StreamTcpFreeConfig(TRUE); | |
1428 | return result; | |
1429 | } | |
1430 | ||
1431 | /** \test Send a banner and record in three chunks. */ | |
1432 | static int SSHParserTest11(void) { | |
1433 | int result = 0; | |
1434 | Flow f; | |
1435 | uint8_t sshbuf1[] = "Welcome to this ssh server\nSSH-"; | |
1436 | uint32_t sshlen1 = sizeof(sshbuf1) - 1; | |
1437 | uint8_t sshbuf2[] = "2.0-MySSHClient-0.5.1\r\n"; | |
1438 | uint32_t sshlen2 = sizeof(sshbuf2) - 1; | |
1439 | uint8_t sshbuf3[] = { 0x00, 0x00, 0x00, 0x03,0x01, 21, 0x00}; | |
1440 | uint32_t sshlen3 = sizeof(sshbuf3); | |
1441 | TcpSession ssn; | |
1442 | ||
1443 | memset(&f, 0, sizeof(f)); | |
1444 | memset(&ssn, 0, sizeof(ssn)); | |
1445 | f.protoctx = (void *)&ssn; | |
1446 | ||
1447 | StreamTcpInitConfig(TRUE); | |
1448 | FlowL7DataPtrInit(&f); | |
1449 | ||
1450 | int r = AppLayerParse(&f, ALPROTO_SSH, STREAM_TOSERVER, sshbuf1, sshlen1); | |
1451 | if (r != 0) { | |
1452 | printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r); | |
1453 | goto end; | |
1454 | } | |
1455 | ||
1456 | r = AppLayerParse(&f, ALPROTO_SSH, STREAM_TOSERVER, sshbuf2, sshlen2); | |
1457 | if (r != 0) { | |
1458 | printf("toserver chunk 2 returned %" PRId32 ", expected 0: ", r); | |
1459 | goto end; | |
1460 | } | |
1461 | ||
1462 | r = AppLayerParse(&f, ALPROTO_SSH, STREAM_TOSERVER, sshbuf3, sshlen3); | |
1463 | if (r != 0) { | |
1464 | printf("toserver chunk 3 returned %" PRId32 ", expected 0: ", r); | |
1465 | goto end; | |
1466 | } | |
1467 | ||
1468 | SshState *ssh_state = f.aldata[AlpGetStateIdx(ALPROTO_SSH)]; | |
1469 | if (ssh_state == NULL) { | |
1470 | printf("no ssh state: "); | |
1471 | goto end; | |
1472 | } | |
1473 | ||
1474 | if ( !(ssh_state->flags & SSH_FLAG_CLIENT_VERSION_PARSED)) { | |
1475 | printf("Client version string not parsed: "); | |
1476 | goto end; | |
1477 | } | |
1478 | ||
1479 | if (ssh_state->client_software_version == NULL) { | |
1480 | printf("Client version string not parsed: "); | |
1481 | goto end; | |
1482 | } | |
1483 | ||
1484 | if (ssh_state->client_proto_version == NULL) { | |
1485 | printf("Client version string not parsed: "); | |
1486 | goto end; | |
1487 | } | |
1488 | ||
1489 | if (strncmp((char*)ssh_state->client_software_version, "MySSHClient-0.5.1", strlen("MySSHClient-0.5.1")) != 0) { | |
1490 | printf("Client version string not parsed correctly: "); | |
1491 | goto end; | |
1492 | } | |
1493 | ||
1494 | if (strncmp((char*)ssh_state->client_proto_version, "2.0", strlen("2.0")) != 0) { | |
1495 | printf("Client version string not parsed correctly: "); | |
1496 | goto end; | |
1497 | } | |
1498 | ||
1499 | if ( !(ssh_state->flags & SSH_FLAG_PARSER_DONE)) { | |
1500 | printf("Didn't detect the msg code of new keys (ciphered data starts): "); | |
1501 | goto end; | |
1502 | } | |
1503 | ||
1504 | result = 1; | |
1505 | end: | |
1506 | FlowL7DataPtrFree(&f); | |
1507 | StreamTcpFreeConfig(TRUE); | |
1508 | return result; | |
1509 | } | |
1510 | ||
1511 | /** \test Send a banner and 2 records record in four chunks. */ | |
1512 | static int SSHParserTest12(void) { | |
1513 | int result = 0; | |
1514 | Flow f; | |
1515 | uint8_t sshbuf1[] = "Welcome to this ssh server\nSSH-"; | |
1516 | uint32_t sshlen1 = sizeof(sshbuf1) - 1; | |
1517 | uint8_t sshbuf2[] = "2.0-MySSHClient-0.5.1\r\n"; | |
1518 | uint32_t sshlen2 = sizeof(sshbuf2) - 1; | |
1519 | uint8_t sshbuf3[] = { 0x00, 0x00, 0x00, 0x03,0x01, 17, 0x00}; | |
1520 | uint32_t sshlen3 = sizeof(sshbuf3); | |
1521 | uint8_t sshbuf4[] = { 0x00, 0x00, 0x00, 0x03,0x01, 21, 0x00}; | |
1522 | uint32_t sshlen4 = sizeof(sshbuf4); | |
1523 | TcpSession ssn; | |
1524 | ||
1525 | memset(&f, 0, sizeof(f)); | |
1526 | memset(&ssn, 0, sizeof(ssn)); | |
1527 | f.protoctx = (void *)&ssn; | |
1528 | ||
1529 | StreamTcpInitConfig(TRUE); | |
1530 | FlowL7DataPtrInit(&f); | |
1531 | ||
1532 | int r = AppLayerParse(&f, ALPROTO_SSH, STREAM_TOSERVER, sshbuf1, sshlen1); | |
1533 | if (r != 0) { | |
1534 | printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r); | |
1535 | goto end; | |
1536 | } | |
1537 | ||
1538 | r = AppLayerParse(&f, ALPROTO_SSH, STREAM_TOSERVER, sshbuf2, sshlen2); | |
1539 | if (r != 0) { | |
1540 | printf("toserver chunk 2 returned %" PRId32 ", expected 0: ", r); | |
1541 | goto end; | |
1542 | } | |
1543 | ||
1544 | r = AppLayerParse(&f, ALPROTO_SSH, STREAM_TOSERVER, sshbuf3, sshlen3); | |
1545 | if (r != 0) { | |
1546 | printf("toserver chunk 3 returned %" PRId32 ", expected 0: ", r); | |
1547 | goto end; | |
1548 | } | |
1549 | ||
1550 | r = AppLayerParse(&f, ALPROTO_SSH, STREAM_TOSERVER, sshbuf4, sshlen4); | |
1551 | if (r != 0) { | |
1552 | printf("toserver chunk 4 returned %" PRId32 ", expected 0: ", r); | |
1553 | goto end; | |
1554 | } | |
1555 | ||
1556 | SshState *ssh_state = f.aldata[AlpGetStateIdx(ALPROTO_SSH)]; | |
1557 | if (ssh_state == NULL) { | |
1558 | printf("no ssh state: "); | |
1559 | goto end; | |
1560 | } | |
1561 | ||
1562 | if ( !(ssh_state->flags & SSH_FLAG_CLIENT_VERSION_PARSED)) { | |
1563 | printf("Client version string not parsed: "); | |
1564 | goto end; | |
1565 | } | |
1566 | ||
1567 | if (ssh_state->client_software_version == NULL) { | |
1568 | printf("Client version string not parsed: "); | |
1569 | goto end; | |
1570 | } | |
1571 | ||
1572 | if (ssh_state->client_proto_version == NULL) { | |
1573 | printf("Client version string not parsed: "); | |
1574 | goto end; | |
1575 | } | |
1576 | ||
1577 | if (strncmp((char*)ssh_state->client_software_version, "MySSHClient-0.5.1", strlen("MySSHClient-0.5.1")) != 0) { | |
1578 | printf("Client version string not parsed correctly: "); | |
1579 | goto end; | |
1580 | } | |
1581 | ||
1582 | if (strncmp((char*)ssh_state->client_proto_version, "2.0", strlen("2.0")) != 0) { | |
1583 | printf("Client version string not parsed correctly: "); | |
1584 | goto end; | |
1585 | } | |
1586 | ||
1587 | if ( !(ssh_state->flags & SSH_FLAG_PARSER_DONE)) { | |
1588 | printf("Didn't detect the msg code of new keys (ciphered data starts): "); | |
1589 | goto end; | |
1590 | } | |
1591 | ||
1592 | result = 1; | |
1593 | end: | |
1594 | FlowL7DataPtrFree(&f); | |
1595 | StreamTcpFreeConfig(TRUE); | |
1596 | return result; | |
1597 | } | |
1598 | ||
1599 | /** \test Send toserver a banner and record in three chunks. */ | |
1600 | static int SSHParserTest13(void) { | |
1601 | int result = 0; | |
1602 | Flow f; | |
1603 | uint8_t sshbuf1[] = "Welcome to this ssh server\nSSH-"; | |
1604 | uint32_t sshlen1 = sizeof(sshbuf1) - 1; | |
1605 | uint8_t sshbuf2[] = "2.0-MySSHClient-0.5.1\r\n"; | |
1606 | uint32_t sshlen2 = sizeof(sshbuf2) - 1; | |
1607 | uint8_t sshbuf3[] = { 0x00, 0x00, 0x00, 0x03,0x01, 21, 0x00}; | |
1608 | uint32_t sshlen3 = sizeof(sshbuf3); | |
1609 | TcpSession ssn; | |
1610 | ||
1611 | memset(&f, 0, sizeof(f)); | |
1612 | memset(&ssn, 0, sizeof(ssn)); | |
1613 | f.protoctx = (void *)&ssn; | |
1614 | ||
1615 | StreamTcpInitConfig(TRUE); | |
1616 | FlowL7DataPtrInit(&f); | |
1617 | ||
1618 | int r = AppLayerParse(&f, ALPROTO_SSH, STREAM_TOCLIENT, sshbuf1, sshlen1); | |
1619 | if (r != 0) { | |
1620 | printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r); | |
1621 | goto end; | |
1622 | } | |
1623 | ||
1624 | r = AppLayerParse(&f, ALPROTO_SSH, STREAM_TOCLIENT, sshbuf2, sshlen2); | |
1625 | if (r != 0) { | |
1626 | printf("toserver chunk 2 returned %" PRId32 ", expected 0: ", r); | |
1627 | goto end; | |
1628 | } | |
1629 | ||
1630 | r = AppLayerParse(&f, ALPROTO_SSH, STREAM_TOCLIENT, sshbuf3, sshlen3); | |
1631 | if (r != 0) { | |
1632 | printf("toserver chunk 3 returned %" PRId32 ", expected 0: ", r); | |
1633 | goto end; | |
1634 | } | |
1635 | ||
1636 | SshState *ssh_state = f.aldata[AlpGetStateIdx(ALPROTO_SSH)]; | |
1637 | if (ssh_state == NULL) { | |
1638 | printf("no ssh state: "); | |
1639 | goto end; | |
1640 | } | |
1641 | ||
1642 | if ( !(ssh_state->flags & SSH_FLAG_SERVER_VERSION_PARSED)) { | |
1643 | printf("Client version string not parsed: "); | |
1644 | goto end; | |
1645 | } | |
1646 | ||
1647 | if (ssh_state->server_software_version == NULL) { | |
1648 | printf("Client version string not parsed: "); | |
1649 | goto end; | |
1650 | } | |
1651 | ||
1652 | if (ssh_state->server_proto_version == NULL) { | |
1653 | printf("Client version string not parsed: "); | |
1654 | goto end; | |
1655 | } | |
1656 | ||
1657 | if (strncmp((char*)ssh_state->server_software_version, "MySSHClient-0.5.1", strlen("MySSHClient-0.5.1")) != 0) { | |
1658 | printf("Client version string not parsed correctly: "); | |
1659 | goto end; | |
1660 | } | |
1661 | ||
1662 | if (strncmp((char*)ssh_state->server_proto_version, "2.0", strlen("2.0")) != 0) { | |
1663 | printf("Client version string not parsed correctly: "); | |
1664 | goto end; | |
1665 | } | |
1666 | ||
1667 | if ( !(ssh_state->flags & SSH_FLAG_PARSER_DONE)) { | |
1668 | printf("Didn't detect the msg code of new keys (ciphered data starts): "); | |
1669 | goto end; | |
1670 | } | |
1671 | ||
1672 | result = 1; | |
1673 | end: | |
1674 | FlowL7DataPtrFree(&f); | |
1675 | StreamTcpFreeConfig(TRUE); | |
1676 | return result; | |
1677 | } | |
1678 | ||
1679 | /** \test Send toserver a banner and 2 records record in four chunks. */ | |
1680 | static int SSHParserTest14(void) { | |
1681 | int result = 0; | |
1682 | Flow f; | |
1683 | uint8_t sshbuf1[] = "Welcome to this ssh server\nSSH-"; | |
1684 | uint32_t sshlen1 = sizeof(sshbuf1) - 1; | |
1685 | uint8_t sshbuf2[] = "2.0-MySSHClient-0.5.1\r\n"; | |
1686 | uint32_t sshlen2 = sizeof(sshbuf2) - 1; | |
1687 | uint8_t sshbuf3[] = { 0x00, 0x00, 0x00, 0x03,0x01, 17, 0x00}; | |
1688 | uint32_t sshlen3 = sizeof(sshbuf3); | |
1689 | uint8_t sshbuf4[] = { 0x00, 0x00, 0x00, 0x03,0x01, 21, 0x00}; | |
1690 | uint32_t sshlen4 = sizeof(sshbuf4); | |
1691 | TcpSession ssn; | |
1692 | ||
1693 | memset(&f, 0, sizeof(f)); | |
1694 | memset(&ssn, 0, sizeof(ssn)); | |
1695 | f.protoctx = (void *)&ssn; | |
1696 | ||
1697 | StreamTcpInitConfig(TRUE); | |
1698 | FlowL7DataPtrInit(&f); | |
1699 | ||
1700 | int r = AppLayerParse(&f, ALPROTO_SSH, STREAM_TOCLIENT, sshbuf1, sshlen1); | |
1701 | if (r != 0) { | |
1702 | printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r); | |
1703 | goto end; | |
1704 | } | |
1705 | ||
1706 | r = AppLayerParse(&f, ALPROTO_SSH, STREAM_TOCLIENT, sshbuf2, sshlen2); | |
1707 | if (r != 0) { | |
1708 | printf("toserver chunk 2 returned %" PRId32 ", expected 0: ", r); | |
1709 | goto end; | |
1710 | } | |
1711 | ||
1712 | r = AppLayerParse(&f, ALPROTO_SSH, STREAM_TOCLIENT, sshbuf3, sshlen3); | |
1713 | if (r != 0) { | |
1714 | printf("toserver chunk 3 returned %" PRId32 ", expected 0: ", r); | |
1715 | goto end; | |
1716 | } | |
1717 | ||
1718 | r = AppLayerParse(&f, ALPROTO_SSH, STREAM_TOCLIENT, sshbuf4, sshlen4); | |
1719 | if (r != 0) { | |
1720 | printf("toserver chunk 4 returned %" PRId32 ", expected 0: ", r); | |
1721 | goto end; | |
1722 | } | |
1723 | ||
1724 | SshState *ssh_state = f.aldata[AlpGetStateIdx(ALPROTO_SSH)]; | |
1725 | if (ssh_state == NULL) { | |
1726 | printf("no ssh state: "); | |
1727 | goto end; | |
1728 | } | |
1729 | ||
1730 | if ( !(ssh_state->flags & SSH_FLAG_SERVER_VERSION_PARSED)) { | |
1731 | printf("Client version string not parsed: "); | |
1732 | goto end; | |
1733 | } | |
1734 | ||
1735 | if (ssh_state->server_software_version == NULL) { | |
1736 | printf("Client version string not parsed: "); | |
1737 | goto end; | |
1738 | } | |
1739 | ||
1740 | if (ssh_state->server_proto_version == NULL) { | |
1741 | printf("Client version string not parsed: "); | |
1742 | goto end; | |
1743 | } | |
1744 | ||
1745 | if (strncmp((char*)ssh_state->server_software_version, "MySSHClient-0.5.1", strlen("MySSHClient-0.5.1")) != 0) { | |
1746 | printf("Client version string not parsed correctly: "); | |
1747 | goto end; | |
1748 | } | |
1749 | ||
1750 | if (strncmp((char*)ssh_state->server_proto_version, "2.0", strlen("2.0")) != 0) { | |
1751 | printf("Client version string not parsed correctly: "); | |
1752 | goto end; | |
1753 | } | |
1754 | ||
1755 | if ( !(ssh_state->flags & SSH_FLAG_PARSER_DONE)) { | |
1756 | printf("Didn't detect the msg code of new keys (ciphered data starts): "); | |
1757 | goto end; | |
1758 | } | |
1759 | ||
1760 | result = 1; | |
1761 | end: | |
1762 | FlowL7DataPtrFree(&f); | |
1763 | StreamTcpFreeConfig(TRUE); | |
1764 | return result; | |
1765 | } | |
1766 | ||
1767 | #endif /* UNITTESTS */ | |
1768 | ||
1769 | void SSHParserRegisterTests(void) { | |
1770 | #ifdef UNITTESTS | |
1771 | UtRegisterTest("SSHParserTest01 - ToServer", SSHParserTest01, 1); | |
1772 | UtRegisterTest("SSHParserTest02 - ToServer", SSHParserTest02, 1); | |
1773 | UtRegisterTest("SSHParserTest03 - ToServer", SSHParserTest03, 1); | |
1774 | UtRegisterTest("SSHParserTest04 - ToClient", SSHParserTest04, 1); | |
1775 | UtRegisterTest("SSHParserTest05 - ToClient", SSHParserTest05, 1); | |
1776 | UtRegisterTest("SSHParserTest06 - ToClient", SSHParserTest06, 1); | |
1777 | UtRegisterTest("SSHParserTest07 - ToServer 2 chunks", SSHParserTest07, 1); | |
1778 | UtRegisterTest("SSHParserTest08 - ToServer 3 chunks", SSHParserTest08, 1); | |
1779 | UtRegisterTest("SSHParserTest09 - ToClient 2 chunks", SSHParserTest09, 1); | |
1780 | UtRegisterTest("SSHParserTest10 - ToClient 3 chunks", SSHParserTest10, 1); | |
1781 | UtRegisterTest("SSHParserTest11 - ToClient 4 chunks", SSHParserTest11, 1); | |
1782 | UtRegisterTest("SSHParserTest12 - ToClient 4 chunks", SSHParserTest12, 1); | |
1783 | UtRegisterTest("SSHParserTest13 - ToClient 4 chunks", SSHParserTest13, 1); | |
1784 | UtRegisterTest("SSHParserTest14 - ToClient 4 chunks", SSHParserTest14, 1); | |
1785 | #endif /* UNITTESTS */ | |
1786 | } |
0 | /* Copyright (C) 2007-2010 Open Information Security Foundation | |
1 | * | |
2 | * You can copy, redistribute or modify this Program under the terms of | |
3 | * the GNU General Public License version 2 as published by the Free | |
4 | * Software Foundation. | |
5 | * | |
6 | * This program is distributed in the hope that it will be useful, | |
7 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
8 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
9 | * GNU General Public License for more details. | |
10 | * | |
11 | * You should have received a copy of the GNU General Public License | |
12 | * version 2 along with this program; if not, write to the Free Software | |
13 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA | |
14 | * 02110-1301, USA. | |
15 | */ | |
16 | ||
17 | /** | |
18 | * \file | |
19 | * | |
20 | * \author Pablo Rincon <pablo.rincon.crespo@gmail.com> | |
21 | */ | |
22 | ||
23 | #ifndef __APP_LAYER_SSH_H__ | |
24 | #define __APP_LAYER_SSH_H__ | |
25 | ||
26 | #define SSH_FLAG_SERVER_CHANGE_CIPHER_SPEC 0x01 /**< Flag to indicate that | |
27 | server will now on sends | |
28 | encrypted msgs. */ | |
29 | #define SSH_FLAG_CLIENT_CHANGE_CIPHER_SPEC 0x02 /**< Flag to indicate that | |
30 | client will now on sends | |
31 | encrypted msgs. */ | |
32 | ||
33 | #define SSH_FLAG_CLIENT_VERSION_PARSED 0x01 | |
34 | #define SSH_FLAG_SERVER_VERSION_PARSED 0x02 | |
35 | ||
36 | /* This flags indicate that the rest of the communication | |
37 | * must be ciphered, so the parsing finish here */ | |
38 | #define SSH_FLAG_PARSER_DONE 0x04 | |
39 | ||
40 | /* MSG_CODE */ | |
41 | #define SSH_MSG_NEWKEYS 21 | |
42 | ||
43 | enum { | |
44 | SSH_FIELD_NONE = 0, | |
45 | SSH_FIELD_SERVER_VER_STATE_LINE, | |
46 | SSH_FIELD_CLIENT_VER_STATE_LINE, | |
47 | SSH_FIELD_SERVER_PKT_LENGTH, | |
48 | SSH_FIELD_CLIENT_PKT_LENGTH, | |
49 | SSH_FIELD_SERVER_PADDING_LENGTH, | |
50 | SSH_FIELD_CLIENT_PADDING_LENGTH, | |
51 | SSH_FIELD_SERVER_PAYLOAD, | |
52 | SSH_FIELD_CLIENT_PAYLOAD, | |
53 | ||
54 | /* must be last */ | |
55 | SSH_FIELD_MAX, | |
56 | }; | |
57 | ||
58 | /** From SSH-TRANSP rfc | |
59 | ||
60 | SSH Bunary packet structure: | |
61 | uint32 packet_length | |
62 | byte padding_length | |
63 | byte[n1] payload; n1 = packet_length - padding_length - 1 | |
64 | byte[n2] random padding; n2 = padding_length | |
65 | byte[m] mac (Message Authentication Code - MAC); m = mac_length | |
66 | ||
67 | So we are going to do a header struct to store | |
68 | the lenghts and msg_code (inside payload, if any) | |
69 | */ | |
70 | ||
71 | typedef struct SSHHeader_ { | |
72 | uint32_t pkt_len; | |
73 | uint8_t padding_len; | |
74 | uint8_t msg_code; | |
75 | } SshHeader; | |
76 | ||
77 | /** structure to store the SSH state values */ | |
78 | typedef struct SshState_ { | |
79 | uint8_t client_msg_code; /**< Client content type storage field */ | |
80 | uint8_t *client_proto_version; /**< Client SSH version storage field */ | |
81 | uint8_t *client_software_version; /**< Client SSH version storage field */ | |
82 | ||
83 | uint8_t server_msg_code; /**< Server content type storage field */ | |
84 | uint8_t *server_proto_version; /**< Server SSH version storage field */ | |
85 | uint8_t *server_software_version; /**< Server SSH version storage field */ | |
86 | ||
87 | uint8_t flags; /**< Flags to indicate the current SSH | |
88 | sessoin state */ | |
89 | SshHeader srv_hdr; | |
90 | SshHeader cli_hdr; | |
91 | } SshState; | |
92 | ||
93 | void RegisterSSHParsers(void); | |
94 | void SSHParserRegisterTests(void); | |
95 | ||
96 | #endif /* __APP_LAYER_SSH_H__ */ | |
97 |
31 | 31 | #include "flow.h" |
32 | 32 | |
33 | 33 | #include "util-debug.h" |
34 | ||
35 | extern uint8_t engine_mode; | |
34 | 36 | |
35 | 37 | /** \brief Get the active app layer proto from the packet |
36 | 38 | * \param p packet pointer |
208 | 208 | char *msg; |
209 | 209 | char *class_msg; |
210 | 210 | Reference *references; |
211 | uint8_t flags; | |
211 | 212 | } PacketAlert; |
213 | ||
214 | /* After processing an alert by the thresholding module, if at | |
215 | * last it gets triggered, we might want to stick the drop action to | |
216 | * the flow on IPS mode */ | |
217 | #define PACKET_ALERT_FLAG_DROP_FLOW 0x01 | |
212 | 218 | |
213 | 219 | #define PACKET_ALERT_MAX 256 |
214 | 220 |
63 | 63 | return MpmPatternIdStoreGetMaxId(de_ctx->mpm_pattern_id_store); |
64 | 64 | } |
65 | 65 | |
66 | /** | |
67 | * \brief DetectContentParse | |
68 | * \initonly | |
69 | */ | |
66 | 70 | DetectContentData *DetectContentParse (char *contentstr) |
67 | 71 | { |
68 | 72 | DetectContentData *cd = NULL; |
73 | 77 | uint16_t slen = 0; |
74 | 78 | |
75 | 79 | if ((temp = SCStrdup(contentstr)) == NULL) { |
76 | SCLogError(SC_ERR_MEM_ALLOC, "Error allocating memory"); | |
77 | goto error; | |
80 | SCLogError(SC_ERR_MEM_ALLOC, "Error allocating memory. Exiting..."); | |
81 | exit(EXIT_FAILURE); | |
78 | 82 | } |
79 | 83 | |
80 | 84 | if (strlen(temp) == 0) { |
83 | 87 | } |
84 | 88 | |
85 | 89 | cd = SCMalloc(sizeof(DetectContentData)); |
86 | if (cd == NULL) | |
87 | goto error; | |
90 | if (cd == NULL) { | |
91 | SCLogError(SC_ERR_MEM_ALLOC, "Error allocating memory. Exiting..."); | |
92 | exit(EXIT_FAILURE); | |
93 | } | |
94 | ||
88 | 95 | memset(cd, 0, sizeof(DetectContentData)); |
89 | 96 | |
90 | 97 | /* skip the first spaces */ |
95 | 102 | |
96 | 103 | if (temp[pos] == '!') { |
97 | 104 | SCFree(temp); |
98 | if ((temp = SCStrdup(contentstr + pos + 1)) == NULL) | |
99 | goto error; | |
105 | if ((temp = SCStrdup(contentstr + pos + 1)) == NULL) { | |
106 | SCLogError(SC_ERR_MEM_ALLOC, "error allocating memory. exiting..."); | |
107 | exit(EXIT_FAILURE); | |
108 | } | |
100 | 109 | |
101 | 110 | cd->flags |= DETECT_CONTENT_NEGATED; |
102 | 111 | } |
103 | 112 | |
104 | 113 | if (temp[pos] == '\"' && temp[strlen(temp)-1] == '\"') { |
105 | if ((str = SCStrdup(temp + pos + 1)) == NULL) | |
106 | goto error; | |
114 | if ((str = SCStrdup(temp + pos + 1)) == NULL) { | |
115 | SCLogError(SC_ERR_MEM_ALLOC, "error allocating memory. exiting..."); | |
116 | exit(EXIT_FAILURE); | |
117 | } | |
118 | ||
107 | 119 | str[strlen(temp) - pos - 2] = '\0'; |
108 | 120 | } else { |
109 | if ((str = SCStrdup(temp + pos)) == NULL) | |
110 | goto error; | |
121 | if ((str = SCStrdup(temp + pos)) == NULL) { | |
122 | SCLogError(SC_ERR_MEM_ALLOC, "error allocating memory. exiting..."); | |
123 | exit(EXIT_FAILURE); | |
124 | } | |
111 | 125 | } |
112 | 126 | |
113 | 127 | SCFree(temp); |
215 | 229 | } |
216 | 230 | |
217 | 231 | cd->content = SCMalloc(len); |
218 | if (cd->content == NULL) | |
219 | goto error; | |
232 | if (cd->content == NULL) { | |
233 | SCLogError(SC_ERR_MEM_ALLOC, "error allocating memory. exiting..."); | |
234 | exit(EXIT_FAILURE); | |
235 | } | |
220 | 236 | |
221 | 237 | memcpy(cd->content, str, len); |
222 | 238 | cd->content_len = len; |
152 | 152 | &s->pmatch, &s->pmatch_tail, |
153 | 153 | &s->dmatch, &s->dmatch_tail); |
154 | 154 | pm = pm1; |
155 | } else if (pm2_ots->idx > dcem->idx) { | |
155 | } else { | |
156 | 156 | /* within is against pm1, pm = pm1 */ |
157 | 157 | pm = pm1; |
158 | 158 | } |
109 | 109 | return match; |
110 | 110 | } |
111 | 111 | |
112 | int PacketAlertAppend(DetectEngineThreadCtx *det_ctx, Signature *s, Packet *p) | |
112 | int PacketAlertAppend(DetectEngineThreadCtx *det_ctx, Signature *s, Packet *p, uint8_t flags) | |
113 | 113 | { |
114 | 114 | int i = 0; |
115 | 115 | |
137 | 137 | p->alerts.alerts[p->alerts.cnt].class = s->class; |
138 | 138 | p->alerts.alerts[p->alerts.cnt].class_msg = s->class_msg; |
139 | 139 | p->alerts.alerts[p->alerts.cnt].references = s->references; |
140 | p->alerts.alerts[p->alerts.cnt].flags = flags; | |
140 | 141 | } else { |
141 | 142 | /* We need to make room for this s->num |
142 | 143 | (a bit ugly with mamcpy but we are planning changes here)*/ |
161 | 162 | p->alerts.alerts[i].class = s->class; |
162 | 163 | p->alerts.alerts[i].class_msg = s->class_msg; |
163 | 164 | p->alerts.alerts[i].references = s->references; |
165 | p->alerts.alerts[i].flags = flags; | |
164 | 166 | } |
165 | 167 | |
166 | 168 | /* Update the count */ |
255 | 257 | * so we ignore the rest with less prio */ |
256 | 258 | p->alerts.cnt = i; |
257 | 259 | break; |
260 | } else if ( ((p->alerts.alerts[i].flags & PACKET_ALERT_FLAG_DROP_FLOW) || | |
261 | (s->flags & SIG_FLAG_APPLAYER)) | |
262 | && p->flow != NULL) | |
263 | { | |
264 | SCMutexLock(&p->flow->m); | |
265 | /* This will apply only on IPS mode (check StreamTcpPacket) */ | |
266 | p->flow->flags |= FLOW_ACTION_DROP; | |
267 | SCMutexUnlock(&p->flow->m); | |
258 | 268 | } |
259 | 269 | } |
260 | 270 | /* Because we removed the alert from the array, we should |
27 | 27 | #include "detect.h" |
28 | 28 | |
29 | 29 | void PacketAlertFinalize(DetectEngineCtx *, DetectEngineThreadCtx *, Packet *); |
30 | int PacketAlertAppend(DetectEngineThreadCtx *, Signature *, Packet *); | |
30 | int PacketAlertAppend(DetectEngineThreadCtx *, Signature *, Packet *, uint8_t); | |
31 | 31 | int PacketAlertAppendTag(Packet *, PacketAlert *); |
32 | 32 | int PacketAlertCheck(Packet *, uint32_t); |
33 | 33 | int PacketAlertRemove(Packet *, uint16_t); |
982 | 982 | u * 8 + i, s->id, s->msg); |
983 | 983 | |
984 | 984 | if ( !(s->flags & SIG_FLAG_NOALERT)) { |
985 | PacketAlertAppend(det_ctx, s, p); | |
985 | if (s->action & ACTION_DROP) | |
986 | PacketAlertAppend(det_ctx, s, p, PACKET_ALERT_FLAG_DROP_FLOW); | |
987 | else | |
988 | PacketAlertAppend(det_ctx, s, p, 0); | |
986 | 989 | } |
987 | 990 | } |
988 | 991 | } |
1596 | 1596 | * \param co content pattern data |
1597 | 1597 | * |
1598 | 1598 | * \retval id pattern id |
1599 | * \initonly | |
1599 | 1600 | */ |
1600 | 1601 | uint32_t DetectContentGetId(MpmPatternIdStore *ht, DetectContentData *co) { |
1601 | 1602 | SCEnter(); |
101 | 101 | goto end; |
102 | 102 | } |
103 | 103 | |
104 | if (htp_state->body.nchunks == 0) { | |
105 | SCLogDebug("No http chunks to inspect"); | |
106 | goto end; | |
107 | } else { | |
108 | HtpBodyChunk *cur = htp_state->body.first; | |
109 | /* no chunks?!! get out of here */ | |
110 | if (cur == NULL) { | |
104 | htp_tx_t *tx = NULL; | |
105 | size_t idx = 0; | |
106 | ||
107 | for (idx = 0;//hs->new_in_tx_index; | |
108 | idx < list_size(htp_state->connp->conn->transactions); idx++) | |
109 | { | |
110 | tx = list_get(htp_state->connp->conn->transactions, idx); | |
111 | if (tx == NULL) | |
112 | continue; | |
113 | ||
114 | SCHtpTxUserData *htud = (SCHtpTxUserData *) htp_tx_get_user_data(tx); | |
115 | if (htud == NULL) | |
116 | continue; | |
117 | ||
118 | HtpBodyChunk *cur = htud->body.first; | |
119 | ||
120 | if (htud->body.nchunks == 0) { | |
111 | 121 | SCLogDebug("No http chunks to inspect"); |
112 | 122 | goto end; |
123 | } else { | |
124 | /* no chunks?!! get out of here */ | |
125 | if (cur == NULL) { | |
126 | SCLogDebug("No http chunks to inspect"); | |
127 | goto end; | |
128 | } | |
129 | ||
130 | /* this applies only for the client request body like the keyword name says */ | |
131 | if (htud->body.operation != HTP_BODY_REQUEST) { | |
132 | SCLogDebug("htp chunk not a request chunk"); | |
133 | goto end; | |
134 | } | |
135 | ||
136 | /* this is not how we do it now. We can rather hold the PM state from | |
137 | * the previous chunk that was matched, and continue right from where | |
138 | * we left off. We need to devise a scheme to do that, not just for | |
139 | * this keyword, but other keywords need it as well */ | |
140 | uint8_t *chunks_buffer = NULL; | |
141 | uint32_t total_chunks_len = 0; | |
142 | /* club all the chunks into one whole buffer and call the SPM on the buffer */ | |
143 | while (cur != NULL) { | |
144 | total_chunks_len += cur->len; | |
145 | if ( (chunks_buffer = SCRealloc(chunks_buffer, total_chunks_len)) == NULL) { | |
146 | return 0; | |
147 | } | |
148 | memcpy(chunks_buffer + total_chunks_len - cur->len, cur->data, cur->len); | |
149 | cur = cur->next; | |
150 | } | |
151 | /* call the case insensitive version if nocase has been specified in the sig */ | |
152 | if (hcbd->flags & DETECT_AL_HTTP_CLIENT_BODY_NOCASE) { | |
153 | result = (BoyerMooreNocase(hcbd->content, hcbd->content_len, chunks_buffer, | |
154 | total_chunks_len, hcbd->bm_ctx->bmGs, | |
155 | hcbd->bm_ctx->bmBc) != NULL); | |
156 | /* call the case sensitive version if nocase has been specified in the sig */ | |
157 | } else { | |
158 | result = (BoyerMoore(hcbd->content, hcbd->content_len, chunks_buffer, | |
159 | total_chunks_len, hcbd->bm_ctx->bmGs, | |
160 | hcbd->bm_ctx->bmBc) != NULL); | |
161 | } | |
162 | SCFree(chunks_buffer); | |
113 | 163 | } |
114 | ||
115 | /* this applies only for the client request body like the keyword name says */ | |
116 | if (htp_state->body.operation != HTP_BODY_REQUEST) { | |
117 | SCLogDebug("htp chunk not a request chunk"); | |
118 | goto end; | |
119 | } | |
120 | ||
121 | /* this is not how we do it now. We can rather hold the PM state from | |
122 | * the previous chunk that was matched, and continue right from where | |
123 | * we left off. We need to devise a scheme to do that, not just for | |
124 | * this keyword, but other keywords need it as well */ | |
125 | uint8_t *chunks_buffer = NULL; | |
126 | uint32_t total_chunks_len = 0; | |
127 | /* club all the chunks into one whole buffer and call the SPM on the buffer */ | |
128 | while (cur != NULL) { | |
129 | total_chunks_len += cur->len; | |
130 | if ( (chunks_buffer = SCRealloc(chunks_buffer, total_chunks_len)) == NULL) { | |
131 | return 0; | |
132 | } | |
133 | memcpy(chunks_buffer + total_chunks_len - cur->len, cur->data, cur->len); | |
134 | cur = cur->next; | |
135 | } | |
136 | /* call the case insensitive version if nocase has been specified in the sig */ | |
137 | if (hcbd->flags & DETECT_AL_HTTP_CLIENT_BODY_NOCASE) { | |
138 | result = (BoyerMooreNocase(hcbd->content, hcbd->content_len, chunks_buffer, | |
139 | total_chunks_len, hcbd->bm_ctx->bmGs, | |
140 | hcbd->bm_ctx->bmBc) != NULL); | |
141 | /* call the case sensitive version if nocase has been specified in the sig */ | |
142 | } else { | |
143 | result = (BoyerMoore(hcbd->content, hcbd->content_len, chunks_buffer, | |
144 | total_chunks_len, hcbd->bm_ctx->bmGs, | |
145 | hcbd->bm_ctx->bmBc) != NULL); | |
146 | } | |
147 | SCFree(chunks_buffer); | |
148 | 164 | } |
149 | 165 | |
150 | 166 | SCMutexUnlock(&f->m); |
1319 | 1335 | return result; |
1320 | 1336 | } |
1321 | 1337 | |
1338 | /** \test multiple http transactions and body chunks of request handling */ | |
1339 | static int DetectHttpClientBodyTest14(void) { | |
1340 | int result = 0; | |
1341 | Signature *s = NULL; | |
1342 | DetectEngineThreadCtx *det_ctx = NULL; | |
1343 | ThreadVars th_v; | |
1344 | Flow f; | |
1345 | TcpSession ssn; | |
1346 | Packet *p = NULL; | |
1347 | uint8_t httpbuf1[] = "POST / HTTP/1.1\r\n"; | |
1348 | uint8_t httpbuf2[] = "User-Agent: Mozilla/1.0\r\nContent-Length: 10\r\n"; | |
1349 | uint8_t httpbuf3[] = "Cookie: dummy\r\n\r\n"; | |
1350 | uint8_t httpbuf4[] = "Body one!!"; | |
1351 | uint32_t httplen1 = sizeof(httpbuf1) - 1; /* minus the \0 */ | |
1352 | uint32_t httplen2 = sizeof(httpbuf2) - 1; /* minus the \0 */ | |
1353 | uint32_t httplen3 = sizeof(httpbuf3) - 1; /* minus the \0 */ | |
1354 | uint32_t httplen4 = sizeof(httpbuf4) - 1; /* minus the \0 */ | |
1355 | uint8_t httpbuf5[] = "GET /?var=val HTTP/1.1\r\n"; | |
1356 | uint8_t httpbuf6[] = "User-Agent: Firefox/1.0\r\n"; | |
1357 | uint8_t httpbuf7[] = "Cookie: dummy2\r\nContent-Length: 10\r\n\r\nBody two!!"; | |
1358 | uint32_t httplen5 = sizeof(httpbuf5) - 1; /* minus the \0 */ | |
1359 | uint32_t httplen6 = sizeof(httpbuf6) - 1; /* minus the \0 */ | |
1360 | uint32_t httplen7 = sizeof(httpbuf7) - 1; /* minus the \0 */ | |
1361 | ||
1362 | memset(&th_v, 0, sizeof(th_v)); | |
1363 | memset(&f, 0, sizeof(f)); | |
1364 | memset(&ssn, 0, sizeof(ssn)); | |
1365 | ||
1366 | p = UTHBuildPacket(NULL, 0, IPPROTO_TCP); | |
1367 | ||
1368 | FLOW_INITIALIZE(&f); | |
1369 | f.protoctx = (void *)&ssn; | |
1370 | f.proto = IPPROTO_TCP; | |
1371 | f.src.family = AF_INET; | |
1372 | f.dst.family = AF_INET; | |
1373 | ||
1374 | p->flow = &f; | |
1375 | p->flowflags |= FLOW_PKT_TOSERVER; | |
1376 | p->flowflags |= FLOW_PKT_ESTABLISHED; | |
1377 | f.alproto = ALPROTO_HTTP; | |
1378 | ||
1379 | StreamTcpInitConfig(TRUE); | |
1380 | FlowL7DataPtrInit(&f); | |
1381 | ||
1382 | DetectEngineCtx *de_ctx = DetectEngineCtxInit(); | |
1383 | if (de_ctx == NULL) { | |
1384 | goto end; | |
1385 | } | |
1386 | ||
1387 | de_ctx->flags |= DE_QUIET; | |
1388 | ||
1389 | s = DetectEngineAppendSig(de_ctx, "alert tcp any any -> any any (content:\"POST\"; http_method; content:\"Mozilla\"; http_header; content:\"dummy\"; http_cookie; content:\"one\"; http_client_body; sid:1; rev:1;)"); | |
1390 | if (s == NULL) { | |
1391 | printf("sig parse failed: "); | |
1392 | goto end; | |
1393 | } | |
1394 | s = DetectEngineAppendSig(de_ctx, "alert tcp any any -> any any (content:\"GET\"; http_method; content:\"Firefox\"; http_header; content:\"dummy2\"; http_cookie; content:\"two\"; http_client_body; sid:2; rev:1;)"); | |
1395 | if (s == NULL) { | |
1396 | printf("sig2 parse failed: "); | |
1397 | goto end; | |
1398 | } | |
1399 | ||
1400 | SigGroupBuild(de_ctx); | |
1401 | DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx); | |
1402 | ||
1403 | int r = AppLayerParse(&f, ALPROTO_HTTP, STREAM_TOSERVER, httpbuf1, httplen1); | |
1404 | if (r != 0) { | |
1405 | printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r); | |
1406 | goto end; | |
1407 | } | |
1408 | ||
1409 | /* do detect */ | |
1410 | SigMatchSignatures(&th_v, de_ctx, det_ctx, p); | |
1411 | if (PacketAlertCheck(p, 1)) { | |
1412 | printf("sig 1 alerted: "); | |
1413 | goto end; | |
1414 | } | |
1415 | p->alerts.cnt = 0; | |
1416 | ||
1417 | r = AppLayerParse(&f, ALPROTO_HTTP, STREAM_TOSERVER, httpbuf2, httplen2); | |
1418 | if (r != 0) { | |
1419 | printf("toserver chunk 2 returned %" PRId32 ", expected 0: ", r); | |
1420 | goto end; | |
1421 | } | |
1422 | ||
1423 | /* do detect */ | |
1424 | SigMatchSignatures(&th_v, de_ctx, det_ctx, p); | |
1425 | if (PacketAlertCheck(p, 1)) { | |
1426 | printf("sig 1 alerted (2): "); | |
1427 | goto end; | |
1428 | } | |
1429 | p->alerts.cnt = 0; | |
1430 | ||
1431 | r = AppLayerParse(&f, ALPROTO_HTTP, STREAM_TOSERVER, httpbuf3, httplen3); | |
1432 | if (r != 0) { | |
1433 | printf("toserver chunk 3 returned %" PRId32 ", expected 0: ", r); | |
1434 | goto end; | |
1435 | } | |
1436 | ||
1437 | /* do detect */ | |
1438 | SigMatchSignatures(&th_v, de_ctx, det_ctx, p); | |
1439 | if (PacketAlertCheck(p, 1)) { | |
1440 | printf("signature matched, but shouldn't have: "); | |
1441 | goto end; | |
1442 | } | |
1443 | p->alerts.cnt = 0; | |
1444 | ||
1445 | r = AppLayerParse(&f, ALPROTO_HTTP, STREAM_TOSERVER, httpbuf4, httplen4); | |
1446 | if (r != 0) { | |
1447 | printf("toserver chunk 4 returned %" PRId32 ", expected 0: ", r); | |
1448 | result = 0; | |
1449 | goto end; | |
1450 | } | |
1451 | ||
1452 | /* do detect */ | |
1453 | SigMatchSignatures(&th_v, de_ctx, det_ctx, p); | |
1454 | if (!(PacketAlertCheck(p, 1))) { | |
1455 | printf("sig 1 didn't alert: "); | |
1456 | goto end; | |
1457 | } | |
1458 | p->alerts.cnt = 0; | |
1459 | ||
1460 | r = AppLayerParse(&f, ALPROTO_HTTP, STREAM_TOSERVER, httpbuf5, httplen5); | |
1461 | if (r != 0) { | |
1462 | printf("toserver chunk 5 returned %" PRId32 ", expected 0: ", r); | |
1463 | goto end; | |
1464 | } | |
1465 | ||
1466 | /* do detect */ | |
1467 | SigMatchSignatures(&th_v, de_ctx, det_ctx, p); | |
1468 | if (PacketAlertCheck(p, 1)) { | |
1469 | printf("sig 1 alerted (5): "); | |
1470 | goto end; | |
1471 | } | |
1472 | p->alerts.cnt = 0; | |
1473 | ||
1474 | r = AppLayerParse(&f, ALPROTO_HTTP, STREAM_TOSERVER, httpbuf6, httplen6); | |
1475 | if (r != 0) { | |
1476 | printf("toserver chunk 6 returned %" PRId32 ", expected 0: ", r); | |
1477 | goto end; | |
1478 | } | |
1479 | ||
1480 | /* do detect */ | |
1481 | SigMatchSignatures(&th_v, de_ctx, det_ctx, p); | |
1482 | if ((PacketAlertCheck(p, 1)) || (PacketAlertCheck(p, 2))) { | |
1483 | printf("sig 1 alerted (request 2, chunk 6): "); | |
1484 | goto end; | |
1485 | } | |
1486 | p->alerts.cnt = 0; | |
1487 | ||
1488 | SCLogDebug("sending data chunk 7"); | |
1489 | ||
1490 | r = AppLayerParse(&f, ALPROTO_HTTP, STREAM_TOSERVER, httpbuf7, httplen7); | |
1491 | if (r != 0) { | |
1492 | printf("toserver chunk 7 returned %" PRId32 ", expected 0: ", r); | |
1493 | goto end; | |
1494 | } | |
1495 | ||
1496 | /* do detect */ | |
1497 | SigMatchSignatures(&th_v, de_ctx, det_ctx, p); | |
1498 | if (!(PacketAlertCheck(p, 2))) { | |
1499 | printf("signature 2 didn't match, but should have: "); | |
1500 | goto end; | |
1501 | } | |
1502 | p->alerts.cnt = 0; | |
1503 | ||
1504 | HtpState *htp_state = f.aldata[AlpGetStateIdx(ALPROTO_HTTP)]; | |
1505 | if (htp_state == NULL) { | |
1506 | printf("no http state: "); | |
1507 | result = 0; | |
1508 | goto end; | |
1509 | } | |
1510 | ||
1511 | if (list_size(htp_state->connp->conn->transactions) != 2) { | |
1512 | printf("The http app layer doesn't have 2 transactions, but it should: "); | |
1513 | goto end; | |
1514 | } | |
1515 | ||
1516 | result = 1; | |
1517 | end: | |
1518 | if (det_ctx != NULL) { | |
1519 | DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx); | |
1520 | } | |
1521 | if (de_ctx != NULL) { | |
1522 | SigGroupCleanup(de_ctx); | |
1523 | DetectEngineCtxFree(de_ctx); | |
1524 | } | |
1525 | ||
1526 | FlowL7DataPtrFree(&f); | |
1527 | StreamTcpFreeConfig(TRUE); | |
1528 | FLOW_DESTROY(&f); | |
1529 | UTHFreePacket(p); | |
1530 | return result; | |
1531 | } | |
1532 | ||
1533 | /** \test multiple http transactions and body chunks of request handling */ | |
1534 | static int DetectHttpClientBodyTest15(void) { | |
1535 | int result = 0; | |
1536 | Signature *s = NULL; | |
1537 | DetectEngineThreadCtx *det_ctx = NULL; | |
1538 | ThreadVars th_v; | |
1539 | Flow f; | |
1540 | TcpSession ssn; | |
1541 | Packet *p = NULL; | |
1542 | uint8_t httpbuf1[] = "POST / HTTP/1.1\r\n"; | |
1543 | uint8_t httpbuf2[] = "User-Agent: Mozilla/1.0\r\nContent-Length: 10\r\n"; | |
1544 | uint8_t httpbuf3[] = "Cookie: dummy\r\n\r\n"; | |
1545 | uint8_t httpbuf4[] = "Body one!!"; | |
1546 | uint32_t httplen1 = sizeof(httpbuf1) - 1; /* minus the \0 */ | |
1547 | uint32_t httplen2 = sizeof(httpbuf2) - 1; /* minus the \0 */ | |
1548 | uint32_t httplen3 = sizeof(httpbuf3) - 1; /* minus the \0 */ | |
1549 | uint32_t httplen4 = sizeof(httpbuf4) - 1; /* minus the \0 */ | |
1550 | uint8_t httpbuf5[] = "GET /?var=val HTTP/1.1\r\n"; | |
1551 | uint8_t httpbuf6[] = "User-Agent: Firefox/1.0\r\n"; | |
1552 | uint8_t httpbuf7[] = "Cookie: dummy2\r\nContent-Length: 10\r\n\r\nBody two!!"; | |
1553 | uint32_t httplen5 = sizeof(httpbuf5) - 1; /* minus the \0 */ | |
1554 | uint32_t httplen6 = sizeof(httpbuf6) - 1; /* minus the \0 */ | |
1555 | uint32_t httplen7 = sizeof(httpbuf7) - 1; /* minus the \0 */ | |
1556 | ||
1557 | memset(&th_v, 0, sizeof(th_v)); | |
1558 | memset(&f, 0, sizeof(f)); | |
1559 | memset(&ssn, 0, sizeof(ssn)); | |
1560 | ||
1561 | p = UTHBuildPacket(NULL, 0, IPPROTO_TCP); | |
1562 | ||
1563 | FLOW_INITIALIZE(&f); | |
1564 | f.protoctx = (void *)&ssn; | |
1565 | f.proto = IPPROTO_TCP; | |
1566 | f.src.family = AF_INET; | |
1567 | f.dst.family = AF_INET; | |
1568 | ||
1569 | p->flow = &f; | |
1570 | p->flowflags |= FLOW_PKT_TOSERVER; | |
1571 | p->flowflags |= FLOW_PKT_ESTABLISHED; | |
1572 | f.alproto = ALPROTO_HTTP; | |
1573 | ||
1574 | StreamTcpInitConfig(TRUE); | |
1575 | FlowL7DataPtrInit(&f); | |
1576 | ||
1577 | DetectEngineCtx *de_ctx = DetectEngineCtxInit(); | |
1578 | if (de_ctx == NULL) { | |
1579 | goto end; | |
1580 | } | |
1581 | ||
1582 | de_ctx->flags |= DE_QUIET; | |
1583 | ||
1584 | s = DetectEngineAppendSig(de_ctx, "alert tcp any any -> any any (content:\"POST\"; http_method; content:\"Mozilla\"; http_header; content:\"dummy\"; http_cookie; content:\"one\"; http_client_body; sid:1; rev:1;)"); | |
1585 | if (s == NULL) { | |
1586 | printf("sig parse failed: "); | |
1587 | goto end; | |
1588 | } | |
1589 | s = DetectEngineAppendSig(de_ctx, "alert tcp any any -> any any (content:\"GET\"; http_method; content:\"Firefox\"; http_header; content:\"dummy2\"; http_cookie; content:\"two\"; http_client_body; sid:2; rev:1;)"); | |
1590 | if (s == NULL) { | |
1591 | printf("sig2 parse failed: "); | |
1592 | goto end; | |
1593 | } | |
1594 | ||
1595 | SigGroupBuild(de_ctx); | |
1596 | DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx); | |
1597 | ||
1598 | int r = AppLayerParse(&f, ALPROTO_HTTP, STREAM_TOSERVER, httpbuf1, httplen1); | |
1599 | if (r != 0) { | |
1600 | printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r); | |
1601 | goto end; | |
1602 | } | |
1603 | ||
1604 | /* do detect */ | |
1605 | SigMatchSignatures(&th_v, de_ctx, det_ctx, p); | |
1606 | if (PacketAlertCheck(p, 1)) { | |
1607 | printf("sig 1 alerted: "); | |
1608 | goto end; | |
1609 | } | |
1610 | p->alerts.cnt = 0; | |
1611 | ||
1612 | r = AppLayerParse(&f, ALPROTO_HTTP, STREAM_TOSERVER, httpbuf2, httplen2); | |
1613 | if (r != 0) { | |
1614 | printf("toserver chunk 2 returned %" PRId32 ", expected 0: ", r); | |
1615 | goto end; | |
1616 | } | |
1617 | ||
1618 | /* do detect */ | |
1619 | SigMatchSignatures(&th_v, de_ctx, det_ctx, p); | |
1620 | if (PacketAlertCheck(p, 1)) { | |
1621 | printf("sig 1 alerted (2): "); | |
1622 | goto end; | |
1623 | } | |
1624 | p->alerts.cnt = 0; | |
1625 | ||
1626 | r = AppLayerParse(&f, ALPROTO_HTTP, STREAM_TOSERVER, httpbuf3, httplen3); | |
1627 | if (r != 0) { | |
1628 | printf("toserver chunk 3 returned %" PRId32 ", expected 0: ", r); | |
1629 | goto end; | |
1630 | } | |
1631 | ||
1632 | /* do detect */ | |
1633 | SigMatchSignatures(&th_v, de_ctx, det_ctx, p); | |
1634 | if (PacketAlertCheck(p, 1)) { | |
1635 | printf("signature matched, but shouldn't have: "); | |
1636 | goto end; | |
1637 | } | |
1638 | p->alerts.cnt = 0; | |
1639 | ||
1640 | r = AppLayerParse(&f, ALPROTO_HTTP, STREAM_TOSERVER, httpbuf4, httplen4); | |
1641 | if (r != 0) { | |
1642 | printf("toserver chunk 4 returned %" PRId32 ", expected 0: ", r); | |
1643 | result = 0; | |
1644 | goto end; | |
1645 | } | |
1646 | ||
1647 | /* do detect */ | |
1648 | SigMatchSignatures(&th_v, de_ctx, det_ctx, p); | |
1649 | if (!(PacketAlertCheck(p, 1))) { | |
1650 | printf("sig 1 didn't alert: "); | |
1651 | goto end; | |
1652 | } | |
1653 | p->alerts.cnt = 0; | |
1654 | ||
1655 | r = AppLayerParse(&f, ALPROTO_HTTP, STREAM_TOSERVER, httpbuf5, httplen5); | |
1656 | if (r != 0) { | |
1657 | printf("toserver chunk 5 returned %" PRId32 ", expected 0: ", r); | |
1658 | goto end; | |
1659 | } | |
1660 | ||
1661 | /* do detect */ | |
1662 | SigMatchSignatures(&th_v, de_ctx, det_ctx, p); | |
1663 | if (PacketAlertCheck(p, 1)) { | |
1664 | printf("sig 1 alerted (5): "); | |
1665 | goto end; | |
1666 | } | |
1667 | p->alerts.cnt = 0; | |
1668 | ||
1669 | r = AppLayerParse(&f, ALPROTO_HTTP, STREAM_TOSERVER, httpbuf6, httplen6); | |
1670 | if (r != 0) { | |
1671 | printf("toserver chunk 6 returned %" PRId32 ", expected 0: ", r); | |
1672 | goto end; | |
1673 | } | |
1674 | ||
1675 | /* do detect */ | |
1676 | SigMatchSignatures(&th_v, de_ctx, det_ctx, p); | |
1677 | if ((PacketAlertCheck(p, 1)) || (PacketAlertCheck(p, 2))) { | |
1678 | printf("sig 1 alerted (request 2, chunk 6): "); | |
1679 | goto end; | |
1680 | } | |
1681 | p->alerts.cnt = 0; | |
1682 | ||
1683 | SCLogDebug("sending data chunk 7"); | |
1684 | ||
1685 | r = AppLayerParse(&f, ALPROTO_HTTP, STREAM_TOSERVER, httpbuf7, httplen7); | |
1686 | if (r != 0) { | |
1687 | printf("toserver chunk 7 returned %" PRId32 ", expected 0: ", r); | |
1688 | goto end; | |
1689 | } | |
1690 | ||
1691 | /* do detect */ | |
1692 | SigMatchSignatures(&th_v, de_ctx, det_ctx, p); | |
1693 | if (!(PacketAlertCheck(p, 2))) { | |
1694 | printf("signature 2 didn't match, but should have: "); | |
1695 | goto end; | |
1696 | } | |
1697 | p->alerts.cnt = 0; | |
1698 | ||
1699 | HtpState *htp_state = f.aldata[AlpGetStateIdx(ALPROTO_HTTP)]; | |
1700 | if (htp_state == NULL) { | |
1701 | printf("no http state: "); | |
1702 | result = 0; | |
1703 | goto end; | |
1704 | } | |
1705 | ||
1706 | /* hardcoded check of the transactions and it's client body chunks */ | |
1707 | if (list_size(htp_state->connp->conn->transactions) != 2) { | |
1708 | printf("The http app layer doesn't have 2 transactions, but it should: "); | |
1709 | goto end; | |
1710 | } | |
1711 | ||
1712 | htp_tx_t *t1 = list_get(htp_state->connp->conn->transactions, 0); | |
1713 | htp_tx_t *t2 = list_get(htp_state->connp->conn->transactions, 1); | |
1714 | ||
1715 | SCHtpTxUserData *htud = (SCHtpTxUserData *) htp_tx_get_user_data(t1); | |
1716 | ||
1717 | HtpBodyChunk *cur = htud->body.first; | |
1718 | if (htud->body.nchunks == 0) { | |
1719 | SCLogDebug("No body data in t1 (it should be removed only when the tx is destroyed): "); | |
1720 | goto end; | |
1721 | } | |
1722 | ||
1723 | if (memcmp(cur->data, "Body one!!", strlen("Body one!!")) != 0) { | |
1724 | SCLogDebug("Body data in t1 is not correctly set: "); | |
1725 | goto end; | |
1726 | } | |
1727 | ||
1728 | htud = (SCHtpTxUserData *) htp_tx_get_user_data(t2); | |
1729 | ||
1730 | cur = htud->body.first; | |
1731 | if (htud->body.nchunks == 0) { | |
1732 | SCLogDebug("No body data in t1 (it should be removed only when the tx is destroyed): "); | |
1733 | goto end; | |
1734 | } | |
1735 | ||
1736 | if (memcmp(cur->data, "Body two!!", strlen("Body two!!")) != 0) { | |
1737 | SCLogDebug("Body data in t1 is not correctly set: "); | |
1738 | goto end; | |
1739 | } | |
1740 | ||
1741 | result = 1; | |
1742 | end: | |
1743 | if (det_ctx != NULL) { | |
1744 | DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx); | |
1745 | } | |
1746 | if (de_ctx != NULL) { | |
1747 | SigGroupCleanup(de_ctx); | |
1748 | DetectEngineCtxFree(de_ctx); | |
1749 | } | |
1750 | ||
1751 | FlowL7DataPtrFree(&f); | |
1752 | StreamTcpFreeConfig(TRUE); | |
1753 | FLOW_DESTROY(&f); | |
1754 | UTHFreePacket(p); | |
1755 | return result; | |
1756 | } | |
1757 | ||
1758 | ||
1322 | 1759 | #endif /* UNITTESTS */ |
1323 | 1760 | |
1324 | 1761 | void DetectHttpClientBodyRegisterTests(void) |
1337 | 1774 | UtRegisterTest("DetectHttpClientBodyTest11", DetectHttpClientBodyTest11, 1); |
1338 | 1775 | UtRegisterTest("DetectHttpClientBodyTest12", DetectHttpClientBodyTest12, 1); |
1339 | 1776 | UtRegisterTest("DetectHttpClientBodyTest13", DetectHttpClientBodyTest13, 1); |
1777 | UtRegisterTest("DetectHttpClientBodyTest14", DetectHttpClientBodyTest14, 1); | |
1778 | UtRegisterTest("DetectHttpClientBodyTest15", DetectHttpClientBodyTest15, 1); | |
1340 | 1779 | #endif /* UNITTESTS */ |
1341 | 1780 | |
1342 | 1781 | return; |
259 | 259 | /* flag the signature to indicate that we scan the app layer data */ |
260 | 260 | s->flags |= SIG_FLAG_APPLAYER; |
261 | 261 | s->alproto = ALPROTO_HTTP; |
262 | /* enable http request body callback in the http app layer parser */ | |
263 | AppLayerHtpEnableRequestBodyCallback(); | |
264 | 262 | |
265 | 263 | return 0; |
266 | 264 |
97 | 97 | htp_tx_t *tx = NULL; |
98 | 98 | int ret = 0; |
99 | 99 | |
100 | if (hs == NULL) { | |
100 | if (hs == NULL || hs->connp == NULL || hs->connp->conn == NULL) { | |
101 | 101 | SCLogDebug("No HTP state."); |
102 | 102 | SCReturnInt(0); |
103 | 103 | } |
1243 | 1243 | if (sig == NULL) |
1244 | 1244 | goto error; |
1245 | 1245 | |
1246 | /* default gid to 1 */ | |
1247 | sig->gid = 1; | |
1248 | ||
1246 | 1249 | if (SigParse(de_ctx, sig, sigstr, SIG_DIREC_NORMAL) < 0) |
1247 | 1250 | goto error; |
1248 | 1251 | |
1385 | 1388 | /* XXX one day we will support this the way Snort does, |
1386 | 1389 | * through classifications.config */ |
1387 | 1390 | sig->prio = 3; |
1391 | /* default gid to 1 */ | |
1392 | sig->gid = 1; | |
1388 | 1393 | |
1389 | 1394 | if (SigParse(de_ctx, sig, sigstr, SIG_DIREC_NORMAL) < 0) |
1390 | 1395 | goto error; |
2061 | 2066 | DetectEngineAppendSig(de_ctx, "alert tcp any any -> any any (msg:\"boo\"; sid:2; rev:1;)"); |
2062 | 2067 | result &= (de_ctx->sig_list != NULL && de_ctx->sig_list->id == 2 && |
2063 | 2068 | de_ctx->sig_list->rev == 2); |
2069 | if (result == 0) | |
2070 | goto end; | |
2064 | 2071 | result &= (de_ctx->sig_list->next != NULL && de_ctx->sig_list->next->id == 1 && |
2065 | 2072 | de_ctx->sig_list->next->rev == 6); |
2066 | 2073 | if (result == 0) |
2069 | 2076 | DetectEngineAppendSig(de_ctx, "alert tcp any any -> any any (msg:\"boo\"; sid:2; rev:4;)"); |
2070 | 2077 | result &= (de_ctx->sig_list != NULL && de_ctx->sig_list->id == 2 && |
2071 | 2078 | de_ctx->sig_list->rev == 4); |
2079 | if (result == 0) | |
2080 | goto end; | |
2072 | 2081 | result &= (de_ctx->sig_list->next != NULL && de_ctx->sig_list->next->id == 1 && |
2073 | 2082 | de_ctx->sig_list->next->rev == 6); |
2074 | 2083 | if (result == 0) |
78 | 78 | |
79 | 79 | int DetectPcreMatch (ThreadVars *, DetectEngineThreadCtx *, Packet *, Signature *, SigMatch *); |
80 | 80 | int DetectPcreALMatch(ThreadVars *t, DetectEngineThreadCtx *det_ctx, Flow *f, uint8_t flags, void *state, Signature *s, SigMatch *m); |
81 | int DetectPcreALMatchCookie(ThreadVars *t, DetectEngineThreadCtx *det_ctx, Flow *f, uint8_t flags, void *state, Signature *s, SigMatch *m); | |
82 | int DetectPcreALMatchHeader(ThreadVars *t, DetectEngineThreadCtx *det_ctx, Flow *f, uint8_t flags, void *state, Signature *s, SigMatch *m); | |
83 | int DetectPcreALMatchMethod(ThreadVars *t, DetectEngineThreadCtx *det_ctx, Flow *f, uint8_t flags, void *state, Signature *s, SigMatch *m); | |
81 | 84 | static int DetectPcreSetup (DetectEngineCtx *, Signature *, char *); |
82 | 85 | void DetectPcreFree(void *); |
83 | 86 | void DetectPcreRegisterTests(void); |
103 | 106 | sigmatch_table[DETECT_PCRE_HTTPBODY].Setup = NULL; |
104 | 107 | sigmatch_table[DETECT_PCRE_HTTPBODY].Free = DetectPcreFree; |
105 | 108 | sigmatch_table[DETECT_PCRE_HTTPBODY].RegisterTests = NULL; |
106 | ||
107 | 109 | sigmatch_table[DETECT_PCRE_HTTPBODY].flags |= SIGMATCH_PAYLOAD; |
110 | ||
111 | /* The same for Cookie, Method and Header */ | |
112 | sigmatch_table[DETECT_PCRE_HTTPCOOKIE].name = "__pcre_http_cookie__"; /* not a real keyword */ | |
113 | sigmatch_table[DETECT_PCRE_HTTPCOOKIE].Match = NULL; | |
114 | sigmatch_table[DETECT_PCRE_HTTPCOOKIE].AppLayerMatch = DetectPcreALMatchCookie; | |
115 | sigmatch_table[DETECT_PCRE_HTTPCOOKIE].alproto = ALPROTO_HTTP; | |
116 | sigmatch_table[DETECT_PCRE_HTTPCOOKIE].Setup = NULL; | |
117 | sigmatch_table[DETECT_PCRE_HTTPCOOKIE].Free = DetectPcreFree; | |
118 | sigmatch_table[DETECT_PCRE_HTTPCOOKIE].RegisterTests = NULL; | |
119 | sigmatch_table[DETECT_PCRE_HTTPCOOKIE].flags |= SIGMATCH_PAYLOAD; | |
120 | ||
121 | sigmatch_table[DETECT_PCRE_HTTPMETHOD].name = "__pcre_http_method__"; /* not a real keyword */ | |
122 | sigmatch_table[DETECT_PCRE_HTTPMETHOD].Match = NULL; | |
123 | sigmatch_table[DETECT_PCRE_HTTPMETHOD].AppLayerMatch = DetectPcreALMatchMethod; | |
124 | sigmatch_table[DETECT_PCRE_HTTPMETHOD].alproto = ALPROTO_HTTP; | |
125 | sigmatch_table[DETECT_PCRE_HTTPMETHOD].Setup = NULL; | |
126 | sigmatch_table[DETECT_PCRE_HTTPMETHOD].Free = DetectPcreFree; | |
127 | sigmatch_table[DETECT_PCRE_HTTPMETHOD].RegisterTests = NULL; | |
128 | sigmatch_table[DETECT_PCRE_HTTPMETHOD].flags |= SIGMATCH_PAYLOAD; | |
129 | ||
130 | sigmatch_table[DETECT_PCRE_HTTPHEADER].name = "__pcre_http_header__"; /* not a real keyword */ | |
131 | sigmatch_table[DETECT_PCRE_HTTPHEADER].Match = NULL; | |
132 | sigmatch_table[DETECT_PCRE_HTTPHEADER].AppLayerMatch = DetectPcreALMatchHeader; | |
133 | sigmatch_table[DETECT_PCRE_HTTPHEADER].alproto = ALPROTO_HTTP; | |
134 | sigmatch_table[DETECT_PCRE_HTTPHEADER].Setup = NULL; | |
135 | sigmatch_table[DETECT_PCRE_HTTPHEADER].Free = DetectPcreFree; | |
136 | sigmatch_table[DETECT_PCRE_HTTPHEADER].RegisterTests = NULL; | |
137 | sigmatch_table[DETECT_PCRE_HTTPHEADER].flags |= SIGMATCH_PAYLOAD; | |
138 | ||
108 | 139 | |
109 | 140 | const char *eb; |
110 | 141 | int eo; |
160 | 191 | error: |
161 | 192 | /* XXX */ |
162 | 193 | return; |
194 | } | |
195 | ||
196 | /** | |
197 | * \brief Match a regex on data sent at an http method (needs the l7 parser). | |
198 | * | |
199 | * \param det_ctx Thread detection ctx. | |
200 | * \param s Signature. | |
201 | * \param sm SigMatch to match against. | |
202 | * \param data Data to match against. | |
203 | * \param data_len Data length. | |
204 | * | |
205 | * \retval 1: match | |
206 | * \retval 0: no match | |
207 | */ | |
208 | int DetectPcreALDoMatchMethod(DetectEngineThreadCtx *det_ctx, Signature *s, | |
209 | SigMatch *m, Flow *f, uint8_t flags, | |
210 | void *state) | |
211 | { | |
212 | SCEnter(); | |
213 | ||
214 | int ret = 0; | |
215 | int toret = 0; | |
216 | size_t idx; | |
217 | ||
218 | #define MAX_SUBSTRINGS 30 | |
219 | int ov[MAX_SUBSTRINGS]; | |
220 | uint8_t *ptr = NULL; | |
221 | uint16_t len = 0; | |
222 | ||
223 | DetectPcreData *pe = (DetectPcreData *)m->ctx; | |
224 | ||
225 | /* define ptr & len */ | |
226 | SCMutexLock(&f->m); | |
227 | SCLogDebug("got lock %p", &f->m); | |
228 | ||
229 | HtpState *htp_state = (HtpState *)state; | |
230 | if (htp_state == NULL) { | |
231 | SCLogDebug("no HTTP layer state has been received, so no match"); | |
232 | goto end; | |
233 | } | |
234 | ||
235 | if (!(htp_state->flags & HTP_FLAG_STATE_OPEN)) { | |
236 | SCLogDebug("HTP state not yet properly setup, so no match"); | |
237 | goto end; | |
238 | } | |
239 | ||
240 | SCLogDebug("htp_state %p, flow %p", htp_state, f); | |
241 | SCLogDebug("htp_state->connp %p", htp_state->connp); | |
242 | SCLogDebug("htp_state->connp->conn %p", htp_state->connp->conn); | |
243 | ||
244 | if (htp_state->connp == NULL || htp_state->connp->conn == NULL) { | |
245 | SCLogDebug("HTTP connection structure is NULL"); | |
246 | goto end; | |
247 | } | |
248 | ||
249 | htp_tx_t *tx = NULL; | |
250 | ||
251 | for (idx = 0;//htp_state->new_in_tx_index; | |
252 | idx < list_size(htp_state->connp->conn->transactions); idx++) | |
253 | { | |
254 | tx = list_get(htp_state->connp->conn->transactions, idx); | |
255 | if (tx == NULL) | |
256 | continue; | |
257 | ||
258 | ptr = (uint8_t *) bstr_ptr(tx->request_method); | |
259 | len = bstr_size(tx->request_method); | |
260 | if (ptr == NULL) | |
261 | continue; | |
262 | ||
263 | //printf("Matching Method"); | |
264 | //PrintRawUriFp(stdout, (uint8_t*)ptr, len); | |
265 | ||
266 | /* run the actual pcre detection */ | |
267 | ret = pcre_exec(pe->re, pe->sd, (char *)ptr, len, 0, 0, ov, MAX_SUBSTRINGS); | |
268 | SCLogDebug("ret %d (negating %s)", ret, pe->negate ? "set" : "not set"); | |
269 | ||
270 | if (ret == PCRE_ERROR_NOMATCH) { | |
271 | if (pe->negate == 1) { | |
272 | /* regex didn't match with negate option means we | |
273 | * consider it a match */ | |
274 | ret = 1; | |
275 | toret |= ret; | |
276 | break; | |
277 | } else { | |
278 | ret = 0; | |
279 | } | |
280 | toret |= ret; | |
281 | } else if (ret >= 0) { | |
282 | if (pe->negate == 1) { | |
283 | /* regex matched but we're negated, so not | |
284 | * considering it a match */ | |
285 | ret = 0; | |
286 | } else { | |
287 | /* regex matched and we're not negated, | |
288 | * considering it a match */ | |
289 | ret = 1; | |
290 | toret |= ret; | |
291 | break; | |
292 | } | |
293 | } else { | |
294 | SCLogDebug("pcre had matching error"); | |
295 | ret = 0; | |
296 | } | |
297 | } | |
298 | ||
299 | end: | |
300 | SCMutexUnlock(&f->m); | |
301 | SCLogDebug("released lock %p", &f->m); | |
302 | ||
303 | SCReturnInt(toret); | |
304 | } | |
305 | ||
306 | /** | |
307 | * \brief Match a regex on data sent at an http header (needs the l7 parser). | |
308 | * | |
309 | * \param det_ctx Thread detection ctx. | |
310 | * \param s Signature. | |
311 | * \param sm SigMatch to match against. | |
312 | * \param data Data to match against. | |
313 | * \param data_len Data length. | |
314 | * | |
315 | * \retval 1: match | |
316 | * \retval 0: no match | |
317 | */ | |
318 | int DetectPcreALDoMatchHeader(DetectEngineThreadCtx *det_ctx, Signature *s, | |
319 | SigMatch *m, Flow *f, uint8_t flags, | |
320 | void *state) | |
321 | { | |
322 | SCEnter(); | |
323 | ||
324 | int ret = 0; | |
325 | int toret = 0; | |
326 | size_t idx; | |
327 | ||
328 | #define MAX_SUBSTRINGS 30 | |
329 | int ov[MAX_SUBSTRINGS]; | |
330 | uint8_t *ptr = NULL; | |
331 | uint16_t len = 0; | |
332 | ||
333 | DetectPcreData *pe = (DetectPcreData *)m->ctx; | |
334 | ||
335 | /* define ptr & len */ | |
336 | SCMutexLock(&f->m); | |
337 | SCLogDebug("got lock %p", &f->m); | |
338 | ||
339 | HtpState *htp_state = (HtpState *)state; | |
340 | if (htp_state == NULL) { | |
341 | SCLogDebug("no HTTP layer state has been received, so no match"); | |
342 | goto end; | |
343 | } | |
344 | ||
345 | if (!(htp_state->flags & HTP_FLAG_STATE_OPEN)) { | |
346 | SCLogDebug("HTP state not yet properly setup, so no match"); | |
347 | goto end; | |
348 | } | |
349 | ||
350 | SCLogDebug("htp_state %p, flow %p", htp_state, f); | |
351 | SCLogDebug("htp_state->connp %p", htp_state->connp); | |
352 | SCLogDebug("htp_state->connp->conn %p", htp_state->connp->conn); | |
353 | ||
354 | if (htp_state->connp == NULL || htp_state->connp->conn == NULL) { | |
355 | SCLogDebug("HTTP connection structure is NULL"); | |
356 | goto end; | |
357 | } | |
358 | ||
359 | htp_tx_t *tx = NULL; | |
360 | bstr *headers = NULL; | |
361 | ||
362 | for (idx = 0;//htp_state->new_in_tx_index; | |
363 | idx < list_size(htp_state->connp->conn->transactions); idx++) | |
364 | { | |
365 | tx = list_get(htp_state->connp->conn->transactions, idx); | |
366 | if (tx == NULL) | |
367 | continue; | |
368 | ||
369 | SCLogDebug("inspecting tx %p", tx); | |
370 | ||
371 | headers = htp_tx_get_request_headers_raw(tx); | |
372 | if (headers == NULL) | |
373 | continue; | |
374 | ||
375 | ptr = (uint8_t *)bstr_ptr(headers); | |
376 | len = bstr_len(headers); | |
377 | if (ptr == NULL) | |
378 | continue; | |
379 | ||
380 | //printf("Matching Header"); | |
381 | //PrintRawUriFp(stdout, (uint8_t*)ptr, len); | |
382 | ||
383 | /* run the actual pcre detection */ | |
384 | ret = pcre_exec(pe->re, pe->sd, (char *)ptr, len, 0, 0, ov, MAX_SUBSTRINGS); | |
385 | SCLogDebug("ret %d (negating %s)", ret, pe->negate ? "set" : "not set"); | |
386 | ||
387 | if (ret == PCRE_ERROR_NOMATCH) { | |
388 | if (pe->negate == 1) { | |
389 | /* regex didn't match with negate option means we | |
390 | * consider it a match */ | |
391 | ret = 1; | |
392 | toret |= ret; | |
393 | break; | |
394 | } else { | |
395 | ret = 0; | |
396 | } | |
397 | toret |= ret; | |
398 | } else if (ret >= 0) { | |
399 | if (pe->negate == 1) { | |
400 | /* regex matched but we're negated, so not | |
401 | * considering it a match */ | |
402 | ret = 0; | |
403 | } else { | |
404 | /* regex matched and we're not negated, | |
405 | * considering it a match */ | |
406 | ret = 1; | |
407 | toret |= ret; | |
408 | break; | |
409 | } | |
410 | } else { | |
411 | SCLogDebug("pcre had matching error"); | |
412 | ret = 0; | |
413 | } | |
414 | } | |
415 | ||
416 | end: | |
417 | SCMutexUnlock(&f->m); | |
418 | SCLogDebug("released lock %p", &f->m); | |
419 | ||
420 | SCReturnInt(toret); | |
421 | } | |
422 | ||
423 | /** | |
424 | * \brief Match a regex on data sent at an http cookie (needs the l7 parser). | |
425 | * | |
426 | * \param det_ctx Thread detection ctx. | |
427 | * \param s Signature. | |
428 | * \param sm SigMatch to match against. | |
429 | * \param data Data to match against. | |
430 | * \param data_len Data length. | |
431 | * | |
432 | * \retval 1: match | |
433 | * \retval 0: no match | |
434 | */ | |
435 | int DetectPcreALDoMatchCookie(DetectEngineThreadCtx *det_ctx, Signature *s, | |
436 | SigMatch *m, Flow *f, uint8_t flags, | |
437 | void *state) | |
438 | { | |
439 | SCEnter(); | |
440 | ||
441 | int ret = 0; | |
442 | int toret = 0; | |
443 | size_t idx; | |
444 | ||
445 | #define MAX_SUBSTRINGS 30 | |
446 | int ov[MAX_SUBSTRINGS]; | |
447 | uint8_t *ptr = NULL; | |
448 | uint16_t len = 0; | |
449 | ||
450 | DetectPcreData *pe = (DetectPcreData *)m->ctx; | |
451 | ||
452 | /* define ptr & len */ | |
453 | SCMutexLock(&f->m); | |
454 | SCLogDebug("got lock %p", &f->m); | |
455 | ||
456 | HtpState *htp_state = (HtpState *)state; | |
457 | if (htp_state == NULL) { | |
458 | SCLogDebug("no HTTP layer state has been received, so no match"); | |
459 | goto end; | |
460 | } | |
461 | ||
462 | if (!(htp_state->flags & HTP_FLAG_STATE_OPEN)) { | |
463 | SCLogDebug("HTP state not yet properly setup, so no match"); | |
464 | goto end; | |
465 | } | |
466 | ||
467 | SCLogDebug("htp_state %p, flow %p", htp_state, f); | |
468 | SCLogDebug("htp_state->connp %p", htp_state->connp); | |
469 | SCLogDebug("htp_state->connp->conn %p", htp_state->connp->conn); | |
470 | ||
471 | if (htp_state->connp == NULL || htp_state->connp->conn == NULL) { | |
472 | SCLogDebug("HTTP connection structure is NULL"); | |
473 | goto end; | |
474 | } | |
475 | ||
476 | htp_tx_t *tx = NULL; | |
477 | ||
478 | for (idx = 0;//htp_state->new_in_tx_index; | |
479 | idx < list_size(htp_state->connp->conn->transactions); idx++) | |
480 | { | |
481 | tx = list_get(htp_state->connp->conn->transactions, idx); | |
482 | if (tx == NULL) | |
483 | continue; | |
484 | ||
485 | htp_header_t *h = NULL; | |
486 | h = (htp_header_t *) table_getc(tx->request_headers, "Cookie"); | |
487 | if (h == NULL) { | |
488 | SCLogDebug("no HTTP Cookie header in the received request"); | |
489 | goto end; | |
490 | } | |
491 | ptr = (uint8_t *) bstr_ptr(h->value); | |
492 | len = bstr_size(h->value); | |
493 | ||
494 | if (ptr == NULL) | |
495 | continue; | |
496 | ||
497 | //printf("Matching Cookie"); | |
498 | //PrintRawUriFp(stdout, (uint8_t*)ptr, len); | |
499 | ||
500 | SCLogDebug("we have a cookie header"); | |
501 | ||
502 | /* run the actual pcre detection */ | |
503 | ret = pcre_exec(pe->re, pe->sd, (char *)ptr, len, 0, 0, ov, MAX_SUBSTRINGS); | |
504 | SCLogDebug("ret %d (negating %s)", ret, pe->negate ? "set" : "not set"); | |
505 | ||
506 | if (ret == PCRE_ERROR_NOMATCH) { | |
507 | if (pe->negate == 1) { | |
508 | /* regex didn't match with negate option means we | |
509 | * consider it a match */ | |
510 | ret = 1; | |
511 | toret |= ret; | |
512 | break; | |
513 | } else { | |
514 | ret = 0; | |
515 | } | |
516 | toret |= ret; | |
517 | } else if (ret >= 0) { | |
518 | if (pe->negate == 1) { | |
519 | /* regex matched but we're negated, so not | |
520 | * considering it a match */ | |
521 | ret = 0; | |
522 | } else { | |
523 | /* regex matched and we're not negated, | |
524 | * considering it a match */ | |
525 | ret = 1; | |
526 | toret |= ret; | |
527 | break; | |
528 | } | |
529 | } else { | |
530 | SCLogDebug("pcre had matching error"); | |
531 | if (pe->negate == 1) { | |
532 | ret = 1; | |
533 | toret |= ret; | |
534 | break; | |
535 | } else { | |
536 | ret = 0; | |
537 | } | |
538 | toret |= ret; | |
539 | } | |
540 | } | |
541 | ||
542 | end: | |
543 | SCMutexUnlock(&f->m); | |
544 | SCLogDebug("released lock %p", &f->m); | |
545 | ||
546 | SCReturnInt(toret); | |
163 | 547 | } |
164 | 548 | |
165 | 549 | int DetectPcreALDoMatch(DetectEngineThreadCtx *det_ctx, Signature *s, SigMatch *m, Flow *f, uint8_t flags, void *state) { |
182 | 566 | if (htp_state == NULL) { |
183 | 567 | SCLogDebug("No htp state, no match at http body data"); |
184 | 568 | goto unlock; |
185 | } else if (htp_state->body.nchunks == 0) { | |
186 | SCLogDebug("No body data to inspect"); | |
187 | goto unlock; | |
188 | } else { | |
189 | pcreret = 0; | |
190 | int wspace[255]; | |
191 | int flags = PCRE_PARTIAL; | |
192 | ||
193 | HtpBodyChunk *cur = htp_state->body.first; | |
194 | if (cur == NULL) { | |
195 | SCLogDebug("No body chunks to inspect"); | |
569 | } | |
570 | ||
571 | htp_tx_t *tx = NULL; | |
572 | size_t idx = 0; | |
573 | ||
574 | for (idx = 0;//hs->new_in_tx_index; | |
575 | idx < list_size(htp_state->connp->conn->transactions); idx++) | |
576 | { | |
577 | tx = list_get(htp_state->connp->conn->transactions, idx); | |
578 | if (tx == NULL) | |
579 | continue; | |
580 | ||
581 | SCHtpTxUserData *htud = (SCHtpTxUserData *) htp_tx_get_user_data(tx); | |
582 | if (htud == NULL) | |
583 | continue; | |
584 | ||
585 | HtpBodyChunk *cur = htud->body.first; | |
586 | if (htud->body.nchunks == 0) { | |
587 | SCLogDebug("No body data to inspect"); | |
196 | 588 | goto unlock; |
197 | } | |
198 | htp_state->body.pcre_flags |= HTP_PCRE_DONE; | |
199 | ||
200 | while (cur != NULL) { | |
201 | if (SCLogDebugEnabled()) { | |
202 | printf("\n"); | |
203 | PrintRawUriFp(stdout, (uint8_t*)cur->data, cur->len); | |
204 | printf("\n"); | |
589 | } else { | |
590 | pcreret = 0; | |
591 | int wspace[255]; | |
592 | int flags = PCRE_PARTIAL; | |
593 | ||
594 | if (cur == NULL) { | |
595 | SCLogDebug("No body chunks to inspect"); | |
596 | goto unlock; | |
205 | 597 | } |
206 | pcreret = pcre_dfa_exec(pe->re, NULL, (char*)cur->data, cur->len, 0, | |
207 | flags|PCRE_DFA_SHORTEST, ov, MAX_SUBSTRINGS, | |
208 | wspace, MAX_SUBSTRINGS); | |
209 | cur = cur->next; | |
210 | ||
211 | SCLogDebug("Pcre Ret %d", pcreret); | |
212 | switch (pcreret) { | |
213 | case PCRE_ERROR_PARTIAL: | |
214 | /* make pcre to use the working space of the last partial | |
215 | * match, (match over multiple chunks) | |
216 | */ | |
217 | SCLogDebug("partial match"); | |
218 | flags |= PCRE_DFA_RESTART; | |
219 | htp_state->body.pcre_flags |= HTP_PCRE_HAS_MATCH; | |
220 | break; | |
221 | case PCRE_ERROR_NOMATCH: | |
222 | SCLogDebug("no match"); | |
223 | flags = PCRE_PARTIAL; | |
224 | break; | |
225 | case 0: | |
226 | SCLogDebug("Perfect Match!"); | |
227 | ret = 1; | |
228 | goto unlock; | |
229 | break; | |
230 | default: | |
231 | if (pcreret > 0) { | |
232 | SCLogDebug("Match with captured data"); | |
598 | htud->body.pcre_flags |= HTP_PCRE_DONE; | |
599 | ||
600 | while (cur != NULL) { | |
601 | if (SCLogDebugEnabled()) { | |
602 | printf("\n"); | |
603 | PrintRawUriFp(stdout, (uint8_t*)cur->data, cur->len); | |
604 | printf("\n"); | |
605 | } | |
606 | pcreret = pcre_dfa_exec(pe->re, NULL, (char*)cur->data, cur->len, 0, | |
607 | flags|PCRE_DFA_SHORTEST, ov, MAX_SUBSTRINGS, | |
608 | wspace, MAX_SUBSTRINGS); | |
609 | cur = cur->next; | |
610 | ||
611 | SCLogDebug("Pcre Ret %d", pcreret); | |
612 | switch (pcreret) { | |
613 | case PCRE_ERROR_PARTIAL: | |
614 | /* make pcre to use the working space of the last partial | |
615 | * match, (match over multiple chunks) | |
616 | */ | |
617 | SCLogDebug("partial match"); | |
618 | flags |= PCRE_DFA_RESTART; | |
619 | htud->body.pcre_flags |= HTP_PCRE_HAS_MATCH; | |
620 | break; | |
621 | case PCRE_ERROR_NOMATCH: | |
622 | SCLogDebug("no match"); | |
623 | flags = PCRE_PARTIAL; | |
624 | break; | |
625 | case 0: | |
626 | SCLogDebug("Perfect Match!"); | |
233 | 627 | ret = 1; |
234 | } else { | |
235 | SCLogDebug("No match, pcre failed"); | |
236 | ret = 0; | |
237 | } | |
238 | goto unlock; | |
628 | goto unlock; | |
629 | break; | |
630 | default: | |
631 | if (pcreret > 0) { | |
632 | SCLogDebug("Match with captured data"); | |
633 | ret = 1; | |
634 | } else { | |
635 | SCLogDebug("No match, pcre failed"); | |
636 | ret = 0; | |
637 | } | |
638 | goto unlock; | |
639 | } | |
239 | 640 | } |
240 | 641 | } |
241 | 642 | } |
259 | 660 | uint8_t flags, void *state, Signature *s, SigMatch *m) |
260 | 661 | { |
261 | 662 | int r = DetectPcreALDoMatch(det_ctx, s, m, f, flags, state); |
663 | SCReturnInt(r); | |
664 | } | |
665 | ||
666 | /** | |
667 | * \brief match the specified pcre at http header, requesting it from htp/L7 | |
668 | * | |
669 | * \param t pointer to thread vars | |
670 | * \param det_ctx pointer to the pattern matcher thread | |
671 | * \param p pointer to the current packet | |
672 | * \param m pointer to the sigmatch that we will cast into DetectPcreData | |
673 | * | |
674 | * \retval int 0 no match; 1 match | |
675 | */ | |
676 | int DetectPcreALMatchHeader(ThreadVars *t, DetectEngineThreadCtx *det_ctx, Flow *f, | |
677 | uint8_t flags, void *state, Signature *s, SigMatch *m) | |
678 | { | |
679 | int r = DetectPcreALDoMatchHeader(det_ctx, s, m, f, flags, state); | |
680 | SCReturnInt(r); | |
681 | } | |
682 | ||
683 | /** | |
684 | * \brief match the specified pcre at http method, requesting it from htp/L7 | |
685 | * | |
686 | * \param t pointer to thread vars | |
687 | * \param det_ctx pointer to the pattern matcher thread | |
688 | * \param p pointer to the current packet | |
689 | * \param m pointer to the sigmatch that we will cast into DetectPcreData | |
690 | * | |
691 | * \retval int 0 no match; 1 match | |
692 | */ | |
693 | int DetectPcreALMatchMethod(ThreadVars *t, DetectEngineThreadCtx *det_ctx, Flow *f, | |
694 | uint8_t flags, void *state, Signature *s, SigMatch *m) | |
695 | { | |
696 | int r = DetectPcreALDoMatchMethod(det_ctx, s, m, f, flags, state); | |
697 | SCReturnInt(r); | |
698 | } | |
699 | ||
700 | /** | |
701 | * \brief match the specified pcre at http cookie, requesting it from htp/L7 | |
702 | * | |
703 | * \param t pointer to thread vars | |
704 | * \param det_ctx pointer to the pattern matcher thread | |
705 | * \param p pointer to the current packet | |
706 | * \param m pointer to the sigmatch that we will cast into DetectPcreData | |
707 | * | |
708 | * \retval int 0 no match; 1 match | |
709 | */ | |
710 | int DetectPcreALMatchCookie(ThreadVars *t, DetectEngineThreadCtx *det_ctx, Flow *f, | |
711 | uint8_t flags, void *state, Signature *s, SigMatch *m) | |
712 | { | |
713 | int r = DetectPcreALDoMatchCookie(det_ctx, s, m, f, flags, state); | |
262 | 714 | SCReturnInt(r); |
263 | 715 | } |
264 | 716 | |
662 | 1114 | case 'U': /* snort's option */ |
663 | 1115 | pd->flags |= DETECT_PCRE_URI; |
664 | 1116 | break; |
1117 | case 'H': /* snort's option */ | |
1118 | pd->flags |= DETECT_PCRE_HEADER; | |
1119 | break; | |
1120 | case 'M': /* snort's option */ | |
1121 | pd->flags |= DETECT_PCRE_METHOD; | |
1122 | break; | |
1123 | case 'C': /* snort's option */ | |
1124 | pd->flags |= DETECT_PCRE_COOKIE; | |
1125 | break; | |
665 | 1126 | case 'O': |
666 | 1127 | pd->flags |= DETECT_PCRE_MATCH_LIMIT; |
667 | 1128 | break; |
811 | 1272 | switch (s->alproto) { |
812 | 1273 | case ALPROTO_DCERPC: |
813 | 1274 | if ( (pd->flags & DETECT_PCRE_URI) || |
1275 | (pd->flags & DETECT_PCRE_METHOD) || | |
1276 | (pd->flags & DETECT_PCRE_HEADER) || | |
1277 | (pd->flags & DETECT_PCRE_COOKIE) || | |
814 | 1278 | (pd->flags & DETECT_PCRE_HTTP_BODY_AL) ) { |
815 | 1279 | SCLogError(SC_ERR_CONFLICTING_RULE_KEYWORDS, "Invalid option. " |
816 | 1280 | "DCERPC rule has pcre keyword with http related modifier."); |
833 | 1297 | sm->type = DETECT_PCRE; |
834 | 1298 | sm->ctx = (void *)pd; |
835 | 1299 | |
836 | if (pd->flags & DETECT_PCRE_HTTP_BODY_AL) { | |
1300 | if (pd->flags & DETECT_PCRE_HEADER) { | |
1301 | sm->type = DETECT_PCRE_HTTPHEADER; | |
1302 | ||
1303 | SCLogDebug("Header inspection modifier set"); | |
1304 | s->flags |= SIG_FLAG_APPLAYER; | |
1305 | ||
1306 | SigMatchAppendAppLayer(s, sm); | |
1307 | } else if (pd->flags & DETECT_PCRE_COOKIE) { | |
1308 | sm->type = DETECT_PCRE_HTTPCOOKIE; | |
1309 | ||
1310 | SCLogDebug("Cookie inspection modifier set"); | |
1311 | s->flags |= SIG_FLAG_APPLAYER; | |
1312 | ||
1313 | SigMatchAppendAppLayer(s, sm); | |
1314 | } else if (pd->flags & DETECT_PCRE_METHOD) { | |
1315 | sm->type = DETECT_PCRE_HTTPMETHOD; | |
1316 | ||
1317 | SCLogDebug("Method inspection modifier set"); | |
1318 | s->flags |= SIG_FLAG_APPLAYER; | |
1319 | ||
1320 | SigMatchAppendAppLayer(s, sm); | |
1321 | } else if (pd->flags & DETECT_PCRE_HTTP_BODY_AL) { | |
837 | 1322 | sm->type = DETECT_PCRE_HTTPBODY; |
838 | 1323 | |
839 | 1324 | SCLogDebug("Body inspection modifier set"); |
1291 | 1776 | TcpSession ssn; |
1292 | 1777 | Packet *p = NULL; |
1293 | 1778 | ThreadVars th_v; |
1294 | DetectEngineThreadCtx *det_ctx; | |
1779 | DetectEngineThreadCtx *det_ctx = NULL; | |
1295 | 1780 | int result = 0; |
1296 | 1781 | Flow f; |
1297 | 1782 | |
1720 | 2205 | /* do detect for p1 */ |
1721 | 2206 | SigMatchSignatures(&th_v, de_ctx, det_ctx, p1); |
1722 | 2207 | |
2208 | HtpState *http_state = f.aldata[AlpGetStateIdx(ALPROTO_HTTP)]; | |
2209 | if (http_state == NULL) { | |
2210 | printf("no http state: "); | |
2211 | result = 0; | |
2212 | goto end; | |
2213 | } | |
2214 | ||
2215 | if (!(PacketAlertCheck(p1, 1))) { | |
2216 | printf("sid 1 didn't match on p1 but should have: "); | |
2217 | goto end; | |
2218 | } | |
2219 | ||
2220 | if (PacketAlertCheck(p1, 2)) { | |
2221 | printf("sid 2 did match on p1 but shouldn't have: "); | |
2222 | /* It's a partial match over 2 chunks*/ | |
2223 | goto end; | |
2224 | } | |
2225 | ||
1723 | 2226 | r = AppLayerParse(&f, ALPROTO_HTTP, STREAM_TOSERVER, httpbuf2, httplen2); |
1724 | 2227 | if (r != 0) { |
1725 | 2228 | printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r); |
1727 | 2230 | goto end; |
1728 | 2231 | } |
1729 | 2232 | |
1730 | HtpState *http_state = f.aldata[AlpGetStateIdx(ALPROTO_HTTP)]; | |
1731 | if (http_state == NULL) { | |
1732 | printf("no http state: "); | |
1733 | result = 0; | |
1734 | goto end; | |
1735 | } | |
1736 | ||
1737 | 2233 | /* do detect for p2 */ |
1738 | 2234 | SigMatchSignatures(&th_v, de_ctx, det_ctx, p2); |
1739 | ||
1740 | if (!(PacketAlertCheck(p1, 1))) { | |
1741 | printf("sid 1 didn't match on p1 but should have: "); | |
1742 | goto end; | |
1743 | } | |
1744 | ||
1745 | if (PacketAlertCheck(p1, 2)) { | |
1746 | printf("sid 2 did match on p1 but shouldn't have: "); | |
1747 | /* It's a partial match over 2 chunks*/ | |
1748 | goto end; | |
1749 | } | |
1750 | 2235 | |
1751 | 2236 | if ((PacketAlertCheck(p2, 1))) { |
1752 | 2237 | printf("sid 1 did match on p2 but should have: "); |
1830 | 2315 | end: |
1831 | 2316 | if (p != NULL) |
1832 | 2317 | UTHFreePacket(p); |
2318 | return result; | |
2319 | } | |
2320 | ||
2321 | /** \test Check the signature working to alert when cookie modifier is | |
2322 | * passed to pcre | |
2323 | */ | |
2324 | static int DetectPcreTestSig09(void) { | |
2325 | int result = 0; | |
2326 | Flow f; | |
2327 | uint8_t httpbuf1[] = "POST / HTTP/1.0\r\nUser-Agent: Mozilla/1.0\r\n" | |
2328 | "Cookie: dummy\r\n\r\n"; | |
2329 | uint32_t httplen1 = sizeof(httpbuf1) - 1; /* minus the \0 */ | |
2330 | TcpSession ssn; | |
2331 | Packet *p = NULL; | |
2332 | Signature *s = NULL; | |
2333 | ThreadVars th_v; | |
2334 | DetectEngineThreadCtx *det_ctx = NULL; | |
2335 | HtpState *http_state = NULL; | |
2336 | ||
2337 | memset(&th_v, 0, sizeof(th_v)); | |
2338 | memset(&p, 0, sizeof(p)); | |
2339 | memset(&f, 0, sizeof(f)); | |
2340 | memset(&ssn, 0, sizeof(ssn)); | |
2341 | ||
2342 | p = UTHBuildPacket(NULL, 0, IPPROTO_TCP); | |
2343 | ||
2344 | FLOW_INITIALIZE(&f); | |
2345 | f.protoctx = (void *)&ssn; | |
2346 | f.src.family = AF_INET; | |
2347 | f.dst.family = AF_INET; | |
2348 | ||
2349 | p->flow = &f; | |
2350 | p->flowflags |= FLOW_PKT_TOSERVER; | |
2351 | p->flowflags |= FLOW_PKT_ESTABLISHED; | |
2352 | f.alproto = ALPROTO_HTTP; | |
2353 | ||
2354 | StreamTcpInitConfig(TRUE); | |
2355 | FlowL7DataPtrInit(&f); | |
2356 | ||
2357 | DetectEngineCtx *de_ctx = DetectEngineCtxInit(); | |
2358 | if (de_ctx == NULL) { | |
2359 | goto end; | |
2360 | } | |
2361 | ||
2362 | de_ctx->flags |= DE_QUIET; | |
2363 | ||
2364 | s = de_ctx->sig_list = SigInit(de_ctx,"alert http any any -> any any (msg:" | |
2365 | "\"HTTP cookie\"; pcre:\"/dummy/C\"; " | |
2366 | " sid:1;)"); | |
2367 | if (s == NULL) { | |
2368 | printf("sig parse failed: "); | |
2369 | goto end; | |
2370 | } | |
2371 | ||
2372 | SigGroupBuild(de_ctx); | |
2373 | DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx); | |
2374 | ||
2375 | int r = AppLayerParse(&f, ALPROTO_HTTP, STREAM_TOSERVER, httpbuf1, httplen1); | |
2376 | if (r != 0) { | |
2377 | printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r); | |
2378 | goto end; | |
2379 | } | |
2380 | ||
2381 | http_state = f.aldata[AlpGetStateIdx(ALPROTO_HTTP)]; | |
2382 | if (http_state == NULL) { | |
2383 | printf("no http state: "); | |
2384 | goto end; | |
2385 | } | |
2386 | ||
2387 | /* do detect */ | |
2388 | SigMatchSignatures(&th_v, de_ctx, det_ctx, p); | |
2389 | ||
2390 | if (!PacketAlertCheck(p, 1)) { | |
2391 | printf("sig 1 failed to match: "); | |
2392 | goto end; | |
2393 | } | |
2394 | ||
2395 | result = 1; | |
2396 | end: | |
2397 | if (det_ctx != NULL) { | |
2398 | DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx); | |
2399 | } | |
2400 | if (de_ctx != NULL) { | |
2401 | SigGroupCleanup(de_ctx); | |
2402 | DetectEngineCtxFree(de_ctx); | |
2403 | } | |
2404 | ||
2405 | FlowL7DataPtrFree(&f); | |
2406 | StreamTcpFreeConfig(TRUE); | |
2407 | UTHFreePackets(&p, 1); | |
2408 | return result; | |
2409 | } | |
2410 | ||
2411 | /** \test Check the signature working to alert when cookie modifier is | |
2412 | * passed to a negated pcre | |
2413 | */ | |
2414 | static int DetectPcreTestSig10(void) { | |
2415 | int result = 0; | |
2416 | Flow f; | |
2417 | uint8_t httpbuf1[] = "POST / HTTP/1.0\r\nUser-Agent: Mozilla/1.0\r\n" | |
2418 | "Cookie: dummoOOooooO\r\n\r\n"; | |
2419 | uint32_t httplen1 = sizeof(httpbuf1) - 1; /* minus the \0 */ | |
2420 | TcpSession ssn; | |
2421 | Packet *p = NULL; | |
2422 | Signature *s = NULL; | |
2423 | ThreadVars th_v; | |
2424 | DetectEngineThreadCtx *det_ctx = NULL; | |
2425 | HtpState *http_state = NULL; | |
2426 | ||
2427 | memset(&th_v, 0, sizeof(th_v)); | |
2428 | memset(&p, 0, sizeof(p)); | |
2429 | memset(&f, 0, sizeof(f)); | |
2430 | memset(&ssn, 0, sizeof(ssn)); | |
2431 | ||
2432 | p = UTHBuildPacket(NULL, 0, IPPROTO_TCP); | |
2433 | ||
2434 | FLOW_INITIALIZE(&f); | |
2435 | f.protoctx = (void *)&ssn; | |
2436 | f.src.family = AF_INET; | |
2437 | f.dst.family = AF_INET; | |
2438 | ||
2439 | p->flow = &f; | |
2440 | p->flowflags |= FLOW_PKT_TOSERVER; | |
2441 | p->flowflags |= FLOW_PKT_ESTABLISHED; | |
2442 | f.alproto = ALPROTO_HTTP; | |
2443 | ||
2444 | StreamTcpInitConfig(TRUE); | |
2445 | FlowL7DataPtrInit(&f); | |
2446 | ||
2447 | DetectEngineCtx *de_ctx = DetectEngineCtxInit(); | |
2448 | if (de_ctx == NULL) { | |
2449 | goto end; | |
2450 | } | |
2451 | ||
2452 | de_ctx->flags |= DE_QUIET; | |
2453 | ||
2454 | s = de_ctx->sig_list = SigInit(de_ctx,"alert http any any -> any any (msg:" | |
2455 | "\"HTTP cookie\"; pcre:!\"/dummy/C\"; " | |
2456 | " sid:1;)"); | |
2457 | if (s == NULL) { | |
2458 | printf("sig parse failed: "); | |
2459 | goto end; | |
2460 | } | |
2461 | ||
2462 | SigGroupBuild(de_ctx); | |
2463 | DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx); | |
2464 | ||
2465 | int r = AppLayerParse(&f, ALPROTO_HTTP, STREAM_TOSERVER, httpbuf1, httplen1); | |
2466 | if (r != 0) { | |
2467 | printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r); | |
2468 | goto end; | |
2469 | } | |
2470 | ||
2471 | http_state = f.aldata[AlpGetStateIdx(ALPROTO_HTTP)]; | |
2472 | if (http_state == NULL) { | |
2473 | printf("no http state: "); | |
2474 | goto end; | |
2475 | } | |
2476 | ||
2477 | /* do detect */ | |
2478 | SigMatchSignatures(&th_v, de_ctx, det_ctx, p); | |
2479 | ||
2480 | if (!PacketAlertCheck(p, 1)) { | |
2481 | printf("sig 1 should match: "); | |
2482 | goto end; | |
2483 | } | |
2484 | ||
2485 | result = 1; | |
2486 | end: | |
2487 | if (det_ctx != NULL) { | |
2488 | DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx); | |
2489 | } | |
2490 | if (de_ctx != NULL) { | |
2491 | SigGroupCleanup(de_ctx); | |
2492 | DetectEngineCtxFree(de_ctx); | |
2493 | } | |
2494 | ||
2495 | FlowL7DataPtrFree(&f); | |
2496 | StreamTcpFreeConfig(TRUE); | |
2497 | UTHFreePackets(&p, 1); | |
2498 | return result; | |
2499 | } | |
2500 | ||
2501 | /** \test Check the signature working to alert when method modifier is | |
2502 | * passed to pcre | |
2503 | */ | |
2504 | static int DetectPcreTestSig11(void) { | |
2505 | int result = 0; | |
2506 | Flow f; | |
2507 | uint8_t httpbuf1[] = "POST / HTTP/1.0\r\nUser-Agent: Mozilla/1.0\r\n" | |
2508 | "Cookie: dummy\r\n\r\n"; | |
2509 | uint32_t httplen1 = sizeof(httpbuf1) - 1; /* minus the \0 */ | |
2510 | TcpSession ssn; | |
2511 | Packet *p = NULL; | |
2512 | Signature *s = NULL; | |
2513 | ThreadVars th_v; | |
2514 | DetectEngineThreadCtx *det_ctx = NULL; | |
2515 | HtpState *http_state = NULL; | |
2516 | ||
2517 | memset(&th_v, 0, sizeof(th_v)); | |
2518 | memset(&p, 0, sizeof(p)); | |
2519 | memset(&f, 0, sizeof(f)); | |
2520 | memset(&ssn, 0, sizeof(ssn)); | |
2521 | ||
2522 | p = UTHBuildPacket(NULL, 0, IPPROTO_TCP); | |
2523 | ||
2524 | FLOW_INITIALIZE(&f); | |
2525 | f.protoctx = (void *)&ssn; | |
2526 | f.src.family = AF_INET; | |
2527 | f.dst.family = AF_INET; | |
2528 | ||
2529 | p->flow = &f; | |
2530 | p->flowflags |= FLOW_PKT_TOSERVER; | |
2531 | p->flowflags |= FLOW_PKT_ESTABLISHED; | |
2532 | f.alproto = ALPROTO_HTTP; | |
2533 | ||
2534 | StreamTcpInitConfig(TRUE); | |
2535 | FlowL7DataPtrInit(&f); | |
2536 | ||
2537 | DetectEngineCtx *de_ctx = DetectEngineCtxInit(); | |
2538 | if (de_ctx == NULL) { | |
2539 | goto end; | |
2540 | } | |
2541 | ||
2542 | de_ctx->flags |= DE_QUIET; | |
2543 | ||
2544 | s = de_ctx->sig_list = SigInit(de_ctx,"alert http any any -> any any (msg:" | |
2545 | "\"HTTP method\"; pcre:\"/POST/M\"; " | |
2546 | " sid:1;)"); | |
2547 | if (s == NULL) { | |
2548 | printf("sig parse failed: "); | |
2549 | goto end; | |
2550 | } | |
2551 | ||
2552 | SigGroupBuild(de_ctx); | |
2553 | DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx); | |
2554 | ||
2555 | int r = AppLayerParse(&f, ALPROTO_HTTP, STREAM_TOSERVER, httpbuf1, httplen1); | |
2556 | if (r != 0) { | |
2557 | printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r); | |
2558 | goto end; | |
2559 | } | |
2560 | ||
2561 | http_state = f.aldata[AlpGetStateIdx(ALPROTO_HTTP)]; | |
2562 | if (http_state == NULL) { | |
2563 | printf("no http state: "); | |
2564 | goto end; | |
2565 | } | |
2566 | ||
2567 | /* do detect */ | |
2568 | SigMatchSignatures(&th_v, de_ctx, det_ctx, p); | |
2569 | ||
2570 | if (!PacketAlertCheck(p, 1)) { | |
2571 | printf("sig 1 failed to match: "); | |
2572 | goto end; | |
2573 | } | |
2574 | ||
2575 | result = 1; | |
2576 | end: | |
2577 | if (det_ctx != NULL) { | |
2578 | DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx); | |
2579 | } | |
2580 | if (de_ctx != NULL) { | |
2581 | SigGroupCleanup(de_ctx); | |
2582 | DetectEngineCtxFree(de_ctx); | |
2583 | } | |
2584 | ||
2585 | FlowL7DataPtrFree(&f); | |
2586 | StreamTcpFreeConfig(TRUE); | |
2587 | UTHFreePackets(&p, 1); | |
2588 | return result; | |
2589 | } | |
2590 | ||
2591 | /** \test Check the signature working to alert when method modifier is | |
2592 | * passed to a negated pcre | |
2593 | */ | |
2594 | static int DetectPcreTestSig12(void) { | |
2595 | int result = 0; | |
2596 | Flow f; | |
2597 | uint8_t httpbuf1[] = "GET / HTTP/1.0\r\nUser-Agent: Mozilla/1.0\r\n" | |
2598 | "Cookie: dummoOOooooO\r\n\r\n"; | |
2599 | uint32_t httplen1 = sizeof(httpbuf1) - 1; /* minus the \0 */ | |
2600 | TcpSession ssn; | |
2601 | Packet *p = NULL; | |
2602 | Signature *s = NULL; | |
2603 | ThreadVars th_v; | |
2604 | DetectEngineThreadCtx *det_ctx = NULL; | |
2605 | HtpState *http_state = NULL; | |
2606 | ||
2607 | memset(&th_v, 0, sizeof(th_v)); | |
2608 | memset(&p, 0, sizeof(p)); | |
2609 | memset(&f, 0, sizeof(f)); | |
2610 | memset(&ssn, 0, sizeof(ssn)); | |
2611 | ||
2612 | p = UTHBuildPacket(NULL, 0, IPPROTO_TCP); | |
2613 | ||
2614 | FLOW_INITIALIZE(&f); | |
2615 | f.protoctx = (void *)&ssn; | |
2616 | f.src.family = AF_INET; | |
2617 | f.dst.family = AF_INET; | |
2618 | ||
2619 | p->flow = &f; | |
2620 | p->flowflags |= FLOW_PKT_TOSERVER; | |
2621 | p->flowflags |= FLOW_PKT_ESTABLISHED; | |
2622 | f.alproto = ALPROTO_HTTP; | |
2623 | ||
2624 | StreamTcpInitConfig(TRUE); | |
2625 | FlowL7DataPtrInit(&f); | |
2626 | ||
2627 | DetectEngineCtx *de_ctx = DetectEngineCtxInit(); | |
2628 | if (de_ctx == NULL) { | |
2629 | goto end; | |
2630 | } | |
2631 | ||
2632 | de_ctx->flags |= DE_QUIET; | |
2633 | ||
2634 | s = de_ctx->sig_list = SigInit(de_ctx,"alert http any any -> any any (msg:" | |
2635 | "\"HTTP method\"; pcre:!\"/POST/M\"; " | |
2636 | " sid:1;)"); | |
2637 | if (s == NULL) { | |
2638 | printf("sig parse failed: "); | |
2639 | goto end; | |
2640 | } | |
2641 | ||
2642 | SigGroupBuild(de_ctx); | |
2643 | DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx); | |
2644 | ||
2645 | int r = AppLayerParse(&f, ALPROTO_HTTP, STREAM_TOSERVER, httpbuf1, httplen1); | |
2646 | if (r != 0) { | |
2647 | printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r); | |
2648 | goto end; | |
2649 | } | |
2650 | ||
2651 | http_state = f.aldata[AlpGetStateIdx(ALPROTO_HTTP)]; | |
2652 | if (http_state == NULL) { | |
2653 | printf("no http state: "); | |
2654 | goto end; | |
2655 | } | |
2656 | ||
2657 | /* do detect */ | |
2658 | SigMatchSignatures(&th_v, de_ctx, det_ctx, p); | |
2659 | ||
2660 | if (!PacketAlertCheck(p, 1)) { | |
2661 | printf("sig 1 should match: "); | |
2662 | goto end; | |
2663 | } | |
2664 | ||
2665 | result = 1; | |
2666 | end: | |
2667 | if (det_ctx != NULL) { | |
2668 | DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx); | |
2669 | } | |
2670 | if (de_ctx != NULL) { | |
2671 | SigGroupCleanup(de_ctx); | |
2672 | DetectEngineCtxFree(de_ctx); | |
2673 | } | |
2674 | ||
2675 | FlowL7DataPtrFree(&f); | |
2676 | StreamTcpFreeConfig(TRUE); | |
2677 | UTHFreePackets(&p, 1); | |
2678 | return result; | |
2679 | } | |
2680 | ||
2681 | /** \test Check the signature working to alert when header modifier is | |
2682 | * passed to pcre | |
2683 | */ | |
2684 | static int DetectPcreTestSig13(void) { | |
2685 | int result = 0; | |
2686 | Flow f; | |
2687 | uint8_t httpbuf1[] = "POST / HTTP/1.0\r\nUser-Agent: Mozilla/1.0\r\n" | |
2688 | "Cookie: dummy\r\n\r\n"; | |
2689 | uint32_t httplen1 = sizeof(httpbuf1) - 1; /* minus the \0 */ | |
2690 | TcpSession ssn; | |
2691 | Packet *p = NULL; | |
2692 | Signature *s = NULL; | |
2693 | ThreadVars th_v; | |
2694 | DetectEngineThreadCtx *det_ctx = NULL; | |
2695 | HtpState *http_state = NULL; | |
2696 | ||
2697 | memset(&th_v, 0, sizeof(th_v)); | |
2698 | memset(&p, 0, sizeof(p)); | |
2699 | memset(&f, 0, sizeof(f)); | |
2700 | memset(&ssn, 0, sizeof(ssn)); | |
2701 | ||
2702 | p = UTHBuildPacket(NULL, 0, IPPROTO_TCP); | |
2703 | ||
2704 | FLOW_INITIALIZE(&f); | |
2705 | f.protoctx = (void *)&ssn; | |
2706 | f.src.family = AF_INET; | |
2707 | f.dst.family = AF_INET; | |
2708 | ||
2709 | p->flow = &f; | |
2710 | p->flowflags |= FLOW_PKT_TOSERVER; | |
2711 | p->flowflags |= FLOW_PKT_ESTABLISHED; | |
2712 | f.alproto = ALPROTO_HTTP; | |
2713 | ||
2714 | StreamTcpInitConfig(TRUE); | |
2715 | FlowL7DataPtrInit(&f); | |
2716 | ||
2717 | DetectEngineCtx *de_ctx = DetectEngineCtxInit(); | |
2718 | if (de_ctx == NULL) { | |
2719 | goto end; | |
2720 | } | |
2721 | ||
2722 | de_ctx->flags |= DE_QUIET; | |
2723 | ||
2724 | s = de_ctx->sig_list = SigInit(de_ctx,"alert http any any -> any any (msg:" | |
2725 | "\"HTTP header\"; pcre:\"/User[-_]Agent[:]?\\sMozilla/H\"; " | |
2726 | " sid:1;)"); | |
2727 | if (s == NULL) { | |
2728 | printf("sig parse failed: "); | |
2729 | goto end; | |
2730 | } | |
2731 | ||
2732 | SigGroupBuild(de_ctx); | |
2733 | DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx); | |
2734 | ||
2735 | int r = AppLayerParse(&f, ALPROTO_HTTP, STREAM_TOSERVER, httpbuf1, httplen1); | |
2736 | if (r != 0) { | |
2737 | printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r); | |
2738 | goto end; | |
2739 | } | |
2740 | ||
2741 | http_state = f.aldata[AlpGetStateIdx(ALPROTO_HTTP)]; | |
2742 | if (http_state == NULL) { | |
2743 | printf("no http state: "); | |
2744 | goto end; | |
2745 | } | |
2746 | ||
2747 | /* do detect */ | |
2748 | SigMatchSignatures(&th_v, de_ctx, det_ctx, p); | |
2749 | ||
2750 | if (!PacketAlertCheck(p, 1)) { | |
2751 | printf("sig 1 failed to match: "); | |
2752 | goto end; | |
2753 | } | |
2754 | ||
2755 | result = 1; | |
2756 | end: | |
2757 | if (det_ctx != NULL) { | |
2758 | DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx); | |
2759 | } | |
2760 | if (de_ctx != NULL) { | |
2761 | SigGroupCleanup(de_ctx); | |
2762 | DetectEngineCtxFree(de_ctx); | |
2763 | } | |
2764 | ||
2765 | FlowL7DataPtrFree(&f); | |
2766 | StreamTcpFreeConfig(TRUE); | |
2767 | UTHFreePackets(&p, 1); | |
2768 | return result; | |
2769 | } | |
2770 | ||
2771 | /** \test Check the signature working to alert when header modifier is | |
2772 | * passed to a negated pcre | |
2773 | */ | |
2774 | static int DetectPcreTestSig14(void) { | |
2775 | int result = 0; | |
2776 | Flow f; | |
2777 | uint8_t httpbuf1[] = "GET / HTTP/1.0\r\nUser-Agent: IEXPLORER/1.0\r\n" | |
2778 | "Cookie: dummoOOooooO\r\n\r\n"; | |
2779 | uint32_t httplen1 = sizeof(httpbuf1) - 1; /* minus the \0 */ | |
2780 | TcpSession ssn; | |
2781 | Packet *p = NULL; | |
2782 | Signature *s = NULL; | |
2783 | ThreadVars th_v; | |
2784 | DetectEngineThreadCtx *det_ctx = NULL; | |
2785 | HtpState *http_state = NULL; | |
2786 | ||
2787 | memset(&th_v, 0, sizeof(th_v)); | |
2788 | memset(&p, 0, sizeof(p)); | |
2789 | memset(&f, 0, sizeof(f)); | |
2790 | memset(&ssn, 0, sizeof(ssn)); | |
2791 | ||
2792 | p = UTHBuildPacket(NULL, 0, IPPROTO_TCP); | |
2793 | ||
2794 | FLOW_INITIALIZE(&f); | |
2795 | f.protoctx = (void *)&ssn; | |
2796 | f.src.family = AF_INET; | |
2797 | f.dst.family = AF_INET; | |
2798 | ||
2799 | p->flow = &f; | |
2800 | p->flowflags |= FLOW_PKT_TOSERVER; | |
2801 | p->flowflags |= FLOW_PKT_ESTABLISHED; | |
2802 | f.alproto = ALPROTO_HTTP; | |
2803 | ||
2804 | StreamTcpInitConfig(TRUE); | |
2805 | FlowL7DataPtrInit(&f); | |
2806 | ||
2807 | DetectEngineCtx *de_ctx = DetectEngineCtxInit(); | |
2808 | if (de_ctx == NULL) { | |
2809 | goto end; | |
2810 | } | |
2811 | ||
2812 | de_ctx->flags |= DE_QUIET; | |
2813 | ||
2814 | s = de_ctx->sig_list = SigInit(de_ctx,"alert http any any -> any any (msg:" | |
2815 | "\"HTTP header\"; pcre:!\"/User-Agent[:]?\\s+Mozilla/H\"; " | |
2816 | " sid:1;)"); | |
2817 | if (s == NULL) { | |
2818 | printf("sig parse failed: "); | |
2819 | goto end; | |
2820 | } | |
2821 | ||
2822 | SigGroupBuild(de_ctx); | |
2823 | DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx); | |
2824 | ||
2825 | int r = AppLayerParse(&f, ALPROTO_HTTP, STREAM_TOSERVER, httpbuf1, httplen1); | |
2826 | if (r != 0) { | |
2827 | printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r); | |
2828 | goto end; | |
2829 | } | |
2830 | ||
2831 | http_state = f.aldata[AlpGetStateIdx(ALPROTO_HTTP)]; | |
2832 | if (http_state == NULL) { | |
2833 | printf("no http state: "); | |
2834 | goto end; | |
2835 | } | |
2836 | ||
2837 | /* do detect */ | |
2838 | SigMatchSignatures(&th_v, de_ctx, det_ctx, p); | |
2839 | ||
2840 | if (!PacketAlertCheck(p, 1)) { | |
2841 | printf("sig 1 should match: "); | |
2842 | goto end; | |
2843 | } | |
2844 | ||
2845 | result = 1; | |
2846 | end: | |
2847 | if (det_ctx != NULL) { | |
2848 | DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx); | |
2849 | } | |
2850 | if (de_ctx != NULL) { | |
2851 | SigGroupCleanup(de_ctx); | |
2852 | DetectEngineCtxFree(de_ctx); | |
2853 | } | |
2854 | ||
2855 | FlowL7DataPtrFree(&f); | |
2856 | StreamTcpFreeConfig(TRUE); | |
2857 | UTHFreePackets(&p, 1); | |
2858 | return result; | |
2859 | } | |
2860 | ||
2861 | /** \test Test tracking of body chunks per transactions (on requests) | |
2862 | */ | |
2863 | static int DetectPcreTxBodyChunksTest01(void) { | |
2864 | int result = 0; | |
2865 | Flow f; | |
2866 | TcpSession ssn; | |
2867 | Packet *p = NULL; | |
2868 | uint8_t httpbuf1[] = "GET / HTTP/1.1\r\n"; | |
2869 | uint8_t httpbuf2[] = "User-Agent: Mozilla/1.0\r\nContent-Length: 10\r\n"; | |
2870 | uint8_t httpbuf3[] = "Cookie: dummy\r\n\r\n"; | |
2871 | uint8_t httpbuf4[] = "Body one!!"; | |
2872 | uint32_t httplen1 = sizeof(httpbuf1) - 1; /* minus the \0 */ | |
2873 | uint32_t httplen2 = sizeof(httpbuf2) - 1; /* minus the \0 */ | |
2874 | uint32_t httplen3 = sizeof(httpbuf3) - 1; /* minus the \0 */ | |
2875 | uint32_t httplen4 = sizeof(httpbuf4) - 1; /* minus the \0 */ | |
2876 | uint8_t httpbuf5[] = "GET /?var=val HTTP/1.1\r\n"; | |
2877 | uint8_t httpbuf6[] = "User-Agent: Firefox/1.0\r\n"; | |
2878 | uint8_t httpbuf7[] = "Cookie: dummy2\r\nContent-Length: 10\r\n\r\nBody two!!"; | |
2879 | uint32_t httplen5 = sizeof(httpbuf5) - 1; /* minus the \0 */ | |
2880 | uint32_t httplen6 = sizeof(httpbuf6) - 1; /* minus the \0 */ | |
2881 | uint32_t httplen7 = sizeof(httpbuf7) - 1; /* minus the \0 */ | |
2882 | ||
2883 | memset(&f, 0, sizeof(f)); | |
2884 | memset(&ssn, 0, sizeof(ssn)); | |
2885 | ||
2886 | p = UTHBuildPacket(NULL, 0, IPPROTO_TCP); | |
2887 | ||
2888 | FLOW_INITIALIZE(&f); | |
2889 | f.protoctx = (void *)&ssn; | |
2890 | f.proto = IPPROTO_TCP; | |
2891 | f.src.family = AF_INET; | |
2892 | f.dst.family = AF_INET; | |
2893 | ||
2894 | p->flow = &f; | |
2895 | p->flowflags |= FLOW_PKT_TOSERVER; | |
2896 | p->flowflags |= FLOW_PKT_ESTABLISHED; | |
2897 | f.alproto = ALPROTO_HTTP; | |
2898 | ||
2899 | StreamTcpInitConfig(TRUE); | |
2900 | FlowL7DataPtrInit(&f); | |
2901 | ||
2902 | AppLayerHtpEnableRequestBodyCallback(); | |
2903 | ||
2904 | int r = AppLayerParse(&f, ALPROTO_HTTP, STREAM_TOSERVER|STREAM_START, httpbuf1, httplen1); | |
2905 | if (r != 0) { | |
2906 | printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r); | |
2907 | goto end; | |
2908 | } | |
2909 | ||
2910 | r = AppLayerParse(&f, ALPROTO_HTTP, STREAM_TOSERVER, httpbuf2, httplen2); | |
2911 | if (r != 0) { | |
2912 | printf("toserver chunk 2 returned %" PRId32 ", expected 0: ", r); | |
2913 | goto end; | |
2914 | } | |
2915 | ||
2916 | r = AppLayerParse(&f, ALPROTO_HTTP, STREAM_TOSERVER, httpbuf3, httplen3); | |
2917 | if (r != 0) { | |
2918 | printf("toserver chunk 3 returned %" PRId32 ", expected 0: ", r); | |
2919 | goto end; | |
2920 | } | |
2921 | ||
2922 | r = AppLayerParse(&f, ALPROTO_HTTP, STREAM_TOSERVER, httpbuf4, httplen4); | |
2923 | if (r != 0) { | |
2924 | printf("toserver chunk 4 returned %" PRId32 ", expected 0: ", r); | |
2925 | result = 0; | |
2926 | goto end; | |
2927 | } | |
2928 | ||
2929 | r = AppLayerParse(&f, ALPROTO_HTTP, STREAM_TOSERVER, httpbuf5, httplen5); | |
2930 | if (r != 0) { | |
2931 | printf("toserver chunk 5 returned %" PRId32 ", expected 0: ", r); | |
2932 | goto end; | |
2933 | } | |
2934 | ||
2935 | r = AppLayerParse(&f, ALPROTO_HTTP, STREAM_TOSERVER, httpbuf6, httplen6); | |
2936 | if (r != 0) { | |
2937 | printf("toserver chunk 6 returned %" PRId32 ", expected 0: ", r); | |
2938 | goto end; | |
2939 | } | |
2940 | ||
2941 | r = AppLayerParse(&f, ALPROTO_HTTP, STREAM_TOSERVER, httpbuf7, httplen7); | |
2942 | if (r != 0) { | |
2943 | printf("toserver chunk 7 returned %" PRId32 ", expected 0: ", r); | |
2944 | goto end; | |
2945 | } | |
2946 | ||
2947 | /* Now we should have 2 transactions, each with it's own list | |
2948 | * of request body chunks (let's test it) */ | |
2949 | ||
2950 | HtpState *htp_state = f.aldata[AlpGetStateIdx(ALPROTO_HTTP)]; | |
2951 | if (htp_state == NULL) { | |
2952 | printf("no http state: "); | |
2953 | result = 0; | |
2954 | goto end; | |
2955 | } | |
2956 | ||
2957 | /* hardcoded check of the transactions and it's client body chunks */ | |
2958 | if (list_size(htp_state->connp->conn->transactions) != 2) { | |
2959 | printf("The http app layer doesn't have 2 transactions, but it should: "); | |
2960 | goto end; | |
2961 | } | |
2962 | ||
2963 | htp_tx_t *t1 = list_get(htp_state->connp->conn->transactions, 0); | |
2964 | htp_tx_t *t2 = list_get(htp_state->connp->conn->transactions, 1); | |
2965 | ||
2966 | SCHtpTxUserData *htud = (SCHtpTxUserData *) htp_tx_get_user_data(t1); | |
2967 | if (htud == NULL) { | |
2968 | printf("No body data in t1 (it should be removed only when the tx is destroyed): "); | |
2969 | goto end; | |
2970 | } | |
2971 | ||
2972 | HtpBodyChunk *cur = htud->body.first; | |
2973 | if (htud->body.nchunks == 0) { | |
2974 | SCLogDebug("No body data in t1 (it should be removed only when the tx is destroyed): "); | |
2975 | goto end; | |
2976 | } | |
2977 | ||
2978 | if (memcmp(cur->data, "Body one!!", strlen("Body one!!")) != 0) { | |
2979 | SCLogDebug("Body data in t1 is not correctly set: "); | |
2980 | goto end; | |
2981 | } | |
2982 | ||
2983 | htud = (SCHtpTxUserData *) htp_tx_get_user_data(t2); | |
2984 | ||
2985 | cur = htud->body.first; | |
2986 | if (htud->body.nchunks == 0) { | |
2987 | SCLogDebug("No body data in t1 (it should be removed only when the tx is destroyed): "); | |
2988 | goto end; | |
2989 | } | |
2990 | ||
2991 | if (memcmp(cur->data, "Body two!!", strlen("Body two!!")) != 0) { | |
2992 | SCLogDebug("Body data in t1 is not correctly set: "); | |
2993 | goto end; | |
2994 | } | |
2995 | ||
2996 | FlowL7DataPtrFree(&f); | |
2997 | ||
2998 | result = 1; | |
2999 | end: | |
3000 | ||
3001 | StreamTcpFreeConfig(TRUE); | |
3002 | FLOW_DESTROY(&f); | |
3003 | UTHFreePacket(p); | |
3004 | return result; | |
3005 | } | |
3006 | ||
3007 | /** \test test pcre P modifier with multiple pipelined http transactions */ | |
3008 | static int DetectPcreTxBodyChunksTest02(void) { | |
3009 | int result = 0; | |
3010 | Signature *s = NULL; | |
3011 | DetectEngineThreadCtx *det_ctx = NULL; | |
3012 | ThreadVars th_v; | |
3013 | Flow f; | |
3014 | TcpSession ssn; | |
3015 | Packet *p = NULL; | |
3016 | uint8_t httpbuf1[] = "POST / HTTP/1.1\r\n"; | |
3017 | uint8_t httpbuf2[] = "User-Agent: Mozilla/1.0\r\nContent-Length: 10\r\n"; | |
3018 | uint8_t httpbuf3[] = "Cookie: dummy\r\n\r\n"; | |
3019 | uint8_t httpbuf4[] = "Body one!!"; | |
3020 | uint32_t httplen1 = sizeof(httpbuf1) - 1; /* minus the \0 */ | |
3021 | uint32_t httplen2 = sizeof(httpbuf2) - 1; /* minus the \0 */ | |
3022 | uint32_t httplen3 = sizeof(httpbuf3) - 1; /* minus the \0 */ | |
3023 | uint32_t httplen4 = sizeof(httpbuf4) - 1; /* minus the \0 */ | |
3024 | uint8_t httpbuf5[] = "GET /?var=val HTTP/1.1\r\n"; | |
3025 | uint8_t httpbuf6[] = "User-Agent: Firefox/1.0\r\n"; | |
3026 | uint8_t httpbuf7[] = "Cookie: dummy2\r\nContent-Length: 10\r\n\r\nBody two!!"; | |
3027 | uint32_t httplen5 = sizeof(httpbuf5) - 1; /* minus the \0 */ | |
3028 | uint32_t httplen6 = sizeof(httpbuf6) - 1; /* minus the \0 */ | |
3029 | uint32_t httplen7 = sizeof(httpbuf7) - 1; /* minus the \0 */ | |
3030 | ||
3031 | memset(&th_v, 0, sizeof(th_v)); | |
3032 | memset(&f, 0, sizeof(f)); | |
3033 | memset(&ssn, 0, sizeof(ssn)); | |
3034 | ||
3035 | p = UTHBuildPacket(NULL, 0, IPPROTO_TCP); | |
3036 | ||
3037 | FLOW_INITIALIZE(&f); | |
3038 | f.protoctx = (void *)&ssn; | |
3039 | f.proto = IPPROTO_TCP; | |
3040 | f.src.family = AF_INET; | |
3041 | f.dst.family = AF_INET; | |
3042 | ||
3043 | p->flow = &f; | |
3044 | p->flowflags |= FLOW_PKT_TOSERVER; | |
3045 | p->flowflags |= FLOW_PKT_ESTABLISHED; | |
3046 | f.alproto = ALPROTO_HTTP; | |
3047 | ||
3048 | StreamTcpInitConfig(TRUE); | |
3049 | FlowL7DataPtrInit(&f); | |
3050 | ||
3051 | DetectEngineCtx *de_ctx = DetectEngineCtxInit(); | |
3052 | if (de_ctx == NULL) { | |
3053 | goto end; | |
3054 | } | |
3055 | ||
3056 | de_ctx->flags |= DE_QUIET; | |
3057 | ||
3058 | s = DetectEngineAppendSig(de_ctx, "alert tcp any any -> any any (content:\"POST\"; http_method; content:\"Mozilla\"; http_header; content:\"dummy\"; http_cookie; pcre:\"/one/P\"; sid:1; rev:1;)"); | |
3059 | if (s == NULL) { | |
3060 | printf("sig parse failed: "); | |
3061 | goto end; | |
3062 | } | |
3063 | s = DetectEngineAppendSig(de_ctx, "alert tcp any any -> any any (content:\"GET\"; http_method; content:\"Firefox\"; http_header; content:\"dummy2\"; http_cookie; pcre:\"/two/P\"; sid:2; rev:1;)"); | |
3064 | if (s == NULL) { | |
3065 | printf("sig2 parse failed: "); | |
3066 | goto end; | |
3067 | } | |
3068 | ||
3069 | SigGroupBuild(de_ctx); | |
3070 | DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx); | |
3071 | ||
3072 | int r = AppLayerParse(&f, ALPROTO_HTTP, STREAM_TOSERVER, httpbuf1, httplen1); | |
3073 | if (r != 0) { | |
3074 | printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r); | |
3075 | goto end; | |
3076 | } | |
3077 | ||
3078 | /* do detect */ | |
3079 | SigMatchSignatures(&th_v, de_ctx, det_ctx, p); | |
3080 | if (PacketAlertCheck(p, 1)) { | |
3081 | printf("sig 1 alerted: "); | |
3082 | goto end; | |
3083 | } | |
3084 | p->alerts.cnt = 0; | |
3085 | ||
3086 | r = AppLayerParse(&f, ALPROTO_HTTP, STREAM_TOSERVER, httpbuf2, httplen2); | |
3087 | if (r != 0) { | |
3088 | printf("toserver chunk 2 returned %" PRId32 ", expected 0: ", r); | |
3089 | goto end; | |
3090 | } | |
3091 | ||
3092 | /* do detect */ | |
3093 | SigMatchSignatures(&th_v, de_ctx, det_ctx, p); | |
3094 | if (PacketAlertCheck(p, 1)) { | |
3095 | printf("sig 1 alerted (2): "); | |
3096 | goto end; | |
3097 | } | |
3098 | p->alerts.cnt = 0; | |
3099 | ||
3100 | r = AppLayerParse(&f, ALPROTO_HTTP, STREAM_TOSERVER, httpbuf3, httplen3); | |
3101 | if (r != 0) { | |
3102 | printf("toserver chunk 3 returned %" PRId32 ", expected 0: ", r); | |
3103 | goto end; | |
3104 | } | |
3105 | ||
3106 | /* do detect */ | |
3107 | SigMatchSignatures(&th_v, de_ctx, det_ctx, p); | |
3108 | if (PacketAlertCheck(p, 1)) { | |
3109 | printf("signature matched, but shouldn't have: "); | |
3110 | goto end; | |
3111 | } | |
3112 | p->alerts.cnt = 0; | |
3113 | ||
3114 | r = AppLayerParse(&f, ALPROTO_HTTP, STREAM_TOSERVER, httpbuf4, httplen4); | |
3115 | if (r != 0) { | |
3116 | printf("toserver chunk 4 returned %" PRId32 ", expected 0: ", r); | |
3117 | result = 0; | |
3118 | goto end; | |
3119 | } | |
3120 | ||
3121 | /* do detect */ | |
3122 | SigMatchSignatures(&th_v, de_ctx, det_ctx, p); | |
3123 | if (!(PacketAlertCheck(p, 1))) { | |
3124 | printf("sig 1 didn't alert: "); | |
3125 | goto end; | |
3126 | } | |
3127 | p->alerts.cnt = 0; | |
3128 | ||
3129 | r = AppLayerParse(&f, ALPROTO_HTTP, STREAM_TOSERVER, httpbuf5, httplen5); | |
3130 | if (r != 0) { | |
3131 | printf("toserver chunk 5 returned %" PRId32 ", expected 0: ", r); | |
3132 | goto end; | |
3133 | } | |
3134 | ||
3135 | /* do detect */ | |
3136 | SigMatchSignatures(&th_v, de_ctx, det_ctx, p); | |
3137 | if (PacketAlertCheck(p, 1)) { | |
3138 | printf("sig 1 alerted (5): "); | |
3139 | goto end; | |
3140 | } | |
3141 | p->alerts.cnt = 0; | |
3142 | ||
3143 | r = AppLayerParse(&f, ALPROTO_HTTP, STREAM_TOSERVER, httpbuf6, httplen6); | |
3144 | if (r != 0) { | |
3145 | printf("toserver chunk 6 returned %" PRId32 ", expected 0: ", r); | |
3146 | goto end; | |
3147 | } | |
3148 | ||
3149 | /* do detect */ | |
3150 | SigMatchSignatures(&th_v, de_ctx, det_ctx, p); | |
3151 | if ((PacketAlertCheck(p, 1)) || (PacketAlertCheck(p, 2))) { | |
3152 | printf("sig 1 alerted (request 2, chunk 6): "); | |
3153 | goto end; | |
3154 | } | |
3155 | p->alerts.cnt = 0; | |
3156 | ||
3157 | SCLogDebug("sending data chunk 7"); | |
3158 | ||
3159 | r = AppLayerParse(&f, ALPROTO_HTTP, STREAM_TOSERVER, httpbuf7, httplen7); | |
3160 | if (r != 0) { | |
3161 | printf("toserver chunk 7 returned %" PRId32 ", expected 0: ", r); | |
3162 | goto end; | |
3163 | } | |
3164 | ||
3165 | /* do detect */ | |
3166 | SigMatchSignatures(&th_v, de_ctx, det_ctx, p); | |
3167 | if (!(PacketAlertCheck(p, 2))) { | |
3168 | printf("signature 2 didn't match, but should have: "); | |
3169 | goto end; | |
3170 | } | |
3171 | p->alerts.cnt = 0; | |
3172 | ||
3173 | HtpState *htp_state = f.aldata[AlpGetStateIdx(ALPROTO_HTTP)]; | |
3174 | if (htp_state == NULL) { | |
3175 | printf("no http state: "); | |
3176 | result = 0; | |
3177 | goto end; | |
3178 | } | |
3179 | ||
3180 | /* hardcoded check of the transactions and it's client body chunks */ | |
3181 | if (list_size(htp_state->connp->conn->transactions) != 2) { | |
3182 | printf("The http app layer doesn't have 2 transactions, but it should: "); | |
3183 | goto end; | |
3184 | } | |
3185 | ||
3186 | htp_tx_t *t1 = list_get(htp_state->connp->conn->transactions, 0); | |
3187 | htp_tx_t *t2 = list_get(htp_state->connp->conn->transactions, 1); | |
3188 | ||
3189 | SCHtpTxUserData *htud = (SCHtpTxUserData *) htp_tx_get_user_data(t1); | |
3190 | ||
3191 | HtpBodyChunk *cur = htud->body.first; | |
3192 | if (htud->body.nchunks == 0) { | |
3193 | SCLogDebug("No body data in t1 (it should be removed only when the tx is destroyed): "); | |
3194 | goto end; | |
3195 | } | |
3196 | ||
3197 | if (memcmp(cur->data, "Body one!!", strlen("Body one!!")) != 0) { | |
3198 | SCLogDebug("Body data in t1 is not correctly set: "); | |
3199 | goto end; | |
3200 | } | |
3201 | ||
3202 | htud = (SCHtpTxUserData *) htp_tx_get_user_data(t2); | |
3203 | ||
3204 | cur = htud->body.first; | |
3205 | if (htud->body.nchunks == 0) { | |
3206 | SCLogDebug("No body data in t1 (it should be removed only when the tx is destroyed): "); | |
3207 | goto end; | |
3208 | } | |
3209 | ||
3210 | if (memcmp(cur->data, "Body two!!", strlen("Body two!!")) != 0) { | |
3211 | SCLogDebug("Body data in t1 is not correctly set: "); | |
3212 | goto end; | |
3213 | } | |
3214 | ||
3215 | result = 1; | |
3216 | end: | |
3217 | if (det_ctx != NULL) { | |
3218 | DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx); | |
3219 | } | |
3220 | if (de_ctx != NULL) { | |
3221 | SigGroupCleanup(de_ctx); | |
3222 | DetectEngineCtxFree(de_ctx); | |
3223 | } | |
3224 | ||
3225 | FlowL7DataPtrFree(&f); | |
3226 | StreamTcpFreeConfig(TRUE); | |
3227 | FLOW_DESTROY(&f); | |
3228 | UTHFreePacket(p); | |
3229 | return result; | |
3230 | } | |
3231 | ||
3232 | /** \test multiple http transactions and body chunks of request handling */ | |
3233 | static int DetectPcreTxBodyChunksTest03(void) { | |
3234 | int result = 0; | |
3235 | Signature *s = NULL; | |
3236 | DetectEngineThreadCtx *det_ctx = NULL; | |
3237 | ThreadVars th_v; | |
3238 | Flow f; | |
3239 | TcpSession ssn; | |
3240 | Packet *p = NULL; | |
3241 | uint8_t httpbuf1[] = "POST / HTTP/1.1\r\n"; | |
3242 | uint8_t httpbuf2[] = "User-Agent: Mozilla/1.0\r\nContent-Length: 10\r\n"; | |
3243 | uint8_t httpbuf3[] = "Cookie: dummy\r\n\r\n"; | |
3244 | uint8_t httpbuf4[] = "Body one!!"; | |
3245 | uint32_t httplen1 = sizeof(httpbuf1) - 1; /* minus the \0 */ | |
3246 | uint32_t httplen2 = sizeof(httpbuf2) - 1; /* minus the \0 */ | |
3247 | uint32_t httplen3 = sizeof(httpbuf3) - 1; /* minus the \0 */ | |
3248 | uint32_t httplen4 = sizeof(httpbuf4) - 1; /* minus the \0 */ | |
3249 | uint8_t httpbuf5[] = "GET /?var=val HTTP/1.1\r\n"; | |
3250 | uint8_t httpbuf6[] = "User-Agent: Firefox/1.0\r\n"; | |
3251 | uint8_t httpbuf7[] = "Cookie: dummy2\r\nContent-Length: 10\r\n\r\nBody two!!"; | |
3252 | uint32_t httplen5 = sizeof(httpbuf5) - 1; /* minus the \0 */ | |
3253 | uint32_t httplen6 = sizeof(httpbuf6) - 1; /* minus the \0 */ | |
3254 | uint32_t httplen7 = sizeof(httpbuf7) - 1; /* minus the \0 */ | |
3255 | ||
3256 | memset(&th_v, 0, sizeof(th_v)); | |
3257 | memset(&f, 0, sizeof(f)); | |
3258 | memset(&ssn, 0, sizeof(ssn)); | |
3259 | ||
3260 | p = UTHBuildPacket(NULL, 0, IPPROTO_TCP); | |
3261 | ||
3262 | FLOW_INITIALIZE(&f); | |
3263 | f.protoctx = (void *)&ssn; | |
3264 | f.proto = IPPROTO_TCP; | |
3265 | f.src.family = AF_INET; | |
3266 | f.dst.family = AF_INET; | |
3267 | ||
3268 | p->flow = &f; | |
3269 | p->flowflags |= FLOW_PKT_TOSERVER; | |
3270 | p->flowflags |= FLOW_PKT_ESTABLISHED; | |
3271 | f.alproto = ALPROTO_HTTP; | |
3272 | ||
3273 | StreamTcpInitConfig(TRUE); | |
3274 | FlowL7DataPtrInit(&f); | |
3275 | ||
3276 | DetectEngineCtx *de_ctx = DetectEngineCtxInit(); | |
3277 | if (de_ctx == NULL) { | |
3278 | goto end; | |
3279 | } | |
3280 | ||
3281 | de_ctx->flags |= DE_QUIET; | |
3282 | ||
3283 | s = DetectEngineAppendSig(de_ctx, "alert tcp any any -> any any (content:\"POST\"; http_method; content:\"Mozilla\"; http_header; content:\"dummy\"; http_cookie; pcre:\"/one/P\"; sid:1; rev:1;)"); | |
3284 | if (s == NULL) { | |
3285 | printf("sig parse failed: "); | |
3286 | goto end; | |
3287 | } | |
3288 | s = DetectEngineAppendSig(de_ctx, "alert tcp any any -> any any (content:\"GET\"; http_method; content:\"Firefox\"; http_header; content:\"dummy2\"; http_cookie; pcre:\"/two/P\"; sid:2; rev:1;)"); | |
3289 | if (s == NULL) { | |
3290 | printf("sig2 parse failed: "); | |
3291 | goto end; | |
3292 | } | |
3293 | ||
3294 | SigGroupBuild(de_ctx); | |
3295 | DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx); | |
3296 | ||
3297 | int r = AppLayerParse(&f, ALPROTO_HTTP, STREAM_TOSERVER, httpbuf1, httplen1); | |
3298 | if (r != 0) { | |
3299 | printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r); | |
3300 | goto end; | |
3301 | } | |
3302 | ||
3303 | /* do detect */ | |
3304 | SigMatchSignatures(&th_v, de_ctx, det_ctx, p); | |
3305 | if (PacketAlertCheck(p, 1)) { | |
3306 | printf("sig 1 alerted: "); | |
3307 | goto end; | |
3308 | } | |
3309 | p->alerts.cnt = 0; | |
3310 | ||
3311 | r = AppLayerParse(&f, ALPROTO_HTTP, STREAM_TOSERVER, httpbuf2, httplen2); | |
3312 | if (r != 0) { | |
3313 | printf("toserver chunk 2 returned %" PRId32 ", expected 0: ", r); | |
3314 | goto end; | |
3315 | } | |
3316 | ||
3317 | /* do detect */ | |
3318 | SigMatchSignatures(&th_v, de_ctx, det_ctx, p); | |
3319 | if (PacketAlertCheck(p, 1)) { | |
3320 | printf("sig 1 alerted (2): "); | |
3321 | goto end; | |
3322 | } | |
3323 | p->alerts.cnt = 0; | |
3324 | ||
3325 | r = AppLayerParse(&f, ALPROTO_HTTP, STREAM_TOSERVER, httpbuf3, httplen3); | |
3326 | if (r != 0) { | |
3327 | printf("toserver chunk 3 returned %" PRId32 ", expected 0: ", r); | |
3328 | goto end; | |
3329 | } | |
3330 | ||
3331 | /* do detect */ | |
3332 | SigMatchSignatures(&th_v, de_ctx, det_ctx, p); | |
3333 | if (PacketAlertCheck(p, 1)) { | |
3334 | printf("signature matched, but shouldn't have: "); | |
3335 | goto end; | |
3336 | } | |
3337 | p->alerts.cnt = 0; | |
3338 | ||
3339 | r = AppLayerParse(&f, ALPROTO_HTTP, STREAM_TOSERVER, httpbuf4, httplen4); | |
3340 | if (r != 0) { | |
3341 | printf("toserver chunk 4 returned %" PRId32 ", expected 0: ", r); | |
3342 | result = 0; | |
3343 | goto end; | |
3344 | } | |
3345 | ||
3346 | /* do detect */ | |
3347 | SigMatchSignatures(&th_v, de_ctx, det_ctx, p); | |
3348 | if (!(PacketAlertCheck(p, 1))) { | |
3349 | printf("sig 1 didn't alert: "); | |
3350 | goto end; | |
3351 | } | |
3352 | p->alerts.cnt = 0; | |
3353 | ||
3354 | r = AppLayerParse(&f, ALPROTO_HTTP, STREAM_TOSERVER, httpbuf5, httplen5); | |
3355 | if (r != 0) { | |
3356 | printf("toserver chunk 5 returned %" PRId32 ", expected 0: ", r); | |
3357 | goto end; | |
3358 | } | |
3359 | ||
3360 | /* do detect */ | |
3361 | SigMatchSignatures(&th_v, de_ctx, det_ctx, p); | |
3362 | if (PacketAlertCheck(p, 1)) { | |
3363 | printf("sig 1 alerted (5): "); | |
3364 | goto end; | |
3365 | } | |
3366 | p->alerts.cnt = 0; | |
3367 | ||
3368 | r = AppLayerParse(&f, ALPROTO_HTTP, STREAM_TOSERVER, httpbuf6, httplen6); | |
3369 | if (r != 0) { | |
3370 | printf("toserver chunk 6 returned %" PRId32 ", expected 0: ", r); | |
3371 | goto end; | |
3372 | } | |
3373 | ||
3374 | /* do detect */ | |
3375 | SigMatchSignatures(&th_v, de_ctx, det_ctx, p); | |
3376 | if ((PacketAlertCheck(p, 1)) || (PacketAlertCheck(p, 2))) { | |
3377 | printf("sig 1 alerted (request 2, chunk 6): "); | |
3378 | goto end; | |
3379 | } | |
3380 | p->alerts.cnt = 0; | |
3381 | ||
3382 | SCLogDebug("sending data chunk 7"); | |
3383 | ||
3384 | r = AppLayerParse(&f, ALPROTO_HTTP, STREAM_TOSERVER, httpbuf7, httplen7); | |
3385 | if (r != 0) { | |
3386 | printf("toserver chunk 7 returned %" PRId32 ", expected 0: ", r); | |
3387 | goto end; | |
3388 | } | |
3389 | ||
3390 | /* do detect */ | |
3391 | SigMatchSignatures(&th_v, de_ctx, det_ctx, p); | |
3392 | if (!(PacketAlertCheck(p, 2))) { | |
3393 | printf("signature 2 didn't match, but should have: "); | |
3394 | goto end; | |
3395 | } | |
3396 | p->alerts.cnt = 0; | |
3397 | ||
3398 | HtpState *htp_state = f.aldata[AlpGetStateIdx(ALPROTO_HTTP)]; | |
3399 | if (htp_state == NULL) { | |
3400 | printf("no http state: "); | |
3401 | result = 0; | |
3402 | goto end; | |
3403 | } | |
3404 | ||
3405 | if (list_size(htp_state->connp->conn->transactions) != 2) { | |
3406 | printf("The http app layer doesn't have 2 transactions, but it should: "); | |
3407 | goto end; | |
3408 | } | |
3409 | ||
3410 | result = 1; | |
3411 | end: | |
3412 | if (det_ctx != NULL) { | |
3413 | DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx); | |
3414 | } | |
3415 | if (de_ctx != NULL) { | |
3416 | SigGroupCleanup(de_ctx); | |
3417 | DetectEngineCtxFree(de_ctx); | |
3418 | } | |
3419 | ||
3420 | FlowL7DataPtrFree(&f); | |
3421 | StreamTcpFreeConfig(TRUE); | |
3422 | FLOW_DESTROY(&f); | |
3423 | UTHFreePacket(p); | |
1833 | 3424 | return result; |
1834 | 3425 | } |
1835 | 3426 | |
1865 | 3456 | UtRegisterTest("DetectPcreTestSig06", DetectPcreTestSig06, 1); |
1866 | 3457 | UtRegisterTest("DetectPcreTestSig07 -- anchored pcre", DetectPcreTestSig07, 1); |
1867 | 3458 | UtRegisterTest("DetectPcreTestSig08 -- anchored pcre", DetectPcreTestSig08, 1); |
3459 | UtRegisterTest("DetectPcreTestSig09 -- Cookie modifier", DetectPcreTestSig09, 1); | |
3460 | UtRegisterTest("DetectPcreTestSig10 -- negated Cookie modifier", DetectPcreTestSig10, 1); | |
3461 | UtRegisterTest("DetectPcreTestSig11 -- Method modifier", DetectPcreTestSig11, 1); | |
3462 | UtRegisterTest("DetectPcreTestSig12 -- negated Method modifier", DetectPcreTestSig12, 1); | |
3463 | UtRegisterTest("DetectPcreTestSig13 -- Header modifier", DetectPcreTestSig13, 1); | |
3464 | UtRegisterTest("DetectPcreTestSig14 -- negated Header modifier", DetectPcreTestSig14, 1); | |
3465 | UtRegisterTest("DetectPcreTxBodyChunksTest01", DetectPcreTxBodyChunksTest01, 1); | |
3466 | UtRegisterTest("DetectPcreTxBodyChunksTest02 -- modifier P, body chunks per tx", DetectPcreTxBodyChunksTest02, 1); | |
3467 | UtRegisterTest("DetectPcreTxBodyChunksTest03 -- modifier P, body chunks per tx", DetectPcreTxBodyChunksTest03, 1); | |
1868 | 3468 | #endif /* UNITTESTS */ |
1869 | 3469 | } |
1870 | 3470 |
23 | 23 | #ifndef __DETECT_PCRE_H__ |
24 | 24 | #define __DETECT_PCRE_H__ |
25 | 25 | |
26 | #define DETECT_PCRE_RELATIVE 0x01 | |
27 | #define DETECT_PCRE_RAWBYTES 0x02 | |
28 | #define DETECT_PCRE_URI 0x04 | |
26 | #define DETECT_PCRE_RELATIVE 0x0001 | |
27 | #define DETECT_PCRE_RAWBYTES 0x0002 | |
28 | #define DETECT_PCRE_URI 0x0004 | |
29 | 29 | |
30 | #define DETECT_PCRE_CAPTURE_PKT 0x08 | |
31 | #define DETECT_PCRE_CAPTURE_FLOW 0x10 | |
32 | #define DETECT_PCRE_MATCH_LIMIT 0x20 | |
30 | #define DETECT_PCRE_CAPTURE_PKT 0x0008 | |
31 | #define DETECT_PCRE_CAPTURE_FLOW 0x0010 | |
32 | #define DETECT_PCRE_MATCH_LIMIT 0x0020 | |
33 | 33 | |
34 | #define DETECT_PCRE_HTTP_BODY_AL 0x40 | |
35 | #define DETECT_PCRE_RELATIVE_NEXT 0x80 | |
34 | #define DETECT_PCRE_HTTP_BODY_AL 0x0040 | |
35 | #define DETECT_PCRE_RELATIVE_NEXT 0x0080 | |
36 | ||
37 | /* new modifiers 2.8.5.3 support */ | |
38 | #define DETECT_PCRE_HEADER 0x0100 | |
39 | #define DETECT_PCRE_COOKIE 0x0200 | |
40 | #define DETECT_PCRE_METHOD 0x0400 | |
36 | 41 | |
37 | 42 | typedef struct DetectPcreData_ { |
38 | 43 | /* pcre options */ |
40 | 45 | pcre_extra *sd; |
41 | 46 | int opts; |
42 | 47 | |
43 | uint8_t flags; | |
48 | uint16_t flags; | |
44 | 49 | uint8_t negate; |
45 | 50 | |
46 | 51 | char *capname; |
0 | /* Copyright (C) 2007-2010 Open Information Security Foundation | |
1 | * | |
2 | * You can copy, redistribute or modify this Program under the terms of | |
3 | * the GNU General Public License version 2 as published by the Free | |
4 | * Software Foundation. | |
5 | * | |
6 | * This program is distributed in the hope that it will be useful, | |
7 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
8 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
9 | * GNU General Public License for more details. | |
10 | * | |
11 | * You should have received a copy of the GNU General Public License | |
12 | * version 2 along with this program; if not, write to the Free Software | |
13 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA | |
14 | * 02110-1301, USA. | |
15 | */ | |
16 | ||
17 | /** | |
18 | * \file | |
19 | * | |
20 | * \author Pablo Rincon <pablo.rincon.crespo@gmail.com> | |
21 | * | |
22 | * Implements the ssh.protoversion keyword | |
23 | * You can specify a concrete version like ssh.protoversion: 1.66 | |
24 | * or search for protoversion 2 compat (1.99 is considered as 2) like | |
25 | * ssh.protoversion:2_compat | |
26 | * or just the beginning of the string like ssh.protoversion:"1." | |
27 | */ | |
28 | ||
29 | #include "suricata-common.h" | |
30 | #include "threads.h" | |
31 | #include "debug.h" | |
32 | #include "decode.h" | |
33 | ||
34 | #include "detect.h" | |
35 | #include "detect-parse.h" | |
36 | ||
37 | #include "detect-engine.h" | |
38 | #include "detect-engine-mpm.h" | |
39 | #include "detect-engine-state.h" | |
40 | ||
41 | #include "flow.h" | |
42 | #include "flow-var.h" | |
43 | #include "flow-util.h" | |
44 | ||
45 | #include "util-debug.h" | |
46 | #include "util-unittest.h" | |
47 | #include "util-unittest-helper.h" | |
48 | ||
49 | #include "app-layer.h" | |
50 | ||
51 | #include "app-layer-ssh.h" | |
52 | #include "detect-ssh-proto-version.h" | |
53 | ||
54 | #include "stream-tcp.h" | |
55 | ||
56 | /** | |
57 | * \brief Regex for parsing the protoversion string | |
58 | */ | |
59 | #define PARSE_REGEX "^\\s*\"?\\s*([0-9]+([\\.\\-0-9]+)?|2_compat)\\s*\"?\\s*$" | |
60 | ||
61 | static pcre *parse_regex; | |
62 | static pcre_extra *parse_regex_study; | |
63 | ||
64 | int DetectSshVersionMatch (ThreadVars *, DetectEngineThreadCtx *, Flow *, uint8_t, void *, Signature *, SigMatch *); | |
65 | static int DetectSshVersionSetup (DetectEngineCtx *, Signature *, char *); | |
66 | void DetectSshVersionRegisterTests(void); | |
67 | void DetectSshVersionFree(void *); | |
68 | ||
69 | /** | |
70 | * \brief Registration function for keyword: ssh.protoversion | |
71 | */ | |
72 | void DetectSshVersionRegister(void) { | |
73 | sigmatch_table[DETECT_AL_SSH_PROTOVERSION].name = "ssh.protoversion"; | |
74 | sigmatch_table[DETECT_AL_SSH_PROTOVERSION].Match = NULL; | |
75 | sigmatch_table[DETECT_AL_SSH_PROTOVERSION].AppLayerMatch = DetectSshVersionMatch; | |
76 | sigmatch_table[DETECT_AL_SSH_PROTOVERSION].alproto = ALPROTO_SSH; | |
77 | sigmatch_table[DETECT_AL_SSH_PROTOVERSION].Setup = DetectSshVersionSetup; | |
78 | sigmatch_table[DETECT_AL_SSH_PROTOVERSION].Free = DetectSshVersionFree; | |
79 | sigmatch_table[DETECT_AL_SSH_PROTOVERSION].RegisterTests = DetectSshVersionRegisterTests; | |
80 | ||
81 | const char *eb; | |
82 | int eo; | |
83 | int opts = 0; | |
84 | ||
85 | SCLogDebug("registering ssh.protoversion rule option"); | |
86 | ||
87 | parse_regex = pcre_compile(PARSE_REGEX, opts, &eb, &eo, NULL); | |
88 | if (parse_regex == NULL) { | |
89 | SCLogError(SC_ERR_PCRE_COMPILE, "Compile of \"%s\" failed at offset %" PRId32 ": %s", | |
90 | PARSE_REGEX, eo, eb); | |
91 | goto error; | |
92 | } | |
93 | ||
94 | parse_regex_study = pcre_study(parse_regex, 0, &eb); | |
95 | if (eb != NULL) { | |
96 | SCLogError(SC_ERR_PCRE_STUDY, "pcre study failed: %s", eb); | |
97 | goto error; | |
98 | } | |
99 | return; | |
100 | ||
101 | error: | |
102 | return; | |
103 | } | |
104 | ||
105 | /** | |
106 | * \brief match the specified version on a ssh session | |
107 | * | |
108 | * \param t pointer to thread vars | |
109 | * \param det_ctx pointer to the pattern matcher thread | |
110 | * \param p pointer to the current packet | |
111 | * \param m pointer to the sigmatch that we will cast into DetectSshVersionData | |
112 | * | |
113 | * \retval 0 no match | |
114 | * \retval 1 match | |
115 | */ | |
116 | int DetectSshVersionMatch (ThreadVars *t, DetectEngineThreadCtx *det_ctx, Flow *f, uint8_t flags, void *state, Signature *s, SigMatch *m) | |
117 | { | |
118 | SCEnter(); | |
119 | ||
120 | DetectSshVersionData *ssh = (DetectSshVersionData *)m->ctx; | |
121 | SshState *ssh_state = (SshState *)state; | |
122 | if (ssh_state == NULL) { | |
123 | SCLogDebug("no ssh state, no match"); | |
124 | SCReturnInt(0); | |
125 | } | |
126 | ||
127 | int ret = 0; | |
128 | SCMutexLock(&f->m); | |
129 | if (flags & STREAM_TOCLIENT && ssh_state->flags & SSH_FLAG_SERVER_VERSION_PARSED) { | |
130 | if (ssh->flags & SSH_FLAG_PROTOVERSION_2_COMPAT) { | |
131 | SCLogDebug("looking for ssh server protoversion 2 compat"); | |
132 | if (strncmp((char *) ssh_state->server_proto_version, "2", 1) == 0 || | |
133 | strncmp((char *) ssh_state->server_proto_version, "2.", 2) == 0 || | |
134 | strncmp((char *) ssh_state->server_proto_version, "1.99", 4) == 0) | |
135 | ret = 1; | |
136 | } else { | |
137 | SCLogDebug("looking for ssh server protoversion %s length %"PRIu16"", ssh->ver, ssh->len); | |
138 | ret = (strncmp((char *) ssh_state->server_proto_version, (char *) ssh->ver, ssh->len) == 0)? 1 : 0; | |
139 | } | |
140 | } else if (flags & STREAM_TOSERVER && ssh_state->flags & SSH_FLAG_CLIENT_VERSION_PARSED) { | |
141 | if (ssh->flags & SSH_FLAG_PROTOVERSION_2_COMPAT) { | |
142 | SCLogDebug("looking for client ssh client protoversion 2 compat"); | |
143 | if (strncmp((char *) ssh_state->client_proto_version, "2", 1) == 0 || | |
144 | strncmp((char *) ssh_state->client_proto_version, "2.", 2) == 0 || | |
145 | strncmp((char *) ssh_state->client_proto_version, "1.99", 4) == 0) | |
146 | ret = 1; | |
147 | } else { | |
148 | SCLogDebug("looking for ssh client protoversion %s length %"PRIu16"", ssh->ver, ssh->len); | |
149 | ret = (strncmp((char *) ssh_state->client_proto_version, (char *) ssh->ver, ssh->len) == 0)? 1 : 0; | |
150 | } | |
151 | } | |
152 | SCMutexUnlock(&f->m); | |
153 | SCReturnInt(ret); | |
154 | } | |
155 | ||
156 | /** | |
157 | * \brief This function is used to parse IPV4 ip_id passed via keyword: "id" | |
158 | * | |
159 | * \param idstr Pointer to the user provided id option | |
160 | * | |
161 | * \retval id_d pointer to DetectSshVersionData on success | |
162 | * \retval NULL on failure | |
163 | */ | |
164 | DetectSshVersionData *DetectSshVersionParse (char *str) | |
165 | { | |
166 | DetectSshVersionData *ssh = NULL; | |
167 | #define MAX_SUBSTRINGS 30 | |
168 | int ret = 0, res = 0; | |
169 | int ov[MAX_SUBSTRINGS]; | |
170 | ||
171 | ret = pcre_exec(parse_regex, parse_regex_study, str, strlen(str), 0, 0, | |
172 | ov, MAX_SUBSTRINGS); | |
173 | ||
174 | if (ret < 1 || ret > 3) { | |
175 | SCLogError(SC_ERR_PCRE_MATCH, "invalid ssh.protoversion option"); | |
176 | goto error; | |
177 | } | |
178 | ||
179 | if (ret > 1) { | |
180 | const char *str_ptr; | |
181 | res = pcre_get_substring((char *)str, ov, MAX_SUBSTRINGS, 1, &str_ptr); | |
182 | if (res < 0) { | |
183 | SCLogError(SC_ERR_PCRE_GET_SUBSTRING, "pcre_get_substring failed"); | |
184 | goto error; | |
185 | } | |
186 | ||
187 | /* We have a correct id option */ | |
188 | ssh = SCMalloc(sizeof(DetectSshVersionData)); | |
189 | if (ssh == NULL) | |
190 | goto error; | |
191 | ||
192 | memset(ssh, 0x00, sizeof(DetectSshVersionData)); | |
193 | ||
194 | /* If we expect a protocol version 2 or 1.99 (considered 2, we | |
195 | * will compare it with both strings) */ | |
196 | if (strcmp("2_compat", str_ptr) == 0) { | |
197 | ssh->flags |= SSH_FLAG_PROTOVERSION_2_COMPAT; | |
198 | SCLogDebug("will look for ssh protocol version 2 (2, 2.0, 1.99 that's considered as 2"); | |
199 | return ssh; | |
200 | } | |
201 | ||
202 | ssh->ver = (uint8_t *)SCStrdup((char*)str_ptr); | |
203 | if (ssh->ver == NULL) { | |
204 | goto error; | |
205 | } | |
206 | ssh->len = strlen((char *) ssh->ver); | |
207 | ||
208 | SCLogDebug("will look for ssh %s", ssh->ver); | |
209 | } | |
210 | ||
211 | return ssh; | |
212 | ||
213 | error: | |
214 | if (ssh != NULL) | |
215 | DetectSshVersionFree(ssh); | |
216 | return NULL; | |
217 | ||
218 | } | |
219 | ||
220 | /** | |
221 | * \brief this function is used to add the parsed "id" option | |
222 | * \brief into the current signature | |
223 | * | |
224 | * \param de_ctx pointer to the Detection Engine Context | |
225 | * \param s pointer to the Current Signature | |
226 | * \param idstr pointer to the user provided "id" option | |
227 | * | |
228 | * \retval 0 on Success | |
229 | * \retval -1 on Failure | |
230 | */ | |
231 | static int DetectSshVersionSetup (DetectEngineCtx *de_ctx, Signature *s, char *str) | |
232 | { | |
233 | DetectSshVersionData *ssh = NULL; | |
234 | SigMatch *sm = NULL; | |
235 | ||
236 | ssh = DetectSshVersionParse(str); | |
237 | if (ssh == NULL) | |
238 | goto error; | |
239 | ||
240 | /* Okay so far so good, lets get this into a SigMatch | |
241 | * and put it in the Signature. */ | |
242 | sm = SigMatchAlloc(); | |
243 | if (sm == NULL) | |
244 | goto error; | |
245 | ||
246 | sm->type = DETECT_AL_SSH_PROTOVERSION; | |
247 | sm->ctx = (void *)ssh; | |
248 | ||
249 | SigMatchAppendAppLayer(s, sm); | |
250 | ||
251 | if (s->alproto != ALPROTO_UNKNOWN && s->alproto != ALPROTO_SSH) { | |
252 | SCLogError(SC_ERR_CONFLICTING_RULE_KEYWORDS, "rule contains conflicting keywords."); | |
253 | goto error; | |
254 | } | |
255 | ||
256 | s->alproto = ALPROTO_SSH; | |
257 | return 0; | |
258 | ||
259 | error: | |
260 | if (ssh != NULL) | |
261 | DetectSshVersionFree(ssh); | |
262 | if (sm != NULL) | |
263 | SCFree(sm); | |
264 | return -1; | |
265 | ||
266 | } | |
267 | ||
268 | /** | |
269 | * \brief this function will free memory associated with DetectSshVersionData | |
270 | * | |
271 | * \param id_d pointer to DetectSshVersionData | |
272 | */ | |
273 | void DetectSshVersionFree(void *ptr) { | |
274 | DetectSshVersionData *id_d = (DetectSshVersionData *)ptr; | |
275 | SCFree(id_d); | |
276 | } | |
277 | ||
278 | #ifdef UNITTESTS /* UNITTESTS */ | |
279 | ||
280 | /** | |
281 | * \test DetectSshVersionTestParse01 is a test to make sure that we parse | |
282 | * a proto version correctly | |
283 | */ | |
284 | int DetectSshVersionTestParse01 (void) { | |
285 | DetectSshVersionData *ssh = NULL; | |
286 | ssh = DetectSshVersionParse("1.0"); | |
287 | if (ssh != NULL && strncmp((char *) ssh->ver, "1.0", 3) == 0) { | |
288 | DetectSshVersionFree(ssh); | |
289 | return 1; | |
290 | } | |
291 | ||
292 | return 0; | |
293 | } | |
294 | ||
295 | /** | |
296 | * \test DetectSshVersionTestParse02 is a test to make sure that we parse | |
297 | * the proto version (compatible with proto version 2) correctly | |
298 | */ | |
299 | int DetectSshVersionTestParse02 (void) { | |
300 | DetectSshVersionData *ssh = NULL; | |
301 | ssh = DetectSshVersionParse("2_compat"); | |
302 | if (ssh->flags & SSH_FLAG_PROTOVERSION_2_COMPAT) { | |
303 | DetectSshVersionFree(ssh); | |
304 | return 1; | |
305 | } | |
306 | ||
307 | return 0; | |
308 | } | |
309 | ||
310 | /** | |
311 | * \test DetectSshVersionTestParse03 is a test to make sure that we | |
312 | * don't return a ssh_data with an invalid value specified | |
313 | */ | |
314 | int DetectSshVersionTestParse03 (void) { | |
315 | DetectSshVersionData *ssh = NULL; | |
316 | ssh = DetectSshVersionParse("2_com"); | |
317 | if (ssh != NULL) { | |
318 | DetectSshVersionFree(ssh); | |
319 | return 0; | |
320 | } | |
321 | ssh = DetectSshVersionParse(""); | |
322 | if (ssh != NULL) { | |
323 | DetectSshVersionFree(ssh); | |
324 | return 0; | |
325 | } | |
326 | ssh = DetectSshVersionParse(".1"); | |
327 | if (ssh != NULL) { | |
328 | DetectSshVersionFree(ssh); | |
329 | return 0; | |
330 | } | |
331 | ssh = DetectSshVersionParse("lalala"); | |
332 | if (ssh != NULL) { | |
333 | DetectSshVersionFree(ssh); | |
334 | return 0; | |
335 | } | |
336 | ||
337 | return 1; | |
338 | } | |
339 | ||
340 | ||
341 | #include "stream-tcp-reassemble.h" | |
342 | ||
343 | /** \test Send a get request in three chunks + more data. */ | |
344 | static int DetectSshVersionTestDetect01(void) { | |
345 | int result = 0; | |
346 | Flow f; | |
347 | uint8_t sshbuf1[] = "SSH-1."; | |
348 | uint32_t sshlen1 = sizeof(sshbuf1) - 1; | |
349 | uint8_t sshbuf2[] = "10-PuTTY_2.123" ; | |
350 | uint32_t sshlen2 = sizeof(sshbuf2) - 1; | |
351 | uint8_t sshbuf3[] = "\n"; | |
352 | uint32_t sshlen3 = sizeof(sshbuf3) - 1; | |
353 | uint8_t sshbuf4[] = "whatever..."; | |
354 | uint32_t sshlen4 = sizeof(sshbuf4) - 1; | |
355 | TcpSession ssn; | |
356 | Packet *p = NULL; | |
357 | Signature *s = NULL; | |
358 | ThreadVars th_v; | |
359 | DetectEngineThreadCtx *det_ctx = NULL; | |
360 | ||
361 | memset(&th_v, 0, sizeof(th_v)); | |
362 | memset(&f, 0, sizeof(f)); | |
363 | memset(&ssn, 0, sizeof(ssn)); | |
364 | ||
365 | p = UTHBuildPacket(NULL, 0, IPPROTO_TCP); | |
366 | ||
367 | FLOW_INITIALIZE(&f); | |
368 | f.protoctx = (void *)&ssn; | |
369 | p->flow = &f; | |
370 | p->flowflags |= FLOW_PKT_TOSERVER; | |
371 | p->flowflags |= FLOW_PKT_ESTABLISHED; | |
372 | f.alproto = ALPROTO_SSH; | |
373 | ||
374 | StreamTcpInitConfig(TRUE); | |
375 | FlowL7DataPtrInit(&f); | |
376 | ||
377 | DetectEngineCtx *de_ctx = DetectEngineCtxInit(); | |
378 | if (de_ctx == NULL) { | |
379 | goto end; | |
380 | } | |
381 | ||
382 | de_ctx->flags |= DE_QUIET; | |
383 | ||
384 | s = de_ctx->sig_list = SigInit(de_ctx,"alert ssh any any -> any any (msg:\"SSH\"; ssh.protoversion:1.10; sid:1;)"); | |
385 | if (s == NULL) { | |
386 | goto end; | |
387 | } | |
388 | ||
389 | SigGroupBuild(de_ctx); | |
390 | DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx); | |
391 | ||
392 | int r = AppLayerParse(&f, ALPROTO_SSH, STREAM_TOSERVER, sshbuf1, sshlen1); | |
393 | if (r != 0) { | |
394 | printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r); | |
395 | goto end; | |
396 | } | |
397 | ||
398 | r = AppLayerParse(&f, ALPROTO_SSH, STREAM_TOSERVER, sshbuf2, sshlen2); | |
399 | if (r != 0) { | |
400 | printf("toserver chunk 2 returned %" PRId32 ", expected 0: ", r); | |
401 | goto end; | |
402 | } | |
403 | ||
404 | r = AppLayerParse(&f, ALPROTO_SSH, STREAM_TOSERVER, sshbuf3, sshlen3); | |
405 | if (r != 0) { | |
406 | printf("toserver chunk 3 returned %" PRId32 ", expected 0: ", r); | |
407 | goto end; | |
408 | } | |
409 | ||
410 | r = AppLayerParse(&f, ALPROTO_SSH, STREAM_TOSERVER, sshbuf4, sshlen4); | |
411 | if (r != 0) { | |
412 | printf("toserver chunk 4 returned %" PRId32 ", expected 0: ", r); | |
413 | goto end; | |
414 | } | |
415 | ||
416 | SshState *ssh_state = f.aldata[AlpGetStateIdx(ALPROTO_SSH)]; | |
417 | if (ssh_state == NULL) { | |
418 | printf("no ssh state: "); | |
419 | goto end; | |
420 | } | |
421 | ||
422 | /* do detect */ | |
423 | SigMatchSignatures(&th_v, de_ctx, det_ctx, p); | |
424 | ||
425 | if ( !(PacketAlertCheck(p, 1))) { | |
426 | printf("Error, the sig should match: "); | |
427 | goto end; | |
428 | } | |
429 | ||
430 | result = 1; | |
431 | end: | |
432 | SigGroupCleanup(de_ctx); | |
433 | SigCleanSignatures(de_ctx); | |
434 | ||
435 | DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx); | |
436 | DetectEngineCtxFree(de_ctx); | |
437 | ||
438 | FlowL7DataPtrFree(&f); | |
439 | StreamTcpFreeConfig(TRUE); | |
440 | FLOW_DESTROY(&f); | |
441 | ||
442 | UTHFreePackets(&p, 1); | |
443 | return result; | |
444 | } | |
445 | ||
446 | /** \test Send a get request in three chunks + more data. */ | |
447 | static int DetectSshVersionTestDetect02(void) { | |
448 | int result = 0; | |
449 | Flow f; | |
450 | uint8_t sshbuf1[] = "SSH-1."; | |
451 | uint32_t sshlen1 = sizeof(sshbuf1) - 1; | |
452 | uint8_t sshbuf2[] = "99-PuTTY_2.123" ; | |
453 | uint32_t sshlen2 = sizeof(sshbuf2) - 1; | |
454 | uint8_t sshbuf3[] = "\n"; | |
455 | uint32_t sshlen3 = sizeof(sshbuf3) - 1; | |
456 | uint8_t sshbuf4[] = "whatever..."; | |
457 | uint32_t sshlen4 = sizeof(sshbuf4) - 1; | |
458 | TcpSession ssn; | |
459 | Packet *p = NULL; | |
460 | Signature *s = NULL; | |
461 | ThreadVars th_v; | |
462 | DetectEngineThreadCtx *det_ctx = NULL; | |
463 | ||
464 | memset(&th_v, 0, sizeof(th_v)); | |
465 | memset(&f, 0, sizeof(f)); | |
466 | memset(&ssn, 0, sizeof(ssn)); | |
467 | ||
468 | p = UTHBuildPacket(NULL, 0, IPPROTO_TCP); | |
469 | ||
470 | FLOW_INITIALIZE(&f); | |
471 | f.protoctx = (void *)&ssn; | |
472 | p->flow = &f; | |
473 | p->flowflags |= FLOW_PKT_TOSERVER; | |
474 | p->flowflags |= FLOW_PKT_ESTABLISHED; | |
475 | f.alproto = ALPROTO_SSH; | |
476 | ||
477 | StreamTcpInitConfig(TRUE); | |
478 | FlowL7DataPtrInit(&f); | |
479 | ||
480 | DetectEngineCtx *de_ctx = DetectEngineCtxInit(); | |
481 | if (de_ctx == NULL) { | |
482 | goto end; | |
483 | } | |
484 | ||
485 | de_ctx->flags |= DE_QUIET; | |
486 | ||
487 | s = de_ctx->sig_list = SigInit(de_ctx,"alert ssh any any -> any any (msg:\"SSH\"; ssh.protoversion:2_compat; sid:1;)"); | |
488 | if (s == NULL) { | |
489 | goto end; | |
490 | } | |
491 | ||
492 | SigGroupBuild(de_ctx); | |
493 | DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx); | |
494 | ||
495 | int r = AppLayerParse(&f, ALPROTO_SSH, STREAM_TOSERVER, sshbuf1, sshlen1); | |
496 | if (r != 0) { | |
497 | printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r); | |
498 | goto end; | |
499 | } | |
500 | ||
501 | r = AppLayerParse(&f, ALPROTO_SSH, STREAM_TOSERVER, sshbuf2, sshlen2); | |
502 | if (r != 0) { | |
503 | printf("toserver chunk 2 returned %" PRId32 ", expected 0: ", r); | |
504 | goto end; | |
505 | } | |
506 | ||
507 | r = AppLayerParse(&f, ALPROTO_SSH, STREAM_TOSERVER, sshbuf3, sshlen3); | |
508 | if (r != 0) { | |
509 | printf("toserver chunk 3 returned %" PRId32 ", expected 0: ", r); | |
510 | goto end; | |
511 | } | |
512 | ||
513 | r = AppLayerParse(&f, ALPROTO_SSH, STREAM_TOSERVER, sshbuf4, sshlen4); | |
514 | if (r != 0) { | |
515 | printf("toserver chunk 4 returned %" PRId32 ", expected 0: ", r); | |
516 | goto end; | |
517 | } | |
518 | ||
519 | SshState *ssh_state = f.aldata[AlpGetStateIdx(ALPROTO_SSH)]; | |
520 | if (ssh_state == NULL) { | |
521 | printf("no ssh state: "); | |
522 | goto end; | |
523 | } | |
524 | ||
525 | /* do detect */ | |
526 | SigMatchSignatures(&th_v, de_ctx, det_ctx, p); | |
527 | ||
528 | if ( !(PacketAlertCheck(p, 1))) { | |
529 | printf("Error, the sig should match: "); | |
530 | goto end; | |
531 | } | |
532 | ||
533 | result = 1; | |
534 | end: | |
535 | SigGroupCleanup(de_ctx); | |
536 | SigCleanSignatures(de_ctx); | |
537 | ||
538 | DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx); | |
539 | DetectEngineCtxFree(de_ctx); | |
540 | ||
541 | FlowL7DataPtrFree(&f); | |
542 | StreamTcpFreeConfig(TRUE); | |
543 | FLOW_DESTROY(&f); | |
544 | ||
545 | UTHFreePackets(&p, 1); | |
546 | return result; | |
547 | } | |
548 | ||
549 | /** \test Send a get request in three chunks + more data. */ | |
550 | static int DetectSshVersionTestDetect03(void) { | |
551 | int result = 0; | |
552 | Flow f; | |
553 | uint8_t sshbuf1[] = "SSH-1."; | |
554 | uint32_t sshlen1 = sizeof(sshbuf1) - 1; | |
555 | uint8_t sshbuf2[] = "7-PuTTY_2.123" ; | |
556 | uint32_t sshlen2 = sizeof(sshbuf2) - 1; | |
557 | uint8_t sshbuf3[] = "\n"; | |
558 | uint32_t sshlen3 = sizeof(sshbuf3) - 1; | |
559 | uint8_t sshbuf4[] = "whatever..."; | |
560 | uint32_t sshlen4 = sizeof(sshbuf4) - 1; | |
561 | TcpSession ssn; | |
562 | Packet *p = NULL; | |
563 | Signature *s = NULL; | |
564 | ThreadVars th_v; | |
565 | DetectEngineThreadCtx *det_ctx = NULL; | |
566 | ||
567 | memset(&th_v, 0, sizeof(th_v)); | |
568 | memset(&f, 0, sizeof(f)); | |
569 | memset(&ssn, 0, sizeof(ssn)); | |
570 | ||
571 | p = UTHBuildPacket(NULL, 0, IPPROTO_TCP); | |
572 | ||
573 | FLOW_INITIALIZE(&f); | |
574 | f.protoctx = (void *)&ssn; | |
575 | p->flow = &f; | |
576 | p->flowflags |= FLOW_PKT_TOSERVER; | |
577 | p->flowflags |= FLOW_PKT_ESTABLISHED; | |
578 | f.alproto = ALPROTO_SSH; | |
579 | ||
580 | StreamTcpInitConfig(TRUE); | |
581 | FlowL7DataPtrInit(&f); | |
582 | ||
583 | DetectEngineCtx *de_ctx = DetectEngineCtxInit(); | |
584 | if (de_ctx == NULL) { | |
585 | goto end; | |
586 | } | |
587 | ||
588 | de_ctx->flags |= DE_QUIET; | |
589 | ||
590 | s = de_ctx->sig_list = SigInit(de_ctx,"alert ssh any any -> any any (msg:\"SSH\"; ssh.protoversion:2_compat; sid:1;)"); | |
591 | if (s == NULL) { | |
592 | goto end; | |
593 | } | |
594 | ||
595 | SigGroupBuild(de_ctx); | |
596 | DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx); | |
597 | ||
598 | int r = AppLayerParse(&f, ALPROTO_SSH, STREAM_TOSERVER, sshbuf1, sshlen1); | |
599 | if (r != 0) { | |
600 | printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r); | |
601 | goto end; | |
602 | } | |
603 | ||
604 | r = AppLayerParse(&f, ALPROTO_SSH, STREAM_TOSERVER, sshbuf2, sshlen2); | |
605 | if (r != 0) { | |
606 | printf("toserver chunk 2 returned %" PRId32 ", expected 0: ", r); | |
607 | goto end; | |
608 | } | |
609 | ||
610 | r = AppLayerParse(&f, ALPROTO_SSH, STREAM_TOSERVER, sshbuf3, sshlen3); | |
611 | if (r != 0) { | |
612 | printf("toserver chunk 3 returned %" PRId32 ", expected 0: ", r); | |
613 | goto end; | |
614 | } | |
615 | ||
616 | r = AppLayerParse(&f, ALPROTO_SSH, STREAM_TOSERVER, sshbuf4, sshlen4); | |
617 | if (r != 0) { | |
618 | printf("toserver chunk 4 returned %" PRId32 ", expected 0: ", r); | |
619 | goto end; | |
620 | } | |
621 | ||
622 | SshState *ssh_state = f.aldata[AlpGetStateIdx(ALPROTO_SSH)]; | |
623 | if (ssh_state == NULL) { | |
624 | printf("no ssh state: "); | |
625 | goto end; | |
626 | } | |
627 | ||
628 | /* do detect */ | |
629 | SigMatchSignatures(&th_v, de_ctx, det_ctx, p); | |
630 | ||
631 | if (PacketAlertCheck(p, 1)) { | |
632 | printf("Error, 1.7 version is not 2 compat, so the sig should not match: "); | |
633 | goto end; | |
634 | } | |
635 | ||
636 | result = 1; | |
637 | end: | |
638 | SigGroupCleanup(de_ctx); | |
639 | SigCleanSignatures(de_ctx); | |
640 | ||
641 | DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx); | |
642 | DetectEngineCtxFree(de_ctx); | |
643 | ||
644 | FlowL7DataPtrFree(&f); | |
645 | StreamTcpFreeConfig(TRUE); | |
646 | FLOW_DESTROY(&f); | |
647 | ||
648 | UTHFreePackets(&p, 1); | |
649 | return result; | |
650 | } | |
651 | ||
652 | #endif /* UNITTESTS */ | |
653 | ||
654 | /** | |
655 | * \brief this function registers unit tests for DetectSshVersion | |
656 | */ | |
657 | void DetectSshVersionRegisterTests(void) { | |
658 | #ifdef UNITTESTS /* UNITTESTS */ | |
659 | UtRegisterTest("DetectSshVersionTestParse01", DetectSshVersionTestParse01, 1); | |
660 | UtRegisterTest("DetectSshVersionTestParse02", DetectSshVersionTestParse02, 1); | |
661 | UtRegisterTest("DetectSshVersionTestParse03", DetectSshVersionTestParse03, 1); | |
662 | UtRegisterTest("DetectSshVersionTestDetect01", DetectSshVersionTestDetect01, 1); | |
663 | UtRegisterTest("DetectSshVersionTestDetect02", DetectSshVersionTestDetect02, 1); | |
664 | UtRegisterTest("DetectSshVersionTestDetect03", DetectSshVersionTestDetect03, 1); | |
665 | #endif /* UNITTESTS */ | |
666 | } | |
667 |
0 | /* Copyright (C) 2007-2010 Open Information Security Foundation | |
1 | * | |
2 | * You can copy, redistribute or modify this Program under the terms of | |
3 | * the GNU General Public License version 2 as published by the Free | |
4 | * Software Foundation. | |
5 | * | |
6 | * This program is distributed in the hope that it will be useful, | |
7 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
8 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
9 | * GNU General Public License for more details. | |
10 | * | |
11 | * You should have received a copy of the GNU General Public License | |
12 | * version 2 along with this program; if not, write to the Free Software | |
13 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA | |
14 | * 02110-1301, USA. | |
15 | */ | |
16 | ||
17 | /** | |
18 | * \file | |
19 | * | |
20 | * \author Pablo Rincon <pablo.rincon.crespo@gmail.com> | |
21 | */ | |
22 | ||
23 | #ifndef __DETECT_SSH_VERSION_H__ | |
24 | #define __DETECT_SSH_VERSION_H__ | |
25 | ||
26 | /** proto version 1.99 is considered proto version 2 */ | |
27 | #define SSH_FLAG_PROTOVERSION_2_COMPAT 0x01 | |
28 | ||
29 | typedef struct DetectSshVersionData_ { | |
30 | uint8_t *ver; /** ssh version to match */ | |
31 | uint16_t len; /** ssh version length to match */ | |
32 | uint8_t flags; | |
33 | } DetectSshVersionData; | |
34 | ||
35 | /* prototypes */ | |
36 | void DetectSshVersionRegister (void); | |
37 | ||
38 | #endif /* __DETECT_SSH_VERSION_H__ */ | |
39 |
0 | /* Copyright (C) 2007-2010 Open Information Security Foundation | |
1 | * | |
2 | * You can copy, redistribute or modify this Program under the terms of | |
3 | * the GNU General Public License version 2 as published by the Free | |
4 | * Software Foundation. | |
5 | * | |
6 | * This program is distributed in the hope that it will be useful, | |
7 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
8 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
9 | * GNU General Public License for more details. | |
10 | * | |
11 | * You should have received a copy of the GNU General Public License | |
12 | * version 2 along with this program; if not, write to the Free Software | |
13 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA | |
14 | * 02110-1301, USA. | |
15 | */ | |
16 | ||
17 | /** | |
18 | * \file | |
19 | * | |
20 | * \author Pablo Rincon <pablo.rincon.crespo@gmail.com> | |
21 | * | |
22 | * Implements the ssh.softwareversion keyword | |
23 | * You can match over the software version string of ssh, and it will | |
24 | * be compared from the beginning of the string so you can say for | |
25 | * example ssh.softwareversion:"PuTTY" and it can match, or you can | |
26 | * also specify the version, something like | |
27 | * ssh.softwareversion:"PuTTY-Release-0.55" | |
28 | * I find this useful to match over a known vulnerable server/client | |
29 | * software version incombination to other checks, so you can know | |
30 | * that the risk is higher | |
31 | */ | |
32 | ||
33 | #include "suricata-common.h" | |
34 | #include "threads.h" | |
35 | #include "debug.h" | |
36 | #include "decode.h" | |
37 | ||
38 | #include "detect.h" | |
39 | #include "detect-parse.h" | |
40 | ||
41 | #include "detect-engine.h" | |
42 | #include "detect-engine-mpm.h" | |
43 | #include "detect-engine-state.h" | |
44 | ||
45 | #include "flow.h" | |
46 | #include "flow-var.h" | |
47 | #include "flow-util.h" | |
48 | ||
49 | #include "util-debug.h" | |
50 | #include "util-unittest.h" | |
51 | #include "util-unittest-helper.h" | |
52 | ||
53 | #include "app-layer.h" | |
54 | ||
55 | #include "app-layer-ssh.h" | |
56 | #include "detect-ssh-software-version.h" | |
57 | ||
58 | #include "stream-tcp.h" | |
59 | ||
60 | /** | |
61 | * \brief Regex for parsing the softwareversion string | |
62 | */ | |
63 | #define PARSE_REGEX "^\\s*\"?\\s*?([0-9a-zA-Z\\.\\-\\_]+)\\s*\"?\\s*$" | |
64 | ||
65 | static pcre *parse_regex; | |
66 | static pcre_extra *parse_regex_study; | |
67 | ||
68 | int DetectSshSoftwareVersionMatch (ThreadVars *, DetectEngineThreadCtx *, Flow *, uint8_t, void *, Signature *, SigMatch *); | |
69 | static int DetectSshSoftwareVersionSetup (DetectEngineCtx *, Signature *, char *); | |
70 | void DetectSshSoftwareVersionRegisterTests(void); | |
71 | void DetectSshSoftwareVersionFree(void *); | |
72 | void DetectSshSoftwareVersionRegister(void); | |
73 | ||
74 | /** | |
75 | * \brief Registration function for keyword: ssh.softwareversion | |
76 | */ | |
77 | void DetectSshSoftwareVersionRegister(void) { | |
78 | sigmatch_table[DETECT_AL_SSH_SOFTWAREVERSION].name = "ssh.softwareversion"; | |
79 | sigmatch_table[DETECT_AL_SSH_SOFTWAREVERSION].Match = NULL; | |
80 | sigmatch_table[DETECT_AL_SSH_SOFTWAREVERSION].AppLayerMatch = DetectSshSoftwareVersionMatch; | |
81 | sigmatch_table[DETECT_AL_SSH_SOFTWAREVERSION].alproto = ALPROTO_SSH; | |
82 | sigmatch_table[DETECT_AL_SSH_SOFTWAREVERSION].Setup = DetectSshSoftwareVersionSetup; | |
83 | sigmatch_table[DETECT_AL_SSH_SOFTWAREVERSION].Free = DetectSshSoftwareVersionFree; | |
84 | sigmatch_table[DETECT_AL_SSH_SOFTWAREVERSION].RegisterTests = DetectSshSoftwareVersionRegisterTests; | |
85 | ||
86 | const char *eb; | |
87 | int eo; | |
88 | int opts = 0; | |
89 | ||
90 | SCLogDebug("registering ssh.softwareversion rule option"); | |
91 | ||
92 | parse_regex = pcre_compile(PARSE_REGEX, opts, &eb, &eo, NULL); | |
93 | if (parse_regex == NULL) { | |
94 | SCLogError(SC_ERR_PCRE_COMPILE, "Compile of \"%s\" failed at offset %" PRId32 ": %s", | |
95 | PARSE_REGEX, eo, eb); | |
96 | goto error; | |
97 | } | |
98 | ||
99 | parse_regex_study = pcre_study(parse_regex, 0, &eb); | |
100 | if (eb != NULL) { | |
101 | SCLogError(SC_ERR_PCRE_STUDY, "pcre study failed: %s", eb); | |
102 | goto error; | |
103 | } | |
104 | return; | |
105 | ||
106 | error: | |
107 | return; | |
108 | } | |
109 | ||
110 | /** | |
111 | * \brief match the specified version on a ssh session | |
112 | * | |
113 | * \param t pointer to thread vars | |
114 | * \param det_ctx pointer to the pattern matcher thread | |
115 | * \param p pointer to the current packet | |
116 | * \param m pointer to the sigmatch that we will cast into DetectSshSoftwareVersionData | |
117 | * | |
118 | * \retval 0 no match | |
119 | * \retval 1 match | |
120 | */ | |
121 | int DetectSshSoftwareVersionMatch (ThreadVars *t, DetectEngineThreadCtx *det_ctx, Flow *f, uint8_t flags, void *state, Signature *s, SigMatch *m) | |
122 | { | |
123 | SCEnter(); | |
124 | ||
125 | DetectSshSoftwareVersionData *ssh = (DetectSshSoftwareVersionData *)m->ctx; | |
126 | SshState *ssh_state = (SshState *)state; | |
127 | if (ssh_state == NULL) { | |
128 | SCLogDebug("no ssh state, no match"); | |
129 | SCReturnInt(0); | |
130 | } | |
131 | ||
132 | int ret = 0; | |
133 | SCMutexLock(&f->m); | |
134 | if (flags & STREAM_TOCLIENT && ssh_state->flags & SSH_FLAG_SERVER_VERSION_PARSED) { | |
135 | SCLogDebug("looking for ssh server softwareversion %s length %"PRIu16" on %s", ssh->software_ver, ssh->len, ssh_state->server_software_version); | |
136 | ret = (strncmp((char *) ssh_state->server_software_version, (char *) ssh->software_ver, ssh->len) == 0)? 1 : 0; | |
137 | } else if (flags & STREAM_TOSERVER && ssh_state->flags & SSH_FLAG_CLIENT_VERSION_PARSED) { | |
138 | SCLogDebug("looking for ssh client softwareversion %s length %"PRIu16" on %s", ssh->software_ver, ssh->len, ssh_state->client_software_version); | |
139 | ret = (strncmp((char *) ssh_state->client_software_version, (char *) ssh->software_ver, ssh->len) == 0)? 1 : 0; | |
140 | } | |
141 | SCMutexUnlock(&f->m); | |
142 | SCReturnInt(ret); | |
143 | } | |
144 | ||
145 | /** | |
146 | * \brief This function is used to parse IPV4 ip_id passed via keyword: "id" | |
147 | * | |
148 | * \param idstr Pointer to the user provided id option | |
149 | * | |
150 | * \retval id_d pointer to DetectSshSoftwareVersionData on success | |
151 | * \retval NULL on failure | |
152 | */ | |
153 | DetectSshSoftwareVersionData *DetectSshSoftwareVersionParse (char *str) | |
154 | { | |
155 | DetectSshSoftwareVersionData *ssh = NULL; | |
156 | #define MAX_SUBSTRINGS 30 | |
157 | int ret = 0, res = 0; | |
158 | int ov[MAX_SUBSTRINGS]; | |
159 | ||
160 | ret = pcre_exec(parse_regex, parse_regex_study, str, strlen(str), 0, 0, | |
161 | ov, MAX_SUBSTRINGS); | |
162 | ||
163 | if (ret < 1 || ret > 3) { | |
164 | SCLogError(SC_ERR_PCRE_MATCH, "invalid ssh.softwareversion option"); | |
165 | goto error; | |
166 | } | |
167 | ||
168 | if (ret > 1) { | |
169 | const char *str_ptr; | |
170 | res = pcre_get_substring((char *)str, ov, MAX_SUBSTRINGS, 1, &str_ptr); | |
171 | if (res < 0) { | |
172 | SCLogError(SC_ERR_PCRE_GET_SUBSTRING, "pcre_get_substring failed"); | |
173 | goto error; | |
174 | } | |
175 | ||
176 | /* We have a correct id option */ | |
177 | ssh = SCMalloc(sizeof(DetectSshSoftwareVersionData)); | |
178 | if (ssh == NULL) | |
179 | goto error; | |
180 | ||
181 | ssh->software_ver = (uint8_t *)SCStrdup((char*)str_ptr); | |
182 | if (ssh->software_ver == NULL) { | |
183 | goto error; | |
184 | } | |
185 | ssh->len = strlen((char *) ssh->software_ver); | |
186 | ||
187 | SCLogDebug("will look for ssh %s", ssh->software_ver); | |
188 | } | |
189 | ||
190 | return ssh; | |
191 | ||
192 | error: | |
193 | if (ssh != NULL) | |
194 | DetectSshSoftwareVersionFree(ssh); | |
195 | return NULL; | |
196 | ||
197 | } | |
198 | ||
199 | /** | |
200 | * \brief this function is used to add the parsed "id" option | |
201 | * \brief into the current signature | |
202 | * | |
203 | * \param de_ctx pointer to the Detection Engine Context | |
204 | * \param s pointer to the Current Signature | |
205 | * \param idstr pointer to the user provided "id" option | |
206 | * | |
207 | * \retval 0 on Success | |
208 | * \retval -1 on Failure | |
209 | */ | |
210 | static int DetectSshSoftwareVersionSetup (DetectEngineCtx *de_ctx, Signature *s, char *str) | |
211 | { | |
212 | DetectSshSoftwareVersionData *ssh = NULL; | |
213 | SigMatch *sm = NULL; | |
214 | ||
215 | ssh = DetectSshSoftwareVersionParse(str); | |
216 | if (ssh == NULL) goto error; | |
217 | ||
218 | /* Okay so far so good, lets get this into a SigMatch | |
219 | * and put it in the Signature. */ | |
220 | sm = SigMatchAlloc(); | |
221 | if (sm == NULL) | |
222 | goto error; | |
223 | ||
224 | sm->type = DETECT_AL_SSH_SOFTWAREVERSION; | |
225 | sm->ctx = (void *)ssh; | |
226 | ||
227 | SigMatchAppendAppLayer(s, sm); | |
228 | ||
229 | if (s->alproto != ALPROTO_UNKNOWN && s->alproto != ALPROTO_SSH) { | |
230 | SCLogError(SC_ERR_CONFLICTING_RULE_KEYWORDS, "rule contains conflicting keywords."); | |
231 | goto error; | |
232 | } | |
233 | ||
234 | s->alproto = ALPROTO_SSH; | |
235 | return 0; | |
236 | ||
237 | error: | |
238 | if (ssh != NULL) DetectSshSoftwareVersionFree(ssh); | |
239 | if (sm != NULL) SCFree(sm); | |
240 | return -1; | |
241 | ||
242 | } | |
243 | ||
244 | /** | |
245 | * \brief this function will free memory associated with DetectSshSoftwareVersionData | |
246 | * | |
247 | * \param id_d pointer to DetectSshSoftwareVersionData | |
248 | */ | |
249 | void DetectSshSoftwareVersionFree(void *ptr) { | |
250 | DetectSshSoftwareVersionData *id_d = (DetectSshSoftwareVersionData *)ptr; | |
251 | SCFree(id_d); | |
252 | } | |
253 | ||
254 | #ifdef UNITTESTS /* UNITTESTS */ | |
255 | ||
256 | /** | |
257 | * \test DetectSshSoftwareVersionTestParse01 is a test to make sure that we parse | |
258 | * a software version correctly | |
259 | */ | |
260 | int DetectSshSoftwareVersionTestParse01 (void) { | |
261 | DetectSshSoftwareVersionData *ssh = NULL; | |
262 | ssh = DetectSshSoftwareVersionParse("PuTTY_1.0"); | |
263 | if (ssh != NULL && strncmp((char *) ssh->software_ver, "PuTTY_1.0", 9) == 0) { | |
264 | DetectSshSoftwareVersionFree(ssh); | |
265 | return 1; | |
266 | } | |
267 | ||
268 | return 0; | |
269 | } | |
270 | ||
271 | /** | |
272 | * \test DetectSshSoftwareVersionTestParse02 is a test to make sure that we parse | |
273 | * the software version correctly | |
274 | */ | |
275 | int DetectSshSoftwareVersionTestParse02 (void) { | |
276 | DetectSshSoftwareVersionData *ssh = NULL; | |
277 | ssh = DetectSshSoftwareVersionParse("\"SecureCRT-4.0\""); | |
278 | if (ssh != NULL && strncmp((char *) ssh->software_ver, "SecureCRT-4.0", 13) == 0) { | |
279 | DetectSshSoftwareVersionFree(ssh); | |
280 | return 1; | |
281 | } | |
282 | ||
283 | return 0; | |
284 | } | |
285 | ||
286 | /** | |
287 | * \test DetectSshSoftwareVersionTestParse03 is a test to make sure that we | |
288 | * don't return a ssh_data with an empty value specified | |
289 | */ | |
290 | int DetectSshSoftwareVersionTestParse03 (void) { | |
291 | DetectSshSoftwareVersionData *ssh = NULL; | |
292 | ssh = DetectSshSoftwareVersionParse(""); | |
293 | if (ssh != NULL) { | |
294 | DetectSshSoftwareVersionFree(ssh); | |
295 | return 0; | |
296 | } | |
297 | ||
298 | return 1; | |
299 | } | |
300 | ||
301 | ||
302 | #include "stream-tcp-reassemble.h" | |
303 | ||
304 | /** \test Send a get request in three chunks + more data. */ | |
305 | static int DetectSshSoftwareVersionTestDetect01(void) { | |
306 | int result = 0; | |
307 | Flow f; | |
308 | uint8_t sshbuf1[] = "SSH-1."; | |
309 | uint32_t sshlen1 = sizeof(sshbuf1) - 1; | |
310 | uint8_t sshbuf2[] = "10-PuTTY_2.123" ; | |
311 | uint32_t sshlen2 = sizeof(sshbuf2) - 1; | |
312 | uint8_t sshbuf3[] = "\n"; | |
313 | uint32_t sshlen3 = sizeof(sshbuf3) - 1; | |
314 | uint8_t sshbuf4[] = "whatever..."; | |
315 | uint32_t sshlen4 = sizeof(sshbuf4) - 1; | |
316 | TcpSession ssn; | |
317 | Packet *p = NULL; | |
318 | Signature *s = NULL; | |
319 | ThreadVars th_v; | |
320 | DetectEngineThreadCtx *det_ctx = NULL; | |
321 | ||
322 | memset(&th_v, 0, sizeof(th_v)); | |
323 | memset(&f, 0, sizeof(f)); | |
324 | memset(&ssn, 0, sizeof(ssn)); | |
325 | ||
326 | p = UTHBuildPacket(NULL, 0, IPPROTO_TCP); | |
327 | ||
328 | FLOW_INITIALIZE(&f); | |
329 | f.protoctx = (void *)&ssn; | |
330 | p->flow = &f; | |
331 | p->flowflags |= FLOW_PKT_TOSERVER; | |
332 | p->flowflags |= FLOW_PKT_ESTABLISHED; | |
333 | f.alproto = ALPROTO_SSH; | |
334 | ||
335 | StreamTcpInitConfig(TRUE); | |
336 | FlowL7DataPtrInit(&f); | |
337 | ||
338 | DetectEngineCtx *de_ctx = DetectEngineCtxInit(); | |
339 | if (de_ctx == NULL) { | |
340 | goto end; | |
341 | } | |
342 | ||
343 | de_ctx->flags |= DE_QUIET; | |
344 | ||
345 | s = de_ctx->sig_list = SigInit(de_ctx,"alert ssh any any -> any any (msg:\"SSH\"; ssh.softwareversion:PuTTY_2.123; sid:1;)"); | |
346 | if (s == NULL) { | |
347 | goto end; | |
348 | } | |
349 | ||
350 | SigGroupBuild(de_ctx); | |
351 | DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx); | |
352 | ||
353 | int r = AppLayerParse(&f, ALPROTO_SSH, STREAM_TOSERVER, sshbuf1, sshlen1); | |
354 | if (r != 0) { | |
355 | printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r); | |
356 | goto end; | |
357 | } | |
358 | ||
359 | r = AppLayerParse(&f, ALPROTO_SSH, STREAM_TOSERVER, sshbuf2, sshlen2); | |
360 | if (r != 0) { | |
361 | printf("toserver chunk 2 returned %" PRId32 ", expected 0: ", r); | |
362 | goto end; | |
363 | } | |
364 | ||
365 | r = AppLayerParse(&f, ALPROTO_SSH, STREAM_TOSERVER, sshbuf3, sshlen3); | |
366 | if (r != 0) { | |
367 | printf("toserver chunk 3 returned %" PRId32 ", expected 0: ", r); | |
368 | goto end; | |
369 | } | |
370 | ||
371 | r = AppLayerParse(&f, ALPROTO_SSH, STREAM_TOSERVER, sshbuf4, sshlen4); | |
372 | if (r != 0) { | |
373 | printf("toserver chunk 4 returned %" PRId32 ", expected 0: ", r); | |
374 | goto end; | |
375 | } | |
376 | ||
377 | SshState *ssh_state = f.aldata[AlpGetStateIdx(ALPROTO_SSH)]; | |
378 | if (ssh_state == NULL) { | |
379 | printf("no ssh state: "); | |
380 | goto end; | |
381 | } | |
382 | ||
383 | /* do detect */ | |
384 | SigMatchSignatures(&th_v, de_ctx, det_ctx, p); | |
385 | ||
386 | if ( !(PacketAlertCheck(p, 1))) { | |
387 | printf("Error, the sig should match: "); | |
388 | goto end; | |
389 | } | |
390 | ||
391 | result = 1; | |
392 | end: | |
393 | SigGroupCleanup(de_ctx); | |
394 | SigCleanSignatures(de_ctx); | |
395 | ||
396 | DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx); | |
397 | DetectEngineCtxFree(de_ctx); | |
398 | ||
399 | FlowL7DataPtrFree(&f); | |
400 | StreamTcpFreeConfig(TRUE); | |
401 | FLOW_DESTROY(&f); | |
402 | ||
403 | UTHFreePackets(&p, 1); | |
404 | return result; | |
405 | } | |
406 | ||
407 | /** \test Send a get request in three chunks + more data. */ | |
408 | static int DetectSshSoftwareVersionTestDetect02(void) { | |
409 | int result = 0; | |
410 | Flow f; | |
411 | uint8_t sshbuf1[] = "SSH-1.99-Pu"; | |
412 | uint32_t sshlen1 = sizeof(sshbuf1) - 1; | |
413 | uint8_t sshbuf2[] = "TTY_2.123" ; | |
414 | uint32_t sshlen2 = sizeof(sshbuf2) - 1; | |
415 | uint8_t sshbuf3[] = "\n"; | |
416 | uint32_t sshlen3 = sizeof(sshbuf3) - 1; | |
417 | uint8_t sshbuf4[] = "whatever..."; | |
418 | uint32_t sshlen4 = sizeof(sshbuf4) - 1; | |
419 | TcpSession ssn; | |
420 | Packet *p = NULL; | |
421 | Signature *s = NULL; | |
422 | ThreadVars th_v; | |
423 | DetectEngineThreadCtx *det_ctx = NULL; | |
424 | ||
425 | memset(&th_v, 0, sizeof(th_v)); | |
426 | memset(&f, 0, sizeof(f)); | |
427 | memset(&ssn, 0, sizeof(ssn)); | |
428 | ||
429 | p = UTHBuildPacket(NULL, 0, IPPROTO_TCP); | |
430 | ||
431 | FLOW_INITIALIZE(&f); | |
432 | f.protoctx = (void *)&ssn; | |
433 | p->flow = &f; | |
434 | p->flowflags |= FLOW_PKT_TOSERVER; | |
435 | p->flowflags |= FLOW_PKT_ESTABLISHED; | |
436 | f.alproto = ALPROTO_SSH; | |
437 | ||
438 | StreamTcpInitConfig(TRUE); | |
439 | FlowL7DataPtrInit(&f); | |
440 | ||
441 | DetectEngineCtx *de_ctx = DetectEngineCtxInit(); | |
442 | if (de_ctx == NULL) { | |
443 | goto end; | |
444 | } | |
445 | ||
446 | de_ctx->flags |= DE_QUIET; | |
447 | ||
448 | s = de_ctx->sig_list = SigInit(de_ctx,"alert ssh any any -> any any (msg:\"SSH\"; ssh.softwareversion:PuTTY_2.123; sid:1;)"); | |
449 | if (s == NULL) { | |
450 | goto end; | |
451 | } | |
452 | ||
453 | SigGroupBuild(de_ctx); | |
454 | DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx); | |
455 | ||
456 | int r = AppLayerParse(&f, ALPROTO_SSH, STREAM_TOSERVER, sshbuf1, sshlen1); | |
457 | if (r != 0) { | |
458 | printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r); | |
459 | goto end; | |
460 | } | |
461 | ||
462 | r = AppLayerParse(&f, ALPROTO_SSH, STREAM_TOSERVER, sshbuf2, sshlen2); | |
463 | if (r != 0) { | |
464 | printf("toserver chunk 2 returned %" PRId32 ", expected 0: ", r); | |
465 | goto end; | |
466 | } | |
467 | ||
468 | r = AppLayerParse(&f, ALPROTO_SSH, STREAM_TOSERVER, sshbuf3, sshlen3); | |
469 | if (r != 0) { | |
470 | printf("toserver chunk 3 returned %" PRId32 ", expected 0: ", r); | |
471 | goto end; | |
472 | } | |
473 | ||
474 | r = AppLayerParse(&f, ALPROTO_SSH, STREAM_TOSERVER, sshbuf4, sshlen4); | |
475 | if (r != 0) { | |
476 | printf("toserver chunk 4 returned %" PRId32 ", expected 0: ", r); | |
477 | goto end; | |
478 | } | |
479 | ||
480 | SshState *ssh_state = f.aldata[AlpGetStateIdx(ALPROTO_SSH)]; | |
481 | if (ssh_state == NULL) { | |
482 | printf("no ssh state: "); | |
483 | goto end; | |
484 | } | |
485 | ||
486 | /* do detect */ | |
487 | SigMatchSignatures(&th_v, de_ctx, det_ctx, p); | |
488 | ||
489 | if ( !(PacketAlertCheck(p, 1))) { | |
490 | printf("Error, the sig should match: "); | |
491 | goto end; | |
492 | } | |
493 | ||
494 | result = 1; | |
495 | end: | |
496 | SigGroupCleanup(de_ctx); | |
497 | SigCleanSignatures(de_ctx); | |
498 | ||
499 | DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx); | |
500 | DetectEngineCtxFree(de_ctx); | |
501 | ||
502 | FlowL7DataPtrFree(&f); | |
503 | StreamTcpFreeConfig(TRUE); | |
504 | FLOW_DESTROY(&f); | |
505 | ||
506 | UTHFreePackets(&p, 1); | |
507 | return result; | |
508 | } | |
509 | ||
510 | /** \test Send a get request in three chunks + more data. */ | |
511 | static int DetectSshSoftwareVersionTestDetect03(void) { | |
512 | int result = 0; | |
513 | Flow f; | |
514 | uint8_t sshbuf1[] = "SSH-1."; | |
515 | uint32_t sshlen1 = sizeof(sshbuf1) - 1; | |
516 | uint8_t sshbuf2[] = "7-PuTTY_2.123" ; | |
517 | uint32_t sshlen2 = sizeof(sshbuf2) - 1; | |
518 | uint8_t sshbuf3[] = "\n"; | |
519 | uint32_t sshlen3 = sizeof(sshbuf3) - 1; | |
520 | uint8_t sshbuf4[] = "whatever..."; | |
521 | uint32_t sshlen4 = sizeof(sshbuf4) - 1; | |
522 | TcpSession ssn; | |
523 | Packet *p = NULL; | |
524 | Signature *s = NULL; | |
525 | ThreadVars th_v; | |
526 | DetectEngineThreadCtx *det_ctx = NULL; | |
527 | ||
528 | memset(&th_v, 0, sizeof(th_v)); | |
529 | memset(&f, 0, sizeof(f)); | |
530 | memset(&ssn, 0, sizeof(ssn)); | |
531 | ||
532 | p = UTHBuildPacket(NULL, 0, IPPROTO_TCP); | |
533 | ||
534 | FLOW_INITIALIZE(&f); | |
535 | f.protoctx = (void *)&ssn; | |
536 | p->flow = &f; | |
537 | p->flowflags |= FLOW_PKT_TOSERVER; | |
538 | p->flowflags |= FLOW_PKT_ESTABLISHED; | |
539 | f.alproto = ALPROTO_SSH; | |
540 | ||
541 | StreamTcpInitConfig(TRUE); | |
542 | FlowL7DataPtrInit(&f); | |
543 | ||
544 | DetectEngineCtx *de_ctx = DetectEngineCtxInit(); | |
545 | if (de_ctx == NULL) { | |
546 | goto end; | |
547 | } | |
548 | ||
549 | de_ctx->flags |= DE_QUIET; | |
550 | ||
551 | s = de_ctx->sig_list = SigInit(de_ctx,"alert ssh any any -> any any (msg:\"SSH\"; ssh.softwareversion:lalala-3.1.4; sid:1;)"); | |
552 | if (s == NULL) { | |
553 | goto end; | |
554 | } | |
555 | ||
556 | SigGroupBuild(de_ctx); | |
557 | DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx); | |
558 | ||
559 | int r = AppLayerParse(&f, ALPROTO_SSH, STREAM_TOSERVER, sshbuf1, sshlen1); | |
560 | if (r != 0) { | |
561 | printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r); | |
562 | goto end; | |
563 | } | |
564 | ||
565 | r = AppLayerParse(&f, ALPROTO_SSH, STREAM_TOSERVER, sshbuf2, sshlen2); | |
566 | if (r != 0) { | |
567 | printf("toserver chunk 2 returned %" PRId32 ", expected 0: ", r); | |
568 | goto end; | |
569 | } | |
570 | ||
571 | r = AppLayerParse(&f, ALPROTO_SSH, STREAM_TOSERVER, sshbuf3, sshlen3); | |
572 | if (r != 0) { | |
573 | printf("toserver chunk 3 returned %" PRId32 ", expected 0: ", r); | |
574 | goto end; | |
575 | } | |
576 | ||
577 | r = AppLayerParse(&f, ALPROTO_SSH, STREAM_TOSERVER, sshbuf4, sshlen4); | |
578 | if (r != 0) { | |
579 | printf("toserver chunk 4 returned %" PRId32 ", expected 0: ", r); | |
580 | goto end; | |
581 | } | |
582 | ||
583 | SshState *ssh_state = f.aldata[AlpGetStateIdx(ALPROTO_SSH)]; | |
584 | if (ssh_state == NULL) { | |
585 | printf("no ssh state: "); | |
586 | goto end; | |
587 | } | |
588 | ||
589 | /* do detect */ | |
590 | SigMatchSignatures(&th_v, de_ctx, det_ctx, p); | |
591 | ||
592 | if (PacketAlertCheck(p, 1)) { | |
593 | printf("Error, 1.7 version is not 2 compat, so the sig should not match: "); | |
594 | goto end; | |
595 | } | |
596 | ||
597 | result = 1; | |
598 | end: | |
599 | SigGroupCleanup(de_ctx); | |
600 | SigCleanSignatures(de_ctx); | |
601 | ||
602 | DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx); | |
603 | DetectEngineCtxFree(de_ctx); | |
604 | ||
605 | FlowL7DataPtrFree(&f); | |
606 | StreamTcpFreeConfig(TRUE); | |
607 | FLOW_DESTROY(&f); | |
608 | ||
609 | UTHFreePackets(&p, 1); | |
610 | return result; | |
611 | } | |
612 | ||
613 | #endif /* UNITTESTS */ | |
614 | ||
615 | /** | |
616 | * \brief this function registers unit tests for DetectSshSoftwareVersion | |
617 | */ | |
618 | void DetectSshSoftwareVersionRegisterTests(void) { | |
619 | #ifdef UNITTESTS /* UNITTESTS */ | |
620 | UtRegisterTest("DetectSshSoftwareVersionTestParse01", DetectSshSoftwareVersionTestParse01, 1); | |
621 | UtRegisterTest("DetectSshSoftwareVersionTestParse02", DetectSshSoftwareVersionTestParse02, 1); | |
622 | UtRegisterTest("DetectSshSoftwareVersionTestParse03", DetectSshSoftwareVersionTestParse03, 1); | |
623 | UtRegisterTest("DetectSshSoftwareVersionTestDetect01", DetectSshSoftwareVersionTestDetect01, 1); | |
624 | UtRegisterTest("DetectSshSoftwareVersionTestDetect02", DetectSshSoftwareVersionTestDetect02, 1); | |
625 | UtRegisterTest("DetectSshSoftwareVersionTestDetect03", DetectSshSoftwareVersionTestDetect03, 1); | |
626 | #endif /* UNITTESTS */ | |
627 | } | |
628 |
0 | /* Copyright (C) 2007-2010 Open Information Security Foundation | |
1 | * | |
2 | * You can copy, redistribute or modify this Program under the terms of | |
3 | * the GNU General Public License version 2 as published by the Free | |
4 | * Software Foundation. | |
5 | * | |
6 | * This program is distributed in the hope that it will be useful, | |
7 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
8 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
9 | * GNU General Public License for more details. | |
10 | * | |
11 | * You should have received a copy of the GNU General Public License | |
12 | * version 2 along with this program; if not, write to the Free Software | |
13 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA | |
14 | * 02110-1301, USA. | |
15 | */ | |
16 | ||
17 | /** | |
18 | * \file | |
19 | * | |
20 | * \author Pablo Rincon <pablo.rincon.crespo@gmail.com> | |
21 | */ | |
22 | ||
23 | #ifndef __DETECT_SSH_SOFTWARE_VERSION_H__ | |
24 | #define __DETECT_SSH_SOFTWARE_VERSION_H__ | |
25 | ||
26 | typedef struct DetectSshSoftwareVersionData_ { | |
27 | uint8_t *software_ver; /** ssh version to match */ | |
28 | uint16_t len; /** ssh version length to match */ | |
29 | } DetectSshSoftwareVersionData; | |
30 | ||
31 | /* prototypes */ | |
32 | void DetectSshSoftwareVersionRegister(void); | |
33 | void DetectSshSoftwareVersionRegisterTests(void); | |
34 | ||
35 | #endif /* __DETECT_SSH_SOFTWARE_VERSION_H__ */ | |
36 |
155 | 155 | &s->pmatch, &s->pmatch_tail, |
156 | 156 | &s->dmatch, &s->dmatch_tail); |
157 | 157 | pm = pm1; |
158 | } else if (pm2_ots->idx > dcem->idx) { | |
158 | } else { | |
159 | 159 | /* within is against pm1, pm = pm1 */ |
160 | 160 | pm = pm1; |
161 | 161 | } |
50 | 50 | #include "detect-http-method.h" |
51 | 51 | |
52 | 52 | #include "detect-decode-event.h" |
53 | #include "decode.h" | |
53 | 54 | |
54 | 55 | #include "detect-ipopts.h" |
55 | 56 | #include "detect-flags.h" |
118 | 119 | #include "app-layer-protos.h" |
119 | 120 | #include "app-layer-htp.h" |
120 | 121 | #include "detect-tls-version.h" |
122 | #include "detect-ssh-proto-version.h" | |
123 | #include "detect-ssh-software-version.h" | |
121 | 124 | |
122 | 125 | #include "action-globals.h" |
123 | 126 | #include "tm-modules.h" |
143 | 146 | #include "util-privs.h" |
144 | 147 | #include "util-profiling.h" |
145 | 148 | #include "util-validate.h" |
149 | ||
150 | extern uint8_t engine_mode; | |
146 | 151 | |
147 | 152 | SigMatch *SigMatchAlloc(void); |
148 | 153 | void DetectExitPrintStats(ThreadVars *tv, void *data); |
688 | 693 | */ |
689 | 694 | int SigMatchSignatures(ThreadVars *th_v, DetectEngineCtx *de_ctx, DetectEngineThreadCtx *det_ctx, Packet *p) |
690 | 695 | { |
696 | ||
697 | ||
698 | /* No need to perform any detection on this packet, if the the given flag is set.*/ | |
699 | if (p->flags & PKT_NOPACKET_INSPECTION) | |
700 | return 0; | |
701 | ||
691 | 702 | int match = 0, fmatch = 0; |
692 | 703 | Signature *s = NULL; |
693 | 704 | SigMatch *sm = NULL; |
700 | 711 | char use_flow_sgh = FALSE; |
701 | 712 | StreamMsg *smsg = NULL; |
702 | 713 | char no_store_flow_sgh = FALSE; |
714 | uint8_t alert_flags = 0; | |
703 | 715 | |
704 | 716 | SCEnter(); |
705 | 717 | |
779 | 791 | /* if it matched a "pass" rule, we have to let it go */ |
780 | 792 | p->action |= ACTION_PASS; |
781 | 793 | } |
782 | if (p->flow->flags & FLOW_ACTION_DROP) p->action |= ACTION_DROP; | |
794 | /* If we have a drop from IP only module, | |
795 | * we will drop the rest of the flow packets | |
796 | * This will apply only to inline/IPS */ | |
797 | if (p->flow != NULL && | |
798 | (p->flow->flags & FLOW_ACTION_DROP)) | |
799 | { | |
800 | alert_flags = PACKET_ALERT_FLAG_DROP_FLOW; | |
801 | p->action |= ACTION_DROP; | |
802 | } | |
783 | 803 | } else { |
784 | 804 | /* Even without flow we should match the packet src/dst */ |
785 | 805 | IPOnlyMatchPacket(de_ctx, det_ctx, &de_ctx->io_ctx, &det_ctx->io_ctx, p); |
939 | 959 | if (DetectEngineInspectStreamPayload(de_ctx, det_ctx, s, p->flow, smsg_inspect->data.data, smsg_inspect->data.data_len) == 1) { |
940 | 960 | SCLogDebug("match in smsg %p", smsg); |
941 | 961 | pmatch = 1; |
962 | /* Tell the enigne that this reassembled stream can drop the | |
963 | * rest of the pkts with no further inspection */ | |
964 | if (s->action == ACTION_DROP) | |
965 | alert_flags |= PACKET_ALERT_FLAG_DROP_FLOW; | |
942 | 966 | break; |
943 | 967 | } |
944 | 968 | } |
969 | 993 | if (det_ctx->de_state_sig_array[s->num] == DE_STATE_MATCH_NOSTATE) { |
970 | 994 | SCLogDebug("stateful app layer match inspection starting"); |
971 | 995 | if (DeStateDetectStartDetection(th_v, de_ctx, det_ctx, s, |
972 | p->flow, flags, alstate, alproto) != 1) | |
996 | p->flow, flags, alstate, alproto) != 1) { | |
973 | 997 | goto next; |
998 | } else { | |
999 | if (s->action == ACTION_DROP) | |
1000 | alert_flags |= PACKET_ALERT_FLAG_DROP_FLOW; | |
1001 | } | |
974 | 1002 | } else { |
975 | 1003 | SCLogDebug("already having a destate"); |
976 | 1004 | |
978 | 1006 | s->id, (uintmax_t)s->num, DeStateMatchResultToString(det_ctx->de_state_sig_array[s->num])); |
979 | 1007 | if (det_ctx->de_state_sig_array[s->num] != DE_STATE_MATCH_NEW) { |
980 | 1008 | goto next; |
1009 | } else { | |
1010 | if (s->action == ACTION_DROP) | |
1011 | alert_flags |= PACKET_ALERT_FLAG_DROP_FLOW; | |
981 | 1012 | } |
982 | 1013 | } |
983 | 1014 | } |
989 | 1020 | |
990 | 1021 | fmatch = 1; |
991 | 1022 | if (!(s->flags & SIG_FLAG_NOALERT)) { |
992 | PacketAlertAppend(det_ctx, s, p); | |
1023 | PacketAlertAppend(det_ctx, s, p, alert_flags); | |
993 | 1024 | } |
994 | 1025 | } else { |
995 | 1026 | if (s->flags & SIG_FLAG_RECURSIVE) { |
1009 | 1040 | if (!(s->flags & SIG_FLAG_NOALERT)) { |
1010 | 1041 | /* only add once */ |
1011 | 1042 | if (rmatch == 0) { |
1012 | PacketAlertAppend(det_ctx, s, p); | |
1043 | PacketAlertAppend(det_ctx, s, p, alert_flags); | |
1013 | 1044 | } |
1014 | 1045 | } |
1015 | 1046 | rmatch = fmatch = 1; |
1042 | 1073 | if (sm == NULL) { |
1043 | 1074 | fmatch = 1; |
1044 | 1075 | if (!(s->flags & SIG_FLAG_NOALERT)) { |
1045 | PacketAlertAppend(det_ctx, s, p); | |
1076 | PacketAlertAppend(det_ctx, s, p, alert_flags); | |
1046 | 1077 | } |
1047 | 1078 | } |
1048 | 1079 | } else { |
3355 | 3386 | DetectHttpClientBodyRegister(); |
3356 | 3387 | DetectHttpUriRegister(); |
3357 | 3388 | DetectAsn1Register(); |
3389 | DetectSshVersionRegister(); | |
3390 | DetectSshSoftwareVersionRegister(); | |
3358 | 3391 | |
3359 | 3392 | uint8_t i = 0; |
3360 | 3393 | for (i = 0; i < DETECT_TBLSIZE; i++) { |
5087 | 5120 | |
5088 | 5121 | Packet p1, p2; |
5089 | 5122 | ThreadVars th_v; |
5090 | DetectEngineThreadCtx *det_ctx; | |
5123 | DetectEngineThreadCtx *det_ctx = NULL; | |
5091 | 5124 | int result = 0; |
5092 | 5125 | |
5093 | 5126 | uint8_t *buf = (uint8_t *)"GET /one/ HTTP/1.0\r\n" |
8823 | 8856 | return result; |
8824 | 8857 | } |
8825 | 8858 | |
8859 | /** \test test if the engine set flag to drop pkts of a flow that | |
8860 | * triggered a drop action on IPS mode */ | |
8861 | static int SigTestDropFlow01(void) | |
8862 | { | |
8863 | int result = 0; | |
8864 | Flow f; | |
8865 | HtpState *http_state = NULL; | |
8866 | uint8_t http_buf1[] = "POST /one HTTP/1.0\r\n" | |
8867 | "User-Agent: Mozilla/1.0\r\n" | |
8868 | "Cookie: hellocatch\r\n\r\n"; | |
8869 | uint32_t http_buf1_len = sizeof(http_buf1) - 1; | |
8870 | TcpSession ssn; | |
8871 | Packet *p = NULL; | |
8872 | Signature *s = NULL; | |
8873 | ThreadVars tv; | |
8874 | DetectEngineThreadCtx *det_ctx = NULL; | |
8875 | ||
8876 | memset(&tv, 0, sizeof(ThreadVars)); | |
8877 | memset(&f, 0, sizeof(Flow)); | |
8878 | memset(&ssn, 0, sizeof(TcpSession)); | |
8879 | ||
8880 | p = UTHBuildPacket(NULL, 0, IPPROTO_TCP); | |
8881 | ||
8882 | FLOW_INITIALIZE(&f); | |
8883 | f.protoctx = (void *)&ssn; | |
8884 | f.src.family = AF_INET; | |
8885 | f.dst.family = AF_INET; | |
8886 | ||
8887 | p->flow = &f; | |
8888 | p->flowflags |= FLOW_PKT_TOSERVER; | |
8889 | p->flowflags |= FLOW_PKT_ESTABLISHED; | |
8890 | f.alproto = ALPROTO_HTTP; | |
8891 | ||
8892 | StreamTcpInitConfig(TRUE); | |
8893 | FlowL7DataPtrInit(&f); | |
8894 | ||
8895 | DetectEngineCtx *de_ctx = DetectEngineCtxInit(); | |
8896 | if (de_ctx == NULL) { | |
8897 | goto end; | |
8898 | } | |
8899 | de_ctx->mpm_matcher = MPM_B2G; | |
8900 | de_ctx->flags |= DE_QUIET; | |
8901 | ||
8902 | s = de_ctx->sig_list = SigInit(de_ctx, "drop http any any -> any any " | |
8903 | "(msg:\"Test proto match\"; " | |
8904 | "sid:1;)"); | |
8905 | if (s == NULL) { | |
8906 | goto end; | |
8907 | } | |
8908 | ||
8909 | SigGroupBuild(de_ctx); | |
8910 | DetectEngineThreadCtxInit(&tv, (void *)de_ctx, (void *)&det_ctx); | |
8911 | ||
8912 | int r = AppLayerParse(&f, ALPROTO_HTTP, STREAM_TOSERVER, http_buf1, http_buf1_len); | |
8913 | if (r != 0) { | |
8914 | printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r); | |
8915 | goto end; | |
8916 | } | |
8917 | ||
8918 | http_state = f.aldata[AlpGetStateIdx(ALPROTO_HTTP)]; | |
8919 | if (http_state == NULL) { | |
8920 | printf("no http state: "); | |
8921 | goto end; | |
8922 | } | |
8923 | ||
8924 | /* do detect */ | |
8925 | SigMatchSignatures(&tv, de_ctx, det_ctx, p); | |
8926 | ||
8927 | if (!PacketAlertCheck(p, 1)) { | |
8928 | printf("sig 1 didn't alert, but it should: "); | |
8929 | goto end; | |
8930 | } | |
8931 | ||
8932 | if ( !(p->flow->flags & FLOW_ACTION_DROP)) { | |
8933 | printf("sig 1 alerted but flow was not flagged correctly: "); | |
8934 | goto end; | |
8935 | } | |
8936 | ||
8937 | /* Ok, now we know that the flag is set for proto http */ | |
8938 | ||
8939 | result = 1; | |
8940 | ||
8941 | end: | |
8942 | if (det_ctx != NULL) | |
8943 | DetectEngineThreadCtxDeinit(&tv, det_ctx); | |
8944 | if (de_ctx != NULL) | |
8945 | SigGroupCleanup(de_ctx); | |
8946 | if (de_ctx != NULL) | |
8947 | DetectEngineCtxFree(de_ctx); | |
8948 | ||
8949 | StreamTcpFreeConfig(TRUE); | |
8950 | FLOW_DESTROY(&f); | |
8951 | ||
8952 | UTHFreePackets(&p, 1); | |
8953 | return result; | |
8954 | } | |
8955 | ||
8956 | /** \test test if the engine set flag to drop pkts of a flow that | |
8957 | * triggered a drop action on IPS mode */ | |
8958 | static int SigTestDropFlow02(void) | |
8959 | { | |
8960 | int result = 0; | |
8961 | Flow f; | |
8962 | HtpState *http_state = NULL; | |
8963 | uint8_t http_buf1[] = "POST /one HTTP/1.0\r\n" | |
8964 | "User-Agent: Mozilla/1.0\r\n" | |
8965 | "Cookie: hellocatch\r\n\r\n"; | |
8966 | uint32_t http_buf1_len = sizeof(http_buf1) - 1; | |
8967 | TcpSession ssn; | |
8968 | Packet *p = NULL; | |
8969 | Signature *s = NULL; | |
8970 | ThreadVars tv; | |
8971 | DetectEngineThreadCtx *det_ctx = NULL; | |
8972 | ||
8973 | memset(&tv, 0, sizeof(ThreadVars)); | |
8974 | memset(&f, 0, sizeof(Flow)); | |
8975 | memset(&ssn, 0, sizeof(TcpSession)); | |
8976 | ||
8977 | p = UTHBuildPacket(NULL, 0, IPPROTO_TCP); | |
8978 | ||
8979 | FLOW_INITIALIZE(&f); | |
8980 | f.protoctx = (void *)&ssn; | |
8981 | f.src.family = AF_INET; | |
8982 | f.dst.family = AF_INET; | |
8983 | ||
8984 | p->flow = &f; | |
8985 | p->flowflags |= FLOW_PKT_TOSERVER; | |
8986 | p->flowflags |= FLOW_PKT_ESTABLISHED; | |
8987 | f.alproto = ALPROTO_HTTP; | |
8988 | ||
8989 | StreamTcpInitConfig(TRUE); | |
8990 | FlowL7DataPtrInit(&f); | |
8991 | ||
8992 | DetectEngineCtx *de_ctx = DetectEngineCtxInit(); | |
8993 | if (de_ctx == NULL) { | |
8994 | goto end; | |
8995 | } | |
8996 | de_ctx->mpm_matcher = MPM_B2G; | |
8997 | de_ctx->flags |= DE_QUIET; | |
8998 | ||
8999 | s = de_ctx->sig_list = SigInit(de_ctx, "drop tcp any any -> any 80 " | |
9000 | "(msg:\"Test proto match\"; uricontent:\"one\";" | |
9001 | "sid:1;)"); | |
9002 | if (s == NULL) { | |
9003 | goto end; | |
9004 | } | |
9005 | ||
9006 | SigGroupBuild(de_ctx); | |
9007 | DetectEngineThreadCtxInit(&tv, (void *)de_ctx, (void *)&det_ctx); | |
9008 | ||
9009 | int r = AppLayerParse(&f, ALPROTO_HTTP, STREAM_TOSERVER, http_buf1, http_buf1_len); | |
9010 | if (r != 0) { | |
9011 | printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r); | |
9012 | goto end; | |
9013 | } | |
9014 | ||
9015 | http_state = f.aldata[AlpGetStateIdx(ALPROTO_HTTP)]; | |
9016 | if (http_state == NULL) { | |
9017 | printf("no http state: "); | |
9018 | goto end; | |
9019 | } | |
9020 | ||
9021 | /* do detect */ | |
9022 | SigMatchSignatures(&tv, de_ctx, det_ctx, p); | |
9023 | ||
9024 | if (!PacketAlertCheck(p, 1)) { | |
9025 | printf("sig 1 didn't alert, but it should: "); | |
9026 | goto end; | |
9027 | } | |
9028 | ||
9029 | if ( !(p->flow->flags & FLOW_ACTION_DROP)) { | |
9030 | printf("sig 1 alerted but flow was not flagged correctly: "); | |
9031 | goto end; | |
9032 | } | |
9033 | ||
9034 | /* Ok, now we know that the flag is set for app layer sigs | |
9035 | * (ex: inspecting uricontent) */ | |
9036 | ||
9037 | result = 1; | |
9038 | ||
9039 | end: | |
9040 | if (det_ctx != NULL) | |
9041 | DetectEngineThreadCtxDeinit(&tv, det_ctx); | |
9042 | if (de_ctx != NULL) | |
9043 | SigGroupCleanup(de_ctx); | |
9044 | if (de_ctx != NULL) | |
9045 | DetectEngineCtxFree(de_ctx); | |
9046 | ||
9047 | StreamTcpFreeConfig(TRUE); | |
9048 | FLOW_DESTROY(&f); | |
9049 | ||
9050 | UTHFreePackets(&p, 1); | |
9051 | return result; | |
9052 | } | |
9053 | ||
9054 | /** \test test if the engine set flag to drop pkts of a flow that | |
9055 | * triggered a drop action on IPS mode, and it doesn't inspect | |
9056 | * any other packet of the stream */ | |
9057 | static int SigTestDropFlow03(void) | |
9058 | { | |
9059 | int result = 0; | |
9060 | Flow f; | |
9061 | HtpState *http_state = NULL; | |
9062 | uint8_t http_buf1[] = "POST /one HTTP/1.0\r\n" | |
9063 | "User-Agent: Mozilla/1.0\r\n" | |
9064 | "Cookie: hellocatch\r\n\r\n"; | |
9065 | uint32_t http_buf1_len = sizeof(http_buf1) - 1; | |
9066 | ||
9067 | uint8_t http_buf2[] = "POST /two HTTP/1.0\r\n" | |
9068 | "User-Agent: Mozilla/1.0\r\n" | |
9069 | "Cookie: hellocatch\r\n\r\n"; | |
9070 | uint32_t http_buf2_len = sizeof(http_buf1) - 1; | |
9071 | ||
9072 | /* Set the engine mode to IPS */ | |
9073 | SET_ENGINE_MODE_IPS(engine_mode); | |
9074 | ||
9075 | TcpSession ssn; | |
9076 | Packet *p1 = NULL; | |
9077 | Packet *p2 = NULL; | |
9078 | Signature *s = NULL; | |
9079 | ThreadVars tv; | |
9080 | DetectEngineThreadCtx *det_ctx = NULL; | |
9081 | ||
9082 | memset(&tv, 0, sizeof(ThreadVars)); | |
9083 | memset(&f, 0, sizeof(Flow)); | |
9084 | memset(&ssn, 0, sizeof(TcpSession)); | |
9085 | ||
9086 | p1 = UTHBuildPacket(NULL, 0, IPPROTO_TCP); | |
9087 | p2 = UTHBuildPacket(NULL, 0, IPPROTO_TCP); | |
9088 | ||
9089 | FLOW_INITIALIZE(&f); | |
9090 | f.protoctx = (void *)&ssn; | |
9091 | f.src.family = AF_INET; | |
9092 | f.dst.family = AF_INET; | |
9093 | ||
9094 | p1->flow = &f; | |
9095 | p1->flowflags |= FLOW_PKT_TOSERVER; | |
9096 | p1->flowflags |= FLOW_PKT_ESTABLISHED; | |
9097 | ||
9098 | p2->flow = &f; | |
9099 | p2->flowflags |= FLOW_PKT_TOSERVER; | |
9100 | p2->flowflags |= FLOW_PKT_ESTABLISHED; | |
9101 | f.alproto = ALPROTO_HTTP; | |
9102 | ||
9103 | StreamTcpInitConfig(TRUE); | |
9104 | FlowL7DataPtrInit(&f); | |
9105 | ||
9106 | DetectEngineCtx *de_ctx = DetectEngineCtxInit(); | |
9107 | if (de_ctx == NULL) { | |
9108 | goto end; | |
9109 | } | |
9110 | ||
9111 | de_ctx->mpm_matcher = MPM_B2G; | |
9112 | de_ctx->flags |= DE_QUIET; | |
9113 | ||
9114 | s = de_ctx->sig_list = SigInit(de_ctx, "drop tcp any any -> any 80 " | |
9115 | "(msg:\"Test proto match\"; uricontent:\"one\";" | |
9116 | "sid:1;)"); | |
9117 | if (s == NULL) { | |
9118 | goto end; | |
9119 | } | |
9120 | ||
9121 | /* the no inspection flag should be set after the first sig gets triggered, | |
9122 | * so the second packet should not match the next sig (because of no inspection) */ | |
9123 | s = de_ctx->sig_list->next = SigInit(de_ctx, "alert tcp any any -> any 80 " | |
9124 | "(msg:\"Test proto match\"; uricontent:\"two\";" | |
9125 | "sid:2;)"); | |
9126 | if (s == NULL) { | |
9127 | goto end; | |
9128 | } | |
9129 | ||
9130 | SigGroupBuild(de_ctx); | |
9131 | DetectEngineThreadCtxInit(&tv, (void *)de_ctx, (void *)&det_ctx); | |
9132 | ||
9133 | int r = AppLayerParse(&f, ALPROTO_HTTP, STREAM_TOSERVER, http_buf1, http_buf1_len); | |
9134 | if (r != 0) { | |
9135 | printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r); | |
9136 | goto end; | |
9137 | } | |
9138 | ||
9139 | http_state = f.aldata[AlpGetStateIdx(ALPROTO_HTTP)]; | |
9140 | if (http_state == NULL) { | |
9141 | printf("no http state: "); | |
9142 | goto end; | |
9143 | } | |
9144 | ||
9145 | /* do detect */ | |
9146 | SigMatchSignatures(&tv, de_ctx, det_ctx, p1); | |
9147 | ||
9148 | if (!PacketAlertCheck(p1, 1)) { | |
9149 | printf("sig 1 didn't alert on p1, but it should: "); | |
9150 | goto end; | |
9151 | } | |
9152 | ||
9153 | if ( !(p1->flow->flags & FLOW_ACTION_DROP)) { | |
9154 | printf("sig 1 alerted but flow was not flagged correctly: "); | |
9155 | goto end; | |
9156 | } | |
9157 | ||
9158 | /* Second part.. Let's feed with another packet */ | |
9159 | if (StreamTcpCheckFlowDrops(p2) == 1) { | |
9160 | SCLogDebug("This flow/stream triggered a drop rule"); | |
9161 | FlowSetNoPacketInspectionFlag(p2->flow); | |
9162 | DecodeSetNoPacketInspectionFlag(p2); | |
9163 | FlowSetSessionNoApplayerInspectionFlag(p2->flow); | |
9164 | p2->action |= ACTION_DROP; | |
9165 | /* return the segments to the pool */ | |
9166 | StreamTcpSessionPktFree(p2); | |
9167 | } | |
9168 | ||
9169 | ||
9170 | if ( !(p2->flags & PKT_NOPACKET_INSPECTION)) { | |
9171 | printf("The packet was not flagged with no-inspection: "); | |
9172 | goto end; | |
9173 | } | |
9174 | ||
9175 | r = AppLayerParse(&f, ALPROTO_HTTP, STREAM_TOSERVER, http_buf2, http_buf2_len); | |
9176 | if (r != 0) { | |
9177 | printf("toserver chunk 2 returned %" PRId32 ", expected 0: ", r); | |
9178 | goto end; | |
9179 | } | |
9180 | ||
9181 | /* do detect */ | |
9182 | SigMatchSignatures(&tv, de_ctx, det_ctx, p2); | |
9183 | ||
9184 | if (PacketAlertCheck(p2, 1)) { | |
9185 | printf("sig 1 alerted, but it should not since the no pkt inspection should be set: "); | |
9186 | goto end; | |
9187 | } | |
9188 | ||
9189 | if (PacketAlertCheck(p2, 2)) { | |
9190 | printf("sig 2 alerted, but it should not since the no pkt inspection should be set: "); | |
9191 | goto end; | |
9192 | } | |
9193 | ||
9194 | if ( !(p2->action & ACTION_DROP)) { | |
9195 | printf("A \"drop\" action should be set from the flow to the packet: "); | |
9196 | goto end; | |
9197 | } | |
9198 | ||
9199 | result = 1; | |
9200 | ||
9201 | end: | |
9202 | if (det_ctx != NULL) | |
9203 | DetectEngineThreadCtxDeinit(&tv, det_ctx); | |
9204 | if (de_ctx != NULL) | |
9205 | SigGroupCleanup(de_ctx); | |
9206 | if (de_ctx != NULL) | |
9207 | DetectEngineCtxFree(de_ctx); | |
9208 | ||
9209 | StreamTcpFreeConfig(TRUE); | |
9210 | FLOW_DESTROY(&f); | |
9211 | ||
9212 | UTHFreePackets(&p1, 1); | |
9213 | UTHFreePackets(&p2, 1); | |
9214 | ||
9215 | /* Restore mode to IDS */ | |
9216 | SET_ENGINE_MODE_IDS(engine_mode); | |
9217 | return result; | |
9218 | } | |
9219 | ||
9220 | /** \test test if the engine set flag to drop pkts of a flow that | |
9221 | * triggered a drop action on IDS mode, but continue the inspection | |
9222 | * as usual (instead of on IPS mode) */ | |
9223 | static int SigTestDropFlow04(void) | |
9224 | { | |
9225 | int result = 0; | |
9226 | Flow f; | |
9227 | HtpState *http_state = NULL; | |
9228 | uint8_t http_buf1[] = "POST /one HTTP/1.0\r\n" | |
9229 | "User-Agent: Mozilla/1.0\r\n" | |
9230 | "Cookie: hellocatch\r\n\r\n"; | |
9231 | uint32_t http_buf1_len = sizeof(http_buf1) - 1; | |
9232 | ||
9233 | uint8_t http_buf2[] = "POST /two HTTP/1.0\r\n" | |
9234 | "User-Agent: Mozilla/1.0\r\n" | |
9235 | "Cookie: hellocatch\r\n\r\n"; | |
9236 | uint32_t http_buf2_len = sizeof(http_buf1) - 1; | |
9237 | ||
9238 | TcpSession ssn; | |
9239 | Packet *p1 = NULL; | |
9240 | Packet *p2 = NULL; | |
9241 | Signature *s = NULL; | |
9242 | ThreadVars tv; | |
9243 | DetectEngineThreadCtx *det_ctx = NULL; | |
9244 | ||
9245 | memset(&tv, 0, sizeof(ThreadVars)); | |
9246 | memset(&f, 0, sizeof(Flow)); | |
9247 | memset(&ssn, 0, sizeof(TcpSession)); | |
9248 | ||
9249 | p1 = UTHBuildPacket(NULL, 0, IPPROTO_TCP); | |
9250 | p2 = UTHBuildPacket(NULL, 0, IPPROTO_TCP); | |
9251 | ||
9252 | FLOW_INITIALIZE(&f); | |
9253 | f.protoctx = (void *)&ssn; | |
9254 | f.src.family = AF_INET; | |
9255 | f.dst.family = AF_INET; | |
9256 | ||
9257 | p1->flow = &f; | |
9258 | p1->flowflags |= FLOW_PKT_TOSERVER; | |
9259 | p1->flowflags |= FLOW_PKT_ESTABLISHED; | |
9260 | ||
9261 | p2->flow = &f; | |
9262 | p2->flowflags |= FLOW_PKT_TOSERVER; | |
9263 | p2->flowflags |= FLOW_PKT_ESTABLISHED; | |
9264 | f.alproto = ALPROTO_HTTP; | |
9265 | ||
9266 | StreamTcpInitConfig(TRUE); | |
9267 | FlowL7DataPtrInit(&f); | |
9268 | ||
9269 | DetectEngineCtx *de_ctx = DetectEngineCtxInit(); | |
9270 | if (de_ctx == NULL) { | |
9271 | goto end; | |
9272 | } | |
9273 | de_ctx->mpm_matcher = MPM_B2G; | |
9274 | de_ctx->flags |= DE_QUIET; | |
9275 | ||
9276 | s = de_ctx->sig_list = SigInit(de_ctx, "drop tcp any any -> any 80 " | |
9277 | "(msg:\"Test proto match\"; uricontent:\"one\";" | |
9278 | "sid:1;)"); | |
9279 | if (s == NULL) { | |
9280 | goto end; | |
9281 | } | |
9282 | ||
9283 | /* the no inspection flag should be set after the first sig gets triggered, | |
9284 | * so the second packet should not match the next sig (because of no inspection) */ | |
9285 | s = de_ctx->sig_list->next = SigInit(de_ctx, "alert tcp any any -> any 80 " | |
9286 | "(msg:\"Test proto match\"; uricontent:\"two\";" | |
9287 | "sid:2;)"); | |
9288 | if (s == NULL) { | |
9289 | goto end; | |
9290 | } | |
9291 | ||
9292 | SigGroupBuild(de_ctx); | |
9293 | DetectEngineThreadCtxInit(&tv, (void *)de_ctx, (void *)&det_ctx); | |
9294 | ||
9295 | int r = AppLayerParse(&f, ALPROTO_HTTP, STREAM_TOSERVER, http_buf1, http_buf1_len); | |
9296 | if (r != 0) { | |
9297 | printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r); | |
9298 | goto end; | |
9299 | } | |
9300 | ||
9301 | http_state = f.aldata[AlpGetStateIdx(ALPROTO_HTTP)]; | |
9302 | if (http_state == NULL) { | |
9303 | printf("no http state: "); | |
9304 | goto end; | |
9305 | } | |
9306 | ||
9307 | /* do detect */ | |
9308 | SigMatchSignatures(&tv, de_ctx, det_ctx, p1); | |
9309 | ||
9310 | if (!PacketAlertCheck(p1, 1)) { | |
9311 | printf("sig 1 didn't alert on p1, but it should: "); | |
9312 | goto end; | |
9313 | } | |
9314 | ||
9315 | if ( !(p1->flow->flags & FLOW_ACTION_DROP)) { | |
9316 | printf("sig 1 alerted but flow was not flagged correctly: "); | |
9317 | goto end; | |
9318 | } | |
9319 | ||
9320 | /* Second part.. Let's feed with another packet */ | |
9321 | if (StreamTcpCheckFlowDrops(p2) == 1) { | |
9322 | FlowSetNoPacketInspectionFlag(p2->flow); | |
9323 | DecodeSetNoPacketInspectionFlag(p2); | |
9324 | FlowSetSessionNoApplayerInspectionFlag(p2->flow); | |
9325 | p2->action |= ACTION_DROP; | |
9326 | /* return the segments to the pool */ | |
9327 | StreamTcpSessionPktFree(p2); | |
9328 | } | |
9329 | ||
9330 | if ( (p2->flags & PKT_NOPACKET_INSPECTION)) { | |
9331 | printf("The packet was flagged with no-inspection but we are not on IPS mode: "); | |
9332 | goto end; | |
9333 | } | |
9334 | ||
9335 | r = AppLayerParse(&f, ALPROTO_HTTP, STREAM_TOSERVER, http_buf2, http_buf2_len); | |
9336 | if (r != 0) { | |
9337 | printf("toserver chunk 2 returned %" PRId32 ", expected 0: ", r); | |
9338 | goto end; | |
9339 | } | |
9340 | ||
9341 | /* do detect */ | |
9342 | SigMatchSignatures(&tv, de_ctx, det_ctx, p2); | |
9343 | ||
9344 | if (PacketAlertCheck(p2, 1)) { | |
9345 | printf("sig 1 alerted, but it should not since the no pkt inspection should be set: "); | |
9346 | goto end; | |
9347 | } | |
9348 | ||
9349 | if (!PacketAlertCheck(p2, 2)) { | |
9350 | printf("sig 2 didn't alert, but it should, since we are not on IPS mode: "); | |
9351 | goto end; | |
9352 | } | |
9353 | ||
9354 | if (p2->action & ACTION_DROP) { | |
9355 | printf("A \"drop\" action was set from the flow to the packet, but on IDS mode it whould not (it should be inspected as usual: "); | |
9356 | goto end; | |
9357 | } | |
9358 | ||
9359 | result = 1; | |
9360 | ||
9361 | end: | |
9362 | if (det_ctx != NULL) | |
9363 | DetectEngineThreadCtxDeinit(&tv, det_ctx); | |
9364 | if (de_ctx != NULL) | |
9365 | SigGroupCleanup(de_ctx); | |
9366 | if (de_ctx != NULL) | |
9367 | DetectEngineCtxFree(de_ctx); | |
9368 | ||
9369 | StreamTcpFreeConfig(TRUE); | |
9370 | FLOW_DESTROY(&f); | |
9371 | ||
9372 | UTHFreePackets(&p1, 1); | |
9373 | UTHFreePackets(&p2, 1); | |
9374 | ||
9375 | return result; | |
9376 | } | |
9377 | ||
8826 | 9378 | #endif /* UNITTESTS */ |
8827 | 9379 | |
8828 | 9380 | void SigRegisterTests(void) { |
9025 | 9577 | |
9026 | 9578 | UtRegisterTest("SigTestDetectAlertCounter", SigTestDetectAlertCounter, 1); |
9027 | 9579 | |
9580 | UtRegisterTest("SigTestDropFlow01", SigTestDropFlow01, 1); | |
9581 | UtRegisterTest("SigTestDropFlow02", SigTestDropFlow02, 1); | |
9582 | UtRegisterTest("SigTestDropFlow03", SigTestDropFlow03, 1); | |
9583 | UtRegisterTest("SigTestDropFlow04", SigTestDropFlow04, 1); | |
9584 | ||
9028 | 9585 | #endif /* UNITTESTS */ |
9029 | 9586 | } |
9030 | 9587 |
741 | 741 | DETECT_URICONTENT, |
742 | 742 | DETECT_PCRE, |
743 | 743 | DETECT_PCRE_HTTPBODY, |
744 | DETECT_PCRE_HTTPCOOKIE, | |
745 | DETECT_PCRE_HTTPHEADER, | |
746 | DETECT_PCRE_HTTPMETHOD, | |
744 | 747 | DETECT_ACK, |
745 | 748 | DETECT_SEQ, |
746 | 749 | DETECT_DEPTH, |
800 | 803 | DETECT_AL_HTTP_CLIENT_BODY, |
801 | 804 | DETECT_AL_HTTP_HEADER, |
802 | 805 | DETECT_AL_HTTP_URI, |
806 | DETECT_AL_SSH_PROTOVERSION, | |
807 | DETECT_AL_SSH_SOFTWAREVERSION, | |
803 | 808 | |
804 | 809 | DETECT_DCE_IFACE, |
805 | 810 | DETECT_DCE_OPNUM, |
1431 | 1431 | return 1; |
1432 | 1432 | } |
1433 | 1433 | |
1434 | /** \brief Set the No Packet Inspection Flag after locking the flow. | |
1435 | * | |
1436 | * \param f Flow to set the flag in | |
1437 | */ | |
1438 | void FlowLockSetNoPacketInspectionFlag(Flow *f) { | |
1439 | SCEnter(); | |
1440 | ||
1441 | SCLogDebug("flow %p", f); | |
1442 | SCMutexLock(&f->m); | |
1443 | f->flags |= FLOW_NOPACKET_INSPECTION; | |
1444 | SCMutexUnlock(&f->m); | |
1445 | ||
1446 | SCReturn; | |
1447 | } | |
1448 | ||
1449 | /** \brief Set the No Packet Inspection Flag without locking the flow. | |
1450 | * | |
1451 | * \param f Flow to set the flag in | |
1452 | */ | |
1453 | void FlowSetNoPacketInspectionFlag(Flow *f) { | |
1454 | SCEnter(); | |
1455 | ||
1456 | SCLogDebug("flow %p", f); | |
1457 | f->flags |= FLOW_NOPACKET_INSPECTION; | |
1458 | ||
1459 | SCReturn; | |
1460 | } | |
1461 | ||
1462 | /** \brief Set the No payload inspection Flag after locking the flow. | |
1463 | * | |
1464 | * \param f Flow to set the flag in | |
1465 | */ | |
1466 | void FlowLockSetNoPayloadInspectionFlag(Flow *f) { | |
1467 | SCEnter(); | |
1468 | ||
1469 | SCLogDebug("flow %p", f); | |
1470 | SCMutexLock(&f->m); | |
1471 | f->flags |= FLOW_NOPAYLOAD_INSPECTION; | |
1472 | SCMutexUnlock(&f->m); | |
1473 | ||
1474 | SCReturn; | |
1475 | } | |
1476 | ||
1477 | /** \brief Set the No payload inspection Flag without locking the flow. | |
1478 | * | |
1479 | * \param f Flow to set the flag in | |
1480 | */ | |
1481 | void FlowSetNoPayloadInspectionFlag(Flow *f) { | |
1482 | SCEnter(); | |
1483 | ||
1484 | SCLogDebug("flow %p", f); | |
1485 | f->flags |= FLOW_NOPAYLOAD_INSPECTION; | |
1486 | ||
1487 | SCReturn; | |
1488 | } | |
1489 | ||
1490 | /** \brief set flow flag to disable app layer inspection | |
1491 | * | |
1492 | * \param f *LOCKED* flow | |
1493 | */ | |
1494 | void FlowSetSessionNoApplayerInspectionFlag(Flow *f) { | |
1495 | f->alflags |= FLOW_AL_NO_APPLAYER_INSPECTION; | |
1496 | } | |
1497 | ||
1498 | 1434 | #ifdef UNITTESTS |
1499 | 1435 | #include "stream-tcp-private.h" |
1500 | 1436 | #include "threads.h" |
253 | 253 | int FlowSetFlowStateFunc (uint8_t , int (*GetProtoState)(void *)); |
254 | 254 | void FlowUpdateQueue(Flow *); |
255 | 255 | |
256 | void FlowLockSetNoPacketInspectionFlag(Flow *); | |
257 | void FlowSetNoPacketInspectionFlag(Flow *); | |
258 | void FlowLockSetNoPayloadInspectionFlag(Flow *); | |
259 | void FlowSetNoPayloadInspectionFlag(Flow *); | |
260 | void FlowSetSessionNoApplayerInspectionFlag(Flow *); | |
256 | static inline void FlowLockSetNoPacketInspectionFlag(Flow *); | |
257 | static inline void FlowSetNoPacketInspectionFlag(Flow *); | |
258 | static inline void FlowLockSetNoPayloadInspectionFlag(Flow *); | |
259 | static inline void FlowSetNoPayloadInspectionFlag(Flow *); | |
260 | static inline void FlowSetSessionNoApplayerInspectionFlag(Flow *); | |
261 | 261 | |
262 | 262 | int FlowGetPacketDirection(Flow *, Packet *); |
263 | 263 | |
264 | 264 | void FlowL7DataPtrInit(Flow *); |
265 | 265 | void FlowL7DataPtrFree(Flow *); |
266 | 266 | |
267 | /** ----- Inline functions ----- */ | |
268 | ||
269 | /** \brief Set the No Packet Inspection Flag after locking the flow. | |
270 | * | |
271 | * \param f Flow to set the flag in | |
272 | */ | |
273 | static inline void FlowLockSetNoPacketInspectionFlag(Flow *f) { | |
274 | SCEnter(); | |
275 | ||
276 | SCLogDebug("flow %p", f); | |
277 | SCMutexLock(&f->m); | |
278 | f->flags |= FLOW_NOPACKET_INSPECTION; | |
279 | SCMutexUnlock(&f->m); | |
280 | ||
281 | SCReturn; | |
282 | } | |
283 | ||
284 | /** \brief Set the No Packet Inspection Flag without locking the flow. | |
285 | * | |
286 | * \param f Flow to set the flag in | |
287 | */ | |
288 | static inline void FlowSetNoPacketInspectionFlag(Flow *f) { | |
289 | SCEnter(); | |
290 | ||
291 | SCLogDebug("flow %p", f); | |
292 | f->flags |= FLOW_NOPACKET_INSPECTION; | |
293 | ||
294 | SCReturn; | |
295 | } | |
296 | ||
297 | /** \brief Set the No payload inspection Flag after locking the flow. | |
298 | * | |
299 | * \param f Flow to set the flag in | |
300 | */ | |
301 | static inline void FlowLockSetNoPayloadInspectionFlag(Flow *f) { | |
302 | SCEnter(); | |
303 | ||
304 | SCLogDebug("flow %p", f); | |
305 | SCMutexLock(&f->m); | |
306 | f->flags |= FLOW_NOPAYLOAD_INSPECTION; | |
307 | SCMutexUnlock(&f->m); | |
308 | ||
309 | SCReturn; | |
310 | } | |
311 | ||
312 | /** \brief Set the No payload inspection Flag without locking the flow. | |
313 | * | |
314 | * \param f Flow to set the flag in | |
315 | */ | |
316 | static inline void FlowSetNoPayloadInspectionFlag(Flow *f) { | |
317 | SCEnter(); | |
318 | ||
319 | SCLogDebug("flow %p", f); | |
320 | f->flags |= FLOW_NOPAYLOAD_INSPECTION; | |
321 | ||
322 | SCReturn; | |
323 | } | |
324 | ||
325 | /** \brief set flow flag to disable app layer inspection | |
326 | * | |
327 | * \param f *LOCKED* flow | |
328 | */ | |
329 | static inline void FlowSetSessionNoApplayerInspectionFlag(Flow *f) { | |
330 | f->alflags |= FLOW_AL_NO_APPLAYER_INSPECTION; | |
331 | } | |
332 | ||
333 | ||
267 | 334 | #endif /* __FLOW_H__ */ |
268 | 335 |
1594 | 1594 | /* if app layer protocol has been detected, then restore the reassembled |
1595 | 1595 | seq. to the value till reassembling has been done and unset the queue |
1596 | 1596 | init flag permanently for this tcp session */ |
1597 | } else if (stream->tmp_ra_base_seq > stream->ra_base_seq) { | |
1597 | } else if (SEQ_GT(stream->tmp_ra_base_seq, stream->ra_base_seq)) { | |
1598 | 1598 | stream->ra_base_seq = stream->tmp_ra_base_seq; |
1599 | 1599 | ra_ctx->stream_q->flags &= ~STREAMQUEUE_FLAG_INIT; |
1600 | 1600 | ra_base_seq = stream->ra_base_seq; |
1605 | 1605 | /* set the ra_bas_seq to stream->ra_base_seq as now app layer protocol |
1606 | 1606 | has been detected */ |
1607 | 1607 | } else { |
1608 | ra_base_seq = stream->ra_base_seq; | |
1608 | if (SEQ_GT(stream->tmp_ra_base_seq, stream->ra_base_seq)) { | |
1609 | stream->ra_base_seq = stream->tmp_ra_base_seq; | |
1610 | ra_base_seq = stream->ra_base_seq; | |
1611 | } else { | |
1612 | ra_base_seq = stream->ra_base_seq; | |
1613 | } | |
1609 | 1614 | } |
1610 | 1615 | |
1611 | 1616 | /* check if we have enough data to send to L7 */ |
2445 | 2450 | * \param stream Reassembled stream returned from the reassembly functions |
2446 | 2451 | */ |
2447 | 2452 | |
2448 | static int StreamTcpCheckStreamContents(uint8_t *stream_policy, uint16_t sp_size, TcpStream *stream) { | |
2453 | int StreamTcpCheckStreamContents(uint8_t *stream_policy, uint16_t sp_size, TcpStream *stream) { | |
2449 | 2454 | TcpSegment *temp; |
2450 | 2455 | uint16_t i = 0; |
2451 | 2456 | uint8_t j; |
5842 | 5847 | ssn.state = TCP_ESTABLISHED; |
5843 | 5848 | |
5844 | 5849 | ssn.server.reassembly_depth = 1048530; |
5850 | stream_config.reassembly_depth = 0; | |
5845 | 5851 | |
5846 | 5852 | TcpStream *s = NULL; |
5847 | 5853 | s = &ssn.server; |
5900 | 5906 | return ret; |
5901 | 5907 | } |
5902 | 5908 | |
5909 | /** | |
5910 | * \test Test to make sure we detect the sequence wrap around and continue | |
5911 | * stream reassembly properly. | |
5912 | * | |
5913 | * \retval On success it returns 1 and on failure 0. | |
5914 | */ | |
5915 | ||
5916 | static int StreamTcpReassembleTest47 (void) { | |
5917 | int ret = 0; | |
5918 | Packet p; | |
5919 | Flow f; | |
5920 | TCPHdr tcph; | |
5921 | Port sp; | |
5922 | Port dp; | |
5923 | Address src; | |
5924 | Address dst; | |
5925 | struct in_addr in; | |
5926 | TcpSession ssn; | |
5927 | ||
5928 | memset(&p, 0, sizeof (Packet)); | |
5929 | memset(&f, 0, sizeof (Flow)); | |
5930 | memset(&tcph, 0, sizeof (TCPHdr)); | |
5931 | memset(&src, 0, sizeof(Address)); | |
5932 | memset(&dst, 0, sizeof(Address)); | |
5933 | memset(&ssn, 0, sizeof(TcpSession)); | |
5934 | ||
5935 | /* prevent L7 from kicking in */ | |
5936 | StreamMsgQueueSetMinInitChunkLen(FLOW_PKT_TOSERVER, 0); | |
5937 | StreamMsgQueueSetMinInitChunkLen(FLOW_PKT_TOCLIENT, 0); | |
5938 | StreamMsgQueueSetMinChunkLen(FLOW_PKT_TOSERVER, 0); | |
5939 | StreamMsgQueueSetMinChunkLen(FLOW_PKT_TOCLIENT, 0); | |
5940 | ||
5941 | FLOW_INITIALIZE(&f); | |
5942 | StreamTcpInitConfig(TRUE); | |
5943 | TcpReassemblyThreadCtx *ra_ctx = StreamTcpReassembleInitThreadCtx(); | |
5944 | AppLayerDetectProtoThreadInit(); | |
5945 | ||
5946 | uint8_t httpbuf1[] = "GET /EVILSUFF HTTP/1.1\r\n\r\n"; | |
5947 | uint32_t httplen1 = sizeof(httpbuf1) - 1; /* minus the \0 */ | |
5948 | ||
5949 | inet_pton(AF_INET, "1.2.3.4", &in); | |
5950 | src.family = AF_INET; | |
5951 | src.addr_data32[0] = in.s_addr; | |
5952 | inet_pton(AF_INET, "1.2.3.5", &in); | |
5953 | dst.family = AF_INET; | |
5954 | dst.addr_data32[0] = in.s_addr; | |
5955 | sp = 200; | |
5956 | dp = 220; | |
5957 | ||
5958 | ssn.server.ra_base_seq = 572799781UL; | |
5959 | ssn.server.isn = 572799781UL; | |
5960 | ssn.server.last_ack = 572799782UL; | |
5961 | ssn.client.ra_base_seq = 4294967289UL; | |
5962 | ssn.client.isn = 4294967289UL; | |
5963 | ssn.client.last_ack = 21; | |
5964 | f.alproto = ALPROTO_UNKNOWN; | |
5965 | ||
5966 | f.src = src; | |
5967 | f.dst = dst; | |
5968 | f.sp = sp; | |
5969 | f.dp = dp; | |
5970 | f.protoctx = &ssn; | |
5971 | p.flow = &f; | |
5972 | tcph.th_win = htons(5480); | |
5973 | ssn.state = TCP_ESTABLISHED; | |
5974 | TcpStream *s = NULL; | |
5975 | uint8_t cnt = 0; | |
5976 | ||
5977 | for (cnt=0; cnt < httplen1; cnt++) { | |
5978 | tcph.th_seq = htonl(ssn.client.isn + 1 + cnt); | |
5979 | tcph.th_ack = htonl(572799782UL); | |
5980 | tcph.th_flags = TH_ACK|TH_PUSH; | |
5981 | p.tcph = &tcph; | |
5982 | p.flowflags = FLOW_PKT_TOSERVER; | |
5983 | p.payload = &httpbuf1[cnt]; | |
5984 | p.payload_len = 1; | |
5985 | s = &ssn.client; | |
5986 | ||
5987 | if (StreamTcpReassembleHandleSegment(ra_ctx, &ssn, s, &p) == -1) { | |
5988 | printf("failed in segments reassembly, while processing toserver " | |
5989 | "packet\n"); | |
5990 | goto end; | |
5991 | } | |
5992 | ||
5993 | p.flowflags = FLOW_PKT_TOCLIENT; | |
5994 | p.payload = NULL; | |
5995 | p.payload_len = 0; | |
5996 | tcph.th_seq = htonl(572799782UL); | |
5997 | tcph.th_ack = htonl(ssn.client.isn + 1 + cnt); | |
5998 | tcph.th_flags = TH_ACK; | |
5999 | p.tcph = &tcph; | |
6000 | s = &ssn.server; | |
6001 | ||
6002 | if (StreamTcpReassembleHandleSegment(ra_ctx, &ssn, s, &p) == -1) { | |
6003 | printf("failed in segments reassembly, while processing toserver " | |
6004 | "packet\n"); | |
6005 | goto end; | |
6006 | } | |
6007 | ||
6008 | /* Process stream smsgs we may have in queue */ | |
6009 | if (StreamTcpReassembleProcessAppLayer(ra_ctx) < 0) { | |
6010 | printf("failed in processing stream smsgs\n"); | |
6011 | goto end; | |
6012 | } | |
6013 | } | |
6014 | ||
6015 | if (f.alproto != ALPROTO_HTTP) { | |
6016 | printf("App layer protocol (HTTP) should have been detected\n"); | |
6017 | goto end; | |
6018 | } | |
6019 | ||
6020 | ret = 1; | |
6021 | end: | |
6022 | StreamTcpFreeConfig(TRUE); | |
6023 | StreamTcpReassembleFreeThreadCtx(ra_ctx); | |
6024 | return ret; | |
6025 | } | |
5903 | 6026 | #endif /* UNITTESTS */ |
5904 | 6027 | |
5905 | 6028 | /** \brief The Function Register the Unit tests to test the reassembly engine |
5954 | 6077 | UtRegisterTest("StreamTcpReassembleTest44 -- Memcap Test", StreamTcpReassembleTest44, 1); |
5955 | 6078 | UtRegisterTest("StreamTcpReassembleTest45 -- Depth Test", StreamTcpReassembleTest45, 1); |
5956 | 6079 | UtRegisterTest("StreamTcpReassembleTest46 -- Depth Test", StreamTcpReassembleTest46, 1); |
6080 | UtRegisterTest("StreamTcpReassembleTest47 -- TCP Sequence Wraparound Test", StreamTcpReassembleTest47, 1); | |
5957 | 6081 | #endif /* UNITTESTS */ |
5958 | 6082 | } |
72 | 72 | void StreamTcpSetOSPolicy(TcpStream *, Packet *); |
73 | 73 | void StreamTcpReassemblePause (TcpSession *, char ); |
74 | 74 | void StreamTcpReassembleUnPause (TcpSession *, char ); |
75 | int StreamTcpCheckStreamContents(uint8_t *, uint16_t , TcpStream *); | |
75 | 76 | |
76 | 77 | #endif /* __STREAM_TCP_REASSEMBLE_H__ */ |
77 | 78 |
29 | 29 | #include "decode.h" |
30 | 30 | #include "debug.h" |
31 | 31 | #include "detect.h" |
32 | ||
32 | 33 | #include "flow.h" |
34 | #include "flow-util.h" | |
35 | ||
33 | 36 | #include "threads.h" |
34 | 37 | #include "conf.h" |
35 | 38 | #include "conf-yaml-loader.h" |
97 | 100 | static uint64_t ssn_pool_cnt = 0; /** counts ssns, protected by ssn_pool_mutex */ |
98 | 101 | #endif |
99 | 102 | |
103 | extern uint8_t engine_mode; | |
104 | ||
100 | 105 | static SCSpinlock stream_memuse_spinlock; |
101 | 106 | static uint32_t stream_memuse; |
102 | 107 | static uint32_t stream_memuse_max; |
230 | 235 | * |
231 | 236 | * \param p Packet used to identify the stream. |
232 | 237 | */ |
233 | static void StreamTcpSessionPktFree (Packet *p) | |
238 | void StreamTcpSessionPktFree (Packet *p) | |
234 | 239 | { |
235 | 240 | SCEnter(); |
236 | 241 | |
415 | 420 | } else { |
416 | 421 | stream_config.reassembly_depth = 0; |
417 | 422 | } |
423 | ||
424 | char *csum = NULL; | |
425 | if ((ConfGet("stream.checksum_validation", &csum)) == 1) { | |
426 | if (strncmp(csum, "yes", 3) == 0) { | |
427 | stream_config.flags |= STREAMTCP_INIT_FLAG_CHECKSUM_VALIDATION; | |
428 | } | |
429 | /* Default is that we validate the checksum of all the packets */ | |
430 | } else { | |
431 | stream_config.flags |= STREAMTCP_INIT_FLAG_CHECKSUM_VALIDATION; | |
432 | } | |
433 | ||
418 | 434 | if (!quiet) { |
419 | 435 | SCLogInfo("stream.reassembly \"depth\": %"PRIu32"", stream_config.reassembly_depth); |
420 | 436 | } |
512 | 528 | ssn->state = state; |
513 | 529 | |
514 | 530 | FlowUpdateQueue(p->flow); |
515 | } | |
516 | ||
517 | /** | |
518 | * \brief Function to flip the direction When we missed the SYN packet, | |
519 | * SYN/ACK is considered as sent by server, but our engine flagged the | |
520 | * packet as from client for the host whose packet is received first in | |
521 | * the session. | |
522 | * | |
523 | * \param ssn TcpSession to whom this packet belongs | |
524 | * \param p Packet whose flag has to be changed | |
525 | */ | |
526 | static void StreamTcpPacketSwitchDir(TcpSession *ssn, Packet *p) | |
527 | { | |
528 | SCLogDebug("ssn %p: switching pkt direction", ssn); | |
529 | ||
530 | if (PKT_IS_TOSERVER(p)) { | |
531 | p->flowflags &= ~FLOW_PKT_TOSERVER; | |
532 | p->flowflags |= FLOW_PKT_TOCLIENT; | |
533 | } else { | |
534 | p->flowflags &= ~FLOW_PKT_TOCLIENT; | |
535 | p->flowflags |= FLOW_PKT_TOSERVER; | |
536 | } | |
537 | 531 | } |
538 | 532 | |
539 | 533 | /** |
599 | 593 | case TH_SYN | TH_CWR | TH_ECN: |
600 | 594 | case TH_SYN | TH_ECN: |
601 | 595 | case TH_SYN | TH_CWR: |
596 | case TH_SYN | TH_PUSH: | |
597 | case TH_SYN | TH_URG: | |
602 | 598 | case TH_SYN: |
603 | 599 | { |
604 | 600 | if (ssn == NULL) { |
618 | 614 | /* set the sequence numbers and window */ |
619 | 615 | ssn->client.isn = TCP_GET_SEQ(p); |
620 | 616 | ssn->client.ra_base_seq = ssn->client.isn; |
617 | ssn->client.tmp_ra_base_seq = ssn->client.isn; | |
621 | 618 | ssn->client.next_seq = ssn->client.isn + 1; |
622 | 619 | |
623 | 620 | /*Set the stream timestamp value, if packet has timestamp option |
648 | 645 | } |
649 | 646 | case TH_SYN|TH_ACK: |
650 | 647 | case TH_SYN|TH_ACK|TH_ECN: |
648 | case TH_SYN|TH_ACK|TH_ECN|TH_CWR: | |
651 | 649 | if (stream_config.midstream == FALSE && |
652 | 650 | stream_config.async_oneside == FALSE) |
653 | 651 | break; |
671 | 669 | /* sequence number & window */ |
672 | 670 | ssn->server.isn = TCP_GET_SEQ(p); |
673 | 671 | ssn->server.ra_base_seq = ssn->server.isn; |
672 | ssn->server.tmp_ra_base_seq = ssn->server.isn; | |
674 | 673 | ssn->server.next_seq = ssn->server.isn + 1; |
675 | 674 | ssn->server.window = TCP_GET_WINDOW(p); |
676 | 675 | SCLogDebug("ssn %p: server window %u", ssn, ssn->server.window); |
677 | 676 | |
678 | 677 | ssn->client.isn = TCP_GET_ACK(p) - 1; |
679 | 678 | ssn->client.ra_base_seq = ssn->client.isn; |
679 | ssn->client.tmp_ra_base_seq = ssn->client.isn; | |
680 | 680 | ssn->client.next_seq = ssn->client.isn + 1; |
681 | 681 | |
682 | 682 | ssn->client.last_ack = TCP_GET_ACK(p); |
723 | 723 | /* Handle SYN/ACK and 3WHS shake missed together as it is almost |
724 | 724 | * similar. */ |
725 | 725 | case TH_ACK: |
726 | case TH_ACK| TH_URG: | |
726 | 727 | case TH_ACK|TH_ECN: |
728 | case TH_ACK|TH_ECN|TH_CWR: | |
727 | 729 | case TH_ACK|TH_PUSH: |
730 | case TH_ACK|TH_PUSH|TH_URG: | |
728 | 731 | case TH_ACK|TH_PUSH|TH_ECN: |
732 | case TH_ACK|TH_PUSH|TH_ECN|TH_CWR: | |
729 | 733 | if (stream_config.midstream == FALSE) |
730 | 734 | break; |
731 | 735 | if (ssn == NULL) { |
747 | 751 | /* set the sequence numbers and window */ |
748 | 752 | ssn->client.isn = TCP_GET_SEQ(p) - 1; |
749 | 753 | ssn->client.ra_base_seq = ssn->client.isn; |
754 | ssn->client.tmp_ra_base_seq = ssn->client.isn; | |
750 | 755 | ssn->client.next_seq = TCP_GET_SEQ(p) + p->payload_len; |
751 | 756 | ssn->client.window = TCP_GET_WINDOW(p); |
752 | 757 | ssn->client.last_ack = TCP_GET_SEQ(p); |
756 | 761 | |
757 | 762 | ssn->server.isn = TCP_GET_ACK(p) - 1; |
758 | 763 | ssn->server.ra_base_seq = ssn->server.isn; |
764 | ssn->server.tmp_ra_base_seq = ssn->server.isn; | |
759 | 765 | ssn->server.next_seq = ssn->server.isn + 1; |
760 | 766 | ssn->server.last_ack = TCP_GET_ACK(p); |
761 | 767 | ssn->server.next_win = ssn->server.last_ack; |
800 | 806 | case TH_RST: |
801 | 807 | case TH_RST|TH_ACK: |
802 | 808 | case TH_RST|TH_ACK|TH_ECN: |
809 | case TH_RST|TH_ACK|TH_ECN|TH_CWR: | |
803 | 810 | case TH_RST|TH_ACK|TH_PUSH: |
804 | 811 | case TH_RST|TH_ACK|TH_PUSH|TH_ECN: |
812 | case TH_RST|TH_ACK|TH_PUSH|TH_ECN|TH_CWR: | |
805 | 813 | case TH_FIN: |
806 | 814 | case TH_FIN|TH_ACK: |
807 | 815 | case TH_FIN|TH_ACK|TH_ECN: |
816 | case TH_FIN|TH_ACK|TH_ECN|TH_CWR: | |
808 | 817 | case TH_FIN|TH_ACK|TH_PUSH: |
809 | 818 | case TH_FIN|TH_ACK|TH_PUSH|TH_ECN: |
819 | case TH_FIN|TH_ACK|TH_PUSH|TH_ECN|TH_CWR: | |
810 | 820 | BUG_ON(p->flow->protoctx != NULL); |
811 | 821 | SCLogDebug("FIN or RST packet received, no session setup"); |
812 | 822 | break; |
838 | 848 | |
839 | 849 | switch (p->tcph->th_flags) { |
840 | 850 | case TH_SYN: |
851 | case TH_SYN|TH_URG: | |
841 | 852 | case TH_SYN|TH_CWR: |
842 | 853 | case TH_SYN|TH_CWR|TH_ECN: |
843 | 854 | SCLogDebug("ssn %p: SYN packet on state SYN_SENT... resent", ssn); |
862 | 873 | */ |
863 | 874 | ssn->server.isn = TCP_GET_SEQ(p); |
864 | 875 | ssn->server.ra_base_seq = ssn->server.isn; |
876 | ssn->server.tmp_ra_base_seq = ssn->server.isn; | |
865 | 877 | ssn->server.next_seq = ssn->server.isn + 1; |
866 | 878 | |
867 | 879 | /* Set the stream timestamp value, if packet has timestamp |
898 | 910 | break; |
899 | 911 | case TH_SYN|TH_ACK: |
900 | 912 | case TH_SYN|TH_ACK|TH_ECN: |
913 | case TH_SYN|TH_ACK|TH_ECN|TH_CWR: | |
901 | 914 | if (ssn->flags & STREAMTCP_FLAG_4WHS && PKT_IS_TOSERVER(p)) { |
902 | 915 | SCLogDebug("ssn %p: SYN/ACK received on 4WHS session", ssn); |
903 | 916 | |
927 | 940 | /* sequence number & window */ |
928 | 941 | ssn->client.isn = TCP_GET_SEQ(p); |
929 | 942 | ssn->client.ra_base_seq = ssn->client.isn; |
943 | ssn->client.tmp_ra_base_seq = ssn->client.isn; | |
930 | 944 | ssn->client.next_seq = ssn->client.isn + 1; |
931 | 945 | |
932 | 946 | ssn->server.window = TCP_GET_WINDOW(p); |
1005 | 1019 | /* sequence number & window */ |
1006 | 1020 | ssn->server.isn = TCP_GET_SEQ(p); |
1007 | 1021 | ssn->server.ra_base_seq = ssn->server.isn; |
1022 | ssn->server.tmp_ra_base_seq = ssn->server.isn; | |
1008 | 1023 | ssn->server.next_seq = ssn->server.isn + 1; |
1009 | 1024 | |
1010 | 1025 | ssn->client.window = TCP_GET_WINDOW(p); |
1066 | 1081 | ssn->flags &=~ STREAMTCP_FLAG_4WHS; |
1067 | 1082 | break; |
1068 | 1083 | case TH_ACK: |
1084 | case TH_ACK|TH_URG: | |
1069 | 1085 | case TH_ACK|TH_ECN: |
1086 | case TH_ACK|TH_ECN|TH_CWR: | |
1070 | 1087 | case TH_ACK|TH_PUSH: |
1088 | case TH_ACK|TH_PUSH|TH_URG: | |
1071 | 1089 | case TH_ACK|TH_PUSH|TH_ECN: |
1090 | case TH_ACK|TH_PUSH|TH_ECN|TH_CWR: | |
1072 | 1091 | /* Handle the asynchronous stream, when we receive a SYN packet |
1073 | 1092 | and now istead of receving a SYN/ACK we receive a ACK from the |
1074 | 1093 | same host, which sent the SYN, this suggests the ASNYC streams.*/ |
1097 | 1116 | /* Set the server side parameters */ |
1098 | 1117 | ssn->server.isn = TCP_GET_ACK(p) - 1; |
1099 | 1118 | ssn->server.ra_base_seq = ssn->server.isn; |
1119 | ssn->server.tmp_ra_base_seq = ssn->server.isn; | |
1100 | 1120 | ssn->server.next_seq = ssn->server.isn + 1; |
1101 | 1121 | ssn->server.last_ack = ssn->server.next_seq; |
1102 | 1122 | ssn->server.next_win = ssn->server.last_ack; |
1128 | 1148 | case TH_RST: |
1129 | 1149 | case TH_RST|TH_ACK: |
1130 | 1150 | case TH_RST|TH_ACK|TH_ECN: |
1131 | if(ValidReset(ssn, p)){ | |
1132 | if(SEQ_EQ(TCP_GET_SEQ(p), ssn->client.isn) && | |
1133 | SEQ_EQ(TCP_GET_WINDOW(p), 0) && | |
1134 | SEQ_EQ(TCP_GET_ACK(p), (ssn->client.isn + 1))) | |
1135 | { | |
1151 | case TH_RST|TH_ACK|TH_ECN|TH_CWR: | |
1152 | if (ValidReset(ssn, p)) { | |
1153 | if (PKT_IS_TOSERVER(p)) { | |
1154 | if (SEQ_EQ(TCP_GET_SEQ(p), ssn->client.isn) && | |
1155 | SEQ_EQ(TCP_GET_WINDOW(p), 0) && | |
1156 | SEQ_EQ(TCP_GET_ACK(p), (ssn->client.isn + 1))) { | |
1157 | StreamTcpPacketSetState(p, ssn, TCP_CLOSED); | |
1158 | SCLogDebug("ssn %p: Reset received and state changed to " | |
1159 | "TCP_CLOSED", ssn); | |
1160 | StreamTcpSessionPktFree(p); | |
1161 | } | |
1162 | } else { | |
1136 | 1163 | StreamTcpPacketSetState(p, ssn, TCP_CLOSED); |
1137 | 1164 | SCLogDebug("ssn %p: Reset received and state changed to " |
1138 | "TCP_CLOSED", ssn); | |
1165 | "TCP_CLOSED", ssn); | |
1139 | 1166 | StreamTcpSessionPktFree(p); |
1140 | 1167 | } |
1141 | 1168 | } else |
1167 | 1194 | |
1168 | 1195 | switch (p->tcph->th_flags) { |
1169 | 1196 | case TH_SYN: |
1197 | case TH_SYN|TH_URG: | |
1170 | 1198 | case TH_SYN|TH_CWR: |
1171 | 1199 | case TH_SYN|TH_CWR|TH_ECN: |
1172 | 1200 | SCLogDebug("ssn %p: SYN packet on state SYN_RECV... resent", ssn); |
1173 | 1201 | break; |
1174 | 1202 | case TH_SYN|TH_ACK: |
1175 | 1203 | case TH_SYN|TH_ACK|TH_ECN: |
1204 | case TH_SYN|TH_ACK|TH_ECN|TH_CWR: | |
1176 | 1205 | SCLogDebug("ssn %p: SYN/ACK packet on state SYN_RECV. resent", ssn); |
1177 | 1206 | break; |
1178 | 1207 | case TH_ACK: |
1208 | case TH_ACK|TH_URG: | |
1179 | 1209 | case TH_ACK|TH_ECN: |
1210 | case TH_ACK|TH_ECN|TH_CWR: | |
1180 | 1211 | case TH_ACK|TH_PUSH: |
1212 | case TH_ACK|TH_PUSH|TH_URG: | |
1181 | 1213 | case TH_ACK|TH_PUSH|TH_ECN: |
1214 | case TH_ACK|TH_PUSH|TH_ECN|TH_CWR: | |
1182 | 1215 | /* If the timestamp option is enabled for both the streams, then |
1183 | 1216 | * validate the received packet timestamp value against the |
1184 | 1217 | * stream->last_ts. If the timestamp is valid then process the |
1328 | 1361 | case TH_RST: |
1329 | 1362 | case TH_RST|TH_ACK: |
1330 | 1363 | case TH_RST|TH_ACK|TH_ECN: |
1364 | case TH_RST|TH_ACK|TH_ECN|TH_CWR: | |
1331 | 1365 | |
1332 | 1366 | if(ValidReset(ssn, p)) { |
1333 | 1367 | uint8_t reset = TRUE; |
1629 | 1663 | |
1630 | 1664 | switch (p->tcph->th_flags) { |
1631 | 1665 | case TH_SYN: |
1666 | case TH_SYN|TH_URG: | |
1632 | 1667 | case TH_SYN|TH_CWR: |
1633 | 1668 | case TH_SYN|TH_CWR|TH_ECN: |
1634 | 1669 | SCLogDebug("ssn %p: SYN packet on state ESTABLISED... resent", ssn); |
1635 | 1670 | break; |
1636 | 1671 | case TH_SYN|TH_ACK: |
1637 | 1672 | case TH_SYN|TH_ACK|TH_ECN: |
1673 | case TH_SYN|TH_ACK|TH_ECN|TH_CWR: | |
1638 | 1674 | SCLogDebug("ssn %p: SYN/ACK packet on state ESTABLISHED... resent", |
1639 | 1675 | ssn); |
1640 | 1676 | break; |
1677 | case TH_ACK|TH_URG: | |
1641 | 1678 | case TH_ACK: |
1642 | 1679 | case TH_ACK|TH_ECN: |
1643 | 1680 | case TH_ACK|TH_PUSH: |
1644 | 1681 | case TH_ACK|TH_PUSH|TH_ECN: |
1682 | case TH_ACK|TH_PUSH|TH_ECN|TH_CWR: | |
1683 | case TH_ACK|TH_PUSH|TH_URG: | |
1684 | /* Urgent pointer size can be more than the payload size, as it tells | |
1685 | the future coming data from the sender will be handled urgently | |
1686 | untill data of size equal to urgent offset has been processed | |
1687 | (RFC 2147)*/ | |
1645 | 1688 | |
1646 | 1689 | /* If the timestamp option is enabled for both the streams, then |
1647 | 1690 | * validate the received packet timestamp value against the |
1675 | 1718 | case TH_FIN: |
1676 | 1719 | case TH_FIN|TH_ACK: |
1677 | 1720 | case TH_FIN|TH_ACK|TH_ECN: |
1721 | case TH_FIN|TH_ACK|TH_ECN|TH_CWR: | |
1678 | 1722 | case TH_FIN|TH_ACK|TH_PUSH: |
1679 | 1723 | case TH_FIN|TH_ACK|TH_PUSH|TH_ECN: |
1724 | case TH_FIN|TH_ACK|TH_PUSH|TH_ECN|TH_CWR: | |
1680 | 1725 | |
1681 | 1726 | if (ssn->flags & STREAMTCP_FLAG_TIMESTAMP) { |
1682 | 1727 | if (!ValidTimestamp(ssn, p)) |
1695 | 1740 | case TH_RST: |
1696 | 1741 | case TH_RST|TH_ACK: |
1697 | 1742 | case TH_RST|TH_ACK|TH_ECN: |
1743 | case TH_RST|TH_ACK|TH_ECN|TH_CWR: | |
1698 | 1744 | |
1699 | 1745 | if(ValidReset(ssn, p)) { |
1700 | 1746 | if(PKT_IS_TOSERVER(p)) { |
1702 | 1748 | SCLogDebug("ssn %p: Reset received and state changed to " |
1703 | 1749 | "TCP_CLOSED", ssn); |
1704 | 1750 | |
1705 | ssn->client.next_seq = TCP_GET_ACK(p); | |
1706 | ssn->server.next_seq = TCP_GET_SEQ(p) + p->payload_len + 1; | |
1751 | ssn->server.next_seq = TCP_GET_ACK(p); | |
1752 | ssn->client.next_seq = TCP_GET_SEQ(p) + p->payload_len; | |
1707 | 1753 | SCLogDebug("ssn %p: ssn->server.next_seq %" PRIu32 "", ssn, |
1708 | 1754 | ssn->server.next_seq); |
1709 | 1755 | ssn->client.window = TCP_GET_WINDOW(p) << ssn->client.wscale; |
1845 | 1891 | return -1; |
1846 | 1892 | |
1847 | 1893 | switch (p->tcph->th_flags) { |
1894 | case TH_ACK|TH_URG: | |
1848 | 1895 | case TH_ACK: |
1849 | 1896 | case TH_ACK|TH_ECN: |
1897 | case TH_ACK|TH_ECN|TH_CWR: | |
1850 | 1898 | |
1851 | 1899 | if (ssn->flags & STREAMTCP_FLAG_TIMESTAMP) { |
1852 | 1900 | if (!ValidTimestamp(ssn, p)) |
1919 | 1967 | case TH_FIN: |
1920 | 1968 | case TH_FIN|TH_ACK: |
1921 | 1969 | case TH_FIN|TH_ACK|TH_ECN: |
1970 | case TH_FIN|TH_ACK|TH_ECN|TH_CWR: | |
1922 | 1971 | case TH_FIN|TH_ACK|TH_PUSH: |
1923 | 1972 | case TH_FIN|TH_ACK|TH_PUSH|TH_ECN: |
1973 | case TH_FIN|TH_ACK|TH_PUSH|TH_ECN|TH_CWR: | |
1924 | 1974 | |
1925 | 1975 | if (ssn->flags & STREAMTCP_FLAG_TIMESTAMP) { |
1926 | 1976 | if (!ValidTimestamp(ssn, p)) |
2002 | 2052 | case TH_RST: |
2003 | 2053 | case TH_RST|TH_ACK: |
2004 | 2054 | case TH_RST|TH_ACK|TH_ECN: |
2055 | case TH_RST|TH_ACK|TH_ECN|TH_CWR: | |
2005 | 2056 | |
2006 | 2057 | if(ValidReset(ssn, p)) { |
2007 | 2058 | StreamTcpPacketSetState(p, ssn, TCP_CLOSED); |
2038 | 2089 | return -1; |
2039 | 2090 | |
2040 | 2091 | switch (p->tcph->th_flags) { |
2092 | case TH_ACK|TH_URG: | |
2041 | 2093 | case TH_ACK: |
2042 | 2094 | case TH_ACK|TH_ECN: |
2095 | case TH_ACK|TH_ECN|TH_CWR: | |
2043 | 2096 | |
2044 | 2097 | if (ssn->flags & STREAMTCP_FLAG_TIMESTAMP) { |
2045 | 2098 | if (!ValidTimestamp(ssn, p)) |
2113 | 2166 | case TH_RST: |
2114 | 2167 | case TH_RST|TH_ACK: |
2115 | 2168 | case TH_RST|TH_ACK|TH_ECN: |
2169 | case TH_RST|TH_ACK|TH_ECN|TH_CWR: | |
2116 | 2170 | |
2117 | 2171 | if(ValidReset(ssn, p)) { |
2118 | 2172 | StreamTcpPacketSetState(p, ssn, TCP_CLOSED); |
2215 | 2269 | switch(p->tcph->th_flags) { |
2216 | 2270 | case TH_ACK: |
2217 | 2271 | case TH_ACK|TH_ECN: |
2272 | case TH_ACK|TH_ECN|TH_CWR: | |
2218 | 2273 | |
2219 | 2274 | if (ssn->flags & STREAMTCP_FLAG_TIMESTAMP) { |
2220 | 2275 | if (!ValidTimestamp(ssn, p)) |
2309 | 2364 | case TH_FIN: |
2310 | 2365 | case TH_FIN|TH_ACK: |
2311 | 2366 | case TH_FIN|TH_ACK|TH_ECN: |
2367 | case TH_FIN|TH_ACK|TH_ECN|TH_CWR: | |
2312 | 2368 | |
2313 | 2369 | if (ssn->flags & STREAMTCP_FLAG_TIMESTAMP) { |
2314 | 2370 | if (!ValidTimestamp(ssn, p)) |
2372 | 2428 | } |
2373 | 2429 | break; |
2374 | 2430 | case TH_ACK: |
2431 | case TH_ACK|TH_PUSH: | |
2375 | 2432 | case TH_ACK|TH_ECN: |
2433 | case TH_ACK|TH_ECN|TH_CWR: | |
2376 | 2434 | if (ssn->flags & STREAMTCP_FLAG_TIMESTAMP) { |
2377 | 2435 | if (!ValidTimestamp(ssn, p)) |
2378 | 2436 | SCReturnInt(-1); |
2397 | 2455 | if (SEQ_GT(TCP_GET_ACK(p),ssn->client.last_ack)) |
2398 | 2456 | ssn->client.last_ack = TCP_GET_ACK(p); |
2399 | 2457 | |
2458 | if (SEQ_EQ(TCP_GET_SEQ(p),ssn->server.next_seq)) | |
2459 | ssn->server.next_seq += p->payload_len; | |
2460 | ||
2400 | 2461 | StreamTcpReassembleHandleSegment(stt->ra_ctx, ssn, |
2401 | 2462 | &ssn->server, p); |
2402 | 2463 | SCLogDebug("ssn %p: =+ next SEQ %" PRIu32 ", last ACK " |
2403 | 2464 | "%" PRIu32 "", ssn, ssn->server.next_seq, |
2404 | 2465 | ssn->client.last_ack); |
2405 | 2466 | } else { |
2406 | SCLogDebug("ssn %p: pkt (%" PRIu32 ") is to client: SEQ " | |
2467 | SCLogDebug("ssn %p: pkt (%" PRIu32 ") is to server: SEQ " | |
2407 | 2468 | "%" PRIu32 ", ACK %" PRIu32 "", ssn, p->payload_len, |
2408 | 2469 | TCP_GET_SEQ(p), TCP_GET_ACK(p)); |
2409 | 2470 | |
2421 | 2482 | if (SEQ_GT(TCP_GET_ACK(p),ssn->server.last_ack)) |
2422 | 2483 | ssn->server.last_ack = TCP_GET_ACK(p); |
2423 | 2484 | |
2485 | if (SEQ_EQ(TCP_GET_SEQ(p),ssn->client.next_seq)) | |
2486 | ssn->client.next_seq += p->payload_len; | |
2487 | ||
2424 | 2488 | StreamTcpReassembleHandleSegment(stt->ra_ctx, ssn, |
2425 | 2489 | &ssn->client, p); |
2426 | 2490 | SCLogDebug("ssn %p: =+ next SEQ %" PRIu32 ", last ACK " |
2454 | 2518 | switch(p->tcph->th_flags) { |
2455 | 2519 | case TH_ACK: |
2456 | 2520 | case TH_ACK|TH_ECN: |
2521 | case TH_ACK|TH_ECN|TH_CWR: | |
2457 | 2522 | |
2458 | 2523 | if (ssn->flags & STREAMTCP_FLAG_TIMESTAMP) { |
2459 | 2524 | if (!ValidTimestamp(ssn, p)) |
2515 | 2580 | switch(p->tcph->th_flags) { |
2516 | 2581 | case TH_ACK: |
2517 | 2582 | case TH_ACK|TH_ECN: |
2583 | case TH_ACK|TH_ECN|TH_CWR: | |
2518 | 2584 | |
2519 | 2585 | if (ssn->flags & STREAMTCP_FLAG_TIMESTAMP) { |
2520 | 2586 | if (!ValidTimestamp(ssn, p)) |
2589 | 2655 | { |
2590 | 2656 | SCEnter(); |
2591 | 2657 | TcpSession *ssn = (TcpSession *)p->flow->protoctx; |
2658 | ||
2659 | /* If we are on IPS mode, and got a drop action triggered from | |
2660 | * the IP only module, or from a reassembled msg and/or from an | |
2661 | * applayer detection, then drop the rest of the packets of the | |
2662 | * same stream and avoid inspecting it any further */ | |
2663 | if (StreamTcpCheckFlowDrops(p) == 1) { | |
2664 | SCLogDebug("This flow/stream triggered a drop rule"); | |
2665 | FlowSetNoPacketInspectionFlag(p->flow); | |
2666 | DecodeSetNoPacketInspectionFlag(p); | |
2667 | FlowSetSessionNoApplayerInspectionFlag(p->flow); | |
2668 | p->action |= ACTION_DROP; | |
2669 | /* return the segments to the pool */ | |
2670 | StreamTcpSessionPktFree(p); | |
2671 | SCReturnInt(0); | |
2672 | } | |
2592 | 2673 | |
2593 | 2674 | if (ssn == NULL || ssn->state == TCP_NONE) { |
2594 | 2675 | if (StreamTcpPacketStateNone(tv, p, stt, ssn) == -1) |
2640 | 2721 | SCReturnInt(-1); |
2641 | 2722 | break; |
2642 | 2723 | case TCP_CLOSED: |
2643 | SCLogDebug("packet received on closed state"); | |
2724 | /* As our TCP session memory is not returned to pool, until | |
2725 | timeout. If in the mean time we receive any other session from | |
2726 | the same client reusing same port then we switch back to | |
2727 | tcp state none */ | |
2728 | if (PKT_IS_TOSERVER(p) && (p->tcph->th_flags & TH_SYN)) { | |
2729 | if(SEQ_EQ(ssn->client.next_seq, TCP_GET_SEQ(p))) { | |
2730 | if(StreamTcpPacketStateNone(tv,p,stt,ssn)) { | |
2731 | SCReturnInt(-1); | |
2732 | } | |
2733 | } | |
2734 | } else { | |
2735 | SCLogDebug("packet received on closed state"); | |
2736 | } | |
2644 | 2737 | break; |
2645 | 2738 | default: |
2646 | 2739 | SCLogDebug("packet received on default state"); |
2659 | 2752 | SCReturnInt(0); |
2660 | 2753 | } |
2661 | 2754 | |
2755 | /** | |
2756 | * \brief Function to validate the checksum of the received packet. If the | |
2757 | * checksum is invalid, packet will be dropped, as the end system will | |
2758 | * also drop the packet. | |
2759 | * | |
2760 | * \param p Packet of which checksum has to be validated | |
2761 | * \retval 1 if the checksum is valid, otherwise 0 | |
2762 | */ | |
2763 | int StreamTcpValidateChecksum(Packet *p) | |
2764 | { | |
2765 | int ret = 1; | |
2766 | ||
2767 | if (p->tcpc.comp_csum == -1) { | |
2768 | if (PKT_IS_IPV4(p)) { | |
2769 | p->tcpc.comp_csum = TCPCalculateChecksum((uint16_t *)&(p->ip4h->ip_src), | |
2770 | (uint16_t *)p->tcph, | |
2771 | (p->payload_len + | |
2772 | p->tcpvars.hlen) ); | |
2773 | } else if (PKT_IS_IPV6(p)) { | |
2774 | p->tcpc.comp_csum = TCPV6CalculateChecksum((uint16_t *)&(p->ip6h->ip6_src), | |
2775 | (uint16_t *)p->tcph, | |
2776 | (p->payload_len + | |
2777 | p->tcpvars.hlen) ); | |
2778 | } | |
2779 | } | |
2780 | ||
2781 | if (p->tcpc.comp_csum != p->tcph->th_sum) { | |
2782 | ret = 0; | |
2783 | SCLogDebug("Checksum of recevied packet %p is invalid",p); | |
2784 | } | |
2785 | ||
2786 | return ret; | |
2787 | } | |
2788 | ||
2662 | 2789 | TmEcode StreamTcp (ThreadVars *tv, Packet *p, void *data, PacketQueue *pq, PacketQueue *postpq) |
2663 | 2790 | { |
2664 | 2791 | StreamTcpThread *stt = (StreamTcpThread *)data; |
2669 | 2796 | |
2670 | 2797 | if (p->flow == NULL) |
2671 | 2798 | return TM_ECODE_OK; |
2799 | ||
2800 | if ((stream_config.flags & STREAMTCP_INIT_FLAG_CHECKSUM_VALIDATION) && | |
2801 | (StreamTcpValidateChecksum(p) == 0)) | |
2802 | { | |
2803 | return TM_ECODE_OK; | |
2804 | } | |
2672 | 2805 | |
2673 | 2806 | SCMutexLock(&p->flow->m); |
2674 | 2807 | ret = StreamTcpPacket(tv, p, stt); |
5803 | 5936 | return ret; |
5804 | 5937 | } |
5805 | 5938 | |
5939 | #if 0 | |
5940 | /** | |
5941 | * \test Test the resetting of the sesison with bad checksum packet and later | |
5942 | * send the malicious contents on the session. Engine should drop the | |
5943 | * packet with the bad checksum. | |
5944 | * | |
5945 | * \retval On success it returns 1 and on failure 0. | |
5946 | */ | |
5947 | static int StreamTcpTest29(void) | |
5948 | { | |
5949 | Packet p; | |
5950 | Flow f; | |
5951 | ThreadVars tv; | |
5952 | StreamTcpThread stt; | |
5953 | TCPHdr tcph; | |
5954 | TcpSession ssn; | |
5955 | IPV4Hdr ipv4h; | |
5956 | TcpReassemblyThreadCtx *ra_ctx = StreamTcpReassembleInitThreadCtx(); | |
5957 | struct in_addr addr; | |
5958 | struct in_addr addr1; | |
5959 | TCPCache tcpc; | |
5960 | TCPVars tcpvars; | |
5961 | TcpStream server; | |
5962 | TcpStream client; | |
5963 | ||
5964 | memset (&p, 0, sizeof(Packet)); | |
5965 | memset (&f, 0, sizeof(Flow)); | |
5966 | memset(&tv, 0, sizeof (ThreadVars)); | |
5967 | memset(&stt, 0, sizeof (StreamTcpThread)); | |
5968 | memset(&tcph, 0, sizeof (TCPHdr)); | |
5969 | memset (&ipv4h, 0, sizeof(IPV4Hdr)); | |
5970 | memset (&addr, 0, sizeof(addr)); | |
5971 | memset (&addr1, 0, sizeof(addr1)); | |
5972 | memset (&tcpc, 0, sizeof(tcpc)); | |
5973 | memset (&tcpvars, 0, sizeof(tcpvars)); | |
5974 | memset(&ssn, 0, sizeof (TcpSession)); | |
5975 | memset(&server, 0, sizeof (TcpStream)); | |
5976 | memset(&client, 0, sizeof (TcpStream)); | |
5977 | uint8_t packet[1460] = ""; | |
5978 | int result = 1; | |
5979 | ||
5980 | StreamTcpInitConfig(TRUE); | |
5981 | ||
5982 | /* prevent L7 from kicking in */ | |
5983 | StreamMsgQueueSetMinInitChunkLen(FLOW_PKT_TOSERVER, 4096); | |
5984 | StreamMsgQueueSetMinInitChunkLen(FLOW_PKT_TOCLIENT, 4096); | |
5985 | ||
5986 | ssn.client.os_policy = OS_POLICY_BSD; | |
5987 | p.src.family = AF_INET; | |
5988 | p.dst.family = AF_INET; | |
5989 | p.proto = IPPROTO_TCP; | |
5990 | p.flow = &f; | |
5991 | tcph.th_win = 5480; | |
5992 | p.tcph = &tcph; | |
5993 | p.payload = packet; | |
5994 | p.ip4h = &ipv4h; | |
5995 | p.tcpc = tcpc; | |
5996 | p.tcpc.comp_csum = -1; | |
5997 | tcpvars.hlen = 20; | |
5998 | p.tcpvars = tcpvars; | |
5999 | ssn.state = TCP_ESTABLISHED; | |
6000 | addr.s_addr = inet_addr("10.1.3.53"); | |
6001 | p.dst.address.address_un_data32[0] = addr.s_addr; | |
6002 | addr1.s_addr = inet_addr("10.1.3.7"); | |
6003 | p.src.address.address_un_data32[0] = addr1.s_addr; | |
6004 | f.protoctx = &ssn; | |
6005 | stt.ra_ctx = ra_ctx; | |
6006 | ssn.server = server; | |
6007 | ssn.client = client; | |
6008 | ssn.client.isn = 10; | |
6009 | ssn.client.window = 5184; | |
6010 | ssn.client.last_ack = 10; | |
6011 | ssn.client.ra_base_seq = 10; | |
6012 | ssn.client.next_win = 5184; | |
6013 | ssn.server.isn = 119197101; | |
6014 | ssn.server.window = 5184; | |
6015 | ssn.server.next_win = 5184; | |
6016 | ssn.server.last_ack = 119197101; | |
6017 | ssn.server.ra_base_seq = 119197101; | |
6018 | ||
6019 | ||
6020 | ||
6021 | ||
6022 | tcph.th_flags = TH_PUSH | TH_ACK; | |
6023 | p.flowflags = FLOW_PKT_TOSERVER; | |
6024 | p.tcph->th_seq = htonl(11); | |
6025 | p.tcph->th_ack = htonl(119197102); | |
6026 | p.payload_len = 4; | |
6027 | p.ip4h->ip_src = addr1; | |
6028 | p.tcph->th_sum = TCPCalculateChecksum((uint16_t *)&(p.ip4h->ip_src), | |
6029 | (uint16_t *)p.tcph, | |
6030 | (p.payload_len + | |
6031 | p.tcpvars.hlen) ); | |
6032 | ||
6033 | if (StreamTcp(&tv, &p, (void *)&stt, NULL, NULL) != TM_ECODE_OK) { | |
6034 | printf("failed in segment reassmebling\n"); | |
6035 | result &= 0; | |
6036 | goto end; | |
6037 | } | |
6038 | ||
6039 | tcph.th_flags = TH_ACK; | |
6040 | p.flowflags = FLOW_PKT_TOCLIENT; | |
6041 | p.tcph->th_seq = htonl(119197102); | |
6042 | p.tcph->th_ack = htonl(15); | |
6043 | p.payload_len = 0; | |
6044 | p.ip4h->ip_src = addr; | |
6045 | p.tcph->th_sum = TCPCalculateChecksum((uint16_t *)&(p.ip4h->ip_src), | |
6046 | (uint16_t *)p.tcph, | |
6047 | (p.payload_len + | |
6048 | p.tcpvars.hlen) ); | |
6049 | ||
6050 | if (StreamTcp(&tv, &p, (void *)&stt, NULL, NULL) != TM_ECODE_OK) { | |
6051 | printf("failed in segment reassmebling\n"); | |
6052 | result &= 0; | |
6053 | goto end; | |
6054 | } | |
6055 | ||
6056 | tcph.th_flags = TH_RST | TH_ACK; | |
6057 | p.flowflags = FLOW_PKT_TOSERVER; | |
6058 | p.tcph->th_seq = htonl(15); | |
6059 | p.tcph->th_ack = htonl(119197102); | |
6060 | p.payload_len = 0; | |
6061 | p.ip4h->ip_src = addr1; | |
6062 | p.tcph->th_sum = 12345; | |
6063 | ||
6064 | if (StreamTcp(&tv, &p, (void *)&stt, NULL, NULL) != TM_ECODE_OK) { | |
6065 | printf("failed in segment reassmebling\n"); | |
6066 | result &= 0; | |
6067 | goto end; | |
6068 | } | |
6069 | ||
6070 | if (ssn.state != TCP_ESTABLISHED) { | |
6071 | printf("the ssn.state should be TCP_ESTABLISHED(%"PRIu8"), not %"PRIu8"" | |
6072 | "\n", TCP_ESTABLISHED, ssn.state); | |
6073 | result &= 0; | |
6074 | goto end; | |
6075 | } | |
6076 | ||
6077 | end: | |
6078 | StreamTcpReturnStreamSegments(&ssn.client); | |
6079 | StreamTcpFreeConfig(TRUE); | |
6080 | return result; | |
6081 | } | |
6082 | ||
6083 | /** | |
6084 | * \test Test the overlapping of the packet with bad checksum packet and later | |
6085 | * send the malicious contents on the session. Engine should drop the | |
6086 | * packet with the bad checksum. | |
6087 | * | |
6088 | * \retval On success it returns 1 and on failure 0. | |
6089 | */ | |
6090 | static int StreamTcpTest30(void) | |
6091 | { | |
6092 | Packet p; | |
6093 | Flow f; | |
6094 | ThreadVars tv; | |
6095 | StreamTcpThread stt; | |
6096 | TCPHdr tcph; | |
6097 | TcpSession ssn; | |
6098 | IPV4Hdr ipv4h; | |
6099 | TcpReassemblyThreadCtx *ra_ctx = StreamTcpReassembleInitThreadCtx(); | |
6100 | struct in_addr addr; | |
6101 | struct in_addr addr1; | |
6102 | TCPCache tcpc; | |
6103 | TCPVars tcpvars; | |
6104 | TcpStream server; | |
6105 | TcpStream client; | |
6106 | ||
6107 | memset (&p, 0, sizeof(Packet)); | |
6108 | memset (&f, 0, sizeof(Flow)); | |
6109 | memset(&tv, 0, sizeof (ThreadVars)); | |
6110 | memset(&stt, 0, sizeof (StreamTcpThread)); | |
6111 | memset(&tcph, 0, sizeof (TCPHdr)); | |
6112 | memset (&ipv4h, 0, sizeof(IPV4Hdr)); | |
6113 | memset (&addr, 0, sizeof(addr)); | |
6114 | memset (&addr1, 0, sizeof(addr1)); | |
6115 | memset (&tcpc, 0, sizeof(tcpc)); | |
6116 | memset (&tcpvars, 0, sizeof(tcpvars)); | |
6117 | memset(&ssn, 0, sizeof (TcpSession)); | |
6118 | memset(&server, 0, sizeof (TcpStream)); | |
6119 | memset(&client, 0, sizeof (TcpStream)); | |
6120 | uint8_t payload[9] = "AAAAAAAAA"; | |
6121 | uint8_t payload1[9] = "GET /EVIL"; | |
6122 | uint8_t expected_content[9] = { 0x47, 0x45, 0x54, 0x20, 0x2f, 0x45, 0x56, | |
6123 | 0x49, 0x4c }; | |
6124 | int result = 1; | |
6125 | ||
6126 | StreamTcpInitConfig(TRUE); | |
6127 | ||
6128 | /* prevent L7 from kicking in */ | |
6129 | StreamMsgQueueSetMinInitChunkLen(FLOW_PKT_TOSERVER, 4096); | |
6130 | StreamMsgQueueSetMinInitChunkLen(FLOW_PKT_TOCLIENT, 4096); | |
6131 | ||
6132 | ssn.client.os_policy = OS_POLICY_BSD; | |
6133 | p.src.family = AF_INET; | |
6134 | p.dst.family = AF_INET; | |
6135 | p.proto = IPPROTO_TCP; | |
6136 | p.flow = &f; | |
6137 | tcph.th_win = 5480; | |
6138 | p.tcph = &tcph; | |
6139 | p.payload = payload; | |
6140 | p.ip4h = &ipv4h; | |
6141 | p.tcpc = tcpc; | |
6142 | p.tcpc.comp_csum = -1; | |
6143 | p.tcpvars = tcpvars; | |
6144 | ssn.state = TCP_ESTABLISHED; | |
6145 | addr.s_addr = inet_addr("10.1.3.53"); | |
6146 | p.dst.address.address_un_data32[0] = addr.s_addr; | |
6147 | addr1.s_addr = inet_addr("10.1.3.7"); | |
6148 | p.src.address.address_un_data32[0] = addr1.s_addr; | |
6149 | f.protoctx = &ssn; | |
6150 | stt.ra_ctx = ra_ctx; | |
6151 | ssn.server = server; | |
6152 | ssn.client = client; | |
6153 | ssn.client.isn = 10; | |
6154 | ssn.client.window = 5184; | |
6155 | ssn.client.last_ack = 10; | |
6156 | ssn.client.ra_base_seq = 10; | |
6157 | ssn.client.next_win = 5184; | |
6158 | ssn.server.isn = 1351079940; | |
6159 | ssn.server.window = 5184; | |
6160 | ssn.server.next_win = 1351088132; | |
6161 | ssn.server.last_ack = 1351079940; | |
6162 | ssn.server.ra_base_seq = 1351079940; | |
6163 | ||
6164 | tcph.th_flags = TH_PUSH | TH_ACK; | |
6165 | p.flowflags = FLOW_PKT_TOSERVER; | |
6166 | p.tcph->th_seq = htonl(11); | |
6167 | p.tcph->th_ack = htonl(1351079940); | |
6168 | p.payload_len = 9; | |
6169 | p.ip4h->ip_src = addr1; | |
6170 | p.tcph->th_sum = 12345; | |
6171 | ||
6172 | if (StreamTcp(&tv, &p, (void *)&stt, NULL, NULL) != TM_ECODE_OK) { | |
6173 | printf("failed in segment reassmebling\n"); | |
6174 | result &= 0; | |
6175 | goto end; | |
6176 | } | |
6177 | ||
6178 | tcph.th_flags = TH_PUSH | TH_ACK; | |
6179 | p.flowflags = FLOW_PKT_TOSERVER; | |
6180 | p.tcph->th_seq = htonl(11); | |
6181 | p.tcph->th_ack = htonl(1351079940); | |
6182 | p.payload = payload1; | |
6183 | p.payload_len = 9; | |
6184 | p.ip4h->ip_src = addr1; | |
6185 | p.tcph->th_sum = TCPCalculateChecksum((uint16_t *)&(p.ip4h->ip_src), | |
6186 | (uint16_t *)p.tcph, | |
6187 | (p.payload_len + | |
6188 | p.tcpvars.hlen) ); | |
6189 | ||
6190 | if (StreamTcp(&tv, &p, (void *)&stt, NULL, NULL) != TM_ECODE_OK) { | |
6191 | printf("failed in segment reassmebling\n"); | |
6192 | result &= 0; | |
6193 | goto end; | |
6194 | } | |
6195 | ||
6196 | tcph.th_flags = TH_ACK; | |
6197 | p.flowflags = FLOW_PKT_TOCLIENT; | |
6198 | p.tcph->th_seq = htonl(1351079940); | |
6199 | p.tcph->th_ack = htonl(20); | |
6200 | p.payload_len = 0; | |
6201 | p.ip4h->ip_src = addr; | |
6202 | p.tcph->th_sum = TCPCalculateChecksum((uint16_t *)&(p.ip4h->ip_src), | |
6203 | (uint16_t *)p.tcph, | |
6204 | (p.payload_len + | |
6205 | p.tcpvars.hlen) ); | |
6206 | ||
6207 | if (StreamTcp(&tv, &p, (void *)&stt, NULL, NULL) != TM_ECODE_OK) { | |
6208 | printf("failed in segment reassmebling\n"); | |
6209 | result &= 0; | |
6210 | goto end; | |
6211 | } | |
6212 | ||
6213 | if (StreamTcpCheckStreamContents(expected_content, 9, &ssn.client) != 1) { | |
6214 | printf("the contents are not as expected(GET /EVIL), contents are: "); | |
6215 | PrintRawDataFp(stdout, ssn.client.seg_list->payload, 9); | |
6216 | result &= 0; | |
6217 | goto end; | |
6218 | } | |
6219 | ||
6220 | end: | |
6221 | StreamTcpReturnStreamSegments(&ssn.client); | |
6222 | StreamTcpFreeConfig(TRUE); | |
6223 | return result; | |
6224 | } | |
6225 | ||
6226 | /** | |
6227 | * \test Test the multiple SYN packet handling with bad checksum and timestamp | |
6228 | * value. Engine should drop the bad checksum packet and establish | |
6229 | * TCP session correctly. | |
6230 | * | |
6231 | * \retval On success it returns 1 and on failure 0. | |
6232 | */ | |
6233 | static int StreamTcpTest31(void) | |
6234 | { | |
6235 | Packet p; | |
6236 | Flow f; | |
6237 | ThreadVars tv; | |
6238 | StreamTcpThread stt; | |
6239 | TCPHdr tcph; | |
6240 | TcpSession ssn; | |
6241 | IPV4Hdr ipv4h; | |
6242 | TcpReassemblyThreadCtx *ra_ctx = StreamTcpReassembleInitThreadCtx(); | |
6243 | struct in_addr addr; | |
6244 | struct in_addr addr1; | |
6245 | TCPCache tcpc; | |
6246 | TCPVars tcpvars; | |
6247 | TcpStream server; | |
6248 | TcpStream client; | |
6249 | TCPOpt tcpopt; | |
6250 | ||
6251 | memset (&p, 0, sizeof(Packet)); | |
6252 | memset (&f, 0, sizeof(Flow)); | |
6253 | memset(&tv, 0, sizeof (ThreadVars)); | |
6254 | memset(&stt, 0, sizeof (StreamTcpThread)); | |
6255 | memset(&tcph, 0, sizeof (TCPHdr)); | |
6256 | memset (&ipv4h, 0, sizeof(IPV4Hdr)); | |
6257 | memset (&addr, 0, sizeof(addr)); | |
6258 | memset (&addr1, 0, sizeof(addr1)); | |
6259 | memset (&tcpc, 0, sizeof(tcpc)); | |
6260 | memset (&tcpvars, 0, sizeof(tcpvars)); | |
6261 | memset(&ssn, 0, sizeof (TcpSession)); | |
6262 | memset(&server, 0, sizeof (TcpStream)); | |
6263 | memset(&client, 0, sizeof (TcpStream)); | |
6264 | memset(&tcpopt, 0, sizeof (TCPOpt)); | |
6265 | int result = 1; | |
6266 | ||
6267 | StreamTcpInitConfig(TRUE); | |
6268 | ||
6269 | /* prevent L7 from kicking in */ | |
6270 | StreamMsgQueueSetMinInitChunkLen(FLOW_PKT_TOSERVER, 4096); | |
6271 | StreamMsgQueueSetMinInitChunkLen(FLOW_PKT_TOCLIENT, 4096); | |
6272 | ||
6273 | ssn.client.os_policy = OS_POLICY_LINUX; | |
6274 | p.src.family = AF_INET; | |
6275 | p.dst.family = AF_INET; | |
6276 | p.proto = IPPROTO_TCP; | |
6277 | p.flow = &f; | |
6278 | tcph.th_win = 5480; | |
6279 | p.tcph = &tcph; | |
6280 | p.ip4h = &ipv4h; | |
6281 | p.tcpc = tcpc; | |
6282 | p.tcpc.comp_csum = -1; | |
6283 | p.tcpvars = tcpvars; | |
6284 | p.tcpvars.ts = &tcpopt; | |
6285 | addr.s_addr = inet_addr("10.1.3.53"); | |
6286 | p.dst.address.address_un_data32[0] = addr.s_addr; | |
6287 | addr1.s_addr = inet_addr("10.1.3.7"); | |
6288 | p.src.address.address_un_data32[0] = addr1.s_addr; | |
6289 | f.protoctx = &ssn; | |
6290 | stt.ra_ctx = ra_ctx; | |
6291 | ssn.server = server; | |
6292 | ssn.client = client; | |
6293 | ssn.client.isn = 10; | |
6294 | ssn.client.window = 5184; | |
6295 | ssn.client.last_ack = 10; | |
6296 | ssn.client.ra_base_seq = 10; | |
6297 | ssn.client.next_win = 5184; | |
6298 | ssn.server.isn = 1351079940; | |
6299 | ssn.server.window = 5184; | |
6300 | ssn.server.next_win = 1351088132; | |
6301 | ssn.server.last_ack = 1351079940; | |
6302 | ssn.server.ra_base_seq = 1351079940; | |
6303 | ||
6304 | tcph.th_flags = TH_SYN; | |
6305 | p.flowflags = FLOW_PKT_TOSERVER; | |
6306 | p.tcph->th_seq = htonl(10); | |
6307 | p.payload_len = 0; | |
6308 | p.ip4h->ip_src = addr1; | |
6309 | p.tcpc.ts1 = 100; | |
6310 | p.tcph->th_sum = 12345; | |
6311 | ||
6312 | if (StreamTcp(&tv, &p, (void *)&stt, NULL, NULL) != TM_ECODE_OK) { | |
6313 | printf("failed in segment reassmebling\n"); | |
6314 | result &= 0; | |
6315 | goto end; | |
6316 | } | |
6317 | ||
6318 | tcph.th_flags = TH_SYN; | |
6319 | p.flowflags = FLOW_PKT_TOSERVER; | |
6320 | p.tcph->th_seq = htonl(10); | |
6321 | p.payload_len = 0; | |
6322 | p.ip4h->ip_src = addr1; | |
6323 | p.tcpc.ts1 = 10; | |
6324 | p.tcpc.comp_csum = -1; | |
6325 | p.tcph->th_sum = TCPCalculateChecksum((uint16_t *)&(p.ip4h->ip_src), | |
6326 | (uint16_t *)p.tcph, | |
6327 | (p.payload_len + | |
6328 | p.tcpvars.hlen) ); | |
6329 | ||
6330 | if (StreamTcp(&tv, &p, (void *)&stt, NULL, NULL) != TM_ECODE_OK) { | |
6331 | printf("failed in segment reassmebling\n"); | |
6332 | result &= 0; | |
6333 | goto end; | |
6334 | } | |
6335 | ||
6336 | ssn.flags |= STREAMTCP_FLAG_TIMESTAMP; | |
6337 | tcph.th_flags = TH_SYN | TH_ACK; | |
6338 | p.flowflags = FLOW_PKT_TOCLIENT; | |
6339 | p.tcph->th_seq = htonl(1351079940); | |
6340 | p.tcph->th_ack = htonl(11); | |
6341 | p.payload_len = 0; | |
6342 | p.tcpc.ts1 = 10; | |
6343 | p.ip4h->ip_src = addr; | |
6344 | p.tcpc.comp_csum = -1; | |
6345 | p.tcph->th_sum = TCPCalculateChecksum((uint16_t *)&(p.ip4h->ip_src), | |
6346 | (uint16_t *)p.tcph, | |
6347 | (p.payload_len + | |
6348 | p.tcpvars.hlen) ); | |
6349 | ||
6350 | if (StreamTcp(&tv, &p, (void *)&stt, NULL, NULL) != TM_ECODE_OK) { | |
6351 | printf("failed in segment reassmebling\n"); | |
6352 | result &= 0; | |
6353 | goto end; | |
6354 | } | |
6355 | ||
6356 | tcph.th_flags = TH_ACK; | |
6357 | p.flowflags = FLOW_PKT_TOSERVER; | |
6358 | p.tcph->th_seq = htonl(11); | |
6359 | p.tcph->th_ack = htonl(1351079941); | |
6360 | p.payload_len = 0; | |
6361 | p.tcpc.ts1 = 10; | |
6362 | p.ip4h->ip_src = addr1; | |
6363 | p.tcpc.comp_csum = -1; | |
6364 | p.tcph->th_sum = TCPCalculateChecksum((uint16_t *)&(p.ip4h->ip_src), | |
6365 | (uint16_t *)p.tcph, | |
6366 | (p.payload_len + | |
6367 | p.tcpvars.hlen) ); | |
6368 | ||
6369 | if (StreamTcp(&tv, &p, (void *)&stt, NULL, NULL) != TM_ECODE_OK) { | |
6370 | printf("failed in segment reassmebling\n"); | |
6371 | result &= 0; | |
6372 | goto end; | |
6373 | } | |
6374 | ||
6375 | if (ssn.state != TCP_ESTABLISHED) { | |
6376 | printf("the should have been changed to TCP_ESTABLISHED!!\n "); | |
6377 | result &= 0; | |
6378 | goto end; | |
6379 | } | |
6380 | ||
6381 | end: | |
6382 | StreamTcpReturnStreamSegments(&ssn.client); | |
6383 | StreamTcpFreeConfig(TRUE); | |
6384 | return result; | |
6385 | } | |
6386 | ||
6387 | /** | |
6388 | * \test Test the initialization of tcp streams with ECN & CWR flags | |
6389 | * | |
6390 | * \retval On success it returns 1 and on failure 0. | |
6391 | */ | |
6392 | static int StreamTcpTest32(void) { | |
6393 | Packet p; | |
6394 | Flow f; | |
6395 | ThreadVars tv; | |
6396 | StreamTcpThread stt; | |
6397 | uint8_t payload[4]; | |
6398 | TCPHdr tcph; | |
6399 | TcpReassemblyThreadCtx *ra_ctx = StreamTcpReassembleInitThreadCtx(); | |
6400 | int ret = 0; | |
6401 | ||
6402 | memset (&p, 0, sizeof(Packet)); | |
6403 | memset (&f, 0, sizeof(Flow)); | |
6404 | memset(&tv, 0, sizeof (ThreadVars)); | |
6405 | memset(&stt, 0, sizeof (StreamTcpThread)); | |
6406 | memset(&tcph, 0, sizeof (TCPHdr)); | |
6407 | ||
6408 | stt.ra_ctx = ra_ctx; | |
6409 | p.flow = &f; | |
6410 | tcph.th_win = htons(5480); | |
6411 | tcph.th_flags = TH_SYN | TH_CWR | TH_ECN; | |
6412 | p.tcph = &tcph; | |
6413 | p.flowflags = FLOW_PKT_TOSERVER; | |
6414 | ||
6415 | StreamTcpInitConfig(TRUE); | |
6416 | ||
6417 | if (StreamTcpPacket(&tv, &p, &stt) == -1) | |
6418 | goto end; | |
6419 | ||
6420 | p.tcph->th_ack = htonl(1); | |
6421 | p.tcph->th_flags = TH_SYN | TH_ACK | TH_ECN; | |
6422 | p.flowflags = FLOW_PKT_TOCLIENT; | |
6423 | ||
6424 | if (StreamTcpPacket(&tv, &p, &stt) == -1 || (TcpSession *)p.flow->protoctx == NULL) { | |
6425 | printf("failed in processing packet\n"); | |
6426 | goto end; | |
6427 | } | |
6428 | ||
6429 | p.tcph->th_ack = htonl(1); | |
6430 | p.tcph->th_seq = htonl(1); | |
6431 | p.tcph->th_flags = TH_ACK | TH_ECN | TH_CWR; | |
6432 | p.flowflags = FLOW_PKT_TOSERVER; | |
6433 | ||
6434 | if (StreamTcpPacket(&tv, &p, &stt) == -1 || (TcpSession *)p.flow->protoctx == NULL) { | |
6435 | printf("failed in processing packet\n"); | |
6436 | goto end; | |
6437 | } | |
6438 | ||
6439 | p.tcph->th_ack = htonl(1); | |
6440 | p.tcph->th_seq = htonl(2); | |
6441 | p.tcph->th_flags = TH_PUSH | TH_ACK | TH_ECN | TH_CWR; | |
6442 | p.flowflags = FLOW_PKT_TOSERVER; | |
6443 | ||
6444 | StreamTcpCreateTestPacket(payload, 0x41, 3, 4); /*AAA*/ | |
6445 | p.payload = payload; | |
6446 | p.payload_len = 3; | |
6447 | ||
6448 | if (StreamTcpPacket(&tv, &p, &stt) == -1 || (TcpSession *)p.flow->protoctx == NULL) { | |
6449 | printf("failed in processing packet\n"); | |
6450 | goto end; | |
6451 | } | |
6452 | ||
6453 | p.flowflags = FLOW_PKT_TOCLIENT; | |
6454 | p.tcph->th_flags = TH_ACK; | |
6455 | if (StreamTcpPacket(&tv, &p, &stt) == -1 || (TcpSession *)p.flow->protoctx == NULL) { | |
6456 | printf("failed in processing packet\n"); | |
6457 | goto end; | |
6458 | } | |
6459 | ||
6460 | if (((TcpSession *)p.flow->protoctx)->state != TCP_ESTABLISHED) { | |
6461 | printf("the TCP state should be TCP_ESTABLISEHD\n"); | |
6462 | goto end; | |
6463 | } | |
6464 | StreamTcpSessionClear(p.flow->protoctx); | |
6465 | ||
6466 | ret = 1; | |
6467 | end: | |
6468 | StreamTcpFreeConfig(TRUE); | |
6469 | return ret; | |
6470 | } | |
6471 | ||
6472 | /** | |
6473 | * \test Test the allocation of TCP session for a given packet when the same | |
6474 | * ports have been used to start the new session after resetting the | |
6475 | * previous session. | |
6476 | * | |
6477 | * \retval On success it returns 1 and on failure 0. | |
6478 | */ | |
6479 | ||
6480 | static int StreamTcpTest33 (void) { | |
6481 | Packet p; | |
6482 | Flow f; | |
6483 | ThreadVars tv; | |
6484 | StreamTcpThread stt; | |
6485 | TCPHdr tcph; | |
6486 | TcpReassemblyThreadCtx ra_ctx; | |
6487 | StreamMsgQueue stream_q; | |
6488 | memset(&stream_q, 0, sizeof(StreamMsgQueue)); | |
6489 | memset(&ra_ctx, 0, sizeof(TcpReassemblyThreadCtx)); | |
6490 | memset (&p, 0, sizeof(Packet)); | |
6491 | memset (&f, 0, sizeof(Flow)); | |
6492 | memset(&tv, 0, sizeof (ThreadVars)); | |
6493 | memset(&stt, 0, sizeof (StreamTcpThread)); | |
6494 | memset(&tcph, 0, sizeof (TCPHdr)); | |
6495 | p.flow = &f; | |
6496 | tcph.th_win = htons(5480); | |
6497 | tcph.th_flags = TH_SYN; | |
6498 | p.tcph = &tcph; | |
6499 | p.flowflags = FLOW_PKT_TOSERVER; | |
6500 | int ret = 0; | |
6501 | ra_ctx.stream_q = &stream_q; | |
6502 | stt.ra_ctx = &ra_ctx; | |
6503 | ||
6504 | StreamTcpInitConfig(TRUE); | |
6505 | ||
6506 | if (StreamTcpPacket(&tv, &p, &stt) == -1) | |
6507 | goto end; | |
6508 | ||
6509 | p.tcph->th_ack = htonl(1); | |
6510 | p.tcph->th_flags = TH_SYN | TH_ACK; | |
6511 | p.flowflags = FLOW_PKT_TOCLIENT; | |
6512 | ||
6513 | if (StreamTcpPacket(&tv, &p, &stt) == -1) | |
6514 | goto end; | |
6515 | ||
6516 | p.tcph->th_ack = htonl(1); | |
6517 | p.tcph->th_seq = htonl(1); | |
6518 | p.tcph->th_flags = TH_ACK; | |
6519 | p.flowflags = FLOW_PKT_TOSERVER; | |
6520 | ||
6521 | if (StreamTcpPacket(&tv, &p, &stt) == -1) | |
6522 | goto end; | |
6523 | ||
6524 | p.tcph->th_ack = htonl(1); | |
6525 | p.tcph->th_seq = htonl(1); | |
6526 | p.tcph->th_flags = TH_RST | TH_ACK; | |
6527 | p.flowflags = FLOW_PKT_TOSERVER; | |
6528 | ||
6529 | if (StreamTcpPacket(&tv, &p, &stt) == -1) | |
6530 | goto end; | |
6531 | ||
6532 | if (((TcpSession *)(p.flow->protoctx))->state != TCP_CLOSED) { | |
6533 | printf("Tcp session should have been closed\n"); | |
6534 | goto end; | |
6535 | } | |
6536 | ||
6537 | p.tcph->th_seq = htonl(1); | |
6538 | p.tcph->th_flags = TH_SYN; | |
6539 | p.flowflags = FLOW_PKT_TOSERVER; | |
6540 | ||
6541 | if (StreamTcpPacket(&tv, &p, &stt) == -1) | |
6542 | goto end; | |
6543 | ||
6544 | p.tcph->th_seq = htonl(1); | |
6545 | p.tcph->th_ack = htonl(2); | |
6546 | p.tcph->th_flags = TH_SYN | TH_ACK; | |
6547 | p.flowflags = FLOW_PKT_TOCLIENT; | |
6548 | ||
6549 | if (StreamTcpPacket(&tv, &p, &stt) == -1) | |
6550 | goto end; | |
6551 | ||
6552 | p.tcph->th_ack = htonl(2); | |
6553 | p.tcph->th_seq = htonl(2); | |
6554 | p.tcph->th_flags = TH_ACK; | |
6555 | p.flowflags = FLOW_PKT_TOSERVER; | |
6556 | ||
6557 | if (StreamTcpPacket(&tv, &p, &stt) == -1) | |
6558 | goto end; | |
6559 | ||
6560 | if (((TcpSession *)(p.flow->protoctx))->state != TCP_ESTABLISHED) { | |
6561 | printf("Tcp session should have been ESTABLISHED\n"); | |
6562 | goto end; | |
6563 | } | |
6564 | ||
6565 | ret = 1; | |
6566 | end: | |
6567 | StreamTcpSessionClear(p.flow->protoctx); | |
6568 | StreamTcpFreeConfig(TRUE); | |
6569 | return ret; | |
6570 | } | |
6571 | ||
6572 | /** | |
6573 | * \test Test the allocation of TCP session for a given packet when the SYN | |
6574 | * packet is sent with the PUSH flag set. | |
6575 | * | |
6576 | * \retval On success it returns 1 and on failure 0. | |
6577 | */ | |
6578 | ||
6579 | static int StreamTcpTest34 (void) { | |
6580 | Packet p; | |
6581 | Flow f; | |
6582 | ThreadVars tv; | |
6583 | StreamTcpThread stt; | |
6584 | TCPHdr tcph; | |
6585 | TcpReassemblyThreadCtx ra_ctx; | |
6586 | StreamMsgQueue stream_q; | |
6587 | memset(&stream_q, 0, sizeof(StreamMsgQueue)); | |
6588 | memset(&ra_ctx, 0, sizeof(TcpReassemblyThreadCtx)); | |
6589 | memset (&p, 0, sizeof(Packet)); | |
6590 | memset (&f, 0, sizeof(Flow)); | |
6591 | memset(&tv, 0, sizeof (ThreadVars)); | |
6592 | memset(&stt, 0, sizeof (StreamTcpThread)); | |
6593 | memset(&tcph, 0, sizeof (TCPHdr)); | |
6594 | p.flow = &f; | |
6595 | tcph.th_win = htons(5480); | |
6596 | tcph.th_flags = TH_SYN|TH_PUSH; | |
6597 | p.tcph = &tcph; | |
6598 | p.flowflags = FLOW_PKT_TOSERVER; | |
6599 | int ret = 0; | |
6600 | ra_ctx.stream_q = &stream_q; | |
6601 | stt.ra_ctx = &ra_ctx; | |
6602 | ||
6603 | StreamTcpInitConfig(TRUE); | |
6604 | ||
6605 | if (StreamTcpPacket(&tv, &p, &stt) == -1) | |
6606 | goto end; | |
6607 | ||
6608 | p.tcph->th_ack = htonl(1); | |
6609 | p.tcph->th_flags = TH_SYN | TH_ACK; | |
6610 | p.flowflags = FLOW_PKT_TOCLIENT; | |
6611 | ||
6612 | if (StreamTcpPacket(&tv, &p, &stt) == -1) | |
6613 | goto end; | |
6614 | ||
6615 | p.tcph->th_ack = htonl(1); | |
6616 | p.tcph->th_seq = htonl(1); | |
6617 | p.tcph->th_flags = TH_ACK; | |
6618 | p.flowflags = FLOW_PKT_TOSERVER; | |
6619 | ||
6620 | if (StreamTcpPacket(&tv, &p, &stt) == -1) | |
6621 | goto end; | |
6622 | ||
6623 | if (((TcpSession *)(p.flow->protoctx))->state != TCP_ESTABLISHED) { | |
6624 | printf("Tcp session should have been establisehd\n"); | |
6625 | goto end; | |
6626 | } | |
6627 | ||
6628 | ret = 1; | |
6629 | end: | |
6630 | StreamTcpSessionClear(p.flow->protoctx); | |
6631 | StreamTcpFreeConfig(TRUE); | |
6632 | return ret; | |
6633 | } | |
6634 | ||
6635 | /** | |
6636 | * \test Test the allocation of TCP session for a given packet when the SYN | |
6637 | * packet is sent with the URG flag set. | |
6638 | * | |
6639 | * \retval On success it returns 1 and on failure 0. | |
6640 | */ | |
6641 | ||
6642 | static int StreamTcpTest35 (void) { | |
6643 | Packet p; | |
6644 | Flow f; | |
6645 | ThreadVars tv; | |
6646 | StreamTcpThread stt; | |
6647 | TCPHdr tcph; | |
6648 | TcpReassemblyThreadCtx ra_ctx; | |
6649 | StreamMsgQueue stream_q; | |
6650 | memset(&stream_q, 0, sizeof(StreamMsgQueue)); | |
6651 | memset(&ra_ctx, 0, sizeof(TcpReassemblyThreadCtx)); | |
6652 | memset (&p, 0, sizeof(Packet)); | |
6653 | memset (&f, 0, sizeof(Flow)); | |
6654 | memset(&tv, 0, sizeof (ThreadVars)); | |
6655 | memset(&stt, 0, sizeof (StreamTcpThread)); | |
6656 | memset(&tcph, 0, sizeof (TCPHdr)); | |
6657 | p.flow = &f; | |
6658 | tcph.th_win = htons(5480); | |
6659 | tcph.th_flags = TH_SYN|TH_URG; | |
6660 | p.tcph = &tcph; | |
6661 | p.flowflags = FLOW_PKT_TOSERVER; | |
6662 | int ret = 0; | |
6663 | ra_ctx.stream_q = &stream_q; | |
6664 | stt.ra_ctx = &ra_ctx; | |
6665 | ||
6666 | StreamTcpInitConfig(TRUE); | |
6667 | ||
6668 | if (StreamTcpPacket(&tv, &p, &stt) == -1) | |
6669 | goto end; | |
6670 | ||
6671 | p.tcph->th_ack = htonl(1); | |
6672 | p.tcph->th_flags = TH_SYN | TH_ACK; | |
6673 | p.flowflags = FLOW_PKT_TOCLIENT; | |
6674 | ||
6675 | if (StreamTcpPacket(&tv, &p, &stt) == -1) | |
6676 | goto end; | |
6677 | ||
6678 | p.tcph->th_ack = htonl(1); | |
6679 | p.tcph->th_seq = htonl(1); | |
6680 | p.tcph->th_flags = TH_ACK; | |
6681 | p.flowflags = FLOW_PKT_TOSERVER; | |
6682 | ||
6683 | if (StreamTcpPacket(&tv, &p, &stt) == -1) | |
6684 | goto end; | |
6685 | ||
6686 | if (((TcpSession *)(p.flow->protoctx))->state != TCP_ESTABLISHED) { | |
6687 | printf("Tcp session should have been establisehd\n"); | |
6688 | goto end; | |
6689 | } | |
6690 | ||
6691 | ret = 1; | |
6692 | end: | |
6693 | StreamTcpSessionClear(p.flow->protoctx); | |
6694 | StreamTcpFreeConfig(TRUE); | |
6695 | return ret; | |
6696 | } | |
6697 | ||
6698 | /** | |
6699 | * \test Test the processing of PSH and URG flag in tcp session. | |
6700 | * | |
6701 | * \retval On success it returns 1 and on failure 0. | |
6702 | */ | |
6703 | static int StreamTcpTest36(void) { | |
6704 | Packet p; | |
6705 | Flow f; | |
6706 | ThreadVars tv; | |
6707 | StreamTcpThread stt; | |
6708 | uint8_t payload[4]; | |
6709 | TCPHdr tcph; | |
6710 | TcpReassemblyThreadCtx *ra_ctx = StreamTcpReassembleInitThreadCtx(); | |
6711 | int ret = 0; | |
6712 | ||
6713 | memset (&p, 0, sizeof(Packet)); | |
6714 | memset (&f, 0, sizeof(Flow)); | |
6715 | memset(&tv, 0, sizeof (ThreadVars)); | |
6716 | memset(&stt, 0, sizeof (StreamTcpThread)); | |
6717 | memset(&tcph, 0, sizeof (TCPHdr)); | |
6718 | ||
6719 | stt.ra_ctx = ra_ctx; | |
6720 | p.flow = &f; | |
6721 | tcph.th_win = htons(5480); | |
6722 | tcph.th_flags = TH_SYN; | |
6723 | p.tcph = &tcph; | |
6724 | p.flowflags = FLOW_PKT_TOSERVER; | |
6725 | ||
6726 | StreamTcpInitConfig(TRUE); | |
6727 | ||
6728 | if (StreamTcpPacket(&tv, &p, &stt) == -1) { | |
6729 | printf("failed in processing packet\n"); | |
6730 | goto end; | |
6731 | } | |
6732 | ||
6733 | p.tcph->th_ack = htonl(1); | |
6734 | p.tcph->th_flags = TH_SYN | TH_ACK; | |
6735 | p.flowflags = FLOW_PKT_TOCLIENT; | |
6736 | ||
6737 | if (StreamTcpPacket(&tv, &p, &stt) == -1 || (TcpSession *)p.flow->protoctx == NULL) { | |
6738 | printf("failed in processing packet\n"); | |
6739 | goto end; | |
6740 | } | |
6741 | ||
6742 | p.tcph->th_ack = htonl(1); | |
6743 | p.tcph->th_seq = htonl(1); | |
6744 | p.tcph->th_flags = TH_ACK; | |
6745 | p.flowflags = FLOW_PKT_TOSERVER; | |
6746 | ||
6747 | if (StreamTcpPacket(&tv, &p, &stt) == -1 || (TcpSession *)p.flow->protoctx == NULL) { | |
6748 | printf("failed in processing packet\n"); | |
6749 | goto end; | |
6750 | } | |
6751 | ||
6752 | if (((TcpSession *)p.flow->protoctx)->state != TCP_ESTABLISHED) { | |
6753 | printf("the TCP state should be TCP_ESTABLISEHD\n"); | |
6754 | goto end; | |
6755 | } | |
6756 | ||
6757 | p.tcph->th_ack = htonl(2); | |
6758 | p.tcph->th_seq = htonl(1); | |
6759 | p.tcph->th_flags = TH_PUSH | TH_ACK | TH_URG; | |
6760 | p.flowflags = FLOW_PKT_TOSERVER; | |
6761 | ||
6762 | StreamTcpCreateTestPacket(payload, 0x41, 3, 4); /*AAA*/ | |
6763 | p.payload = payload; | |
6764 | p.payload_len = 3; | |
6765 | ||
6766 | if (StreamTcpPacket(&tv, &p, &stt) == -1 || (TcpSession *)p.flow->protoctx == NULL) { | |
6767 | printf("failed in processing packet\n"); | |
6768 | goto end; | |
6769 | } | |
6770 | ||
6771 | if (((TcpSession *)p.flow->protoctx)->client.next_seq != 4) { | |
6772 | printf("the ssn->client.next_seq should be 4, but it is %"PRIu32"\n", | |
6773 | ((TcpSession *)p.flow->protoctx)->client.next_seq); | |
6774 | goto end; | |
6775 | } | |
6776 | ||
6777 | StreamTcpSessionClear(p.flow->protoctx); | |
6778 | ||
6779 | ret = 1; | |
6780 | end: | |
6781 | StreamTcpFreeConfig(TRUE); | |
6782 | return ret; | |
6783 | } | |
6784 | #endif | |
6785 | ||
6786 | /** | |
6787 | * \test Test the processing of out of order FIN packets in tcp session. | |
6788 | * | |
6789 | * \retval On success it returns 1 and on failure 0. | |
6790 | */ | |
6791 | static int StreamTcpTest37(void) { | |
6792 | Packet p; | |
6793 | Flow f; | |
6794 | ThreadVars tv; | |
6795 | StreamTcpThread stt; | |
6796 | uint8_t payload[4]; | |
6797 | TCPHdr tcph; | |
6798 | TcpReassemblyThreadCtx *ra_ctx = StreamTcpReassembleInitThreadCtx(); | |
6799 | int ret = 0; | |
6800 | ||
6801 | memset (&p, 0, sizeof(Packet)); | |
6802 | memset (&f, 0, sizeof(Flow)); | |
6803 | memset(&tv, 0, sizeof (ThreadVars)); | |
6804 | memset(&stt, 0, sizeof (StreamTcpThread)); | |
6805 | memset(&tcph, 0, sizeof (TCPHdr)); | |
6806 | ||
6807 | FLOW_INITIALIZE(&f); | |
6808 | ||
6809 | stt.ra_ctx = ra_ctx; | |
6810 | p.flow = &f; | |
6811 | tcph.th_win = htons(5480); | |
6812 | tcph.th_flags = TH_SYN; | |
6813 | p.tcph = &tcph; | |
6814 | p.flowflags = FLOW_PKT_TOSERVER; | |
6815 | ||
6816 | StreamTcpInitConfig(TRUE); | |
6817 | ||
6818 | if (StreamTcpPacket(&tv, &p, &stt) == -1) { | |
6819 | printf("failed in processing packet\n"); | |
6820 | goto end; | |
6821 | } | |
6822 | ||
6823 | p.tcph->th_ack = htonl(1); | |
6824 | p.tcph->th_flags = TH_SYN | TH_ACK; | |
6825 | p.flowflags = FLOW_PKT_TOCLIENT; | |
6826 | ||
6827 | if (StreamTcpPacket(&tv, &p, &stt) == -1 || (TcpSession *)p.flow->protoctx == NULL) { | |
6828 | printf("failed in processing packet\n"); | |
6829 | goto end; | |
6830 | } | |
6831 | ||
6832 | p.tcph->th_ack = htonl(1); | |
6833 | p.tcph->th_seq = htonl(1); | |
6834 | p.tcph->th_flags = TH_ACK; | |
6835 | p.flowflags = FLOW_PKT_TOSERVER; | |
6836 | ||
6837 | if (StreamTcpPacket(&tv, &p, &stt) == -1 || (TcpSession *)p.flow->protoctx == NULL) { | |
6838 | printf("failed in processing packet\n"); | |
6839 | goto end; | |
6840 | } | |
6841 | ||
6842 | if (((TcpSession *)p.flow->protoctx)->state != TCP_ESTABLISHED) { | |
6843 | printf("the TCP state should be TCP_ESTABLISEHD\n"); | |
6844 | goto end; | |
6845 | } | |
6846 | ||
6847 | p.tcph->th_ack = htonl(2); | |
6848 | p.tcph->th_seq = htonl(4); | |
6849 | p.tcph->th_flags = TH_ACK|TH_FIN; | |
6850 | p.flowflags = FLOW_PKT_TOSERVER; | |
6851 | ||
6852 | if (StreamTcpPacket(&tv, &p, &stt) == -1 || (TcpSession *)p.flow->protoctx == NULL) { | |
6853 | printf("failed in processing packet\n"); | |
6854 | goto end; | |
6855 | } | |
6856 | ||
6857 | if (((TcpSession *)p.flow->protoctx)->state != TCP_CLOSE_WAIT) { | |
6858 | printf("the TCP state should be TCP_CLOSE_WAIT\n"); | |
6859 | goto end; | |
6860 | } | |
6861 | ||
6862 | p.tcph->th_ack = htonl(1); | |
6863 | p.tcph->th_seq = htonl(1); | |
6864 | p.tcph->th_flags = TH_PUSH | TH_ACK; | |
6865 | p.flowflags = FLOW_PKT_TOSERVER; | |
6866 | ||
6867 | StreamTcpCreateTestPacket(payload, 0x41, 3, 4); /*AAA*/ | |
6868 | p.payload = payload; | |
6869 | p.payload_len = 3; | |
6870 | ||
6871 | if (StreamTcpPacket(&tv, &p, &stt) == -1 || (TcpSession *)p.flow->protoctx == NULL) { | |
6872 | printf("failed in processing packet\n"); | |
6873 | goto end; | |
6874 | } | |
6875 | ||
6876 | p.tcph->th_ack = htonl(4); | |
6877 | p.tcph->th_seq = htonl(2); | |
6878 | p.tcph->th_flags = TH_ACK; | |
6879 | p.payload_len = 0; | |
6880 | p.flowflags = FLOW_PKT_TOCLIENT; | |
6881 | ||
6882 | if (StreamTcpPacket(&tv, &p, &stt) == -1 || (TcpSession *)p.flow->protoctx == NULL) { | |
6883 | printf("failed in processing packet\n"); | |
6884 | goto end; | |
6885 | } | |
6886 | ||
6887 | if (((TcpSession *)p.flow->protoctx)->client.tmp_ra_base_seq != 3) { | |
6888 | printf("the ssn->client.next_seq should be 3, but it is %"PRIu32"\n", | |
6889 | ((TcpSession *)p.flow->protoctx)->client.tmp_ra_base_seq); | |
6890 | goto end; | |
6891 | } | |
6892 | ||
6893 | StreamTcpSessionClear(p.flow->protoctx); | |
6894 | ||
6895 | ret = 1; | |
6896 | end: | |
6897 | StreamTcpFreeConfig(TRUE); | |
6898 | return ret; | |
6899 | } | |
5806 | 6900 | #endif /* UNITTESTS */ |
5807 | 6901 | |
5808 | 6902 | void StreamTcpRegisterTests (void) { |
5854 | 6948 | UtRegisterTest("StreamTcpTest27 -- test ecn/cwr sessions", |
5855 | 6949 | StreamTcpTest27, 1); |
5856 | 6950 | UtRegisterTest("StreamTcpTest28 -- Memcap Test", StreamTcpTest28, 1); |
6951 | #if 0 /* VJ 2010/09/01 disabled since they blow up on Fedora and Fedora is | |
6952 | * right about blowing up. The checksum functions are not used properly | |
6953 | * in the tests. */ | |
6954 | UtRegisterTest("StreamTcpTest29 -- Badchecksum Reset Test", StreamTcpTest29, 1); | |
6955 | UtRegisterTest("StreamTcpTest30 -- Badchecksum Overlap Test", StreamTcpTest30, 1); | |
6956 | UtRegisterTest("StreamTcpTest31 -- MultipleSyns Test", StreamTcpTest31, 1); | |
6957 | UtRegisterTest("StreamTcpTest32 -- Bogus CWR Test", StreamTcpTest32, 1); | |
6958 | UtRegisterTest("StreamTcpTest33 -- RST-SYN Again Test", StreamTcpTest33, 1); | |
6959 | UtRegisterTest("StreamTcpTest34 -- SYN-PUSH Test", StreamTcpTest34, 1); | |
6960 | UtRegisterTest("StreamTcpTest35 -- SYN-URG Test", StreamTcpTest35, 1); | |
6961 | UtRegisterTest("StreamTcpTest36 -- PUSH-URG Test", StreamTcpTest36, 1); | |
6962 | #endif | |
6963 | UtRegisterTest("StreamTcpTest37 -- Out of order FIN Test", StreamTcpTest37, 1); | |
5857 | 6964 | |
5858 | 6965 | /* set up the reassembly tests as well */ |
5859 | 6966 | StreamTcpReassembleRegisterTests(); |
34 | 34 | #include "stream-tcp-reassemble.h" |
35 | 35 | |
36 | 36 | #define STREAM_VERBOSE FALSE |
37 | /* Flag to indicate that the checksum validation for the stream engine | |
38 | has been enabled */ | |
39 | #define STREAMTCP_INIT_FLAG_CHECKSUM_VALIDATION 0x01 | |
40 | ||
37 | 41 | /*global flow data*/ |
38 | 42 | typedef struct TcpStreamCnf_ { |
39 | 43 | uint32_t memcap; /** max stream mem usage */ |
43 | 47 | int async_oneside; |
44 | 48 | uint32_t reassembly_memcap; /**< max memory usage for stream reassembly */ |
45 | 49 | uint32_t reassembly_depth; /**< Depth until when we reassemble the stream */ |
50 | uint8_t flags; | |
46 | 51 | } TcpStreamCnf; |
47 | 52 | |
48 | 53 | TcpStreamCnf stream_config; |
51 | 56 | void StreamTcpFreeConfig(char); |
52 | 57 | void StreamTcpRegisterTests (void); |
53 | 58 | |
59 | void StreamTcpSessionPktFree (Packet *); | |
60 | ||
54 | 61 | void StreamTcpIncrMemuse(uint32_t); |
55 | 62 | void StreamTcpDecrMemuse(uint32_t); |
56 | 63 | int StreamTcpCheckMemcap(uint32_t); |
57 | 64 | |
65 | ||
66 | /** ------- Inline functions: ------ */ | |
67 | ||
68 | /** | |
69 | * \brief If we are on IPS mode, and got a drop action triggered from | |
70 | * the IP only module, or from a reassembled msg and/or from an | |
71 | * applayer detection, then drop the rest of the packets of the | |
72 | * same stream and avoid inspecting it any further | |
73 | * \param p pointer to the Packet to check | |
74 | * \retval 1 if we must drop this stream | |
75 | * \retval 0 if the stream still legal | |
76 | */ | |
77 | static inline int StreamTcpCheckFlowDrops(Packet *p) { | |
78 | extern uint8_t engine_mode; | |
79 | /* If we are on IPS mode, and got a drop action triggered from | |
80 | * the IP only module, or from a reassembled msg and/or from an | |
81 | * applayer detection, then drop the rest of the packets of the | |
82 | * same stream and avoid inspecting it any further */ | |
83 | if (IS_ENGINE_MODE_IPS(engine_mode) && (p->flow->flags & FLOW_ACTION_DROP)) | |
84 | return 1; | |
85 | ||
86 | return 0; | |
87 | } | |
88 | ||
89 | /** | |
90 | * \brief Function to flip the direction When we missed the SYN packet, | |
91 | * SYN/ACK is considered as sent by server, but our engine flagged the | |
92 | * packet as from client for the host whose packet is received first in | |
93 | * the session. | |
94 | * | |
95 | * \param ssn TcpSession to whom this packet belongs | |
96 | * \param p Packet whose flag has to be changed | |
97 | */ | |
98 | static inline void StreamTcpPacketSwitchDir(TcpSession *ssn, Packet *p) | |
99 | { | |
100 | SCLogDebug("ssn %p: switching pkt direction", ssn); | |
101 | ||
102 | if (PKT_IS_TOSERVER(p)) { | |
103 | p->flowflags &= ~FLOW_PKT_TOSERVER; | |
104 | p->flowflags |= FLOW_PKT_TOCLIENT; | |
105 | } else { | |
106 | p->flowflags &= ~FLOW_PKT_TOCLIENT; | |
107 | p->flowflags |= FLOW_PKT_TOSERVER; | |
108 | } | |
109 | } | |
110 | ||
111 | ||
58 | 112 | #endif /* __STREAM_TCP_H__ */ |
59 | 113 |
33 | 33 | #include "threads.h" |
34 | 34 | #include "threadvars.h" |
35 | 35 | |
36 | #include "util-atomic.h" | |
36 | 37 | #include "util-spm.h" |
37 | 38 | #include "util-hash.h" |
38 | 39 | #include "util-hashlist.h" |
105 | 106 | #include "app-layer-htp.h" |
106 | 107 | #include "app-layer-ftp.h" |
107 | 108 | #include "app-layer-ssl.h" |
109 | #include "app-layer-ssh.h" | |
108 | 110 | |
109 | 111 | #include "util-radix-tree.h" |
110 | 112 | #include "util-host-os-info.h" |
140 | 142 | #include "tmqh-packetpool.h" |
141 | 143 | |
142 | 144 | #include "util-ringbuffer.h" |
145 | #include "util-mem.h" | |
143 | 146 | |
144 | 147 | /* |
145 | 148 | * we put this here, because we only use it here in main. |
148 | 151 | volatile sig_atomic_t sighup_count = 0; |
149 | 152 | volatile sig_atomic_t sigterm_count = 0; |
150 | 153 | |
154 | /* | |
155 | * Flag to indicate if the engine is at the initialization | |
156 | * or already processing packets. 2 stages: SURICATA_INIT, | |
157 | * SURICATA_RUNTIME and SURICATA_FINALIZE | |
158 | */ | |
159 | SC_ATOMIC_DECLARE(unsigned int, engine_stage); | |
160 | ||
151 | 161 | /* Max packets processed simultaniously. */ |
152 | 162 | #define DEFAULT_MAX_PENDING_PACKETS 50 |
153 | 163 | |
156 | 166 | |
157 | 167 | /** Run mode selected */ |
158 | 168 | int run_mode = MODE_UNKNOWN; |
169 | ||
170 | /** Engine mode: inline (ENGINE_MODE_IPS) or just | |
171 | * detection mode (ENGINE_MODE_IDS by default) */ | |
172 | uint8_t engine_mode = ENGINE_MODE_IDS; | |
159 | 173 | |
160 | 174 | /** Maximum packets to simultaneously process. */ |
161 | 175 | intmax_t max_pending_packets; |
370 | 384 | |
371 | 385 | sc_set_caps = FALSE; |
372 | 386 | |
387 | SC_ATOMIC_INIT(engine_stage); | |
388 | ||
373 | 389 | /* initialize the logging subsys */ |
374 | 390 | SCLogInitLogModule(NULL); |
391 | ||
392 | /* By default use IDS mode, but if nfq or ipfw | |
393 | * are specified, IPS mode will overwrite this */ | |
394 | SET_ENGINE_MODE_IDS(engine_mode); | |
375 | 395 | |
376 | 396 | #ifdef OS_WIN32 |
377 | 397 | /* service initialization */ |
614 | 634 | #ifdef NFQ |
615 | 635 | if (run_mode == MODE_UNKNOWN) { |
616 | 636 | run_mode = MODE_NFQ; |
637 | SET_ENGINE_MODE_IPS(engine_mode); | |
617 | 638 | } else { |
618 | 639 | SCLogError(SC_ERR_MULTIPLE_RUN_MODE, "more than one run mode " |
619 | 640 | "has been specified"); |
630 | 651 | #ifdef IPFW |
631 | 652 | if (run_mode == MODE_UNKNOWN) { |
632 | 653 | run_mode = MODE_IPFW; |
654 | SET_ENGINE_MODE_IPS(engine_mode); | |
633 | 655 | } else { |
634 | 656 | SCLogError(SC_ERR_MULTIPLE_RUN_MODE, "more than one run mode " |
635 | 657 | "has been specified"); |
827 | 849 | RegisterDCERPCUDPParsers(); |
828 | 850 | RegisterFTPParsers(); |
829 | 851 | RegisterSSLParsers(); |
852 | RegisterSSHParsers(); | |
830 | 853 | AppLayerParsersInitPostProcess(); |
831 | 854 | |
832 | 855 | #ifdef UNITTESTS |
863 | 886 | DecodeVLANRegisterTests(); |
864 | 887 | HTPParserRegisterTests(); |
865 | 888 | TLSParserRegisterTests(); |
889 | SSHParserRegisterTests(); | |
866 | 890 | SMBParserRegisterTests(); |
867 | 891 | DCERPCParserRegisterTests(); |
868 | 892 | DCERPCUDPParserRegisterTests(); |
1104 | 1128 | exit(EXIT_FAILURE); |
1105 | 1129 | } |
1106 | 1130 | |
1131 | SC_ATOMIC_CAS(&engine_stage, SURICATA_INIT, SURICATA_RUNTIME); | |
1132 | ||
1107 | 1133 | /* Un-pause all the paused threads */ |
1108 | 1134 | TmThreadContinueThreads(); |
1109 | 1135 | |
1161 | 1187 | |
1162 | 1188 | usleep(100); |
1163 | 1189 | } |
1190 | ||
1191 | /* Update the engine stage/status flag */ | |
1192 | SC_ATOMIC_CAS(&engine_stage, SURICATA_RUNTIME, SURICATA_DEINIT); | |
1164 | 1193 | |
1165 | 1194 | |
1166 | 1195 | FlowShutdown(); |
1231 | 1260 | return 0; |
1232 | 1261 | } |
1233 | 1262 | #endif /* OS_WIN32 */ |
1263 | ||
1264 | SC_ATOMIC_DESTROY(engine_stage); | |
1234 | 1265 | exit(EXIT_SUCCESS); |
1235 | 1266 | } |
29 | 29 | |
30 | 30 | /* the name of our binary */ |
31 | 31 | #define PROG_NAME "Suricata" |
32 | #define PROG_VER "1.0.1" | |
32 | #define PROG_VER "1.0.2" | |
33 | 33 | |
34 | 34 | /* runtime engine control flags */ |
35 | 35 | #define SURICATA_STOP 0x01 /**< gracefully stop the engine: process all |
49 | 49 | MODE_ERF_FILE, |
50 | 50 | MODE_DAG, |
51 | 51 | }; |
52 | ||
53 | /* Engine stage/status*/ | |
54 | enum { | |
55 | SURICATA_INIT = 0, | |
56 | SURICATA_RUNTIME, | |
57 | SURICATA_DEINIT | |
58 | }; | |
59 | ||
60 | /* Engine is acting as */ | |
61 | enum { | |
62 | ENGINE_MODE_IDS, | |
63 | ENGINE_MODE_IPS, | |
64 | }; | |
65 | ||
66 | /** You can use this macros to set/check if we have real drop capabilities */ | |
67 | #define SET_ENGINE_MODE_IPS(engine_mode) (engine_mode = ENGINE_MODE_IPS); | |
68 | #define SET_ENGINE_MODE_IDS(engine_mode) (engine_mode = ENGINE_MODE_IDS); | |
69 | #define IS_ENGINE_MODE_IPS(engine_mode) (engine_mode == ENGINE_MODE_IPS) | |
70 | #define IS_ENGINE_MODE_IDS(engine_mode) (engine_mode == ENGINE_MODE_IDS) | |
52 | 71 | |
53 | 72 | /* queue's between various other threads |
54 | 73 | * XXX move to the TmQueue structure later |
43 | 43 | #include "tmqh-packetpool.h" |
44 | 44 | |
45 | 45 | #include "util-ringbuffer.h" |
46 | #include "util-debug.h" | |
47 | #include "util-error.h" | |
46 | 48 | |
47 | 49 | static RingBuffer16 *ringbuffer = NULL; |
48 | ||
50 | /** | |
51 | * \brief TmqhPacketpoolRegister | |
52 | * \initonly | |
53 | */ | |
49 | 54 | void TmqhPacketpoolRegister (void) { |
50 | 55 | tmqh_table[TMQH_PACKETPOOL].name = "packetpool"; |
51 | 56 | tmqh_table[TMQH_PACKETPOOL].InHandler = TmqhInputPacketpool; |
52 | 57 | tmqh_table[TMQH_PACKETPOOL].OutHandler = TmqhOutputPacketpool; |
53 | 58 | |
54 | 59 | ringbuffer = RingBufferInit(); |
60 | if (ringbuffer == NULL) { | |
61 | SCLogError(SC_ERR_FATAL, "Error registering Packet pool handler (at ring buffer init)"); | |
62 | exit(EXIT_FAILURE); | |
63 | } | |
55 | 64 | } |
56 | 65 | |
57 | 66 | int PacketPoolIsEmpty(void) { |
42 | 42 | void TmqhOutputRingBufferSrMw(ThreadVars *t, Packet *p); |
43 | 43 | void TmqhInputRingBufferShutdownHandler(ThreadVars *); |
44 | 44 | |
45 | /** | |
46 | * \brief TmqhRingBufferRegister | |
47 | * \initonly | |
48 | */ | |
45 | 49 | void TmqhRingBufferRegister (void) { |
46 | 50 | tmqh_table[TMQH_RINGBUFFER_MRSW].name = "ringbuffer_mrsw"; |
47 | 51 | tmqh_table[TMQH_RINGBUFFER_MRSW].InHandler = TmqhInputRingBufferMrSw; |
63 | 67 | int i = 0; |
64 | 68 | for (i = 0; i < 256; i++) { |
65 | 69 | ringbuffers[i] = RingBuffer8Init(); |
70 | if (ringbuffers[i] == NULL) { | |
71 | SCLogError(SC_ERR_FATAL, "Error allocating memory to register Ringbuffers. Exiting..."); | |
72 | exit(EXIT_FAILURE); | |
73 | } | |
66 | 74 | } |
67 | 75 | } |
68 | 76 |
51 | 51 | #define SC_ATOMIC_DECLARE(type, name) \ |
52 | 52 | type name ## _sc_atomic__; \ |
53 | 53 | SCSpinlock name ## _sc_lock__ |
54 | ||
55 | /** | |
56 | * \brief wrapper to reference an atomic variable already declared on another file (including the spin lock) | |
57 | * | |
58 | */ | |
59 | #define SC_ATOMIC_EXTERN(type, name) \ | |
60 | extern type name ## _sc_atomic__; \ | |
61 | extern SCSpinlock name ## _sc_lock__ | |
54 | 62 | |
55 | 63 | /** |
56 | 64 | * \brief wrapper to declare an atomic variable including a (spin) lock |
199 | 207 | type name ## _sc_atomic__ |
200 | 208 | |
201 | 209 | /** |
210 | * \brief wrapper for referencing an atomic variable declared on another file. | |
211 | * | |
212 | * \warning Only char, short, int, long, long long and their unsigned | |
213 | * versions are supported. | |
214 | * | |
215 | * \param type Type of the variable (char, short, int, long, long long) | |
216 | * \param name Name of the variable. | |
217 | * | |
218 | * We just declare the variable here as we rely on atomic operations | |
219 | * to modify it, so no need for locks. | |
220 | * | |
221 | */ | |
222 | #define SC_ATOMIC_EXTERN(type, name) \ | |
223 | extern type name ## _sc_atomic__ | |
224 | ||
225 | /** | |
202 | 226 | * \brief wrapper for declaring an atomic variable and initializing it. |
203 | 227 | **/ |
204 | 228 | #define SC_ATOMIC_DECL_AND_INIT(type, name) \ |
523 | 523 | * \brief Returns a new output_interface_context |
524 | 524 | * |
525 | 525 | * \retval iface_ctx Pointer to a newly allocated output_interface_context |
526 | * \initonly | |
526 | 527 | */ |
527 | 528 | static inline SCLogOPIfaceCtx *SCLogAllocLogOPIfaceCtx() |
528 | 529 | { |
546 | 547 | * \param log_level Override of the global_log_level by this interface |
547 | 548 | * |
548 | 549 | * \retval iface_ctx Pointer to the file output interface context created |
550 | * \initonly | |
549 | 551 | */ |
550 | 552 | static inline SCLogOPIfaceCtx *SCLogInitFileOPIface(const char *file, |
551 | 553 | const char *log_format, |
606 | 608 | * \param log_level Override of the global_log_level by this interface |
607 | 609 | * |
608 | 610 | * \retval iface_ctx Pointer to the console output interface context created |
611 | * \initonly | |
609 | 612 | */ |
610 | 613 | static inline SCLogOPIfaceCtx *SCLogInitConsoleOPIface(const char *log_format, |
611 | 614 | SCLogLevel log_level) |
933 | 936 | * conf file |
934 | 937 | * |
935 | 938 | * \retval sc_lid Pointer to the newly created SCLogInitData |
939 | * \initonly | |
936 | 940 | */ |
937 | 941 | SCLogInitData *SCLogAllocLogInitData(void) |
938 | 942 | { |
939 | 943 | SCLogInitData *sc_lid = NULL; |
940 | 944 | |
941 | if ( (sc_lid = malloc(sizeof(SCLogInitData))) == NULL) | |
942 | return NULL; | |
945 | if ( (sc_lid = malloc(sizeof(SCLogInitData))) == NULL) { | |
946 | SCLogError(SC_ERR_FATAL, "Fatal error encountered initializing the logging subsytem. Out of memory. Exiting..."); | |
947 | exit(EXIT_FAILURE); | |
948 | } | |
943 | 949 | memset(sc_lid, 0, sizeof(SCLogInitData)); |
944 | 950 | |
945 | 951 | return sc_lid; |
1072 | 1078 | * \param sc_lid The initialization data for the logging module. If sc_lid is |
1073 | 1079 | * NULL, we would stick to the default configuration for the |
1074 | 1080 | * logging subsystem. |
1075 | * | |
1081 | * \initonly | |
1076 | 1082 | */ |
1077 | 1083 | void SCLogInitLogModule(SCLogInitData *sc_lid) |
1078 | 1084 | { |
76 | 76 | CASE_CODE (SC_ERR_COUNTER_EXCEEDED); |
77 | 77 | CASE_CODE (SC_ERR_INVALID_CHECKSUM); |
78 | 78 | CASE_CODE (SC_ERR_SPRINTF); |
79 | CASE_CODE (SC_ERR_FATAL); | |
79 | 80 | CASE_CODE (SC_ERR_INVALID_ARGUMENT); |
80 | 81 | CASE_CODE (SC_ERR_SPINLOCK); |
81 | 82 | CASE_CODE (SC_ERR_INVALID_ENUM_MAP); |
75 | 75 | struct in_addr *addr = NULL; |
76 | 76 | |
77 | 77 | if ( (addr = SCMalloc(sizeof(struct in_addr))) == NULL) { |
78 | SCLogError(SC_ERR_FATAL, "Fatal error encountered in SCHInfoValidateIPV4Address. Exiting..."); | |
79 | exit(EXIT_FAILURE); | |
78 | SCLogError(SC_ERR_MEM_ALLOC, "Fatal error encountered in SCHInfoValidateIPV4Address. Mem not allocated"); | |
79 | return NULL; | |
80 | 80 | } |
81 | 81 | |
82 | 82 | if (inet_pton(AF_INET, addr_str, addr) <= 0) { |
123 | 123 | * \retval user_data On success, pointer to the user_data that has to be sent |
124 | 124 | * along with the key, to be added to the Radix tree; NULL on |
125 | 125 | * failure |
126 | * \initonly | |
126 | 127 | */ |
127 | 128 | static void *SCHInfoAllocUserDataOSPolicy(const char *host_os) |
128 | 129 | { |
129 | 130 | int *user_data = NULL; |
130 | 131 | |
131 | if ( (user_data = SCMalloc(sizeof(int))) == NULL) | |
132 | return NULL; | |
132 | if ( (user_data = SCMalloc(sizeof(int))) == NULL) { | |
133 | SCLogError(SC_ERR_FATAL, "Error allocating memory. Exiting"); | |
134 | exit(EXIT_FAILURE); | |
135 | } | |
133 | 136 | |
134 | 137 | /* the host os flavour that has to be sent as user data */ |
135 | 138 | if ( (*user_data = SCMapEnumNameToValue(host_os, sc_hinfo_os_policy_map)) == -1) { |
198 | 201 | * |
199 | 202 | * \retval 0 On successfully adding the host os info to the Radix tree |
200 | 203 | * \retval -1 On failure |
204 | * \initonly (only specified from config, at the startup) | |
201 | 205 | */ |
202 | 206 | int SCHInfoAddHostOSInfo(char *host_os, char *host_os_ip_range, int is_ipv4) |
203 | 207 | { |
394 | 398 | |
395 | 399 | /** |
396 | 400 | * \brief Load the host os policy information from the configuration. |
401 | * | |
402 | * \initonly (A mem alloc error should cause an exit failure) | |
397 | 403 | */ |
398 | 404 | void SCHInfoLoadFromConfig(void) |
399 | 405 | { |
29 | 29 | #ifndef __UTIL_MEM_H__ |
30 | 30 | #define __UTIL_MEM_H__ |
31 | 31 | |
32 | #include "util-atomic.h" | |
33 | SC_ATOMIC_EXTERN(unsigned int, engine_stage); | |
34 | ||
32 | 35 | /* Use this only if you want to debug memory allocation and free() |
33 | 36 | * It will log a lot of lines more, so think that is a performance killer */ |
34 | 37 | |
48 | 51 | ptrmem = malloc(a); \ |
49 | 52 | if (ptrmem == NULL && a > 0) { \ |
50 | 53 | SCLogError(SC_ERR_MEM_ALLOC, "SCMalloc failed: %s, while trying " \ |
51 | "to allocate %"PRIdMAX" bytes", strerror(errno), (intmax_t)a); \ | |
54 | "to allocate %"PRIuMAX" bytes", strerror(errno), (uintmax_t)a); \ | |
55 | if (SC_ATOMIC_GET(engine_stage) == SURICATA_INIT) {\ | |
56 | SCLogError(SC_ERR_FATAL, "Out of memory. The engine cannot be initialized. Exiting..."); \ | |
57 | exit(EXIT_FAILURE); \ | |
58 | } \ | |
52 | 59 | } \ |
53 | 60 | \ |
54 | 61 | global_mem += a; \ |
55 | 62 | if (print_mem_flag == 1) \ |
56 | SCLogInfo("SCMalloc return at %p of size %"PRIdMAX, \ | |
57 | ptrmem, (intmax_t)a); \ | |
63 | SCLogInfo("SCMalloc return at %p of size %"PRIuMAX, \ | |
64 | ptrmem, (uintmax_t)a); \ | |
58 | 65 | \ |
59 | 66 | (void*)ptrmem; \ |
60 | 67 | }) |
67 | 74 | ptrmem = realloc(x, a); \ |
68 | 75 | if (ptrmem == NULL && a > 0) { \ |
69 | 76 | SCLogError(SC_ERR_MEM_ALLOC, "SCRealloc failed: %s, while trying " \ |
70 | "to allocate %"PRIdMAX" bytes", strerror(errno), (intmax_t)a); \ | |
77 | "to allocate %"PRIuMAX" bytes", strerror(errno), (uintmax_t)a); \ | |
78 | if (SC_ATOMIC_GET(engine_stage) == SURICATA_INIT) {\ | |
79 | SCLogError(SC_ERR_FATAL, "Out of memory. The engine cannot be initialized. Exiting..."); \ | |
80 | exit(EXIT_FAILURE); \ | |
81 | } \ | |
71 | 82 | } \ |
72 | 83 | \ |
73 | 84 | global_mem += a; \ |
74 | 85 | if (print_mem_flag == 1) \ |
75 | SCLogInfo("SCRealloc return at %p (old:%p) of size %"PRIdMAX, \ | |
76 | ptrmem, x, (intmax_t)a); \ | |
86 | SCLogInfo("SCRealloc return at %p (old:%p) of size %"PRIuMAX, \ | |
87 | ptrmem, x, (uintmax_t)a); \ | |
77 | 88 | \ |
78 | 89 | (void*)ptrmem; \ |
79 | 90 | }) |
86 | 97 | ptrmem = calloc(nm, a); \ |
87 | 98 | if (ptrmem == NULL && a > 0) { \ |
88 | 99 | SCLogError(SC_ERR_MEM_ALLOC, "SCCalloc failed: %s, while trying " \ |
89 | "to allocate %"PRIdMAX" bytes", strerror(errno), (intmax_t)a); \ | |
100 | "to allocate %"PRIuMAX" bytes", strerror(errno), (uintmax_t)a); \ | |
101 | if (SC_ATOMIC_GET(engine_stage) == SURICATA_INIT) {\ | |
102 | SCLogError(SC_ERR_FATAL, "Out of memory. The engine cannot be initialized. Exiting..."); \ | |
103 | exit(EXIT_FAILURE); \ | |
104 | } \ | |
90 | 105 | } \ |
91 | 106 | \ |
92 | 107 | global_mem += a*nm; \ |
93 | 108 | if (print_mem_flag == 1) \ |
94 | SCLogInfo("SCCalloc return at %p of size %"PRIdMAX" nm %"PRIdMAX, \ | |
95 | ptrmem, (intmax_t)a, (intmax_t)nm); \ | |
109 | SCLogInfo("SCCalloc return at %p of size %"PRIuMAX" nm %"PRIuMAX, \ | |
110 | ptrmem, (uintmax_t)a, (uintmax_t)nm); \ | |
96 | 111 | \ |
97 | 112 | (void*)ptrmem; \ |
98 | 113 | }) |
101 | 116 | char *ptrmem = NULL; \ |
102 | 117 | extern size_t global_mem; \ |
103 | 118 | extern uint8_t print_mem_flag; \ |
104 | size_t len = strlen(a); \ | |
105 | 119 | \ |
106 | 120 | ptrmem = strdup(a); \ |
107 | if (ptrmem == NULL && len > 0) { \ | |
121 | if (ptrmem == NULL) { \ | |
122 | size_t len = strlen(a); \ | |
108 | 123 | SCLogError(SC_ERR_MEM_ALLOC, "SCStrdup failed: %s, while trying " \ |
109 | "to allocate %"PRIu64" bytes", strerror(errno), (intmax_t)len); \ | |
124 | "to allocate %"PRIuMAX" bytes", strerror(errno), (uintmax_t)len); \ | |
125 | if (SC_ATOMIC_GET(engine_stage) == SURICATA_INIT) {\ | |
126 | SCLogError(SC_ERR_FATAL, "Out of memory. The engine cannot be initialized. Exiting..."); \ | |
127 | exit(EXIT_FAILURE); \ | |
128 | } \ | |
110 | 129 | } \ |
111 | 130 | \ |
112 | 131 | global_mem += len; \ |
113 | 132 | if (print_mem_flag == 1) \ |
114 | SCLogInfo("SCStrdup return at %p of size %"PRIdMAX, \ | |
115 | ptrmem, (intmax_t)len); \ | |
133 | SCLogInfo("SCStrdup return at %p of size %"PRIuMAX, \ | |
134 | ptrmem, (uintmax_t)len); \ | |
116 | 135 | \ |
117 | 136 | (void*)ptrmem; \ |
118 | 137 | }) |
125 | 144 | }) |
126 | 145 | |
127 | 146 | #else /* DBG_MEM_ALLOC */ |
128 | ||
129 | 147 | #if 0 |
130 | #define SCMalloc(a) ({ \ | |
131 | void *ptrmem = malloc(a); \ | |
132 | if (ptrmem == NULL) { \ | |
133 | SCLogError(SC_ERR_MEM_ALLOC, "SCMalloc failed: %s, while trying " \ | |
134 | "to allocate %"PRIdMAX" bytes", strerror(errno), (intmax_t)a); \ | |
135 | } \ | |
136 | (void*)ptrmem; \ | |
137 | }) | |
138 | ||
139 | #define SCRealloc(x, a) ({ \ | |
140 | void *ptrmem = realloc(x, a); \ | |
141 | if (ptrmem == NULL) { \ | |
142 | SCLogError(SC_ERR_MEM_ALLOC, "SCRealloc failed: %s, while trying " \ | |
143 | "to allocate %"PRIdMAX" bytes", strerror(errno), (intmax_t)a); \ | |
144 | } \ | |
145 | (void*)ptrmem; \ | |
146 | }) | |
147 | ||
148 | #define SCCalloc(nm, a) ({ \ | |
149 | void *ptrmem = calloc(nm, a); \ | |
150 | if (ptrmem == NULL) { \ | |
151 | SCLogError(SC_ERR_MEM_ALLOC, "SCCalloc failed: %s, while trying " \ | |
152 | "to allocate %"PRIdMAX" bytes", strerror(errno), (intmax_t)a); \ | |
153 | } \ | |
154 | (void*)ptrmem; \ | |
155 | }) | |
156 | ||
157 | #define SCStrdup(a) ({ \ | |
158 | char *ptrmem = strdup(a); \ | |
159 | if (ptrmem == NULL) { \ | |
160 | SCLogError(SC_ERR_MEM_ALLOC, "SCStrdup failed: %s, while trying " \ | |
161 | "to allocate %"PRIdMAX" bytes", strerror(errno), (intmax_t)strlen(a)); \ | |
162 | } \ | |
163 | (void*)ptrmem; \ | |
164 | }) | |
165 | #endif | |
148 | /* without any checks */ | |
166 | 149 | #define SCMalloc malloc |
167 | 150 | #define SCRealloc realloc |
168 | 151 | #define SCCalloc calloc |
169 | 152 | #define SCStrdup strdup |
170 | ||
171 | 153 | #define SCFree(a) free((a)) |
154 | #endif | |
155 | ||
156 | #define SCMalloc(a) ({ \ | |
157 | void *ptrmem = NULL; \ | |
158 | \ | |
159 | ptrmem = malloc(a); \ | |
160 | if (ptrmem == NULL && a > 0) { \ | |
161 | SCLogError(SC_ERR_MEM_ALLOC, "SCMalloc failed: %s, while trying " \ | |
162 | "to allocate %"PRIuMAX" bytes", strerror(errno), (uintmax_t)a); \ | |
163 | if (SC_ATOMIC_GET(engine_stage) == SURICATA_INIT) {\ | |
164 | SCLogError(SC_ERR_FATAL, "Out of memory. The engine cannot be initialized. Exiting..."); \ | |
165 | exit(EXIT_FAILURE); \ | |
166 | } \ | |
167 | } \ | |
168 | (void*)ptrmem; \ | |
169 | }) | |
170 | ||
171 | #define SCRealloc(x, a) ({ \ | |
172 | void *ptrmem = NULL; \ | |
173 | \ | |
174 | ptrmem = realloc(x, a); \ | |
175 | if (ptrmem == NULL && a > 0) { \ | |
176 | SCLogError(SC_ERR_MEM_ALLOC, "SCRealloc failed: %s, while trying " \ | |
177 | "to allocate %"PRIuMAX" bytes", strerror(errno), (uintmax_t)a); \ | |
178 | if (SC_ATOMIC_GET(engine_stage) == SURICATA_INIT) {\ | |
179 | SCLogError(SC_ERR_FATAL, "Out of memory. The engine cannot be initialized. Exiting..."); \ | |
180 | exit(EXIT_FAILURE); \ | |
181 | } \ | |
182 | } \ | |
183 | (void*)ptrmem; \ | |
184 | }) | |
185 | ||
186 | #define SCCalloc(nm, a) ({ \ | |
187 | void *ptrmem = NULL; \ | |
188 | \ | |
189 | ptrmem = calloc(nm, a); \ | |
190 | if (ptrmem == NULL && a > 0) { \ | |
191 | SCLogError(SC_ERR_MEM_ALLOC, "SCCalloc failed: %s, while trying " \ | |
192 | "to allocate %"PRIuMAX" bytes", strerror(errno), (uintmax_t)a); \ | |
193 | if (SC_ATOMIC_GET(engine_stage) == SURICATA_INIT) {\ | |
194 | SCLogError(SC_ERR_FATAL, "Out of memory. The engine cannot be initialized. Exiting..."); \ | |
195 | exit(EXIT_FAILURE); \ | |
196 | } \ | |
197 | } \ | |
198 | (void*)ptrmem; \ | |
199 | }) | |
200 | ||
201 | #define SCStrdup(a) ({ \ | |
202 | char *ptrmem = NULL; \ | |
203 | \ | |
204 | ptrmem = strdup(a); \ | |
205 | if (ptrmem == NULL) { \ | |
206 | size_t len = strlen(a); \ | |
207 | SCLogError(SC_ERR_MEM_ALLOC, "SCStrdup failed: %s, while trying " \ | |
208 | "to allocate %"PRIuMAX" bytes", strerror(errno), (uintmax_t)len); \ | |
209 | if (SC_ATOMIC_GET(engine_stage) == SURICATA_INIT) {\ | |
210 | SCLogError(SC_ERR_FATAL, "Out of memory. The engine cannot be initialized. Exiting..."); \ | |
211 | exit(EXIT_FAILURE); \ | |
212 | } \ | |
213 | } \ | |
214 | (void*)ptrmem; \ | |
215 | }) | |
216 | ||
217 | #define SCFree(a) ({ \ | |
218 | free(a); \ | |
219 | }) | |
172 | 220 | |
173 | 221 | #endif /* DBG_MEM_ALLOC */ |
174 | 222 |
122 | 122 | printf("\n"); |
123 | 123 | } |
124 | 124 | |
125 | /** | |
126 | * \brief B2gAllocPattern allocates a new pattern structure | |
127 | * and initialize the data | |
128 | * \initonly | |
129 | */ | |
125 | 130 | static inline B2gPattern *B2gAllocPattern(MpmCtx *mpm_ctx) { |
126 | 131 | B2gPattern *p = SCMalloc(sizeof(B2gPattern)); |
127 | 132 | if (p == NULL) |
274 | 279 | * \param pid pattern id |
275 | 280 | * \param sid signature id (internal id) |
276 | 281 | * \param flags pattern MPM_PATTERN_* flags |
282 | * | |
283 | * \initonly | |
277 | 284 | */ |
278 | 285 | static int B2gAddPattern(MpmCtx *mpm_ctx, uint8_t *pat, uint16_t patlen, uint16_t offset, uint16_t depth, uint32_t pid, uint32_t sid, uint8_t flags) { |
279 | 286 | B2gCtx *ctx = (B2gCtx *)mpm_ctx->ctx; |
140 | 140 | { |
141 | 141 | SCRadixUserData *user_data = SCMalloc(sizeof(SCRadixUserData)); |
142 | 142 | if (user_data == NULL) { |
143 | SCLogError(SC_ERR_MEM_ALLOC, "Error allocating memory"); | |
143 | 144 | return NULL; |
144 | 145 | } |
145 | 146 | |
475 | 476 | SCRadixNode *node = NULL; |
476 | 477 | |
477 | 478 | if ( (node = SCMalloc(sizeof(SCRadixNode))) == NULL) { |
478 | SCLogError(SC_ERR_FATAL, "Fatal error encountered in SCRadixCreateNode. Exiting..."); | |
479 | exit(EXIT_FAILURE); | |
479 | SCLogError(SC_ERR_FATAL, "Fatal error encountered in SCRadixCreateNode. Mem not allocated..."); | |
480 | return NULL; | |
480 | 481 | } |
481 | 482 | memset(node, 0, sizeof(SCRadixNode)); |
482 | 483 | |
510 | 511 | * cleanup API to free the user suppplied data |
511 | 512 | * |
512 | 513 | * \retval tree The newly created radix tree on success |
514 | * | |
515 | * \initonly (all radix trees should be created at init) | |
513 | 516 | */ |
514 | 517 | SCRadixTree *SCRadixCreateRadixTree(void (*Free)(void*), void (*PrintData)(void*)) |
515 | 518 | { |
614 | 617 | /* the very first element in the radix tree */ |
615 | 618 | if (tree->head == NULL) { |
616 | 619 | node = SCRadixCreateNode(); |
620 | if (node == NULL) | |
621 | return NULL; | |
617 | 622 | node->prefix = prefix; |
618 | 623 | node->bit = prefix->bitlen; |
619 | 624 | tree->head = node; |
631 | 636 | node->netmask_cnt++; |
632 | 637 | if ( (node->netmasks = SCRealloc(node->netmasks, (node->netmask_cnt * |
633 | 638 | sizeof(uint8_t)))) == NULL) { |
634 | SCLogError(SC_ERR_FATAL, "Fatal error encountered in SCRadixAddKey. Exiting..."); | |
635 | exit(EXIT_FAILURE); | |
639 | SCLogError(SC_ERR_MEM_ALLOC, "Fatal error encountered in SCRadixAddKey. Mem not allocated"); | |
640 | return NULL; | |
636 | 641 | } |
637 | 642 | node->netmasks[0] = netmask; |
638 | 643 | return node; |
749 | 754 | node->netmask_cnt++; |
750 | 755 | if ( (node->netmasks = SCRealloc(node->netmasks, (node->netmask_cnt * |
751 | 756 | sizeof(uint8_t)))) == NULL) { |
752 | SCLogError(SC_ERR_FATAL, "Fatal error encountered in SCRadixAddKey. Exiting..."); | |
753 | exit(EXIT_FAILURE); | |
757 | SCLogError(SC_ERR_FATAL, "Fatal error encountered in SCRadixAddKey. Mem not allocated..."); | |
758 | return NULL; | |
754 | 759 | } |
755 | 760 | |
756 | 761 | if (node->netmask_cnt == 1) { |
821 | 826 | |
822 | 827 | if ( (inter_node->netmasks = SCMalloc((node->netmask_cnt - i) * |
823 | 828 | sizeof(uint8_t))) == NULL) { |
824 | SCLogError(SC_ERR_FATAL, "Fatal error encountered in SCRadixAddKey. Exiting..."); | |
825 | exit(EXIT_FAILURE); | |
829 | SCLogError(SC_ERR_MEM_ALLOC, "Fatal error encountered in SCRadixAddKey. Mem not allocated..."); | |
830 | return NULL; | |
826 | 831 | } |
827 | 832 | |
828 | 833 | for (j = 0; j < (node->netmask_cnt - i); j++) |
61 | 61 | * \param str pointer to the pattern string |
62 | 62 | * \param size length of the string |
63 | 63 | * \retval BmCtx pointer to the newly created Context for the pattern |
64 | * \initonly BoyerMoore contexts should be created at init | |
64 | 65 | */ |
65 | 66 | BmCtx *BoyerMooreCtxInit(uint8_t *needle, uint32_t needle_len) { |
66 | 67 | BmCtx *new = SCMalloc(sizeof(BmCtx)); |
79 | 80 | } |
80 | 81 | |
81 | 82 | /* Prepare good Suffixes */ |
82 | PreBmGs(needle, needle_len, new->bmGs); | |
83 | if (PreBmGs(needle, needle_len, new->bmGs) == -1) { | |
84 | SCLogError(SC_ERR_FATAL, "Fatal error encountered in BooyerMooreCtxInit. Exiting..."); | |
85 | exit(EXIT_FAILURE); | |
86 | } | |
87 | ||
83 | 88 | |
84 | 89 | return new; |
85 | 90 | } |
152 | 157 | * \param x pointer to the pattern string |
153 | 158 | * \param m length of the string |
154 | 159 | * \param bmGs pointer to an empty array that will hold the prefixes (shifts) |
155 | */ | |
156 | void PreBmGs(const uint8_t *x, int32_t m, int32_t *bmGs) { | |
160 | * \retval 0 ok, -1 failed | |
161 | */ | |
162 | int PreBmGs(const uint8_t *x, int32_t m, int32_t *bmGs) { | |
157 | 163 | int32_t i, j; |
158 | 164 | int32_t *suff; |
159 | 165 | |
160 | 166 | suff = SCMalloc(sizeof(int32_t) * (m + 1)); |
161 | 167 | if (suff == NULL) |
162 | return; | |
168 | return -1; | |
163 | 169 | |
164 | 170 | BoyerMooreSuffixes(x, m, suff); |
165 | 171 | |
177 | 183 | for (i = 0; i <= m - 2; ++i) |
178 | 184 | bmGs[m - 1 - suff[i]] = m - 1 - i; |
179 | 185 | SCFree(suff); |
186 | return 0; | |
180 | 187 | } |
181 | 188 | |
182 | 189 | /** |
41 | 41 | void BoyerMooreCtxToNocase(BmCtx *, uint8_t *, uint32_t); |
42 | 42 | void PreBmBc(const uint8_t *x, int32_t m, int32_t *bmBc); |
43 | 43 | void BoyerMooreSuffixes(const uint8_t *x, int32_t m, int32_t *suff); |
44 | void PreBmGs(const uint8_t *x, int32_t m, int32_t *bmGs); | |
44 | int PreBmGs(const uint8_t *, int32_t, int32_t *); | |
45 | 45 | uint8_t *BoyerMoore(uint8_t *x, int32_t m, uint8_t *y, int32_t n, int32_t *bmGs, int32_t *bmBc); |
46 | 46 | void PreBmBcNocase(const uint8_t *x, int32_t m, int32_t *bmBc); |
47 | 47 | void BoyerMooreSuffixesNocase(const uint8_t *x, int32_t m, int32_t *suff); |
1270 | 1270 | |
1271 | 1271 | Packet *p = UTHBuildPacket((uint8_t*)"lalala", 6, IPPROTO_TCP); |
1272 | 1272 | ThreadVars th_v; |
1273 | DetectEngineThreadCtx *det_ctx; | |
1273 | DetectEngineThreadCtx *det_ctx = NULL; | |
1274 | 1274 | int alerts = 0; |
1275 | 1275 | |
1276 | 1276 | memset(&th_v, 0, sizeof(th_v)); |
1363 | 1363 | Packet *p2 = UTHBuildPacketSrcDst((uint8_t*)"lalala", 6, IPPROTO_TCP, "172.26.0.1", "172.26.0.10"); |
1364 | 1364 | |
1365 | 1365 | ThreadVars th_v; |
1366 | DetectEngineThreadCtx *det_ctx; | |
1366 | DetectEngineThreadCtx *det_ctx = NULL; | |
1367 | 1367 | int alerts = 0; |
1368 | 1368 | |
1369 | 1369 | memset(&th_v, 0, sizeof(th_v)); |
1458 | 1458 | |
1459 | 1459 | Packet *p = UTHBuildPacket((uint8_t*)"lalala", 6, IPPROTO_TCP); |
1460 | 1460 | ThreadVars th_v; |
1461 | DetectEngineThreadCtx *det_ctx; | |
1461 | DetectEngineThreadCtx *det_ctx = NULL; | |
1462 | 1462 | int alerts10 = 0; |
1463 | 1463 | int alerts11 = 0; |
1464 | 1464 | int alerts12 = 0; |
1574 | 1574 | |
1575 | 1575 | Packet *p = UTHBuildPacket((uint8_t*)"lalala", 6, IPPROTO_TCP); |
1576 | 1576 | ThreadVars th_v; |
1577 | DetectEngineThreadCtx *det_ctx; | |
1577 | DetectEngineThreadCtx *det_ctx = NULL; | |
1578 | 1578 | int alerts10 = 0; |
1579 | 1579 | int alerts11 = 0; |
1580 | 1580 | int alerts12 = 0; |
238 | 238 | # |
239 | 239 | # stream: |
240 | 240 | # memcap: 33554432 # 32mb tcp session memcap |
241 | # checksum_validation: yes # To validate the checksum of received | |
242 | # packet. If csum validation is specified as | |
243 | # "yes", then packet with invalid csum will not | |
244 | # be processed by the engine stream/app layer. | |
241 | 245 | # max_sessions: 262144 # 256k concurrent sessions |
242 | 246 | # prealloc_sessions: 32768 # 32k sessions prealloc'd |
243 | 247 | # midstream: false # don't allow midstream session pickups |
247 | 251 | # depth: 1048576 # 1 MB reassembly depth |
248 | 252 | stream: |
249 | 253 | memcap: 33554432 |
254 | checksum_validation: yes | |
250 | 255 | reassembly: |
251 | 256 | memcap: 67108864 |
252 | 257 | depth: 1048576 |