src/mail/ngx_mail_ssl_module.c - nginx-1.7.10

Global variables defined

Functions defined

Macros 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_mail.h>


  8. #define NGX_DEFAULT_CIPHERS     "HIGH:!aNULL:!MD5"
  9. #define NGX_DEFAULT_ECDH_CURVE  "prime256v1"


  10. static void *ngx_mail_ssl_create_conf(ngx_conf_t *cf);
  11. static char *ngx_mail_ssl_merge_conf(ngx_conf_t *cf, void *parent, void *child);

  12. static char *ngx_mail_ssl_enable(ngx_conf_t *cf, ngx_command_t *cmd,
  13.     void *conf);
  14. static char *ngx_mail_ssl_starttls(ngx_conf_t *cf, ngx_command_t *cmd,
  15.     void *conf);
  16. static char *ngx_mail_ssl_password_file(ngx_conf_t *cf, ngx_command_t *cmd,
  17.     void *conf);
  18. static char *ngx_mail_ssl_session_cache(ngx_conf_t *cf, ngx_command_t *cmd,
  19.     void *conf);


  20. static ngx_conf_enum_t  ngx_mail_starttls_state[] = {
  21.     { ngx_string("off"), NGX_MAIL_STARTTLS_OFF },
  22.     { ngx_string("on"), NGX_MAIL_STARTTLS_ON },
  23.     { ngx_string("only"), NGX_MAIL_STARTTLS_ONLY },
  24.     { ngx_null_string, 0 }
  25. };



  26. static ngx_conf_bitmask_t  ngx_mail_ssl_protocols[] = {
  27.     { ngx_string("SSLv2"), NGX_SSL_SSLv2 },
  28.     { ngx_string("SSLv3"), NGX_SSL_SSLv3 },
  29.     { ngx_string("TLSv1"), NGX_SSL_TLSv1 },
  30.     { ngx_string("TLSv1.1"), NGX_SSL_TLSv1_1 },
  31.     { ngx_string("TLSv1.2"), NGX_SSL_TLSv1_2 },
  32.     { ngx_null_string, 0 }
  33. };


  34. static ngx_command_t  ngx_mail_ssl_commands[] = {

  35.     { ngx_string("ssl"),
  36.       NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_FLAG,
  37.       ngx_mail_ssl_enable,
  38.       NGX_MAIL_SRV_CONF_OFFSET,
  39.       offsetof(ngx_mail_ssl_conf_t, enable),
  40.       NULL },

  41.     { ngx_string("starttls"),
  42.       NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_TAKE1,
  43.       ngx_mail_ssl_starttls,
  44.       NGX_MAIL_SRV_CONF_OFFSET,
  45.       offsetof(ngx_mail_ssl_conf_t, starttls),
  46.       ngx_mail_starttls_state },

  47.     { ngx_string("ssl_certificate"),
  48.       NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_TAKE1,
  49.       ngx_conf_set_str_slot,
  50.       NGX_MAIL_SRV_CONF_OFFSET,
  51.       offsetof(ngx_mail_ssl_conf_t, certificate),
  52.       NULL },

  53.     { ngx_string("ssl_certificate_key"),
  54.       NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_TAKE1,
  55.       ngx_conf_set_str_slot,
  56.       NGX_MAIL_SRV_CONF_OFFSET,
  57.       offsetof(ngx_mail_ssl_conf_t, certificate_key),
  58.       NULL },

  59.     { ngx_string("ssl_password_file"),
  60.       NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_TAKE1,
  61.       ngx_mail_ssl_password_file,
  62.       NGX_MAIL_SRV_CONF_OFFSET,
  63.       0,
  64.       NULL },

  65.     { ngx_string("ssl_dhparam"),
  66.       NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_TAKE1,
  67.       ngx_conf_set_str_slot,
  68.       NGX_MAIL_SRV_CONF_OFFSET,
  69.       offsetof(ngx_mail_ssl_conf_t, dhparam),
  70.       NULL },

  71.     { ngx_string("ssl_ecdh_curve"),
  72.       NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_TAKE1,
  73.       ngx_conf_set_str_slot,
  74.       NGX_MAIL_SRV_CONF_OFFSET,
  75.       offsetof(ngx_mail_ssl_conf_t, ecdh_curve),
  76.       NULL },

  77.     { ngx_string("ssl_protocols"),
  78.       NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_1MORE,
  79.       ngx_conf_set_bitmask_slot,
  80.       NGX_MAIL_SRV_CONF_OFFSET,
  81.       offsetof(ngx_mail_ssl_conf_t, protocols),
  82.       &ngx_mail_ssl_protocols },

  83.     { ngx_string("ssl_ciphers"),
  84.       NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_TAKE1,
  85.       ngx_conf_set_str_slot,
  86.       NGX_MAIL_SRV_CONF_OFFSET,
  87.       offsetof(ngx_mail_ssl_conf_t, ciphers),
  88.       NULL },

  89.     { ngx_string("ssl_prefer_server_ciphers"),
  90.       NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_FLAG,
  91.       ngx_conf_set_flag_slot,
  92.       NGX_MAIL_SRV_CONF_OFFSET,
  93.       offsetof(ngx_mail_ssl_conf_t, prefer_server_ciphers),
  94.       NULL },

  95.     { ngx_string("ssl_session_cache"),
  96.       NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_TAKE12,
  97.       ngx_mail_ssl_session_cache,
  98.       NGX_MAIL_SRV_CONF_OFFSET,
  99.       0,
  100.       NULL },

  101.     { ngx_string("ssl_session_tickets"),
  102.       NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_FLAG,
  103.       ngx_conf_set_flag_slot,
  104.       NGX_MAIL_SRV_CONF_OFFSET,
  105.       offsetof(ngx_mail_ssl_conf_t, session_tickets),
  106.       NULL },

  107.     { ngx_string("ssl_session_ticket_key"),
  108.       NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_TAKE1,
  109.       ngx_conf_set_str_array_slot,
  110.       NGX_MAIL_SRV_CONF_OFFSET,
  111.       offsetof(ngx_mail_ssl_conf_t, session_ticket_keys),
  112.       NULL },

  113.     { ngx_string("ssl_session_timeout"),
  114.       NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_TAKE1,
  115.       ngx_conf_set_sec_slot,
  116.       NGX_MAIL_SRV_CONF_OFFSET,
  117.       offsetof(ngx_mail_ssl_conf_t, session_timeout),
  118.       NULL },

  119.       ngx_null_command
  120. };


  121. static ngx_mail_module_t  ngx_mail_ssl_module_ctx = {
  122.     NULL,                                  /* protocol */

  123.     NULL,                                  /* create main configuration */
  124.     NULL,                                  /* init main configuration */

  125.     ngx_mail_ssl_create_conf,              /* create server configuration */
  126.     ngx_mail_ssl_merge_conf                /* merge server configuration */
  127. };


  128. ngx_module_t  ngx_mail_ssl_module = {
  129.     NGX_MODULE_V1,
  130.     &ngx_mail_ssl_module_ctx,              /* module context */
  131.     ngx_mail_ssl_commands,                 /* module directives */
  132.     NGX_MAIL_MODULE,                       /* module type */
  133.     NULL,                                  /* init master */
  134.     NULL,                                  /* init module */
  135.     NULL,                                  /* init process */
  136.     NULL,                                  /* init thread */
  137.     NULL,                                  /* exit thread */
  138.     NULL,                                  /* exit process */
  139.     NULL,                                  /* exit master */
  140.     NGX_MODULE_V1_PADDING
  141. };


  142. static ngx_str_t ngx_mail_ssl_sess_id_ctx = ngx_string("MAIL");


  143. static void *
  144. ngx_mail_ssl_create_conf(ngx_conf_t *cf)
  145. {
  146.     ngx_mail_ssl_conf_t  *scf;

  147.     scf = ngx_pcalloc(cf->pool, sizeof(ngx_mail_ssl_conf_t));
  148.     if (scf == NULL) {
  149.         return NULL;
  150.     }

  151.     /*
  152.      * set by ngx_pcalloc():
  153.      *
  154.      *     scf->protocols = 0;
  155.      *     scf->certificate = { 0, NULL };
  156.      *     scf->certificate_key = { 0, NULL };
  157.      *     scf->dhparam = { 0, NULL };
  158.      *     scf->ecdh_curve = { 0, NULL };
  159.      *     scf->ciphers = { 0, NULL };
  160.      *     scf->shm_zone = NULL;
  161.      */

  162.     scf->enable = NGX_CONF_UNSET;
  163.     scf->starttls = NGX_CONF_UNSET_UINT;
  164.     scf->passwords = NGX_CONF_UNSET_PTR;
  165.     scf->prefer_server_ciphers = NGX_CONF_UNSET;
  166.     scf->builtin_session_cache = NGX_CONF_UNSET;
  167.     scf->session_timeout = NGX_CONF_UNSET;
  168.     scf->session_tickets = NGX_CONF_UNSET;
  169.     scf->session_ticket_keys = NGX_CONF_UNSET_PTR;

  170.     return scf;
  171. }


  172. static char *
  173. ngx_mail_ssl_merge_conf(ngx_conf_t *cf, void *parent, void *child)
  174. {
  175.     ngx_mail_ssl_conf_t *prev = parent;
  176.     ngx_mail_ssl_conf_t *conf = child;

  177.     char                *mode;
  178.     ngx_pool_cleanup_t  *cln;

  179.     ngx_conf_merge_value(conf->enable, prev->enable, 0);
  180.     ngx_conf_merge_uint_value(conf->starttls, prev->starttls,
  181.                          NGX_MAIL_STARTTLS_OFF);

  182.     ngx_conf_merge_value(conf->session_timeout,
  183.                          prev->session_timeout, 300);

  184.     ngx_conf_merge_value(conf->prefer_server_ciphers,
  185.                          prev->prefer_server_ciphers, 0);

  186.     ngx_conf_merge_bitmask_value(conf->protocols, prev->protocols,
  187.                          (NGX_CONF_BITMASK_SET|NGX_SSL_SSLv3|NGX_SSL_TLSv1
  188.                           |NGX_SSL_TLSv1_1|NGX_SSL_TLSv1_2));

  189.     ngx_conf_merge_str_value(conf->certificate, prev->certificate, "");
  190.     ngx_conf_merge_str_value(conf->certificate_key, prev->certificate_key, "");

  191.     ngx_conf_merge_ptr_value(conf->passwords, prev->passwords, NULL);

  192.     ngx_conf_merge_str_value(conf->dhparam, prev->dhparam, "");

  193.     ngx_conf_merge_str_value(conf->ecdh_curve, prev->ecdh_curve,
  194.                          NGX_DEFAULT_ECDH_CURVE);

  195.     ngx_conf_merge_str_value(conf->ciphers, prev->ciphers, NGX_DEFAULT_CIPHERS);


  196.     conf->ssl.log = cf->log;

  197.     if (conf->enable) {
  198.        mode = "ssl";

  199.     } else if (conf->starttls != NGX_MAIL_STARTTLS_OFF) {
  200.        mode = "starttls";

  201.     } else {
  202.        mode = "";
  203.     }

  204.     if (conf->file == NULL) {
  205.         conf->file = prev->file;
  206.         conf->line = prev->line;
  207.     }

  208.     if (*mode) {

  209.         if (conf->certificate.len == 0) {
  210.             ngx_log_error(NGX_LOG_EMERG, cf->log, 0,
  211.                           "no \"ssl_certificate\" is defined for "
  212.                           "the \"%s\" directive in %s:%ui",
  213.                           mode, conf->file, conf->line);
  214.             return NGX_CONF_ERROR;
  215.         }

  216.         if (conf->certificate_key.len == 0) {
  217.             ngx_log_error(NGX_LOG_EMERG, cf->log, 0,
  218.                           "no \"ssl_certificate_key\" is defined for "
  219.                           "the \"%s\" directive in %s:%ui",
  220.                           mode, conf->file, conf->line);
  221.             return NGX_CONF_ERROR;
  222.         }

  223.     } else {

  224.         if (conf->certificate.len == 0) {
  225.             return NGX_CONF_OK;
  226.         }

  227.         if (conf->certificate_key.len == 0) {
  228.             ngx_log_error(NGX_LOG_EMERG, cf->log, 0,
  229.                           "no \"ssl_certificate_key\" is defined "
  230.                           "for certificate \"%V\"",
  231.                           &conf->certificate);
  232.             return NGX_CONF_ERROR;
  233.         }
  234.     }

  235.     if (ngx_ssl_create(&conf->ssl, conf->protocols, NULL) != NGX_OK) {
  236.         return NGX_CONF_ERROR;
  237.     }

  238.     cln = ngx_pool_cleanup_add(cf->pool, 0);
  239.     if (cln == NULL) {
  240.         return NGX_CONF_ERROR;
  241.     }

  242.     cln->handler = ngx_ssl_cleanup_ctx;
  243.     cln->data = &conf->ssl;

  244.     if (ngx_ssl_certificate(cf, &conf->ssl, &conf->certificate,
  245.                             &conf->certificate_key, conf->passwords)
  246.         != NGX_OK)
  247.     {
  248.         return NGX_CONF_ERROR;
  249.     }

  250.     if (SSL_CTX_set_cipher_list(conf->ssl.ctx,
  251.                                 (const char *) conf->ciphers.data)
  252.         == 0)
  253.     {
  254.         ngx_ssl_error(NGX_LOG_EMERG, cf->log, 0,
  255.                       "SSL_CTX_set_cipher_list(\"%V\") failed",
  256.                       &conf->ciphers);
  257.         return NGX_CONF_ERROR;
  258.     }

  259.     if (conf->prefer_server_ciphers) {
  260.         SSL_CTX_set_options(conf->ssl.ctx, SSL_OP_CIPHER_SERVER_PREFERENCE);
  261.     }

  262.     SSL_CTX_set_tmp_rsa_callback(conf->ssl.ctx, ngx_ssl_rsa512_key_callback);

  263.     if (ngx_ssl_dhparam(cf, &conf->ssl, &conf->dhparam) != NGX_OK) {
  264.         return NGX_CONF_ERROR;
  265.     }

  266.     if (ngx_ssl_ecdh_curve(cf, &conf->ssl, &conf->ecdh_curve) != NGX_OK) {
  267.         return NGX_CONF_ERROR;
  268.     }

  269.     ngx_conf_merge_value(conf->builtin_session_cache,
  270.                          prev->builtin_session_cache, NGX_SSL_NONE_SCACHE);

  271.     if (conf->shm_zone == NULL) {
  272.         conf->shm_zone = prev->shm_zone;
  273.     }

  274.     if (ngx_ssl_session_cache(&conf->ssl, &ngx_mail_ssl_sess_id_ctx,
  275.                               conf->builtin_session_cache,
  276.                               conf->shm_zone, conf->session_timeout)
  277.         != NGX_OK)
  278.     {
  279.         return NGX_CONF_ERROR;
  280.     }

  281.     ngx_conf_merge_value(conf->session_tickets,
  282.                          prev->session_tickets, 1);

  283. #ifdef SSL_OP_NO_TICKET
  284.     if (!conf->session_tickets) {
  285.         SSL_CTX_set_options(conf->ssl.ctx, SSL_OP_NO_TICKET);
  286.     }
  287. #endif

  288.     ngx_conf_merge_ptr_value(conf->session_ticket_keys,
  289.                          prev->session_ticket_keys, NULL);

  290.     if (ngx_ssl_session_ticket_keys(cf, &conf->ssl, conf->session_ticket_keys)
  291.         != NGX_OK)
  292.     {
  293.         return NGX_CONF_ERROR;
  294.     }

  295.     return NGX_CONF_OK;
  296. }


  297. static char *
  298. ngx_mail_ssl_enable(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
  299. {
  300.     ngx_mail_ssl_conf_t  *scf = conf;

  301.     char  *rv;

  302.     rv = ngx_conf_set_flag_slot(cf, cmd, conf);

  303.     if (rv != NGX_CONF_OK) {
  304.         return rv;
  305.     }

  306.     if (scf->enable && (ngx_int_t) scf->starttls > NGX_MAIL_STARTTLS_OFF) {
  307.         ngx_conf_log_error(NGX_LOG_WARN, cf, 0,
  308.                            "\"starttls\" directive conflicts with \"ssl on\"");
  309.         return NGX_CONF_ERROR;
  310.     }

  311.     scf->file = cf->conf_file->file.name.data;
  312.     scf->line = cf->conf_file->line;

  313.     return NGX_CONF_OK;
  314. }


  315. static char *
  316. ngx_mail_ssl_starttls(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
  317. {
  318.     ngx_mail_ssl_conf_t  *scf = conf;

  319.     char  *rv;

  320.     rv = ngx_conf_set_enum_slot(cf, cmd, conf);

  321.     if (rv != NGX_CONF_OK) {
  322.         return rv;
  323.     }

  324.     if (scf->enable == 1 && (ngx_int_t) scf->starttls > NGX_MAIL_STARTTLS_OFF) {
  325.         ngx_conf_log_error(NGX_LOG_WARN, cf, 0,
  326.                            "\"ssl\" directive conflicts with \"starttls\"");
  327.         return NGX_CONF_ERROR;
  328.     }

  329.     scf->file = cf->conf_file->file.name.data;
  330.     scf->line = cf->conf_file->line;

  331.     return NGX_CONF_OK;
  332. }


  333. static char *
  334. ngx_mail_ssl_password_file(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
  335. {
  336.     ngx_mail_ssl_conf_t  *scf = conf;

  337.     ngx_str_t  *value;

  338.     if (scf->passwords != NGX_CONF_UNSET_PTR) {
  339.         return "is duplicate";
  340.     }

  341.     value = cf->args->elts;

  342.     scf->passwords = ngx_ssl_read_password_file(cf, &value[1]);

  343.     if (scf->passwords == NULL) {
  344.         return NGX_CONF_ERROR;
  345.     }

  346.     return NGX_CONF_OK;
  347. }


  348. static char *
  349. ngx_mail_ssl_session_cache(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
  350. {
  351.     ngx_mail_ssl_conf_t  *scf = conf;

  352.     size_t       len;
  353.     ngx_str_t   *value, name, size;
  354.     ngx_int_t    n;
  355.     ngx_uint_t   i, j;

  356.     value = cf->args->elts;

  357.     for (i = 1; i < cf->args->nelts; i++) {

  358.         if (ngx_strcmp(value[i].data, "off") == 0) {
  359.             scf->builtin_session_cache = NGX_SSL_NO_SCACHE;
  360.             continue;
  361.         }

  362.         if (ngx_strcmp(value[i].data, "none") == 0) {
  363.             scf->builtin_session_cache = NGX_SSL_NONE_SCACHE;
  364.             continue;
  365.         }

  366.         if (ngx_strcmp(value[i].data, "builtin") == 0) {
  367.             scf->builtin_session_cache = NGX_SSL_DFLT_BUILTIN_SCACHE;
  368.             continue;
  369.         }

  370.         if (value[i].len > sizeof("builtin:") - 1
  371.             && ngx_strncmp(value[i].data, "builtin:", sizeof("builtin:") - 1)
  372.                == 0)
  373.         {
  374.             n = ngx_atoi(value[i].data + sizeof("builtin:") - 1,
  375.                          value[i].len - (sizeof("builtin:") - 1));

  376.             if (n == NGX_ERROR) {
  377.                 goto invalid;
  378.             }

  379.             scf->builtin_session_cache = n;

  380.             continue;
  381.         }

  382.         if (value[i].len > sizeof("shared:") - 1
  383.             && ngx_strncmp(value[i].data, "shared:", sizeof("shared:") - 1)
  384.                == 0)
  385.         {
  386.             len = 0;

  387.             for (j = sizeof("shared:") - 1; j < value[i].len; j++) {
  388.                 if (value[i].data[j] == ':') {
  389.                     break;
  390.                 }

  391.                 len++;
  392.             }

  393.             if (len == 0) {
  394.                 goto invalid;
  395.             }

  396.             name.len = len;
  397.             name.data = value[i].data + sizeof("shared:") - 1;

  398.             size.len = value[i].len - j - 1;
  399.             size.data = name.data + len + 1;

  400.             n = ngx_parse_size(&size);

  401.             if (n == NGX_ERROR) {
  402.                 goto invalid;
  403.             }

  404.             if (n < (ngx_int_t) (8 * ngx_pagesize)) {
  405.                 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
  406.                                    "session cache \"%V\" is too small",
  407.                                    &value[i]);

  408.                 return NGX_CONF_ERROR;
  409.             }

  410.             scf->shm_zone = ngx_shared_memory_add(cf, &name, n,
  411.                                                    &ngx_mail_ssl_module);
  412.             if (scf->shm_zone == NULL) {
  413.                 return NGX_CONF_ERROR;
  414.             }

  415.             scf->shm_zone->init = ngx_ssl_session_cache_init;

  416.             continue;
  417.         }

  418.         goto invalid;
  419.     }

  420.     if (scf->shm_zone && scf->builtin_session_cache == NGX_CONF_UNSET) {
  421.         scf->builtin_session_cache = NGX_SSL_NO_BUILTIN_SCACHE;
  422.     }

  423.     return NGX_CONF_OK;

  424. invalid:

  425.     ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
  426.                        "invalid session cache \"%V\"", &value[i]);

  427.     return NGX_CONF_ERROR;
  428. }