1 /* 2 * Copyright (c) 2003 - 2005 Kungliga Tekniska H�gskolan 3 * (Royal Institute of Technology, Stockholm, Sweden). 4 * All rights reserved. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 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. 16 * 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. 20 * 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 31 * SUCH DAMAGE. 32 */ 33 34 #include "test_locl.h" 35 #include <gssapi.h> 36 #include "gss_common.h" 37 #include <base64.h> 38 39 RCSID("$Id: http_client.c 14861 2005-04-20 10:38:37Z lha $"); 40 41 /* 42 * A simplistic client implementing draft-brezak-spnego-http-04.txt 43 */ 44 45 static int 46 do_connect (const char *hostname, const char *port) 47 { 48 struct addrinfo *ai, *a; 49 struct addrinfo hints; 50 int error; 51 int s = -1; 52 53 memset (&hints, 0, sizeof(hints)); 54 hints.ai_family = PF_UNSPEC; 55 hints.ai_socktype = SOCK_STREAM; 56 hints.ai_protocol = 0; 57 58 error = getaddrinfo (hostname, port, &hints, &ai); 59 if (error) 60 errx (1, "getaddrinfo(%s): %s", hostname, gai_strerror(error)); 61 62 for (a = ai; a != NULL; a = a->ai_next) { 63 s = socket (a->ai_family, a->ai_socktype, a->ai_protocol); 64 if (s < 0) 65 continue; 66 if (connect (s, a->ai_addr, a->ai_addrlen) < 0) { 67 warn ("connect(%s)", hostname); 68 close (s); 69 continue; 70 } 71 break; 72 } 73 freeaddrinfo (ai); 74 if (a == NULL) 75 errx (1, "failed to contact %s", hostname); 76 77 return s; 78 } 79 80 static void 81 fdprintf(int s, const char *fmt, ...) 82 { 83 size_t len; 84 ssize_t ret; 85 va_list ap; 86 char *str, *buf; 87 88 va_start(ap, fmt); 89 vasprintf(&str, fmt, ap); 90 va_end(ap); 91 92 if (str == NULL) 93 errx(1, "vasprintf"); 94 95 buf = str; 96 len = strlen(buf); 97 while (len) { 98 ret = write(s, buf, len); 99 if (ret == 0) 100 err(1, "connection closed"); 101 else if (ret < 0) 102 err(1, "error"); 103 len -= ret; 104 buf += ret; 105 } 106 free(str); 107 } 108 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"; 116 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", 122 "service" }, 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 } 127 }; 128 129 static int num_http_args = sizeof(http_args) / sizeof(http_args[0]); 130 131 static void 132 usage(int code) 133 { 134 arg_printusage(http_args, num_http_args, NULL, "host [page]"); 135 exit(code); 136 } 137 138 /* 139 * 140 */ 141 142 struct http_req { 143 char *response; 144 char **headers; 145 int num_headers; 146 void *body; 147 size_t body_size; 148 }; 149 150 151 static void 152 http_req_zero(struct http_req *req) 153 { 154 req->response = NULL; 155 req->headers = NULL; 156 req->num_headers = 0; 157 req->body = NULL; 158 req->body_size = 0; 159 } 160 161 static void 162 http_req_free(struct http_req *req) 163 { 164 int i; 165 166 free(req->response); 167 for (i = 0; i < req->num_headers; i++) 168 free(req->headers[i]); 169 free(req->headers); 170 free(req->body); 171 http_req_zero(req); 172 } 173 174 static const char * 175 http_find_header(struct http_req *req, const char *header) 176 { 177 int i, len = strlen(header); 178 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; 182 } 183 } 184 return NULL; 185 } 186 187 188 static int 189 http_query(const char *host, const char *page, 190 char **headers, int num_headers, struct http_req *req) 191 { 192 enum { RESPONSE, HEADER, BODY } state; 193 ssize_t ret; 194 char in_buf[1024], *in_ptr = in_buf; 195 size_t in_len = 0; 196 int s, i; 197 198 http_req_zero(req); 199 200 s = do_connect(host, port_str); 201 if (s < 0) 202 errx(1, "connection failed"); 203 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); 208 209 state = RESPONSE; 210 211 while (1) { 212 ret = read (s, in_ptr, sizeof(in_buf) - in_len - 1); 213 if (ret == 0) 214 break; 215 else if (ret < 0) 216 err (1, "read: %lu", (unsigned long)ret); 217 218 in_buf[ret + in_len] = '\0'; 219 220 if (state == HEADER || state == RESPONSE) { 221 char *p; 222 223 in_len += ret; 224 in_ptr += ret; 225 226 while (1) { 227 p = strstr(in_buf, "\r\n"); 228 229 if (p == NULL) { 230 break; 231 } else if (p == in_buf) { 232 memmove(in_buf, in_buf + 2, sizeof(in_buf) - 2); 233 state = BODY; 234 in_len -= 2; 235 in_ptr -= 2; 236 break; 237 } else if (state == RESPONSE) { 238 req->response = strndup(in_buf, p - in_buf); 239 state = HEADER; 240 } else { 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) 245 errx(1, "strdup"); 246 req->num_headers++; 247 } 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; 251 } 252 } 253 254 if (state == BODY) { 255 256 req->body = erealloc(req->body, req->body_size + ret + 1); 257 258 memcpy((char *)req->body + req->body_size, in_buf, ret); 259 req->body_size += ret; 260 ((char *)req->body)[req->body_size] = '\0'; 261 262 in_ptr = in_buf; 263 in_len = 0; 264 } else 265 abort(); 266 } 267 268 if (verbose_flag) { 269 int i; 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); 274 } 275 276 close(s); 277 return 0; 278 } 279 280 281 int 282 main(int argc, char **argv) 283 { 284 struct http_req req; 285 const char *host, *page; 286 int i, done, print_body, gssapi_done, gssapi_started; 287 char *headers[10]; /* XXX */ 288 int num_headers; 289 gss_ctx_id_t context_hdl = GSS_C_NO_CONTEXT; 290 gss_name_t server = GSS_C_NO_NAME; 291 int optind = 0; 292 gss_OID mech_oid; 293 OM_uint32 flags; 294 295 setprogname(argv[0]); 296 297 if(getarg(http_args, num_http_args, argc, argv, &optind)) 298 usage(1); 299 300 if (help_flag) 301 usage (0); 302 303 if(version_flag) { 304 print_version(NULL); 305 exit(0); 306 } 307 308 argc -= optind; 309 argv += optind; 310 311 mech_oid = select_mech(mech); 312 313 if (argc != 1 && argc != 2) 314 errx(1, "usage: %s host [page]", getprogname()); 315 host = argv[0]; 316 if (argc == 2) 317 page = argv[1]; 318 else 319 page = "/"; 320 321 flags = 0; 322 if (delegate_flag) 323 flags |= GSS_C_DELEG_FLAG; 324 if (mutual_flag) 325 flags |= GSS_C_MUTUAL_FLAG; 326 327 done = 0; 328 num_headers = 0; 329 gssapi_done = 1; 330 gssapi_started = 0; 331 do { 332 print_body = 0; 333 334 http_query(host, page, headers, num_headers, &req); 335 for (i = 0 ; i < num_headers; i++) 336 free(headers[i]); 337 num_headers = 0; 338 339 if (strstr(req.response, " 200 ") != NULL) { 340 print_body = 1; 341 done = 1; 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); 345 gssapi_done = 0; 346 } 347 348 if (!gssapi_done) { 349 const char *h = http_find_header(&req, "WWW-Authenticate:"); 350 if (h == NULL) 351 errx(1, "Got %s but missed `WWW-Authenticate'", req.response); 352 353 if (strncasecmp(h, "Negotiate", 9) == 0) { 354 OM_uint32 maj_stat, min_stat; 355 gss_buffer_desc input_token, output_token; 356 357 if (verbose_flag) 358 printf("Negotiate found\n"); 359 360 if (server == GSS_C_NO_NAME) { 361 char *name; 362 asprintf(&name, "%s@%s", gss_service, host); 363 input_token.length = strlen(name); 364 input_token.value = name; 365 366 maj_stat = gss_import_name(&min_stat, 367 &input_token, 368 GSS_C_NT_HOSTBASED_SERVICE, 369 &server); 370 if (GSS_ERROR(maj_stat)) 371 gss_err (1, min_stat, "gss_inport_name"); 372 free(name); 373 input_token.length = 0; 374 input_token.value = NULL; 375 } 376 377 i = 9; 378 while(h[i] && isspace((unsigned char)h[i])) 379 i++; 380 if (h[i] != '\0') { 381 int len = strlen(&h[i]); 382 if (len == 0) 383 errx(1, "invalid Negotiate token"); 384 input_token.value = emalloc(len); 385 len = base64_decode(&h[i], input_token.value); 386 if (len < 0) 387 errx(1, "invalid base64 Negotiate token %s", &h[i]); 388 input_token.length = len; 389 } else { 390 if (gssapi_started) 391 errx(1, "Negotiate already started"); 392 gssapi_started = 1; 393 394 input_token.length = 0; 395 input_token.value = NULL; 396 } 397 398 maj_stat = 399 gss_init_sec_context(&min_stat, 400 GSS_C_NO_CREDENTIAL, 401 &context_hdl, 402 server, 403 mech_oid, 404 flags, 405 0, 406 GSS_C_NO_CHANNEL_BINDINGS, 407 &input_token, 408 NULL, 409 &output_token, 410 NULL, 411 NULL); 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) 415 gssapi_done = 0; 416 else { 417 gss_name_t targ_name, src_name; 418 gss_buffer_desc name_buffer; 419 gss_OID mech_type; 420 421 gssapi_done = 1; 422 423 printf("Negotiate done: %s\n", mech); 424 425 maj_stat = gss_inquire_context(&min_stat, 426 context_hdl, 427 &src_name, 428 &targ_name, 429 NULL, 430 &mech_type, 431 NULL, 432 NULL, 433 NULL); 434 if (GSS_ERROR(maj_stat)) 435 gss_err (1, min_stat, "gss_inquire_context"); 436 437 maj_stat = gss_display_name(&min_stat, 438 src_name, 439 &name_buffer, 440 NULL); 441 if (GSS_ERROR(maj_stat)) 442 gss_err (1, min_stat, "gss_display_name"); 443 444 printf("Source: %.*s\n", 445 (int)name_buffer.length, 446 (char *)name_buffer.value); 447 448 gss_release_buffer(&min_stat, &name_buffer); 449 450 maj_stat = gss_display_name(&min_stat, 451 targ_name, 452 &name_buffer, 453 NULL); 454 if (GSS_ERROR(maj_stat)) 455 gss_err (1, min_stat, "gss_display_name"); 456 457 printf("Target: %.*s\n", 458 (int)name_buffer.length, 459 (char *)name_buffer.value); 460 461 gss_release_name(&min_stat, &targ_name); 462 gss_release_buffer(&min_stat, &name_buffer); 463 } 464 465 if (output_token.length) { 466 char *neg_token; 467 468 base64_encode(output_token.value, 469 output_token.length, 470 &neg_token); 471 472 asprintf(&headers[0], "Authorization: Negotiate %s", 473 neg_token); 474 475 num_headers = 1; 476 free(neg_token); 477 gss_release_buffer(&min_stat, &output_token); 478 } 479 if (input_token.length) 480 free(input_token.value); 481 482 } else 483 done = 1; 484 } else 485 done = 1; 486 487 if (verbose_flag) { 488 printf("%s\n\n", req.response); 489 490 for (i = 0; i < req.num_headers; i++) 491 printf("%s\n", req.headers[i]); 492 printf("\n"); 493 } 494 if (print_body || verbose_flag) 495 printf("%.*s\n", (int)req.body_size, (char *)req.body); 496 497 http_req_free(&req); 498 } while (!done); 499 500 if (gssapi_done == 0) 501 errx(1, "gssapi not done but http dance done"); 502 503 return 0; 504 } 505