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
usage(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
connect_to_server(char * host,u_short port)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
client_establish_context(int s,char * service_name,OM_uint32 gss_flags,int auth_flag,int v1_format,gss_OID oid,char * username,char * password,gss_ctx_id_t * gss_context,OM_uint32 * ret_flags)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
read_file(char * file_name,gss_buffer_t in_buf)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
call_server(char * host,u_short port,gss_OID oid,char * service_name,OM_uint32 gss_flags,int auth_flag,int wrap_flag,int encrypt_flag,int mic_flag,int v1_format,char * msg,int use_file,int mcount,char * username,char * password)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
parse_oid(char * mechanism,gss_OID * oid)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
InitHandles(void)628 InitHandles(void)
629 {
630 hMutex = CreateMutex(NULL, FALSE, NULL);
631 hEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
632 }
633
634 void
CleanupHandles(void)635 CleanupHandles(void)
636 {
637 CloseHandle(hMutex);
638 CloseHandle(hEvent);
639 }
640
641 BOOL
WaitAndIncrementThreadCounter(void)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
DecrementAndSignalThreadCounter(void)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
worker_bee(void * unused)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
main(int argc,char ** argv)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