66
77use MySQLReplication \BinaryDataReader \BinaryDataReader ;
88use MySQLReplication \Config \Config ;
9- use MySQLReplication \Exception \MySQLReplicationException ;
109use MySQLReplication \Gtid \GtidCollection ;
1110use MySQLReplication \Repository \RepositoryInterface ;
1211use MySQLReplication \Socket \SocketInterface ;
@@ -17,6 +16,7 @@ class BinLogSocketConnect
1716 private const COM_BINLOG_DUMP = 0x12 ;
1817 private const COM_REGISTER_SLAVE = 0x15 ;
1918 private const COM_BINLOG_DUMP_GTID = 0x1e ;
19+ private const AUTH_SWITCH_PACKET = 254 ;
2020 /**
2121 * https://dev.mysql.com/doc/dev/mysql-server/latest/page_protocol_connection_phase.html 00 FE
2222 */
@@ -47,7 +47,8 @@ public function __construct(
4747 'Server version name: ' . $ this ->binLogServerInfo ->versionName . ', revision: ' . $ this ->binLogServerInfo ->versionRevision
4848 );
4949
50- $ this ->authenticate ();
50+
51+ $ this ->authenticate ($ this ->binLogServerInfo ->authPlugin );
5152 $ this ->getBinlogStream ();
5253 }
5354
@@ -110,56 +111,60 @@ private function isWriteSuccessful(string $data): void
110111 }
111112 }
112113
113- private function authenticate (): void
114+ private function authenticate (BinLogAuthPluginMode $ authPlugin ): void
114115 {
115- if ($ this ->binLogServerInfo ->authPlugin === null ) {
116- throw new MySQLReplicationException (
117- MySQLReplicationException::BINLOG_AUTH_NOT_SUPPORTED ,
118- MySQLReplicationException::BINLOG_AUTH_NOT_SUPPORTED_CODE
119- );
120- }
121-
122116 $ this ->logger ->info (
123- 'Trying to authenticate user: ' . $ this ->config ->user . ' using ' . $ this -> binLogServerInfo -> authPlugin ->value . ' plugin '
117+ 'Trying to authenticate user: ' . $ this ->config ->user . ' using ' . $ authPlugin ->value . ' default plugin '
124118 );
125119
126120 $ data = pack ('L ' , self ::getCapabilities ());
127121 $ data .= pack ('L ' , $ this ->binaryDataMaxLength );
128122 $ data .= chr (33 );
129123 $ data .= str_repeat (chr (0 ), 23 );
130124 $ data .= $ this ->config ->user . chr (0 );
131-
132- $ auth = '' ;
133- if ($ this ->binLogServerInfo ->authPlugin === BinLogAuthPluginMode::MysqlNativePassword) {
134- $ auth = $ this ->authenticateMysqlNativePasswordPlugin ();
135- } elseif ($ this ->binLogServerInfo ->authPlugin === BinLogAuthPluginMode::CachingSha2Password) {
136- $ auth = $ this ->authenticateCachingSha2PasswordPlugin ();
137- }
138-
125+ $ auth = $ this ->getAuthData ($ authPlugin , $ this ->binLogServerInfo ->salt );
139126 $ data .= chr (strlen ($ auth )) . $ auth ;
140- $ data .= $ this -> binLogServerInfo -> authPlugin ->value . chr (0 );
127+ $ data .= $ authPlugin ->value . chr (0 );
141128 $ str = pack ('L ' , strlen ($ data ));
142129 $ s = $ str [0 ] . $ str [1 ] . $ str [2 ];
143130 $ data = $ s . chr (1 ) . $ data ;
144131
145132 $ this ->socket ->writeToSocket ($ data );
146- $ this ->getResponse ();
133+ $ response = $ this ->getResponse ();
134+
135+ // Check for AUTH_SWITCH_PACKET
136+ if (isset ($ response [0 ]) && ord ($ response [0 ]) === self ::AUTH_SWITCH_PACKET ) {
137+ $ this ->switchAuth ($ response );
138+ }
147139
148140 $ this ->logger ->info ('User authenticated ' );
149141 }
150142
151- private function authenticateCachingSha2PasswordPlugin (): string
143+ private function getAuthData (?BinLogAuthPluginMode $ authPlugin , string $ salt ): string
144+ {
145+ if ($ authPlugin === BinLogAuthPluginMode::MysqlNativePassword) {
146+ return $ this ->authenticateMysqlNativePasswordPlugin ($ salt );
147+ }
148+
149+ if ($ authPlugin === BinLogAuthPluginMode::CachingSha2Password) {
150+ return $ this ->authenticateCachingSha2PasswordPlugin ($ salt );
151+ }
152+
153+ return '' ;
154+ }
155+
156+ private function authenticateCachingSha2PasswordPlugin (string $ salt ): string
152157 {
153158 $ hash1 = hash ('sha256 ' , $ this ->config ->password , true );
154159 $ hash2 = hash ('sha256 ' , $ hash1 , true );
155- $ hash3 = hash ('sha256 ' , $ hash2 . $ this -> binLogServerInfo -> salt , true );
160+ $ hash3 = hash ('sha256 ' , $ hash2 . $ salt , true );
156161 return $ hash1 ^ $ hash3 ;
157162 }
158163
159- private function authenticateMysqlNativePasswordPlugin (): string
164+ private function authenticateMysqlNativePasswordPlugin (string $ salt ): string
160165 {
161166 $ hash1 = sha1 ($ this ->config ->password , true );
162- $ hash2 = sha1 ($ this -> binLogServerInfo -> salt . sha1 (sha1 ($ this ->config ->password , true ), true ), true );
167+ $ hash2 = sha1 ($ salt . sha1 (sha1 ($ this ->config ->password , true ), true ), true );
163168 return $ hash1 ^ $ hash2 ;
164169 }
165170
@@ -316,4 +321,17 @@ private function setBinLogDump(): void
316321
317322 $ this ->logger ->info ('Set binlog to start from: ' . $ binFileName . ': ' . $ binFilePos );
318323 }
324+
325+ private function switchAuth (string $ response ): void
326+ {
327+ // skip AUTH_SWITCH_PACKET byte
328+ $ offset = 1 ;
329+ $ authPluginSwitched = BinLogAuthPluginMode::make (BinaryDataReader::decodeNullLength ($ response , $ offset ));
330+ $ salt = BinaryDataReader::decodeNullLength ($ response , $ offset );
331+ $ auth = $ this ->getAuthData ($ authPluginSwitched , $ salt );
332+
333+ $ this ->logger ->info ('Auth switch packet received, switching to ' . $ authPluginSwitched ->value );
334+
335+ $ this ->socket ->writeToSocket (pack ('L ' , (strlen ($ auth )) | (3 << 24 )) . $ auth );
336+ }
319337}
0 commit comments