8000 Accepting unsafe mysql_old_password auth method - fixes #37 · mauricio/postgresql-async@701c9dc · GitHub
[go: up one dir, main page]

Skip to content
This repository was archived by the owner on Dec 3, 2019. It is now read-only.

Commit 701c9dc

Browse files
committed
Accepting unsafe mysql_old_password auth method - fixes #37
1 parent 5b04723 commit 701c9dc

18 files changed

+247
-50
lines changed

mysql-async/src/main/scala/com/github/mauricio/async/db/mysql/MySQLConnection.scala

Lines changed: 5 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -26,21 +26,11 @@ import com.github.mauricio.async.db.mysql.util.CharsetMapper
2626
import com.github.mauricio.async.db.util.ChannelFutureTransformer.toFuture
2727
import com.github.mauricio.async.db.util._
2828
import java.util.concurrent.atomic.{AtomicLong,AtomicReference}
29-
import scala.Some
3029
import scala.concurrent.{ExecutionContext, Promise, Future}
31-
import scala.util.Failure
32-
import scala.util.Success
3330
import io.netty.channel.{EventLoopGroup, ChannelHandlerContext}
34-
import com.github.mauricio.async.db.mysql.message.server.HandshakeMessage
35-
import com.github.mauricio.async.db.mysql.message.client.HandshakeResponseMessage
36-
import com.github.mauricio.async.db.mysql.message.server.ErrorMessage
37-
import com.github.mauricio.async.db.mysql.message.client.QueryMessage
3831
import scala.util.Failure
3932
import scala.Some
40-
import com.github.mauricio.async.db.mysql.message.server.OkMessage
41-
import com.github.mauricio.async.db.mysql.message.client.PreparedStatementMessage
4233
import scala.util.Success
43-
import com.github.mauricio.async.db.mysql.message.server.EOFMessage
4434

4535
object MySQLConnection {
4636
final val Counter = new AtomicLong()
@@ -95,9 +85,6 @@ class MySQLConnection(
9585
}
9686

9787
def close: Future[Connection] = {
98-
99-
log.debug("Closing connection")
100-
10188
if ( this.isConnected ) {
10289
if (!this.disconnectionPromise.isCompleted) {
10390
val exception = new DatabaseException("Connection is being closed")
@@ -125,7 +112,7 @@ class MySQLConnection(
125112
}
126113

127114
override def exceptionCaught(throwable: Throwable) {
128-
log.error("Transport failure", throwable)
115+
log.error("Transport failure ", throwable)
129116
setException(throwable)
130117
}
131118

@@ -190,6 +177,10 @@ class MySQLConnection(
190177
))
191178
}
192179

180+
override def switchAuthentication( message : AuthenticationSwitchRequest ) {
181+
this.connectionHandler.write(new AuthenticationSwitchResponse( configuration.password, message ))
182+
}
183+
193184
def sendQuery(query: String): Future[QueryResult] = {
194185
this.validateIsReadyForQuery()
195186
val promise = Promise[QueryResult]

mysql-async/src/main/scala/com/github/mauricio/async/db/mysql/codec/MySQLConnectionHandler.scala

Lines changed: 38 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ import scala.Some
3434
import scala.annotation.switch
3535
import scala.collection.mutable.{ArrayBuffer, HashMap}
3636
import scala.concurrent._
37+
import com.github.mauricio.async.db.exceptions.DatabaseException
3738

3839
class MySQLConnectionHandler(
3940
configuration: Configuration,
@@ -85,9 +86,6 @@ class MySQLConnectionHandler(
8586
}
8687

8788
override def channelRead0(ctx: ChannelHandlerContext, message: Object) {
88-
89-
//log.debug("Message received {}", message)
90-
9189
message match {
9290
case m: ServerMessage => {
9391
(m.kind: @switch) match {
@@ -103,16 +101,7 @@ class MySQLConnectionHandler(
103101
handlerDelegate.onError(m.asInstanceOf[ErrorMessage])
104102
}
105103
case ServerMessage.EOF => {
106-
107-
val resultSet = this.currentQuery
108-
this.clearQueryState
109-
110-
if ( resultSet != null ) {
111-
handlerDelegate.onResultSet( resultSet, m.asInstanceOf[EOFMessage] )
112-
} else {
113-
handlerDelegate.onEOF(m.asInstanceOf[EOFMessage])
114-
}
115-
104+
this.handleEOF(m)
116105
}
117106
case ServerMessage.ColumnDefinition => {
118107
val message = m.asInstanceOf[ColumnDefinitionMessage]
@@ -162,9 +151,15 @@ class MySQLConnectionHandler(
162151
}
163152

164153
override def channelActive(ctx: ChannelHandlerContext): Unit = {
154+
log.debug("Channel became active")
165155
handlerDelegate.connected(ctx)
166156
}
167157

158+
159+
override def channelInactive(ctx: ChannelHandlerContext) {
160+
log.debug("Channel became inactive")
161+
}
162+
168163
override def exceptionCaught(ctx: ChannelHandlerContext, cause: Throwable) {
169164
// unwrap CodecException if needed
170165
cause match {
@@ -212,6 +207,8 @@ class MySQLConnectionHandler(
212207
writeAndHandleError(message)
213208
}
214209

210+
def write( message : AuthenticationSwitchResponse ) : ChannelFuture = writeAndHandleError(message)
211+
215212
def write( message : QuitMessage ) : ChannelFuture = {
216213
writeAndHandleError(message)
217214
}
@@ -225,7 +222,7 @@ class MySQLConnectionHandler(
225222
}
226223

227224
def isConnected : Boolean = {
228-
if ( this.currentContext != null ) {
225+
if ( this.currentContext != null && this.currentContext.channel() != null ) {
229226
this.currentContext.channel.isActive
230227
} else {
231228
false
@@ -267,13 +264,36 @@ class MySQLConnectionHandler(
267264
}
268265

269266
private def writeAndHandleError( message : Any ) : ChannelFuture = {
270-
val future = this.currentContext.writeAndFlush(message)
271267

272-
future.onFailure {
273-
case e : Throwable => handleException(e)
268+
if ( this.currentContext.channel().isActive ) {
269+
val future = this.currentContext.writeAndFlush(message)
270+
271+
future.onFailure {
272+
case e : Throwable => handleException(e)
273+
}
274+
275+
future
276+
} else {
277+
throw new DatabaseException("This channel is not active and can't take messages")
274278
}
279+
}
275280

276-
future
281+
private def handleEOF( m : ServerMessage ) {
282+
m match {
283+
case eof : EOFMessage => {
284+
val resultSet = this.currentQuery
285+
this.clearQueryState
286+
287+
if ( resultSet != null ) {
288+
handlerDelegate.onResultSet( resultSet, eof )
289+
} else {
290+
handlerDelegate.onEOF(eof)
291+
}
292+
}
293+
case authenticationSwitch : AuthenticationSwitchRequest => {
294+
handlerDelegate.switchAuthentication(authenticationSwitch)
295+
}
296+
}
277297
}
278298

279299
}

mysql-async/src/main/scala/com/github/mauricio/async/db/mysql/codec/MySQLFrameDecoder.scala

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ class MySQLFrameDecoder(charset: Charset, connectionId : String) extends ByteToM
4040
private final val columnDecoder = new ColumnDefinitionDecoder(charset, new DecoderRegistry(charset))
4141
private final val rowDecoder = new ResultSetRowDecoder(charset)
4242
private final val preparedStatementPrepareDecoder = new PreparedStatementPrepareResponseDecoder()
43+
private final val authenticationSwitchDecoder = new AuthenticationSwitchRequestDecoder(charset)
4344

4445
private[codec] var processingColumns = false
4546
private[codec] var processingParams = false
@@ -104,8 +105,14 @@ class MySQLFrameDecoder(charset: Charset, connectionId : String) extends ByteToM
104105
this.processingColumns = false
105106
ColumnProcessingFinishedDecoder
106107
} else {
107-
this.clear
108-
EOFMessageDecoder
108+
109+
if ( this.isInQuery ) {
110+
this.clear
111+
EOFMessageDecoder
112+
} else {
113+
this.authenticationSwitchDecoder
114+
}
115+
109116
}
110117
}
111118

mysql-async/src/main/scala/com/github/mauricio/async/db/mysql/codec/MySQLHandlerDelegate.scala

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
package com.github.mauricio.async.db.mysql.codec
1818

1919
import com.github.mauricio.async.db.ResultSet
20-
import com.github.mauricio.async.db.mysql.message.server.{EOFMessage, OkMessage, ErrorMessage, HandshakeMessage}
20+
import com.github.mauricio.async.db.mysql.message.server._
2121
import io.netty.channel.ChannelHandlerContext
2222

2323
trait MySQLHandlerDelegate {
@@ -29,5 +29,6 @@ trait MySQLHandlerDelegate {
2929
def exceptionCaught( exception : Throwable )
3030
def connected( ctx : ChannelHandlerContext )
3131
def onResultSet( resultSet : ResultSet, message : EOFMessage )
32+
def switchAuthentication( message : AuthenticationSwitchRequest )
3233

3334
}

mysql-async/src/main/scala/com/github/mauricio/async/db/mysql/codec/MySQLOneToOneEncoder.scala

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ class MySQLOneToOneEncoder(charset: Charset, charsetMapper: CharsetMapper) exten
4141
private final val rowEncoder = new BinaryRowEncoder(charset)
4242
private final val prepareEncoder = new PreparedStatementPrepareEncoder(charset)
4343
private final val executeEncoder = new PreparedStatementExecuteEncoder(rowEncoder)
44+
private final val authenticationSwitchEncoder = new AuthenticationSwitchResponseEncoder(charset)
4445

4546
private var sequence = 1
4647

@@ -66,6 +67,10 @@ class MySQLOneToOneEncoder(charset: Charset, charsetMapper: CharsetMapper) exten
6667
sequence = 0
6768
this.prepareEncoder
6869
}
70+
case ClientMessage.AuthSwitchResponse => {
71+
sequence += 1
72+
this.authenticationSwitchEncoder
73+
}
6974
case _ => throw new EncoderNotAvailableException(message)
7075
}
7176

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
package com.github.mauricio.async.db.mysql.decoder
2+
3+
import java.nio.charset.Charset
4+
import io.netty.buffer.ByteBuf
5+
import com.github.mauricio.async.db.mysql.message.server.{AuthenticationSwitchRequest, ServerMessage}
6+
import com.github.mauricio.async.db.util.ChannelWrapper.bufferToWrapper
7+
8+
class AuthenticationSwitchRequestDecoder( charset : Charset ) extends MessageDecoder {
9+
def decode(buffer: ByteBuf): ServerMessage = {
10+
new AuthenticationSwitchRequest(
11+
buffer.readCString(charset),
12+
buffer.readUntilEOF(charset)
13+
)
14+
}
15+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
package com.github.mauricio.async.db.mysql.encoder
2+
3+
import com.github.mauricio.async.db.mysql.message.client.{AuthenticationSwitchResponse, ClientMessage}
4+
import io.netty.buffer.ByteBuf
5+
import com.github.mauricio.async.db.exceptions.UnsupportedAuthenticationMethodException
6+
import com.github.mauricio.async.db.mysql.encoder.auth.AuthenticationMethod
7+
import java.nio.charset.Charset
8+
import com.github.mauricio.async.db.util.ByteBufferUtils
9+
10+
class AuthenticationSwitchResponseEncoder( charset : Charset ) extends MessageEncoder {
11+
12+
def encode(message: ClientMessage): ByteBuf = {
13+
val switch = message.asInstanceOf[AuthenticationSwitchResponse]
14+
15+
val method = switch.request.method
16+
val authenticator = AuthenticationMethod.Availables.getOrElse(
17+
method, { throw new UnsupportedAuthenticationMethodException(method) })
18+
19+
val buffer = ByteBufferUtils.packetBuffer()
20+
21+
val bytes = authenticator.generateAuthentication(charset, switch.password, switch.request.seed.getBytes(charset) )
22+
buffer.writeBytes(bytes)
23+
24+
buffer
25+
}
26+
27+
}

mysql-async/src/main/scala/com/github/mauricio/async/db/mysql/encoder/HandshakeResponseEncoder.scala

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ package com.github.mauricio.async.db.mysql.encoder
1818

1919
import io.netty.buffer.ByteBuf
2020
import com.github.mauricio.async.db.exceptions.UnsupportedAuthenticationMethodException
21-
import com.github.mauricio.async.db.mysql.encoder.auth.MySQLNativePasswordAuthentication
21+
import com.github.mauricio.async.db.mysql.encoder.auth.{AuthenticationMethod, MySQLNativePasswordAuthentication}
2222
import com.github.mauricio.async.db.mysql.message.client.{HandshakeResponseMessage, ClientMessage}
2323
import com.github.mauricio.async.db.mysql.util.CharsetMapper
2424
import com.github.mauricio.async.db.util.{Log, ByteBufferUtils}
@@ -47,7 +47,7 @@ class HandshakeResponseEncoder(charset: Charset, charsetMapper: CharsetMapper) e
4747

4848
import HandshakeResponseEncoder._
4949

50-
private val authenticationMethods = Map("mysql_native_password" -> new MySQLNativePasswordAuthentication(charset))
50+
private val authenticationMethods = AuthenticationMethod.Availables
5151

5252
def encode(message: ClientMessage): ByteBuf = {
5353

@@ -78,7 +78,7 @@ class HandshakeResponseEncoder(charset: Charset, charsetMapper: CharsetMapper) e
7878
val method = m.authenticationMethod.get
7979
val authenticator = this.authenticationMethods.getOrElse(
8080
method, { throw new UnsupportedAuthenticationMethodException(method) })
81-
val bytes = authenticator.generateAuthentication( m.username, m.password, m.seed )
81+
val bytes = authenticator.generateAuthentication(charset, m.password, m.seed )
8282
buffer.writeByte(bytes.length)
8383
buffer.writeBytes(bytes)
8484
} else {

mysql-async/src/main/scala/com/github/mauricio/async/db/mysql/encoder/auth/AuthenticationMethod.scala

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,17 @@
1616

1717
package com.github.mauricio.async.db.mysql.encoder.auth
1818

19+
import java.nio.charset.Charset
20+
21+
object AuthenticationMethod {
22+
final val Availables = Map(
23+
"mysql_native_password" -> MySQLNativePasswordAuthentication,
24+
"mysql_old_password" -> OldPasswordAuthentication
25+
)
26+
}
27+
1928
trait AuthenticationMethod {
2029

21-
def generateAuthentication( username : String, password : Option[String], seed : Array[Byte] ) : Array[Byte]
30+
def generateAuthentication( charset : Charset, password : Option[String], seed : Array[Byte] ) : Array[Byte]
2231

2332
}

mysql-async/src/main/scala/com/github/mauricio/async/db/mysql/encoder/auth/MySQLNativePasswordAuthentication.scala

Lines changed: 5 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -19,25 +19,21 @@ package com.github.mauricio.async.db.mysql.encoder.auth
1919
import java.nio.charset.Charset
2020
import java.security.MessageDigest
2121

22-
object MySQLNativePasswordAuthentication {
23-
final val EmptyArray = Array.empty[Byte]
24-
}
22+
object MySQLNativePasswordAuthentication extends AuthenticationMethod {
2523

26-
class MySQLNativePasswordAuthentication( charset : Charset ) extends AuthenticationMethod {
27-
28-
import MySQLNativePasswordAuthentication.EmptyArray
24+
final val EmptyArray = Array.empty[Byte]
2925

30-
def generateAuthentication(username: String, password: Option[String], seed : Array[Byte]): Array[Byte] = {
26+
def generateAuthentication(charset : Charset, password: Option[String], seed : Array[Byte]): Array[Byte] = {
3127

3228
if ( password.isDefined ) {
33-
scramble411( password.get, seed )
29+
scramble411(charset, password.get, seed )
3430
} else {
3531
EmptyArray
3632
}
3733

3834
}
3935

40-
private def scramble411( password : String, seed : Array[Byte] ) : Array[Byte] = {
36+
private def scramble411(charset : Charset, password : String, seed : Array[Byte] ) : Array[Byte] = {
4137

4238
val messageDigest = MessageDigest.getInstance("SHA-1")
4339
val initialDigest = messageDigest.digest(password.getBytes(charset))

0 commit comments

Comments
 (0)
0