File: [local] / src / usr.sbin / smtpd / rfc5322.c (download)
Revision 1.4, Tue Dec 5 13:38:25 2023 UTC (6 months ago) by op
Branch: MAIN
CVS Tags: OPENBSD_7_5_BASE, OPENBSD_7_5, HEAD Changes since 1.3: +3 -2 lines
reject headers that start with a space or tab
If the first header starts with a space but still contains a colon
character, it is added to the body mail effectively appending it to the
Received header due to the folding rules.
Issue reported by Crystal Kolipe
ok millert@, giovanni@
|
/* $OpenBSD: rfc5322.c,v 1.4 2023/12/05 13:38:25 op Exp $ */
/*
* Copyright (c) 2018 Eric Faurot <eric@openbsd.org>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include <errno.h>
#include <limits.h>
#include <stdlib.h>
#include <string.h>
#include "rfc5322.h"
struct buf {
char *buf;
size_t bufsz;
size_t buflen;
size_t bufmax;
};
static int buf_alloc(struct buf *, size_t);
static int buf_grow(struct buf *, size_t);
static int buf_cat(struct buf *, const char *);
struct rfc5322_parser {
const char *line;
int state; /* last parser state */
int next; /* parser needs data */
int unfold;
const char *currhdr;
struct buf hdr;
struct buf val;
};
struct rfc5322_parser *
rfc5322_parser_new(void)
{
struct rfc5322_parser *parser;
parser = calloc(1, sizeof(*parser));
if (parser == NULL)
return NULL;
rfc5322_clear(parser);
parser->hdr.bufmax = 1024;
parser->val.bufmax = 65536;
return parser;
}
void
rfc5322_free(struct rfc5322_parser *parser)
{
free(parser->hdr.buf);
free(parser->val.buf);
free(parser);
}
void
rfc5322_clear(struct rfc5322_parser *parser)
{
parser->line = NULL;
parser->state = RFC5322_NONE;
parser->next = 0;
parser->hdr.buflen = 0;
parser->val.buflen = 0;
}
int
rfc5322_push(struct rfc5322_parser *parser, const char *line)
{
if (parser->line) {
errno = EALREADY;
return -1;
}
parser->line = line;
parser->next = 0;
return 0;
}
int
rfc5322_unfold_header(struct rfc5322_parser *parser)
{
if (parser->unfold) {
errno = EALREADY;
return -1;
}
if (parser->currhdr == NULL) {
errno = EOPNOTSUPP;
return -1;
}
if (buf_cat(&parser->val, parser->currhdr) == -1)
return -1;
parser->currhdr = NULL;
parser->unfold = 1;
return 0;
}
static int
_rfc5322_next(struct rfc5322_parser *parser, struct rfc5322_result *res)
{
size_t len;
const char *pos, *line;
line = parser->line;
switch(parser->state) {
case RFC5322_HEADER_START:
case RFC5322_HEADER_CONT:
res->hdr = parser->hdr.buf;
if (line && (line[0] == ' ' || line[0] == '\t')) {
parser->line = NULL;
parser->next = 1;
if (parser->unfold) {
if (buf_cat(&parser->val, "\n") == -1 ||
buf_cat(&parser->val, line) == -1)
return -1;
}
res->value = line;
return RFC5322_HEADER_CONT;
}
if (parser->unfold) {
parser->val.buflen = 0;
parser->unfold = 0;
res->value = parser->val.buf;
}
return RFC5322_HEADER_END;
case RFC5322_NONE:
case RFC5322_HEADER_END:
if (line && line[0] != ' ' && line[0] != '\t' &&
(pos = strchr(line, ':'))) {
len = pos - line;
if (buf_grow(&parser->hdr, len + 1) == -1)
return -1;
(void)memcpy(parser->hdr.buf, line, len);
parser->hdr.buf[len] = '\0';
parser->hdr.buflen = len + 1;
parser->line = NULL;
parser->next = 1;
parser->currhdr = pos + 1;
res->hdr = parser->hdr.buf;
res->value = pos + 1;
return RFC5322_HEADER_START;
}
return RFC5322_END_OF_HEADERS;
case RFC5322_END_OF_HEADERS:
if (line == NULL)
return RFC5322_END_OF_MESSAGE;
if (line[0] == '\0') {
parser->line = NULL;
parser->next = 1;
res->value = line;
return RFC5322_BODY_START;
}
errno = EINVAL;
return -1;
case RFC5322_BODY_START:
case RFC5322_BODY:
if (line == NULL)
return RFC5322_END_OF_MESSAGE;
parser->line = NULL;
parser->next = 1;
res->value = line;
return RFC5322_BODY;
case RFC5322_END_OF_MESSAGE:
errno = ENOMSG;
return -1;
default:
errno = EINVAL;
return -1;
}
}
int
rfc5322_next(struct rfc5322_parser *parser, struct rfc5322_result *res)
{
memset(res, 0, sizeof(*res));
if (parser->next)
return RFC5322_NONE;
return (parser->state = _rfc5322_next(parser, res));
}
static int
buf_alloc(struct buf *b, size_t need)
{
char *buf;
size_t alloc;
if (b->buf && b->bufsz >= need)
return 0;
if (need >= b->bufmax) {
errno = ERANGE;
return -1;
}
#define N 256
alloc = N * (need / N) + ((need % N) ? N : 0);
#undef N
buf = reallocarray(b->buf, alloc, 1);
if (buf == NULL)
return -1;
b->buf = buf;
b->bufsz = alloc;
return 0;
}
static int
buf_grow(struct buf *b, size_t sz)
{
if (SIZE_T_MAX - b->buflen <= sz) {
errno = ERANGE;
return -1;
}
return buf_alloc(b, b->buflen + sz);
}
static int
buf_cat(struct buf *b, const char *s)
{
size_t len = strlen(s);
if (buf_grow(b, len + 1) == -1)
return -1;
(void)memmove(b->buf + b->buflen, s, len + 1);
b->buflen += len;
return 0;
}