Commit 4eebae08 authored by Rafal Somla's avatar Rafal Somla

BUG#11879051: FIRST REPLY LENGTH LIMIT (255) CAN BE VIOLATED

BEFORE: First packet sent by client-side plugin (generated by Windows
function InitializeSecurityContext()) could be longer than 255 bytes 
violating the limitation imposed by authentication protocol.

AFTER: Handshake protocol is  changed so that if first client's reply is 
longer than 254 bytes then  it is be sent in 2 parts. However, for replies
shorter than 255 bytes nothing changes.

ADDITIONAL CHANGES: 
- The generic packet processing loop  (Handshake::packet_processing_loop) 
has been refactored. Communication with the peer has been abstracted
into virtual methods read/write_packet() which are implemented in client 
and server and transparently do the required splitting and gluing of packets.
- Make it possible to optionally use dbug library in the plugin.
- Add code for testing splitting of long first client reply.
parent c8e48ac3
......@@ -19,6 +19,8 @@
ADD_DEFINITIONS(-DSECURITY_WIN32)
ADD_DEFINITIONS(-DDEBUG_ERRROR_LOG) # no error logging in production builds
ADD_DEFINITIONS(-DWINAUTH_USE_DBUG_LIB) # it is OK to use dbug library in statically
# linked plugin
SET(HEADERS common.h handshake.h)
SET(PLUGIN_SOURCES plugin_client.cc handshake_client.cc log_client.cc common.cc handshake.cc)
......
......@@ -41,54 +41,65 @@ struct error_log_level
typedef enum {INFO, WARNING, ERROR} type;
};
#undef DBUG_ASSERT
#ifndef DBUG_OFF
#define DBUG_ASSERT(X) assert(X)
/*
If DEBUG_ERROR_LOG is defined then error logging happens only
in debug-copiled code. Otherwise ERROR_LOG() expands to
error_log_print() even in production code. Note that in client
plugin, error_log_print() will print nothing if opt_auth_win_clinet_log
is 0.
Note: Macro ERROR_LOG() can use printf-like format string like this:
ERROR_LOG(Level, ("format string", args));
The implementation should handle it correctly. Currently it is passed
to fprintf() (see error_log_vprint() function).
*/
extern "C" int opt_auth_win_client_log;
#if defined(DEBUG_ERROR_LOG) && defined(DBUG_OFF)
#define ERROR_LOG(Level, Msg) do {} while (0)
#else
#define DBUG_ASSERT(X) do {} while (0)
#define ERROR_LOG(Level, Msg) error_log_print< error_log_level::Level > Msg
#endif
extern "C" int opt_auth_win_client_log;
/*
Note1: Double level of indirection in definition of DBUG_PRINT allows to
temporary redefine or disable DBUG_PRINT macro and then easily return to
the original definition (in terms of DBUG_PRINT_DO).
void error_log_vprint(error_log_level::type level,
const char *fmt, va_list args);
Note2: DBUG_PRINT() can use printf-like format string like this:
template <error_log_level::type Level>
void error_log_print(const char *fmt, ...)
{
va_list args;
va_start(args, fmt);
error_log_vprint(Level, fmt, args);
va_end(args);
}
DBUG_PRINT(Keyword, ("format string", args));
typedef char Error_message_buf[1024];
const char* get_last_error_message(Error_message_buf);
The implementation should handle it correctly. Currently it is passed
to fprintf() (see debug_msg() function).
/*
Internal implementation of debug message printing which does not use
dbug library. This is invoked via macro:
DBUG_PRINT_DO(Keyword, ("format string", args));
This is supposed to be used as an implementation of DBUG_PRINT() macro,
unless the dbug library implementation is used or debug messages are disabled.
*/
#ifndef DBUG_OFF
#define DBUG_PRINT_DO(Keyword, Msg) \
do { \
if (2 > opt_auth_win_client_log) break; \
fprintf(stderr, "winauth: %s: ", Keyword); \
debug_msg Msg; \
} while (0)
#else
#define DBUG_PRINT_DO(K, M) do {} while (0)
#endif
#undef DBUG_PRINT
#define DBUG_PRINT(Keyword, Msg) DBUG_PRINT_DO(Keyword, Msg)
/*
If DEBUG_ERROR_LOG is defined then error logging happens only
in debug-copiled code. Otherwise ERROR_LOG() expands to
error_log_print() even in production code. Note that in client
plugin, error_log_print() will print nothing if opt_auth_win_clinet_log
is 0.
*/
#if defined(DEBUG_ERROR_LOG) && defined(DBUG_OFF)
#define ERROR_LOG(Level, Msg) do {} while (0)
#else
#define ERROR_LOG(Level, Msg) error_log_print< error_log_level::Level > Msg
#endif
inline
void debug_msg(const char *fmt, ...)
......@@ -101,21 +112,38 @@ void debug_msg(const char *fmt, ...)
va_end(args);
}
#else
#define DBUG_PRINT_DO(K, M) do {} while (0)
#endif
void error_log_vprint(error_log_level::type level,
const char *fmt, va_list args);
template <error_log_level::type Level>
void error_log_print(const char *fmt, ...)
{
va_list args;
va_start(args, fmt);
error_log_vprint(Level, fmt, args);
va_end(args);
}
#ifndef WINAUTH_USE_DBUG_LIB
typedef char Error_message_buf[1024];
const char* get_last_error_message(Error_message_buf);
#undef DBUG_PRINT
#define DBUG_PRINT(Keyword, Msg) DBUG_PRINT_DO(Keyword, Msg)
/*
Redefine few more debug macros to make sure that no symbols from
dbug library are used.
*/
#undef DBUG_ENTER
#define DBUG_ENTER(X) do {} while (0)
#undef DBUG_RETURN
#define DBUG_RETURN(X) return (X)
#undef DBUG_ASSERT
#ifndef DBUG_OFF
#define DBUG_ASSERT(X) assert (X)
#else
#define DBUG_ASSERT(X) do {} while (0)
#endif
#undef DBUG_DUMP
#define DBUG_DUMP(A,B,C) do {} while (0)
#endif
/** Blob class *************************************************************/
......@@ -158,15 +186,21 @@ public:
return m_len;
}
byte operator[](unsigned pos) const
byte& operator[](unsigned pos) const
{
return pos < len() ? m_ptr[pos] : 0x00;
static byte out_of_range= 0; // alas, no exceptions...
return pos < len() ? m_ptr[pos] : out_of_range;
}
bool is_null() const
{
return m_ptr == NULL;
}
void trim(size_t l)
{
m_len= l;
}
};
......
......@@ -90,17 +90,19 @@ Handshake::~Handshake()
@note In case of error, appropriate error message is logged.
*/
int Handshake::packet_processing_loop(Connection &con)
int Handshake::packet_processing_loop()
{
unsigned round= 1;
m_round= 0;
do {
++m_round;
// Read packet send by the peer
DBUG_PRINT("info", ("Waiting for packet"));
Blob packet= con.read();
if (con.error() || packet.is_null())
Blob packet= read_packet();
if (error())
{
ERROR_LOG(ERROR, ("Error reading packet in round %d", round));
ERROR_LOG(ERROR, ("Error reading packet in round %d", m_round));
return 1;
}
DBUG_PRINT("info", ("Got packet of length %d", packet.len()));
......@@ -113,7 +115,7 @@ int Handshake::packet_processing_loop(Connection &con)
if (error())
{
ERROR_LOG(ERROR, ("Error processing packet in round %d", round));
ERROR_LOG(ERROR, ("Error processing packet in round %d", m_round));
return 1;
}
......@@ -124,14 +126,13 @@ int Handshake::packet_processing_loop(Connection &con)
if (!new_data.is_null())
{
++round;
DBUG_PRINT("info", ("Round %d started", round));
DBUG_PRINT("info", ("Round %d started", m_round));
DBUG_PRINT("info", ("Sending packet of length %d", new_data.len()));
int ret= con.write(new_data);
int ret= write_packet(new_data);
if (ret)
{
ERROR_LOG(ERROR, ("Error writing packet in round %d", round));
ERROR_LOG(ERROR, ("Error writing packet in round %d", m_round));
return 1;
}
DBUG_PRINT("info", ("Data sent"));
......@@ -139,7 +140,7 @@ int Handshake::packet_processing_loop(Connection &con)
else if (!is_complete())
{
ERROR_LOG(ERROR, ("No data to send in round %d"
" but handshake is not complete", round));
" but handshake is not complete", m_round));
return 1;
}
......@@ -148,16 +149,16 @@ int Handshake::packet_processing_loop(Connection &con)
too many rounds.
*/
if (round > MAX_HANDSHAKE_ROUNDS)
if (m_round > MAX_HANDSHAKE_ROUNDS)
{
ERROR_LOG(ERROR, ("Authentication handshake could not be completed"
" after %d rounds", round));
" after %d rounds", m_round));
return 1;
}
} while(!is_complete());
ERROR_LOG(INFO, ("Handshake completed after %d rounds", round));
ERROR_LOG(INFO, ("Handshake completed after %d rounds", m_round));
return 0;
}
......
......@@ -100,7 +100,7 @@ public:
Handshake(const char *ssp, side_t side);
virtual ~Handshake();
int Handshake::packet_processing_loop(Connection &con);
int Handshake::packet_processing_loop();
bool virtual is_complete() const
{
......@@ -126,6 +126,13 @@ protected:
/// Stores attributes of the created security context.
ULONG m_atts;
/**
Round of the handshake (starting from round 1). One round
consist of reading packet from the other side, processing it and
optionally sending a reply (see @c packet_processing_loop()).
*/
unsigned int m_round;
/// If non-zero, stores error code of the last failed operation.
int m_error;
......@@ -152,7 +159,13 @@ protected:
@return A blob with data to be sent to the other end or null blob if
no more data needs to be exchanged.
*/
virtual Blob process_data(const Blob &data)= 0;
virtual Blob process_data(const Blob &data) =0;
/// Read packet from the other end.
virtual Blob read_packet() =0;
/// Write packet to the other end.
virtual int write_packet(Blob &data) =0;
#ifndef DBUG_OFF
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment