1 /* ************************************************************************** 2 * $OpenLDAP: /com/novell/sasl/client/DigestChallenge.java,v 1.3 2005/01/17 15:00:54 sunilk Exp $ 3 * 4 * Copyright (C) 2003 Novell, Inc. All Rights Reserved. 5 * 6 * THIS WORK IS SUBJECT TO U.S. AND INTERNATIONAL COPYRIGHT LAWS AND 7 * TREATIES. USE, MODIFICATION, AND REDISTRIBUTION OF THIS WORK IS SUBJECT 8 * TO VERSION 2.0.1 OF THE OPENLDAP PUBLIC LICENSE, A COPY OF WHICH IS 9 * AVAILABLE AT HTTP://WWW.OPENLDAP.ORG/LICENSE.HTML OR IN THE FILE "LICENSE" 10 * IN THE TOP-LEVEL DIRECTORY OF THE DISTRIBUTION. ANY USE OR EXPLOITATION 11 * OF THIS WORK OTHER THAN AS AUTHORIZED IN VERSION 2.0.1 OF THE OPENLDAP 12 * PUBLIC LICENSE, OR OTHER PRIOR WRITTEN CONSENT FROM NOVELL, COULD SUBJECT 13 * THE PERPETRATOR TO CRIMINAL AND CIVIL LIABILITY. 14 ******************************************************************************/ 15 package com.novell.sasl.client; 16 17 import java.util.*; 18 import org.apache.harmony.javax.security.sasl.*; 19 20 /** 21 * Implements the DigestChallenge class which will be used by the 22 * DigestMD5SaslClient class 23 */ 24 class DigestChallenge extends Object 25 { 26 public static final int QOP_AUTH = 0x01; 27 public static final int QOP_AUTH_INT = 0x02; 28 public static final int QOP_AUTH_CONF = 0x04; 29 public static final int QOP_UNRECOGNIZED = 0x08; 30 31 private static final int CIPHER_3DES = 0x01; 32 private static final int CIPHER_DES = 0x02; 33 private static final int CIPHER_RC4_40 = 0x04; 34 private static final int CIPHER_RC4 = 0x08; 35 private static final int CIPHER_RC4_56 = 0x10; 36 private static final int CIPHER_UNRECOGNIZED = 0x20; 37 private static final int CIPHER_RECOGNIZED_MASK = 38 CIPHER_3DES | CIPHER_DES | CIPHER_RC4_40 | CIPHER_RC4 | CIPHER_RC4_56; 39 40 private ArrayList m_realms; 41 private String m_nonce; 42 private int m_qop; 43 private boolean m_staleFlag; 44 private int m_maxBuf; 45 private String m_characterSet; 46 private String m_algorithm; 47 private int m_cipherOptions; 48 49 DigestChallenge( 50 byte[] challenge) 51 throws SaslException 52 { 53 m_realms = new ArrayList(5); 54 m_nonce = null; 55 m_qop = 0; 56 m_staleFlag = false; 57 m_maxBuf = -1; 58 m_characterSet = null; 59 m_algorithm = null; 60 m_cipherOptions = 0; 61 62 DirectiveList dirList = new DirectiveList(challenge); 63 try 64 { 65 dirList.parseDirectives(); 66 checkSemantics(dirList); 67 } 68 catch (SaslException e) 69 { 70 } 71 } 72 73 /** 74 * Checks the semantics of the directives in the directive list as parsed 75 * from the digest challenge byte array. 76 * 77 * @param dirList the list of directives parsed from the digest challenge 78 * 79 * @exception SaslException If a semantic error occurs 80 */ 81 void checkSemantics( 82 DirectiveList dirList) throws SaslException 83 { 84 Iterator directives = dirList.getIterator(); 85 ParsedDirective directive; 86 String name; 87 88 while (directives.hasNext()) 89 { 90 directive = (ParsedDirective)directives.next(); 91 name = directive.getName(); 92 if (name.equals("realm")) 93 handleRealm(directive); 94 else if (name.equals("nonce")) 95 handleNonce(directive); 96 else if (name.equals("qop")) 97 handleQop(directive); 98 else if (name.equals("maxbuf")) 99 handleMaxbuf(directive); 100 else if (name.equals("charset")) 101 handleCharset(directive); 102 else if (name.equals("algorithm")) 103 handleAlgorithm(directive); 104 else if (name.equals("cipher")) 105 handleCipher(directive); 106 else if (name.equals("stale")) 107 handleStale(directive); 108 } 109 110 /* post semantic check */ 111 if (-1 == m_maxBuf) 112 m_maxBuf = 65536; 113 114 if (m_qop == 0) 115 m_qop = QOP_AUTH; 116 else if ( (m_qop & QOP_AUTH) != QOP_AUTH ) 117 throw new SaslException("Only qop-auth is supported by client"); 118 else if ( ((m_qop & QOP_AUTH_CONF) == QOP_AUTH_CONF) && 119 (0 == (m_cipherOptions & CIPHER_RECOGNIZED_MASK)) ) 120 throw new SaslException("Invalid cipher options"); 121 else if (null == m_nonce) 122 throw new SaslException("Missing nonce directive"); 123 else if (m_staleFlag) 124 throw new SaslException("Unexpected stale flag"); 125 else if ( null == m_algorithm ) 126 throw new SaslException("Missing algorithm directive"); 127 } 128 129 /** 130 * This function implements the semenatics of the nonce directive. 131 * 132 * @param pd ParsedDirective 133 * 134 * @exception SaslException If an error occurs due to too many nonce 135 * values 136 */ 137 void handleNonce( 138 ParsedDirective pd) throws SaslException 139 { 140 if (null != m_nonce) 141 throw new SaslException("Too many nonce values."); 142 143 m_nonce = pd.getValue(); 144 } 145 146 /** 147 * This function implements the semenatics of the realm directive. 148 * 149 * @param pd ParsedDirective 150 */ 151 void handleRealm( 152 ParsedDirective pd) 153 { 154 m_realms.add(pd.getValue()); 155 } 156 157 /** 158 * This function implements the semenatics of the qop (quality of protection) 159 * directive. The value of the qop directive is as defined below: 160 * qop-options = "qop" "=" <"> qop-list <"> 161 * qop-list = 1#qop-value 162 * qop-value = "auth" | "auth-int" | "auth-conf" | token 163 * 164 * @param pd ParsedDirective 165 * 166 * @exception SaslException If an error occurs due to too many qop 167 * directives 168 */ 169 void handleQop( 170 ParsedDirective pd) throws SaslException 171 { 172 String token; 173 TokenParser parser; 174 175 if (m_qop != 0) 176 throw new SaslException("Too many qop directives."); 177 178 parser = new TokenParser(pd.getValue()); 179 for (token = parser.parseToken(); 180 token != null; 181 token = parser.parseToken()) 182 { 183 if (token.equals("auth")) 184 m_qop |= QOP_AUTH; 185 else if (token.equals("auth-int")) 186 m_qop |= QOP_AUTH_INT; 187 else if (token.equals("auth-conf")) 188 m_qop |= QOP_AUTH_CONF; 189 else 190 m_qop |= QOP_UNRECOGNIZED; 191 } 192 } 193 194 /** 195 * This function implements the semenatics of the Maxbuf directive. 196 * the value is defined as: 1*DIGIT 197 * 198 * @param pd ParsedDirective 199 * 200 * @exception SaslException If an error occur 201 */ 202 void handleMaxbuf( 203 ParsedDirective pd) throws SaslException 204 { 205 if (-1 != m_maxBuf) /*it's initialized to -1 */ 206 throw new SaslException("Too many maxBuf directives."); 207 208 m_maxBuf = Integer.parseInt(pd.getValue()); 209 210 if (0 == m_maxBuf) 211 throw new SaslException("Max buf value must be greater than zero."); 212 } 213 214 /** 215 * This function implements the semenatics of the charset directive. 216 * the value is defined as: 1*DIGIT 217 * 218 * @param pd ParsedDirective 219 * 220 * @exception SaslException If an error occurs dur to too many charset 221 * directives or Invalid character encoding 222 * directive 223 */ 224 void handleCharset( 225 ParsedDirective pd) throws SaslException 226 { 227 if (null != m_characterSet) 228 throw new SaslException("Too many charset directives."); 229 230 m_characterSet = pd.getValue(); 231 232 if (!m_characterSet.equals("utf-8")) 233 throw new SaslException("Invalid character encoding directive"); 234 } 235 236 /** 237 * This function implements the semenatics of the charset directive. 238 * the value is defined as: 1*DIGIT 239 * 240 * @param pd ParsedDirective 241 * 242 * @exception SaslException If an error occurs due to too many algorith 243 * directive or Invalid algorithm directive 244 * value 245 */ 246 void handleAlgorithm( 247 ParsedDirective pd) throws SaslException 248 { 249 if (null != m_algorithm) 250 throw new SaslException("Too many algorithm directives."); 251 252 m_algorithm = pd.getValue(); 253 254 if (!"md5-sess".equals(m_algorithm)) 255 throw new SaslException("Invalid algorithm directive value: " + 256 m_algorithm); 257 } 258 259 /** 260 * This function implements the semenatics of the cipher-opts directive 261 * directive. The value of the qop directive is as defined below: 262 * qop-options = "qop" "=" <"> qop-list <"> 263 * qop-list = 1#qop-value 264 * qop-value = "auth" | "auth-int" | "auth-conf" | token 265 * 266 * @param pd ParsedDirective 267 * 268 * @exception SaslException If an error occurs due to Too many cipher 269 * directives 270 */ 271 void handleCipher( 272 ParsedDirective pd) throws SaslException 273 { 274 String token; 275 TokenParser parser; 276 277 if (0 != m_cipherOptions) 278 throw new SaslException("Too many cipher directives."); 279 280 parser = new TokenParser(pd.getValue()); 281 token = parser.parseToken(); 282 for (token = parser.parseToken(); 283 token != null; 284 token = parser.parseToken()) 285 { 286 if ("3des".equals(token)) 287 m_cipherOptions |= CIPHER_3DES; 288 else if ("des".equals(token)) 289 m_cipherOptions |= CIPHER_DES; 290 else if ("rc4-40".equals(token)) 291 m_cipherOptions |= CIPHER_RC4_40; 292 else if ("rc4".equals(token)) 293 m_cipherOptions |= CIPHER_RC4; 294 else if ("rc4-56".equals(token)) 295 m_cipherOptions |= CIPHER_RC4_56; 296 else 297 m_cipherOptions |= CIPHER_UNRECOGNIZED; 298 } 299 300 if (m_cipherOptions == 0) 301 m_cipherOptions = CIPHER_UNRECOGNIZED; 302 } 303 304 /** 305 * This function implements the semenatics of the stale directive. 306 * 307 * @param pd ParsedDirective 308 * 309 * @exception SaslException If an error occurs due to Too many stale 310 * directives or Invalid stale directive value 311 */ 312 void handleStale( 313 ParsedDirective pd) throws SaslException 314 { 315 if (false != m_staleFlag) 316 throw new SaslException("Too many stale directives."); 317 318 if ("true".equals(pd.getValue())) 319 m_staleFlag = true; 320 else 321 throw new SaslException("Invalid stale directive value: " + 322 pd.getValue()); 323 } 324 325 /** 326 * Return the list of the All the Realms 327 * 328 * @return List of all the realms 329 */ 330 public ArrayList getRealms() 331 { 332 return m_realms; 333 } 334 335 /** 336 * @return Returns the Nonce 337 */ 338 public String getNonce() 339 { 340 return m_nonce; 341 } 342 343 /** 344 * Return the quality-of-protection 345 * 346 * @return The quality-of-protection 347 */ 348 public int getQop() 349 { 350 return m_qop; 351 } 352 353 /** 354 * @return The state of the Staleflag 355 */ 356 public boolean getStaleFlag() 357 { 358 return m_staleFlag; 359 } 360 361 /** 362 * @return The Maximum Buffer value 363 */ 364 public int getMaxBuf() 365 { 366 return m_maxBuf; 367 } 368 369 /** 370 * @return character set values as string 371 */ 372 public String getCharacterSet() 373 { 374 return m_characterSet; 375 } 376 377 /** 378 * @return The String value of the algorithm 379 */ 380 public String getAlgorithm() 381 { 382 return m_algorithm; 383 } 384 385 /** 386 * @return The cipher options 387 */ 388 public int getCipherOptions() 389 { 390 return m_cipherOptions; 391 } 392 } 393 394