Mercurial > hg > nginx-quic
view src/mail/ngx_mail_imap_handler.c @ 7629:f47f7d3d1bfa
Mp4: fixed possible chunk offset overflow.
In "co64" atom chunk start offset is a 64-bit unsigned integer. When trimming
the "mdat" atom, chunk offsets are casted to off_t values which are typically
64-bit signed integers. A specially crafted mp4 file with huge chunk offsets
may lead to off_t overflow and result in negative trim boundaries.
The consequences of the overflow are:
- Incorrect Content-Length header value in the response.
- Negative left boundary of the response file buffer holding the trimmed "mdat".
This leads to pread()/sendfile() errors followed by closing the client
connection.
On rare systems where off_t is a 32-bit integer, this scenario is also feasible
with the "stco" atom.
The fix is to add checks which make sure data chunks referenced by each track
are within the mp4 file boundaries. Additionally a few more checks are added to
ensure mp4 file consistency and log errors.
author | Roman Arutyunyan <arut@nginx.com> |
---|---|
date | Wed, 26 Feb 2020 15:10:46 +0300 |
parents | b2915d99ee8d |
children | ab6257dac2a8 |
line wrap: on
line source
/* * Copyright (C) Igor Sysoev * Copyright (C) Nginx, Inc. */ #include <ngx_config.h> #include <ngx_core.h> #include <ngx_event.h> #include <ngx_mail.h> #include <ngx_mail_imap_module.h> static ngx_int_t ngx_mail_imap_login(ngx_mail_session_t *s, ngx_connection_t *c); static ngx_int_t ngx_mail_imap_authenticate(ngx_mail_session_t *s, ngx_connection_t *c); static ngx_int_t ngx_mail_imap_capability(ngx_mail_session_t *s, ngx_connection_t *c); static ngx_int_t ngx_mail_imap_starttls(ngx_mail_session_t *s, ngx_connection_t *c); static u_char imap_greeting[] = "* OK IMAP4 ready" CRLF; static u_char imap_star[] = "* "; static u_char imap_ok[] = "OK completed" CRLF; static u_char imap_next[] = "+ OK" CRLF; static u_char imap_plain_next[] = "+ " CRLF; static u_char imap_username[] = "+ VXNlcm5hbWU6" CRLF; static u_char imap_password[] = "+ UGFzc3dvcmQ6" CRLF; static u_char imap_bye[] = "* BYE" CRLF; static u_char imap_invalid_command[] = "BAD invalid command" CRLF; void ngx_mail_imap_init_session(ngx_mail_session_t *s, ngx_connection_t *c) { ngx_mail_core_srv_conf_t *cscf; cscf = ngx_mail_get_module_srv_conf(s, ngx_mail_core_module); ngx_str_set(&s->out, imap_greeting); c->read->handler = ngx_mail_imap_init_protocol; ngx_add_timer(c->read, cscf->timeout); if (ngx_handle_read_event(c->read, 0) != NGX_OK) { ngx_mail_close_connection(c); } ngx_mail_send(c->write); } void ngx_mail_imap_init_protocol(ngx_event_t *rev) { ngx_connection_t *c; ngx_mail_session_t *s; ngx_mail_imap_srv_conf_t *iscf; c = rev->data; c->log->action = "in auth state"; if (rev->timedout) { ngx_log_error(NGX_LOG_INFO, c->log, NGX_ETIMEDOUT, "client timed out"); c->timedout = 1; ngx_mail_close_connection(c); return; } s = c->data; if (s->buffer == NULL) { if (ngx_array_init(&s->args, c->pool, 2, sizeof(ngx_str_t)) == NGX_ERROR) { ngx_mail_session_internal_server_error(s); return; } iscf = ngx_mail_get_module_srv_conf(s, ngx_mail_imap_module); s->buffer = ngx_create_temp_buf(c->pool, iscf->client_buffer_size); if (s->buffer == NULL) { ngx_mail_session_internal_server_error(s); return; } } s->mail_state = ngx_imap_start; c->read->handler = ngx_mail_imap_auth_state; ngx_mail_imap_auth_state(rev); } void ngx_mail_imap_auth_state(ngx_event_t *rev) { u_char *p, *dst, *src, *end; ngx_str_t *arg; ngx_int_t rc; ngx_uint_t tag, i; ngx_connection_t *c; ngx_mail_session_t *s; c = rev->data; s = c->data; ngx_log_debug0(NGX_LOG_DEBUG_MAIL, c->log, 0, "imap auth state"); if (rev->timedout) { ngx_log_error(NGX_LOG_INFO, c->log, NGX_ETIMEDOUT, "client timed out"); c->timedout = 1; ngx_mail_close_connection(c); return; } if (s->out.len) { ngx_log_debug0(NGX_LOG_DEBUG_MAIL, c->log, 0, "imap send handler busy"); s->blocked = 1; return; } s->blocked = 0; rc = ngx_mail_read_command(s, c); if (rc == NGX_AGAIN || rc == NGX_ERROR) { return; } tag = 1; s->text.len = 0; ngx_str_set(&s->out, imap_ok); if (rc == NGX_OK) { ngx_log_debug1(NGX_LOG_DEBUG_MAIL, c->log, 0, "imap auth command: %i", s->command); if (s->backslash) { arg = s->args.elts; for (i = 0; i < s->args.nelts; i++) { dst = arg[i].data; end = dst + arg[i].len; for (src = dst; src < end; dst++) { *dst = *src; if (*src++ == '\\') { *dst = *src++; } } arg[i].len = dst - arg[i].data; } s->backslash = 0; } switch (s->mail_state) { case ngx_imap_start: switch (s->command) { case NGX_IMAP_LOGIN: rc = ngx_mail_imap_login(s, c); break; case NGX_IMAP_AUTHENTICATE: rc = ngx_mail_imap_authenticate(s, c); tag = (rc != NGX_OK); break; case NGX_IMAP_CAPABILITY: rc = ngx_mail_imap_capability(s, c); break; case NGX_IMAP_LOGOUT: s->quit = 1; ngx_str_set(&s->text, imap_bye); break; case NGX_IMAP_NOOP: break; case NGX_IMAP_STARTTLS: rc = ngx_mail_imap_starttls(s, c); break; default: rc = NGX_MAIL_PARSE_INVALID_COMMAND; break; } break; case ngx_imap_auth_login_username: rc = ngx_mail_auth_login_username(s, c, 0); tag = 0; ngx_str_set(&s->out, imap_password); s->mail_state = ngx_imap_auth_login_password; break; case ngx_imap_auth_login_password: rc = ngx_mail_auth_login_password(s, c); break; case ngx_imap_auth_plain: rc = ngx_mail_auth_plain(s, c, 0); break; case ngx_imap_auth_cram_md5: rc = ngx_mail_auth_cram_md5(s, c); break; case ngx_imap_auth_external: rc = ngx_mail_auth_external(s, c, 0); break; } } else if (rc == NGX_IMAP_NEXT) { tag = 0; ngx_str_set(&s->out, imap_next); } switch (rc) { case NGX_DONE: ngx_mail_auth(s, c); return; case NGX_ERROR: ngx_mail_session_internal_server_error(s); return; case NGX_MAIL_PARSE_INVALID_COMMAND: s->state = 0; ngx_str_set(&s->out, imap_invalid_command); s->mail_state = ngx_imap_start; break; } if (tag) { if (s->tag.len == 0) { ngx_str_set(&s->tag, imap_star); } if (s->tagged_line.len < s->tag.len + s->text.len + s->out.len) { s->tagged_line.len = s->tag.len + s->text.len + s->out.len; s->tagged_line.data = ngx_pnalloc(c->pool, s->tagged_line.len); if (s->tagged_line.data == NULL) { ngx_mail_close_connection(c); return; } } p = s->tagged_line.data; if (s->text.len) { p = ngx_cpymem(p, s->text.data, s->text.len); } p = ngx_cpymem(p, s->tag.data, s->tag.len); ngx_memcpy(p, s->out.data, s->out.len); s->out.len = s->text.len + s->tag.len + s->out.len; s->out.data = s->tagged_line.data; } if (rc != NGX_IMAP_NEXT) { s->args.nelts = 0; if (s->state) { /* preserve tag */ s->arg_start = s->buffer->start + s->tag.len; s->buffer->pos = s->arg_start; s->buffer->last = s->arg_start; } else { s->buffer->pos = s->buffer->start; s->buffer->last = s->buffer->start; s->tag.len = 0; } } ngx_mail_send(c->write); } static ngx_int_t ngx_mail_imap_login(ngx_mail_session_t *s, ngx_connection_t *c) { ngx_str_t *arg; #if (NGX_MAIL_SSL) if (ngx_mail_starttls_only(s, c)) { return NGX_MAIL_PARSE_INVALID_COMMAND; } #endif arg = s->args.elts; if (s->args.nelts != 2 || arg[0].len == 0) { return NGX_MAIL_PARSE_INVALID_COMMAND; } s->login.len = arg[0].len; s->login.data = ngx_pnalloc(c->pool, s->login.len); if (s->login.data == NULL) { return NGX_ERROR; } ngx_memcpy(s->login.data, arg[0].data, s->login.len); s->passwd.len = arg[1].len; s->passwd.data = ngx_pnalloc(c->pool, s->passwd.len); if (s->passwd.data == NULL) { return NGX_ERROR; } ngx_memcpy(s->passwd.data, arg[1].data, s->passwd.len); #if (NGX_DEBUG_MAIL_PASSWD) ngx_log_debug2(NGX_LOG_DEBUG_MAIL, c->log, 0, "imap login:\"%V\" passwd:\"%V\"", &s->login, &s->passwd); #else ngx_log_debug1(NGX_LOG_DEBUG_MAIL, c->log, 0, "imap login:\"%V\"", &s->login); #endif return NGX_DONE; } static ngx_int_t ngx_mail_imap_authenticate(ngx_mail_session_t *s, ngx_connection_t *c) { ngx_int_t rc; ngx_mail_core_srv_conf_t *cscf; ngx_mail_imap_srv_conf_t *iscf; #if (NGX_MAIL_SSL) if (ngx_mail_starttls_only(s, c)) { return NGX_MAIL_PARSE_INVALID_COMMAND; } #endif iscf = ngx_mail_get_module_srv_conf(s, ngx_mail_imap_module); rc = ngx_mail_auth_parse(s, c); switch (rc) { case NGX_MAIL_AUTH_LOGIN: ngx_str_set(&s->out, imap_username); s->mail_state = ngx_imap_auth_login_username; return NGX_OK; case NGX_MAIL_AUTH_LOGIN_USERNAME: ngx_str_set(&s->out, imap_password); s->mail_state = ngx_imap_auth_login_password; return ngx_mail_auth_login_username(s, c, 1); case NGX_MAIL_AUTH_PLAIN: ngx_str_set(&s->out, imap_plain_next); s->mail_state = ngx_imap_auth_plain; return NGX_OK; case NGX_MAIL_AUTH_CRAM_MD5: if (!(iscf->auth_methods & NGX_MAIL_AUTH_CRAM_MD5_ENABLED)) { return NGX_MAIL_PARSE_INVALID_COMMAND; } if (s->salt.data == NULL) { cscf = ngx_mail_get_module_srv_conf(s, ngx_mail_core_module); if (ngx_mail_salt(s, c, cscf) != NGX_OK) { return NGX_ERROR; } } if (ngx_mail_auth_cram_md5_salt(s, c, "+ ", 2) == NGX_OK) { s->mail_state = ngx_imap_auth_cram_md5; return NGX_OK; } return NGX_ERROR; case NGX_MAIL_AUTH_EXTERNAL: if (!(iscf->auth_methods & NGX_MAIL_AUTH_EXTERNAL_ENABLED)) { return NGX_MAIL_PARSE_INVALID_COMMAND; } ngx_str_set(&s->out, imap_username); s->mail_state = ngx_imap_auth_external; return NGX_OK; } return rc; } static ngx_int_t ngx_mail_imap_capability(ngx_mail_session_t *s, ngx_connection_t *c) { ngx_mail_imap_srv_conf_t *iscf; iscf = ngx_mail_get_module_srv_conf(s, ngx_mail_imap_module); #if (NGX_MAIL_SSL) if (c->ssl == NULL) { ngx_mail_ssl_conf_t *sslcf; sslcf = ngx_mail_get_module_srv_conf(s, ngx_mail_ssl_module); if (sslcf->starttls == NGX_MAIL_STARTTLS_ON) { s->text = iscf->starttls_capability; return NGX_OK; } if (sslcf->starttls == NGX_MAIL_STARTTLS_ONLY) { s->text = iscf->starttls_only_capability; return NGX_OK; } } #endif s->text = iscf->capability; return NGX_OK; } static ngx_int_t ngx_mail_imap_starttls(ngx_mail_session_t *s, ngx_connection_t *c) { #if (NGX_MAIL_SSL) ngx_mail_ssl_conf_t *sslcf; if (c->ssl == NULL) { sslcf = ngx_mail_get_module_srv_conf(s, ngx_mail_ssl_module); if (sslcf->starttls) { c->read->handler = ngx_mail_starttls_handler; return NGX_OK; } } #endif return NGX_MAIL_PARSE_INVALID_COMMAND; }