summaryrefslogblamecommitdiffstats
path: root/src/main/java/org/openslx/taskmanager/tasks/PortScan.java
blob: bf414794d0b6270e57b7fb338a27b7de93f467b9 (plain) (tree)
1
2
3
4
5
6
7
8

                                      

                           

                                  
                                         
                           




                                            
                                                 
                                                   

                               
 
                                       

                                                
                                          




                                          
 



                            

                                   


                                             



                                                                                                                                               


















                                                                     














                                                                                                                        

                                                                                                       
                                                               




                                                                     




                                                                                                                                          





                                                    
                                                                                       


                                                    



                                         







                                                                                    


                                                                           


                                                   
                                                                                       
                                    
                                                                     


                                         
                                                                                                           




                                                                                                    
                                                                                
                                                               



                                                                        
                                                                                             
                                                                                    
                                                                                                                 
                                         
                                                                                                     
                                                                                        














                                                                                                                                      
                                                                                                                           

                                                                         









                                                                        






                                                                                                                                                       
                 

                                                                                                                            







                                                                

                                                    
 
                                                                  















                                                       

                                                        
 
                                                                                                                                       




                                                           

                                                                 



                 
package org.openslx.taskmanager.tasks;

import java.io.File;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.apache.commons.io.FileUtils;
import org.openslx.satserver.util.Exec;
import org.openslx.taskmanager.api.AbstractTask;
import org.openslx.util.PrioThreadFactory;

import com.google.gson.annotations.Expose;

public class PortScan extends AbstractTask
{

	@Expose
	private String host;
	@Expose
	private int[] ports;
	@Expose
	private String certificate;

	private Output status = new Output();

	private String certFile = null;

	private static final Pattern verifyPattern = Pattern.compile( "^\\s*verify return code:\\s+(\\d+)(\\D|$)", Pattern.CASE_INSENSITIVE );;

	@Override
	protected boolean initTask()
	{
		this.setStatusObject( this.status );

		if ( this.host == null || this.host.isEmpty() ) {
			status.addMessage( "No host given!" );
			return false;
		}
		if ( this.ports == null || this.ports.length == 0 ) {
			status.addMessage( "No ports given!" );
			return false;
		}
		return true;
	}

	@Override
	protected boolean execute()
	{
		// Create usable cert file if possible
		File tmpFile = null;
		if ( certificate == null || certificate.isEmpty() || certificate.equals( "default" ) ) {
			certFile = CreateLdapConfig.DEFAULT_CA_BUNDLE;
		} else {
			try {
				tmpFile = File.createTempFile( "bwlp-", ".pem" );
				FileUtils.writeStringToFile( tmpFile, certificate, StandardCharsets.UTF_8 );
			} catch ( IOException e ) {
				status.addMessage( "Error creating temporary file for certificate: " + e.getMessage() );
				return false;
			}
			certFile = tmpFile.getAbsolutePath();
		}
		// Execute scan in parallel (4 tasks) to speed things up
		ExecutorService tp = Executors.newFixedThreadPool( ports.length > 4 ? 4 : ports.length,
				new PrioThreadFactory( "PortScan" ) );
		final List<Result> results = new ArrayList<>();
		for ( final int port : ports ) {
			tp.submit( new Callable<Object>() {
				@Override
				public Object call() throws Exception
				{
					try {
						results.add( testPort( port ) );
					} catch ( Exception e ) {
						status.addMessage( "Exception occured when checking port " + port + ": " + e.toString() );
					}
					return null;
				}
			} );
		}
		tp.shutdown();
		try {
			tp.awaitTermination( ports.length * 2 + 10, TimeUnit.SECONDS );
		} catch ( InterruptedException e ) {
			// ...
		}
		if ( tmpFile != null ) {
			tmpFile.delete();
		}
		status.ports = results;
		return true;
	}

	private Result testPort( int port )
	{
		boolean open = false;
		final AtomicReference<String> fingerprint = new AtomicReference<>();
		final AtomicReference<String> notAfter = new AtomicReference<>();
		final AtomicInteger verifyResult = new AtomicInteger( -1 );
		final StringBuffer messages = new StringBuffer();
		final StringBuffer certList = new StringBuffer();

		try {
			Socket sock = new Socket();
			sock.connect( new InetSocketAddress( this.host, port ), 1500 );
			open = true;
			messages.append( "Found open port " + port );
			sock.close();
		} catch ( Exception e ) {
			if ( !open ) {
				messages.append( "Found closed port " + port + " (" + e.toString() + ")" );
			}
		}
		if ( open ) {
			String str = this.host.replaceAll( "[^a-zA-Z0-9\\.\\-_]", "" ) + ":" + port;
			// Is open, see if it is running SSL
			int exitCode = Exec.syncAt( 4, new Exec.ExecCallback() {
				private boolean inCert = false;

				@Override
				public void processStdOut( String line )
				{
					if ( line.toLowerCase().startsWith( "notafter=" ) ) {
						notAfter.set( line.substring( 9 ) );
						messages.append( "\nCertificate valid until " + notAfter.get() );
					}
					if ( line.toLowerCase().startsWith( "sha1 fingerprint=" ) ) {
						fingerprint.set( line.substring( 17 ) );
						messages.append( "\nCertificate fingerprint: " + fingerprint.get() );
					}
					if ( !inCert && line.equals( "-----BEGIN CERTIFICATE-----" ) ) {
						inCert = true;
					}
					if ( inCert ) {
						if ( line.equals( "-----END CERTIFICATE-----" ) ) {
							inCert = false;
						}
						certList.append( line );
						certList.append( '\n' );
					}
					Matcher m;
					if ( verifyResult.get() == -1 && null != ( m = verifyPattern.matcher( line ) ) && m.find() ) {
						try {
							verifyResult.compareAndSet( -1, Integer.parseInt( m.group( 1 ) ) );
						} catch ( Exception e ) {
						}
					}
				}

				@Override
				public void processStdErr( String line )
				{
					// Nothing will be here
				}

			}, "/", "/bin/sh", "-c",
					"openssl s_client -CAfile '" + certFile + "' -showcerts -connect " + str + " </dev/null 2> /dev/null; RET=$? ;"
							+ " openssl s_client -connect " + str + " </dev/null 2> /dev/null "
							+ " | openssl x509 -noout -enddate -fingerprint -sha1 2>&1 ; exit $(( RET + $? ))" );
			if ( exitCode != 0 && ( fingerprint.get() == null || fingerprint.get().isEmpty() ) ) {
				verifyResult.set( -2 );
			}
			messages.append( "\nVerify result: " + verifyResult.get() );
		}
		status.addMessage( messages.toString() );
		return new Result( port, open, fingerprint.get(), notAfter.get(), verifyResult.get(), certList.toString() );
	}

	/**
	 * Output - contains additional status data of this task
	 */
	private static class Output
	{
		protected String messages = null;
		@SuppressWarnings( "unused" )
		protected List<Result> ports = null;

		private synchronized void addMessage( String str )
		{
			if ( messages == null ) {
				messages = str;
			} else {
				messages += "\n" + str;
			}
		}
	}

	@SuppressWarnings( "unused" )
	private static class Result
	{
		protected final int port;
		protected final boolean open;
		protected final String certFingerprint;
		protected final String notAfter;
		protected final int verifyResult;
		protected final String certificateChain;

		public Result( int port, boolean open, String fingerprint, String notAfter, int verifyResult, String certificateChain )
		{
			this.port = port;
			this.open = open;
			this.certFingerprint = fingerprint;
			this.notAfter = notAfter;
			this.verifyResult = verifyResult;
			this.certificateChain = certificateChain;
		}
	}

}