commit 0df0e18e7fdf2674e4a22f0ca8e57a157dad6ca9 Author: Jeff Garzik <jeff@xxxxxxxxxx> Date: Fri Mar 5 10:04:00 2010 -0500 Protocol: replace SSL/no-SSL split ports with in-band SSL negotiation Initiating SSL immediately upon connection, based on incoming TCP connection port, is a relic of poor HTTP design and should not have been carried over to chunk protocol. Modern protocols (SMTP, IMAP, etc.) all support binding to a single port, and then negotiating SSL/TLS based on a command from the client. Change chunk protocol such that clients must issue a CHO_START_TLS command at the beginning of the TCP connection, if they wish encryption. Signed-off-by: Jeff Garzik <jgarzik@xxxxxxxxxx> diff --git a/doc/setup.txt b/doc/setup.txt index a5e7c66..5ef7225 100644 --- a/doc/setup.txt +++ b/doc/setup.txt @@ -27,14 +27,6 @@ _cld._udp.phx2.ex.com has SRV record 10 50 8081 maika.phx2.ex.com. <Port>8082</Port> </Listen> - You can also set the interface and the encryption: - - <Listen> - <Node>192.168.1.24</Node> - <Port>18082</Port> - <Encrypt>true</Encrypt> - </Listen> - For clouds and their many cheap nodes with one Ethernet it usually is not a great idea to specify interfaces, since they often use IPv6 or acquire IP addresses from DHCP. So, just specify a port. diff --git a/include/chunk_msg.h b/include/chunk_msg.h index 154201d..a25fcb5 100644 --- a/include/chunk_msg.h +++ b/include/chunk_msg.h @@ -32,16 +32,22 @@ enum { }; enum chunksrv_ops { - CHO_NOP = 0, - CHO_GET = 1, - CHO_GET_META = 2, - CHO_PUT = 3, - CHO_DEL = 4, - CHO_LIST = 5, - CHO_LOGIN = 6, - CHO_TABLE_OPEN = 7, - CHO_CHECK_START = 8, - CHO_CHECK_STATUS = 9, + CHO_NOP = 0, /* No-op (ping server) */ + CHO_GET = 1, /* GET object */ + CHO_GET_META = 2, /* GET object metadata */ + CHO_PUT = 3, /* PUT object */ + CHO_DEL = 4, /* Delete object */ + CHO_LIST = 5, /* List objects */ + CHO_LOGIN = 6, /* Login as user */ + CHO_TABLE_OPEN = 7, /* Open table */ + CHO_CHECK_START = 8, /* Begin self-check */ + CHO_CHECK_STATUS = 9, /* Query self-check status */ + + /* START-TLS is special. It MUST be the first request of a TCP + * cxn, and no chunkd-specific response is returned. The SSL + * functions' success/failure is sufficient indication. + */ + CHO_START_TLS = 10, /* Encrypt all subsequent msgs */ }; enum chunk_errcode { diff --git a/lib/chunkdc.c b/lib/chunkdc.c index e9e0e0f..9c1e967 100644 --- a/lib/chunkdc.c +++ b/lib/chunkdc.c @@ -191,6 +191,27 @@ static bool stc_login(struct st_client *stc) return true; } +static bool stc_start_tls_cmd(struct st_client *stc) +{ + struct chunksrv_req *req = (struct chunksrv_req *) stc->req_buf; + + if (stc->verbose) + fprintf(stderr, "libstc: START-TLS\n"); + + /* initialize request */ + req_init(stc, req); + req->op = CHO_START_TLS; + strcpy(req->sig, "START-TLS"); + + /* write request */ + if (!net_write(stc, req, req_len(req))) + return false; + + /* no response - SSL negotiation begins immediately */ + + return true; +} + struct st_client *stc_new(const char *service_host, int port, const char *user, const char *secret_key, bool use_ssl) @@ -253,6 +274,9 @@ struct st_client *stc_new(const char *service_host, int port, goto err_out; if (use_ssl) { + if (!stc_start_tls_cmd(stc)) + goto err_out; + stc->ssl_ctx = SSL_CTX_new(TLSv1_client_method()); if (!stc->ssl_ctx) goto err_out; @@ -266,7 +290,8 @@ struct st_client *stc_new(const char *service_host, int port, if (!SSL_set_fd(stc->ssl, stc->fd)) goto err_out_ssl; - if (SSL_connect(stc->ssl) <= 0) + rc = SSL_connect(stc->ssl); + if (rc <= 0) goto err_out_ssl; } @@ -276,9 +301,15 @@ struct st_client *stc_new(const char *service_host, int port, return stc; err_out_ssl: - SSL_free(stc->ssl); + if (stc->ssl) { + SSL_free(stc->ssl); + stc->ssl = NULL; + } err_out_ctx: - SSL_CTX_free(stc->ssl_ctx); + if (stc->ssl_ctx) { + SSL_CTX_free(stc->ssl_ctx); + stc->ssl_ctx = NULL; + } err_out: close(stc->fd); stc_free(stc); diff --git a/server/chunkd.h b/server/chunkd.h index 42bcb21..cd83f7b 100644 --- a/server/chunkd.h +++ b/server/chunkd.h @@ -102,6 +102,7 @@ struct client { SSL *ssl; bool read_want_write; bool write_want_read; + bool first_req; struct list_head write_q; /* list of async writes */ bool writing; @@ -150,7 +151,6 @@ struct listen_cfg { char *node; char *port; char *port_file; - bool encrypt; }; struct geo { diff --git a/server/cldu.c b/server/cldu.c index 9c27138..dbcf11d 100644 --- a/server/cldu.c +++ b/server/cldu.c @@ -540,11 +540,9 @@ static int cldu_make_ffile(char **ret, struct cld_session *sp) rc = asprintf(&str, " <Socket>\r\n" - " <Type>%s</Type>\r\n" " <Host>%s</Host>\r\n" " <Port>%s</Port>\r\n" " </Socket>\r\n", - cfg->encrypt ? "chunk-ssl" : "chunk", host, cfg->port); if (rc == -1) { diff --git a/server/config.c b/server/config.c index 69b5e9b..0422239 100644 --- a/server/config.c +++ b/server/config.c @@ -398,16 +398,6 @@ static void cfg_elm_end (GMarkupParseContext *context, } } - else if (cc->in_listen && cc->text && - !strcmp(element_name, "Encrypt")) { - if (!strcasecmp(cc->text, "yes") || - !strcasecmp(cc->text, "true")) - cc->tmp_listen.encrypt = true; - - free(cc->text); - cc->text = NULL; - } - else if (!strcmp(element_name, "Group") && cc->text) { free(chunkd_srv.group); chunkd_srv.group = cc->text; diff --git a/server/server.c b/server/server.c index 0d83311..4d9a09e 100644 --- a/server/server.c +++ b/server/server.c @@ -357,7 +357,7 @@ static void cli_free(struct client *cli) free(cli); } -static struct client *cli_alloc(bool use_ssl) +static struct client *cli_alloc(void) { struct client *cli; @@ -366,18 +366,10 @@ static struct client *cli_alloc(bool use_ssl) if (!cli) return NULL; - if (use_ssl) { - cli->ssl = SSL_new(ssl_ctx); - if (!cli->ssl) { - applog(LOG_ERR, "SSL_new failed"); - free(cli); - return NULL; - } - } - cli->state = evt_read_fixed; INIT_LIST_HEAD(&cli->write_q); cli->req_ptr = &cli->creq; + cli->first_req = true; return cli; } @@ -1038,6 +1030,7 @@ static const char *op2str(enum chunksrv_ops op) case CHO_TABLE_OPEN: return "CHO_TABLE_OPEN"; case CHO_CHECK_START: return "CHO_CHECK_START"; case CHO_CHECK_STATUS: return "CHO_CHECK_STATUS"; + case CHO_START_TLS: return "CHO_START_TLS"; default: return "BUG/UNKNOWN!"; @@ -1082,7 +1075,8 @@ static bool cli_evt_exec_req(struct client *cli, unsigned int events) cli->state = evt_recycle; - if (G_UNLIKELY((!logged_in) && (req->op != CHO_LOGIN))) { + if (G_UNLIKELY((!logged_in) && (req->op != CHO_LOGIN) && + (req->op != CHO_START_TLS))) { cli->state = evt_dispose; return true; } @@ -1142,11 +1136,37 @@ static bool cli_evt_exec_req(struct client *cli, unsigned int events) case CHO_CHECK_STATUS: rcb = chk_status(cli); break; + case CHO_START_TLS: + if (!cli->first_req) { + cli->state = evt_dispose; + rcb = true; + } else { + cli->ssl = SSL_new(ssl_ctx); + if (!cli->ssl) { + applog(LOG_ERR, "SSL_new failed"); + cli->state = evt_dispose; + rcb = true; + break; + } + + if (!SSL_set_fd(cli->ssl, cli->fd)) { + applog(LOG_ERR, "SSL_set_fd failed"); + cli->state = evt_dispose; + rcb = true; + break; + } + + cli->state = evt_ssl_accept; + rcb = true; + } + break; default: rcb = cli_err(cli, che_InvalidURI, true); break; } + cli->first_req = false; + out: return rcb; @@ -1220,7 +1240,7 @@ static bool cli_evt_ssl_accept(struct client *cli, unsigned int events) rc = SSL_accept(cli->ssl); if (rc > 0) { - cli->state = evt_read_fixed; + cli->state = evt_recycle; return true; } @@ -1236,6 +1256,8 @@ static bool cli_evt_ssl_accept(struct client *cli, unsigned int events) return false; } + applog(LOG_ERR, "SSL_accept returned %d", rc); + out: cli->state = evt_dispose; return true; @@ -1294,10 +1316,10 @@ static bool tcp_srv_event(int fd, short events, void *userdata) socklen_t addrlen = sizeof(struct sockaddr_in6); struct client *cli; char host[64]; - int rc, on = 1; + int on = 1; struct server_poll *sp; - cli = cli_alloc(sock->cfg->encrypt); + cli = cli_alloc(); if (!cli) { applog(LOG_ERR, "out of memory"); return false; /* end main loop; terminate server */ @@ -1321,32 +1343,6 @@ static bool tcp_srv_event(int fd, short events, void *userdata) applog(LOG_WARNING, "TCP_NODELAY failed: %s", strerror(errno)); - if (sock->cfg->encrypt) { - if (!SSL_set_fd(cli->ssl, cli->fd)) - goto err_out_fd; - - rc = SSL_accept(cli->ssl); - if (rc <= 0) { - rc = SSL_get_error(cli->ssl, rc); - if (rc == SSL_ERROR_WANT_READ) - cli->state = evt_ssl_accept; - else if (rc == SSL_ERROR_WANT_WRITE) { - cli->state = evt_ssl_accept; - cli->read_want_write = true; - } - else { - unsigned long e = ERR_get_error(); - char estr[121] = "(none?)"; - - if (e) - ERR_error_string(e, estr); - applog(LOG_WARNING, "%s SSL error %s", - cli->addr_host, estr); - goto err_out_fd; - } - } - } - sp = calloc(1, sizeof(*sp)); if (!sp) goto err_out_fd; @@ -1356,11 +1352,6 @@ static bool tcp_srv_event(int fd, short events, void *userdata) sp->cb = tcp_cli_event; sp->userdata = cli; - if (cli->read_want_write) { - cli->writing = true; - sp->events |= POLLOUT; - } - g_hash_table_insert(chunkd_srv.fd_info, GINT_TO_POINTER(cli->fd), sp); /* pretty-print incoming cxn info */ diff --git a/test/auth.c b/test/auth.c index 78b1f8b..e4bfaf0 100644 --- a/test/auth.c +++ b/test/auth.c @@ -43,7 +43,7 @@ static void test(bool do_encrypt) size_t len = 0; void *mem; - port = stc_readport(do_encrypt ? TEST_PORTFILE_SSL : TEST_PORTFILE); + port = stc_readport(TEST_PORTFILE); OK(port > 0); stc1 = stc_new(TEST_HOST, port, TEST_USER, TEST_USER_KEY, do_encrypt); diff --git a/test/basic-object.c b/test/basic-object.c index c4d803a..92ec8c8 100644 --- a/test/basic-object.c +++ b/test/basic-object.c @@ -41,7 +41,7 @@ static void test(bool do_encrypt) size_t len = 0; void *mem; - port = stc_readport(do_encrypt ? TEST_PORTFILE_SSL : TEST_PORTFILE); + port = stc_readport(TEST_PORTFILE); OK(port > 0); stc = stc_new(TEST_HOST, port, TEST_USER, TEST_USER_KEY, do_encrypt); diff --git a/test/it-works.c b/test/it-works.c index 597c58b..a3c01ee 100644 --- a/test/it-works.c +++ b/test/it-works.c @@ -35,7 +35,7 @@ static void test(bool ssl) int port; bool rcb; - port = stc_readport(ssl ? TEST_PORTFILE_SSL : TEST_PORTFILE); + port = stc_readport(TEST_PORTFILE); OK(port > 0); stc = stc_new(TEST_HOST, port, TEST_USER, TEST_USER_KEY, ssl); diff --git a/test/large-object.c b/test/large-object.c index 5188aad..d1a031e 100644 --- a/test/large-object.c +++ b/test/large-object.c @@ -108,7 +108,7 @@ static void test(bool do_encrypt) memset(data, 0xdeadbeef, sizeof(data)); - port = stc_readport(do_encrypt ? TEST_PORTFILE_SSL : TEST_PORTFILE); + port = stc_readport(TEST_PORTFILE); OK(port > 0); stc = stc_new(TEST_HOST, port, TEST_USER, TEST_USER_KEY, do_encrypt); diff --git a/test/lotsa-objects.c b/test/lotsa-objects.c index 048c1c3..d7dd95c 100644 --- a/test/lotsa-objects.c +++ b/test/lotsa-objects.c @@ -47,7 +47,7 @@ static void test(int n_objects, bool do_encrypt) char *k; struct timeval ta, tb; - port = stc_readport(do_encrypt ? TEST_PORTFILE_SSL : TEST_PORTFILE); + port = stc_readport(TEST_PORTFILE); OK(port > 0); stc = stc_new(TEST_HOST, port, TEST_USER, TEST_USER_KEY, do_encrypt); diff --git a/test/nop.c b/test/nop.c index 526b8c0..bd89a7b 100644 --- a/test/nop.c +++ b/test/nop.c @@ -42,7 +42,7 @@ static void test(int n_nops, bool do_encrypt) int i; struct timeval ta, tb; - port = stc_readport(do_encrypt ? TEST_PORTFILE_SSL : TEST_PORTFILE); + port = stc_readport(TEST_PORTFILE); OK(port > 0); stc = stc_new(TEST_HOST, port, TEST_USER, TEST_USER_KEY, do_encrypt); diff --git a/test/server-test.cfg b/test/server-test.cfg index d98fca4..29b69e2 100644 --- a/test/server-test.cfg +++ b/test/server-test.cfg @@ -10,12 +10,6 @@ <PortFile>chunkd.port</PortFile> </Listen> -<Listen> - <Port>auto</Port> - <PortFile>chunkd-ssl.port</PortFile> - <Encrypt>true</Encrypt> -</Listen> - <PID>chunkd.pid</PID> <Path>data/chunk</Path> diff --git a/test/test.h b/test/test.h index 75aa250..cc4111f 100644 --- a/test/test.h +++ b/test/test.h @@ -36,7 +36,6 @@ #define TEST_PORTFILE_CLD "cld.port" #define TEST_PORTFILE "chunkd.port" -#define TEST_PORTFILE_SSL "chunkd-ssl.port" #define TEST_CHUNKD_CFG "server-test.cfg" -- To unsubscribe from this list: send the line "unsubscribe hail-devel" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html