All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.sshtools.agent.openssh.OpenSSHAgentConnection Maven / Gradle / Ivy

/**
 * (c) 2002-2021 JADAPTIVE Limited. All Rights Reserved.
 *
 * This file is part of the Maverick Synergy Java SSH API.
 *
 * Maverick Synergy is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * Maverick Synergy 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 Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with Maverick Synergy.  If not, see .
 */
package com.sshtools.agent.openssh;

import java.io.Closeable;
import java.io.IOException;
import java.io.InputStream;
import java.io.InterruptedIOException;
import java.io.OutputStream;

import com.sshtools.agent.AgentMessage;
import com.sshtools.agent.KeyStore;
import com.sshtools.agent.exceptions.InvalidMessageException;
import com.sshtools.agent.exceptions.KeyTimeoutException;
import com.sshtools.agent.server.SshAgentConnection;
import com.sshtools.common.logger.Log;
import com.sshtools.common.ssh.SshException;
import com.sshtools.common.ssh.components.SshPublicKey;
import com.sshtools.common.util.ByteArrayReader;
import com.sshtools.common.util.ByteArrayWriter;

/**
 * Represents a single connection on the agent server.
 */
public class OpenSSHAgentConnection implements Runnable, SshAgentConnection {
   
	private static final int SSH_AGENT_RSA_SHA2_256 = 2;
	private static final int SSH_AGENT_RSA_SHA2_512 = 4;
	
    InputStream in;
    OutputStream out;
    KeyStore keystore;
    Closeable closeable;
    boolean isRFCAgent = true;
    
    public OpenSSHAgentConnection(KeyStore keystore, InputStream in, OutputStream out, Closeable closeable) {
        this.keystore = keystore;
        this.in = in;
        this.out = out;
        this.closeable = closeable;
    }


    /**
     * Send a success message.
     *
     * @throws IOException if an IO error occurs
     */
    protected void sendAgentSuccess() throws IOException {
        AgentMessage msg = new AgentMessage(OpenSSHAgentMessages.SSH_AGENT_SUCCESS);
        sendMessage(msg);
    }

    /**
     * Send a failure message
     *
     * @param errorcode the error code of the failure
     *
     * @throws IOException if an IO error occurs
     */
    protected void sendAgentFailure() throws IOException {
        SshAgentFailure msg = new SshAgentFailure();
        sendMessage(msg);
    }

    /**
     * Send the agents key list to the remote side. This supplies all the
     * public keys.
     *
     * @throws IOException if an IO error occurs
     */
    protected void sendAgentKeyList() throws IOException {
        AgentMessage msg=null;
    	msg = new SshAgentKeyList(keystore.getPublicKeys());
    	sendMessage(msg);
    }

    /**
     * Send the completed signing operation data.
     *
     * @param data the data generating from the signing operation
     *
     * @throws IOException if an IO error occurs
     */
    protected void sendOperationComplete(byte[] data) throws IOException {
        SshAgentOperationComplete msg = new SshAgentOperationComplete(data);
        sendMessage(msg);
    }


    /**
     * Sends a subsystem message.
     *
     * @param msg the subsystem message to send
     *
     * @throws IOException if an IO error occurs
     */
    protected void sendMessage(AgentMessage msg) throws IOException {
        Log.info("Sending message " + msg.getMessageName());

        byte[] msgdata;
		try {
			msgdata = msg.toByteArray();
			 out.write(ByteArrayWriter.encodeInt(msgdata.length));
		     out.write(msgdata);
		     out.flush();
		} catch (InvalidMessageException e) {
			throw new IOException(e.getMessage());
		}
       
    }

    /**
     * Called when the remote side adds a key the agent.
     *
     * @param msg the message containing the key
     *
     * @throws IOException if an IO error occurs
     */
    protected void onAddKey(SshAgentAddKey msg) throws IOException {
    	if(!keystore.isLocked()){
    		if (keystore.addKey(msg.getPrivateKey(), msg.getPublicKey(),
                    msg.getDescription(), msg.getKeyConstraints())) {
    			sendAgentSuccess();
    		} else {
    			sendAgentFailure();
    		}
        }else{
        	sendAgentFailure();
        }
    	
    }

    /**
     * Called when the remote side requests that all keys be removed from the
     * agent.
     *
     * @param msg the delete all keys message
     *
     * @throws IOException if an IO error occurs
     */
    protected void onDeleteAllKeys(AgentMessage msg)
        throws IOException {
    	if(!keystore.isLocked()){
    		if(keystore.deleteAllKeys()) {
    			sendAgentSuccess();
    		}     
        }
        
    	sendAgentFailure();
        
        
    }

    /**
     * Called by the remote side when a list of the agents keys is required
     *
     * @param msg the list all keys message
     *
     * @throws IOException if an IO error occurs
     */
    protected void onListKeys(AgentMessage msg) throws IOException {
    	if(!keystore.isLocked()){
    		sendAgentKeyList();
        }else{
        	sendAgentFailure();
        }
        
    }

    /**
     * Called by the remote side to initiate a private key operation.
     *
     * @param msg the private key operation message
     *
     * @throws IOException if an IO error occurs
     */
    protected void onPrivateKeyOp(SshAgentPrivateKeyOp msg)
        throws IOException {
        try {
        	SshPublicKey key = msg.getPublicKey();
            byte[] sig = keystore.performHashAndSign(key,
                        null, msg.getOperationData(), 
                        msg.getFlags().intValue());
            
            sendOperationComplete(encodeSignature(key, msg.getFlags().intValue(), sig));
            
        } catch (KeyTimeoutException ex) {
            sendAgentFailure();
        } catch (InvalidMessageException ex) {
            sendAgentFailure();
        } catch (SshException e) {
        	sendAgentFailure();
			
		}
    }

    protected byte[] encodeSignature(SshPublicKey key, int flags, byte[] signature) throws IOException {
		ByteArrayWriter sig = new ByteArrayWriter();

		try {
			switch(flags) {
			case SSH_AGENT_RSA_SHA2_256:
				sig.writeString("rsa-sha2-256");
				break;
			case SSH_AGENT_RSA_SHA2_512:
				sig.writeString("rsa-sha2-512");
				break;
			default:
				sig.writeString(key.getSigningAlgorithm());
				break;
			}
			
			sig.writeBinaryString(signature);
			return sig.toByteArray();
		} finally {
			sig.close();
		}
	}
    /**
     * Called by the remote side to delete a key from the agent
     *
     * @param msg the message containin the key to delete
     *
     * @throws IOException if an IO error occurs
     */
    protected void onDeleteKey(SshAgentDeleteKey msg) throws IOException {
        if (keystore.deleteKey(msg.getPublicKey())) {
            sendAgentSuccess();
        } else {
            sendAgentFailure();
        }
    }

    /**
     * Called by the remote side when the agent is to be locked
     *
     * @param msg the message containing a password
     *
     * @throws IOException if an IO error occurs
     */
    protected void onLock(SshAgentLock msg) throws IOException {
        if (keystore.lock(msg.getPassword())) {
            sendAgentSuccess();
        } else {
            sendAgentFailure();
        }
    }

    /**
     * Called by the remote side when the agent is to be unlocked
     *
     * @param msg the message containin the password
     *
     * @throws IOException if an IO error occurs
     */
    protected void onUnlock(SshAgentUnlock msg) throws IOException {
        if (keystore.unlock(msg.getPassword())) {
            sendAgentSuccess();
        } else {
            sendAgentFailure();
        }
    }

    /**
     * The connection thread
     */
    public void run() {
        try {
            Log.info("Starting agent connection thread");

            byte[] lendata = new byte[4];
            byte[] msgdata;
            int len;
            int read;
            boolean alive = true;

            while (alive) {
                // Read the first 4 bytes to determine the length of the message
                len = 0;

                while (len < lendata.length) {
                    try {
                        read = 0;
                        read = in.read(lendata, len, lendata.length - len);

                        if (read >= 0) {
                            len += read;
                        } else {
                            alive = false;

                            break;
                        }
                    } catch (InterruptedIOException ex) {
                        if (ex.bytesTransferred > 0) {
                            len += ex.bytesTransferred;
                        }
                    }
                }

                if (alive) {
                    len = (int) ByteArrayReader.readInt(lendata, 0);
                    msgdata = new byte[len];
                    len = 0;

                    while (len < msgdata.length) {
                        try {
                            len += in.read(msgdata, len, msgdata.length - len);
                        } catch (InterruptedIOException ex1) {
                            len += ex1.bytesTransferred;
                        }
                    }

                    onMessageReceived(msgdata);
                }
            }
        } catch (IOException ex) {
            Log.info("The agent connection terminated");
        } finally {
            try {
            	if(closeable!=null) {
            		closeable.close();
            	}
            } catch (Exception ex) {
            }
        }

        Log.info("Exiting agent connection thread");
        
    }

    /**
     * Process a message and route to the handler method
     *
     * @param msgdata the raw message received
     *
     * @throws IOException if an IO error occurs
     */
    protected void onMessageReceived(byte[] msgdata) throws IOException {
       try{
    	switch ((int) (msgdata[0] & 0xFF)) {

        case OpenSSHAgentMessages.SSH2_AGENTC_ADD_IDENTITY: {
            Log.info("Adding key to agent");

            SshAgentAddKey msg = new SshAgentAddKey();
            msg.fromByteArray(msgdata);
			onAddKey(msg);

            break;
        }

        case OpenSSHAgentMessages.SSH2_AGENTC_ADD_ID_CONSTRAINED: {
        
        	break;
        }
        
        case OpenSSHAgentMessages.SSH2_AGENTC_REQUEST_IDENTITIES: {
            Log.info("Listing agent keys");

            AgentMessage msg = new AgentMessage(OpenSSHAgentMessages.SSH2_AGENTC_REQUEST_IDENTITIES);
            msg.fromByteArray(msgdata);
            onListKeys(msg);

            break;
        }
        
        case OpenSSHAgentMessages.SSH2_AGENTC_REMOVE_IDENTITY: {
            Log.info("Deleting key from agent");

            SshAgentDeleteKey msg = new SshAgentDeleteKey();
            msg.fromByteArray(msgdata);
            onDeleteKey(msg);

            break;
        }
        
        case OpenSSHAgentMessages.SSH2_AGENTC_REMOVE_ALL_IDENTITIES: {
            Log.info("Deleting all keys from agent");

            AgentMessage msg = new AgentMessage(OpenSSHAgentMessages.SSH2_AGENTC_REMOVE_ALL_IDENTITIES);
            msg.fromByteArray(msgdata);
			onDeleteAllKeys(msg);

            break;
        }

        case OpenSSHAgentMessages.SSH2_AGENTC_SIGN_REQUEST: {
            Log.info("Performing agent private key operation");

            SshAgentPrivateKeyOp msg = new SshAgentPrivateKeyOp();
            msg.fromByteArray(msgdata);
            onPrivateKeyOp(msg);

            break;
        }

        case OpenSSHAgentMessages.SSH_AGENTC_LOCK: {
            Log.info("Locking agent");

            SshAgentLock msg = new SshAgentLock(isRFCAgent);
            msg.fromByteArray(msgdata);
            onLock(msg);

            break;
        }

        case OpenSSHAgentMessages.SSH_AGENTC_UNLOCK: {
            Log.info("Unlocking agent");

            SshAgentUnlock msg = new SshAgentUnlock(isRFCAgent);
            msg.fromByteArray(msgdata);
            onUnlock(msg);

            break;
        }


        default:
            throw new IOException("Unrecognized message type " +
                String.valueOf(msgdata[0]) + " received");
        }
       } catch (InvalidMessageException e) {
			e.printStackTrace();
	   }
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy