/* Copyright (C) 2011 the GSS-PROXY contributors, see COPYING for license */ #include "config.h" #include #include #include #include #include #include #include #include #include #include #include #include #include "src/gp_debug.h" #include "src/gp_log.h" #include "popt.h" #include #include "t_utils.h" #define _(STRING) gettext(STRING) static const char *actor = ""; const char **argv; static int gptest_inq_context(gss_ctx_id_t ctx) { gss_name_t targ_name = GSS_C_NO_NAME; gss_name_t src_name = GSS_C_NO_NAME; OM_uint32 lifetime_rec = -1; OM_uint32 ctx_flags = -1; gss_OID mech_type = GSS_C_NO_OID; int locally_initiated = -1; int open = -1; gss_buffer_desc sname = {0}; gss_buffer_desc tname = {0}; gss_buffer_desc mechstr = {0}; OM_uint32 time_rec = -1; OM_uint32 maj, min; maj = gss_inquire_context(&min, ctx, &src_name, &targ_name, &lifetime_rec, &mech_type, &ctx_flags, &locally_initiated, &open); if (maj == GSS_S_COMPLETE) { gss_OID type = GSS_C_NO_OID; maj = gss_display_name(&min, src_name, &sname, &type); if (maj != GSS_S_COMPLETE) { goto done; } maj = gss_display_name(&min, targ_name, &tname, &type); if (maj != GSS_S_COMPLETE) { goto done; } maj = gss_oid_to_str(&min, mech_type, &mechstr); if (maj != GSS_S_COMPLETE) { goto done; } DEBUG("Context properties: " "[ s:%s, t:%s, m:%s, l:%d, f:%d, i:%d, o:%d ]\n", (char *)sname.value, (char *)tname.value, (char *)mechstr.value, (int)lifetime_rec, (int)ctx_flags, locally_initiated, open); } maj = gss_context_time(&min, ctx, &time_rec); if (maj) { DEBUG("gss_context_time failed\n"); gp_log_failure(GSS_C_NO_OID, maj, min); goto done; } DEBUG("Context validity: %d sec.\n", time_rec); done: (void)gss_release_buffer(&min, &sname); (void)gss_release_buffer(&min, &tname); (void)gss_release_buffer(&min, &mechstr); return maj; } #define PROXY_LOCAL_ONLY 0 #define PROXY_LOCAL_FIRST 1 #define PROXY_REMOTE_FIRST 2 #define PROXY_REMOTE_ONLY 3 #define GSSPROXY_BEHAVIOR_ENV "GSSPROXY_BEHAVIOR" static const char *lookup_gssproxy_behavior(int proxy_mode) { switch(proxy_mode) { case PROXY_LOCAL_ONLY: return "LOCAL_ONLY"; case PROXY_LOCAL_FIRST: return "LOCAL_FIRST"; case PROXY_REMOTE_FIRST: return "REMOTE_FIRST"; case PROXY_REMOTE_ONLY: return "REMOTE_ONLY"; default: break; } return NULL; } static int setup_gssproxy_behavior(int proxy_mode) { const char *env; if (getenv(GSSPROXY_BEHAVIOR_ENV)) { return 0; } env = lookup_gssproxy_behavior(proxy_mode); if (env == NULL) { return -1; } return setenv(GSSPROXY_BEHAVIOR_ENV, env, 0); } struct aproc { int proxy_type; int *cli_pipe; int *srv_pipe; char *target; }; void run_client(struct aproc *data) { uint32_t ret_maj; uint32_t ret_min; char buffer[MAX_RPC_SIZE]; uint32_t buflen; gss_buffer_desc target_buf; gss_buffer_desc in_token = GSS_C_EMPTY_BUFFER; gss_buffer_desc out_token = GSS_C_EMPTY_BUFFER; gss_name_t name = GSS_C_NO_NAME; gss_ctx_id_t ctx = GSS_C_NO_CONTEXT; gss_cred_id_t cred_handle = GSS_C_NO_CREDENTIAL; gss_buffer_desc msg_buf = GSS_C_EMPTY_BUFFER; char *message = "SECRET"; int ret = -1; gss_iov_buffer_desc iov[2] = { { 0, { 0, NULL } }, { 0, { 0, NULL } } }; int sealed; uint32_t max_size = 0; ret = setup_gssproxy_behavior(data->proxy_type); if (ret) { goto done; } DEBUG("%s behavior: %s\n", actor, getenv(GSSPROXY_BEHAVIOR_ENV)); target_buf.value = (void *)data->target; target_buf.length = strlen(data->target) + 1; ret_maj = gss_import_name(&ret_min, &target_buf, GSS_C_NT_HOSTBASED_SERVICE, &name); if (ret_maj) { DEBUG("gss_import_name failed\n"); goto done; } do { ret_maj = gss_init_sec_context(&ret_min, cred_handle, &ctx, name, GSS_C_NO_OID, GSS_C_MUTUAL_FLAG | GSS_C_REPLAY_FLAG, 0, GSS_C_NO_CHANNEL_BINDINGS, &in_token, NULL, &out_token, NULL, NULL); if (ret_maj != GSS_S_COMPLETE && ret_maj != GSS_S_CONTINUE_NEEDED) { DEBUG("gss_init_sec_context() failed with: %d\n", ret_maj); gp_log_failure(GSS_C_NO_OID, ret_maj, ret_min); goto done; } if (out_token.length != 0) { /* send to server */ ret = t_send_buffer(data->srv_pipe[1], out_token.value, out_token.length); if (ret) { DEBUG("Failed to send data to server!\n"); goto done; } gss_release_buffer(&ret_min, &out_token); } if (!ctx) { DEBUG("No context!\n"); goto done; } if (ret_maj == GSS_S_CONTINUE_NEEDED) { /* and wait for reply */ ret = t_recv_buffer(data->cli_pipe[0], buffer, &buflen); if (ret) { DEBUG("Failed to receive data from server!\n"); goto done; } in_token.value = buffer; in_token.length = buflen; } } while (ret_maj == GSS_S_CONTINUE_NEEDED); ret = gptest_inq_context(ctx); if (ret) { DEBUG("Failed to inquire context!\n"); goto done; } /* test gss_wrap_size_limit */ ret_maj = gss_wrap_size_limit(&ret_min, ctx, 1, /* conf_req */ GSS_C_QOP_DEFAULT, /* qop_req */ 4096, /* size_req */ &max_size); if (ret_maj) { DEBUG("gss_wrap_size_limit failed.\n"); gp_log_failure(GSS_C_NO_OID, ret_maj, ret_min); goto done; } /* test encryption */ msg_buf.length = strlen(message) + 1; msg_buf.value = (void *)message; ret_maj = gss_wrap(&ret_min, ctx, 1, 0, &msg_buf, NULL, &out_token); if (ret_maj != GSS_S_COMPLETE) { DEBUG("Failed to wrap message.\n"); gp_log_failure(GSS_C_NO_OID, ret_maj, ret_min); goto done; } ret = t_send_buffer(data->srv_pipe[1], out_token.value, out_token.length); if (ret) { DEBUG("Failed to send data to server!\n"); goto done; } gss_release_buffer(&ret_min, &out_token); ret = t_recv_buffer(data->cli_pipe[0], buffer, &buflen); if (ret) { DEBUG("Failed to receive data from server!\n"); goto done; } msg_buf.value = (void *)buffer; msg_buf.length = buflen; buffer[buflen] = '\0'; in_token.value = (void *)&buffer[buflen + 1]; ret = t_recv_buffer(data->cli_pipe[0], in_token.value, &buflen); if (ret) { DEBUG("Failed to receive data from server!\n"); goto done; } in_token.length = buflen; ret_maj = gss_verify_mic(&ret_min, ctx, &msg_buf, &in_token, NULL); if (ret_maj != GSS_S_COMPLETE) { DEBUG("Failed to verify message (%s).\n", buffer); gp_log_failure(GSS_C_NO_OID, ret_maj, ret_min); goto done; } fprintf(stdout, "Client, RECV: [%s]\n", buffer); /* test gss_wrap_iov_length */ iov[0].type = GSS_IOV_BUFFER_TYPE_HEADER; iov[0].buffer.value = NULL; iov[0].buffer.length = 0; iov[1].type = GSS_IOV_BUFFER_TYPE_DATA; iov[1].buffer.value = NULL; iov[1].buffer.length = msg_buf.length; ret_maj = gss_wrap_iov_length(&ret_min, ctx, 1, /* seal */ GSS_C_QOP_DEFAULT, &sealed, iov, 2); if (ret_maj != GSS_S_COMPLETE) { DEBUG("gss_wrap_iov_length failed\n"); gp_log_failure(GSS_C_NO_OID, ret_maj, ret_min); goto done; } buflen = iov[0].buffer.length; /* test gss_wrap_iov */ iov[0].type = GSS_IOV_BUFFER_TYPE_HEADER; iov[0].buffer.length = buflen; iov[0].buffer.value = malloc(iov[0].buffer.length); if (!iov[0].buffer.value) { DEBUG("Out of memory\n"); goto done; } iov[1].type = GSS_IOV_BUFFER_TYPE_DATA; iov[1].buffer.value = msg_buf.value; iov[1].buffer.length = msg_buf.length; ret_maj = gss_wrap_iov(&ret_min, ctx, 1, /* seal */ GSS_C_QOP_DEFAULT, &sealed, iov, 2); if (ret_maj != GSS_S_COMPLETE) { DEBUG("gss_wrap_iov failed.\n"); gp_log_failure(GSS_C_NO_OID, ret_maj, ret_min); goto done; } ret = t_send_buffer(data->srv_pipe[1], iov[0].buffer.value, iov[0].buffer.length); if (ret) { DEBUG("Failed to send data to server!\n"); goto done; } ret = t_send_buffer(data->srv_pipe[1], iov[1].buffer.value, iov[1].buffer.length); if (ret) { DEBUG("Failed to send data to server!\n"); goto done; } ret_maj = gss_delete_sec_context(&ret_min, &ctx, &out_token); if (ret_maj != GSS_S_COMPLETE) { DEBUG("Failed to delete context!\n"); gp_log_failure(GSS_C_NO_OID, ret_maj, ret_min); goto done; } ret = t_send_buffer(data->srv_pipe[1], out_token.value, out_token.length); if (ret) { DEBUG("Failed to send data to server!\n"); goto done; } gss_release_buffer(&ret_min, &out_token); DEBUG("Success!\n"); done: free(iov[0].buffer.value); gss_release_name(&ret_min, &name); gss_release_buffer(&ret_min, &out_token); close(data->cli_pipe[0]); close(data->srv_pipe[1]); exit(ret); } void run_server(struct aproc *data) { char buffer[MAX_RPC_SIZE]; uint32_t buflen; gss_buffer_desc in_token = GSS_C_EMPTY_BUFFER; uint32_t ret_maj; uint32_t ret_min; gss_ctx_id_t context_handle = GSS_C_NO_CONTEXT; gss_cred_id_t cred_handle = GSS_C_NO_CREDENTIAL; gss_name_t src_name; gss_buffer_desc out_token = GSS_C_EMPTY_BUFFER; gss_cred_id_t deleg_cred = GSS_C_NO_CREDENTIAL; gss_OID_set mech_set = GSS_C_NO_OID_SET; gss_OID_set mech_names = GSS_C_NO_OID_SET; gss_OID_set mech_types = GSS_C_NO_OID_SET; gss_OID_set mech_attrs = GSS_C_NO_OID_SET; gss_OID_set known_mech_attrs = GSS_C_NO_OID_SET; gss_buffer_desc sasl_mech_name = GSS_C_EMPTY_BUFFER; gss_buffer_desc mech_name = GSS_C_EMPTY_BUFFER; gss_buffer_desc mech_description = GSS_C_EMPTY_BUFFER; gss_buffer_desc name = GSS_C_EMPTY_BUFFER; gss_buffer_desc short_desc = GSS_C_EMPTY_BUFFER; gss_buffer_desc long_desc = GSS_C_EMPTY_BUFFER; gss_OID_set mechs = GSS_C_NO_OID_SET; gss_buffer_desc const_buf; gss_name_t target_name = GSS_C_NO_NAME; gss_name_t canon_name = GSS_C_NO_NAME; gss_buffer_desc out_name_buf = GSS_C_EMPTY_BUFFER; gss_OID out_name_type = GSS_C_NO_OID; gss_buffer_desc exported_name = GSS_C_EMPTY_BUFFER; const char *message = "This message is authentic!"; int ret = -1; gss_iov_buffer_desc iov[2]; int sealed; ret = setup_gssproxy_behavior(data->proxy_type); if (ret) { goto done; } DEBUG("%s behavior: %s\n", actor, getenv(GSSPROXY_BEHAVIOR_ENV)); const_buf.value = (void *)data->target; const_buf.length = strlen(data->target) + 1; /* import name family functions tests */ ret_maj = gss_import_name(&ret_min, &const_buf, GSS_C_NT_HOSTBASED_SERVICE, &target_name); if (ret_maj) { DEBUG("gssproxy returned an error: %d\n", ret_maj); gp_log_failure(GSS_C_NO_OID, ret_maj, ret_min); goto done; } ret_maj = gss_canonicalize_name(&ret_min, target_name, gss_mech_krb5, &canon_name); if (ret_maj) { DEBUG("gssproxy returned an error: %d\n", ret_maj); gp_log_failure(GSS_C_NO_OID, ret_maj, ret_min); goto done; } ret_maj = gss_export_name(&ret_min, canon_name, &exported_name); if (ret_maj) { DEBUG("gss_export_name() failed with: %d\n", ret_maj); gp_log_failure(GSS_C_NO_OID, ret_maj, ret_min); goto done; } #if 0 /* disabled until gss_export_name_composite server-side is fixed */ gss_release_buffer(&ret_min, &exported_name); ret_maj = gss_export_name_composite(&ret_min, canon_name, &exported_name); if (ret_maj) { DEBUG("gss_export_name_composite() failed with: %d\n", ret_maj); gp_log_failure(GSS_C_NO_OID, ret_maj, ret_min); goto done; } #endif ret_maj = gss_display_name(&ret_min, canon_name, &out_name_buf, &out_name_type); if (ret_maj) { DEBUG("gssproxy returned an error: %d\n", ret_maj); gp_log_failure(GSS_C_NO_OID, ret_maj, ret_min); goto done; } fprintf(stdout, "Acquiring for: %s\n", (char *)out_name_buf.value); /* indicate mechs family functions tests */ ret_maj = gss_indicate_mechs(&ret_min, &mech_set); if (ret_maj) { DEBUG("gssproxy returned an error: %d\n", ret_maj); gp_log_failure(GSS_C_NO_OID, ret_maj, ret_min); goto done; } ret_maj = gss_inquire_names_for_mech(&ret_min, &mech_set->elements[0], &mech_names); if (ret_maj) { DEBUG("gssproxy returned an error: %d\n", ret_maj); gp_log_failure(&mech_set->elements[0], ret_maj, ret_min); goto done; } ret_maj = gss_inquire_attrs_for_mech(&ret_min, &mech_set->elements[0], &mech_attrs, &known_mech_attrs); if (ret_maj) { DEBUG("gssproxy returned an error: %d\n", ret_maj); gp_log_failure(&mech_set->elements[0], ret_maj, ret_min); goto done; } ret_maj = gss_inquire_saslname_for_mech(&ret_min, &mech_set->elements[0], &sasl_mech_name, &mech_name, &mech_description); if (ret_maj) { DEBUG("gssproxy returned an error: %d\n", ret_maj); gp_log_failure(&mech_set->elements[0], ret_maj, ret_min); goto done; } ret_maj = gss_display_mech_attr(&ret_min, &mech_attrs->elements[0], &name, &short_desc, &long_desc); if (ret_maj) { DEBUG("gssproxy returned an error: %d\n", ret_maj); gp_log_failure(GSS_C_NO_OID, ret_maj, ret_min); goto done; } ret_maj = gss_indicate_mechs_by_attrs(&ret_min, GSS_C_NO_OID_SET, GSS_C_NO_OID_SET, GSS_C_NO_OID_SET, &mechs); if (ret_maj) { DEBUG("gssproxy returned an error: %d\n", ret_maj); gp_log_failure(GSS_C_NO_OID, ret_maj, ret_min); goto done; } ret_maj = gss_inquire_mechs_for_name(&ret_min, target_name, &mech_types); if (ret_maj) { DEBUG("gssproxy returned an error: %d\n", ret_maj); gp_log_failure(GSS_C_NO_OID, ret_maj, ret_min); goto done; } ret_maj = gss_acquire_cred(&ret_min, GSS_C_NO_NAME, GSS_C_INDEFINITE, mech_set, GSS_C_ACCEPT, &cred_handle, NULL, NULL); if (ret_maj) { DEBUG("gssproxy returned an error: %d\n", ret_maj); gp_log_failure(GSS_C_NO_OID, ret_maj, ret_min); goto done; } ret = t_recv_buffer(data->srv_pipe[0], buffer, &buflen); if (ret) { DEBUG("Failed to get data from client!\n"); goto done; } in_token.value = buffer; in_token.length = buflen; ret_maj = gss_accept_sec_context(&ret_min, &context_handle, cred_handle, &in_token, GSS_C_NO_CHANNEL_BINDINGS, &src_name, NULL, &out_token, NULL, NULL, &deleg_cred); if (ret_maj) { DEBUG("gssproxy returned an error: %d\n", ret_maj); gp_log_failure(GSS_C_NO_OID, ret_maj, ret_min); goto done; } if (out_token.length) { ret = t_send_buffer(data->cli_pipe[1], out_token.value, out_token.length); if (ret) { DEBUG("Failed to send data to client!\n"); goto done; } } gss_release_buffer(&ret_min, &out_token); ret = gptest_inq_context(context_handle); if (ret) { DEBUG("Failed to inquire context!\n"); goto done; } ret = t_recv_buffer(data->srv_pipe[0], buffer, &buflen); if (ret) { DEBUG("Failed to get data from client!\n"); goto done; } in_token.value = buffer; in_token.length = buflen; ret_maj = gss_unwrap(&ret_min, context_handle, &in_token, &out_token, NULL, NULL); if (ret_maj != GSS_S_COMPLETE) { DEBUG("Failed to unwrap message.\n"); gp_log_failure(GSS_C_NO_OID, ret_maj, ret_min); goto done; } fprintf(stdout, "Server, RECV: %s\n", (char *)out_token.value); gss_release_buffer(&ret_min, &out_token); in_token.value = message; in_token.length = strlen(message); ret_maj = gss_get_mic(&ret_min, context_handle, 0, &in_token, &out_token); if (ret_maj != GSS_S_COMPLETE) { DEBUG("Failed to protect message.\n"); gp_log_failure(GSS_C_NO_OID, ret_maj, ret_min); goto done; } ret = t_send_buffer(data->cli_pipe[1], in_token.value, in_token.length); if (ret) { DEBUG("Failed to send data to client!\n"); goto done; } ret = t_send_buffer(data->cli_pipe[1], out_token.value, out_token.length); if (ret) { DEBUG("Failed to send data to client!\n"); goto done; } gss_release_buffer(&ret_min, &out_token); /* test gss_unwrap_iov */ ret = t_recv_buffer(data->srv_pipe[0], buffer, &buflen); if (ret) { DEBUG("Failed to get data from client!\n"); goto done; } iov[0].type = GSS_IOV_BUFFER_TYPE_HEADER; iov[0].buffer.value = buffer; iov[0].buffer.length = buflen; ret = t_recv_buffer(data->srv_pipe[0], buffer+buflen, &buflen); if (ret) { DEBUG("Failed to get data from client!\n"); goto done; } iov[1].type = GSS_IOV_BUFFER_TYPE_DATA; iov[1].buffer.value = buffer+iov[0].buffer.length; iov[1].buffer.length = buflen; ret_maj = gss_unwrap_iov(&ret_min, context_handle, &sealed, NULL, /* qop_state */ iov, 2); if (ret_maj != GSS_S_COMPLETE) { DEBUG("gss_unwrap_iov failed\n"); gp_log_failure(GSS_C_NO_OID, ret_maj, ret_min); goto done; } ret = t_recv_buffer(data->srv_pipe[0], buffer, &buflen); if (ret) { DEBUG("Failed to get data from client!\n"); goto done; } const_buf.value = buffer; const_buf.length = buflen; ret_maj = gss_process_context_token(&ret_min, context_handle, &const_buf); if (ret_maj != GSS_S_COMPLETE) { DEBUG("Failed to process context token.\n"); gp_log_failure(GSS_C_NO_OID, ret_maj, ret_min); goto done; } /* The follwing will cause the context to leak, but it is unavoidable until * gss_process_context_token() is fixed to at least NULL the internal * context in the union context. */ context_handle = GSS_C_NO_CONTEXT; DEBUG("Success!\n"); done: gss_release_name(&ret_min, &src_name); gss_release_buffer(&ret_min, &out_token); gss_release_cred(&ret_min, &deleg_cred); gss_delete_sec_context(&ret_min, &context_handle, GSS_C_NO_BUFFER); gss_release_oid_set(&ret_min, &mech_set); gss_release_oid_set(&ret_min, &mech_names); gss_release_oid_set(&ret_min, &mech_types); gss_release_oid_set(&ret_min, &mech_attrs); gss_release_oid_set(&ret_min, &known_mech_attrs); gss_release_buffer(&ret_min, &sasl_mech_name); gss_release_buffer(&ret_min, &mech_name); gss_release_buffer(&ret_min, &mech_description); gss_release_buffer(&ret_min, &name); gss_release_buffer(&ret_min, &short_desc); gss_release_buffer(&ret_min, &long_desc); gss_release_oid_set(&ret_min, &mechs); gss_release_name(&ret_min, &target_name); gss_release_name(&ret_min, &canon_name); gss_release_buffer(&ret_min, &out_name_buf); gss_release_oid(&ret_min, &out_name_type); gss_release_buffer(&ret_min, &exported_name); close(data->srv_pipe[0]); close(data->cli_pipe[1]); exit(ret); } static int run_cli_srv_test(int server_proxy_type, int client_proxy_type, char *target) { int srv_pipe[2]; int cli_pipe[2]; struct aproc client, server; pid_t cli, srv, w; int closewait; int options; int status; int ret; ret = pipe(srv_pipe); if (ret) { return -1; } ret = pipe(cli_pipe); if (ret) { return -1; } srv = -1; cli = -1; server.proxy_type = server_proxy_type; server.srv_pipe = srv_pipe; server.cli_pipe = cli_pipe; server.target = target; srv = fork(); switch (srv) { case -1: ret = -1; goto done; case 0: actor = "SERVER"; run_server(&server); exit(0); default: close(srv_pipe[0]); close(cli_pipe[1]); break; } client.proxy_type = client_proxy_type; client.srv_pipe = srv_pipe; client.cli_pipe = cli_pipe; client.target = target; cli = fork(); switch (cli) { case -1: ret = -1; goto done; case 0: actor = "CLIENT"; run_client(&client); exit(0); default: close(srv_pipe[1]); close(cli_pipe[0]); break; } closewait = -1; while (cli != -1 || srv != -1) { if (closewait < 0) { options = 0; } else { options = WNOHANG; } w = waitpid(-1, &status, options); if (w == cli) { cli = -1; } else if (w == srv) { srv = -1; } else { /* ? */ ret = -1; goto done; } /* wait 0.1s for max ten times for the other process to terminate, * then just exit */ if (closewait > 10) { ret = -1; goto done; } usleep(100000); closewait++; } ret = 0; done: if (cli > 0) { kill(cli, SIGKILL); } if (srv > 0) { kill(srv, SIGKILL); } return ret; } int main(int argc, const char *main_argv[]) { int opt; poptContext pc; int opt_version = 0; int opt_all = 0; char *opt_target = NULL; int ret, i, k; struct poptOption long_options[] = { POPT_AUTOHELP {"target", 't', POPT_ARG_STRING, &opt_target, 0, \ _("Specify the target name used for the tests"), NULL}, \ {"version", '\0', POPT_ARG_NONE, &opt_version, 0, \ _("Print version number and exit"), NULL }, \ {"all", '\0', POPT_ARG_NONE, &opt_all, 0, \ _("Test all gssproxy modes"), NULL }, \ POPT_TABLEEND }; argv = main_argv; pc = poptGetContext(argv[0], argc, argv, long_options, 0); while((opt = poptGetNextOpt(pc)) != -1) { switch(opt) { default: fprintf(stderr, "\nInvalid option %s: %s\n\n", poptBadOption(pc, 0), poptStrerror(opt)); poptPrintUsage(pc, stderr, 0); return 1; } } if (opt_version) { puts(VERSION""DISTRO_VERSION""PRERELEASE_VERSION); return 0; } if (opt_target == NULL) { fprintf(stderr, "Missing target!\n"); poptPrintUsage(pc, stderr, 0); return 1; } if (!opt_all) { return run_cli_srv_test(PROXY_LOCAL_ONLY, PROXY_LOCAL_ONLY, opt_target); } for (i=0; i<4; i++) { for (k=0; k<4; k++) { ret = run_cli_srv_test(i, k, opt_target); fprintf(stderr, "Running test with server proxy mode %s " "and client proxy mode %s %s\n", lookup_gssproxy_behavior(i), lookup_gssproxy_behavior(k), ret ? "failed" : "succeeded"); if (ret) { return ret; } } } return ret; }