src/mail/ngx_mail_pop3_handler.c - nginx source code

Global variables defined

Functions defined

Source code


  1. /*
  2. * Copyright (C) Igor Sysoev
  3. * Copyright (C) Nginx, Inc.
  4. */


  5. #include <ngx_config.h>
  6. #include <ngx_core.h>
  7. #include <ngx_event.h>
  8. #include <ngx_mail.h>
  9. #include <ngx_mail_pop3_module.h>


  10. static ngx_int_t ngx_mail_pop3_user(ngx_mail_session_t *s, ngx_connection_t *c);
  11. static ngx_int_t ngx_mail_pop3_pass(ngx_mail_session_t *s, ngx_connection_t *c);
  12. static ngx_int_t ngx_mail_pop3_capa(ngx_mail_session_t *s, ngx_connection_t *c,
  13.     ngx_int_t stls);
  14. static ngx_int_t ngx_mail_pop3_stls(ngx_mail_session_t *s, ngx_connection_t *c);
  15. static ngx_int_t ngx_mail_pop3_apop(ngx_mail_session_t *s, ngx_connection_t *c);
  16. static ngx_int_t ngx_mail_pop3_auth(ngx_mail_session_t *s, ngx_connection_t *c);


  17. static u_char  pop3_greeting[] = "+OK POP3 ready" CRLF;
  18. static u_char  pop3_ok[] = "+OK" CRLF;
  19. static u_char  pop3_next[] = "+ " CRLF;
  20. static u_char  pop3_username[] = "+ VXNlcm5hbWU6" CRLF;
  21. static u_char  pop3_password[] = "+ UGFzc3dvcmQ6" CRLF;
  22. static u_char  pop3_invalid_command[] = "-ERR invalid command" CRLF;


  23. void
  24. ngx_mail_pop3_init_session(ngx_mail_session_t *s, ngx_connection_t *c)
  25. {
  26.     u_char                    *p;
  27.     ngx_mail_core_srv_conf_t  *cscf;
  28.     ngx_mail_pop3_srv_conf_t  *pscf;

  29.     pscf = ngx_mail_get_module_srv_conf(s, ngx_mail_pop3_module);
  30.     cscf = ngx_mail_get_module_srv_conf(s, ngx_mail_core_module);

  31.     if (pscf->auth_methods
  32.         & (NGX_MAIL_AUTH_APOP_ENABLED|NGX_MAIL_AUTH_CRAM_MD5_ENABLED))
  33.     {
  34.         if (ngx_mail_salt(s, c, cscf) != NGX_OK) {
  35.             ngx_mail_session_internal_server_error(s);
  36.             return;
  37.         }

  38.         s->out.data = ngx_pnalloc(c->pool, sizeof(pop3_greeting) + s->salt.len);
  39.         if (s->out.data == NULL) {
  40.             ngx_mail_session_internal_server_error(s);
  41.             return;
  42.         }

  43.         p = ngx_cpymem(s->out.data, pop3_greeting, sizeof(pop3_greeting) - 3);
  44.         *p++ = ' ';
  45.         p = ngx_cpymem(p, s->salt.data, s->salt.len);

  46.         s->out.len = p - s->out.data;

  47.     } else {
  48.         ngx_str_set(&s->out, pop3_greeting);
  49.     }

  50.     c->read->handler = ngx_mail_pop3_init_protocol;

  51.     ngx_add_timer(c->read, cscf->timeout);

  52.     if (ngx_handle_read_event(c->read, 0) != NGX_OK) {
  53.         ngx_mail_close_connection(c);
  54.     }

  55.     ngx_mail_send(c->write);
  56. }


  57. void
  58. ngx_mail_pop3_init_protocol(ngx_event_t *rev)
  59. {
  60.     ngx_connection_t    *c;
  61.     ngx_mail_session_t  *s;

  62.     c = rev->data;

  63.     c->log->action = "in auth state";

  64.     if (rev->timedout) {
  65.         ngx_log_error(NGX_LOG_INFO, c->log, NGX_ETIMEDOUT, "client timed out");
  66.         c->timedout = 1;
  67.         ngx_mail_close_connection(c);
  68.         return;
  69.     }

  70.     s = c->data;

  71.     if (s->buffer == NULL) {
  72.         if (ngx_array_init(&s->args, c->pool, 2, sizeof(ngx_str_t))
  73.             == NGX_ERROR)
  74.         {
  75.             ngx_mail_session_internal_server_error(s);
  76.             return;
  77.         }

  78.         s->buffer = ngx_create_temp_buf(c->pool, 128);
  79.         if (s->buffer == NULL) {
  80.             ngx_mail_session_internal_server_error(s);
  81.             return;
  82.         }
  83.     }

  84.     s->mail_state = ngx_pop3_start;
  85.     c->read->handler = ngx_mail_pop3_auth_state;

  86.     ngx_mail_pop3_auth_state(rev);
  87. }


  88. void
  89. ngx_mail_pop3_auth_state(ngx_event_t *rev)
  90. {
  91.     ngx_int_t            rc;
  92.     ngx_connection_t    *c;
  93.     ngx_mail_session_t  *s;

  94.     c = rev->data;
  95.     s = c->data;

  96.     ngx_log_debug0(NGX_LOG_DEBUG_MAIL, c->log, 0, "pop3 auth state");

  97.     if (rev->timedout) {
  98.         ngx_log_error(NGX_LOG_INFO, c->log, NGX_ETIMEDOUT, "client timed out");
  99.         c->timedout = 1;
  100.         ngx_mail_close_connection(c);
  101.         return;
  102.     }

  103.     if (s->out.len) {
  104.         ngx_log_debug0(NGX_LOG_DEBUG_MAIL, c->log, 0, "pop3 send handler busy");
  105.         s->blocked = 1;

  106.         if (ngx_handle_read_event(c->read, 0) != NGX_OK) {
  107.             ngx_mail_close_connection(c);
  108.             return;
  109.         }

  110.         return;
  111.     }

  112.     s->blocked = 0;

  113.     rc = ngx_mail_read_command(s, c);

  114.     if (rc == NGX_AGAIN) {
  115.         if (ngx_handle_read_event(c->read, 0) != NGX_OK) {
  116.             ngx_mail_session_internal_server_error(s);
  117.             return;
  118.         }

  119.         return;
  120.     }

  121.     if (rc == NGX_ERROR) {
  122.         return;
  123.     }

  124.     ngx_str_set(&s->out, pop3_ok);

  125.     if (rc == NGX_OK) {
  126.         switch (s->mail_state) {

  127.         case ngx_pop3_start:

  128.             switch (s->command) {

  129.             case NGX_POP3_USER:
  130.                 rc = ngx_mail_pop3_user(s, c);
  131.                 break;

  132.             case NGX_POP3_CAPA:
  133.                 rc = ngx_mail_pop3_capa(s, c, 1);
  134.                 break;

  135.             case NGX_POP3_APOP:
  136.                 rc = ngx_mail_pop3_apop(s, c);
  137.                 break;

  138.             case NGX_POP3_AUTH:
  139.                 rc = ngx_mail_pop3_auth(s, c);
  140.                 break;

  141.             case NGX_POP3_QUIT:
  142.                 s->quit = 1;
  143.                 break;

  144.             case NGX_POP3_NOOP:
  145.                 break;

  146.             case NGX_POP3_STLS:
  147.                 rc = ngx_mail_pop3_stls(s, c);
  148.                 break;

  149.             default:
  150.                 rc = NGX_MAIL_PARSE_INVALID_COMMAND;
  151.                 break;
  152.             }

  153.             break;

  154.         case ngx_pop3_user:

  155.             switch (s->command) {

  156.             case NGX_POP3_PASS:
  157.                 rc = ngx_mail_pop3_pass(s, c);
  158.                 break;

  159.             case NGX_POP3_CAPA:
  160.                 rc = ngx_mail_pop3_capa(s, c, 0);
  161.                 break;

  162.             case NGX_POP3_QUIT:
  163.                 s->quit = 1;
  164.                 break;

  165.             case NGX_POP3_NOOP:
  166.                 break;

  167.             default:
  168.                 rc = NGX_MAIL_PARSE_INVALID_COMMAND;
  169.                 break;
  170.             }

  171.             break;

  172.         /* suppress warnings */
  173.         case ngx_pop3_passwd:
  174.             break;

  175.         case ngx_pop3_auth_login_username:
  176.             rc = ngx_mail_auth_login_username(s, c, 0);

  177.             ngx_str_set(&s->out, pop3_password);
  178.             s->mail_state = ngx_pop3_auth_login_password;
  179.             break;

  180.         case ngx_pop3_auth_login_password:
  181.             rc = ngx_mail_auth_login_password(s, c);
  182.             break;

  183.         case ngx_pop3_auth_plain:
  184.             rc = ngx_mail_auth_plain(s, c, 0);
  185.             break;

  186.         case ngx_pop3_auth_cram_md5:
  187.             rc = ngx_mail_auth_cram_md5(s, c);
  188.             break;

  189.         case ngx_pop3_auth_external:
  190.             rc = ngx_mail_auth_external(s, c, 0);
  191.             break;
  192.         }
  193.     }

  194.     if (s->buffer->pos < s->buffer->last) {
  195.         s->blocked = 1;
  196.     }

  197.     switch (rc) {

  198.     case NGX_DONE:
  199.         ngx_mail_auth(s, c);
  200.         return;

  201.     case NGX_ERROR:
  202.         ngx_mail_session_internal_server_error(s);
  203.         return;

  204.     case NGX_MAIL_PARSE_INVALID_COMMAND:
  205.         s->mail_state = ngx_pop3_start;
  206.         s->state = 0;

  207.         ngx_str_set(&s->out, pop3_invalid_command);

  208.         /* fall through */

  209.     case NGX_OK:

  210.         s->args.nelts = 0;

  211.         if (s->buffer->pos == s->buffer->last) {
  212.             s->buffer->pos = s->buffer->start;
  213.             s->buffer->last = s->buffer->start;
  214.         }

  215.         if (s->state) {
  216.             s->arg_start = s->buffer->pos;
  217.         }

  218.         if (ngx_handle_read_event(c->read, 0) != NGX_OK) {
  219.             ngx_mail_session_internal_server_error(s);
  220.             return;
  221.         }

  222.         ngx_mail_send(c->write);
  223.     }
  224. }

  225. static ngx_int_t
  226. ngx_mail_pop3_user(ngx_mail_session_t *s, ngx_connection_t *c)
  227. {
  228.     ngx_str_t  *arg;

  229. #if (NGX_MAIL_SSL)
  230.     if (ngx_mail_starttls_only(s, c)) {
  231.         return NGX_MAIL_PARSE_INVALID_COMMAND;
  232.     }
  233. #endif

  234.     if (s->args.nelts != 1) {
  235.         return NGX_MAIL_PARSE_INVALID_COMMAND;
  236.     }

  237.     arg = s->args.elts;
  238.     s->login.len = arg[0].len;
  239.     s->login.data = ngx_pnalloc(c->pool, s->login.len);
  240.     if (s->login.data == NULL) {
  241.         return NGX_ERROR;
  242.     }

  243.     ngx_memcpy(s->login.data, arg[0].data, s->login.len);

  244.     ngx_log_debug1(NGX_LOG_DEBUG_MAIL, c->log, 0,
  245.                    "pop3 login: \"%V\"", &s->login);

  246.     s->mail_state = ngx_pop3_user;

  247.     return NGX_OK;
  248. }


  249. static ngx_int_t
  250. ngx_mail_pop3_pass(ngx_mail_session_t *s, ngx_connection_t *c)
  251. {
  252.     ngx_str_t  *arg;

  253.     if (s->args.nelts != 1) {
  254.         return NGX_MAIL_PARSE_INVALID_COMMAND;
  255.     }

  256.     arg = s->args.elts;
  257.     s->passwd.len = arg[0].len;
  258.     s->passwd.data = ngx_pnalloc(c->pool, s->passwd.len);
  259.     if (s->passwd.data == NULL) {
  260.         return NGX_ERROR;
  261.     }

  262.     ngx_memcpy(s->passwd.data, arg[0].data, s->passwd.len);

  263. #if (NGX_DEBUG_MAIL_PASSWD)
  264.     ngx_log_debug1(NGX_LOG_DEBUG_MAIL, c->log, 0,
  265.                    "pop3 passwd: \"%V\"", &s->passwd);
  266. #endif

  267.     return NGX_DONE;
  268. }


  269. static ngx_int_t
  270. ngx_mail_pop3_capa(ngx_mail_session_t *s, ngx_connection_t *c, ngx_int_t stls)
  271. {
  272.     ngx_mail_pop3_srv_conf_t  *pscf;

  273.     pscf = ngx_mail_get_module_srv_conf(s, ngx_mail_pop3_module);

  274. #if (NGX_MAIL_SSL)

  275.     if (stls && c->ssl == NULL) {
  276.         ngx_mail_ssl_conf_t  *sslcf;

  277.         sslcf = ngx_mail_get_module_srv_conf(s, ngx_mail_ssl_module);

  278.         if (sslcf->starttls == NGX_MAIL_STARTTLS_ON) {
  279.             s->out = pscf->starttls_capability;
  280.             return NGX_OK;
  281.         }

  282.         if (sslcf->starttls == NGX_MAIL_STARTTLS_ONLY) {
  283.             s->out = pscf->starttls_only_capability;
  284.             return NGX_OK;
  285.         }
  286.     }

  287. #endif

  288.     s->out = pscf->capability;
  289.     return NGX_OK;
  290. }


  291. static ngx_int_t
  292. ngx_mail_pop3_stls(ngx_mail_session_t *s, ngx_connection_t *c)
  293. {
  294. #if (NGX_MAIL_SSL)
  295.     ngx_mail_ssl_conf_t  *sslcf;

  296.     if (c->ssl == NULL) {
  297.         sslcf = ngx_mail_get_module_srv_conf(s, ngx_mail_ssl_module);
  298.         if (sslcf->starttls) {
  299.             s->buffer->pos = s->buffer->start;
  300.             s->buffer->last = s->buffer->start;
  301.             c->read->handler = ngx_mail_starttls_handler;
  302.             return NGX_OK;
  303.         }
  304.     }

  305. #endif

  306.     return NGX_MAIL_PARSE_INVALID_COMMAND;
  307. }


  308. static ngx_int_t
  309. ngx_mail_pop3_apop(ngx_mail_session_t *s, ngx_connection_t *c)
  310. {
  311.     ngx_str_t                 *arg;
  312.     ngx_mail_pop3_srv_conf_t  *pscf;

  313. #if (NGX_MAIL_SSL)
  314.     if (ngx_mail_starttls_only(s, c)) {
  315.         return NGX_MAIL_PARSE_INVALID_COMMAND;
  316.     }
  317. #endif

  318.     if (s->args.nelts != 2) {
  319.         return NGX_MAIL_PARSE_INVALID_COMMAND;
  320.     }

  321.     pscf = ngx_mail_get_module_srv_conf(s, ngx_mail_pop3_module);

  322.     if (!(pscf->auth_methods & NGX_MAIL_AUTH_APOP_ENABLED)) {
  323.         return NGX_MAIL_PARSE_INVALID_COMMAND;
  324.     }

  325.     arg = s->args.elts;

  326.     s->login.len = arg[0].len;
  327.     s->login.data = ngx_pnalloc(c->pool, s->login.len);
  328.     if (s->login.data == NULL) {
  329.         return NGX_ERROR;
  330.     }

  331.     ngx_memcpy(s->login.data, arg[0].data, s->login.len);

  332.     s->passwd.len = arg[1].len;
  333.     s->passwd.data = ngx_pnalloc(c->pool, s->passwd.len);
  334.     if (s->passwd.data == NULL) {
  335.         return NGX_ERROR;
  336.     }

  337.     ngx_memcpy(s->passwd.data, arg[1].data, s->passwd.len);

  338.     ngx_log_debug2(NGX_LOG_DEBUG_MAIL, c->log, 0,
  339.                    "pop3 apop: \"%V\" \"%V\"", &s->login, &s->passwd);

  340.     s->auth_method = NGX_MAIL_AUTH_APOP;

  341.     return NGX_DONE;
  342. }


  343. static ngx_int_t
  344. ngx_mail_pop3_auth(ngx_mail_session_t *s, ngx_connection_t *c)
  345. {
  346.     ngx_int_t                  rc;
  347.     ngx_mail_pop3_srv_conf_t  *pscf;

  348. #if (NGX_MAIL_SSL)
  349.     if (ngx_mail_starttls_only(s, c)) {
  350.         return NGX_MAIL_PARSE_INVALID_COMMAND;
  351.     }
  352. #endif

  353.     pscf = ngx_mail_get_module_srv_conf(s, ngx_mail_pop3_module);

  354.     if (s->args.nelts == 0) {
  355.         s->out = pscf->auth_capability;
  356.         s->state = 0;

  357.         return NGX_OK;
  358.     }

  359.     rc = ngx_mail_auth_parse(s, c);

  360.     switch (rc) {

  361.     case NGX_MAIL_AUTH_LOGIN:

  362.         ngx_str_set(&s->out, pop3_username);
  363.         s->mail_state = ngx_pop3_auth_login_username;

  364.         return NGX_OK;

  365.     case NGX_MAIL_AUTH_LOGIN_USERNAME:

  366.         ngx_str_set(&s->out, pop3_password);
  367.         s->mail_state = ngx_pop3_auth_login_password;

  368.         return ngx_mail_auth_login_username(s, c, 1);

  369.     case NGX_MAIL_AUTH_PLAIN:

  370.         ngx_str_set(&s->out, pop3_next);
  371.         s->mail_state = ngx_pop3_auth_plain;

  372.         return NGX_OK;

  373.     case NGX_MAIL_AUTH_CRAM_MD5:

  374.         if (!(pscf->auth_methods & NGX_MAIL_AUTH_CRAM_MD5_ENABLED)) {
  375.             return NGX_MAIL_PARSE_INVALID_COMMAND;
  376.         }

  377.         if (ngx_mail_auth_cram_md5_salt(s, c, "+ ", 2) == NGX_OK) {
  378.             s->mail_state = ngx_pop3_auth_cram_md5;
  379.             return NGX_OK;
  380.         }

  381.         return NGX_ERROR;

  382.     case NGX_MAIL_AUTH_EXTERNAL:

  383.         if (!(pscf->auth_methods & NGX_MAIL_AUTH_EXTERNAL_ENABLED)) {
  384.             return NGX_MAIL_PARSE_INVALID_COMMAND;
  385.         }

  386.         ngx_str_set(&s->out, pop3_username);
  387.         s->mail_state = ngx_pop3_auth_external;

  388.         return NGX_OK;
  389.     }

  390.     return rc;
  391. }