diff --git a/book/api/metrics-generated.md b/book/api/metrics-generated.md index c02bc40c972..9c74eccc5fc 100644 --- a/book/api/metrics-generated.md +++ b/book/api/metrics-generated.md @@ -900,6 +900,7 @@ | snapct_​predicted_​slot | gauge | The predicted slot from which replay starts after snapshot loading finishes. Might change if snapshot load is aborted and restarted | | snapct_​gossip_​fresh_​count | gauge | Number of fresh gossip peers seen when collecting gossip peers. | | snapct_​gossip_​total_​count | gauge | Number of total gossip peers seen when collecting gossip peers. | +| snapct_​ssl_​alloc_​errors | counter | Number of SSL allocation errors encountered. | @@ -910,6 +911,7 @@ | Metric | Type | Description | |--------|------|-------------| | snapld_​state | gauge | State of the tile. 0=IDLE, 1=PROCESSING, 2=FINISHING, 3=ERROR, 4=SHUTDOWN | +| snapld_​ssl_​alloc_​errors | counter | Number of SSL allocation errors encountered. | @@ -1200,6 +1202,5 @@ | tower_​hard_​forks_​seen | counter | Number of hard forks we've seen (block ids with multiple candidate bank hashes) | | tower_​hard_​forks_​pruned | counter | Number of hard forks (candidate bank hashes) we've pruned | | tower_​hard_​forks_​active | gauge | Currently active hard forks | -| tower_​hard_​forks_​max_​width | gauge | The max width of hard forks (block id with most candidate bank hashes) we've ever seen | diff --git a/deps.sh b/deps.sh index cc7b3a7de61..c69b49c4f4e 100755 --- a/deps.sh +++ b/deps.sh @@ -505,10 +505,10 @@ install_openssl () { echo "[+] Configuring OpenSSL" ./config \ - -static \ -fPIC \ --prefix="$PREFIX" \ --libdir=lib \ + threads \ no-engine \ no-static-engine \ no-weak-ssl-ciphers \ diff --git a/src/app/firedancer/config/testnet.toml b/src/app/firedancer/config/testnet.toml index 829d3b0a232..8cfff59748a 100644 --- a/src/app/firedancer/config/testnet.toml +++ b/src/app/firedancer/config/testnet.toml @@ -10,7 +10,7 @@ max_account_records = 200_000_000 [snapshots] [snapshots.sources] - servers = [ "http://solana-testnet-rpc.jumpisolated.com:8899" ] + servers = [ "http://solana-testnet-rpc.jumpisolated.com:8899", "https://solana-testnet-tls.jumpisolated.com" ] [snapshots.sources.gossip] allow_any = false allow_list = [] diff --git a/src/app/firedancer/topology.c b/src/app/firedancer/topology.c index 374cf67acdd..17b949eda21 100644 --- a/src/app/firedancer/topology.c +++ b/src/app/firedancer/topology.c @@ -185,6 +185,9 @@ setup_topo_vinyl_cache( fd_topo_t * topo, return line_obj; } +/* Resolves a hostname to a single ip address. If multiple ip address + records are returned by getaddrinfo, only the first IPV4 address is + returned via ip_addr. */ static int resolve_address( char const * address, uint * ip_addr ) { @@ -209,43 +212,98 @@ resolve_address( char const * address, return resolved; } +/* Resolves a hostname to multiple ip addresses, specified by + ip_addr_cnt. ip_addrs points to an array of fd_ip4_port_t objects. + hints points to an optionally NULL addrinfo hints object. If hints + is NULL, a default hints settings containing the IPV4 address family + hint will be used. */ static int -resolve_peer( char const * peer, - fd_ip4_port_t * ip4_port ) { +resolve_addresses( char const * address, + struct addrinfo const * hints, + fd_ip4_port_t * ip_addrs, + ulong ip_addr_cnt ) { + struct addrinfo default_hints = { .ai_family = AF_INET }; + if( FD_UNLIKELY( !hints ) ) { + hints = &default_hints; + } + + struct addrinfo * res; + int err = getaddrinfo( address, NULL, hints, &res ); + if( FD_UNLIKELY( err ) ) { + FD_LOG_WARNING(( "cannot resolve address \"%s\": %i-%s", address, err, gai_strerror( err ) )); + return 0; + } + + int resolved = 0; + for( struct addrinfo * cur=res; cur; cur=cur->ai_next ) { + if( FD_UNLIKELY( (ulong)resolved>=ip_addr_cnt ) ) break; + if( FD_UNLIKELY( cur->ai_addr->sa_family!=AF_INET ) ) continue; + struct sockaddr_in const * addr = (struct sockaddr_in const *)cur->ai_addr; + ip_addrs[ resolved ].addr = addr->sin_addr.s_addr; + resolved++; + } + + freeaddrinfo( res ); + return resolved; +} + +static int +resolve_peer( char const * peer, + struct addrinfo const * addr_resolve_hints, + char const * config_str, + char hostname[ static 256UL ], + fd_ip4_port_t * ip4_port, + ulong ip4_port_cnt, + int * is_https ) { /* Split host:port */ + int https = 0; char const * host_port = peer; if( FD_LIKELY( strncmp( peer, "http://", 7UL )==0 ) ) { + if( FD_LIKELY( is_https ) ) *is_https = 0; host_port += 7UL; } else if( FD_LIKELY( strncmp( peer, "https://", 8UL )==0 ) ) { + if( FD_LIKELY( is_https ) ) *is_https = 1; host_port += 8UL; + https = 1; } - char const * colon = strrchr( host_port, ':' ); - if( FD_UNLIKELY( !colon ) ) { - FD_LOG_ERR(( "invalid [gossip.entrypoints] entry \"%s\": no port number", host_port )); + char const * colon = strrchr( host_port, ':' ); + char const * host_end = colon; + if( FD_LIKELY( FD_UNLIKELY( !colon && !https ) ) ) { + FD_LOG_ERR(( "invalid [%s] entry \"%s\": no port number", config_str, host_port )); + host_end = colon; + } else if( FD_LIKELY( !colon && https ) ) { + host_end = host_port + strlen( host_port ); } - char fqdn[ 255 ]; - ulong fqdn_len = (ulong)( colon-host_port ); - if( FD_UNLIKELY( fqdn_len>254 ) ) { - FD_LOG_ERR(( "invalid [gossip.entrypoints] entry \"%s\": hostname too long", host_port )); + ulong fqdn_len = (ulong)( host_end-host_port ); + if( FD_UNLIKELY( fqdn_len>255 ) ) { + FD_LOG_ERR(( "invalid [%s] entry \"%s\": hostname too long", config_str, host_port )); } - fd_memcpy( fqdn, host_port, fqdn_len ); - fqdn[ fqdn_len ] = '\0'; + fd_memcpy( hostname, host_port, fqdn_len ); + hostname[ fqdn_len ] = '\0'; + + /* Resolve hostname */ + int resolved = resolve_addresses( hostname, addr_resolve_hints, ip4_port, ip4_port_cnt ); /* Parse port number */ - char const * port_str = colon+1; - char const * endptr = NULL; - ulong port = strtoul( port_str, (char **)&endptr, 10 ); - if( FD_UNLIKELY( !endptr || !port || port>USHORT_MAX || *endptr!='\0' ) ) { - FD_LOG_ERR(( "invalid [gossip.entrypoints] entry \"%s\": invalid port number", host_port )); + if( FD_LIKELY( colon ) ) { + char const * port_str = host_end+1; + char const * endptr = NULL; + ulong port = strtoul( port_str, (char **)&endptr, 10 ); + if( FD_UNLIKELY( endptr==port_str || !port || port>USHORT_MAX || *endptr!='\0' ) ) { + FD_LOG_ERR(( "invalid [%s] entry \"%s\": invalid port number", config_str, host_port )); + } + for( ulong i=0UL; i<(ulong)resolved; i++ ) ip4_port[ i ].port = fd_ushort_bswap( (ushort)port ); + } else if( FD_LIKELY( !colon && https ) ) { + /* use default https port */ + for( ulong i=0UL; i<(ulong)resolved; i++ ) ip4_port[ i ].port = fd_ushort_bswap( 443U ); + } else { + FD_LOG_ERR(( "invalid [%s] entry \"%s\": no port number", config_str, host_port )); } - ip4_port->port = (ushort)fd_ushort_bswap( (ushort)port ); - /* Resolve hostname */ - int resolved = resolve_address( fqdn, &ip4_port->addr ); return resolved; } @@ -253,7 +311,8 @@ static void resolve_gossip_entrypoints( config_t * config ) { ulong entrypoint_cnt = config->gossip.entrypoints_cnt; for( ulong i=0UL; igossip.entrypoints[ i ], &config->gossip.resolved_entrypoints[ i ] ) ) ) { + char hostname[ 256UL ]; + if( FD_UNLIKELY( 0==resolve_peer( config->gossip.entrypoints[ i ], NULL, "gossip.entrypoints", hostname, &config->gossip.resolved_entrypoints[ i ], 1, NULL ) ) ) { FD_LOG_ERR(( "failed to resolve address of [gossip.entrypoints] entry \"%s\"", config->gossip.entrypoints[ i ] )); } } @@ -1080,12 +1139,32 @@ fd_topo_configure_tile( fd_topo_tile_t * tile, FD_LOG_ERR(( "[snapshots.sources.gossip.block_list[%lu] invalid (%s)", i, config->firedancer.snapshots.sources.gossip.block_list[ i ] )); } } + + ulong resolved_peers_cnt = 0UL; for( ulong i=0UL; isnapct.sources.servers_cnt; i++ ) { - if( FD_UNLIKELY( !resolve_peer( config->firedancer.snapshots.sources.servers[ i ], &tile->snapct.sources.servers[ i ] ) ) ) { + fd_ip4_port_t resolved_addrs[ FD_TOPO_MAX_RESOLVED_ADDRS ]; + struct addrinfo hints = { .ai_family = AF_INET, .ai_socktype = SOCK_STREAM }; + int num_resolved = resolve_peer( config->firedancer.snapshots.sources.servers[ i ], + &hints, + "snapshots.sources.servers", + tile->snapct.sources.servers[ resolved_peers_cnt ].hostname, + resolved_addrs, + FD_TOPO_MAX_RESOLVED_ADDRS, + &tile->snapct.sources.servers[ resolved_peers_cnt ].is_https ); + if( FD_UNLIKELY( 0==num_resolved ) ) { FD_LOG_ERR(( "[snapshots.sources.servers[%lu] invalid (%s)", i, config->firedancer.snapshots.sources.servers[ i ] )); + } else { + for( ulong i=0UL; i<(ulong)num_resolved; i++ ) tile->snapct.sources.servers[ resolved_peers_cnt+i ].addr = resolved_addrs[ i ]; + for( ulong i=1UL; i<(ulong)num_resolved; i++ ) { + tile->snapct.sources.servers[ resolved_peers_cnt+i ].is_https = tile->snapct.sources.servers[ resolved_peers_cnt ].is_https; + fd_memcpy( tile->snapct.sources.servers[ resolved_peers_cnt+i ].hostname, + tile->snapct.sources.servers[ resolved_peers_cnt ].hostname, + sizeof(tile->snapct.sources.servers[ resolved_peers_cnt ].hostname) ); + } + resolved_peers_cnt += (ulong)num_resolved; } } - + tile->snapct.sources.servers_cnt = resolved_peers_cnt; } else if( FD_UNLIKELY( !strcmp( tile->name, "snapld" ) ) ) { fd_memcpy( tile->snapld.snapshots_path, config->paths.snapshots, PATH_MAX ); diff --git a/src/disco/bundle/fd_bundle_tile.c b/src/disco/bundle/fd_bundle_tile.c index c669f47e8bd..88d99401714 100644 --- a/src/disco/bundle/fd_bundle_tile.c +++ b/src/disco/bundle/fd_bundle_tile.c @@ -5,6 +5,7 @@ #include "../keyguard/fd_keyload.h" #include "../plugin/fd_plugin.h" #include "../../waltz/http/fd_url.h" +#include "../../waltz/openssl/fd_openssl_tile.h" #include #include /* opendir */ @@ -55,6 +56,9 @@ metrics_write( fd_bundle_tile_t * ctx ) { FD_MCNT_SET( BUNDLE, ERRORS_PROTOBUF, ctx->metrics.decode_fail_cnt ); FD_MCNT_SET( BUNDLE, ERRORS_TRANSPORT, ctx->metrics.transport_fail_cnt ); FD_MCNT_SET( BUNDLE, ERRORS_NO_FEE_INFO, ctx->metrics.missing_builder_info_fail_cnt ); +#if FD_HAS_OPENSSL + FD_MCNT_SET( BUNDLE, ERRORS_SSL_ALLOC, fd_ossl_alloc_errors ); +#endif FD_MGAUGE_SET( BUNDLE, RTT_SAMPLE, (ulong)ctx->rtt->latest_rtt ); FD_MGAUGE_SET( BUNDLE, RTT_SMOOTHED, (ulong)ctx->rtt->smoothed_rtt ); @@ -247,65 +251,6 @@ fd_bundle_tile_parse_endpoint( fd_bundle_tile_t * ctx, #if FD_HAS_OPENSSL -/* OpenSSL allows us to specify custom memory allocation functions, - which we want to point to an fd_alloc_t, but it does not let us use a - context object. Instead we stash it in this thread local, which is - OK because the parent workspace exists for the duration of the SSL - context, and the process only has one thread. - - Currently fd_alloc doesn't support realloc, so it's implemented on - top of malloc and free, and then also it doesn't support getting the - size of an allocation from the pointer, which we need for realloc, so - we pad each alloc by 8 bytes and stuff the size into the first 8 - bytes. */ -static FD_TL fd_alloc_t * fd_quic_ssl_mem_function_ctx = NULL; - -static void * -crypto_malloc( ulong num, - char const * file, - int line ) { - (void)file; (void)line; - void * result = fd_alloc_malloc( fd_quic_ssl_mem_function_ctx, 16UL, num + 8UL ); - if( FD_UNLIKELY( !result ) ) { - FD_MCNT_INC( BUNDLE, ERRORS_SSL_ALLOC, 1UL ); - return NULL; - } - *(ulong *)result = num; - return (uchar *)result + 8UL; -} - -static void -crypto_free( void * addr, - char const * file, - int line ) { - (void)file; - (void)line; - - if( FD_UNLIKELY( !addr ) ) return; - fd_alloc_free( fd_quic_ssl_mem_function_ctx, (uchar *)addr - 8UL ); -} - -static void * -crypto_realloc( void * addr, - ulong num, - char const * file, - int line ) { - if( FD_UNLIKELY( !addr ) ) return crypto_malloc( num, file, line ); - if( FD_UNLIKELY( !num ) ) { - crypto_free( addr, file, line ); - return NULL; - } - - void * new = fd_alloc_malloc( fd_quic_ssl_mem_function_ctx, 16UL, num + 8UL ); - if( FD_UNLIKELY( !new ) ) return NULL; - - ulong old_num = *(ulong *)( (uchar *)addr - 8UL ); - fd_memcpy( (uchar*)new + 8, (uchar*)addr, fd_ulong_min( old_num, num ) ); - fd_alloc_free( fd_quic_ssl_mem_function_ctx, (uchar *)addr - 8UL ); - *(ulong *)new = num; - return (uchar*)new + 8UL; -} - static void fd_ossl_keylog_callback( SSL const * ssl, char const * line ) { @@ -321,58 +266,6 @@ fd_ossl_keylog_callback( SSL const * ssl, } } -static void -fd_bundle_tile_load_certs( SSL_CTX * ssl_ctx ) { - X509_STORE * ca_certs = X509_STORE_new(); - if( FD_UNLIKELY( !ca_certs ) ) { - FD_LOG_ERR(( "X509_STORE_new failed" )); - } - - static char const default_dir[] = "/etc/ssl/certs/"; - DIR * dir = opendir( default_dir ); - if( FD_UNLIKELY( !dir ) ) { - FD_LOG_ERR(( "opendir(%s) failed (%i-%s)", default_dir, errno, fd_io_strerror( errno ) )); - } - - struct dirent * entry; - errno = 0; // clear old value since entry can be NULL when reaching end of directory. - while( (entry = readdir( dir )) ) { - if( !strcmp( entry->d_name, "." ) || !strcmp( entry->d_name, ".." ) ) continue; - - char cert_path[ PATH_MAX ]; - char * p = fd_cstr_init( cert_path ); - p = fd_cstr_append_text( p, default_dir, sizeof(default_dir)-1 ); - p = fd_cstr_append_cstr_safe( p, entry->d_name, (ulong)(cert_path+sizeof(cert_path)-1) - (ulong)p ); - fd_cstr_fini( p ); - - if( !X509_STORE_load_locations( ca_certs, cert_path, NULL ) ) { - /* Not all files in /etc/ssl/certs are valid certs, so ignore errors */ - continue; - } - errno = 0; - } - - if( FD_UNLIKELY( errno && errno!=ENOENT ) ) { - FD_LOG_ERR(( "readdir(%s) failed (%i-%s)", default_dir, errno, fd_io_strerror( errno ) )); - } - - STACK_OF(X509) * cert_list = X509_STORE_get1_all_certs( ca_certs ); - FD_LOG_INFO(( "Loaded %d CA certs from %s into OpenSSL", sk_X509_num( cert_list ), default_dir )); - if( fd_log_level_logfile()==0 ) { - for( int i=0; issl_alloc = alloc; - fd_quic_ssl_mem_function_ctx = alloc; - - if( FD_UNLIKELY( !CRYPTO_set_mem_functions( crypto_malloc, crypto_realloc, crypto_free ) ) ) { - FD_LOG_ERR(( "CRYPTO_set_mem_functions failed" )); - } - - OPENSSL_init_ssl( - OPENSSL_INIT_LOAD_SSL_STRINGS | - OPENSSL_INIT_LOAD_CRYPTO_STRINGS | - OPENSSL_INIT_NO_LOAD_CONFIG, - NULL - ); + ctx->ssl_alloc = alloc; + fd_ossl_tile_init( alloc ); SSL_CTX * ssl_ctx = SSL_CTX_new( TLS_client_method() ); if( FD_UNLIKELY( !ssl_ctx ) ) { @@ -417,8 +299,7 @@ fd_bundle_tile_init_openssl( fd_bundle_tile_t * ctx, } if( tls_cert_verify ) { - fd_bundle_tile_load_certs( ssl_ctx ); - SSL_CTX_set_verify( ssl_ctx, SSL_VERIFY_PEER, NULL ); + fd_ossl_load_certs( ssl_ctx ); } if( FD_LIKELY( ctx->keylog_fd >= 0 ) ) { diff --git a/src/disco/metrics/generated/fd_metrics_snapct.c b/src/disco/metrics/generated/fd_metrics_snapct.c index ba035bbae8d..25d8beddf28 100644 --- a/src/disco/metrics/generated/fd_metrics_snapct.c +++ b/src/disco/metrics/generated/fd_metrics_snapct.c @@ -16,4 +16,5 @@ const fd_metrics_meta_t FD_METRICS_SNAPCT[FD_METRICS_SNAPCT_TOTAL] = { DECLARE_METRIC( SNAPCT_PREDICTED_SLOT, GAUGE ), DECLARE_METRIC( SNAPCT_GOSSIP_FRESH_COUNT, GAUGE ), DECLARE_METRIC( SNAPCT_GOSSIP_TOTAL_COUNT, GAUGE ), + DECLARE_METRIC( SNAPCT_SSL_ALLOC_ERRORS, COUNTER ), }; diff --git a/src/disco/metrics/generated/fd_metrics_snapct.h b/src/disco/metrics/generated/fd_metrics_snapct.h index a93a8f9a5cb..7665799d915 100644 --- a/src/disco/metrics/generated/fd_metrics_snapct.h +++ b/src/disco/metrics/generated/fd_metrics_snapct.h @@ -90,7 +90,13 @@ #define FD_METRICS_GAUGE_SNAPCT_GOSSIP_TOTAL_COUNT_DESC "Number of total gossip peers seen when collecting gossip peers." #define FD_METRICS_GAUGE_SNAPCT_GOSSIP_TOTAL_COUNT_CVT (FD_METRICS_CONVERTER_NONE) -#define FD_METRICS_SNAPCT_TOTAL (14UL) +#define FD_METRICS_COUNTER_SNAPCT_SSL_ALLOC_ERRORS_OFF (30UL) +#define FD_METRICS_COUNTER_SNAPCT_SSL_ALLOC_ERRORS_NAME "snapct_ssl_alloc_errors" +#define FD_METRICS_COUNTER_SNAPCT_SSL_ALLOC_ERRORS_TYPE (FD_METRICS_TYPE_COUNTER) +#define FD_METRICS_COUNTER_SNAPCT_SSL_ALLOC_ERRORS_DESC "Number of SSL allocation errors encountered." +#define FD_METRICS_COUNTER_SNAPCT_SSL_ALLOC_ERRORS_CVT (FD_METRICS_CONVERTER_NONE) + +#define FD_METRICS_SNAPCT_TOTAL (15UL) extern const fd_metrics_meta_t FD_METRICS_SNAPCT[FD_METRICS_SNAPCT_TOTAL]; #endif /* HEADER_fd_src_disco_metrics_generated_fd_metrics_snapct_h */ diff --git a/src/disco/metrics/generated/fd_metrics_snapld.c b/src/disco/metrics/generated/fd_metrics_snapld.c index 78f8a09f587..53c048b8014 100644 --- a/src/disco/metrics/generated/fd_metrics_snapld.c +++ b/src/disco/metrics/generated/fd_metrics_snapld.c @@ -3,4 +3,5 @@ const fd_metrics_meta_t FD_METRICS_SNAPLD[FD_METRICS_SNAPLD_TOTAL] = { DECLARE_METRIC( SNAPLD_STATE, GAUGE ), + DECLARE_METRIC( SNAPLD_SSL_ALLOC_ERRORS, COUNTER ), }; diff --git a/src/disco/metrics/generated/fd_metrics_snapld.h b/src/disco/metrics/generated/fd_metrics_snapld.h index 8add394913c..ebab731af55 100644 --- a/src/disco/metrics/generated/fd_metrics_snapld.h +++ b/src/disco/metrics/generated/fd_metrics_snapld.h @@ -12,7 +12,13 @@ #define FD_METRICS_GAUGE_SNAPLD_STATE_DESC "State of the tile. 0=IDLE, 1=PROCESSING, 2=FINISHING, 3=ERROR, 4=SHUTDOWN" #define FD_METRICS_GAUGE_SNAPLD_STATE_CVT (FD_METRICS_CONVERTER_NONE) -#define FD_METRICS_SNAPLD_TOTAL (1UL) +#define FD_METRICS_COUNTER_SNAPLD_SSL_ALLOC_ERRORS_OFF (17UL) +#define FD_METRICS_COUNTER_SNAPLD_SSL_ALLOC_ERRORS_NAME "snapld_ssl_alloc_errors" +#define FD_METRICS_COUNTER_SNAPLD_SSL_ALLOC_ERRORS_TYPE (FD_METRICS_TYPE_COUNTER) +#define FD_METRICS_COUNTER_SNAPLD_SSL_ALLOC_ERRORS_DESC "Number of SSL allocation errors encountered." +#define FD_METRICS_COUNTER_SNAPLD_SSL_ALLOC_ERRORS_CVT (FD_METRICS_CONVERTER_NONE) + +#define FD_METRICS_SNAPLD_TOTAL (2UL) extern const fd_metrics_meta_t FD_METRICS_SNAPLD[FD_METRICS_SNAPLD_TOTAL]; #endif /* HEADER_fd_src_disco_metrics_generated_fd_metrics_snapld_h */ diff --git a/src/disco/metrics/generated/fd_metrics_tower.c b/src/disco/metrics/generated/fd_metrics_tower.c index 89fd394d404..13ce7ab6fb6 100644 --- a/src/disco/metrics/generated/fd_metrics_tower.c +++ b/src/disco/metrics/generated/fd_metrics_tower.c @@ -16,5 +16,4 @@ const fd_metrics_meta_t FD_METRICS_TOWER[FD_METRICS_TOWER_TOTAL] = { DECLARE_METRIC( TOWER_HARD_FORKS_SEEN, COUNTER ), DECLARE_METRIC( TOWER_HARD_FORKS_PRUNED, COUNTER ), DECLARE_METRIC( TOWER_HARD_FORKS_ACTIVE, GAUGE ), - DECLARE_METRIC( TOWER_HARD_FORKS_MAX_WIDTH, GAUGE ), }; diff --git a/src/disco/metrics/generated/fd_metrics_tower.h b/src/disco/metrics/generated/fd_metrics_tower.h index 332b04fa022..8de9e4272f7 100644 --- a/src/disco/metrics/generated/fd_metrics_tower.h +++ b/src/disco/metrics/generated/fd_metrics_tower.h @@ -90,13 +90,7 @@ #define FD_METRICS_GAUGE_TOWER_HARD_FORKS_ACTIVE_DESC "Currently active hard forks" #define FD_METRICS_GAUGE_TOWER_HARD_FORKS_ACTIVE_CVT (FD_METRICS_CONVERTER_NONE) -#define FD_METRICS_GAUGE_TOWER_HARD_FORKS_MAX_WIDTH_OFF (30UL) -#define FD_METRICS_GAUGE_TOWER_HARD_FORKS_MAX_WIDTH_NAME "tower_hard_forks_max_width" -#define FD_METRICS_GAUGE_TOWER_HARD_FORKS_MAX_WIDTH_TYPE (FD_METRICS_TYPE_GAUGE) -#define FD_METRICS_GAUGE_TOWER_HARD_FORKS_MAX_WIDTH_DESC "The max width of hard forks (block id with most candidate bank hashes) we've ever seen" -#define FD_METRICS_GAUGE_TOWER_HARD_FORKS_MAX_WIDTH_CVT (FD_METRICS_CONVERTER_NONE) - -#define FD_METRICS_TOWER_TOTAL (15UL) +#define FD_METRICS_TOWER_TOTAL (14UL) extern const fd_metrics_meta_t FD_METRICS_TOWER[FD_METRICS_TOWER_TOTAL]; #endif /* HEADER_fd_src_disco_metrics_generated_fd_metrics_tower_h */ diff --git a/src/disco/metrics/metrics.xml b/src/disco/metrics/metrics.xml index 7eac549fa2e..a2e6f5c2dd8 100644 --- a/src/disco/metrics/metrics.xml +++ b/src/disco/metrics/metrics.xml @@ -1039,10 +1039,12 @@ metric introduced. + + diff --git a/src/disco/topo/fd_topo.h b/src/disco/topo/fd_topo.h index 4d1f6bce059..4c1f0e9a5aa 100644 --- a/src/disco/topo/fd_topo.h +++ b/src/disco/topo/fd_topo.h @@ -516,8 +516,10 @@ struct fd_topo_tile { int slices_fd; } shredcap; -#define FD_TOPO_SNAPSHOTS_GOSSIP_LIST_MAX (32UL) -#define FD_TOPO_SNAPSHOTS_SERVERS_MAX (16UL) +#define FD_TOPO_SNAPSHOTS_GOSSIP_LIST_MAX (32UL) +#define FD_TOPO_SNAPSHOTS_SERVERS_MAX (16UL) +#define FD_TOPO_MAX_RESOLVED_ADDRS ( 4UL) +#define FD_TOPO_SNAPSHOTS_SERVERS_MAX_RESOLVED (FD_TOPO_MAX_RESOLVED_ADDRS*FD_TOPO_SNAPSHOTS_SERVERS_MAX) struct fd_topo_tile_snapct { char snapshots_path[ PATH_MAX ]; @@ -535,7 +537,11 @@ struct fd_topo_tile { } gossip; ulong servers_cnt; - fd_ip4_port_t servers[ FD_TOPO_SNAPSHOTS_SERVERS_MAX ]; + struct { + fd_ip4_port_t addr; + char hostname[ 256UL ]; + int is_https; + } servers[ FD_TOPO_SNAPSHOTS_SERVERS_MAX_RESOLVED ]; } sources; int incremental_snapshots; diff --git a/src/discof/restore/fd_snapct_tile.c b/src/discof/restore/fd_snapct_tile.c index d1605a5f415..cfe0406859d 100644 --- a/src/discof/restore/fd_snapct_tile.c +++ b/src/discof/restore/fd_snapct_tile.c @@ -8,6 +8,7 @@ #include "../../disco/topo/fd_topo.h" #include "../../disco/metrics/fd_metrics.h" #include "../../flamenco/gossip/fd_gossip_types.h" +#include "../../waltz/openssl/fd_openssl_tile.h" #include #include @@ -32,7 +33,7 @@ /* FIXME: Handle cases where the slot number we start downloading differs from advertised */ #define GOSSIP_PEERS_MAX (FD_CONTACT_INFO_TABLE_SIZE) -#define SERVER_PEERS_MAX (FD_TOPO_SNAPSHOTS_SERVERS_MAX) +#define SERVER_PEERS_MAX (FD_TOPO_SNAPSHOTS_SERVERS_MAX_RESOLVED) #define TOTAL_PEERS_MAX (GOSSIP_PEERS_MAX + SERVER_PEERS_MAX) #define IN_KIND_ACK (0) @@ -165,6 +166,13 @@ download_enabled( fd_topo_tile_t const * tile ) { return gossip_enabled( tile ) || tile->snapct.sources.servers_cnt>0UL; } +FD_FN_CONST static inline ulong +loose_footprint( fd_topo_tile_t const * tile ) { + (void)tile; + /* Leftover space for OpenSSL allocations */ + return 1<<26UL; /* 64 MiB */ +} + static ulong scratch_align( void ) { return fd_ulong_max( alignof(fd_snapct_tile_t), @@ -184,6 +192,7 @@ scratch_footprint( fd_topo_tile_t const * tile FD_PARAM_UNUSED ) { l = FD_LAYOUT_APPEND( l, gossip_ci_map_align(), gossip_ci_map_footprint( gossip_ci_map_chain_cnt_est( GOSSIP_PEERS_MAX ) ) ); l = FD_LAYOUT_APPEND( l, fd_http_resolver_align(), fd_http_resolver_footprint( SERVER_PEERS_MAX ) ); l = FD_LAYOUT_APPEND( l, fd_sspeer_selector_align(), fd_sspeer_selector_footprint( TOTAL_PEERS_MAX ) ); + l = FD_LAYOUT_APPEND( l, fd_alloc_align(), fd_alloc_footprint() ); return FD_LAYOUT_FINI( l, scratch_align() ); } @@ -238,6 +247,10 @@ metrics_write( fd_snapct_tile_t * ctx ) { FD_MGAUGE_SET( SNAPCT, PREDICTED_SLOT, ctx->predicted_incremental.slot ); +#if FD_HAS_OPENSSL + FD_MCNT_SET( SNAPCT, SSL_ALLOC_ERRORS, fd_ossl_alloc_errors ); +#endif + FD_MGAUGE_SET( SNAPCT, STATE, (ulong)ctx->state ); } @@ -406,7 +419,16 @@ init_load( fd_snapct_tile_t * ctx, fd_ssctrl_init_t * out = fd_chunk_to_laddr( ctx->out_ld.mem, ctx->out_ld.chunk ); out->file = file; out->zstd = !file || (full ? ctx->local_in.full_snapshot_zstd : ctx->local_in.incremental_snapshot_zstd); - if( !file ) out->addr = ctx->addr; + if( !file ) { + out->addr = ctx->addr; + for( ulong i=0UL; iaddr.l==ctx->config.sources.servers[ i ].addr.l ) ) { + fd_cstr_ncpy( out->hostname, ctx->config.sources.servers[ i ].hostname, sizeof(out->hostname) ); + out->is_https = ctx->config.sources.servers[ i ].is_https; + break; + } + } + } fd_stem_publish( stem, ctx->out_ld.idx, full ? FD_SNAPSHOT_MSG_CTRL_INIT_FULL : FD_SNAPSHOT_MSG_CTRL_INIT_INCR, ctx->out_ld.chunk, sizeof(fd_ssctrl_init_t), 0UL, 0UL, 0UL ); ctx->out_ld.chunk = fd_dcache_compact_next( ctx->out_ld.chunk, sizeof(fd_ssctrl_init_t), ctx->out_ld.chunk0, ctx->out_ld.wmark ); ctx->flush_ack = 0; @@ -452,11 +474,18 @@ log_download( fd_snapct_tile_t * ctx, } for( ulong i=0UL; iconfig.sources.servers_cnt; i++ ) { - if( addr.l==ctx->config.sources.servers[ i ].l ) { - FD_LOG_NOTICE(( "downloading %s snapshot at slot %lu from configured server with index %lu at http://" FD_IP4_ADDR_FMT ":%hu/%s", - full ? "full" : "incremental", slot, i, - FD_IP4_ADDR_FMT_ARGS( addr.addr ), fd_ushort_bswap( addr.port ), - full ? "snapshot.tar.bz2" : "incremental-snapshot.tar.bz2" )); + if( addr.l==ctx->config.sources.servers[ i ].addr.l ) { + if( ctx->config.sources.servers[ i ].is_https ) { + FD_LOG_NOTICE(( "downloading %s snapshot at slot %lu from configured server with index %lu at https://%s:%hu/%s", + full ? "full" : "incremental", slot, i, + ctx->config.sources.servers[ i ].hostname, fd_ushort_bswap( addr.port ), + full ? "snapshot.tar.bz2" : "incremental-snapshot.tar.bz2" )); + } else { + FD_LOG_NOTICE(( "downloading %s snapshot at slot %lu from configured server with index %lu at http://" FD_IP4_ADDR_FMT ":%hu/%s", + full ? "full" : "incremental", slot, i, + FD_IP4_ADDR_FMT_ARGS( addr.addr ), fd_ushort_bswap( addr.port ), + full ? "snapshot.tar.bz2" : "incremental-snapshot.tar.bz2" )); + } return; } } @@ -1156,11 +1185,23 @@ privileged_init( fd_topo_t * topo, void * scratch = fd_topo_obj_laddr( topo, tile->tile_obj_id ); FD_SCRATCH_ALLOC_INIT( l, scratch ); - fd_snapct_tile_t * ctx = FD_SCRATCH_ALLOC_APPEND( l, alignof(fd_snapct_tile_t), sizeof(fd_snapct_tile_t) ); - void * _ssping = FD_SCRATCH_ALLOC_APPEND( l, fd_ssping_align(), fd_ssping_footprint( TOTAL_PEERS_MAX ) ); + fd_snapct_tile_t * ctx = FD_SCRATCH_ALLOC_APPEND( l, alignof(fd_snapct_tile_t), sizeof(fd_snapct_tile_t) ); + void * _ssping = FD_SCRATCH_ALLOC_APPEND( l, fd_ssping_align(), fd_ssping_footprint( TOTAL_PEERS_MAX ) ); + FD_SCRATCH_ALLOC_APPEND( l, alignof(gossip_ci_entry_t), sizeof(gossip_ci_entry_t)*GOSSIP_PEERS_MAX ); + FD_SCRATCH_ALLOC_APPEND( l, gossip_ci_map_align(), gossip_ci_map_footprint( gossip_ci_map_chain_cnt_est( GOSSIP_PEERS_MAX ) ) ); + void * _ssresolver = FD_SCRATCH_ALLOC_APPEND( l, fd_http_resolver_align(), fd_http_resolver_footprint( SERVER_PEERS_MAX ) ); + FD_SCRATCH_ALLOC_APPEND( l, fd_sspeer_selector_align(), fd_sspeer_selector_footprint( TOTAL_PEERS_MAX ) ); + +#if FD_HAS_OPENSSL + void * _alloc = FD_SCRATCH_ALLOC_APPEND( l, fd_alloc_align(), fd_alloc_footprint() ); + fd_alloc_t * alloc = fd_alloc_join( fd_alloc_new( _alloc, 1UL ), tile->kind_id ); + fd_ossl_tile_init( alloc ); +#endif ctx->ssping = NULL; - if( FD_LIKELY( download_enabled( tile ) ) ) ctx->ssping = fd_ssping_join( fd_ssping_new( _ssping, TOTAL_PEERS_MAX, 1UL, on_ping, ctx ) ); + if( FD_LIKELY( download_enabled( tile ) ) ) ctx->ssping = fd_ssping_join( fd_ssping_new( _ssping, TOTAL_PEERS_MAX, 1UL, on_ping, ctx ) ); + if( FD_LIKELY( tile->snapct.sources.servers_cnt ) ) ctx->ssresolver = fd_http_resolver_join( fd_http_resolver_new( _ssresolver, SERVER_PEERS_MAX, tile->snapct.incremental_snapshots, on_resolve, ctx ) ); + else ctx->ssresolver = NULL; /* FIXME: We will keep too many snapshots if we have local snapshots but elect not to use them due to their age. */ @@ -1272,19 +1313,20 @@ unprivileged_init( fd_topo_t * topo, FD_SCRATCH_ALLOC_APPEND( l, fd_ssping_align(), fd_ssping_footprint( TOTAL_PEERS_MAX ) ); void * _ci_table = FD_SCRATCH_ALLOC_APPEND( l, alignof(gossip_ci_entry_t), sizeof(gossip_ci_entry_t) * GOSSIP_PEERS_MAX ); void * _ci_map = FD_SCRATCH_ALLOC_APPEND( l, gossip_ci_map_align(), gossip_ci_map_footprint( gossip_ci_map_chain_cnt_est( GOSSIP_PEERS_MAX ) ) ); - void * _ssresolver = FD_SCRATCH_ALLOC_APPEND( l, fd_http_resolver_align(), fd_http_resolver_footprint( SERVER_PEERS_MAX ) ); + FD_SCRATCH_ALLOC_APPEND( l, fd_http_resolver_align(), fd_http_resolver_footprint( SERVER_PEERS_MAX ) ); void * _selector = FD_SCRATCH_ALLOC_APPEND( l, fd_sspeer_selector_align(), fd_sspeer_selector_footprint( TOTAL_PEERS_MAX ) ); fd_memcpy( &ctx->config, &tile->snapct, sizeof(ctx->config) ); ctx->gossip_enabled = gossip_enabled( tile ); ctx->download_enabled = download_enabled( tile ); - ctx->ssresolver = NULL; if( ctx->config.sources.servers_cnt ) { - ctx->ssresolver = fd_http_resolver_join( fd_http_resolver_new( _ssresolver, SERVER_PEERS_MAX, ctx->config.incremental_snapshots, on_resolve, ctx ) ); for( ulong i=0UL; isnapct.sources.servers_cnt; i++ ) { - fd_ssping_add ( ctx->ssping, tile->snapct.sources.servers[ i ] ); - fd_http_resolver_add( ctx->ssresolver, tile->snapct.sources.servers[ i ] ); + fd_ssping_add ( ctx->ssping, tile->snapct.sources.servers[ i ].addr ); + fd_http_resolver_add( ctx->ssresolver, + tile->snapct.sources.servers[ i ].addr, + tile->snapct.sources.servers[ i ].hostname, + tile->snapct.sources.servers[ i ].is_https ); } } @@ -1365,6 +1407,7 @@ fd_topo_run_tile_t fd_tile_snapct = { .populate_allowed_fds = populate_allowed_fds, .scratch_align = scratch_align, .scratch_footprint = scratch_footprint, + .loose_footprint = loose_footprint, .privileged_init = privileged_init, .unprivileged_init = unprivileged_init, .run = stem_run, diff --git a/src/discof/restore/fd_snapct_tile.seccomppolicy b/src/discof/restore/fd_snapct_tile.seccomppolicy index 88fa53a0e88..0df50f06cc3 100644 --- a/src/discof/restore/fd_snapct_tile.seccomppolicy +++ b/src/discof/restore/fd_snapct_tile.seccomppolicy @@ -8,13 +8,26 @@ uint logfile_fd, uint dir_fd, uint out_full_fd, uint out_inc_fd, uint ping_fd # are always written to the log file. # # arg 0 is the file descriptor to write to. The boot process ensures -# that descriptor 2 is always STDERR. The other files that can be -# written to are the out descriptors for the full and incremental -# snapshots. -write: (or (eq (arg 0) 2) - (eq (arg 0) logfile_fd) - (eq (arg 0) out_full_fd) - (eq (arg 0) out_inc_fd)) +# that descriptor 2 is always STDERR. Other files that can be +# written to include the out descriptors for the full and incremental +# snapshots. OpenSSL also calls write on the underlying socket +# descriptor. We will restrict this being called on the snapshot +# directory file descriptor and the ping file descriptor. +write: (not (or (eq (arg 0) dir_fd) + (eq (arg 0) ping_fd))) + +# openssl: read +# +# OpenSSL calls read to receive bytes from a socket. We will restrict +# this being called on the stderr, the log file descriptor, the snapshot +# directory, the full and incremental snapshot files, and the ping file +# descriptor. +read: (not (or (eq (arg 0) 2) + (eq (arg 0) logfile_fd) + (eq (arg 0) dir_fd) + (eq (arg 0) out_full_fd) + (eq (arg 0) out_inc_fd) + (eq (arg 0) ping_fd))) # logging: 'WARNING' and above fsync the logfile to disk immediately # @@ -113,5 +126,19 @@ setsockopt: (and (not (or (eq (arg 0) 2) renameat: (and (eq (arg 0) dir_fd) (eq (arg 2) dir_fd)) +# openssl: RAND_bytes requires getpid +# +# The OpenSSL implementation calls getpid() as an implementation +# detail, they save the PID in a global and reseed the RNG if the PID +# ever changes (the process was forked). We don't need this logic since +# our process can't fork. +getpid + +# openssl: RAND_bytes requires getrandom +# +# The OpenSSL implementation calls getrandom internally for periodically +# reseeding the RNG. +getrandom + # shutdown: exit is called on shutdown exit: (eq (arg 0) 0) diff --git a/src/discof/restore/fd_snapld_tile.c b/src/discof/restore/fd_snapld_tile.c index ed29eec37d8..d1991f67c12 100644 --- a/src/discof/restore/fd_snapld_tile.c +++ b/src/discof/restore/fd_snapld_tile.c @@ -1,10 +1,13 @@ +#define _GNU_SOURCE #include "utils/fd_ssarchive.h" #include "utils/fd_ssctrl.h" #include "utils/fd_sshttp.h" #include "../../disco/topo/fd_topo.h" #include "../../disco/metrics/fd_metrics.h" +#include "../../waltz/openssl/fd_openssl_tile.h" +#include /* memfd_create */ #include #include #include @@ -29,8 +32,14 @@ typedef struct fd_snapld_tile { int load_file; int sent_meta; + int awaiting_ack; + ulong awaiting_ack_sig; + int local_full_fd; int local_incr_fd; + int sockfd; + + int is_https; fd_sshttp_t * sshttp; @@ -58,15 +67,33 @@ scratch_footprint( fd_topo_tile_t const * tile FD_PARAM_UNUSED ) { ulong l = FD_LAYOUT_INIT; l = FD_LAYOUT_APPEND( l, alignof(fd_snapld_tile_t), sizeof(fd_snapld_tile_t) ); l = FD_LAYOUT_APPEND( l, fd_sshttp_align(), fd_sshttp_footprint() ); + l = FD_LAYOUT_APPEND( l, fd_alloc_align(), fd_alloc_footprint() ); return FD_LAYOUT_FINI( l, scratch_align() ); } +FD_FN_CONST static inline ulong +loose_footprint( fd_topo_tile_t const * tile ) { + (void)tile; + /* Leftover space for OpenSSL allocations */ + return 1UL<<26UL; /* 64 MiB */ +} + static void privileged_init( fd_topo_t * topo, fd_topo_tile_t * tile ) { void * scratch = fd_topo_obj_laddr( topo, tile->tile_obj_id ); FD_SCRATCH_ALLOC_INIT( l, scratch ); fd_snapld_tile_t * ctx = FD_SCRATCH_ALLOC_APPEND( l, alignof(fd_snapld_tile_t), sizeof(fd_snapld_tile_t) ); + void * _sshttp = FD_SCRATCH_ALLOC_APPEND( l, fd_sshttp_align(), fd_sshttp_footprint() ); + +#if FD_HAS_OPENSSL + void * _alloc = FD_SCRATCH_ALLOC_APPEND( l, fd_alloc_align(), fd_alloc_footprint() ); + fd_alloc_t * alloc = fd_alloc_join( fd_alloc_new( _alloc, 1UL ), tile->kind_id ); + fd_ossl_tile_init( alloc ); +#endif + + ctx->sshttp = fd_sshttp_join( fd_sshttp_new( _sshttp ) ); + FD_TEST( ctx->sshttp ); /* FIXME: Allow incremental_snapshots=0 config */ ulong full_slot = ULONG_MAX; @@ -91,6 +118,12 @@ privileged_init( fd_topo_t * topo, if( FD_UNLIKELY( -1==ctx->local_incr_fd ) ) FD_LOG_ERR(( "open() failed `%s` (%i-%s)", incr_path, errno, fd_io_strerror( errno ) )); } } + + /* Create a temporary file descriptor for our socket file descriptor. + It is closed later in unprivileged init so that the sandbox sees + an existent file descriptor. */ + ctx->sockfd = memfd_create( "snapld.sockfd", 0 ); + if( FD_UNLIKELY( -1==ctx->sockfd ) ) FD_LOG_ERR(( "memfd_create() failed (%i-%s)", errno, fd_io_strerror( errno ) )); } static ulong @@ -111,6 +144,7 @@ populate_allowed_fds( fd_topo_t const * topo, fd_snapld_tile_t * ctx = FD_SCRATCH_ALLOC_APPEND( l, alignof(fd_snapld_tile_t), sizeof(fd_snapld_tile_t) ); if( FD_LIKELY( -1!=ctx->local_full_fd ) ) out_fds[ out_cnt++ ] = ctx->local_full_fd; if( FD_LIKELY( -1!=ctx->local_incr_fd ) ) out_fds[ out_cnt++ ] = ctx->local_incr_fd; + out_fds[ out_cnt++ ] = ctx->sockfd; return out_cnt; } @@ -124,7 +158,7 @@ populate_allowed_seccomp( fd_topo_t const * topo, FD_SCRATCH_ALLOC_INIT( l, scratch ); fd_snapld_tile_t * ctx = FD_SCRATCH_ALLOC_APPEND( l, alignof(fd_snapld_tile_t), sizeof(fd_snapld_tile_t) ); - populate_sock_filter_policy_fd_snapld_tile( out_cnt, out, (uint)fd_log_private_logfile_fd(), (uint)ctx->local_full_fd, (uint)ctx->local_incr_fd ); + populate_sock_filter_policy_fd_snapld_tile( out_cnt, out, (uint)fd_log_private_logfile_fd(), (uint)ctx->local_full_fd, (uint)ctx->local_incr_fd, (uint)ctx->sockfd ); return sock_filter_policy_fd_snapld_tile_instr_cnt; } @@ -134,15 +168,11 @@ unprivileged_init( fd_topo_t * topo, void * scratch = fd_topo_obj_laddr( topo, tile->tile_obj_id ); FD_SCRATCH_ALLOC_INIT( l, scratch ); fd_snapld_tile_t * ctx = FD_SCRATCH_ALLOC_APPEND( l, alignof(fd_snapld_tile_t), sizeof(fd_snapld_tile_t) ); - void * _sshttp = FD_SCRATCH_ALLOC_APPEND( l, fd_sshttp_align(), fd_sshttp_footprint() ); fd_memcpy( ctx->config.path, tile->snapld.snapshots_path, PATH_MAX ); ctx->state = FD_SNAPSHOT_STATE_IDLE; - ctx->sshttp = fd_sshttp_join( fd_sshttp_new( _sshttp ) ); - FD_TEST( ctx->sshttp ); - FD_TEST( tile->in_cnt==1UL ); fd_topo_link_t const * in_link = &topo->links[ tile->in_link_id[ 0 ] ]; FD_TEST( 0==strcmp( in_link->name, "snapct_ld" ) ); @@ -156,6 +186,15 @@ unprivileged_init( fd_topo_t * topo, ctx->out_dc.wmark = fd_dcache_compact_wmark ( ctx->out_dc.mem, out_link->dcache, out_link->mtu ); ctx->out_dc.chunk = ctx->out_dc.chunk0; ctx->out_dc.mtu = out_link->mtu; + + ctx->awaiting_ack = 0; + ctx->awaiting_ack_sig = 0; + ctx->is_https = 0; + + /* We can only close the temporary socket file descriptor after + entering the sandbox because the sandbox checks all file + descriptors are existent. */ + if( -1==close( ctx->sockfd ) ) FD_LOG_ERR((" close() failed (%i-%s)", errno, fd_io_strerror( errno ) )); } static int @@ -165,7 +204,10 @@ should_shutdown( fd_snapld_tile_t * ctx ) { static void metrics_write( fd_snapld_tile_t * ctx ) { - FD_MGAUGE_SET( SNAPLD, STATE, (ulong)(ctx->state) ); +#if FD_HAS_OPENSSL + FD_MCNT_SET( SNAPLD, SSL_ALLOC_ERRORS, fd_ossl_alloc_errors ); +#endif + FD_MGAUGE_SET( SNAPLD, STATE, (ulong)(ctx->state) ); } static void @@ -173,6 +215,13 @@ after_credit( fd_snapld_tile_t * ctx, fd_stem_context_t * stem, int * opt_poll_in FD_PARAM_UNUSED, int * charge_busy ) { + if( FD_UNLIKELY( ctx->awaiting_ack && ctx->state==FD_SNAPSHOT_STATE_FINISHING ) ) { + ctx->awaiting_ack = 0; + ctx->state = FD_SNAPSHOT_STATE_IDLE; + fd_stem_publish( stem, 0UL, ctx->awaiting_ack_sig, 0UL, 0UL, 0UL, 0UL, 0UL ); + return; + } + if( ctx->state!=FD_SNAPSHOT_STATE_PROCESSING ) { fd_log_sleep( (long)1e6 ); return; @@ -258,12 +307,13 @@ returnable_frag( fd_snapld_tile_t * ctx, ctx->load_file = msg_in->file; ctx->state = FD_SNAPSHOT_STATE_PROCESSING; ctx->sent_meta = 0; + ctx->is_https = msg_in->is_https; if( ctx->load_file ) { if( FD_UNLIKELY( 0!=lseek( ctx->load_full ? ctx->local_full_fd : ctx->local_incr_fd, 0, SEEK_SET ) ) ) FD_LOG_ERR(( "lseek(0) failed (%i-%s)", errno, fd_io_strerror( errno ) )); } else { - if( ctx->load_full ) fd_sshttp_init( ctx->sshttp, msg_in->addr, "/snapshot.tar.bz2", 17UL, fd_log_wallclock() ); - else fd_sshttp_init( ctx->sshttp, msg_in->addr, "/incremental-snapshot.tar.bz2", 29UL, fd_log_wallclock() ); + if( ctx->load_full ) fd_sshttp_init( ctx->sshttp, msg_in->addr, msg_in->hostname, msg_in->is_https, "/snapshot.tar.bz2", 17UL, fd_log_wallclock() ); + else fd_sshttp_init( ctx->sshttp, msg_in->addr, msg_in->hostname, msg_in->is_https, "/incremental-snapshot.tar.bz2", 29UL, fd_log_wallclock() ); } fd_ssctrl_init_t * msg_out = fd_chunk_to_laddr( ctx->out_dc.mem, ctx->out_dc.chunk ); fd_memcpy( msg_out, msg_in, sz ); @@ -286,9 +336,16 @@ returnable_frag( fd_snapld_tile_t * ctx, ctx->state==FD_SNAPSHOT_STATE_FINISHING || ctx->state==FD_SNAPSHOT_STATE_ERROR ); if( FD_UNLIKELY( ctx->state!=FD_SNAPSHOT_STATE_FINISHING ) ) { - ctx->state = FD_SNAPSHOT_STATE_ERROR; - fd_stem_publish( stem, 0UL, FD_SNAPSHOT_MSG_CTRL_ERROR, 0UL, 0UL, 0UL, 0UL, 0UL ); - return 0; + /* snapld should be in the finishing state when reading from a + file or downloading from http. It is only allowed to still + be in progress for shutting down an https connection. */ + FD_TEST( ctx->is_https ); + /* snapld might not be done with shutting down an https + connection. Save the sig here and send the message when + snapld is in the finishing state. */ + ctx->awaiting_ack = 1; + ctx->awaiting_ack_sig = sig; + return 0; /* return directly to avoid fowarding the message */ } ctx->state = FD_SNAPSHOT_STATE_IDLE; break; @@ -329,6 +386,7 @@ fd_topo_run_tile_t fd_tile_snapld = { .populate_allowed_fds = populate_allowed_fds, .scratch_align = scratch_align, .scratch_footprint = scratch_footprint, + .loose_footprint = loose_footprint, .privileged_init = privileged_init, .unprivileged_init = unprivileged_init, .run = stem_run, diff --git a/src/discof/restore/fd_snapld_tile.seccomppolicy b/src/discof/restore/fd_snapld_tile.seccomppolicy index 191ac6ff44a..2d140cba840 100644 --- a/src/discof/restore/fd_snapld_tile.seccomppolicy +++ b/src/discof/restore/fd_snapld_tile.seccomppolicy @@ -1,4 +1,4 @@ -uint logfile_fd, uint in_full_fd, uint in_incr_fd +uint logfile_fd, uint in_full_fd, uint in_incr_fd, uint sockfd # logging: all log messages are written to a file and/or pipe # @@ -8,9 +8,11 @@ uint logfile_fd, uint in_full_fd, uint in_incr_fd # arg 0 is the file descriptor to write to. The boot process ensures # that descriptor 2 is always STDERR. The other files that can be # written to are the out descriptors for the full and incremental -# snapshots. +# snapshots and the socket file descriptor, which OpenSSL writes to +# during the lifetime of an https connection. write: (or (eq (arg 0) 2) - (eq (arg 0) logfile_fd)) + (eq (arg 0) logfile_fd) + (eq (arg 0) sockfd)) # logging: 'WARNING' and above fsync the logfile to disk immediately # @@ -18,11 +20,13 @@ write: (or (eq (arg 0) 2) fsync: (eq (arg 0) logfile_fd) # snapshot: need to be able to read from an open file for snapshot -# loading +# loading and from the socket file descriptor. OpenSSL calls read to +# receive bytes from an https connection. # # arg 0 is the file descriptor that we want to read from read: (or (eq (arg 0) in_full_fd) - (eq (arg 0) in_incr_fd)) + (eq (arg 0) in_incr_fd) + (eq (arg 0) sockfd)) # snapshot: Uses lseek to reset position in input file lseek: (and (or (eq (arg 0) in_full_fd) @@ -87,5 +91,19 @@ ppoll: (eq (arg 1) 1) # snapshot: yield to scheduler during idle pipeline to save power clock_nanosleep +# openssl: RAND_bytes requires getpid +# +# The OpenSSL implementation calls getpid() as an implementation detail, +# they save the PID in a global and reseed the RNG if the PID ever +# changes (the process was forked). We don't need this logic since our +# process can't fork. +getpid + +# openssl: RAND_bytes requires getrandom +# +# The OpenSSL implementation calls getrandom internally for periodically +# reseeding the RNG. +getrandom + # shutdown: exit is called on shutdown exit: (eq (arg 0) 0) diff --git a/src/discof/restore/generated/fd_snapct_tile_seccomp.h b/src/discof/restore/generated/fd_snapct_tile_seccomp.h index e0ad2251aad..bcb04dcf0cc 100644 --- a/src/discof/restore/generated/fd_snapct_tile_seccomp.h +++ b/src/discof/restore/generated/fd_snapct_tile_seccomp.h @@ -24,58 +24,80 @@ #else # error "Target architecture is unsupported by seccomp." #endif -static const unsigned int sock_filter_policy_fd_snapct_tile_instr_cnt = 106; +static const unsigned int sock_filter_policy_fd_snapct_tile_instr_cnt = 117; static void populate_sock_filter_policy_fd_snapct_tile( ulong out_cnt, struct sock_filter * out, uint logfile_fd, uint dir_fd, uint out_full_fd, uint out_inc_fd, uint ping_fd ) { - FD_TEST( out_cnt >= 106 ); - struct sock_filter filter[106] = { + FD_TEST( out_cnt >= 117 ); + struct sock_filter filter[117] = { /* Check: Jump to RET_KILL_PROCESS if the script's arch != the runtime arch */ BPF_STMT( BPF_LD | BPF_W | BPF_ABS, ( offsetof( struct seccomp_data, arch ) ) ), - BPF_JUMP( BPF_JMP | BPF_JEQ | BPF_K, ARCH_NR, 0, /* RET_KILL_PROCESS */ 102 ), + BPF_JUMP( BPF_JMP | BPF_JEQ | BPF_K, ARCH_NR, 0, /* RET_KILL_PROCESS */ 113 ), /* loading syscall number in accumulator */ BPF_STMT( BPF_LD | BPF_W | BPF_ABS, ( offsetof( struct seccomp_data, nr ) ) ), /* allow write based on expression */ - BPF_JUMP( BPF_JMP | BPF_JEQ | BPF_K, SYS_write, /* check_write */ 12, 0 ), + BPF_JUMP( BPF_JMP | BPF_JEQ | BPF_K, SYS_write, /* check_write */ 15, 0 ), + /* allow read based on expression */ + BPF_JUMP( BPF_JMP | BPF_JEQ | BPF_K, SYS_read, /* check_read */ 18, 0 ), /* allow fsync based on expression */ - BPF_JUMP( BPF_JMP | BPF_JEQ | BPF_K, SYS_fsync, /* check_fsync */ 19, 0 ), + BPF_JUMP( BPF_JMP | BPF_JEQ | BPF_K, SYS_fsync, /* check_fsync */ 29, 0 ), /* allow socket based on expression */ - BPF_JUMP( BPF_JMP | BPF_JEQ | BPF_K, SYS_socket, /* check_socket */ 20, 0 ), + BPF_JUMP( BPF_JMP | BPF_JEQ | BPF_K, SYS_socket, /* check_socket */ 30, 0 ), /* allow connect based on expression */ - BPF_JUMP( BPF_JMP | BPF_JEQ | BPF_K, SYS_connect, /* check_connect */ 25, 0 ), + BPF_JUMP( BPF_JMP | BPF_JEQ | BPF_K, SYS_connect, /* check_connect */ 35, 0 ), /* allow close based on expression */ - BPF_JUMP( BPF_JMP | BPF_JEQ | BPF_K, SYS_close, /* check_close */ 36, 0 ), + BPF_JUMP( BPF_JMP | BPF_JEQ | BPF_K, SYS_close, /* check_close */ 46, 0 ), /* simply allow ppoll */ - BPF_JUMP( BPF_JMP | BPF_JEQ | BPF_K, SYS_ppoll, /* RET_ALLOW */ 96, 0 ), + BPF_JUMP( BPF_JMP | BPF_JEQ | BPF_K, SYS_ppoll, /* RET_ALLOW */ 106, 0 ), /* allow sendto based on expression */ - BPF_JUMP( BPF_JMP | BPF_JEQ | BPF_K, SYS_sendto, /* check_sendto */ 46, 0 ), + BPF_JUMP( BPF_JMP | BPF_JEQ | BPF_K, SYS_sendto, /* check_sendto */ 56, 0 ), /* allow sendmmsg based on expression */ - BPF_JUMP( BPF_JMP | BPF_JEQ | BPF_K, SYS_sendmmsg, /* check_sendmmsg */ 57, 0 ), + BPF_JUMP( BPF_JMP | BPF_JEQ | BPF_K, SYS_sendmmsg, /* check_sendmmsg */ 67, 0 ), /* allow recvfrom based on expression */ - BPF_JUMP( BPF_JMP | BPF_JEQ | BPF_K, SYS_recvfrom, /* check_recvfrom */ 60, 0 ), + BPF_JUMP( BPF_JMP | BPF_JEQ | BPF_K, SYS_recvfrom, /* check_recvfrom */ 70, 0 ), /* allow setsockopt based on expression */ - BPF_JUMP( BPF_JMP | BPF_JEQ | BPF_K, SYS_setsockopt, /* check_setsockopt */ 69, 0 ), + BPF_JUMP( BPF_JMP | BPF_JEQ | BPF_K, SYS_setsockopt, /* check_setsockopt */ 79, 0 ), /* allow renameat based on expression */ - BPF_JUMP( BPF_JMP | BPF_JEQ | BPF_K, SYS_renameat, /* check_renameat */ 84, 0 ), + BPF_JUMP( BPF_JMP | BPF_JEQ | BPF_K, SYS_renameat, /* check_renameat */ 94, 0 ), + /* simply allow getpid */ + BPF_JUMP( BPF_JMP | BPF_JEQ | BPF_K, SYS_getpid, /* RET_ALLOW */ 100, 0 ), + /* simply allow getrandom */ + BPF_JUMP( BPF_JMP | BPF_JEQ | BPF_K, SYS_getrandom, /* RET_ALLOW */ 99, 0 ), /* allow exit based on expression */ - BPF_JUMP( BPF_JMP | BPF_JEQ | BPF_K, SYS_exit, /* check_exit */ 87, 0 ), + BPF_JUMP( BPF_JMP | BPF_JEQ | BPF_K, SYS_exit, /* check_exit */ 95, 0 ), /* none of the syscalls matched */ - { BPF_JMP | BPF_JA, 0, 0, /* RET_KILL_PROCESS */ 88 }, + { BPF_JMP | BPF_JA, 0, 0, /* RET_KILL_PROCESS */ 96 }, // check_write: /* load syscall argument 0 in accumulator */ BPF_STMT( BPF_LD | BPF_W | BPF_ABS, offsetof(struct seccomp_data, args[0])), - BPF_JUMP( BPF_JMP | BPF_JEQ | BPF_K, 2, /* RET_ALLOW */ 87, /* lbl_1 */ 0 ), + BPF_JUMP( BPF_JMP | BPF_JEQ | BPF_K, dir_fd, /* RET_KILL_PROCESS */ 94, /* lbl_1 */ 0 ), // lbl_1: /* load syscall argument 0 in accumulator */ BPF_STMT( BPF_LD | BPF_W | BPF_ABS, offsetof(struct seccomp_data, args[0])), - BPF_JUMP( BPF_JMP | BPF_JEQ | BPF_K, logfile_fd, /* RET_ALLOW */ 85, /* lbl_2 */ 0 ), + BPF_JUMP( BPF_JMP | BPF_JEQ | BPF_K, ping_fd, /* RET_KILL_PROCESS */ 92, /* RET_ALLOW */ 93 ), +// check_read: + /* load syscall argument 0 in accumulator */ + BPF_STMT( BPF_LD | BPF_W | BPF_ABS, offsetof(struct seccomp_data, args[0])), + BPF_JUMP( BPF_JMP | BPF_JEQ | BPF_K, 2, /* RET_KILL_PROCESS */ 90, /* lbl_2 */ 0 ), // lbl_2: /* load syscall argument 0 in accumulator */ BPF_STMT( BPF_LD | BPF_W | BPF_ABS, offsetof(struct seccomp_data, args[0])), - BPF_JUMP( BPF_JMP | BPF_JEQ | BPF_K, out_full_fd, /* RET_ALLOW */ 83, /* lbl_3 */ 0 ), + BPF_JUMP( BPF_JMP | BPF_JEQ | BPF_K, logfile_fd, /* RET_KILL_PROCESS */ 88, /* lbl_3 */ 0 ), // lbl_3: /* load syscall argument 0 in accumulator */ BPF_STMT( BPF_LD | BPF_W | BPF_ABS, offsetof(struct seccomp_data, args[0])), - BPF_JUMP( BPF_JMP | BPF_JEQ | BPF_K, out_inc_fd, /* RET_ALLOW */ 81, /* RET_KILL_PROCESS */ 80 ), + BPF_JUMP( BPF_JMP | BPF_JEQ | BPF_K, dir_fd, /* RET_KILL_PROCESS */ 86, /* lbl_4 */ 0 ), +// lbl_4: + /* load syscall argument 0 in accumulator */ + BPF_STMT( BPF_LD | BPF_W | BPF_ABS, offsetof(struct seccomp_data, args[0])), + BPF_JUMP( BPF_JMP | BPF_JEQ | BPF_K, out_full_fd, /* RET_KILL_PROCESS */ 84, /* lbl_5 */ 0 ), +// lbl_5: + /* load syscall argument 0 in accumulator */ + BPF_STMT( BPF_LD | BPF_W | BPF_ABS, offsetof(struct seccomp_data, args[0])), + BPF_JUMP( BPF_JMP | BPF_JEQ | BPF_K, out_inc_fd, /* RET_KILL_PROCESS */ 82, /* lbl_6 */ 0 ), +// lbl_6: + /* load syscall argument 0 in accumulator */ + BPF_STMT( BPF_LD | BPF_W | BPF_ABS, offsetof(struct seccomp_data, args[0])), + BPF_JUMP( BPF_JMP | BPF_JEQ | BPF_K, ping_fd, /* RET_KILL_PROCESS */ 80, /* RET_ALLOW */ 81 ), // check_fsync: /* load syscall argument 0 in accumulator */ BPF_STMT( BPF_LD | BPF_W | BPF_ABS, offsetof(struct seccomp_data, args[0])), @@ -83,152 +105,152 @@ static void populate_sock_filter_policy_fd_snapct_tile( ulong out_cnt, struct so // check_socket: /* load syscall argument 0 in accumulator */ BPF_STMT( BPF_LD | BPF_W | BPF_ABS, offsetof(struct seccomp_data, args[0])), - BPF_JUMP( BPF_JMP | BPF_JEQ | BPF_K, AF_INET, /* lbl_4 */ 0, /* RET_KILL_PROCESS */ 76 ), -// lbl_4: + BPF_JUMP( BPF_JMP | BPF_JEQ | BPF_K, AF_INET, /* lbl_7 */ 0, /* RET_KILL_PROCESS */ 76 ), +// lbl_7: /* load syscall argument 1 in accumulator */ BPF_STMT( BPF_LD | BPF_W | BPF_ABS, offsetof(struct seccomp_data, args[1])), - BPF_JUMP( BPF_JMP | BPF_JEQ | BPF_K, SOCK_STREAM|SOCK_NONBLOCK, /* lbl_5 */ 0, /* RET_KILL_PROCESS */ 74 ), -// lbl_5: + BPF_JUMP( BPF_JMP | BPF_JEQ | BPF_K, SOCK_STREAM|SOCK_NONBLOCK, /* lbl_8 */ 0, /* RET_KILL_PROCESS */ 74 ), +// lbl_8: /* load syscall argument 2 in accumulator */ BPF_STMT( BPF_LD | BPF_W | BPF_ABS, offsetof(struct seccomp_data, args[2])), BPF_JUMP( BPF_JMP | BPF_JEQ | BPF_K, 0, /* RET_ALLOW */ 73, /* RET_KILL_PROCESS */ 72 ), // check_connect: /* load syscall argument 0 in accumulator */ BPF_STMT( BPF_LD | BPF_W | BPF_ABS, offsetof(struct seccomp_data, args[0])), - BPF_JUMP( BPF_JMP | BPF_JEQ | BPF_K, 2, /* RET_KILL_PROCESS */ 70, /* lbl_6 */ 0 ), -// lbl_6: + BPF_JUMP( BPF_JMP | BPF_JEQ | BPF_K, 2, /* RET_KILL_PROCESS */ 70, /* lbl_9 */ 0 ), +// lbl_9: /* load syscall argument 0 in accumulator */ BPF_STMT( BPF_LD | BPF_W | BPF_ABS, offsetof(struct seccomp_data, args[0])), - BPF_JUMP( BPF_JMP | BPF_JEQ | BPF_K, logfile_fd, /* RET_KILL_PROCESS */ 68, /* lbl_7 */ 0 ), -// lbl_7: + BPF_JUMP( BPF_JMP | BPF_JEQ | BPF_K, logfile_fd, /* RET_KILL_PROCESS */ 68, /* lbl_10 */ 0 ), +// lbl_10: /* load syscall argument 0 in accumulator */ BPF_STMT( BPF_LD | BPF_W | BPF_ABS, offsetof(struct seccomp_data, args[0])), - BPF_JUMP( BPF_JMP | BPF_JEQ | BPF_K, dir_fd, /* RET_KILL_PROCESS */ 66, /* lbl_8 */ 0 ), -// lbl_8: + BPF_JUMP( BPF_JMP | BPF_JEQ | BPF_K, dir_fd, /* RET_KILL_PROCESS */ 66, /* lbl_11 */ 0 ), +// lbl_11: /* load syscall argument 0 in accumulator */ BPF_STMT( BPF_LD | BPF_W | BPF_ABS, offsetof(struct seccomp_data, args[0])), - BPF_JUMP( BPF_JMP | BPF_JEQ | BPF_K, out_full_fd, /* RET_KILL_PROCESS */ 64, /* lbl_9 */ 0 ), -// lbl_9: + BPF_JUMP( BPF_JMP | BPF_JEQ | BPF_K, out_full_fd, /* RET_KILL_PROCESS */ 64, /* lbl_12 */ 0 ), +// lbl_12: /* load syscall argument 0 in accumulator */ BPF_STMT( BPF_LD | BPF_W | BPF_ABS, offsetof(struct seccomp_data, args[0])), - BPF_JUMP( BPF_JMP | BPF_JEQ | BPF_K, out_inc_fd, /* RET_KILL_PROCESS */ 62, /* lbl_10 */ 0 ), -// lbl_10: + BPF_JUMP( BPF_JMP | BPF_JEQ | BPF_K, out_inc_fd, /* RET_KILL_PROCESS */ 62, /* lbl_13 */ 0 ), +// lbl_13: /* load syscall argument 0 in accumulator */ BPF_STMT( BPF_LD | BPF_W | BPF_ABS, offsetof(struct seccomp_data, args[0])), BPF_JUMP( BPF_JMP | BPF_JEQ | BPF_K, ping_fd, /* RET_KILL_PROCESS */ 60, /* RET_ALLOW */ 61 ), // check_close: /* load syscall argument 0 in accumulator */ BPF_STMT( BPF_LD | BPF_W | BPF_ABS, offsetof(struct seccomp_data, args[0])), - BPF_JUMP( BPF_JMP | BPF_JEQ | BPF_K, 2, /* RET_KILL_PROCESS */ 58, /* lbl_11 */ 0 ), -// lbl_11: + BPF_JUMP( BPF_JMP | BPF_JEQ | BPF_K, 2, /* RET_KILL_PROCESS */ 58, /* lbl_14 */ 0 ), +// lbl_14: /* load syscall argument 0 in accumulator */ BPF_STMT( BPF_LD | BPF_W | BPF_ABS, offsetof(struct seccomp_data, args[0])), - BPF_JUMP( BPF_JMP | BPF_JEQ | BPF_K, logfile_fd, /* RET_KILL_PROCESS */ 56, /* lbl_12 */ 0 ), -// lbl_12: + BPF_JUMP( BPF_JMP | BPF_JEQ | BPF_K, logfile_fd, /* RET_KILL_PROCESS */ 56, /* lbl_15 */ 0 ), +// lbl_15: /* load syscall argument 0 in accumulator */ BPF_STMT( BPF_LD | BPF_W | BPF_ABS, offsetof(struct seccomp_data, args[0])), - BPF_JUMP( BPF_JMP | BPF_JEQ | BPF_K, dir_fd, /* RET_KILL_PROCESS */ 54, /* lbl_13 */ 0 ), -// lbl_13: + BPF_JUMP( BPF_JMP | BPF_JEQ | BPF_K, dir_fd, /* RET_KILL_PROCESS */ 54, /* lbl_16 */ 0 ), +// lbl_16: /* load syscall argument 0 in accumulator */ BPF_STMT( BPF_LD | BPF_W | BPF_ABS, offsetof(struct seccomp_data, args[0])), - BPF_JUMP( BPF_JMP | BPF_JEQ | BPF_K, out_full_fd, /* RET_KILL_PROCESS */ 52, /* lbl_14 */ 0 ), -// lbl_14: + BPF_JUMP( BPF_JMP | BPF_JEQ | BPF_K, out_full_fd, /* RET_KILL_PROCESS */ 52, /* lbl_17 */ 0 ), +// lbl_17: /* load syscall argument 0 in accumulator */ BPF_STMT( BPF_LD | BPF_W | BPF_ABS, offsetof(struct seccomp_data, args[0])), - BPF_JUMP( BPF_JMP | BPF_JEQ | BPF_K, out_inc_fd, /* RET_KILL_PROCESS */ 50, /* lbl_15 */ 0 ), -// lbl_15: + BPF_JUMP( BPF_JMP | BPF_JEQ | BPF_K, out_inc_fd, /* RET_KILL_PROCESS */ 50, /* lbl_18 */ 0 ), +// lbl_18: /* load syscall argument 0 in accumulator */ BPF_STMT( BPF_LD | BPF_W | BPF_ABS, offsetof(struct seccomp_data, args[0])), BPF_JUMP( BPF_JMP | BPF_JEQ | BPF_K, ping_fd, /* RET_KILL_PROCESS */ 48, /* RET_ALLOW */ 49 ), // check_sendto: /* load syscall argument 0 in accumulator */ BPF_STMT( BPF_LD | BPF_W | BPF_ABS, offsetof(struct seccomp_data, args[0])), - BPF_JUMP( BPF_JMP | BPF_JEQ | BPF_K, 2, /* RET_KILL_PROCESS */ 46, /* lbl_16 */ 0 ), -// lbl_16: + BPF_JUMP( BPF_JMP | BPF_JEQ | BPF_K, 2, /* RET_KILL_PROCESS */ 46, /* lbl_19 */ 0 ), +// lbl_19: /* load syscall argument 0 in accumulator */ BPF_STMT( BPF_LD | BPF_W | BPF_ABS, offsetof(struct seccomp_data, args[0])), - BPF_JUMP( BPF_JMP | BPF_JEQ | BPF_K, logfile_fd, /* RET_KILL_PROCESS */ 44, /* lbl_17 */ 0 ), -// lbl_17: + BPF_JUMP( BPF_JMP | BPF_JEQ | BPF_K, logfile_fd, /* RET_KILL_PROCESS */ 44, /* lbl_20 */ 0 ), +// lbl_20: /* load syscall argument 0 in accumulator */ BPF_STMT( BPF_LD | BPF_W | BPF_ABS, offsetof(struct seccomp_data, args[0])), - BPF_JUMP( BPF_JMP | BPF_JEQ | BPF_K, dir_fd, /* RET_KILL_PROCESS */ 42, /* lbl_18 */ 0 ), -// lbl_18: + BPF_JUMP( BPF_JMP | BPF_JEQ | BPF_K, dir_fd, /* RET_KILL_PROCESS */ 42, /* lbl_21 */ 0 ), +// lbl_21: /* load syscall argument 0 in accumulator */ BPF_STMT( BPF_LD | BPF_W | BPF_ABS, offsetof(struct seccomp_data, args[0])), - BPF_JUMP( BPF_JMP | BPF_JEQ | BPF_K, out_full_fd, /* RET_KILL_PROCESS */ 40, /* lbl_19 */ 0 ), -// lbl_19: + BPF_JUMP( BPF_JMP | BPF_JEQ | BPF_K, out_full_fd, /* RET_KILL_PROCESS */ 40, /* lbl_22 */ 0 ), +// lbl_22: /* load syscall argument 0 in accumulator */ BPF_STMT( BPF_LD | BPF_W | BPF_ABS, offsetof(struct seccomp_data, args[0])), - BPF_JUMP( BPF_JMP | BPF_JEQ | BPF_K, out_inc_fd, /* RET_KILL_PROCESS */ 38, /* lbl_20 */ 0 ), -// lbl_20: + BPF_JUMP( BPF_JMP | BPF_JEQ | BPF_K, out_inc_fd, /* RET_KILL_PROCESS */ 38, /* lbl_23 */ 0 ), +// lbl_23: /* load syscall argument 0 in accumulator */ BPF_STMT( BPF_LD | BPF_W | BPF_ABS, offsetof(struct seccomp_data, args[0])), BPF_JUMP( BPF_JMP | BPF_JEQ | BPF_K, ping_fd, /* RET_KILL_PROCESS */ 36, /* RET_ALLOW */ 37 ), // check_sendmmsg: /* load syscall argument 0 in accumulator */ BPF_STMT( BPF_LD | BPF_W | BPF_ABS, offsetof(struct seccomp_data, args[0])), - BPF_JUMP( BPF_JMP | BPF_JEQ | BPF_K, ping_fd, /* lbl_21 */ 0, /* RET_KILL_PROCESS */ 34 ), -// lbl_21: + BPF_JUMP( BPF_JMP | BPF_JEQ | BPF_K, ping_fd, /* lbl_24 */ 0, /* RET_KILL_PROCESS */ 34 ), +// lbl_24: /* load syscall argument 3 in accumulator */ BPF_STMT( BPF_LD | BPF_W | BPF_ABS, offsetof(struct seccomp_data, args[3])), BPF_JUMP( BPF_JMP | BPF_JEQ | BPF_K, 0, /* RET_ALLOW */ 33, /* RET_KILL_PROCESS */ 32 ), // check_recvfrom: /* load syscall argument 0 in accumulator */ BPF_STMT( BPF_LD | BPF_W | BPF_ABS, offsetof(struct seccomp_data, args[0])), - BPF_JUMP( BPF_JMP | BPF_JEQ | BPF_K, 2, /* RET_KILL_PROCESS */ 30, /* lbl_22 */ 0 ), -// lbl_22: + BPF_JUMP( BPF_JMP | BPF_JEQ | BPF_K, 2, /* RET_KILL_PROCESS */ 30, /* lbl_25 */ 0 ), +// lbl_25: /* load syscall argument 0 in accumulator */ BPF_STMT( BPF_LD | BPF_W | BPF_ABS, offsetof(struct seccomp_data, args[0])), - BPF_JUMP( BPF_JMP | BPF_JEQ | BPF_K, logfile_fd, /* RET_KILL_PROCESS */ 28, /* lbl_23 */ 0 ), -// lbl_23: + BPF_JUMP( BPF_JMP | BPF_JEQ | BPF_K, logfile_fd, /* RET_KILL_PROCESS */ 28, /* lbl_26 */ 0 ), +// lbl_26: /* load syscall argument 0 in accumulator */ BPF_STMT( BPF_LD | BPF_W | BPF_ABS, offsetof(struct seccomp_data, args[0])), - BPF_JUMP( BPF_JMP | BPF_JEQ | BPF_K, dir_fd, /* RET_KILL_PROCESS */ 26, /* lbl_24 */ 0 ), -// lbl_24: + BPF_JUMP( BPF_JMP | BPF_JEQ | BPF_K, dir_fd, /* RET_KILL_PROCESS */ 26, /* lbl_27 */ 0 ), +// lbl_27: /* load syscall argument 0 in accumulator */ BPF_STMT( BPF_LD | BPF_W | BPF_ABS, offsetof(struct seccomp_data, args[0])), - BPF_JUMP( BPF_JMP | BPF_JEQ | BPF_K, out_full_fd, /* RET_KILL_PROCESS */ 24, /* lbl_25 */ 0 ), -// lbl_25: + BPF_JUMP( BPF_JMP | BPF_JEQ | BPF_K, out_full_fd, /* RET_KILL_PROCESS */ 24, /* lbl_28 */ 0 ), +// lbl_28: /* load syscall argument 0 in accumulator */ BPF_STMT( BPF_LD | BPF_W | BPF_ABS, offsetof(struct seccomp_data, args[0])), BPF_JUMP( BPF_JMP | BPF_JEQ | BPF_K, out_inc_fd, /* RET_KILL_PROCESS */ 22, /* RET_ALLOW */ 23 ), // check_setsockopt: /* load syscall argument 0 in accumulator */ BPF_STMT( BPF_LD | BPF_W | BPF_ABS, offsetof(struct seccomp_data, args[0])), - BPF_JUMP( BPF_JMP | BPF_JEQ | BPF_K, 2, /* RET_KILL_PROCESS */ 20, /* lbl_27 */ 0 ), -// lbl_27: + BPF_JUMP( BPF_JMP | BPF_JEQ | BPF_K, 2, /* RET_KILL_PROCESS */ 20, /* lbl_30 */ 0 ), +// lbl_30: /* load syscall argument 0 in accumulator */ BPF_STMT( BPF_LD | BPF_W | BPF_ABS, offsetof(struct seccomp_data, args[0])), - BPF_JUMP( BPF_JMP | BPF_JEQ | BPF_K, logfile_fd, /* RET_KILL_PROCESS */ 18, /* lbl_28 */ 0 ), -// lbl_28: + BPF_JUMP( BPF_JMP | BPF_JEQ | BPF_K, logfile_fd, /* RET_KILL_PROCESS */ 18, /* lbl_31 */ 0 ), +// lbl_31: /* load syscall argument 0 in accumulator */ BPF_STMT( BPF_LD | BPF_W | BPF_ABS, offsetof(struct seccomp_data, args[0])), - BPF_JUMP( BPF_JMP | BPF_JEQ | BPF_K, dir_fd, /* RET_KILL_PROCESS */ 16, /* lbl_29 */ 0 ), -// lbl_29: + BPF_JUMP( BPF_JMP | BPF_JEQ | BPF_K, dir_fd, /* RET_KILL_PROCESS */ 16, /* lbl_32 */ 0 ), +// lbl_32: /* load syscall argument 0 in accumulator */ BPF_STMT( BPF_LD | BPF_W | BPF_ABS, offsetof(struct seccomp_data, args[0])), - BPF_JUMP( BPF_JMP | BPF_JEQ | BPF_K, out_full_fd, /* RET_KILL_PROCESS */ 14, /* lbl_30 */ 0 ), -// lbl_30: + BPF_JUMP( BPF_JMP | BPF_JEQ | BPF_K, out_full_fd, /* RET_KILL_PROCESS */ 14, /* lbl_33 */ 0 ), +// lbl_33: /* load syscall argument 0 in accumulator */ BPF_STMT( BPF_LD | BPF_W | BPF_ABS, offsetof(struct seccomp_data, args[0])), - BPF_JUMP( BPF_JMP | BPF_JEQ | BPF_K, out_inc_fd, /* RET_KILL_PROCESS */ 12, /* lbl_31 */ 0 ), -// lbl_31: + BPF_JUMP( BPF_JMP | BPF_JEQ | BPF_K, out_inc_fd, /* RET_KILL_PROCESS */ 12, /* lbl_34 */ 0 ), +// lbl_34: /* load syscall argument 0 in accumulator */ BPF_STMT( BPF_LD | BPF_W | BPF_ABS, offsetof(struct seccomp_data, args[0])), - BPF_JUMP( BPF_JMP | BPF_JEQ | BPF_K, ping_fd, /* RET_KILL_PROCESS */ 10, /* lbl_26 */ 0 ), -// lbl_26: + BPF_JUMP( BPF_JMP | BPF_JEQ | BPF_K, ping_fd, /* RET_KILL_PROCESS */ 10, /* lbl_29 */ 0 ), +// lbl_29: /* load syscall argument 1 in accumulator */ BPF_STMT( BPF_LD | BPF_W | BPF_ABS, offsetof(struct seccomp_data, args[1])), - BPF_JUMP( BPF_JMP | BPF_JEQ | BPF_K, IPPROTO_TCP, /* lbl_32 */ 0, /* RET_KILL_PROCESS */ 8 ), -// lbl_32: + BPF_JUMP( BPF_JMP | BPF_JEQ | BPF_K, IPPROTO_TCP, /* lbl_35 */ 0, /* RET_KILL_PROCESS */ 8 ), +// lbl_35: /* load syscall argument 2 in accumulator */ BPF_STMT( BPF_LD | BPF_W | BPF_ABS, offsetof(struct seccomp_data, args[2])), BPF_JUMP( BPF_JMP | BPF_JEQ | BPF_K, TCP_NODELAY, /* RET_ALLOW */ 7, /* RET_KILL_PROCESS */ 6 ), // check_renameat: /* load syscall argument 0 in accumulator */ BPF_STMT( BPF_LD | BPF_W | BPF_ABS, offsetof(struct seccomp_data, args[0])), - BPF_JUMP( BPF_JMP | BPF_JEQ | BPF_K, dir_fd, /* lbl_33 */ 0, /* RET_KILL_PROCESS */ 4 ), -// lbl_33: + BPF_JUMP( BPF_JMP | BPF_JEQ | BPF_K, dir_fd, /* lbl_36 */ 0, /* RET_KILL_PROCESS */ 4 ), +// lbl_36: /* load syscall argument 2 in accumulator */ BPF_STMT( BPF_LD | BPF_W | BPF_ABS, offsetof(struct seccomp_data, args[2])), BPF_JUMP( BPF_JMP | BPF_JEQ | BPF_K, dir_fd, /* RET_ALLOW */ 3, /* RET_KILL_PROCESS */ 2 ), diff --git a/src/discof/restore/generated/fd_snapld_tile_seccomp.h b/src/discof/restore/generated/fd_snapld_tile_seccomp.h index ec18f23a3fd..ea96c33873f 100644 --- a/src/discof/restore/generated/fd_snapld_tile_seccomp.h +++ b/src/discof/restore/generated/fd_snapld_tile_seccomp.h @@ -24,151 +24,163 @@ #else # error "Target architecture is unsupported by seccomp." #endif -static const unsigned int sock_filter_policy_fd_snapld_tile_instr_cnt = 78; +static const unsigned int sock_filter_policy_fd_snapld_tile_instr_cnt = 84; -static void populate_sock_filter_policy_fd_snapld_tile( ulong out_cnt, struct sock_filter * out, uint logfile_fd, uint in_full_fd, uint in_incr_fd ) { - FD_TEST( out_cnt >= 78 ); - struct sock_filter filter[78] = { +static void populate_sock_filter_policy_fd_snapld_tile( ulong out_cnt, struct sock_filter * out, uint logfile_fd, uint in_full_fd, uint in_incr_fd, uint sockfd ) { + FD_TEST( out_cnt >= 84 ); + struct sock_filter filter[84] = { /* Check: Jump to RET_KILL_PROCESS if the script's arch != the runtime arch */ BPF_STMT( BPF_LD | BPF_W | BPF_ABS, ( offsetof( struct seccomp_data, arch ) ) ), - BPF_JUMP( BPF_JMP | BPF_JEQ | BPF_K, ARCH_NR, 0, /* RET_KILL_PROCESS */ 74 ), + BPF_JUMP( BPF_JMP | BPF_JEQ | BPF_K, ARCH_NR, 0, /* RET_KILL_PROCESS */ 80 ), /* loading syscall number in accumulator */ BPF_STMT( BPF_LD | BPF_W | BPF_ABS, ( offsetof( struct seccomp_data, nr ) ) ), /* allow write based on expression */ - BPF_JUMP( BPF_JMP | BPF_JEQ | BPF_K, SYS_write, /* check_write */ 12, 0 ), + BPF_JUMP( BPF_JMP | BPF_JEQ | BPF_K, SYS_write, /* check_write */ 14, 0 ), /* allow fsync based on expression */ - BPF_JUMP( BPF_JMP | BPF_JEQ | BPF_K, SYS_fsync, /* check_fsync */ 15, 0 ), + BPF_JUMP( BPF_JMP | BPF_JEQ | BPF_K, SYS_fsync, /* check_fsync */ 19, 0 ), /* allow read based on expression */ - BPF_JUMP( BPF_JMP | BPF_JEQ | BPF_K, SYS_read, /* check_read */ 16, 0 ), + BPF_JUMP( BPF_JMP | BPF_JEQ | BPF_K, SYS_read, /* check_read */ 20, 0 ), /* allow lseek based on expression */ - BPF_JUMP( BPF_JMP | BPF_JEQ | BPF_K, SYS_lseek, /* check_lseek */ 19, 0 ), + BPF_JUMP( BPF_JMP | BPF_JEQ | BPF_K, SYS_lseek, /* check_lseek */ 25, 0 ), /* allow socket based on expression */ - BPF_JUMP( BPF_JMP | BPF_JEQ | BPF_K, SYS_socket, /* check_socket */ 26, 0 ), + BPF_JUMP( BPF_JMP | BPF_JEQ | BPF_K, SYS_socket, /* check_socket */ 32, 0 ), /* allow connect based on expression */ - BPF_JUMP( BPF_JMP | BPF_JEQ | BPF_K, SYS_connect, /* check_connect */ 31, 0 ), + BPF_JUMP( BPF_JMP | BPF_JEQ | BPF_K, SYS_connect, /* check_connect */ 37, 0 ), /* allow close based on expression */ - BPF_JUMP( BPF_JMP | BPF_JEQ | BPF_K, SYS_close, /* check_close */ 38, 0 ), + BPF_JUMP( BPF_JMP | BPF_JEQ | BPF_K, SYS_close, /* check_close */ 44, 0 ), /* allow sendto based on expression */ - BPF_JUMP( BPF_JMP | BPF_JEQ | BPF_K, SYS_sendto, /* check_sendto */ 45, 0 ), + BPF_JUMP( BPF_JMP | BPF_JEQ | BPF_K, SYS_sendto, /* check_sendto */ 51, 0 ), /* allow recvfrom based on expression */ - BPF_JUMP( BPF_JMP | BPF_JEQ | BPF_K, SYS_recvfrom, /* check_recvfrom */ 52, 0 ), + BPF_JUMP( BPF_JMP | BPF_JEQ | BPF_K, SYS_recvfrom, /* check_recvfrom */ 58, 0 ), /* allow ppoll based on expression */ - BPF_JUMP( BPF_JMP | BPF_JEQ | BPF_K, SYS_ppoll, /* check_ppoll */ 59, 0 ), + BPF_JUMP( BPF_JMP | BPF_JEQ | BPF_K, SYS_ppoll, /* check_ppoll */ 65, 0 ), /* simply allow clock_nanosleep */ - BPF_JUMP( BPF_JMP | BPF_JEQ | BPF_K, SYS_clock_nanosleep, /* RET_ALLOW */ 63, 0 ), + BPF_JUMP( BPF_JMP | BPF_JEQ | BPF_K, SYS_clock_nanosleep, /* RET_ALLOW */ 69, 0 ), + /* simply allow getpid */ + BPF_JUMP( BPF_JMP | BPF_JEQ | BPF_K, SYS_getpid, /* RET_ALLOW */ 68, 0 ), + /* simply allow getrandom */ + BPF_JUMP( BPF_JMP | BPF_JEQ | BPF_K, SYS_getrandom, /* RET_ALLOW */ 67, 0 ), /* allow exit based on expression */ - BPF_JUMP( BPF_JMP | BPF_JEQ | BPF_K, SYS_exit, /* check_exit */ 59, 0 ), + BPF_JUMP( BPF_JMP | BPF_JEQ | BPF_K, SYS_exit, /* check_exit */ 63, 0 ), /* none of the syscalls matched */ - { BPF_JMP | BPF_JA, 0, 0, /* RET_KILL_PROCESS */ 60 }, + { BPF_JMP | BPF_JA, 0, 0, /* RET_KILL_PROCESS */ 64 }, // check_write: /* load syscall argument 0 in accumulator */ BPF_STMT( BPF_LD | BPF_W | BPF_ABS, offsetof(struct seccomp_data, args[0])), - BPF_JUMP( BPF_JMP | BPF_JEQ | BPF_K, 2, /* RET_ALLOW */ 59, /* lbl_1 */ 0 ), + BPF_JUMP( BPF_JMP | BPF_JEQ | BPF_K, 2, /* RET_ALLOW */ 63, /* lbl_1 */ 0 ), // lbl_1: /* load syscall argument 0 in accumulator */ BPF_STMT( BPF_LD | BPF_W | BPF_ABS, offsetof(struct seccomp_data, args[0])), - BPF_JUMP( BPF_JMP | BPF_JEQ | BPF_K, logfile_fd, /* RET_ALLOW */ 57, /* RET_KILL_PROCESS */ 56 ), + BPF_JUMP( BPF_JMP | BPF_JEQ | BPF_K, logfile_fd, /* RET_ALLOW */ 61, /* lbl_2 */ 0 ), +// lbl_2: + /* load syscall argument 0 in accumulator */ + BPF_STMT( BPF_LD | BPF_W | BPF_ABS, offsetof(struct seccomp_data, args[0])), + BPF_JUMP( BPF_JMP | BPF_JEQ | BPF_K, sockfd, /* RET_ALLOW */ 59, /* RET_KILL_PROCESS */ 58 ), // check_fsync: /* load syscall argument 0 in accumulator */ BPF_STMT( BPF_LD | BPF_W | BPF_ABS, offsetof(struct seccomp_data, args[0])), - BPF_JUMP( BPF_JMP | BPF_JEQ | BPF_K, logfile_fd, /* RET_ALLOW */ 55, /* RET_KILL_PROCESS */ 54 ), + BPF_JUMP( BPF_JMP | BPF_JEQ | BPF_K, logfile_fd, /* RET_ALLOW */ 57, /* RET_KILL_PROCESS */ 56 ), // check_read: /* load syscall argument 0 in accumulator */ BPF_STMT( BPF_LD | BPF_W | BPF_ABS, offsetof(struct seccomp_data, args[0])), - BPF_JUMP( BPF_JMP | BPF_JEQ | BPF_K, in_full_fd, /* RET_ALLOW */ 53, /* lbl_2 */ 0 ), -// lbl_2: + BPF_JUMP( BPF_JMP | BPF_JEQ | BPF_K, in_full_fd, /* RET_ALLOW */ 55, /* lbl_3 */ 0 ), +// lbl_3: /* load syscall argument 0 in accumulator */ BPF_STMT( BPF_LD | BPF_W | BPF_ABS, offsetof(struct seccomp_data, args[0])), - BPF_JUMP( BPF_JMP | BPF_JEQ | BPF_K, in_incr_fd, /* RET_ALLOW */ 51, /* RET_KILL_PROCESS */ 50 ), + BPF_JUMP( BPF_JMP | BPF_JEQ | BPF_K, in_incr_fd, /* RET_ALLOW */ 53, /* lbl_4 */ 0 ), +// lbl_4: + /* load syscall argument 0 in accumulator */ + BPF_STMT( BPF_LD | BPF_W | BPF_ABS, offsetof(struct seccomp_data, args[0])), + BPF_JUMP( BPF_JMP | BPF_JEQ | BPF_K, sockfd, /* RET_ALLOW */ 51, /* RET_KILL_PROCESS */ 50 ), // check_lseek: /* load syscall argument 0 in accumulator */ BPF_STMT( BPF_LD | BPF_W | BPF_ABS, offsetof(struct seccomp_data, args[0])), - BPF_JUMP( BPF_JMP | BPF_JEQ | BPF_K, in_full_fd, /* lbl_3 */ 2, /* lbl_4 */ 0 ), -// lbl_4: + BPF_JUMP( BPF_JMP | BPF_JEQ | BPF_K, in_full_fd, /* lbl_5 */ 2, /* lbl_6 */ 0 ), +// lbl_6: /* load syscall argument 0 in accumulator */ BPF_STMT( BPF_LD | BPF_W | BPF_ABS, offsetof(struct seccomp_data, args[0])), - BPF_JUMP( BPF_JMP | BPF_JEQ | BPF_K, in_incr_fd, /* lbl_3 */ 0, /* RET_KILL_PROCESS */ 46 ), -// lbl_3: + BPF_JUMP( BPF_JMP | BPF_JEQ | BPF_K, in_incr_fd, /* lbl_5 */ 0, /* RET_KILL_PROCESS */ 46 ), +// lbl_5: /* load syscall argument 1 in accumulator */ BPF_STMT( BPF_LD | BPF_W | BPF_ABS, offsetof(struct seccomp_data, args[1])), - BPF_JUMP( BPF_JMP | BPF_JEQ | BPF_K, 0, /* lbl_5 */ 0, /* RET_KILL_PROCESS */ 44 ), -// lbl_5: + BPF_JUMP( BPF_JMP | BPF_JEQ | BPF_K, 0, /* lbl_7 */ 0, /* RET_KILL_PROCESS */ 44 ), +// lbl_7: /* load syscall argument 2 in accumulator */ BPF_STMT( BPF_LD | BPF_W | BPF_ABS, offsetof(struct seccomp_data, args[2])), BPF_JUMP( BPF_JMP | BPF_JEQ | BPF_K, SEEK_SET, /* RET_ALLOW */ 43, /* RET_KILL_PROCESS */ 42 ), // check_socket: /* load syscall argument 0 in accumulator */ BPF_STMT( BPF_LD | BPF_W | BPF_ABS, offsetof(struct seccomp_data, args[0])), - BPF_JUMP( BPF_JMP | BPF_JEQ | BPF_K, AF_INET, /* lbl_6 */ 0, /* RET_KILL_PROCESS */ 40 ), -// lbl_6: + BPF_JUMP( BPF_JMP | BPF_JEQ | BPF_K, AF_INET, /* lbl_8 */ 0, /* RET_KILL_PROCESS */ 40 ), +// lbl_8: /* load syscall argument 1 in accumulator */ BPF_STMT( BPF_LD | BPF_W | BPF_ABS, offsetof(struct seccomp_data, args[1])), - BPF_JUMP( BPF_JMP | BPF_JEQ | BPF_K, SOCK_STREAM|SOCK_NONBLOCK, /* lbl_7 */ 0, /* RET_KILL_PROCESS */ 38 ), -// lbl_7: + BPF_JUMP( BPF_JMP | BPF_JEQ | BPF_K, SOCK_STREAM|SOCK_NONBLOCK, /* lbl_9 */ 0, /* RET_KILL_PROCESS */ 38 ), +// lbl_9: /* load syscall argument 2 in accumulator */ BPF_STMT( BPF_LD | BPF_W | BPF_ABS, offsetof(struct seccomp_data, args[2])), BPF_JUMP( BPF_JMP | BPF_JEQ | BPF_K, 0, /* RET_ALLOW */ 37, /* RET_KILL_PROCESS */ 36 ), // check_connect: /* load syscall argument 0 in accumulator */ BPF_STMT( BPF_LD | BPF_W | BPF_ABS, offsetof(struct seccomp_data, args[0])), - BPF_JUMP( BPF_JMP | BPF_JEQ | BPF_K, 2, /* RET_KILL_PROCESS */ 34, /* lbl_8 */ 0 ), -// lbl_8: + BPF_JUMP( BPF_JMP | BPF_JEQ | BPF_K, 2, /* RET_KILL_PROCESS */ 34, /* lbl_10 */ 0 ), +// lbl_10: /* load syscall argument 0 in accumulator */ BPF_STMT( BPF_LD | BPF_W | BPF_ABS, offsetof(struct seccomp_data, args[0])), - BPF_JUMP( BPF_JMP | BPF_JEQ | BPF_K, logfile_fd, /* RET_KILL_PROCESS */ 32, /* lbl_9 */ 0 ), -// lbl_9: + BPF_JUMP( BPF_JMP | BPF_JEQ | BPF_K, logfile_fd, /* RET_KILL_PROCESS */ 32, /* lbl_11 */ 0 ), +// lbl_11: /* load syscall argument 0 in accumulator */ BPF_STMT( BPF_LD | BPF_W | BPF_ABS, offsetof(struct seccomp_data, args[0])), - BPF_JUMP( BPF_JMP | BPF_JEQ | BPF_K, in_full_fd, /* RET_KILL_PROCESS */ 30, /* lbl_10 */ 0 ), -// lbl_10: + BPF_JUMP( BPF_JMP | BPF_JEQ | BPF_K, in_full_fd, /* RET_KILL_PROCESS */ 30, /* lbl_12 */ 0 ), +// lbl_12: /* load syscall argument 0 in accumulator */ BPF_STMT( BPF_LD | BPF_W | BPF_ABS, offsetof(struct seccomp_data, args[0])), BPF_JUMP( BPF_JMP | BPF_JEQ | BPF_K, in_incr_fd, /* RET_KILL_PROCESS */ 28, /* RET_ALLOW */ 29 ), // check_close: /* load syscall argument 0 in accumulator */ BPF_STMT( BPF_LD | BPF_W | BPF_ABS, offsetof(struct seccomp_data, args[0])), - BPF_JUMP( BPF_JMP | BPF_JEQ | BPF_K, 2, /* RET_KILL_PROCESS */ 26, /* lbl_11 */ 0 ), -// lbl_11: + BPF_JUMP( BPF_JMP | BPF_JEQ | BPF_K, 2, /* RET_KILL_PROCESS */ 26, /* lbl_13 */ 0 ), +// lbl_13: /* load syscall argument 0 in accumulator */ BPF_STMT( BPF_LD | BPF_W | BPF_ABS, offsetof(struct seccomp_data, args[0])), - BPF_JUMP( BPF_JMP | BPF_JEQ | BPF_K, logfile_fd, /* RET_KILL_PROCESS */ 24, /* lbl_12 */ 0 ), -// lbl_12: + BPF_JUMP( BPF_JMP | BPF_JEQ | BPF_K, logfile_fd, /* RET_KILL_PROCESS */ 24, /* lbl_14 */ 0 ), +// lbl_14: /* load syscall argument 0 in accumulator */ BPF_STMT( BPF_LD | BPF_W | BPF_ABS, offsetof(struct seccomp_data, args[0])), - BPF_JUMP( BPF_JMP | BPF_JEQ | BPF_K, in_full_fd, /* RET_KILL_PROCESS */ 22, /* lbl_13 */ 0 ), -// lbl_13: + BPF_JUMP( BPF_JMP | BPF_JEQ | BPF_K, in_full_fd, /* RET_KILL_PROCESS */ 22, /* lbl_15 */ 0 ), +// lbl_15: /* load syscall argument 0 in accumulator */ BPF_STMT( BPF_LD | BPF_W | BPF_ABS, offsetof(struct seccomp_data, args[0])), BPF_JUMP( BPF_JMP | BPF_JEQ | BPF_K, in_incr_fd, /* RET_KILL_PROCESS */ 20, /* RET_ALLOW */ 21 ), // check_sendto: /* load syscall argument 0 in accumulator */ BPF_STMT( BPF_LD | BPF_W | BPF_ABS, offsetof(struct seccomp_data, args[0])), - BPF_JUMP( BPF_JMP | BPF_JEQ | BPF_K, 2, /* RET_KILL_PROCESS */ 18, /* lbl_14 */ 0 ), -// lbl_14: + BPF_JUMP( BPF_JMP | BPF_JEQ | BPF_K, 2, /* RET_KILL_PROCESS */ 18, /* lbl_16 */ 0 ), +// lbl_16: /* load syscall argument 0 in accumulator */ BPF_STMT( BPF_LD | BPF_W | BPF_ABS, offsetof(struct seccomp_data, args[0])), - BPF_JUMP( BPF_JMP | BPF_JEQ | BPF_K, logfile_fd, /* RET_KILL_PROCESS */ 16, /* lbl_15 */ 0 ), -// lbl_15: + BPF_JUMP( BPF_JMP | BPF_JEQ | BPF_K, logfile_fd, /* RET_KILL_PROCESS */ 16, /* lbl_17 */ 0 ), +// lbl_17: /* load syscall argument 0 in accumulator */ BPF_STMT( BPF_LD | BPF_W | BPF_ABS, offsetof(struct seccomp_data, args[0])), - BPF_JUMP( BPF_JMP | BPF_JEQ | BPF_K, in_full_fd, /* RET_KILL_PROCESS */ 14, /* lbl_16 */ 0 ), -// lbl_16: + BPF_JUMP( BPF_JMP | BPF_JEQ | BPF_K, in_full_fd, /* RET_KILL_PROCESS */ 14, /* lbl_18 */ 0 ), +// lbl_18: /* load syscall argument 0 in accumulator */ BPF_STMT( BPF_LD | BPF_W | BPF_ABS, offsetof(struct seccomp_data, args[0])), BPF_JUMP( BPF_JMP | BPF_JEQ | BPF_K, in_incr_fd, /* RET_KILL_PROCESS */ 12, /* RET_ALLOW */ 13 ), // check_recvfrom: /* load syscall argument 0 in accumulator */ BPF_STMT( BPF_LD | BPF_W | BPF_ABS, offsetof(struct seccomp_data, args[0])), - BPF_JUMP( BPF_JMP | BPF_JEQ | BPF_K, 2, /* RET_KILL_PROCESS */ 10, /* lbl_17 */ 0 ), -// lbl_17: + BPF_JUMP( BPF_JMP | BPF_JEQ | BPF_K, 2, /* RET_KILL_PROCESS */ 10, /* lbl_19 */ 0 ), +// lbl_19: /* load syscall argument 0 in accumulator */ BPF_STMT( BPF_LD | BPF_W | BPF_ABS, offsetof(struct seccomp_data, args[0])), - BPF_JUMP( BPF_JMP | BPF_JEQ | BPF_K, logfile_fd, /* RET_KILL_PROCESS */ 8, /* lbl_18 */ 0 ), -// lbl_18: + BPF_JUMP( BPF_JMP | BPF_JEQ | BPF_K, logfile_fd, /* RET_KILL_PROCESS */ 8, /* lbl_20 */ 0 ), +// lbl_20: /* load syscall argument 0 in accumulator */ BPF_STMT( BPF_LD | BPF_W | BPF_ABS, offsetof(struct seccomp_data, args[0])), - BPF_JUMP( BPF_JMP | BPF_JEQ | BPF_K, in_full_fd, /* RET_KILL_PROCESS */ 6, /* lbl_19 */ 0 ), -// lbl_19: + BPF_JUMP( BPF_JMP | BPF_JEQ | BPF_K, in_full_fd, /* RET_KILL_PROCESS */ 6, /* lbl_21 */ 0 ), +// lbl_21: /* load syscall argument 0 in accumulator */ BPF_STMT( BPF_LD | BPF_W | BPF_ABS, offsetof(struct seccomp_data, args[0])), BPF_JUMP( BPF_JMP | BPF_JEQ | BPF_K, in_incr_fd, /* RET_KILL_PROCESS */ 4, /* RET_ALLOW */ 5 ), diff --git a/src/discof/restore/utils/fd_http_resolver.c b/src/discof/restore/utils/fd_http_resolver.c index 216257d5c10..421ea335634 100644 --- a/src/discof/restore/utils/fd_http_resolver.c +++ b/src/discof/restore/utils/fd_http_resolver.c @@ -11,13 +11,18 @@ #include #include +#if FD_HAS_OPENSSL +#include +#include "../../../waltz/openssl/fd_openssl_tile.h" +#endif + #define PEER_STATE_UNRESOLVED (0) #define PEER_STATE_REFRESHING (1) #define PEER_STATE_VALID (2) #define PEER_STATE_INVALID (3) #define PEER_DEADLINE_NANOS_VALID (5L*1000L*1000L*1000L) /* 5 seconds */ -#define PEER_DEADLINE_NANOS_RESOLVE (1L*1000L*1000L*1000L) /* 1 second */ +#define PEER_DEADLINE_NANOS_RESOLVE (2L*1000L*1000L*1000L) /* 2 seconds */ #define PEER_DEADLINE_NANOS_INVALID (5L*1000L*1000L*1000L) /* 5 seconds */ /* FIXME: The fds/fds_len/idx logic is fragile, replace with something @@ -25,6 +30,8 @@ struct fd_ssresolve_peer { fd_ip4_port_t addr; + char const * hostname; + int is_https; fd_ssinfo_t ssinfo; fd_ssresolve_t * full_ssresolve; @@ -76,6 +83,10 @@ struct fd_http_resolver_private { void * cb_arg; fd_http_resolver_on_resolve_fn_t on_resolve_cb; +#if FD_HAS_OPENSSL + SSL_CTX * ssl_ctx; +#endif + ulong magic; /* ==FD_HTTP_RESOLVER_MAGIC */ }; @@ -155,6 +166,22 @@ fd_http_resolver_new( void * shmem, resolver->cb_arg = cb_arg; resolver->on_resolve_cb = on_resolve_cb; +#if FD_HAS_OPENSSL + SSL_CTX * ssl_ctx = SSL_CTX_new( TLS_client_method() ); + if( FD_UNLIKELY( !ssl_ctx ) ) { + FD_LOG_ERR(( "SSL_CTX_new failed" )); + } + + if( FD_UNLIKELY( !SSL_CTX_set_min_proto_version( ssl_ctx, TLS1_3_VERSION ) ) ) { + FD_LOG_ERR(( "SSL_CTX_set_min_proto_version(ssl_ctx,TLS1_3_VERSION) failed" )); + } + + /* transfering ownership of ssl_ctx by assignment */ + resolver->ssl_ctx = ssl_ctx; + + fd_ossl_load_certs( resolver->ssl_ctx ); +#endif + FD_COMPILER_MFENCE(); FD_VOLATILE( resolver->magic ) = FD_HTTP_RESOLVER_MAGIC; FD_COMPILER_MFENCE(); @@ -186,13 +213,17 @@ fd_http_resolver_join( void * shresolver ) { void fd_http_resolver_add( fd_http_resolver_t * resolver, - fd_ip4_port_t addr ) { + fd_ip4_port_t addr, + char const * hostname, + int is_https ) { if( !peer_pool_free( resolver->pool ) ) { FD_LOG_ERR(( "peer pool exhausted" )); } fd_ssresolve_peer_t * peer = peer_pool_ele_acquire( resolver->pool ); peer->state = PEER_STATE_UNRESOLVED; peer->addr = addr; + peer->hostname = hostname; + peer->is_https = is_https; peer->fd.idx = ULONG_MAX; peer->ssinfo.full.slot = ULONG_MAX; peer->ssinfo.incremental.base_slot = ULONG_MAX; @@ -240,14 +271,31 @@ peer_connect( fd_http_resolver_t * resolver, resolver->fds_idx[ resolver->fds_len ] = peer_pool_idx( resolver->pool, peer ); peer->fd.idx = resolver->fds_len; resolver->fds_len++; - fd_ssresolve_init( peer->full_ssresolve, peer->addr, resolver->fds[ peer->fd.idx ].fd, 1 ); + + if( FD_UNLIKELY( peer->is_https ) ) { +#if FD_HAS_OPENSSL + fd_ssresolve_init_https( peer->full_ssresolve, peer->addr, resolver->fds[ peer->fd.idx ].fd, 1, peer->hostname, resolver->ssl_ctx ); +#else + FD_LOG_ERR(( "peer %s requires https but firedancer is built without openssl support. Please remove this peer from your validator config.", peer->hostname )); +#endif + } else { + fd_ssresolve_init( peer->full_ssresolve, peer->addr, resolver->fds[ peer->fd.idx ].fd, 1 ); + } if( FD_LIKELY( resolver->incremental_snapshot_fetch ) ) { err = create_socket( resolver, peer ); /* incremental */ if( FD_UNLIKELY( err ) ) return err; resolver->fds_idx[ resolver->fds_len ] = peer_pool_idx( resolver->pool, peer ); resolver->fds_len++; - fd_ssresolve_init( peer->inc_ssresolve, peer->addr, resolver->fds[ peer->fd.idx+1UL ].fd, 0 ); + if( FD_UNLIKELY( peer->is_https ) ) { +#if FD_HAS_OPENSSL + fd_ssresolve_init_https( peer->inc_ssresolve, peer->addr, resolver->fds[ peer->fd.idx+1UL ].fd, 0, peer->hostname, resolver->ssl_ctx ); +#else + FD_LOG_ERR(( "peer requires https but firedancer is built without openssl support" )); +#endif + } else { + fd_ssresolve_init( peer->inc_ssresolve, peer->addr, resolver->fds[ peer->fd.idx+1UL ].fd, 0 ); + } } else { resolver->fds[ resolver->fds_len ] = (struct pollfd) { .fd = -1, @@ -266,9 +314,9 @@ remove_peer( fd_http_resolver_t * resolver, ulong idx ) { FD_TEST( idxfds_len ); - /* FIXME: These sockets should be closed at the correct location */ - close( resolver->fds[ idx ].fd ); - if( FD_LIKELY( -1!=resolver->fds[ idx+1UL ].fd ) ) close( resolver->fds[ idx+1UL ].fd ); + fd_ssresolve_peer_t * cur_peer = peer_pool_ele( resolver->pool, resolver->fds_idx[ idx ] ); + fd_ssresolve_cancel( cur_peer->full_ssresolve ); + fd_ssresolve_cancel( cur_peer->inc_ssresolve ); if( FD_UNLIKELY( resolver->fds_len==2UL ) ) { resolver->fds_len = 0UL; @@ -306,6 +354,7 @@ poll_resolve( fd_http_resolver_t * resolver, fd_ssresolve_t * ssresolve, ulong idx, long now ) { + FD_TEST( !fd_ssresolve_is_done( ssresolve ) ); if( FD_LIKELY( pfd->revents & POLLOUT ) ) { int res = fd_ssresolve_advance_poll_out( ssresolve ); @@ -324,7 +373,7 @@ poll_resolve( fd_http_resolver_t * resolver, return -1; } else if( FD_UNLIKELY( res==FD_SSRESOLVE_ADVANCE_AGAIN ) ) { return -1; - } else { /* FD_SSRESOLVE_ADVANCE_SUCCESS */ + } else if( FD_LIKELY( res==FD_SSRESOLVE_ADVANCE_RESULT ) ) { FD_TEST( peer->deadline_nanos>now ); if( resolve_result.base_slot==ULONG_MAX ) { diff --git a/src/discof/restore/utils/fd_http_resolver.h b/src/discof/restore/utils/fd_http_resolver.h index bc9461a4bfa..08738c4fa86 100644 --- a/src/discof/restore/utils/fd_http_resolver.h +++ b/src/discof/restore/utils/fd_http_resolver.h @@ -36,7 +36,9 @@ fd_http_resolver_new( void * shmem, be unique. */ void fd_http_resolver_add( fd_http_resolver_t * resolver, - fd_ip4_port_t addr ); + fd_ip4_port_t addr, + char const * hostname, + int is_https ); fd_http_resolver_t * fd_http_resolver_join( void * shresolve ); diff --git a/src/discof/restore/utils/fd_ssctrl.h b/src/discof/restore/utils/fd_ssctrl.h index f00f9653f0a..b95cc3d2d68 100644 --- a/src/discof/restore/utils/fd_ssctrl.h +++ b/src/discof/restore/utils/fd_ssctrl.h @@ -88,6 +88,8 @@ typedef struct fd_ssctrl_init { int file; int zstd; fd_ip4_port_t addr; + char hostname[ 256UL ]; + int is_https; } fd_ssctrl_init_t; /* Sent by snapld to tell snapct metadata about a downloaded snapshot. */ diff --git a/src/discof/restore/utils/fd_sshttp.c b/src/discof/restore/utils/fd_sshttp.c index 211a68e5ae3..3a6592f2fef 100644 --- a/src/discof/restore/utils/fd_sshttp.c +++ b/src/discof/restore/utils/fd_sshttp.c @@ -3,6 +3,7 @@ #include "fd_ssarchive.h" #include "../../../waltz/http/picohttpparser.h" +#include "../../../waltz/openssl/fd_openssl_tile.h" #include "../../../util/log/fd_log.h" #include "../../../flamenco/types/fd_types_custom.h" @@ -14,23 +15,39 @@ #include #include +#if FD_HAS_OPENSSL +#include +#endif + #define FD_SSHTTP_MAGIC (0xF17EDA2CE5811900) /* FIREDANCE HTTP V0 */ -#define FD_SSHTTP_STATE_INIT (0) /* start */ -#define FD_SSHTTP_STATE_REQ (1) /* sending request */ -#define FD_SSHTTP_STATE_RESP (2) /* receiving response headers */ -#define FD_SSHTTP_STATE_DL (3) /* downloading response body */ +#define FD_SSHTTP_STATE_INIT (0) /* start */ +#define FD_SSHTTP_STATE_CONNECT (1) /* connecting ssl */ +#define FD_SSHTTP_STATE_REQ (2) /* sending request */ +#define FD_SSHTTP_STATE_RESP (3) /* receiving response headers */ +#define FD_SSHTTP_STATE_DL (4) /* downloading response body */ +#define FD_SSHTTP_STATE_SHUTTING_DOWN (5) /* shutting down ssl */ +#define FD_SSHTTP_STATE_REDIRECT (6) /* redirect after shutting down ssl */ +#define FD_SSHTTP_STATE_DONE (7) /* done */ + +#define FD_SSHTTP_DEADLINE_NANOS (1L*1000L*1000L*1000L) /* 1 second */ /* FIXME: Cleanup / standardize all the error logging. */ struct fd_sshttp_private { int state; + int next_state; /* used for state transitions in https connection */ long deadline; ulong empty_recvs; int hops; + char location[ PATH_MAX ]; + ulong location_len; + fd_ip4_port_t addr; + char const * hostname; + int is_https; int sockfd; char request[ 4096UL ]; @@ -42,6 +59,11 @@ struct fd_sshttp_private { char snapshot_name[ PATH_MAX ]; +#if FD_HAS_OPENSSL + SSL_CTX * ssl_ctx; + SSL * ssl; +#endif + ulong content_len; ulong content_read; @@ -80,8 +102,27 @@ fd_sshttp_new( void * shmem ) { sshttp->content_len = 0UL; fd_cstr_fini( sshttp->snapshot_name ); +#if FD_HAS_OPENSSL + sshttp->ssl = NULL; + sshttp->ssl_ctx = NULL; + + SSL_CTX * ssl_ctx = SSL_CTX_new( TLS_client_method() ); + if( FD_UNLIKELY( !ssl_ctx ) ) { + FD_LOG_ERR(( "SSL_CTX_new failed" )); + } + + if( FD_UNLIKELY( !SSL_CTX_set_min_proto_version( ssl_ctx, TLS1_3_VERSION ) ) ) { + FD_LOG_ERR(( "SSL_CTX_set_min_proto_version(ssl_ctx,TLS1_3_VERSION) failed" )); + } + + /* transfering ownership of ssl_ctx by assignment */ + sshttp->ssl_ctx = ssl_ctx; + + fd_ossl_load_certs( sshttp->ssl_ctx ); +#endif + FD_COMPILER_MFENCE(); - FD_VOLATILE( sshttp->magic ) = FD_SSHTTP_MAGIC; + sshttp->magic = FD_SSHTTP_MAGIC; FD_COMPILER_MFENCE(); return (void *)sshttp; @@ -109,31 +150,78 @@ fd_sshttp_join( void * shhttp ) { return sshttp; } +#if FD_HAS_OPENSSL +static void +http_init_ssl( fd_sshttp_t * http ) { + FD_TEST( http->hostname ); + FD_TEST( http->ssl_ctx ); + + http->ssl = SSL_new( http->ssl_ctx ); + if( FD_UNLIKELY( !http->ssl ) ) { + FD_LOG_ERR(( "SSL_new failed" )); + } + + static uchar const alpn_protos[] = { 8, 'h', 't', 't', 'p', '/', '1', '.', '1' }; + int alpn_res = SSL_set_alpn_protos( http->ssl, alpn_protos, sizeof(alpn_protos) ); + if( FD_UNLIKELY( alpn_res!=0 ) ) { + FD_LOG_ERR(( "SSL_set_alpn_protos failed (%d)", alpn_res )); + } + + /* set SNI */ + int set1_host_res = SSL_set1_host( http->ssl, http->hostname ); + if( FD_UNLIKELY( !set1_host_res ) ) { + FD_LOG_ERR(( "SSL_set1_host failed (%d)", set1_host_res )); + } +} +#endif + void fd_sshttp_init( fd_sshttp_t * http, fd_ip4_port_t addr, + char const * hostname, + int is_https, char const * path, ulong path_len, long now ) { FD_TEST( http->state==FD_SSHTTP_STATE_INIT ); - http->hops = 4UL; + http->hostname = hostname; + http->is_https = is_https; + + if( FD_LIKELY( is_https ) ) { +#if FD_HAS_OPENSSL + http_init_ssl( http ); +#else + FD_LOG_ERR(( "cannot make HTTPS connection without OpenSSL" )); +#endif + } + http->hops = 4UL; http->request_sent = 0UL; - FD_TEST( fd_cstr_printf_check( http->request, sizeof(http->request), &http->request_len, - "GET %.*s HTTP/1.1\r\n" - "User-Agent: Firedancer\r\n" - "Accept: */*\r\n" - "Accept-Encoding: identity\r\n" - "Host: " FD_IP4_ADDR_FMT "\r\n\r\n", - (int)path_len, path, FD_IP4_ADDR_FMT_ARGS( addr.addr ) ) ); + if( FD_LIKELY( is_https ) ) { + FD_TEST( fd_cstr_printf_check( http->request, sizeof(http->request), &http->request_len, + "GET %.*s HTTP/1.1\r\n" + "User-Agent: Firedancer\r\n" + "Accept: */*\r\n" + "Accept-Encoding: identity\r\n" + "Host: %s\r\n\r\n", + (int)path_len, path, hostname ) ); + } else { + FD_TEST( fd_cstr_printf_check( http->request, sizeof(http->request), &http->request_len, + "GET %.*s HTTP/1.1\r\n" + "User-Agent: Firedancer\r\n" + "Accept: */*\r\n" + "Accept-Encoding: identity\r\n" + "Host: " FD_IP4_ADDR_FMT "\r\n\r\n", + (int)path_len, path, FD_IP4_ADDR_FMT_ARGS( addr.addr ) ) ); + } http->response_len = 0UL; http->content_len = 0UL; http->content_read = 0UL; http->empty_recvs = 0UL; - http->addr = addr; + http->addr = addr; http->sockfd = socket( AF_INET, SOCK_STREAM|SOCK_NONBLOCK, 0 ); if( FD_UNLIKELY( -1==http->sockfd ) ) FD_LOG_ERR(( "socket() failed (%d-%s)", errno, fd_io_strerror( errno ) )); @@ -149,10 +237,119 @@ fd_sshttp_init( fd_sshttp_t * http, } } + if( FD_LIKELY( is_https ) ) { + http->state = FD_SSHTTP_STATE_CONNECT; + http->deadline = now + FD_SSHTTP_DEADLINE_NANOS; + } else { + http->state = FD_SSHTTP_STATE_REQ; + http->deadline = now + FD_SSHTTP_DEADLINE_NANOS; + } +} + +#if FD_HAS_OPENSSL +static int +http_connect_ssl( fd_sshttp_t * http, + long now ) { + if( FD_UNLIKELY( now>http->deadline ) ) { + FD_LOG_WARNING(("deadline exceeded during connect")); + fd_sshttp_cancel( http ); + return FD_SSHTTP_ADVANCE_ERROR; + } + + FD_TEST( http->ssl ); + SSL_set_fd( http->ssl, http->sockfd ); + int ssl_err = SSL_connect( http->ssl ); + if( FD_UNLIKELY( ssl_err!=1 ) ) { + int ssl_err_code = SSL_get_error( http->ssl, ssl_err ); + if( FD_UNLIKELY( ssl_err_code!=SSL_ERROR_WANT_READ && ssl_err_code!=SSL_ERROR_WANT_WRITE ) ) { + FD_LOG_WARNING(( "SSL_connect failed (%d)", ssl_err )); + SSL_free( http->ssl ); + http->ssl = NULL; + return FD_SSHTTP_ADVANCE_ERROR; + } + /* in progress */ + return FD_SSHTTP_ADVANCE_AGAIN; + } + http->state = FD_SSHTTP_STATE_REQ; - http->deadline = now + 500L*1000L*1000L; + http->deadline = now + FD_SSHTTP_DEADLINE_NANOS; + return FD_SSHTTP_ADVANCE_AGAIN; } +static int +http_shutdown_ssl( fd_sshttp_t * http, + long now ) { + if( FD_UNLIKELY( now>http->deadline ) ) { + FD_LOG_WARNING(("deadline exceeded during shutdown")); + fd_sshttp_cancel( http ); + return FD_SSHTTP_ADVANCE_ERROR; + } + + int res = SSL_shutdown( http->ssl ); + if( FD_LIKELY( res<=0 ) ) { + int ssl_err_code = SSL_get_error( http->ssl, res ); + if( FD_UNLIKELY( ssl_err_code!=SSL_ERROR_WANT_READ && ssl_err_code!=SSL_ERROR_WANT_WRITE && res!=0 ) ) { + FD_LOG_WARNING(( "SSL_shutdown failed (%d)", ssl_err_code )); + SSL_free( http->ssl ); + http->ssl = NULL; + return FD_SSHTTP_ADVANCE_ERROR; + } + + return FD_SSHTTP_ADVANCE_AGAIN; + } + + http->state = http->next_state; + return FD_SSHTTP_ADVANCE_AGAIN; +} + +static long +http_recv_ssl( fd_sshttp_t * http, + void * buf, + ulong bufsz ) { + int read_res = SSL_read( http->ssl, buf, (int)bufsz ); + if( FD_UNLIKELY( read_res<=0 ) ) { + int ssl_err = SSL_get_error( http->ssl, read_res ); + + if( FD_UNLIKELY( ssl_err!=SSL_ERROR_WANT_READ && ssl_err!=SSL_ERROR_WANT_WRITE ) ) { + FD_LOG_WARNING(( "SSL_read failed (%d)", ssl_err )); + return FD_SSHTTP_ADVANCE_ERROR; + } + + return FD_SSHTTP_ADVANCE_AGAIN; + } + + return (long)read_res; +} + +static long +http_send_ssl( fd_sshttp_t * http, + void * buf, + ulong bufsz ) { + int write_res = SSL_write( http->ssl, buf, (int)bufsz ); + if( FD_UNLIKELY( write_res<=0 ) ) { + int ssl_err = SSL_get_error( http->ssl, write_res ); + + if( FD_UNLIKELY( ssl_err!=SSL_ERROR_WANT_READ && ssl_err!=SSL_ERROR_WANT_WRITE ) ) { + FD_LOG_WARNING(( "SSL_write failed (%d)", ssl_err )); + return FD_SSHTTP_ADVANCE_ERROR; + } + + return FD_SSHTTP_ADVANCE_AGAIN; + } + + return (long)write_res; +} + +static int +setup_redirect( fd_sshttp_t * http, + long now ) { + fd_sshttp_cancel( http ); + fd_sshttp_init( http, http->addr, http->hostname, http->is_https, http->location, http->location_len, now ); + return FD_SSHTTP_ADVANCE_AGAIN; +} + +#endif + void fd_sshttp_cancel( fd_sshttp_t * http ) { if( FD_LIKELY( http->state!=FD_SSHTTP_STATE_INIT && -1!=http->sockfd ) ) { @@ -160,6 +357,64 @@ fd_sshttp_cancel( fd_sshttp_t * http ) { http->sockfd = -1; } http->state = FD_SSHTTP_STATE_INIT; + +#if FD_HAS_OPENSSL + if( FD_LIKELY( http->ssl ) ) { + SSL_free( http->ssl ); + http->ssl = NULL; + } +#endif +} + +static long +http_send( fd_sshttp_t * http, + void * buf, + ulong bufsz ) { +#if FD_HAS_OPENSSL + if( FD_LIKELY( http->is_https ) ) return http_send_ssl( http, buf, bufsz ); +#endif + + long sent = sendto( http->sockfd, buf, bufsz, 0, NULL, 0 ); + if( FD_UNLIKELY( -1==sent && errno==EAGAIN ) ) return FD_SSHTTP_ADVANCE_AGAIN; + else if( FD_UNLIKELY( -1==sent ) ) { + FD_LOG_WARNING(( "sendto() failed (%d-%s)", errno, fd_io_strerror( errno ) )); + fd_sshttp_cancel( http ); + return FD_SSHTTP_ADVANCE_ERROR; + } + + return sent; +} + +static long +http_recv( fd_sshttp_t * http, + void * buf, + ulong bufsz ) { +#if FD_HAS_OPENSSL + if( FD_LIKELY( http->is_https ) ) return http_recv_ssl( http, buf, bufsz ); +#endif + + long read = recvfrom( http->sockfd, buf, bufsz, 0, NULL, NULL ); + if( FD_UNLIKELY( -1==read && errno==EAGAIN ) ) { + if( FD_UNLIKELY( ++http->empty_recvs>8UL ) ) { + /* If we have gone several iterations without having any data to + read, sleep the thread for up to one millisecond, or until + the socket is readable again, whichever comes first. */ + struct pollfd pfd = { + .fd = http->sockfd, + .events = POLLIN, + }; + if( -1==fd_syscall_poll( &pfd, 1 /*fds*/, 1 /*ms*/ ) ) { + FD_LOG_ERR(("fd_syscall_poll() failed (%i-%s)", errno, fd_io_strerror( errno ) )); + } + } + return FD_SSHTTP_ADVANCE_AGAIN; + } else if( FD_UNLIKELY( -1==read ) ) { + fd_sshttp_cancel( http ); + return FD_SSHTTP_ADVANCE_ERROR; + } + http->empty_recvs = 0UL; + + return read; } static int @@ -170,18 +425,14 @@ send_request( fd_sshttp_t * http, return FD_SSHTTP_ADVANCE_ERROR; } - long sent = sendto( http->sockfd, http->request+http->request_sent, http->request_len-http->request_sent, 0, NULL, 0 ); - if( FD_UNLIKELY( -1==sent && errno==EAGAIN ) ) return FD_SSHTTP_ADVANCE_AGAIN; - else if( FD_UNLIKELY( -1==sent ) ) { - fd_sshttp_cancel( http ); - return FD_SSHTTP_ADVANCE_ERROR; - } + long sent = http_send( http, http->request+http->request_sent, http->request_len-http->request_sent ); + if( FD_UNLIKELY( sent<=0 ) ) return (int)sent; http->request_sent += (ulong)sent; if( FD_UNLIKELY( http->request_sent==http->request_len ) ) { - http->state = FD_SSHTTP_STATE_RESP; + http->state = FD_SSHTTP_STATE_RESP; http->response_len = 0UL; - http->deadline = now + 500L*1000L*1000L; + http->deadline = now + FD_SSHTTP_DEADLINE_NANOS; } return FD_SSHTTP_ADVANCE_AGAIN; @@ -264,12 +515,21 @@ follow_redirect( fd_sshttp_t * http, return FD_SSHTTP_ADVANCE_ERROR; } - FD_LOG_NOTICE(( "following redirect to http://" FD_IP4_ADDR_FMT ":%hu%.*s", - FD_IP4_ADDR_FMT_ARGS( http->addr.addr ), fd_ushort_bswap( http->addr.port ), + FD_LOG_NOTICE(( "following redirect to %s://" FD_IP4_ADDR_FMT ":%hu%.*s", + http->is_https ? "https" : "http", FD_IP4_ADDR_FMT_ARGS( http->addr.addr ), fd_ushort_bswap( http->addr.port ), (int)location_len, location )); - fd_sshttp_cancel( http ); - fd_sshttp_init( http, http->addr, location, location_len, now ); + if( FD_UNLIKELY( http->is_https ) ) { + http->next_state = FD_SSHTTP_STATE_REDIRECT; + http->state = FD_SSHTTP_STATE_SHUTTING_DOWN; + http->location_len = location_len; + FD_TEST( location_lenlocation, location, location_len ); + http->location[ location_len ] = '\0'; + } else { + fd_sshttp_cancel( http ); + fd_sshttp_init( http, http->addr, http->hostname, http->is_https, location, location_len, now ); + } return FD_SSHTTP_ADVANCE_AGAIN; } @@ -285,13 +545,8 @@ read_response( fd_sshttp_t * http, return FD_SSHTTP_ADVANCE_ERROR; } - long read = recvfrom( http->sockfd, http->response+http->response_len, sizeof(http->response)-http->response_len, 0, NULL, NULL ); - if( FD_UNLIKELY( -1==read && errno==EAGAIN ) ) return FD_SSHTTP_ADVANCE_AGAIN; - else if( FD_UNLIKELY( -1==read ) ) { - FD_LOG_WARNING(( "recv() failed (%d-%s)", errno, fd_io_strerror( errno ) )); - fd_sshttp_cancel( http ); - return FD_SSHTTP_ADVANCE_ERROR; - } + long read = http_recv( http, http->response+http->response_len, sizeof(http->response)-http->response_len ); + if( FD_UNLIKELY( read<=0 ) ) return (int)read; http->response_len += (ulong)read; @@ -362,32 +617,26 @@ read_response( fd_sshttp_t * http, static int read_body( fd_sshttp_t * http, ulong * data_len, - uchar * data ) { + uchar * data, + long now ) { /* FIXME: Add a forward-progress timeout */ if( FD_UNLIKELY( http->content_read>=http->content_len ) ) { - fd_sshttp_cancel( http ); - return FD_SSHTTP_ADVANCE_DONE; - } - - long read = recvfrom( http->sockfd, data, fd_ulong_min( *data_len, http->content_len-http->content_read ), 0, NULL, NULL ); - if( FD_UNLIKELY( -1==read && errno==EAGAIN ) ) { - if( FD_UNLIKELY( ++http->empty_recvs>8UL ) ) { - /* If we have gone several iterations without having any data to - read, sleep the thread for up to one millisecond, or until - the socket is readable again, whichever comes first. */ - struct pollfd pfd = { - .fd = http->sockfd, - .events = POLLIN, - }; - fd_syscall_poll( &pfd, 1 /*fds*/, 1 /*ms*/ ); + if( FD_UNLIKELY( http->is_https ) ) { + http->next_state = FD_SSHTTP_STATE_DONE; + http->state = FD_SSHTTP_STATE_SHUTTING_DOWN; + http->deadline = now + FD_SSHTTP_DEADLINE_NANOS; + return FD_SSHTTP_ADVANCE_AGAIN; + } else { + fd_sshttp_cancel( http ); + http->state = FD_SSHTTP_STATE_INIT; + return FD_SSHTTP_ADVANCE_DONE; } - return FD_SSHTTP_ADVANCE_AGAIN; - } else if( FD_UNLIKELY( -1==read ) ) { - fd_sshttp_cancel( http ); - return FD_SSHTTP_ADVANCE_ERROR; } - http->empty_recvs = 0UL; + + FD_TEST( http->content_readcontent_len ); + long read = http_recv( http, data, fd_ulong_min( *data_len, http->content_len-http->content_read ) ); + if( FD_UNLIKELY( read<=0 ) ) return (int)read; if( FD_UNLIKELY( !read ) ) return FD_SSHTTP_ADVANCE_AGAIN; @@ -413,10 +662,19 @@ fd_sshttp_advance( fd_sshttp_t * http, uchar * data, long now ) { switch( http->state ) { - case FD_SSHTTP_STATE_INIT: return FD_SSHTTP_ADVANCE_ERROR; - case FD_SSHTTP_STATE_REQ: return send_request( http, now ); - case FD_SSHTTP_STATE_RESP: return read_response( http, data_len, data, now ); - case FD_SSHTTP_STATE_DL: return read_body( http, data_len, data ); - default: return FD_SSHTTP_ADVANCE_ERROR; + case FD_SSHTTP_STATE_INIT: return FD_SSHTTP_ADVANCE_AGAIN; +#if FD_HAS_OPENSSL + case FD_SSHTTP_STATE_CONNECT: return http_connect_ssl( http, now ); + case FD_SSHTTP_STATE_SHUTTING_DOWN: return http_shutdown_ssl( http, now ); + case FD_SSHTTP_STATE_REDIRECT: return setup_redirect( http, now ); +#endif + case FD_SSHTTP_STATE_REQ: return send_request( http, now ); + case FD_SSHTTP_STATE_RESP: return read_response( http, data_len, data, now ); + case FD_SSHTTP_STATE_DL: return read_body( http, data_len, data, now ); + case FD_SSHTTP_STATE_DONE: + fd_sshttp_cancel( http ); + http->state = FD_SSHTTP_STATE_INIT; + return FD_SSHTTP_ADVANCE_DONE; + default: return FD_SSHTTP_ADVANCE_ERROR; } } diff --git a/src/discof/restore/utils/fd_sshttp.h b/src/discof/restore/utils/fd_sshttp.h index 83130d26cf7..2cf91a26673 100644 --- a/src/discof/restore/utils/fd_sshttp.h +++ b/src/discof/restore/utils/fd_sshttp.h @@ -29,6 +29,8 @@ fd_sshttp_content_len( fd_sshttp_t const * http ); void fd_sshttp_init( fd_sshttp_t * http, fd_ip4_port_t addr, + char const * hostname, + int is_https, char const * path, ulong path_len, long now ); diff --git a/src/discof/restore/utils/fd_sspeer_selector.c b/src/discof/restore/utils/fd_sspeer_selector.c index e253ccb4b9c..336ff12bc78 100644 --- a/src/discof/restore/utils/fd_sspeer_selector.c +++ b/src/discof/restore/utils/fd_sspeer_selector.c @@ -319,9 +319,9 @@ fd_sspeer_selector_best( fd_sspeer_selector_t * selector, (!incremental || (incremental && peer->ssinfo.incremental.base_slot==base_slot) ) ) ) { return (fd_sspeer_t){ - .addr = peer->addr, - .ssinfo = peer->ssinfo, - .score = peer->score, + .addr = peer->addr, + .ssinfo = peer->ssinfo, + .score = peer->score, }; } } diff --git a/src/discof/restore/utils/fd_ssresolve.c b/src/discof/restore/utils/fd_ssresolve.c index 40733f73846..c1a04d476ae 100644 --- a/src/discof/restore/utils/fd_ssresolve.c +++ b/src/discof/restore/utils/fd_ssresolve.c @@ -13,9 +13,14 @@ #include #include -#define FD_SSRESOLVE_STATE_REQ (0) /* sending request for snapshot */ -#define FD_SSRESOLVE_STATE_RESP (1) /* receiving snapshot response */ -#define FD_SSRESOLVE_STATE_DONE (2) /* done */ +/* TODO: consider refactoring the common http code in ssresolve and + sshttp into a common library */ + +#define FD_SSRESOLVE_CONNECT (0) /* connecting ssl */ +#define FD_SSRESOLVE_STATE_REQ (1) /* sending request for snapshot */ +#define FD_SSRESOLVE_STATE_RESP (2) /* receiving snapshot response */ +#define FD_SSRESOLVE_STATE_SHUTTING_DOWN (3) /* shutting down ssl */ +#define FD_SSRESOLVE_STATE_DONE (4) /* done */ struct fd_ssresolve_private { int state; @@ -24,6 +29,8 @@ struct fd_ssresolve_private { fd_ip4_port_t addr; int sockfd; int full; + int is_https; + char const * hostname; char request[ 4096UL ]; ulong request_sent; @@ -32,6 +39,10 @@ struct fd_ssresolve_private { ulong response_len; char response[ USHORT_MAX ]; +#if FD_HAS_OPENSSL + SSL * ssl; +#endif + ulong magic; }; @@ -67,6 +78,11 @@ fd_ssresolve_new( void * shmem ) { ssresolve->request_sent = 0UL; ssresolve->request_len = 0UL; ssresolve->response_len = 0UL; + ssresolve->sockfd = -1; + +#if FD_HAS_OPENSSL + ssresolve->ssl = NULL; +#endif FD_COMPILER_MFENCE(); FD_VOLATILE( ssresolve->magic ) = FD_SSRESOLVE_MAGIC; @@ -110,27 +126,86 @@ fd_ssresolve_init( fd_ssresolve_t * ssresolve, ssresolve->request_sent = 0UL; ssresolve->request_len = 0UL; ssresolve->response_len = 0UL; + ssresolve->is_https = 0; } +#if FD_HAS_OPENSSL +void +fd_ssresolve_init_https( fd_ssresolve_t * ssresolve, + fd_ip4_port_t addr, + int sockfd, + int full, + char const * hostname, + SSL_CTX * ssl_ctx ) { + ssresolve->addr = addr; + ssresolve->sockfd = sockfd; + ssresolve->full = full; + + ssresolve->state = FD_SSRESOLVE_CONNECT; + ssresolve->request_sent = 0UL; + ssresolve->request_len = 0UL; + ssresolve->response_len = 0UL; + ssresolve->is_https = 1; + ssresolve->hostname = hostname; + + ssresolve->ssl = SSL_new( ssl_ctx ); + if( FD_UNLIKELY( !ssresolve->ssl ) ) { + FD_LOG_ERR(( "SSL_new failed" )); + } + + static uchar const alpn_protos[] = { 8, 'h', 't', 't', 'p', '/', '1', '.', '1' }; + int alpn_res = SSL_set_alpn_protos( ssresolve->ssl, alpn_protos, sizeof(alpn_protos) ); + if( FD_UNLIKELY( alpn_res!=0 ) ) { + FD_LOG_ERR(( "SSL_set_alpn_protos failed (%d)", alpn_res )); + } + + /* set SNI */ + FD_TEST( hostname && hostname[ 0 ]!='\0' ); + int set1_host_res = SSL_set1_host( ssresolve->ssl, hostname ); + if( FD_UNLIKELY( !set1_host_res ) ) { + FD_LOG_ERR(( "SSL_set1_host failed (%d)", set1_host_res )); + } +} +#endif + static void -fd_ssresolve_render_req( fd_ssresolve_t * ssresolve, - fd_ip4_port_t addr ) { +fd_ssresolve_render_req( fd_ssresolve_t * ssresolve ) { if( FD_LIKELY( ssresolve->full ) ) { - FD_TEST( fd_cstr_printf_check( ssresolve->request, sizeof(ssresolve->request), &ssresolve->request_len, - "GET /snapshot.tar.bz2 HTTP/1.1\r\n" + if( FD_UNLIKELY( ssresolve->is_https ) ) { + FD_TEST( fd_cstr_printf_check( ssresolve->request, sizeof(ssresolve->request), &ssresolve->request_len, + "HEAD /snapshot.tar.bz2 HTTP/1.1\r\n" + "User-Agent: Firedancer\r\n" + "Accept: */*\r\n" + "Accept-Encoding: identity\r\n" + "Host: %s\r\n\r\n", + ssresolve->hostname ) ); + } else { + FD_TEST( fd_cstr_printf_check( ssresolve->request, sizeof(ssresolve->request), &ssresolve->request_len, + "HEAD /snapshot.tar.bz2 HTTP/1.1\r\n" "User-Agent: Firedancer\r\n" "Accept: */*\r\n" "Accept-Encoding: identity\r\n" "Host: " FD_IP4_ADDR_FMT "\r\n\r\n", - FD_IP4_ADDR_FMT_ARGS( addr.addr ) ) ); + FD_IP4_ADDR_FMT_ARGS( ssresolve->addr.addr ) ) ); + } } else { - FD_TEST( fd_cstr_printf_check( ssresolve->request, sizeof(ssresolve->request), &ssresolve->request_len, - "GET /incremental-snapshot.tar.bz2 HTTP/1.1\r\n" + if( FD_UNLIKELY( ssresolve->is_https ) ) { + FD_TEST( fd_cstr_printf_check( ssresolve->request, sizeof(ssresolve->request), &ssresolve->request_len, + "HEAD /incremental-snapshot.tar.bz2 HTTP/1.1\r\n" + "User-Agent: Firedancer\r\n" + "Accept: */*\r\n" + "Accept-Encoding: identity\r\n" + "Host: %s\r\n\r\n", + ssresolve->hostname ) ); + } else { + FD_TEST( fd_cstr_printf_check( ssresolve->request, sizeof(ssresolve->request), &ssresolve->request_len, + "HEAD /incremental-snapshot.tar.bz2 HTTP/1.1\r\n" "User-Agent: Firedancer\r\n" "Accept: */*\r\n" "Accept-Encoding: identity\r\n" "Host: " FD_IP4_ADDR_FMT "\r\n\r\n", - FD_IP4_ADDR_FMT_ARGS( addr.addr ) ) ); + FD_IP4_ADDR_FMT_ARGS( ssresolve->addr.addr ) ) ); + } } } @@ -139,13 +214,32 @@ fd_ssresolve_send_request( fd_ssresolve_t * ssresolve ) { FD_TEST( ssresolve->state==FD_SSRESOLVE_STATE_REQ ); if( FD_UNLIKELY( !ssresolve->request_len ) ) { - fd_ssresolve_render_req( ssresolve, ssresolve->addr ); + fd_ssresolve_render_req( ssresolve ); } - long sent = sendto( ssresolve->sockfd, ssresolve->request+ssresolve->request_sent, ssresolve->request_len-ssresolve->request_sent, 0, NULL, 0 ); - if( FD_UNLIKELY( -1==sent && errno==EAGAIN ) ) return FD_SSRESOLVE_ADVANCE_AGAIN; - else if( FD_UNLIKELY( -1==sent ) ) { - return FD_SSRESOLVE_ADVANCE_ERROR; + long sent = 0L; + if( FD_LIKELY( ssresolve->is_https ) ) { +#if FD_HAS_OPENSSL + int write_res = SSL_write( ssresolve->ssl, ssresolve->request+ssresolve->request_sent, (int)(ssresolve->request_len-ssresolve->request_sent) ); + if( FD_UNLIKELY( write_res<=0 ) ) { + int ssl_err = SSL_get_error( ssresolve->ssl, write_res ); + + if( FD_UNLIKELY( ssl_err!=SSL_ERROR_WANT_READ && ssl_err!=SSL_ERROR_WANT_WRITE ) ) { + FD_LOG_WARNING(( "SSL_write failed (%d)", ssl_err )); + return FD_SSRESOLVE_ADVANCE_ERROR; + } + + return FD_SSRESOLVE_ADVANCE_AGAIN; + } + + sent = (long)write_res; +#else + FD_LOG_ERR(( "cannot use HTTPS without OpenSSL" )); +#endif + } else { + sent = sendto( ssresolve->sockfd, ssresolve->request+ssresolve->request_sent, ssresolve->request_len-ssresolve->request_sent, 0, NULL, 0 ); + if( FD_UNLIKELY( -1==sent && errno==EAGAIN ) ) return FD_SSRESOLVE_ADVANCE_AGAIN; + else if( FD_UNLIKELY( -1==sent ) ) return FD_SSRESOLVE_ADVANCE_ERROR; } ssresolve->request_sent += (ulong)sent; @@ -202,19 +296,42 @@ fd_ssresolve_parse_redirect( fd_ssresolve_t * ssresolve, result->base_slot = full_entry_slot; } - ssresolve->state = FD_SSRESOLVE_STATE_DONE; - return FD_SSRESOLVE_ADVANCE_SUCCESS; + if( FD_UNLIKELY( ssresolve->is_https ) ) ssresolve->state = FD_SSRESOLVE_STATE_SHUTTING_DOWN; + else ssresolve->state = FD_SSRESOLVE_STATE_DONE; + return FD_SSRESOLVE_ADVANCE_RESULT; } static int fd_ssresolve_read_response( fd_ssresolve_t * ssresolve, fd_ssresolve_result_t * result ) { FD_TEST( ssresolve->state==FD_SSRESOLVE_STATE_RESP ); - long read = recvfrom( ssresolve->sockfd, ssresolve->response+ssresolve->response_len, sizeof(ssresolve->response)-ssresolve->response_len, 0, NULL, NULL ); - if( FD_UNLIKELY( -1==read && errno==EAGAIN ) ) return FD_SSRESOLVE_ADVANCE_AGAIN; - else if( FD_UNLIKELY( -1==read ) ) { - FD_LOG_WARNING(( "recvfrom() failed (%d-%s)", errno, fd_io_strerror( errno ) )); - return FD_SSRESOLVE_ADVANCE_ERROR; + + long read = 0L; + if( FD_LIKELY( ssresolve->is_https ) ) { +#if FD_HAS_OPENSSL + int read_res = SSL_read( ssresolve->ssl, ssresolve->response+ssresolve->response_len, (int)(sizeof(ssresolve->response)-ssresolve->response_len) ); + if( FD_UNLIKELY( read_res<=0 ) ) { + int ssl_err = SSL_get_error( ssresolve->ssl, read_res ); + + if( FD_UNLIKELY( ssl_err!=SSL_ERROR_WANT_READ && ssl_err!=SSL_ERROR_WANT_WRITE ) ) { + FD_LOG_WARNING(( "SSL_read failed (%d)", ssl_err )); + return FD_SSRESOLVE_ADVANCE_ERROR; + } + + return FD_SSRESOLVE_ADVANCE_AGAIN; + } + + read = (long)read_res; +#else + FD_LOG_ERR(( "cannot use HTTPS without OpenSSL" )); +#endif + } else { + read = recvfrom( ssresolve->sockfd, ssresolve->response+ssresolve->response_len, sizeof(ssresolve->response)-ssresolve->response_len, 0, NULL, NULL ); + if( FD_UNLIKELY( -1==read && errno==EAGAIN ) ) return FD_SSRESOLVE_ADVANCE_AGAIN; + else if( FD_UNLIKELY( -1==read ) ) { + FD_LOG_WARNING(( "recvfrom() failed (%d-%s)", errno, fd_io_strerror( errno ) )); + return FD_SSRESOLVE_ADVANCE_ERROR; + } } ssresolve->response_len += (ulong)read; @@ -254,10 +371,62 @@ fd_ssresolve_read_response( fd_ssresolve_t * ssresolve, return FD_SSRESOLVE_ADVANCE_ERROR; } +#if FD_HAS_OPENSSL +static int +ssresolve_connect_ssl( fd_ssresolve_t * ssresolve ) { + FD_TEST( ssresolve->ssl ); + SSL_set_fd( ssresolve->ssl, ssresolve->sockfd ); + int ssl_err = SSL_connect( ssresolve->ssl ); + if( FD_UNLIKELY( ssl_err!=1 ) ) { + int ssl_err_code = SSL_get_error( ssresolve->ssl, ssl_err ); + if( FD_UNLIKELY( ssl_err_code!=SSL_ERROR_WANT_READ && ssl_err_code!=SSL_ERROR_WANT_WRITE ) ) { + FD_LOG_WARNING(( "SSL_connect failed (%d)", ssl_err_code )); + SSL_free( ssresolve->ssl ); + ssresolve->ssl = NULL; + return FD_SSRESOLVE_ADVANCE_ERROR; + } + /* in progress */ + return FD_SSRESOLVE_ADVANCE_AGAIN; + } + + ssresolve->state = FD_SSRESOLVE_STATE_REQ; + return FD_SSRESOLVE_ADVANCE_AGAIN; +} + +static int +ssresolve_shutdown_ssl( fd_ssresolve_t * ssresolve ) { + int res = SSL_shutdown( ssresolve->ssl ); + if( FD_LIKELY( res<=0 ) ) { + int ssl_err_code = SSL_get_error( ssresolve->ssl, res ); + if( FD_UNLIKELY( ssl_err_code!=SSL_ERROR_WANT_READ && ssl_err_code!=SSL_ERROR_WANT_WRITE && res!=0 ) ) { + FD_LOG_WARNING(( "SSL_shutdown failed (%d)", ssl_err_code )); + SSL_free( ssresolve->ssl ); + ssresolve->ssl = NULL; + return FD_SSRESOLVE_ADVANCE_ERROR; + } + + return FD_SSRESOLVE_ADVANCE_AGAIN; + } + + ssresolve->state = FD_SSRESOLVE_STATE_DONE; + return FD_SSRESOLVE_ADVANCE_SUCCESS; +} +#endif + int fd_ssresolve_advance_poll_out( fd_ssresolve_t * ssresolve ) { int res; switch( ssresolve->state ) { +#if FD_HAS_OPENSSL + case FD_SSRESOLVE_CONNECT: { + res = ssresolve_connect_ssl( ssresolve ); + break; + } + case FD_SSRESOLVE_STATE_SHUTTING_DOWN: { + res = ssresolve_shutdown_ssl( ssresolve ); + break; + } +#endif case FD_SSRESOLVE_STATE_REQ: { res = fd_ssresolve_send_request( ssresolve ); break; @@ -279,6 +448,16 @@ fd_ssresolve_advance_poll_in( fd_ssresolve_t * ssresolve, fd_ssresolve_result_t * result ) { int res; switch( ssresolve->state ) { +#if FD_HAS_OPENSSL + case FD_SSRESOLVE_CONNECT: { + res = ssresolve_connect_ssl( ssresolve ); + break; + } + case FD_SSRESOLVE_STATE_SHUTTING_DOWN: { + res = ssresolve_shutdown_ssl( ssresolve ); + break; + } +#endif case FD_SSRESOLVE_STATE_RESP: { res = fd_ssresolve_read_response( ssresolve, result ); break; @@ -287,6 +466,10 @@ fd_ssresolve_advance_poll_in( fd_ssresolve_t * ssresolve, res = FD_SSRESOLVE_ADVANCE_AGAIN; break; } + case FD_SSRESOLVE_STATE_DONE: { + res = FD_SSRESOLVE_ADVANCE_SUCCESS; + break; + } default: { FD_LOG_ERR(( "unexpected state %d", ssresolve->state )); return FD_SSRESOLVE_ADVANCE_ERROR; @@ -300,3 +483,17 @@ int fd_ssresolve_is_done( fd_ssresolve_t * ssresolve ) { return ssresolve->state==FD_SSRESOLVE_STATE_DONE; } + +void +fd_ssresolve_cancel( fd_ssresolve_t * ssresolve ) { + if( FD_LIKELY( ssresolve->sockfd!=-1 ) ) { + if( FD_UNLIKELY( -1==close( ssresolve->sockfd ) ) ) FD_LOG_ERR(( "close() failed (%i-%s)", errno, fd_io_strerror( errno ) )); + ssresolve->sockfd = -1; + } +#if FD_HAS_OPENSSL + if( FD_LIKELY( ssresolve->ssl ) ) { + SSL_free( ssresolve->ssl ); + ssresolve->ssl = NULL; + } +#endif +} diff --git a/src/discof/restore/utils/fd_ssresolve.h b/src/discof/restore/utils/fd_ssresolve.h index a5353a3003e..24238bb5e24 100644 --- a/src/discof/restore/utils/fd_ssresolve.h +++ b/src/discof/restore/utils/fd_ssresolve.h @@ -4,6 +4,10 @@ #include "../../../util/fd_util_base.h" #include "../../../util/net/fd_net_headers.h" +#if FD_HAS_OPENSSL +#include +#endif + #define FD_SSRESOLVE_MAGIC (0xF17EDA2CE55E510) /* FIREDANCER HTTP RESOLVE V0 */ #define FD_SSRESOLVE_ALIGN (8UL) @@ -42,9 +46,20 @@ fd_ssresolve_init( fd_ssresolve_t * ssresolve, int sockfd, int full ); +#if FD_HAS_OPENSSL +void +fd_ssresolve_init_https( fd_ssresolve_t * ssresolve, + fd_ip4_port_t addr, + int sockfd, + int full, + char const * hostname, + SSL_CTX * ssl_ctx ); +#endif + #define FD_SSRESOLVE_ADVANCE_ERROR (-1) /* fatal error */ #define FD_SSRESOLVE_ADVANCE_AGAIN ( 0) /* try again */ -#define FD_SSRESOLVE_ADVANCE_SUCCESS ( 1) /* success */ +#define FD_SSRESOLVE_ADVANCE_SUCCESS ( 1) /* successful advance */ +#define FD_SSRESOLVE_ADVANCE_RESULT ( 2) /* successful advance with valid resolve result */ /* fd_ssresolve_advance_poll_out advances the ssresolve state machine when its socket file descriptor is ready for sending data. */ @@ -63,6 +78,9 @@ fd_ssresolve_advance_poll_in( fd_ssresolve_t * ssresolve, int fd_ssresolve_is_done( fd_ssresolve_t * ssresolve ); +void +fd_ssresolve_cancel( fd_ssresolve_t * ssresolve ); + FD_PROTOTYPES_END #endif /* HEADER_fd_src_discof_restore_utils_fd_ssresolve_h */ diff --git a/src/discof/tower/fd_tower_tile.c b/src/discof/tower/fd_tower_tile.c index 422c8f11e23..da4b1274d5f 100644 --- a/src/discof/tower/fd_tower_tile.c +++ b/src/discof/tower/fd_tower_tile.c @@ -189,8 +189,7 @@ metrics_write( ctx_t * ctx ) { FD_MCNT_SET( TOWER, HARD_FORKS_SEEN, ctx->metrics.hard_forks.seen ); FD_MCNT_SET( TOWER, HARD_FORKS_PRUNED, ctx->metrics.hard_forks.pruned ); - FD_MGAUGE_SET( TOWER, HARD_FORKS_ACTIVE, ctx->metrics.hard_forks.active ); - FD_MGAUGE_SET( TOWER, HARD_FORKS_MAX_WIDTH, ctx->metrics.hard_forks.max_width ); + FD_MGAUGE_SET( TOWER, HARD_FORKS_ACTIVE, ctx->metrics.hard_forks.active ); } static void diff --git a/src/waltz/openssl/Local.mk b/src/waltz/openssl/Local.mk index 0bf978e681b..69ff56f6266 100644 --- a/src/waltz/openssl/Local.mk +++ b/src/waltz/openssl/Local.mk @@ -1,4 +1,5 @@ $(call add-hdrs,fd_openssl.h) ifdef FD_HAS_OPENSSL $(call add-objs,fd_openssl,fd_waltz) +$(call add-objs,fd_openssl_tile,fd_waltz) endif diff --git a/src/waltz/openssl/fd_openssl_tile.c b/src/waltz/openssl/fd_openssl_tile.c new file mode 100644 index 00000000000..4ab84c1b625 --- /dev/null +++ b/src/waltz/openssl/fd_openssl_tile.c @@ -0,0 +1,155 @@ +#if FD_HAS_OPENSSL +#include "../../util/bits/fd_bits.h" +#include "fd_openssl_tile.h" + +#include +#include +#include + +/* Thread-local alloc object for each tile that uses OpenSSL. */ +FD_TL fd_alloc_t * fd_ossl_alloc = NULL; +FD_TL ulong fd_ossl_alloc_errors = 0UL; + +/* OpenSSL tries to read files and allocate memory and other dumb things + on a thread local basis, so we need a special initializer process to + make OpenSSL use our custom allocators before seccomp kicks in. + + OpenSSL allows us to specify custom memory allocation functions, + which we want to point to an fd_alloc_t, but it does not let us use a + context object. Instead we stash it in this thread local, which is + OK because the parent workspace exists for the duration of the SSL + context, and the process only has one thread. + + Currently fd_alloc doesn't support realloc, so it's implemented on + top of malloc and free, and then also it doesn't support getting the + size of an allocation from the pointer, which we need for realloc, so + we pad each alloc by 8 bytes and stuff the size into the first 8 + bytes. */ + +static void * +crypto_malloc( ulong num, + char const * file, + int line ) { + (void)file; + (void)line; + void * result = fd_alloc_malloc( fd_ossl_alloc, 8UL, num + 8UL ); + if( FD_UNLIKELY( !result ) ) { + fd_ossl_alloc_errors++; + return NULL; + } + *(ulong*)result = num; + return (uchar*)result + 8UL; +} + +static void +crypto_free( void * addr, + char const * file, + int line ) { + (void)file; + (void)line; + + if( FD_UNLIKELY( !addr ) ) return; + fd_alloc_free( fd_ossl_alloc, (uchar*)addr - 8UL ); +} + +static void * +crypto_realloc( void * addr, + ulong num, + char const * file, + int line ) { + (void)file; + (void)line; + + if( FD_UNLIKELY( !addr ) ) return crypto_malloc( num, file, line ); + if( FD_UNLIKELY( !num ) ) { + crypto_free( addr, file, line ); + return NULL; + } + + void * new = fd_alloc_malloc( fd_ossl_alloc, 8UL, num + 8UL ); + if( FD_UNLIKELY( !new ) ) return NULL; + + ulong old_num = *(ulong*)( (uchar*)addr - 8UL ); + fd_memcpy( (uchar*)new + 8, (uchar*)addr, fd_ulong_min( old_num, num ) ); + fd_alloc_free( fd_ossl_alloc, (uchar*)addr - 8UL ); + *(ulong*)new = num; + return (uchar*)new + 8UL; +} + +void +fd_ossl_tile_init( fd_alloc_t * alloc ) { + /* OpenSSL's CRYPTO_set_mem_functions is a global operation so it can + only be called once for all threads/processes. */ + FD_ONCE_BEGIN { + if( FD_UNLIKELY( !CRYPTO_set_mem_functions( crypto_malloc, crypto_realloc, crypto_free ) ) ) { + FD_LOG_ERR(( "CRYPTO_set_mem_functions failed" )); + } + } FD_ONCE_END; + + FD_TEST( alloc ); + fd_ossl_alloc = alloc; + + FD_ONCE_BEGIN { + OPENSSL_init_ssl( + OPENSSL_INIT_LOAD_SSL_STRINGS | + OPENSSL_INIT_LOAD_CRYPTO_STRINGS | + OPENSSL_INIT_NO_LOAD_CONFIG, + NULL ); + } FD_ONCE_END; +} + +void +fd_ossl_load_certs( SSL_CTX * ssl_ctx ) { + X509_STORE * ca_certs = X509_STORE_new(); + if( FD_UNLIKELY( !ca_certs ) ) { + FD_LOG_ERR(( "X509_STORE_new failed" )); + } + + static char const default_dir[] = "/etc/ssl/certs/"; + DIR * dir = opendir( default_dir ); + if( FD_UNLIKELY( !dir ) ) { + FD_LOG_ERR(( "opendir(%s) failed (%i-%s)", default_dir, errno, fd_io_strerror( errno ) )); + } + + struct dirent * entry; + errno = 0; // clear old value since entry can be NULL when reaching end of directory. + while( (entry = readdir( dir )) ) { + if( !strcmp( entry->d_name, "." ) || !strcmp( entry->d_name, ".." ) ) continue; + + char cert_path[ PATH_MAX ]; + char * p = fd_cstr_init( cert_path ); + p = fd_cstr_append_text( p, default_dir, sizeof(default_dir)-1 ); + p = fd_cstr_append_cstr_safe( p, entry->d_name, (ulong)(cert_path+sizeof(cert_path)-1) - (ulong)p ); + fd_cstr_fini( p ); + + if( !X509_STORE_load_locations( ca_certs, cert_path, NULL ) ) { + /* Not all files in /etc/ssl/certs are valid certs, so ignore errors */ + continue; + } + errno = 0; + } + + if( FD_UNLIKELY( errno && errno!=ENOENT ) ) { + FD_LOG_ERR(( "readdir(%s) failed (%i-%s)", default_dir, errno, fd_io_strerror( errno ) )); + } + + STACK_OF(X509) * cert_list = X509_STORE_get1_all_certs( ca_certs ); + FD_LOG_INFO(( "Loaded %d CA certs from %s into OpenSSL", sk_X509_num( cert_list ), default_dir )); + if( fd_log_level_logfile()==0 ) { + for( int i=0; i + +#include "../../util/alloc/fd_alloc.h" + +/* Each tile that uses OpenSSL must initialize the thread-local + fd_ossl_alloc alloc object so that openSSL can allocate out of the + tile's wksp. The tile must also define a loose_footprint callback + function to allocate extra memory in the tile's workspace. */ +extern FD_TL fd_alloc_t * fd_ossl_alloc; + +/* Stores the number of ssl alloc errors per tile. Can be optionally + written back to a tile metric in METRICS_WRITE. */ +extern FD_TL ulong fd_ossl_alloc_errors; + +/* fd_ossl_tile_init is called in a tile's privileged init to + initialize OpenSSL. See fd_snapld_tile.c for reference. */ +void +fd_ossl_tile_init( fd_alloc_t * alloc ); + +/* fd_ossl_load_certs manually loads certificates into an SSL_CTX + object. This should be called right after calling SSL_CTX_new in + privileged init in a tile. */ +void +fd_ossl_load_certs( SSL_CTX * ssl_ctx ); + +#endif /* HEADER_fd_src_waltz_openssl_fd_openssl_tile_h */ + +#endif /* FD_HAS_OPENSSL */