comparison src/mail/ngx_mail_auth_http_module.c @ 9290:4538c1ffb0f8

Mail: added support for XOAUTH2 and OAUTHBEARER authentication. This patch adds support for the OAUTHBEARER SASL mechanism as defined by RFC 7628, as well as pre-RFC XOAUTH2 SASL mechanism. For both mechanisms, the "Auth-User" header is set to the client identity obtained from the initial SASL response sent by the client, and the "Auth-Pass" header is set to the Bearer token itself. The auth server may return the "Auth-Error-SASL" header, which is passed to the client as an additional SASL challenge. It is expected to contain mechanism-specific error details, base64-encoded. After the client responds (with an empty SASL response for XAUTH2, or with "AQ==" dummy response for OAUTHBEARER), the error message from the "Auth-Status" header is sent. Based on a patch by Rob Mueller.
author Maxim Dounin <mdounin@mdounin.ru>
date Mon, 03 Jun 2024 18:03:11 +0300
parents 46ecad404a29
children
comparison
equal deleted inserted replaced
9289:20017bff0de8 9290:4538c1ffb0f8
51 ngx_str_t addr; 51 ngx_str_t addr;
52 ngx_str_t port; 52 ngx_str_t port;
53 ngx_str_t err; 53 ngx_str_t err;
54 ngx_str_t errmsg; 54 ngx_str_t errmsg;
55 ngx_str_t errcode; 55 ngx_str_t errcode;
56 ngx_str_t errsasl;
56 57
57 time_t sleep; 58 time_t sleep;
58 59
59 ngx_pool_t *pool; 60 ngx_pool_t *pool;
60 }; 61 };
65 static void ngx_mail_auth_http_ignore_status_line(ngx_mail_session_t *s, 66 static void ngx_mail_auth_http_ignore_status_line(ngx_mail_session_t *s,
66 ngx_mail_auth_http_ctx_t *ctx); 67 ngx_mail_auth_http_ctx_t *ctx);
67 static void ngx_mail_auth_http_process_headers(ngx_mail_session_t *s, 68 static void ngx_mail_auth_http_process_headers(ngx_mail_session_t *s,
68 ngx_mail_auth_http_ctx_t *ctx); 69 ngx_mail_auth_http_ctx_t *ctx);
69 static void ngx_mail_auth_sleep_handler(ngx_event_t *rev); 70 static void ngx_mail_auth_sleep_handler(ngx_event_t *rev);
71 static void ngx_mail_auth_send_error(ngx_mail_session_t *s);
70 static ngx_int_t ngx_mail_auth_http_parse_header_line(ngx_mail_session_t *s, 72 static ngx_int_t ngx_mail_auth_http_parse_header_line(ngx_mail_session_t *s,
71 ngx_mail_auth_http_ctx_t *ctx); 73 ngx_mail_auth_http_ctx_t *ctx);
72 static void ngx_mail_auth_http_block_read(ngx_event_t *rev); 74 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); 75 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, 76 static ngx_buf_t *ngx_mail_auth_http_create_request(ngx_mail_session_t *s,
150 ngx_string("plain"), 152 ngx_string("plain"),
151 ngx_string("plain"), 153 ngx_string("plain"),
152 ngx_string("apop"), 154 ngx_string("apop"),
153 ngx_string("cram-md5"), 155 ngx_string("cram-md5"),
154 ngx_string("external"), 156 ngx_string("external"),
157 ngx_string("xoauth2"),
158 ngx_string("oauthbearer"),
155 ngx_string("none") 159 ngx_string("none")
156 }; 160 };
157 161
158 static ngx_str_t ngx_mail_smtp_errcode = ngx_string("535 5.7.0"); 162 static ngx_str_t ngx_mail_smtp_errcode = ngx_string("535 5.7.0");
159 163
675 ctx->errcode.len); 679 ctx->errcode.len);
676 680
677 continue; 681 continue;
678 } 682 }
679 683
684 if (len == sizeof("Auth-Error-SASL") - 1
685 && ngx_strncasecmp(ctx->header_name_start,
686 (u_char *) "Auth-Error-SASL",
687 sizeof("Auth-Error-SASL") - 1)
688 == 0)
689 {
690 if (s->auth_method != NGX_MAIL_AUTH_XOAUTH2
691 && s->auth_method != NGX_MAIL_AUTH_OAUTHBEARER)
692 {
693 continue;
694 }
695
696 len = ctx->header_end - ctx->header_start;
697
698 if (s->protocol == NGX_MAIL_SMTP_PROTOCOL) {
699 size = len + sizeof("334 " CRLF) - 1;
700
701 } else {
702 size = len + sizeof("+ " CRLF) - 1;
703 }
704
705 p = ngx_pnalloc(s->connection->pool, size);
706 if (p == NULL) {
707 ngx_close_connection(ctx->peer.connection);
708 ngx_destroy_pool(ctx->pool);
709 ngx_mail_session_internal_server_error(s);
710 return;
711 }
712
713 ctx->errsasl.len = size;
714 ctx->errsasl.data = p;
715
716 if (s->protocol == NGX_MAIL_SMTP_PROTOCOL) {
717 *p++ = '3'; *p++ = '3'; *p++ = '4'; *p++ = ' ';
718
719 } else {
720 *p++ = '+'; *p++ = ' ';
721 }
722
723 p = ngx_cpymem(p, ctx->header_start, len);
724 *p++ = CR; *p = LF;
725
726 continue;
727 }
728
680 /* ignore other headers */ 729 /* ignore other headers */
681 730
682 continue; 731 continue;
683 } 732 }
684 733
715 *p++ = ' '; 764 *p++ = ' ';
716 p = ngx_cpymem(p, ctx->errmsg.data, ctx->errmsg.len); 765 p = ngx_cpymem(p, ctx->errmsg.data, ctx->errmsg.len);
717 *p++ = CR; *p = LF; 766 *p++ = CR; *p = LF;
718 } 767 }
719 768
720 s->out = ctx->err; 769 s->out = ctx->errsasl;
770 s->auth_err = ctx->err;
721 timer = ctx->sleep; 771 timer = ctx->sleep;
722 772
723 ngx_destroy_pool(ctx->pool); 773 ngx_destroy_pool(ctx->pool);
724 774
725 if (timer == 0) { 775 if (timer == 0) {
726 s->quit = 1; 776 s->auth_quit = 1;
727 ngx_mail_send(s->connection->write); 777 ngx_mail_auth_send_error(s);
728 return; 778 return;
729 } 779 }
730 780
731 ngx_add_timer(s->connection->read, (ngx_msec_t) (timer * 1000)); 781 ngx_add_timer(s->connection->read, (ngx_msec_t) (timer * 1000));
732 782
856 906
857 907
858 static void 908 static void
859 ngx_mail_auth_sleep_handler(ngx_event_t *rev) 909 ngx_mail_auth_sleep_handler(ngx_event_t *rev)
860 { 910 {
861 ngx_connection_t *c; 911 ngx_connection_t *c;
862 ngx_mail_session_t *s; 912 ngx_mail_session_t *s;
863 ngx_mail_core_srv_conf_t *cscf;
864 913
865 ngx_log_debug0(NGX_LOG_DEBUG_MAIL, rev->log, 0, "mail auth sleep handler"); 914 ngx_log_debug0(NGX_LOG_DEBUG_MAIL, rev->log, 0, "mail auth sleep handler");
866 915
867 c = rev->data; 916 c = rev->data;
868 s = c->data; 917 s = c->data;
875 s->auth_wait = 0; 924 s->auth_wait = 0;
876 ngx_mail_auth_http_init(s); 925 ngx_mail_auth_http_init(s);
877 return; 926 return;
878 } 927 }
879 928
880 cscf = ngx_mail_get_module_srv_conf(s, ngx_mail_core_module); 929 ngx_mail_auth_send_error(s);
881
882 rev->handler = cscf->protocol->auth_state;
883
884 s->mail_state = 0;
885 s->auth_method = NGX_MAIL_AUTH_PLAIN;
886 s->tag.len = 0;
887
888 c->log->action = "in auth state";
889
890 ngx_mail_send(c->write);
891
892 if (c->destroyed) {
893 return;
894 }
895
896 ngx_add_timer(rev, cscf->timeout);
897
898 if (rev->ready) {
899 rev->handler(rev);
900 return;
901 }
902
903 if (ngx_handle_read_event(rev, 0) != NGX_OK) {
904 ngx_mail_close_connection(c);
905 }
906
907 return; 930 return;
908 } 931 }
909 932
910 if (rev->active) { 933 if (rev->active) {
911 if (ngx_handle_read_event(rev, 0) != NGX_OK) { 934 if (ngx_handle_read_event(rev, 0) != NGX_OK) {
912 ngx_mail_close_connection(c); 935 ngx_mail_close_connection(c);
913 } 936 }
937 }
938 }
939
940
941 static void
942 ngx_mail_auth_send_error(ngx_mail_session_t *s)
943 {
944 ngx_event_t *rev;
945 ngx_connection_t *c;
946 ngx_mail_core_srv_conf_t *cscf;
947
948 c = s->connection;
949 rev = c->read;
950
951 cscf = ngx_mail_get_module_srv_conf(s, ngx_mail_core_module);
952
953 rev->handler = cscf->protocol->auth_state;
954
955 s->auth_method = NGX_MAIL_AUTH_PLAIN;
956
957 c->log->action = "in auth state";
958
959 if (s->out.len == 0) {
960 s->out = s->auth_err;
961 s->quit = s->auth_quit;
962 ngx_str_null(&s->auth_err);
963
964 s->state = 0;
965 s->mail_state = 0;
966 s->tag.len = 0;
967
968 } else {
969 s->auth_err.len -= s->tag.len;
970 s->auth_err.data += s->tag.len;
971 }
972
973 ngx_mail_send(c->write);
974
975 if (c->destroyed) {
976 return;
977 }
978
979 ngx_add_timer(rev, cscf->timeout);
980
981 if (rev->ready) {
982 rev->handler(rev);
983 return;
984 }
985
986 if (ngx_handle_read_event(rev, 0) != NGX_OK) {
987 ngx_mail_close_connection(c);
914 } 988 }
915 } 989 }
916 990
917 991
918 static ngx_int_t 992 static ngx_int_t