357 lines · 12.9 KB · Java
RawDownload
/* * Copyright (c) 2000, 2009, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation.  Oracle designates this * particular file as subject to the "Classpath" exception as provided * by Oracle in the LICENSE file that accompanied this code. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ package com.sun.security.sasl.util; import javax.security.sasl.*;import java.io.*;import java.util.Map;import java.util.StringTokenizer;import java.security.AccessController;import java.security.PrivilegedAction; import java.util.logging.Logger;import java.util.logging.Level; import sun.misc.HexDumpEncoder; /** * The base class used by client and server implementations of SASL * mechanisms to process properties passed in the props argument * and strings with the same format (e.g., used in digest-md5). * * Also contains utilities for doing int to network-byte-order * transformations. * * @author Rosanna Lee */public abstract class AbstractSaslImpl {     protected boolean completed = false;    protected boolean privacy = false;    protected boolean integrity = false;    protected byte[] qop;           // ordered list of qops    protected byte allQop;          // a mask indicating which QOPs are requested    protected byte[] strength;      // ordered list of cipher strengths     // These are relevant only when privacy or integray have been negotiated    protected int sendMaxBufSize = 0;     // specified by peer but can override    protected int recvMaxBufSize = 65536; // optionally specified by self    protected int rawSendSize;            // derived from sendMaxBufSize     protected String myClassName;     protected AbstractSaslImpl(Map props, String className) throws SaslException {        myClassName = className;         // Parse properties  to set desired context options        if (props != null) {            String prop;             // "auth", "auth-int", "auth-conf"            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());                }            }             // "low", "medium", "high"            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());            }             // Max receive buffer size            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);                }            }             // Max send buffer size            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;        }    }     /**     * Determines whether this mechanism has completed.     *     * @return true if has completed; false otherwise;     */    public boolean isComplete() {        return completed;    }     /**     * Retrieves the negotiated property.     * @exception SaslException if this authentication exchange has not completed     */    public Object getNegotiatedProperty(String propName) {        if (!completed) {            throw new IllegalStateException("SASL authentication not completed");        }         if (propName.equals(Sasl.QOP)) {            if (privacy) {                return "auth-conf";            } else if (integrity) {                return "auth-int";            } else {                return "auth";            }        } else if (propName.equals(Sasl.MAX_BUFFER)) {            return Integer.toString(recvMaxBufSize);        } else if (propName.equals(Sasl.RAW_SEND_SIZE)) {            return Integer.toString(rawSendSize);        } else if (propName.equals(MAX_SEND_BUF)) {            return Integer.toString(sendMaxBufSize);        } else {            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;   // default        }         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;   // default        }         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;    // save what was parsed                    }                }            }            if (!found && !ignore) {                throw new SaslException(                    "Invalid token in " + propName + ": " + propVal);            }        }        // Initialize rest of array with 0        for (int j = i; j < answer.length; j++) {            answer[j] = 0;        }        return answer;    }      /**     * Outputs a byte array and converts     */    protected static final void traceOutput(String srcClass, String srcMethod,        String traceTag, byte[] output) {        traceOutput(srcClass, srcMethod, traceTag, output, 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;            }             ByteArrayOutputStream out = new ByteArrayOutputStream(len);            new HexDumpEncoder().encodeBuffer(                new ByteArrayInputStream(output, offset, len), out);             // Message id supplied by caller as part of traceTag            logger.logp(lev, srcClass, srcMethod, "{0} ( {1} ): {2}",                new Object[] {traceTag, new Integer(origlen), out.toString()});        } catch (Exception e) {            logger.logp(Level.WARNING, srcClass, srcMethod,                "SASLIMPL09:Error generating trace output: {0}", e);        }    }      /**     * Returns the integer represented by  4 bytes in network byte order.     */    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;    }     /**     * Encodes an integer into 4 bytes in network byte order in the buffer     * supplied.     */    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;        }    }     // ---------------- Constants  -----------------    private static final String SASL_LOGGER_NAME = "javax.security.sasl";    protected static final String MAX_SEND_BUF = "javax.security.sasl.sendmaxbuffer";     /**     * Logger for debug messages     */    protected static final Logger logger = Logger.getLogger(SASL_LOGGER_NAME);     // default 0 (no protection); 1 (integrity only)    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};}