1 /*
2 * Licensed to the Apache Software Foundation (ASF) under one
3 * or more contributor license agreements. See the NOTICE file
4 * distributed with this work for additional information
5 * regarding copyright ownership. The ASF licenses this file
6 * to you under the Apache License, Version 2.0 (the
7 * "License"); you may not use this file except in compliance
8 * with the License. You may obtain a copy of the License at
9 *
10 * http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing,
13 * software distributed under the License is distributed on an
14 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15 * KIND, either express or implied. See the License for the
16 * specific language governing permissions and limitations
17 * under the License.
18 *
19 */
20 package org.apache.mina.example.proxy;
21
22 import java.io.DataInputStream;
23 import java.io.DataOutputStream;
24 import java.io.IOException;
25 import java.net.ServerSocket;
26 import java.net.Socket;
27
28 import org.apache.mina.proxy.handlers.socks.SocksProxyConstants;
29 import org.apache.mina.proxy.utils.ByteUtilities;
30 import org.ietf.jgss.GSSContext;
31 import org.ietf.jgss.GSSCredential;
32 import org.ietf.jgss.GSSException;
33 import org.ietf.jgss.GSSManager;
34 import org.ietf.jgss.Oid;
35 import org.slf4j.Logger;
36 import org.slf4j.LoggerFactory;
37
38 /**
39 * Socks5GSSAPITestServer.java - Basic test server for SOCKS5 GSSAPI authentication.
40 *
41 * NOTE: Launch this program with the following params in a pre-configured Kerberos V env.
42 * Do not forget to replace < ... > vars with your own values.
43 *
44 * -Djava.security.krb5.realm=<your_krb_realm>
45 * -Djavax.security.auth.useSubjectCredsOnly=false
46 * -Djava.security.krb5.kdc=<your_kdc_hostname>
47 * -Djava.security.auth.login.config=${workspace_loc}\Mina2Proxy\src\bcsLogin.conf
48 * -Dsun.security.krb5.debug=true
49 *
50 * @author <a href="http://mina.apache.org">Apache MINA Project</a>
51 * @since MINA 2.0.0-M3
52 */
53 public class Socks5GSSAPITestServer {
54
55 private final static Logger logger = LoggerFactory
56 .getLogger(Socks5GSSAPITestServer.class);
57
58 /**
59 * NOTE : change this to comply with your Kerberos environment.
60 */
61 protected final static String SERVICE_NAME = "host/myworkstation.local.network";
62
63 /**
64 * Selected mechanism message: advertises client to use SocksV5 protocol with
65 * GSSAPI authentication.
66 */
67 public final static byte[] SELECT_GSSAPI_AUTH_MSG = new byte[] {
68 SocksProxyConstants.SOCKS_VERSION_5,
69 SocksProxyConstants.GSSAPI_AUTH };
70
71 /**
72 * Simulates a Socks v5 server using only Kerberos V authentication.
73 *
74 * @param localPort the local port used to bind the server
75 * @throws IOException
76 * @throws GSSException
77 */
78 private static void doHandShake(int localPort) throws IOException,
79 GSSException {
80 ServerSocket ss = new ServerSocket(localPort);
81 GSSManager manager = GSSManager.getInstance();
82
83 /*
84 * Create a GSSContext to receive the incoming request from the client.
85 * Use null for the server credentials passed in to tell the underlying
86 * mechanism to use whatever credentials it has available that can be
87 * used to accept this connection.
88 */
89 GSSCredential serverCreds = manager.createCredential(manager
90 .createName(SERVICE_NAME, null),
91 GSSCredential.DEFAULT_LIFETIME, new Oid(
92 SocksProxyConstants.KERBEROS_V5_OID),
93 GSSCredential.ACCEPT_ONLY);
94
95 while (true) {
96 logger.debug("Waiting for incoming connection on port {} ...",
97 localPort);
98 GSSContext context = manager.createContext(serverCreds);
99 Socket socket = ss.accept();
100
101 try {
102 DataInputStream inStream = new DataInputStream(socket
103 .getInputStream());
104 DataOutputStream outStream = new DataOutputStream(socket
105 .getOutputStream());
106
107 logger.debug("Got connection from client @ {}", socket
108 .getInetAddress());
109
110 // Read SOCKS5 greeting packet
111 byte ver = (byte) inStream.read();
112 if (ver != 0x05) {
113 throw new IllegalStateException(
114 "Wrong socks version received - " + ver);
115 }
116 byte nbAuthMethods = (byte) inStream.read();
117 byte[] methods = new byte[nbAuthMethods];
118 inStream.readFully(methods);
119
120 boolean found = false;
121 for (byte b : methods) {
122 if (b == SocksProxyConstants.GSSAPI_AUTH) {
123 found = true;
124 break;
125 }
126 }
127
128 if (!found) {
129 throw new IllegalStateException(
130 "Client does not support GSSAPI authentication");
131 }
132
133 // Send selected mechanism message
134 outStream.write(SELECT_GSSAPI_AUTH_MSG);
135 outStream.flush();
136
137 // Do the context establishment loop
138 byte[] token = null;
139
140 while (!context.isEstablished()) {
141 byte authVersion = (byte) inStream.read();
142
143 if (authVersion != 0x01) {
144 throw new IllegalStateException(
145 "Wrong socks GSSAPI auth version received: "
146 + authVersion);
147 }
148
149 byte mtyp = (byte) inStream.read();
150 if (mtyp != 0x01) {
151 throw new IllegalArgumentException(
152 "Message type should be equal to 1.");
153 }
154
155 int len = inStream.readShort();
156 token = new byte[len];
157 inStream.readFully(token);
158 logger.debug(" Received Token[{}] = {}", len,
159 ByteUtilities.asHex(token));
160
161 token = context.acceptSecContext(token, 0, token.length);
162
163 // Send a token to the peer if one was generated by acceptSecContext
164 if (token != null) {
165 logger.debug(" Sending Token[{}] = {}", token.length,
166 ByteUtilities.asHex(token));
167 outStream.writeByte(authVersion);
168 outStream.writeByte(mtyp);
169 outStream.writeShort(token.length);
170 outStream.write(token);
171 outStream.flush();
172 }
173 }
174
175 logger.debug("Context Established !");
176 logger.debug("Client is {}", context.getSrcName());
177 logger.debug("Server is {}", context.getTargName());
178
179 /*
180 * If mutual authentication did not take place, then
181 * only the client was authenticated to the
182 * server. Otherwise, both client and server were
183 * authenticated to each other.
184 */
185 if (context.getMutualAuthState()) {
186 logger.debug("Mutual authentication took place !");
187 }
188
189 // We can now abort the process after a short time as auth is OK
190 // and finally block will close session
191 Thread.sleep(500);
192 } catch (Exception ex) {
193 //ex.printStackTrace();
194 } finally {
195 context.dispose();
196 socket.close();
197 }
198 }
199 }
200
201 /**
202 * {@inheritDoc}
203 */
204 public static void main(String[] args) throws Exception {
205 // Obtain the command-line arguments and parse the port number
206 if (args.length != 1) {
207 System.err
208 .println("Usage: java <options> Socks5GSSAPITestServer <localPort>");
209 System.exit(-1);
210 }
211
212 doHandShake(Integer.parseInt(args[0]));
213 System.exit(0);
214 }
215 }