View Javadoc
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 }