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_event_connect.h>
12 #include <ngx_mail.h>
13 
14 
15 typedef struct {
16     ngx_addr_t                     *peer;
17 
18     ngx_msec_t                      timeout;
19     ngx_flag_t                      pass_client_cert;
20 
21     ngx_str_t                       host_header;
22     ngx_str_t                       uri;
23     ngx_str_t                       header;
24 
25     ngx_array_t                    *headers;
26 
27     u_char                         *file;
28     ngx_uint_t                      line;
29 } ngx_mail_auth_http_conf_t;
30 
31 
32 typedef struct ngx_mail_auth_http_ctx_s  ngx_mail_auth_http_ctx_t;
33 
34 typedef void (*ngx_mail_auth_http_handler_pt)(ngx_mail_session_t *s,
35     ngx_mail_auth_http_ctx_t *ctx);
36 
37 struct ngx_mail_auth_http_ctx_s {
38     ngx_buf_t                      *request;
39     ngx_buf_t                      *response;
40     ngx_peer_connection_t           peer;
41 
42     ngx_mail_auth_http_handler_pt   handler;
43 
44     ngx_uint_t                      state;
45 
46     u_char                         *header_name_start;
47     u_char                         *header_name_end;
48     u_char                         *header_start;
49     u_char                         *header_end;
50 
51     ngx_str_t                       addr;
52     ngx_str_t                       port;
53     ngx_str_t                       err;
54     ngx_str_t                       errmsg;
55     ngx_str_t                       errcode;
56 
57     time_t                          sleep;
58 
59     ngx_pool_t                     *pool;
60 };
61 
62 
63 static void ngx_mail_auth_http_write_handler(ngx_event_t *wev);
64 static void ngx_mail_auth_http_read_handler(ngx_event_t *rev);
65 static void ngx_mail_auth_http_ignore_status_line(ngx_mail_session_t *s,
66     ngx_mail_auth_http_ctx_t *ctx);
67 static void ngx_mail_auth_http_process_headers(ngx_mail_session_t *s,
68     ngx_mail_auth_http_ctx_t *ctx);
69 static void ngx_mail_auth_sleep_handler(ngx_event_t *rev);
70 static ngx_int_t ngx_mail_auth_http_parse_header_line(ngx_mail_session_t *s,
71     ngx_mail_auth_http_ctx_t *ctx);
72 static void ngx_mail_auth_http_block_read(ngx_event_t *rev);
73 static void ngx_mail_auth_http_dummy_handler(ngx_event_t *ev);
74 static ngx_buf_t *ngx_mail_auth_http_create_request(ngx_mail_session_t *s,
75     ngx_pool_t *pool, ngx_mail_auth_http_conf_t *ahcf);
76 static ngx_int_t ngx_mail_auth_http_escape(ngx_pool_t *pool, ngx_str_t *text,
77     ngx_str_t *escaped);
78 
79 static void *ngx_mail_auth_http_create_conf(ngx_conf_t *cf);
80 static char *ngx_mail_auth_http_merge_conf(ngx_conf_t *cf, void *parent,
81     void *child);
82 static char *ngx_mail_auth_http(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);
83 static char *ngx_mail_auth_http_header(ngx_conf_t *cf, ngx_command_t *cmd,
84     void *conf);
85 
86 
87 static ngx_command_t  ngx_mail_auth_http_commands[] = {
88 
89     { ngx_string("auth_http"),
90       NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_TAKE1,
91       ngx_mail_auth_http,
92       NGX_MAIL_SRV_CONF_OFFSET,
93       0,
94       NULL },
95 
96     { ngx_string("auth_http_timeout"),
97       NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_TAKE1,
98       ngx_conf_set_msec_slot,
99       NGX_MAIL_SRV_CONF_OFFSET,
100       offsetof(ngx_mail_auth_http_conf_t, timeout),
101       NULL },
102 
103     { ngx_string("auth_http_header"),
104       NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_TAKE2,
105       ngx_mail_auth_http_header,
106       NGX_MAIL_SRV_CONF_OFFSET,
107       0,
108       NULL },
109 
110     { ngx_string("auth_http_pass_client_cert"),
111       NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_FLAG,
112       ngx_conf_set_flag_slot,
113       NGX_MAIL_SRV_CONF_OFFSET,
114       offsetof(ngx_mail_auth_http_conf_t, pass_client_cert),
115       NULL },
116 
117       ngx_null_command
118 };
119 
120 
121 static ngx_mail_module_t  ngx_mail_auth_http_module_ctx = {
122     NULL,                                  /* protocol */
123 
124     NULL,                                  /* create main configuration */
125     NULL,                                  /* init main configuration */
126 
127     ngx_mail_auth_http_create_conf,        /* create server configuration */
128     ngx_mail_auth_http_merge_conf          /* merge server configuration */
129 };
130 
131 
132 ngx_module_t  ngx_mail_auth_http_module = {
133     NGX_MODULE_V1,
134     &ngx_mail_auth_http_module_ctx,        /* module context */
135     ngx_mail_auth_http_commands,           /* module directives */
136     NGX_MAIL_MODULE,                       /* module type */
137     NULL,                                  /* init master */
138     NULL,                                  /* init module */
139     NULL,                                  /* init process */
140     NULL,                                  /* init thread */
141     NULL,                                  /* exit thread */
142     NULL,                                  /* exit process */
143     NULL,                                  /* exit master */
144     NGX_MODULE_V1_PADDING
145 };
146 
147 
148 static ngx_str_t   ngx_mail_auth_http_method[] = {
149     ngx_string("plain"),
150     ngx_string("plain"),
151     ngx_string("plain"),
152     ngx_string("apop"),
153     ngx_string("cram-md5"),
154     ngx_string("external"),
155     ngx_string("none")
156 };
157 
158 static ngx_str_t   ngx_mail_smtp_errcode = ngx_string("535 5.7.0");
159 
160 
161 void
ngx_mail_auth_http_init(ngx_mail_session_t * s)162 ngx_mail_auth_http_init(ngx_mail_session_t *s)
163 {
164     ngx_int_t                   rc;
165     ngx_pool_t                 *pool;
166     ngx_mail_auth_http_ctx_t   *ctx;
167     ngx_mail_auth_http_conf_t  *ahcf;
168 
169     s->connection->log->action = "in http auth state";
170 
171     pool = ngx_create_pool(2048, s->connection->log);
172     if (pool == NULL) {
173         ngx_mail_session_internal_server_error(s);
174         return;
175     }
176 
177     ctx = ngx_pcalloc(pool, sizeof(ngx_mail_auth_http_ctx_t));
178     if (ctx == NULL) {
179         ngx_destroy_pool(pool);
180         ngx_mail_session_internal_server_error(s);
181         return;
182     }
183 
184     ctx->pool = pool;
185 
186     ahcf = ngx_mail_get_module_srv_conf(s, ngx_mail_auth_http_module);
187 
188     ctx->request = ngx_mail_auth_http_create_request(s, pool, ahcf);
189     if (ctx->request == NULL) {
190         ngx_destroy_pool(ctx->pool);
191         ngx_mail_session_internal_server_error(s);
192         return;
193     }
194 
195     ngx_mail_set_ctx(s, ctx, ngx_mail_auth_http_module);
196 
197     ctx->peer.sockaddr = ahcf->peer->sockaddr;
198     ctx->peer.socklen = ahcf->peer->socklen;
199     ctx->peer.name = &ahcf->peer->name;
200     ctx->peer.get = ngx_event_get_peer;
201     ctx->peer.log = s->connection->log;
202     ctx->peer.log_error = NGX_ERROR_ERR;
203 
204     rc = ngx_event_connect_peer(&ctx->peer);
205 
206     if (rc == NGX_ERROR || rc == NGX_BUSY || rc == NGX_DECLINED) {
207         if (ctx->peer.connection) {
208             ngx_close_connection(ctx->peer.connection);
209         }
210 
211         ngx_destroy_pool(ctx->pool);
212         ngx_mail_session_internal_server_error(s);
213         return;
214     }
215 
216     ctx->peer.connection->data = s;
217     ctx->peer.connection->pool = s->connection->pool;
218 
219     s->connection->read->handler = ngx_mail_auth_http_block_read;
220     ctx->peer.connection->read->handler = ngx_mail_auth_http_read_handler;
221     ctx->peer.connection->write->handler = ngx_mail_auth_http_write_handler;
222 
223     ctx->handler = ngx_mail_auth_http_ignore_status_line;
224 
225     ngx_add_timer(ctx->peer.connection->read, ahcf->timeout);
226     ngx_add_timer(ctx->peer.connection->write, ahcf->timeout);
227 
228     if (rc == NGX_OK) {
229         ngx_mail_auth_http_write_handler(ctx->peer.connection->write);
230         return;
231     }
232 }
233 
234 
235 static void
ngx_mail_auth_http_write_handler(ngx_event_t * wev)236 ngx_mail_auth_http_write_handler(ngx_event_t *wev)
237 {
238     ssize_t                     n, size;
239     ngx_connection_t           *c;
240     ngx_mail_session_t         *s;
241     ngx_mail_auth_http_ctx_t   *ctx;
242     ngx_mail_auth_http_conf_t  *ahcf;
243 
244     c = wev->data;
245     s = c->data;
246 
247     ctx = ngx_mail_get_module_ctx(s, ngx_mail_auth_http_module);
248 
249     ngx_log_debug0(NGX_LOG_DEBUG_MAIL, wev->log, 0,
250                    "mail auth http write handler");
251 
252     if (wev->timedout) {
253         ngx_log_error(NGX_LOG_ERR, wev->log, NGX_ETIMEDOUT,
254                       "auth http server %V timed out", ctx->peer.name);
255         ngx_close_connection(c);
256         ngx_destroy_pool(ctx->pool);
257         ngx_mail_session_internal_server_error(s);
258         return;
259     }
260 
261     size = ctx->request->last - ctx->request->pos;
262 
263     n = ngx_send(c, ctx->request->pos, size);
264 
265     if (n == NGX_ERROR) {
266         ngx_close_connection(c);
267         ngx_destroy_pool(ctx->pool);
268         ngx_mail_session_internal_server_error(s);
269         return;
270     }
271 
272     if (n > 0) {
273         ctx->request->pos += n;
274 
275         if (n == size) {
276             wev->handler = ngx_mail_auth_http_dummy_handler;
277 
278             if (wev->timer_set) {
279                 ngx_del_timer(wev);
280             }
281 
282             if (ngx_handle_write_event(wev, 0) != NGX_OK) {
283                 ngx_close_connection(c);
284                 ngx_destroy_pool(ctx->pool);
285                 ngx_mail_session_internal_server_error(s);
286             }
287 
288             return;
289         }
290     }
291 
292     if (!wev->timer_set) {
293         ahcf = ngx_mail_get_module_srv_conf(s, ngx_mail_auth_http_module);
294         ngx_add_timer(wev, ahcf->timeout);
295     }
296 }
297 
298 
299 static void
ngx_mail_auth_http_read_handler(ngx_event_t * rev)300 ngx_mail_auth_http_read_handler(ngx_event_t *rev)
301 {
302     ssize_t                     n, size;
303     ngx_connection_t          *c;
304     ngx_mail_session_t        *s;
305     ngx_mail_auth_http_ctx_t  *ctx;
306 
307     c = rev->data;
308     s = c->data;
309 
310     ngx_log_debug0(NGX_LOG_DEBUG_MAIL, rev->log, 0,
311                    "mail auth http read handler");
312 
313     ctx = ngx_mail_get_module_ctx(s, ngx_mail_auth_http_module);
314 
315     if (rev->timedout) {
316         ngx_log_error(NGX_LOG_ERR, rev->log, NGX_ETIMEDOUT,
317                       "auth http server %V timed out", ctx->peer.name);
318         ngx_close_connection(c);
319         ngx_destroy_pool(ctx->pool);
320         ngx_mail_session_internal_server_error(s);
321         return;
322     }
323 
324     if (ctx->response == NULL) {
325         ctx->response = ngx_create_temp_buf(ctx->pool, 1024);
326         if (ctx->response == NULL) {
327             ngx_close_connection(c);
328             ngx_destroy_pool(ctx->pool);
329             ngx_mail_session_internal_server_error(s);
330             return;
331         }
332     }
333 
334     size = ctx->response->end - ctx->response->last;
335 
336     n = ngx_recv(c, ctx->response->pos, size);
337 
338     if (n > 0) {
339         ctx->response->last += n;
340 
341         ctx->handler(s, ctx);
342         return;
343     }
344 
345     if (n == NGX_AGAIN) {
346         return;
347     }
348 
349     ngx_close_connection(c);
350     ngx_destroy_pool(ctx->pool);
351     ngx_mail_session_internal_server_error(s);
352 }
353 
354 
355 static void
ngx_mail_auth_http_ignore_status_line(ngx_mail_session_t * s,ngx_mail_auth_http_ctx_t * ctx)356 ngx_mail_auth_http_ignore_status_line(ngx_mail_session_t *s,
357     ngx_mail_auth_http_ctx_t *ctx)
358 {
359     u_char  *p, ch;
360     enum  {
361         sw_start = 0,
362         sw_H,
363         sw_HT,
364         sw_HTT,
365         sw_HTTP,
366         sw_skip,
367         sw_almost_done
368     } state;
369 
370     ngx_log_debug0(NGX_LOG_DEBUG_MAIL, s->connection->log, 0,
371                    "mail auth http process status line");
372 
373     state = ctx->state;
374 
375     for (p = ctx->response->pos; p < ctx->response->last; p++) {
376         ch = *p;
377 
378         switch (state) {
379 
380         /* "HTTP/" */
381         case sw_start:
382             if (ch == 'H') {
383                 state = sw_H;
384                 break;
385             }
386             goto next;
387 
388         case sw_H:
389             if (ch == 'T') {
390                 state = sw_HT;
391                 break;
392             }
393             goto next;
394 
395         case sw_HT:
396             if (ch == 'T') {
397                 state = sw_HTT;
398                 break;
399             }
400             goto next;
401 
402         case sw_HTT:
403             if (ch == 'P') {
404                 state = sw_HTTP;
405                 break;
406             }
407             goto next;
408 
409         case sw_HTTP:
410             if (ch == '/') {
411                 state = sw_skip;
412                 break;
413             }
414             goto next;
415 
416         /* any text until end of line */
417         case sw_skip:
418             switch (ch) {
419             case CR:
420                 state = sw_almost_done;
421 
422                 break;
423             case LF:
424                 goto done;
425             }
426             break;
427 
428         /* end of status line */
429         case sw_almost_done:
430             if (ch == LF) {
431                 goto done;
432             }
433 
434             ngx_log_error(NGX_LOG_ERR, s->connection->log, 0,
435                           "auth http server %V sent invalid response",
436                           ctx->peer.name);
437             ngx_close_connection(ctx->peer.connection);
438             ngx_destroy_pool(ctx->pool);
439             ngx_mail_session_internal_server_error(s);
440             return;
441         }
442     }
443 
444     ctx->response->pos = p;
445     ctx->state = state;
446 
447     return;
448 
449 next:
450 
451     p = ctx->response->start - 1;
452 
453 done:
454 
455     ctx->response->pos = p + 1;
456     ctx->state = 0;
457     ctx->handler = ngx_mail_auth_http_process_headers;
458     ctx->handler(s, ctx);
459 }
460 
461 
462 static void
ngx_mail_auth_http_process_headers(ngx_mail_session_t * s,ngx_mail_auth_http_ctx_t * ctx)463 ngx_mail_auth_http_process_headers(ngx_mail_session_t *s,
464     ngx_mail_auth_http_ctx_t *ctx)
465 {
466     u_char      *p;
467     time_t       timer;
468     size_t       len, size;
469     ngx_int_t    rc, port, n;
470     ngx_addr_t  *peer;
471 
472     ngx_log_debug0(NGX_LOG_DEBUG_MAIL, s->connection->log, 0,
473                    "mail auth http process headers");
474 
475     for ( ;; ) {
476         rc = ngx_mail_auth_http_parse_header_line(s, ctx);
477 
478         if (rc == NGX_OK) {
479 
480 #if (NGX_DEBUG)
481             {
482             ngx_str_t  key, value;
483 
484             key.len = ctx->header_name_end - ctx->header_name_start;
485             key.data = ctx->header_name_start;
486             value.len = ctx->header_end - ctx->header_start;
487             value.data = ctx->header_start;
488 
489             ngx_log_debug2(NGX_LOG_DEBUG_MAIL, s->connection->log, 0,
490                            "mail auth http header: \"%V: %V\"",
491                            &key, &value);
492             }
493 #endif
494 
495             len = ctx->header_name_end - ctx->header_name_start;
496 
497             if (len == sizeof("Auth-Status") - 1
498                 && ngx_strncasecmp(ctx->header_name_start,
499                                    (u_char *) "Auth-Status",
500                                    sizeof("Auth-Status") - 1)
501                    == 0)
502             {
503                 len = ctx->header_end - ctx->header_start;
504 
505                 if (len == 2
506                     && ctx->header_start[0] == 'O'
507                     && ctx->header_start[1] == 'K')
508                 {
509                     continue;
510                 }
511 
512                 if (len == 4
513                     && ctx->header_start[0] == 'W'
514                     && ctx->header_start[1] == 'A'
515                     && ctx->header_start[2] == 'I'
516                     && ctx->header_start[3] == 'T')
517                 {
518                     s->auth_wait = 1;
519                     continue;
520                 }
521 
522                 ctx->errmsg.len = len;
523                 ctx->errmsg.data = ctx->header_start;
524 
525                 switch (s->protocol) {
526 
527                 case NGX_MAIL_POP3_PROTOCOL:
528                     size = sizeof("-ERR ") - 1 + len + sizeof(CRLF) - 1;
529                     break;
530 
531                 case NGX_MAIL_IMAP_PROTOCOL:
532                     size = s->tag.len + sizeof("NO ") - 1 + len
533                            + sizeof(CRLF) - 1;
534                     break;
535 
536                 default: /* NGX_MAIL_SMTP_PROTOCOL */
537                     ctx->err = ctx->errmsg;
538                     continue;
539                 }
540 
541                 p = ngx_pnalloc(s->connection->pool, size);
542                 if (p == NULL) {
543                     ngx_close_connection(ctx->peer.connection);
544                     ngx_destroy_pool(ctx->pool);
545                     ngx_mail_session_internal_server_error(s);
546                     return;
547                 }
548 
549                 ctx->err.data = p;
550 
551                 switch (s->protocol) {
552 
553                 case NGX_MAIL_POP3_PROTOCOL:
554                     *p++ = '-'; *p++ = 'E'; *p++ = 'R'; *p++ = 'R'; *p++ = ' ';
555                     break;
556 
557                 case NGX_MAIL_IMAP_PROTOCOL:
558                     p = ngx_cpymem(p, s->tag.data, s->tag.len);
559                     *p++ = 'N'; *p++ = 'O'; *p++ = ' ';
560                     break;
561 
562                 default: /* NGX_MAIL_SMTP_PROTOCOL */
563                     break;
564                 }
565 
566                 p = ngx_cpymem(p, ctx->header_start, len);
567                 *p++ = CR; *p++ = LF;
568 
569                 ctx->err.len = p - ctx->err.data;
570 
571                 continue;
572             }
573 
574             if (len == sizeof("Auth-Server") - 1
575                 && ngx_strncasecmp(ctx->header_name_start,
576                                    (u_char *) "Auth-Server",
577                                    sizeof("Auth-Server") - 1)
578                     == 0)
579             {
580                 ctx->addr.len = ctx->header_end - ctx->header_start;
581                 ctx->addr.data = ctx->header_start;
582 
583                 continue;
584             }
585 
586             if (len == sizeof("Auth-Port") - 1
587                 && ngx_strncasecmp(ctx->header_name_start,
588                                    (u_char *) "Auth-Port",
589                                    sizeof("Auth-Port") - 1)
590                    == 0)
591             {
592                 ctx->port.len = ctx->header_end - ctx->header_start;
593                 ctx->port.data = ctx->header_start;
594 
595                 continue;
596             }
597 
598             if (len == sizeof("Auth-User") - 1
599                 && ngx_strncasecmp(ctx->header_name_start,
600                                    (u_char *) "Auth-User",
601                                    sizeof("Auth-User") - 1)
602                    == 0)
603             {
604                 s->login.len = ctx->header_end - ctx->header_start;
605 
606                 s->login.data = ngx_pnalloc(s->connection->pool, s->login.len);
607                 if (s->login.data == NULL) {
608                     ngx_close_connection(ctx->peer.connection);
609                     ngx_destroy_pool(ctx->pool);
610                     ngx_mail_session_internal_server_error(s);
611                     return;
612                 }
613 
614                 ngx_memcpy(s->login.data, ctx->header_start, s->login.len);
615 
616                 continue;
617             }
618 
619             if (len == sizeof("Auth-Pass") - 1
620                 && ngx_strncasecmp(ctx->header_name_start,
621                                    (u_char *) "Auth-Pass",
622                                    sizeof("Auth-Pass") - 1)
623                    == 0)
624             {
625                 s->passwd.len = ctx->header_end - ctx->header_start;
626 
627                 s->passwd.data = ngx_pnalloc(s->connection->pool,
628                                              s->passwd.len);
629                 if (s->passwd.data == NULL) {
630                     ngx_close_connection(ctx->peer.connection);
631                     ngx_destroy_pool(ctx->pool);
632                     ngx_mail_session_internal_server_error(s);
633                     return;
634                 }
635 
636                 ngx_memcpy(s->passwd.data, ctx->header_start, s->passwd.len);
637 
638                 continue;
639             }
640 
641             if (len == sizeof("Auth-Wait") - 1
642                 && ngx_strncasecmp(ctx->header_name_start,
643                                    (u_char *) "Auth-Wait",
644                                    sizeof("Auth-Wait") - 1)
645                    == 0)
646             {
647                 n = ngx_atoi(ctx->header_start,
648                              ctx->header_end - ctx->header_start);
649 
650                 if (n != NGX_ERROR) {
651                     ctx->sleep = n;
652                 }
653 
654                 continue;
655             }
656 
657             if (len == sizeof("Auth-Error-Code") - 1
658                 && ngx_strncasecmp(ctx->header_name_start,
659                                    (u_char *) "Auth-Error-Code",
660                                    sizeof("Auth-Error-Code") - 1)
661                    == 0)
662             {
663                 ctx->errcode.len = ctx->header_end - ctx->header_start;
664 
665                 ctx->errcode.data = ngx_pnalloc(s->connection->pool,
666                                                 ctx->errcode.len);
667                 if (ctx->errcode.data == NULL) {
668                     ngx_close_connection(ctx->peer.connection);
669                     ngx_destroy_pool(ctx->pool);
670                     ngx_mail_session_internal_server_error(s);
671                     return;
672                 }
673 
674                 ngx_memcpy(ctx->errcode.data, ctx->header_start,
675                            ctx->errcode.len);
676 
677                 continue;
678             }
679 
680             /* ignore other headers */
681 
682             continue;
683         }
684 
685         if (rc == NGX_DONE) {
686             ngx_log_debug0(NGX_LOG_DEBUG_MAIL, s->connection->log, 0,
687                            "mail auth http header done");
688 
689             ngx_close_connection(ctx->peer.connection);
690 
691             if (ctx->err.len) {
692 
693                 ngx_log_error(NGX_LOG_INFO, s->connection->log, 0,
694                               "client login failed: \"%V\"", &ctx->errmsg);
695 
696                 if (s->protocol == NGX_MAIL_SMTP_PROTOCOL) {
697 
698                     if (ctx->errcode.len == 0) {
699                         ctx->errcode = ngx_mail_smtp_errcode;
700                     }
701 
702                     ctx->err.len = ctx->errcode.len + ctx->errmsg.len
703                                    + sizeof(" " CRLF) - 1;
704 
705                     p = ngx_pnalloc(s->connection->pool, ctx->err.len);
706                     if (p == NULL) {
707                         ngx_destroy_pool(ctx->pool);
708                         ngx_mail_session_internal_server_error(s);
709                         return;
710                     }
711 
712                     ctx->err.data = p;
713 
714                     p = ngx_cpymem(p, ctx->errcode.data, ctx->errcode.len);
715                     *p++ = ' ';
716                     p = ngx_cpymem(p, ctx->errmsg.data, ctx->errmsg.len);
717                     *p++ = CR; *p = LF;
718                 }
719 
720                 s->out = ctx->err;
721                 timer = ctx->sleep;
722 
723                 ngx_destroy_pool(ctx->pool);
724 
725                 if (timer == 0) {
726                     s->quit = 1;
727                     ngx_mail_send(s->connection->write);
728                     return;
729                 }
730 
731                 ngx_add_timer(s->connection->read, (ngx_msec_t) (timer * 1000));
732 
733                 s->connection->read->handler = ngx_mail_auth_sleep_handler;
734 
735                 return;
736             }
737 
738             if (s->auth_wait) {
739                 timer = ctx->sleep;
740 
741                 ngx_destroy_pool(ctx->pool);
742 
743                 if (timer == 0) {
744                     ngx_mail_auth_http_init(s);
745                     return;
746                 }
747 
748                 ngx_add_timer(s->connection->read, (ngx_msec_t) (timer * 1000));
749 
750                 s->connection->read->handler = ngx_mail_auth_sleep_handler;
751 
752                 return;
753             }
754 
755             if (ctx->addr.len == 0 || ctx->port.len == 0) {
756                 ngx_log_error(NGX_LOG_ERR, s->connection->log, 0,
757                               "auth http server %V did not send server or port",
758                               ctx->peer.name);
759                 ngx_destroy_pool(ctx->pool);
760                 ngx_mail_session_internal_server_error(s);
761                 return;
762             }
763 
764             if (s->passwd.data == NULL
765                 && s->protocol != NGX_MAIL_SMTP_PROTOCOL)
766             {
767                 ngx_log_error(NGX_LOG_ERR, s->connection->log, 0,
768                               "auth http server %V did not send password",
769                               ctx->peer.name);
770                 ngx_destroy_pool(ctx->pool);
771                 ngx_mail_session_internal_server_error(s);
772                 return;
773             }
774 
775             peer = ngx_pcalloc(s->connection->pool, sizeof(ngx_addr_t));
776             if (peer == NULL) {
777                 ngx_destroy_pool(ctx->pool);
778                 ngx_mail_session_internal_server_error(s);
779                 return;
780             }
781 
782             rc = ngx_parse_addr(s->connection->pool, peer,
783                                 ctx->addr.data, ctx->addr.len);
784 
785             switch (rc) {
786             case NGX_OK:
787                 break;
788 
789             case NGX_DECLINED:
790                 ngx_log_error(NGX_LOG_ERR, s->connection->log, 0,
791                               "auth http server %V sent invalid server "
792                               "address:\"%V\"",
793                               ctx->peer.name, &ctx->addr);
794                 /* fall through */
795 
796             default:
797                 ngx_destroy_pool(ctx->pool);
798                 ngx_mail_session_internal_server_error(s);
799                 return;
800             }
801 
802             port = ngx_atoi(ctx->port.data, ctx->port.len);
803             if (port == NGX_ERROR || port < 1 || port > 65535) {
804                 ngx_log_error(NGX_LOG_ERR, s->connection->log, 0,
805                               "auth http server %V sent invalid server "
806                               "port:\"%V\"",
807                               ctx->peer.name, &ctx->port);
808                 ngx_destroy_pool(ctx->pool);
809                 ngx_mail_session_internal_server_error(s);
810                 return;
811             }
812 
813             ngx_inet_set_port(peer->sockaddr, (in_port_t) port);
814 
815             len = ctx->addr.len + 1 + ctx->port.len;
816 
817             peer->name.len = len;
818 
819             peer->name.data = ngx_pnalloc(s->connection->pool, len);
820             if (peer->name.data == NULL) {
821                 ngx_destroy_pool(ctx->pool);
822                 ngx_mail_session_internal_server_error(s);
823                 return;
824             }
825 
826             len = ctx->addr.len;
827 
828             ngx_memcpy(peer->name.data, ctx->addr.data, len);
829 
830             peer->name.data[len++] = ':';
831 
832             ngx_memcpy(peer->name.data + len, ctx->port.data, ctx->port.len);
833 
834             ngx_destroy_pool(ctx->pool);
835             ngx_mail_proxy_init(s, peer);
836 
837             return;
838         }
839 
840         if (rc == NGX_AGAIN ) {
841             return;
842         }
843 
844         /* rc == NGX_ERROR */
845 
846         ngx_log_error(NGX_LOG_ERR, s->connection->log, 0,
847                       "auth http server %V sent invalid header in response",
848                       ctx->peer.name);
849         ngx_close_connection(ctx->peer.connection);
850         ngx_destroy_pool(ctx->pool);
851         ngx_mail_session_internal_server_error(s);
852 
853         return;
854     }
855 }
856 
857 
858 static void
ngx_mail_auth_sleep_handler(ngx_event_t * rev)859 ngx_mail_auth_sleep_handler(ngx_event_t *rev)
860 {
861     ngx_connection_t          *c;
862     ngx_mail_session_t        *s;
863     ngx_mail_core_srv_conf_t  *cscf;
864 
865     ngx_log_debug0(NGX_LOG_DEBUG_MAIL, rev->log, 0, "mail auth sleep handler");
866 
867     c = rev->data;
868     s = c->data;
869 
870     if (rev->timedout) {
871 
872         rev->timedout = 0;
873 
874         if (s->auth_wait) {
875             s->auth_wait = 0;
876             ngx_mail_auth_http_init(s);
877             return;
878         }
879 
880         cscf = ngx_mail_get_module_srv_conf(s, ngx_mail_core_module);
881 
882         rev->handler = cscf->protocol->auth_state;
883 
884         s->mail_state = 0;
885         s->auth_method = NGX_MAIL_AUTH_PLAIN;
886 
887         c->log->action = "in auth state";
888 
889         ngx_mail_send(c->write);
890 
891         if (c->destroyed) {
892             return;
893         }
894 
895         ngx_add_timer(rev, cscf->timeout);
896 
897         if (rev->ready) {
898             rev->handler(rev);
899             return;
900         }
901 
902         if (ngx_handle_read_event(rev, 0) != NGX_OK) {
903             ngx_mail_close_connection(c);
904         }
905 
906         return;
907     }
908 
909     if (rev->active) {
910         if (ngx_handle_read_event(rev, 0) != NGX_OK) {
911             ngx_mail_close_connection(c);
912         }
913     }
914 }
915 
916 
917 static ngx_int_t
ngx_mail_auth_http_parse_header_line(ngx_mail_session_t * s,ngx_mail_auth_http_ctx_t * ctx)918 ngx_mail_auth_http_parse_header_line(ngx_mail_session_t *s,
919     ngx_mail_auth_http_ctx_t *ctx)
920 {
921     u_char      c, ch, *p;
922     enum {
923         sw_start = 0,
924         sw_name,
925         sw_space_before_value,
926         sw_value,
927         sw_space_after_value,
928         sw_almost_done,
929         sw_header_almost_done
930     } state;
931 
932     state = ctx->state;
933 
934     for (p = ctx->response->pos; p < ctx->response->last; p++) {
935         ch = *p;
936 
937         switch (state) {
938 
939         /* first char */
940         case sw_start:
941 
942             switch (ch) {
943             case CR:
944                 ctx->header_end = p;
945                 state = sw_header_almost_done;
946                 break;
947             case LF:
948                 ctx->header_end = p;
949                 goto header_done;
950             default:
951                 state = sw_name;
952                 ctx->header_name_start = p;
953 
954                 c = (u_char) (ch | 0x20);
955                 if (c >= 'a' && c <= 'z') {
956                     break;
957                 }
958 
959                 if (ch >= '0' && ch <= '9') {
960                     break;
961                 }
962 
963                 return NGX_ERROR;
964             }
965             break;
966 
967         /* header name */
968         case sw_name:
969             c = (u_char) (ch | 0x20);
970             if (c >= 'a' && c <= 'z') {
971                 break;
972             }
973 
974             if (ch == ':') {
975                 ctx->header_name_end = p;
976                 state = sw_space_before_value;
977                 break;
978             }
979 
980             if (ch == '-') {
981                 break;
982             }
983 
984             if (ch >= '0' && ch <= '9') {
985                 break;
986             }
987 
988             if (ch == CR) {
989                 ctx->header_name_end = p;
990                 ctx->header_start = p;
991                 ctx->header_end = p;
992                 state = sw_almost_done;
993                 break;
994             }
995 
996             if (ch == LF) {
997                 ctx->header_name_end = p;
998                 ctx->header_start = p;
999                 ctx->header_end = p;
1000                 goto done;
1001             }
1002 
1003             return NGX_ERROR;
1004 
1005         /* space* before header value */
1006         case sw_space_before_value:
1007             switch (ch) {
1008             case ' ':
1009                 break;
1010             case CR:
1011                 ctx->header_start = p;
1012                 ctx->header_end = p;
1013                 state = sw_almost_done;
1014                 break;
1015             case LF:
1016                 ctx->header_start = p;
1017                 ctx->header_end = p;
1018                 goto done;
1019             default:
1020                 ctx->header_start = p;
1021                 state = sw_value;
1022                 break;
1023             }
1024             break;
1025 
1026         /* header value */
1027         case sw_value:
1028             switch (ch) {
1029             case ' ':
1030                 ctx->header_end = p;
1031                 state = sw_space_after_value;
1032                 break;
1033             case CR:
1034                 ctx->header_end = p;
1035                 state = sw_almost_done;
1036                 break;
1037             case LF:
1038                 ctx->header_end = p;
1039                 goto done;
1040             }
1041             break;
1042 
1043         /* space* before end of header line */
1044         case sw_space_after_value:
1045             switch (ch) {
1046             case ' ':
1047                 break;
1048             case CR:
1049                 state = sw_almost_done;
1050                 break;
1051             case LF:
1052                 goto done;
1053             default:
1054                 state = sw_value;
1055                 break;
1056             }
1057             break;
1058 
1059         /* end of header line */
1060         case sw_almost_done:
1061             switch (ch) {
1062             case LF:
1063                 goto done;
1064             default:
1065                 return NGX_ERROR;
1066             }
1067 
1068         /* end of header */
1069         case sw_header_almost_done:
1070             switch (ch) {
1071             case LF:
1072                 goto header_done;
1073             default:
1074                 return NGX_ERROR;
1075             }
1076         }
1077     }
1078 
1079     ctx->response->pos = p;
1080     ctx->state = state;
1081 
1082     return NGX_AGAIN;
1083 
1084 done:
1085 
1086     ctx->response->pos = p + 1;
1087     ctx->state = sw_start;
1088 
1089     return NGX_OK;
1090 
1091 header_done:
1092 
1093     ctx->response->pos = p + 1;
1094     ctx->state = sw_start;
1095 
1096     return NGX_DONE;
1097 }
1098 
1099 
1100 static void
ngx_mail_auth_http_block_read(ngx_event_t * rev)1101 ngx_mail_auth_http_block_read(ngx_event_t *rev)
1102 {
1103     ngx_connection_t          *c;
1104     ngx_mail_session_t        *s;
1105     ngx_mail_auth_http_ctx_t  *ctx;
1106 
1107     ngx_log_debug0(NGX_LOG_DEBUG_MAIL, rev->log, 0,
1108                    "mail auth http block read");
1109 
1110     if (ngx_handle_read_event(rev, 0) != NGX_OK) {
1111         c = rev->data;
1112         s = c->data;
1113 
1114         ctx = ngx_mail_get_module_ctx(s, ngx_mail_auth_http_module);
1115 
1116         ngx_close_connection(ctx->peer.connection);
1117         ngx_destroy_pool(ctx->pool);
1118         ngx_mail_session_internal_server_error(s);
1119     }
1120 }
1121 
1122 
1123 static void
ngx_mail_auth_http_dummy_handler(ngx_event_t * ev)1124 ngx_mail_auth_http_dummy_handler(ngx_event_t *ev)
1125 {
1126     ngx_log_debug0(NGX_LOG_DEBUG_MAIL, ev->log, 0,
1127                    "mail auth http dummy handler");
1128 }
1129 
1130 
1131 static ngx_buf_t *
ngx_mail_auth_http_create_request(ngx_mail_session_t * s,ngx_pool_t * pool,ngx_mail_auth_http_conf_t * ahcf)1132 ngx_mail_auth_http_create_request(ngx_mail_session_t *s, ngx_pool_t *pool,
1133     ngx_mail_auth_http_conf_t *ahcf)
1134 {
1135     size_t                     len;
1136     ngx_buf_t                 *b;
1137     ngx_str_t                  login, passwd;
1138 #if (NGX_MAIL_SSL)
1139     ngx_str_t                  verify, subject, issuer, serial, fingerprint,
1140                                raw_cert, cert;
1141     ngx_connection_t          *c;
1142     ngx_mail_ssl_conf_t       *sslcf;
1143 #endif
1144     ngx_mail_core_srv_conf_t  *cscf;
1145 
1146     if (ngx_mail_auth_http_escape(pool, &s->login, &login) != NGX_OK) {
1147         return NULL;
1148     }
1149 
1150     if (ngx_mail_auth_http_escape(pool, &s->passwd, &passwd) != NGX_OK) {
1151         return NULL;
1152     }
1153 
1154 #if (NGX_MAIL_SSL)
1155 
1156     c = s->connection;
1157     sslcf = ngx_mail_get_module_srv_conf(s, ngx_mail_ssl_module);
1158 
1159     if (c->ssl && sslcf->verify) {
1160 
1161         /* certificate details */
1162 
1163         if (ngx_ssl_get_client_verify(c, pool, &verify) != NGX_OK) {
1164             return NULL;
1165         }
1166 
1167         if (ngx_ssl_get_subject_dn(c, pool, &subject) != NGX_OK) {
1168             return NULL;
1169         }
1170 
1171         if (ngx_ssl_get_issuer_dn(c, pool, &issuer) != NGX_OK) {
1172             return NULL;
1173         }
1174 
1175         if (ngx_ssl_get_serial_number(c, pool, &serial) != NGX_OK) {
1176             return NULL;
1177         }
1178 
1179         if (ngx_ssl_get_fingerprint(c, pool, &fingerprint) != NGX_OK) {
1180             return NULL;
1181         }
1182 
1183         if (ahcf->pass_client_cert) {
1184 
1185             /* certificate itself, if configured */
1186 
1187             if (ngx_ssl_get_raw_certificate(c, pool, &raw_cert) != NGX_OK) {
1188                 return NULL;
1189             }
1190 
1191             if (ngx_mail_auth_http_escape(pool, &raw_cert, &cert) != NGX_OK) {
1192                 return NULL;
1193             }
1194 
1195         } else {
1196             ngx_str_null(&cert);
1197         }
1198 
1199     } else {
1200         ngx_str_null(&verify);
1201         ngx_str_null(&subject);
1202         ngx_str_null(&issuer);
1203         ngx_str_null(&serial);
1204         ngx_str_null(&fingerprint);
1205         ngx_str_null(&cert);
1206     }
1207 
1208 #endif
1209 
1210     cscf = ngx_mail_get_module_srv_conf(s, ngx_mail_core_module);
1211 
1212     len = sizeof("GET ") - 1 + ahcf->uri.len + sizeof(" HTTP/1.0" CRLF) - 1
1213           + sizeof("Host: ") - 1 + ahcf->host_header.len + sizeof(CRLF) - 1
1214           + sizeof("Auth-Method: ") - 1
1215                 + ngx_mail_auth_http_method[s->auth_method].len
1216                 + sizeof(CRLF) - 1
1217           + sizeof("Auth-User: ") - 1 + login.len + sizeof(CRLF) - 1
1218           + sizeof("Auth-Pass: ") - 1 + passwd.len + sizeof(CRLF) - 1
1219           + sizeof("Auth-Salt: ") - 1 + s->salt.len
1220           + sizeof("Auth-Protocol: ") - 1 + cscf->protocol->name.len
1221                 + sizeof(CRLF) - 1
1222           + sizeof("Auth-Login-Attempt: ") - 1 + NGX_INT_T_LEN
1223                 + sizeof(CRLF) - 1
1224           + sizeof("Client-IP: ") - 1 + s->connection->addr_text.len
1225                 + sizeof(CRLF) - 1
1226           + sizeof("Client-Host: ") - 1 + s->host.len + sizeof(CRLF) - 1
1227           + sizeof("Auth-SMTP-Helo: ") - 1 + s->smtp_helo.len + sizeof(CRLF) - 1
1228           + sizeof("Auth-SMTP-From: ") - 1 + s->smtp_from.len + sizeof(CRLF) - 1
1229           + sizeof("Auth-SMTP-To: ") - 1 + s->smtp_to.len + sizeof(CRLF) - 1
1230 #if (NGX_MAIL_SSL)
1231           + sizeof("Auth-SSL: on" CRLF) - 1
1232           + sizeof("Auth-SSL-Verify: ") - 1 + verify.len + sizeof(CRLF) - 1
1233           + sizeof("Auth-SSL-Subject: ") - 1 + subject.len + sizeof(CRLF) - 1
1234           + sizeof("Auth-SSL-Issuer: ") - 1 + issuer.len + sizeof(CRLF) - 1
1235           + sizeof("Auth-SSL-Serial: ") - 1 + serial.len + sizeof(CRLF) - 1
1236           + sizeof("Auth-SSL-Fingerprint: ") - 1 + fingerprint.len
1237               + sizeof(CRLF) - 1
1238           + sizeof("Auth-SSL-Cert: ") - 1 + cert.len + sizeof(CRLF) - 1
1239 #endif
1240           + ahcf->header.len
1241           + sizeof(CRLF) - 1;
1242 
1243     b = ngx_create_temp_buf(pool, len);
1244     if (b == NULL) {
1245         return NULL;
1246     }
1247 
1248     b->last = ngx_cpymem(b->last, "GET ", sizeof("GET ") - 1);
1249     b->last = ngx_copy(b->last, ahcf->uri.data, ahcf->uri.len);
1250     b->last = ngx_cpymem(b->last, " HTTP/1.0" CRLF,
1251                          sizeof(" HTTP/1.0" CRLF) - 1);
1252 
1253     b->last = ngx_cpymem(b->last, "Host: ", sizeof("Host: ") - 1);
1254     b->last = ngx_copy(b->last, ahcf->host_header.data,
1255                          ahcf->host_header.len);
1256     *b->last++ = CR; *b->last++ = LF;
1257 
1258     b->last = ngx_cpymem(b->last, "Auth-Method: ",
1259                          sizeof("Auth-Method: ") - 1);
1260     b->last = ngx_cpymem(b->last,
1261                          ngx_mail_auth_http_method[s->auth_method].data,
1262                          ngx_mail_auth_http_method[s->auth_method].len);
1263     *b->last++ = CR; *b->last++ = LF;
1264 
1265     b->last = ngx_cpymem(b->last, "Auth-User: ", sizeof("Auth-User: ") - 1);
1266     b->last = ngx_copy(b->last, login.data, login.len);
1267     *b->last++ = CR; *b->last++ = LF;
1268 
1269     b->last = ngx_cpymem(b->last, "Auth-Pass: ", sizeof("Auth-Pass: ") - 1);
1270     b->last = ngx_copy(b->last, passwd.data, passwd.len);
1271     *b->last++ = CR; *b->last++ = LF;
1272 
1273     if (s->auth_method != NGX_MAIL_AUTH_PLAIN && s->salt.len) {
1274         b->last = ngx_cpymem(b->last, "Auth-Salt: ", sizeof("Auth-Salt: ") - 1);
1275         b->last = ngx_copy(b->last, s->salt.data, s->salt.len);
1276 
1277         s->passwd.data = NULL;
1278     }
1279 
1280     b->last = ngx_cpymem(b->last, "Auth-Protocol: ",
1281                          sizeof("Auth-Protocol: ") - 1);
1282     b->last = ngx_cpymem(b->last, cscf->protocol->name.data,
1283                          cscf->protocol->name.len);
1284     *b->last++ = CR; *b->last++ = LF;
1285 
1286     b->last = ngx_sprintf(b->last, "Auth-Login-Attempt: %ui" CRLF,
1287                           s->login_attempt);
1288 
1289     b->last = ngx_cpymem(b->last, "Client-IP: ", sizeof("Client-IP: ") - 1);
1290     b->last = ngx_copy(b->last, s->connection->addr_text.data,
1291                        s->connection->addr_text.len);
1292     *b->last++ = CR; *b->last++ = LF;
1293 
1294     if (s->host.len) {
1295         b->last = ngx_cpymem(b->last, "Client-Host: ",
1296                              sizeof("Client-Host: ") - 1);
1297         b->last = ngx_copy(b->last, s->host.data, s->host.len);
1298         *b->last++ = CR; *b->last++ = LF;
1299     }
1300 
1301     if (s->auth_method == NGX_MAIL_AUTH_NONE) {
1302 
1303         /* HELO, MAIL FROM, and RCPT TO can't contain CRLF, no need to escape */
1304 
1305         b->last = ngx_cpymem(b->last, "Auth-SMTP-Helo: ",
1306                              sizeof("Auth-SMTP-Helo: ") - 1);
1307         b->last = ngx_copy(b->last, s->smtp_helo.data, s->smtp_helo.len);
1308         *b->last++ = CR; *b->last++ = LF;
1309 
1310         b->last = ngx_cpymem(b->last, "Auth-SMTP-From: ",
1311                              sizeof("Auth-SMTP-From: ") - 1);
1312         b->last = ngx_copy(b->last, s->smtp_from.data, s->smtp_from.len);
1313         *b->last++ = CR; *b->last++ = LF;
1314 
1315         b->last = ngx_cpymem(b->last, "Auth-SMTP-To: ",
1316                              sizeof("Auth-SMTP-To: ") - 1);
1317         b->last = ngx_copy(b->last, s->smtp_to.data, s->smtp_to.len);
1318         *b->last++ = CR; *b->last++ = LF;
1319 
1320     }
1321 
1322 #if (NGX_MAIL_SSL)
1323 
1324     if (c->ssl) {
1325         b->last = ngx_cpymem(b->last, "Auth-SSL: on" CRLF,
1326                              sizeof("Auth-SSL: on" CRLF) - 1);
1327 
1328         if (verify.len) {
1329             b->last = ngx_cpymem(b->last, "Auth-SSL-Verify: ",
1330                                  sizeof("Auth-SSL-Verify: ") - 1);
1331             b->last = ngx_copy(b->last, verify.data, verify.len);
1332             *b->last++ = CR; *b->last++ = LF;
1333         }
1334 
1335         if (subject.len) {
1336             b->last = ngx_cpymem(b->last, "Auth-SSL-Subject: ",
1337                                  sizeof("Auth-SSL-Subject: ") - 1);
1338             b->last = ngx_copy(b->last, subject.data, subject.len);
1339             *b->last++ = CR; *b->last++ = LF;
1340         }
1341 
1342         if (issuer.len) {
1343             b->last = ngx_cpymem(b->last, "Auth-SSL-Issuer: ",
1344                                  sizeof("Auth-SSL-Issuer: ") - 1);
1345             b->last = ngx_copy(b->last, issuer.data, issuer.len);
1346             *b->last++ = CR; *b->last++ = LF;
1347         }
1348 
1349         if (serial.len) {
1350             b->last = ngx_cpymem(b->last, "Auth-SSL-Serial: ",
1351                                  sizeof("Auth-SSL-Serial: ") - 1);
1352             b->last = ngx_copy(b->last, serial.data, serial.len);
1353             *b->last++ = CR; *b->last++ = LF;
1354         }
1355 
1356         if (fingerprint.len) {
1357             b->last = ngx_cpymem(b->last, "Auth-SSL-Fingerprint: ",
1358                                  sizeof("Auth-SSL-Fingerprint: ") - 1);
1359             b->last = ngx_copy(b->last, fingerprint.data, fingerprint.len);
1360             *b->last++ = CR; *b->last++ = LF;
1361         }
1362 
1363         if (cert.len) {
1364             b->last = ngx_cpymem(b->last, "Auth-SSL-Cert: ",
1365                                  sizeof("Auth-SSL-Cert: ") - 1);
1366             b->last = ngx_copy(b->last, cert.data, cert.len);
1367             *b->last++ = CR; *b->last++ = LF;
1368         }
1369     }
1370 
1371 #endif
1372 
1373     if (ahcf->header.len) {
1374         b->last = ngx_copy(b->last, ahcf->header.data, ahcf->header.len);
1375     }
1376 
1377     /* add "\r\n" at the header end */
1378     *b->last++ = CR; *b->last++ = LF;
1379 
1380 #if (NGX_DEBUG_MAIL_PASSWD)
1381     ngx_log_debug2(NGX_LOG_DEBUG_MAIL, s->connection->log, 0,
1382                    "mail auth http header:%N\"%*s\"",
1383                    (size_t) (b->last - b->pos), b->pos);
1384 #endif
1385 
1386     return b;
1387 }
1388 
1389 
1390 static ngx_int_t
ngx_mail_auth_http_escape(ngx_pool_t * pool,ngx_str_t * text,ngx_str_t * escaped)1391 ngx_mail_auth_http_escape(ngx_pool_t *pool, ngx_str_t *text, ngx_str_t *escaped)
1392 {
1393     u_char     *p;
1394     uintptr_t   n;
1395 
1396     n = ngx_escape_uri(NULL, text->data, text->len, NGX_ESCAPE_MAIL_AUTH);
1397 
1398     if (n == 0) {
1399         *escaped = *text;
1400         return NGX_OK;
1401     }
1402 
1403     escaped->len = text->len + n * 2;
1404 
1405     p = ngx_pnalloc(pool, escaped->len);
1406     if (p == NULL) {
1407         return NGX_ERROR;
1408     }
1409 
1410     (void) ngx_escape_uri(p, text->data, text->len, NGX_ESCAPE_MAIL_AUTH);
1411 
1412     escaped->data = p;
1413 
1414     return NGX_OK;
1415 }
1416 
1417 
1418 static void *
ngx_mail_auth_http_create_conf(ngx_conf_t * cf)1419 ngx_mail_auth_http_create_conf(ngx_conf_t *cf)
1420 {
1421     ngx_mail_auth_http_conf_t  *ahcf;
1422 
1423     ahcf = ngx_pcalloc(cf->pool, sizeof(ngx_mail_auth_http_conf_t));
1424     if (ahcf == NULL) {
1425         return NULL;
1426     }
1427 
1428     ahcf->timeout = NGX_CONF_UNSET_MSEC;
1429     ahcf->pass_client_cert = NGX_CONF_UNSET;
1430 
1431     ahcf->file = cf->conf_file->file.name.data;
1432     ahcf->line = cf->conf_file->line;
1433 
1434     return ahcf;
1435 }
1436 
1437 
1438 static char *
ngx_mail_auth_http_merge_conf(ngx_conf_t * cf,void * parent,void * child)1439 ngx_mail_auth_http_merge_conf(ngx_conf_t *cf, void *parent, void *child)
1440 {
1441     ngx_mail_auth_http_conf_t *prev = parent;
1442     ngx_mail_auth_http_conf_t *conf = child;
1443 
1444     u_char           *p;
1445     size_t            len;
1446     ngx_uint_t        i;
1447     ngx_table_elt_t  *header;
1448 
1449     if (conf->peer == NULL) {
1450         conf->peer = prev->peer;
1451         conf->host_header = prev->host_header;
1452         conf->uri = prev->uri;
1453 
1454         if (conf->peer == NULL) {
1455             ngx_log_error(NGX_LOG_EMERG, cf->log, 0,
1456                           "no \"auth_http\" is defined for server in %s:%ui",
1457                           conf->file, conf->line);
1458 
1459             return NGX_CONF_ERROR;
1460         }
1461     }
1462 
1463     ngx_conf_merge_msec_value(conf->timeout, prev->timeout, 60000);
1464 
1465     ngx_conf_merge_value(conf->pass_client_cert, prev->pass_client_cert, 0);
1466 
1467     if (conf->headers == NULL) {
1468         conf->headers = prev->headers;
1469         conf->header = prev->header;
1470     }
1471 
1472     if (conf->headers && conf->header.len == 0) {
1473         len = 0;
1474         header = conf->headers->elts;
1475         for (i = 0; i < conf->headers->nelts; i++) {
1476             len += header[i].key.len + 2 + header[i].value.len + 2;
1477         }
1478 
1479         p = ngx_pnalloc(cf->pool, len);
1480         if (p == NULL) {
1481             return NGX_CONF_ERROR;
1482         }
1483 
1484         conf->header.len = len;
1485         conf->header.data = p;
1486 
1487         for (i = 0; i < conf->headers->nelts; i++) {
1488             p = ngx_cpymem(p, header[i].key.data, header[i].key.len);
1489             *p++ = ':'; *p++ = ' ';
1490             p = ngx_cpymem(p, header[i].value.data, header[i].value.len);
1491             *p++ = CR; *p++ = LF;
1492         }
1493     }
1494 
1495     return NGX_CONF_OK;
1496 }
1497 
1498 
1499 static char *
ngx_mail_auth_http(ngx_conf_t * cf,ngx_command_t * cmd,void * conf)1500 ngx_mail_auth_http(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
1501 {
1502     ngx_mail_auth_http_conf_t *ahcf = conf;
1503 
1504     ngx_str_t  *value;
1505     ngx_url_t   u;
1506 
1507     value = cf->args->elts;
1508 
1509     ngx_memzero(&u, sizeof(ngx_url_t));
1510 
1511     u.url = value[1];
1512     u.default_port = 80;
1513     u.uri_part = 1;
1514 
1515     if (ngx_strncmp(u.url.data, "http://", 7) == 0) {
1516         u.url.len -= 7;
1517         u.url.data += 7;
1518     }
1519 
1520     if (ngx_parse_url(cf->pool, &u) != NGX_OK) {
1521         if (u.err) {
1522             ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
1523                                "%s in auth_http \"%V\"", u.err, &u.url);
1524         }
1525 
1526         return NGX_CONF_ERROR;
1527     }
1528 
1529     ahcf->peer = u.addrs;
1530 
1531     if (u.family != AF_UNIX) {
1532         ahcf->host_header = u.host;
1533 
1534     } else {
1535         ngx_str_set(&ahcf->host_header, "localhost");
1536     }
1537 
1538     ahcf->uri = u.uri;
1539 
1540     if (ahcf->uri.len == 0) {
1541         ngx_str_set(&ahcf->uri, "/");
1542     }
1543 
1544     return NGX_CONF_OK;
1545 }
1546 
1547 
1548 static char *
ngx_mail_auth_http_header(ngx_conf_t * cf,ngx_command_t * cmd,void * conf)1549 ngx_mail_auth_http_header(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
1550 {
1551     ngx_mail_auth_http_conf_t *ahcf = conf;
1552 
1553     ngx_str_t        *value;
1554     ngx_table_elt_t  *header;
1555 
1556     if (ahcf->headers == NULL) {
1557         ahcf->headers = ngx_array_create(cf->pool, 1, sizeof(ngx_table_elt_t));
1558         if (ahcf->headers == NULL) {
1559             return NGX_CONF_ERROR;
1560         }
1561     }
1562 
1563     header = ngx_array_push(ahcf->headers);
1564     if (header == NULL) {
1565         return NGX_CONF_ERROR;
1566     }
1567 
1568     value = cf->args->elts;
1569 
1570     header->key = value[1];
1571     header->value = value[2];
1572 
1573     return NGX_CONF_OK;
1574 }
1575