struct event *params_event;
struct imap_id_params *params;
string_t *log_reply;
+ /* Number of external (non internal x-*) key/value pairs accounted so
+ far. Used to bound the pre-login accounting (logging, event fields,
+ client_id) done per external pair. */
+ unsigned int processed_pairs_count;
};
struct imap_client {
#include "imap-login-settings.h"
#include "imap-login-client.h"
+/* RFC 2971 says a client SHOULD NOT send more than 30 field-value pairs.
+ Allow that many to be accounted for (logged / kept in client_id); pairs
+ beyond this are still parsed and internal x-* handlers still run, but they
+ are not added to the logging/client_id accounting that grows per pair. */
+#define IMAP_ID_MAX_ACCOUNTED_PAIRS 30
+
struct imap_id_params_forward {
const char *key;
const char *value;
imap_id_param_handler_find(key);
bool is_login_id_param = handler != NULL;
+ /* Only external pairs are logged / kept in client_id, and only those
+ grow per-pair with count, so only they need bounding to avoid a
+ pre-login CPU/memory DoS. Internal x-* handlers must keep running
+ regardless (e.g. a proxy may legitimately forward more than 30
+ fields) and are not counted against the limit. */
+ bool account_pair = !is_login_id_param &&
+ client->cmd_id->processed_pairs_count < IMAP_ID_MAX_ACCOUNTED_PAIRS;
+ if (account_pair)
+ client->cmd_id->processed_pairs_count++;
+
if (!is_login_id_param) {
/* not an internal key */
} else if (client->id_logged) {
}
}
- if (client->set->imap_id_retain && !is_login_id_param &&
+ if (account_pair && client->set->imap_id_retain &&
(client->common.client_id == NULL ||
str_len(client->common.client_id) + kvlen < LOGIN_MAX_CLIENT_ID_LEN)) {
if (client->common.client_id == NULL) {
imap_append_quoted(client->common.client_id, value);
}
- if (!is_login_id_param)
+ if (account_pair)
imap_id_add_log_entry(log_entry, key, value);
return TRUE;
}