001/* 002 * Licensed to the Apache Software Foundation (ASF) under one 003 * or more contributor license agreements. See the NOTICE file 004 * distributed with this work for additional information 005 * regarding copyright ownership. The ASF licenses this file 006 * to you under the Apache License, Version 2.0 (the 007 * "License"); you may not use this file except in compliance 008 * with the License. You may obtain a copy of the License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, 013 * software distributed under the License is distributed on an 014 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 015 * KIND, either express or implied. See the License for the 016 * specific language governing permissions and limitations 017 * under the License. 018 * 019 */ 020package org.apache.mina.example.proxy; 021 022import java.io.DataInputStream; 023import java.io.DataOutputStream; 024import java.io.IOException; 025import java.net.ServerSocket; 026import java.net.Socket; 027 028import org.apache.mina.proxy.handlers.socks.SocksProxyConstants; 029import org.apache.mina.proxy.utils.ByteUtilities; 030import org.ietf.jgss.GSSContext; 031import org.ietf.jgss.GSSCredential; 032import org.ietf.jgss.GSSException; 033import org.ietf.jgss.GSSManager; 034import org.ietf.jgss.Oid; 035import org.slf4j.Logger; 036import org.slf4j.LoggerFactory; 037 038/** 039 * Socks5GSSAPITestServer.java - Basic test server for SOCKS5 GSSAPI authentication. 040 * 041 * NOTE: Launch this program with the following params in a pre-configured Kerberos V env. 042 * Do not forget to replace < ... > vars with your own values. 043 * 044 * -Djava.security.krb5.realm=<your_krb_realm> 045 * -Djavax.security.auth.useSubjectCredsOnly=false 046 * -Djava.security.krb5.kdc=<your_kdc_hostname> 047 * -Djava.security.auth.login.config=${workspace_loc}\Mina2Proxy\src\bcsLogin.conf 048 * -Dsun.security.krb5.debug=true 049 * 050 * @author <a href="http://mina.apache.org">Apache MINA Project</a> 051 * @since MINA 2.0.0-M3 052 */ 053public class Socks5GSSAPITestServer { 054 055 private final static Logger logger = LoggerFactory 056 .getLogger(Socks5GSSAPITestServer.class); 057 058 /** 059 * NOTE : change this to comply with your Kerberos environment. 060 */ 061 protected final static String SERVICE_NAME = "host/myworkstation.local.network"; 062 063 /** 064 * Selected mechanism message: advertises client to use SocksV5 protocol with 065 * GSSAPI authentication. 066 */ 067 public final static byte[] SELECT_GSSAPI_AUTH_MSG = new byte[] { 068 SocksProxyConstants.SOCKS_VERSION_5, 069 SocksProxyConstants.GSSAPI_AUTH }; 070 071 /** 072 * Simulates a Socks v5 server using only Kerberos V authentication. 073 * 074 * @param localPort the local port used to bind the server 075 * @throws IOException 076 * @throws GSSException 077 */ 078 private static void doHandShake(int localPort) throws IOException, 079 GSSException { 080 ServerSocket ss = new ServerSocket(localPort); 081 GSSManager manager = GSSManager.getInstance(); 082 083 /* 084 * Create a GSSContext to receive the incoming request from the client. 085 * Use null for the server credentials passed in to tell the underlying 086 * mechanism to use whatever credentials it has available that can be 087 * used to accept this connection. 088 */ 089 GSSCredential serverCreds = manager.createCredential(manager 090 .createName(SERVICE_NAME, null), 091 GSSCredential.DEFAULT_LIFETIME, new Oid( 092 SocksProxyConstants.KERBEROS_V5_OID), 093 GSSCredential.ACCEPT_ONLY); 094 095 while (true) { 096 logger.debug("Waiting for incoming connection on port {} ...", 097 localPort); 098 GSSContext context = manager.createContext(serverCreds); 099 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}