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