1/* 2 * lws-minimal-secure-streams-proxy 3 * 4 * Written in 2010-2020 by Andy Green <andy@warmcat.com> 5 * 6 * This file is made available under the Creative Commons CC0 1.0 7 * Universal Public Domain Dedication. 8 * 9 * 10 * This is the proxy part for examples built to use it to connect to... it has 11 * the policy and the core SS function, but it doesn't contain any of the user 12 * code "business logic"... that's in the clients. 13 * 14 * The proxy side has the policy and performs the onward connection proxying 15 * fulfilment. The clients state the streamtype name they want and ask for the 16 * client to do the connection part. 17 * 18 * Rideshare information is being parsed out at the proxy side; the SSS RX part 19 * also brings with it rideshare names. 20 * 21 * Metadata is passed back over SSS from the client in the TX messages for the 22 * proxy to use per the policy. 23 */ 24 25#include <libwebsockets.h> 26#include <string.h> 27#include <signal.h> 28 29#if defined(__APPLE__) || defined(__linux__) 30#include <execinfo.h> 31#include <assert.h> 32#endif 33 34static int interrupted, bad = 1, port = 0 /* unix domain socket */; 35static const char *ibind = NULL; /* default to unix domain skt "proxy.ss.lws" */ 36static lws_state_notify_link_t nl; 37static struct lws_context *context; 38 39/* 40 * We just define enough policy so it can fetch the latest one securely 41 */ 42 43static const char * const default_ss_policy = 44 "{" 45 "\"release\":" "\"01234567\"," 46 "\"product\":" "\"myproduct\"," 47 "\"schema-version\":" "1," 48 "\"retry\": [" /* named backoff / retry strategies */ 49 "{\"default\": {" 50 "\"backoff\": [" "1000," 51 "2000," 52 "3000," 53 "5000," 54 "10000" 55 "]," 56 "\"conceal\":" "5," 57 "\"jitterpc\":" "20," 58 "\"svalidping\":" "30," 59 "\"svalidhup\":" "35" 60 "}}" 61 "]," 62 "\"certs\": [" /* named individual certificates in BASE64 DER */ 63 /* 64 * Let's Encrypt certs for warmcat.com / libwebsockets.org 65 * 66 * We fetch the real policy from there using SS and switch to 67 * using that. 68 */ 69 "{\"isrg_root_x1\": \"" 70 "MIIFazCCA1OgAwIBAgIRAIIQz7DSQONZRGPgu2OCiwAwDQYJKoZIhvcNAQELBQAw" 71 "TzELMAkGA1UEBhMCVVMxKTAnBgNVBAoTIEludGVybmV0IFNlY3VyaXR5IFJlc2Vh" 72 "cmNoIEdyb3VwMRUwEwYDVQQDEwxJU1JHIFJvb3QgWDEwHhcNMTUwNjA0MTEwNDM4" 73 "WhcNMzUwNjA0MTEwNDM4WjBPMQswCQYDVQQGEwJVUzEpMCcGA1UEChMgSW50ZXJu" 74 "ZXQgU2VjdXJpdHkgUmVzZWFyY2ggR3JvdXAxFTATBgNVBAMTDElTUkcgUm9vdCBY" 75 "MTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAK3oJHP0FDfzm54rVygc" 76 "h77ct984kIxuPOZXoHj3dcKi/vVqbvYATyjb3miGbESTtrFj/RQSa78f0uoxmyF+" 77 "0TM8ukj13Xnfs7j/EvEhmkvBioZxaUpmZmyPfjxwv60pIgbz5MDmgK7iS4+3mX6U" 78 "A5/TR5d8mUgjU+g4rk8Kb4Mu0UlXjIB0ttov0DiNewNwIRt18jA8+o+u3dpjq+sW" 79 "T8KOEUt+zwvo/7V3LvSye0rgTBIlDHCNAymg4VMk7BPZ7hm/ELNKjD+Jo2FR3qyH" 80 "B5T0Y3HsLuJvW5iB4YlcNHlsdu87kGJ55tukmi8mxdAQ4Q7e2RCOFvu396j3x+UC" 81 "B5iPNgiV5+I3lg02dZ77DnKxHZu8A/lJBdiB3QW0KtZB6awBdpUKD9jf1b0SHzUv" 82 "KBds0pjBqAlkd25HN7rOrFleaJ1/ctaJxQZBKT5ZPt0m9STJEadao0xAH0ahmbWn" 83 "OlFuhjuefXKnEgV4We0+UXgVCwOPjdAvBbI+e0ocS3MFEvzG6uBQE3xDk3SzynTn" 84 "jh8BCNAw1FtxNrQHusEwMFxIt4I7mKZ9YIqioymCzLq9gwQbooMDQaHWBfEbwrbw" 85 "qHyGO0aoSCqI3Haadr8faqU9GY/rOPNk3sgrDQoo//fb4hVC1CLQJ13hef4Y53CI" 86 "rU7m2Ys6xt0nUW7/vGT1M0NPAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNV" 87 "HRMBAf8EBTADAQH/MB0GA1UdDgQWBBR5tFnme7bl5AFzgAiIyBpY9umbbjANBgkq" 88 "hkiG9w0BAQsFAAOCAgEAVR9YqbyyqFDQDLHYGmkgJykIrGF1XIpu+ILlaS/V9lZL" 89 "ubhzEFnTIZd+50xx+7LSYK05qAvqFyFWhfFQDlnrzuBZ6brJFe+GnY+EgPbk6ZGQ" 90 "3BebYhtF8GaV0nxvwuo77x/Py9auJ/GpsMiu/X1+mvoiBOv/2X/qkSsisRcOj/KK" 91 "NFtY2PwByVS5uCbMiogziUwthDyC3+6WVwW6LLv3xLfHTjuCvjHIInNzktHCgKQ5" 92 "ORAzI4JMPJ+GslWYHb4phowim57iaztXOoJwTdwJx4nLCgdNbOhdjsnvzqvHu7Ur" 93 "TkXWStAmzOVyyghqpZXjFaH3pO3JLF+l+/+sKAIuvtd7u+Nxe5AW0wdeRlN8NwdC" 94 "jNPElpzVmbUq4JUagEiuTDkHzsxHpFKVK7q4+63SM1N95R1NbdWhscdCb+ZAJzVc" 95 "oyi3B43njTOQ5yOf+1CceWxG1bQVs5ZufpsMljq4Ui0/1lvh+wjChP4kqKOJ2qxq" 96 "4RgqsahDYVvTH9w7jXbyLeiNdd8XM2w9U/t7y0Ff/9yi0GE44Za4rF2LN9d11TPA" 97 "mRGunUHBcnWEvgJBQl9nJEiU0Zsnvgc/ubhPgXRR4Xq37Z0j4r7g1SgEEzwxA57d" 98 "emyPxgcYxn/eR44/KJ4EBs+lVDR3veyJm+kXQ99b21/+jh5Xos1AnX5iItreGCc=" 99 "\"}" 100 "]," 101 "\"trust_stores\": [" /* named cert chains */ 102 "{" 103 "\"name\": \"le_via_isrg\"," 104 "\"stack\": [" 105 "\"isrg_root_x1\"" 106 "]" 107 "}" 108 "]," 109 "\"s\": [{" 110 "\"captive_portal_detect\": {" 111 "\"endpoint\": \"connectivitycheck.android.com\"," 112 "\"http_url\": \"generate_204\"," 113 "\"port\": 80," 114 "\"protocol\": \"h1\"," 115 "\"http_method\": \"GET\"," 116 "\"opportunistic\": true," 117 "\"http_expect\": 204," 118 "\"http_fail_redirect\": true" 119 "}," 120 "\"fetch_policy\": {" 121 "\"endpoint\":" "\"warmcat.com\"," 122 "\"port\":" "443," 123 "\"protocol\":" "\"h1\"," 124 "\"http_method\":" "\"GET\"," 125 "\"http_url\":" "\"policy/minimal-proxy-v4.2-v2.json\"," 126 "\"tls\":" "true," 127 "\"opportunistic\":" "true," 128 "\"retry\":" "\"default\"," 129 "\"tls_trust_store\":" "\"le_via_isrg\"" 130 "}}" 131 "}" 132; 133 134static const char *canned_root_token_payload = 135 "grant_type=refresh_token" 136 "&refresh_token=Atzr|IwEBIJedGXjDqsU_vMxykqOMg" 137 "SHfYe3CPcedueWEMWSDMaDnEmiW8RlR1Kns7Cb4B-TOSnqp7ifVsY4BMY2B8tpHfO39XP" 138 "zfu9HapGjTR458IyHX44FE71pWJkGZ79uVBpljP4sazJuk8XS3Oe_yLnm_DIO6fU1nU3Y" 139 "0flYmsOiOAQE_gRk_pdlmEtHnpMA-9rLw3mkY5L89Ty9kUygBsiFaYatouROhbsTn8-jW" 140 "k1zZLUDpT6ICtBXSnrCIg0pUbZevPFhTwdXd6eX-u4rq0W-XaDvPWFO7au-iPb4Zk5eZE" 141 "iX6sissYrtNmuEXc2uHu7MnQO1hHCaTdIO2CANVumf-PHSD8xseamyh04sLV5JgFzY45S" 142 "KvKMajiUZuLkMokOx86rjC2Hdkx5DO7G-dbG1ufBDG-N79pFMSs7Ck5pc283IdLoJkCQc" 143 "AGvTX8o8I29QqkcGou-9TKhOJmpX8As94T61ok0UqqEKPJ7RhfQHHYdCtsdwxgvfVr9qI" 144 "xL_hDCcTho8opCVX-6QhJHl6SQFlTw13" 145 "&client_id=" 146 "amzn1.application-oa2-client.4823334c434b4190a2b5a42c07938a2d"; 147 148#if defined(LWS_WITH_SECURE_STREAMS_AUTH_SIGV4) 149static char *aws_keyid = NULL, 150 *aws_key = NULL; 151#endif 152 153static int 154app_system_state_nf(lws_state_manager_t *mgr, lws_state_notify_link_t *link, 155 int current, int target) 156{ 157 struct lws_context *context = lws_system_context_from_system_mgr(mgr); 158 lws_system_blob_t *ab = lws_system_get_blob(context, 159 LWS_SYSBLOB_TYPE_AUTH, 1 /* AUTH_IDX_ROOT */); 160 size_t size; 161 162 /* 163 * For the things we care about, let's notice if we are trying to get 164 * past them when we haven't solved them yet, and make the system 165 * state wait while we trigger the dependent action. 166 */ 167 switch (target) { 168 case LWS_SYSTATE_REGISTERED: 169 size = lws_system_blob_get_size(ab); 170 if (size) 171 break; 172 173 /* let's register our canned root token so auth can use it */ 174 lws_system_blob_direct_set(ab, 175 (const uint8_t *)canned_root_token_payload, 176 strlen(canned_root_token_payload)); 177 break; 178 case LWS_SYSTATE_OPERATIONAL: 179 if (current == LWS_SYSTATE_OPERATIONAL) { 180#if defined(LWS_WITH_SECURE_STREAMS_AUTH_SIGV4) 181 182 if (lws_aws_filesystem_credentials_helper( 183 "~/.aws/credentials", 184 "aws_access_key_id", 185 "aws_secret_access_key", 186 &aws_keyid, &aws_key)) 187 return -1; 188 189 lws_ss_sigv4_set_aws_key(context, 0, aws_keyid, aws_key); 190#endif 191 /* 192 * At this point we have DHCP, ntp, system auth token 193 * and we can reasonably create the proxy 194 */ 195 if (lws_ss_proxy_create(context, ibind, port)) { 196 lwsl_err("%s: failed to create ss proxy\n", 197 __func__); 198 return -1; 199 } 200 } 201 break; 202 case LWS_SYSTATE_POLICY_INVALID: 203 /* 204 * This is a NOP since we used direct set... but in a real 205 * system this could easily change to be done on the heap, then 206 * this would be important 207 */ 208 lws_system_blob_destroy(lws_system_get_blob(context, 209 LWS_SYSBLOB_TYPE_AUTH, 210 1 /* AUTH_IDX_ROOT */)); 211 break; 212 } 213 214 return 0; 215} 216 217static lws_state_notify_link_t * const app_notifier_list[] = { 218 &nl, NULL 219}; 220 221#if defined(LWS_WITH_SYS_METRICS) 222 223static int 224my_metric_report(lws_metric_pub_t *mp) 225{ 226 lws_metric_bucket_t *sub = mp->u.hist.head; 227 char buf[192]; 228 229 do { 230 if (lws_metrics_format(mp, &sub, buf, sizeof(buf))) 231 lwsl_user("%s: %s\n", __func__, buf); 232 } while ((mp->flags & LWSMTFL_REPORT_HIST) && sub); 233 234 /* 0 = leave metric to accumulate, 1 = reset the metric */ 235 236 return 1; 237} 238 239static const lws_system_ops_t system_ops = { 240 .metric_report = my_metric_report, 241}; 242 243#endif 244 245static void 246sigint_handler(int sig) 247{ 248 lwsl_notice("%s\n", __func__); 249 interrupted = 1; 250 lws_cancel_service(context); 251} 252 253int main(int argc, const char **argv) 254{ 255 struct lws_context_creation_info info; 256 const char *p; 257 int n = 0; 258 259 memset(&info, 0, sizeof info); 260 lws_cmdline_option_handle_builtin(argc, argv, &info); 261 262 signal(SIGINT, sigint_handler); 263 264 /* connect to ssproxy via UDS by default, else via tcp with this port */ 265 if ((p = lws_cmdline_option(argc, argv, "-p"))) 266 port = atoi(p); 267 268 /* UDS "proxy.ss.lws" in abstract namespace, else this socket path; 269 * when -p given this can specify the network interface to bind to */ 270 if ((p = lws_cmdline_option(argc, argv, "-i"))) 271 ibind = p; 272 273 lwsl_user("LWS secure streams Proxy [-d<verb>]\n"); 274 275 info.options = LWS_SERVER_OPTION_EXPLICIT_VHOSTS | 276 LWS_SERVER_OPTION_H2_JUST_FIX_WINDOW_UPDATE_OVERFLOW | 277 LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT; 278 info.fd_limit_per_thread = 1 + 26 + 1; 279 info.pss_policies_json = default_ss_policy; 280 info.port = CONTEXT_PORT_NO_LISTEN; 281 282 /* integrate us with lws system state management when context created */ 283 nl.name = "app"; 284 nl.notify_cb = app_system_state_nf; 285 info.register_notifier_list = app_notifier_list; 286 287 info.pt_serv_buf_size = (unsigned int)((6144 * 2) + 2048); 288 info.max_http_header_data = (unsigned short)(6144 + 2048); 289 290#if defined(LWS_WITH_SYS_METRICS) 291 info.system_ops = &system_ops; 292 info.metrics_prefix = "ssproxy"; 293#endif 294 295 context = lws_create_context(&info); 296 if (!context) { 297 lwsl_err("lws init failed\n"); 298 return 1; 299 } 300 301 /* the event loop */ 302 303 do { 304 n = lws_service(context, 0); 305 } while (n >= 0 && !interrupted); 306 307 bad = 0; 308 309#if defined(LWS_WITH_SECURE_STREAMS_AUTH_SIGV4) 310 if (aws_keyid) 311 free(aws_keyid); 312 if (aws_key) 313 free(aws_key); 314#endif 315 316 lws_context_destroy(context); 317 lwsl_user("Completed: %s\n", bad ? "failed" : "OK"); 318 319 return bad; 320} 321