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 @@ ...@@ -19,6 +19,8 @@
ADD_DEFINITIONS(-DSECURITY_WIN32) ADD_DEFINITIONS(-DSECURITY_WIN32)
ADD_DEFINITIONS(-DDEBUG_ERRROR_LOG) # no error logging in production builds 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(HEADERS common.h handshake.h)
SET(PLUGIN_SOURCES plugin_client.cc handshake_client.cc log_client.cc common.cc handshake.cc) SET(PLUGIN_SOURCES plugin_client.cc handshake_client.cc log_client.cc common.cc handshake.cc)
......
...@@ -41,54 +41,65 @@ struct error_log_level ...@@ -41,54 +41,65 @@ struct error_log_level
typedef enum {INFO, WARNING, ERROR} type; 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 #else
#define DBUG_ASSERT(X) do {} while (0) #define ERROR_LOG(Level, Msg) error_log_print< error_log_level::Level > Msg
#endif #endif
extern "C" int opt_auth_win_client_log;
/* void error_log_vprint(error_log_level::type level,
Note1: Double level of indirection in definition of DBUG_PRINT allows to const char *fmt, va_list args);
temporary redefine or disable DBUG_PRINT macro and then easily return to
the original definition (in terms of DBUG_PRINT_DO).
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 #ifndef DBUG_OFF
#define DBUG_PRINT_DO(Keyword, Msg) \ #define DBUG_PRINT_DO(Keyword, Msg) \
do { \ do { \
if (2 > opt_auth_win_client_log) break; \ if (2 > opt_auth_win_client_log) break; \
fprintf(stderr, "winauth: %s: ", Keyword); \ fprintf(stderr, "winauth: %s: ", Keyword); \
debug_msg Msg; \ debug_msg Msg; \
} while (0) } 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 inline
void debug_msg(const char *fmt, ...) void debug_msg(const char *fmt, ...)
...@@ -101,21 +112,38 @@ void debug_msg(const char *fmt, ...) ...@@ -101,21 +112,38 @@ void debug_msg(const char *fmt, ...)
va_end(args); 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> #ifndef WINAUTH_USE_DBUG_LIB
void error_log_print(const char *fmt, ...)
{
va_list args;
va_start(args, fmt);
error_log_vprint(Level, fmt, args);
va_end(args);
}
typedef char Error_message_buf[1024]; #undef DBUG_PRINT
const char* get_last_error_message(Error_message_buf); #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 *************************************************************/ /** Blob class *************************************************************/
...@@ -158,15 +186,21 @@ public: ...@@ -158,15 +186,21 @@ public:
return m_len; 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 bool is_null() const
{ {
return m_ptr == NULL; return m_ptr == NULL;
} }
void trim(size_t l)
{
m_len= l;
}
}; };
......
...@@ -90,17 +90,19 @@ Handshake::~Handshake() ...@@ -90,17 +90,19 @@ Handshake::~Handshake()
@note In case of error, appropriate error message is logged. @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 { do {
++m_round;
// Read packet send by the peer // Read packet send by the peer
DBUG_PRINT("info", ("Waiting for packet")); DBUG_PRINT("info", ("Waiting for packet"));
Blob packet= con.read(); Blob packet= read_packet();
if (con.error() || packet.is_null()) 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; return 1;
} }
DBUG_PRINT("info", ("Got packet of length %d", packet.len())); DBUG_PRINT("info", ("Got packet of length %d", packet.len()));
...@@ -113,7 +115,7 @@ int Handshake::packet_processing_loop(Connection &con) ...@@ -113,7 +115,7 @@ int Handshake::packet_processing_loop(Connection &con)
if (error()) 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; return 1;
} }
...@@ -124,14 +126,13 @@ int Handshake::packet_processing_loop(Connection &con) ...@@ -124,14 +126,13 @@ int Handshake::packet_processing_loop(Connection &con)
if (!new_data.is_null()) if (!new_data.is_null())
{ {
++round; DBUG_PRINT("info", ("Round %d started", m_round));
DBUG_PRINT("info", ("Round %d started", round));
DBUG_PRINT("info", ("Sending packet of length %d", new_data.len())); 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) 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; return 1;
} }
DBUG_PRINT("info", ("Data sent")); DBUG_PRINT("info", ("Data sent"));
...@@ -139,7 +140,7 @@ int Handshake::packet_processing_loop(Connection &con) ...@@ -139,7 +140,7 @@ int Handshake::packet_processing_loop(Connection &con)
else if (!is_complete()) else if (!is_complete())
{ {
ERROR_LOG(ERROR, ("No data to send in round %d" 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; return 1;
} }
...@@ -148,16 +149,16 @@ int Handshake::packet_processing_loop(Connection &con) ...@@ -148,16 +149,16 @@ int Handshake::packet_processing_loop(Connection &con)
too many rounds. too many rounds.
*/ */
if (round > MAX_HANDSHAKE_ROUNDS) if (m_round > MAX_HANDSHAKE_ROUNDS)
{ {
ERROR_LOG(ERROR, ("Authentication handshake could not be completed" ERROR_LOG(ERROR, ("Authentication handshake could not be completed"
" after %d rounds", round)); " after %d rounds", m_round));
return 1; return 1;
} }
} while(!is_complete()); } while(!is_complete());
ERROR_LOG(INFO, ("Handshake completed after %d rounds", round)); ERROR_LOG(INFO, ("Handshake completed after %d rounds", m_round));
return 0; return 0;
} }
......
...@@ -100,7 +100,7 @@ public: ...@@ -100,7 +100,7 @@ public:
Handshake(const char *ssp, side_t side); Handshake(const char *ssp, side_t side);
virtual ~Handshake(); virtual ~Handshake();
int Handshake::packet_processing_loop(Connection &con); int Handshake::packet_processing_loop();
bool virtual is_complete() const bool virtual is_complete() const
{ {
...@@ -126,6 +126,13 @@ protected: ...@@ -126,6 +126,13 @@ protected:
/// Stores attributes of the created security context. /// Stores attributes of the created security context.
ULONG m_atts; 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. /// If non-zero, stores error code of the last failed operation.
int m_error; int m_error;
...@@ -152,7 +159,13 @@ protected: ...@@ -152,7 +159,13 @@ protected:
@return A blob with data to be sent to the other end or null blob if @return A blob with data to be sent to the other end or null blob if
no more data needs to be exchanged. 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 #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