xref: /freebsd/crypto/krb5/src/tests/gss-threads/gss-client.c (revision 7f2fe78b9dd5f51c821d771b63d2e096f6fd49e9)
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