xref: /freebsd/crypto/krb5/src/appl/gss-sample/gss-client.c (revision f1c4c3daccbaf3820f0e2224de53df12fc952fcc)
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