2 * Copyright (c) 2003 - 2005 Kungliga Tekniska Högskolan
3 * (Royal Institute of Technology, Stockholm, Sweden).
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
17 * 3. Neither the name of the Institute nor the names of its contributors
18 * may be used to endorse or promote products derived from this software
19 * without specific prior written permission.
21 * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24 * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34 #include "test_locl.h"
36 #include "gss_common.h"
39 __RCSID("$Heimdal: http_client.c 14861 2005-04-20 10:38:37Z lha $"
43 * A simplistic client implementing draft-brezak-spnego-http-04.txt
47 do_connect (const char *hostname
, const char *port
)
49 struct addrinfo
*ai
, *a
;
50 struct addrinfo hints
;
54 memset (&hints
, 0, sizeof(hints
));
55 hints
.ai_family
= PF_UNSPEC
;
56 hints
.ai_socktype
= SOCK_STREAM
;
57 hints
.ai_protocol
= 0;
59 error
= getaddrinfo (hostname
, port
, &hints
, &ai
);
61 errx (1, "getaddrinfo(%s): %s", hostname
, gai_strerror(error
));
63 for (a
= ai
; a
!= NULL
; a
= a
->ai_next
) {
64 s
= socket (a
->ai_family
, a
->ai_socktype
, a
->ai_protocol
);
67 if (connect (s
, a
->ai_addr
, a
->ai_addrlen
) < 0) {
68 warn ("connect(%s)", hostname
);
76 errx (1, "failed to contact %s", hostname
);
82 fdprintf(int s
, const char *fmt
, ...)
90 vasprintf(&str
, fmt
, ap
);
99 ret
= write(s
, buf
, len
);
101 err(1, "connection closed");
110 static int help_flag
;
111 static int version_flag
;
112 static int verbose_flag
;
113 static int mutual_flag
= 1;
114 static int delegate_flag
;
115 static char *port_str
= "http";
116 static char *gss_service
= "HTTP";
118 static 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",
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
}
130 static int num_http_args
= sizeof(http_args
) / sizeof(http_args
[0]);
135 arg_printusage(http_args
, num_http_args
, NULL
, "host [page]");
153 http_req_zero(struct http_req
*req
)
155 req
->response
= NULL
;
157 req
->num_headers
= 0;
163 http_req_free(struct http_req
*req
)
168 for (i
= 0; i
< req
->num_headers
; i
++)
169 free(req
->headers
[i
]);
176 http_find_header(struct http_req
*req
, const char *header
)
178 int i
, len
= strlen(header
);
180 for (i
= 0; i
< req
->num_headers
; i
++) {
181 if (strncasecmp(header
, req
->headers
[i
], len
) == 0) {
182 return req
->headers
[i
] + len
+ 1;
190 http_query(const char *host
, const char *page
,
191 char **headers
, int num_headers
, struct http_req
*req
)
193 enum { RESPONSE
, HEADER
, BODY
} state
;
195 char in_buf
[1024], *in_ptr
= in_buf
;
201 s
= do_connect(host
, port_str
);
203 errx(1, "connection failed");
205 fdprintf(s
, "GET %s HTTP/1.0\r\n", page
);
206 for (i
= 0; i
< num_headers
; i
++)
207 fdprintf(s
, "%s\r\n", headers
[i
]);
208 fdprintf(s
, "Host: %s\r\n\r\n", host
);
213 ret
= read (s
, in_ptr
, sizeof(in_buf
) - in_len
- 1);
217 err (1, "read: %lu", (unsigned long)ret
);
219 in_buf
[ret
+ in_len
] = '\0';
221 if (state
== HEADER
|| state
== RESPONSE
) {
228 p
= strstr(in_buf
, "\r\n");
232 } else if (p
== in_buf
) {
233 memmove(in_buf
, in_buf
+ 2, sizeof(in_buf
) - 2);
238 } else if (state
== RESPONSE
) {
239 req
->response
= strndup(in_buf
, p
- in_buf
);
242 req
->headers
= realloc(req
->headers
,
243 (req
->num_headers
+ 1) * sizeof(req
->headers
[0]));
244 req
->headers
[req
->num_headers
] = strndup(in_buf
, p
- in_buf
);
245 if (req
->headers
[req
->num_headers
] == NULL
)
249 memmove(in_buf
, p
+ 2, sizeof(in_buf
) - (p
- in_buf
) - 2);
250 in_len
-= (p
- in_buf
) + 2;
251 in_ptr
-= (p
- in_buf
) + 2;
257 req
->body
= erealloc(req
->body
, req
->body_size
+ ret
+ 1);
259 memcpy((char *)req
->body
+ req
->body_size
, in_buf
, ret
);
260 req
->body_size
+= ret
;
261 ((char *)req
->body
)[req
->body_size
] = '\0';
271 printf("response: %s\n", req
->response
);
272 for (i
= 0; i
< req
->num_headers
; i
++)
273 printf("header[%d] %s\n", i
, req
->headers
[i
]);
274 printf("body: %.*s\n", (int)req
->body_size
, (char *)req
->body
);
283 main(int argc
, char **argv
)
286 const char *host
, *page
;
287 int i
, done
, print_body
, gssapi_done
, gssapi_started
;
288 char *headers
[10]; /* XXX */
290 gss_ctx_id_t context_hdl
= GSS_C_NO_CONTEXT
;
291 gss_name_t server
= GSS_C_NO_NAME
;
296 setprogname(argv
[0]);
298 if(getarg(http_args
, num_http_args
, argc
, argv
, &optind
))
312 mech_oid
= select_mech(mech
);
314 if (argc
!= 1 && argc
!= 2)
315 errx(1, "usage: %s host [page]", getprogname());
324 flags
|= GSS_C_DELEG_FLAG
;
326 flags
|= GSS_C_MUTUAL_FLAG
;
335 http_query(host
, page
, headers
, num_headers
, &req
);
336 for (i
= 0 ; i
< num_headers
; i
++)
340 if (strstr(req
.response
, " 200 ") != NULL
) {
343 } else if (strstr(req
.response
, " 401 ") != NULL
) {
344 if (http_find_header(&req
, "WWW-Authenticate:") == NULL
)
345 errx(1, "Got %s but missed `WWW-Authenticate'", req
.response
);
350 const char *h
= http_find_header(&req
, "WWW-Authenticate:");
352 errx(1, "Got %s but missed `WWW-Authenticate'", req
.response
);
354 if (strncasecmp(h
, "Negotiate", 9) == 0) {
355 OM_uint32 maj_stat
, min_stat
;
356 gss_buffer_desc input_token
, output_token
;
359 printf("Negotiate found\n");
361 if (server
== GSS_C_NO_NAME
) {
363 asprintf(&name
, "%s@%s", gss_service
, host
);
364 input_token
.length
= strlen(name
);
365 input_token
.value
= name
;
367 maj_stat
= gss_import_name(&min_stat
,
369 GSS_C_NT_HOSTBASED_SERVICE
,
371 if (GSS_ERROR(maj_stat
))
372 gss_err (1, min_stat
, "gss_inport_name");
374 input_token
.length
= 0;
375 input_token
.value
= NULL
;
379 while(h
[i
] && isspace((unsigned char)h
[i
]))
382 int len
= strlen(&h
[i
]);
384 errx(1, "invalid Negotiate token");
385 input_token
.value
= emalloc(len
);
386 len
= base64_decode(&h
[i
], input_token
.value
);
388 errx(1, "invalid base64 Negotiate token %s", &h
[i
]);
389 input_token
.length
= len
;
392 errx(1, "Negotiate already started");
395 input_token
.length
= 0;
396 input_token
.value
= NULL
;
400 gss_init_sec_context(&min_stat
,
407 GSS_C_NO_CHANNEL_BINDINGS
,
413 if (GSS_ERROR(maj_stat
))
414 gss_err (1, min_stat
, "gss_init_sec_context");
415 else if (maj_stat
& GSS_S_CONTINUE_NEEDED
)
418 gss_name_t targ_name
, src_name
;
419 gss_buffer_desc name_buffer
;
424 printf("Negotiate done: %s\n", mech
);
426 maj_stat
= gss_inquire_context(&min_stat
,
435 if (GSS_ERROR(maj_stat
))
436 gss_err (1, min_stat
, "gss_inquire_context");
438 maj_stat
= gss_display_name(&min_stat
,
442 if (GSS_ERROR(maj_stat
))
443 gss_err (1, min_stat
, "gss_display_name");
445 printf("Source: %.*s\n",
446 (int)name_buffer
.length
,
447 (char *)name_buffer
.value
);
449 gss_release_buffer(&min_stat
, &name_buffer
);
451 maj_stat
= gss_display_name(&min_stat
,
455 if (GSS_ERROR(maj_stat
))
456 gss_err (1, min_stat
, "gss_display_name");
458 printf("Target: %.*s\n",
459 (int)name_buffer
.length
,
460 (char *)name_buffer
.value
);
462 gss_release_name(&min_stat
, &targ_name
);
463 gss_release_buffer(&min_stat
, &name_buffer
);
466 if (output_token
.length
) {
469 base64_encode(output_token
.value
,
473 asprintf(&headers
[0], "Authorization: Negotiate %s",
478 gss_release_buffer(&min_stat
, &output_token
);
480 if (input_token
.length
)
481 free(input_token
.value
);
489 printf("%s\n\n", req
.response
);
491 for (i
= 0; i
< req
.num_headers
; i
++)
492 printf("%s\n", req
.headers
[i
]);
495 if (print_body
|| verbose_flag
)
496 printf("%.*s\n", (int)req
.body_size
, (char *)req
.body
);
501 if (gssapi_done
== 0)
502 errx(1, "gssapi not done but http dance done");