View Javadoc
1   /*
2    * Copyright (C) 2018, Thomas Wolf <thomas.wolf@paranor.ch> and others
3    *
4    * This program and the accompanying materials are made available under the
5    * terms of the Eclipse Distribution License v. 1.0 which is available at
6    * https://www.eclipse.org/org/documents/edl-v10.php.
7    *
8    * SPDX-License-Identifier: BSD-3-Clause
9    */
10  package org.eclipse.jgit.internal.transport.sshd;
11  
12  import java.net.InetAddress;
13  import java.net.InetSocketAddress;
14  import java.net.UnknownHostException;
15  import java.util.Collection;
16  import java.util.Collections;
17  import java.util.LinkedHashMap;
18  import java.util.Map;
19  import java.util.concurrent.atomic.AtomicBoolean;
20  
21  import org.eclipse.jgit.annotations.NonNull;
22  import org.ietf.jgss.GSSContext;
23  import org.ietf.jgss.GSSException;
24  import org.ietf.jgss.GSSManager;
25  import org.ietf.jgss.GSSName;
26  import org.ietf.jgss.Oid;
27  
28  /**
29   * Global repository of GSS-API mechanisms that we can use.
30   */
31  public class GssApiMechanisms {
32  
33  	private GssApiMechanisms() {
34  		// No instantiation
35  	}
36  
37  	/** Prefix to use with {@link GSSName#NT_HOSTBASED_SERVICE}. */
38  	public static final String GSSAPI_HOST_PREFIX = "host@"; //$NON-NLS-1$
39  
40  	/** The {@link Oid} of Kerberos 5. */
41  	public static final Oid KERBEROS_5 = createOid("1.2.840.113554.1.2.2"); //$NON-NLS-1$
42  
43  	/** SGNEGO is not to be used with ssh. */
44  	public static final Oid SPNEGO = createOid("1.3.6.1.5.5.2"); //$NON-NLS-1$
45  
46  	/** Protects {@link #supportedMechanisms}. */
47  	private static final Object LOCK = new Object();
48  
49  	/**
50  	 * The {@link AtomicBoolean} is set to {@code true} when the mechanism could
51  	 * be initialized successfully at least once.
52  	 */
53  	private static Map<Oid, Boolean> supportedMechanisms;
54  
55  	/**
56  	 * Retrieves an immutable collection of the supported mechanisms.
57  	 *
58  	 * @return the supported mechanisms
59  	 */
60  	@NonNull
61  	public static Collection<Oid> getSupportedMechanisms() {
62  		synchronized (LOCK) {
63  			if (supportedMechanisms == null) {
64  				GSSManager manager = GSSManager.getInstance();
65  				Oid[] mechs = manager.getMechs();
66  				Map<Oid, Boolean> mechanisms = new LinkedHashMap<>();
67  				if (mechs != null) {
68  					for (Oid oid : mechs) {
69  						mechanisms.put(oid, Boolean.FALSE);
70  					}
71  				}
72  				supportedMechanisms = mechanisms;
73  			}
74  			return Collections.unmodifiableSet(supportedMechanisms.keySet());
75  		}
76  	}
77  
78  	/**
79  	 * Report that this mechanism was used successfully.
80  	 *
81  	 * @param mechanism
82  	 *            that worked
83  	 */
84  	public static void worked(@NonNull Oid mechanism) {
85  		synchronized (LOCK) {
86  			supportedMechanisms.put(mechanism, Boolean.TRUE);
87  		}
88  	}
89  
90  	/**
91  	 * Mark the mechanisms as failed.
92  	 *
93  	 * @param mechanism
94  	 *            to mark
95  	 */
96  	public static void failed(@NonNull Oid mechanism) {
97  		synchronized (LOCK) {
98  			Boolean worked = supportedMechanisms.get(mechanism);
99  			if (worked != null && !worked.booleanValue()) {
100 				// If it never worked, remove it
101 				supportedMechanisms.remove(mechanism);
102 			}
103 		}
104 	}
105 
106 	/**
107 	 * Resolves an {@link InetSocketAddress}.
108 	 *
109 	 * @param remote
110 	 *            to resolve
111 	 * @return the resolved {@link InetAddress}, or {@code null} if unresolved.
112 	 */
113 	public static InetAddress resolve(@NonNull InetSocketAddress remote) {
114 		InetAddress address = remote.getAddress();
115 		if (address == null) {
116 			try {
117 				address = InetAddress.getByName(remote.getHostString());
118 			} catch (UnknownHostException e) {
119 				return null;
120 			}
121 		}
122 		return address;
123 	}
124 
125 	/**
126 	 * Determines a canonical host name for use use with GSS-API.
127 	 *
128 	 * @param remote
129 	 *            to get the host name from
130 	 * @return the canonical host name, if it can be determined, otherwise the
131 	 *         {@link InetSocketAddress#getHostString() unprocessed host name}.
132 	 */
133 	@NonNull
134 	public static String getCanonicalName(@NonNull InetSocketAddress remote) {
135 		InetAddress address = resolve(remote);
136 		if (address == null) {
137 			return remote.getHostString();
138 		}
139 		return address.getCanonicalHostName();
140 	}
141 
142 	/**
143 	 * Creates a {@link GSSContext} for the given mechanism to authenticate with
144 	 * the host given by {@code fqdn}.
145 	 *
146 	 * @param mechanism
147 	 *            {@link Oid} of the mechanism to use
148 	 * @param fqdn
149 	 *            fully qualified domain name of the host to authenticate with
150 	 * @return the context, if the mechanism is available and the context could
151 	 *         be created, or {@code null} otherwise
152 	 */
153 	public static GSSContext createContext(@NonNull Oid mechanism,
154 			@NonNull String fqdn) {
155 		GSSContext context = null;
156 		try {
157 			GSSManager manager = GSSManager.getInstance();
158 			context = manager.createContext(
159 					manager.createName(
160 							GssApiMechanisms.GSSAPI_HOST_PREFIX + fqdn,
161 							GSSName.NT_HOSTBASED_SERVICE),
162 					mechanism, null, GSSContext.DEFAULT_LIFETIME);
163 		} catch (GSSException e) {
164 			closeContextSilently(context);
165 			failed(mechanism);
166 			return null;
167 		}
168 		worked(mechanism);
169 		return context;
170 	}
171 
172 	/**
173 	 * Closes (disposes of) a {@link GSSContext} ignoring any
174 	 * {@link GSSException}s.
175 	 *
176 	 * @param context
177 	 *            to dispose
178 	 */
179 	public static void closeContextSilently(GSSContext context) {
180 		if (context != null) {
181 			try {
182 				context.dispose();
183 			} catch (GSSException e) {
184 				// Ignore
185 			}
186 		}
187 	}
188 
189 	private static Oid createOid(String rep) {
190 		try {
191 			return new Oid(rep);
192 		} catch (GSSException e) {
193 			// Does not occur
194 			return null;
195 		}
196 	}
197 
198 }