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