Skip to content

Commit

Permalink
Double boot protection (#657)
Browse files Browse the repository at this point in the history
  • Loading branch information
pkova authored Jul 10, 2024
2 parents a53619a + fad9af6 commit 85ae9b3
Show file tree
Hide file tree
Showing 4 changed files with 222 additions and 35 deletions.
22 changes: 2 additions & 20 deletions pkg/vere/dawn.c
Original file line number Diff line number Diff line change
Expand Up @@ -41,24 +41,6 @@ _dawn_buf_to_oct(uv_buf_t buf_u)
return u3nc(len, u3i_bytes(buf_u.len, (const c3_y*)buf_u.base));
}


/* _dawn_curl_alloc(): allocate a response buffer for curl
*/
static size_t
_dawn_curl_alloc(void* dat_v, size_t uni_t, size_t mem_t, void* buf_v)
{
uv_buf_t* buf_u = buf_v;

size_t siz_t = uni_t * mem_t;
buf_u->base = c3_realloc(buf_u->base, 1 + siz_t + buf_u->len);

memcpy(buf_u->base + buf_u->len, dat_v, siz_t);
buf_u->len += siz_t;
buf_u->base[buf_u->len] = 0;

return siz_t;
}

/* _dawn_post_json(): POST JSON to url_c
*/
static uv_buf_t
Expand All @@ -84,7 +66,7 @@ _dawn_post_json(c3_c* url_c, uv_buf_t lod_u)
//
u3K.ssl_curl_f(curl);
curl_easy_setopt(curl, CURLOPT_URL, url_c);
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, _dawn_curl_alloc);
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, king_curl_alloc);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void*)&buf_u);
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, hed_u);

Expand Down Expand Up @@ -131,7 +113,7 @@ _dawn_get_jam(c3_c* url_c)
//
u3K.ssl_curl_f(curl);
curl_easy_setopt(curl, CURLOPT_URL, url_c);
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, _dawn_curl_alloc);
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, king_curl_alloc);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void*)&buf_u);

result = curl_easy_perform(curl);
Expand Down
24 changes: 14 additions & 10 deletions pkg/vere/king.c
Original file line number Diff line number Diff line change
Expand Up @@ -270,11 +270,11 @@ _king_pier(u3_noun pier)
u3z(pier);
}

/* _king_curl_alloc(): allocate a response buffer for curl
/* king_curl_alloc(): allocate a response buffer for curl
** XX deduplicate with dawn.c
*/
static size_t
_king_curl_alloc(void* dat_v, size_t uni_t, size_t mem_t, void* buf_v)
size_t
king_curl_alloc(void* dat_v, size_t uni_t, size_t mem_t, void* buf_v)
{
uv_buf_t* buf_u = buf_v;

Expand All @@ -288,11 +288,11 @@ _king_curl_alloc(void* dat_v, size_t uni_t, size_t mem_t, void* buf_v)
return siz_t;
}

/* _king_curl_bytes(): HTTP GET url_c, produce response body bytes.
/* king_curl_bytes(): HTTP GET url_c, produce response body bytes.
** XX deduplicate with dawn.c
*/
static c3_i
_king_curl_bytes(c3_c* url_c, c3_w* len_w, c3_y** hun_y, c3_t veb_t)
c3_i
king_curl_bytes(c3_c* url_c, c3_w* len_w, c3_y** hun_y, c3_t veb_t)
{
c3_i ret_i = 0;
CURL *cul_u;
Expand All @@ -308,8 +308,9 @@ _king_curl_bytes(c3_c* url_c, c3_w* len_w, c3_y** hun_y, c3_t veb_t)

u3K.ssl_curl_f(cul_u);
curl_easy_setopt(cul_u, CURLOPT_URL, url_c);
curl_easy_setopt(cul_u, CURLOPT_WRITEFUNCTION, _king_curl_alloc);
curl_easy_setopt(cul_u, CURLOPT_WRITEFUNCTION, king_curl_alloc);
curl_easy_setopt(cul_u, CURLOPT_WRITEDATA, (void*)&buf_u);
curl_easy_setopt(cul_u, CURLOPT_SERVER_RESPONSE_TIMEOUT, 30);

while ( 5 > try_y ) {
sleep(try_y++);
Expand All @@ -328,6 +329,9 @@ _king_curl_bytes(c3_c* url_c, c3_w* len_w, c3_y** hun_y, c3_t veb_t)
u3l_log("curl: error fetching %s: HTTP %ld", url_c, cod_i);
}
ret_i = -2;
if ( 400 <= cod_i && cod_i < 500 ) {
break;
}
}
else {
*len_w = buf_u.len;
Expand All @@ -351,7 +355,7 @@ _king_get_atom(c3_c* url_c)
c3_y* hun_y;
u3_noun pro;

if ( _king_curl_bytes(url_c, &len_w, &hun_y, 1) ) {
if ( king_curl_bytes(url_c, &len_w, &hun_y, 1) ) {
u3_king_bail();
exit(1);
}
Expand Down Expand Up @@ -431,7 +435,7 @@ u3_king_next(c3_c* pac_c, c3_c** out_c)
// skip printfs on failed requests (/next is usually not present)
//REVIEW new retry logic means this case will take longer. make retries optional?
//
if ( _king_curl_bytes(url_c, &len_w, &hun_y, 0) ) {
if ( king_curl_bytes(url_c, &len_w, &hun_y, 0) ) {
c3_free(url_c);

ret_i = asprintf(&url_c, "%s/%s/last", ver_hos_c, pac_c);
Expand All @@ -440,7 +444,7 @@ u3_king_next(c3_c* pac_c, c3_c** out_c)
// enable printfs on failed requests (/last must be present)
// XX support channel redirections
//
if ( _king_curl_bytes(url_c, &len_w, &hun_y, 1) )
if ( king_curl_bytes(url_c, &len_w, &hun_y, 1) )
{
c3_free(url_c);
return -2;
Expand Down
200 changes: 195 additions & 5 deletions pkg/vere/pier.c
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
#include "pace.h"
#include "vere.h"
#include "version.h"
#include "curl/curl.h"

#define PIER_READ_BATCH 1000ULL
#define PIER_PLAY_BATCH 500ULL
Expand Down Expand Up @@ -529,6 +530,175 @@ _pier_on_scry_done(void* ptr_v, u3_noun nun)
u3z(nun);
}

static c3_c*
_resolve_czar(u3_work* wok_u, c3_c* who_c)
{
u3_noun czar = u3dc("scot", 'p', wok_u->pir_u->who_d[0] & ((1 << 8) - 1));
c3_c* czar_c = u3r_string(czar);

c3_c url[256];
c3_w len_w;
c3_y* hun_y;

sprintf(url, "https://%s.urbit.org/~/sponsor/%s", czar_c+1, who_c);

c3_i ret_i = king_curl_bytes(url, &len_w, &hun_y, 1);
if (!ret_i) {
c3_free(czar_c);
czar_c = (c3_c*)hun_y;
}

u3z(czar);
return czar_c;
}

static c3_o
_czar_boot_data(c3_c* czar_c,
c3_c* who_c,
c3_w* bone_w,
c3_w* czar_glx_w,
c3_w* czar_ryf_w,
c3_w* czar_lyf_w,
c3_w* czar_bon_w,
c3_w* czar_ack_w)
{
c3_c url[256];
c3_w len_w;
c3_y* hun_y = 0;

if ( bone_w != NULL ) {
sprintf(url, "https://%s.urbit.org/~/boot/%s/%d",
czar_c+1, who_c, *bone_w + 1);
} else {
sprintf(url, "https://%s.urbit.org/~/boot/%s", czar_c+1, who_c);
}

c3_o ret_o = c3n;
c3_i ret_i = king_curl_bytes(url, &len_w, &hun_y, 1);
if ( !ret_i ) {
u3_noun jamd = u3i_bytes(len_w, hun_y);
u3_noun cued = u3qe_cue(jamd);

u3_noun czar_glx, czar_ryf, czar_lyf, czar_bon, czar_ack;

if ( (c3y == u3r_hext(cued, 0, &czar_glx, &czar_ryf,
&czar_lyf, &czar_bon, &czar_ack)) &&
(c3y == u3r_safe_word(czar_glx, czar_glx_w)) &&
(c3y == u3r_safe_word(czar_ryf, czar_ryf_w)) &&
(c3y == u3r_safe_word(czar_lyf, czar_lyf_w)) &&
(c3y == u3du(czar_bon)) &&
(c3y == u3r_safe_word(u3t(czar_bon), czar_bon_w)) &&
(c3y == u3du(czar_ack)) &&
(c3y == u3r_safe_word(u3t(czar_ack), czar_ack_w)) ) {
ret_o = c3y;
}

u3z(jamd);
u3z(cued);
c3_free(hun_y);
}

return ret_o;
}

static void
_boot_scry_cb(void* vod_p, u3_noun nun)
{
u3_work* wok_u = (u3_work*)vod_p;

u3_atom who = u3dc("scot", c3__p, u3i_chubs(2, wok_u->pir_u->who_d));
c3_c* who_c = u3r_string(who);

u3_noun rem, glx, ryf, bon, cur, nex;
c3_w glx_w, ryf_w, bon_w, cur_w, nex_w;

c3_w czar_glx_w, czar_ryf_w, czar_lyf_w, czar_bon_w, czar_ack_w = 0xFFFFFFFF;

if ( (c3y == u3r_qual(nun, 0, 0, 0, &rem)) &&
(c3y == u3r_hext(rem, &glx, &ryf, 0, &bon, &cur, &nex)) ) {
/*
* Boot scry succeeded. Proceed to cross reference networking state against
* sponsoring galaxy.
*/
glx_w = u3r_word(0, glx); ryf_w = u3r_word(0, ryf);
bon_w = u3r_word(0, bon); cur_w = u3r_word(0, cur);
nex_w = u3r_word(0, nex);

u3_atom czar = u3dc("scot", c3__p, glx_w);
c3_c* czar_c = u3r_string(czar);

if ( c3n == _czar_boot_data(czar_c, who_c, &bon_w,
&czar_glx_w, &czar_ryf_w,
&czar_lyf_w, &czar_bon_w,
&czar_ack_w) ) {
u3l_log("boot: peer-state unvailable on czar, cannot protect from double boot");
_pier_work(wok_u);
} else {
if ( czar_ryf_w == ryf_w ) {
c3_w ack_w = cur_w - 1;
if ( czar_ack_w == 0xFFFFFFFF ) {
// This codepath should never be hit
u3l_log("boot: message-sink-state unvailable on czar, cannot protect from double boot");
_pier_work(wok_u);
} else if ( (czar_ack_w == ack_w) ||
((nex_w > cur_w) && (czar_ack_w - 1 == ack_w)) ) {
_pier_work(wok_u);
} else {
u3l_log("boot: failed: czar last ack: %d, ship last ack: %d",
czar_ack_w, ack_w);
u3_king_bail();
}
} else {
// Trying to boot old ship after breach
u3l_log("boot: failed: rift in czar peer-state: %d, current rift: %d",
czar_ryf_w, ryf_w);
u3_king_bail();
}
}

u3z(czar);
c3_free(czar_c);
} else if ( c3y == u3r_trel(nun, 0, 0, &rem) && rem == 0 ) {
/*
* Data not available for boot scry. Check against sponsoring galaxy.
* If peer state exists exit(1) unless ship has breached,
* otherwise continue boot.
*/
c3_c* czar_c = _resolve_czar(wok_u, who_c);

if ( c3n == _czar_boot_data(czar_c, who_c, 0,
&czar_glx_w, &czar_ryf_w,
&czar_lyf_w, 0, 0) ) {
c3_free(czar_c);
_pier_work(wok_u);
} else {
// Peer state found under czar
c3_free(czar_c);
u3_weak kf_ryf = wok_u->pir_u->ryf;
if ( kf_ryf == u3_none ) {
u3l_log("boot: keyfile rift unavailable, cannot protect from double boot");
_pier_work(wok_u);
} else if ( kf_ryf > czar_ryf_w ) {
// Ship has breached, continue boot
_pier_work(wok_u);
} else {
u3l_log("boot: failed: rift in czar peer state: %d, keyfile rift: %d",
czar_ryf_w, kf_ryf);
u3_king_bail();
}
}
} else {
/*
* Boot scry endpoint doesn't exists. Most likely old arvo.
* Continue boot and hope for the best.
*/
u3l_log("boot: %%boot scry endpoint doesn't exist, cannot protect from double boot");
_pier_work(wok_u);
}
u3z(nun); u3z(who);
c3_free(who_c);
}

/* _pier_work_init(): begin processing new events
*/
static void
Expand Down Expand Up @@ -615,7 +785,20 @@ _pier_work_init(u3_pier* pir_u)
u3_auto_talk(wok_u->car_u);
}

_pier_work(wok_u);
c3_d pi_d = wok_u->pir_u->who_d[0];
c3_d pt_d = wok_u->pir_u->who_d[1];

if ( (pi_d < 256 && pt_d == 0) || (c3n == u3_Host.ops_u.net) ) {
// Skip double boot protection for galaxies and local mode ships
//
_pier_work(wok_u);
} else {
// Double boot protection
//
u3_noun pex = u3nc(u3i_string("boot"), u3_nul);
u3_pier_peek_last(pir_u, u3nc(u3_nul, u3_nul), c3__ax, u3_nul, pex,
pir_u->wok_u, _boot_scry_cb);
}
}

/* _pier_wyrd_good(): %wyrd version negotation succeeded.
Expand Down Expand Up @@ -1609,7 +1792,7 @@ u3_pier_slog(u3_pier* pir_u)
/* _pier_init(): create a pier, loading existing.
*/
static u3_pier*
_pier_init(c3_w wag_w, c3_c* pax_c)
_pier_init(c3_w wag_w, c3_c* pax_c, u3_weak ryf)
{
// create pier
//
Expand All @@ -1618,6 +1801,7 @@ _pier_init(c3_w wag_w, c3_c* pax_c)
pir_u->pax_c = pax_c;
pir_u->sat_e = u3_psat_init;
pir_u->liv_o = c3n;
pir_u->ryf = ryf;

// XX remove
//
Expand Down Expand Up @@ -1691,8 +1875,9 @@ u3_pier*
u3_pier_stay(c3_w wag_w, u3_noun pax)
{
u3_pier* pir_u;
u3_weak rift = u3_none;

if ( !(pir_u = _pier_init(wag_w, u3r_string(pax))) ) {
if ( !(pir_u = _pier_init(wag_w, u3r_string(pax), rift)) ) {
fprintf(stderr, "pier: stay: init fail\r\n");
u3_king_bail();
return 0;
Expand Down Expand Up @@ -1848,7 +2033,7 @@ _pier_boot_make(u3_noun who,

// include additional key configuration events if we have multiple keys
//
if ( (u3_none != fed) && (c3y == u3du(u3h(fed))) ) {
if ( (u3_none != fed) && (c3y == u3du(u3h(fed))) && (u3h(u3h(fed))) == 1) {
u3_noun wir = u3nt(c3__j, c3__seed, u3_nul);
u3_noun tag = u3i_string("rekey");
u3_noun kyz = u3t(u3t(fed));
Expand Down Expand Up @@ -2027,8 +2212,13 @@ u3_pier_boot(c3_w wag_w, // config flags
u3_noun mor) // extra boot sequence props
{
u3_pier* pir_u;
u3_weak rift = u3_none;
if (fed != u3_none && c3y == u3du(u3h(fed)) && u3h(u3h(fed)) == 2) {
rift = u3h(u3t(u3t(fed)));
u3k(rift);
}

if ( !(pir_u = _pier_init(wag_w, u3r_string(pax))) ) {
if ( !(pir_u = _pier_init(wag_w, u3r_string(pax), rift)) ) {
fprintf(stderr, "pier: boot: init fail\r\n");
u3_king_bail();
return 0;
Expand Down
Loading

0 comments on commit 85ae9b3

Please sign in to comment.