Line data Source code
1 317 : int client_line(LOGGER log, CONNECTION* client, DATABASE* database, PATHPOOL* path_pool){
2 317 : CLIENT* client_data = (CLIENT*)client->aux_data;
3 :
4 317 : logprintf(log, LOG_ALL_IO, ">> %s\n", client_data->recv_buffer);
5 317 : switch(client_data->state){
6 : case STATE_NEW:
7 21 : return smtpstate_new(log, client, database, path_pool);
8 : case STATE_IDLE:
9 102 : return smtpstate_idle(log, client, database, path_pool);
10 : case STATE_AUTH:
11 3 : return smtpstate_auth(log, client, database, path_pool);
12 : case STATE_RECIPIENTS:
13 89 : return smtpstate_recipients(log, client, database, path_pool);
14 : case STATE_DATA:
15 102 : return smtpstate_data(log, client, database, path_pool);
16 : default:
17 : //TODO resolve to plugin handler
18 0 : break;
19 : }
20 0 : return 0;
21 : }
22 :
23 2 : int client_free(LOGGER log, CONNECTION* client){
24 2 : CLIENT* client_data = (CLIENT*)client->aux_data;
25 :
26 2 : logprintf(log, LOG_DEBUG, "Freeing client data\n");
27 2 : mail_reset(&(client_data->current_mail));
28 2 : if(client_data->current_mail.data){
29 2 : free(client_data->current_mail.data);
30 2 : client_data->current_mail.data = NULL;
31 2 : client_data->current_mail.data_allocated = 0;
32 : }
33 2 : return 0;
34 : }
35 :
36 106 : int client_memtimeout(LOGGER log, CONNECTION* client){
37 106 : CLIENT* client_data = (CLIENT*)client->aux_data;
38 106 : int delta = time(NULL) - client_data->last_action;
39 :
40 106 : if(client_data->current_mail.data && delta > CMAIL_MEMORY_TIMEOUT){
41 0 : client_free(log, client);
42 : }
43 :
44 106 : return 0;
45 : }
46 :
47 15 : int client_resolve(LOGGER log, CONNECTION* client){
48 : struct sockaddr_storage data;
49 15 : socklen_t len = sizeof(struct sockaddr_storage);
50 15 : CLIENT* client_data = (CLIENT*)client->aux_data;
51 : int status;
52 :
53 15 : if(getpeername(client->fd, (struct sockaddr*)&data, &len) < 0){
54 0 : logprintf(log, LOG_ERROR, "Failed to get peer name: %s\n", strerror(errno));
55 0 : return -1;
56 : }
57 :
58 15 : status=getnameinfo((struct sockaddr*)&data, len, client_data->peer_name, MAX_FQDN_LENGTH - 1, NULL, 0, 0);
59 15 : if(status){
60 0 : logprintf(log, LOG_WARNING, "Failed to resolve peer: %s\n", gai_strerror(status));
61 0 : return -1;
62 : }
63 :
64 15 : logprintf(log, LOG_INFO, "Connection from %s\n", client_data->peer_name);
65 :
66 15 : return 0;
67 : }
68 :
69 84 : bool client_timeout(LOGGER log, CONNECTION* client){
70 84 : CLIENT* client_data = (CLIENT*)client->aux_data;
71 84 : int delta = time(NULL) - client_data->last_action;
72 :
73 84 : if(delta < 0){
74 0 : logprintf(log, LOG_ERROR, "Time reported an error or skipped ahead: %s\n", strerror(errno));
75 0 : return false;
76 : }
77 :
78 84 : logprintf(log, LOG_DEBUG, "Client has activity delta %d seconds\n", delta);
79 : /*
80 : switch(client_data->state){
81 : case STATE_NEW:
82 : case STATE_IDLE:
83 : case STATE_AUTH:
84 : case STATE_RECIPIENTS:
85 : case STATE_DATA:
86 : }
87 : */
88 :
89 : //According to RFC 5321 4.5.3.2.7, the server timeout is always 5 minutes and does not depend on client state
90 84 : return delta > SMTP_SERVER_TIMEOUT;
91 : }
92 :
93 15 : int client_accept(LOGGER log, DATABASE* database, CONNECTION* listener, CONNPOOL* clients){
94 15 : int client_slot = -1, flags, status;
95 15 : LISTENER* listener_data = (LISTENER*)listener->aux_data;
96 45 : CLIENT empty_data = {
97 : .listener = listener,
98 : .state = STATE_NEW,
99 : .recv_offset = 0,
100 15 : .last_action = time(NULL),
101 : .connection_score = 0,
102 : .peer_name = "",
103 : .current_mail = {
104 : .submitter = NULL,
105 : .reverse_path = {
106 : .delimiter_position = 0,
107 : .in_transaction = false,
108 : .path = "",
109 : .route = {
110 : .router = NULL,
111 : .argument = NULL
112 : }
113 : },
114 : .forward_paths = {
115 : NULL
116 : },
117 : .message_id = "",
118 : .protocol = "unknown",
119 : //these need to persist between clients
120 : .data_offset = 0,
121 : .data_allocated = 0,
122 15 : .data_max = listener_data->max_size,
123 : .hop_count = 0,
124 : .header_offset = 0,
125 : .data = NULL
126 : },
127 : .originating_route = {
128 : .router = NULL,
129 : .argument = NULL
130 : },
131 : .sasl_user = {
132 : .authorized = NULL,
133 : .authenticated = NULL
134 : },
135 : .sasl_context = {
136 : .method = SASL_INVALID
137 : //rest is automatically reset by sasl_begin
138 : }
139 : };
140 : CLIENT* actual_data;
141 :
142 15 : if(connpool_active(*clients) >= CMAIL_MAX_CONCURRENT_CLIENTS){
143 0 : logprintf(log, LOG_INFO, "Not accepting new client, limit reached\n");
144 0 : return 1;
145 : }
146 :
147 15 : client_slot = connpool_add(clients, accept(listener->fd, NULL, NULL));
148 :
149 15 : if(client_slot < 0){
150 0 : logprintf(log, LOG_ERROR, "Failed to pool client socket\n");
151 0 : return -1;
152 : }
153 :
154 : //set socket nonblocking
155 15 : flags = fcntl(clients->conns[client_slot].fd, F_GETFL, 0);
156 15 : if(flags < 0){
157 0 : flags = 0;
158 : }
159 15 : status = fcntl(clients->conns[client_slot].fd, F_SETFL, flags | O_NONBLOCK);
160 15 : if(status < 0){
161 0 : logprintf(log, LOG_ERROR, "Failed to make client socket nonblocking: %s\n", strerror(errno));
162 : }
163 :
164 15 : if(!(clients->conns[client_slot].aux_data)){
165 2 : clients->conns[client_slot].aux_data = malloc(sizeof(CLIENT));
166 2 : if(!clients->conns[client_slot].aux_data){
167 0 : logprintf(log, LOG_ERROR, "Failed to allocate client data set\n");
168 0 : return -1;
169 : }
170 : }
171 : else{
172 : //preserve old data
173 13 : actual_data = (CLIENT*)clients->conns[client_slot].aux_data;
174 13 : empty_data.current_mail.data_allocated = actual_data->current_mail.data_allocated;
175 13 : empty_data.current_mail.data = actual_data->current_mail.data;
176 : }
177 :
178 : //initialise / reset client data structure
179 15 : actual_data = (CLIENT*)clients->conns[client_slot].aux_data;
180 15 : *actual_data = empty_data;
181 :
182 15 : if(client_resolve(log, &(clients->conns[client_slot])) < 0){
183 0 : logprintf(log, LOG_WARNING, "Peer resolution failed\n");
184 : //FIXME this might be bigger than we think
185 : }
186 :
187 15 : if(listener_data->fixed_user){
188 0 : actual_data->sasl_user.authenticated = common_strdup(listener_data->fixed_user);
189 0 : actual_data->sasl_user.authorized = common_strdup(listener_data->fixed_user);
190 0 : if(!actual_data->sasl_user.authenticated || !actual_data->sasl_user.authorized){
191 0 : logprintf(log, LOG_ERROR, "Failed to allocate memory for fixed user authentication data\n");
192 : //TODO fail this connection
193 : }
194 : else{
195 : //query routing data
196 0 : actual_data->originating_route = route_query(log, database, actual_data->sasl_user.authorized);
197 : }
198 : }
199 :
200 15 : actual_data->current_mail.submitter = actual_data->peer_name;
201 15 : logprintf(log, LOG_DEBUG, "Initialized client data to peername %s, submitter %s\n", actual_data->peer_name, actual_data->current_mail.submitter);
202 :
203 : #ifndef CMAIL_NO_TLS
204 : //if on tlsonly port, immediately wait for negotiation
205 15 : if(listener->tls_mode == TLS_ONLY){
206 1 : logprintf(log, LOG_INFO, "Listen socket is TLSONLY, waiting for negotiation...\n");
207 1 : clients->conns[client_slot].tls_mode = TLS_NEGOTIATE;
208 1 : return tls_init_serverpeer(log, &(clients->conns[client_slot]), listener_data->tls_priorities, listener_data->tls_cert);
209 : }
210 : #endif
211 :
212 14 : client_send(log, &(clients->conns[client_slot]), "220 %s ESMTP service ready\r\n", listener_data->announce_domain);
213 14 : return 0;
214 : }
215 :
216 15 : int client_close(CONNECTION* client){
217 15 : CLIENT* client_data = (CLIENT*)client->aux_data;
218 :
219 : #ifndef CMAIL_NO_TLS
220 : //shut down the tls session
221 15 : if(client->tls_mode != TLS_NONE){
222 6 : gnutls_bye(client->tls_session, GNUTLS_SHUT_RDWR);
223 6 : gnutls_deinit(client->tls_session);
224 6 : client->tls_mode = TLS_NONE;
225 : }
226 : #endif
227 :
228 : //close the socket
229 15 : close(client->fd);
230 :
231 : //reset mail buffer contents
232 15 : mail_reset(&(client_data->current_mail));
233 :
234 : //reset authentication
235 15 : sasl_reset_user(&(client_data->sasl_user), true);
236 :
237 : //reset originating route
238 15 : route_free(&(client_data->originating_route));
239 :
240 : //return the connpool slot
241 15 : client->fd = -1;
242 :
243 15 : return 0;
244 : }
245 :
246 293 : int client_process(LOGGER log, CONNECTION* client, DATABASE* database, PATHPOOL* path_pool){
247 293 : CLIENT* client_data = (CLIENT*)client->aux_data;
248 293 : LISTENER* listener_data = (LISTENER*)client_data->listener->aux_data;
249 : ssize_t left, bytes, line_length;
250 :
251 : #ifndef CMAIL_NO_TLS
252 : do{
253 : #endif
254 293 : left = sizeof(client_data->recv_buffer) - client_data->recv_offset;
255 :
256 293 : if(left < 2){
257 : //unterminated line
258 : //FIXME this might be kind of a harsh response
259 0 : logprintf(log, LOG_WARNING, "Line too long, closing client connection\n");
260 0 : client_send(log, client, "500 Line too long\r\n");
261 0 : client_close(client);
262 0 : return 0;
263 : }
264 :
265 293 : bytes = network_read(log, client, client_data->recv_buffer+client_data->recv_offset, left);
266 :
267 : //failed to read from socket
268 293 : if(bytes < 0){
269 : #ifndef CMAIL_NO_TLS
270 9 : switch(client->tls_mode){
271 : case TLS_NONE:
272 : #endif
273 0 : switch(errno){
274 : case EAGAIN:
275 0 : logprintf(log, LOG_WARNING, "Read signaled, but blocked\n");
276 0 : return 0;
277 : default:
278 0 : logprintf(log, LOG_ERROR, "Failed to read from client: %s\n", strerror(errno));
279 0 : client_close(client);
280 0 : return -1;
281 : }
282 : #ifndef CMAIL_NO_TLS
283 : break;
284 : case TLS_NEGOTIATE:
285 : //errors during TLS negotiation
286 6 : if(bytes == -2){
287 0 : client_close(client);
288 : }
289 6 : return 0;
290 : case TLS_ONLY:
291 3 : switch(bytes){
292 : case GNUTLS_E_INTERRUPTED:
293 : case GNUTLS_E_AGAIN:
294 0 : logprintf(log, LOG_WARNING, "TLS read signaled, but blocked\n");
295 0 : return 0;
296 : default:
297 3 : logprintf(log, LOG_ERROR, "GnuTLS reported an error while reading: %s\n", gnutls_strerror(bytes));
298 3 : client_close(client);
299 3 : return -1;
300 : }
301 : }
302 : #endif
303 : }
304 :
305 : //client disconnect / handshake success
306 284 : else if(bytes == 0){
307 : #ifndef CMAIL_NO_TLS
308 14 : switch(client->tls_mode){
309 : case TLS_NEGOTIATE:
310 : //tls handshake ok
311 6 : client->tls_mode = TLS_ONLY;
312 6 : if(client_data->listener->tls_mode == TLS_ONLY){
313 : //send greeting if listener is tlsonly
314 1 : client_send(log, client, "220 %s ESMTPS service ready\r\n", listener_data->announce_domain);
315 : }
316 6 : break;
317 : default:
318 : #endif
319 8 : logprintf(log, LOG_INFO, "Client has disconnected\n");
320 8 : client_close(client);
321 : #ifndef CMAIL_NO_TLS
322 : }
323 : #endif
324 14 : return 0;
325 : }
326 :
327 270 : logprintf(log, LOG_DEBUG, "Received %d bytes of data, recv_offset is %d\n", bytes, client_data->recv_offset);
328 :
329 : do{
330 587 : line_length = common_next_line(log, client_data->recv_buffer, &(client_data->recv_offset), &bytes);
331 587 : if(line_length >= 0){
332 317 : if(line_length >= SMTP_MAX_LINE_LENGTH - 2){
333 0 : logprintf(log, LOG_WARNING, "Line too long, ignoring\n");
334 0 : client_send(log, client, "500 Line too long\r\n");
335 : //client_line(log, client, database, path_pool);
336 : //FIXME might handle this more sensibly
337 : }
338 : else{
339 : //update last_action only upon complete lines to kill slowloris style attacks
340 317 : client_data->last_action = time(NULL);
341 :
342 317 : client_data->connection_score += client_line(log, client, database, path_pool);
343 :
344 : //disconnect the client after too many failed commands
345 317 : if(client_data->connection_score < CMAIL_FAILSCORE_LIMIT){
346 0 : logprintf(log, LOG_WARNING, "Disconnecting client because of bad connection score\n");
347 0 : client_send(log, client, "500 Too many failed commands\r\n");
348 0 : client_close(client);
349 0 : return 0;
350 : }
351 : }
352 : }
353 : }
354 587 : while(line_length >= 0);
355 :
356 : #ifndef CMAIL_NO_TLS
357 : }
358 270 : while(client->tls_mode == TLS_ONLY && gnutls_record_check_pending(client->tls_session));
359 : #endif
360 :
361 270 : return 0;
362 : }
|