int main(void) { struct kerberos_config *config; struct remctl *r; struct remctl_output *output; char *tmpdir, *confpath; FILE *conf; const char *test[] = { "test", "test", NULL }; /* Unless we have Kerberos available, we can't really do anything. */ config = kerberos_setup(TAP_KRB_NEEDS_KEYTAB); /* Write out our empty configuration file. */ tmpdir = test_tmpdir(); basprintf(&confpath, "%s/conf-empty", tmpdir); conf = fopen(confpath, "w"); if (conf == NULL) sysbail("cannot create %s", confpath); fclose(conf); /* Now we can start remctl with our temporary configuration file. */ remctld_start(config, "tmp/conf-empty", NULL); plan(7); /* Test that we get a valid UNKNOWN_COMMAND error. */ r = remctl_new(); ok(remctl_open(r, "localhost", 14373, config->principal), "remctl_open"); ok(remctl_command(r, test), "remctl_command"); output = remctl_output(r); ok(output != NULL, "first output token is not null"); if (output == NULL) ok_block(4, 0, "...and has correct content"); else { is_int(REMCTL_OUT_ERROR, output->type, "...and is an error"); is_int(15, output->length, "...and is right length"); if (output->data == NULL) ok(0, "...and has the right error message"); else ok(memcmp("Unknown command", output->data, 15) == 0, "...and has the right error message"); is_int(ERROR_UNKNOWN_COMMAND, output->error, "...and error number"); } remctl_close(r); unlink(confpath); free(confpath); test_tmpdir_free(tmpdir); return 0; }
int main(void) { struct kerberos_config *config; const char *err; struct remctl *r; struct remctl_output *output; const char *command[] = { "test", "env", "REMOTE_ADDR", NULL }; /* Set up Kerberos and remctld. */ config = kerberos_setup(TAP_KRB_NEEDS_KEYTAB); remctld_start(config, "data/conf-simple", (char *) 0); plan(10); /* Successful connection to 127.0.0.1. */ r = remctl_new(); ok(r != NULL, "remctl_new"); ok(remctl_set_source_ip(r, "127.0.0.1"), "remctl_set_source_ip"); ok(remctl_open(r, "127.0.0.1", 14373, config->principal), "remctl_open to 127.0.0.1"); ok(remctl_command(r, command), "remctl_command"); output = remctl_output(r); ok(output != NULL, "remctl_output"); if (output == NULL) ok_block(3, 0, "remctl_output failed"); else { is_int(REMCTL_OUT_OUTPUT, output->type, "output token"); ok(memcmp("127.0.0.1\n", output->data, strlen("127.0.0.1\n")) == 0, "correct IP address"); is_int(strlen("127.0.0.1\n"), output->length, "correct length"); } /* Failed connection to ::1. */ ok(!remctl_open(r, "::1", 14373, config->principal), "remctl_open to ::1"); err = remctl_error(r); diag("error: %s", err); ok(((strncmp("cannot connect to ::1 (port 14373): ", err, strlen("cannot connect to ::1 (port 14373): ")) == 0) || (strncmp("unknown host ::1: ", err, strlen("unknown host ::1: ")) == 0)), "failed with correct error"); remctl_close(r); return 0; }
int main(void) { struct kerberos_config *config; const char *cache; struct remctl *r; struct remctl_output *output; int status; const char *command[] = { "test", "test", NULL }; /* Set up Kerberos and remctld. */ config = kerberos_setup(TAP_KRB_NEEDS_KEYTAB); remctld_start(config, "data/conf-simple", (char *) 0); plan(12); /* Get the current ticket cache and then change KRB5CCNAME. */ cache = getenv("KRB5CCNAME"); if (cache == NULL) bail("failed to set KRB5CCNAME"); putenv((char *) "KRB5CCNAME=./nonexistent-file"); /* Connecting without setting the ticket cache should fail. */ r = remctl_new(); ok(r != NULL, "remctl_new"); ok(!remctl_open(r, "127.0.0.1", 14373, config->principal), "remctl_open to 127.0.0.1"); /* Set the ticket cache and connect to 127.0.0.1 and run a command. */ status = remctl_set_ccache(r, cache); if (!status) { is_string("setting credential cache not supported", remctl_error(r), "remctl_set_ccache failed with correct error"); skip_block(9, "credential cache setting not supported"); } else { ok(remctl_set_ccache(r, cache), "remctl_set_ccache"); ok(remctl_open(r, "127.0.0.1", 14373, config->principal), "remctl_open to 127.0.0.1"); ok(remctl_command(r, command), "remctl_command"); output = remctl_output(r); ok(output != NULL, "remctl_output #1"); if (output == NULL) ok_block(3, 0, "remctl_output failed"); else { is_int(REMCTL_OUT_OUTPUT, output->type, "output token"); ok(memcmp("hello world\n", output->data, strlen("hello world\n")) == 0, "correct output"); is_int(strlen("hello world\n"), output->length, "correct length"); } output = remctl_output(r); ok(output != NULL, "remctl_output #2"); if (output == NULL) ok_block(2, 0, "remctl_output failed"); else { is_int(REMCTL_OUT_STATUS, output->type, "status token"); is_int(0, output->status, "status is correct"); } } remctl_close(r); return 0; }
/* * Main routine. Parse the arguments, open the remctl connection, send the * command, and then call process_response. */ int main(int argc, char *argv[]) { int option, status; char *server_host; struct addrinfo hints, *ai; const char *source = NULL; const char *service_name = NULL; unsigned short port = 0; struct remctl *r; int errorcode = 0; /* Set up logging and identity. */ message_program_name = "remctl"; if (!socket_init()) die("failed to initialize socket library"); /* * Parse options. The + tells GNU getopt to stop option parsing at the * first non-argument rather than proceeding on to find options anywhere. * Without this, it's hard to call remote programs that take options. * Non-GNU getopt will treat the + as a supported option, which is handled * below. */ while ((option = getopt(argc, argv, "+b:dhp:s:v")) != EOF) { switch (option) { case 'b': source = optarg; break; case 'd': message_handlers_debug(1, message_log_stderr); break; case 'h': usage(0); break; case 'p': port = atoi(optarg); break; case 's': service_name = optarg; break; case 'v': printf("%s\n", PACKAGE_STRING); exit(0); break; case '+': fprintf(stderr, "%s: invalid option -- +\n", argv[0]); default: usage(1); break; } } argc -= optind; argv += optind; if (argc < 2) usage(1); server_host = *argv++; argc--; /* * If service_name isn't set, the remctl library uses host/<server> * (host@<server> in GSS-API parlance). However, if the server to which * we're connecting is a DNS-load-balanced name, we have to be careful * what principal name we use. * * Ideally, we would let the GSS-API library handle this and choose * whether to canonicalize the <server> in the principal name based on the * krb5.conf rdns setting and similar configuration. However, with DNS * load balancing, this still may fail. At the time of network * connection, we will connect to whatever the name resolves to then. * After we connect, we authenticate, and the GSS-API library will then * separately canonicalize the hostname. It could get a different answer * than we got for our network connection, leading to an authentication * failure. * * Therefore, if the principal isn't specified, we canonicalize the * hostname to which we're connecting before we connect. Then, the * additional canonicalization possibly done by the GSS-API library should * return the same results and be consistent. * * Note that this opens the possibility of a subtle attack through DNS * spoofing, since both the principal used and the host to which we're * connecting can be changed by varying the DNS response. * * If the principal is specified explicitly, assume the user knows what * they're doing and don't do any of this. */ if (service_name == NULL) { memset(&hints, 0, sizeof(hints)); hints.ai_flags = AI_CANONNAME; status = getaddrinfo(server_host, NULL, &hints, &ai); if (status != 0) die("cannot resolve host %s: %s", server_host, gai_strerror(status)); server_host = xstrdup(ai->ai_canonname); freeaddrinfo(ai); } /* Open connection. */ r = remctl_new(); if (r == NULL) sysdie("cannot initialize remctl connection"); if (source != NULL) if (!remctl_set_source_ip(r, source)) die("%s", remctl_error(r)); if (!remctl_open(r, server_host, port, service_name)) die("%s", remctl_error(r)); /* Do the work. */ if (!remctl_command(r, (const char **) argv)) die("%s", remctl_error(r)); if (!process_response(r, &errorcode)) die("%s", remctl_error(r)); /* Shut down cleanly. */ remctl_close(r); socket_shutdown(); return errorcode; }