summaryrefslogtreecommitdiff
path: root/src/lib/rpc/unit-test
diff options
context:
space:
mode:
Diffstat (limited to 'src/lib/rpc/unit-test')
-rw-r--r--src/lib/rpc/unit-test/Makefile.in66
-rw-r--r--src/lib/rpc/unit-test/client.c304
-rw-r--r--src/lib/rpc/unit-test/config/unix.exp174
-rw-r--r--src/lib/rpc/unit-test/deps37
-rw-r--r--src/lib/rpc/unit-test/lib/helpers.exp236
-rw-r--r--src/lib/rpc/unit-test/rpc_test.0/expire.exp49
-rw-r--r--src/lib/rpc/unit-test/rpc_test.0/fullrun.exp91
-rw-r--r--src/lib/rpc/unit-test/rpc_test.0/gsserr.exp30
-rw-r--r--src/lib/rpc/unit-test/rpc_test.h13
-rw-r--r--src/lib/rpc/unit-test/rpc_test.x30
-rw-r--r--src/lib/rpc/unit-test/rpc_test_clnt.c22
-rwxr-xr-xsrc/lib/rpc/unit-test/rpc_test_setup.sh59
-rw-r--r--src/lib/rpc/unit-test/rpc_test_svc.c67
-rw-r--r--src/lib/rpc/unit-test/server.c259
14 files changed, 1437 insertions, 0 deletions
diff --git a/src/lib/rpc/unit-test/Makefile.in b/src/lib/rpc/unit-test/Makefile.in
new file mode 100644
index 000000000000..6f29e33c9151
--- /dev/null
+++ b/src/lib/rpc/unit-test/Makefile.in
@@ -0,0 +1,66 @@
+mydir=lib$(S)rpc$(S)unit-test
+BUILDTOP=$(REL)..$(S)..$(S)..
+
+OBJS= client.o rpc_test_clnt.o rpc_test_svc.o server.o
+SRCS= client.c rpc_test_clnt.c rpc_test_svc.c server.c
+
+all: client server
+
+client: client.o rpc_test_clnt.o $(GSSRPC_DEPLIBS) $(KRB5_BASE_DEPLIBS)
+ $(CC_LINK) -o client client.o rpc_test_clnt.o \
+ $(GSSRPC_LIBS) $(KRB5_BASE_LIBS)
+
+server: server.o rpc_test_svc.o $(GSSRPC_DEPLIBS) $(KRB5_BASE_DEPLIBS)
+ $(CC_LINK) -o server server.o rpc_test_svc.o \
+ $(GSSRPC_LIBS) $(KRB5_BASE_LIBS)
+
+client.o server.o: rpc_test.h
+
+# If rpc_test.h and rpc_test_*.c do not work on your system, you can
+# try using rpcgen by uncommenting these lines (be sure to uncomment
+# then in the generated not Makefile.in).
+# rpc_test.h rpc_test_clnt.c rpc_test_svc.c: rpc_test.x
+# -rm -f rpc_test_clnt.c rpc_test_svc.c rpc_test.h
+# -ln -s $(srcdir)/rpc_test.x .
+# rpcgen -l rpc_test.x -o rpc_test_clnt.c
+# rpcgen -m rpc_test.x -o rpc_test_svc.c
+# rpcgen -h rpc_test.x -o rpc_test.h
+#
+# clean:
+# rm -f rpc_test.h rpc_test_clnt.c rpc_test_svc.c
+#
+
+check unit-test: unit-test-@DO_TEST@
+
+unit-test-:
+ @echo "+++"
+ @echo "+++ WARNING: lib/rpc unit tests not run."
+ @echo "+++ Either tcl, runtest, or Perl is unavailable."
+ @echo "+++"
+ @echo 'Skipped rpc tests: runtest or Perl not found' >> $(SKIPTESTS)
+
+unit-test-ok: unit-test-body
+
+PASS=@PASS@
+unit-test-body:
+ $(RM) krb5cc_rpc_test_*
+ $(ENV_SETUP) $(VALGRIND) $(START_SERVERS)
+ RPC_TEST_SRVTAB=/tmp/rpc_test_v5srvtab.$$$$ ; export RPC_TEST_SRVTAB ; \
+ trap "echo Failed, cleaning up... ; rm -f $$RPC_TEST_SRVTAB ; $(ENV_SETUP) $(STOP_SERVERS) ; trap '' 0 ; exit 1" 0 1 2 3 14 15 ; \
+ if $(ENV_SETUP) \
+ $(RUNTEST) SERVER=./server CLIENT=./client \
+ KINIT=$(BUILDTOP)/clients/kinit/kinit \
+ KDESTROY=$(BUILDTOP)/clients/kdestroy/kdestroy \
+ PRIOCNTL_HACK=@PRIOCNTL_HACK@ VALGRIND="$(VALGRIND)" \
+ PASS="$(PASS)" --tool rpc_test $(RUNTESTFLAGS) ; \
+ then \
+ echo Cleaning up... ; \
+ rm -f $$RPC_TEST_SRVTAB krb5cc_rpc_test_* ; \
+ $(ENV_SETUP) $(STOP_SERVERS) ; \
+ trap 0 ; exit 0 ; \
+ else exit 1 ; fi
+
+clean:
+ $(RM) server client
+ $(RM) dbg.log rpc_test.log rpc_test.sum
+
diff --git a/src/lib/rpc/unit-test/client.c b/src/lib/rpc/unit-test/client.c
new file mode 100644
index 000000000000..5edde49dfa8d
--- /dev/null
+++ b/src/lib/rpc/unit-test/client.c
@@ -0,0 +1,304 @@
+/*
+ * Copyright 1993 OpenVision Technologies, Inc., All Rights Reserved.
+ *
+ * $Id$
+ *
+ */
+
+#include "autoconf.h"
+#include <stdio.h>
+#include <string.h>
+#include <netdb.h>
+#include <sys/socket.h>
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#include <gssrpc/rpc.h>
+#include <gssapi/gssapi.h>
+#include <gssapi/gssapi_krb5.h>
+#include <gssrpc/rpc.h>
+#include <gssrpc/auth_gssapi.h>
+#include "rpc_test.h"
+
+#define BIG_BUF 4096
+/* copied from auth_gssapi.c for hackery */
+struct auth_gssapi_data {
+ bool_t established;
+ CLIENT *clnt;
+ gss_ctx_id_t context;
+ gss_buffer_desc client_handle;
+ OM_uint32 seq_num;
+ int def_cred;
+
+ /* pre-serialized ah_cred */
+ u_char cred_buf[MAX_AUTH_BYTES];
+ int32_t cred_len;
+};
+#define AUTH_PRIVATE(auth) ((struct auth_gssapi_data *)auth->ah_private)
+
+extern int auth_debug_gssapi;
+char *whoami;
+
+#ifdef __GNUC__
+__attribute__((noreturn))
+#endif
+static void usage()
+{
+ fprintf(stderr, "usage: %s {-t|-u} [-a] [-s num] [-m num] host service [count]\n",
+ whoami);
+ exit(1);
+}
+
+int
+main(argc, argv)
+ int argc;
+ char **argv;
+{
+ char *host, *port, *target, *echo_arg, **echo_resp, buf[BIG_BUF];
+ CLIENT *clnt;
+ AUTH *tmp_auth;
+ struct rpc_err e;
+ int auth_once, sock, use_tcp;
+ unsigned int count, i;
+ extern int optind;
+ extern char *optarg;
+ extern int svc_debug_gssapi, misc_debug_gssapi, auth_debug_gssapi;
+ int c;
+ struct sockaddr_in sin;
+ struct hostent *h;
+ struct timeval tv;
+
+ extern int krb5_gss_dbg_client_expcreds;
+ krb5_gss_dbg_client_expcreds = 1;
+
+ whoami = argv[0];
+ count = 1026;
+ auth_once = 0;
+ use_tcp = -1;
+
+ while ((c = getopt(argc, argv, "a:m:os:tu")) != -1) {
+ switch (c) {
+ case 'a':
+ auth_debug_gssapi = atoi(optarg);
+ break;
+ case 'm':
+ misc_debug_gssapi = atoi(optarg);
+ break;
+ case 'o':
+ auth_once++;
+ break;
+ case 's':
+ svc_debug_gssapi = atoi(optarg);
+ break;
+ case 't':
+ use_tcp = 1;
+ break;
+ case 'u':
+ use_tcp = 0;
+ break;
+ case '?':
+ usage();
+ break;
+ }
+ }
+ if (use_tcp == -1)
+ usage();
+
+ argv += optind;
+ argc -= optind;
+
+ switch (argc) {
+ case 4:
+ count = atoi(argv[3]);
+ if (count > BIG_BUF-1) {
+ fprintf(stderr, "Test count cannot exceed %d.\n", BIG_BUF-1);
+ usage();
+ }
+ case 3:
+ host = argv[0];
+ port = argv[1];
+ target = argv[2];
+ break;
+ default:
+ usage();
+ }
+
+ /* get server address */
+ h = gethostbyname(host);
+ if (h == NULL) {
+ fprintf(stderr, "Can't resolve hostname %s\n", host);
+ exit(1);
+ }
+ memset(&sin, 0, sizeof(sin));
+ sin.sin_family = h->h_addrtype;
+ sin.sin_port = ntohs(atoi(port));
+ memmove(&sin.sin_addr, h->h_addr, sizeof(sin.sin_addr));
+
+ /* client handle to rstat */
+ sock = RPC_ANYSOCK;
+ if (use_tcp) {
+ clnt = clnttcp_create(&sin, RPC_TEST_PROG, RPC_TEST_VERS_1, &sock, 0,
+ 0);
+ } else {
+ tv.tv_sec = 5;
+ tv.tv_usec = 0;
+ clnt = clntudp_create(&sin, RPC_TEST_PROG, RPC_TEST_VERS_1, tv,
+ &sock);
+ }
+ if (clnt == NULL) {
+ clnt_pcreateerror(whoami);
+ exit(1);
+ }
+
+ clnt->cl_auth = auth_gssapi_create_default(clnt, target);
+ if (clnt->cl_auth == NULL) {
+ clnt_pcreateerror(whoami);
+ exit(2);
+ }
+
+ /*
+ * Call the echo service multiple times.
+ */
+ echo_arg = buf;
+ for (i = 0; i < 3; i++) {
+ snprintf(buf, sizeof(buf), "testing %d\n", i);
+
+ echo_resp = rpc_test_echo_1(&echo_arg, clnt);
+ if (echo_resp == NULL) {
+ fprintf(stderr, "RPC_TEST_ECHO call %d%s", i,
+ clnt_sperror(clnt, ""));
+ }
+ if (strncmp(*echo_resp, "Echo: ", 6) &&
+ strcmp(echo_arg, (*echo_resp) + 6) != 0)
+ fprintf(stderr, "RPC_TEST_ECHO call %d response wrong: "
+ "arg = %s, resp = %s\n", i, echo_arg, *echo_resp);
+ gssrpc_xdr_free(xdr_wrapstring, echo_resp);
+ }
+
+ /*
+ * Make a call with an invalid verifier and check for error;
+ * server should log error message. It is important to
+ *increment* seq_num here, since a decrement would be fixed (see
+ * below). Note that seq_num will be incremented (by
+ * authg_gssapi_refresh) twice, so we need to decrement by three
+ * to reset.
+ */
+ AUTH_PRIVATE(clnt->cl_auth)->seq_num++;
+
+ echo_arg = "testing with bad verf";
+
+ echo_resp = rpc_test_echo_1(&echo_arg, clnt);
+ if (echo_resp == NULL) {
+ CLNT_GETERR(clnt, &e);
+ if (e.re_status != RPC_AUTHERROR || e.re_why != AUTH_REJECTEDVERF)
+ clnt_perror(clnt, whoami);
+ } else {
+ fprintf(stderr, "bad seq didn't cause failure\n");
+ gssrpc_xdr_free(xdr_wrapstring, echo_resp);
+ }
+
+ AUTH_PRIVATE(clnt->cl_auth)->seq_num -= 3;
+
+ /*
+ * Make sure we're resyncronized.
+ */
+ echo_arg = "testing for reset";
+ echo_resp = rpc_test_echo_1(&echo_arg, clnt);
+ if (echo_resp == NULL)
+ clnt_perror(clnt, "Sequence number improperly reset");
+ else
+ gssrpc_xdr_free(xdr_wrapstring, echo_resp);
+
+ /*
+ * Now simulate a lost server response, and see if
+ * auth_gssapi_refresh recovers.
+ */
+ AUTH_PRIVATE(clnt->cl_auth)->seq_num--;
+ echo_arg = "forcing auto-resynchronization";
+ echo_resp = rpc_test_echo_1(&echo_arg, clnt);
+ if (echo_resp == NULL)
+ clnt_perror(clnt, "Auto-resynchronization failed");
+ else
+ gssrpc_xdr_free(xdr_wrapstring, echo_resp);
+
+ /*
+ * Now make sure auto-resyncrhonization actually worked
+ */
+ echo_arg = "testing for resynchronization";
+ echo_resp = rpc_test_echo_1(&echo_arg, clnt);
+ if (echo_resp == NULL)
+ clnt_perror(clnt, "Auto-resynchronization did not work");
+ else
+ gssrpc_xdr_free(xdr_wrapstring, echo_resp);
+
+ /*
+ * Test fix for secure-rpc/586, part 1: btree keys must be
+ * unique. Create another context from the same credentials; it
+ * should have the same expiration time and will cause the server
+ * to abort if the clients are not differentiated.
+ *
+ * Test fix for secure-rpc/586, part 2: btree keys cannot be
+ * mutated in place. To test this: a second client, *with a
+ * later expiration time*, must be run. The second client should
+ * destroy itself *after* the first one; if the key-mutating bug
+ * is not fixed, the second client_data will be in the btree
+ * before the first, but its key will be larger; thus, when the
+ * first client calls AUTH_DESTROY, the server won't find it in
+ * the btree and call abort.
+ *
+ * For unknown reasons, running just a second client didn't
+ * tickle the bug; the btree code seemed to guess which node to
+ * look at first. Running a total of three clients does ticket
+ * the bug. Thus, the full test sequence looks like this:
+ *
+ * kinit -l 20m user && client server test@ddn 200
+ * sleep 1
+ * kini -l 30m user && client server test@ddn 300
+ * sleep 1
+ * kinit -l 40m user && client server test@ddn 400
+ */
+ if (! auth_once) {
+ tmp_auth = clnt->cl_auth;
+ clnt->cl_auth = auth_gssapi_create_default(clnt, target);
+ if (clnt->cl_auth == NULL) {
+ clnt_pcreateerror(whoami);
+ exit(2);
+ }
+ AUTH_DESTROY(clnt->cl_auth);
+ clnt->cl_auth = tmp_auth;
+ }
+
+ /*
+ * Try RPC calls with argument/result lengths [0, 1025]. Do
+ * this last, since it takes a while..
+ */
+ echo_arg = buf;
+ memset(buf, 0, count+1);
+ for (i = 0; i < count; i++) {
+ echo_resp = rpc_test_echo_1(&echo_arg, clnt);
+ if (echo_resp == NULL) {
+ fprintf(stderr, "RPC_TEST_LENGTHS call %d%s", i,
+ clnt_sperror(clnt, ""));
+ break;
+ } else {
+ if (strncmp(*echo_resp, "Echo: ", 6) &&
+ strcmp(echo_arg, (*echo_resp) + 6) != 0)
+ fprintf(stderr,
+ "RPC_TEST_LENGTHS call %d response wrong\n", i);
+ gssrpc_xdr_free(xdr_wrapstring, echo_resp);
+ }
+
+ /* cycle from 1 to 255 */
+ buf[i] = (i % 255) + 1;
+
+ if (i % 100 == 0) {
+ fputc('.', stdout);
+ fflush(stdout);
+ }
+ }
+ fputc('\n', stdout);
+
+ AUTH_DESTROY(clnt->cl_auth);
+ CLNT_DESTROY(clnt);
+ exit(0);
+}
diff --git a/src/lib/rpc/unit-test/config/unix.exp b/src/lib/rpc/unit-test/config/unix.exp
new file mode 100644
index 000000000000..ba57b703e950
--- /dev/null
+++ b/src/lib/rpc/unit-test/config/unix.exp
@@ -0,0 +1,174 @@
+#
+# $Id$
+#
+
+set kill /bin/kill
+set sleep /bin/sleep
+set kinit $KINIT
+set kdestroy $KDESTROY
+
+set hostname [exec hostname]
+
+# Hack around Solaris 9 kernel race condition that causes last output
+# from a pty to get dropped.
+if { $PRIOCNTL_HACK } {
+ catch {exec priocntl -s -c FX -m 30 -p 30 -i pid [getpid]}
+ rename spawn oldspawn
+ proc spawn { args } {
+ upvar 1 spawn_id spawn_id
+ set newargs {}
+ set inflags 1
+ set eatnext 0
+ foreach arg $args {
+ if { $arg == "-ignore" \
+ || $arg == "-open" \
+ || $arg == "-leaveopen" } {
+ lappend newargs $arg
+ set eatnext 1
+ continue
+ }
+ if [string match "-*" $arg] {
+ lappend newargs $arg
+ continue
+ }
+ if { $eatnext } {
+ set eatnext 0
+ lappend newargs $arg
+ continue
+ }
+ if { $inflags } {
+ set inflags 0
+ set newargs [concat $newargs {priocntl -e -c FX -p 0}]
+ }
+ lappend newargs $arg
+ }
+ set pid [eval oldspawn $newargs]
+ return $pid
+ }
+}
+
+if { [string length $VALGRIND] } {
+ rename spawn valgrind_aux_spawn
+ proc spawn { args } {
+ global VALGRIND
+ upvar 1 spawn_id spawn_id
+ set newargs {}
+ set inflags 1
+ set eatnext 0
+ foreach arg $args {
+ if { $arg == "-ignore" \
+ || $arg == "-open" \
+ || $arg == "-leaveopen" } {
+ lappend newargs $arg
+ set eatnext 1
+ continue
+ }
+ if [string match "-*" $arg] {
+ lappend newargs $arg
+ continue
+ }
+ if { $eatnext } {
+ set eatnext 0
+ lappend newargs $arg
+ continue
+ }
+ if { $inflags } {
+ set inflags 0
+ # Only run valgrind for local programs, not
+ # system ones.
+#&&![string match "/bin/sh" $arg] sh is used to start kadmind!
+ if [string match "/" [string index $arg 0]]&&![string match "/bin/ls" $arg]&&![regexp {/kshd$} $arg] {
+ set newargs [concat $newargs $VALGRIND]
+ } elseif [string match "." [string index $arg 0]] {
+ set newargs [concat $newargs $VALGRIND]
+ }
+ }
+ lappend newargs $arg
+ }
+ set pid [eval valgrind_aux_spawn $newargs]
+ return $pid
+ }
+}
+
+# this will initialize the database and keytab
+load_lib "helpers.exp"
+
+proc rpc_test_version {} {
+ global CLIENT
+ global SERVER
+
+ clone_output "$CLIENT version <unknown>"
+ clone_output "$SERVER version <unknown>"
+}
+
+proc rpc_test_load {} {
+ #
+}
+
+# rpc_test_exit -- clean up and exit
+proc rpc_test_exit {} {
+ global server_id
+ global server_pid
+ global server_started
+ global kill
+
+ if {[catch {
+ expect {
+ -i $server_id
+ eof {
+ fail "server exited!"
+ verbose $expect_out(buffer) 1
+ }
+ timeout { pass "server survived" }
+ }
+ } tmp]} {
+ fail "server exited! (expect failed)"
+ }
+}
+
+#
+# rpc_test_start -- start the rpc_test server running
+#
+proc rpc_test_start { } {
+ global SERVER PROT
+ global server_id
+ global server_pid
+ global server_started
+ global server_port
+ global env
+
+ if [info exists server_pid] { rpc_test_exit }
+
+ set env(KRB5_KTNAME) FILE:$env(RPC_TEST_SRVTAB)
+
+ verbose "% $SERVER" 1
+ set server_pid [spawn $SERVER $PROT]
+ set server_id $spawn_id
+ set server_started 1
+ set server_port -1
+
+ unset env(KRB5_KTNAME)
+
+ set timeout 30
+
+ expect {
+ -re "port: (\[0-9\]*)\r\n" {
+ set server_port $expect_out(1,string)
+ }
+ "running" { }
+ eof {
+ send_error "server exited!"
+ verbose $expect_out(buffer) 1
+ }
+ timeout {
+ send_error "server didn't start in $timeout seconds"
+ verbose $expect_out(buffer) 1
+ }
+ }
+
+}
+
+set MULTIPASS {
+ {tcp PROT=-t dummy=[rpc_test_start]}
+ {udp PROT=-u dummy=[rpc_test_start]}
+}
diff --git a/src/lib/rpc/unit-test/deps b/src/lib/rpc/unit-test/deps
new file mode 100644
index 000000000000..335eb672cf49
--- /dev/null
+++ b/src/lib/rpc/unit-test/deps
@@ -0,0 +1,37 @@
+#
+# Generated makefile dependencies follow.
+#
+$(OUTPRE)client.$(OBJEXT): $(BUILDTOP)/include/autoconf.h \
+ $(BUILDTOP)/include/gssapi/gssapi.h $(BUILDTOP)/include/gssapi/gssapi_ext.h \
+ $(BUILDTOP)/include/gssapi/gssapi_krb5.h $(BUILDTOP)/include/gssrpc/types.h \
+ $(BUILDTOP)/include/krb5/krb5.h $(COM_ERR_DEPS) $(top_srcdir)/include/gssrpc/auth.h \
+ $(top_srcdir)/include/gssrpc/auth_gss.h $(top_srcdir)/include/gssrpc/auth_gssapi.h \
+ $(top_srcdir)/include/gssrpc/auth_unix.h $(top_srcdir)/include/gssrpc/clnt.h \
+ $(top_srcdir)/include/gssrpc/rename.h $(top_srcdir)/include/gssrpc/rpc.h \
+ $(top_srcdir)/include/gssrpc/rpc_msg.h $(top_srcdir)/include/gssrpc/svc.h \
+ $(top_srcdir)/include/gssrpc/svc_auth.h $(top_srcdir)/include/gssrpc/xdr.h \
+ $(top_srcdir)/include/krb5.h client.c rpc_test.h
+$(OUTPRE)rpc_test_clnt.$(OBJEXT): $(BUILDTOP)/include/gssapi/gssapi.h \
+ $(BUILDTOP)/include/gssrpc/types.h $(top_srcdir)/include/gssrpc/auth.h \
+ $(top_srcdir)/include/gssrpc/auth_gss.h $(top_srcdir)/include/gssrpc/auth_unix.h \
+ $(top_srcdir)/include/gssrpc/clnt.h $(top_srcdir)/include/gssrpc/rename.h \
+ $(top_srcdir)/include/gssrpc/rpc.h $(top_srcdir)/include/gssrpc/rpc_msg.h \
+ $(top_srcdir)/include/gssrpc/svc.h $(top_srcdir)/include/gssrpc/svc_auth.h \
+ $(top_srcdir)/include/gssrpc/xdr.h rpc_test.h rpc_test_clnt.c
+$(OUTPRE)rpc_test_svc.$(OBJEXT): $(BUILDTOP)/include/gssapi/gssapi.h \
+ $(BUILDTOP)/include/gssrpc/types.h $(top_srcdir)/include/gssrpc/auth.h \
+ $(top_srcdir)/include/gssrpc/auth_gss.h $(top_srcdir)/include/gssrpc/auth_unix.h \
+ $(top_srcdir)/include/gssrpc/clnt.h $(top_srcdir)/include/gssrpc/rename.h \
+ $(top_srcdir)/include/gssrpc/rpc.h $(top_srcdir)/include/gssrpc/rpc_msg.h \
+ $(top_srcdir)/include/gssrpc/svc.h $(top_srcdir)/include/gssrpc/svc_auth.h \
+ $(top_srcdir)/include/gssrpc/xdr.h rpc_test.h rpc_test_svc.c
+$(OUTPRE)server.$(OBJEXT): $(BUILDTOP)/include/autoconf.h \
+ $(BUILDTOP)/include/gssapi/gssapi.h $(BUILDTOP)/include/gssapi/gssapi_generic.h \
+ $(BUILDTOP)/include/gssrpc/types.h $(top_srcdir)/include/gssrpc/auth.h \
+ $(top_srcdir)/include/gssrpc/auth_gss.h $(top_srcdir)/include/gssrpc/auth_gssapi.h \
+ $(top_srcdir)/include/gssrpc/auth_unix.h $(top_srcdir)/include/gssrpc/clnt.h \
+ $(top_srcdir)/include/gssrpc/pmap_clnt.h $(top_srcdir)/include/gssrpc/rename.h \
+ $(top_srcdir)/include/gssrpc/rpc.h $(top_srcdir)/include/gssrpc/rpc_msg.h \
+ $(top_srcdir)/include/gssrpc/svc.h $(top_srcdir)/include/gssrpc/svc_auth.h \
+ $(top_srcdir)/include/gssrpc/xdr.h $(top_srcdir)/include/k5-platform.h \
+ $(top_srcdir)/include/k5-thread.h rpc_test.h server.c
diff --git a/src/lib/rpc/unit-test/lib/helpers.exp b/src/lib/rpc/unit-test/lib/helpers.exp
new file mode 100644
index 000000000000..a1b078374af5
--- /dev/null
+++ b/src/lib/rpc/unit-test/lib/helpers.exp
@@ -0,0 +1,236 @@
+if {[info commands exp_version] != {}} {
+ set exp_version_4 [regexp {^4} [exp_version]]
+} else {
+ set exp_version_4 [regexp {^4} [expect_version]]
+}
+
+# Backward compatibility until we're using expect 5 everywhere
+if {$exp_version_4} {
+ global wait_error_index wait_errno_index wait_status_index
+ set wait_error_index 0
+ set wait_errno_index 1
+ set wait_status_index 1
+} else {
+ set wait_error_index 2
+ set wait_errno_index 3
+ set wait_status_index 3
+}
+
+proc set_from_env {varname default_value} {
+ global env
+ upvar $varname v
+
+ if [info exists env($varname)] {
+ set v $env($varname)
+ } else {
+ set v $default_value
+ }
+}
+proc expect_tcl_prompt {} {
+ global kadmin_tcl_spawn_id
+ expect {
+ -i $kadmin_tcl_spawn_id
+ -re "^% $" { }
+ -re . { perror "unexpected output {$expect_out(buffer)} from subprocess, expecting tcl prompt" }
+ timeout { perror "timeout waiting for tcl prompt" }
+ eof { perror "eof from subprocess when expecting tcl prompt" }
+ }
+}
+proc send_tcl_cmd_await_echo {cmd} {
+ global kadmin_tcl_spawn_id
+ send -i $kadmin_tcl_spawn_id "$cmd\n"
+ expect {
+ -i $kadmin_tcl_spawn_id
+ -ex "$cmd\r\n" { }
+ timeout { perror "timeout waiting for tcl subprocess to echo input" }
+ eof { perror "eof waiting for tcl subprocess to echo input" }
+ }
+}
+proc expect_kadm_ok {} {
+ global kadmin_tcl_spawn_id
+ expect {
+ -i $kadmin_tcl_spawn_id
+ -re "^OK KADM5_OK \[^\n\]*\n" {}
+ -re "^ERROR \[^\n\]*\n" { perror "kadmin tcl subprocess reported unexpected error" }
+ -re "^marshall_new_creds: \[^\n\]*\n" { exp_continue }
+ -re "^gssapi_\[^\n\]*\n" { exp_continue }
+ -re "^\r?\n" { exp_continue }
+ eof { perror "kadmin tcl subprocess died" }
+ default { perror "didn't get ok back" }
+ }
+}
+# trying to translate rpc_test_setup.sh into inline tcl...
+proc setup_database {} {
+ global env spawn_id kadmin_tcl_spawn_id TESTDIR MAKE_KEYTAB CANON_HOST
+
+ # XXXXX
+ set_from_env TOP {/x/x/x/x/x}
+ send_user "TOP=$TOP\n"
+
+ set_from_env TESTDIR $env(TOP)/testing
+ set_from_env CLNTTCL $TESTDIR/util/kadm5_clnt_tcl
+ set_from_env TCLUTIL $TESTDIR/tcl/util.t
+ set env(TCLUTIL) $TCLUTIL
+ set_from_env MAKE_KEYTAB $TESTDIR/scripts/make-host-keytab.pl
+ set env(PATH) "$TOP/install/admin:$env(PATH)"
+
+ # $VERBOSE ?
+
+ if [info exists spawn_id] { set x $spawn_id }
+ spawn $CLNTTCL
+ set kadmin_tcl_spawn_id $spawn_id
+ if [info exists x] { set spawn_id $x }
+
+ expect_tcl_prompt
+ # tcl 8.4 for some reason screws up autodetection of output EOL
+ # translation. Work around it for now.
+ send_tcl_cmd_await_echo "if { \[info commands fconfigure\] != \"\" } { fconfigure stdout -translation lf }"
+ expect_tcl_prompt
+ send_tcl_cmd_await_echo "source {$TCLUTIL}"
+ expect_tcl_prompt
+ send_tcl_cmd_await_echo "set h {$CANON_HOST}"
+ expect {
+ -ex "$CANON_HOST\r\n" { }
+ timeout { perror "timeout waiting for subprocess" }
+ eof { perror "eof from subprocess" }
+ }
+ expect_tcl_prompt
+
+ send_tcl_cmd_await_echo {kadm5_init admin admin $KADM5_ADMIN_SERVICE null $KADM5_STRUCT_VERSION $KADM5_API_VERSION_2 server_handle}
+ expect_kadm_ok
+ expect "^% "
+ send_tcl_cmd_await_echo {kadm5_create_principal $server_handle [simple_principal server/$h] {KADM5_PRINCIPAL} admin}
+ expect_kadm_ok
+ expect "^% "
+ send_tcl_cmd_await_echo {kadm5_randkey_principal $server_handle server/$h key null}
+ expect_kadm_ok
+ expect "^% "
+ send_tcl_cmd_await_echo {kadm5_create_principal $server_handle [simple_principal notserver/$h] {KADM5_PRINCIPAL} admin}
+ expect_kadm_ok
+ expect "^% "
+ send_tcl_cmd_await_echo {kadm5_randkey_principal $server_handle notserver/$h key null}
+ expect_kadm_ok
+ expect "^% "
+ send_tcl_cmd_await_echo {kadm5_destroy $server_handle}
+ expect_kadm_ok
+ expect "^% "
+ wait -nowait -i $spawn_id
+ close -i $spawn_id
+}
+
+if ![info exists CANON_HOST] {
+ set CANON_HOST [exec $env(QUALNAME)]
+ setup_database
+ file delete $env(RPC_TEST_SRVTAB)
+ exec $env(MAKE_KEYTAB) -princ "server/$CANON_HOST" $env(RPC_TEST_SRVTAB)
+}
+
+
+proc kinit {princ pass lifetime} {
+ global kinit
+ global wait_error_index wait_errno_index wait_status_index
+
+ spawn -noecho $kinit -5 -l $lifetime $princ
+ expect {
+ -re "Password for $princ.*: " { send "$pass\n"; expect eof }
+ timeout { perror "Timeout waiting for kinit"; close }
+ eof
+ }
+
+ set ret [wait]
+ if {[lindex $ret $wait_error_index] == -1} {
+ perror \
+ "wait(kinit $princ) returned error [lindex $ret $wait_errno_index]"
+ } else {
+ if {[lindex $ret $wait_status_index] != 0} {
+ perror \
+ "kinit $princ failed with [lindex $ret $wait_status_index]"
+ }
+ }
+}
+
+proc flush_server {} {
+ global server_id
+ global expect_out
+
+ verbose "flushing server output" 1
+
+ while {1} {
+ set timeout 5
+
+ expect {
+ -i $server_id
+ -re "^.+$" {
+ verbose "server output: $expect_out(buffer)"
+ }
+ timeout { break }
+ }
+ }
+}
+
+proc start_client {testname ccname user password lifetime count
+ {target ""}} {
+ global env CLIENT PROT hostname server_port spawn_id verbose
+
+ if {$target == ""} {
+ set target "server@$hostname"
+ }
+
+ set env(KRB5CCNAME) FILE:[pwd]/krb5cc_rpc_test_$ccname
+ kinit $user $password $lifetime
+
+ if {$verbose > 0} {
+ spawn $CLIENT -a 1 -s 1 -m 1 $PROT $hostname $server_port $target $count
+ } else {
+ spawn $CLIENT $PROT $hostname $server_port $target $count
+ }
+
+ verbose "$testname: client $ccname started"
+
+ unset env(KRB5CCNAME)
+}
+
+proc eof_client {testname ccname id status} {
+ verbose "$testname: eof'ing for client $ccname" 1
+
+ expect {
+ -i $id
+ -re "^marshall_new_creds\[^\n\]*\n" { exp_continue }
+ -re "^gssapi_\[^\n\]*\n" { exp_continue }
+ -re "^\r?\n" { exp_continue }
+ eof { verbose $expect_out(buffer) 1 }
+ timeout {
+ fail "$testname: timeout waiting for client $ccname to exit"
+ }
+ }
+ wait_client $testname $ccname $id $status
+}
+
+
+proc wait_client {testname ccname id status} {
+ global env
+ global kill
+ global kdestroy
+ global wait_error_index wait_errno_index wait_status_index
+
+ verbose "$testname: waiting for client $ccname" 1
+
+ set ret [wait -i $id]
+ if {[lindex $ret $wait_error_index] == -1} {
+ fail \
+ "$testname: wait $ccname returned error [lindex $ret $wait_errno_index]"
+ } else {
+ if {[lindex $ret $wait_status_index] == $status} {
+ pass "$testname: client $ccname"
+ } else {
+ fail "$testname: client $ccname: unexpected return status [lindex $ret $wait_status_index], should be $status."
+ }
+ }
+
+ set env(KRB5CCNAME) FILE:[pwd]/krb5cc_rpc_test_$ccname
+ if {[catch "exec $kdestroy -5"] != 0} {
+ perror "$testname: cannot destroy client $ccname ccache"
+ }
+
+ unset env(KRB5CCNAME)
+}
diff --git a/src/lib/rpc/unit-test/rpc_test.0/expire.exp b/src/lib/rpc/unit-test/rpc_test.0/expire.exp
new file mode 100644
index 000000000000..e19cca0ef613
--- /dev/null
+++ b/src/lib/rpc/unit-test/rpc_test.0/expire.exp
@@ -0,0 +1,49 @@
+set timeout 40
+
+load_lib "helpers.exp"
+
+global server_started
+
+proc expired {} {
+ global spawn_id server_id
+
+ start_client expired expired testuser notathena -1m 100
+ eof_client expired expired $spawn_id 2
+
+ expect {
+ -i $server_id
+ -re "rpc_test server: Authen.*failed:.*credential.*expired" { pass "expired" }
+ timeout { fail "expired: timeout waiting for expired creds error" }
+ }
+
+ flush_server
+}
+
+# This test doesn't work after #6948, because the client won't try to
+# authenticate using an expired TGT.
+#if { $server_started } {expired }
+
+proc overlap {} {
+ global spawn_id
+
+ start_client expire 1 testuser notathena 20m 100
+ set client1_id $spawn_id
+ flush_server
+
+ start_client expire 2 testuser notathena 40m 300
+ set client2_id $spawn_id
+ flush_server
+
+ start_client expire 3 testuser notathena 60m 500
+ set client3_id $spawn_id
+ flush_server
+
+ eof_client expire 1 $client1_id 0
+ eof_client expire 2 $client2_id 0
+ eof_client expire 3 $client3_id 0
+
+ flush_server
+}
+if { $server_started } {overlap}
+
+
diff --git a/src/lib/rpc/unit-test/rpc_test.0/fullrun.exp b/src/lib/rpc/unit-test/rpc_test.0/fullrun.exp
new file mode 100644
index 000000000000..73083de1f14d
--- /dev/null
+++ b/src/lib/rpc/unit-test/rpc_test.0/fullrun.exp
@@ -0,0 +1,91 @@
+set timeout 120
+
+load_lib "helpers.exp"
+
+global spawn_id
+global server_id
+global server_started
+
+if { !$server_started } {return}
+
+# Start the client and do a full run
+start_client "full run" fullrun testuser notathena 8h 1026
+set client_id $spawn_id
+
+#
+# test: did we get 11 dots?
+#
+verbose "Starting RPC echo test. This will take about 50 seconds.\n"
+
+set ver_line "rpc_test server: bad verifier\[^\r\n\]*\[\r\n]+"
+
+set dots 0
+set server_lines 0
+while {1} {
+ expect {
+ -i $server_id
+ -re $ver_line {
+ verbose "Got line from server."
+ incr server_lines
+ }
+ default {
+ exp_continue
+ }
+
+ -i $client_id
+ . {
+ incr dots
+ verbose "$expect_out(buffer)" 1
+ if ($dots==11) { break }
+ }
+ eof {
+ #
+ # test: was the exit status right?
+ #
+ wait_client "full run" fullrun $client_id 0
+ break
+ }
+
+ timeout {
+ verbose "Timeout waiting for dot\n" 1
+ fail "full run: timeout waiting for dot"
+ break
+ }
+ }
+}
+if {$dots==11} {
+ pass "fullrun: echo test"
+} else {
+ fail "fullrun: echo test: expected 11 dots, got $dots"
+}
+
+#
+# test: server logged four bad verifiers?
+#
+verbose "full run: checking server output"
+
+# Small timeout, since the server should have already printed everything
+set timeout 5
+
+while {$server_lines < 4} {
+ expect {
+ -i $server_id
+ -re $ver_line {
+ incr server_lines
+ }
+ -re ".+\r\n" {
+ verbose "Unexpected server output: $expect_out(buffer)"
+ }
+ default {
+ break
+ }
+ }
+}
+
+if {$server_lines == 4} {
+ pass "fullrun: bad verifiers"
+} else {
+ fail "fullrun: expected four bad verifiers, got $server_lines"
+}
+
+flush_server
diff --git a/src/lib/rpc/unit-test/rpc_test.0/gsserr.exp b/src/lib/rpc/unit-test/rpc_test.0/gsserr.exp
new file mode 100644
index 000000000000..005971989630
--- /dev/null
+++ b/src/lib/rpc/unit-test/rpc_test.0/gsserr.exp
@@ -0,0 +1,30 @@
+set timeout 30
+
+load_lib "helpers.exp"
+
+global spawn_id
+global server_id
+global server_started
+global hostname
+
+if { !$server_started } {return}
+
+start_client "gss err" gsserr testuser notathena 8h 1026 notserver@$hostname
+
+eof_client "gss err" gsserr $spawn_id 2
+
+#
+# test: server logged an authentication attempted failed?
+#
+verbose "gss err: checking server output"
+
+expect {
+ -i $server_id
+ -re "rpc_test server: Authent.*failed: .* not found in keytab" {
+ pass "gss err: server logged auth error"
+ }
+ eof { fail "gss err: server exited" }
+ timeout { fail "gss err: timeout waiting for server output" }
+}
+
+flush_server
diff --git a/src/lib/rpc/unit-test/rpc_test.h b/src/lib/rpc/unit-test/rpc_test.h
new file mode 100644
index 000000000000..2ad039955b25
--- /dev/null
+++ b/src/lib/rpc/unit-test/rpc_test.h
@@ -0,0 +1,13 @@
+#ifndef _RPC_TEST_H_RPCGEN
+#define _RPC_TEST_H_RPCGEN
+
+#include <gssrpc/rpc.h>
+
+#define RPC_TEST_PROG ((unsigned long)(1000001))
+#define RPC_TEST_VERS_1 ((unsigned long)(1))
+#define RPC_TEST_ECHO ((unsigned long)(1))
+extern char ** rpc_test_echo_1_svc(char **, struct svc_req *);
+extern char ** rpc_test_echo_1(char **, CLIENT *);
+extern void rpc_test_prog_1_svc(struct svc_req *, SVCXPRT *);
+
+#endif /* !_RPC_TEST_H_RPCGEN */
diff --git a/src/lib/rpc/unit-test/rpc_test.x b/src/lib/rpc/unit-test/rpc_test.x
new file mode 100644
index 000000000000..e9ae27c97950
--- /dev/null
+++ b/src/lib/rpc/unit-test/rpc_test.x
@@ -0,0 +1,30 @@
+/*
+ * Copyright 1993 OpenVision Technologies, Inc., All Rights Reserved.
+ *
+ * $Id$
+ * $Source$
+ *
+ * $Log$
+ * Revision 1.2 1996/07/22 20:41:42 marc
+ * this commit includes all the changes on the OV_9510_INTEGRATION and
+ * OV_MERGE branches. This includes, but is not limited to, the new openvision
+ * admin system, and major changes to gssapi to add functionality, and bring
+ * the implementation in line with rfc1964. before committing, the
+ * code was built and tested for netbsd and solaris.
+ *
+ * Revision 1.1.4.1 1996/07/18 04:20:04 marc
+ * merged in changes from OV_9510_BP to OV_9510_FINAL1
+ *
+# Revision 1.1.2.1 1996/06/20 23:42:06 marc
+# File added to the repository on a branch
+#
+# Revision 1.1 1993/11/03 23:53:58 bjaspan
+# Initial revision
+#
+ */
+
+program RPC_TEST_PROG {
+ version RPC_TEST_VERS_1 {
+ string RPC_TEST_ECHO(string) = 1;
+ } = 1;
+} = 1000001;
diff --git a/src/lib/rpc/unit-test/rpc_test_clnt.c b/src/lib/rpc/unit-test/rpc_test_clnt.c
new file mode 100644
index 000000000000..4e4a18a72019
--- /dev/null
+++ b/src/lib/rpc/unit-test/rpc_test_clnt.c
@@ -0,0 +1,22 @@
+#include "rpc_test.h"
+#include <string.h>
+
+/* Default timeout can be changed using clnt_control() */
+static struct timeval TIMEOUT = { 25, 0 };
+
+char **
+rpc_test_echo_1(argp, clnt)
+ char **argp;
+ CLIENT *clnt;
+{
+ static char *clnt_res;
+
+ memset(&clnt_res, 0, sizeof (clnt_res));
+ if (clnt_call(clnt, RPC_TEST_ECHO,
+ (xdrproc_t) xdr_wrapstring, (caddr_t) argp,
+ (xdrproc_t) xdr_wrapstring, (caddr_t) &clnt_res,
+ TIMEOUT) != RPC_SUCCESS) {
+ return (NULL);
+ }
+ return (&clnt_res);
+}
diff --git a/src/lib/rpc/unit-test/rpc_test_setup.sh b/src/lib/rpc/unit-test/rpc_test_setup.sh
new file mode 100755
index 000000000000..968f52a67045
--- /dev/null
+++ b/src/lib/rpc/unit-test/rpc_test_setup.sh
@@ -0,0 +1,59 @@
+#!/bin/sh
+#
+# This script performs additional setup for the RPC unit test. It
+# assumes that gmake has put TOP and RPC_TEST_SRVTAB into the
+# environment.
+#
+# $Id$
+# $Source$
+
+DUMMY=${TESTDIR=$TOP/testing}
+DUMMY=${CLNTTCL=$TESTDIR/util/kadm5_clnt_tcl}
+DUMMY=${TCLUTIL=$TESTDIR/tcl/util.t}; export TCLUTIL
+DUMMY=${MAKE_KEYTAB=$TESTDIR/scripts/make-host-keytab.pl}
+
+if $VERBOSE; then
+ REDIRECT=
+else
+ REDIRECT='>/dev/null'
+fi
+
+PATH=$TOP/install/admin:$PATH; export PATH
+
+CANON_HOST=`$QUALNAME`
+export CANON_HOST
+
+cat - > /tmp/rpc_test_setup$$ <<\EOF
+source $env(TCLUTIL)
+set h $env(CANON_HOST)
+puts stdout [kadm5_init admin admin $KADM5_ADMIN_SERVICE null $KADM5_STRUCT_VERSION $KADM5_API_VERSION_2 server_handle]
+if ![info exists server_handle] { exit 1 }
+puts stdout [kadm5_create_principal $server_handle [simple_principal server/$h] {KADM5_PRINCIPAL} admin]
+puts stdout [kadm5_randkey_principal $server_handle server/$h key null]
+puts stdout [kadm5_create_principal $server_handle [simple_principal notserver/$h] {KADM5_PRINCIPAL} admin]
+puts stdout [kadm5_randkey_principal $server_handle notserver/$h key null]
+puts stdout [kadm5_destroy $server_handle]
+EOF
+eval "$CLNTTCL $REDIRECT < /tmp/rpc_test_setup$$"
+if test $? != 0 ; then
+ rm /tmp/rpc_test_setup$$
+ echo 1>&2 error setting up database for tests
+ exit 1
+fi
+rm /tmp/rpc_test_setup$$
+
+rm -f $RPC_TEST_SRVTAB
+
+eval $MAKE_KEYTAB -princ server/$CANON_HOST $RPC_TEST_SRVTAB $REDIRECT
+
+# grep -s "$CANON_HOST SECURE-TEST.OV.COM" /etc/krb.realms
+# if [ $? != 0 ]; then
+# eval echo \"Adding \$CANON_HOST SECURE-TEST.OV.COM to /etc/krb.realms\" $REDIRECT
+# ed /etc/krb.realms <<EOF >/dev/null
+# 1i
+# $CANON_HOST SECURE-TEST.OV.COM
+# .
+# w
+# q
+# EOF
+# fi
diff --git a/src/lib/rpc/unit-test/rpc_test_svc.c b/src/lib/rpc/unit-test/rpc_test_svc.c
new file mode 100644
index 000000000000..88939f0a9538
--- /dev/null
+++ b/src/lib/rpc/unit-test/rpc_test_svc.c
@@ -0,0 +1,67 @@
+#include "rpc_test.h"
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h> /* getenv, exit */
+#include <sys/types.h>
+#include <syslog.h>
+
+/* States a server can be in wrt request */
+
+#define _IDLE 0
+#define _SERVED 1
+
+static int _rpcsvcstate = _IDLE; /* Set when a request is serviced */
+static int _rpcsvccount = 0; /* Number of requests being serviced */
+
+void
+rpc_test_prog_1_svc(rqstp, transp)
+ struct svc_req *rqstp;
+ register SVCXPRT *transp;
+{
+ union {
+ char *rpc_test_echo_1_arg;
+ } argument;
+ char *result;
+ bool_t (*xdr_argument)(), (*xdr_result)();
+ char *(*local)();
+
+ _rpcsvccount++;
+ switch (rqstp->rq_proc) {
+ case NULLPROC:
+ (void) svc_sendreply(transp, xdr_void,
+ (char *)NULL);
+ _rpcsvccount--;
+ _rpcsvcstate = _SERVED;
+ return;
+
+ case RPC_TEST_ECHO:
+ xdr_argument = xdr_wrapstring;
+ xdr_result = xdr_wrapstring;
+ local = (char *(*)()) rpc_test_echo_1_svc;
+ break;
+
+ default:
+ svcerr_noproc(transp);
+ _rpcsvccount--;
+ _rpcsvcstate = _SERVED;
+ return;
+ }
+ (void) memset(&argument, 0, sizeof (argument));
+ if (!svc_getargs(transp, xdr_argument, &argument)) {
+ svcerr_decode(transp);
+ _rpcsvccount--;
+ _rpcsvcstate = _SERVED;
+ return;
+ }
+ result = (*local)(&argument, rqstp);
+ if (result != NULL && !svc_sendreply(transp, xdr_result, result)) {
+ svcerr_systemerr(transp);
+ }
+ if (!svc_freeargs(transp, xdr_argument, &argument)) {
+ syslog(LOG_ERR, "unable to free arguments");
+ exit(1);
+ }
+ _rpcsvccount--;
+ _rpcsvcstate = _SERVED;
+ return;
+}
diff --git a/src/lib/rpc/unit-test/server.c b/src/lib/rpc/unit-test/server.c
new file mode 100644
index 000000000000..745155805628
--- /dev/null
+++ b/src/lib/rpc/unit-test/server.c
@@ -0,0 +1,259 @@
+/*
+ * Copyright 1993 OpenVision Technologies, Inc., All Rights Reserved.
+ *
+ * $Id$
+ * $Source$
+ */
+
+#include "k5-platform.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include "autoconf.h"
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#include <string.h>
+#include <signal.h>
+#include <gssrpc/rpc.h>
+#include <gssrpc/pmap_clnt.h>
+#include <arpa/inet.h> /* inet_ntoa */
+#include <gssapi/gssapi.h>
+#include <gssapi/gssapi_generic.h>
+#include <gssrpc/auth_gssapi.h>
+#include <sys/param.h> /* MAXHOSTNAMELEN */
+#include "rpc_test.h"
+
+extern int svc_debug_gssapi, misc_debug_gssapi;
+
+void rpc_test_badauth(OM_uint32 major, OM_uint32 minor,
+ struct sockaddr_in *addr, caddr_t data);
+void log_miscerr(struct svc_req *rqst, struct rpc_msg *msg, char
+ *error, char *data);
+void log_badauth_display_status(OM_uint32 major, OM_uint32 minor);
+void log_badauth_display_status_1(OM_uint32 code, int type, int rec);
+static void rpc_test_badverf(gss_name_t client, gss_name_t server,
+ struct svc_req *rqst, struct rpc_msg *msg,
+ caddr_t data);
+
+#ifndef SERVICE_NAME
+#define SERVICE_NAME "server"
+#endif
+
+static void usage()
+{
+ fprintf(stderr, "Usage: server {-t|-u} [svc-debug] [misc-debug]\n");
+ exit(1);
+}
+
+#ifdef POSIX_SIGNALS
+static void handlesig(int dummy)
+#else
+static void handlesig(void)
+#endif
+{
+ exit(0);
+}
+
+int
+main(int argc, char **argv)
+{
+ int c, prot;
+ auth_gssapi_name names[2];
+ register SVCXPRT *transp;
+ extern int optind;
+#ifdef POSIX_SIGNALS
+ struct sigaction sa;
+#endif
+
+ names[0].name = SERVICE_NAME;
+ names[0].type = (gss_OID) gss_nt_service_name;
+ names[1].name = 0;
+ names[1].type = 0;
+
+ prot = 0;
+ while ((c = getopt(argc, argv, "tu")) != -1) {
+ switch (c) {
+ case 't':
+ prot = IPPROTO_TCP;
+ break;
+ case 'u':
+ prot = IPPROTO_UDP;
+ break;
+ case '?':
+ usage();
+ break;
+ }
+ }
+ if (prot == 0)
+ usage();
+
+ argv += optind;
+ argc -= optind;
+
+ switch (argc) {
+ case 2:
+ misc_debug_gssapi = atoi(argv[1]);
+ case 1:
+ svc_debug_gssapi = atoi(argv[0]);
+ case 0:
+ break;
+ default:
+ usage();
+ exit(1);
+ }
+
+ (void) pmap_unset(RPC_TEST_PROG, RPC_TEST_VERS_1);
+
+ if (prot == IPPROTO_TCP)
+ transp = svctcp_create(RPC_ANYSOCK, 0, 0);
+ else
+ transp = svcudp_create(RPC_ANYSOCK);
+ if (transp == NULL) {
+ fprintf(stderr, "cannot create tcp service.");
+ exit(1);
+ }
+ if (!svc_register(transp, RPC_TEST_PROG, RPC_TEST_VERS_1,
+ rpc_test_prog_1_svc, 0)) {
+ fprintf(stderr,
+ "unable to register (RPC_TEST_PROG, RPC_TEST_VERS_1, %s).",
+ prot == IPPROTO_TCP ? "tcp" : "udp");
+ exit(1);
+ }
+ printf("port: %d\n", (int)transp->xp_port);
+
+ if (svcauth_gssapi_set_names(names, 0) == FALSE) {
+ fprintf(stderr, "unable to set gssapi names\n");
+ exit(1);
+ }
+
+ svcauth_gssapi_set_log_badauth_func(rpc_test_badauth, NULL);
+ svcauth_gssapi_set_log_badverf_func(rpc_test_badverf, NULL);
+ svcauth_gssapi_set_log_miscerr_func(log_miscerr, NULL);
+
+#ifdef POSIX_SIGNALS
+ (void) sigemptyset(&sa.sa_mask);
+ sa.sa_flags = 0;
+ sa.sa_handler = handlesig;
+ (void) sigaction(SIGHUP, &sa, NULL);
+ (void) sigaction(SIGINT, &sa, NULL);
+ (void) sigaction(SIGTERM, &sa, NULL);
+#else
+ signal(SIGHUP, handlesig);
+ signal(SIGINT, handlesig);
+ signal(SIGTERM, handlesig);
+#endif
+ printf("running\n");
+
+ svc_run();
+ fprintf(stderr, "svc_run returned");
+ exit(1);
+ /* NOTREACHED */
+}
+
+char **rpc_test_echo_1_svc(char **arg, struct svc_req *h)
+{
+ static char *res = NULL;
+
+ if (res)
+ free(res);
+ asprintf(&res, "Echo: %s", *arg);
+ return &res;
+}
+
+static void rpc_test_badverf(gss_name_t client, gss_name_t server,
+ struct svc_req *rqst, struct rpc_msg *msg,
+ caddr_t data)
+{
+ OM_uint32 minor_stat;
+ gss_OID type;
+ gss_buffer_desc client_name, server_name;
+
+ (void) gss_display_name(&minor_stat, client, &client_name, &type);
+ (void) gss_display_name(&minor_stat, server, &server_name, &type);
+
+ printf("rpc_test server: bad verifier from %.*s at %s:%d for %.*s\n",
+ (int) client_name.length, (char *) client_name.value,
+ inet_ntoa(rqst->rq_xprt->xp_raddr.sin_addr),
+ ntohs(rqst->rq_xprt->xp_raddr.sin_port),
+ (int) server_name.length, (char *) server_name.value);
+
+ (void) gss_release_buffer(&minor_stat, &client_name);
+ (void) gss_release_buffer(&minor_stat, &server_name);
+}
+
+/*
+ * Function: log_badauth
+ *
+ * Purpose: Callback from GSS-API Sun RPC for authentication
+ * failures/errors.
+ *
+ * Arguments:
+ * major (r) GSS-API major status
+ * minor (r) GSS-API minor status
+ * addr (r) originating address
+ * data (r) arbitrary data (NULL), not used
+ *
+ * Effects:
+ *
+ * Logs the GSS-API error to stdout.
+ */
+void rpc_test_badauth(OM_uint32 major, OM_uint32 minor,
+ struct sockaddr_in *addr, caddr_t data)
+{
+ char *a;
+
+ /* Authentication attempt failed: <IP address>, <GSS-API error */
+ /* strings> */
+
+ a = inet_ntoa(addr->sin_addr);
+
+ printf("rpc_test server: Authentication attempt failed: %s", a);
+ log_badauth_display_status(major, minor);
+ printf("\n");
+}
+
+void log_miscerr(struct svc_req *rqst, struct rpc_msg *msg,
+ char *error, char *data)
+{
+ char *a;
+
+ a = inet_ntoa(rqst->rq_xprt->xp_raddr.sin_addr);
+ printf("Miscellaneous RPC error: %s, %s\n", a, error);
+}
+
+void log_badauth_display_status(OM_uint32 major, OM_uint32 minor)
+{
+ log_badauth_display_status_1(major, GSS_C_GSS_CODE, 0);
+ log_badauth_display_status_1(minor, GSS_C_MECH_CODE, 0);
+}
+
+void log_badauth_display_status_1(OM_uint32 code, int type, int rec)
+{
+ OM_uint32 gssstat, minor_stat, msg_ctx;
+ gss_buffer_desc msg;
+
+ msg_ctx = 0;
+ while (1) {
+ gssstat = gss_display_status(&minor_stat, code,
+ type, GSS_C_NULL_OID,
+ &msg_ctx, &msg);
+ if (gssstat != GSS_S_COMPLETE) {
+ if (!rec) {
+ log_badauth_display_status_1(gssstat,GSS_C_GSS_CODE,1);
+ log_badauth_display_status_1(minor_stat,
+ GSS_C_MECH_CODE, 1);
+ } else
+ printf("GSS-API authentication error %.*s: "
+ "recursive failure!\n", (int) msg.length,
+ (char *)msg.value);
+ return;
+ }
+
+ printf(", %.*s", (int) msg.length, (char *)msg.value);
+ (void) gss_release_buffer(&minor_stat, &msg);
+
+ if (!msg_ctx)
+ break;
+ }
+}