org.globus.gsi.gssapi.GlobusGSSName Maven / Gradle / Ivy
The newest version!
/*
* Copyright 1999-2010 University of Chicago
*
* Licensed 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.globus.gsi.gssapi;
import java.net.InetAddress;
import java.net.UnknownHostException;
import org.globus.common.CoGProperties;
import org.globus.gsi.util.CertificateUtil;
import org.ietf.jgss.GSSName;
import org.ietf.jgss.GSSException;
import org.ietf.jgss.Oid;
import javax.security.auth.x500.X500Principal;
import java.io.IOException;
import java.io.Serializable;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
import java.util.concurrent.*;
import java.util.regex.Pattern;
/**
* An implementation of GSSName
.
*/
public class GlobusGSSName implements GSSName, Serializable {
static class ReverseDNSCache {
static class MapEntry {
final Future hostName;
Long inserted;
public MapEntry(Future hostName, Long inserted) {
this.hostName = hostName;
this.inserted = inserted;
}
}
// Use TreeMap to avoid clustering in any case
final protected Map cache = new TreeMap();
final long duration;
final ExecutorService threads = Executors.newCachedThreadPool(new ThreadFactory() {
public Thread newThread(Runnable runnable) {
Thread t = new Thread(runnable);
t.setName("Reverse DNS request");
t.setDaemon(true);
return t;
}
});
long oldest = System.currentTimeMillis();
public ReverseDNSCache(long duration) {
this.duration = duration;
}
protected void enforceConstraints() {
if(oldest + duration < System.currentTimeMillis()) {
long newOldest = System.currentTimeMillis();
List toClear = new LinkedList();
for(Map.Entry e: cache.entrySet()) {
if(e.getValue().inserted + duration < System.currentTimeMillis()) toClear.add(e.getKey());
else if(e.getValue().inserted < newOldest) newOldest = e.getValue().inserted;
}
for(String k: toClear) cache.remove(k);
oldest = newOldest;
}
}
protected synchronized Future getCached(final String ip) {
MapEntry inCache = cache.get(ip);
if(inCache == null) {
Future name = threads.submit(new Callable() {
public String call() throws Exception {
return queryHost(ip);
}
});
inCache = new MapEntry(name, System.currentTimeMillis());
cache.put(ip, inCache);
} else {
inCache.inserted = System.currentTimeMillis();
}
enforceConstraints();
return inCache.hostName;
}
public String resolve(String ip) throws UnknownHostException {
try {
return getCached(ip).get();
} catch(InterruptedException e) {
throw new UnknownHostException(e.getMessage());
} catch(ExecutionException e) {
throw new UnknownHostException(e.getMessage());
}
}
}
static String queryHost(String name) throws UnknownHostException {
InetAddress i = InetAddress.getByName(name);
return InetAddress.getByName(i.getHostAddress()).getHostName();
}
final static ReverseDNSCache reverseDNSCache = new ReverseDNSCache(CoGProperties.getDefault().getReveseDNSCacheLifetime());
protected Oid nameType;
protected X500Principal name;
// set toString called
protected String globusID;
// set when constructing with GSSName.NT_HOSTBASED_SERVICE as name type
// or in the getter
protected String hostBasedServiceCN;
public GlobusGSSName() {
this.nameType = GSSName.NT_ANONYMOUS;
this.name = null;
}
public GlobusGSSName(X500Principal name) {
if (name == null) {
this.nameType = GSSName.NT_ANONYMOUS;
}
this.name = name;
}
public GlobusGSSName(byte[] name) {
if (name == null) {
this.nameType = GSSName.NT_ANONYMOUS;
this.name = null;
} else {
this.name = new X500Principal(name);
}
}
/**
* Creates name from Globus DN
*
* @param name Globus DN (e.g. /C=US/O=Globus/..) If null
* it is considered set as GSSName.ANONYMOUS
name type.
*/
public GlobusGSSName(String name)
throws GSSException {
if (name == null) {
this.nameType = GSSName.NT_ANONYMOUS;
this.name = null;
} else {
try {
this.name = CertificateUtil.toPrincipal(name);
} catch (Exception e) {
throw new GlobusGSSException(GSSException.BAD_NAME, e);
}
}
}
/**
* Creates name from X509 name of specified type.
*
* @param name
* Globus DN (e.g. /C=US/O=Globus/..) or service@host name. If null
* it is considered set as GSSName.ANONYMOUS
name type.
* @param nameType name type. Only GSSName.NT_ANONYMOUS
* or GSSName.NT_HOSTBASED_SERVICE
is supported.
* Maybe be null.
*/
public GlobusGSSName(String name, Oid nameType)
throws GSSException {
if (name == null) {
if (nameType != null && !nameType.equals(GSSName.NT_ANONYMOUS)) {
throw new GSSException(GSSException.BAD_NAMETYPE);
}
this.name = null;
this.nameType = GSSName.NT_ANONYMOUS;
} else {
if (nameType != null) {
if (nameType.equals(GSSName.NT_HOSTBASED_SERVICE)) {
int atPos = name.indexOf('@');
if (atPos == -1 || (atPos+1 >= name.length())) {
throw new GlobusGSSException(GSSException.FAILURE,
GlobusGSSException.BAD_NAME,
"badName00");
}
// performs reverse DNS lookup
String host = name.substring(atPos+1);
try {
if (CoGProperties.getDefault().getReverseDNSCacheType().equals(CoGProperties.THREADED_CACHE)) {
host = reverseDNSCache.resolve(host);
} else {
host = queryHost(host);
}
} catch (UnknownHostException e) {
throw new GlobusGSSException(GSSException.FAILURE, e);
}
hostBasedServiceCN = name.substring(0, atPos) + "/" + host;
this.name = new X500Principal("CN=" + hostBasedServiceCN);
} else {
throw new GSSException(GSSException.BAD_NAMETYPE);
}
} else {
try {
this.name = CertificateUtil.toPrincipal(name);
} catch (Exception e) {
throw new GlobusGSSException(GSSException.BAD_NAME, e);
}
}
this.nameType = nameType;
}
// both subject & nameType might be null
}
public boolean isAnonymous() {
return (this.name == null);
}
public boolean isMN() {
return true;
}
public boolean equals(GSSName another)
throws GSSException {
if (another == null) {
return false;
}
if (isAnonymous()) {
return another.isAnonymous();
}
if (another.isAnonymous()) {
return false;
}
if (!(another instanceof GlobusGSSName)) {
throw new GSSException(GSSException.FAILURE);
}
GlobusGSSName other = (GlobusGSSName)another;
// both are not anonymous
// both have non-null subjects
// nametypes might be different! (null)
if ((nameType != null && nameType.equals(GSSName.NT_HOSTBASED_SERVICE)) ||
(other.nameType != null && other.nameType.equals(GSSName.NT_HOSTBASED_SERVICE))) {
// perform host based comparison
String hp1 = this.getHostBasedServiceCN(true);
String hp2 = other.getHostBasedServiceCN(true);
if (hp1 == null || hp2 == null) {
// something is really wrong
return false;
}
String service1 = getService(hp1);
String service2 = getService(hp2);
// service types do not match
if (!service1.equalsIgnoreCase(service2)) {
return false;
}
String host1 = getHost(hp1);
String host2 = getHost(hp2);
int i1=0;
int i2=0;
int s1 = host1.length();
int s2 = host2.length();
char h1;
char h2;
while (i1 < s1 && i2 < s2) {
h1 = Character.toUpperCase(host1.charAt(i1));
h2 = Character.toUpperCase(host2.charAt(i2));
if (h1 == h2) {
if (h1 == '.') {
return host1.equalsIgnoreCase(host2);
}
i1++;
i2++;
} else if (h1 == '.' && h2 == '-') {
return compareHost(host2, i2, host1, i1);
} else if (h1 == '-' && h2 == '.') {
return compareHost(host1, i1, host2, i2);
} else {
return false;
}
}
return (i1 == i2);
} else {
// perform regular comparison
// cross-check getStringNameType()
// that's not implemented right now
return toString().equalsIgnoreCase(another.toString());
}
}
/**
* Returns globus ID string representation of the name.
* If name represents is an anonymous name string
* "" is returned.
*/
public String toString() {
if (this.name == null) {
return "";
} else {
if (this.globusID == null) {
this.globusID = CertificateUtil.toGlobusID(name);
}
return this.globusID;
}
}
/**
* Returns the CN corresponding to the host part of the DN
* @param last true if the CN is assumed to be the last CN attribute
* in the RFC 2253 formatted DN, else false to assume it is the first DN
* attribute
* @return the CN of the host based service
*/
protected String getHostBasedServiceCN(boolean last) {
if (hostBasedServiceCN == null) {
String dn = name.getName();
int cnStart;
if (last) {
// use the last instance of CN in the DN
cnStart = dn.lastIndexOf("CN=") + 3;
} else {
// use the first instance of CN in the DN
cnStart = dn.indexOf("CN=") + 3;
}
if (cnStart == -1) {
return null;
}
int cnEnd = dn.indexOf(",", cnStart);
if (cnEnd == -1) {
int nextAtt = dn.indexOf("=", cnStart);
if (nextAtt == -1) {
// CN is the last attribute in the DN
cnEnd = dn.length();
} else {
// unexpected DN format (attributes not comma delimited)
return null;
}
}
hostBasedServiceCN = name.getName().substring(cnStart, cnEnd);
}
return hostBasedServiceCN;
}
private static String getService(String name) {
int pos = name.indexOf('/');
return (pos == -1) ? "host" : name.substring(0, pos);
}
private static String getHost(String name) {
int pos = name.indexOf('/');
return (pos == -1) ? name : name.substring(pos+1);
}
private static boolean compareHost(String host1, int i,
String host2, int j) {
if (host1.charAt(i) != '-') {
throw new IllegalArgumentException();
}
int size = host1.length();
while (i < size ) {
if (host1.charAt(i) == '.') {
break;
} else {
i++;
}
}
if (size - i == host2.length() - j) {
return host1.regionMatches(i,
host2,
j,
size - i);
} else {
return false;
}
}
// ----------------------------------
/**
* Currently not implemented.
*/
public Oid getStringNameType()
throws GSSException {
throw new GSSException(GSSException.UNAVAILABLE);
}
/**
* Currently not implemented.
*/
public byte[] export()
throws GSSException {
throw new GSSException(GSSException.UNAVAILABLE);
}
/**
* Currently not implemented.
*/
public GSSName canonicalize(Oid mech)
throws GSSException {
throw new GSSException(GSSException.UNAVAILABLE);
}
private void writeObject(ObjectOutputStream oos) throws IOException {
oos.writeObject(this.nameType);
oos.writeObject(name.getName());
}
private void readObject(ObjectInputStream ois)
throws IOException, ClassNotFoundException {
this.nameType = (Oid)ois.readObject();
this.name = new X500Principal((String)ois.readObject());
}
}