diff --git a/jams-common/pom.xml b/jams-common/pom.xml index 065b4c7495ea8df8fa4e347d69376cf681f860ff..5bc99ad07fec94f340f460b601b1f2e8eda156f0 100644 --- a/jams-common/pom.xml +++ b/jams-common/pom.xml @@ -27,6 +27,11 @@ <artifactId>xbean-classloader</artifactId> <version>${xbean.version}</version> </dependency> + <dependency> + <groupId>org.zeromq</groupId> + <artifactId>jeromq</artifactId> + <version>0.5.2</version> + </dependency> </dependencies> </project> \ No newline at end of file diff --git a/jams-common/src/main/java/module-info.java b/jams-common/src/main/java/module-info.java index 37e5ce3059229a0f59d880bf050ae093cc3da22e..4358de73c8dc950dee314af827dfc1df62752566 100644 --- a/jams-common/src/main/java/module-info.java +++ b/jams-common/src/main/java/module-info.java @@ -56,5 +56,6 @@ module jams.common { requires java.base; requires java.sql; requires org.apache.xbean.classloader; + requires jeromq; } diff --git a/jams-common/src/main/java/net/jami/jams/common/utils/LicenseUtils.java b/jams-common/src/main/java/net/jami/jams/common/utils/LicenseUtils.java new file mode 100644 index 0000000000000000000000000000000000000000..6dda71e7b8a8ddf65e6095c9c03aef26a29b15e5 --- /dev/null +++ b/jams-common/src/main/java/net/jami/jams/common/utils/LicenseUtils.java @@ -0,0 +1,65 @@ +package net.jami.jams.common.utils; + + +import lombok.extern.slf4j.Slf4j; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.net.URL; +import java.util.Enumeration; +import java.util.jar.JarEntry; +import java.util.jar.JarFile; +import java.util.jar.Manifest; + +@Slf4j +public class LicenseUtils { + + + public static String checkVersion(String destinationDir, String jarPath) throws IOException { + String resp = ""; + File file = new File(jarPath); + JarFile jar = new JarFile(file); + + // first get all directories, + // then make those directory on the destination Path + for (Enumeration<JarEntry> enums = jar.entries(); enums.hasMoreElements(); ) { + JarEntry entry = (JarEntry) enums.nextElement(); + + String fileName = destinationDir + File.separator + entry.getName(); + File f = new File(fileName); + + if (fileName.endsWith("/")) { + f.mkdirs(); + } + } + + //now create all files + for (Enumeration<JarEntry> enums = jar.entries(); enums.hasMoreElements(); ) { + JarEntry entry = (JarEntry) enums.nextElement(); + if (entry.toString().contains("META-INF/MANIFEST.MF")) { + String fileName = destinationDir + File.separator + entry.getName(); + File f = new File(fileName); + + if (!fileName.endsWith("/")) { + InputStream is = jar.getInputStream(entry); + FileOutputStream fos = new FileOutputStream(f); + + // write contents of 'is' to 'fos' + while (is.available() > 0) { + fos.write(is.read()); + } + + fos.close(); + is.close(); + } + } + } + + String manifestPath = destinationDir + "/META-INF/MANIFEST.MF"; + Manifest manifest = new Manifest(new URL("file:///" + manifestPath).openStream()); + resp = manifest.getMainAttributes().getValue("Implementation-Version"); + return resp; + } +} diff --git a/jams-common/src/main/java/net/jami/jams/common/utils/UpdateInterface.java b/jams-common/src/main/java/net/jami/jams/common/utils/UpdateInterface.java new file mode 100644 index 0000000000000000000000000000000000000000..7bfcf4499a031bca37bc558e7f506bf64edcbcd9 --- /dev/null +++ b/jams-common/src/main/java/net/jami/jams/common/utils/UpdateInterface.java @@ -0,0 +1,46 @@ +package net.jami.jams.common.utils; + +import lombok.Getter; +import lombok.Setter; +import org.zeromq.SocketType; +import org.zeromq.ZMQ; + +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.logging.Logger; + +@Getter +@Setter +public class UpdateInterface extends Thread { + + private AtomicBoolean updateAvailable = new AtomicBoolean(false); + private volatile String versions; + private ZMQ.Context context = ZMQ.context(1); + private ZMQ.Socket sender = context.socket(SocketType.REQ); + private ZMQ.Socket receiver = context.socket(SocketType.SUB); + private final static Logger logger = Logger.getLogger(UpdateInterface.class.getName()); + + public UpdateInterface() { + receiver.connect("tcp://localhost:4572"); + sender.connect("tcp://localhost:4573"); + receiver.subscribe("UPDATE"); + this.start(); + } + + public void approveUpdate(){ + sender.send("DO-UPDATE"); + } + + @Override + public void run() { + while(true){ + try{ + receiver.recv(); + updateAvailable.set(true); + versions = receiver.recvStr(); + } + catch (Exception e){ + System.out.println(e.toString()); + } + } + } +} diff --git a/jams-launcher/pom.xml b/jams-launcher/pom.xml index 54ecb9f5d32d9e26da7c18bd05fb75afbe9b5883..9a63478b6d47c33f5454300045db1a48467958f2 100644 --- a/jams-launcher/pom.xml +++ b/jams-launcher/pom.xml @@ -36,11 +36,6 @@ <artifactId>guava</artifactId> <version>20.0</version> </dependency> - <dependency> - <groupId>org.zeromq</groupId> - <artifactId>jeromq</artifactId> - <version>0.5.2</version> - </dependency> <dependency> <groupId>com.blockfeed</groupId> <artifactId>messaging</artifactId> diff --git a/jams-server/pom.xml b/jams-server/pom.xml index 16d4bc93a882176e56580aec0eaf03f8167df009..df79a7a973d6746c7d9745a60f0c4b535f483b48 100644 --- a/jams-server/pom.xml +++ b/jams-server/pom.xml @@ -66,6 +66,11 @@ <artifactId>asm</artifactId> <version>${asm.version}</version> </dependency> + <dependency> + <groupId>org.json</groupId> + <artifactId>json</artifactId> + <version>20190722</version> + </dependency> </dependencies> <build> diff --git a/jams-server/src/main/java/module-info.java b/jams-server/src/main/java/module-info.java index 6024a4383902e5a0f5003b4416d8271996a6bcd7..b695686d7f4179a841768289a220bac0b94f7e9f 100644 --- a/jams-server/src/main/java/module-info.java +++ b/jams-server/src/main/java/module-info.java @@ -14,6 +14,10 @@ module jams.server { requires jami.dht; requires nimbus.jose.jwt; requires java.desktop; + requires org.json; + requires java.naming; + requires java.logging; + requires javax.servlet.api; exports net.jami.jams.server.servlets.general to org.apache.tomcat.embed.core; exports net.jami.jams.server.servlets.filters to org.apache.tomcat.embed.core; exports net.jami.jams.server.servlets.api.auth.login to org.apache.tomcat.embed.core; diff --git a/jams-server/src/main/java/net/jami/jams/server/Server.java b/jams-server/src/main/java/net/jami/jams/server/Server.java index a4ae6537c442e9932961641c925a3bcf1ff7b33a..ad882a203f2578ae7a07c59057c00f6bce0cffa9 100644 --- a/jams-server/src/main/java/net/jami/jams/server/Server.java +++ b/jams-server/src/main/java/net/jami/jams/server/Server.java @@ -11,6 +11,7 @@ import net.jami.jams.common.jami.NameServer; import net.jami.jams.common.serialization.JsoniterRegistry; import net.jami.jams.common.server.ServerSettings; import net.jami.jams.common.utils.LibraryLoader; +import net.jami.jams.common.utils.UpdateInterface; import net.jami.jams.nameserver.LocalNameServer; import net.jami.jams.nameserver.PublicNameServer; import net.jami.jams.server.core.TomcatLauncher; @@ -26,6 +27,7 @@ import java.util.concurrent.atomic.AtomicBoolean; public class Server { public static AtomicBoolean isInstalled = new AtomicBoolean(false); + private static volatile boolean activated = false; static { JsoniterRegistry.initCodecs(); @@ -37,6 +39,7 @@ public class Server { public static AuthenticationModule userAuthenticationModule; public static NameServer nameServer; private static TomcatLauncher tomcatLauncher = null; + public static final UpdateInterface updateInterface = new UpdateInterface(); public static void main(String[] args) { //Start tomcat. @@ -89,4 +92,12 @@ public class Server { log.info("Started server with empty modules waiting for setup..."); } } + + public static boolean isActivated() { + return activated; + } + + public static void setActivated(boolean isActivated) { + Server.activated = isActivated; + } } diff --git a/jams-server/src/main/java/net/jami/jams/server/licensing/LicenseService.java b/jams-server/src/main/java/net/jami/jams/server/licensing/LicenseService.java new file mode 100644 index 0000000000000000000000000000000000000000..1e0f30935cfbc0a0deb2e834faba4d43aadafa00 --- /dev/null +++ b/jams-server/src/main/java/net/jami/jams/server/licensing/LicenseService.java @@ -0,0 +1,90 @@ +package net.jami.jams.server.licensing; + +import lombok.Getter; +import lombok.Setter; +import lombok.extern.slf4j.Slf4j; +import net.jami.jams.server.Server; +import org.json.JSONObject; + +import javax.naming.ldap.LdapName; +import javax.naming.ldap.Rdn; +import java.io.ByteArrayInputStream; +import java.io.File; +import java.io.InputStream; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.security.cert.Certificate; +import java.security.cert.CertificateFactory; +import java.security.cert.X509Certificate; +import java.util.Base64; +import java.util.concurrent.atomic.AtomicBoolean; + +@Getter +@Setter +@Slf4j +public class LicenseService { + + private AtomicBoolean activationStatus = new AtomicBoolean(false); + private String licenseType = "COMMUNITY"; + + //Load the license. + public void loadLicense() { + try { + CertificateFactory cf = CertificateFactory.getInstance("X.509"); + //Assuming the file exists, we just read the file. + String b64License = new String(Files.readAllBytes(Path.of(System.getProperty("user.dir") + File.separator + "license.dat"))); + //Since this is base64, we need to decode it. + String strLicense = new String(Base64.getDecoder().decode(b64License)); + //Now we need to split it. This is actually easy. + int cutPoint = strLicense.indexOf("-----BEGIN PRIVATE KEY-----"); + String publicKey = strLicense.substring(0,cutPoint); + + InputStream inputStream = new ByteArrayInputStream(publicKey.getBytes(StandardCharsets.UTF_8)); + Certificate c = cf.generateCertificate(inputStream); + String dn = ((X509Certificate)c).getSubjectDN().toString(); + LdapName ln = new LdapName(dn); + byte[] array = null; + for(Rdn rdn : ln.getRdns()) { + try { + array = Base64.getDecoder().decode(((String)rdn.getValue()).getBytes()); + } catch (IllegalArgumentException e) { + + } + } + + //This is kept inside the resources/oem folder, this is a lot less violent than what we had before. + //TODO: You should re-use the same technique to validate whatever they stick in the textbox. + InputStream input = LicenseService.class.getClassLoader().getResourceAsStream("oem/ca.crt"); + if (input == null) { + System.out.println("No CA Found... this is critical!"); + System.exit(-1); + } + Certificate ca = cf.generateCertificate(input); + try{ + ((X509Certificate) c).checkValidity(); + c.verify(ca.getPublicKey()); + Server.setActivated(true); + if (array != null) { + JSONObject jsonObject = new JSONObject(new String(array)); + licenseType = (String) jsonObject.get("type"); + } + } + catch (Exception e){ + Server.setActivated(false); + licenseType = "COMMUNITY"; + log.warn("Your license is no longer valid or has been tampered with - " + e.toString()); + } + } + catch (Exception e){ + Server.setActivated(false); + licenseType = "COMMUNITY"; + //logger.warning("An exception occurred while checking your license: " + e.toString()); + } + } + + public String getLicenseType() { + loadLicense(); + return licenseType; + } +} diff --git a/jams-server/src/main/java/net/jami/jams/server/servlets/api/update/LicenseServlet.java b/jams-server/src/main/java/net/jami/jams/server/servlets/api/update/LicenseServlet.java new file mode 100644 index 0000000000000000000000000000000000000000..de2518e1b2e002f8e5be1458852755164d38260d --- /dev/null +++ b/jams-server/src/main/java/net/jami/jams/server/servlets/api/update/LicenseServlet.java @@ -0,0 +1,39 @@ +package net.jami.jams.server.servlets.api.update; + +import com.jsoniter.output.JsonStream; +import net.jami.jams.ca.JamsCA; +import net.jami.jams.common.cryptoengineapi.CertificateAuthority; +import net.jami.jams.common.utils.LicenseUtils; +import net.jami.jams.server.Server; +import net.jami.jams.server.licensing.LicenseService; +import net.jami.jams.server.startup.CryptoEngineLoader; + +import javax.servlet.annotation.WebServlet; +import javax.servlet.http.HttpServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.util.HashMap; + + +@WebServlet("/api/auth/license") +public class LicenseServlet extends HttpServlet { + + + @Override + protected void doGet(HttpServletRequest req, HttpServletResponse resp) { + resp.setHeader("Access-Control-Allow-Origin", JamsCA.serverDomain); + resp.setContentType("application/json"); + try { + resp.setStatus(200); + HashMap<String,Object> payload = new HashMap<>(); + payload.put("isActive", Server.isActivated()); + payload.put("licenseType", new LicenseService().getLicenseType()); + payload.put("currentVersion", LicenseUtils.checkVersion(System.getProperty("user.dir") + "/tmpjar/", System.getProperty("user.dir") + "/jams-server.jar")); + resp.getOutputStream().write(JsonStream.serialize(payload).getBytes()); + } + catch (Exception e){ + resp.setStatus(403); + } + } + +} \ No newline at end of file diff --git a/jams-server/src/main/java/net/jami/jams/server/servlets/api/update/NeedsUpdateServlet.java b/jams-server/src/main/java/net/jami/jams/server/servlets/api/update/NeedsUpdateServlet.java new file mode 100644 index 0000000000000000000000000000000000000000..cae916d93dc1f27aeb0a5e280ba50d3381d675f7 --- /dev/null +++ b/jams-server/src/main/java/net/jami/jams/server/servlets/api/update/NeedsUpdateServlet.java @@ -0,0 +1,46 @@ +package net.jami.jams.server.servlets.api.update; + +import com.jsoniter.output.JsonStream; +import net.jami.jams.ca.JamsCA; +import net.jami.jams.server.Server; + +import javax.servlet.ServletException; +import javax.servlet.annotation.WebServlet; +import javax.servlet.http.HttpServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.util.HashMap; +import java.util.logging.Logger; + +@WebServlet("/api/checkupdate") +public class NeedsUpdateServlet extends HttpServlet { + + private final static Logger logger = Logger.getLogger(NeedsUpdateServlet.class.getName()); + + @Override + protected void doGet(HttpServletRequest req, HttpServletResponse resp) { + resp.setHeader("Access-Control-Allow-Origin", JamsCA.serverDomain); + resp.setContentType("application/json"); + try { + HashMap<String,Object> payload = new HashMap<>(); + if(!Server.updateInterface.getVersions().equals("{}")) { + payload.put("updateAvailable", Server.updateInterface.getUpdateAvailable().get()); + payload.put("newVersions", Server.updateInterface.getVersions()); + } + else + payload.put("updateAvailable",false); + + resp.getOutputStream().write(JsonStream.serialize(payload).getBytes()); + resp.setStatus(200); + } + catch (Exception e){ + resp.setStatus(500); + } + } + + @Override + protected void doPut(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { + + } +} diff --git a/jams-server/src/main/java/net/jami/jams/server/servlets/api/update/StartUpdateServlet.java b/jams-server/src/main/java/net/jami/jams/server/servlets/api/update/StartUpdateServlet.java new file mode 100644 index 0000000000000000000000000000000000000000..52b1e38be8fd68afd411a3c070d54a2b812abe71 --- /dev/null +++ b/jams-server/src/main/java/net/jami/jams/server/servlets/api/update/StartUpdateServlet.java @@ -0,0 +1,29 @@ +package net.jami.jams.server.servlets.api.update; + +import net.jami.jams.ca.JamsCA; +import net.jami.jams.server.Server; + +import javax.servlet.annotation.WebServlet; +import javax.servlet.http.HttpServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.util.logging.Logger; + +@WebServlet("/api/startupdate") +public class StartUpdateServlet extends HttpServlet { + + private final static Logger logger = Logger.getLogger(StartUpdateServlet.class.getName()); + + @Override + protected void doGet(HttpServletRequest req, HttpServletResponse resp) { + resp.setHeader("Access-Control-Allow-Origin", JamsCA.serverDomain); + resp.setContentType("application/json"); + try { + Server.updateInterface.approveUpdate(); + resp.setStatus(200); + } + catch (Exception e){ + resp.setStatus(500); + } + } +}