Commit 9a2c570d authored by chiaming2000's avatar chiaming2000
Browse files

Simulator: Initial lock/unlock pin operation implementation. Lock Enable

and Lock Disable features are also implemented in this commit.

Admin Client: Added interface to lock/unlock device.

Please see KineticAdminClient API JavaDoc for detailed documentation.
parent f972e769
Loading
Loading
Loading
Loading
+74 −40
Original line number Diff line number Diff line
@@ -198,46 +198,7 @@ public class DefaultAdminClient implements KineticAdminClient {
        }
    }
    
    //@Override
    public void instantEraseOld(byte[] pin) throws KineticException {
    
//        KineticMessage km = MessageFactory.createKineticMessageWithBuilder();
//
//        Message.Builder request = (Builder) km.getMessage();
//
//        Setup.Builder setup = request.getCommandBuilder().getBodyBuilder()
//                .getSetupBuilder();
//
//        if (pin != null && pin.length > 0) {
//            setup.setPin(ByteString.copyFrom(pin));
//        }
//
//        setup.setInstantSecureErase(true);
//
//        KineticMessage response = configureSetupPolicy(km);
//
//        if (response.getMessage().getCommand().getHeader().getMessageType() != MessageType.SETUP_RESPONSE) {
//            throw new KineticException("received wrong message type.");
//        }
//
//        if (response.getMessage().getCommand().getStatus().getCode() == Status.StatusCode.NOT_AUTHORIZED) {
//
//            throw new KineticException("Authorized Exception: "
//                    + response.getMessage().getCommand().getStatus().getCode()
//                    + ": "
//                    + response.getMessage().getCommand().getStatus()
//                            .getStatusMessage());
//        }
//
//        if (response.getMessage().getCommand().getStatus().getCode() != Status.StatusCode.SUCCESS) {
//            throw new KineticException("Unknown Error: "
//                    + response.getMessage().getCommand().getStatus().getCode()
//                    + ": "
//                    + response.getMessage().getCommand().getStatus()
//                            .getStatusMessage());
//        }

    }

    private void validate(List<ACL> acls) throws KineticException {
        if (null == acls || acls.isEmpty() || 0 == acls.size()) {
@@ -673,4 +634,77 @@ public class DefaultAdminClient implements KineticAdminClient {
        }
    }

    @Override
    public void secureErase(byte[] pin) throws KineticException {
        this.instantErase(pin);
    }

    @Override
    public void lockDevice(byte[] pin) throws KineticException {
        
        if (pin == null || pin.length == 0) {
            throw new KineticException ("Pin mut not be null or empty");
        }
        
        KineticMessage km = MessageFactory.createKineticMessageWithBuilder();
        
        Message.Builder mb = (Message.Builder) km.getMessage();
        mb.setAuthType(AuthType.PINAUTH);
        
        mb.setPinAuth(PINauth.newBuilder().setPin(ByteString.copyFrom(pin)));
        
        Command.Builder commandBuilder = (Command.Builder) km.getCommand();
        
        commandBuilder.getHeaderBuilder()
        .setMessageType(MessageType.PINOP);
        
        commandBuilder.getBodyBuilder().getPinOpBuilder().setPinOpType(PinOpType.LOCK_PINOP);
        
        KineticMessage response = this.kineticClient.request(km);
        
        if (response.getCommand().getStatus().getCode() != StatusCode.SUCCESS) {
            
            KineticException ke = new KineticException ("Pin op lock device failed.");
            ke.setRequestMessage(km);
            ke.setResponseMessage(response);
            
            throw ke;
        }
        
    }

    @Override
    public void unLockDevice(byte[] pin) throws KineticException {
        
        if (pin == null || pin.length == 0) {
            throw new KineticException ("Pin mut not be null or empty");
        }
        
        KineticMessage km = MessageFactory.createKineticMessageWithBuilder();
        
        Message.Builder mb = (Message.Builder) km.getMessage();
        mb.setAuthType(AuthType.PINAUTH);
        
        mb.setPinAuth(PINauth.newBuilder().setPin(ByteString.copyFrom(pin)));
        
        Command.Builder commandBuilder = (Command.Builder) km.getCommand();
        
        commandBuilder.getHeaderBuilder()
        .setMessageType(MessageType.PINOP);
        
        commandBuilder.getBodyBuilder().getPinOpBuilder().setPinOpType(PinOpType.UNLOCK_PINOP);
        
        KineticMessage response = this.kineticClient.request(km);
        
        if (response.getCommand().getStatus().getCode() != StatusCode.SUCCESS) {
            
            KineticException ke = new KineticException ("Pin op lock device failed.");
            ke.setRequestMessage(km);
            ke.setResponseMessage(response);
            
            throw ke;
        }
        
    }
    
}
+57 −4
Original line number Diff line number Diff line
@@ -30,6 +30,7 @@ import kinetic.client.KineticException;
 * <p>
 * All administrative operations by default use SSL connections to connect to the Drive or Simulator. 
 * And this is the only supported transport for all administrative operations.
 * <p>
 * 
 * @author James Hughes.
 * @author Chiaming Yang
@@ -151,6 +152,17 @@ public interface KineticAdminClient {

    /**
     * Set the access control list to the Kinetic drive.
     * <p>
     * Lock Enable: Will be utilized to enable lock on power cycle. 
     * <p>
     * If the Client sets a non-empty value for the lock pin, device will be locked after a power cycle. 
     * Access to the data is not immediately impacted, but a subsequent power cycle will result in a locked drive.
     * <p>
     * Lock Disable: If the Client sets an empty value for the lock pin, lock enable feature is turned off. 
     * This provides the Client with a mechanism for turning off the locking feature after previously enabling it.
     * <p>
     * A device must be in unlock mode before a Security operation can be performed.
     * <p>
     *  
     * @param acls
     *            ACL information needs to be set.
@@ -176,9 +188,50 @@ public interface KineticAdminClient {
     * 
     * @throws KineticException
     *             if unable to load firmware bytes to the drive.
     *             
     * 
     */
    public void instantErase(byte[] pin) throws KineticException;
    
    /**
     * Securely erase all user data, configurations, and setup information on the 
     * drive.
     * 
     * @param pin the pin used to authenticate for this operation.
     * 
     * @throws KineticException if unable to perform the pin operation.
     * 
     * @see #setSecurity(List, byte[], byte[], byte[], byte[])
     */
    public void secureErase (byte[] pin) throws KineticException;
    
    /**
     * Lock the device with the specified pin.
     * <p>
     * If the Client has set a non-zero length locking pin (to enable locking), a subsequent call to lockDevice will
     * lock the device.  
     * 
     * @param pin the pin to authenticate to the service.
     *
     * @throws KineticException if any internal error occurred.
     * 
     * @see #setSecurity(List, byte[], byte[], byte[], byte[])
     */
    public void lockDevice (byte[] pin) throws KineticException;
    
    /**
     * Unlock the device with the specified pin.
     * <p>
     * A successful unLockDevice call will unlock the previous locked device.
     * 
     * @param pin the pin to authenticate to the service.
     * 
     * @throws KineticException if any internal error occurred.
     * 
     * @see #setSecurity(List, byte[], byte[], byte[], byte[])
     */
    public void unLockDevice (byte[] pin) throws KineticException;
    
    /**
     * Set cluster version with the specified version.
     * 
+52 −0
Original line number Diff line number Diff line
/**
 * 
 * Copyright (C) 2014 Seagate Technology.
 * 
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 * 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 General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 *
 */
package com.seagate.kinetic.simulator.internal;

public class DeviceLockedException extends Exception {

    /**
     * 
     */
    private static final long serialVersionUID = 7677042683846221836L;

    public DeviceLockedException() {
       ;
    }

    public DeviceLockedException(String message) {
        super(message);
    }

    public DeviceLockedException(Throwable cause) {
        super(cause);
    }

    public DeviceLockedException(String message, Throwable cause) {
        super(message, cause);
      
    }

    public DeviceLockedException(String message, Throwable cause,
            boolean enableSuppression, boolean writableStackTrace) {
        super(message, cause, enableSuppression, writableStackTrace);
        
    }

}
+148 −99
Original line number Diff line number Diff line
@@ -46,9 +46,8 @@ public abstract class PinOperationHandler {
            .getName());

    public static void handleOperation(KineticMessage request,
            KineticMessage respond, SimulatorEngine engine) throws KVStoreException, KineticException {
        
        boolean hasPermission = false;
            KineticMessage respond, SimulatorEngine engine)
            throws KVStoreException, KineticException {

        Message.Builder messageBuilder = (Message.Builder) respond.getMessage();
        // set pin auth
@@ -57,41 +56,53 @@ public abstract class PinOperationHandler {
        Command.Builder commandBuilder = (Command.Builder) respond.getCommand();

        // set reply type
        commandBuilder.getHeaderBuilder()
        .setMessageType(MessageType.PINOP_RESPONSE);
        commandBuilder.getHeaderBuilder().setMessageType(
                MessageType.PINOP_RESPONSE);

        // set ack sequence
        commandBuilder.getHeaderBuilder()
        .setAckSequence(request.getCommand().getHeader().getSequence());
        
        // check if met TLS requirement
        if (isSecureChannel (request, commandBuilder) == false) {
            
            /**
             * TLS requirement not met, return with INVALID_REQUEST. 
             */
            return;
        }
        commandBuilder.getHeaderBuilder().setAckSequence(
                request.getCommand().getHeader().getSequence());

        // request pin
        ByteString requestPin = request.getMessage().getPinAuth().getPin();

        // request pin op type
        PinOpType pinOpType = request.getCommand().getBody().getPinOp().getPinOpType();
        PinOpType pinOpType = request.getCommand().getBody().getPinOp()
                .getPinOpType();

        try {

            // check if met TLS requirement
            checkSecureChannel(request);

            switch (pinOpType) {
            case LOCK_PINOP:
                
                // check if not empty
                checkRequestPin (requestPin);
                
                // check if has permission
            hasPermission = comparePin (requestPin, engine.getSecurityPin().getLockPin());
            if (hasPermission) {
                comparePin(requestPin, engine.getSecurityPin().getLockPin());

                // lock device
                engine.setDeviceLocked(true);
                
                logger.info("Device locked ...");
            }

                break;
            case UNLOCK_PINOP:
            hasPermission = comparePin (requestPin, engine.getSecurityPin().getLockPin());
            if (hasPermission) {
                 
                // check if not empty
                checkRequestPin (requestPin);
                
                // check if has permission
                comparePin(requestPin, engine.getSecurityPin().getLockPin());
                
                // unlock device
                engine.setDeviceLocked(false);

                logger.info("Device unlocked ...");
            }

                break;
            case ERASE_PINOP:
                // Both erase operations will return
@@ -101,45 +112,60 @@ public abstract class PinOperationHandler {
                // or not. The implication is that it may be faster
                // than the secure operation.

            hasPermission = comparePin (requestPin, engine.getSecurityPin().getErasePin());
            if (hasPermission) {
                
                // reset store
                engine.getStore().reset();
                
                //reset setup
                resetSetup (engine);
                comparePin(requestPin, engine.getSecurityPin().getErasePin());
                
                //reset security
                SecurityHandler.resetSecurity(engine);
            }
                // do erase
                doErase (engine);
                break;
            case SECURE_ERASE_PINOP:
                // Erase the device in a way that will
                // physical access and disassembly of the device
                // will not
            hasPermission = comparePin (requestPin, engine.getSecurityPin().getErasePin());
            if (hasPermission) {
                engine.getStore().reset();
                resetSetup (engine);  
                SecurityHandler.resetSecurity(engine);
            }
                comparePin(requestPin, engine.getSecurityPin().getErasePin());
                
                // do erase
                doErase (engine);
                break;
            case INVALID_PINOP:
            hasPermission = false;
            break;
                throw new InvalidRequestException("Invalid Pin Op Type: "
                        + pinOpType);
            default:
           hasPermission = false;
           break;
                throw new InvalidRequestException("Invalid Pin Op Type: "
                        + pinOpType);

            }

        if (hasPermission == false) {
            commandBuilder.getStatusBuilder().setCode(StatusCode.NOT_AUTHORIZED);
            commandBuilder.getStatusBuilder().setStatusMessage("invalid pin: " + requestPin);
        } catch (KVSecurityException se) {
            commandBuilder.getStatusBuilder()
                    .setCode(StatusCode.NOT_AUTHORIZED);
            commandBuilder.getStatusBuilder().setStatusMessage(se.getMessage());
            logger.warning("unauthorized pin opeartion request");
        } catch (InvalidRequestException ire) {
            commandBuilder.getStatusBuilder().setCode(
                    StatusCode.INVALID_REQUEST);
            commandBuilder.getStatusBuilder()
                    .setStatusMessage(ire.getMessage());
        }

            logger.warning("unauthorized pin opeartion request, pin=" + requestPin);
    }
    
    /**
     * Perform secure erase pin operation.
     * 
     * @param engine
     * @throws KVStoreException
     * @throws KineticException
     */
    public static void doErase(SimulatorEngine engine) throws KVStoreException,
            KineticException {
        // reset store
        engine.getStore().reset();

        // reset setup
        resetSetup(engine);

        // reset security
        SecurityHandler.resetSecurity(engine);
    }
    
    private static void resetSetup(SimulatorEngine engine) {
@@ -160,30 +186,53 @@ public abstract class PinOperationHandler {
     * @param devicePin device pin.
     * @return true if the same, otherwise return false
     */
    private static boolean comparePin (ByteString requestPin, ByteString devicePin) {
        
        boolean hasPermission = false;
    private static void comparePin (ByteString requestPin, ByteString devicePin) throws KVSecurityException {
        
        /**
         * if not set, simply returns.
         */
        if (devicePin == null || devicePin.isEmpty()) {
            // if not set, set to true
            hasPermission = true;   
        } else if (devicePin.equals(requestPin)) {
            // if request pin is the same as drive pin
            hasPermission = true;
            return;
        }
        
        return hasPermission;
        /**
         * compare if pins are equal.
         */
        if (devicePin.equals(requestPin) == false) {
            throw new KVSecurityException ("pin does not match., requestPin=" + requestPin);
        }
    }
    
    private static boolean isSecureChannel (KineticMessage request, Command.Builder respCommandBuilder) {
    /**
     * Check if the request op is under TLS channel.
     * 
     * @param request
     * @throws InvalidRequestException
     */
    private static void checkSecureChannel(KineticMessage request)
            throws InvalidRequestException {

        boolean hasPermission = request.getIsSecureChannel();

        if (hasPermission == false) {
            respCommandBuilder.getStatusBuilder().setCode(StatusCode.INVALID_REQUEST);
            respCommandBuilder.getStatusBuilder().setStatusMessage("TLS channel is required for Pin operation");
            throw new InvalidRequestException(
                    "TLS channel is required for Pin operation");
        }

        return hasPermission;
    }
    
    /**
     * Check if the pin is not null or empty.
     * 
     * @param pin pin to be validated.
     * 
     * @throws InvalidRequestException if pin is null or empty.
     */
    private static void checkRequestPin (ByteString pin) throws InvalidRequestException {
        
        if (pin.isEmpty()) {
            throw new InvalidRequestException ("Pin cannot be empty");
        }
    }  
    
}
+24 −17
Original line number Diff line number Diff line
@@ -23,7 +23,6 @@ import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
@@ -36,7 +35,6 @@ import com.seagate.kinetic.common.lib.HMACAlgorithmUtil;
import com.seagate.kinetic.common.lib.KineticMessage;
import com.seagate.kinetic.common.lib.RoleUtil;
import com.seagate.kinetic.proto.Kinetic.Command;

import com.seagate.kinetic.proto.Kinetic.Command.MessageType;
import com.seagate.kinetic.proto.Kinetic.Command.Security;
import com.seagate.kinetic.proto.Kinetic.Command.Security.ACL;
@@ -93,7 +91,7 @@ public abstract class SecurityHandler {

    public static synchronized Map<Long, ACL> handleSecurity(
            KineticMessage request, KineticMessage response,
            Map<Long, ACL> currentMap, SecurityPin securityPin, String kineticHome)
            SimulatorEngine engine)
            throws KVStoreException, IOException {

        Command.Builder commandBuilder = (Command.Builder) response
@@ -111,7 +109,7 @@ public abstract class SecurityHandler {
                    || !HMACAlgorithmUtil.isSupported(acl.getHmacAlgorithm())) {
                commandBuilder.getStatusBuilder().setCode(
                        StatusCode.NO_SUCH_HMAC_ALGORITHM);
                return currentMap;
                return engine.getAclMap();
            }

            for (ACL.Scope domain : acl.getScopeList()) {
@@ -121,7 +119,7 @@ public abstract class SecurityHandler {
                            StatusCode.INVALID_REQUEST);
                    commandBuilder.getStatusBuilder().setStatusMessage(
                            "Offset in domain is less than 0.");
                    return currentMap;
                    return engine.getAclMap();
                }

                List<Permission> roleOfList = domain.getPermissionList();
@@ -130,7 +128,7 @@ public abstract class SecurityHandler {
                            StatusCode.INVALID_REQUEST);
                    commandBuilder.getStatusBuilder().setStatusMessage(
                            "No role set in acl");
                    return currentMap;
                    return engine.getAclMap();
                }

                for (Permission role : roleOfList) {
@@ -140,7 +138,7 @@ public abstract class SecurityHandler {
                        commandBuilder.getStatusBuilder().setStatusMessage(
                                "Role is invalid in acl. Role is: "
                                        + role.toString());
                        return currentMap;
                        return engine.getAclMap();
                    }
                }
            }
@@ -150,7 +148,7 @@ public abstract class SecurityHandler {
        Security security = request.getCommand().getBody().getSecurity(); 
        
        // get current erase pin
        ByteString currentErasePin = securityPin.getErasePin();
        ByteString currentErasePin = engine.getSecurityPin().getErasePin();
        
        // need to compare if we need the old pin
        if ((currentErasePin != null) && (currentErasePin.isEmpty() == false)) {
@@ -164,16 +162,16 @@ public abstract class SecurityHandler {
                commandBuilder.getStatusBuilder().setStatusMessage(
                        "Invalid old erase pin: " + oldErasePin);
                
                return currentMap;
                return engine.getAclMap();
            } 
        }
        
        // get current lock pin
        ByteString currentLockPin = securityPin.getLockPin();
        ByteString currentLockPin = engine.getSecurityPin().getLockPin();
        
        // need to compare if we need the old pin
        if ((currentLockPin != null) && (currentLockPin.isEmpty() == false)) {
            // get old erase pin
            // get old lock pin
            ByteString oldLockPin = security.getOldLockPIN();
            
            // compare old with current
@@ -183,27 +181,27 @@ public abstract class SecurityHandler {
                commandBuilder.getStatusBuilder().setStatusMessage(
                        "Invalid old lock pin: " + oldLockPin);
                
                return currentMap;
                return engine.getAclMap();
            } 
        }
  
        // update acl map
        for (ACL acl : aclList) {
            currentMap.put(acl.getIdentity(), acl);
            engine.getAclMap().put(acl.getIdentity(), acl);
        }
        
        // set erase pin
        securityPin.setErasePin(security.getNewErasePIN());
        engine.getSecurityPin().setErasePin(security.getNewErasePIN());
              
        //set lock pin
        securityPin.setLockPin(security.getNewLockPIN());
        engine.getSecurityPin().setLockPin(security.getNewLockPIN());
        
        SecurityHandler.persistAcl(request.getCommand().getBody().getSecurity()
                .toByteArray(), kineticHome);
                .toByteArray(), engine.getKineticHome());
        
        commandBuilder.getStatusBuilder().setCode(StatusCode.SUCCESS);

        return currentMap;
        return engine.getAclMap();
    }

    private static void persistAcl(byte[] contents, String kineticHome)
@@ -254,6 +252,14 @@ public abstract class SecurityHandler {
                
                // set lock pin in cache
                engine.getSecurityPin().setLockPin(security.getNewLockPIN());
                
                // lock the device since it was lock enabled
                if (engine.getSecurityPin().getLockPin().isEmpty() == false) {
                    
                    engine.setDeviceLocked(true);
                    
                    logger.warning ("******* Device is locked ********");
                }
            }
        } 
        
@@ -313,4 +319,5 @@ public abstract class SecurityHandler {
        
        logger.info("reset security data to its factory defaults ...");
    }
   
}
Loading