1178825Sdfr/*
2178825Sdfr * Copyright (c) 2003 - 2005 Kungliga Tekniska H�gskolan
3178825Sdfr * (Royal Institute of Technology, Stockholm, Sweden).
4178825Sdfr * All rights reserved.
5178825Sdfr *
6178825Sdfr * Redistribution and use in source and binary forms, with or without
7178825Sdfr * modification, are permitted provided that the following conditions
8178825Sdfr * are met:
9178825Sdfr *
10178825Sdfr * 1. Redistributions of source code must retain the above copyright
11178825Sdfr *    notice, this list of conditions and the following disclaimer.
12178825Sdfr *
13178825Sdfr * 2. Redistributions in binary form must reproduce the above copyright
14178825Sdfr *    notice, this list of conditions and the following disclaimer in the
15178825Sdfr *    documentation and/or other materials provided with the distribution.
16178825Sdfr *
17178825Sdfr * 3. Neither the name of the Institute nor the names of its contributors
18178825Sdfr *    may be used to endorse or promote products derived from this software
19178825Sdfr *    without specific prior written permission.
20178825Sdfr *
21178825Sdfr * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
22178825Sdfr * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23178825Sdfr * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24178825Sdfr * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
25178825Sdfr * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26178825Sdfr * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27178825Sdfr * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28178825Sdfr * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29178825Sdfr * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30178825Sdfr * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31178825Sdfr * SUCH DAMAGE.
32178825Sdfr */
33178825Sdfr
34178825Sdfr#include "test_locl.h"
35178825Sdfr#include <gssapi.h>
36178825Sdfr#include "gss_common.h"
37178825Sdfr#include <base64.h>
38178825Sdfr
39178825SdfrRCSID("$Id: http_client.c 14861 2005-04-20 10:38:37Z lha $");
40178825Sdfr
41178825Sdfr/*
42178825Sdfr * A simplistic client implementing draft-brezak-spnego-http-04.txt
43178825Sdfr */
44178825Sdfr
45178825Sdfrstatic int
46178825Sdfrdo_connect (const char *hostname, const char *port)
47178825Sdfr{
48178825Sdfr    struct addrinfo *ai, *a;
49178825Sdfr    struct addrinfo hints;
50178825Sdfr    int error;
51178825Sdfr    int s = -1;
52178825Sdfr
53178825Sdfr    memset (&hints, 0, sizeof(hints));
54178825Sdfr    hints.ai_family = PF_UNSPEC;
55178825Sdfr    hints.ai_socktype = SOCK_STREAM;
56178825Sdfr    hints.ai_protocol = 0;
57178825Sdfr
58178825Sdfr    error = getaddrinfo (hostname, port, &hints, &ai);
59178825Sdfr    if (error)
60178825Sdfr	errx (1, "getaddrinfo(%s): %s", hostname, gai_strerror(error));
61178825Sdfr
62178825Sdfr    for (a = ai; a != NULL; a = a->ai_next) {
63178825Sdfr	s = socket (a->ai_family, a->ai_socktype, a->ai_protocol);
64178825Sdfr	if (s < 0)
65178825Sdfr	    continue;
66178825Sdfr	if (connect (s, a->ai_addr, a->ai_addrlen) < 0) {
67178825Sdfr	    warn ("connect(%s)", hostname);
68178825Sdfr 	    close (s);
69178825Sdfr 	    continue;
70178825Sdfr	}
71178825Sdfr	break;
72178825Sdfr    }
73178825Sdfr    freeaddrinfo (ai);
74178825Sdfr    if (a == NULL)
75178825Sdfr	errx (1, "failed to contact %s", hostname);
76178825Sdfr
77178825Sdfr    return s;
78178825Sdfr}
79178825Sdfr
80178825Sdfrstatic void
81178825Sdfrfdprintf(int s, const char *fmt, ...)
82178825Sdfr{
83178825Sdfr    size_t len;
84178825Sdfr    ssize_t ret;
85178825Sdfr    va_list ap;
86178825Sdfr    char *str, *buf;
87178825Sdfr
88178825Sdfr    va_start(ap, fmt);
89178825Sdfr    vasprintf(&str, fmt, ap);
90178825Sdfr    va_end(ap);
91178825Sdfr
92178825Sdfr    if (str == NULL)
93178825Sdfr	errx(1, "vasprintf");
94178825Sdfr
95178825Sdfr    buf = str;
96178825Sdfr    len = strlen(buf);
97178825Sdfr    while (len) {
98178825Sdfr	ret = write(s, buf, len);
99178825Sdfr	if (ret == 0)
100178825Sdfr	    err(1, "connection closed");
101178825Sdfr	else if (ret < 0)
102178825Sdfr	    err(1, "error");
103178825Sdfr	len -= ret;
104178825Sdfr	buf += ret;
105178825Sdfr    }
106178825Sdfr    free(str);
107178825Sdfr}
108178825Sdfr
109178825Sdfrstatic int help_flag;
110178825Sdfrstatic int version_flag;
111178825Sdfrstatic int verbose_flag;
112178825Sdfrstatic int mutual_flag = 1;
113178825Sdfrstatic int delegate_flag;
114178825Sdfrstatic char *port_str = "http";
115178825Sdfrstatic char *gss_service = "HTTP";
116178825Sdfr
117178825Sdfrstatic struct getargs http_args[] = {
118178825Sdfr    { "verbose", 'v', arg_flag, &verbose_flag, "verbose logging", },
119178825Sdfr    { "port", 'p', arg_string, &port_str, "port to connect to", "port" },
120178825Sdfr    { "delegate", 0, arg_flag, &delegate_flag, "gssapi delegate credential" },
121178825Sdfr    { "gss-service", 's', arg_string, &gss_service, "gssapi service to use",
122178825Sdfr      "service" },
123178825Sdfr    { "mech", 'm', arg_string, &mech, "gssapi mech to use", "mech" },
124178825Sdfr    { "mutual", 0, arg_negative_flag, &mutual_flag, "no gssapi mutual auth" },
125178825Sdfr    { "help", 'h', arg_flag, &help_flag },
126178825Sdfr    { "version", 0, arg_flag, &version_flag }
127178825Sdfr};
128178825Sdfr
129178825Sdfrstatic int num_http_args = sizeof(http_args) / sizeof(http_args[0]);
130178825Sdfr
131178825Sdfrstatic void
132178825Sdfrusage(int code)
133178825Sdfr{
134178825Sdfr    arg_printusage(http_args, num_http_args, NULL, "host [page]");
135178825Sdfr    exit(code);
136178825Sdfr}
137178825Sdfr
138178825Sdfr/*
139178825Sdfr *
140178825Sdfr */
141178825Sdfr
142178825Sdfrstruct http_req {
143178825Sdfr    char *response;
144178825Sdfr    char **headers;
145178825Sdfr    int num_headers;
146178825Sdfr    void *body;
147178825Sdfr    size_t body_size;
148178825Sdfr};
149178825Sdfr
150178825Sdfr
151178825Sdfrstatic void
152178825Sdfrhttp_req_zero(struct http_req *req)
153178825Sdfr{
154178825Sdfr    req->response = NULL;
155178825Sdfr    req->headers = NULL;
156178825Sdfr    req->num_headers = 0;
157178825Sdfr    req->body = NULL;
158178825Sdfr    req->body_size = 0;
159178825Sdfr}
160178825Sdfr
161178825Sdfrstatic void
162178825Sdfrhttp_req_free(struct http_req *req)
163178825Sdfr{
164178825Sdfr    int i;
165178825Sdfr
166178825Sdfr    free(req->response);
167178825Sdfr    for (i = 0; i < req->num_headers; i++)
168178825Sdfr	free(req->headers[i]);
169178825Sdfr    free(req->headers);
170178825Sdfr    free(req->body);
171178825Sdfr    http_req_zero(req);
172178825Sdfr}
173178825Sdfr
174178825Sdfrstatic const char *
175178825Sdfrhttp_find_header(struct http_req *req, const char *header)
176178825Sdfr{
177178825Sdfr    int i, len = strlen(header);
178178825Sdfr
179178825Sdfr    for (i = 0; i < req->num_headers; i++) {
180178825Sdfr	if (strncasecmp(header, req->headers[i], len) == 0) {
181178825Sdfr	    return req->headers[i] + len + 1;
182178825Sdfr	}
183178825Sdfr    }
184178825Sdfr    return NULL;
185178825Sdfr}
186178825Sdfr
187178825Sdfr
188178825Sdfrstatic int
189178825Sdfrhttp_query(const char *host, const char *page,
190178825Sdfr	   char **headers, int num_headers, struct http_req *req)
191178825Sdfr{
192178825Sdfr    enum { RESPONSE, HEADER, BODY } state;
193178825Sdfr    ssize_t ret;
194178825Sdfr    char in_buf[1024], *in_ptr = in_buf;
195178825Sdfr    size_t in_len = 0;
196178825Sdfr    int s, i;
197178825Sdfr
198178825Sdfr    http_req_zero(req);
199178825Sdfr
200178825Sdfr    s = do_connect(host, port_str);
201178825Sdfr    if (s < 0)
202178825Sdfr	errx(1, "connection failed");
203178825Sdfr
204178825Sdfr    fdprintf(s, "GET %s HTTP/1.0\r\n", page);
205178825Sdfr    for (i = 0; i < num_headers; i++)
206178825Sdfr	fdprintf(s, "%s\r\n", headers[i]);
207178825Sdfr    fdprintf(s, "Host: %s\r\n\r\n", host);
208178825Sdfr
209178825Sdfr    state = RESPONSE;
210178825Sdfr
211178825Sdfr    while (1) {
212178825Sdfr	ret = read (s, in_ptr, sizeof(in_buf) - in_len - 1);
213178825Sdfr	if (ret == 0)
214178825Sdfr	    break;
215178825Sdfr	else if (ret < 0)
216178825Sdfr	    err (1, "read: %lu", (unsigned long)ret);
217178825Sdfr
218178825Sdfr	in_buf[ret + in_len] = '\0';
219178825Sdfr
220178825Sdfr	if (state == HEADER || state == RESPONSE) {
221178825Sdfr	    char *p;
222178825Sdfr
223178825Sdfr	    in_len += ret;
224178825Sdfr	    in_ptr += ret;
225178825Sdfr
226178825Sdfr	    while (1) {
227178825Sdfr		p = strstr(in_buf, "\r\n");
228178825Sdfr
229178825Sdfr		if (p == NULL) {
230178825Sdfr		    break;
231178825Sdfr		} else if (p == in_buf) {
232178825Sdfr		    memmove(in_buf, in_buf + 2, sizeof(in_buf) - 2);
233178825Sdfr		    state = BODY;
234178825Sdfr		    in_len -= 2;
235178825Sdfr		    in_ptr -= 2;
236178825Sdfr		    break;
237178825Sdfr		} else if (state == RESPONSE) {
238178825Sdfr		    req->response = strndup(in_buf, p - in_buf);
239178825Sdfr		    state = HEADER;
240178825Sdfr		} else {
241178825Sdfr		    req->headers = realloc(req->headers,
242178825Sdfr					   (req->num_headers + 1) * sizeof(req->headers[0]));
243178825Sdfr		    req->headers[req->num_headers] = strndup(in_buf, p - in_buf);
244178825Sdfr		    if (req->headers[req->num_headers] == NULL)
245178825Sdfr			errx(1, "strdup");
246178825Sdfr		    req->num_headers++;
247178825Sdfr		}
248178825Sdfr		memmove(in_buf, p + 2, sizeof(in_buf) - (p - in_buf) - 2);
249178825Sdfr		in_len -= (p - in_buf) + 2;
250178825Sdfr		in_ptr -= (p - in_buf) + 2;
251178825Sdfr	    }
252178825Sdfr	}
253178825Sdfr
254178825Sdfr	if (state == BODY) {
255178825Sdfr
256178825Sdfr	    req->body = erealloc(req->body, req->body_size + ret + 1);
257178825Sdfr
258178825Sdfr	    memcpy((char *)req->body + req->body_size, in_buf, ret);
259178825Sdfr	    req->body_size += ret;
260178825Sdfr	    ((char *)req->body)[req->body_size] = '\0';
261178825Sdfr
262178825Sdfr	    in_ptr = in_buf;
263178825Sdfr	    in_len = 0;
264178825Sdfr	} else
265178825Sdfr	    abort();
266178825Sdfr    }
267178825Sdfr
268178825Sdfr    if (verbose_flag) {
269178825Sdfr	int i;
270178825Sdfr	printf("response: %s\n", req->response);
271178825Sdfr	for (i = 0; i < req->num_headers; i++)
272178825Sdfr	    printf("header[%d] %s\n", i, req->headers[i]);
273178825Sdfr	printf("body: %.*s\n", (int)req->body_size, (char *)req->body);
274178825Sdfr    }
275178825Sdfr
276178825Sdfr    close(s);
277178825Sdfr    return 0;
278178825Sdfr}
279178825Sdfr
280178825Sdfr
281178825Sdfrint
282178825Sdfrmain(int argc, char **argv)
283178825Sdfr{
284178825Sdfr    struct http_req req;
285178825Sdfr    const char *host, *page;
286178825Sdfr    int i, done, print_body, gssapi_done, gssapi_started;
287178825Sdfr    char *headers[10]; /* XXX */
288178825Sdfr    int num_headers;
289178825Sdfr    gss_ctx_id_t context_hdl = GSS_C_NO_CONTEXT;
290178825Sdfr    gss_name_t server = GSS_C_NO_NAME;
291178825Sdfr    int optind = 0;
292178825Sdfr    gss_OID mech_oid;
293178825Sdfr    OM_uint32 flags;
294178825Sdfr
295178825Sdfr    setprogname(argv[0]);
296178825Sdfr
297178825Sdfr    if(getarg(http_args, num_http_args, argc, argv, &optind))
298178825Sdfr	usage(1);
299178825Sdfr
300178825Sdfr    if (help_flag)
301178825Sdfr	usage (0);
302178825Sdfr
303178825Sdfr    if(version_flag) {
304178825Sdfr	print_version(NULL);
305178825Sdfr	exit(0);
306178825Sdfr    }
307178825Sdfr
308178825Sdfr    argc -= optind;
309178825Sdfr    argv += optind;
310178825Sdfr
311178825Sdfr    mech_oid = select_mech(mech);
312178825Sdfr
313178825Sdfr    if (argc != 1 && argc != 2)
314178825Sdfr	errx(1, "usage: %s host [page]", getprogname());
315178825Sdfr    host = argv[0];
316178825Sdfr    if (argc == 2)
317178825Sdfr	page = argv[1];
318178825Sdfr    else
319178825Sdfr	page = "/";
320178825Sdfr
321178825Sdfr    flags = 0;
322178825Sdfr    if (delegate_flag)
323178825Sdfr	flags |= GSS_C_DELEG_FLAG;
324178825Sdfr    if (mutual_flag)
325178825Sdfr	flags |= GSS_C_MUTUAL_FLAG;
326178825Sdfr
327178825Sdfr    done = 0;
328178825Sdfr    num_headers = 0;
329178825Sdfr    gssapi_done = 1;
330178825Sdfr    gssapi_started = 0;
331178825Sdfr    do {
332178825Sdfr	print_body = 0;
333178825Sdfr
334178825Sdfr	http_query(host, page, headers, num_headers, &req);
335178825Sdfr	for (i = 0 ; i < num_headers; i++)
336178825Sdfr	    free(headers[i]);
337178825Sdfr	num_headers = 0;
338178825Sdfr
339178825Sdfr	if (strstr(req.response, " 200 ") != NULL) {
340178825Sdfr	    print_body = 1;
341178825Sdfr	    done = 1;
342178825Sdfr	} else if (strstr(req.response, " 401 ") != NULL) {
343178825Sdfr	    if (http_find_header(&req, "WWW-Authenticate:") == NULL)
344178825Sdfr		errx(1, "Got %s but missed `WWW-Authenticate'", req.response);
345178825Sdfr	    gssapi_done = 0;
346178825Sdfr	}
347178825Sdfr
348178825Sdfr	if (!gssapi_done) {
349178825Sdfr	    const char *h = http_find_header(&req, "WWW-Authenticate:");
350178825Sdfr	    if (h == NULL)
351178825Sdfr		errx(1, "Got %s but missed `WWW-Authenticate'", req.response);
352178825Sdfr
353178825Sdfr	    if (strncasecmp(h, "Negotiate", 9) == 0) {
354178825Sdfr		OM_uint32 maj_stat, min_stat;
355178825Sdfr		gss_buffer_desc input_token, output_token;
356178825Sdfr
357178825Sdfr		if (verbose_flag)
358178825Sdfr		    printf("Negotiate found\n");
359178825Sdfr
360178825Sdfr		if (server == GSS_C_NO_NAME) {
361178825Sdfr		    char *name;
362178825Sdfr		    asprintf(&name, "%s@%s", gss_service, host);
363178825Sdfr		    input_token.length = strlen(name);
364178825Sdfr		    input_token.value = name;
365178825Sdfr
366178825Sdfr		    maj_stat = gss_import_name(&min_stat,
367178825Sdfr					       &input_token,
368178825Sdfr					       GSS_C_NT_HOSTBASED_SERVICE,
369178825Sdfr					       &server);
370178825Sdfr		    if (GSS_ERROR(maj_stat))
371178825Sdfr			gss_err (1, min_stat, "gss_inport_name");
372178825Sdfr		    free(name);
373178825Sdfr		    input_token.length = 0;
374178825Sdfr		    input_token.value = NULL;
375178825Sdfr		}
376178825Sdfr
377178825Sdfr		i = 9;
378178825Sdfr		while(h[i] && isspace((unsigned char)h[i]))
379178825Sdfr		    i++;
380178825Sdfr		if (h[i] != '\0') {
381178825Sdfr		    int len = strlen(&h[i]);
382178825Sdfr		    if (len == 0)
383178825Sdfr			errx(1, "invalid Negotiate token");
384178825Sdfr		    input_token.value = emalloc(len);
385178825Sdfr		    len = base64_decode(&h[i], input_token.value);
386178825Sdfr		    if (len < 0)
387178825Sdfr			errx(1, "invalid base64 Negotiate token %s", &h[i]);
388178825Sdfr		    input_token.length = len;
389178825Sdfr		} else {
390178825Sdfr		    if (gssapi_started)
391178825Sdfr			errx(1, "Negotiate already started");
392178825Sdfr		    gssapi_started = 1;
393178825Sdfr
394178825Sdfr		    input_token.length = 0;
395178825Sdfr		    input_token.value = NULL;
396178825Sdfr		}
397178825Sdfr
398178825Sdfr		maj_stat =
399178825Sdfr		    gss_init_sec_context(&min_stat,
400178825Sdfr					 GSS_C_NO_CREDENTIAL,
401178825Sdfr					 &context_hdl,
402178825Sdfr					 server,
403178825Sdfr					 mech_oid,
404178825Sdfr					 flags,
405178825Sdfr					 0,
406178825Sdfr					 GSS_C_NO_CHANNEL_BINDINGS,
407178825Sdfr					 &input_token,
408178825Sdfr					 NULL,
409178825Sdfr					 &output_token,
410178825Sdfr					 NULL,
411178825Sdfr					 NULL);
412178825Sdfr		if (GSS_ERROR(maj_stat))
413178825Sdfr		    gss_err (1, min_stat, "gss_init_sec_context");
414178825Sdfr		else if (maj_stat & GSS_S_CONTINUE_NEEDED)
415178825Sdfr		    gssapi_done = 0;
416178825Sdfr		else {
417178825Sdfr		    gss_name_t targ_name, src_name;
418178825Sdfr		    gss_buffer_desc name_buffer;
419178825Sdfr		    gss_OID mech_type;
420178825Sdfr
421178825Sdfr		    gssapi_done = 1;
422178825Sdfr
423178825Sdfr		    printf("Negotiate done: %s\n", mech);
424178825Sdfr
425178825Sdfr		    maj_stat = gss_inquire_context(&min_stat,
426178825Sdfr						   context_hdl,
427178825Sdfr						   &src_name,
428178825Sdfr						   &targ_name,
429178825Sdfr						   NULL,
430178825Sdfr						   &mech_type,
431178825Sdfr						   NULL,
432178825Sdfr						   NULL,
433178825Sdfr						   NULL);
434178825Sdfr		    if (GSS_ERROR(maj_stat))
435178825Sdfr			gss_err (1, min_stat, "gss_inquire_context");
436178825Sdfr
437178825Sdfr		    maj_stat = gss_display_name(&min_stat,
438178825Sdfr						src_name,
439178825Sdfr						&name_buffer,
440178825Sdfr						NULL);
441178825Sdfr		    if (GSS_ERROR(maj_stat))
442178825Sdfr			gss_err (1, min_stat, "gss_display_name");
443178825Sdfr
444178825Sdfr		    printf("Source: %.*s\n",
445178825Sdfr			   (int)name_buffer.length,
446178825Sdfr			   (char *)name_buffer.value);
447178825Sdfr
448178825Sdfr		    gss_release_buffer(&min_stat, &name_buffer);
449178825Sdfr
450178825Sdfr		    maj_stat = gss_display_name(&min_stat,
451178825Sdfr						targ_name,
452178825Sdfr						&name_buffer,
453178825Sdfr						NULL);
454178825Sdfr		    if (GSS_ERROR(maj_stat))
455178825Sdfr			gss_err (1, min_stat, "gss_display_name");
456178825Sdfr
457178825Sdfr		    printf("Target: %.*s\n",
458178825Sdfr			   (int)name_buffer.length,
459178825Sdfr			   (char *)name_buffer.value);
460178825Sdfr
461178825Sdfr		    gss_release_name(&min_stat, &targ_name);
462178825Sdfr		    gss_release_buffer(&min_stat, &name_buffer);
463178825Sdfr		}
464178825Sdfr
465178825Sdfr		if (output_token.length) {
466178825Sdfr		    char *neg_token;
467178825Sdfr
468178825Sdfr		    base64_encode(output_token.value,
469178825Sdfr				  output_token.length,
470178825Sdfr				  &neg_token);
471178825Sdfr
472178825Sdfr		    asprintf(&headers[0], "Authorization: Negotiate %s",
473178825Sdfr			     neg_token);
474178825Sdfr
475178825Sdfr		    num_headers = 1;
476178825Sdfr		    free(neg_token);
477178825Sdfr		    gss_release_buffer(&min_stat, &output_token);
478178825Sdfr		}
479178825Sdfr		if (input_token.length)
480178825Sdfr		    free(input_token.value);
481178825Sdfr
482178825Sdfr	    } else
483178825Sdfr		done = 1;
484178825Sdfr	} else
485178825Sdfr	    done = 1;
486178825Sdfr
487178825Sdfr	if (verbose_flag) {
488178825Sdfr	    printf("%s\n\n", req.response);
489178825Sdfr
490178825Sdfr	    for (i = 0; i < req.num_headers; i++)
491178825Sdfr		printf("%s\n", req.headers[i]);
492178825Sdfr	    printf("\n");
493178825Sdfr	}
494178825Sdfr	if (print_body || verbose_flag)
495178825Sdfr	    printf("%.*s\n", (int)req.body_size, (char *)req.body);
496178825Sdfr
497178825Sdfr	http_req_free(&req);
498178825Sdfr    } while (!done);
499178825Sdfr
500178825Sdfr    if (gssapi_done == 0)
501178825Sdfr	errx(1, "gssapi not done but http dance done");
502178825Sdfr
503178825Sdfr    return 0;
504178825Sdfr}
505