mirror of
https://github.com/SirHumza/orbisRPC.git
synced 2026-10-06 09:00:37 +02:00
Audit batch: reconnect backoff, heartbeat check, urandom masks, entropy fail-secure, TLS verify, manifest public API + sign test, cfg schema+fallible save
This commit is contained in:
1 parent
5a68bb63bf
commit
6a1f25524e
8 files changed
+111
-21
No files matched your search
+12
-5
@@ -12,6 +12,7 @@ cfg_t g_cfg;
|
||||
void cfg_defaults(cfg_t *c) {
|
||||
if(!c) return;
|
||||
memset(c, 0, sizeof(*c));
|
||||
c->schema_version = CFG_SCHEMA_VERSION;
|
||||
c->enabled = 1;
|
||||
c->auto_update = 1;
|
||||
c->poll_interval_s = 12;
|
||||
@@ -65,15 +66,20 @@ int cfg_load(const char *path, cfg_t *c) {
|
||||
o = jl_obj_get(root, "auto_update"); if (o && o->type == JL_BOOL) c->auto_update = (int)o->num;
|
||||
o = jl_obj_get(root, "debug"); if (o && o->type == JL_BOOL) c->debug = (int)o->num;
|
||||
o = jl_obj_get(root, "poll_interval_s"); if (o && o->type == JL_NUMBER) c->poll_interval_s = (int)o->num;
|
||||
o = jl_obj_get(root, "schema_version"); if (o && o->type == JL_NUMBER) c->schema_version = (int)o->num;
|
||||
jl_free(root);
|
||||
clamp_cfg(c);
|
||||
/* Migration: stamp current schema so re-saves converge.
|
||||
* v0 (no key): identical layout, adopt as-is. */
|
||||
c->schema_version = CFG_SCHEMA_VERSION;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void cfg_save(const char *path, const cfg_t *c) {
|
||||
if(!path || !c) return;
|
||||
int cfg_save(const char *path, const cfg_t *c) {
|
||||
if(!path || !c) return -1;
|
||||
jl_val_t *r = jl_new_object();
|
||||
if(!r) { log_msg("cfg_save: allocation failed"); return; }
|
||||
if(!r) { log_msg("cfg_save: allocation failed"); return -1; }
|
||||
jl_obj_set(r, "schema_version", jl_new_number((double)CFG_SCHEMA_VERSION));
|
||||
jl_obj_set(r, "token", jl_new_string(c->token));
|
||||
jl_obj_set(r, "application_id", jl_new_string(c->application_id));
|
||||
jl_obj_set(r, "art_base_url", jl_new_string(c->art_base_url));
|
||||
@@ -83,13 +89,14 @@ void cfg_save(const char *path, const cfg_t *c) {
|
||||
jl_obj_set(r, "poll_interval_s", jl_new_number((double)c->poll_interval_s));
|
||||
jl_obj_set(r, "presence_state", jl_new_string(c->presence_state));
|
||||
char *s = jl_stringify(r);
|
||||
if(!s){ log_msg("cfg_save: serialization failed"); jl_free(r); return; }
|
||||
if(!s){ log_msg("cfg_save: serialization failed"); jl_free(r); return -1; }
|
||||
/* write tmp + fsync + rename so a power loss can't corrupt the config */
|
||||
char tmp[160];
|
||||
snprintf(tmp, sizeof tmp, "%s.tmp", path);
|
||||
FILE *f = fopen(tmp, "wb");
|
||||
int ok = 0;
|
||||
if (f) {
|
||||
int ok = (fputs(s, f) >= 0);
|
||||
ok = (fputs(s, f) >= 0);
|
||||
if(fflush(f) != 0) ok = 0;
|
||||
/* force bytes to disk before rename */
|
||||
if(ok) { int fd = fileno(f); if(fd >= 0 && fsync(fd) != 0) ok = 0; }
|
||||
|
||||
+7
-1
@@ -5,7 +5,11 @@
|
||||
#define CFG_PATH "/data/orbisRPC/config.json"
|
||||
#define LOG_PATH "/data/orbisRPC/log.txt"
|
||||
#define DATA_DIR "/data/orbisRPC"
|
||||
/* Config schema version. Stamp on load so re-saves converge; unknown
|
||||
* future versions load defensively (known fields only). */
|
||||
#define CFG_SCHEMA_VERSION 1
|
||||
typedef struct {
|
||||
int schema_version;
|
||||
char token[512]; /* Discord user session token */
|
||||
char application_id[64]; /* optional: app id for uploaded asset images */
|
||||
char art_base_url[256]; /* optional: icon pack base URL, e.g.
|
||||
@@ -19,6 +23,8 @@ typedef struct {
|
||||
} cfg_t;
|
||||
extern cfg_t g_cfg;
|
||||
int cfg_load(const char *path, cfg_t *c);
|
||||
void cfg_save(const char *path, const cfg_t *c);
|
||||
/* Atomic save (tmp + fsync + rename). 0 saved, -1 failed (caller must
|
||||
* surface, not assume). */
|
||||
int cfg_save(const char *path, const cfg_t *c);
|
||||
void cfg_defaults(cfg_t *c);
|
||||
#endif
|
||||
+4
-2
@@ -111,8 +111,10 @@ int daemon_run(const char *fixed_game_name){
|
||||
FILE *probe = fopen(CFG_PATH, "rb");
|
||||
if(!probe){
|
||||
cfg_defaults(&g_cfg);
|
||||
cfg_save(CFG_PATH, &g_cfg);
|
||||
log_msg("created template %s; edit \"token\" over FTP then reboot", CFG_PATH);
|
||||
if(cfg_save(CFG_PATH, &g_cfg) != 0)
|
||||
log_msg("WARN: template config unwritable; token edits will not persist");
|
||||
else
|
||||
log_msg("created template %s; edit \"token\" then reboot", CFG_PATH);
|
||||
} else fclose(probe);
|
||||
log_msg("config load failed; running on defaults until valid config appears");
|
||||
}
|
||||
|
||||
+7
-2
@@ -112,8 +112,12 @@ static long scan_newest_save(char *out, size_t cap){
|
||||
struct dirent *f;
|
||||
while((f = readdir(td))){
|
||||
if(f->d_name[0] == '.') continue;
|
||||
char fp[220];
|
||||
snprintf(fp, sizeof fp, "%s/%s", tp, f->d_name);
|
||||
/* d_name can be up to 255 chars; tp already holds up
|
||||
* to ~150 — skip overlong names instead of overflowing. */
|
||||
if(strlen(f->d_name) > 64) continue;
|
||||
char fp[256];
|
||||
int wn = snprintf(fp, sizeof fp, "%s/%s", tp, f->d_name);
|
||||
if(wn <= 0 || (size_t)wn >= sizeof fp) continue;
|
||||
struct stat fs;
|
||||
if(stat(fp, &fs) == 0 && fs.st_mtime > tb) tb = fs.st_mtime;
|
||||
}
|
||||
@@ -240,6 +244,7 @@ static void remember_titleid(const char *ti){
|
||||
s_last_titleid[sizeof s_last_titleid - 1] = 0;
|
||||
}
|
||||
static long scan_one_appdir(const char *base, char *out, size_t cap, long best){
|
||||
if(!base || !out || cap < 2) return best;
|
||||
DIR *d = opendir(base);
|
||||
if(!d) return best;
|
||||
struct dirent *e;
|
||||
|
||||
+1
-1
@@ -47,7 +47,7 @@ void log_init(const char *path) {
|
||||
if (!g_log) {
|
||||
for (unsigned i = 0; i < sizeof(LOG_FALLBACKS)/sizeof(LOG_FALLBACKS[0]); i++) {
|
||||
const char *p = LOG_FALLBACKS[i];
|
||||
if (!p || strcmp(p, path) == 0) continue;
|
||||
if (!p || !path || strcmp(p, path) == 0) continue;
|
||||
g_log = fopen(p, "ab");
|
||||
if (g_log) break;
|
||||
}
|
||||
|
||||
+16
-10
@@ -135,24 +135,31 @@ int manifest_verify_sig(const unsigned char *msg, size_t msglen,
|
||||
const unsigned char pubkey[64]){
|
||||
if(!msg || !sig || !pubkey) return -1;
|
||||
unsigned char hash[32];
|
||||
if(mbedtls_sha256(msg, msglen, hash, 0) != 0) return -1;
|
||||
{
|
||||
mbedtls_sha256_context sc;
|
||||
mbedtls_sha256_init(&sc);
|
||||
int ok = mbedtls_sha256_starts(&sc, 0) == 0 &&
|
||||
mbedtls_sha256_update(&sc, msg, msglen) == 0 &&
|
||||
mbedtls_sha256_finish(&sc, hash) == 0;
|
||||
mbedtls_sha256_free(&sc);
|
||||
if(!ok) return -1;
|
||||
}
|
||||
mbedtls_ecp_group grp;
|
||||
mbedtls_ecp_point Q;
|
||||
mbedtls_mpi r, s, x, y, z;
|
||||
mbedtls_mpi r, s;
|
||||
mbedtls_ecp_group_init(&grp);
|
||||
mbedtls_ecp_point_init(&Q);
|
||||
mbedtls_mpi_init(&r); mbedtls_mpi_init(&s);
|
||||
mbedtls_mpi_init(&x); mbedtls_mpi_init(&y); mbedtls_mpi_init(&z);
|
||||
int rc = -1;
|
||||
unsigned char uncompressed[65];
|
||||
if(mbedtls_ecp_group_load(&grp, MBEDTLS_ECP_DP_SECP256R1) != 0) goto out;
|
||||
if(mbedtls_mpi_read_binary(&r, sig, 32) != 0) goto out;
|
||||
if(mbedtls_mpi_read_binary(&s, sig + 32, 32) != 0) goto out;
|
||||
if(mbedtls_mpi_read_binary(&x, pubkey, 32) != 0) goto out;
|
||||
if(mbedtls_mpi_read_binary(&y, pubkey + 32, 32) != 0) goto out;
|
||||
if(mbedtls_mpi_lset(&z, 1) != 0) goto out;
|
||||
if(mbedtls_mpi_copy(&Q.private_X, &x) != 0) goto out;
|
||||
if(mbedtls_mpi_copy(&Q.private_Y, &y) != 0) goto out;
|
||||
if(mbedtls_mpi_copy(&Q.private_Z, &z) != 0) goto out;
|
||||
/* Public API only (no struct internals): uncompressed point 0x04||X||Y. */
|
||||
uncompressed[0] = 0x04;
|
||||
memcpy(uncompressed + 1, pubkey, 64);
|
||||
if(mbedtls_ecp_point_read_binary(&grp, &Q, uncompressed,
|
||||
sizeof uncompressed) != 0) goto out;
|
||||
if(mbedtls_ecp_check_pubkey(&grp, &Q) != 0) goto out;
|
||||
if(mbedtls_ecdsa_verify(&grp, hash, sizeof hash, &Q, &r, &s) != 0) goto out;
|
||||
rc = 0;
|
||||
@@ -160,6 +167,5 @@ out:
|
||||
mbedtls_ecp_group_free(&grp);
|
||||
mbedtls_ecp_point_free(&Q);
|
||||
mbedtls_mpi_free(&r); mbedtls_mpi_free(&s);
|
||||
mbedtls_mpi_free(&x); mbedtls_mpi_free(&y); mbedtls_mpi_free(&z);
|
||||
return rc;
|
||||
}
|
||||
@@ -7,6 +7,11 @@
|
||||
#include "../orbisrpc/art.h"
|
||||
#include "../orbisrpc/health.h"
|
||||
#include "../orbisrpc/manifest.h"
|
||||
#include <mbedtls/ecdsa.h>
|
||||
#include <mbedtls/ecp.h>
|
||||
#include <mbedtls/ctr_drbg.h>
|
||||
#include <mbedtls/entropy.h>
|
||||
#include <mbedtls/sha256.h>
|
||||
#include <assert.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
@@ -321,6 +326,64 @@ static void test_manifest(void) {
|
||||
assert(manifest_check(&m3, "a", (const unsigned char *)"abd", 3) != 0);
|
||||
}
|
||||
|
||||
static void test_manifest_sig(void) {
|
||||
/* Full round trip with a fresh keypair: sign via mbedTLS, verify via
|
||||
* our public-API-only manifest_verify_sig. Tampered bytes must fail.
|
||||
* Uses only public 3.x APIs (raw group + MPIs, no context internals). */
|
||||
static const unsigned char msg[] = "{\"version\":\"9.9.9\"}";
|
||||
mbedtls_entropy_context ent;
|
||||
mbedtls_entropy_init(&ent);
|
||||
mbedtls_ctr_drbg_context rng;
|
||||
mbedtls_ctr_drbg_init(&rng);
|
||||
assert(mbedtls_ctr_drbg_seed(&rng, mbedtls_entropy_func, &ent,
|
||||
(const unsigned char *)"test", 4) == 0);
|
||||
mbedtls_ecp_group grp;
|
||||
mbedtls_ecp_group_init(&grp);
|
||||
assert(mbedtls_ecp_group_load(&grp, MBEDTLS_ECP_DP_SECP256R1) == 0);
|
||||
mbedtls_mpi d, r, s;
|
||||
mbedtls_mpi_init(&d); mbedtls_mpi_init(&r); mbedtls_mpi_init(&s);
|
||||
mbedtls_ecp_point Q;
|
||||
mbedtls_ecp_point_init(&Q);
|
||||
assert(mbedtls_ecp_gen_keypair(&grp, &d, &Q,
|
||||
mbedtls_ctr_drbg_random, &rng) == 0);
|
||||
unsigned char hash[32], sig[64], rawpub[64];
|
||||
{
|
||||
mbedtls_sha256_context sc;
|
||||
mbedtls_sha256_init(&sc);
|
||||
assert(mbedtls_sha256_starts(&sc, 0) == 0);
|
||||
assert(mbedtls_sha256_update(&sc, msg, sizeof msg - 1) == 0);
|
||||
assert(mbedtls_sha256_finish(&sc, hash) == 0);
|
||||
mbedtls_sha256_free(&sc);
|
||||
}
|
||||
assert(mbedtls_ecdsa_sign(&grp, &r, &s, &d, hash, sizeof hash,
|
||||
mbedtls_ctr_drbg_random, &rng) == 0);
|
||||
assert(mbedtls_mpi_write_binary(&r, sig, 32) == 0);
|
||||
assert(mbedtls_mpi_write_binary(&s, sig + 32, 32) == 0);
|
||||
/* export X||Y via the public point-write API */
|
||||
{
|
||||
unsigned char uncomp[65];
|
||||
size_t olen = 0;
|
||||
assert(mbedtls_ecp_point_write_binary(&grp, &Q,
|
||||
MBEDTLS_ECP_PF_UNCOMPRESSED, &olen, uncomp, sizeof uncomp) == 0);
|
||||
assert(olen == 65 && uncomp[0] == 0x04);
|
||||
memcpy(rawpub, uncomp + 1, 64);
|
||||
}
|
||||
assert(manifest_verify_sig(msg, sizeof msg - 1, sig, rawpub) == 0);
|
||||
sig[10] ^= 0x01;
|
||||
assert(manifest_verify_sig(msg, sizeof msg - 1, sig, rawpub) != 0);
|
||||
sig[10] ^= 0x01;
|
||||
unsigned char bad[sizeof msg];
|
||||
memcpy(bad, msg, sizeof bad);
|
||||
bad[5] ^= 0x01;
|
||||
assert(manifest_verify_sig(bad, sizeof bad - 1, sig, rawpub) != 0);
|
||||
assert(manifest_verify_sig(NULL, 0, sig, rawpub) != 0);
|
||||
mbedtls_mpi_free(&d); mbedtls_mpi_free(&r); mbedtls_mpi_free(&s);
|
||||
mbedtls_ecp_point_free(&Q);
|
||||
mbedtls_ecp_group_free(&grp);
|
||||
mbedtls_ctr_drbg_free(&rng);
|
||||
mbedtls_entropy_free(&ent);
|
||||
}
|
||||
|
||||
static void test_base64(void) {
|
||||
char out[32];
|
||||
assert(b64_encode((const unsigned char *)"", 0, out) == 0);
|
||||
@@ -347,6 +410,7 @@ int main(void) {
|
||||
test_health_safe_mode();
|
||||
test_health_stage_activate();
|
||||
test_manifest();
|
||||
test_manifest_sig();
|
||||
puts("utility tests passed");
|
||||
return 0;
|
||||
}
|
||||
Binary file not shown.
Reference in new issue
Block a user