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(void) 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 int result = -1, st; 186 gss_buffer_desc send_tok, recv_tok, pwbuf, *token_ptr; 187 gss_name_t target_name = GSS_C_NO_NAME, gss_username = GSS_C_NO_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_OID_set_desc mechs, neg_mechs, *mechsp = GSS_C_NO_OID_SET; 192 193 if (!auth_flag) 194 return send_token(s, TOKEN_NOOP, empty_token); 195 196 if (spnego) { 197 mechs.elements = &gss_spnego_mechanism_oid_desc; 198 mechs.count = 1; 199 mechsp = &mechs; 200 } else if (oid != GSS_C_NO_OID) { 201 mechs.elements = oid; 202 mechs.count = 1; 203 mechsp = &mechs; 204 } else { 205 mechs.elements = NULL; 206 mechs.count = 0; 207 } 208 209 if (username != NULL) { 210 send_tok.value = username; 211 send_tok.length = strlen(username); 212 213 maj_stat = gss_import_name(&min_stat, &send_tok, 214 (gss_OID) gss_nt_user_name, &gss_username); 215 if (maj_stat != GSS_S_COMPLETE) { 216 display_status("parsing client name", maj_stat, min_stat); 217 goto cleanup; 218 } 219 } 220 221 if (password != NULL) { 222 pwbuf.value = password; 223 pwbuf.length = strlen(password); 224 225 maj_stat = gss_acquire_cred_with_password(&min_stat, gss_username, 226 &pwbuf, 0, mechsp, 227 GSS_C_INITIATE, &cred, NULL, 228 NULL); 229 } else if (gss_username != GSS_C_NO_NAME) { 230 maj_stat = gss_acquire_cred(&min_stat, gss_username, 0, mechsp, 231 GSS_C_INITIATE, &cred, NULL, NULL); 232 } else { 233 maj_stat = GSS_S_COMPLETE; 234 } 235 if (maj_stat != GSS_S_COMPLETE) { 236 display_status("acquiring creds", maj_stat, min_stat); 237 goto cleanup; 238 } 239 if (spnego && oid != GSS_C_NO_OID) { 240 neg_mechs.elements = oid; 241 neg_mechs.count = 1; 242 maj_stat = gss_set_neg_mechs(&min_stat, cred, &neg_mechs); 243 if (maj_stat != GSS_S_COMPLETE) { 244 display_status("setting neg mechs", maj_stat, min_stat); 245 goto cleanup; 246 } 247 } 248 249 /* Import the name into target_name. Use send_tok to save local variable 250 * space. */ 251 send_tok.value = service_name; 252 send_tok.length = strlen(service_name); 253 maj_stat = gss_import_name(&min_stat, &send_tok, 254 (gss_OID) gss_nt_service_name, &target_name); 255 if (maj_stat != GSS_S_COMPLETE) { 256 display_status("parsing name", maj_stat, min_stat); 257 goto cleanup; 258 } 259 260 if (!v1_format) { 261 if (send_token(s, TOKEN_NOOP | TOKEN_CONTEXT_NEXT, empty_token) < 0) 262 goto cleanup; 263 } 264 265 /* 266 * Perform the context-establishment loop. 267 * 268 * On each pass through the loop, token_ptr points to the token to send to 269 * the server (or GSS_C_NO_BUFFER on the first pass). Every generated 270 * token is stored in send_tok which is then transmitted to the server; 271 * every received token is stored in recv_tok, which token_ptr is then set 272 * to, to be processed by the next call to gss_init_sec_context. 273 * 274 * GSS-API guarantees that send_tok's length will be non-zero if and only 275 * if the server is expecting another token from us, and that 276 * gss_init_sec_context returns GSS_S_CONTINUE_NEEDED if and only if the 277 * server has another token to send us. 278 */ 279 280 token_ptr = GSS_C_NO_BUFFER; 281 *gss_context = GSS_C_NO_CONTEXT; 282 283 do { 284 maj_stat = gss_init_sec_context(&init_sec_min_stat, cred, gss_context, 285 target_name, mechs.elements, gss_flags, 286 0, NULL, token_ptr, NULL, &send_tok, 287 ret_flags, NULL); 288 289 if (token_ptr != GSS_C_NO_BUFFER) 290 free(recv_tok.value); 291 292 if (send_tok.length > 0) { 293 if (verbose) { 294 printf("Sending init_sec_context token (size=%d)...", 295 (int) send_tok.length); 296 } 297 st = send_token(s, v1_format ? 0 : TOKEN_CONTEXT, &send_tok); 298 (void) gss_release_buffer(&min_stat, &send_tok); 299 if (st < 0) 300 goto cleanup; 301 } 302 303 if (maj_stat != GSS_S_COMPLETE && maj_stat != GSS_S_CONTINUE_NEEDED) { 304 display_status("initializing context", maj_stat, 305 init_sec_min_stat); 306 goto cleanup; 307 } 308 309 if (maj_stat == GSS_S_CONTINUE_NEEDED) { 310 if (verbose) 311 printf("continue needed..."); 312 if (recv_token(s, &token_flags, &recv_tok) < 0) 313 goto cleanup; 314 token_ptr = &recv_tok; 315 } 316 if (verbose) 317 printf("\n"); 318 } while (maj_stat == GSS_S_CONTINUE_NEEDED); 319 320 result = 0; 321 322 cleanup: 323 (void) gss_release_name(&min_stat, &gss_username); 324 (void) gss_release_cred(&min_stat, &cred); 325 (void) gss_release_name(&min_stat, &target_name); 326 return result; 327 } 328 329 static void 330 read_file(char *file_name, gss_buffer_t in_buf) 331 { 332 int fd, count; 333 struct stat stat_buf; 334 335 if ((fd = open(file_name, O_RDONLY, 0)) < 0) { 336 perror("open"); 337 fprintf(stderr, "Couldn't open file %s\n", file_name); 338 exit(1); 339 } 340 if (fstat(fd, &stat_buf) < 0) { 341 perror("fstat"); 342 exit(1); 343 } 344 in_buf->length = stat_buf.st_size; 345 346 if (in_buf->length == 0) { 347 in_buf->value = NULL; 348 return; 349 } 350 351 if ((in_buf->value = malloc(in_buf->length)) == 0) { 352 fprintf(stderr, "Couldn't allocate %d byte buffer for reading file\n", 353 (int) in_buf->length); 354 exit(1); 355 } 356 357 /* this code used to check for incomplete reads, but you can't get 358 * an incomplete read on any file for which fstat() is meaningful */ 359 360 count = read(fd, in_buf->value, in_buf->length); 361 if (count < 0) { 362 perror("read"); 363 exit(1); 364 } 365 if (count < (int)in_buf->length) 366 fprintf(stderr, "Warning, only read in %d bytes, expected %d\n", 367 count, (int) in_buf->length); 368 } 369 370 /* 371 * Function: call_server 372 * 373 * Purpose: Call the "sign" service. 374 * 375 * Arguments: 376 * 377 * host (r) the host providing the service 378 * port (r) the port to connect to on host 379 * service_name (r) the GSS-API service name to authenticate to 380 * gss_flags (r) GSS-API delegation flag (if any) 381 * auth_flag (r) whether to do authentication 382 * wrap_flag (r) whether to do message wrapping at all 383 * encrypt_flag (r) whether to do encryption while wrapping 384 * mic_flag (r) whether to request a MIC from the server 385 * msg (r) the message to have "signed" 386 * use_file (r) whether to treat msg as an input file name 387 * mcount (r) the number of times to send the message 388 * 389 * Returns: 0 on success, -1 on failure 390 * 391 * Effects: 392 * 393 * call_server opens a TCP connection to <host:port> and establishes a 394 * GSS-API context with service_name over the connection. It then 395 * seals msg in a GSS-API token with gss_wrap, sends it to the server, 396 * reads back a GSS-API signature block for msg from the server, and 397 * verifies it with gss_verify. -1 is returned if any step fails, 398 * otherwise 0 is returned. */ 399 static int 400 call_server(char *host, u_short port, gss_OID oid, char *service_name, 401 OM_uint32 gss_flags, int auth_flag, int wrap_flag, 402 int encrypt_flag, int mic_flag, int v1_format, char *msg, 403 int use_file, int mcount, char *username, char *password) 404 { 405 gss_ctx_id_t context = GSS_C_NO_CONTEXT; 406 gss_buffer_desc in_buf, out_buf; 407 int s = -1, result = -1, state; 408 OM_uint32 ret_flags; 409 OM_uint32 maj_stat, min_stat; 410 gss_name_t src_name = GSS_C_NO_NAME, targ_name = GSS_C_NO_NAME; 411 gss_buffer_desc sname = GSS_C_EMPTY_BUFFER, tname = GSS_C_EMPTY_BUFFER; 412 OM_uint32 lifetime; 413 gss_OID mechanism, name_type; 414 int is_local; 415 OM_uint32 context_flags; 416 int is_open; 417 gss_qop_t qop_state; 418 gss_OID_set mech_names; 419 gss_buffer_desc oid_name; 420 size_t i; 421 int token_flags; 422 423 /* Open connection */ 424 if ((s = connect_to_server(host, port)) < 0) 425 goto cleanup; 426 427 /* Establish context */ 428 if (client_establish_context(s, service_name, gss_flags, auth_flag, 429 v1_format, oid, username, password, 430 &context, &ret_flags) < 0) { 431 goto cleanup; 432 } 433 434 if (auth_flag && verbose) { 435 /* display the flags */ 436 display_ctx_flags(ret_flags); 437 438 /* Get context information */ 439 maj_stat = gss_inquire_context(&min_stat, context, 440 &src_name, &targ_name, &lifetime, 441 &mechanism, &context_flags, 442 &is_local, &is_open); 443 if (maj_stat != GSS_S_COMPLETE) { 444 display_status("inquiring context", maj_stat, min_stat); 445 goto cleanup; 446 } 447 448 maj_stat = gss_display_name(&min_stat, src_name, &sname, &name_type); 449 if (maj_stat != GSS_S_COMPLETE) { 450 display_status("displaying source name", maj_stat, min_stat); 451 goto cleanup; 452 } 453 maj_stat = gss_display_name(&min_stat, targ_name, &tname, 454 (gss_OID *) NULL); 455 if (maj_stat != GSS_S_COMPLETE) { 456 display_status("displaying target name", maj_stat, min_stat); 457 goto cleanup; 458 } 459 printf("\"%.*s\" to \"%.*s\", lifetime %d, flags %x, %s, %s\n", 460 (int) sname.length, (char *) sname.value, 461 (int) tname.length, (char *) tname.value, lifetime, 462 context_flags, 463 (is_local) ? "locally initiated" : "remotely initiated", 464 (is_open) ? "open" : "closed"); 465 466 maj_stat = gss_oid_to_str(&min_stat, name_type, &oid_name); 467 if (maj_stat != GSS_S_COMPLETE) { 468 display_status("converting oid->string", maj_stat, min_stat); 469 goto cleanup; 470 } 471 printf("Name type of source name is %.*s.\n", 472 (int) oid_name.length, (char *) oid_name.value); 473 (void) gss_release_buffer(&min_stat, &oid_name); 474 475 /* Now get the names supported by the mechanism */ 476 maj_stat = gss_inquire_names_for_mech(&min_stat, 477 mechanism, &mech_names); 478 if (maj_stat != GSS_S_COMPLETE) { 479 display_status("inquiring mech names", maj_stat, min_stat); 480 goto cleanup; 481 } 482 483 maj_stat = gss_oid_to_str(&min_stat, mechanism, &oid_name); 484 if (maj_stat != GSS_S_COMPLETE) { 485 display_status("converting oid->string", maj_stat, min_stat); 486 goto cleanup; 487 } 488 printf("Mechanism %.*s supports %d names\n", 489 (int) oid_name.length, (char *) oid_name.value, 490 (int) mech_names->count); 491 (void) gss_release_buffer(&min_stat, &oid_name); 492 493 for (i = 0; i < mech_names->count; i++) { 494 maj_stat = gss_oid_to_str(&min_stat, 495 &mech_names->elements[i], &oid_name); 496 if (maj_stat != GSS_S_COMPLETE) { 497 display_status("converting oid->string", maj_stat, min_stat); 498 goto cleanup; 499 } 500 printf(" %d: %.*s\n", (int) i, 501 (int) oid_name.length, (char *) oid_name.value); 502 503 (void) gss_release_buffer(&min_stat, &oid_name); 504 } 505 (void) gss_release_oid_set(&min_stat, &mech_names); 506 } 507 508 if (use_file) { 509 read_file(msg, &in_buf); 510 } else { 511 /* Seal the message */ 512 in_buf.value = msg; 513 in_buf.length = strlen((char *)in_buf.value); 514 } 515 516 for (i = 0; i < (size_t)mcount; i++) { 517 if (wrap_flag) { 518 maj_stat = 519 gss_wrap(&min_stat, context, encrypt_flag, GSS_C_QOP_DEFAULT, 520 &in_buf, &state, &out_buf); 521 if (maj_stat != GSS_S_COMPLETE) { 522 display_status("wrapping message", maj_stat, min_stat); 523 goto cleanup; 524 } else if (encrypt_flag && !state) { 525 fprintf(stderr, "Warning! Message not encrypted.\n"); 526 } 527 } else { 528 out_buf = in_buf; 529 } 530 531 /* Send to server */ 532 if (send_token(s, (v1_format ? 0 533 : (TOKEN_DATA | 534 (wrap_flag ? TOKEN_WRAPPED : 0) | 535 (encrypt_flag ? TOKEN_ENCRYPTED : 0) | 536 (mic_flag ? TOKEN_SEND_MIC : 0))), 537 &out_buf) < 0) 538 goto cleanup; 539 540 if (out_buf.value != in_buf.value) 541 (void) gss_release_buffer(&min_stat, &out_buf); 542 543 /* Read signature block into out_buf */ 544 if (recv_token(s, &token_flags, &out_buf) < 0) 545 goto cleanup; 546 547 if (mic_flag) { 548 /* Verify signature block */ 549 maj_stat = gss_verify_mic(&min_stat, context, &in_buf, 550 &out_buf, &qop_state); 551 if (maj_stat != GSS_S_COMPLETE) { 552 display_status("verifying signature", maj_stat, min_stat); 553 goto cleanup; 554 } 555 556 if (verbose) 557 printf("Signature verified.\n"); 558 } else { 559 if (verbose) 560 printf("Response received.\n"); 561 } 562 563 free(out_buf.value); 564 } 565 566 if (use_file) 567 free(in_buf.value); 568 569 /* Send NOOP */ 570 if (!v1_format) 571 (void) send_token(s, TOKEN_NOOP, empty_token); 572 573 result = 0; 574 575 cleanup: 576 (void) gss_release_name(&min_stat, &src_name); 577 (void) gss_release_name(&min_stat, &targ_name); 578 (void) gss_release_buffer(&min_stat, &sname); 579 (void) gss_release_buffer(&min_stat, &tname); 580 (void) gss_delete_sec_context(&min_stat, &context, GSS_C_NO_BUFFER); 581 if (s >= 0) 582 (void) closesocket(s); 583 return result; 584 } 585 586 static void 587 parse_oid(char *mechanism, gss_OID * oid) 588 { 589 char *mechstr = 0; 590 gss_buffer_desc tok; 591 OM_uint32 maj_stat, min_stat; 592 size_t i, mechlen = strlen(mechanism); 593 594 if (isdigit((int) mechanism[0])) { 595 mechstr = malloc(mechlen + 5); 596 if (!mechstr) { 597 fprintf(stderr, "Couldn't allocate mechanism scratch!\n"); 598 return; 599 } 600 mechstr[0] = '{'; 601 mechstr[1] = ' '; 602 for (i = 0; i < mechlen; i++) 603 mechstr[i + 2] = (mechanism[i] == '.') ? ' ' : mechanism[i]; 604 mechstr[mechlen + 2] = ' '; 605 mechstr[mechlen + 3] = ' '; 606 mechstr[mechlen + 4] = '\0'; 607 tok.value = mechstr; 608 } else 609 tok.value = mechanism; 610 tok.length = strlen(tok.value); 611 maj_stat = gss_str_to_oid(&min_stat, &tok, oid); 612 if (maj_stat != GSS_S_COMPLETE) { 613 display_status("str_to_oid", maj_stat, min_stat); 614 return; 615 } 616 if (mechstr) 617 free(mechstr); 618 } 619 620 static int max_threads = 1; 621 622 #ifdef _WIN32 623 static thread_count = 0; 624 static HANDLE hMutex = NULL; 625 static HANDLE hEvent = NULL; 626 627 void 628 InitHandles(void) 629 { 630 hMutex = CreateMutex(NULL, FALSE, NULL); 631 hEvent = CreateEvent(NULL, FALSE, FALSE, NULL); 632 } 633 634 void 635 CleanupHandles(void) 636 { 637 CloseHandle(hMutex); 638 CloseHandle(hEvent); 639 } 640 641 BOOL 642 WaitAndIncrementThreadCounter(void) 643 { 644 for (;;) { 645 if (WaitForSingleObject(hMutex, INFINITE) == WAIT_OBJECT_0) { 646 if (thread_count < max_threads) { 647 thread_count++; 648 ReleaseMutex(hMutex); 649 return TRUE; 650 } else { 651 ReleaseMutex(hMutex); 652 653 if (WaitForSingleObject(hEvent, INFINITE) == WAIT_OBJECT_0) { 654 continue; 655 } else { 656 return FALSE; 657 } 658 } 659 } else { 660 return FALSE; 661 } 662 } 663 } 664 665 BOOL 666 DecrementAndSignalThreadCounter(void) 667 { 668 if (WaitForSingleObject(hMutex, INFINITE) == WAIT_OBJECT_0) { 669 if (thread_count == max_threads) 670 ResetEvent(hEvent); 671 thread_count--; 672 ReleaseMutex(hMutex); 673 return TRUE; 674 } else { 675 return FALSE; 676 } 677 } 678 #endif 679 680 static char *service_name, *server_host, *msg; 681 static char *mechanism = 0; 682 static u_short port = 4444; 683 static int use_file = 0; 684 static OM_uint32 gss_flags = GSS_C_MUTUAL_FLAG | GSS_C_REPLAY_FLAG; 685 static OM_uint32 min_stat; 686 static gss_OID oid = GSS_C_NULL_OID; 687 static int mcount = 1, ccount = 1; 688 static int auth_flag, wrap_flag, encrypt_flag, mic_flag, v1_format; 689 static char *username = NULL; 690 static char *password = NULL; 691 692 static void 693 worker_bee(void *unused) 694 { 695 if (call_server(server_host, port, oid, service_name, 696 gss_flags, auth_flag, wrap_flag, encrypt_flag, mic_flag, 697 v1_format, msg, use_file, mcount, username, password) < 0) 698 exit(1); 699 700 #ifdef _WIN32 701 if (max_threads > 1) 702 DecrementAndSignalThreadCounter(); 703 #endif 704 } 705 706 int 707 main(int argc, char **argv) 708 { 709 int i; 710 711 display_file = stdout; 712 auth_flag = wrap_flag = encrypt_flag = mic_flag = 1; 713 v1_format = 0; 714 715 /* Parse arguments. */ 716 argc--; 717 argv++; 718 while (argc) { 719 if (strcmp(*argv, "-port") == 0) { 720 argc--; 721 argv++; 722 if (!argc) 723 usage(); 724 port = atoi(*argv); 725 } else if (strcmp(*argv, "-mech") == 0) { 726 argc--; 727 argv++; 728 if (!argc) 729 usage(); 730 mechanism = *argv; 731 } else if (strcmp(*argv, "-user") == 0) { 732 argc--; 733 argv++; 734 if (!argc) 735 usage(); 736 username = *argv; 737 } else if (strcmp(*argv, "-pass") == 0) { 738 argc--; 739 argv++; 740 if (!argc) 741 usage(); 742 password = *argv; 743 } else if (strcmp(*argv, "-iakerb") == 0) { 744 mechanism = "{ 1 3 6 1 5 2 5 }"; 745 } else if (strcmp(*argv, "-spnego") == 0) { 746 spnego = 1; 747 } else if (strcmp(*argv, "-krb5") == 0) { 748 mechanism = "{ 1 2 840 113554 1 2 2 }"; 749 #ifdef _WIN32 750 } else if (strcmp(*argv, "-threads") == 0) { 751 argc--; 752 argv++; 753 if (!argc) 754 usage(); 755 max_threads = atoi(*argv); 756 #endif 757 } else if (strcmp(*argv, "-dce") == 0) { 758 gss_flags |= GSS_C_DCE_STYLE; 759 } else if (strcmp(*argv, "-d") == 0) { 760 gss_flags |= GSS_C_DELEG_FLAG; 761 } else if (strcmp(*argv, "-seq") == 0) { 762 gss_flags |= GSS_C_SEQUENCE_FLAG; 763 } else if (strcmp(*argv, "-noreplay") == 0) { 764 gss_flags &= ~GSS_C_REPLAY_FLAG; 765 } else if (strcmp(*argv, "-nomutual") == 0) { 766 gss_flags &= ~GSS_C_MUTUAL_FLAG; 767 } else if (strcmp(*argv, "-f") == 0) { 768 use_file = 1; 769 } else if (strcmp(*argv, "-q") == 0) { 770 verbose = 0; 771 } else if (strcmp(*argv, "-ccount") == 0) { 772 argc--; 773 argv++; 774 if (!argc) 775 usage(); 776 ccount = atoi(*argv); 777 if (ccount <= 0) 778 usage(); 779 } else if (strcmp(*argv, "-mcount") == 0) { 780 argc--; 781 argv++; 782 if (!argc) 783 usage(); 784 mcount = atoi(*argv); 785 if (mcount < 0) 786 usage(); 787 } else if (strcmp(*argv, "-na") == 0) { 788 auth_flag = wrap_flag = encrypt_flag = mic_flag = 0; 789 } else if (strcmp(*argv, "-nw") == 0) { 790 wrap_flag = 0; 791 } else if (strcmp(*argv, "-nx") == 0) { 792 encrypt_flag = 0; 793 } else if (strcmp(*argv, "-nm") == 0) { 794 mic_flag = 0; 795 } else if (strcmp(*argv, "-v1") == 0) { 796 v1_format = 1; 797 } else 798 break; 799 argc--; 800 argv++; 801 } 802 if (argc != 3) 803 usage(); 804 805 #ifdef _WIN32 806 if (max_threads < 1) { 807 fprintf(stderr, "warning: there must be at least one thread\n"); 808 max_threads = 1; 809 } 810 #endif 811 812 server_host = *argv++; 813 service_name = *argv++; 814 msg = *argv++; 815 816 if (mechanism) 817 parse_oid(mechanism, &oid); 818 819 if (max_threads == 1) { 820 for (i = 0; i < ccount; i++) { 821 worker_bee(0); 822 } 823 } else { 824 #ifdef _WIN32 825 for (i = 0; i < ccount; i++) { 826 if (WaitAndIncrementThreadCounter()) { 827 uintptr_t handle = _beginthread(worker_bee, 0, (void *) 0); 828 if (handle == (uintptr_t) - 1) { 829 exit(1); 830 } 831 } else { 832 exit(1); 833 } 834 } 835 #else 836 /* boom */ 837 assert(max_threads == 1); 838 #endif 839 } 840 841 if (oid != GSS_C_NULL_OID) 842 (void) gss_release_oid(&min_stat, &oid); 843 844 #ifdef _WIN32 845 CleanupHandles(); 846 #endif 847 848 return 0; 849 } 850