From b5c2b8fa138efadfa52d64be2d16be063cf6c268 Mon Sep 17 00:00:00 2001 From: Tom Yu Date: Fri, 11 Feb 2000 23:06:47 +0000 Subject: [PATCH] * README, gss-client.c, gss-misc.c, gss-misc.h, gss-server.c: Patches from jik to make gss-sample more versatile. git-svn-id: svn://anonsvn.mit.edu/krb5/trunk@12039 dc483132-0cff-0310-8789-dd5450dbe970 --- src/appl/gss-sample/ChangeLog | 5 + src/appl/gss-sample/README | 162 ++++++---- src/appl/gss-sample/gss-client.c | 526 ++++++++++++++++++------------- src/appl/gss-sample/gss-misc.c | 54 +++- src/appl/gss-sample/gss-misc.h | 18 +- src/appl/gss-sample/gss-server.c | 383 +++++++++++++--------- 6 files changed, 711 insertions(+), 437 deletions(-) diff --git a/src/appl/gss-sample/ChangeLog b/src/appl/gss-sample/ChangeLog index fb37b228b..004bff51b 100644 --- a/src/appl/gss-sample/ChangeLog +++ b/src/appl/gss-sample/ChangeLog @@ -1,3 +1,8 @@ +2000-02-11 Tom Yu + + * README, gss-client.c, gss-misc.c, gss-misc.h, gss-server.c: + Patches from jik to make gss-sample more versatile. + 2000-01-31 Danilo Almeida * gss-client.c, gss-server.c, gss-misc.c: Include Windows headers diff --git a/src/appl/gss-sample/README b/src/appl/gss-sample/README index 52b1b2143..ac90086ad 100644 --- a/src/appl/gss-sample/README +++ b/src/appl/gss-sample/README @@ -19,76 +19,132 @@ # PERFORMANCE OF THIS SOFTWARE. This directory contains a sample GSS-API client and server -application. Each invocation of the client performs the following -exchange with the server: - - 1. The client and server establish a GSS-API context. The - server prints the identity of the client. - - 2. The client sends a sealed (encrypted) message to the - server. - - 3. The server decrypts the message and prints it. - - 4. The server produces a signature block for the message and - sends it to the client. - - 5. The client verifies the signature block. - -Obviously, this exchange does not perform a tremendously valuable -function; however, it demostrates the use of primary GSS-API -interfaces. - +application. In addition to serving as an example of GSS-API +programming, this application is also intended to be a tool for +testing the performance of GSS-API implementations. + +Each time the client is invoked, it performs one or more exchanges +with the server. Each exchange with the server consists primarily of +the following steps: + + 1. A TCP/IP connection is established. + + 2. (optional, on by default) The client and server establish a + GSS-API context, and the server prints the identify of the + client. + + / 3. The client sends a message to the server. The message may + / be plaintext, cryptographically "signed" but not encrypted, + | or encrypted (default). + | +0 or | 4. The server decrypts the message (if necessary), verifies +more | its signature (if there is one) and prints it. +times| + | 5. The server sends either a signature block (the default) or an + | empty token back to the client to acknowledge the message. + \ + \ 6. If the server sent a signature block, the client verifies + it and prints a message indicating that it was verified. + + 7. The client sends an empty block to the server to tell it + that the exchange is finished. + + 8. The client and server close the TCP/IP connection and + destroy the GSS-API context. + The server's command line usage is - - gss-server [-port port] [-k keytab] service_name - + + gss-server [-port port] [-verbose] [-once] [-inetd] [-export] + [-logfile file] service_name + where service_name is a GSS-API service name of the form "service@host" (or just "service", in which case the local host name -is used). The server will accept TCP connections on port (default -4444) and establish contexts as service_name. If you compile with -GSS_KRB5 defined and link against the MIT Kerberos libraries, the -k -option specifies a keytab to use instead of the default one. - +is used). The command-line options have the following meanings: + +-port The TCP port on which to accept connections. Default is 4444. + +-once Tells the server to exit after a single exchange, rather than + persisting. + +-inetd Tells the server that it is running out of inetd, so it should + interact with the client on stdin rather than binding to a + network port. Implies "-once". + +-export Tells the server to test the gss_export_sec_context function + after establishing a context with a client. + +-logfile + The file to which the server should append its output, rather + than sending it to stdout. + The client's command line usage is - gss-client [-port port] [-d] host service_name msg + gss-client [-port port] [-mech mechanism] [-d] [-f] [-q] + [-ccount count] [-mcount count] [-na] [-nw] [-nx] [-nm] + host service_name msg where host is the host running the server, service_name is the service name that the server will establish connections as (if you don't specify the host name in the service name when running gss-server, and it's running on a different machine from gss-client, make sure to specify the server's host name in the service name you specify to -gss-client!) and msg is the message. The client connects to the TCP -on (default 4444) and performs a context -establishment. The "-d" option specifies delegation - a forwardable -TGT will be sent to the server, which will put it in its credential -cache (you must kinit -f for this to work). The -v2 option means that -the GSSAPI v2 calls should be used (and tested). +gss-client!) and msg is the message. The command-line options have +the following meanings: -If you are using this sample application with OpenVision's Kerberos 5 -GSS-API mechanism: +-port The TCP port to which to connect. Default is 4444. -1. Link the client and server with -lgssapi_krb5 -lkrb5 -lcrypto --lisode -lcom_err. +-mech The OID of the GSS-API mechanism to use. -2. Make sure that the principal corresponding to service_name is in -the default or specified keytab on the server host, and that the -gss-server process can read the keytab. For example, the service name -"host@server" corresponds to the Kerberos principal -"host/server.domain.com@REALM". +-d Tells the client to delegate credentials to the server. For + the Kerberos GSS-API mechanism, this means that a forwardable + TGT will be sent to the server, which will put it in its + credential cache (you must have acquired your tickets with + "kinit -f" for this to work). -This sample application uses the following GSS-API functions: +-f Tells the client that the "msg" argument is actually the name + of a file whose contents should be used as the message. + +-q Tells the client to be quiet, i.e., to only print error + messages. + +-ccount Specifies how many sessions the client should initiate with + the server (the "connection count"). + +-mcount Specifies how many times the message should be sent to the + server in each session (the "message count"). + +-na Tells the client not to do any authentication with the + server. Implies "-nw", "-nx" and "-nm". - gss_accept_sec_context gss_release_buffer - gss_acquire_cred gss_release_cred - gss_delete_sec_context gss_release_name - gss_display_name gss_seal - gss_display_status gss_sign - gss_import_name gss_unseal - gss_init_sec_context gss_verify +-nw Tells the client not to "wrap" messages. Implies "-nx". + +-nx Tells the client not to encrypt messages. + +-nm Tells the client not to ask the server to send back a + cryptographic checksum ("MIC"). + +To run the server on a host, you need to make sure that the principal +corresponding to service_name is in the default keytab on the server +host, and that the gss-server process can read the keytab. For +example, the service name "host@server" corresponds to the Kerberos +principal "host/server.domain.com@REALM". + +This sample application uses the following GSS-API functions: -Barry Jaspan, bjaspan@security.ov.com + gss_accept_sec_context gss_inquire_names_for_mech + gss_acquire_cred gss_oid_to_str + gss_delete_sec_context gss_release_buffer + gss_display_name gss_release_cred + gss_display_status gss_release_name + gss_export_sec_context gss_release_oid + gss_get_mic gss_release_oid_set + gss_import_name gss_str_to_oid + gss_import_sec_context gss_unwrap + gss_init_sec_context gss_verify_mic + gss_inquire_context gss_wrap + +This application was originally written by Barry Jaspan of OpenVision +Technologies, Inc. It was updated significantly by Jonathan Kamens of OpenVision Technologies, Inc. $Id$ diff --git a/src/appl/gss-sample/gss-client.c b/src/appl/gss-sample/gss-client.c index 5e4ed6093..7ad407a7f 100644 --- a/src/appl/gss-sample/gss-client.c +++ b/src/appl/gss-sample/gss-client.c @@ -45,10 +45,13 @@ static char *rcsid = "$Header$"; #include #include "gss-misc.h" +static int verbose = 1; + void usage() { - fprintf(stderr, "Usage: gss-client [-port port] [-d] host service \ -msg\n"); + fprintf(stderr, "Usage: gss-client [-port port] [-mech mechanism] [-d]\n"); + fprintf(stderr, " [-f] [-q] [-ccount count] [-mcount count]\n"); + fprintf(stderr, " [-na] [-nw] [-nx] [-nm] host service msg\n"); exit(1); } @@ -109,6 +112,9 @@ int connect_to_server(host, port) * * s (r) an established TCP connection to the service * service_name (r) the ASCII service name of the service + * deleg_flag (r) GSS-API delegation flag (if any) + * auth_flag (r) whether to actually do authentication + * oid (r) OID of the mechanism to use * context (w) the established GSS-API context * ret_flags (w) the returned flags from init_sec_context * @@ -126,104 +132,121 @@ int connect_to_server(host, port) * unsuccessful, the GSS-API error messages are displayed on stderr * and -1 is returned. */ -int client_establish_context(s, service_name, deleg_flag, oid, +int client_establish_context(s, service_name, deleg_flag, auth_flag, oid, gss_context, ret_flags) int s; char *service_name; gss_OID oid; OM_uint32 deleg_flag; + int auth_flag; gss_ctx_id_t *gss_context; OM_uint32 *ret_flags; { - gss_buffer_desc send_tok, recv_tok, *token_ptr; - gss_name_t target_name; - OM_uint32 maj_stat, min_stat, init_sec_min_stat; - - /* - * Import the name into target_name. Use send_tok to save - * local variable space. - */ - send_tok.value = service_name; - send_tok.length = strlen(service_name) + 1; - maj_stat = gss_import_name(&min_stat, &send_tok, - (gss_OID) gss_nt_service_name, &target_name); - if (maj_stat != GSS_S_COMPLETE) { - display_status("parsing name", maj_stat, min_stat); - return -1; - } + if (auth_flag) { + gss_buffer_desc send_tok, recv_tok, *token_ptr; + gss_name_t target_name; + OM_uint32 maj_stat, min_stat, init_sec_min_stat; + int token_flags; + + /* + * Import the name into target_name. Use send_tok to save + * local variable space. + */ + send_tok.value = service_name; + send_tok.length = strlen(service_name) + 1; + maj_stat = gss_import_name(&min_stat, &send_tok, + (gss_OID) gss_nt_service_name, &target_name); + if (maj_stat != GSS_S_COMPLETE) { + display_status("parsing name", maj_stat, min_stat); + return -1; + } - /* - * Perform the context-establishement loop. - * - * On each pass through the loop, token_ptr points to the token - * to send to the server (or GSS_C_NO_BUFFER on the first pass). - * Every generated token is stored in send_tok which is then - * transmitted to the server; every received token is stored in - * recv_tok, which token_ptr is then set to, to be processed by - * the next call to gss_init_sec_context. - * - * GSS-API guarantees that send_tok's length will be non-zero - * if and only if the server is expecting another token from us, - * and that gss_init_sec_context returns GSS_S_CONTINUE_NEEDED if - * and only if the server has another token to send us. - */ + if (send_token(s, TOKEN_NOOP|TOKEN_CONTEXT_NEXT, empty_token) < 0) { + (void) gss_release_name(&min_stat, &target_name); + return -1; + } + + /* + * Perform the context-establishement loop. + * + * On each pass through the loop, token_ptr points to the token + * to send to the server (or GSS_C_NO_BUFFER on the first pass). + * Every generated token is stored in send_tok which is then + * transmitted to the server; every received token is stored in + * recv_tok, which token_ptr is then set to, to be processed by + * the next call to gss_init_sec_context. + * + * GSS-API guarantees that send_tok's length will be non-zero + * if and only if the server is expecting another token from us, + * and that gss_init_sec_context returns GSS_S_CONTINUE_NEEDED if + * and only if the server has another token to send us. + */ - token_ptr = GSS_C_NO_BUFFER; - *gss_context = GSS_C_NO_CONTEXT; - - do { - maj_stat = - gss_init_sec_context(&init_sec_min_stat, - GSS_C_NO_CREDENTIAL, - gss_context, - target_name, - oid, - GSS_C_MUTUAL_FLAG | GSS_C_REPLAY_FLAG | - deleg_flag, - 0, - NULL, /* no channel bindings */ - token_ptr, - NULL, /* ignore mech type */ - &send_tok, - ret_flags, - NULL); /* ignore time_rec */ - - if (token_ptr != GSS_C_NO_BUFFER) - (void) gss_release_buffer(&min_stat, &recv_tok); - - if (send_tok.length != 0) { - printf("Sending init_sec_context token (size=%d)...", - send_tok.length); - if (send_token(s, &send_tok) < 0) { - (void) gss_release_buffer(&min_stat, &send_tok); - (void) gss_release_name(&min_stat, &target_name); - return -1; - } - } - (void) gss_release_buffer(&min_stat, &send_tok); - - if (maj_stat!=GSS_S_COMPLETE && maj_stat!=GSS_S_CONTINUE_NEEDED) { - display_status("initializing context", maj_stat, - init_sec_min_stat); - (void) gss_release_name(&min_stat, &target_name); - if (*gss_context == GSS_C_NO_CONTEXT) - gss_delete_sec_context(&min_stat, gss_context, - GSS_C_NO_BUFFER); - return -1; - } + token_ptr = GSS_C_NO_BUFFER; + *gss_context = GSS_C_NO_CONTEXT; + + do { + maj_stat = + gss_init_sec_context(&init_sec_min_stat, + GSS_C_NO_CREDENTIAL, + gss_context, + target_name, + oid, + GSS_C_MUTUAL_FLAG | GSS_C_REPLAY_FLAG | + deleg_flag, + 0, + NULL, /* no channel bindings */ + token_ptr, + NULL, /* ignore mech type */ + &send_tok, + ret_flags, + NULL); /* ignore time_rec */ + + if (token_ptr != GSS_C_NO_BUFFER) + (void) gss_release_buffer(&min_stat, &recv_tok); + + if (send_tok.length != 0) { + if (verbose) + printf("Sending init_sec_context token (size=%d)...", + send_tok.length); + if (send_token(s, TOKEN_CONTEXT, &send_tok) < 0) { + (void) gss_release_buffer(&min_stat, &send_tok); + (void) gss_release_name(&min_stat, &target_name); + return -1; + } + } + (void) gss_release_buffer(&min_stat, &send_tok); + + if (maj_stat!=GSS_S_COMPLETE && maj_stat!=GSS_S_CONTINUE_NEEDED) { + display_status("initializing context", maj_stat, + init_sec_min_stat); + (void) gss_release_name(&min_stat, &target_name); + if (*gss_context == GSS_C_NO_CONTEXT) + gss_delete_sec_context(&min_stat, gss_context, + GSS_C_NO_BUFFER); + return -1; + } - if (maj_stat == GSS_S_CONTINUE_NEEDED) { - printf("continue needed..."); - if (recv_token(s, &recv_tok) < 0) { - (void) gss_release_name(&min_stat, &target_name); - return -1; - } - token_ptr = &recv_tok; - } - printf("\n"); - } while (maj_stat == GSS_S_CONTINUE_NEEDED); - - (void) gss_release_name(&min_stat, &target_name); + if (maj_stat == GSS_S_CONTINUE_NEEDED) { + if (verbose) + printf("continue needed..."); + if (recv_token(s, &token_flags, &recv_tok) < 0) { + (void) gss_release_name(&min_stat, &target_name); + return -1; + } + token_ptr = &recv_tok; + } + if (verbose) + printf("\n"); + } while (maj_stat == GSS_S_CONTINUE_NEEDED); + + (void) gss_release_name(&min_stat, &target_name); + } + else { + if (send_token(s, TOKEN_NOOP, empty_token) < 0) + return -1; + } + return 0; } @@ -278,8 +301,15 @@ void read_file(file_name, in_buf) * * host (r) the host providing the service * port (r) the port to connect to on host - * service_name (r) the GSS-API service name to authenticate to + * service_name (r) the GSS-API service name to authenticate to + * deleg_flag (r) GSS-API delegation flag (if any) + * auth_flag (r) whether to do authentication + * wrap_flag (r) whether to do message wrapping at all + * encrypt_flag (r) whether to do encryption while wrapping + * mic_flag (r) whether to request a MIC from the server * msg (r) the message to have "signed" + * use_file (r) whether to treat msg as an input file name + * mcount (r) the number of times to send the message * * Returns: 0 on success, -1 on failure * @@ -287,18 +317,22 @@ void read_file(file_name, in_buf) * * call_server opens a TCP connection to and establishes a * GSS-API context with service_name over the connection. It then - * seals msg in a GSS-API token with gss_seal, sends it to the server, + * seals msg in a GSS-API token with gss_wrap, sends it to the server, * reads back a GSS-API signature block for msg from the server, and * verifies it with gss_verify. -1 is returned if any step fails, * otherwise 0 is returned. */ -int call_server(host, port, oid, service_name, deleg_flag, msg, use_file) +int call_server(host, port, oid, service_name, deleg_flag, auth_flag, + wrap_flag, encrypt_flag, mic_flag, msg, use_file, + mcount) char *host; u_short port; gss_OID oid; char *service_name; OM_uint32 deleg_flag; + int auth_flag, wrap_flag, encrypt_flag, mic_flag; char *msg; int use_file; + int mcount; { gss_ctx_id_t context; gss_buffer_desc in_buf, out_buf; @@ -316,103 +350,108 @@ int call_server(host, port, oid, service_name, deleg_flag, msg, use_file) gss_OID_set mech_names; gss_buffer_desc oid_name; size_t i; + int token_flags; /* Open connection */ if ((s = connect_to_server(host, port)) < 0) return -1; /* Establish context */ - if (client_establish_context(s, service_name, deleg_flag, oid, &context, - &ret_flags) < 0) { + if (client_establish_context(s, service_name, deleg_flag, auth_flag, + oid, &context, &ret_flags) < 0) { (void) close(s); return -1; } - /* display the flags */ - display_ctx_flags(ret_flags); - - /* Get context information */ - maj_stat = gss_inquire_context(&min_stat, context, - &src_name, &targ_name, &lifetime, - &mechanism, &context_flags, - &is_local, - &is_open); - if (maj_stat != GSS_S_COMPLETE) { - display_status("inquiring context", maj_stat, min_stat); - return -1; - } + if (auth_flag) { + if (verbose) { + /* display the flags */ + display_ctx_flags(ret_flags); + + /* Get context information */ + maj_stat = gss_inquire_context(&min_stat, context, + &src_name, &targ_name, &lifetime, + &mechanism, &context_flags, + &is_local, + &is_open); + if (maj_stat != GSS_S_COMPLETE) { + display_status("inquiring context", maj_stat, min_stat); + return -1; + } - maj_stat = gss_display_name(&min_stat, src_name, &sname, - &name_type); - if (maj_stat != GSS_S_COMPLETE) { - display_status("displaying source name", maj_stat, min_stat); - return -1; - } - maj_stat = gss_display_name(&min_stat, targ_name, &tname, - (gss_OID *) NULL); - if (maj_stat != GSS_S_COMPLETE) { - display_status("displaying target name", maj_stat, min_stat); - return -1; - } - fprintf(stderr, "\"%.*s\" to \"%.*s\", lifetime %d, flags %x, %s, %s\n", - (int) sname.length, (char *) sname.value, - (int) tname.length, (char *) tname.value, lifetime, - context_flags, - (is_local) ? "locally initiated" : "remotely initiated", - (is_open) ? "open" : "closed"); - - (void) gss_release_name(&min_stat, &src_name); - (void) gss_release_name(&min_stat, &targ_name); - (void) gss_release_buffer(&min_stat, &sname); - (void) gss_release_buffer(&min_stat, &tname); - - maj_stat = gss_oid_to_str(&min_stat, - name_type, - &oid_name); - if (maj_stat != GSS_S_COMPLETE) { - display_status("converting oid->string", maj_stat, min_stat); - return -1; - } - fprintf(stderr, "Name type of source name is %.*s.\n", - (int) oid_name.length, (char *) oid_name.value); - (void) gss_release_buffer(&min_stat, &oid_name); - - /* Now get the names supported by the mechanism */ - maj_stat = gss_inquire_names_for_mech(&min_stat, - mechanism, - &mech_names); - if (maj_stat != GSS_S_COMPLETE) { - display_status("inquiring mech names", maj_stat, min_stat); - return -1; - } + maj_stat = gss_display_name(&min_stat, src_name, &sname, + &name_type); + if (maj_stat != GSS_S_COMPLETE) { + display_status("displaying source name", maj_stat, min_stat); + return -1; + } + maj_stat = gss_display_name(&min_stat, targ_name, &tname, + (gss_OID *) NULL); + if (maj_stat != GSS_S_COMPLETE) { + display_status("displaying target name", maj_stat, min_stat); + return -1; + } + printf("\"%.*s\" to \"%.*s\", lifetime %d, flags %x, %s, %s\n", + (int) sname.length, (char *) sname.value, + (int) tname.length, (char *) tname.value, lifetime, + context_flags, + (is_local) ? "locally initiated" : "remotely initiated", + (is_open) ? "open" : "closed"); + + (void) gss_release_name(&min_stat, &src_name); + (void) gss_release_name(&min_stat, &targ_name); + (void) gss_release_buffer(&min_stat, &sname); + (void) gss_release_buffer(&min_stat, &tname); - maj_stat = gss_oid_to_str(&min_stat, - mechanism, - &oid_name); - if (maj_stat != GSS_S_COMPLETE) { - display_status("converting oid->string", maj_stat, min_stat); - return -1; - } - fprintf(stderr, "Mechanism %.*s supports %d names\n", - (int) oid_name.length, (char *) oid_name.value, - mech_names->count); - (void) gss_release_buffer(&min_stat, &oid_name); + maj_stat = gss_oid_to_str(&min_stat, + name_type, + &oid_name); + if (maj_stat != GSS_S_COMPLETE) { + display_status("converting oid->string", maj_stat, min_stat); + return -1; + } + printf("Name type of source name is %.*s.\n", + (int) oid_name.length, (char *) oid_name.value); + (void) gss_release_buffer(&min_stat, &oid_name); + + /* Now get the names supported by the mechanism */ + maj_stat = gss_inquire_names_for_mech(&min_stat, + mechanism, + &mech_names); + if (maj_stat != GSS_S_COMPLETE) { + display_status("inquiring mech names", maj_stat, min_stat); + return -1; + } - for (i=0; icount; i++) { maj_stat = gss_oid_to_str(&min_stat, - &mech_names->elements[i], + mechanism, &oid_name); if (maj_stat != GSS_S_COMPLETE) { + display_status("converting oid->string", maj_stat, min_stat); + return -1; + } + printf("Mechanism %.*s supports %d names\n", + (int) oid_name.length, (char *) oid_name.value, + mech_names->count); + (void) gss_release_buffer(&min_stat, &oid_name); + + for (i=0; icount; i++) { + maj_stat = gss_oid_to_str(&min_stat, + &mech_names->elements[i], + &oid_name); + if (maj_stat != GSS_S_COMPLETE) { display_status("converting oid->string", maj_stat, min_stat); return -1; - } - fprintf(stderr, " %d: %.*s\n", i, - (int) oid_name.length, (char *) oid_name.value); + } + printf(" %d: %.*s\n", i, + (int) oid_name.length, (char *) oid_name.value); - (void) gss_release_buffer(&min_stat, &oid_name); + (void) gss_release_buffer(&min_stat, &oid_name); + } + (void) gss_release_oid_set(&min_stat, &mech_names); + } } - (void) gss_release_oid_set(&min_stat, &mech_names); - + if (use_file) { read_file(msg, &in_buf); } else { @@ -421,58 +460,83 @@ int call_server(host, port, oid, service_name, deleg_flag, msg, use_file) in_buf.length = strlen(msg); } - maj_stat = gss_wrap(&min_stat, context, 1, GSS_C_QOP_DEFAULT, - &in_buf, &state, &out_buf); - if (maj_stat != GSS_S_COMPLETE) { - display_status("sealing message", maj_stat, min_stat); - (void) close(s); - (void) gss_delete_sec_context(&min_stat, &context, GSS_C_NO_BUFFER); - return -1; - } else if (! state) { - fprintf(stderr, "Warning! Message not encrypted.\n"); - } + for (i = 0; i < mcount; i++) { + if (wrap_flag) { + maj_stat = gss_wrap(&min_stat, context, encrypt_flag, GSS_C_QOP_DEFAULT, + &in_buf, &state, &out_buf); + if (maj_stat != GSS_S_COMPLETE) { + display_status("wrapping message", maj_stat, min_stat); + (void) close(s); + (void) gss_delete_sec_context(&min_stat, &context, GSS_C_NO_BUFFER); + return -1; + } else if (encrypt_flag && ! state) { + fprintf(stderr, "Warning! Message not encrypted.\n"); + } + } + else { + out_buf = in_buf; + } + + /* Send to server */ + if (send_token(s, (TOKEN_DATA | + (wrap_flag ? TOKEN_WRAPPED : 0) | + (encrypt_flag ? TOKEN_ENCRYPTED : 0) | + (mic_flag ? TOKEN_SEND_MIC : 0)), &out_buf) < 0) { + (void) close(s); + (void) gss_delete_sec_context(&min_stat, &context, GSS_C_NO_BUFFER); + return -1; + } + if (out_buf.value != in_buf.value) + (void) gss_release_buffer(&min_stat, &out_buf); + + /* Read signature block into out_buf */ + if (recv_token(s, &token_flags, &out_buf) < 0) { + (void) close(s); + (void) gss_delete_sec_context(&min_stat, &context, GSS_C_NO_BUFFER); + return -1; + } - /* Send to server */ - if (send_token(s, &out_buf) < 0) { - (void) close(s); - (void) gss_delete_sec_context(&min_stat, &context, GSS_C_NO_BUFFER); - return -1; - } - (void) gss_release_buffer(&min_stat, &out_buf); + if (mic_flag) { + /* Verify signature block */ + maj_stat = gss_verify_mic(&min_stat, context, &in_buf, + &out_buf, &qop_state); + if (maj_stat != GSS_S_COMPLETE) { + display_status("verifying signature", maj_stat, min_stat); + (void) close(s); + (void) gss_delete_sec_context(&min_stat, &context, GSS_C_NO_BUFFER); + return -1; + } - /* Read signature block into out_buf */ - if (recv_token(s, &out_buf) < 0) { - (void) close(s); - (void) gss_delete_sec_context(&min_stat, &context, GSS_C_NO_BUFFER); - return -1; - } + if (verbose) + printf("Signature verified.\n"); + } + else { + if (verbose) + printf("Response received.\n"); + } - /* Verify signature block */ - maj_stat = gss_verify_mic(&min_stat, context, &in_buf, - &out_buf, &qop_state); - if (maj_stat != GSS_S_COMPLETE) { - display_status("verifying signature", maj_stat, min_stat); - (void) close(s); - (void) gss_delete_sec_context(&min_stat, &context, GSS_C_NO_BUFFER); - return -1; + (void) gss_release_buffer(&min_stat, &out_buf); } - (void) gss_release_buffer(&min_stat, &out_buf); if (use_file) - free(in_buf.value); - - printf("Signature verified.\n"); + free(in_buf.value); + + /* Send NOOP */ + (void) send_token(s, TOKEN_NOOP, empty_token); + + if (auth_flag) { + /* Delete context */ + maj_stat = gss_delete_sec_context(&min_stat, &context, &out_buf); + if (maj_stat != GSS_S_COMPLETE) { + display_status("deleting context", maj_stat, min_stat); + (void) close(s); + (void) gss_delete_sec_context(&min_stat, &context, GSS_C_NO_BUFFER); + return -1; + } - /* Delete context */ - maj_stat = gss_delete_sec_context(&min_stat, &context, &out_buf); - if (maj_stat != GSS_S_COMPLETE) { - display_status("deleting context", maj_stat, min_stat); - (void) close(s); - (void) gss_delete_sec_context(&min_stat, &context, GSS_C_NO_BUFFER); - return -1; + (void) gss_release_buffer(&min_stat, &out_buf); } - (void) gss_release_buffer(&min_stat, &out_buf); (void) close(s); return 0; } @@ -486,7 +550,7 @@ static void parse_oid(char *mechanism, gss_OID *oid) if (isdigit(mechanism[0])) { mechstr = malloc(strlen(mechanism)+5); if (!mechstr) { - printf("Couldn't allocate mechanism scratch!\n"); + fprintf(stderr, "Couldn't allocate mechanism scratch!\n"); return; } sprintf(mechstr, "{ %s }", mechanism); @@ -516,8 +580,12 @@ int main(argc, argv) int use_file = 0; OM_uint32 deleg_flag = 0, min_stat; gss_OID oid = GSS_C_NULL_OID; - + int mcount = 1, ccount = 1; + int i; + int auth_flag, wrap_flag, encrypt_flag, mic_flag; + display_file = stdout; + auth_flag = wrap_flag = encrypt_flag = mic_flag = 1; /* Parse arguments. */ argc--; argv++; @@ -534,6 +602,26 @@ int main(argc, argv) deleg_flag = GSS_C_DELEG_FLAG; } else if (strcmp(*argv, "-f") == 0) { use_file = 1; + } else if (strcmp(*argv, "-q") == 0) { + verbose = 0; + } else if (strcmp(*argv, "-ccount") == 0) { + argc--; argv++; + if (!argc) usage(); + ccount = atoi(*argv); + if (ccount <= 0) usage(); + } else if (strcmp(*argv, "-mcount") == 0) { + argc--; argv++; + if (!argc) usage(); + mcount = atoi(*argv); + if (mcount < 0) usage(); + } else if (strcmp(*argv, "-na") == 0) { + auth_flag = wrap_flag = encrypt_flag = mic_flag = 0; + } else if (strcmp(*argv, "-nw") == 0) { + wrap_flag = 0; + } else if (strcmp(*argv, "-nx") == 0) { + encrypt_flag = 0; + } else if (strcmp(*argv, "-nm") == 0) { + mic_flag = 0; } else break; argc--; argv++; @@ -548,13 +636,15 @@ int main(argc, argv) if (mechanism) parse_oid(mechanism, &oid); - if (call_server(server_host, port, oid, service_name, - deleg_flag, msg, use_file) < 0) - exit(1); + for (i = 0; i < ccount; i++) { + if (call_server(server_host, port, oid, service_name, + deleg_flag, auth_flag, wrap_flag, encrypt_flag, mic_flag, + msg, use_file, mcount) < 0) + exit(1); + } if (oid != GSS_C_NULL_OID) (void) gss_release_oid(&min_stat, &oid); return 0; } - diff --git a/src/appl/gss-sample/gss-misc.c b/src/appl/gss-sample/gss-misc.c index fedd3766a..d2b6cab86 100644 --- a/src/appl/gss-sample/gss-misc.c +++ b/src/appl/gss-sample/gss-misc.c @@ -49,6 +49,9 @@ extern char *malloc(); FILE *display_file; +gss_buffer_desc empty_token_buf = { 0, (void *) "" }; +gss_buffer_t empty_token = &empty_token_buf; + static void display_status_1 PROTOTYPE( (char *m, OM_uint32 code, int type) ); @@ -98,21 +101,32 @@ static int read_all(int fildes, char *buf, unsigned int nbyte) * Arguments: * * s (r) an open file descriptor + * flags (r) the flags to write * tok (r) the token to write * * Returns: 0 on success, -1 on failure * * Effects: * - * send_token writes the token length (as a network long) and then the - * token data to the file descriptor s. It returns 0 on success, and - * -1 if an error occurs or if it could not write all the data. + * send_token writes the token flags (a single byte, even though + * they're passed in in an integer), then the token length (as a + * network long) and then the token data to the file descriptor s. It + * returns 0 on success, and -1 if an error occurs or if it could not + * write all the data. */ -int send_token(s, tok) +int send_token(s, flags, tok) int s; + int flags; gss_buffer_t tok; { int len, ret; + unsigned char char_flags = (unsigned char) flags; + + ret = write_all(s, (char *)&char_flags, 1); + if (ret != 1) { + perror("sending token flags"); + return -1; + } len = htonl(tok->length); @@ -151,24 +165,40 @@ int send_token(s, tok) * Arguments: * * s (r) an open file descriptor + * flags (w) the read flags * tok (w) the read token * * Returns: 0 on success, -1 on failure * * Effects: * - * recv_token reads the token length (as a network long), allocates - * memory to hold the data, and then reads the token data from the - * file descriptor s. It blocks to read the length and data, if - * necessary. On a successful return, the token should be freed with - * gss_release_buffer. It returns 0 on success, and -1 if an error - * occurs or if it could not read all the data. + * recv_token reads the token flags (a single byte, even though + * they're stored into an integer, then reads the token length (as a + * network long), allocates memory to hold the data, and then reads + * the token data from the file descriptor s. It blocks to read the + * length and data, if necessary. On a successful return, the token + * should be freed with gss_release_buffer. It returns 0 on success, + * and -1 if an error occurs or if it could not read all the data. */ -int recv_token(s, tok) +int recv_token(s, flags, tok) int s; + int *flags; gss_buffer_t tok; { int ret; + unsigned char char_flags; + + ret = read_all(s, (char *) &char_flags, 1); + if (ret < 0) { + perror("reading token flags"); + return -1; + } else if (! ret) { + if (display_file) + fputs("reading token flags: 0 bytes read\n", display_file); + return -1; + } else { + *flags = (int) char_flags; + } ret = read_all(s, (char *) &tok->length, 4); if (ret < 0) { @@ -184,7 +214,7 @@ int recv_token(s, tok) tok->length = ntohl(tok->length); tok->value = (char *) malloc(tok->length); - if (tok->value == NULL) { + if (tok->length && tok->value == NULL) { if (display_file) fprintf(display_file, "Out of memory allocating token data\n"); diff --git a/src/appl/gss-sample/gss-misc.h b/src/appl/gss-sample/gss-misc.h index bfdcad2f4..82c55bd78 100644 --- a/src/appl/gss-sample/gss-misc.h +++ b/src/appl/gss-sample/gss-misc.h @@ -33,9 +33,9 @@ extern FILE *display_file; int send_token - PROTOTYPE( (int s, gss_buffer_t tok) ); + PROTOTYPE( (int s, int flags, gss_buffer_t tok) ); int recv_token - PROTOTYPE( (int s, gss_buffer_t tok) ); + PROTOTYPE( (int s, int *flags, gss_buffer_t tok) ); void display_status PROTOTYPE( (char *msg, OM_uint32 maj_stat, OM_uint32 min_stat) ); void display_ctx_flags @@ -43,4 +43,18 @@ void display_ctx_flags void print_token PROTOTYPE( (gss_buffer_t tok) ); +/* Token types */ +#define TOKEN_NOOP (1<<0) +#define TOKEN_CONTEXT (1<<1) +#define TOKEN_DATA (1<<2) +#define TOKEN_MIC (1<<3) + +/* Token flags */ +#define TOKEN_CONTEXT_NEXT (1<<4) +#define TOKEN_WRAPPED (1<<5) +#define TOKEN_ENCRYPTED (1<<6) +#define TOKEN_SEND_MIC (1<<7) + +extern gss_buffer_t empty_token; + #endif diff --git a/src/appl/gss-sample/gss-server.c b/src/appl/gss-sample/gss-server.c index 6a163db9a..8d2ca27ac 100644 --- a/src/appl/gss-sample/gss-server.c +++ b/src/appl/gss-sample/gss-server.c @@ -51,8 +51,8 @@ static char *rcsid = "$Header$"; void usage() { - fprintf(stderr, "Usage: gss-server [-port port] [-verbose]\n"); - fprintf(stderr, " [-inetd] [-logfile file] service_name\n"); + fprintf(stderr, "Usage: gss-server [-port port] [-verbose] [-once]\n"); + fprintf(stderr, " [-inetd] [-export] [-logfile file] [service_name]\n"); exit(1); } @@ -144,89 +144,111 @@ int server_establish_context(s, server_creds, context, client_name, ret_flags) gss_OID doid; OM_uint32 maj_stat, min_stat, acc_sec_min_stat; gss_buffer_desc oid_name; + int token_flags; + + if (recv_token(s, &token_flags, &recv_tok) < 0) + return -1; + + (void) gss_release_buffer(&min_stat, &recv_tok); + if (! (token_flags & TOKEN_NOOP)) { + if (log) + fprintf(log, "Expected NOOP token, got %d token instead\n", + token_flags); + return -1; + } *context = GSS_C_NO_CONTEXT; - - do { - if (recv_token(s, &recv_tok) < 0) - return -1; - - if (verbose && log) { - fprintf(log, "Received token (size=%d): \n", recv_tok.length); - print_token(&recv_tok); - } - - maj_stat = - gss_accept_sec_context(&acc_sec_min_stat, - context, - server_creds, - &recv_tok, - GSS_C_NO_CHANNEL_BINDINGS, - &client, - &doid, - &send_tok, - ret_flags, - NULL, /* ignore time_rec */ - NULL); /* ignore del_cred_handle */ - - (void) gss_release_buffer(&min_stat, &recv_tok); - - if (send_tok.length != 0) { - if (verbose && log) { - fprintf(log, - "Sending accept_sec_context token (size=%d):\n", - send_tok.length); - print_token(&send_tok); - } - if (send_token(s, &send_tok) < 0) { - fprintf(log, "failure sending token\n"); - return -1; - } - - (void) gss_release_buffer(&min_stat, &send_tok); - } - if (maj_stat!=GSS_S_COMPLETE && maj_stat!=GSS_S_CONTINUE_NEEDED) { - display_status("accepting context", maj_stat, - acc_sec_min_stat); - if (*context == GSS_C_NO_CONTEXT) - gss_delete_sec_context(&min_stat, context, - GSS_C_NO_BUFFER); - return -1; - } - - if (verbose && log) { - if (maj_stat == GSS_S_CONTINUE_NEEDED) - fprintf(log, "continue needed...\n"); - else - fprintf(log, "\n"); - fflush(log); - } - } while (maj_stat == GSS_S_CONTINUE_NEEDED); - - /* display the flags */ - display_ctx_flags(*ret_flags); - - if (verbose && log) { + + if (token_flags & TOKEN_CONTEXT_NEXT) { + do { + if (recv_token(s, &token_flags, &recv_tok) < 0) + return -1; + + if (verbose && log) { + fprintf(log, "Received token (size=%d): \n", recv_tok.length); + print_token(&recv_tok); + } + + maj_stat = + gss_accept_sec_context(&acc_sec_min_stat, + context, + server_creds, + &recv_tok, + GSS_C_NO_CHANNEL_BINDINGS, + &client, + &doid, + &send_tok, + ret_flags, + NULL, /* ignore time_rec */ + NULL); /* ignore del_cred_handle */ + + (void) gss_release_buffer(&min_stat, &recv_tok); + + if (send_tok.length != 0) { + if (verbose && log) { + fprintf(log, + "Sending accept_sec_context token (size=%d):\n", + send_tok.length); + print_token(&send_tok); + } + if (send_token(s, TOKEN_CONTEXT, &send_tok) < 0) { + if (log) + fprintf(log, "failure sending token\n"); + return -1; + } + + (void) gss_release_buffer(&min_stat, &send_tok); + } + if (maj_stat!=GSS_S_COMPLETE && maj_stat!=GSS_S_CONTINUE_NEEDED) { + display_status("accepting context", maj_stat, + acc_sec_min_stat); + if (*context == GSS_C_NO_CONTEXT) + gss_delete_sec_context(&min_stat, context, + GSS_C_NO_BUFFER); + return -1; + } + + if (verbose && log) { + if (maj_stat == GSS_S_CONTINUE_NEEDED) + fprintf(log, "continue needed...\n"); + else + fprintf(log, "\n"); + fflush(log); + } + } while (maj_stat == GSS_S_CONTINUE_NEEDED); + + /* display the flags */ + display_ctx_flags(*ret_flags); + + if (verbose && log) { maj_stat = gss_oid_to_str(&min_stat, doid, &oid_name); if (maj_stat != GSS_S_COMPLETE) { - display_status("converting oid->string", maj_stat, min_stat); - return -1; + display_status("converting oid->string", maj_stat, min_stat); + return -1; } fprintf(log, "Accepted connection using mechanism OID %.*s.\n", (int) oid_name.length, (char *) oid_name.value); (void) gss_release_buffer(&min_stat, &oid_name); - } + } - maj_stat = gss_display_name(&min_stat, client, client_name, &doid); - if (maj_stat != GSS_S_COMPLETE) { - display_status("displaying name", maj_stat, min_stat); - return -1; + maj_stat = gss_display_name(&min_stat, client, client_name, &doid); + if (maj_stat != GSS_S_COMPLETE) { + display_status("displaying name", maj_stat, min_stat); + return -1; + } + maj_stat = gss_release_name(&min_stat, &client); + if (maj_stat != GSS_S_COMPLETE) { + display_status("releasing name", maj_stat, min_stat); + return -1; + } } - maj_stat = gss_release_name(&min_stat, &client); - if (maj_stat != GSS_S_COMPLETE) { - display_status("releasing name", maj_stat, min_stat); - return -1; + else { + client_name->length = *ret_flags = 0; + + if (log) + fprintf(log, "Accepted unauthenticated connection.\n"); } + return 0; } @@ -311,8 +333,9 @@ int test_import_export_context(context) copied_token.length = context_token.length; copied_token.value = malloc(context_token.length); if (copied_token.value == 0) { + if (log) fprintf(log, "Couldn't allocate memory to copy context token.\n"); - return 1; + return 1; } memcpy(copied_token.value, context_token.value, copied_token.length); maj_stat = gss_import_sec_context(&min_stat, &copied_token, context); @@ -340,6 +363,7 @@ int test_import_export_context(context) * accept()ed * service_name (r) the ASCII name of the GSS-API service to * establish a context as + * export (r) whether to test context exporting * * Returns: -1 on error * @@ -354,83 +378,128 @@ int test_import_export_context(context) * * If any error occurs, -1 is returned. */ -int sign_server(s, server_creds) +int sign_server(s, server_creds, export) int s; gss_cred_id_t server_creds; + int export; { gss_buffer_desc client_name, xmit_buf, msg_buf; gss_ctx_id_t context; OM_uint32 maj_stat, min_stat; int i, conf_state, ret_flags; char *cp; - + int token_flags; + /* Establish a context with the client */ if (server_establish_context(s, server_creds, &context, &client_name, &ret_flags) < 0) return(-1); - - printf("Accepted connection: \"%.*s\"\n", - (int) client_name.length, (char *) client_name.value); - (void) gss_release_buffer(&min_stat, &client_name); - for (i=0; i < 3; i++) - if (test_import_export_context(&context)) - return -1; - - /* Receive the sealed message token */ - if (recv_token(s, &xmit_buf) < 0) - return(-1); - - if (verbose && log) { - fprintf(log, "Sealed message token:\n"); - print_token(&xmit_buf); + if (context == GSS_C_NO_CONTEXT) { + printf("Accepted unauthenticated connection.\n"); } - - maj_stat = gss_unwrap(&min_stat, context, &xmit_buf, &msg_buf, - &conf_state, (gss_qop_t *) NULL); - if (maj_stat != GSS_S_COMPLETE) { - display_status("unsealing message", maj_stat, min_stat); - return(-1); - } else if (! conf_state) { - fprintf(stderr, "Warning! Message not encrypted.\n"); - } - - (void) gss_release_buffer(&min_stat, &xmit_buf); - - fprintf(log, "Received message: "); - cp = msg_buf.value; - if ((isprint(cp[0]) || isspace(cp[0])) && - (isprint(cp[1]) || isspace(cp[1]))) { - fprintf(log, "\"%.*s\"\n", msg_buf.length, msg_buf.value); - } else { - printf("\n"); - print_token(&msg_buf); - } - - /* Produce a signature block for the message */ - maj_stat = gss_get_mic(&min_stat, context, GSS_C_QOP_DEFAULT, - &msg_buf, &xmit_buf); - if (maj_stat != GSS_S_COMPLETE) { - display_status("signing message", maj_stat, min_stat); - return(-1); + else { + printf("Accepted connection: \"%.*s\"\n", + (int) client_name.length, (char *) client_name.value); + (void) gss_release_buffer(&min_stat, &client_name); + + if (export) { + for (i=0; i < 3; i++) + if (test_import_export_context(&context)) + return -1; + } } - (void) gss_release_buffer(&min_stat, &msg_buf); + do { + /* Receive the message token */ + if (recv_token(s, &token_flags, &xmit_buf) < 0) + return(-1); + + if (token_flags & TOKEN_NOOP) { + if (log) + fprintf(log, "NOOP token\n"); + (void) gss_release_buffer(&min_stat, &xmit_buf); + break; + } + + if (verbose && log) { + fprintf(log, "Message token (flags=%d):\n", token_flags); + print_token(&xmit_buf); + } + + if ((context == GSS_C_NO_CONTEXT) && + (token_flags & (TOKEN_WRAPPED|TOKEN_ENCRYPTED|TOKEN_SEND_MIC))) { + if (log) + fprintf(log, + "Unauthenticated client requested authenticated services!\n"); + (void) gss_release_buffer(&min_stat, &xmit_buf); + return(-1); + } + + if (token_flags & TOKEN_WRAPPED) { + maj_stat = gss_unwrap(&min_stat, context, &xmit_buf, &msg_buf, + &conf_state, (gss_qop_t *) NULL); + if (maj_stat != GSS_S_COMPLETE) { + display_status("unsealing message", maj_stat, min_stat); + (void) gss_release_buffer(&min_stat, &xmit_buf); + return(-1); + } else if (! conf_state && (token_flags & TOKEN_ENCRYPTED)) { + fprintf(stderr, "Warning! Message not encrypted.\n"); + } - /* Send the signature block to the client */ - if (send_token(s, &xmit_buf) < 0) - return(-1); + (void) gss_release_buffer(&min_stat, &xmit_buf); + } + else { + msg_buf = xmit_buf; + } + + if (log) { + fprintf(log, "Received message: "); + cp = msg_buf.value; + if ((isprint(cp[0]) || isspace(cp[0])) && + (isprint(cp[1]) || isspace(cp[1]))) { + fprintf(log, "\"%.*s\"\n", msg_buf.length, msg_buf.value); + } else { + fprintf(log, "\n"); + print_token(&msg_buf); + } + } - (void) gss_release_buffer(&min_stat, &xmit_buf); + if (token_flags & TOKEN_SEND_MIC) { + /* Produce a signature block for the message */ + maj_stat = gss_get_mic(&min_stat, context, GSS_C_QOP_DEFAULT, + &msg_buf, &xmit_buf); + if (maj_stat != GSS_S_COMPLETE) { + display_status("signing message", maj_stat, min_stat); + return(-1); + } - /* Delete context */ - maj_stat = gss_delete_sec_context(&min_stat, &context, NULL); - if (maj_stat != GSS_S_COMPLETE) { - display_status("deleting context", maj_stat, min_stat); - return(-1); + (void) gss_release_buffer(&min_stat, &msg_buf); + + /* Send the signature block to the client */ + if (send_token(s, TOKEN_MIC, &xmit_buf) < 0) + return(-1); + + (void) gss_release_buffer(&min_stat, &xmit_buf); + } + else { + (void) gss_release_buffer(&min_stat, &msg_buf); + if (send_token(s, TOKEN_NOOP, empty_token) < 0) + return(-1); + } + } while (1 /* loop will break if NOOP received */); + + if (context != GSS_C_NO_CONTEXT) { + /* Delete context */ + maj_stat = gss_delete_sec_context(&min_stat, &context, NULL); + if (maj_stat != GSS_S_COMPLETE) { + display_status("deleting context", maj_stat, min_stat); + return(-1); + } } - fflush(log); + if (log) + fflush(log); return(0); } @@ -447,6 +516,7 @@ main(argc, argv) int s; int once = 0; int do_inetd = 0; + int export = 0; log = stdout; display_file = stdout; @@ -462,14 +532,25 @@ main(argc, argv) once = 1; } else if (strcmp(*argv, "-inetd") == 0) { do_inetd = 1; + } else if (strcmp(*argv, "-export") == 0) { + export = 1; } else if (strcmp(*argv, "-logfile") == 0) { argc--; argv++; if (!argc) usage(); - log = fopen(*argv, "a"); - display_file = log; - if (!log) { + /* Gross hack, but it makes it unnecessary to add an + extra argument to disable logging, and makes the code + more efficient because it doesn't actually write data + to /dev/null. */ + if (! strcmp(*argv, "/dev/null")) { + log = display_file = NULL; + } + else { + log = fopen(*argv, "a"); + display_file = log; + if (!log) { perror(*argv); exit(1); + } } } else break; @@ -490,31 +571,29 @@ main(argc, argv) close(1); close(2); - sign_server(0, server_creds); + sign_server(0, server_creds, export); close(0); } else { int stmp; - if ((stmp = create_socket(port)) >= 0) { - do { - /* Accept a TCP connection */ - if ((s = accept(stmp, NULL, 0)) < 0) { - perror("accepting connection"); - continue; - } - /* this return value is not checked, because there's - not really anything to do if it fails */ - sign_server(s, server_creds); - close(s); - } while (!once); - - close(stmp); - } + if ((stmp = create_socket(port)) >= 0) { + do { + /* Accept a TCP connection */ + if ((s = accept(stmp, NULL, 0)) < 0) { + perror("accepting connection"); + continue; + } + /* this return value is not checked, because there's + not really anything to do if it fails */ + sign_server(s, server_creds); + close(s); + } while (!once); + + close(stmp); + } } (void) gss_release_cred(&min_stat, &server_creds); - /*NOTREACHED*/ - (void) close(s); return 0; } -- 2.26.2