private void gotKexInit(SSHPacket buf) throws TransportException { buf.rpos(buf.rpos() - 1); final Proposal serverProposal = new Proposal(buf); negotiatedAlgs = clientProposal.negotiate(serverProposal); log.debug("Negotiated algorithms: {}", negotiatedAlgs); for(AlgorithmsVerifier v: algorithmVerifiers) { log.debug("Trying to verify algorithms with {}", v); if(!v.verify(negotiatedAlgs)) { throw new TransportException(DisconnectReason.KEY_EXCHANGE_FAILED, "Failed to verify negotiated algorithms `" + negotiatedAlgs + "`"); } } kex = Factory.Named.Util.create(transport.getConfig().getKeyExchangeFactories(), negotiatedAlgs.getKeyExchangeAlgorithm()); try { kex.init(transport, transport.getServerID(), transport.getClientID(), serverProposal.getPacket().getCompactData(), clientProposal.getPacket().getCompactData()); } catch (GeneralSecurityException e) { throw new TransportException(DisconnectReason.KEY_EXCHANGE_FAILED, e); } }
public Proposal(Config config) { kex = Factory.Named.Util.getNames(config.getKeyExchangeFactories()); sig = Factory.Named.Util.getNames(config.getSignatureFactories()); c2sCipher = s2cCipher = Factory.Named.Util.getNames(config.getCipherFactories()); c2sMAC = s2cMAC = Factory.Named.Util.getNames(config.getMACFactories()); c2sComp = s2cComp = Factory.Named.Util.getNames(config.getCompressionFactories()); packet = new SSHPacket(Message.KEXINIT); // Put cookie packet.ensureCapacity(16); config.getRandomFactory().create().fill(packet.array(), packet.wpos(), 16); packet.wpos(packet.wpos() + 16); // Put algorithm lists packet.putString(toCommaString(kex)); packet.putString(toCommaString(sig)); packet.putString(toCommaString(c2sCipher)); packet.putString(toCommaString(s2cCipher)); packet.putString(toCommaString(c2sMAC)); packet.putString(toCommaString(s2cMAC)); packet.putString(toCommaString(c2sComp)); packet.putString(toCommaString(s2cComp)); packet.putString(""); packet.putString(""); packet.putBoolean(false); // Optimistic next packet does not follow packet.putUInt32(0); // "Reserved" for future by spec }
private boolean parseGexGroup(SSHPacket buffer) throws Buffer.BufferException, GeneralSecurityException, TransportException { BigInteger p = buffer.readMPInt(); BigInteger g = buffer.readMPInt(); int bitLength = p.bitLength(); if (bitLength < minBits || bitLength > maxBits) { throw new GeneralSecurityException("Server generated gex p is out of range (" + bitLength + " bits)"); } log.debug("Received server p bitlength {}", bitLength); dh.init(new DHParameterSpec(p, g), trans.getConfig().getRandomFactory()); log.debug("Sending {}", Message.KEX_DH_GEX_INIT); trans.write(new SSHPacket(Message.KEX_DH_GEX_INIT).putBytes(dh.getE())); return false; } }
/** * Builds a {@link SSHPacket} containing the fields common to all authentication method. Method-specific fields can * further be put into this buffer. */ protected SSHPacket buildReq() throws UserAuthException { return new SSHPacket(Message.USERAUTH_REQUEST) // SSH_MSG_USERAUTH_REQUEST .putString(params.getUsername()) // username goes first .putString(params.getNextServiceName()) // the service that we'd like on success .putString(name); // name of auth method }
while (true) { if (packetLength == -1) { assert inputBuffer.rpos() == 0 : "buffer cleared"; bytesNeeded = 4 - inputBuffer.available(); if (bytesNeeded <= 0) { packetLength = inputBuffer.readUInt32AsInt(); checkPacketLength(packetLength); } else { assert inputBuffer.rpos() == 4 : "packet length read"; bytesNeeded = packetLength + mac.getBlockSize() - inputBuffer.available(); if (bytesNeeded <= 0) { seq = seq + 1 & 0xffffffffL; checkMAC(inputBuffer.array()); decryptBuffer(4, packetLength); inputBuffer.wpos(packetLength + 4 - inputBuffer.readByte()); final SSHPacket plain = usingCompression() ? decompressed() : inputBuffer; if (log.isTraceEnabled()) { log.trace("Received packet #{}: {}", seq, plain.printHex()); packetHandler.handle(plain.readMessageID(), plain); // Process the decoded packet inputBuffer.clear(); packetLength = -1; } else {
if (log.isTraceEnabled()) { log.trace("Encoding packet #{}: {}", seq + 1, buffer.printHex()); final int payloadSize = buffer.available(); int lengthWithoutPadding; if (etm) { final int startOfPacket = buffer.rpos() - 5; int packetLen = 1 + payloadSize + padLen; // packetLength = padLen (1 byte) + payload + padding buffer.wpos(startOfPacket); buffer.putUInt32(packetLen); buffer.putByte((byte) padLen); buffer.wpos(endOfPadding); prng.fill(buffer.array(), endOfPadding - padLen, padLen); cipher.update(buffer.array(), startOfPacket + 4, packetLen); putMAC(buffer, startOfPacket, endOfPadding); } else { cipher.update(buffer.array(), startOfPacket, 4 + packetLen); buffer.rpos(startOfPacket); // Make ready-to-read
packet.wpos(headerOffset); packet.putMessageID(Message.CHANNEL_DATA); packet.putUInt32(chan.getRecipient()); packet.putUInt32(writeNow); packet.wpos(dataOffset + writeNow); leftOvers.putRawBytes(packet.array(), packet.wpos(), leftOverBytes); win.consume(writeNow); packet.rpos(headerOffset); packet.wpos(dataOffset); packet.putBuffer(leftOvers); leftOvers.clear();
@Override public Promise<SSHPacket, ConnectionException> sendGlobalRequest(String name, boolean wantReply, byte[] specifics) throws TransportException { synchronized (globalReqPromises) { log.debug("Making global request for `{}`", name); trans.write(new SSHPacket(Message.GLOBAL_REQUEST).putString(name) .putBoolean(wantReply) .putRawBytes(specifics)); Promise<SSHPacket, ConnectionException> promise = null; if (wantReply) { promise = new Promise<SSHPacket, ConnectionException>("global req for " + name, ConnectionException.chainer, trans.getConfig().getLoggerFactory()); globalReqPromises.add(promise); } return promise; } }
protected SSHPacket putPubKey(SSHPacket reqBuf) throws UserAuthException { reqBuf .putString(algorithm) .putBytes(identity.getBlob()).getCompactData(); return reqBuf; }
protected Event<ConnectionException> sendChannelRequest(String reqType, boolean wantReply, Buffer.PlainBuffer reqSpecific) throws TransportException { log.debug("Sending channel request for `{}`", reqType); synchronized (chanReqResponseEvents) { trans.write( newBuffer(Message.CHANNEL_REQUEST) .putString(reqType) .putBoolean(wantReply) .putBuffer(reqSpecific) ); Event<ConnectionException> responseEvent = null; if (wantReply) { responseEvent = new Event<ConnectionException>("chan#" + id + " / " + "chanreq for " + reqType, ConnectionException.chainer, loggerFactory); chanReqResponseEvents.add(responseEvent); } return responseEvent; } }
protected void receiveInto(ChannelInputStream stream, SSHPacket buf) throws ConnectionException, TransportException { final int len; try { len = buf.readUInt32AsInt(); } catch (Buffer.BufferException be) { throw new ConnectionException(be); } if (len < 0 || len > getLocalMaxPacketSize() || len > buf.available()) { throw new ConnectionException(DisconnectReason.PROTOCOL_ERROR, "Bad item length: " + len); } if (log.isTraceEnabled()) { log.trace("IN #{}: {}", id, ByteArrayUtils.printHex(buf.array(), buf.rpos(), len)); } stream.receive(buf.array(), buf.rpos(), len); }
@Override public long write(SSHPacket payload) throws TransportException { writeLock.lock(); try { if (kexer.isKexOngoing()) { // Only transport layer packets (1 to 49) allowed except SERVICE_REQUEST final Message m = Message.fromByte(payload.array()[payload.rpos()]); if (!m.in(1, 49) || m == Message.SERVICE_REQUEST) { assert m != Message.KEXINIT; kexer.waitForDone(); } } else if (encoder.getSequenceNumber() == 0) // We get here every 2**32th packet kexer.startKex(true); final long seq = encoder.encode(payload); try { connInfo.out.write(payload.array(), payload.rpos(), payload.available()); connInfo.out.flush(); } catch (IOException ioe) { throw new TransportException(ioe); } return seq; } finally { writeLock.unlock(); } }
@Override public void handle(Message cmd, SSHPacket buf) throws UserAuthException, TransportException { if (cmd == Message.USERAUTH_60 && newPasswordProvider != null) { log.info("Received SSH_MSG_USERAUTH_PASSWD_CHANGEREQ."); try { String prompt = buf.readString(); buf.readString(); // lang-tag AccountResource resource = makeAccountResource(); char[] newPassword = newPasswordProvider.provideNewPassword(resource, prompt); SSHPacket sshPacket = super.buildReq().putBoolean(true).putSensitiveString(pwdf.reqPassword(resource)).putSensitiveString(newPassword); params.getTransport().write(sshPacket); } catch (Buffer.BufferException e) { throw new TransportException(e); } } else if (cmd == Message.USERAUTH_60) { throw new UserAuthException("Password change request received; unsupported operation (newPassword was 'null')"); } else { super.handle(cmd, buf); } }
private SSHPacket checkHeaderSpace(SSHPacket buffer) { if (buffer.rpos() < 5) { log.warn("Performance cost: when sending a packet, ensure that " + "5 bytes are available in front of the buffer"); SSHPacket nb = new SSHPacket(buffer.available() + 5); nb.rpos(5); nb.wpos(5); nb.putBuffer(buffer); buffer = nb; } return buffer; }
@Override public SSHPacket buildReq() throws UserAuthException { final AccountResource accountResource = makeAccountResource(); log.debug("Requesting password for {}", accountResource); return super.buildReq() // the generic stuff .putBoolean(false) // no, we are not responding to a CHANGEREQ .putSensitiveString(pwdf.reqPassword(accountResource)); }
private void respond(CharArrWrap[] userReplies) throws TransportException { final SSHPacket pkt = new SSHPacket(Message.USERAUTH_INFO_RESPONSE).putUInt32(userReplies.length); for (final CharArrWrap response : userReplies) pkt.putSensitiveString(response.arr); params.getTransport().write(pkt); }
private void gotGlobalRequest(SSHPacket buf) throws ConnectionException, TransportException { try { final String requestName = buf.readString(); boolean wantReply = buf.readBoolean(); log.debug("Received GLOBAL_REQUEST `{}`; want reply: {}", requestName, wantReply); if (wantReply) { trans.write(new SSHPacket(Message.REQUEST_FAILURE)); } } catch (Buffer.BufferException be) { throw new ConnectionException(be); } }