Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

imapd: track characters used in IMAP tags #5055

Open
wants to merge 5 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
150 changes: 148 additions & 2 deletions imap/imapd.c
Original file line number Diff line number Diff line change
Expand Up @@ -891,7 +891,10 @@ static void imapd_log_client_behavior(void)
"%s%s%s%s"
"%s%s%s%s"
"%s%s%s%s"
"%s%s",
"%s%s"
"%s%s%s%s%s%s"
"%s%s%s%s%s%s"
"%s%s%s%s%s",

session_id(),
imapd_userid ? imapd_userid : "",
Expand Down Expand Up @@ -925,7 +928,27 @@ static void imapd_log_client_behavior(void)
client_behavior.did_unselect ? " unselect=<1>" : "",

client_behavior.did_utf8_accept ? " utf8_accept=<1>" : "",
client_behavior.did_xlist ? " xlist=<1>" : "");
client_behavior.did_xlist ? " xlist=<1>" : "",

client_behavior.did_tag_only_num ? " tag_only_num=<1>" : "",
client_behavior.did_tag_only_alpha ? " tag_only_alpha=<1>" : "",
client_behavior.did_tag_only_alnum ? " tag_only_alnum=<1>" : "",
client_behavior.did_tag_only_alnumdot? " tag_only_alnumdot=<1>" : "",
client_behavior.did_tag_only_base64 ? " tag_only_base64=<1>" : "",
client_behavior.did_tag_only_onedot ? " tag_only_onedot=<1>" : "",

client_behavior.did_tag_num ? " tag_num=<1>" : "",
client_behavior.did_tag_alpha ? " tag_alpha=<1>" : "",
client_behavior.did_tag_dot ? " tag_dot=<1>" : "",
client_behavior.did_tag_sep ? " tag_sep=<1>" : "",
client_behavior.did_tag_colon ? " tag_colon=<1>" : "",
client_behavior.did_tag_angle ? " tag_angle=<1>" : "",

client_behavior.did_tag_base64 ? " tag_bases64=<1>" : "",
client_behavior.did_tag_other ? " tag_other=<1>" : "",
client_behavior.did_tag_POST ? " tag_POST=<1>" : "",
client_behavior.did_tag_PUT ? " tag_PUT=<1>" : "",
client_behavior.did_tag_inv ? " tag_inv=<1>" : "");
}

static void imapd_reset(void)
Expand Down Expand Up @@ -1473,6 +1496,127 @@ static void imapd_check(struct backend *be, unsigned tell_flags)
}
}

static void record_client_tag_behaviour(const char *tag)
{
#define TAG_SAW_NUM (1<<0)
#define TAG_SAW_ALPHA (1<<1)
#define TAG_SAW_DOT (1<<2)
#define TAG_SAW_ANGLE (1<<3)
#define TAG_SAW_SEP (1<<4)
#define TAG_SAW_COLON (1<<5)
#define TAG_SAW_B64EXTRA (1<<6)
#define TAG_SAW_OTHER (1<<31)

#define TAG_BASE64 (TAG_SAW_NUM | TAG_SAW_ALPHA \
| TAG_SAW_SEP | TAG_SAW_B64EXTRA)
uint32_t saw = 0;
const char *p;

/* whole string checks */
if (strcmp(tag, "POST") == 0) {
client_behavior.did_tag_POST = 1;
client_behavior.did_tag_only_alpha = 1;
return;
}
else if (strcmp(tag, "PUT") == 0) {
client_behavior.did_tag_PUT = 1;
client_behavior.did_tag_only_alpha = 1;
return;
}

/* collect character classes */
for (p = tag; p && *p; p++) {
if (*p >= '0' && *p <= '9') {
saw |= TAG_SAW_NUM;
}
else if ((*p >= 'a' && *p <= 'z') || (*p >= 'A' && *p <= 'Z')) {
saw |= TAG_SAW_ALPHA;
}
else if (*p == '.') {
saw |= TAG_SAW_DOT;
}
else if (*p == '<' || *p == '>') {
saw |= TAG_SAW_ANGLE;
}
else if (*p == '-' || *p == '_') {
saw |= TAG_SAW_SEP;
}
else if (*p == ':') {
saw |= TAG_SAW_COLON;
}
else if (*p == '+' || *p == '/' || *p == ',' || *p == '=') {
saw |= TAG_SAW_B64EXTRA;
}
else {
saw |= TAG_SAW_OTHER;
}
}

/* exclusive checks */
if (saw == TAG_SAW_NUM) {
client_behavior.did_tag_only_num = 1;
return;
}
else if (saw == TAG_SAW_ALPHA) {
client_behavior.did_tag_only_alpha = 1;
return;
}
else if (saw == TAG_SAW_DOT && !tag[1]) {
client_behavior.did_tag_only_onedot = 1;
return;
}
else if (saw == (TAG_SAW_NUM | TAG_SAW_ALPHA)) {
client_behavior.did_tag_only_alnum = 1;
return;
}
else if ((saw & (TAG_SAW_NUM | TAG_SAW_ALPHA))
&& (saw & ~(TAG_SAW_NUM | TAG_SAW_ALPHA | TAG_SAW_DOT)) == 0)
{
client_behavior.did_tag_only_alnumdot = 1;
return;
}
else if (saw && (saw & ~TAG_BASE64) == 0) {
client_behavior.did_tag_only_base64 = 1;
return;
}

/* if we haven't returned yet there's some odd stuff in there */
xsyslog(LOG_INFO, "saw an unusual tag", "tag=<%s>", tag);

/* inclusive checks */
if ((saw & TAG_SAW_NUM)) {
client_behavior.did_tag_num = 1;
}

if ((saw & TAG_SAW_ALPHA)) {
client_behavior.did_tag_alpha = 1;
}

if ((saw & TAG_SAW_DOT)) {
client_behavior.did_tag_dot = 1;
}

if ((saw & TAG_SAW_SEP)) {
client_behavior.did_tag_sep = 1;
}

if ((saw & TAG_SAW_COLON)) {
client_behavior.did_tag_colon = 1;
}

if ((saw & TAG_SAW_ANGLE)) {
client_behavior.did_tag_angle = 1;
}

if ((saw & TAG_SAW_B64EXTRA)) {
client_behavior.did_tag_base64 = 1;
}

if ((saw & TAG_SAW_OTHER)) {
client_behavior.did_tag_other = 1;
}
}

#define IS_EOL(c, pin) ((c = (c == '\r') ? prot_getc(pin) : c) == '\n')

/*
Expand Down Expand Up @@ -1590,10 +1734,12 @@ static void cmdloop(void)
goto done;
}
if (c != ' ' || !imparse_isatom(tag.s) || (tag.s[0] == '*' && !tag.s[1])) {
client_behavior.did_tag_inv = 1;
prot_printf(imapd_out, "* BAD Invalid tag\r\n");
eatline(imapd_in, c);
continue;
}
record_client_tag_behaviour(buf_cstring(&tag));

/* Parse command name */
c = getword(imapd_in, &cmd);
Expand Down
19 changes: 19 additions & 0 deletions imap/imapd.h
Original file line number Diff line number Diff line change
Expand Up @@ -474,6 +474,25 @@ struct client_behavior_registry {

/* non-standard - track for possible deprecation */
uint32_t did_xlist : 1; /* used XLIST */

/* what kinds of tags is this client using? */
uint32_t did_tag_only_num : 1; /* only numbers */
uint32_t did_tag_only_alpha : 1; /* only letters */
uint32_t did_tag_only_alnum : 1; /* only numbers and letters */
uint32_t did_tag_only_alnumdot : 1; /* only numbers, letters, and dots */
uint32_t did_tag_only_base64 : 1; /* only base64 characters */
uint32_t did_tag_only_onedot : 1; /* single dot */
uint32_t did_tag_num : 1; /* tags contain numbers */
uint32_t did_tag_alpha : 1; /* tags contain letters */
uint32_t did_tag_dot : 1; /* tags contain dots */
uint32_t did_tag_sep : 1; /* tags contain - or _ */
uint32_t did_tag_colon : 1; /* tags contain : */
uint32_t did_tag_angle : 1; /* tags contain < or > */
uint32_t did_tag_base64 : 1; /* tags contains base64 punc characters */
uint32_t did_tag_other : 1; /* tags contains other characters */
uint32_t did_tag_POST : 1; /* used string "POST" as a tag */
uint32_t did_tag_PUT : 1; /* used string "PUT" as a tag */
uint32_t did_tag_inv : 1; /* we rejected an invalid tag */
};

#endif /* INCLUDED_IMAPD_H */