Ejemplo n.º 1
0
HTTPScode
https_recv(struct https_request *req, int *code, const char **body, int *len,
        int msecs)
{
        int n, err;
        
        if (BIO_reset(req->body) != 1) {
                ctx->errstr = _SSL_strerror();
                return (HTTPS_ERR_LIB);
        }
        /* Read loop sentinel set by parser in __on_message_done() */
        while (!req->done) {
                while ((n = BIO_read(req->cbio, ctx->parse_buf,
                            sizeof(ctx->parse_buf))) <= 0) {
                        if ((n = _BIO_wait(req->cbio, msecs)) != 1) {
                                ctx->errstr = n ? _SSL_strerror() :
                                    "Connection closed";
                                return (HTTPS_ERR_SERVER);
                        }
                }
                if ((err = http_parser_execute(req->parser,
                            &ctx->parse_settings, ctx->parse_buf, n)) != n) {
                        ctx->errstr = http_errno_description(err);
                        return (HTTPS_ERR_SERVER);
                }
        }
        *len = BIO_get_mem_data(req->body, (char **)body);
        *code = req->parser->status_code;
        
        return (HTTPS_OK);
}
Ejemplo n.º 2
0
HTTPScode
https_send(struct https_request *req, const char *method, const char *uri,
    const char *qs, const char *hdrs)
{
        char *p;
        int n, qsbody;
            
        req->done = 0;
        
        /* Format HTTP/1.1 request line */
        qsbody = (strcmp(method, "GET") != 0 &&
                  strcmp(method, "HEAD") != 0 &&
                  strcmp(method, "DELETE") != 0);
        
        BIO_printf(req->cbio, "%s %s", method, uri);
        if (qs != NULL && !qsbody)
                BIO_printf(req->cbio, "?%s", qs);
        BIO_puts(req->cbio, " HTTP/1.1\r\n");

        /* Add host header */
        BIO_printf(req->cbio, "Host: %s", req->host);
        if (strcmp(req->port, "443") != 0)
                BIO_printf(req->cbio, ":%s", req->port);
        BIO_puts(req->cbio, "\r\n");

        /* Add User-agent header */
        BIO_printf(req->cbio,
                   "User-Agent: %s\r\n",
                   ctx->useragent);

        /* Add headers */
        if (hdrs != NULL && (p = strdup(hdrs)) != NULL) {
                BIO_printf(req->cbio, "%s\r\n", strtok(p, "\r\n"));
                while ((p = strtok(NULL, "\r\n")) != NULL && *p)
                        BIO_printf(req->cbio, "%s\r\n", p);
        }
        /* Finish request */
        if (qs != NULL && qsbody) {
                BIO_printf(req->cbio,
                    "Content-Type: application/x-www-form-urlencoded\r\n"
                    "Content-Length: %d\r\n", (int)strlen(qs));
                BIO_printf(req->cbio, "\r\n%s", qs);
        } else {
                BIO_puts(req->cbio, "\r\n\r\n");
        }
        /* Send request */
        while (BIO_flush(req->cbio) != 1) {
                if ((n = _BIO_wait(req->cbio, -1)) != 1) {
                        ctx->errstr = n ? _SSL_strerror() : "Write timed out";
                        return (HTTPS_ERR_SERVER);
                }
        }
        return (HTTPS_OK);
}
Ejemplo n.º 3
0
HTTPScode
https_recv(struct https_request *req, int *code, const char **body, int *len, int msecs_timeout)
{
        BUF_MEM *bm;
        int n, err;

        if (BIO_reset(req->body) != 1) {
                ctx->errstr = _SSL_strerror();
                return (HTTPS_ERR_LIB);
        }
        /* Read loop sentinel set by parser in __on_message_done() */
        while (!req->done) {
                while ((n = BIO_read(req->cbio, ctx->parse_buf,
                            sizeof(ctx->parse_buf))) <= 0) {
                        if ((n = _BIO_wait(req->cbio, msecs_timeout)) != 1) {
                                ctx->errstr = n ? _SSL_strerror() :
                                    "Connection closed";
                                return (HTTPS_ERR_SERVER);
                        }
                }
                if ((err = http_parser_execute(req->parser,
                            &ctx->parse_settings, ctx->parse_buf, n)) != n) {
                        ctx->errstr = http_errno_description(err);
                        return (HTTPS_ERR_SERVER);
                }
        }
        /* XXX - NUL-terminate body for string parsing */
        BIO_write(req->body, "\0", 1);
        BIO_get_mem_ptr(req->body, &bm);
        
        *code = req->parser->status_code;
        *body = bm->data;
        *len = bm->length - 1;
        
        return (HTTPS_OK);
}
Ejemplo n.º 4
0
HTTPScode
https_open(struct https_request **reqp, const char *host)
{
        struct https_request *req;
        BIO *b64, *sbio;
        char *p;
        int n;
        int connection_error = 0;
        const char *api_host;
        const char *api_port;

        /* Set up our handle */
        n = 1;
        if ((req = calloc(1, sizeof(*req))) == NULL ||
            (req->host = strdup(host)) == NULL ||
            (req->parser = malloc(sizeof(http_parser))) == NULL) {
                ctx->errstr = strerror(errno);
                https_close(&req);
                return (HTTPS_ERR_SYSTEM);
        }
        if ((p = strchr(req->host, ':')) != NULL) {
                *p = '\0';
                req->port = p + 1;
        } else {
                req->port = "443";
        }
        if ((req->body = BIO_new(BIO_s_mem())) == NULL) {
                ctx->errstr = _SSL_strerror();
                https_close(&req);
                return (HTTPS_ERR_LIB);
        }
        http_parser_init(req->parser, HTTP_RESPONSE);
        req->parser->data = req;

        /* Connect to server */
        if (ctx->proxy) {
            api_host = ctx->proxy;
            api_port = ctx->proxy_port;
        } else {
            api_host = req->host;
            api_port = req->port;
        }
        connection_error = _establish_connection(req, api_host, api_port);
        if (connection_error != HTTPS_OK) {
            https_close(&req);
            return connection_error;
        }

        /* Tunnel through proxy, if specified */
        if (ctx->proxy != NULL) {
                BIO_printf(req->cbio,
                    "CONNECT %s:%s HTTP/1.0\r\n"
                    "User-Agent: %s\r\n",
                    req->host, req->port, ctx->useragent);
                
                if (ctx->proxy_auth != NULL) {
                        b64 = BIO_push(BIO_new(BIO_f_base64()),
                            BIO_new(BIO_s_mem()));
                        BIO_set_flags(b64,BIO_FLAGS_BASE64_NO_NL);
                        BIO_write(b64, ctx->proxy_auth,
                            strlen(ctx->proxy_auth));
                        (void)BIO_flush(b64);
                        n = BIO_get_mem_data(b64, &p);

                        BIO_puts(req->cbio, "Proxy-Authorization: Basic ");
                        BIO_write(req->cbio, p, n);
                        BIO_puts(req->cbio, "\r\n");
                        BIO_free_all(b64);
                }
                BIO_puts(req->cbio, "\r\n");
                (void)BIO_flush(req->cbio);
                
                while ((n = BIO_read(req->cbio, ctx->parse_buf,
                            sizeof(ctx->parse_buf))) <= 0) {
                        _BIO_wait(req->cbio, 5000);
                }
                if (strncmp("HTTP/1.0 200", ctx->parse_buf, 12) != 0) {
                        snprintf(ctx->errbuf, sizeof(ctx->errbuf),
                            "Proxy error: %s", ctx->parse_buf);
                        ctx->errstr = strtok(ctx->errbuf, "\r\n");
                        https_close(&req);
                        if (n < 12 || atoi(ctx->parse_buf + 9) < 500)
                                return (HTTPS_ERR_CLIENT);
                        return (HTTPS_ERR_SERVER);
                }
        }
        /* Establish SSL connection */
        if ((sbio = BIO_new_ssl(ctx->ssl_ctx, 1)) == NULL) {
                https_close(&req);
                return (HTTPS_ERR_LIB);
        }
        req->cbio = BIO_push(sbio, req->cbio);
        BIO_get_ssl(req->cbio, &req->ssl);
        
        while (BIO_do_handshake(req->cbio) <= 0) {
                if ((n = _BIO_wait(req->cbio, 5000)) != 1) {
                        ctx->errstr = n ? _SSL_strerror() :
                            "SSL handshake timed out";
                        https_close(&req);
                        return (n ? HTTPS_ERR_SYSTEM : HTTPS_ERR_SERVER);
                }
        }
        /* Validate server certificate name */
        if (_SSL_check_server_cert(req->ssl, req->host) != 1) {
                ctx->errstr = "Certificate name validation failed";
                https_close(&req);
                return (HTTPS_ERR_LIB);
        }
        *reqp = req;
        
        return (HTTPS_OK);
}
Ejemplo n.º 5
0
HTTPScode
https_init(const char *useragent, const char *cafile, const char *proxy)
{
        X509_STORE *store;
        X509 *cert;
        BIO *bio;
        char *p;
        
        if ((ctx = calloc(1, sizeof(*ctx))) == NULL ||
            (ctx->useragent = strdup(useragent)) == NULL) {
                ctx->errstr = strerror(errno);
                return (HTTPS_ERR_SYSTEM);
        }
        /* Initialize SSL context */
        SSL_library_init();
        SSL_load_error_strings();
        OpenSSL_add_ssl_algorithms();

        /* XXX - ape openssl s_client -rand for testing on ancient systems */
        if (!RAND_status()) {
                if ((p = getenv("RANDFILE")) != NULL) {
                        RAND_load_file(p, 8192);
                } else {
                        ctx->errstr = "No /dev/random, EGD, or $RANDFILE";
                        return (HTTPS_ERR_LIB);
                }
        }
        if ((ctx->ssl_ctx = SSL_CTX_new(TLSv1_client_method())) == NULL) {
                ctx->errstr = _SSL_strerror();
                return (HTTPS_ERR_LIB);
        }
        /* Set up our CA cert */
        if (cafile == NULL) {
                /* Load default CA cert from memory */
                if ((bio = BIO_new_mem_buf((void *)CACERT_PEM, -1)) == NULL ||
                    (store = SSL_CTX_get_cert_store(ctx->ssl_ctx)) == NULL) {
                        ctx->errstr = _SSL_strerror();
                        return (HTTPS_ERR_LIB);
                }
                while ((cert = PEM_read_bio_X509(bio, NULL, 0, NULL)) != NULL) {
                        X509_STORE_add_cert(store, cert);
                        X509_free(cert);
                }
                BIO_free_all(bio);
                SSL_CTX_set_verify(ctx->ssl_ctx, SSL_VERIFY_PEER, NULL);
        } else if (cafile[0] == '\0') {                
                /* Skip verification */
                SSL_CTX_set_verify(ctx->ssl_ctx, SSL_VERIFY_NONE, NULL);
        } else {
                /* Load CA cert from file */
                if (!SSL_CTX_load_verify_locations(ctx->ssl_ctx,
                        cafile, NULL)) {
                        SSL_CTX_free(ctx->ssl_ctx);
                        ctx->errstr = _SSL_strerror();
                        return (HTTPS_ERR_CLIENT);
                }
                SSL_CTX_set_verify(ctx->ssl_ctx, SSL_VERIFY_PEER, NULL);
        }
        /* Save our proxy config if any */
        if ((p = getenv("http_proxy")) != NULL ||
            (p = (char *)proxy) != NULL) {
                if (strstr(p, "://") != NULL) {
                        if (strncmp(p, "http://", 7) != 0) {
                                ctx->errstr = "http_proxy must be HTTP";
                                return (HTTPS_ERR_CLIENT);
                        }
                        p += 7;
                }
                p = strdup(p);
                
                if ((ctx->proxy = strchr(p, '@')) != NULL) {
                        *ctx->proxy++ = '\0';
                        ctx->proxy_auth = p;
                } else {
                        ctx->proxy = p;
                }
                strtok(ctx->proxy, "/");
                
                if ((ctx->proxy_port = strchr(ctx->proxy, ':')) != NULL) {
                        *ctx->proxy_port++ = '\0';
                } else {
                        ctx->proxy_port = "80";
                }
        }
        /* Set HTTP parser callbacks */
        ctx->parse_settings.on_body = __on_body;
        ctx->parse_settings.on_message_complete = __on_message_complete;

        signal(SIGPIPE, SIG_IGN);
        
        return (0);
}
Ejemplo n.º 6
0
/*
 * Establishes the connection to the Duo server.  On successful return,
 * req->cbio is connected and ready to use.
 * Return HTTPS_OK on success, error code on failure.
 */
static HTTPScode
_establish_connection(struct https_request * const req,
        const char * const api_host,
        const char * const api_port)
{
#ifndef HAVE_GETADDRINFO
    /* Systems that don't have getaddrinfo can use the BIO
       wrappers, but only get IPv4 support. */
    int n;

    if ((req->cbio = BIO_new(BIO_s_connect())) == NULL) {
        ctx->errstr = _SSL_strerror();
        return HTTPS_ERR_LIB;
    }
    BIO_set_conn_hostname(req->cbio, api_host);
    BIO_set_conn_port(req->cbio, api_port);
    BIO_set_nbio(req->cbio, 1);

    while (BIO_do_connect(req->cbio) <= 0) {
        if ((n = _BIO_wait(req->cbio, 10000)) != 1) {
            ctx->errstr = n ? _SSL_strerror() :
                "Connection timed out";
            return (n ? HTTPS_ERR_SYSTEM : HTTPS_ERR_SERVER);
        }
    }

    return HTTPS_OK;

#else /* HAVE_GETADDRINFO */

    /* IPv6 Support
     * BIO wrapped io does not support IPv6 addressing.  To work around,
     * resolve the address and connect the socket manually.  Then pass
     * the connected socket to the BIO wrapper with BIO_new_socket.
     */
    int connected_socket = -1;
    int socket_error = 0;
    /* Address Lookup */
    struct addrinfo *res = NULL;
    struct addrinfo *cur_res = NULL;
    struct addrinfo hints;
    int error;

    memset(&hints, 0, sizeof(hints));
    hints.ai_family = PF_UNSPEC;
    hints.ai_socktype = SOCK_STREAM;

    error = getaddrinfo(api_host,
            api_port,
            &hints,
            &res);
    if (error) {
        ctx->errstr = gai_strerror(error);
        return HTTPS_ERR_SYSTEM;
    }

    /* Connect */
    for (cur_res = res; cur_res; cur_res = cur_res->ai_next) {
        int connretries = 3;
        while (connected_socket == -1 && connretries--) {
            int sock_flags;

            connected_socket = socket(cur_res->ai_family,
                    cur_res->ai_socktype,
                    cur_res->ai_protocol);
            if (connected_socket == -1) {
                continue;
            }
            sock_flags = fcntl(connected_socket, F_GETFL, 0);
            fcntl(connected_socket, F_SETFL, sock_flags|O_NONBLOCK);

            if (connect(connected_socket, cur_res->ai_addr, cur_res->ai_addrlen) != 0 &&
                    errno != EINPROGRESS) {
                close(connected_socket);
                connected_socket = -1;
                break;
            }
            socket_error = _fd_wait(connected_socket, 10000);
            if (socket_error != 1) {
                close(connected_socket);
                connected_socket = -1;
                continue;
            }
            /* Connected! */
            break;
        }
    }
    cur_res = NULL;
    freeaddrinfo(res);
    res = NULL;

    if (connected_socket == -1) {
        ctx->errstr = "Failed to connect";
        return socket_error ? HTTPS_ERR_SYSTEM : HTTPS_ERR_SERVER;
    }

    if ((req->cbio = BIO_new_socket(connected_socket, BIO_CLOSE)) == NULL) {
        ctx->errstr = _SSL_strerror();
        return (HTTPS_ERR_LIB);
    }
    BIO_set_conn_hostname(req->cbio, api_host);
    BIO_set_conn_port(req->cbio, api_port);
    BIO_set_nbio(req->cbio, 1);

    return HTTPS_OK;

#endif /* HAVE_GETADDRINFO */
}
Ejemplo n.º 7
0
HTTPScode
https_send(struct https_request *req, const char *method, const char *uri,
    int argc, char *argv[])
{
        BIO *b64;
	HMAC_CTX hmac;
	unsigned char MD[SHA_DIGEST_LENGTH];
        char *qs, *p;
        int i, n, is_get;
            
        req->done = 0;
        
        /* Generate query string and canonical request to sign */
	if ((qs = _argv_to_qs(argc, argv)) == NULL ||
            (asprintf(&p, "%s\n%s\n%s\n%s", method, req->host, uri, qs)) < 0) {
                free(qs);
                ctx->errstr = strerror(errno);
                return (HTTPS_ERR_LIB);
        }
        /* Format request */
        if ((is_get = (strcmp(method, "GET") == 0))) {
                BIO_printf(req->cbio, "GET %s?%s HTTP/1.1\r\n", uri, qs);
        } else {
                BIO_printf(req->cbio, "%s %s HTTP/1.1\r\n", method, uri);
        }
        if (strcmp(req->port, "443") == 0) {
                BIO_printf(req->cbio, "Host: %s\r\n", req->host);
        } else {
                BIO_printf(req->cbio, "Host: %s:%s\r\n", req->host, req->port);
        }
        /* Add User-Agent header */
        BIO_printf(req->cbio,
                   "User-Agent: %s\r\n",
                   ctx->useragent);
        /* Add signature */
        BIO_puts(req->cbio, "Authorization: Basic ");

	HMAC_CTX_init(&hmac);
	HMAC_Init(&hmac, ctx->skey, strlen(ctx->skey), EVP_sha1());
	HMAC_Update(&hmac, (unsigned char *)p, strlen(p));
	HMAC_Final(&hmac, MD, NULL);
	HMAC_CTX_cleanup(&hmac);
        free(p);
        
        b64 = _BIO_new_base64();
        BIO_printf(b64, "%s:", ctx->ikey);
        for (i = 0; i < sizeof(MD); i++) {
                BIO_printf(b64, "%02x", MD[i]);
        }
        (void)BIO_flush(b64);
        n = BIO_get_mem_data(b64, &p);
        BIO_write(req->cbio, p, n);
        BIO_free_all(b64);

        /* Finish request */
        if (!is_get) {
                BIO_printf(req->cbio,
                    "\r\nContent-Type: application/x-www-form-urlencoded\r\n"
                    "Content-Length: %d\r\n\r\n%s",
                    (int)strlen(qs), qs);
        } else {
                BIO_puts(req->cbio, "\r\n\r\n");
        }
        /* Send request */
        while (BIO_flush(req->cbio) != 1) {
                if ((n = _BIO_wait(req->cbio, -1)) != 1) {
                        ctx->errstr = n ? _SSL_strerror() : "Write timed out";
                        return (HTTPS_ERR_SERVER);
                }
        }
        return (HTTPS_OK);
}