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_pop3_module.h>
13 
14 
15 static void *ngx_mail_pop3_create_srv_conf(ngx_conf_t *cf);
16 static char *ngx_mail_pop3_merge_srv_conf(ngx_conf_t *cf, void *parent,
17     void *child);
18 
19 
20 static ngx_str_t  ngx_mail_pop3_default_capabilities[] = {
21     ngx_string("TOP"),
22     ngx_string("USER"),
23     ngx_string("UIDL"),
24     ngx_null_string
25 };
26 
27 
28 static ngx_conf_bitmask_t  ngx_mail_pop3_auth_methods[] = {
29     { ngx_string("plain"), NGX_MAIL_AUTH_PLAIN_ENABLED },
30     { ngx_string("apop"), NGX_MAIL_AUTH_APOP_ENABLED },
31     { ngx_string("cram-md5"), NGX_MAIL_AUTH_CRAM_MD5_ENABLED },
32     { ngx_string("external"), NGX_MAIL_AUTH_EXTERNAL_ENABLED },
33     { ngx_null_string, 0 }
34 };
35 
36 
37 static ngx_str_t  ngx_mail_pop3_auth_methods_names[] = {
38     ngx_string("PLAIN"),
39     ngx_string("LOGIN"),
40     ngx_null_string,  /* APOP */
41     ngx_string("CRAM-MD5"),
42     ngx_string("EXTERNAL"),
43     ngx_null_string   /* NONE */
44 };
45 
46 
47 static ngx_mail_protocol_t  ngx_mail_pop3_protocol = {
48     ngx_string("pop3"),
49     { 110, 995, 0, 0 },
50     NGX_MAIL_POP3_PROTOCOL,
51 
52     ngx_mail_pop3_init_session,
53     ngx_mail_pop3_init_protocol,
54     ngx_mail_pop3_parse_command,
55     ngx_mail_pop3_auth_state,
56 
57     ngx_string("-ERR internal server error" CRLF),
58     ngx_string("-ERR SSL certificate error" CRLF),
59     ngx_string("-ERR No required SSL certificate" CRLF)
60 };
61 
62 
63 static ngx_command_t  ngx_mail_pop3_commands[] = {
64 
65     { ngx_string("pop3_capabilities"),
66       NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_1MORE,
67       ngx_mail_capabilities,
68       NGX_MAIL_SRV_CONF_OFFSET,
69       offsetof(ngx_mail_pop3_srv_conf_t, capabilities),
70       NULL },
71 
72     { ngx_string("pop3_auth"),
73       NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_1MORE,
74       ngx_conf_set_bitmask_slot,
75       NGX_MAIL_SRV_CONF_OFFSET,
76       offsetof(ngx_mail_pop3_srv_conf_t, auth_methods),
77       &ngx_mail_pop3_auth_methods },
78 
79       ngx_null_command
80 };
81 
82 
83 static ngx_mail_module_t  ngx_mail_pop3_module_ctx = {
84     &ngx_mail_pop3_protocol,               /* protocol */
85 
86     NULL,                                  /* create main configuration */
87     NULL,                                  /* init main configuration */
88 
89     ngx_mail_pop3_create_srv_conf,         /* create server configuration */
90     ngx_mail_pop3_merge_srv_conf           /* merge server configuration */
91 };
92 
93 
94 ngx_module_t  ngx_mail_pop3_module = {
95     NGX_MODULE_V1,
96     &ngx_mail_pop3_module_ctx,             /* module context */
97     ngx_mail_pop3_commands,                /* module directives */
98     NGX_MAIL_MODULE,                       /* module type */
99     NULL,                                  /* init master */
100     NULL,                                  /* init module */
101     NULL,                                  /* init process */
102     NULL,                                  /* init thread */
103     NULL,                                  /* exit thread */
104     NULL,                                  /* exit process */
105     NULL,                                  /* exit master */
106     NGX_MODULE_V1_PADDING
107 };
108 
109 
110 static void *
ngx_mail_pop3_create_srv_conf(ngx_conf_t * cf)111 ngx_mail_pop3_create_srv_conf(ngx_conf_t *cf)
112 {
113     ngx_mail_pop3_srv_conf_t  *pscf;
114 
115     pscf = ngx_pcalloc(cf->pool, sizeof(ngx_mail_pop3_srv_conf_t));
116     if (pscf == NULL) {
117         return NULL;
118     }
119 
120     if (ngx_array_init(&pscf->capabilities, cf->pool, 4, sizeof(ngx_str_t))
121         != NGX_OK)
122     {
123         return NULL;
124     }
125 
126     return pscf;
127 }
128 
129 
130 static char *
ngx_mail_pop3_merge_srv_conf(ngx_conf_t * cf,void * parent,void * child)131 ngx_mail_pop3_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child)
132 {
133     ngx_mail_pop3_srv_conf_t *prev = parent;
134     ngx_mail_pop3_srv_conf_t *conf = child;
135 
136     u_char      *p;
137     size_t       size, stls_only_size;
138     ngx_str_t   *c, *d;
139     ngx_uint_t   i, m;
140 
141     ngx_conf_merge_bitmask_value(conf->auth_methods,
142                                  prev->auth_methods,
143                                  (NGX_CONF_BITMASK_SET
144                                   |NGX_MAIL_AUTH_PLAIN_ENABLED));
145 
146     if (conf->auth_methods & NGX_MAIL_AUTH_PLAIN_ENABLED) {
147         conf->auth_methods |= NGX_MAIL_AUTH_LOGIN_ENABLED;
148     }
149 
150     if (conf->capabilities.nelts == 0) {
151         conf->capabilities = prev->capabilities;
152     }
153 
154     if (conf->capabilities.nelts == 0) {
155 
156         for (d = ngx_mail_pop3_default_capabilities; d->len; d++) {
157             c = ngx_array_push(&conf->capabilities);
158             if (c == NULL) {
159                 return NGX_CONF_ERROR;
160             }
161 
162             *c = *d;
163         }
164     }
165 
166     size = sizeof("+OK Capability list follows" CRLF) - 1
167            + sizeof("." CRLF) - 1;
168 
169     stls_only_size = size + sizeof("STLS" CRLF) - 1;
170 
171     c = conf->capabilities.elts;
172     for (i = 0; i < conf->capabilities.nelts; i++) {
173         size += c[i].len + sizeof(CRLF) - 1;
174 
175         if (ngx_strcasecmp(c[i].data, (u_char *) "USER") == 0) {
176             continue;
177         }
178 
179         stls_only_size += c[i].len + sizeof(CRLF) - 1;
180     }
181 
182     size += sizeof("SASL") - 1 + sizeof(CRLF) - 1;
183 
184     for (m = NGX_MAIL_AUTH_PLAIN_ENABLED, i = 0;
185          m <= NGX_MAIL_AUTH_EXTERNAL_ENABLED;
186          m <<= 1, i++)
187     {
188         if (ngx_mail_pop3_auth_methods_names[i].len == 0) {
189             continue;
190         }
191 
192         if (m & conf->auth_methods) {
193             size += 1 + ngx_mail_pop3_auth_methods_names[i].len;
194         }
195     }
196 
197     p = ngx_pnalloc(cf->pool, size);
198     if (p == NULL) {
199         return NGX_CONF_ERROR;
200     }
201 
202     conf->capability.len = size;
203     conf->capability.data = p;
204 
205     p = ngx_cpymem(p, "+OK Capability list follows" CRLF,
206                    sizeof("+OK Capability list follows" CRLF) - 1);
207 
208     for (i = 0; i < conf->capabilities.nelts; i++) {
209         p = ngx_cpymem(p, c[i].data, c[i].len);
210         *p++ = CR; *p++ = LF;
211     }
212 
213     p = ngx_cpymem(p, "SASL", sizeof("SASL") - 1);
214 
215     for (m = NGX_MAIL_AUTH_PLAIN_ENABLED, i = 0;
216          m <= NGX_MAIL_AUTH_EXTERNAL_ENABLED;
217          m <<= 1, i++)
218     {
219         if (ngx_mail_pop3_auth_methods_names[i].len == 0) {
220             continue;
221         }
222 
223         if (m & conf->auth_methods) {
224             *p++ = ' ';
225             p = ngx_cpymem(p, ngx_mail_pop3_auth_methods_names[i].data,
226                            ngx_mail_pop3_auth_methods_names[i].len);
227         }
228     }
229 
230     *p++ = CR; *p++ = LF;
231 
232     *p++ = '.'; *p++ = CR; *p = LF;
233 
234 
235     size += sizeof("STLS" CRLF) - 1;
236 
237     p = ngx_pnalloc(cf->pool, size);
238     if (p == NULL) {
239         return NGX_CONF_ERROR;
240     }
241 
242     conf->starttls_capability.len = size;
243     conf->starttls_capability.data = p;
244 
245     p = ngx_cpymem(p, conf->capability.data,
246                    conf->capability.len - (sizeof("." CRLF) - 1));
247 
248     p = ngx_cpymem(p, "STLS" CRLF, sizeof("STLS" CRLF) - 1);
249     *p++ = '.'; *p++ = CR; *p = LF;
250 
251 
252     size = sizeof("+OK methods supported:" CRLF) - 1
253            + sizeof("." CRLF) - 1;
254 
255     for (m = NGX_MAIL_AUTH_PLAIN_ENABLED, i = 0;
256          m <= NGX_MAIL_AUTH_EXTERNAL_ENABLED;
257          m <<= 1, i++)
258     {
259         if (ngx_mail_pop3_auth_methods_names[i].len == 0) {
260             continue;
261         }
262 
263         if (m & conf->auth_methods) {
264             size += ngx_mail_pop3_auth_methods_names[i].len
265                     + sizeof(CRLF) - 1;
266         }
267     }
268 
269     p = ngx_pnalloc(cf->pool, size);
270     if (p == NULL) {
271         return NGX_CONF_ERROR;
272     }
273 
274     conf->auth_capability.data = p;
275     conf->auth_capability.len = size;
276 
277     p = ngx_cpymem(p, "+OK methods supported:" CRLF,
278                    sizeof("+OK methods supported:" CRLF) - 1);
279 
280     for (m = NGX_MAIL_AUTH_PLAIN_ENABLED, i = 0;
281          m <= NGX_MAIL_AUTH_EXTERNAL_ENABLED;
282          m <<= 1, i++)
283     {
284         if (ngx_mail_pop3_auth_methods_names[i].len == 0) {
285             continue;
286         }
287 
288         if (m & conf->auth_methods) {
289             p = ngx_cpymem(p, ngx_mail_pop3_auth_methods_names[i].data,
290                            ngx_mail_pop3_auth_methods_names[i].len);
291             *p++ = CR; *p++ = LF;
292         }
293     }
294 
295     *p++ = '.'; *p++ = CR; *p = LF;
296 
297 
298     p = ngx_pnalloc(cf->pool, stls_only_size);
299     if (p == NULL) {
300         return NGX_CONF_ERROR;
301     }
302 
303     conf->starttls_only_capability.len = stls_only_size;
304     conf->starttls_only_capability.data = p;
305 
306     p = ngx_cpymem(p, "+OK Capability list follows" CRLF,
307                    sizeof("+OK Capability list follows" CRLF) - 1);
308 
309     for (i = 0; i < conf->capabilities.nelts; i++) {
310         if (ngx_strcasecmp(c[i].data, (u_char *) "USER") == 0) {
311             continue;
312         }
313 
314         p = ngx_cpymem(p, c[i].data, c[i].len);
315         *p++ = CR; *p++ = LF;
316     }
317 
318     p = ngx_cpymem(p, "STLS" CRLF, sizeof("STLS" CRLF) - 1);
319     *p++ = '.'; *p++ = CR; *p = LF;
320 
321     return NGX_CONF_OK;
322 }
323