1 /* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */ 2 /* 3 * Copyright 1994 by OpenVision Technologies, Inc. 4 * 5 * Permission to use, copy, modify, distribute, and sell this software 6 * and its documentation for any purpose is hereby granted without fee, 7 * provided that the above copyright notice appears in all copies and 8 * that both that copyright notice and this permission notice appear in 9 * supporting documentation, and that the name of OpenVision not be used 10 * in advertising or publicity pertaining to distribution of the software 11 * without specific, written prior permission. OpenVision makes no 12 * representations about the suitability of this software for any 13 * purpose. It is provided "as is" without express or implied warranty. 14 * 15 * OPENVISION DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, 16 * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO 17 * EVENT SHALL OPENVISION BE LIABLE FOR ANY SPECIAL, INDIRECT OR 18 * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF 19 * USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR 20 * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 21 * PERFORMANCE OF THIS SOFTWARE. 22 */ 23 /* 24 * Copyright (C) 2003, 2004, 2005 by the Massachusetts Institute of Technology. 25 * All rights reserved. 26 * 27 * Export of this software from the United States of America may 28 * require a specific license from the United States Government. 29 * It is the responsibility of any person or organization contemplating 30 * export to obtain such a license before exporting. 31 * 32 * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and 33 * distribute this software and its documentation for any purpose and 34 * without fee is hereby granted, provided that the above copyright 35 * notice appear in all copies and that both that copyright notice and 36 * this permission notice appear in supporting documentation, and that 37 * the name of M.I.T. not be used in advertising or publicity pertaining 38 * to distribution of the software without specific, written prior 39 * permission. Furthermore if you modify this software you must label 40 * your software as modified software and not distribute it in such a 41 * fashion that it might be confused with the original M.I.T. software. 42 * M.I.T. makes no representations about the suitability of 43 * this software for any purpose. It is provided "as is" without express 44 * or implied warranty. 45 */ 46 47 #include <stdio.h> 48 #include <stdlib.h> 49 #include <string.h> 50 #ifdef _WIN32 51 #include <windows.h> 52 #include <winsock2.h> 53 #else 54 #include <assert.h> 55 #include <unistd.h> 56 #include <ctype.h> 57 #include <sys/types.h> 58 #include <sys/socket.h> 59 #include <netinet/in.h> 60 #include <netdb.h> 61 #include <errno.h> 62 #include <sys/stat.h> 63 #include <fcntl.h> 64 #endif 65 66 #include <gssapi/gssapi_generic.h> 67 #include <gssapi/gssapi_krb5.h> 68 #include <gssapi/gssapi_ext.h> 69 #include "gss-misc.h" 70 #include "port-sockets.h" 71 72 static int verbose = 1; 73 static int spnego = 0; 74 static gss_OID_desc gss_spnego_mechanism_oid_desc = 75 {6, (void *)"\x2b\x06\x01\x05\x05\x02"}; 76 77 static void 78 usage() 79 { 80 fprintf(stderr, "Usage: gss-client [-port port] [-mech mechanism] " 81 "[-spnego] [-d]\n"); 82 fprintf(stderr, " [-seq] [-noreplay] [-nomutual] [-user user] " 83 "[-pass pw]"); 84 #ifdef _WIN32 85 fprintf(stderr, " [-threads num]"); 86 #endif 87 fprintf(stderr, "\n"); 88 fprintf(stderr, " [-f] [-q] [-ccount count] [-mcount count]\n"); 89 fprintf(stderr, " [-v1] [-na] [-nw] [-nx] [-nm] host service msg\n"); 90 exit(1); 91 } 92 93 /* 94 * Function: connect_to_server 95 * 96 * Purpose: Opens a TCP connection to the name host and port. 97 * 98 * Arguments: 99 * 100 * host (r) the target host name 101 * port (r) the target port, in host byte order 102 * 103 * Returns: the established socket file descriptor, or -1 on failure 104 * 105 * Effects: 106 * 107 * The host name is resolved with gethostbyname(), and the socket is 108 * opened and connected. If an error occurs, an error message is 109 * displayed and -1 is returned. 110 */ 111 static int 112 connect_to_server(char *host, u_short port) 113 { 114 struct sockaddr_in saddr; 115 struct hostent *hp; 116 int s; 117 118 #ifdef _WIN32 119 WSADATA wsadata; 120 int wsastartuperror = WSAStartup(0x202, &wsadata); 121 if (wsastartuperror) { 122 fprintf(stderr, "WSAStartup error: %x\n", wsastartuperror); 123 return -1; 124 } 125 #endif 126 127 if ((hp = gethostbyname(host)) == NULL) { 128 fprintf(stderr, "Unknown host: %s\n", host); 129 return -1; 130 } 131 132 saddr.sin_family = hp->h_addrtype; 133 memcpy(&saddr.sin_addr, hp->h_addr, sizeof(saddr.sin_addr)); 134 saddr.sin_port = htons(port); 135 136 if ((s = socket(AF_INET, SOCK_STREAM, 0)) < 0) { 137 perror("creating socket"); 138 return -1; 139 } 140 if (connect(s, (struct sockaddr *) &saddr, sizeof(saddr)) < 0) { 141 perror("connecting to server"); 142 (void) closesocket(s); 143 return -1; 144 } 145 return s; 146 } 147 148 /* 149 * Function: client_establish_context 150 * 151 * Purpose: establishes a GSS-API context with a specified service and 152 * returns the context handle 153 * 154 * Arguments: 155 * 156 * s (r) an established TCP connection to the service 157 * service_name(r) the ASCII service name of the service 158 * gss_flags (r) GSS-API delegation flag (if any) 159 * auth_flag (r) whether to actually do authentication 160 * v1_format (r) whether the v1 sample protocol should be used 161 * oid (r) OID of the mechanism to use 162 * context (w) the established GSS-API context 163 * ret_flags (w) the returned flags from init_sec_context 164 * 165 * Returns: 0 on success, -1 on failure 166 * 167 * Effects: 168 * 169 * service_name is imported as a GSS-API name and a GSS-API context is 170 * established with the corresponding service; the service should be 171 * listening on the TCP connection s. The default GSS-API mechanism 172 * is used, and mutual authentication and replay detection are 173 * requested. 174 * 175 * If successful, the context handle is returned in context. If 176 * unsuccessful, the GSS-API error messages are displayed on stderr 177 * and -1 is returned. 178 */ 179 static int 180 client_establish_context(int s, char *service_name, OM_uint32 gss_flags, 181 int auth_flag, int v1_format, gss_OID oid, 182 char *username, char *password, 183 gss_ctx_id_t *gss_context, OM_uint32 *ret_flags) 184 { 185 if (auth_flag) { 186 gss_buffer_desc send_tok, recv_tok, *token_ptr; 187 gss_name_t target_name; 188 OM_uint32 maj_stat, min_stat, init_sec_min_stat; 189 int token_flags; 190 gss_cred_id_t cred = GSS_C_NO_CREDENTIAL; 191 gss_name_t gss_username = GSS_C_NO_NAME; 192 gss_OID_set_desc mechs, *mechsp = GSS_C_NO_OID_SET; 193 194 if (spnego) { 195 mechs.elements = &gss_spnego_mechanism_oid_desc; 196 mechs.count = 1; 197 mechsp = &mechs; 198 } else if (oid != GSS_C_NO_OID) { 199 mechs.elements = oid; 200 mechs.count = 1; 201 mechsp = &mechs; 202 } else { 203 mechs.elements = NULL; 204 mechs.count = 0; 205 } 206 207 if (username != NULL) { 208 send_tok.value = username; 209 send_tok.length = strlen(username); 210 211 maj_stat = gss_import_name(&min_stat, &send_tok, 212 (gss_OID) gss_nt_user_name, 213 &gss_username); 214 if (maj_stat != GSS_S_COMPLETE) { 215 display_status("parsing client name", maj_stat, min_stat); 216 return -1; 217 } 218 } 219 220 if (password != NULL) { 221 gss_buffer_desc pwbuf; 222 223 pwbuf.value = password; 224 pwbuf.length = strlen(password); 225 226 maj_stat = gss_acquire_cred_with_password(&min_stat, 227 gss_username, 228 &pwbuf, 0, 229 mechsp, GSS_C_INITIATE, 230 &cred, NULL, NULL); 231 } else if (gss_username != GSS_C_NO_NAME) { 232 maj_stat = gss_acquire_cred(&min_stat, 233 gss_username, 0, 234 mechsp, GSS_C_INITIATE, 235 &cred, NULL, NULL); 236 } else 237 maj_stat = GSS_S_COMPLETE; 238 if (maj_stat != GSS_S_COMPLETE) { 239 display_status("acquiring creds", maj_stat, min_stat); 240 gss_release_name(&min_stat, &gss_username); 241 return -1; 242 } 243 if (spnego && oid != GSS_C_NO_OID) { 244 gss_OID_set_desc neg_mechs; 245 246 neg_mechs.elements = oid; 247 neg_mechs.count = 1; 248 249 maj_stat = gss_set_neg_mechs(&min_stat, cred, &neg_mechs); 250 if (maj_stat != GSS_S_COMPLETE) { 251 display_status("setting neg mechs", maj_stat, min_stat); 252 gss_release_name(&min_stat, &gss_username); 253 gss_release_cred(&min_stat, &cred); 254 return -1; 255 } 256 } 257 gss_release_name(&min_stat, &gss_username); 258 259 /* 260 * Import the name into target_name. Use send_tok to save 261 * local variable space. 262 */ 263 send_tok.value = service_name; 264 send_tok.length = strlen(service_name); 265 maj_stat = gss_import_name(&min_stat, &send_tok, 266 (gss_OID) gss_nt_service_name, 267 &target_name); 268 if (maj_stat != GSS_S_COMPLETE) { 269 display_status("parsing name", maj_stat, min_stat); 270 return -1; 271 } 272 273 if (!v1_format) { 274 if (send_token(s, TOKEN_NOOP | TOKEN_CONTEXT_NEXT, empty_token) < 275 0) { 276 (void) gss_release_name(&min_stat, &target_name); 277 return -1; 278 } 279 } 280 281 /* 282 * Perform the context-establishement loop. 283 * 284 * On each pass through the loop, token_ptr points to the token 285 * to send to the server (or GSS_C_NO_BUFFER on the first pass). 286 * Every generated token is stored in send_tok which is then 287 * transmitted to the server; every received token is stored in 288 * recv_tok, which token_ptr is then set to, to be processed by 289 * the next call to gss_init_sec_context. 290 * 291 * GSS-API guarantees that send_tok's length will be non-zero 292 * if and only if the server is expecting another token from us, 293 * and that gss_init_sec_context returns GSS_S_CONTINUE_NEEDED if 294 * and only if the server has another token to send us. 295 */ 296 297 token_ptr = GSS_C_NO_BUFFER; 298 *gss_context = GSS_C_NO_CONTEXT; 299 300 do { 301 maj_stat = gss_init_sec_context(&init_sec_min_stat, 302 cred, gss_context, 303 target_name, mechs.elements, 304 gss_flags, 0, 305 NULL, /* channel bindings */ 306 token_ptr, NULL, /* mech type */ 307 &send_tok, ret_flags, 308 NULL); /* time_rec */ 309 310 if (token_ptr != GSS_C_NO_BUFFER) 311 free(recv_tok.value); 312 313 if (send_tok.length != 0) { 314 if (verbose) 315 printf("Sending init_sec_context token (size=%d)...", 316 (int) send_tok.length); 317 if (send_token(s, v1_format ? 0 : TOKEN_CONTEXT, &send_tok) < 318 0) { 319 (void) gss_release_buffer(&min_stat, &send_tok); 320 (void) gss_release_name(&min_stat, &target_name); 321 return -1; 322 } 323 } 324 (void) gss_release_buffer(&min_stat, &send_tok); 325 326 if (maj_stat != GSS_S_COMPLETE 327 && maj_stat != GSS_S_CONTINUE_NEEDED) { 328 display_status("initializing context", maj_stat, 329 init_sec_min_stat); 330 (void) gss_release_name(&min_stat, &target_name); 331 (void) gss_release_cred(&min_stat, &cred); 332 if (*gss_context != GSS_C_NO_CONTEXT) 333 gss_delete_sec_context(&min_stat, gss_context, 334 GSS_C_NO_BUFFER); 335 return -1; 336 } 337 338 if (maj_stat == GSS_S_CONTINUE_NEEDED) { 339 if (verbose) 340 printf("continue needed..."); 341 if (recv_token(s, &token_flags, &recv_tok) < 0) { 342 (void) gss_release_name(&min_stat, &target_name); 343 return -1; 344 } 345 token_ptr = &recv_tok; 346 } 347 if (verbose) 348 printf("\n"); 349 } while (maj_stat == GSS_S_CONTINUE_NEEDED); 350 351 (void) gss_release_cred(&min_stat, &cred); 352 (void) gss_release_name(&min_stat, &target_name); 353 } else { 354 if (send_token(s, TOKEN_NOOP, empty_token) < 0) 355 return -1; 356 } 357 358 return 0; 359 } 360 361 static void 362 read_file(file_name, in_buf) 363 char *file_name; 364 gss_buffer_t in_buf; 365 { 366 int fd, count; 367 struct stat stat_buf; 368 369 if ((fd = open(file_name, O_RDONLY, 0)) < 0) { 370 perror("open"); 371 fprintf(stderr, "Couldn't open file %s\n", file_name); 372 exit(1); 373 } 374 if (fstat(fd, &stat_buf) < 0) { 375 perror("fstat"); 376 exit(1); 377 } 378 in_buf->length = stat_buf.st_size; 379 380 if (in_buf->length == 0) { 381 in_buf->value = NULL; 382 return; 383 } 384 385 if ((in_buf->value = malloc(in_buf->length)) == 0) { 386 fprintf(stderr, "Couldn't allocate %d byte buffer for reading file\n", 387 (int) in_buf->length); 388 exit(1); 389 } 390 391 /* this code used to check for incomplete reads, but you can't get 392 * an incomplete read on any file for which fstat() is meaningful */ 393 394 count = read(fd, in_buf->value, in_buf->length); 395 if (count < 0) { 396 perror("read"); 397 exit(1); 398 } 399 if (count < (int)in_buf->length) 400 fprintf(stderr, "Warning, only read in %d bytes, expected %d\n", 401 count, (int) in_buf->length); 402 } 403 404 /* 405 * Function: call_server 406 * 407 * Purpose: Call the "sign" service. 408 * 409 * Arguments: 410 * 411 * host (r) the host providing the service 412 * port (r) the port to connect to on host 413 * service_name (r) the GSS-API service name to authenticate to 414 * gss_flags (r) GSS-API delegation flag (if any) 415 * auth_flag (r) whether to do authentication 416 * wrap_flag (r) whether to do message wrapping at all 417 * encrypt_flag (r) whether to do encryption while wrapping 418 * mic_flag (r) whether to request a MIC from the server 419 * msg (r) the message to have "signed" 420 * use_file (r) whether to treat msg as an input file name 421 * mcount (r) the number of times to send the message 422 * 423 * Returns: 0 on success, -1 on failure 424 * 425 * Effects: 426 * 427 * call_server opens a TCP connection to <host:port> and establishes a 428 * GSS-API context with service_name over the connection. It then 429 * seals msg in a GSS-API token with gss_wrap, sends it to the server, 430 * reads back a GSS-API signature block for msg from the server, and 431 * verifies it with gss_verify. -1 is returned if any step fails, 432 * otherwise 0 is returned. */ 433 static int 434 call_server(host, port, oid, service_name, gss_flags, auth_flag, 435 wrap_flag, encrypt_flag, mic_flag, v1_format, msg, use_file, 436 mcount, username, password) 437 char *host; 438 u_short port; 439 gss_OID oid; 440 char *service_name; 441 OM_uint32 gss_flags; 442 int auth_flag, wrap_flag, encrypt_flag, mic_flag; 443 int v1_format; 444 char *msg; 445 int use_file; 446 int mcount; 447 char *username; 448 char *password; 449 { 450 gss_ctx_id_t context = GSS_C_NO_CONTEXT; 451 gss_buffer_desc in_buf, out_buf; 452 int s, state; 453 OM_uint32 ret_flags; 454 OM_uint32 maj_stat, min_stat; 455 gss_name_t src_name, targ_name; 456 gss_buffer_desc sname, tname; 457 OM_uint32 lifetime; 458 gss_OID mechanism, name_type; 459 int is_local; 460 OM_uint32 context_flags; 461 int is_open; 462 gss_qop_t qop_state; 463 gss_OID_set mech_names; 464 gss_buffer_desc oid_name; 465 size_t i; 466 int token_flags; 467 468 /* Open connection */ 469 if ((s = connect_to_server(host, port)) < 0) 470 return -1; 471 472 /* Establish context */ 473 if (client_establish_context(s, service_name, gss_flags, auth_flag, 474 v1_format, oid, username, password, 475 &context, &ret_flags) < 0) { 476 (void) closesocket(s); 477 return -1; 478 } 479 480 if (auth_flag && verbose) { 481 /* display the flags */ 482 display_ctx_flags(ret_flags); 483 484 /* Get context information */ 485 maj_stat = gss_inquire_context(&min_stat, context, 486 &src_name, &targ_name, &lifetime, 487 &mechanism, &context_flags, 488 &is_local, &is_open); 489 if (maj_stat != GSS_S_COMPLETE) { 490 display_status("inquiring context", maj_stat, min_stat); 491 return -1; 492 } 493 494 maj_stat = gss_display_name(&min_stat, src_name, &sname, &name_type); 495 if (maj_stat != GSS_S_COMPLETE) { 496 display_status("displaying source name", maj_stat, min_stat); 497 return -1; 498 } 499 maj_stat = gss_display_name(&min_stat, targ_name, &tname, 500 (gss_OID *) NULL); 501 if (maj_stat != GSS_S_COMPLETE) { 502 display_status("displaying target name", maj_stat, min_stat); 503 return -1; 504 } 505 printf("\"%.*s\" to \"%.*s\", lifetime %d, flags %x, %s, %s\n", 506 (int) sname.length, (char *) sname.value, 507 (int) tname.length, (char *) tname.value, lifetime, 508 context_flags, 509 (is_local) ? "locally initiated" : "remotely initiated", 510 (is_open) ? "open" : "closed"); 511 512 (void) gss_release_name(&min_stat, &src_name); 513 (void) gss_release_name(&min_stat, &targ_name); 514 (void) gss_release_buffer(&min_stat, &sname); 515 (void) gss_release_buffer(&min_stat, &tname); 516 517 maj_stat = gss_oid_to_str(&min_stat, name_type, &oid_name); 518 if (maj_stat != GSS_S_COMPLETE) { 519 display_status("converting oid->string", maj_stat, min_stat); 520 return -1; 521 } 522 printf("Name type of source name is %.*s.\n", 523 (int) oid_name.length, (char *) oid_name.value); 524 (void) gss_release_buffer(&min_stat, &oid_name); 525 526 /* Now get the names supported by the mechanism */ 527 maj_stat = gss_inquire_names_for_mech(&min_stat, 528 mechanism, &mech_names); 529 if (maj_stat != GSS_S_COMPLETE) { 530 display_status("inquiring mech names", maj_stat, min_stat); 531 return -1; 532 } 533 534 maj_stat = gss_oid_to_str(&min_stat, mechanism, &oid_name); 535 if (maj_stat != GSS_S_COMPLETE) { 536 display_status("converting oid->string", maj_stat, min_stat); 537 return -1; 538 } 539 printf("Mechanism %.*s supports %d names\n", 540 (int) oid_name.length, (char *) oid_name.value, 541 (int) mech_names->count); 542 (void) gss_release_buffer(&min_stat, &oid_name); 543 544 for (i = 0; i < mech_names->count; i++) { 545 maj_stat = gss_oid_to_str(&min_stat, 546 &mech_names->elements[i], &oid_name); 547 if (maj_stat != GSS_S_COMPLETE) { 548 display_status("converting oid->string", maj_stat, min_stat); 549 return -1; 550 } 551 printf(" %d: %.*s\n", (int) i, 552 (int) oid_name.length, (char *) oid_name.value); 553 554 (void) gss_release_buffer(&min_stat, &oid_name); 555 } 556 (void) gss_release_oid_set(&min_stat, &mech_names); 557 } 558 559 if (use_file) { 560 read_file(msg, &in_buf); 561 } else { 562 /* Seal the message */ 563 in_buf.value = msg; 564 in_buf.length = strlen((char *)in_buf.value); 565 } 566 567 for (i = 0; i < (size_t)mcount; i++) { 568 if (wrap_flag) { 569 maj_stat = 570 gss_wrap(&min_stat, context, encrypt_flag, GSS_C_QOP_DEFAULT, 571 &in_buf, &state, &out_buf); 572 if (maj_stat != GSS_S_COMPLETE) { 573 display_status("wrapping message", maj_stat, min_stat); 574 (void) closesocket(s); 575 (void) gss_delete_sec_context(&min_stat, &context, 576 GSS_C_NO_BUFFER); 577 return -1; 578 } else if (encrypt_flag && !state) { 579 fprintf(stderr, "Warning! Message not encrypted.\n"); 580 } 581 } else { 582 out_buf = in_buf; 583 } 584 585 /* Send to server */ 586 if (send_token(s, (v1_format ? 0 587 : (TOKEN_DATA | 588 (wrap_flag ? TOKEN_WRAPPED : 0) | 589 (encrypt_flag ? TOKEN_ENCRYPTED : 0) | 590 (mic_flag ? TOKEN_SEND_MIC : 0))), 591 &out_buf) < 0) { 592 (void) closesocket(s); 593 (void) gss_delete_sec_context(&min_stat, &context, 594 GSS_C_NO_BUFFER); 595 return -1; 596 } 597 if (out_buf.value != in_buf.value) 598 (void) gss_release_buffer(&min_stat, &out_buf); 599 600 /* Read signature block into out_buf */ 601 if (recv_token(s, &token_flags, &out_buf) < 0) { 602 (void) closesocket(s); 603 (void) gss_delete_sec_context(&min_stat, &context, 604 GSS_C_NO_BUFFER); 605 return -1; 606 } 607 608 if (mic_flag) { 609 /* Verify signature block */ 610 maj_stat = gss_verify_mic(&min_stat, context, &in_buf, 611 &out_buf, &qop_state); 612 if (maj_stat != GSS_S_COMPLETE) { 613 display_status("verifying signature", maj_stat, min_stat); 614 (void) closesocket(s); 615 (void) gss_delete_sec_context(&min_stat, &context, 616 GSS_C_NO_BUFFER); 617 return -1; 618 } 619 620 if (verbose) 621 printf("Signature verified.\n"); 622 } else { 623 if (verbose) 624 printf("Response received.\n"); 625 } 626 627 free(out_buf.value); 628 } 629 630 if (use_file) 631 free(in_buf.value); 632 633 /* Send NOOP */ 634 if (!v1_format) 635 (void) send_token(s, TOKEN_NOOP, empty_token); 636 637 if (auth_flag) { 638 /* Delete context */ 639 maj_stat = gss_delete_sec_context(&min_stat, &context, &out_buf); 640 if (maj_stat != GSS_S_COMPLETE) { 641 display_status("deleting context", maj_stat, min_stat); 642 (void) closesocket(s); 643 (void) gss_delete_sec_context(&min_stat, &context, 644 GSS_C_NO_BUFFER); 645 return -1; 646 } 647 648 (void) gss_release_buffer(&min_stat, &out_buf); 649 } 650 651 (void) closesocket(s); 652 653 return 0; 654 } 655 656 static void 657 parse_oid(char *mechanism, gss_OID * oid) 658 { 659 char *mechstr = 0; 660 gss_buffer_desc tok; 661 OM_uint32 maj_stat, min_stat; 662 size_t i, mechlen = strlen(mechanism); 663 664 if (isdigit((int) mechanism[0])) { 665 mechstr = malloc(mechlen + 5); 666 if (!mechstr) { 667 fprintf(stderr, "Couldn't allocate mechanism scratch!\n"); 668 return; 669 } 670 mechstr[0] = '{'; 671 mechstr[1] = ' '; 672 for (i = 0; i < mechlen; i++) 673 mechstr[i + 2] = (mechanism[i] == '.') ? ' ' : mechanism[i]; 674 mechstr[mechlen + 2] = ' '; 675 mechstr[mechlen + 3] = ' '; 676 mechstr[mechlen + 4] = '\0'; 677 tok.value = mechstr; 678 } else 679 tok.value = mechanism; 680 tok.length = strlen(tok.value); 681 maj_stat = gss_str_to_oid(&min_stat, &tok, oid); 682 if (maj_stat != GSS_S_COMPLETE) { 683 display_status("str_to_oid", maj_stat, min_stat); 684 return; 685 } 686 if (mechstr) 687 free(mechstr); 688 } 689 690 static int max_threads = 1; 691 692 #ifdef _WIN32 693 static thread_count = 0; 694 static HANDLE hMutex = NULL; 695 static HANDLE hEvent = NULL; 696 697 void 698 InitHandles(void) 699 { 700 hMutex = CreateMutex(NULL, FALSE, NULL); 701 hEvent = CreateEvent(NULL, FALSE, FALSE, NULL); 702 } 703 704 void 705 CleanupHandles(void) 706 { 707 CloseHandle(hMutex); 708 CloseHandle(hEvent); 709 } 710 711 BOOL 712 WaitAndIncrementThreadCounter(void) 713 { 714 for (;;) { 715 if (WaitForSingleObject(hMutex, INFINITE) == WAIT_OBJECT_0) { 716 if (thread_count < max_threads) { 717 thread_count++; 718 ReleaseMutex(hMutex); 719 return TRUE; 720 } else { 721 ReleaseMutex(hMutex); 722 723 if (WaitForSingleObject(hEvent, INFINITE) == WAIT_OBJECT_0) { 724 continue; 725 } else { 726 return FALSE; 727 } 728 } 729 } else { 730 return FALSE; 731 } 732 } 733 } 734 735 BOOL 736 DecrementAndSignalThreadCounter(void) 737 { 738 if (WaitForSingleObject(hMutex, INFINITE) == WAIT_OBJECT_0) { 739 if (thread_count == max_threads) 740 ResetEvent(hEvent); 741 thread_count--; 742 ReleaseMutex(hMutex); 743 return TRUE; 744 } else { 745 return FALSE; 746 } 747 } 748 #endif 749 750 static char *service_name, *server_host, *msg; 751 static char *mechanism = 0; 752 static u_short port = 4444; 753 static int use_file = 0; 754 static OM_uint32 gss_flags = GSS_C_MUTUAL_FLAG | GSS_C_REPLAY_FLAG; 755 static OM_uint32 min_stat; 756 static gss_OID oid = GSS_C_NULL_OID; 757 static int mcount = 1, ccount = 1; 758 static int auth_flag, wrap_flag, encrypt_flag, mic_flag, v1_format; 759 static char *username = NULL; 760 static char *password = NULL; 761 762 static void 763 worker_bee(void *unused) 764 { 765 if (call_server(server_host, port, oid, service_name, 766 gss_flags, auth_flag, wrap_flag, encrypt_flag, mic_flag, 767 v1_format, msg, use_file, mcount, username, password) < 0) 768 exit(1); 769 770 #ifdef _WIN32 771 if (max_threads > 1) 772 DecrementAndSignalThreadCounter(); 773 #endif 774 } 775 776 int 777 main(argc, argv) 778 int argc; 779 char **argv; 780 { 781 int i; 782 783 display_file = stdout; 784 auth_flag = wrap_flag = encrypt_flag = mic_flag = 1; 785 v1_format = 0; 786 787 /* Parse arguments. */ 788 argc--; 789 argv++; 790 while (argc) { 791 if (strcmp(*argv, "-port") == 0) { 792 argc--; 793 argv++; 794 if (!argc) 795 usage(); 796 port = atoi(*argv); 797 } else if (strcmp(*argv, "-mech") == 0) { 798 argc--; 799 argv++; 800 if (!argc) 801 usage(); 802 mechanism = *argv; 803 } else if (strcmp(*argv, "-user") == 0) { 804 argc--; 805 argv++; 806 if (!argc) 807 usage(); 808 username = *argv; 809 } else if (strcmp(*argv, "-pass") == 0) { 810 argc--; 811 argv++; 812 if (!argc) 813 usage(); 814 password = *argv; 815 } else if (strcmp(*argv, "-iakerb") == 0) { 816 mechanism = "{ 1 3 6 1 5 2 5 }"; 817 } else if (strcmp(*argv, "-spnego") == 0) { 818 spnego = 1; 819 } else if (strcmp(*argv, "-krb5") == 0) { 820 mechanism = "{ 1 2 840 113554 1 2 2 }"; 821 #ifdef _WIN32 822 } else if (strcmp(*argv, "-threads") == 0) { 823 argc--; 824 argv++; 825 if (!argc) 826 usage(); 827 max_threads = atoi(*argv); 828 #endif 829 } else if (strcmp(*argv, "-dce") == 0) { 830 gss_flags |= GSS_C_DCE_STYLE; 831 } else if (strcmp(*argv, "-d") == 0) { 832 gss_flags |= GSS_C_DELEG_FLAG; 833 } else if (strcmp(*argv, "-seq") == 0) { 834 gss_flags |= GSS_C_SEQUENCE_FLAG; 835 } else if (strcmp(*argv, "-noreplay") == 0) { 836 gss_flags &= ~GSS_C_REPLAY_FLAG; 837 } else if (strcmp(*argv, "-nomutual") == 0) { 838 gss_flags &= ~GSS_C_MUTUAL_FLAG; 839 } else if (strcmp(*argv, "-f") == 0) { 840 use_file = 1; 841 } else if (strcmp(*argv, "-q") == 0) { 842 verbose = 0; 843 } else if (strcmp(*argv, "-ccount") == 0) { 844 argc--; 845 argv++; 846 if (!argc) 847 usage(); 848 ccount = atoi(*argv); 849 if (ccount <= 0) 850 usage(); 851 } else if (strcmp(*argv, "-mcount") == 0) { 852 argc--; 853 argv++; 854 if (!argc) 855 usage(); 856 mcount = atoi(*argv); 857 if (mcount < 0) 858 usage(); 859 } else if (strcmp(*argv, "-na") == 0) { 860 auth_flag = wrap_flag = encrypt_flag = mic_flag = 0; 861 } else if (strcmp(*argv, "-nw") == 0) { 862 wrap_flag = 0; 863 } else if (strcmp(*argv, "-nx") == 0) { 864 encrypt_flag = 0; 865 } else if (strcmp(*argv, "-nm") == 0) { 866 mic_flag = 0; 867 } else if (strcmp(*argv, "-v1") == 0) { 868 v1_format = 1; 869 } else 870 break; 871 argc--; 872 argv++; 873 } 874 if (argc != 3) 875 usage(); 876 877 #ifdef _WIN32 878 if (max_threads < 1) { 879 fprintf(stderr, "warning: there must be at least one thread\n"); 880 max_threads = 1; 881 } 882 #endif 883 884 server_host = *argv++; 885 service_name = *argv++; 886 msg = *argv++; 887 888 if (mechanism) 889 parse_oid(mechanism, &oid); 890 891 if (max_threads == 1) { 892 for (i = 0; i < ccount; i++) { 893 worker_bee(0); 894 } 895 } else { 896 #ifdef _WIN32 897 for (i = 0; i < ccount; i++) { 898 if (WaitAndIncrementThreadCounter()) { 899 uintptr_t handle = _beginthread(worker_bee, 0, (void *) 0); 900 if (handle == (uintptr_t) - 1) { 901 exit(1); 902 } 903 } else { 904 exit(1); 905 } 906 } 907 #else 908 /* boom */ 909 assert(max_threads == 1); 910 #endif 911 } 912 913 if (oid != GSS_C_NULL_OID) 914 (void) gss_release_oid(&min_stat, &oid); 915 916 #ifdef _WIN32 917 CleanupHandles(); 918 #endif 919 920 return 0; 921 } 922