Coverage Report - org.jscsi.target.connection.stage.login.SecurityNegotiationStage
 
Classes in this File Line Coverage Branch Coverage Complexity
SecurityNegotiationStage
0%
0/54
0%
0/48
11
 
 1  
 package org.jscsi.target.connection.stage.login;
 2  
 
 3  
 
 4  
 import java.io.IOException;
 5  
 import java.security.DigestException;
 6  
 import java.util.List;
 7  
 import java.util.Vector;
 8  
 
 9  
 import org.jscsi.exception.InternetSCSIException;
 10  
 import org.jscsi.parser.BasicHeaderSegment;
 11  
 import org.jscsi.parser.ProtocolDataUnit;
 12  
 import org.jscsi.parser.login.LoginStage;
 13  
 import org.jscsi.parser.login.LoginStatus;
 14  
 import org.jscsi.target.connection.phase.TargetLoginPhase;
 15  
 import org.jscsi.target.settings.SettingsException;
 16  
 import org.jscsi.target.settings.TextKeyword;
 17  
 import org.jscsi.target.settings.TextParameter;
 18  
 import org.slf4j.Logger;
 19  
 import org.slf4j.LoggerFactory;
 20  
 
 21  
 
 22  
 /**
 23  
  * A {@link TargetLoginStage} sub-class representing Security Negotiation Stages.
 24  
  * 
 25  
  * @author Andreas Ergenzinger
 26  
  */
 27  
 public final class SecurityNegotiationStage extends TargetLoginStage {
 28  
 
 29  0
     private static final Logger LOGGER = LoggerFactory.getLogger(SecurityNegotiationStage.class);
 30  
 
 31  
     /**
 32  
      * The constructor.
 33  
      * 
 34  
      * @param targetLoginPhase the login phase this stage is a part of
 35  
      */
 36  
     public SecurityNegotiationStage (TargetLoginPhase targetLoginPhase) {
 37  0
         super(targetLoginPhase, LoginStage.SECURITY_NEGOTIATION);
 38  0
     }
 39  
 
 40  
     @Override
 41  
     public void execute (ProtocolDataUnit initialPdu) throws IOException , InterruptedException , InternetSCSIException , DigestException , SettingsException {
 42  
 
 43  
         // "receive" initial PDU
 44  0
         BasicHeaderSegment bhs = initialPdu.getBasicHeaderSegment();
 45  0
         initiatorTaskTag = bhs.getInitiatorTaskTag();
 46  
 
 47  0
         boolean authenticated = false;
 48  
 
 49  
         do {// while initiator is not willing and not authorized to transit to
 50  
             // next stage
 51  
 
 52  
             // build text parameter string from current login PDU sequence
 53  0
             final String requestTextParameters = receivePduSequence(initialPdu);
 54  
 
 55  
             // split key-value pairs
 56  0
             final List<String> requestKeyValuePairs = TextParameter.tokenizeKeyValuePairs(requestTextParameters);
 57  
 
 58  
             // Vector for AuthMethod keys
 59  0
             final List<String> authMethodKeyValuePairs = new Vector<String>();
 60  
 
 61  
             // log initiator's key-value pairs
 62  0
             if (LOGGER.isDebugEnabled()) {
 63  0
                 final StringBuilder sb = new StringBuilder();
 64  0
                 sb.append("request key value pairs:\n");
 65  0
                 for (String s : requestKeyValuePairs)
 66  0
                     sb.append("   " + s + "\n");
 67  0
                 LOGGER.debug(sb.toString());
 68  
             }
 69  
 
 70  
             // extract available AuthMethod key-value pair, so that settings can
 71  
             // finish
 72  
             // processing the other parameters before authorization begins
 73  0
             String authMethodValues = null;
 74  0
             if (!authenticated) {// authentication part one
 75  0
                 for (int i = 0; i < requestKeyValuePairs.size(); ++i) {
 76  0
                     final String[] split = TextParameter.splitKeyValuePair(requestKeyValuePairs.get(i));
 77  0
                     if (split == null) {
 78  0
                         sendRejectPdu(LoginStatus.INITIATOR_ERROR);
 79  0
                         throw new InternetSCSIException("key=value format error: " + requestKeyValuePairs.get(i));
 80  
                     }
 81  0
                     if (TextKeyword.AUTH_METHOD.equals(split[0])) {
 82  0
                         authMethodValues = split[1];
 83  
                         // remove key-value pair from Vector
 84  0
                         requestKeyValuePairs.remove(i--);// correct for shifted
 85  
                                                          // indices
 86  
                         // no break here to catch all authMethodKeyValuePairs in
 87  
                         // else block
 88  0
                     } else if (isAuthenticationKey(split[0])) {
 89  
                         // move key-value pair to authMethodKeyValuePairs
 90  0
                         authMethodKeyValuePairs.add(requestKeyValuePairs.remove(i--));// correct for shifted
 91  
                                                                                       // indices
 92  
                     }
 93  
                 }
 94  0
                 if (authMethodValues == null) {// missing AuthMethod key
 95  0
                     sendRejectPdu(LoginStatus.MISSING_PARAMETER);// require
 96  
                                                                  // AuthMethod
 97  
                                                                  // to be
 98  
                                                                  // specified in
 99  
                                                                  // first PDU
 100  
                                                                  // sequence
 101  
                     // close connection
 102  0
                     throw new InternetSCSIException("Missing AuthMethod key-value pair");
 103  
                 }
 104  
             }
 105  
 
 106  
             // negotiate remaining parameters
 107  0
             final Vector<String> responseKeyValuePairs = new Vector<String>();// these
 108  
                                                                               // will
 109  
                                                                               // be
 110  
                                                                               // sent
 111  
                                                                               // back
 112  0
             if (!negotiator.negotiate(session.getTargetServer(), stageNumber, connection.isLeadingConnection(), ((TargetLoginPhase) targetPhase).getFirstPduAndSetToFalse(), requestKeyValuePairs, responseKeyValuePairs)) {
 113  
                 // negotiation error
 114  0
                 sendRejectPdu(LoginStatus.INITIATOR_ERROR);
 115  0
                 throw new InternetSCSIException("negotiation failure");
 116  
             }
 117  
 
 118  
             // ** authentication ** (part two)
 119  0
             if (!authenticated) {
 120  0
                 if (authMethodValues.contains(TextKeyword.NONE)) {
 121  
 
 122  0
                     authenticated = true;
 123  0
                     responseKeyValuePairs.add(TextParameter.toKeyValuePair(TextKeyword.AUTH_METHOD,// key
 124  
                             TextKeyword.NONE));// value
 125  
 
 126  
                     // concatenate key value pairs to single string
 127  0
                     final String responseString = TextParameter.concatenateKeyValuePairs(responseKeyValuePairs);
 128  
 
 129  0
                     if (LOGGER.isDebugEnabled()) LOGGER.debug("response: " + responseString);
 130  
 
 131  
                     // send reply (sequence), set transit bit of last PDU
 132  0
                     sendPduSequence(responseString, requestedNextStageNumber);
 133  
 
 134  
                     // leave this (and proceed to next) stage
 135  0
                     if (requestedNextStageNumber == LoginStage.LOGIN_OPERATIONAL_NEGOTIATION || requestedNextStageNumber == LoginStage.FULL_FEATURE_PHASE) {
 136  0
                         nextStageNumber = requestedNextStageNumber;
 137  0
                         return;
 138  
                     }
 139  0
                 } else {
 140  
                     // TODO support CHAP (and use String
 141  
                     // authMethodKeyValuePairs)
 142  0
                     LOGGER.error("initiator attempted CHAP authentication");
 143  
                     // nextStageNumber = null;//no change
 144  0
                     return;
 145  
                 }
 146  
 
 147  
             }
 148  
 
 149  0
         } while (!bhs.isFinalFlag() && !authenticated);
 150  0
     }
 151  
 
 152  
     /**
 153  
      * Checks if a the parameter is the key of an AuthMethod Key, which means one of the following (where &#60;key&#62;
 154  
      * depends on the AuthMethod prefix):
 155  
      * <ul>
 156  
      * <li>CHAP_&#60;key&#62;/il>
 157  
      * <li>KRB_&#60;key&#62;/il>
 158  
      * <li>SPKM_&#60;key&#62;/il>
 159  
      * <li>SRP_&#60;key&#62;/il>
 160  
      * </ul>
 161  
      * 
 162  
      * @param <i>key</i> part of a <i>key-value</i> pair
 163  
      * @return <code>true</code> if the String is an AuthMethod key, <code>false</code> if it is not.
 164  
      */
 165  
     private final boolean isAuthenticationKey (final String key) {
 166  0
         if (key == null || key.length() < 5) return false;
 167  0
         final String fourChars = key.substring(0, 4);
 168  0
         final String fiveChars = key.substring(0, 5);
 169  0
         if ("CHAP_".matches(fiveChars) || "KRB_".matches(fourChars) || "SPKM_".matches(fiveChars) || "SRP_".matches(fourChars) || (key.length() >= 10 && "TargetAuth".matches(key.substring(0, 10)))) return true;
 170  0
         return false;
 171  
     }
 172  
 }