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"
35 #include <gssapi/gssapi.h>
36 #include <gssapi/gssapi_krb5.h>
37 #include <gssapi/gssapi_spnego.h>
38 #include "gss_common.h"
44 * A simplistic client implementing draft-brezak-spnego-http-04.txt
48 do_connect (const char *hostname
, const char *port
)
50 struct addrinfo
*ai
, *a
;
51 struct addrinfo hints
;
55 memset (&hints
, 0, sizeof(hints
));
56 hints
.ai_family
= PF_UNSPEC
;
57 hints
.ai_socktype
= SOCK_STREAM
;
58 hints
.ai_protocol
= 0;
60 error
= getaddrinfo (hostname
, port
, &hints
, &ai
);
62 errx (1, "getaddrinfo(%s): %s", hostname
, gai_strerror(error
));
64 for (a
= ai
; a
!= NULL
; a
= a
->ai_next
) {
65 s
= socket (a
->ai_family
, a
->ai_socktype
, a
->ai_protocol
);
68 if (connect (s
, a
->ai_addr
, a
->ai_addrlen
) < 0) {
69 warn ("connect(%s)", hostname
);
77 errx (1, "failed to contact %s", hostname
);
83 fdprintf(int s
, const char *fmt
, ...)
91 vasprintf(&str
, fmt
, ap
);
100 ret
= write(s
, buf
, len
);
102 err(1, "connection closed");
111 static int help_flag
;
112 static int version_flag
;
113 static int verbose_flag
;
114 static int mutual_flag
= 1;
115 static int delegate_flag
;
116 static char *port_str
= "http";
117 static char *gss_service
= "HTTP";
119 static struct getargs http_args
[] = {
120 { "verbose", 'v', arg_flag
, &verbose_flag
, "verbose logging", NULL
},
121 { "port", 'p', arg_string
, &port_str
, "port to connect to", "port" },
122 { "delegate", 0, arg_flag
, &delegate_flag
, "gssapi delegate credential",
124 { "gss-service", 's', arg_string
, &gss_service
, "gssapi service to use",
126 { "mech", 'm', arg_string
, &mech
, "gssapi mech to use", "mech" },
127 { "mutual", 0, arg_negative_flag
, &mutual_flag
, "no gssapi mutual auth",
129 { "help", 'h', arg_flag
, &help_flag
, NULL
, NULL
},
130 { "version", 0, arg_flag
, &version_flag
, NULL
, NULL
}
133 static int num_http_args
= sizeof(http_args
) / sizeof(http_args
[0]);
138 arg_printusage(http_args
, num_http_args
, NULL
, "host [page]");
156 http_req_zero(struct http_req
*req
)
158 req
->response
= NULL
;
160 req
->num_headers
= 0;
166 http_req_free(struct http_req
*req
)
171 for (i
= 0; i
< req
->num_headers
; i
++)
172 free(req
->headers
[i
]);
179 http_find_header(struct http_req
*req
, const char *header
)
181 int i
, len
= strlen(header
);
183 for (i
= 0; i
< req
->num_headers
; i
++) {
184 if (strncasecmp(header
, req
->headers
[i
], len
) == 0) {
185 return req
->headers
[i
] + len
+ 1;
193 http_query(const char *host
, const char *page
,
194 char **headers
, struct http_req
*req
)
196 enum { RESPONSE
, HEADER
, BODY
} state
;
198 char in_buf
[1024], *in_ptr
= in_buf
;
204 s
= do_connect(host
, port_str
);
206 errx(1, "connection failed");
208 fdprintf(s
, "GET %s HTTP/1.0\r\n", page
);
209 for (i
= 0; headers
[i
]; i
++)
210 fdprintf(s
, "%s\r\n", headers
[i
]);
211 fdprintf(s
, "Host: %s\r\n\r\n", host
);
216 ret
= read (s
, in_ptr
, sizeof(in_buf
) - in_len
- 1);
220 err (1, "read: %lu", (unsigned long)ret
);
222 in_buf
[ret
+ in_len
] = '\0';
224 if (state
== HEADER
|| state
== RESPONSE
) {
231 p
= strstr(in_buf
, "\r\n");
235 } else if (p
== in_buf
) {
236 memmove(in_buf
, in_buf
+ 2, sizeof(in_buf
) - 2);
241 } else if (state
== RESPONSE
) {
242 req
->response
= emalloc(p
- in_buf
+ 1);
243 memcpy(req
->response
, in_buf
, p
- in_buf
);
244 req
->response
[p
- in_buf
] = '\0';
247 req
->headers
= realloc(req
->headers
,
248 (req
->num_headers
+ 1) * sizeof(req
->headers
[0]));
249 req
->headers
[req
->num_headers
] = emalloc(p
- in_buf
+ 1);
250 memcpy(req
->headers
[req
->num_headers
], in_buf
, p
- in_buf
);
251 req
->headers
[req
->num_headers
][p
- in_buf
] = '\0';
252 if (req
->headers
[req
->num_headers
] == NULL
)
256 memmove(in_buf
, p
+ 2, sizeof(in_buf
) - (p
- in_buf
) - 2);
257 in_len
-= (p
- in_buf
) + 2;
258 in_ptr
-= (p
- in_buf
) + 2;
264 req
->body
= erealloc(req
->body
, req
->body_size
+ ret
+ 1);
266 memcpy((char *)req
->body
+ req
->body_size
, in_buf
, ret
);
267 req
->body_size
+= ret
;
268 ((char *)req
->body
)[req
->body_size
] = '\0';
278 printf("response: %s\n", req
->response
);
279 for (i
= 0; i
< req
->num_headers
; i
++)
280 printf("header[%d] %s\n", i
, req
->headers
[i
]);
281 printf("body: %.*s\n", (int)req
->body_size
, (char *)req
->body
);
290 main(int argc
, char **argv
)
293 const char *host
, *page
;
294 int i
, done
, print_body
, gssapi_done
, gssapi_started
;
295 char *headers
[10] = { 0 };
297 gss_ctx_id_t context_hdl
= GSS_C_NO_CONTEXT
;
298 gss_name_t server
= GSS_C_NO_NAME
;
303 setprogname(argv
[0]);
305 if(getarg(http_args
, num_http_args
, argc
, argv
, &optind
))
319 mech_oid
= select_mech(mech
);
321 if (argc
!= 1 && argc
!= 2)
322 errx(1, "usage: %s host [page]", getprogname());
331 flags
|= GSS_C_DELEG_FLAG
;
333 flags
|= GSS_C_MUTUAL_FLAG
;
342 http_query(host
, page
, headers
, &req
);
343 for (i
= 0 ; headers
[i
]; i
++) {
349 if (strstr(req
.response
, " 200 ") != NULL
) {
352 } else if (strstr(req
.response
, " 401 ") != NULL
) {
353 if (http_find_header(&req
, "WWW-Authenticate:") == NULL
)
354 errx(1, "Got %s but missed `WWW-Authenticate'", req
.response
);
359 const char *h
= http_find_header(&req
, "WWW-Authenticate:");
361 errx(1, "Got %s but missed `WWW-Authenticate'", req
.response
);
363 if (strncasecmp(h
, "Negotiate", 9) == 0) {
364 OM_uint32 maj_stat
, min_stat
;
365 gss_buffer_desc input_token
, output_token
;
368 printf("Negotiate found\n");
370 if (server
== GSS_C_NO_NAME
) {
372 asprintf(&name
, "%s@%s", gss_service
, host
);
373 input_token
.length
= strlen(name
);
374 input_token
.value
= name
;
376 maj_stat
= gss_import_name(&min_stat
,
378 GSS_C_NT_HOSTBASED_SERVICE
,
380 if (GSS_ERROR(maj_stat
))
381 gss_err (1, min_stat
, "gss_inport_name");
383 input_token
.length
= 0;
384 input_token
.value
= NULL
;
388 while(h
[i
] && isspace((unsigned char)h
[i
]))
391 int len
= strlen(&h
[i
]);
393 errx(1, "invalid Negotiate token");
394 input_token
.value
= emalloc(len
);
395 len
= rk_base64_decode(&h
[i
], input_token
.value
);
397 errx(1, "invalid base64 Negotiate token %s", &h
[i
]);
398 input_token
.length
= len
;
401 errx(1, "Negotiate already started");
404 input_token
.length
= 0;
405 input_token
.value
= NULL
;
409 gss_init_sec_context(&min_stat
,
416 GSS_C_NO_CHANNEL_BINDINGS
,
422 if (GSS_ERROR(maj_stat
))
423 gss_err (1, min_stat
, "gss_init_sec_context");
424 else if (maj_stat
& GSS_S_CONTINUE_NEEDED
)
427 gss_name_t targ_name
, src_name
;
428 gss_buffer_desc name_buffer
;
433 printf("Negotiate done: %s\n", mech
);
435 maj_stat
= gss_inquire_context(&min_stat
,
444 if (GSS_ERROR(maj_stat
))
445 gss_err (1, min_stat
, "gss_inquire_context");
447 maj_stat
= gss_display_name(&min_stat
,
451 if (GSS_ERROR(maj_stat
))
452 gss_err (1, min_stat
, "gss_display_name");
454 printf("Source: %.*s\n",
455 (int)name_buffer
.length
,
456 (char *)name_buffer
.value
);
458 gss_release_buffer(&min_stat
, &name_buffer
);
460 maj_stat
= gss_display_name(&min_stat
,
464 if (GSS_ERROR(maj_stat
))
465 gss_err (1, min_stat
, "gss_display_name");
467 printf("Target: %.*s\n",
468 (int)name_buffer
.length
,
469 (char *)name_buffer
.value
);
471 gss_release_name(&min_stat
, &targ_name
);
472 gss_release_buffer(&min_stat
, &name_buffer
);
475 if (output_token
.length
) {
478 rk_base64_encode(output_token
.value
,
482 asprintf(&headers
[num_headers
++], "Authorization: Negotiate %s",
486 gss_release_buffer(&min_stat
, &output_token
);
488 if (input_token
.length
)
489 free(input_token
.value
);
497 printf("%s\n\n", req
.response
);
499 for (i
= 0; i
< req
.num_headers
; i
++)
500 printf("%s\n", req
.headers
[i
]);
503 if (print_body
|| verbose_flag
)
504 printf("%.*s\n", (int)req
.body_size
, (char *)req
.body
);
509 if (gssapi_done
== 0)
510 errx(1, "gssapi not done but http dance done");