LCOV - code coverage report
Current view: top level - cmail-popd - popstatemachine.c (source / functions) Hit Total Coverage
Test: popd.info Lines: 116 157 73.9 %
Date: 2015-11-25 19:05:59 Functions: 2 3 66.7 %

          Line data    Source code
       1          71 : int state_authorization(LOGGER log, CONNECTION* client, DATABASE* database){
       2          71 :         CLIENT* client_data = (CLIENT*)client->aux_data;
       3          71 :         LISTENER* listener_data = (LISTENER*)client_data->listener->aux_data;
       4             : 
       5          71 :         int status = SASL_OK;
       6          71 :         char* method = NULL;
       7          71 :         char* parameter = NULL;
       8          71 :         char* challenge = NULL;
       9             : 
      10             :         #ifndef CMAIL_NO_TLS
      11             :         //disable login on tls-required auth
      12          71 :         if(client->tls_mode == TLS_ONLY || !listener_data->tls_require){
      13             :         #endif
      14             : 
      15             :                 //handle sasl continuation
      16          45 :                 if(client_data->auth.method == AUTH_SASL && !client_data->auth.auth_ok){
      17           2 :                         if(!strcmp(client_data->recv_buffer, "*")){
      18             :                                 //cancel authentication
      19           1 :                                 logprintf(log, LOG_INFO, "Client cancelled authentication\n");
      20           1 :                                 sasl_cancel(&(client_data->auth.ctx));
      21           1 :                                 auth_reset(&(client_data->auth));
      22           1 :                                 client_send(log, client, "-ERR Authentication canceled\r\n");
      23           1 :                                 return 0;
      24             :                         }
      25           1 :                         else if(client_data->auth.ctx.method != SASL_INVALID){
      26           1 :                                 status = sasl_continue(log, &(client_data->auth.ctx), client_data->recv_buffer, &challenge);
      27             :                                 //do not return immediately, response handled below
      28             :                         }
      29             :                         else{
      30             :                                 //This should never happen
      31           0 :                                 logprintf(log, LOG_INFO, "Client in invalid SASL state, resetting\n");
      32           0 :                                 sasl_cancel(&(client_data->auth.ctx));
      33           0 :                                 auth_reset(&(client_data->auth));
      34           0 :                                 client_send(log, client, "-ERR Invalid state\r\n");
      35           0 :                                 return -1;
      36             :                         }
      37             :                 }
      38             : 
      39             :                 //handle sasl initiation
      40          44 :                 if(status == SASL_OK && !strncasecmp(client_data->recv_buffer, "auth ", 5)){
      41           6 :                         client_data->auth.method = AUTH_SASL;
      42             : 
      43             :                         //tokenize along spaces
      44           6 :                         method = strtok(client_data->recv_buffer + 5, " ");
      45           6 :                         if(method){
      46           5 :                                 parameter = strtok(NULL, " ");
      47           5 :                                 logprintf(log, LOG_DEBUG, "Beginning SASL with method %s parameter %s\n", method, parameter?parameter:"null");
      48           5 :                                 status = sasl_begin(log, &(client_data->auth.ctx), &(client_data->auth.user), method, parameter, &challenge);
      49             :                         }
      50             :                         else{
      51           1 :                                 logprintf(log, LOG_WARNING, "Client tried auth without supplying method\n");
      52           1 :                                 status = SASL_UNKNOWN_METHOD;
      53             :                         }
      54             :                 }
      55             : 
      56             :                 //handle SASL outcome
      57          44 :                 if(status != SASL_OK){
      58           7 :                         switch(status){
      59             :                                 case SASL_ERROR_PROCESSING:
      60           0 :                                         logprintf(log, LOG_ERROR, "SASL processing error\r\n");
      61           0 :                                         auth_reset(&(client_data->auth));
      62           0 :                                         client_send(log, client, "-ERR SASL Internal Error\r\n");
      63           0 :                                         return -1;
      64             :                                 case SASL_ERROR_DATA:
      65           2 :                                         logprintf(log, LOG_ERROR, "SASL failed to parse data\r\n");
      66           2 :                                         auth_reset(&(client_data->auth));
      67           2 :                                         client_send(log, client, "-ERR SASL Invalid data provided\r\n");
      68           2 :                                         return -1;
      69             :                                 case SASL_UNKNOWN_METHOD:
      70           1 :                                         logprintf(log, LOG_WARNING, "Client tried unsupported authentication method: %s\n", client_data->recv_buffer+5);
      71           1 :                                         auth_reset(&(client_data->auth));
      72           1 :                                         client_send(log, client, "-ERR Unknown SASL mechanism\r\n");
      73           1 :                                         return -1;
      74             :                                 case SASL_CONTINUE:
      75           2 :                                         logprintf(log, LOG_INFO, "Asking for SASL continuation\r\n");
      76           2 :                                         client_send(log, client, "+ %s\r\n", challenge ? challenge:"");
      77           2 :                                         return 0;
      78             :                                 case SASL_DATA_OK:
      79           2 :                                         sasl_reset_ctx(&(client_data->auth.ctx), true);
      80             : 
      81             :                                         //check auth data
      82           2 :                                         if(!challenge || auth_validate(log, database, client_data->auth.user.authenticated, challenge, &(client_data->auth.user.authorized)) < 0){
      83             :                                                 //login failed
      84           1 :                                                 auth_reset(&(client_data->auth));
      85           1 :                                                 logprintf(log, LOG_INFO, "Client failed to authenticate\n");
      86           1 :                                                 client_send(log, client, "-ERR Authentication failed\r\n");
      87           1 :                                                 return -1;
      88             :                                         }
      89             : 
      90           1 :                                         client_data->auth.auth_ok = true; //FIXME is this actually needed?
      91             : 
      92           1 :                                         if(!client_data->auth.user.authorized){
      93           0 :                                                 auth_reset(&(client_data->auth));
      94           0 :                                                 logprintf(log, LOG_ERROR, "Failed to allocate memory for authorized user\n");
      95           0 :                                                 client_send(log, client, "-ERR Failed to allocate memory\r\n");
      96           0 :                                                 return 0;
      97             :                                         }
      98             : 
      99           1 :                                         logprintf(log, LOG_INFO, "Client authenticated as user %s\n", client_data->auth.user.authenticated);
     100             : 
     101             :                                         //authentication ok, try to acquire the maildrop
     102           1 :                                         if(maildrop_acquire(log, database, &(client_data->maildrop), client_data->auth.user.authorized) < 0){
     103           0 :                                                 auth_reset(&(client_data->auth));
     104           0 :                                                 client_send(log, client, "-ERR Failed to lock the maildrop\r\n");
     105           0 :                                                 return 0;
     106             :                                         }
     107             : 
     108           1 :                                         client_data->state = STATE_TRANSACTION;
     109           1 :                                         client_send(log, client, "+OK Lock and load\r\n");
     110             : 
     111             :                                         //decrease the failscore
     112           1 :                                         return 1;
     113             :                         }
     114             : 
     115           0 :                         logprintf(log, LOG_ERROR, "Invalid branch reached in AUTH\n");
     116           0 :                         return -1;
     117             :                 }
     118             : 
     119          37 :                 if(!strncasecmp(client_data->recv_buffer, "user ", 5)){
     120          12 :                         if(client_data->auth.user.authenticated){
     121           2 :                                 logprintf(log, LOG_INFO, "Client issued multiple USER commands\n");
     122           2 :                                 client_send(log, client, "-ERR User already set\r\n");
     123           2 :                                 return -1;
     124             :                         }
     125             : 
     126          10 :                         logprintf(log, LOG_INFO, "Client sends user %s\n", client_data->recv_buffer+5);
     127          10 :                         client_data->auth.method = AUTH_USER;
     128          10 :                         client_data->auth.user.authenticated = common_strdup(client_data->recv_buffer+5);
     129          10 :                         if(!client_data->auth.user.authenticated){
     130           0 :                                 logprintf(log, LOG_WARNING, "Failed to allocate memory for user name\n");
     131           0 :                                 client_send(log, client, "-ERR Out of memory\r\n");
     132           0 :                                 return -1;
     133             :                         }
     134             : 
     135          10 :                         client_send(log, client, "+OK Go ahead\r\n");
     136          10 :                         return 0;
     137             :                 }
     138             : 
     139          25 :                 if(!strncasecmp(client_data->recv_buffer, "pass ", 5)){
     140          12 :                         if(!client_data->auth.user.authenticated || client_data->auth.method != AUTH_USER){
     141           2 :                                 logprintf(log, LOG_WARNING, "Client tried PASS without user or in another method\n");
     142           2 :                                 client_send(log, client, "-ERR Not possible now\r\n");
     143           2 :                                 return -1;
     144             :                         }
     145             : 
     146          10 :                         if(auth_validate(log, database, client_data->auth.user.authenticated, client_data->recv_buffer + 5, &(client_data->auth.user.authorized)) < 0){
     147             :                                 //failed to authenticate
     148           4 :                                 logprintf(log, LOG_INFO, "Failed to authenticate client\n");
     149           4 :                                 auth_reset(&(client_data->auth));
     150           4 :                                 client_send(log, client, "-ERR Login failed\r\n");
     151             : 
     152             :                                 //increase failscore
     153           4 :                                 return -1;
     154             :                         }
     155             : 
     156           6 :                         client_data->auth.auth_ok = true; //FIXME is this actually needed?
     157             : 
     158           6 :                         if(!client_data->auth.user.authorized){
     159           0 :                                 logprintf(log, LOG_ERROR, "Failed to allocate memory for authorization user name\n");
     160           0 :                                 auth_reset(&(client_data->auth));
     161           0 :                                 client_send(log, client, "-ERR Internal error\r\n");
     162           0 :                                 return 0;
     163             :                         }
     164             : 
     165             :                         //authentication ok, try to acquire the maildrop
     166           6 :                         if(maildrop_acquire(log, database, &(client_data->maildrop), client_data->auth.user.authorized) < 0){
     167           0 :                                 auth_reset(&(client_data->auth));
     168           0 :                                 client_send(log, client, "-ERR Failed to lock the maildrop\r\n");
     169           0 :                                 return 0;
     170             :                         }
     171             : 
     172           6 :                         logprintf(log, LOG_INFO, "Client authenticated as user %s\n", client_data->auth.user.authenticated);
     173           6 :                         client_data->state = STATE_TRANSACTION;
     174           6 :                         client_send(log, client, "+OK Lock and load\r\n");
     175             : 
     176             :                         //decrease the failscore
     177           6 :                         return 1;
     178             :                 }
     179             :         #ifndef CMAIL_NO_TLS
     180             :         }
     181             :         #endif
     182             : 
     183          39 :         if(!strncasecmp(client_data->recv_buffer, "capa", 4)){
     184          10 :                 return pop_capa(log, client, database);
     185             :         }
     186             : 
     187          29 :         if(!strncasecmp(client_data->recv_buffer, "quit", 4)){
     188           4 :                 return pop_quit(log, client, database);
     189             :         }
     190             : 
     191             :         #ifndef CMAIL_NO_TLS
     192          25 :         if(!strncasecmp(client_data->recv_buffer, "stls", 4)){
     193          10 :                 if(client->tls_mode != TLS_NONE || client_data->listener->tls_mode != TLS_NEGOTIATE){
     194           2 :                         logprintf(log, LOG_WARNING, "Client tried STARTTLS at wrong time\n");
     195           2 :                         client_send(log, client, "-ERR Not possible now\r\n");
     196           2 :                         return -1;
     197             :                 }
     198             : 
     199           8 :                 client_send(log, client, "+OK Start TLS negotiation\r\n");
     200             : 
     201           8 :                 client->tls_mode = TLS_NEGOTIATE;
     202             : 
     203             :                 //FIXME this should be properly handled with a conditional
     204           8 :                 return tls_init_serverpeer(log, client, listener_data->tls_priorities, listener_data->tls_cert);
     205             :         }
     206             :         #endif
     207             : 
     208          15 :         if(!strncasecmp(client_data->recv_buffer, "xyzzy", 5)){
     209           2 :                 return pop_xyzzy(log, client, database);
     210             :         }
     211             : 
     212          13 :         client_send(log, client, "-ERR Unkown command\r\n");
     213          13 :         return -1;
     214             : }
     215             : 
     216          12 : int state_transaction(LOGGER log, CONNECTION* client, DATABASE* database){
     217          12 :         CLIENT* client_data = (CLIENT*)client->aux_data;
     218             : 
     219          12 :         if(!strncasecmp(client_data->recv_buffer, "capa", 4)){
     220           2 :                 return pop_capa(log, client, database);
     221             :         }
     222             : 
     223          10 :         if(!strncasecmp(client_data->recv_buffer, "quit", 4)){
     224           4 :                 return pop_quit(log, client, database);
     225             :         }
     226             : 
     227           6 :         if(!strncasecmp(client_data->recv_buffer, "xyzzy", 5)){
     228           2 :                 return pop_xyzzy(log, client, database);
     229             :         }
     230             : 
     231           4 :         if(!strncasecmp(client_data->recv_buffer, "stat", 4)){
     232           0 :                 return pop_stat(log, client, database);
     233             :         }
     234             : 
     235           4 :         if(!strncasecmp(client_data->recv_buffer, "list", 4)){
     236           0 :                 return pop_list(log, client, database, strtoul(client_data->recv_buffer+4, NULL, 10));
     237             :         }
     238             : 
     239           4 :         if(!strncasecmp(client_data->recv_buffer, "retr", 4)){
     240           0 :                 return pop_retr(log, client, database, strtoul(client_data->recv_buffer+4, NULL, 10));
     241             :         }
     242             : 
     243           4 :         if(!strncasecmp(client_data->recv_buffer, "dele", 4)){
     244           0 :                 return pop_dele(log, client, database, strtoul(client_data->recv_buffer+4, NULL, 10));
     245             :         }
     246             : 
     247           4 :         if(!strncasecmp(client_data->recv_buffer, "uidl", 4)){
     248           0 :                 return pop_uidl(log, client, database, strtoul(client_data->recv_buffer+4, NULL, 10));
     249             :         }
     250             : 
     251           4 :         if(!strncasecmp(client_data->recv_buffer, "rset", 4)){
     252           0 :                 return pop_rset(log, client, database);
     253             :         }
     254             : 
     255           4 :         if(!strncasecmp(client_data->recv_buffer, "noop", 4)){
     256           2 :                 logprintf(log, LOG_DEBUG, "Client noop\n");
     257           2 :                 client_send(log, client, "+OK Nothing happens\r\n");
     258           2 :                 return 0;
     259             :         }
     260             : 
     261           2 :         client_send(log, client, "-ERR Unkown command\r\n");
     262           2 :         return -1;
     263             : }
     264             : 
     265           0 : int state_update(LOGGER log, CONNECTION* client, DATABASE* database){
     266           0 :         CLIENT* client_data = (CLIENT*)client->aux_data;
     267             : 
     268             :         //this should probably never be reached
     269           0 :         logprintf(log, LOG_WARNING, "Commands received while in UPDATE state\n");
     270             : 
     271           0 :         if(!strncasecmp(client_data->recv_buffer, "quit", 4)){
     272           0 :                 return pop_quit(log, client, database);
     273             :         }
     274             : 
     275           0 :         client_send(log, client, "-ERR Unkown command\r\n");
     276           0 :         return -1;
     277             : }

Generated by: LCOV version 1.11