| 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 */  | 
 | 
 | 
 | 
package com.sun.security.sasl.util;  | 
 | 
 | 
 | 
import javax.security.sasl.*;  | 
 | 
import java.io.*;  | 
 | 
import java.util.Map;  | 
 | 
import java.util.StringTokenizer;  | 
 | 
 | 
 | 
import java.util.logging.Logger;  | 
 | 
import java.util.logging.Level;  | 
 | 
 | 
 | 
import sun.misc.HexDumpEncoder;  | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 */  | 
 | 
public abstract class AbstractSaslImpl { | 
 | 
 | 
 | 
    protected boolean completed = false;  | 
 | 
    protected boolean privacy = false;  | 
 | 
    protected boolean integrity = false;  | 
 | 
    protected byte[] qop;             | 
 | 
    protected byte allQop;            | 
 | 
    protected byte[] strength;        | 
 | 
 | 
 | 
    // These are relevant only when privacy or integray have been negotiated  | 
 | 
    protected int sendMaxBufSize = 0;       | 
 | 
    protected int recvMaxBufSize = 65536;   | 
 | 
    protected int rawSendSize;              | 
 | 
 | 
 | 
    protected String myClassName;  | 
 | 
 | 
 | 
    protected AbstractSaslImpl(Map<String, ?> props, String className)  | 
 | 
            throws SaslException { | 
 | 
        myClassName = className;  | 
 | 
 | 
 | 
          | 
 | 
        if (props != null) { | 
 | 
            String prop;  | 
 | 
 | 
 | 
              | 
 | 
            qop = parseQop(prop=(String)props.get(Sasl.QOP));  | 
 | 
            logger.logp(Level.FINE, myClassName, "constructor",  | 
 | 
                "SASLIMPL01:Preferred qop property: {0}", prop); | 
 | 
            allQop = combineMasks(qop);  | 
 | 
 | 
 | 
            if (logger.isLoggable(Level.FINE)) { | 
 | 
                logger.logp(Level.FINE, myClassName, "constructor",  | 
 | 
                    "SASLIMPL02:Preferred qop mask: {0}", new Byte(allQop)); | 
 | 
 | 
 | 
                if (qop.length > 0) { | 
 | 
                    StringBuffer qopbuf = new StringBuffer();  | 
 | 
                    for (int i = 0; i < qop.length; i++) { | 
 | 
                        qopbuf.append(Byte.toString(qop[i]));  | 
 | 
                        qopbuf.append(' '); | 
 | 
                    }  | 
 | 
                    logger.logp(Level.FINE, myClassName, "constructor",  | 
 | 
                        "SASLIMPL03:Preferred qops : {0}", qopbuf.toString()); | 
 | 
                }  | 
 | 
            }  | 
 | 
 | 
 | 
              | 
 | 
            strength = parseStrength(prop=(String)props.get(Sasl.STRENGTH));  | 
 | 
            logger.logp(Level.FINE, myClassName, "constructor",  | 
 | 
                "SASLIMPL04:Preferred strength property: {0}", prop); | 
 | 
            if (logger.isLoggable(Level.FINE) && strength.length > 0) { | 
 | 
                StringBuffer strbuf = new StringBuffer();  | 
 | 
                for (int i = 0; i < strength.length; i++) { | 
 | 
                    strbuf.append(Byte.toString(strength[i]));  | 
 | 
                    strbuf.append(' '); | 
 | 
                }  | 
 | 
                logger.logp(Level.FINE, myClassName, "constructor",  | 
 | 
                    "SASLIMPL05:Cipher strengths: {0}", strbuf.toString()); | 
 | 
            }  | 
 | 
 | 
 | 
              | 
 | 
            prop = (String)props.get(Sasl.MAX_BUFFER);  | 
 | 
            if (prop != null) { | 
 | 
                try { | 
 | 
                    logger.logp(Level.FINE, myClassName, "constructor",  | 
 | 
                        "SASLIMPL06:Max receive buffer size: {0}", prop); | 
 | 
                    recvMaxBufSize = Integer.parseInt(prop);  | 
 | 
                } catch (NumberFormatException e) { | 
 | 
                    throw new SaslException(  | 
 | 
                "Property must be string representation of integer: " +  | 
 | 
                        Sasl.MAX_BUFFER);  | 
 | 
                }  | 
 | 
            }  | 
 | 
 | 
 | 
              | 
 | 
            prop = (String)props.get(MAX_SEND_BUF);  | 
 | 
            if (prop != null) { | 
 | 
                try { | 
 | 
                    logger.logp(Level.FINE, myClassName, "constructor",  | 
 | 
                        "SASLIMPL07:Max send buffer size: {0}", prop); | 
 | 
                    sendMaxBufSize = Integer.parseInt(prop);  | 
 | 
                } catch (NumberFormatException e) { | 
 | 
                    throw new SaslException(  | 
 | 
                "Property must be string representation of integer: " +  | 
 | 
                        MAX_SEND_BUF);  | 
 | 
                }  | 
 | 
            }  | 
 | 
        } else { | 
 | 
            qop = DEFAULT_QOP;  | 
 | 
            allQop = NO_PROTECTION;  | 
 | 
            strength = STRENGTH_MASKS;  | 
 | 
        }  | 
 | 
    }  | 
 | 
 | 
 | 
      | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
     */  | 
 | 
    public boolean isComplete() { | 
 | 
        return completed;  | 
 | 
    }  | 
 | 
 | 
 | 
      | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
     */  | 
 | 
    public Object getNegotiatedProperty(String propName) { | 
 | 
        if (!completed) { | 
 | 
            throw new IllegalStateException("SASL authentication not completed"); | 
 | 
        }  | 
 | 
        switch (propName) { | 
 | 
            case Sasl.QOP:  | 
 | 
                if (privacy) { | 
 | 
                    return "auth-conf";  | 
 | 
                } else if (integrity) { | 
 | 
                    return "auth-int";  | 
 | 
                } else { | 
 | 
                    return "auth";  | 
 | 
                }  | 
 | 
            case Sasl.MAX_BUFFER:  | 
 | 
                return Integer.toString(recvMaxBufSize);  | 
 | 
            case Sasl.RAW_SEND_SIZE:  | 
 | 
                return Integer.toString(rawSendSize);  | 
 | 
            case MAX_SEND_BUF:  | 
 | 
                return Integer.toString(sendMaxBufSize);  | 
 | 
            default:  | 
 | 
                return null;  | 
 | 
        }  | 
 | 
    }  | 
 | 
 | 
 | 
    protected static final byte combineMasks(byte[] in) { | 
 | 
        byte answer = 0;  | 
 | 
        for (int i = 0; i < in.length; i++) { | 
 | 
            answer |= in[i];  | 
 | 
        }  | 
 | 
        return answer;  | 
 | 
    }  | 
 | 
 | 
 | 
    protected static final byte findPreferredMask(byte pref, byte[] in) { | 
 | 
        for (int i = 0; i < in.length; i++) { | 
 | 
            if ((in[i]&pref) != 0) { | 
 | 
                return in[i];  | 
 | 
            }  | 
 | 
        }  | 
 | 
        return (byte)0;  | 
 | 
    }  | 
 | 
 | 
 | 
    private static final byte[] parseQop(String qop) throws SaslException { | 
 | 
        return parseQop(qop, null, false);  | 
 | 
    }  | 
 | 
 | 
 | 
    protected static final byte[] parseQop(String qop, String[] saveTokens,  | 
 | 
        boolean ignore) throws SaslException { | 
 | 
        if (qop == null) { | 
 | 
            return DEFAULT_QOP;     | 
 | 
        }  | 
 | 
 | 
 | 
        return parseProp(Sasl.QOP, qop, QOP_TOKENS, QOP_MASKS, saveTokens, ignore);  | 
 | 
    }  | 
 | 
 | 
 | 
    private static final byte[] parseStrength(String strength)  | 
 | 
        throws SaslException { | 
 | 
        if (strength == null) { | 
 | 
            return DEFAULT_STRENGTH;     | 
 | 
        }  | 
 | 
 | 
 | 
        return parseProp(Sasl.STRENGTH, strength, STRENGTH_TOKENS,  | 
 | 
            STRENGTH_MASKS, null, false);  | 
 | 
    }  | 
 | 
 | 
 | 
    private static final byte[] parseProp(String propName, String propVal,  | 
 | 
        String[] vals, byte[] masks, String[] tokens, boolean ignore)  | 
 | 
        throws SaslException { | 
 | 
 | 
 | 
        StringTokenizer parser = new StringTokenizer(propVal, ", \t\n");  | 
 | 
        String token;  | 
 | 
        byte[] answer = new byte[vals.length];  | 
 | 
        int i = 0;  | 
 | 
        boolean found;  | 
 | 
 | 
 | 
        while (parser.hasMoreTokens() && i < answer.length) { | 
 | 
            token = parser.nextToken();  | 
 | 
            found = false;  | 
 | 
            for (int j = 0; !found && j < vals.length; j++) { | 
 | 
                if (token.equalsIgnoreCase(vals[j])) { | 
 | 
                    found = true;  | 
 | 
                    answer[i++] = masks[j];  | 
 | 
                    if (tokens != null) { | 
 | 
                        tokens[j] = token;      | 
 | 
                    }  | 
 | 
                }  | 
 | 
            }  | 
 | 
            if (!found && !ignore) { | 
 | 
                throw new SaslException(  | 
 | 
                    "Invalid token in " + propName + ": " + propVal);  | 
 | 
            }  | 
 | 
        }  | 
 | 
          | 
 | 
        for (int j = i; j < answer.length; j++) { | 
 | 
            answer[j] = 0;  | 
 | 
        }  | 
 | 
        return answer;  | 
 | 
    }  | 
 | 
 | 
 | 
 | 
 | 
      | 
 | 
 | 
 | 
     */  | 
 | 
    protected static final void traceOutput(String srcClass, String srcMethod,  | 
 | 
        String traceTag, byte[] output) { | 
 | 
        traceOutput(srcClass, srcMethod, traceTag, output, 0,  | 
 | 
                output == null ? 0 : output.length);  | 
 | 
    }  | 
 | 
 | 
 | 
    protected static final void traceOutput(String srcClass, String srcMethod,  | 
 | 
        String traceTag, byte[] output, int offset, int len) { | 
 | 
        try { | 
 | 
            int origlen = len;  | 
 | 
            Level lev;  | 
 | 
 | 
 | 
            if (!logger.isLoggable(Level.FINEST)) { | 
 | 
                len = Math.min(16, len);  | 
 | 
                lev = Level.FINER;  | 
 | 
            } else { | 
 | 
                lev = Level.FINEST;  | 
 | 
            }  | 
 | 
 | 
 | 
            String content;  | 
 | 
 | 
 | 
            if (output != null) { | 
 | 
                ByteArrayOutputStream out = new ByteArrayOutputStream(len);  | 
 | 
                new HexDumpEncoder().encodeBuffer(  | 
 | 
                    new ByteArrayInputStream(output, offset, len), out);  | 
 | 
                content = out.toString();  | 
 | 
            } else { | 
 | 
                content = "NULL";  | 
 | 
            }  | 
 | 
 | 
 | 
              | 
 | 
            logger.logp(lev, srcClass, srcMethod, "{0} ( {1} ): {2}", | 
 | 
                new Object[] {traceTag, new Integer(origlen), content}); | 
 | 
        } catch (Exception e) { | 
 | 
            logger.logp(Level.WARNING, srcClass, srcMethod,  | 
 | 
                "SASLIMPL09:Error generating trace output: {0}", e); | 
 | 
        }  | 
 | 
    }  | 
 | 
 | 
 | 
 | 
 | 
      | 
 | 
 | 
 | 
     */  | 
 | 
    protected static final int networkByteOrderToInt(byte[] buf, int start,  | 
 | 
        int count) { | 
 | 
        if (count > 4) { | 
 | 
            throw new IllegalArgumentException("Cannot handle more than 4 bytes"); | 
 | 
        }  | 
 | 
 | 
 | 
        int answer = 0;  | 
 | 
 | 
 | 
        for (int i = 0; i < count; i++) { | 
 | 
            answer <<= 8;  | 
 | 
            answer |= ((int)buf[start+i] & 0xff);  | 
 | 
        }  | 
 | 
        return answer;  | 
 | 
    }  | 
 | 
 | 
 | 
      | 
 | 
 | 
 | 
 | 
 | 
     */  | 
 | 
    protected static final void intToNetworkByteOrder(int num, byte[] buf,  | 
 | 
        int start, int count) { | 
 | 
        if (count > 4) { | 
 | 
            throw new IllegalArgumentException("Cannot handle more than 4 bytes"); | 
 | 
        }  | 
 | 
 | 
 | 
        for (int i = count-1; i >= 0; i--) { | 
 | 
            buf[start+i] = (byte)(num & 0xff);  | 
 | 
            num >>>= 8;  | 
 | 
        }  | 
 | 
    }  | 
 | 
 | 
 | 
      | 
 | 
    private static final String SASL_LOGGER_NAME = "javax.security.sasl";  | 
 | 
    protected static final String MAX_SEND_BUF = "javax.security.sasl.sendmaxbuffer";  | 
 | 
 | 
 | 
      | 
 | 
 | 
 | 
     */  | 
 | 
    protected static final Logger logger = Logger.getLogger(SASL_LOGGER_NAME);  | 
 | 
 | 
 | 
      | 
 | 
    protected static final byte NO_PROTECTION = (byte)1;  | 
 | 
    protected static final byte INTEGRITY_ONLY_PROTECTION = (byte)2;  | 
 | 
    protected static final byte PRIVACY_PROTECTION = (byte)4;  | 
 | 
 | 
 | 
    protected static final byte LOW_STRENGTH = (byte)1;  | 
 | 
    protected static final byte MEDIUM_STRENGTH = (byte)2;  | 
 | 
    protected static final byte HIGH_STRENGTH = (byte)4;  | 
 | 
 | 
 | 
    private static final byte[] DEFAULT_QOP = new byte[]{NO_PROTECTION}; | 
 | 
    private static final String[] QOP_TOKENS = {"auth-conf", | 
 | 
                                       "auth-int",  | 
 | 
                                       "auth"};  | 
 | 
    private static final byte[] QOP_MASKS = {PRIVACY_PROTECTION, | 
 | 
                                     INTEGRITY_ONLY_PROTECTION,  | 
 | 
                                     NO_PROTECTION};  | 
 | 
 | 
 | 
    private static final byte[] DEFAULT_STRENGTH = new byte[]{ | 
 | 
        HIGH_STRENGTH, MEDIUM_STRENGTH, LOW_STRENGTH};  | 
 | 
    private static final String[] STRENGTH_TOKENS = {"low", | 
 | 
                                                     "medium",  | 
 | 
                                                     "high"};  | 
 | 
    private static final byte[] STRENGTH_MASKS = {LOW_STRENGTH, | 
 | 
                                                  MEDIUM_STRENGTH,  | 
 | 
                                                  HIGH_STRENGTH};  | 
 | 
}  |