LCOV - code coverage report
Current view: top level - cmail-smtpd - client.c (source / functions) Hit Total Coverage
Test: smtpd.info Lines: 113 155 72.9 %
Date: 2015-11-25 19:06:20 Functions: 8 8 100.0 %

          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             : }

Generated by: LCOV version 1.11