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 }