--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/share/classes/com/sun/security/sasl/util/AbstractSaslImpl.java Sat Dec 01 00:00:00 2007 +0000
@@ -0,0 +1,366 @@
+/*
+ * Copyright 2000-2003 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.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 {
+ /**
+ * Logger for debug messages
+ */
+ protected static Logger logger; // set in initLogger(); lazily loads logger
+
+ 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 {
+ initLogger();
+ 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;
+ }
+ }
+
+ /**
+ * Sets logger field.
+ */
+ private static synchronized void initLogger() {
+ if (logger == null) {
+ logger = Logger.getLogger(SASL_LOGGER_NAME);
+ }
+ }
+
+ // ---------------- Constants -----------------
+ private static final String SASL_LOGGER_NAME = "javax.security.sasl";
+ protected static final String MAX_SEND_BUF = "javax.security.sasl.sendmaxbuffer";
+
+ // 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};
+}