Пример #1
0
Файл: proxy.c Проект: ancuop/h2o
void h2o__proxy_process_request(h2o_req_t *req)
{
    h2o_req_overrides_t *overrides = req->overrides;
    h2o_http1client_ctx_t *client_ctx = get_client_ctx(req);
    struct rp_generator_t *self;

    if (overrides != NULL) {
        if (overrides->socketpool != NULL) {
            self = proxy_send_prepare(req, 1);
            h2o_http1client_connect_with_pool(&self->client, self, client_ctx, overrides->socketpool, on_connect);
            return;
        } else if (overrides->hostport.host.base != NULL) {
            self = proxy_send_prepare(req, 0);
            h2o_http1client_connect(&self->client, self, client_ctx, req->overrides->hostport.host, req->overrides->hostport.port,
                                    0, on_connect);
            return;
        }
    }
    { /* default logic */
        h2o_iovec_t host;
        uint16_t port;
        if (h2o_url_parse_hostport(req->authority.base, req->authority.len, &host, &port) == NULL) {
            h2o_req_log_error(req, "lib/core/proxy.c", "invalid URL supplied for internal redirection:%s://%.*s%.*s",
                              req->scheme->name.base, (int)req->authority.len, req->authority.base, (int)req->path.len,
                              req->path.base);
            h2o_send_error(req, 502, "Gateway Error", "internal error", 0);
            return;
        }
        if (port == 65535)
            port = req->scheme->default_port;
        self = proxy_send_prepare(req, 0);
        h2o_http1client_connect(&self->client, self, client_ctx, host, port, req->scheme == &H2O_URL_SCHEME_HTTPS, on_connect);
        return;
    }
}
Пример #2
0
void on_connect(EV_P_ struct ev_io *io, int revents) {
    while (1) {
        struct sockaddr_in client_addr;
        socklen_t len = sizeof (struct sockaddr_in);
        int client_sock = accept(io->fd, (struct sockaddr *) &client_addr, &len);
        if (client_sock >= 0) {

            if (set_nonblock(client_sock) == -1) {
                shutdown_printerr(client_sock, "can't set the socket mode O_NONBLOCK for client\n");
                return;
            }

            if (set_linger(client_sock) == -1) {
                shutdown_printerr(client_sock, "can't set SO_LINGER sock option for client\n");
                return;
            }

            if (set_keepalive(client_sock) == -1) {
                shutdown_printerr(client_sock, "can't set SO_KEEPALIVE sock option for client\n");
                return;
            }

            server_ctx_t *srv_ctx = (server_ctx_t *) ev_userdata(loop);
            client_ctx_t* cli_ctx = get_client_ctx(srv_ctx);
            cli_ctx->io.ctx = cli_ctx;
            cli_ctx->connected_at = time(NULL);
            uuid_generate(cli_ctx->uuid);
            memcpy(&cli_ctx->client_addr, &client_addr, sizeof (struct sockaddr_in));
            ev_io_init((ev_io *) & cli_ctx->io, client_read_write, client_sock, EV_READ);
            ev_io_start(loop, (ev_io *) & cli_ctx->io);
            char time_buff[32];
            strftime(time_buff, sizeof (time_buff), "%Y-%m-%d %H:%M:%S %Z", localtime(&cli_ctx->connected_at));
            char *addr = inet_ntoa(cli_ctx->client_addr.sin_addr);
            char uuid_buff[37];
            uuid_unparse_lower(cli_ctx->uuid, (char *) &uuid_buff);
            printf("client accepted %s:%hu %s at %s\n", addr, client_addr.sin_port, &uuid_buff, &time_buff);
            char *welcome_msg = server_welcome(srv_ctx, cli_ctx);
            send_message(loop, cli_ctx->uuid, welcome_msg, strlen(welcome_msg));
            free(welcome_msg);
            char *new_client_msg = server_client_connected(cli_ctx);
            for (ssize_t i = 0; i < srv_ctx->clients_count; i++) {
                if (uuid_compare(srv_ctx->clients[i]->uuid, cli_ctx->uuid) != 0) {
                    send_message(loop, srv_ctx->clients[i]->uuid, new_client_msg, strlen(new_client_msg));
                }
            }
            free(new_client_msg);
        } else {
            if (errno == EAGAIN)
                return;
            if (errno == EMFILE || errno == ENFILE) {
                fprintf(stderr, "out of file descriptors\n");
                return;
            } else if (errno != EINTR) {
                fprintf(stderr, "accept connections error\n");
                return;
            }
        }
    }
}
Пример #3
0
static struct rp_generator_t *proxy_send_prepare(h2o_req_t *req, int keepalive)
{
    struct rp_generator_t *self = h2o_mem_alloc_shared(&req->pool, sizeof(*self), on_generator_dispose);
    h2o_http1client_ctx_t *client_ctx = get_client_ctx(req);

    self->super.proceed = do_proceed;
    self->super.stop = do_close;
    self->src_req = req;
    if (client_ctx->websocket_timeout != NULL && h2o_lcstris(req->upgrade.base, req->upgrade.len, H2O_STRLIT("websocket"))) {
        self->is_websocket_handshake = 1;
    } else {
        self->is_websocket_handshake = 0;
    }
    self->up_req.bufs[0] = build_request(req, keepalive, self->is_websocket_handshake);
    self->up_req.bufs[1] = req->entity;
    self->up_req.is_head = h2o_memis(req->method.base, req->method.len, H2O_STRLIT("HEAD"));
    h2o_buffer_init(&self->last_content_before_send, &h2o_socket_buffer_prototype);
    h2o_doublebuffer_init(&self->sending, &h2o_socket_buffer_prototype);

    return self;
}
Пример #4
0
/*
 * This function is used by the server side of the EST proxy to respond to an
 * incoming CSR Attributes request.  This function is similar to the Client API
 * function, est_client_get_csrattrs().
  */
static int est_proxy_handle_csr_attrs (EST_CTX *ctx, void *http_ctx)
{
    int rv = EST_ERR_NONE;
    int pop_present;
    char *csr_data, *csr_data_pop;
    int csr_len, csr_pop_len;
    EST_CTX *client_ctx;

    /*
     * get the client context for this thread
     */
    client_ctx = get_client_ctx(ctx);
    if (!client_ctx) {
        EST_LOG_ERR("Unable to obtain client context for proxy operation");
	return (EST_ERR_NO_CTX);
    }

    /*
     * Invoke client code to retrieve the CSR attributes.
     * Note: there is no need to authenticate the client (see sec 4.5)
     */
    EST_LOG_INFO("Proxy get csr attributes");
    rv = est_client_get_csrattrs(client_ctx, (unsigned char **)&csr_data, &csr_len);
    /*
     * csr_data points to the memory allocated to hold the csr attributes,
     * which will be freed in this call stack.  To prevent a double-free
     * we null the to pointer on the client context.
     */
    client_ctx->retrieved_csrattrs = NULL;
    client_ctx->retrieved_csrattrs_len = 0;
    if (rv == EST_ERR_NONE) {
	ctx->csr_pop_present = 0;
	if (ctx->server_enable_pop) {
	    rv = est_is_challengePassword_present(csr_data, csr_len, &pop_present);
	    if (rv != EST_ERR_NONE) {
		EST_LOG_ERR("Error during PoP/sanity check");
		est_send_http_error(ctx, http_ctx, EST_ERR_HTTP_NO_CONTENT);
		return (EST_ERR_NONE);
	    }
	    ctx->csr_pop_present = pop_present;

	    if (!ctx->csr_pop_present) {
		if (csr_len == 0) {
                    csr_data = malloc(EST_CSRATTRS_POP_LEN + 1);
		    if (!csr_data) {
			return (EST_ERR_MALLOC);
		    }
		    strncpy(csr_data, EST_CSRATTRS_POP, EST_CSRATTRS_POP_LEN);
		    csr_data[EST_CSRATTRS_POP_LEN] = 0;
		    csr_len = EST_CSRATTRS_POP_LEN;
		    return (est_send_csrattr_data(ctx, csr_data, csr_len, http_ctx));
		}
		rv = est_add_challengePassword(csr_data, csr_len, &csr_data_pop, &csr_pop_len);
		if (rv != EST_ERR_NONE) {
		    if (csr_data) {
		        free(csr_data);
		    }
		    EST_LOG_ERR("Error during add PoP");
		    est_send_http_error(ctx, http_ctx, EST_ERR_HTTP_NO_CONTENT);
		    return (EST_ERR_NONE);
		}
		if (csr_data) {
		    free(csr_data);
		}
		csr_data = csr_data_pop;
		csr_len = csr_pop_len;
	    }
	}
    } else {
	EST_LOG_ERR("Server not reachable or sent corrupt attributes");
	est_send_http_error(ctx, http_ctx, EST_ERR_HTTP_NO_CONTENT);
	return (EST_ERR_NONE);
    }
    return (est_send_csrattr_data(ctx, csr_data, csr_len, http_ctx));
}
Пример #5
0
/*
 * This function is used by the server side of the EST proxy to respond to an
 * incoming Simple Enroll request.  This function is similar to the Client API
 * function, est_client_enroll_req(), except it bypasses some things that are
 * not done when functioning as a proxy, such as signing the CSR, not
 * inserting the TLS unique id and instead including the id-kp-cmcRA usage
 * extension.
 */
static EST_ERROR est_proxy_handle_simple_enroll (EST_CTX *ctx, void *http_ctx,
                                                 SSL *ssl, const char *ct,
                                                 char *body, int body_len,
					         int reenroll)
{
    EST_ERROR rv;
    BUF_MEM *pkcs10;
    unsigned char *pkcs7;
    int pkcs7_len = 0;
    X509_REQ *csr = NULL;
    EST_CTX *client_ctx;
     
    /*
     * Make sure the client has sent us a PKCS10 CSR request
     */
    if (strcmp(ct, "application/pkcs10")) {
        return (EST_ERR_BAD_CONTENT_TYPE);
    }

    /*
     * Authenticate the client
     */
    switch (est_enroll_auth(ctx, http_ctx, ssl, reenroll)) {
    case EST_HTTP_AUTH:
    case EST_SRP_AUTH:
    case EST_CERT_AUTH:
        break;
    case EST_HTTP_AUTH_PENDING:
        return (EST_ERR_AUTH_PENDING);
        break;
    case EST_UNAUTHORIZED:
    default:
        return (EST_ERR_AUTH_FAIL);
        break;
    }

    /*
     * Parse the PKCS10 CSR from the client
     */
    csr = est_server_parse_csr((unsigned char*)body, body_len);
    if (!csr) {
	EST_LOG_ERR("Unable to parse the PKCS10 CSR sent by the client");
	return (EST_ERR_BAD_PKCS10);
    }
    
    /*
     * Perform a sanity check on the CSR
     */
    if (est_server_check_csr(csr)) {
	EST_LOG_ERR("PKCS10 CSR sent by the client failed sanity check");
	X509_REQ_free(csr);
	return (EST_ERR_BAD_PKCS10);
    }

    /*
     * Do the PoP check (Proof of Possession).  The challenge password
     * in the pkcs10 request should match the TLS unique ID.
     */
    rv = est_tls_uid_auth(ctx, ssl, csr);
    X509_REQ_free(csr);

    if (rv != EST_ERR_NONE) {
        return (EST_ERR_AUTH_FAIL_TLSUID);
    }

    /*
     * body now points to the pkcs10 data, pass
     * this to the enrollment routine.  Need to hi-jack
     * a BUF_MEM.  Attach the body to a new BUF_MEM
     */
    pkcs10 = BUF_MEM_new();
    pkcs10->data = body;
    pkcs10->length = body_len;
    pkcs10->max = body_len;

    /*
     * get the client context for this thread
     */
    client_ctx = get_client_ctx(ctx);
    if (!client_ctx) {
        EST_LOG_ERR("Unable to obtain client context for proxy operation");
        est_proxy_free_ossl_bufmem(pkcs10);
	return (EST_ERR_NO_CTX);
    }

    /*
     * Allocate some space to hold the cert that we
     * expect to receive from the EST server.
     */
    pkcs7 = malloc(EST_CA_MAX); 

    /*
     * Attempt to enroll the CSR from the client
     */
    rv = est_proxy_send_enroll_request(client_ctx, pkcs10, pkcs7, &pkcs7_len, reenroll);

    /*
     * Handle any errors that likely occurred
     */
    switch (rv) {
    case EST_ERR_AUTH_FAIL:
        /* Try one more time if we're doing Digest auth */
        if ((ctx->auth_mode == AUTH_DIGEST ||
             ctx->auth_mode == AUTH_BASIC  ||
             ctx->auth_mode == AUTH_TOKEN)) {
            
            EST_LOG_INFO("HTTP Auth failed, trying again with digest/basic parameters");

            rv = est_proxy_send_enroll_request(client_ctx, pkcs10, pkcs7, &pkcs7_len, reenroll);
	    if (rv == EST_ERR_CA_ENROLL_RETRY) {
	        rv = est_proxy_propagate_retry(client_ctx, http_ctx);
	    } else if (rv != EST_ERR_NONE) {
                EST_LOG_WARN("EST enrollment failed, error code is %d", rv);
            }
        }
        break;
    case EST_ERR_CA_ENROLL_RETRY:
	rv = est_proxy_propagate_retry(client_ctx, http_ctx);
	break;
    default:
        EST_LOG_WARN("Initial EST enrollment request error code is %d", rv);
	break;
    }

    client_ctx->auth_mode = AUTH_NONE;
    
    /*
     * Prevent OpenSSL from freeing our data
     */
    est_proxy_free_ossl_bufmem(pkcs10);

    /*
     * If we have a cert response from the EST server, let's forward
     * it back to the EST client
     */
    if (pkcs7_len > 0) {
        rv = est_proxy_propagate_pkcs7(http_ctx, pkcs7, pkcs7_len);
    }
    free(pkcs7);

    return (rv);
}
Пример #6
0
/*
 * est_proxy_retrieve_cacerts() issues a request to the server to obtain the
 * CA Certs chain to be used for Get CA Certs requests from clients.
 * The CA Cert chain returned from the server are passed back to the caller.
 *
 * It's the responsibility of the caller to free up this buffer.
 */
EST_ERROR est_proxy_retrieve_cacerts (EST_CTX *ctx, unsigned char **cacerts_rtn,
                                      int *cacerts_rtn_len)
{
    EST_CTX *client_ctx;
    EST_ERROR rv;
    int rcvd_cacerts_len;
    unsigned char *rcvd_cacerts;

    if (ctx == NULL) {
        EST_LOG_ERR("Ctx not passed to %s", __FUNCTION__);
        return (EST_ERR_NO_CTX);
    }
    if (cacerts_rtn == NULL || cacerts_rtn_len == NULL) {
        EST_LOG_ERR("Ctx not passed to %s", __FUNCTION__);
        return (EST_ERR_INVALID_PARAMETERS);        
    }
    
    *cacerts_rtn = NULL;
    *cacerts_rtn_len = 0;

    /*
     * Get the client context for this thread
     */
    client_ctx = get_client_ctx(ctx);
    if (!client_ctx) {
        EST_LOG_ERR("Unable to obtain client context for proxy operation");
        return (EST_ERR_NO_CTX);
    }

    rv = est_client_get_cacerts(client_ctx, &rcvd_cacerts_len);
    if (rv != EST_ERR_NONE) {
        EST_LOG_ERR("Unable to retrieve CA Certs from upstream server RC = %s",
                    EST_ERR_NUM_TO_STR(rv));
        return (rv);
    }
    
    /*
     * Allocate a buffer to retrieve the CA certs
     * and get them copied in
     */
    rcvd_cacerts = malloc(rcvd_cacerts_len);
    if (rcvd_cacerts == NULL) {
        EST_LOG_ERR("Unable to malloc buffer for cacerts received from server");
        return (EST_ERR_MALLOC);
    }
    
    rv = est_client_copy_cacerts(client_ctx, rcvd_cacerts);
    if (rv != EST_ERR_NONE) {
        EST_LOG_ERR("Unable to copy CA Certs from upstream server RC = %s",
                    EST_ERR_NUM_TO_STR(rv));
        free(rcvd_cacerts);
        return (rv);
    }

    /*
     * The retrieving of the CA certs through the normal client
     * interface causes the client to go back into an uninitialized state.
     * In this case though, we're getting it just for passing it back
     * to the downstream clients, so we're going to put this client
     * context back into the initialized state
     */
    client_ctx->est_client_initialized = 1;
    
    *cacerts_rtn = rcvd_cacerts;
    *cacerts_rtn_len = rcvd_cacerts_len;
    return (EST_ERR_NONE);
}
Пример #7
0
static h2o_http1client_body_cb on_head(h2o_http1client_t *client, const char *errstr, int minor_version, int status,
                                       h2o_iovec_t msg, struct phr_header *headers, size_t num_headers)
{
    struct rp_generator_t *self = client->data;
    h2o_req_t *req = self->src_req;
    size_t i;

    if (errstr != NULL && errstr != h2o_http1client_error_is_eos) {
        self->client = NULL;
        h2o_req_log_error(req, "lib/core/proxy.c", "%s", errstr);
        h2o_send_error(req, 502, "Gateway Error", errstr, 0);
        return NULL;
    }

    /* copy the response (note: all the headers must be copied; http1client discards the input once we return from this callback) */
    req->res.status = status;
    req->res.reason = h2o_strdup(&req->pool, msg.base, msg.len).base;
    for (i = 0; i != num_headers; ++i) {
        const h2o_token_t *token = h2o_lookup_token(headers[i].name, headers[i].name_len);
        h2o_iovec_t value;
        if (token != NULL) {
            if (token->proxy_should_drop) {
                goto Skip;
            }
            if (token == H2O_TOKEN_CONTENT_LENGTH) {
                if (req->res.content_length != SIZE_MAX ||
                    (req->res.content_length = h2o_strtosize(headers[i].value, headers[i].value_len)) == SIZE_MAX) {
                    self->client = NULL;
                    h2o_req_log_error(req, "lib/core/proxy.c", "%s", "invalid response from upstream (malformed content-length)");
                    h2o_send_error(req, 502, "Gateway Error", "invalid response from upstream", 0);
                    return NULL;
                }
                goto Skip;
            } else if (token == H2O_TOKEN_LOCATION) {
                if (req->res_is_delegated && (300 <= status && status <= 399) && status != 304) {
                    self->client = NULL;
                    h2o_iovec_t method = h2o_get_redirect_method(req->method, status);
                    h2o_send_redirect_internal(req, method, headers[i].value, headers[i].value_len, 1);
                    return NULL;
                }
                if (req->overrides != NULL && req->overrides->location_rewrite.match != NULL) {
                    value =
                        rewrite_location(&req->pool, headers[i].value, headers[i].value_len, req->overrides->location_rewrite.match,
                                         req->input.scheme, req->input.authority, req->overrides->location_rewrite.path_prefix);
                    if (value.base != NULL)
                        goto AddHeader;
                }
                goto AddHeaderDuped;
            } else if (token == H2O_TOKEN_LINK) {
                h2o_puth_path_in_link_header(req, headers[i].value, headers[i].value_len);
            }
        /* default behaviour, transfer the header downstream */
        AddHeaderDuped:
            value = h2o_strdup(&req->pool, headers[i].value, headers[i].value_len);
        AddHeader:
            h2o_add_header(&req->pool, &req->res.headers, token, value.base, value.len);
        Skip:
            ;
        } else {
            h2o_iovec_t name = h2o_strdup(&req->pool, headers[i].name, headers[i].name_len);
            h2o_iovec_t value = h2o_strdup(&req->pool, headers[i].value, headers[i].value_len);
            h2o_add_header_by_str(&req->pool, &req->res.headers, name.base, name.len, 0, value.base, value.len);
        }
    }

    if (self->is_websocket_handshake && req->res.status == 101) {
        h2o_http1client_ctx_t *client_ctx = get_client_ctx(req);
        assert(client_ctx->websocket_timeout != NULL);
        h2o_add_header(&req->pool, &req->res.headers, H2O_TOKEN_UPGRADE, H2O_STRLIT("websocket"));
        on_websocket_upgrade(self, client_ctx->websocket_timeout);
        self->client = NULL;
        return NULL;
    }
    /* declare the start of the response */
    h2o_start_response(req, &self->super);

    if (errstr == h2o_http1client_error_is_eos) {
        self->client = NULL;
        h2o_send(req, NULL, 0, 1);
        return NULL;
    }

    return on_body;
}