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"
42 * A simplistic client implementing draft-brezak-spnego-http-04.txt
46 do_connect (const char *hostname
, const char *port
)
48 struct addrinfo
*ai
, *a
;
49 struct addrinfo hints
;
53 memset (&hints
, 0, sizeof(hints
));
54 hints
.ai_family
= PF_UNSPEC
;
55 hints
.ai_socktype
= SOCK_STREAM
;
56 hints
.ai_protocol
= 0;
58 error
= getaddrinfo (hostname
, port
, &hints
, &ai
);
60 errx (1, "getaddrinfo(%s): %s", hostname
, gai_strerror(error
));
62 for (a
= ai
; a
!= NULL
; a
= a
->ai_next
) {
63 s
= socket (a
->ai_family
, a
->ai_socktype
, a
->ai_protocol
);
66 if (connect (s
, a
->ai_addr
, a
->ai_addrlen
) < 0) {
67 warn ("connect(%s)", hostname
);
75 errx (1, "failed to contact %s", hostname
);
81 fdprintf(int s
, const char *fmt
, ...)
89 vasprintf(&str
, fmt
, ap
);
98 ret
= write(s
, buf
, len
);
100 err(1, "connection closed");
109 static int help_flag
;
110 static int version_flag
;
111 static int verbose_flag
;
112 static int mutual_flag
= 1;
113 static int delegate_flag
;
114 static char *port_str
= "http";
115 static char *gss_service
= "HTTP";
117 static struct getargs http_args
[] = {
118 { "verbose", 'v', arg_flag
, &verbose_flag
, "verbose logging", },
119 { "port", 'p', arg_string
, &port_str
, "port to connect to", "port" },
120 { "delegate", 0, arg_flag
, &delegate_flag
, "gssapi delegate credential" },
121 { "gss-service", 's', arg_string
, &gss_service
, "gssapi service to use",
123 { "mech", 'm', arg_string
, &mech
, "gssapi mech to use", "mech" },
124 { "mutual", 0, arg_negative_flag
, &mutual_flag
, "no gssapi mutual auth" },
125 { "help", 'h', arg_flag
, &help_flag
},
126 { "version", 0, arg_flag
, &version_flag
}
129 static int num_http_args
= sizeof(http_args
) / sizeof(http_args
[0]);
134 arg_printusage(http_args
, num_http_args
, NULL
, "host [page]");
152 http_req_zero(struct http_req
*req
)
154 req
->response
= NULL
;
156 req
->num_headers
= 0;
162 http_req_free(struct http_req
*req
)
167 for (i
= 0; i
< req
->num_headers
; i
++)
168 free(req
->headers
[i
]);
175 http_find_header(struct http_req
*req
, const char *header
)
177 int i
, len
= strlen(header
);
179 for (i
= 0; i
< req
->num_headers
; i
++) {
180 if (strncasecmp(header
, req
->headers
[i
], len
) == 0) {
181 return req
->headers
[i
] + len
+ 1;
189 http_query(const char *host
, const char *page
,
190 char **headers
, int num_headers
, struct http_req
*req
)
192 enum { RESPONSE
, HEADER
, BODY
} state
;
194 char in_buf
[1024], *in_ptr
= in_buf
;
200 s
= do_connect(host
, port_str
);
202 errx(1, "connection failed");
204 fdprintf(s
, "GET %s HTTP/1.0\r\n", page
);
205 for (i
= 0; i
< num_headers
; i
++)
206 fdprintf(s
, "%s\r\n", headers
[i
]);
207 fdprintf(s
, "Host: %s\r\n\r\n", host
);
212 ret
= read (s
, in_ptr
, sizeof(in_buf
) - in_len
- 1);
216 err (1, "read: %lu", (unsigned long)ret
);
218 in_buf
[ret
+ in_len
] = '\0';
220 if (state
== HEADER
|| state
== RESPONSE
) {
227 p
= strstr(in_buf
, "\r\n");
231 } else if (p
== in_buf
) {
232 memmove(in_buf
, in_buf
+ 2, sizeof(in_buf
) - 2);
237 } else if (state
== RESPONSE
) {
238 req
->response
= strndup(in_buf
, p
- in_buf
);
241 req
->headers
= realloc(req
->headers
,
242 (req
->num_headers
+ 1) * sizeof(req
->headers
[0]));
243 req
->headers
[req
->num_headers
] = strndup(in_buf
, p
- in_buf
);
244 if (req
->headers
[req
->num_headers
] == NULL
)
248 memmove(in_buf
, p
+ 2, sizeof(in_buf
) - (p
- in_buf
) - 2);
249 in_len
-= (p
- in_buf
) + 2;
250 in_ptr
-= (p
- in_buf
) + 2;
256 req
->body
= erealloc(req
->body
, req
->body_size
+ ret
+ 1);
258 memcpy((char *)req
->body
+ req
->body_size
, in_buf
, ret
);
259 req
->body_size
+= ret
;
260 ((char *)req
->body
)[req
->body_size
] = '\0';
270 printf("response: %s\n", req
->response
);
271 for (i
= 0; i
< req
->num_headers
; i
++)
272 printf("header[%d] %s\n", i
, req
->headers
[i
]);
273 printf("body: %.*s\n", (int)req
->body_size
, (char *)req
->body
);
282 main(int argc
, char **argv
)
285 const char *host
, *page
;
286 int i
, done
, print_body
, gssapi_done
, gssapi_started
;
287 char *headers
[10]; /* XXX */
289 gss_ctx_id_t context_hdl
= GSS_C_NO_CONTEXT
;
290 gss_name_t server
= GSS_C_NO_NAME
;
295 setprogname(argv
[0]);
297 if(getarg(http_args
, num_http_args
, argc
, argv
, &optind
))
311 mech_oid
= select_mech(mech
);
313 if (argc
!= 1 && argc
!= 2)
314 errx(1, "usage: %s host [page]", getprogname());
323 flags
|= GSS_C_DELEG_FLAG
;
325 flags
|= GSS_C_MUTUAL_FLAG
;
334 http_query(host
, page
, headers
, num_headers
, &req
);
335 for (i
= 0 ; i
< num_headers
; i
++)
339 if (strstr(req
.response
, " 200 ") != NULL
) {
342 } else if (strstr(req
.response
, " 401 ") != NULL
) {
343 if (http_find_header(&req
, "WWW-Authenticate:") == NULL
)
344 errx(1, "Got %s but missed `WWW-Authenticate'", req
.response
);
349 const char *h
= http_find_header(&req
, "WWW-Authenticate:");
351 errx(1, "Got %s but missed `WWW-Authenticate'", req
.response
);
353 if (strncasecmp(h
, "Negotiate", 9) == 0) {
354 OM_uint32 maj_stat
, min_stat
;
355 gss_buffer_desc input_token
, output_token
;
358 printf("Negotiate found\n");
360 if (server
== GSS_C_NO_NAME
) {
362 asprintf(&name
, "%s@%s", gss_service
, host
);
363 input_token
.length
= strlen(name
);
364 input_token
.value
= name
;
366 maj_stat
= gss_import_name(&min_stat
,
368 GSS_C_NT_HOSTBASED_SERVICE
,
370 if (GSS_ERROR(maj_stat
))
371 gss_err (1, min_stat
, "gss_inport_name");
373 input_token
.length
= 0;
374 input_token
.value
= NULL
;
378 while(h
[i
] && isspace((unsigned char)h
[i
]))
381 int len
= strlen(&h
[i
]);
383 errx(1, "invalid Negotiate token");
384 input_token
.value
= emalloc(len
);
385 len
= base64_decode(&h
[i
], input_token
.value
);
387 errx(1, "invalid base64 Negotiate token %s", &h
[i
]);
388 input_token
.length
= len
;
391 errx(1, "Negotiate already started");
394 input_token
.length
= 0;
395 input_token
.value
= NULL
;
399 gss_init_sec_context(&min_stat
,
406 GSS_C_NO_CHANNEL_BINDINGS
,
412 if (GSS_ERROR(maj_stat
))
413 gss_err (1, min_stat
, "gss_init_sec_context");
414 else if (maj_stat
& GSS_S_CONTINUE_NEEDED
)
417 gss_name_t targ_name
, src_name
;
418 gss_buffer_desc name_buffer
;
423 printf("Negotiate done: %s\n", mech
);
425 maj_stat
= gss_inquire_context(&min_stat
,
434 if (GSS_ERROR(maj_stat
))
435 gss_err (1, min_stat
, "gss_inquire_context");
437 maj_stat
= gss_display_name(&min_stat
,
441 if (GSS_ERROR(maj_stat
))
442 gss_err (1, min_stat
, "gss_display_name");
444 printf("Source: %.*s\n",
445 (int)name_buffer
.length
,
446 (char *)name_buffer
.value
);
448 gss_release_buffer(&min_stat
, &name_buffer
);
450 maj_stat
= gss_display_name(&min_stat
,
454 if (GSS_ERROR(maj_stat
))
455 gss_err (1, min_stat
, "gss_display_name");
457 printf("Target: %.*s\n",
458 (int)name_buffer
.length
,
459 (char *)name_buffer
.value
);
461 gss_release_name(&min_stat
, &targ_name
);
462 gss_release_buffer(&min_stat
, &name_buffer
);
465 if (output_token
.length
) {
468 base64_encode(output_token
.value
,
472 asprintf(&headers
[0], "Authorization: Negotiate %s",
477 gss_release_buffer(&min_stat
, &output_token
);
479 if (input_token
.length
)
480 free(input_token
.value
);
488 printf("%s\n\n", req
.response
);
490 for (i
= 0; i
< req
.num_headers
; i
++)
491 printf("%s\n", req
.headers
[i
]);
494 if (print_body
|| verbose_flag
)
495 printf("%.*s\n", (int)req
.body_size
, (char *)req
.body
);
500 if (gssapi_done
== 0)
501 errx(1, "gssapi not done but http dance done");