zookeeper-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From an...@apache.org
Subject [15/51] [partial] zookeeper git commit: ZOOKEEPER-3032: MAVEN MIGRATION - branch-3.5 - zookeeper-server
Date Wed, 24 Oct 2018 09:31:10 GMT
http://git-wip-us.apache.org/repos/asf/zookeeper/blob/43d71c2e/zookeeper-server/src/main/java/org/apache/zookeeper/common/ZKHostnameVerifier.java
----------------------------------------------------------------------
diff --git a/zookeeper-server/src/main/java/org/apache/zookeeper/common/ZKHostnameVerifier.java
b/zookeeper-server/src/main/java/org/apache/zookeeper/common/ZKHostnameVerifier.java
new file mode 100644
index 0000000..740fef0
--- /dev/null
+++ b/zookeeper-server/src/main/java/org/apache/zookeeper/common/ZKHostnameVerifier.java
@@ -0,0 +1,349 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.zookeeper.common;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.naming.InvalidNameException;
+import javax.naming.NamingException;
+import javax.naming.directory.Attribute;
+import javax.naming.directory.Attributes;
+import javax.naming.ldap.LdapName;
+import javax.naming.ldap.Rdn;
+import javax.net.ssl.HostnameVerifier;
+import javax.net.ssl.SSLException;
+import javax.net.ssl.SSLPeerUnverifiedException;
+import javax.net.ssl.SSLSession;
+import javax.security.auth.x500.X500Principal;
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+import java.security.cert.Certificate;
+import java.security.cert.CertificateParsingException;
+import java.security.cert.X509Certificate;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.Locale;
+import java.util.NoSuchElementException;
+import java.util.Objects;
+import java.util.regex.Pattern;
+
+/**
+ * Note: copied from Apache httpclient with some modifications. We want host verification,
but depending
+ * on the httpclient jar caused unexplained performance regressions (even when the code was
not used).
+ */
+final class ZKHostnameVerifier implements HostnameVerifier {
+
+    /**
+     * Note: copied from Apache httpclient with some minor modifications. We want host verification,
but depending
+     * on the httpclient jar caused unexplained performance regressions (even when the code
was not used).
+     */
+    private static final class SubjectName {
+        static final int DNS = 2;
+        static final int IP = 7;
+
+        private final String value;
+        private final int type;
+
+        static SubjectName IP(final String value) {
+            return new SubjectName(value, IP);
+        }
+
+        static SubjectName DNS(final String value) {
+            return new SubjectName(value, DNS);
+        }
+
+        SubjectName(final String value, final int type) {
+            if (type != DNS && type != IP) {
+                throw new IllegalArgumentException("Invalid type: " + type);
+            }
+            this.value = Objects.requireNonNull(value);
+            this.type = type;
+        }
+
+        public int getType() {
+            return type;
+        }
+
+        public String getValue() {
+            return value;
+        }
+
+        @Override
+        public String toString() {
+            return value;
+        }
+    }
+
+    /**
+     * Note: copied from Apache httpclient. We want host verification, but depending on the
+     * httpclient jar caused unexplained performance regressions (even when the code was
not used).
+     */
+    private static class InetAddressUtils {
+        private InetAddressUtils() {}
+
+        private static final Pattern IPV4_PATTERN = Pattern.compile(
+                "^(25[0-5]|2[0-4]\\d|[0-1]?\\d?\\d)(\\.(25[0-5]|2[0-4]\\d|[0-1]?\\d?\\d)){3}$");
+
+        private static final Pattern IPV6_STD_PATTERN = Pattern.compile(
+                "^(?:[0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4}$");
+
+        private static final Pattern IPV6_HEX_COMPRESSED_PATTERN = Pattern.compile(
+                "^((?:[0-9A-Fa-f]{1,4}(?::[0-9A-Fa-f]{1,4})*)?)::((?:[0-9A-Fa-f]{1,4}(?::[0-9A-Fa-f]{1,4})*)?)$");
+
+        static boolean isIPv4Address(final String input) {
+            return IPV4_PATTERN.matcher(input).matches();
+        }
+
+        static boolean isIPv6StdAddress(final String input) {
+            return IPV6_STD_PATTERN.matcher(input).matches();
+        }
+
+        static boolean isIPv6HexCompressedAddress(final String input) {
+            return IPV6_HEX_COMPRESSED_PATTERN.matcher(input).matches();
+        }
+
+        static boolean isIPv6Address(final String input) {
+            return isIPv6StdAddress(input) || isIPv6HexCompressedAddress(input);
+        }
+    }
+
+    enum HostNameType {
+
+        IPv4(7), IPv6(7), DNS(2);
+
+        final int subjectType;
+
+        HostNameType(final int subjectType) {
+            this.subjectType = subjectType;
+        }
+
+    }
+
+    private final Logger log = LoggerFactory.getLogger(ZKHostnameVerifier.class);
+
+    @Override
+    public boolean verify(final String host, final SSLSession session) {
+        try {
+            final Certificate[] certs = session.getPeerCertificates();
+            final X509Certificate x509 = (X509Certificate) certs[0];
+            verify(host, x509);
+            return true;
+        } catch (final SSLException ex) {
+            if (log.isDebugEnabled()) {
+                log.debug(ex.getMessage(), ex);
+            }
+            return false;
+        }
+    }
+
+    void verify(final String host, final X509Certificate cert) throws SSLException {
+        final HostNameType hostType = determineHostFormat(host);
+        final List<SubjectName> subjectAlts = getSubjectAltNames(cert);
+        if (subjectAlts != null && !subjectAlts.isEmpty()) {
+            switch (hostType) {
+                case IPv4:
+                    matchIPAddress(host, subjectAlts);
+                    break;
+                case IPv6:
+                    matchIPv6Address(host, subjectAlts);
+                    break;
+                default:
+                    matchDNSName(host, subjectAlts);
+            }
+        } else {
+            // CN matching has been deprecated by rfc2818 and can be used
+            // as fallback only when no subjectAlts are available
+            final X500Principal subjectPrincipal = cert.getSubjectX500Principal();
+            final String cn = extractCN(subjectPrincipal.getName(X500Principal.RFC2253));
+            if (cn == null) {
+                throw new SSLException("Certificate subject for <" + host + "> doesn't
contain " +
+                        "a common name and does not have alternative names");
+            }
+            matchCN(host, cn);
+        }
+    }
+
+    private static void matchIPAddress(final String host, final List<SubjectName> subjectAlts)
throws SSLException {
+        for (int i = 0; i < subjectAlts.size(); i++) {
+            final SubjectName subjectAlt = subjectAlts.get(i);
+            if (subjectAlt.getType() == SubjectName.IP) {
+                if (host.equals(subjectAlt.getValue())) {
+                    return;
+                }
+            }
+        }
+        throw new SSLPeerUnverifiedException("Certificate for <" + host + "> doesn't
match any " +
+                "of the subject alternative names: " + subjectAlts);
+    }
+
+    private static void matchIPv6Address(final String host, final List<SubjectName>
subjectAlts) throws SSLException {
+        final String normalisedHost = normaliseAddress(host);
+        for (int i = 0; i < subjectAlts.size(); i++) {
+            final SubjectName subjectAlt = subjectAlts.get(i);
+            if (subjectAlt.getType() == SubjectName.IP) {
+                final String normalizedSubjectAlt = normaliseAddress(subjectAlt.getValue());
+                if (normalisedHost.equals(normalizedSubjectAlt)) {
+                    return;
+                }
+            }
+        }
+        throw new SSLPeerUnverifiedException("Certificate for <" + host + "> doesn't
match any " +
+                "of the subject alternative names: " + subjectAlts);
+    }
+
+    private static void matchDNSName(final String host, final List<SubjectName> subjectAlts)
throws SSLException {
+        final String normalizedHost = host.toLowerCase(Locale.ROOT);
+        for (int i = 0; i < subjectAlts.size(); i++) {
+            final SubjectName subjectAlt = subjectAlts.get(i);
+            if (subjectAlt.getType() == SubjectName.DNS) {
+                final String normalizedSubjectAlt = subjectAlt.getValue().toLowerCase(Locale.ROOT);
+                if (matchIdentityStrict(normalizedHost, normalizedSubjectAlt)) {
+                    return;
+                }
+            }
+        }
+        throw new SSLPeerUnverifiedException("Certificate for <" + host + "> doesn't
match any " +
+                "of the subject alternative names: " + subjectAlts);
+    }
+
+    private static void matchCN(final String host, final String cn) throws SSLException {
+        final String normalizedHost = host.toLowerCase(Locale.ROOT);
+        final String normalizedCn = cn.toLowerCase(Locale.ROOT);
+        if (!matchIdentityStrict(normalizedHost, normalizedCn)) {
+            throw new SSLPeerUnverifiedException("Certificate for <" + host + "> doesn't
match " +
+                    "common name of the certificate subject: " + cn);
+        }
+    }
+
+    private static boolean matchIdentity(final String host, final String identity,
+                                         final boolean strict) {
+        // RFC 2818, 3.1. Server Identity
+        // "...Names may contain the wildcard
+        // character * which is considered to match any single domain name
+        // component or component fragment..."
+        // Based on this statement presuming only singular wildcard is legal
+        final int asteriskIdx = identity.indexOf('*');
+        if (asteriskIdx != -1) {
+            final String prefix = identity.substring(0, asteriskIdx);
+            final String suffix = identity.substring(asteriskIdx + 1);
+            if (!prefix.isEmpty() && !host.startsWith(prefix)) {
+                return false;
+            }
+            if (!suffix.isEmpty() && !host.endsWith(suffix)) {
+                return false;
+            }
+            // Additional sanity checks on content selected by wildcard can be done here
+            if (strict) {
+                final String remainder = host.substring(
+                        prefix.length(), host.length() - suffix.length());
+                if (remainder.contains(".")) {
+                    return false;
+                }
+            }
+            return true;
+        }
+        return host.equalsIgnoreCase(identity);
+    }
+
+    private static boolean matchIdentityStrict(final String host, final String identity)
{
+        return matchIdentity(host, identity, true);
+    }
+
+    private static String extractCN(final String subjectPrincipal) throws SSLException {
+        if (subjectPrincipal == null) {
+            return null;
+        }
+        try {
+            final LdapName subjectDN = new LdapName(subjectPrincipal);
+            final List<Rdn> rdns = subjectDN.getRdns();
+            for (int i = rdns.size() - 1; i >= 0; i--) {
+                final Rdn rds = rdns.get(i);
+                final Attributes attributes = rds.toAttributes();
+                final Attribute cn = attributes.get("cn");
+                if (cn != null) {
+                    try {
+                        final Object value = cn.get();
+                        if (value != null) {
+                            return value.toString();
+                        }
+                    } catch (final NoSuchElementException ignore) {
+                        // ignore exception
+                    } catch (final NamingException ignore) {
+                        // ignore exception
+                    }
+                }
+            }
+            return null;
+        } catch (final InvalidNameException e) {
+            throw new SSLException(subjectPrincipal + " is not a valid X500 distinguished
name");
+        }
+    }
+
+    private static HostNameType determineHostFormat(final String host) {
+        if (InetAddressUtils.isIPv4Address(host)) {
+            return HostNameType.IPv4;
+        }
+        String s = host;
+        if (s.startsWith("[") && s.endsWith("]")) {
+            s = host.substring(1, host.length() - 1);
+        }
+        if (InetAddressUtils.isIPv6Address(s)) {
+            return HostNameType.IPv6;
+        }
+        return HostNameType.DNS;
+    }
+
+    private static List<SubjectName> getSubjectAltNames(final X509Certificate cert)
{
+        try {
+            final Collection<List<?>> entries = cert.getSubjectAlternativeNames();
+            if (entries == null) {
+                return Collections.emptyList();
+            }
+            final List<SubjectName> result = new ArrayList<SubjectName>();
+            for (List<?> entry: entries) {
+                final Integer type = entry.size() >= 2 ? (Integer) entry.get(0) : null;
+                if (type != null) {
+                    final String s = (String) entry.get(1);
+                    result.add(new SubjectName(s, type));
+                }
+            }
+            return result;
+        } catch (final CertificateParsingException ignore) {
+            return Collections.emptyList();
+        }
+    }
+
+    /*
+     * Normalize IPv6 or DNS name.
+     */
+    private static String normaliseAddress(final String hostname) {
+        if (hostname == null) {
+            return hostname;
+        }
+        try {
+            final InetAddress inetAddress = InetAddress.getByName(hostname);
+            return inetAddress.getHostAddress();
+        } catch (final UnknownHostException unexpected) { // Should not happen, because we
check for IPv6 address above
+            return hostname;
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/zookeeper/blob/43d71c2e/zookeeper-server/src/main/java/org/apache/zookeeper/common/ZKTrustManager.java
----------------------------------------------------------------------
diff --git a/zookeeper-server/src/main/java/org/apache/zookeeper/common/ZKTrustManager.java
b/zookeeper-server/src/main/java/org/apache/zookeeper/common/ZKTrustManager.java
new file mode 100644
index 0000000..73006d0
--- /dev/null
+++ b/zookeeper-server/src/main/java/org/apache/zookeeper/common/ZKTrustManager.java
@@ -0,0 +1,150 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.zookeeper.common;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.net.ssl.SSLEngine;
+import javax.net.ssl.SSLException;
+import javax.net.ssl.X509ExtendedTrustManager;
+import java.net.InetAddress;
+import java.net.Socket;
+import java.net.UnknownHostException;
+import java.security.cert.CertificateException;
+import java.security.cert.X509Certificate;
+
+/**
+ * A custom TrustManager that supports hostname verification via org.apache.http.conn.ssl.DefaultHostnameVerifier.
+ *
+ * We attempt to perform verification using just the IP address first and if that fails will
attempt to perform a
+ * reverse DNS lookup and verify using the hostname.
+ */
+public class ZKTrustManager extends X509ExtendedTrustManager {
+
+    private static final Logger LOG = LoggerFactory.getLogger(ZKTrustManager.class);
+
+    private X509ExtendedTrustManager x509ExtendedTrustManager;
+    private boolean serverHostnameVerificationEnabled;
+    private boolean clientHostnameVerificationEnabled;
+
+    private ZKHostnameVerifier hostnameVerifier;
+
+    /**
+     * Instantiate a new ZKTrustManager.
+     *
+     * @param x509ExtendedTrustManager The trustmanager to use for checkClientTrusted/checkServerTrusted
logic
+     * @param serverHostnameVerificationEnabled  If true, this TrustManager should verify
hostnames of servers that this
+     *                                 instance connects to.
+     * @param clientHostnameVerificationEnabled  If true, the hostname of a client connecting
to this machine will be
+     *                                           verified.
+     */
+    ZKTrustManager(X509ExtendedTrustManager x509ExtendedTrustManager, boolean serverHostnameVerificationEnabled,
+                   boolean clientHostnameVerificationEnabled) {
+        this.x509ExtendedTrustManager = x509ExtendedTrustManager;
+        this.serverHostnameVerificationEnabled = serverHostnameVerificationEnabled;
+        this.clientHostnameVerificationEnabled = clientHostnameVerificationEnabled;
+        hostnameVerifier = new ZKHostnameVerifier();
+    }
+
+    @Override
+    public X509Certificate[] getAcceptedIssuers() {
+        return x509ExtendedTrustManager.getAcceptedIssuers();
+    }
+
+    @Override
+    public void checkClientTrusted(X509Certificate[] chain, String authType, Socket socket)
throws CertificateException {
+        x509ExtendedTrustManager.checkClientTrusted(chain, authType, socket);
+        if (clientHostnameVerificationEnabled) {
+            performHostVerification(socket.getInetAddress(), chain[0]);
+        }
+    }
+
+    @Override
+    public void checkServerTrusted(X509Certificate[] chain, String authType, Socket socket)
throws CertificateException {
+        x509ExtendedTrustManager.checkServerTrusted(chain, authType, socket);
+        if (serverHostnameVerificationEnabled) {
+            performHostVerification(socket.getInetAddress(), chain[0]);
+        }
+    }
+
+    @Override
+    public void checkClientTrusted(X509Certificate[] chain, String authType, SSLEngine engine)
throws CertificateException {
+        x509ExtendedTrustManager.checkServerTrusted(chain, authType, engine);
+        if (clientHostnameVerificationEnabled) {
+            try {
+                performHostVerification(InetAddress.getByName(engine.getPeerHost()), chain[0]);
+            } catch (UnknownHostException e) {
+                throw new CertificateException("Failed to verify host", e);
+            }
+        }
+    }
+
+    @Override
+    public void checkServerTrusted(X509Certificate[] chain, String authType, SSLEngine engine)
+            throws CertificateException {
+        x509ExtendedTrustManager.checkServerTrusted(chain, authType, engine);
+        if (serverHostnameVerificationEnabled) {
+            try {
+                performHostVerification(InetAddress.getByName(engine.getPeerHost()), chain[0]);
+            } catch (UnknownHostException e) {
+                throw new CertificateException("Failed to verify host", e);
+            }
+        }
+    }
+
+    @Override
+    public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException
{
+        x509ExtendedTrustManager.checkClientTrusted(chain, authType);
+    }
+
+    @Override
+    public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException
{
+        x509ExtendedTrustManager.checkServerTrusted(chain, authType);
+    }
+
+    /**
+     * Compares peer's hostname with the one stored in the provided client certificate. Performs
verification
+     * with the help of provided HostnameVerifier.
+     *
+     * @param inetAddress Peer's inet address.
+     * @param certificate Peer's certificate
+     * @throws CertificateException Thrown if the provided certificate doesn't match the
peer hostname.
+     */
+    private void performHostVerification(InetAddress inetAddress, X509Certificate certificate)
+            throws CertificateException {
+        String hostAddress = "";
+        String hostName = "";
+        try {
+            hostAddress = inetAddress.getHostAddress();
+            hostnameVerifier.verify(hostAddress, certificate);
+        } catch (SSLException addressVerificationException) {
+            try {
+                LOG.debug("Failed to verify host address: {} attempting to verify host name
with reverse dns lookup",
+                        hostAddress, addressVerificationException);
+                hostName = inetAddress.getHostName();
+                hostnameVerifier.verify(hostName, certificate);
+            } catch (SSLException hostnameVerificationException) {
+                LOG.error("Failed to verify host address: {}", hostAddress, addressVerificationException);
+                LOG.error("Failed to verify hostname: {}", hostName, hostnameVerificationException);
+                throw new CertificateException("Failed to verify both host address and host
name",
+                        hostnameVerificationException);
+            }
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/zookeeper/blob/43d71c2e/zookeeper-server/src/main/java/org/apache/zookeeper/util/SecurityUtils.java
----------------------------------------------------------------------
diff --git a/zookeeper-server/src/main/java/org/apache/zookeeper/util/SecurityUtils.java b/zookeeper-server/src/main/java/org/apache/zookeeper/util/SecurityUtils.java
new file mode 100644
index 0000000..67484e4
--- /dev/null
+++ b/zookeeper-server/src/main/java/org/apache/zookeeper/util/SecurityUtils.java
@@ -0,0 +1,298 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.zookeeper.util;
+
+import java.security.Principal;
+import java.security.PrivilegedActionException;
+import java.security.PrivilegedExceptionAction;
+
+import javax.security.auth.Subject;
+import javax.security.auth.callback.CallbackHandler;
+import javax.security.sasl.Sasl;
+import javax.security.sasl.SaslClient;
+import javax.security.sasl.SaslException;
+import javax.security.sasl.SaslServer;
+
+import org.apache.zookeeper.SaslClientCallbackHandler;
+import org.apache.zookeeper.server.auth.KerberosName;
+import org.ietf.jgss.GSSContext;
+import org.ietf.jgss.GSSCredential;
+import org.ietf.jgss.GSSException;
+import org.ietf.jgss.GSSManager;
+import org.ietf.jgss.GSSName;
+import org.ietf.jgss.Oid;
+import org.slf4j.Logger;
+
+public final class SecurityUtils {
+
+    public static final String QUORUM_HOSTNAME_PATTERN = "_HOST";
+
+    /**
+     * Create an instance of a SaslClient. It will return null if there is an exception.
+     *
+     * @param subject subject
+     * @param servicePrincipal principal
+     * @param protocol name of the protocol for which the authentication is being performed
+     * @param serverName name of the server to authenticate to
+     * @param LOG logger
+     * @param entity can be either zookeeper client or quorum learner
+     *
+     * @return saslclient object
+     * @throws SaslException
+     */
+    public static SaslClient createSaslClient(final Subject subject,
+            final String servicePrincipal, final String protocol,
+            final String serverName, final Logger LOG, final String entity) throws SaslException
{
+        SaslClient saslClient;
+        // Use subject.getPrincipals().isEmpty() as an indication of which SASL
+        // mechanism to use: if empty, use DIGEST-MD5; otherwise, use GSSAPI.
+        if (subject.getPrincipals().isEmpty()) {
+            // no principals: must not be GSSAPI: use DIGEST-MD5 mechanism
+            // instead.
+            LOG.info("{} will use DIGEST-MD5 as SASL mechanism.", entity);
+            String[] mechs = { "DIGEST-MD5" };
+            String username = (String) (subject.getPublicCredentials()
+                    .toArray()[0]);
+            String password = (String) (subject.getPrivateCredentials()
+                    .toArray()[0]);
+            // 'domain' parameter is hard-wired between the server and client
+            saslClient = Sasl.createSaslClient(mechs, username, protocol,
+                    serverName, null, new SaslClientCallbackHandler(password, entity));
+            return saslClient;
+        } else { // GSSAPI.
+            final Object[] principals = subject.getPrincipals().toArray();
+            // determine client principal from subject.
+            final Principal clientPrincipal = (Principal) principals[0];
+            boolean usingNativeJgss = Boolean
+                    .getBoolean("sun.security.jgss.native");
+            if (usingNativeJgss) {
+                // http://docs.oracle.com/javase/6/docs/technotes/guides/security/jgss/jgss-features.html
+                // """
+                // In addition, when performing operations as a particular
+                // Subject, e.g. Subject.doAs(...) or
+                // Subject.doAsPrivileged(...),
+                // the to-be-used GSSCredential should be added to Subject's
+                // private credential set. Otherwise, the GSS operations will
+                // fail since no credential is found.
+                // """
+                try {
+                    GSSManager manager = GSSManager.getInstance();
+                    Oid krb5Mechanism = new Oid("1.2.840.113554.1.2.2");
+                    GSSCredential cred = manager.createCredential(null,
+                            GSSContext.DEFAULT_LIFETIME, krb5Mechanism,
+                            GSSCredential.INITIATE_ONLY);
+                    subject.getPrivateCredentials().add(cred);
+                    LOG.debug("Added private credential to {} principal name: '{}'",
+                            entity, clientPrincipal);
+                } catch (GSSException ex) {
+                    LOG.warn("Cannot add private credential to subject; "
+                                    + "authentication at the server may fail", ex);
+                }
+            }
+            final KerberosName clientKerberosName = new KerberosName(
+                    clientPrincipal.getName());
+            // assume that server and client are in the same realm (by default;
+            // unless the system property
+            // "zookeeper.server.realm" is set).
+            String serverRealm = System.getProperty("zookeeper.server.realm",
+                    clientKerberosName.getRealm());
+            KerberosName serviceKerberosName = new KerberosName(
+                    servicePrincipal + "@" + serverRealm);
+            final String serviceName = serviceKerberosName.getServiceName();
+            final String serviceHostname = serviceKerberosName.getHostName();
+            final String clientPrincipalName = clientKerberosName.toString();
+            try {
+                saslClient = Subject.doAs(subject,
+                        new PrivilegedExceptionAction<SaslClient>() {
+                            public SaslClient run() throws SaslException {
+                                LOG.info("{} will use GSSAPI as SASL mechanism.", entity);
+                                String[] mechs = { "GSSAPI" };
+                                LOG.debug("creating sasl client: {}={};service={};serviceHostname={}",
+                                        new Object[] { entity, clientPrincipalName, serviceName,
serviceHostname });
+                                SaslClient saslClient = Sasl.createSaslClient(
+                                        mechs, clientPrincipalName, serviceName,
+                                        serviceHostname, null,
+                                        new SaslClientCallbackHandler(null, entity));
+                                return saslClient;
+                            }
+                        });
+                return saslClient;
+            } catch (Exception e) {
+                LOG.error("Exception while trying to create SASL client", e);
+                return null;
+            }
+        }
+    }
+
+    /**
+     * Create an instance of a SaslServer. It will return null if there is an exception.
+     *
+     * @param subject subject
+     * @param protocol protocol
+     * @param serverName server name
+     * @param callbackHandler login callback handler
+     * @param LOG logger
+     * @return sasl server object
+     */
+    public static SaslServer createSaslServer(final Subject subject,
+            final String protocol, final String serverName,
+            final CallbackHandler callbackHandler, final Logger LOG) {
+        if (subject != null) {
+            // server is using a JAAS-authenticated subject: determine service
+            // principal name and hostname from zk server's subject.
+            if (subject.getPrincipals().size() > 0) {
+                try {
+                    final Object[] principals = subject.getPrincipals()
+                            .toArray();
+                    final Principal servicePrincipal = (Principal) principals[0];
+
+                    // e.g. servicePrincipalNameAndHostname :=
+                    // "zookeeper/myhost.foo.com@FOO.COM"
+                    final String servicePrincipalNameAndHostname = servicePrincipal
+                            .getName();
+
+                    int indexOf = servicePrincipalNameAndHostname.indexOf("/");
+
+                    // e.g. servicePrincipalName := "zookeeper"
+                    final String servicePrincipalName = servicePrincipalNameAndHostname
+                            .substring(0, indexOf);
+
+                    // e.g. serviceHostnameAndKerbDomain :=
+                    // "myhost.foo.com@FOO.COM"
+                    final String serviceHostnameAndKerbDomain = servicePrincipalNameAndHostname
+                            .substring(indexOf + 1,
+                                    servicePrincipalNameAndHostname.length());
+
+                    indexOf = serviceHostnameAndKerbDomain.indexOf("@");
+                    // e.g. serviceHostname := "myhost.foo.com"
+                    final String serviceHostname = serviceHostnameAndKerbDomain
+                            .substring(0, indexOf);
+
+                    // TODO: should depend on zoo.cfg specified mechs, but if
+                    // subject is non-null, it can be assumed to be GSSAPI.
+                    final String mech = "GSSAPI";
+
+                    LOG.debug("serviceHostname is '" + serviceHostname + "'");
+                    LOG.debug("servicePrincipalName is '" + servicePrincipalName
+                            + "'");
+                    LOG.debug("SASL mechanism(mech) is '" + mech + "'");
+
+                    boolean usingNativeJgss = Boolean
+                            .getBoolean("sun.security.jgss.native");
+                    if (usingNativeJgss) {
+                        // http://docs.oracle.com/javase/6/docs/technotes/guides/security/jgss/jgss-features.html
+                        // """
+                        // In addition, when performing operations as a
+                        // particular
+                        // Subject, e.g. Subject.doAs(...) or
+                        // Subject.doAsPrivileged(...), the to-be-used
+                        // GSSCredential should be added to Subject's
+                        // private credential set. Otherwise, the GSS operations
+                        // will fail since no credential is found.
+                        // """
+                        try {
+                            GSSManager manager = GSSManager.getInstance();
+                            Oid krb5Mechanism = new Oid("1.2.840.113554.1.2.2");
+                            GSSName gssName = manager.createName(
+                                    servicePrincipalName + "@"
+                                            + serviceHostname,
+                                    GSSName.NT_HOSTBASED_SERVICE);
+                            GSSCredential cred = manager.createCredential(
+                                    gssName, GSSContext.DEFAULT_LIFETIME,
+                                    krb5Mechanism, GSSCredential.ACCEPT_ONLY);
+                            subject.getPrivateCredentials().add(cred);
+                            LOG.debug("Added private credential to service principal name:
'{}',"
+                                            + " GSSCredential name: {}", servicePrincipalName,
cred.getName());
+                        } catch (GSSException ex) {
+                            LOG.warn("Cannot add private credential to subject; "
+                                            + "clients authentication may fail", ex);
+                        }
+                    }
+                    try {
+                        return Subject.doAs(subject,
+                                new PrivilegedExceptionAction<SaslServer>() {
+                                    public SaslServer run() {
+                                        try {
+                                            SaslServer saslServer;
+                                            saslServer = Sasl.createSaslServer(
+                                                    mech, servicePrincipalName,
+                                                    serviceHostname, null,
+                                                    callbackHandler);
+                                            return saslServer;
+                                        } catch (SaslException e) {
+                                            LOG.error("Zookeeper Server failed to create
a SaslServer to interact with a client during session initiation: ", e);
+                                            return null;
+                                        }
+                                    }
+                                });
+                    } catch (PrivilegedActionException e) {
+                        // TODO: exit server at this point(?)
+                        LOG.error("Zookeeper Quorum member experienced a PrivilegedActionException
exception while creating a SaslServer using a JAAS principal context:", e);
+                    }
+                } catch (IndexOutOfBoundsException e) {
+                    LOG.error("server principal name/hostname determination error: ", e);
+                }
+            } else {
+                // JAAS non-GSSAPI authentication: assuming and supporting only
+                // DIGEST-MD5 mechanism for now.
+                // TODO: use 'authMech=' value in zoo.cfg.
+                try {
+                    SaslServer saslServer = Sasl.createSaslServer("DIGEST-MD5",
+                            protocol, serverName, null, callbackHandler);
+                    return saslServer;
+                } catch (SaslException e) {
+                    LOG.error("Zookeeper Quorum member failed to create a SaslServer to interact
with a client during session initiation", e);
+                }
+            }
+        }
+        return null;
+    }
+
+    /**
+     * Convert Kerberos principal name pattern to valid Kerberos principal name.
+     * If the principal name contains hostname pattern "_HOST" then it replaces
+     * with the given hostname, which should be fully-qualified domain name.
+     *
+     * @param principalConfig
+     *            the Kerberos principal name conf value to convert
+     * @param hostname
+     *            the fully-qualified domain name used for substitution
+     * @return converted Kerberos principal name
+     */
+    public static String getServerPrincipal(String principalConfig,
+            String hostname) {
+        String[] components = getComponents(principalConfig);
+        if (components == null || components.length != 2
+                || !components[1].equals(QUORUM_HOSTNAME_PATTERN)) {
+            return principalConfig;
+        } else {
+            return replacePattern(components, hostname);
+        }
+    }
+
+    private static String[] getComponents(String principalConfig) {
+        if (principalConfig == null)
+            return null;
+        return principalConfig.split("[/]");
+    }
+
+    private static String replacePattern(String[] components, String hostname) {
+        return components[0] + "/" + hostname.toLowerCase();
+    }
+}

http://git-wip-us.apache.org/repos/asf/zookeeper/blob/43d71c2e/zookeeper-server/src/main/java/org/apache/zookeeper/version/util/VerGen.java
----------------------------------------------------------------------
diff --git a/zookeeper-server/src/main/java/org/apache/zookeeper/version/util/VerGen.java
b/zookeeper-server/src/main/java/org/apache/zookeeper/version/util/VerGen.java
new file mode 100644
index 0000000..014f01d
--- /dev/null
+++ b/zookeeper-server/src/main/java/org/apache/zookeeper/version/util/VerGen.java
@@ -0,0 +1,167 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.zookeeper.version.util;
+
+import java.io.File;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+public class VerGen {
+    private static final String PACKAGE_NAME = "org.apache.zookeeper.version";
+    private static final String TYPE_NAME = "Info";
+
+    static void printUsage() {
+        System.out.print("Usage:\tjava  -cp <classpath> org.apache.zookeeper."
+                + "version.util.VerGen maj.min.micro[-qualifier] rev buildDate");
+        System.exit(1);
+    }
+
+    public static void generateFile(File outputDir, Version version, String rev, String buildDate)
+    {
+        String path = PACKAGE_NAME.replaceAll("\\.", "/");
+        File pkgdir = new File(outputDir, path);
+        if (!pkgdir.exists()) {
+            // create the pkg directory
+            boolean ret = pkgdir.mkdirs();
+            if (!ret) {
+                System.out.println("Cannnot create directory: " + path);
+                System.exit(1);
+            }
+        } else if (!pkgdir.isDirectory()) {
+            // not a directory
+            System.out.println(path + " is not a directory.");
+            System.exit(1);
+        }
+
+        try (FileWriter w = new FileWriter(new File(pkgdir, TYPE_NAME + ".java"))) {
+            w.write("// Do not edit!\n// File generated by org.apache.zookeeper"
+                    + ".version.util.VerGen.\n");
+            w.write("/**\n");
+            w.write("* Licensed to the Apache Software Foundation (ASF) under one\n");
+            w.write("* or more contributor license agreements.  See the NOTICE file\n");
+            w.write("* distributed with this work for additional information\n");
+            w.write("* regarding copyright ownership.  The ASF licenses this file\n");
+            w.write("* to you under the Apache License, Version 2.0 (the\n");
+            w.write("* \"License\"); you may not use this file except in compliance\n");
+            w.write("* with the License.  You may obtain a copy of the License at\n");
+            w.write("*\n");
+            w.write("*     http://www.apache.org/licenses/LICENSE-2.0\n");
+            w.write("*\n");
+            w.write("* Unless required by applicable law or agreed to in writing, software\n");
+            w.write("* distributed under the License is distributed on an \"AS IS\" BASIS,\n");
+            w.write("* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n");
+            w.write("* See the License for the specific language governing permissions and\n");
+            w.write("* limitations under the License.\n");
+            w.write("*/\n");
+            w.write("\n");
+            w.write("package " + PACKAGE_NAME + ";\n\n");
+            w.write("public interface " + TYPE_NAME + " {\n");
+            w.write("    int MAJOR=" + version.maj + ";\n");
+            w.write("    int MINOR=" + version.min + ";\n");
+            w.write("    int MICRO=" + version.micro + ";\n");
+            w.write("    String QUALIFIER="
+                    + (version.qualifier == null ? null :
+                        "\"" + version.qualifier + "\"")
+                    + ";\n");
+            if (rev.equals("-1")) {
+                System.out.println("Unknown REVISION number, using " + rev);
+            }
+            w.write("    int REVISION=-1; //TODO: remove as related to SVN VCS\n");
+            w.write("    String REVISION_HASH=\"" + rev + "\";\n");
+            w.write("    String BUILD_DATE=\"" + buildDate
+                    + "\";\n");
+            w.write("}\n");
+        } catch (IOException e) {
+            System.out.println("Unable to generate version.Info file: "
+                    + e.getMessage());
+            System.exit(1);
+        }
+    }
+
+    public static class Version {
+        public int maj;
+        public int min;
+        public int micro;
+        public String qualifier;
+    }
+
+    public static Version parseVersionString(String input) {
+        Version result = new Version();
+
+        Pattern p = Pattern.compile("^(\\d+)\\.(\\d+)\\.(\\d+)((\\.\\d+)*)(-(.+))?$");
+        Matcher m = p.matcher(input);
+
+        if (!m.matches()) {
+            return null;
+        }
+        result.maj = Integer.parseInt(m.group(1));
+        result.min = Integer.parseInt(m.group(2));
+        result.micro = Integer.parseInt(m.group(3));
+        if (m.groupCount() == 7) {
+            result.qualifier = m.group(7);
+        } else {
+            result.qualifier = null;
+        }
+        return result;
+    }
+
+    /**
+     * Emits a org.apache.zookeeper.version.Info interface file with version and
+     * revision information constants set to the values passed in as command
+     * line parameters. The file is created in the current directory. <br>
+     * Usage: java org.apache.zookeeper.version.util.VerGen maj.min.micro[-qualifier]
+     * rev buildDate
+     *
+     * @param args
+     *            <ul>
+     *            <li>maj - major version number
+     *            <li>min - minor version number
+     *            <li>micro - minor minor version number
+     *            <li>qualifier - optional qualifier (dash followed by qualifier text)
+     *            <li>rev - current Git revision number
+     *            <li>buildDate - date the build
+     *            </ul>
+     */
+    public static void main(String[] args) {
+        if (args.length != 3)
+            printUsage();
+        try {
+            Version version = parseVersionString(args[0]);
+            if (version == null) {
+                System.err.println(
+                        "Invalid version number format, must be \"x.y.z(-.*)?\"");
+                System.exit(1);
+            }
+            String rev = args[1];
+            if (rev == null || rev.trim().isEmpty()) {
+                rev = "-1";
+            } else {
+                rev = rev.trim();
+            }
+            generateFile(new File("."), version, rev, args[2]);
+        } catch (NumberFormatException e) {
+            System.err.println(
+                    "All version-related parameters must be valid integers!");
+            throw e;
+        }
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/zookeeper/blob/43d71c2e/zookeeper-server/src/main/resources/lib/cobertura/README.txt
----------------------------------------------------------------------
diff --git a/zookeeper-server/src/main/resources/lib/cobertura/README.txt b/zookeeper-server/src/main/resources/lib/cobertura/README.txt
new file mode 100644
index 0000000..f5ba88f
--- /dev/null
+++ b/zookeeper-server/src/main/resources/lib/cobertura/README.txt
@@ -0,0 +1,3 @@
+Download the cobertura binary from the following location and unpack it into this directory.
Run "cobertura-report" target from build.xml to generate coverage report.
+
+http://cobertura.sourceforge.net/download.html


Mime
View raw message