Harden IMAP and POP TLS state handling
Debian Trixie package bundle / packages (push) Successful in 20m44s
Debian Trixie package bundle / packages (push) Successful in 20m44s
This commit is contained in:
@@ -37,6 +37,10 @@ ad-hoc downgrade.
|
||||
AUTH capabilities are generated from the GNU GSASL mechanisms which Bongo
|
||||
has actually enabled. Password mechanisms are advertised only on a TLS
|
||||
connection; unencrypted connections offer STARTTLS and keep LOGIN disabled.
|
||||
STARTTLS rejects arguments and repeated or post-authentication use, discards
|
||||
already buffered cleartext before the TLS handshake, and closes the connection
|
||||
without attempting a plaintext IMAP response if negotiation fails. A client
|
||||
which ignores `LOGINDISABLED` receives a tagged `NO` without a password lookup.
|
||||
|
||||
Relevant standards are [RFC 9051](https://www.rfc-editor.org/rfc/rfc9051.html),
|
||||
[RFC 3501](https://www.rfc-editor.org/rfc/rfc3501.html),
|
||||
|
||||
@@ -34,6 +34,10 @@ POP3 implements the normal transaction commands and advertises `TOP`,
|
||||
`RESP-CODES`, `AUTH-RESP-CODE`, `PIPELINING`, `EXPIRE NEVER`, `UIDL`, and
|
||||
`UTF8`. STLS is offered before authentication; SASL and password login are
|
||||
advertised only according to the TLS policy. POP3S provides implicit TLS.
|
||||
After a successful `UTF8` command, `STLS` is no longer advertised and is
|
||||
rejected so that the negotiated UTF-8 session state cannot cross into TLS.
|
||||
Both IMAP STARTTLS and POP3 STLS discard buffered cleartext before negotiating
|
||||
TLS.
|
||||
|
||||
## Server-side filtering
|
||||
|
||||
|
||||
@@ -154,7 +154,7 @@ inputs and outputs is absent from the ZIP.
|
||||
| ID | Test | R1 | R2 | R3 |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| IMAP-01 | Capability list matches implemented rev2/rev1/legacy behaviour | | | |
|
||||
| IMAP-02 | Port 143 STARTTLS and pre-TLS `LOGINDISABLED` | | | |
|
||||
| IMAP-02 | Port 143 STARTTLS, pre-TLS `LOGINDISABLED`, tagged `NO` for plaintext LOGIN, repeated STARTTLS rejection | | | |
|
||||
| IMAP-03 | Port 993 TLS, LOGIN, SASL PLAIN, and SASL LOGIN | | | |
|
||||
| IMAP-04 | CAPABILITY, NOOP, LOGOUT, ID, and NAMESPACE | | | |
|
||||
| IMAP-05 | LIST, LSUB, extended LIST, LIST-STATUS, hierarchy quoting | | | |
|
||||
@@ -171,18 +171,18 @@ inputs and outputs is absent from the ZIP.
|
||||
| IMAP-16 | ENABLE UTF8=ACCEPT and UTF-8 mailbox/message names | | | |
|
||||
| IMAP-17 | SPECIAL-USE and CREATE-SPECIAL-USE folders | | | |
|
||||
| IMAP-18 | Old-client plain IMAP4 subset without rev1/rev2 assumptions | | | |
|
||||
| IMAP-19 | Invalid states, malformed sets/literals, oversized/pipelined input | | | |
|
||||
| IMAP-19 | Invalid states, malformed sets/literals, oversized input, and STARTTLS plaintext-pipeline injection resistance | | | |
|
||||
| IMAP-20 | Reconnect, simultaneous clients, restart, mailbox consistency | | | |
|
||||
|
||||
## POP3 and ManageSieve
|
||||
|
||||
| ID | Test | R1 | R2 | R3 |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| POP-01 | Port 110 CAPA/STLS; credentials unavailable before TLS | | | |
|
||||
| POP-01 | Port 110 CAPA/STLS; credentials unavailable before TLS; buffered plaintext discarded | | | |
|
||||
| POP-02 | Port 995 TLS with USER/PASS and shared GSASL mechanisms | | | |
|
||||
| POP-03 | STAT, LIST, UIDL, RETR, TOP, and dot transparency | | | |
|
||||
| POP-04 | DELE, RSET, NOOP, QUIT commit, and disconnect rollback | | | |
|
||||
| POP-05 | UTF8, PIPELINING, response codes, malformed input, timeout | | | |
|
||||
| POP-05 | UTF8, STLS rejection after UTF8, PIPELINING, response codes, malformed input, timeout | | | |
|
||||
| SIEVE-01 | Port 4190 greeting, STARTTLS, and GSASL authentication | | | |
|
||||
| SIEVE-02 | CAPABILITY, HAVESPACE, PUT/CHECK/LIST/GETSCRIPT | | | |
|
||||
| SIEVE-03 | SETACTIVE, RENAME, DELETE, LOGOUT, and persistence | | | |
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
add_executable(bongoimap
|
||||
imapd.c
|
||||
capabilities.c
|
||||
protocol.c
|
||||
enable.c
|
||||
utf8-mailbox.c
|
||||
append-message.c
|
||||
@@ -46,6 +47,15 @@ if(BUILD_TESTING)
|
||||
)
|
||||
add_test(NAME imap-capabilities COMMAND imap-capabilities-test)
|
||||
|
||||
add_executable(imap-protocol-test
|
||||
tests/protocol-test.c
|
||||
protocol.c
|
||||
)
|
||||
target_include_directories(imap-protocol-test PRIVATE
|
||||
${CMAKE_CURRENT_SOURCE_DIR}
|
||||
)
|
||||
add_test(NAME imap-protocol COMMAND imap-protocol-test)
|
||||
|
||||
add_executable(imap-enable-test
|
||||
tests/enable-test.c
|
||||
enable.c
|
||||
|
||||
@@ -98,6 +98,8 @@ typedef enum {
|
||||
STATUS_INVALID_ARGUMENT,
|
||||
STATUS_INVALID_STATE,
|
||||
STATUS_UNKNOWN_COMMAND,
|
||||
STATUS_LOGIN_DISABLED,
|
||||
STATUS_TLS_UNAVAILABLE,
|
||||
STATUS_TLS_NEGOTIATION_FAILURE,
|
||||
STATUS_CHARSET_NOT_SUPPORTED,
|
||||
STATUS_UID_NOT_FOUND,
|
||||
@@ -156,6 +158,8 @@ static ImapErrorString ImapErrorStrings[] = {
|
||||
{ STATUS_INVALID_ARGUMENT, "%s BAD %s invalid arguments\r\n" },
|
||||
{ STATUS_INVALID_STATE, "%s BAD %s invalid command in current state\r\n" },
|
||||
{ STATUS_UNKNOWN_COMMAND, "* BAD command unrecognized\r\n" },
|
||||
{ STATUS_LOGIN_DISABLED, "%s NO %s plaintext LOGIN disabled; use STARTTLS\r\n" },
|
||||
{ STATUS_TLS_UNAVAILABLE, "%s NO %s TLS unavailable\r\n" },
|
||||
{ STATUS_TLS_NEGOTIATION_FAILURE, "%s BAD %s negotiation failed\r\n" },
|
||||
{ 0, NULL }
|
||||
};
|
||||
|
||||
@@ -0,0 +1,57 @@
|
||||
/****************************************************************************
|
||||
* <Novell-copyright>
|
||||
* Copyright (c) 2001 Novell, Inc. All Rights Reserved.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of version 2 of the GNU General Public License
|
||||
* as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, contact Novell, Inc.
|
||||
*
|
||||
* To contact Novell about this file by physical or electronic mail, you
|
||||
* may find current contact information at www.novell.com.
|
||||
* </Novell-copyright>
|
||||
****************************************************************************/
|
||||
|
||||
#include "protocol.h"
|
||||
|
||||
#include <ctype.h>
|
||||
#include <string.h>
|
||||
|
||||
static int
|
||||
EqualsIgnoreCase(const char *left, const char *right)
|
||||
{
|
||||
if (!left || !right) return 0;
|
||||
while (*left && *right) {
|
||||
if (toupper((unsigned char)*left) != toupper((unsigned char)*right))
|
||||
return 0;
|
||||
left++;
|
||||
right++;
|
||||
}
|
||||
return *left == '\0' && *right == '\0';
|
||||
}
|
||||
|
||||
IMAPStartTlsStatus
|
||||
IMAPStartTlsCheck(const char *command_line, int authenticated,
|
||||
int tls_active, int tls_available)
|
||||
{
|
||||
if (!EqualsIgnoreCase(command_line, "STARTTLS"))
|
||||
return IMAP_STARTTLS_INVALID_ARGUMENTS;
|
||||
if (authenticated || tls_active) return IMAP_STARTTLS_INVALID_STATE;
|
||||
if (!tls_available) return IMAP_STARTTLS_UNAVAILABLE;
|
||||
return IMAP_STARTTLS_READY;
|
||||
}
|
||||
|
||||
IMAPLoginStatus
|
||||
IMAPLoginCheck(int authenticated, int tls_active)
|
||||
{
|
||||
if (authenticated) return IMAP_LOGIN_INVALID_STATE;
|
||||
if (!tls_active) return IMAP_LOGIN_DISABLED;
|
||||
return IMAP_LOGIN_READY;
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
/****************************************************************************
|
||||
* <Novell-copyright>
|
||||
* Copyright (c) 2001 Novell, Inc. All Rights Reserved.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of version 2 of the GNU General Public License
|
||||
* as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, contact Novell, Inc.
|
||||
*
|
||||
* To contact Novell about this file by physical or electronic mail, you
|
||||
* may find current contact information at www.novell.com.
|
||||
* </Novell-copyright>
|
||||
****************************************************************************/
|
||||
|
||||
#ifndef BONGO_IMAP_PROTOCOL_H
|
||||
#define BONGO_IMAP_PROTOCOL_H
|
||||
|
||||
typedef enum {
|
||||
IMAP_STARTTLS_READY = 0,
|
||||
IMAP_STARTTLS_INVALID_ARGUMENTS,
|
||||
IMAP_STARTTLS_INVALID_STATE,
|
||||
IMAP_STARTTLS_UNAVAILABLE
|
||||
} IMAPStartTlsStatus;
|
||||
|
||||
typedef enum {
|
||||
IMAP_LOGIN_READY = 0,
|
||||
IMAP_LOGIN_DISABLED,
|
||||
IMAP_LOGIN_INVALID_STATE
|
||||
} IMAPLoginStatus;
|
||||
|
||||
IMAPStartTlsStatus IMAPStartTlsCheck(const char *command_line,
|
||||
int authenticated, int tls_active,
|
||||
int tls_available);
|
||||
IMAPLoginStatus IMAPLoginCheck(int authenticated, int tls_active);
|
||||
|
||||
#endif
|
||||
+49
-17
@@ -29,6 +29,7 @@
|
||||
#include <bongosasl.h>
|
||||
|
||||
#include "imapd.h"
|
||||
#include "protocol.h"
|
||||
|
||||
void
|
||||
ImapAnnotateAliasClient(ImapSession *session)
|
||||
@@ -298,10 +299,20 @@ ImapCommandLogin(void *param)
|
||||
{
|
||||
ImapSession *session = (ImapSession *)param;
|
||||
long ccode;
|
||||
IMAPLoginStatus status;
|
||||
char *username = NULL;
|
||||
char *password = NULL;
|
||||
|
||||
if (session->client.state == STATE_FRESH && session->client.conn->ssl.enable) {
|
||||
status = IMAPLoginCheck(session->client.state >= STATE_AUTH,
|
||||
session->client.conn->ssl.enable);
|
||||
if (status == IMAP_LOGIN_DISABLED)
|
||||
return(SendError(session->client.conn, session->command.tag, "LOGIN",
|
||||
STATUS_LOGIN_DISABLED));
|
||||
if (status == IMAP_LOGIN_INVALID_STATE)
|
||||
return(SendError(session->client.conn, session->command.tag, "LOGIN",
|
||||
STATUS_INVALID_STATE));
|
||||
|
||||
if (status == IMAP_LOGIN_READY) {
|
||||
ccode = GrabTwoArguments(session, session->command.buffer + 6, &username, &password);
|
||||
if (ccode == STATUS_CONTINUE) {
|
||||
X500Encode(username);
|
||||
@@ -335,27 +346,48 @@ int
|
||||
ImapCommandStartTls(void *param)
|
||||
{
|
||||
long ccode;
|
||||
IMAPStartTlsStatus status;
|
||||
|
||||
ImapSession *session = (ImapSession *)param;
|
||||
|
||||
if (session->client.state < STATE_AUTH) {
|
||||
if (Imap.server.ssl.config.options) {
|
||||
if (((ccode = ConnWriteF(session->client.conn, "%s OK Begin TLS negotiation\r\n",session->command.tag)) != -1) && (ccode = ConnFlush(session->client.conn))) {
|
||||
session->client.conn->ssl.enable = TRUE;
|
||||
if (ConnNegotiate(session->client.conn, Imap.server.ssl.context)) {
|
||||
return(STATUS_CONTINUE);
|
||||
}
|
||||
status = IMAPStartTlsCheck(session->command.buffer,
|
||||
session->client.state >= STATE_AUTH,
|
||||
session->client.conn->ssl.enable,
|
||||
Imap.server.ssl.context != NULL);
|
||||
if (status == IMAP_STARTTLS_INVALID_ARGUMENTS)
|
||||
return(SendError(session->client.conn, session->command.tag,
|
||||
"STARTTLS", STATUS_INVALID_ARGUMENT));
|
||||
if (status == IMAP_STARTTLS_INVALID_STATE)
|
||||
return(SendError(session->client.conn, session->command.tag,
|
||||
"STARTTLS", STATUS_INVALID_STATE));
|
||||
if (status == IMAP_STARTTLS_UNAVAILABLE)
|
||||
return(SendError(session->client.conn, session->command.tag,
|
||||
"STARTTLS", STATUS_TLS_UNAVAILABLE));
|
||||
|
||||
return(SendError(session->client.conn, session->command.tag, "STARTTLS", STATUS_TLS_NEGOTIATION_FAILURE));
|
||||
}
|
||||
ccode = ConnWriteF(session->client.conn,
|
||||
"%s OK Begin TLS negotiation\r\n",
|
||||
session->command.tag);
|
||||
if (ccode == -1 || ConnFlush(session->client.conn) == -1)
|
||||
return(STATUS_ABORT);
|
||||
|
||||
return(STATUS_ABORT);
|
||||
}
|
||||
|
||||
return(SendError(session->client.conn, session->command.tag, "", STATUS_UNKNOWN_COMMAND));
|
||||
}
|
||||
|
||||
return(SendError(session->client.conn, session->command.tag, "STARTTLS", STATUS_INVALID_STATE));
|
||||
/*
|
||||
* Never carry pipelined cleartext across STARTTLS. RFC 9051 requires
|
||||
* extra bytes already received after the command to become TLS input or
|
||||
* to be discarded. Discarding them avoids plaintext command injection
|
||||
* and matches the POP3 STLS path.
|
||||
*/
|
||||
session->client.conn->receive.read =
|
||||
session->client.conn->receive.write =
|
||||
session->client.conn->receive.buffer;
|
||||
session->client.conn->receive.remaining = CONN_TCP_MTU;
|
||||
session->client.conn->receive.buffer[0] = '\0';
|
||||
session->client.conn->ssl.enable = TRUE;
|
||||
session->client.enabledCapabilities = 0;
|
||||
|
||||
if (ConnNegotiate(session->client.conn, Imap.server.ssl.context))
|
||||
return(STATUS_CONTINUE);
|
||||
|
||||
/* TLS bytes may already have been exchanged; no IMAP response is safe. */
|
||||
ConnClose(session->client.conn);
|
||||
return(STATUS_ABORT);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
/****************************************************************************
|
||||
* <Novell-copyright>
|
||||
* Copyright (c) 2001 Novell, Inc. All Rights Reserved.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of version 2 of the GNU General Public License
|
||||
* as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, contact Novell, Inc.
|
||||
*
|
||||
* To contact Novell about this file by physical or electronic mail, you
|
||||
* may find current contact information at www.novell.com.
|
||||
* </Novell-copyright>
|
||||
****************************************************************************/
|
||||
|
||||
#include "protocol.h"
|
||||
|
||||
#include <assert.h>
|
||||
|
||||
int
|
||||
main(void)
|
||||
{
|
||||
assert(IMAPStartTlsCheck("STARTTLS", 0, 0, 1) == IMAP_STARTTLS_READY);
|
||||
assert(IMAPStartTlsCheck("starttls", 0, 0, 1) == IMAP_STARTTLS_READY);
|
||||
assert(IMAPStartTlsCheck("STARTTLS ", 0, 0, 1) ==
|
||||
IMAP_STARTTLS_INVALID_ARGUMENTS);
|
||||
assert(IMAPStartTlsCheck("STARTTLS extra", 0, 0, 1) ==
|
||||
IMAP_STARTTLS_INVALID_ARGUMENTS);
|
||||
assert(IMAPStartTlsCheck("STARTTLS", 1, 0, 1) ==
|
||||
IMAP_STARTTLS_INVALID_STATE);
|
||||
assert(IMAPStartTlsCheck("STARTTLS", 0, 1, 1) ==
|
||||
IMAP_STARTTLS_INVALID_STATE);
|
||||
assert(IMAPStartTlsCheck("STARTTLS", 0, 0, 0) ==
|
||||
IMAP_STARTTLS_UNAVAILABLE);
|
||||
assert(IMAPLoginCheck(0, 1) == IMAP_LOGIN_READY);
|
||||
assert(IMAPLoginCheck(0, 0) == IMAP_LOGIN_DISABLED);
|
||||
assert(IMAPLoginCheck(1, 1) == IMAP_LOGIN_INVALID_STATE);
|
||||
assert(IMAPLoginCheck(1, 0) == IMAP_LOGIN_INVALID_STATE);
|
||||
return 0;
|
||||
}
|
||||
@@ -47,6 +47,7 @@ Append(char *buffer, size_t buffer_size, size_t *length,
|
||||
size_t
|
||||
POP3FormatCapabilities(char *buffer, size_t buffer_size,
|
||||
int authorization_state, int tls_active,
|
||||
int utf8_active,
|
||||
int stls_available,
|
||||
const char *sasl_mechanisms)
|
||||
{
|
||||
@@ -63,7 +64,8 @@ POP3FormatCapabilities(char *buffer, size_t buffer_size,
|
||||
sasl_mechanisms)) ||
|
||||
(authorization_state && tls_active &&
|
||||
!Append(buffer, buffer_size, &length, "USER\r\n")) ||
|
||||
(authorization_state && !tls_active && stls_available &&
|
||||
(authorization_state && !tls_active && !utf8_active &&
|
||||
stls_available &&
|
||||
!Append(buffer, buffer_size, &length, "STLS\r\n")) ||
|
||||
!Append(buffer, buffer_size, &length, ".\r\n"))
|
||||
return 0;
|
||||
|
||||
@@ -27,6 +27,7 @@
|
||||
/* Returns the number of bytes written, or 0 if the buffer was too small. */
|
||||
size_t POP3FormatCapabilities(char *buffer, size_t buffer_size,
|
||||
int authorization_state, int tls_active,
|
||||
int utf8_active,
|
||||
int stls_available,
|
||||
const char *sasl_mechanisms);
|
||||
|
||||
|
||||
+13
-12
@@ -1136,7 +1136,7 @@ POP3CommandCapa(void *param)
|
||||
}
|
||||
capabilityLength = POP3FormatCapabilities(
|
||||
client->buffer, sizeof(client->buffer), authorizationState,
|
||||
client->conn->ssl.enable,
|
||||
client->conn->ssl.enable, client->utf8Mode,
|
||||
POP3.server.ssl.enable && POP3.server.ssl.context != NULL, mechanisms);
|
||||
ccode = capabilityLength
|
||||
? ConnWrite(client->conn, client->buffer, capabilityLength)
|
||||
@@ -1533,6 +1533,7 @@ POP3CommandSTLS(void *param)
|
||||
{
|
||||
int ccode;
|
||||
BOOL result;
|
||||
POP3STLSStatus status;
|
||||
POP3Client *client = (POP3Client *)param;
|
||||
|
||||
if (!POP3ArgumentsEmpty(client->command +
|
||||
@@ -1544,19 +1545,19 @@ POP3CommandSTLS(void *param)
|
||||
return ConnWrite(client->conn, MSGERRSYNTAX,
|
||||
sizeof(MSGERRSYNTAX) - 1);
|
||||
|
||||
if ((client->state == POP3_CLIENT_FRESH ||
|
||||
client->state == POP3_CLIENT_AUTHORIZATION) &&
|
||||
!client->conn->ssl.enable) {
|
||||
if (POP3.server.ssl.enable && POP3.server.ssl.context) {
|
||||
ccode = ConnWrite(client->conn, MSGOKSTARTTLS, sizeof(MSGOKSTARTTLS) - 1);
|
||||
} else {
|
||||
return ConnWrite(client->conn,
|
||||
"-ERR [SYS/PERM] TLS unavailable\r\n",
|
||||
sizeof("-ERR [SYS/PERM] TLS unavailable\r\n") - 1);
|
||||
}
|
||||
} else {
|
||||
status = POP3STLSCheck(client->state == POP3_CLIENT_FRESH ||
|
||||
client->state == POP3_CLIENT_AUTHORIZATION,
|
||||
client->conn->ssl.enable, client->utf8Mode,
|
||||
POP3.server.ssl.enable && POP3.server.ssl.context);
|
||||
if (status == POP3_STLS_INVALID_STATE) {
|
||||
return(ConnWrite(client->conn, MSGERRBADSTATE, sizeof(MSGERRBADSTATE) - 1));
|
||||
}
|
||||
if (status == POP3_STLS_UNAVAILABLE)
|
||||
return ConnWrite(client->conn,
|
||||
"-ERR [SYS/PERM] TLS unavailable\r\n",
|
||||
sizeof("-ERR [SYS/PERM] TLS unavailable\r\n") - 1);
|
||||
|
||||
ccode = ConnWrite(client->conn, MSGOKSTARTTLS, sizeof(MSGOKSTARTTLS) - 1);
|
||||
|
||||
if (ccode != -1) {
|
||||
ccode = ConnFlush(client->conn);
|
||||
|
||||
@@ -57,6 +57,16 @@ POP3ArgumentsEmpty(const char *arguments)
|
||||
return arguments && *arguments == '\0';
|
||||
}
|
||||
|
||||
POP3STLSStatus
|
||||
POP3STLSCheck(int authorization_state, int tls_active, int utf8_active,
|
||||
int tls_available)
|
||||
{
|
||||
if (!authorization_state || tls_active || utf8_active)
|
||||
return POP3_STLS_INVALID_STATE;
|
||||
if (!tls_available) return POP3_STLS_UNAVAILABLE;
|
||||
return POP3_STLS_READY;
|
||||
}
|
||||
|
||||
int
|
||||
POP3ParseMessageNumber(const char *arguments, uint32_t *message_number)
|
||||
{
|
||||
|
||||
@@ -35,10 +35,18 @@ typedef struct {
|
||||
int ends_in_cr;
|
||||
} POP3MultilineWriter;
|
||||
|
||||
typedef enum {
|
||||
POP3_STLS_READY = 0,
|
||||
POP3_STLS_INVALID_STATE,
|
||||
POP3_STLS_UNAVAILABLE
|
||||
} POP3STLSStatus;
|
||||
|
||||
int POP3Contains8Bit(const void *data, size_t length);
|
||||
int POP3UTF8Valid(const void *data, size_t length);
|
||||
|
||||
int POP3ArgumentsEmpty(const char *arguments);
|
||||
POP3STLSStatus POP3STLSCheck(int authorization_state, int tls_active,
|
||||
int utf8_active, int tls_available);
|
||||
int POP3ParseMessageNumber(const char *arguments, uint32_t *message_number);
|
||||
int POP3ParseTopArguments(const char *arguments, uint32_t *message_number,
|
||||
uint32_t *line_count);
|
||||
|
||||
@@ -30,29 +30,35 @@ main(void)
|
||||
char buffer[1024];
|
||||
char small[8];
|
||||
|
||||
assert(POP3FormatCapabilities(buffer, sizeof(buffer), 1, 0, 0, NULL) > 0);
|
||||
assert(POP3FormatCapabilities(buffer, sizeof(buffer), 1, 0, 0, 0,
|
||||
NULL) > 0);
|
||||
assert(!strstr(buffer, "STLS"));
|
||||
assert(!strstr(buffer, "SASL"));
|
||||
assert(!strstr(buffer, "USER"));
|
||||
assert(strstr(buffer, "UTF8\r\n"));
|
||||
|
||||
assert(POP3FormatCapabilities(buffer, sizeof(buffer), 1, 0, 1, NULL) > 0);
|
||||
assert(POP3FormatCapabilities(buffer, sizeof(buffer), 1, 0, 0, 1,
|
||||
NULL) > 0);
|
||||
assert(strstr(buffer, "STLS\r\n"));
|
||||
assert(!strstr(buffer, "SASL"));
|
||||
|
||||
assert(POP3FormatCapabilities(buffer, sizeof(buffer), 1, 1, 1,
|
||||
assert(POP3FormatCapabilities(buffer, sizeof(buffer), 1, 1, 0, 1,
|
||||
"PLAIN LOGIN") > 0);
|
||||
assert(!strstr(buffer, "STLS"));
|
||||
assert(strstr(buffer, "SASL PLAIN LOGIN\r\n"));
|
||||
assert(strstr(buffer, "USER\r\n"));
|
||||
|
||||
assert(POP3FormatCapabilities(buffer, sizeof(buffer), 0, 1, 1,
|
||||
assert(POP3FormatCapabilities(buffer, sizeof(buffer), 0, 1, 0, 1,
|
||||
"PLAIN LOGIN") > 0);
|
||||
assert(!strstr(buffer, "STLS"));
|
||||
assert(strstr(buffer, "SASL PLAIN LOGIN\r\n"));
|
||||
assert(!strstr(buffer, "USER"));
|
||||
|
||||
assert(POP3FormatCapabilities(small, sizeof(small), 1, 1, 1,
|
||||
assert(POP3FormatCapabilities(buffer, sizeof(buffer), 1, 0, 1, 1,
|
||||
NULL) > 0);
|
||||
assert(!strstr(buffer, "STLS"));
|
||||
|
||||
assert(POP3FormatCapabilities(small, sizeof(small), 1, 1, 0, 1,
|
||||
"PLAIN LOGIN") == 0);
|
||||
assert(small[0] == '\0');
|
||||
return 0;
|
||||
|
||||
@@ -49,6 +49,11 @@ TestArguments(void)
|
||||
|
||||
assert(POP3ArgumentsEmpty(""));
|
||||
assert(!POP3ArgumentsEmpty(" "));
|
||||
assert(POP3STLSCheck(1, 0, 0, 1) == POP3_STLS_READY);
|
||||
assert(POP3STLSCheck(0, 0, 0, 1) == POP3_STLS_INVALID_STATE);
|
||||
assert(POP3STLSCheck(1, 1, 0, 1) == POP3_STLS_INVALID_STATE);
|
||||
assert(POP3STLSCheck(1, 0, 1, 1) == POP3_STLS_INVALID_STATE);
|
||||
assert(POP3STLSCheck(1, 0, 0, 0) == POP3_STLS_UNAVAILABLE);
|
||||
assert(POP3ParseMessageNumber(" 1", &message) && message == 1);
|
||||
assert(POP3ParseMessageNumber("\t4294967295", &message) &&
|
||||
message == UINT32_MAX);
|
||||
|
||||
Reference in New Issue
Block a user