]>
Commit | Line | Data |
---|---|---|
2d78afe4 | 1 | /* Copyright (C) 2017-2021 Open Information Security Foundation |
1930b1f5 JL |
2 | * |
3 | * You can copy, redistribute or modify this Program under the terms of | |
4 | * the GNU General Public License version 2 as published by the Free | |
5 | * Software Foundation. | |
6 | * | |
7 | * This program is distributed in the hope that it will be useful, | |
8 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
9 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
10 | * GNU General Public License for more details. | |
11 | * | |
12 | * You should have received a copy of the GNU General Public License | |
13 | * version 2 along with this program; if not, write to the Free Software | |
14 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA | |
15 | * 02110-1301, USA. | |
16 | */ | |
17 | ||
18 | /** | |
19 | * \file | |
20 | * | |
21 | * \author Jeff Lucovsky <jeff@lucovsky.org> | |
22 | * | |
23 | * Implement JSON/eve logging app-layer FTP. | |
24 | */ | |
25 | ||
26 | ||
27 | #include "suricata-common.h" | |
28 | #include "debug.h" | |
29 | #include "detect.h" | |
30 | #include "pkt-var.h" | |
31 | #include "conf.h" | |
32 | ||
33 | #include "threads.h" | |
34 | #include "threadvars.h" | |
35 | #include "tm-threads.h" | |
36 | ||
37 | #include "util-unittest.h" | |
38 | #include "util-buffer.h" | |
39 | #include "util-debug.h" | |
66c565e9 | 40 | #include "util-mem.h" |
1930b1f5 JL |
41 | |
42 | #include "output.h" | |
43 | #include "output-json.h" | |
44 | ||
45 | #include "app-layer.h" | |
46 | #include "app-layer-parser.h" | |
47 | ||
48 | #include "app-layer-ftp.h" | |
49 | #include "output-json-ftp.h" | |
50 | ||
648bd5af | 51 | static void EveFTPLogCommand(Flow *f, FTPTransaction *tx, JsonBuilder *jb) |
1930b1f5 | 52 | { |
9cf4e2e4 | 53 | /* Preallocate array objects to simplify failure case */ |
03de315b | 54 | JsonBuilder *js_resplist = NULL; |
9cf4e2e4 | 55 | if (!TAILQ_EMPTY(&tx->response_list)) { |
03de315b | 56 | js_resplist = jb_new_array(); |
9cf4e2e4 | 57 | |
1f19ab10 | 58 | if (unlikely(js_resplist == NULL)) { |
bd22e0d7 | 59 | return; |
9cf4e2e4 JL |
60 | } |
61 | } | |
03de315b | 62 | jb_set_string(jb, "command", tx->command_descriptor->command_name); |
9cf4e2e4 JL |
63 | uint32_t min_length = tx->command_descriptor->command_length + 1; /* command + space */ |
64 | if (tx->request_length > min_length) { | |
03de315b JL |
65 | jb_set_string_from_bytes(jb, |
66 | "command_data", | |
67 | (const uint8_t *)tx->request + min_length, | |
68 | tx->request_length - min_length - 1); | |
9cf4e2e4 JL |
69 | } |
70 | ||
71 | if (!TAILQ_EMPTY(&tx->response_list)) { | |
03de315b JL |
72 | int resp_code_cnt = 0; |
73 | int resp_cnt = 0; | |
9cf4e2e4 | 74 | FTPString *response; |
1f19ab10 | 75 | bool is_cc_array_open = false; |
9cf4e2e4 | 76 | TAILQ_FOREACH(response, &tx->response_list, next) { |
66c565e9 JL |
77 | /* handle multiple lines within the response, \r\n delimited */ |
78 | uint8_t *where = response->str; | |
aa3f784d | 79 | uint16_t length = response->len ? response->len -1 : 0; |
66c565e9 JL |
80 | uint16_t pos; |
81 | while ((pos = JsonGetNextLineFromBuffer((const char *)where, length)) != UINT16_MAX) { | |
82 | uint16_t offset = 0; | |
83 | /* Try to find a completion code for this line */ | |
84 | if (pos >= 3) { | |
85 | /* Gather the completion code if present */ | |
86 | if (isdigit(where[0]) && isdigit(where[1]) && isdigit(where[2])) { | |
1f19ab10 JL |
87 | if (!is_cc_array_open) { |
88 | jb_open_array(jb, "completion_code"); | |
89 | is_cc_array_open = true; | |
90 | } | |
91 | jb_append_string_from_bytes(jb, (const uint8_t *)where, 3); | |
03de315b | 92 | resp_code_cnt++; |
66c565e9 JL |
93 | offset = 4; |
94 | } | |
1930b1f5 | 95 | } |
66c565e9 JL |
96 | /* move past 3 character completion code */ |
97 | if (pos >= offset) { | |
03de315b JL |
98 | jb_append_string_from_bytes(js_resplist, (const uint8_t *)where + offset, pos - offset); |
99 | resp_cnt++; | |
66c565e9 JL |
100 | } |
101 | ||
102 | where += pos; | |
103 | length -= pos; | |
1930b1f5 JL |
104 | } |
105 | } | |
9cf4e2e4 | 106 | |
1f19ab10 JL |
107 | if (is_cc_array_open) { |
108 | jb_close(jb); | |
109 | } | |
03de315b JL |
110 | if (resp_cnt) { |
111 | jb_close(js_resplist); | |
112 | jb_set_object(jb, "reply", js_resplist); | |
113 | } | |
114 | jb_free(js_resplist); | |
9cf4e2e4 JL |
115 | } |
116 | ||
117 | if (tx->dyn_port) { | |
03de315b | 118 | jb_set_uint(jb, "dynamic_port", tx->dyn_port); |
1930b1f5 JL |
119 | } |
120 | ||
9cf4e2e4 JL |
121 | if (tx->command_descriptor->command == FTP_COMMAND_PORT || |
122 | tx->command_descriptor->command == FTP_COMMAND_EPRT) { | |
03de315b JL |
123 | if (tx->active) { |
124 | JB_SET_STRING(jb, "mode", "active"); | |
125 | } else { | |
126 | JB_SET_STRING(jb, "mode", "passive"); | |
127 | } | |
128 | } | |
129 | ||
130 | if (tx->done) { | |
131 | JB_SET_STRING(jb, "reply_received", "yes"); | |
132 | } else { | |
133 | JB_SET_STRING(jb, "reply_received", "no"); | |
1930b1f5 JL |
134 | } |
135 | } | |
136 | ||
9cf4e2e4 | 137 | |
1930b1f5 JL |
138 | static int JsonFTPLogger(ThreadVars *tv, void *thread_data, |
139 | const Packet *p, Flow *f, void *state, void *vtx, uint64_t tx_id) | |
140 | { | |
141 | SCEnter(); | |
67c4621b | 142 | OutputJsonThreadCtx *thread = thread_data; |
1930b1f5 | 143 | |
9cf4e2e4 JL |
144 | const char *event_type; |
145 | if (f->alproto == ALPROTO_FTPDATA) { | |
146 | event_type = "ftp_data"; | |
147 | } else { | |
148 | event_type = "ftp"; | |
149 | } | |
1930b1f5 | 150 | FTPTransaction *tx = vtx; |
1930b1f5 | 151 | |
67c4621b JI |
152 | JsonBuilder *jb = |
153 | CreateEveHeaderWithTxId(p, LOG_DIR_FLOW, event_type, NULL, tx_id, thread->ctx); | |
03de315b | 154 | if (likely(jb)) { |
03de315b | 155 | jb_open_object(jb, event_type); |
9cf4e2e4 | 156 | if (f->alproto == ALPROTO_FTPDATA) { |
648bd5af | 157 | EveFTPDataAddMetadata(f, jb); |
9cf4e2e4 | 158 | } else { |
648bd5af | 159 | EveFTPLogCommand(f, tx, jb); |
9cf4e2e4 JL |
160 | } |
161 | ||
03de315b JL |
162 | if (!jb_close(jb)) { |
163 | goto fail; | |
9cf4e2e4 | 164 | } |
1930b1f5 | 165 | |
06f58650 | 166 | OutputJsonBuilderBuffer(jb, thread); |
1930b1f5 | 167 | |
03de315b | 168 | jb_free(jb); |
1930b1f5 JL |
169 | } |
170 | return TM_ECODE_OK; | |
03de315b JL |
171 | |
172 | fail: | |
173 | jb_free(jb); | |
174 | return TM_ECODE_FAILED; | |
1930b1f5 JL |
175 | } |
176 | ||
1930b1f5 JL |
177 | static OutputInitResult OutputFTPLogInitSub(ConfNode *conf, |
178 | OutputCtx *parent_ctx) | |
179 | { | |
1930b1f5 JL |
180 | AppLayerParserRegisterLogger(IPPROTO_TCP, ALPROTO_FTP); |
181 | AppLayerParserRegisterLogger(IPPROTO_TCP, ALPROTO_FTPDATA); | |
67c4621b | 182 | return OutputJsonLogInitSub(conf, parent_ctx); |
1930b1f5 JL |
183 | } |
184 | ||
185 | void JsonFTPLogRegister(void) | |
186 | { | |
187 | /* Register as an eve sub-module. */ | |
67c4621b JI |
188 | OutputRegisterTxSubModule(LOGGER_JSON_FTP, "eve-log", "JsonFTPLog", "eve-log.ftp", |
189 | OutputFTPLogInitSub, ALPROTO_FTP, JsonFTPLogger, JsonLogThreadInit, JsonLogThreadDeinit, | |
190 | NULL); | |
191 | OutputRegisterTxSubModule(LOGGER_JSON_FTP, "eve-log", "JsonFTPLog", "eve-log.ftp", | |
192 | OutputFTPLogInitSub, ALPROTO_FTPDATA, JsonFTPLogger, JsonLogThreadInit, | |
193 | JsonLogThreadDeinit, NULL); | |
1930b1f5 JL |
194 | |
195 | SCLogDebug("FTP JSON logger registered."); | |
196 | } |