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 ******************************************************************************/
15package com.novell.sasl.client;
16
17import java.util.*;
18import org.apache.harmony.javax.security.sasl.*;
19
20/**
21 * Implements the DigestChallenge class which will be used by the
22 * DigestMD5SaslClient class
23 */
24class 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