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, 2008 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 "k5-platform.h"
48 #ifdef _WIN32
49 #include <windows.h>
50 #include <winsock.h>
51 #else
52 #include <unistd.h>
53 #include <ctype.h>
54 #include <sys/types.h>
55 #include <sys/socket.h>
56 #include <netinet/in.h>
57 #include <netdb.h>
58 #include <sys/stat.h>
59 #include <fcntl.h>
60 #include <pthread.h>
61 #endif
62
63 #include <gssapi/gssapi_generic.h>
64 #include "gss-misc.h"
65 #include "port-sockets.h"
66 #include "fake-addrinfo.h"
67
68 static int verbose = 1;
69
70 static void
usage()71 usage()
72 {
73 fprintf(stderr, "Usage: gss-client [-port port] [-mech mechanism] [-d]\n");
74 fprintf(stderr, " [-seq] [-noreplay] [-nomutual]");
75 fprintf(stderr, " [-threads num]");
76 fprintf(stderr, "\n");
77 fprintf(stderr, " [-f] [-q] [-ccount count] [-mcount count]\n");
78 fprintf(stderr, " [-v1] [-na] [-nw] [-nx] [-nm] host service msg\n");
79 exit(1);
80 }
81
82 /*
83 * Function: get_server_info
84 *
85 * Purpose: Sets up a socket address for the named host and port.
86 *
87 * Arguments:
88 *
89 * host (r) the target host name
90 * port (r) the target port, in host byte order
91 *
92 * Returns: 0 on success, or -1 on failure
93 *
94 * Effects:
95 *
96 * The host name is resolved with gethostbyname(), and "saddr" is set
97 * to the desired socket address. If an error occurs, an error
98 * message is displayed and -1 is returned.
99 */
100 struct sockaddr_in saddr;
101 static int
get_server_info(char * host,u_short port)102 get_server_info(char *host, u_short port)
103 {
104 struct hostent *hp;
105
106 hp = gethostbyname(host);
107 if (hp == NULL) {
108 fprintf(stderr, "Unknown host: %s\n", host);
109 return -1;
110 }
111
112 saddr.sin_family = hp->h_addrtype;
113 memcpy(&saddr.sin_addr, hp->h_addr, sizeof(saddr.sin_addr));
114 saddr.sin_port = htons(port);
115 return 0;
116 }
117
118 /*
119 * Function: connect_to_server
120 *
121 * Purpose: Opens a TCP connection to the name host and port.
122 *
123 * Arguments:
124 *
125 * host (r) the target host name
126 * port (r) the target port, in host byte order
127 *
128 * Returns: the established socket file descriptor, or -1 on failure
129 *
130 * Effects:
131 *
132 * The host name is resolved with gethostbyname(), and the socket is
133 * opened and connected. If an error occurs, an error message is
134 * displayed and -1 is returned.
135 */
136 static int
connect_to_server()137 connect_to_server()
138 {
139 int s;
140
141 s = socket(AF_INET, SOCK_STREAM, 0);
142 if (s < 0) {
143 perror("creating socket");
144 return -1;
145 }
146 if (connect(s, (struct sockaddr *)&saddr, sizeof(saddr)) < 0) {
147 perror("connecting to server");
148 (void)closesocket(s);
149 return -1;
150 }
151 return s;
152 }
153
154 /*
155 * Function: client_establish_context
156 *
157 * Purpose: establishes a GSS-API context with a specified service and
158 * returns the context handle
159 *
160 * Arguments:
161 *
162 * s (r) an established TCP connection to the service
163 * service_name (r) the ASCII service name of the service
164 * gss_flags (r) GSS-API delegation flag (if any)
165 * auth_flag (r) whether to actually do authentication
166 * v1_format (r) whether the v1 sample protocol should be used
167 * oid (r) OID of the mechanism to use
168 * context (w) the established GSS-API context
169 * ret_flags (w) the returned flags from init_sec_context
170 *
171 * Returns: 0 on success, -1 on failure
172 *
173 * Effects:
174 *
175 * service_name is imported as a GSS-API name and a GSS-API context is
176 * established with the corresponding service; the service should be
177 * listening on the TCP connection s. The default GSS-API mechanism
178 * is used, and mutual authentication and replay detection are
179 * requested.
180 *
181 * If successful, the context handle is returned in context. If
182 * unsuccessful, the GSS-API error messages are displayed on stderr
183 * and -1 is returned.
184 */
185 static int
client_establish_context(int s,char * service_name,OM_uint32 gss_flags,int auth_flag,int v1_format,gss_OID oid,gss_ctx_id_t * gss_context,OM_uint32 * ret_flags)186 client_establish_context(int s, char *service_name, OM_uint32 gss_flags,
187 int auth_flag, int v1_format, gss_OID oid,
188 gss_ctx_id_t *gss_context, OM_uint32 *ret_flags)
189 {
190 if (auth_flag) {
191 gss_buffer_desc send_tok, recv_tok, *token_ptr;
192 gss_name_t target_name;
193 OM_uint32 maj_stat, min_stat, init_sec_min_stat;
194 int token_flags;
195
196 /*
197 * Import the name into target_name. Use send_tok to save
198 * local variable space.
199 */
200 send_tok.value = service_name;
201 send_tok.length = strlen(service_name);
202 maj_stat = gss_import_name(&min_stat, &send_tok,
203 (gss_OID)gss_nt_service_name, &target_name);
204 if (maj_stat != GSS_S_COMPLETE) {
205 display_status("parsing name", maj_stat, min_stat);
206 return -1;
207 }
208
209 if (!v1_format) {
210 if (send_token(s, TOKEN_NOOP | TOKEN_CONTEXT_NEXT,
211 empty_token) < 0) {
212 (void)gss_release_name(&min_stat, &target_name);
213 return -1;
214 }
215 }
216
217 /*
218 * Perform the context-establishement loop.
219 *
220 * On each pass through the loop, token_ptr points to the token
221 * to send to the server (or GSS_C_NO_BUFFER on the first pass).
222 * Every generated token is stored in send_tok which is then
223 * transmitted to the server; every received token is stored in
224 * recv_tok, which token_ptr is then set to, to be processed by
225 * the next call to gss_init_sec_context.
226 *
227 * GSS-API guarantees that send_tok's length will be non-zero
228 * if and only if the server is expecting another token from us,
229 * and that gss_init_sec_context returns GSS_S_CONTINUE_NEEDED if
230 * and only if the server has another token to send us.
231 */
232
233 token_ptr = GSS_C_NO_BUFFER;
234 *gss_context = GSS_C_NO_CONTEXT;
235
236 do {
237 maj_stat = gss_init_sec_context(&init_sec_min_stat,
238 GSS_C_NO_CREDENTIAL, gss_context,
239 target_name, oid, gss_flags, 0,
240 NULL, token_ptr, NULL, &send_tok,
241 ret_flags, NULL);
242
243 if (token_ptr != GSS_C_NO_BUFFER)
244 free(recv_tok.value);
245
246 if (send_tok.length != 0) {
247 if (verbose) {
248 printf("Sending init_sec_context token (size=%d)...",
249 (int)send_tok.length);
250 }
251 if (send_token(s, v1_format ? 0 : TOKEN_CONTEXT,
252 &send_tok) < 0) {
253 (void)gss_release_buffer(&min_stat, &send_tok);
254 (void)gss_release_name(&min_stat, &target_name);
255 if (*gss_context != GSS_C_NO_CONTEXT) {
256 gss_delete_sec_context(&min_stat, gss_context,
257 GSS_C_NO_BUFFER);
258 *gss_context = GSS_C_NO_CONTEXT;
259 }
260 return -1;
261 }
262 }
263 (void)gss_release_buffer(&min_stat, &send_tok);
264
265 if (maj_stat != GSS_S_COMPLETE &&
266 maj_stat != GSS_S_CONTINUE_NEEDED) {
267 display_status("initializing context", maj_stat,
268 init_sec_min_stat);
269 (void)gss_release_name(&min_stat, &target_name);
270 if (*gss_context != GSS_C_NO_CONTEXT) {
271 gss_delete_sec_context(&min_stat, gss_context,
272 GSS_C_NO_BUFFER);
273 }
274 return -1;
275 }
276
277 if (maj_stat == GSS_S_CONTINUE_NEEDED) {
278 if (verbose)
279 printf("continue needed...");
280 if (recv_token(s, &token_flags, &recv_tok) < 0) {
281 (void)gss_release_name(&min_stat, &target_name);
282 return -1;
283 }
284 token_ptr = &recv_tok;
285 }
286 if (verbose)
287 printf("\n");
288 } while (maj_stat == GSS_S_CONTINUE_NEEDED);
289
290 (void)gss_release_name(&min_stat, &target_name);
291 } else if (send_token(s, TOKEN_NOOP, empty_token) < 0) {
292 return -1;
293 }
294
295 return 0;
296 }
297
298 static void
read_file(char * file_name,gss_buffer_t in_buf)299 read_file(char *file_name, gss_buffer_t in_buf)
300 {
301 int fd, count;
302 struct stat stat_buf;
303
304 fd = open(file_name, O_RDONLY, 0);
305 if (fd < 0) {
306 perror("open");
307 fprintf(stderr, "Couldn't open file %s\n", file_name);
308 exit(2);
309 }
310 if (fstat(fd, &stat_buf) < 0) {
311 perror("fstat");
312 exit(3);
313 }
314 in_buf->length = stat_buf.st_size;
315
316 if (in_buf->length == 0) {
317 in_buf->value = NULL;
318 return;
319 }
320
321 in_buf->value = malloc(in_buf->length);
322 if (in_buf->value == NULL) {
323 fprintf(stderr, "Couldn't allocate %d byte buffer for reading file\n",
324 (int)in_buf->length);
325 exit(4);
326 }
327
328 /* This code used to check for incomplete reads, but you can't get
329 * an incomplete read on any file for which fstat() is meaningful. */
330
331 count = read(fd, in_buf->value, in_buf->length);
332 if (count < 0) {
333 perror("read");
334 exit(5);
335 }
336 if ((size_t)count < in_buf->length) {
337 fprintf(stderr, "Warning, only read in %d bytes, expected %d\n",
338 count, (int)in_buf->length);
339 }
340 }
341
342 /*
343 * Function: call_server
344 *
345 * Purpose: Call the "sign" service.
346 *
347 * Arguments:
348 *
349 * host (r) the host providing the service
350 * port (r) the port to connect to on host
351 * service_name (r) the GSS-API service name to authenticate to
352 * gss_flags (r) GSS-API delegation flag (if any)
353 * auth_flag (r) whether to do authentication
354 * wrap_flag (r) whether to do message wrapping at all
355 * encrypt_flag (r) whether to do encryption while wrapping
356 * mic_flag (r) whether to request a MIC from the server
357 * msg (r) the message to have "signed"
358 * use_file (r) whether to treat msg as an input file name
359 * mcount (r) the number of times to send the message
360 *
361 * Returns: 0 on success, -1 on failure
362 *
363 * Effects:
364 *
365 * call_server opens a TCP connection to <host:port> and establishes a
366 * GSS-API context with service_name over the connection. It then
367 * seals msg in a GSS-API token with gss_wrap, sends it to the server,
368 * reads back a GSS-API signature block for msg from the server, and
369 * verifies it with gss_verify. -1 is returned if any step fails,
370 * otherwise 0 is returned.
371 */
372 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,size_t mcount)373 call_server(char *host, u_short port, gss_OID oid, char *service_name,
374 OM_uint32 gss_flags, int auth_flag, int wrap_flag,
375 int encrypt_flag, int mic_flag, int v1_format, char *msg,
376 int use_file, size_t mcount)
377 {
378 gss_ctx_id_t context;
379 gss_buffer_desc in_buf, out_buf, sname, tname, oid_name;
380 int s, state, is_local, is_open, flags, token_flags;
381 OM_uint32 ret_flags, maj_stat, min_stat, lifetime, context_flags;
382 gss_name_t src_name, targ_name;
383 gss_OID mechanism, name_type;
384 gss_qop_t qop_state;
385 gss_OID_set mech_names;
386 size_t i;
387
388 /* Open connection. */
389 s = connect_to_server();
390 if (s < 0)
391 return -1;
392
393 /* Establish context. */
394 if (client_establish_context(s, service_name, gss_flags, auth_flag,
395 v1_format, oid, &context, &ret_flags) < 0) {
396 (void)closesocket(s);
397 return -1;
398 }
399
400 if (auth_flag && verbose) {
401 /* Display the flags. */
402 display_ctx_flags(ret_flags);
403
404 /* Get context information. */
405 maj_stat = gss_inquire_context(&min_stat, context, &src_name,
406 &targ_name, &lifetime, &mechanism,
407 &context_flags, &is_local, &is_open);
408 if (maj_stat != GSS_S_COMPLETE) {
409 display_status("inquiring context", maj_stat, min_stat);
410 return -1;
411 }
412
413 maj_stat = gss_display_name(&min_stat, src_name, &sname, &name_type);
414 if (maj_stat != GSS_S_COMPLETE) {
415 display_status("displaying source name", maj_stat, min_stat);
416 return -1;
417 }
418 maj_stat = gss_display_name(&min_stat, targ_name, &tname, NULL);
419 if (maj_stat != GSS_S_COMPLETE) {
420 display_status("displaying target name", maj_stat, min_stat);
421 return -1;
422 }
423 printf("\"%.*s\" to \"%.*s\", lifetime %d, flags %x, %s, %s\n",
424 (int)sname.length, (char *)sname.value,
425 (int)tname.length, (char *)tname.value, lifetime, context_flags,
426 is_local ? "locally initiated" : "remotely initiated",
427 is_open ? "open" : "closed");
428
429 (void)gss_release_name(&min_stat, &src_name);
430 (void)gss_release_name(&min_stat, &targ_name);
431 (void)gss_release_buffer(&min_stat, &sname);
432 (void)gss_release_buffer(&min_stat, &tname);
433
434 maj_stat = gss_oid_to_str(&min_stat, name_type, &oid_name);
435 if (maj_stat != GSS_S_COMPLETE) {
436 display_status("converting oid->string", maj_stat, min_stat);
437 return -1;
438 }
439 printf("Name type of source name is %.*s.\n", (int)oid_name.length,
440 (char *)oid_name.value);
441 (void)gss_release_buffer(&min_stat, &oid_name);
442
443 /* Now get the names supported by the mechanism. */
444 maj_stat = gss_inquire_names_for_mech(&min_stat, mechanism,
445 &mech_names);
446 if (maj_stat != GSS_S_COMPLETE) {
447 display_status("inquiring mech names", maj_stat, min_stat);
448 return -1;
449 }
450
451 maj_stat = gss_oid_to_str(&min_stat, mechanism, &oid_name);
452 if (maj_stat != GSS_S_COMPLETE) {
453 display_status("converting oid->string", maj_stat, min_stat);
454 return -1;
455 }
456 printf("Mechanism %.*s supports %d names\n", (int)oid_name.length,
457 (char *)oid_name.value, (int)mech_names->count);
458 (void)gss_release_buffer(&min_stat, &oid_name);
459
460 for (i = 0; i < mech_names->count; i++) {
461 maj_stat = gss_oid_to_str(&min_stat, &mech_names->elements[i],
462 &oid_name);
463 if (maj_stat != GSS_S_COMPLETE) {
464 display_status("converting oid->string", maj_stat, min_stat);
465 return -1;
466 }
467 printf(" %d: %.*s\n", (int)i, (int)oid_name.length,
468 (char *)oid_name.value);
469
470 (void)gss_release_buffer(&min_stat, &oid_name);
471 }
472 (void)gss_release_oid_set(&min_stat, &mech_names);
473 }
474
475 if (use_file) {
476 read_file(msg, &in_buf);
477 } else {
478 /* Seal the message. */
479 in_buf.value = msg;
480 in_buf.length = strlen(msg);
481 }
482
483 for (i = 0; i < mcount; i++) {
484 if (wrap_flag) {
485 maj_stat = gss_wrap(&min_stat, context, encrypt_flag,
486 GSS_C_QOP_DEFAULT, &in_buf, &state, &out_buf);
487 if (maj_stat != GSS_S_COMPLETE) {
488 display_status("wrapping message", maj_stat, min_stat);
489 (void)closesocket(s);
490 (void)gss_delete_sec_context(&min_stat, &context,
491 GSS_C_NO_BUFFER);
492 return -1;
493 } else if (encrypt_flag && !state) {
494 fprintf(stderr, "Warning! Message not encrypted.\n");
495 }
496 } else {
497 out_buf = in_buf;
498 }
499
500 /* Send to server. */
501 flags = 0;
502 if (!v1_format) {
503 flags = TOKEN_DATA | (wrap_flag ? TOKEN_WRAPPED : 0) |
504 (encrypt_flag ? TOKEN_ENCRYPTED : 0) |
505 (mic_flag ? TOKEN_SEND_MIC : 0);
506 }
507 if (send_token(s, flags, &out_buf) < 0) {
508 (void)closesocket(s);
509 (void)gss_delete_sec_context(&min_stat, &context, GSS_C_NO_BUFFER);
510 return -1;
511 }
512 if (out_buf.value != in_buf.value)
513 (void)gss_release_buffer(&min_stat, &out_buf);
514
515 /* Read signature block into out_buf. */
516 if (recv_token(s, &token_flags, &out_buf) < 0) {
517 (void)closesocket(s);
518 (void)gss_delete_sec_context(&min_stat, &context, GSS_C_NO_BUFFER);
519 return -1;
520 }
521
522 if (mic_flag) {
523 /* Verify signature block. */
524 maj_stat = gss_verify_mic(&min_stat, context, &in_buf, &out_buf,
525 &qop_state);
526 if (maj_stat != GSS_S_COMPLETE) {
527 display_status("verifying signature", maj_stat, min_stat);
528 (void)closesocket(s);
529 (void)gss_delete_sec_context(&min_stat, &context,
530 GSS_C_NO_BUFFER);
531 return -1;
532 }
533
534 if (verbose)
535 printf("Signature verified.\n");
536 } else if (verbose) {
537 printf("Response received.\n");
538 }
539
540 free(out_buf.value);
541 }
542
543 if (use_file)
544 free(in_buf.value);
545
546 /* Send NOOP. */
547 if (!v1_format)
548 (void)send_token(s, TOKEN_NOOP, empty_token);
549
550 if (auth_flag) {
551 /* Delete context. */
552 maj_stat = gss_delete_sec_context(&min_stat, &context, &out_buf);
553 if (maj_stat != GSS_S_COMPLETE) {
554 display_status("deleting context", maj_stat, min_stat);
555 (void)closesocket(s);
556 (void)gss_delete_sec_context(&min_stat, &context, GSS_C_NO_BUFFER);
557 return -1;
558 }
559
560 (void)gss_release_buffer(&min_stat, &out_buf);
561 }
562
563 (void)closesocket(s);
564 return 0;
565 }
566
567 static void
parse_oid(char * mechanism,gss_OID * oid)568 parse_oid(char *mechanism, gss_OID *oid)
569 {
570 char *mechstr = 0, *cp;
571 gss_buffer_desc tok;
572 OM_uint32 maj_stat, min_stat;
573
574 if (isdigit((unsigned char)mechanism[0])) {
575 if (asprintf(&mechstr, "{ %s }", mechanism) < 0) {
576 fprintf(stderr, "Couldn't allocate mechanism scratch!\n");
577 return;
578 }
579 for (cp = mechstr; *cp; cp++) {
580 if (*cp == '.')
581 *cp = ' ';
582 }
583 tok.value = mechstr;
584 } else {
585 tok.value = mechanism;
586 }
587 tok.length = strlen(tok.value);
588 maj_stat = gss_str_to_oid(&min_stat, &tok, oid);
589 if (maj_stat != GSS_S_COMPLETE) {
590 display_status("str_to_oid", maj_stat, min_stat);
591 return;
592 }
593 if (mechstr)
594 free(mechstr);
595 }
596
597 static int max_threads = 1;
598
599 #ifdef _WIN32
600 static thread_count = 0;
601 static HANDLE hMutex = NULL;
602 static HANDLE hEvent = NULL;
603
604 void
init_handles(void)605 init_handles(void)
606 {
607 hMutex = CreateMutex(NULL, FALSE, NULL);
608 hEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
609 }
610
611 void
cleanup_handles(void)612 cleanup_handles(void)
613 {
614 CloseHandle(hMutex);
615 CloseHandle(hEvent);
616 }
617
618 BOOL
wait_and_increment_thread_counter(void)619 wait_and_increment_thread_counter(void)
620 {
621 for (;;) {
622 if (WaitForSingleObject(hMutex, INFINITE) == WAIT_OBJECT_0) {
623 if (thread_count < max_threads) {
624 thread_count++;
625 ReleaseMutex(hMutex);
626 return TRUE;
627 } else {
628 ReleaseMutex(hMutex);
629
630 if (WaitForSingleObject(hEvent, INFINITE) == WAIT_OBJECT_0)
631 continue;
632 else
633 return FALSE;
634 }
635 } else {
636 return FALSE;
637 }
638 }
639 }
640
641 BOOL
decrement_and_signal_thread_counter(void)642 decrement_and_signal_thread_counter(void)
643 {
644 if (WaitForSingleObject(hMutex, INFINITE) == WAIT_OBJECT_0) {
645 if (thread_count == max_threads)
646 SetEvent(hEvent);
647 thread_count--;
648 ReleaseMutex(hMutex);
649 return TRUE;
650 } else {
651 return FALSE;
652 }
653 }
654
655 #else /* assume pthread */
656
657 static pthread_mutex_t counter_mutex = PTHREAD_MUTEX_INITIALIZER;
658 static pthread_cond_t counter_cond = PTHREAD_COND_INITIALIZER;
659 int counter = 0;
660
661 static int
wait_and_increment_thread_counter(void)662 wait_and_increment_thread_counter(void)
663 {
664 int err;
665
666 err = pthread_mutex_lock(&counter_mutex);
667 if (err) {
668 perror("pthread_mutex_lock");
669 return 0;
670 }
671 if (counter == max_threads) {
672 err = pthread_cond_wait(&counter_cond, &counter_mutex);
673 if (err) {
674 pthread_mutex_unlock(&counter_mutex);
675 perror("pthread_cond_wait");
676 return 0;
677 }
678 }
679 counter++;
680 pthread_mutex_unlock(&counter_mutex);
681 return 1;
682 }
683
684 static void
decrement_and_signal_thread_counter(void)685 decrement_and_signal_thread_counter(void)
686 {
687 int err;
688
689 sleep(1);
690 err = pthread_mutex_lock(&counter_mutex);
691 if (err) {
692 perror("pthread_mutex_lock");
693 return;
694 }
695 if (counter == max_threads)
696 pthread_cond_broadcast(&counter_cond);
697 counter--;
698 pthread_mutex_unlock(&counter_mutex);
699 }
700
701 #endif
702
703 static char *service_name, *server_host, *msg;
704 static char *mechanism = 0;
705 static u_short port = 4444;
706 static int use_file = 0;
707 static OM_uint32 gss_flags = GSS_C_MUTUAL_FLAG | GSS_C_REPLAY_FLAG;
708 static OM_uint32 min_stat;
709 static gss_OID oid = GSS_C_NULL_OID;
710 static int mcount = 1, ccount = 1;
711 static int auth_flag, wrap_flag, encrypt_flag, mic_flag, v1_format;
712
713 static void *
worker_bee(void * unused)714 worker_bee(void *unused)
715 {
716 printf("worker bee!\n");
717 if (call_server(server_host, port, oid, service_name,
718 gss_flags, auth_flag, wrap_flag, encrypt_flag, mic_flag,
719 v1_format, msg, use_file, mcount) < 0) {
720 if (max_threads == 1)
721 exit(6);
722 }
723
724 if (max_threads > 1)
725 decrement_and_signal_thread_counter();
726 free(unused);
727 return NULL;
728 }
729
730 int
main(int argc,char ** argv)731 main(int argc, char **argv)
732 {
733 int i;
734
735 display_file = stdout;
736 auth_flag = wrap_flag = encrypt_flag = mic_flag = 1;
737 v1_format = 0;
738
739 /* Parse arguments. */
740 argc--;
741 argv++;
742 while (argc) {
743 if (strcmp(*argv, "-port") == 0) {
744 argc--;
745 argv++;
746 if (!argc)
747 usage();
748 port = atoi(*argv);
749 } else if (strcmp(*argv, "-mech") == 0) {
750 argc--;
751 argv++;
752 if (!argc)
753 usage();
754 mechanism = *argv;
755 } else if (strcmp(*argv, "-threads") == 0) {
756 argc--;
757 argv++;
758 if (!argc)
759 usage();
760 max_threads = atoi(*argv);
761 } else if (strcmp(*argv, "-d") == 0) {
762 gss_flags |= GSS_C_DELEG_FLAG;
763 } else if (strcmp(*argv, "-seq") == 0) {
764 gss_flags |= GSS_C_SEQUENCE_FLAG;
765 } else if (strcmp(*argv, "-noreplay") == 0) {
766 gss_flags &= ~GSS_C_REPLAY_FLAG;
767 } else if (strcmp(*argv, "-nomutual") == 0) {
768 gss_flags &= ~GSS_C_MUTUAL_FLAG;
769 } else if (strcmp(*argv, "-f") == 0) {
770 use_file = 1;
771 } else if (strcmp(*argv, "-q") == 0) {
772 verbose = 0;
773 } else if (strcmp(*argv, "-ccount") == 0) {
774 argc--;
775 argv++;
776 if (!argc)
777 usage();
778 ccount = atoi(*argv);
779 if (ccount <= 0)
780 usage();
781 } else if (strcmp(*argv, "-mcount") == 0) {
782 argc--;
783 argv++;
784 if (!argc)
785 usage();
786 mcount = atoi(*argv);
787 if (mcount < 0)
788 usage();
789 } else if (strcmp(*argv, "-na") == 0) {
790 auth_flag = wrap_flag = encrypt_flag = mic_flag = 0;
791 } else if (strcmp(*argv, "-nw") == 0) {
792 wrap_flag = 0;
793 } else if (strcmp(*argv, "-nx") == 0) {
794 encrypt_flag = 0;
795 } else if (strcmp(*argv, "-nm") == 0) {
796 mic_flag = 0;
797 } else if (strcmp(*argv, "-v1") == 0) {
798 v1_format = 1;
799 } else {
800 break;
801 }
802 argc--;
803 argv++;
804 }
805 if (argc != 3)
806 usage();
807
808 #ifdef _WIN32
809 if (max_threads < 1) {
810 fprintf(stderr, "warning: there must be at least one thread\n");
811 max_threads = 1;
812 }
813
814 init_handles();
815 SetEnvironmentVariable("KERBEROSLOGIN_NEVER_PROMPT", "1");
816 #endif
817
818 server_host = *argv++;
819 service_name = *argv++;
820 msg = *argv++;
821
822 if (mechanism)
823 parse_oid(mechanism, &oid);
824
825 if (get_server_info(server_host, port) < 0)
826 exit(1);
827
828 if (max_threads == 1) {
829 for (i = 0; i < ccount; i++)
830 worker_bee(0);
831 } else {
832 for (i = 0; i < ccount; i++) {
833 if (wait_and_increment_thread_counter()) {
834 #ifdef _WIN32
835 uintptr_t handle = _beginthread(worker_bee, 0, (void *)NULL);
836 if (handle == (uintptr_t)-1)
837 exit(7);
838 #else
839 int err;
840 pthread_t thr;
841 err = pthread_create(&thr, 0, worker_bee, malloc(12));
842 if (err) {
843 perror("pthread_create");
844 exit(7);
845 }
846 (void)pthread_detach(thr);
847 #endif
848 } else {
849 exit(8);
850 }
851 }
852 }
853
854 if (oid != GSS_C_NULL_OID)
855 (void)gss_release_oid(&min_stat, &oid);
856
857 #ifdef _WIN32
858 cleanup_handles();
859 #else
860 if (max_threads > 1)
861 sleep(10);
862 #endif
863
864 return 0;
865 }
866