Path: blob/main/crypto/heimdal/appl/test/http_client.c
34878 views
/*1* Copyright (c) 2003 - 2005 Kungliga Tekniska Högskolan2* (Royal Institute of Technology, Stockholm, Sweden).3* All rights reserved.4*5* Redistribution and use in source and binary forms, with or without6* modification, are permitted provided that the following conditions7* are met:8*9* 1. Redistributions of source code must retain the above copyright10* notice, this list of conditions and the following disclaimer.11*12* 2. Redistributions in binary form must reproduce the above copyright13* notice, this list of conditions and the following disclaimer in the14* documentation and/or other materials provided with the distribution.15*16* 3. Neither the name of the Institute nor the names of its contributors17* may be used to endorse or promote products derived from this software18* without specific prior written permission.19*20* THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND21* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE22* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE23* ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE24* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL25* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS26* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)27* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT28* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY29* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF30* SUCH DAMAGE.31*/3233#include "test_locl.h"34#include <gssapi/gssapi.h>35#include <gssapi/gssapi_krb5.h>36#include <gssapi/gssapi_spnego.h>37#include "gss_common.h"38#include <base64.h>3940RCSID("$Id$");4142/*43* A simplistic client implementing draft-brezak-spnego-http-04.txt44*/4546static int47do_connect (const char *hostname, const char *port)48{49struct addrinfo *ai, *a;50struct addrinfo hints;51int error;52int s = -1;5354memset (&hints, 0, sizeof(hints));55hints.ai_family = PF_UNSPEC;56hints.ai_socktype = SOCK_STREAM;57hints.ai_protocol = 0;5859error = getaddrinfo (hostname, port, &hints, &ai);60if (error)61errx (1, "getaddrinfo(%s): %s", hostname, gai_strerror(error));6263for (a = ai; a != NULL; a = a->ai_next) {64s = socket (a->ai_family, a->ai_socktype, a->ai_protocol);65if (s < 0)66continue;67if (connect (s, a->ai_addr, a->ai_addrlen) < 0) {68warn ("connect(%s)", hostname);69close (s);70continue;71}72break;73}74freeaddrinfo (ai);75if (a == NULL)76errx (1, "failed to contact %s", hostname);7778return s;79}8081static void82fdprintf(int s, const char *fmt, ...)83{84size_t len;85ssize_t ret;86va_list ap;87char *str, *buf;8889va_start(ap, fmt);90vasprintf(&str, fmt, ap);91va_end(ap);9293if (str == NULL)94errx(1, "vasprintf");9596buf = str;97len = strlen(buf);98while (len) {99ret = write(s, buf, len);100if (ret == 0)101err(1, "connection closed");102else if (ret < 0)103err(1, "error");104len -= ret;105buf += ret;106}107free(str);108}109110static int help_flag;111static int version_flag;112static int verbose_flag;113static int mutual_flag = 1;114static int delegate_flag;115static char *port_str = "http";116static char *gss_service = "HTTP";117118static struct getargs http_args[] = {119{ "verbose", 'v', arg_flag, &verbose_flag, "verbose logging", },120{ "port", 'p', arg_string, &port_str, "port to connect to", "port" },121{ "delegate", 0, arg_flag, &delegate_flag, "gssapi delegate credential" },122{ "gss-service", 's', arg_string, &gss_service, "gssapi service to use",123"service" },124{ "mech", 'm', arg_string, &mech, "gssapi mech to use", "mech" },125{ "mutual", 0, arg_negative_flag, &mutual_flag, "no gssapi mutual auth" },126{ "help", 'h', arg_flag, &help_flag },127{ "version", 0, arg_flag, &version_flag }128};129130static int num_http_args = sizeof(http_args) / sizeof(http_args[0]);131132static void133usage(int code)134{135arg_printusage(http_args, num_http_args, NULL, "host [page]");136exit(code);137}138139/*140*141*/142143struct http_req {144char *response;145char **headers;146int num_headers;147void *body;148size_t body_size;149};150151152static void153http_req_zero(struct http_req *req)154{155req->response = NULL;156req->headers = NULL;157req->num_headers = 0;158req->body = NULL;159req->body_size = 0;160}161162static void163http_req_free(struct http_req *req)164{165int i;166167free(req->response);168for (i = 0; i < req->num_headers; i++)169free(req->headers[i]);170free(req->headers);171free(req->body);172http_req_zero(req);173}174175static const char *176http_find_header(struct http_req *req, const char *header)177{178int i, len = strlen(header);179180for (i = 0; i < req->num_headers; i++) {181if (strncasecmp(header, req->headers[i], len) == 0) {182return req->headers[i] + len + 1;183}184}185return NULL;186}187188189static int190http_query(const char *host, const char *page,191char **headers, int num_headers, struct http_req *req)192{193enum { RESPONSE, HEADER, BODY } state;194ssize_t ret;195char in_buf[1024], *in_ptr = in_buf;196size_t in_len = 0;197int s, i;198199http_req_zero(req);200201s = do_connect(host, port_str);202if (s < 0)203errx(1, "connection failed");204205fdprintf(s, "GET %s HTTP/1.0\r\n", page);206for (i = 0; i < num_headers; i++)207fdprintf(s, "%s\r\n", headers[i]);208fdprintf(s, "Host: %s\r\n\r\n", host);209210state = RESPONSE;211212while (1) {213ret = read (s, in_ptr, sizeof(in_buf) - in_len - 1);214if (ret == 0)215break;216else if (ret < 0)217err (1, "read: %lu", (unsigned long)ret);218219in_buf[ret + in_len] = '\0';220221if (state == HEADER || state == RESPONSE) {222char *p;223224in_len += ret;225in_ptr += ret;226227while (1) {228p = strstr(in_buf, "\r\n");229230if (p == NULL) {231break;232} else if (p == in_buf) {233memmove(in_buf, in_buf + 2, sizeof(in_buf) - 2);234state = BODY;235in_len -= 2;236in_ptr -= 2;237break;238} else if (state == RESPONSE) {239req->response = emalloc(p - in_buf + 1);240memcpy(req->response, in_buf, p - in_buf);241req->response[p - in_buf] = '\0';242state = HEADER;243} else {244req->headers = realloc(req->headers,245(req->num_headers + 1) * sizeof(req->headers[0]));246req->headers[req->num_headers] = emalloc(p - in_buf + 1);247memcpy(req->headers[req->num_headers], in_buf, p - in_buf);248req->headers[req->num_headers][p - in_buf] = '\0';249if (req->headers[req->num_headers] == NULL)250errx(1, "strdup");251req->num_headers++;252}253memmove(in_buf, p + 2, sizeof(in_buf) - (p - in_buf) - 2);254in_len -= (p - in_buf) + 2;255in_ptr -= (p - in_buf) + 2;256}257}258259if (state == BODY) {260261req->body = erealloc(req->body, req->body_size + ret + 1);262263memcpy((char *)req->body + req->body_size, in_buf, ret);264req->body_size += ret;265((char *)req->body)[req->body_size] = '\0';266267in_ptr = in_buf;268in_len = 0;269} else270abort();271}272273if (verbose_flag) {274int i;275printf("response: %s\n", req->response);276for (i = 0; i < req->num_headers; i++)277printf("header[%d] %s\n", i, req->headers[i]);278printf("body: %.*s\n", (int)req->body_size, (char *)req->body);279}280281close(s);282return 0;283}284285286int287main(int argc, char **argv)288{289struct http_req req;290const char *host, *page;291int i, done, print_body, gssapi_done, gssapi_started;292char *headers[10]; /* XXX */293int num_headers;294gss_ctx_id_t context_hdl = GSS_C_NO_CONTEXT;295gss_name_t server = GSS_C_NO_NAME;296int optind = 0;297gss_OID mech_oid;298OM_uint32 flags;299300setprogname(argv[0]);301302if(getarg(http_args, num_http_args, argc, argv, &optind))303usage(1);304305if (help_flag)306usage (0);307308if(version_flag) {309print_version(NULL);310exit(0);311}312313argc -= optind;314argv += optind;315316mech_oid = select_mech(mech);317318if (argc != 1 && argc != 2)319errx(1, "usage: %s host [page]", getprogname());320host = argv[0];321if (argc == 2)322page = argv[1];323else324page = "/";325326flags = 0;327if (delegate_flag)328flags |= GSS_C_DELEG_FLAG;329if (mutual_flag)330flags |= GSS_C_MUTUAL_FLAG;331332done = 0;333num_headers = 0;334gssapi_done = 1;335gssapi_started = 0;336do {337print_body = 0;338339http_query(host, page, headers, num_headers, &req);340for (i = 0 ; i < num_headers; i++)341free(headers[i]);342num_headers = 0;343344if (strstr(req.response, " 200 ") != NULL) {345print_body = 1;346done = 1;347} else if (strstr(req.response, " 401 ") != NULL) {348if (http_find_header(&req, "WWW-Authenticate:") == NULL)349errx(1, "Got %s but missed `WWW-Authenticate'", req.response);350gssapi_done = 0;351}352353if (!gssapi_done) {354const char *h = http_find_header(&req, "WWW-Authenticate:");355if (h == NULL)356errx(1, "Got %s but missed `WWW-Authenticate'", req.response);357358if (strncasecmp(h, "Negotiate", 9) == 0) {359OM_uint32 maj_stat, min_stat;360gss_buffer_desc input_token, output_token;361362if (verbose_flag)363printf("Negotiate found\n");364365if (server == GSS_C_NO_NAME) {366char *name;367asprintf(&name, "%s@%s", gss_service, host);368input_token.length = strlen(name);369input_token.value = name;370371maj_stat = gss_import_name(&min_stat,372&input_token,373GSS_C_NT_HOSTBASED_SERVICE,374&server);375if (GSS_ERROR(maj_stat))376gss_err (1, min_stat, "gss_inport_name");377free(name);378input_token.length = 0;379input_token.value = NULL;380}381382i = 9;383while(h[i] && isspace((unsigned char)h[i]))384i++;385if (h[i] != '\0') {386int len = strlen(&h[i]);387if (len == 0)388errx(1, "invalid Negotiate token");389input_token.value = emalloc(len);390len = base64_decode(&h[i], input_token.value);391if (len < 0)392errx(1, "invalid base64 Negotiate token %s", &h[i]);393input_token.length = len;394} else {395if (gssapi_started)396errx(1, "Negotiate already started");397gssapi_started = 1;398399input_token.length = 0;400input_token.value = NULL;401}402403maj_stat =404gss_init_sec_context(&min_stat,405GSS_C_NO_CREDENTIAL,406&context_hdl,407server,408mech_oid,409flags,4100,411GSS_C_NO_CHANNEL_BINDINGS,412&input_token,413NULL,414&output_token,415NULL,416NULL);417if (GSS_ERROR(maj_stat))418gss_err (1, min_stat, "gss_init_sec_context");419else if (maj_stat & GSS_S_CONTINUE_NEEDED)420gssapi_done = 0;421else {422gss_name_t targ_name, src_name;423gss_buffer_desc name_buffer;424gss_OID mech_type;425426gssapi_done = 1;427428printf("Negotiate done: %s\n", mech);429430maj_stat = gss_inquire_context(&min_stat,431context_hdl,432&src_name,433&targ_name,434NULL,435&mech_type,436NULL,437NULL,438NULL);439if (GSS_ERROR(maj_stat))440gss_err (1, min_stat, "gss_inquire_context");441442maj_stat = gss_display_name(&min_stat,443src_name,444&name_buffer,445NULL);446if (GSS_ERROR(maj_stat))447gss_err (1, min_stat, "gss_display_name");448449printf("Source: %.*s\n",450(int)name_buffer.length,451(char *)name_buffer.value);452453gss_release_buffer(&min_stat, &name_buffer);454455maj_stat = gss_display_name(&min_stat,456targ_name,457&name_buffer,458NULL);459if (GSS_ERROR(maj_stat))460gss_err (1, min_stat, "gss_display_name");461462printf("Target: %.*s\n",463(int)name_buffer.length,464(char *)name_buffer.value);465466gss_release_name(&min_stat, &targ_name);467gss_release_buffer(&min_stat, &name_buffer);468}469470if (output_token.length) {471char *neg_token;472473base64_encode(output_token.value,474output_token.length,475&neg_token);476477asprintf(&headers[0], "Authorization: Negotiate %s",478neg_token);479480num_headers = 1;481free(neg_token);482gss_release_buffer(&min_stat, &output_token);483}484if (input_token.length)485free(input_token.value);486487} else488done = 1;489} else490done = 1;491492if (verbose_flag) {493printf("%s\n\n", req.response);494495for (i = 0; i < req.num_headers; i++)496printf("%s\n", req.headers[i]);497printf("\n");498}499if (print_body || verbose_flag)500printf("%.*s\n", (int)req.body_size, (char *)req.body);501502http_req_free(&req);503} while (!done);504505if (gssapi_done == 0)506errx(1, "gssapi not done but http dance done");507508return 0;509}510511512