struct duo_ctx * duo_close(struct duo_ctx *ctx) { if (ctx != NULL) { if (ctx->https != NULL) https_close(&ctx->https); duo_reset(ctx); free(ctx->host); free(ctx); } return (NULL); }
static duo_code_t duo_call(struct duo_ctx *ctx, const char *method, const char *uri) { int i, code, err, ret; code = 0; ctx->body = NULL; ctx->body_len = 0; for (i = 0; i < 3; i++) { if (ctx->https == NULL && (err = https_open(&ctx->https, ctx->host)) != HTTPS_OK) { if (err == HTTPS_ERR_SERVER) { sleep(1 << i); continue; } break; } if ((err = https_send(ctx->https, method, uri, ctx->argc, ctx->argv)) == HTTPS_OK && (err = https_recv(ctx->https, &code, &ctx->body, &ctx->body_len)) == HTTPS_OK) { break; } https_close(&ctx->https); } duo_reset(ctx); if (code == 0) { ret = DUO_CONN_ERROR; _duo_seterr(ctx, "Couldn't connect to %s: %s\n", ctx->host, https_geterr()); } else if (code / 100 == 2) { /* 2xx indicates DUO_OK */ ret = DUO_OK; } else if (code == 401) { /* 401 indicates an invalid ikey or skey */ ret = DUO_CLIENT_ERROR; _duo_seterr(ctx, "Invalid ikey or skey"); } else if (code / 100 == 5) { /* 5xx indicates an internal server error */ ret = DUO_SERVER_ERROR; _duo_seterr(ctx, "HTTP %d", code); } else { /* abort on any other HTTP codes */ ret = DUO_ABORT; _duo_seterr(ctx, "HTTP %d", code); } return (ret); }
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); }