1 
2 /*
3  * Copyright (C) Igor Sysoev
4  * Copyright (C) Nginx, Inc.
5  */
6 
7 
8 #include <ngx_config.h>
9 #include <ngx_core.h>
10 #include <ngx_event.h>
11 #include <ngx_mail.h>
12 #include <ngx_mail_smtp_module.h>
13 
14 
15 static void *ngx_mail_smtp_create_srv_conf(ngx_conf_t *cf);
16 static char *ngx_mail_smtp_merge_srv_conf(ngx_conf_t *cf, void *parent,
17     void *child);
18 
19 
20 static ngx_conf_bitmask_t  ngx_mail_smtp_auth_methods[] = {
21     { ngx_string("plain"), NGX_MAIL_AUTH_PLAIN_ENABLED },
22     { ngx_string("login"), NGX_MAIL_AUTH_LOGIN_ENABLED },
23     { ngx_string("cram-md5"), NGX_MAIL_AUTH_CRAM_MD5_ENABLED },
24     { ngx_string("external"), NGX_MAIL_AUTH_EXTERNAL_ENABLED },
25     { ngx_string("none"), NGX_MAIL_AUTH_NONE_ENABLED },
26     { ngx_null_string, 0 }
27 };
28 
29 
30 static ngx_str_t  ngx_mail_smtp_auth_methods_names[] = {
31     ngx_string("PLAIN"),
32     ngx_string("LOGIN"),
33     ngx_null_string,  /* APOP */
34     ngx_string("CRAM-MD5"),
35     ngx_string("EXTERNAL"),
36     ngx_null_string   /* NONE */
37 };
38 
39 
40 static ngx_mail_protocol_t  ngx_mail_smtp_protocol = {
41     ngx_string("smtp"),
42     { 25, 465, 587, 0 },
43     NGX_MAIL_SMTP_PROTOCOL,
44 
45     ngx_mail_smtp_init_session,
46     ngx_mail_smtp_init_protocol,
47     ngx_mail_smtp_parse_command,
48     ngx_mail_smtp_auth_state,
49 
50     ngx_string("451 4.3.2 Internal server error" CRLF),
51     ngx_string("421 4.7.1 SSL certificate error" CRLF),
52     ngx_string("421 4.7.1 No required SSL certificate" CRLF)
53 };
54 
55 
56 static ngx_command_t  ngx_mail_smtp_commands[] = {
57 
58     { ngx_string("smtp_client_buffer"),
59       NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_TAKE1,
60       ngx_conf_set_size_slot,
61       NGX_MAIL_SRV_CONF_OFFSET,
62       offsetof(ngx_mail_smtp_srv_conf_t, client_buffer_size),
63       NULL },
64 
65     { ngx_string("smtp_greeting_delay"),
66       NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_TAKE1,
67       ngx_conf_set_msec_slot,
68       NGX_MAIL_SRV_CONF_OFFSET,
69       offsetof(ngx_mail_smtp_srv_conf_t, greeting_delay),
70       NULL },
71 
72     { ngx_string("smtp_capabilities"),
73       NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_1MORE,
74       ngx_mail_capabilities,
75       NGX_MAIL_SRV_CONF_OFFSET,
76       offsetof(ngx_mail_smtp_srv_conf_t, capabilities),
77       NULL },
78 
79     { ngx_string("smtp_auth"),
80       NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_1MORE,
81       ngx_conf_set_bitmask_slot,
82       NGX_MAIL_SRV_CONF_OFFSET,
83       offsetof(ngx_mail_smtp_srv_conf_t, auth_methods),
84       &ngx_mail_smtp_auth_methods },
85 
86       ngx_null_command
87 };
88 
89 
90 static ngx_mail_module_t  ngx_mail_smtp_module_ctx = {
91     &ngx_mail_smtp_protocol,               /* protocol */
92 
93     NULL,                                  /* create main configuration */
94     NULL,                                  /* init main configuration */
95 
96     ngx_mail_smtp_create_srv_conf,         /* create server configuration */
97     ngx_mail_smtp_merge_srv_conf           /* merge server configuration */
98 };
99 
100 
101 ngx_module_t  ngx_mail_smtp_module = {
102     NGX_MODULE_V1,
103     &ngx_mail_smtp_module_ctx,             /* module context */
104     ngx_mail_smtp_commands,                /* module directives */
105     NGX_MAIL_MODULE,                       /* module type */
106     NULL,                                  /* init master */
107     NULL,                                  /* init module */
108     NULL,                                  /* init process */
109     NULL,                                  /* init thread */
110     NULL,                                  /* exit thread */
111     NULL,                                  /* exit process */
112     NULL,                                  /* exit master */
113     NGX_MODULE_V1_PADDING
114 };
115 
116 
117 static void *
ngx_mail_smtp_create_srv_conf(ngx_conf_t * cf)118 ngx_mail_smtp_create_srv_conf(ngx_conf_t *cf)
119 {
120     ngx_mail_smtp_srv_conf_t  *sscf;
121 
122     sscf = ngx_pcalloc(cf->pool, sizeof(ngx_mail_smtp_srv_conf_t));
123     if (sscf == NULL) {
124         return NULL;
125     }
126 
127     sscf->client_buffer_size = NGX_CONF_UNSET_SIZE;
128     sscf->greeting_delay = NGX_CONF_UNSET_MSEC;
129 
130     if (ngx_array_init(&sscf->capabilities, cf->pool, 4, sizeof(ngx_str_t))
131         != NGX_OK)
132     {
133         return NULL;
134     }
135 
136     return sscf;
137 }
138 
139 
140 static char *
ngx_mail_smtp_merge_srv_conf(ngx_conf_t * cf,void * parent,void * child)141 ngx_mail_smtp_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child)
142 {
143     ngx_mail_smtp_srv_conf_t *prev = parent;
144     ngx_mail_smtp_srv_conf_t *conf = child;
145 
146     u_char                    *p, *auth, *last;
147     size_t                     size;
148     ngx_str_t                 *c;
149     ngx_uint_t                 i, m, auth_enabled;
150     ngx_mail_core_srv_conf_t  *cscf;
151 
152     ngx_conf_merge_size_value(conf->client_buffer_size,
153                               prev->client_buffer_size,
154                               (size_t) ngx_pagesize);
155 
156     ngx_conf_merge_msec_value(conf->greeting_delay,
157                               prev->greeting_delay, 0);
158 
159     ngx_conf_merge_bitmask_value(conf->auth_methods,
160                               prev->auth_methods,
161                               (NGX_CONF_BITMASK_SET
162                                |NGX_MAIL_AUTH_PLAIN_ENABLED
163                                |NGX_MAIL_AUTH_LOGIN_ENABLED));
164 
165 
166     cscf = ngx_mail_conf_get_module_srv_conf(cf, ngx_mail_core_module);
167 
168     size = sizeof("220  ESMTP ready" CRLF) - 1 + cscf->server_name.len;
169 
170     p = ngx_pnalloc(cf->pool, size);
171     if (p == NULL) {
172         return NGX_CONF_ERROR;
173     }
174 
175     conf->greeting.len = size;
176     conf->greeting.data = p;
177 
178     *p++ = '2'; *p++ = '2'; *p++ = '0'; *p++ = ' ';
179     p = ngx_cpymem(p, cscf->server_name.data, cscf->server_name.len);
180     ngx_memcpy(p, " ESMTP ready" CRLF, sizeof(" ESMTP ready" CRLF) - 1);
181 
182 
183     size = sizeof("250 " CRLF) - 1 + cscf->server_name.len;
184 
185     p = ngx_pnalloc(cf->pool, size);
186     if (p == NULL) {
187         return NGX_CONF_ERROR;
188     }
189 
190     conf->server_name.len = size;
191     conf->server_name.data = p;
192 
193     *p++ = '2'; *p++ = '5'; *p++ = '0'; *p++ = ' ';
194     p = ngx_cpymem(p, cscf->server_name.data, cscf->server_name.len);
195     *p++ = CR; *p = LF;
196 
197 
198     if (conf->capabilities.nelts == 0) {
199         conf->capabilities = prev->capabilities;
200     }
201 
202     size = sizeof("250-") - 1 + cscf->server_name.len + sizeof(CRLF) - 1;
203 
204     c = conf->capabilities.elts;
205     for (i = 0; i < conf->capabilities.nelts; i++) {
206         size += sizeof("250 ") - 1 + c[i].len + sizeof(CRLF) - 1;
207     }
208 
209     auth_enabled = 0;
210 
211     for (m = NGX_MAIL_AUTH_PLAIN_ENABLED, i = 0;
212          m <= NGX_MAIL_AUTH_EXTERNAL_ENABLED;
213          m <<= 1, i++)
214     {
215         if (m & conf->auth_methods) {
216             size += 1 + ngx_mail_smtp_auth_methods_names[i].len;
217             auth_enabled = 1;
218         }
219     }
220 
221     if (auth_enabled) {
222         size += sizeof("250 AUTH") - 1 + sizeof(CRLF) - 1;
223     }
224 
225     p = ngx_pnalloc(cf->pool, size);
226     if (p == NULL) {
227         return NGX_CONF_ERROR;
228     }
229 
230     conf->capability.len = size;
231     conf->capability.data = p;
232 
233     last = p;
234 
235     *p++ = '2'; *p++ = '5'; *p++ = '0'; *p++ = '-';
236     p = ngx_cpymem(p, cscf->server_name.data, cscf->server_name.len);
237     *p++ = CR; *p++ = LF;
238 
239     for (i = 0; i < conf->capabilities.nelts; i++) {
240         last = p;
241         *p++ = '2'; *p++ = '5'; *p++ = '0'; *p++ = '-';
242         p = ngx_cpymem(p, c[i].data, c[i].len);
243         *p++ = CR; *p++ = LF;
244     }
245 
246     auth = p;
247 
248     if (auth_enabled) {
249         last = p;
250 
251         *p++ = '2'; *p++ = '5'; *p++ = '0'; *p++ = ' ';
252         *p++ = 'A'; *p++ = 'U'; *p++ = 'T'; *p++ = 'H';
253 
254         for (m = NGX_MAIL_AUTH_PLAIN_ENABLED, i = 0;
255              m <= NGX_MAIL_AUTH_EXTERNAL_ENABLED;
256              m <<= 1, i++)
257         {
258             if (m & conf->auth_methods) {
259                 *p++ = ' ';
260                 p = ngx_cpymem(p, ngx_mail_smtp_auth_methods_names[i].data,
261                                ngx_mail_smtp_auth_methods_names[i].len);
262             }
263         }
264 
265         *p++ = CR; *p = LF;
266 
267     } else {
268         last[3] = ' ';
269     }
270 
271     size += sizeof("250 STARTTLS" CRLF) - 1;
272 
273     p = ngx_pnalloc(cf->pool, size);
274     if (p == NULL) {
275         return NGX_CONF_ERROR;
276     }
277 
278     conf->starttls_capability.len = size;
279     conf->starttls_capability.data = p;
280 
281     p = ngx_cpymem(p, conf->capability.data, conf->capability.len);
282 
283     ngx_memcpy(p, "250 STARTTLS" CRLF, sizeof("250 STARTTLS" CRLF) - 1);
284 
285     p = conf->starttls_capability.data
286         + (last - conf->capability.data) + 3;
287     *p = '-';
288 
289     size = (auth - conf->capability.data)
290             + sizeof("250 STARTTLS" CRLF) - 1;
291 
292     p = ngx_pnalloc(cf->pool, size);
293     if (p == NULL) {
294         return NGX_CONF_ERROR;
295     }
296 
297     conf->starttls_only_capability.len = size;
298     conf->starttls_only_capability.data = p;
299 
300     p = ngx_cpymem(p, conf->capability.data, auth - conf->capability.data);
301 
302     ngx_memcpy(p, "250 STARTTLS" CRLF, sizeof("250 STARTTLS" CRLF) - 1);
303 
304     if (last < auth) {
305         p = conf->starttls_only_capability.data
306             + (last - conf->capability.data) + 3;
307         *p = '-';
308     }
309 
310     return NGX_CONF_OK;
311 }
312