Skip to content

Commit ebba694

Browse files
committed
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.
1 parent 4cfc0e5 commit ebba694

File tree

5 files changed

+276
-133
lines changed

5 files changed

+276
-133
lines changed

libmysql/authentication_win/CMakeLists.txt

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,9 @@
1818
#
1919

2020
ADD_DEFINITIONS(-DSECURITY_WIN32)
21-
ADD_DEFINITIONS(-DDEBUG_ERRROR_LOG) # no error logging in production builds
21+
ADD_DEFINITIONS(-DDEBUG_ERRROR_LOG) # no error logging in production builds
22+
ADD_DEFINITIONS(-DWINAUTH_USE_DBUG_LIB) # it is OK to use dbug library in statically
23+
# linked plugin
2224

2325
SET(HEADERS common.h handshake.h)
2426
SET(PLUGIN_SOURCES plugin_client.cc handshake_client.cc log_client.cc common.cc handshake.cc)

libmysql/authentication_win/common.h

Lines changed: 80 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -41,54 +41,65 @@ struct error_log_level
4141
typedef enum {INFO, WARNING, ERROR} type;
4242
};
4343

44-
#undef DBUG_ASSERT
45-
#ifndef DBUG_OFF
46-
#define DBUG_ASSERT(X) assert(X)
44+
45+
/*
46+
If DEBUG_ERROR_LOG is defined then error logging happens only
47+
in debug-copiled code. Otherwise ERROR_LOG() expands to
48+
error_log_print() even in production code. Note that in client
49+
plugin, error_log_print() will print nothing if opt_auth_win_clinet_log
50+
is 0.
51+
52+
Note: Macro ERROR_LOG() can use printf-like format string like this:
53+
54+
ERROR_LOG(Level, ("format string", args));
55+
56+
The implementation should handle it correctly. Currently it is passed
57+
to fprintf() (see error_log_vprint() function).
58+
*/
59+
60+
extern "C" int opt_auth_win_client_log;
61+
62+
#if defined(DEBUG_ERROR_LOG) && defined(DBUG_OFF)
63+
#define ERROR_LOG(Level, Msg) do {} while (0)
4764
#else
48-
#define DBUG_ASSERT(X) do {} while (0)
65+
#define ERROR_LOG(Level, Msg) error_log_print< error_log_level::Level > Msg
4966
#endif
5067

51-
extern "C" int opt_auth_win_client_log;
5268

53-
/*
54-
Note1: Double level of indirection in definition of DBUG_PRINT allows to
55-
temporary redefine or disable DBUG_PRINT macro and then easily return to
56-
the original definition (in terms of DBUG_PRINT_DO).
69+
void error_log_vprint(error_log_level::type level,
70+
const char *fmt, va_list args);
5771

58-
Note2: DBUG_PRINT() can use printf-like format string like this:
72+
template <error_log_level::type Level>
73+
void error_log_print(const char *fmt, ...)
74+
{
75+
va_list args;
76+
va_start(args, fmt);
77+
error_log_vprint(Level, fmt, args);
78+
va_end(args);
79+
}
5980

60-
DBUG_PRINT(Keyword, ("format string", args));
81+
typedef char Error_message_buf[1024];
82+
const char* get_last_error_message(Error_message_buf);
6183

62-
The implementation should handle it correctly. Currently it is passed
63-
to fprintf() (see debug_msg() function).
84+
85+
/*
86+
Internal implementation of debug message printing which does not use
87+
dbug library. This is invoked via macro:
88+
89+
DBUG_PRINT_DO(Keyword, ("format string", args));
90+
91+
This is supposed to be used as an implementation of DBUG_PRINT() macro,
92+
unless the dbug library implementation is used or debug messages are disabled.
6493
*/
6594

6695
#ifndef DBUG_OFF
96+
6797
#define DBUG_PRINT_DO(Keyword, Msg) \
6898
do { \
6999
if (2 > opt_auth_win_client_log) break; \
70100
fprintf(stderr, "winauth: %s: ", Keyword); \
71101
debug_msg Msg; \
72102
} while (0)
73-
#else
74-
#define DBUG_PRINT_DO(K, M) do {} while (0)
75-
#endif
76-
77-
#undef DBUG_PRINT
78-
#define DBUG_PRINT(Keyword, Msg) DBUG_PRINT_DO(Keyword, Msg)
79-
80-
/*
81-
If DEBUG_ERROR_LOG is defined then error logging happens only
82-
in debug-copiled code. Otherwise ERROR_LOG() expands to
83-
error_log_print() even in production code. Note that in client
84-
plugin, error_log_print() will print nothing if opt_auth_win_clinet_log
85-
is 0.
86-
*/
87-
#if defined(DEBUG_ERROR_LOG) && defined(DBUG_OFF)
88-
#define ERROR_LOG(Level, Msg) do {} while (0)
89-
#else
90-
#define ERROR_LOG(Level, Msg) error_log_print< error_log_level::Level > Msg
91-
#endif
92103

93104
inline
94105
void debug_msg(const char *fmt, ...)
@@ -101,21 +112,38 @@ void debug_msg(const char *fmt, ...)
101112
va_end(args);
102113
}
103114

115+
#else
116+
#define DBUG_PRINT_DO(K, M) do {} while (0)
117+
#endif
104118

105-
void error_log_vprint(error_log_level::type level,
106-
const char *fmt, va_list args);
107119

108-
template <error_log_level::type Level>
109-
void error_log_print(const char *fmt, ...)
110-
{
111-
va_list args;
112-
va_start(args, fmt);
113-
error_log_vprint(Level, fmt, args);
114-
va_end(args);
115-
}
120+
#ifndef WINAUTH_USE_DBUG_LIB
116121

117-
typedef char Error_message_buf[1024];
118-
const char* get_last_error_message(Error_message_buf);
122+
#undef DBUG_PRINT
123+
#define DBUG_PRINT(Keyword, Msg) DBUG_PRINT_DO(Keyword, Msg)
124+
125+
/*
126+
Redefine few more debug macros to make sure that no symbols from
127+
dbug library are used.
128+
*/
129+
130+
#undef DBUG_ENTER
131+
#define DBUG_ENTER(X) do {} while (0)
132+
133+
#undef DBUG_RETURN
134+
#define DBUG_RETURN(X) return (X)
135+
136+
#undef DBUG_ASSERT
137+
#ifndef DBUG_OFF
138+
#define DBUG_ASSERT(X) assert (X)
139+
#else
140+
#define DBUG_ASSERT(X) do {} while (0)
141+
#endif
142+
143+
#undef DBUG_DUMP
144+
#define DBUG_DUMP(A,B,C) do {} while (0)
145+
146+
#endif
119147

120148

121149
/** Blob class *************************************************************/
@@ -158,15 +186,21 @@ class Blob
158186
return m_len;
159187
}
160188

161-
byte operator[](unsigned pos) const
189+
byte& operator[](unsigned pos) const
162190
{
163-
return pos < len() ? m_ptr[pos] : 0x00;
191+
static byte out_of_range= 0; // alas, no exceptions...
192+
return pos < len() ? m_ptr[pos] : out_of_range;
164193
}
165194

166195
bool is_null() const
167196
{
168197
return m_ptr == NULL;
169198
}
199+
200+
void trim(size_t l)
201+
{
202+
m_len= l;
203+
}
170204
};
171205

172206

libmysql/authentication_win/handshake.cc

Lines changed: 15 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -90,17 +90,19 @@ Handshake::~Handshake()
9090
9191
@note In case of error, appropriate error message is logged.
9292
*/
93-
int Handshake::packet_processing_loop(Connection &con)
93+
int Handshake::packet_processing_loop()
9494
{
95-
unsigned round= 1;
95+
m_round= 0;
9696

9797
do {
98+
++m_round;
9899
// Read packet send by the peer
100+
99101
DBUG_PRINT("info", ("Waiting for packet"));
100-
Blob packet= con.read();
101-
if (con.error() || packet.is_null())
102+
Blob packet= read_packet();
103+
if (error())
102104
{
103-
ERROR_LOG(ERROR, ("Error reading packet in round %d", round));
105+
ERROR_LOG(ERROR, ("Error reading packet in round %d", m_round));
104106
return 1;
105107
}
106108
DBUG_PRINT("info", ("Got packet of length %d", packet.len()));
@@ -113,7 +115,7 @@ int Handshake::packet_processing_loop(Connection &con)
113115

114116
if (error())
115117
{
116-
ERROR_LOG(ERROR, ("Error processing packet in round %d", round));
118+
ERROR_LOG(ERROR, ("Error processing packet in round %d", m_round));
117119
return 1;
118120
}
119121

@@ -124,22 +126,21 @@ int Handshake::packet_processing_loop(Connection &con)
124126

125127
if (!new_data.is_null())
126128
{
127-
++round;
128-
DBUG_PRINT("info", ("Round %d started", round));
129+
DBUG_PRINT("info", ("Round %d started", m_round));
129130

130131
DBUG_PRINT("info", ("Sending packet of length %d", new_data.len()));
131-
int ret= con.write(new_data);
132+
int ret= write_packet(new_data);
132133
if (ret)
133134
{
134-
ERROR_LOG(ERROR, ("Error writing packet in round %d", round));
135+
ERROR_LOG(ERROR, ("Error writing packet in round %d", m_round));
135136
return 1;
136137
}
137138
DBUG_PRINT("info", ("Data sent"));
138139
}
139140
else if (!is_complete())
140141
{
141142
ERROR_LOG(ERROR, ("No data to send in round %d"
142-
" but handshake is not complete", round));
143+
" but handshake is not complete", m_round));
143144
return 1;
144145
}
145146

@@ -148,16 +149,16 @@ int Handshake::packet_processing_loop(Connection &con)
148149
too many rounds.
149150
*/
150151

151-
if (round > MAX_HANDSHAKE_ROUNDS)
152+
if (m_round > MAX_HANDSHAKE_ROUNDS)
152153
{
153154
ERROR_LOG(ERROR, ("Authentication handshake could not be completed"
154-
" after %d rounds", round));
155+
" after %d rounds", m_round));
155156
return 1;
156157
}
157158

158159
} while(!is_complete());
159160

160-
ERROR_LOG(INFO, ("Handshake completed after %d rounds", round));
161+
ERROR_LOG(INFO, ("Handshake completed after %d rounds", m_round));
161162
return 0;
162163
}
163164

libmysql/authentication_win/handshake.h

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,7 @@ class Handshake
100100
Handshake(const char *ssp, side_t side);
101101
virtual ~Handshake();
102102

103-
int Handshake::packet_processing_loop(Connection &con);
103+
int Handshake::packet_processing_loop();
104104

105105
bool virtual is_complete() const
106106
{
@@ -126,6 +126,13 @@ class Handshake
126126
/// Stores attributes of the created security context.
127127
ULONG m_atts;
128128

129+
/**
130+
Round of the handshake (starting from round 1). One round
131+
consist of reading packet from the other side, processing it and
132+
optionally sending a reply (see @c packet_processing_loop()).
133+
*/
134+
unsigned int m_round;
135+
129136
/// If non-zero, stores error code of the last failed operation.
130137
int m_error;
131138

@@ -152,7 +159,13 @@ class Handshake
152159
@return A blob with data to be sent to the other end or null blob if
153160
no more data needs to be exchanged.
154161
*/
155-
virtual Blob process_data(const Blob &data)= 0;
162+
virtual Blob process_data(const Blob &data) =0;
163+
164+
/// Read packet from the other end.
165+
virtual Blob read_packet() =0;
166+
167+
/// Write packet to the other end.
168+
virtual int write_packet(Blob &data) =0;
156169

157170
#ifndef DBUG_OFF
158171

0 commit comments

Comments
 (0)