0N/A/*
2362N/A * Copyright (c) 2000, 2009, Oracle and/or its affiliates. All rights reserved.
0N/A * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
0N/A *
0N/A * This code is free software; you can redistribute it and/or modify it
0N/A * under the terms of the GNU General Public License version 2 only, as
2362N/A * published by the Free Software Foundation. Oracle designates this
0N/A * particular file as subject to the "Classpath" exception as provided
2362N/A * by Oracle in the LICENSE file that accompanied this code.
0N/A *
0N/A * This code is distributed in the hope that it will be useful, but WITHOUT
0N/A * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
0N/A * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
0N/A * version 2 for more details (a copy is included in the LICENSE file that
0N/A * accompanied this code).
0N/A *
0N/A * You should have received a copy of the GNU General Public License version
0N/A * 2 along with this work; if not, write to the Free Software Foundation,
0N/A * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
0N/A *
2362N/A * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
2362N/A * or visit www.oracle.com if you need additional information or have any
2362N/A * questions.
0N/A */
0N/A
0N/Apackage com.sun.security.sasl.util;
0N/A
0N/Aimport javax.security.sasl.*;
0N/Aimport java.io.*;
0N/Aimport java.util.Map;
0N/Aimport java.util.StringTokenizer;
0N/Aimport java.security.AccessController;
0N/Aimport java.security.PrivilegedAction;
0N/A
0N/Aimport java.util.logging.Logger;
0N/Aimport java.util.logging.Level;
0N/A
0N/Aimport sun.misc.HexDumpEncoder;
0N/A
0N/A/**
0N/A * The base class used by client and server implementations of SASL
0N/A * mechanisms to process properties passed in the props argument
0N/A * and strings with the same format (e.g., used in digest-md5).
0N/A *
0N/A * Also contains utilities for doing int to network-byte-order
0N/A * transformations.
0N/A *
0N/A * @author Rosanna Lee
0N/A */
0N/Apublic abstract class AbstractSaslImpl {
0N/A
0N/A protected boolean completed = false;
0N/A protected boolean privacy = false;
0N/A protected boolean integrity = false;
0N/A protected byte[] qop; // ordered list of qops
0N/A protected byte allQop; // a mask indicating which QOPs are requested
0N/A protected byte[] strength; // ordered list of cipher strengths
0N/A
0N/A // These are relevant only when privacy or integray have been negotiated
0N/A protected int sendMaxBufSize = 0; // specified by peer but can override
0N/A protected int recvMaxBufSize = 65536; // optionally specified by self
0N/A protected int rawSendSize; // derived from sendMaxBufSize
0N/A
0N/A protected String myClassName;
0N/A
0N/A protected AbstractSaslImpl(Map props, String className) throws SaslException {
0N/A myClassName = className;
0N/A
0N/A // Parse properties to set desired context options
0N/A if (props != null) {
0N/A String prop;
0N/A
0N/A // "auth", "auth-int", "auth-conf"
0N/A qop = parseQop(prop=(String)props.get(Sasl.QOP));
0N/A logger.logp(Level.FINE, myClassName, "constructor",
0N/A "SASLIMPL01:Preferred qop property: {0}", prop);
0N/A allQop = combineMasks(qop);
0N/A
0N/A if (logger.isLoggable(Level.FINE)) {
0N/A logger.logp(Level.FINE, myClassName, "constructor",
0N/A "SASLIMPL02:Preferred qop mask: {0}", new Byte(allQop));
0N/A
0N/A if (qop.length > 0) {
0N/A StringBuffer qopbuf = new StringBuffer();
0N/A for (int i = 0; i < qop.length; i++) {
0N/A qopbuf.append(Byte.toString(qop[i]));
0N/A qopbuf.append(' ');
0N/A }
0N/A logger.logp(Level.FINE, myClassName, "constructor",
0N/A "SASLIMPL03:Preferred qops : {0}", qopbuf.toString());
0N/A }
0N/A }
0N/A
0N/A // "low", "medium", "high"
0N/A strength = parseStrength(prop=(String)props.get(Sasl.STRENGTH));
0N/A logger.logp(Level.FINE, myClassName, "constructor",
0N/A "SASLIMPL04:Preferred strength property: {0}", prop);
0N/A if (logger.isLoggable(Level.FINE) && strength.length > 0) {
0N/A StringBuffer strbuf = new StringBuffer();
0N/A for (int i = 0; i < strength.length; i++) {
0N/A strbuf.append(Byte.toString(strength[i]));
0N/A strbuf.append(' ');
0N/A }
0N/A logger.logp(Level.FINE, myClassName, "constructor",
0N/A "SASLIMPL05:Cipher strengths: {0}", strbuf.toString());
0N/A }
0N/A
0N/A // Max receive buffer size
0N/A prop = (String)props.get(Sasl.MAX_BUFFER);
0N/A if (prop != null) {
0N/A try {
0N/A logger.logp(Level.FINE, myClassName, "constructor",
0N/A "SASLIMPL06:Max receive buffer size: {0}", prop);
0N/A recvMaxBufSize = Integer.parseInt(prop);
0N/A } catch (NumberFormatException e) {
0N/A throw new SaslException(
0N/A "Property must be string representation of integer: " +
0N/A Sasl.MAX_BUFFER);
0N/A }
0N/A }
0N/A
0N/A // Max send buffer size
0N/A prop = (String)props.get(MAX_SEND_BUF);
0N/A if (prop != null) {
0N/A try {
0N/A logger.logp(Level.FINE, myClassName, "constructor",
0N/A "SASLIMPL07:Max send buffer size: {0}", prop);
0N/A sendMaxBufSize = Integer.parseInt(prop);
0N/A } catch (NumberFormatException e) {
0N/A throw new SaslException(
0N/A "Property must be string representation of integer: " +
0N/A MAX_SEND_BUF);
0N/A }
0N/A }
0N/A } else {
0N/A qop = DEFAULT_QOP;
0N/A allQop = NO_PROTECTION;
0N/A strength = STRENGTH_MASKS;
0N/A }
0N/A }
0N/A
0N/A /**
0N/A * Determines whether this mechanism has completed.
0N/A *
0N/A * @return true if has completed; false otherwise;
0N/A */
0N/A public boolean isComplete() {
0N/A return completed;
0N/A }
0N/A
0N/A /**
0N/A * Retrieves the negotiated property.
0N/A * @exception SaslException if this authentication exchange has not completed
0N/A */
0N/A public Object getNegotiatedProperty(String propName) {
0N/A if (!completed) {
0N/A throw new IllegalStateException("SASL authentication not completed");
0N/A }
0N/A
0N/A if (propName.equals(Sasl.QOP)) {
0N/A if (privacy) {
0N/A return "auth-conf";
0N/A } else if (integrity) {
0N/A return "auth-int";
0N/A } else {
0N/A return "auth";
0N/A }
0N/A } else if (propName.equals(Sasl.MAX_BUFFER)) {
0N/A return Integer.toString(recvMaxBufSize);
0N/A } else if (propName.equals(Sasl.RAW_SEND_SIZE)) {
0N/A return Integer.toString(rawSendSize);
0N/A } else if (propName.equals(MAX_SEND_BUF)) {
0N/A return Integer.toString(sendMaxBufSize);
0N/A } else {
0N/A return null;
0N/A }
0N/A }
0N/A
0N/A protected static final byte combineMasks(byte[] in) {
0N/A byte answer = 0;
0N/A for (int i = 0; i < in.length; i++) {
0N/A answer |= in[i];
0N/A }
0N/A return answer;
0N/A }
0N/A
0N/A protected static final byte findPreferredMask(byte pref, byte[] in) {
0N/A for (int i = 0; i < in.length; i++) {
0N/A if ((in[i]&pref) != 0) {
0N/A return in[i];
0N/A }
0N/A }
0N/A return (byte)0;
0N/A }
0N/A
0N/A private static final byte[] parseQop(String qop) throws SaslException {
0N/A return parseQop(qop, null, false);
0N/A }
0N/A
0N/A protected static final byte[] parseQop(String qop, String[] saveTokens,
0N/A boolean ignore) throws SaslException {
0N/A if (qop == null) {
0N/A return DEFAULT_QOP; // default
0N/A }
0N/A
0N/A return parseProp(Sasl.QOP, qop, QOP_TOKENS, QOP_MASKS, saveTokens, ignore);
0N/A }
0N/A
0N/A private static final byte[] parseStrength(String strength)
0N/A throws SaslException {
0N/A if (strength == null) {
0N/A return DEFAULT_STRENGTH; // default
0N/A }
0N/A
0N/A return parseProp(Sasl.STRENGTH, strength, STRENGTH_TOKENS,
0N/A STRENGTH_MASKS, null, false);
0N/A }
0N/A
0N/A private static final byte[] parseProp(String propName, String propVal,
0N/A String[] vals, byte[] masks, String[] tokens, boolean ignore)
0N/A throws SaslException {
0N/A
0N/A StringTokenizer parser = new StringTokenizer(propVal, ", \t\n");
0N/A String token;
0N/A byte[] answer = new byte[vals.length];
0N/A int i = 0;
0N/A boolean found;
0N/A
0N/A while (parser.hasMoreTokens() && i < answer.length) {
0N/A token = parser.nextToken();
0N/A found = false;
0N/A for (int j = 0; !found && j < vals.length; j++) {
0N/A if (token.equalsIgnoreCase(vals[j])) {
0N/A found = true;
0N/A answer[i++] = masks[j];
0N/A if (tokens != null) {
0N/A tokens[j] = token; // save what was parsed
0N/A }
0N/A }
0N/A }
0N/A if (!found && !ignore) {
0N/A throw new SaslException(
0N/A "Invalid token in " + propName + ": " + propVal);
0N/A }
0N/A }
0N/A // Initialize rest of array with 0
0N/A for (int j = i; j < answer.length; j++) {
0N/A answer[j] = 0;
0N/A }
0N/A return answer;
0N/A }
0N/A
0N/A
0N/A /**
6446N/A * Outputs a byte array. Can be null.
0N/A */
0N/A protected static final void traceOutput(String srcClass, String srcMethod,
0N/A String traceTag, byte[] output) {
6446N/A traceOutput(srcClass, srcMethod, traceTag, output, 0,
6446N/A output == null ? 0 : output.length);
0N/A }
0N/A
0N/A protected static final void traceOutput(String srcClass, String srcMethod,
0N/A String traceTag, byte[] output, int offset, int len) {
0N/A try {
0N/A int origlen = len;
0N/A Level lev;
0N/A
0N/A if (!logger.isLoggable(Level.FINEST)) {
0N/A len = Math.min(16, len);
0N/A lev = Level.FINER;
0N/A } else {
0N/A lev = Level.FINEST;
0N/A }
0N/A
6446N/A String content;
6446N/A
6446N/A if (output != null) {
6446N/A ByteArrayOutputStream out = new ByteArrayOutputStream(len);
6446N/A new HexDumpEncoder().encodeBuffer(
6446N/A new ByteArrayInputStream(output, offset, len), out);
6446N/A content = out.toString();
6446N/A } else {
6446N/A content = "NULL";
6446N/A }
0N/A
0N/A // Message id supplied by caller as part of traceTag
0N/A logger.logp(lev, srcClass, srcMethod, "{0} ( {1} ): {2}",
6446N/A new Object[] {traceTag, new Integer(origlen), content});
0N/A } catch (Exception e) {
0N/A logger.logp(Level.WARNING, srcClass, srcMethod,
0N/A "SASLIMPL09:Error generating trace output: {0}", e);
0N/A }
0N/A }
0N/A
0N/A
0N/A /**
0N/A * Returns the integer represented by 4 bytes in network byte order.
0N/A */
0N/A protected static final int networkByteOrderToInt(byte[] buf, int start,
0N/A int count) {
0N/A if (count > 4) {
0N/A throw new IllegalArgumentException("Cannot handle more than 4 bytes");
0N/A }
0N/A
0N/A int answer = 0;
0N/A
0N/A for (int i = 0; i < count; i++) {
0N/A answer <<= 8;
0N/A answer |= ((int)buf[start+i] & 0xff);
0N/A }
0N/A return answer;
0N/A }
0N/A
0N/A /**
0N/A * Encodes an integer into 4 bytes in network byte order in the buffer
0N/A * supplied.
0N/A */
0N/A protected static final void intToNetworkByteOrder(int num, byte[] buf,
0N/A int start, int count) {
0N/A if (count > 4) {
0N/A throw new IllegalArgumentException("Cannot handle more than 4 bytes");
0N/A }
0N/A
0N/A for (int i = count-1; i >= 0; i--) {
0N/A buf[start+i] = (byte)(num & 0xff);
0N/A num >>>= 8;
0N/A }
0N/A }
0N/A
0N/A // ---------------- Constants -----------------
0N/A private static final String SASL_LOGGER_NAME = "javax.security.sasl";
0N/A protected static final String MAX_SEND_BUF = "javax.security.sasl.sendmaxbuffer";
0N/A
1525N/A /**
1525N/A * Logger for debug messages
1525N/A */
1525N/A protected static final Logger logger = Logger.getLogger(SASL_LOGGER_NAME);
1525N/A
0N/A // default 0 (no protection); 1 (integrity only)
0N/A protected static final byte NO_PROTECTION = (byte)1;
0N/A protected static final byte INTEGRITY_ONLY_PROTECTION = (byte)2;
0N/A protected static final byte PRIVACY_PROTECTION = (byte)4;
0N/A
0N/A protected static final byte LOW_STRENGTH = (byte)1;
0N/A protected static final byte MEDIUM_STRENGTH = (byte)2;
0N/A protected static final byte HIGH_STRENGTH = (byte)4;
0N/A
0N/A private static final byte[] DEFAULT_QOP = new byte[]{NO_PROTECTION};
0N/A private static final String[] QOP_TOKENS = {"auth-conf",
0N/A "auth-int",
0N/A "auth"};
0N/A private static final byte[] QOP_MASKS = {PRIVACY_PROTECTION,
0N/A INTEGRITY_ONLY_PROTECTION,
0N/A NO_PROTECTION};
0N/A
0N/A private static final byte[] DEFAULT_STRENGTH = new byte[]{
0N/A HIGH_STRENGTH, MEDIUM_STRENGTH, LOW_STRENGTH};
0N/A private static final String[] STRENGTH_TOKENS = {"low",
0N/A "medium",
0N/A "high"};
0N/A private static final byte[] STRENGTH_MASKS = {LOW_STRENGTH,
0N/A MEDIUM_STRENGTH,
0N/A HIGH_STRENGTH};
0N/A}