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
24
25
26
27 public final class SecurityNegotiationStage extends TargetLoginStage {
28
29 private static final Logger LOGGER = LoggerFactory.getLogger(SecurityNegotiationStage.class);
30
31
32
33
34
35
36 public SecurityNegotiationStage (TargetLoginPhase targetLoginPhase) {
37 super(targetLoginPhase, LoginStage.SECURITY_NEGOTIATION);
38 }
39
40 @Override
41 public void execute (ProtocolDataUnit initialPdu) throws IOException , InterruptedException , InternetSCSIException , DigestException , SettingsException {
42
43
44 BasicHeaderSegment bhs = initialPdu.getBasicHeaderSegment();
45 initiatorTaskTag = bhs.getInitiatorTaskTag();
46
47 boolean authenticated = false;
48
49 do {
50
51
52
53 final String requestTextParameters = receivePduSequence(initialPdu);
54
55
56 final List<String> requestKeyValuePairs = TextParameter.tokenizeKeyValuePairs(requestTextParameters);
57
58
59 final List<String> authMethodKeyValuePairs = new Vector<String>();
60
61
62 if (LOGGER.isDebugEnabled()) {
63 final StringBuilder sb = new StringBuilder();
64 sb.append("request key value pairs:\n");
65 for (String s : requestKeyValuePairs)
66 sb.append(" " + s + "\n");
67 LOGGER.debug(sb.toString());
68 }
69
70
71
72
73 String authMethodValues = null;
74 if (!authenticated) {
75 for (int i = 0; i < requestKeyValuePairs.size(); ++i) {
76 final String[] split = TextParameter.splitKeyValuePair(requestKeyValuePairs.get(i));
77 if (split == null) {
78 sendRejectPdu(LoginStatus.INITIATOR_ERROR);
79 throw new InternetSCSIException("key=value format error: " + requestKeyValuePairs.get(i));
80 }
81 if (TextKeyword.AUTH_METHOD.equals(split[0])) {
82 authMethodValues = split[1];
83
84 requestKeyValuePairs.remove(i--);
85
86
87
88 } else if (isAuthenticationKey(split[0])) {
89
90 authMethodKeyValuePairs.add(requestKeyValuePairs.remove(i--));
91
92 }
93 }
94 if (authMethodValues == null) {
95 sendRejectPdu(LoginStatus.MISSING_PARAMETER);
96
97
98
99
100
101
102 throw new InternetSCSIException("Missing AuthMethod key-value pair");
103 }
104 }
105
106
107 final Vector<String> responseKeyValuePairs = new Vector<String>();
108
109
110
111
112 if (!negotiator.negotiate(session.getTargetServer(), stageNumber, connection.isLeadingConnection(), ((TargetLoginPhase) targetPhase).getFirstPduAndSetToFalse(), requestKeyValuePairs, responseKeyValuePairs)) {
113
114 sendRejectPdu(LoginStatus.INITIATOR_ERROR);
115 throw new InternetSCSIException("negotiation failure");
116 }
117
118
119 if (!authenticated) {
120 if (authMethodValues.contains(TextKeyword.NONE)) {
121
122 authenticated = true;
123 responseKeyValuePairs.add(TextParameter.toKeyValuePair(TextKeyword.AUTH_METHOD,
124 TextKeyword.NONE));
125
126
127 final String responseString = TextParameter.concatenateKeyValuePairs(responseKeyValuePairs);
128
129 if (LOGGER.isDebugEnabled()) LOGGER.debug("response: " + responseString);
130
131
132 sendPduSequence(responseString, requestedNextStageNumber);
133
134
135 if (requestedNextStageNumber == LoginStage.LOGIN_OPERATIONAL_NEGOTIATION || requestedNextStageNumber == LoginStage.FULL_FEATURE_PHASE) {
136 nextStageNumber = requestedNextStageNumber;
137 return;
138 }
139 } else {
140
141
142 LOGGER.error("initiator attempted CHAP authentication");
143
144 return;
145 }
146
147 }
148
149 } while (!bhs.isFinalFlag() && !authenticated);
150 }
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165 private final boolean isAuthenticationKey (final String key) {
166 if (key == null || key.length() < 5) return false;
167 final String fourChars = key.substring(0, 4);
168 final String fiveChars = key.substring(0, 5);
169 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 return false;
171 }
172 }