package com.tesora.dve.db.mysql.portal.protocol;
/*
* #%L
* Tesora Inc.
* Database Virtualization Engine
* %%
* Copyright (C) 2011 - 2014 Tesora Inc.
* %%
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
* #L%
*/
import io.netty.buffer.ByteBuf;
import io.netty.util.CharsetUtil;
import org.apache.commons.codec.digest.DigestUtils;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.charset.Charset;
public class MSPAuthenticateV10MessageMessage extends BaseMSPMessage<MSPAuthenticateV10MessageMessage.ParsedData> {
public static final MSPAuthenticateV10MessageMessage PROTOTYPE = new MSPAuthenticateV10MessageMessage();
private static final int MAX_PACKET_SIZE = 0xffffff;
static class ParsedData {
ClientCapabilities caps;
int maxPacketSize;
byte charsetID;
String username;
String password;
String initialDatabase;
}
protected MSPAuthenticateV10MessageMessage() {
super();
}
protected MSPAuthenticateV10MessageMessage(ByteBuf backing) {
super(backing);
}
public static MSPAuthenticateV10MessageMessage newMessage(ByteBuf backing){
return new MSPAuthenticateV10MessageMessage(backing.slice());
}
@Override
public MSPAuthenticateV10MessageMessage newPrototype(ByteBuf source) {
source = source.slice();
return new MSPAuthenticateV10MessageMessage(source);
}
@Override
public byte getMysqlMessageType() {
return (byte) 0xc0;
}
@Override
protected ParsedData unmarshall(ByteBuf source) {
ParsedData parseValues = new ParsedData();
parseValues.caps = new ClientCapabilities(source.readUnsignedInt());
parseValues.maxPacketSize = source.readInt();
parseValues.charsetID = source.readByte();
source.skipBytes(23); // login request has a 23 byte filler
parseValues.username = source.readSlice(source.bytesBefore((byte) 0)).toString(CharsetUtil.UTF_8);
source.skipBytes(1); // skip the NULL terminator
byte passwordLength = source.readByte();
parseValues.password = source.readSlice(passwordLength).toString(CharsetUtil.ISO_8859_1);
// if the clientCapabilities flag has the CLIENT_CONNECT_WITH_DB bit set,
// then this message contains an initial database to connect to
if ( parseValues.caps.connectWithDB() ) {
parseValues.initialDatabase = source.readSlice(source.bytesBefore((byte) 0)).toString(CharsetUtil.UTF_8);
source.skipBytes(1); // skip the NULL terminator
} else {
parseValues.initialDatabase = "";
}
return parseValues;
}
public ClientCapabilities getClientCapabilities() {
return readState().caps;
}
public int getMaxPacketSize() {
return readState().maxPacketSize;
}
public byte getCharsetID() {
return readState().charsetID;
}
public String getUsername() {
return readState().username;
}
public String getPassword() {
return readState().password;
}
public String getInitialDatabase() {
return readState().initialDatabase;
}
public static void write(ByteBuf out, String userName, String userPassword, String salt, Charset charset, int mysqlCharsetID, int capabilitiesFlag) {
ByteBuf leBuf = out.order(ByteOrder.LITTLE_ENDIAN);
int payloadSizeIndex = leBuf.writerIndex();
leBuf.writeMedium(0);
leBuf.writeByte(1);
int payloadStartIndex = leBuf.writerIndex();
leBuf.writeInt(capabilitiesFlag);
leBuf.writeInt(MAX_PACKET_SIZE);
// leBuf.writeByte(serverGreeting.getServerCharsetId());
leBuf.writeByte(mysqlCharsetID);
leBuf.writeZero(23);
leBuf.writeBytes(userName.getBytes(charset));
leBuf.writeZero(1);
if ((capabilitiesFlag & ClientCapabilities.CLIENT_SECURE_CONNECTION) > 0) {
byte[] securePassword = computeSecurePassword(userPassword, salt);
leBuf.writeByte(securePassword.length);
leBuf.writeBytes(securePassword);
} else {
leBuf.writeBytes(userPassword.getBytes(charset));
leBuf.writeZero(1);
}
leBuf.setMedium(payloadSizeIndex, leBuf.writerIndex()-payloadStartIndex);
}
public static byte[] computeSecurePassword(String password, String salt) {
byte[] sha1password = DigestUtils.sha1(password);
byte[] seedbytes = salt.getBytes();
ByteBuffer bb = ByteBuffer.allocate(sha1password.length + seedbytes.length);
bb.put(seedbytes);
bb.put(DigestUtils.sha1(sha1password));
byte[] sha1parttwo = DigestUtils.sha1(bb.array());
byte[] securePassword = new byte[sha1password.length];
for(int i = 0; i < securePassword.length; ++i)
securePassword[i] = (byte) (sha1password[i] ^ sha1parttwo[i]);
return securePassword;
}
public static String computeSecurePasswordString(String password, String salt) {
return new String(computeSecurePassword(password, salt), CharsetUtil.ISO_8859_1);
}
}