/* * Test the network write function with a timeout. We fork off a child * process that runs delay_reader, and then we write 64KB to the network in * two chunks, once with a timeout and once without, and then try a third time * when we should time out. */ static void test_network_write(void) { socket_type fd, c; pid_t child; struct sockaddr_in sin; socklen_t slen; char *buffer; /* Create the data that we're going to send. */ buffer = bmalloc(4096 * 1024); memset(buffer, 'a', 4096 * 1024); /* Create the listening socket. */ fd = network_bind_ipv4(SOCK_STREAM, "127.0.0.1", 11119); if (fd == INVALID_SOCKET) sysbail("cannot create or bind socket"); if (listen(fd, 1) < 0) sysbail("cannot listen to socket"); /* Create the child, which will connect and then read data with delay. */ child = fork(); if (child < 0) sysbail("cannot fork"); else if (child == 0) { socket_close(fd); client_delay_reader("127.0.0.1"); } /* Set an alarm just in case our timeouts don't work. */ alarm(10); /* Accept the client connection. */ slen = sizeof(struct sockaddr_in); c = accept(fd, &sin, &slen); if (c == INVALID_SOCKET) sysbail("cannot accept on socket"); /* Test some successful writes with and without a timeout. */ socket_set_errno(0); ok(network_write(c, buffer, 32 * 1024, 0), "network_write"); ok(network_write(c, buffer, 32 * 1024, 1), "network_write with timeout"); /* * A longer write cannot be completely absorbed before the client sleep, * so should fail with a timeout. */ ok(!network_write(c, buffer, 4096 * 1024, 1), "network_write aborted with timeout"); is_int(ETIMEDOUT, socket_errno, "...with correct error"); alarm(0); /* Clean up. */ socket_close(c); kill(child, SIGTERM); waitpid(child, NULL, 0); socket_close(fd); free(buffer); }
/* * Create a server socket, wait for a connection, and return the connected * socket. */ static socket_type create_server(void) { socket_type fd, conn; int marker; struct sockaddr_in saddr; int on = 1; const void *onaddr = &on; saddr.sin_family = AF_INET; saddr.sin_addr.s_addr = htonl(0x7f000001UL); saddr.sin_port = htons(14373); fd = socket(AF_INET, SOCK_STREAM, 0); if (fd == INVALID_SOCKET) sysbail("error creating socket"); setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, onaddr, sizeof(on)); if (bind(fd, (struct sockaddr *) &saddr, sizeof(saddr)) < 0) sysbail("error binding socket"); if (listen(fd, 1) < 0) sysbail("error listening on socket"); marker = open("server-ready", O_WRONLY | O_CREAT | O_TRUNC, 0666); if (marker < 0) sysbail("cannot create marker file"); close(marker); conn = accept(fd, NULL, 0); if (conn == INVALID_SOCKET) sysbail("error accepting connection"); socket_close(fd); return conn; }
/* * Bring up a server on port 11119 on the loopback address and test connecting * to it via IPv4 using network_client_create. Takes an optional source * address to use for client connections. */ static void test_create_ipv4(const char *source) { socket_type fd; pid_t child; int status; /* Create the socket and listen to it. */ fd = network_bind_ipv4(SOCK_STREAM, "127.0.0.1", 11119); if (fd == INVALID_SOCKET) sysbail("cannot create or bind socket"); ok(fd != INVALID_SOCKET, "IPv4 network client"); if (listen(fd, 1) < 0) sysbail("cannot listen to socket"); /* Fork off a child that uses network_client_create. */ child = fork(); if (child < 0) sysbail("cannot fork"); else if (child == 0) client_create_writer(source); else { test_server_accept(fd); waitpid(child, &status, 0); is_int(0, status, "client made correct connections"); } }
/* * Test the network read function with a timeout. We fork off a child process * that runs delay_writer, and then we read from the network twice, once with * a timeout and once without, and then try a third time when we should time * out. */ static void test_network_read(void) { socket_type fd, c; pid_t child; struct sockaddr_in sin; socklen_t slen; char buffer[4]; /* Create the listening socket. */ fd = network_bind_ipv4(SOCK_STREAM, "127.0.0.1", 11119); if (fd == INVALID_SOCKET) sysbail("cannot create or bind socket"); if (listen(fd, 1) < 0) sysbail("cannot listen to socket"); /* Fork off a child process that writes some data with delays. */ child = fork(); if (child < 0) sysbail("cannot fork"); else if (child == 0) { socket_close(fd); client_delay_writer("127.0.0.1"); } /* Set an alarm just in case our timeouts don't work. */ alarm(10); /* Accept the client connection. */ slen = sizeof(sin); c = accept(fd, &sin, &slen); if (c == INVALID_SOCKET) sysbail("cannot accept on socket"); /* Now test a couple of simple reads, with and without timeout. */ socket_set_errno(0); ok(network_read(c, buffer, sizeof(buffer), 0), "network_read"); ok(memcmp("one\n", buffer, sizeof(buffer)) == 0, "...with good data"); ok(network_read(c, buffer, sizeof(buffer), 1), "network_read with timeout"); ok(memcmp("two\n", buffer, sizeof(buffer)) == 0, "...with good data"); /* * The third read should abort with a timeout, since the writer is writing * with a ten second delay. */ ok(!network_read(c, buffer, sizeof(buffer), 1), "network_read aborted with timeout"); is_int(ETIMEDOUT, socket_errno, "...with correct error"); ok(memcmp("two\n", buffer, sizeof(buffer)) == 0, "...and data unchanged"); alarm(0); /* Clean up. */ socket_close(c); kill(child, SIGTERM); waitpid(child, NULL, 0); socket_close(fd); }
/* * Kill a process and wait for it to exit. Returns the status of the process. * Calls bail on a system failure or a failure of the process to exit. * * We are quite aggressive with error reporting here because child processes * that don't exit or that don't exist often indicate some form of test * failure. */ static int process_kill(struct process *process) { int result, i; int status = -1; struct timeval tv; unsigned long pid = process->pid; /* If the process is not a child, just kill it and hope. */ if (!process->is_child) { if (kill(process->pid, SIGTERM) < 0 && errno != ESRCH) sysbail("cannot send SIGTERM to process %lu", pid); return 0; } /* Check if the process has already exited. */ result = waitpid(process->pid, &status, WNOHANG); if (result < 0) sysbail("cannot wait for child process %lu", pid); else if (result > 0) return status; /* * Kill the process and wait for it to exit. I don't want to go to the * work of setting up a SIGCHLD handler or a full event loop here, so we * effectively poll every tenth of a second for process exit (and * hopefully faster when it does since the SIGCHLD may interrupt our * select, although we're racing with it. */ if (kill(process->pid, SIGTERM) < 0 && errno != ESRCH) sysbail("cannot send SIGTERM to child process %lu", pid); for (i = 0; i < PROCESS_WAIT * 10; i++) { tv.tv_sec = 0; tv.tv_usec = 100000; select(0, NULL, NULL, NULL, &tv); result = waitpid(process->pid, &status, WNOHANG); if (result < 0) sysbail("cannot wait for child process %lu", pid); else if (result > 0) return status; } /* The process still hasn't exited. Bail. */ bail("child process %lu did not exit on SIGTERM", pid); /* Not reached, but some compilers may get confused. */ return status; }
/* * Reallocate memory, reporting a fatal error with bail on failure. */ void * brealloc(void *p, size_t size) { p = realloc(p, size); if (p == NULL) sysbail("failed to realloc %lu bytes", (unsigned long) size); return p; }
int main(void) { pam_handle_t *pamh; struct pam_args *args; struct pam_conv conv = { NULL, NULL }; char *expected; struct output *seen; #ifdef HAVE_KRB5 krb5_error_code code; krb5_principal princ; #endif plan(27); if (pam_start("test", NULL, &conv, &pamh) != PAM_SUCCESS) sysbail("Fake PAM initialization failed"); args = putil_args_new(pamh, 0); if (args == NULL) bail("cannot create PAM argument struct"); TEST(putil_crit, LOG_CRIT, "putil_crit"); TEST(putil_err, LOG_ERR, "putil_err"); putil_debug(args, "%s", "foo"); ok(pam_output() == NULL, "putil_debug without debug on"); args->debug = true; TEST(putil_debug, LOG_DEBUG, "putil_debug"); args->debug = false; TEST_PAM(putil_crit_pam, PAM_SYSTEM_ERR, LOG_CRIT, "putil_crit_pam S"); TEST_PAM(putil_crit_pam, PAM_BUF_ERR, LOG_CRIT, "putil_crit_pam B"); TEST_PAM(putil_crit_pam, PAM_SUCCESS, LOG_CRIT, "putil_crit_pam ok"); TEST_PAM(putil_err_pam, PAM_SYSTEM_ERR, LOG_ERR, "putil_err_pam"); putil_debug_pam(args, PAM_SYSTEM_ERR, "%s", "bar"); ok(pam_output() == NULL, "putil_debug_pam without debug on"); args->debug = true; TEST_PAM(putil_debug_pam, PAM_SYSTEM_ERR, LOG_DEBUG, "putil_debug_pam"); TEST_PAM(putil_debug_pam, PAM_SUCCESS, LOG_DEBUG, "putil_debug_pam ok"); args->debug = false; #ifdef HAVE_KRB5 TEST_KRB5(putil_crit_krb5, LOG_CRIT, "putil_crit_krb5"); TEST_KRB5(putil_err_krb5, LOG_ERR, "putil_err_krb5"); code = krb5_parse_name(args->ctx, "foo@[email protected]", &princ); putil_debug_krb5(args, code, "%s", "krb"); ok(pam_output() == NULL, "putil_debug_krb5 without debug on"); args->debug = true; TEST_KRB5(putil_debug_krb5, LOG_DEBUG, "putil_debug_krb5"); args->debug = false; #else skip_block(4, "not built with Kerberos support"); #endif putil_args_free(args); pam_end(pamh, 0); return 0; }
/* * Read the PID of a process from a file. This is necessary when running * under fakeroot to get the actual PID of the remctld process. */ static long read_pidfile(const char *path) { FILE *file; char buffer[BUFSIZ]; long pid; file = fopen(path, "r"); if (file == NULL) sysbail("cannot open %s", path); if (fgets(buffer, sizeof(buffer), file) == NULL) sysbail("cannot read from %s", path); fclose(file); pid = strtol(buffer, NULL, 10); if (pid <= 0) bail("cannot read PID from %s", path); return pid; }
/* * vsprintf into a newly allocated string, reporting a fatal error with bail * on failure. */ void bvasprintf(char **strp, const char *fmt, va_list args) { int status; status = vasprintf(strp, fmt, args); if (status < 0) sysbail("failed to allocate memory for vasprintf"); }
/* * Allocate cleared memory, reporting a fatal error with bail on failure. */ void * bcalloc(size_t n, size_t size) { void *p; p = calloc(n, size); if (p == NULL) sysbail("failed to calloc %lu", (unsigned long)(n * size)); return p; }
/* * Allocate memory, reporting a fatal error with bail on failure. */ void * bmalloc(size_t size) { void *p; p = malloc(size); if (p == NULL) sysbail("failed to malloc %lu", (unsigned long) size); return p; }
/* * Copy a string, reporting a fatal error with bail on failure. */ char * bstrdup(const char *s) { char *p; size_t len; len = strlen(s) + 1; p = malloc(len); if (p == NULL) sysbail("failed to strdup %lu bytes", (unsigned long) len); memcpy(p, s, len); return p; }
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) { char *path, *tmp; FILE *output; const char *build; struct stat st; size_t length; output = fopen("c-tmpdir.output", "w"); if (output == NULL) sysbail("cannot create c-tmpdir.output"); fprintf(output, "Path to temporary directory: %s/tmp\n", getenv("C_TAP_BUILD")); fclose(output); build = getenv("C_TAP_BUILD"); length = strlen(build) + strlen("/tmp") + 1; path = bcalloc_type(length, char); sprintf(path, "%s/tmp", build); if (access(path, F_OK) == 0) bail("%s already exists", path); free(path); path = test_tmpdir(); printf("Path to temporary directory: %s\n", path); if (stat(path, &st) < 0) sysbail("cannot stat %s", path); if (!S_ISDIR(st.st_mode)) sysbail("%s is not a directory", path); tmp = bstrdup(path); test_tmpdir_free(path); if (stat(tmp, &st) == 0) bail("temporary directory not removed"); free(tmp); return 0; }
/* * Create a client socket, it for a connection, and return the connected * socket. */ static socket_type create_client(void) { socket_type fd; struct sockaddr_in saddr; struct timeval tv; saddr.sin_family = AF_INET; saddr.sin_addr.s_addr = htonl(0x7f000001UL); saddr.sin_port = htons(14373); fd = socket(AF_INET, SOCK_STREAM, 0); if (fd == INVALID_SOCKET) sysbail("error creating socket"); alarm(1); while (access("server-ready", F_OK) != 0) { tv.tv_sec = 0; tv.tv_usec = 10000; select(0, NULL, NULL, NULL, &tv); } alarm(0); if (connect(fd, (struct sockaddr *) &saddr, sizeof(saddr)) < 0) sysbail("error connecting"); return fd; }
int main(void) { pam_handle_t *pamh; struct pam_conv conv = { NULL, NULL }; struct pam_args *args; plan(12); if (pam_start("test", NULL, &conv, &pamh) != PAM_SUCCESS) sysbail("Fake PAM initialization failed"); args = putil_args_new(pamh, 0); ok(args != NULL, "New args struct is not NULL"); if (args == NULL) ok_block(7, 0, "...args struct is NULL"); else { ok(args->pamh == pamh, "...and pamh is correct"); ok(args->config == NULL, "...and config is NULL"); ok(args->user == NULL, "...and user is NULL"); is_int(args->debug, false, "...and debug is false"); is_int(args->silent, false, "...and silent is false"); #ifdef HAVE_KRB5 ok(args->ctx != NULL, "...and the Kerberos context is initialized"); ok(args->realm == NULL, "...and realm is NULL"); #else skip_block(2, "Kerberos support not configured"); #endif } putil_args_free(args); ok(1, "Freeing the args struct works"); args = putil_args_new(pamh, PAM_SILENT); ok(args != NULL, "New args struct with PAM_SILENT is not NULL"); if (args == NULL) ok(0, "...args is NULL"); else is_int(args->silent, true, "...and silent is true"); putil_args_free(args); putil_args_free(NULL); ok(1, "Freeing a NULL args struct works"); pam_end(pamh, 0); return 0; }
/* * Copy up to n characters of a string, reporting a fatal error with bail on * failure. Don't use the system strndup function, since it may not exist and * the TAP library doesn't assume any portability support. */ char * bstrndup(const char *s, size_t n) { const char *p; char *copy; size_t length; /* Don't assume that the source string is nul-terminated. */ for (p = s; (size_t) (p - s) < n && *p != '\0'; p++) ; length = p - s; copy = malloc(length + 1); if (p == NULL) sysbail("failed to strndup %lu bytes", (unsigned long) length); memcpy(copy, s, length); copy[length] = '\0'; return copy; }
/* * Create a temporary directory, tmp, under BUILD if set and the current * directory if it does not. Returns the path to the temporary directory in * newly allocated memory, and calls bail on any failure. The return value * should be freed with test_tmpdir_free. * * This function uses sprintf because it attempts to be independent of all * other portability layers. The use immediately after a memory allocation * should be safe without using snprintf or strlcpy/strlcat. */ char * test_tmpdir(void) { const char *build; char *path = NULL; size_t length; build = getenv("BUILD"); if (build == NULL) build = "."; length = strlen(build) + strlen("/tmp") + 1; path = bmalloc(length); snprintf(path, length, "%s/tmp", build); if (access(path, X_OK) < 0) if (mkdir(path, 0777) < 0) sysbail("error creating temporary directory %s", path); return path; }
/* * Read a line from a file into a BUFSIZ buffer, failing if the line was too * long to fit into the buffer, and returns a copy of that line in newly * allocated memory. Ignores blank lines and comments. Caller is responsible * for freeing. Returns NULL on end of file and fails on read errors. */ static char * readline(FILE *file) { char buffer[BUFSIZ]; char *line, *first; do { line = fgets(buffer, sizeof(buffer), file); if (line == NULL) { if (feof(file)) return NULL; sysbail("cannot read line from script"); } if (buffer[strlen(buffer) - 1] != '\n') bail("script line too long"); buffer[strlen(buffer) - 1] = '\0'; first = skip_whitespace(buffer); } while (first[0] == '#' || first[0] == '\0'); line = bstrdup(buffer); return line; }
int main(void) { struct buffer one = { 0, 0, 0, NULL }; struct buffer two = { 0, 0, 0, NULL }; struct buffer *three; int fd; char *data; ssize_t count; size_t offset; plan(89); /* buffer_set, buffer_append, buffer_swap */ buffer_set(&one, test_string1, sizeof(test_string1)); is_int(1024, one.size, "minimum size is 1024"); is_int(0, one.used, "used starts at 0"); is_int(sizeof(test_string1), one.left, "left is correct"); is_string(test_string1, one.data, "data is corect"); buffer_append(&one, test_string2, sizeof(test_string2)); is_int(1024, one.size, "appended data doesn't change size"); is_int(0, one.used, "or used"); is_int(sizeof(test_string3), one.left, "but left is the right size"); ok(memcmp(one.data, test_string3, sizeof(test_string3)) == 0, "and the resulting data is correct"); one.left -= sizeof(test_string1); one.used += sizeof(test_string1); buffer_append(&one, test_string1, sizeof(test_string1)); is_int(1024, one.size, "size still isn't larger after adding data"); is_int(sizeof(test_string1), one.used, "and used is preserved on append"); is_int(sizeof(test_string3), one.left, "and left is updated properly"); ok(memcmp(one.data + one.used, test_string2, sizeof(test_string2)) == 0, "and the middle data is unchanged"); ok(memcmp(one.data + one.used + sizeof(test_string2), test_string1, sizeof(test_string1)) == 0, "and the final data is correct"); buffer_set(&one, test_string1, sizeof(test_string1)); buffer_set(&two, test_string2, sizeof(test_string2)); buffer_swap(&one, &two); is_int(1024, one.size, "swap #1 size is correct"); is_int(0, one.used, "swap #1 used is correct"); is_int(sizeof(test_string2), one.left, "swap #1 left is correct"); is_string(test_string2, one.data, "swap #1 data is correct"); is_int(1024, two.size, "swap #2 size is correct"); is_int(0, two.used, "swap #2 used is correct"); is_int(sizeof(test_string1), two.left, "swap #2 left is correct"); is_string(test_string1, two.data, "swap #2 data is correct"); free(one.data); free(two.data); one.data = NULL; two.data = NULL; one.size = 0; two.size = 0; /* buffer_resize */ three = buffer_new(); ok(three != NULL, "buffer_new works"); if (three == NULL) bail("buffer_new returned NULL"); is_int(0, three->size, "initial size is 0"); buffer_set(three, test_string1, sizeof(test_string1)); is_int(1024, three->size, "size becomes 1024 when adding data"); buffer_resize(three, 512); is_int(1024, three->size, "resizing to something smaller doesn't change"); buffer_resize(three, 1025); is_int(2048, three->size, "resizing to something larger goes to 2048"); buffer_free(three); /* buffer_read, buffer_find_string, buffer_compact */ fd = open("buffer-test", O_RDWR | O_CREAT | O_TRUNC, 0666); if (fd < 0) sysbail("cannot create buffer-test"); data = bmalloc(2048); memset(data, 'a', 1023); data[1023] = '\r'; data[1024] = '\n'; memset(data + 1025, 'b', 1023); if (xwrite(fd, data, 2048) < 2048) sysbail("cannot write to buffer-test"); if (lseek(fd, 0, SEEK_SET) == (off_t) -1) sysbail("cannot rewind buffer-test"); three = buffer_new(); ok(three != NULL, "buffer_new works"); if (three == NULL) bail("buffer_new returned NULL"); is_int(0, three->size, "and initial size is 0"); buffer_resize(three, 1024); is_int(1024, three->size, "resize to 1024 works"); count = buffer_read(three, fd); is_int(1024, count, "reading into a buffer of size 1024 reads 1024"); offset = 0; ok(!buffer_find_string(three, "\r\n", 0, &offset), "buffer_find_string with truncated string fails"); is_int(0, offset, "and offset is unchanged"); ok(memcmp(three->data, data, three->size) == 0, "buffer data is correct"); buffer_resize(three, 2048); is_int(2048, three->size, "resizing the buffer to 2048 works"); count = buffer_read(three, fd); is_int(1024, count, "and now we can read the rest of the data"); ok(memcmp(three->data, data, 2048) == 0, "and it's all there"); ok(!buffer_find_string(three, "\r\n", 1024, &offset), "buffer_find_string with a string starting before offset fails"); is_int(0, offset, "and offset is unchanged"); ok(buffer_find_string(three, "\r\n", 0, &offset), "finding the string on the whole buffer works"); is_int(1023, offset, "and returns the correct location"); three->used += 400; three->left -= 400; buffer_compact(three); is_int(2048, three->size, "compacting buffer doesn't change the size"); is_int(0, three->used, "but used is now zero"); is_int(1648, three->left, "and left is decreased appropriately"); ok(memcmp(three->data, data + 400, 1648) == 0, "and the data is correct"); count = buffer_read(three, fd); is_int(0, count, "reading at EOF returns 0"); close(fd); unlink("buffer-test"); free(data); buffer_free(three); /* buffer_sprintf and buffer_append_sprintf */ three = buffer_new(); buffer_append_sprintf(three, "testing %d testing", 6); is_int(0, three->used, "buffer_append_sprintf doesn't change used"); is_int(17, three->left, "but sets left correctly"); buffer_append(three, "", 1); is_int(18, three->left, "appending a nul works"); is_string("testing 6 testing", three->data, "and the data is correct"); three->left--; three->used += 5; three->left -= 5; buffer_append_sprintf(three, " %d", 7); is_int(14, three->left, "appending a digit works"); buffer_append(three, "", 1); is_string("testing 6 testing 7", three->data, "and the data is correct"); buffer_sprintf(three, "%d testing", 8); is_int(9, three->left, "replacing the buffer works"); is_string("8 testing", three->data, "and the results are correct"); data = bmalloc(1050); memset(data, 'a', 1049); data[1049] = '\0'; is_int(1024, three->size, "size before large sprintf is 1024"); buffer_sprintf(three, "%s", data); is_int(2048, three->size, "size after large sprintf is 2048"); is_int(1049, three->left, "and left is correct"); buffer_append(three, "", 1); is_string(data, three->data, "and data is correct"); free(data); buffer_free(three); /* buffer_read_all */ fd = open("buffer-test", O_RDWR | O_CREAT | O_TRUNC, 0666); if (fd < 0) sysbail("cannot create buffer-test"); data = bmalloc(2049); memset(data, 'a', 2049); if (xwrite(fd, data, 2049) < 2049) sysbail("cannot write to buffer-test"); if (lseek(fd, 0, SEEK_SET) == (off_t) -1) sysbail("cannot rewind buffer-test"); three = buffer_new(); ok(buffer_read_all(three, fd), "buffer_read_all succeeds"); is_int(0, three->used, "and unused is zero"); is_int(2049, three->left, "and left is correct"); is_int(4096, three->size, "and size is correct"); ok(memcmp(data, three->data, 2049) == 0, "and data is correct"); if (lseek(fd, 0, SEEK_SET) == (off_t) -1) sysbail("cannot rewind buffer-test"); ok(buffer_read_all(three, fd), "reading again succeeds"); is_int(0, three->used, "and used is correct"); is_int(4098, three->left, "and left is now larger"); is_int(8192, three->size, "and size doubled"); ok(memcmp(data, three->data + 2049, 2049) == 0, "and data is correct"); /* buffer_read_file */ if (lseek(fd, 0, SEEK_SET) == (off_t) -1) sysbail("cannot rewind buffer-test"); buffer_free(three); three = buffer_new(); ok(buffer_read_file(three, fd), "buffer_read_file succeeds"); is_int(0, three->used, "and leaves unused at 0"); is_int(2049, three->left, "and left is correct"); is_int(3072, three->size, "and size is a multiple of 1024"); ok(memcmp(data, three->data, 2049) == 0, "and the data is correct"); /* buffer_read_all and buffer_read_file errors */ close(fd); ok(!buffer_read_all(three, fd), "buffer_read_all on closed fd fails"); is_int(3072, three->size, "and size is unchanged"); ok(!buffer_read_file(three, fd), "buffer_read_file on closed fd fails"); is_int(3072, three->size, "and size is unchanged"); is_int(2049, three->left, "and left is unchanged"); unlink("buffer-test"); free(data); buffer_free(three); /* buffer_vsprintf and buffer_append_vsprintf */ three = buffer_new(); test_append_vsprintf(three, "testing %d testing", 6); is_int(0, three->used, "buffer_append_vsprintf leaves used as 0"); is_int(17, three->left, "and left is correct"); buffer_append(three, "", 1); is_int(18, three->left, "and left is correct after appending a nul"); is_string("testing 6 testing", three->data, "and data is correct"); three->left--; three->used += 5; three->left -= 5; test_append_vsprintf(three, " %d", 7); is_int(14, three->left, "and appending results in the correct left"); buffer_append(three, "", 1); is_string("testing 6 testing 7", three->data, "and the right data"); test_vsprintf(three, "%d testing", 8); is_int(9, three->left, "replacing the buffer results in the correct size"); is_string("8 testing", three->data, "and the correct data"); data = bmalloc(1050); memset(data, 'a', 1049); data[1049] = '\0'; is_int(1024, three->size, "size is 1024 before large vsprintf"); test_vsprintf(three, "%s", data); is_int(2048, three->size, "and 2048 afterwards"); is_int(1049, three->left, "and left is correct"); buffer_append(three, "", 1); is_string(data, three->data, "and data is correct"); free(data); buffer_free(three); /* Test buffer_free with NULL and ensure it doesn't explode. */ buffer_free(NULL); return 0; }
/* * Test connect timeouts using IPv4. Bring up a server on port 11119 on the * loopback address and test connections to it. The server only accepts one * connection at a time, so a subsequent connection will time out. */ static void test_timeout_ipv4(void) { socket_type fd, c; pid_t child; socket_type block[20]; int i, err; /* * Create the listening socket. We set the listening queue size to 1, * but some operating systems, including Linux, will allow more * connection attempts to succeed than the backlog size. We'll therefore * have to hammer this server with connections to try to get it to fail. */ fd = network_bind_ipv4(SOCK_STREAM, "127.0.0.1", 11119); if (fd == INVALID_SOCKET) sysbail("cannot create or bind socket"); if (listen(fd, 1) < 0) sysbail("cannot listen to socket"); /* Fork off a child that just runs accept once and then sleeps. */ child = fork(); if (child < 0) sysbail("cannot fork"); else if (child == 0) { alarm(10); c = accept(fd, NULL, 0); if (c == INVALID_SOCKET) _exit(1); sleep(9); _exit(0); } /* In the parent. Open that first connection. */ socket_close(fd); c = network_connect_host("127.0.0.1", 11119, NULL, 1); ok(c != INVALID_SOCKET, "Timeout: first connection worked"); /* * It can take up to fifteen connections on Linux before connections start * actually timing out, and sometimes they never do. */ alarm(20); for (i = 0; i < (int) ARRAY_SIZE(block); i++) { block[i] = network_connect_host("127.0.0.1", 11119, NULL, 1); if (block[i] == INVALID_SOCKET) break; } err = socket_errno; /* * If we reached the end of the array, we can't force a connection * timeout, so just skip this test. It's also possible that the * connection will fail with ECONNRESET or ECONNREFUSED if the nine second * sleep in the child passed, so skip in that case as well. Otherwise, * expect a failure due to timeout in a reasonable amount of time (less * than our 20-second alarm). */ if (i == ARRAY_SIZE(block)) skip_block(2, "short listen queue does not prevent connections"); else { diag("Finally timed out on socket %d", i); ok(block[i] == INVALID_SOCKET, "Later connection timed out"); if (err == ECONNRESET || err == ECONNREFUSED) skip("connections rejected without timeout"); else is_int(ETIMEDOUT, err, "...with correct error code"); } alarm(0); /* Shut down the client and clean up resources. */ kill(child, SIGTERM); waitpid(child, NULL, 0); socket_close(c); for (i--; i >= 0; i--) if (block[i] != INVALID_SOCKET) socket_close(block[i]); socket_close(fd); }
int main(void) { int master, data, out1, out2; socklen_t size; ssize_t status; struct sockaddr_in sin; pid_t child; char buffer[] = "D"; plan(8); /* Parent will create the socket first to get the port number. */ memset(&sin, '\0', sizeof(sin)); sin.sin_family = AF_INET; master = socket(AF_INET, SOCK_STREAM, 0); if (master == -1) sysbail("socket creation failed"); if (bind(master, (struct sockaddr *) &sin, sizeof(sin)) < 0) sysbail("bind failed"); size = sizeof(sin); if (getsockname(master, (struct sockaddr *) &sin, &size) < 0) sysbail("getsockname failed"); if (listen(master, 1) < 0) sysbail("listen failed"); /* Duplicate standard output to test close-on-exec. */ out1 = 8; out2 = 9; if (dup2(fileno(stdout), out1) < 0) sysbail("cannot dup stdout to fd 8"); if (dup2(fileno(stdout), out2) < 0) sysbail("cannot dup stdout to fd 9"); ok(fdflag_close_exec(out1, true), "set fd 8 to close-on-exec"); ok(fdflag_close_exec(out2, true), "set fd 9 to close-on-exec"); ok(fdflag_close_exec(out2, false), "set fd 9 back to regular"); /* * Fork, child closes the open socket and then tries to connect, parent * calls listen() and accept() on it. Parent will then set the socket * non-blocking and try to read from it to see what happens, then write to * the socket and close it, triggering the child close and exit. * * Before the child exits, it will exec a shell that will print "no" to * the duplicate of stdout that the parent created and then the ok to * regular stdout. */ child = fork(); if (child < 0) { sysbail("fork failed"); } else if (child != 0) { size = sizeof(sin); data = accept(master, (struct sockaddr *) &sin, &size); close(master); if (data < 0) sysbail("accept failed"); ok(fdflag_nonblocking(data, true), "set socket non-blocking"); status = read(data, buffer, sizeof(buffer)); is_int(-1, status, "got -1 from non-blocking read"); is_int(EAGAIN, errno, "...with EAGAIN errno"); if (write(data, buffer, sizeof(buffer)) < (ssize_t) sizeof(buffer)) sysbail("write failed"); close(data); testnum += 2; } else { data = socket(AF_INET, SOCK_STREAM, 0); if (data < 0) sysbail("child socket failed"); if (connect(data, (struct sockaddr *) &sin, sizeof(sin)) < 0) sysbail("child connect failed"); if (read(data, buffer, sizeof(buffer)) < (ssize_t) sizeof(buffer)) sysbail("read failed"); fclose(stderr); execlp("sh", "sh", "-c", "printf 'not ' >&8; echo ok 7; echo 'ok 8' >&9", (char *) 0); sysbail("exec failed"); } waitpid(child, NULL, 0); exit(0); }
int main(void) { struct script_config config; struct kerberos_config *krbconf; char *k5login; struct extra extra; struct passwd pwd; FILE *file; /* Load the Kerberos principal and password from a file. */ krbconf = kerberos_setup(TAP_KRB_NEEDS_PASSWORD); memset(&config, 0, sizeof(config)); config.user = krbconf->username; extra.realm = krbconf->realm; config.authtok = krbconf->password; config.extra[0] = krbconf->userprinc; /* Generate a testing krb5.conf file. */ kerberos_generate_conf(krbconf->realm); /* Create a fake passwd struct for our user. */ memset(&pwd, 0, sizeof(pwd)); pwd.pw_name = krbconf->username; pwd.pw_uid = getuid(); pwd.pw_gid = getgid(); basprintf(&pwd.pw_dir, "%s/tmp", getenv("BUILD")); pam_set_pwd(&pwd); plan_lazy(); /* Basic test. */ run_script("data/scripts/cache/basic", &config); /* Check the cache status before the session is closed. */ config.callback = check_cache; config.data = &extra; run_script("data/scripts/cache/open-session", &config); /* Change the authenticating user and test search_k5login. */ pwd.pw_name = (char *) "testuser"; config.user = "******"; basprintf(&k5login, "%s/.k5login", pwd.pw_dir); file = fopen(k5login, "w"); if (file == NULL) sysbail("cannot create %s", k5login); if (fprintf(file, "%s\n", krbconf->userprinc) < 0) sysbail("cannot write to %s", k5login); if (fclose(file) < 0) sysbail("cannot flush %s", k5login); run_script("data/scripts/cache/search-k5login", &config); config.callback = NULL; run_script("data/scripts/cache/search-k5login-debug", &config); unlink(k5login); free(k5login); /* Test search_k5login when no .k5login file exists. */ pwd.pw_name = krbconf->username; config.user = krbconf->username; diag("testing search_k5login with no .k5login file"); run_script("data/scripts/cache/search-k5login", &config); free(pwd.pw_dir); return 0; }
int main(void) { int flag, status; socklen_t flaglen; struct addrinfo *ai4, *ai6; struct addrinfo hints; char addr[INET6_ADDRSTRLEN]; char *p; socket_type fd; static const char *port = "119"; static const char *ipv6_addr = "FEDC:BA98:7654:3210:FEDC:BA98:7654:3210"; #ifndef HAVE_INET6 skip_all("IPv6 not supported"); #endif /* Set up the plan. */ plan(34); /* Get IPv4 and IPv6 sockaddrs to use for subsequent tests. */ memset(&hints, 0, sizeof(hints)); hints.ai_flags = AI_NUMERICHOST; hints.ai_socktype = SOCK_STREAM; status = getaddrinfo("127.0.0.1", port, &hints, &ai4); if (status != 0) bail("getaddrinfo on 127.0.0.1 failed: %s", gai_strerror(status)); status = getaddrinfo(ipv6_addr, port, &hints, &ai6); if (status != 0) bail("getaddr on %s failed: %s", ipv6_addr, gai_strerror(status)); /* Test network_sockaddr_sprint. */ ok(network_sockaddr_sprint(addr, sizeof(addr), ai6->ai_addr), "sprint of IPv6 address"); for (p = addr; *p != '\0'; p++) if (islower((unsigned char) *p)) *p = toupper((unsigned char) *p); is_string(ipv6_addr, addr, "...with right results"); /* Test network_sockaddr_port. */ is_int(119, network_sockaddr_port(ai6->ai_addr), "sockaddr_port IPv6"); /* Test network_sockaddr_equal. */ ok(network_sockaddr_equal(ai6->ai_addr, ai6->ai_addr), "sockaddr_equal IPv6"); ok(!network_sockaddr_equal(ai4->ai_addr, ai6->ai_addr), "...and not equal to IPv4"); ok(!network_sockaddr_equal(ai6->ai_addr, ai4->ai_addr), "...other way around"); freeaddrinfo(ai6); /* Test IPv4 mapped addresses. */ status = getaddrinfo("::ffff:7f00:1", NULL, &hints, &ai6); if (status != 0) bail("getaddr on ::ffff:7f00:1 failed: %s", gai_strerror(status)); ok(network_sockaddr_sprint(addr, sizeof(addr), ai6->ai_addr), "sprint of IPv4-mapped address"); is_string("127.0.0.1", addr, "...with right IPv4 result"); ok(network_sockaddr_equal(ai4->ai_addr, ai6->ai_addr), "sockaddr_equal of IPv4-mapped address"); ok(network_sockaddr_equal(ai6->ai_addr, ai4->ai_addr), "...and other way around"); freeaddrinfo(ai4); status = getaddrinfo("127.0.0.2", NULL, &hints, &ai4); if (status != 0) bail("getaddrinfo on 127.0.0.2 failed: %s", gai_strerror(status)); ok(!network_sockaddr_equal(ai4->ai_addr, ai6->ai_addr), "...but not some other address"); ok(!network_sockaddr_equal(ai6->ai_addr, ai4->ai_addr), "...and the other way around"); freeaddrinfo(ai6); freeaddrinfo(ai4); /* Tests for network_addr_compare. */ is_addr_compare(1, ipv6_addr, ipv6_addr, NULL); is_addr_compare(1, ipv6_addr, ipv6_addr, "128"); is_addr_compare(1, ipv6_addr, ipv6_addr, "60"); is_addr_compare(1, "::127", "0:0::127", "128"); is_addr_compare(1, "::127", "0:0::128", "120"); is_addr_compare(0, "::127", "0:0::128", "128"); is_addr_compare(0, "::7fff", "0:0::8000", "113"); is_addr_compare(1, "::7fff", "0:0::8000", "112"); is_addr_compare(0, "::3:ffff", "::2:ffff", "120"); is_addr_compare(0, "::3:ffff", "::2:ffff", "119"); is_addr_compare(0, "ffff::1", "7fff::1", "1"); is_addr_compare(1, "ffff::1", "7fff::1", "0"); is_addr_compare(0, "fffg::1", "fffg::1", NULL); is_addr_compare(0, "ffff::1", "7fff::1", "-1"); is_addr_compare(0, "ffff::1", "ffff::1", "-1"); is_addr_compare(0, "ffff::1", "ffff::1", "129"); /* Test setting various socket options. */ fd = socket(PF_INET6, SOCK_STREAM, IPPROTO_IP); if (fd == INVALID_SOCKET) sysbail("cannot create socket"); network_set_reuseaddr(fd); #ifdef SO_REUSEADDR flag = 0; flaglen = sizeof(flag); is_int(0, getsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &flag, &flaglen), "Getting SO_REUSEADDR works"); ok(flag, "...and it is set"); #else skip_block(2, "SO_REUSEADDR not supported"); #endif network_set_v6only(fd); #ifdef IPV6_V6ONLY flag = 0; flaglen = sizeof(flag); is_int(0, getsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &flag, &flaglen), "Getting IPV6_V6ONLY works"); ok(flag, "...and it is set"); #else skip_block(2, "IPV6_V6ONLY not supported"); #endif network_set_freebind(fd); #ifdef IP_FREEBIND flag = 0; flaglen = sizeof(flag); is_int(0, getsockopt(fd, IPPROTO_IP, IP_FREEBIND, &flag, &flaglen), "Getting IP_FREEBIND works"); ok(flag, "...and it is set"); #else skip_block(2, "IP_FREEBIND not supported"); #endif close(fd); return 0; }
int main(void) { socket_type fd; struct sockaddr_in sin; size_t size; ssize_t result; struct lbcd_reply reply; struct lbcd_request request; /* Declare a plan. */ plan(65); /* Start the lbcd daemon, allowing load and rr services. */ lbcd_start("-a", "load", "-a", "rr", NULL); /* Set up our client socket. */ fd = network_client_create(PF_INET, SOCK_DGRAM, "127.0.0.1"); if (fd == INVALID_SOCKET) sysbail("cannot create client socket"); memset(&sin, 0, sizeof(sin)); sin.sin_family = AF_INET; sin.sin_port = htons(14330); sin.sin_addr.s_addr = htonl(0x7f000001UL); if (connect(fd, (struct sockaddr *) &sin, sizeof(sin)) < 0) sysbail("cannot connect client socket"); /* Send a simple version three query and get the reply. */ memset(&request, 0, sizeof(request)); request.h.version = htons(3); request.h.id = htons(10); request.h.op = htons(LBCD_OP_LBINFO); result = send(fd, &request, sizeof(struct lbcd_header), 0); if (result != (ssize_t) sizeof(struct lbcd_header)) sysbail("cannot send simple version 3 query"); memset(&reply, 0, sizeof(reply)); result = recv(fd, &reply, sizeof(reply), 0); /* Check that the reply is what we expect. */ is_sane_reply(&request, &reply, result, "simple protocol three query"); /* Send a version two query and get the reply. */ request.h.version = htons(2); request.h.id = htons(20); request.h.status = htons(200); result = send(fd, &request, sizeof(struct lbcd_header), 0); if (result != (ssize_t) sizeof(struct lbcd_header)) sysbail("cannot send version 2 query"); memset(&reply, 0, sizeof(reply)); result = recv(fd, &reply, sizeof(reply), 0); /* Check that the reply is what we expect. */ is_sane_reply(&request, &reply, result, "protocol two query"); /* * Send a more complex version three query with three requested services: * default, which should always be allowed, load, which should match * default, and rr. */ request.h.version = htons(3); request.h.status = htons(3); strlcpy(request.names[0], "default", sizeof(request.names[0])); strlcpy(request.names[1], "load", sizeof(request.names[0])); strlcpy(request.names[2], "rr", sizeof(request.names[0])); size = sizeof(struct lbcd_header) + 3 * sizeof(lbcd_name_type); result = send(fd, &request, size, 0); if (result != (ssize_t) size) sysbail("cannot send complex version 3 query"); memset(&reply, 0, sizeof(reply)); result = recv(fd, &reply, sizeof(reply), 0); /* Check that the reply is what we expect. */ is_sane_reply(&request, &reply, result, "complex protocol three query"); is_int(ntohl(reply.weights[0].host_weight), ntohl(reply.weights[1].host_weight), "...default service weight matches first weight"); is_int(ntohl(reply.weights[0].host_incr), ntohl(reply.weights[1].host_incr), "...default service increment matches first increment"); is_int(ntohl(reply.weights[0].host_weight), ntohl(reply.weights[2].host_weight), "...load service weight matches first weight"); is_int(ntohl(reply.weights[0].host_incr), ntohl(reply.weights[2].host_incr), "...load service increment matches first increment"); is_int(1, ntohl(reply.weights[3].host_weight), "...rr service weight is 1"); is_int(1, ntohl(reply.weights[3].host_incr), "...rr service increment is 1"); /* All done. Clean up and return. */ close(fd); return 0; }
int main(void) { pam_handle_t *pamh; struct pam_conv conv = { NULL, NULL }; char **env; size_t i; /* * Skip this test if the native PAM library doesn't support a PAM * environment, since we "break" pam_putenv to mirror the native behavior * in that case. */ #ifndef HAVE_PAM_GETENV skip_all("system doesn't support PAM environment"); #endif plan(33); /* Basic environment manipulation. */ if (pam_start("test", NULL, &conv, &pamh) != PAM_SUCCESS) sysbail("Fake PAM initialization failed"); is_int(PAM_BAD_ITEM, pam_putenv(pamh, "TEST"), "delete when NULL"); ok(pam_getenv(pamh, "TEST") == NULL, "getenv when NULL"); env = pam_getenvlist(pamh); ok(env != NULL, "getenvlist when NULL returns non-NULL"); is_string(NULL, env[0], "...but first element is NULL"); for (i = 0; env[i] != NULL; i++) free(env[i]); free(env); /* putenv and getenv. */ is_int(PAM_SUCCESS, pam_putenv(pamh, "TEST=foo"), "putenv TEST"); is_string("foo", pam_getenv(pamh, "TEST"), "getenv TEST"); is_int(PAM_SUCCESS, pam_putenv(pamh, "FOO=bar"), "putenv FOO"); is_int(PAM_SUCCESS, pam_putenv(pamh, "BAR=baz"), "putenv BAR"); is_string("foo", pam_getenv(pamh, "TEST"), "getenv TEST"); is_string("bar", pam_getenv(pamh, "FOO"), "getenv FOO"); is_string("baz", pam_getenv(pamh, "BAR"), "getenv BAR"); ok(pam_getenv(pamh, "BAZ") == NULL, "getenv BAZ is NULL"); /* Replacing and deleting environment variables. */ is_int(PAM_BAD_ITEM, pam_putenv(pamh, "BAZ"), "putenv nonexistent delete"); is_int(PAM_SUCCESS, pam_putenv(pamh, "FOO=foo"), "putenv replace"); is_int(PAM_SUCCESS, pam_putenv(pamh, "FOON=bar=n"), "putenv prefix"); is_string("foo", pam_getenv(pamh, "FOO"), "getenv FOO"); is_string("bar=n", pam_getenv(pamh, "FOON"), "getenv FOON"); is_int(PAM_BAD_ITEM, pam_putenv(pamh, "FO"), "putenv delete FO"); is_int(PAM_SUCCESS, pam_putenv(pamh, "FOO"), "putenv delete FOO"); ok(pam_getenv(pamh, "FOO") == NULL, "getenv FOO is NULL"); is_string("bar=n", pam_getenv(pamh, "FOON"), "getenv FOON"); is_string("baz", pam_getenv(pamh, "BAR"), "getenv BAR"); /* pam_getenvlist. */ env = pam_getenvlist(pamh); ok(env != NULL, "getenvlist not NULL"); is_string("TEST=foo", env[0], "getenvlist TEST"); is_string("BAR=baz", env[1], "getenvlist BAR"); is_string("FOON=bar=n", env[2], "getenvlist FOON"); ok(env[3] == NULL, "getenvlist length"); for (i = 0; env[i] != NULL; i++) free(env[i]); free(env); is_int(PAM_SUCCESS, pam_putenv(pamh, "FOO=foo"), "putenv FOO"); is_string("TEST=foo", pamh->environ[0], "pamh environ TEST"); is_string("BAR=baz", pamh->environ[1], "pamh environ BAR"); is_string("FOON=bar=n", pamh->environ[2], "pamh environ FOON"); is_string("FOO=foo", pamh->environ[3], "pamh environ FOO"); ok(pamh->environ[4] == NULL, "pamh environ length"); pam_end(pamh, 0); return 0; }
int main(void) { pam_handle_t *pamh; struct pam_args *args; struct pam_conv conv = { NULL, NULL }; bool status; struct vector *cells; char *program; struct output *seen; const char *argv_bool[2] = { NULL, NULL }; const char *argv_err[2] = { NULL, NULL }; const char *argv_empty[] = { NULL }; #ifdef HAVE_KRB5 const char *argv_all[] = { "cells=stanford.edu,ir.stanford.edu", "debug", "expires=1d", "ignore_root", "minimum_uid=1000", "program=/bin/true" }; char *krb5conf; #else const char *argv_all[] = { "cells=stanford.edu,ir.stanford.edu", "debug", "expires=86400", "ignore_root", "minimum_uid=1000", "program=/bin/true" }; #endif if (pam_start("test", NULL, &conv, &pamh) != PAM_SUCCESS) sysbail("cannot create pam_handle_t"); args = putil_args_new(pamh, 0); if (args == NULL) bail("cannot create PAM argument struct"); plan(161); /* First, check just the defaults. */ args->config = config_new(); status = putil_args_defaults(args, options, optlen); ok(status, "Setting the defaults"); ok(args->config->cells == NULL, "...cells default"); is_int(false, args->config->debug, "...debug default"); is_int(10, args->config->expires, "...expires default"); is_int(true, args->config->ignore_root, "...ignore_root default"); is_int(0, args->config->minimum_uid, "...minimum_uid default"); ok(args->config->program == NULL, "...program default"); /* Now parse an empty set of PAM arguments. Nothing should change. */ status = putil_args_parse(args, 0, argv_empty, options, optlen); ok(status, "Parse of empty argv"); ok(args->config->cells == NULL, "...cells still default"); is_int(false, args->config->debug, "...debug still default"); is_int(10, args->config->expires, "...expires default"); is_int(true, args->config->ignore_root, "...ignore_root still default"); is_int(0, args->config->minimum_uid, "...minimum_uid still default"); ok(args->config->program == NULL, "...program still default"); /* Now, check setting everything. */ status = putil_args_parse(args, 6, argv_all, options, optlen); ok(status, "Parse of full argv"); if (args->config->cells == NULL) ok_block(4, false, "...cells is set"); else { ok(args->config->cells != NULL, "...cells is set"); is_int(2, args->config->cells->count, "...with two cells"); is_string("stanford.edu", args->config->cells->strings[0], "...first is stanford.edu"); is_string("ir.stanford.edu", args->config->cells->strings[1], "...second is ir.stanford.edu"); } is_int(true, args->config->debug, "...debug is set"); is_int(86400, args->config->expires, "...expires is set"); is_int(true, args->config->ignore_root, "...ignore_root is set"); is_int(1000, args->config->minimum_uid, "...minimum_uid is set"); is_string("/bin/true", args->config->program, "...program is set"); config_free(args->config); args->config = NULL; /* Test deep copying of defaults. */ cells = vector_new(); if (cells == NULL) sysbail("cannot allocate memory"); vector_add(cells, "foo.com"); vector_add(cells, "bar.com"); options[0].defaults.list = cells; program = strdup("/bin/false"); if (program == NULL) sysbail("cannot allocate memory"); options[5].defaults.string = program; args->config = config_new(); status = putil_args_defaults(args, options, optlen); ok(status, "Setting defaults with new defaults"); if (args->config->cells == NULL) ok_block(4, false, "...cells is set"); else { ok(args->config->cells != NULL, "...cells is set"); is_int(2, args->config->cells->count, "...with two cells"); is_string("foo.com", args->config->cells->strings[0], "...first is foo.com"); is_string("bar.com", args->config->cells->strings[1], "...second is bar.com"); } is_string("/bin/false", args->config->program, "...program is /bin/false"); status = putil_args_parse(args, 6, argv_all, options, optlen); ok(status, "Parse of full argv after defaults"); if (args->config->cells == NULL) ok_block(4, false, "...cells is set"); else { ok(args->config->cells != NULL, "...cells is set"); is_int(2, args->config->cells->count, "...with two cells"); is_string("stanford.edu", args->config->cells->strings[0], "...first is stanford.edu"); is_string("ir.stanford.edu", args->config->cells->strings[1], "...second is ir.stanford.edu"); } is_int(true, args->config->debug, "...debug is set"); is_int(86400, args->config->expires, "...expires is set"); is_int(true, args->config->ignore_root, "...ignore_root is set"); is_int(1000, args->config->minimum_uid, "...minimum_uid is set"); is_string("/bin/true", args->config->program, "...program is set"); is_string("foo.com", cells->strings[0], "...first cell after parse"); is_string("bar.com", cells->strings[1], "...second cell after parse"); is_string("/bin/false", program, "...string after parse"); config_free(args->config); args->config = NULL; is_string("foo.com", cells->strings[0], "...first cell after free"); is_string("bar.com", cells->strings[1], "...second cell after free"); is_string("/bin/false", program, "...string after free"); options[0].defaults.list = NULL; options[5].defaults.string = NULL; vector_free(cells); free(program); /* Test specifying the default for a vector parameter as a string. */ options[0].type = TYPE_STRLIST; options[0].defaults.string = "foo.com,bar.com"; args->config = config_new(); status = putil_args_defaults(args, options, optlen); ok(status, "Setting defaults with string default for vector"); if (args->config->cells == NULL) ok_block(4, false, "...cells is set"); else { ok(args->config->cells != NULL, "...cells is set"); is_int(2, args->config->cells->count, "...with two cells"); is_string("foo.com", args->config->cells->strings[0], "...first is foo.com"); is_string("bar.com", args->config->cells->strings[1], "...second is bar.com"); } config_free(args->config); args->config = NULL; options[0].type = TYPE_LIST; options[0].defaults.string = NULL; /* Should be no errors so far. */ ok(pam_output() == NULL, "No errors so far"); /* Test various ways of spelling booleans. */ args->config = config_new(); TEST_BOOL("debug", args->config->debug, true); TEST_BOOL("debug=false", args->config->debug, false); TEST_BOOL("debug=true", args->config->debug, true); TEST_BOOL("debug=no", args->config->debug, false); TEST_BOOL("debug=yes", args->config->debug, true); TEST_BOOL("debug=off", args->config->debug, false); TEST_BOOL("debug=on", args->config->debug, true); TEST_BOOL("debug=0", args->config->debug, false); TEST_BOOL("debug=1", args->config->debug, true); TEST_BOOL("debug=False", args->config->debug, false); TEST_BOOL("debug=trUe", args->config->debug, true); TEST_BOOL("debug=No", args->config->debug, false); TEST_BOOL("debug=Yes", args->config->debug, true); TEST_BOOL("debug=OFF", args->config->debug, false); TEST_BOOL("debug=ON", args->config->debug, true); config_free(args->config); args->config = NULL; /* Test for various parsing errors. */ args->config = config_new(); TEST_ERROR("debug=", LOG_ERR, "invalid boolean in setting: debug="); TEST_ERROR("debug=truth", LOG_ERR, "invalid boolean in setting: debug=truth"); TEST_ERROR("minimum_uid", LOG_ERR, "value missing for option minimum_uid"); TEST_ERROR("minimum_uid=", LOG_ERR, "value missing for option minimum_uid="); TEST_ERROR("minimum_uid=foo", LOG_ERR, "invalid number in setting: minimum_uid=foo"); TEST_ERROR("minimum_uid=1000foo", LOG_ERR, "invalid number in setting: minimum_uid=1000foo"); TEST_ERROR("program", LOG_ERR, "value missing for option program"); TEST_ERROR("cells", LOG_ERR, "value missing for option cells"); config_free(args->config); args->config = NULL; #ifdef HAVE_KRB5 /* Test for Kerberos krb5.conf option parsing. */ krb5conf = test_file_path("data/krb5-pam.conf"); if (krb5conf == NULL) bail("cannot find data/krb5-pam.conf"); if (setenv("KRB5_CONFIG", krb5conf, 1) < 0) sysbail("cannot set KRB5_CONFIG"); krb5_free_context(args->ctx); status = krb5_init_context(&args->ctx); if (status != 0) bail("cannot parse test krb5.conf file"); args->config = config_new(); status = putil_args_defaults(args, options, optlen); ok(status, "Setting the defaults"); status = putil_args_krb5(args, "testing", options, optlen); ok(status, "Options from krb5.conf"); ok(args->config->cells == NULL, "...cells default"); is_int(true, args->config->debug, "...debug set from krb5.conf"); is_int(1800, args->config->expires, "...expires set from krb5.conf"); is_int(true, args->config->ignore_root, "...ignore_root default"); is_int(1000, args->config->minimum_uid, "...minimum_uid set from krb5.conf"); ok(args->config->program == NULL, "...program default"); status = putil_args_krb5(args, "other-test", options, optlen); ok(status, "Options from krb5.conf (other-test)"); is_int(-1000, args->config->minimum_uid, "...minimum_uid set from krb5.conf other-test"); /* Test with a realm set, which should expose more settings. */ krb5_free_context(args->ctx); status = krb5_init_context(&args->ctx); if (status != 0) bail("cannot parse test krb5.conf file"); args->realm = strdup("FOO.COM"); if (args->realm == NULL) sysbail("cannot allocate memory"); status = putil_args_krb5(args, "testing", options, optlen); ok(status, "Options from krb5.conf with FOO.COM"); is_int(2, args->config->cells->count, "...cells count from krb5.conf"); is_string("foo.com", args->config->cells->strings[0], "...first cell from krb5.conf"); is_string("bar.com", args->config->cells->strings[1], "...second cell from krb5.conf"); is_int(true, args->config->debug, "...debug set from krb5.conf"); is_int(1800, args->config->expires, "...expires set from krb5.conf"); is_int(true, args->config->ignore_root, "...ignore_root default"); is_int(1000, args->config->minimum_uid, "...minimum_uid set from krb5.conf"); is_string("/bin/false", args->config->program, "...program from krb5.conf"); /* Test with a different realm. */ free(args->realm); args->realm = strdup("BAR.COM"); if (args->realm == NULL) sysbail("cannot allocate memory"); status = putil_args_krb5(args, "testing", options, optlen); ok(status, "Options from krb5.conf with BAR.COM"); is_int(2, args->config->cells->count, "...cells count from krb5.conf"); is_string("bar.com", args->config->cells->strings[0], "...first cell from krb5.conf"); is_string("foo.com", args->config->cells->strings[1], "...second cell from krb5.conf"); is_int(true, args->config->debug, "...debug set from krb5.conf"); is_int(1800, args->config->expires, "...expires set from krb5.conf"); is_int(true, args->config->ignore_root, "...ignore_root default"); is_int(1000, args->config->minimum_uid, "...minimum_uid set from krb5.conf"); is_string("echo /bin/true", args->config->program, "...program from krb5.conf"); config_free(args->config); args->config = config_new(); status = putil_args_krb5(args, "other-test", options, optlen); ok(status, "Options from krb5.conf (other-test with realm)"); ok(args->config->cells == NULL, "...cells is NULL"); is_string("echo /bin/true", args->config->program, "...program from krb5.conf"); config_free(args->config); args->config = NULL; /* Test for time parsing errors. */ args->config = config_new(); TEST_ERROR("expires=ft87", LOG_ERR, "bad time value in setting: expires=ft87"); config_free(args->config); /* Test error reporting from the krb5.conf parser. */ args->config = config_new(); status = putil_args_krb5(args, "bad-number", options, optlen); ok(status, "Options from krb5.conf (bad-number)"); seen = pam_output(); is_string("invalid number in krb5.conf setting for minimum_uid: 1000foo", seen->lines[0].line, "...and correct error reported"); is_int(LOG_ERR, seen->lines[0].priority, "...with correct priority"); pam_output_free(seen); config_free(args->config); args->config = NULL; /* Test error reporting on times from the krb5.conf parser. */ args->config = config_new(); status = putil_args_krb5(args, "bad-time", options, optlen); ok(status, "Options from krb5.conf (bad-time)"); seen = pam_output(); if (seen == NULL) ok_block(2, false, "...no error output"); else { is_string("invalid time in krb5.conf setting for expires: ft87", seen->lines[0].line, "...and correct error reported"); is_int(LOG_ERR, seen->lines[0].priority, "...with correct priority"); } pam_output_free(seen); config_free(args->config); args->config = NULL; test_file_path_free(krb5conf); #else /* !HAVE_KRB5 */ skip_block(37, "Kerberos support not configured"); #endif putil_args_free(args); pam_end(pamh, 0); return 0; }
int main(void) { struct rule rule = { NULL, 0, NULL, NULL, NULL, NULL, NULL, 0, NULL, NULL, 0, 0, NULL, NULL, NULL }; const char *acls[5]; plan(78); if (chdir(getenv("C_TAP_SOURCE")) < 0) sysbail("can't chdir to C_TAP_SOURCE"); rule.file = (char *) "TEST"; rule.acls = (char **) acls; acls[0] = "data/acl-simple"; acls[1] = NULL; acls[2] = NULL; acls[3] = NULL; acls[4] = NULL; ok(acl_permit(&rule, "*****@*****.**"), "simple 1"); ok(acl_permit(&rule, "*****@*****.**"), "simple 2"); ok(acl_permit(&rule, "*****@*****.**"), "simple 3"); ok(acl_permit(&rule, "*****@*****.**"), "simple 4"); ok(acl_permit(&rule, "*****@*****.**"), "simple 5"); ok(!acl_permit(&rule, "*****@*****.**"), "no 1"); ok(!acl_permit(&rule, "*****@*****.**"), "no 2"); ok(!acl_permit(&rule, "*****@*****.**"), "no 3"); ok(!acl_permit(&rule, "*****@*****.**"), "no 4"); /* Okay, now capture and check the errors. */ acls[0] = "data/acl-bad-include"; acls[1] = "data/acls/valid"; errors_capture(); ok(!acl_permit(&rule, "*****@*****.**"), "included file not found"); is_string("data/acl-bad-include:1: included file data/acl-nosuchfile" " not found\n", errors, "...and correct error message"); acls[0] = "data/acl-recursive"; errors_capture(); ok(!acl_permit(&rule, "*****@*****.**"), "recursive ACL inclusion"); is_string("data/acl-recursive:3: data/acl-recursive recursively" " included\n", errors, "...and correct error message"); acls[0] = "data/acls/valid-2"; acls[1] = "data/acl-too-long"; errors_capture(); ok(acl_permit(&rule, "*****@*****.**"), "granted access based on first ACL file"); ok(errors == NULL, "...with no errors"); ok(!acl_permit(&rule, "*****@*****.**"), "...but failed when we hit second file with long line"); is_string("data/acl-too-long:1: ACL file line too long\n", errors, "...with correct error message"); acls[0] = "data/acl-no-such-file"; acls[1] = "data/acls/valid"; errors_capture(); ok(!acl_permit(&rule, "*****@*****.**"), "no such ACL file"); is_string("TEST:0: included file data/acl-no-such-file not found\n", errors, "...with correct error message"); errors_capture(); ok(!acl_permit(&rule, "*****@*****.**"), "...even with a principal in an ACL file"); is_string("TEST:0: included file data/acl-no-such-file not found\n", errors, "...still with right error message"); acls[0] = "data/acl-bad-syntax"; errors_capture(); ok(!acl_permit(&rule, "*****@*****.**"), "incorrect syntax"); is_string("data/acl-bad-syntax:2: parse error\n", errors, "...with correct error message"); errors_uncapture(); /* Check that file: works at the top level. */ acls[0] = "file:data/acl-simple"; acls[1] = NULL; ok(acl_permit(&rule, "*****@*****.**"), "file: success"); ok(!acl_permit(&rule, "*****@*****.**"), "file: failure"); /* Check that include syntax works. */ ok(acl_permit(&rule, "*****@*****.**"), "include 1"); ok(acl_permit(&rule, "*****@*****.**"), "include 2"); ok(acl_permit(&rule, "*****@*****.**"), "include 3"); ok(acl_permit(&rule, "*****@*****.**"), "include 4"); ok(acl_permit(&rule, "*****@*****.**"), "include 5"); ok(!acl_permit(&rule, "*****@*****.**"), "include failure"); /* Check that princ: works at the top level. */ acls[0] = "princ:[email protected]"; ok(acl_permit(&rule, "*****@*****.**"), "princ: success"); ok(!acl_permit(&rule, "*****@*****.**"), "princ: failure"); /* Check that deny: works at the top level. */ acls[0] = "deny:princ:[email protected]"; acls[1] = "princ:[email protected]"; acls[2] = "princ:[email protected]"; acls[3] = NULL; ok(acl_permit(&rule, "*****@*****.**"), "deny: success"); ok(!acl_permit(&rule, "*****@*****.**"), "deny: failure"); /* And make sure deny interacts correctly with files. */ acls[0] = "data/acl-simple"; acls[1] = "princ:[email protected]"; acls[2] = NULL; ok(!acl_permit(&rule, "*****@*****.**"), "deny in file beats later princ"); acls[0] = "deny:princ:[email protected]"; acls[1] = "data/acl-simple"; ok(!acl_permit(&rule, "*****@*****.**"), "deny overrides later file"); /* * Ensure deny never affirmatively grants access, so deny:deny: matches * nothing. */ acls[0] = "deny:deny:princ:[email protected]"; acls[1] = "data/acl-simple"; ok(acl_permit(&rule, "*****@*****.**"), "deny:deny does nothing"); ok(acl_permit(&rule, "*****@*****.**"), "deny:deny doesn't break anything"); /* * Denying a file denies anything that would match the file, and nothing * that wouldn't, including due to an embedded deny. */ acls[0] = "deny:file:data/acl-simple"; acls[1] = "princ:[email protected]"; acls[2] = "princ:[email protected]"; acls[3] = "princ:[email protected]"; acls[4] = NULL; ok(!acl_permit(&rule, "*****@*****.**"), "deny of a file works"); ok(acl_permit(&rule, "*****@*****.**"), "...and doesn't break anything"); ok(acl_permit(&rule, "*****@*****.**"), "...and deny inside a denied file is ignored"); /* Check for an invalid ACL scheme. */ acls[0] = "ihateyou:verymuch"; acls[1] = "data/acls/valid"; errors_capture(); ok(!acl_permit(&rule, "*****@*****.**"), "invalid ACL scheme"); is_string("TEST:0: invalid ACL scheme 'ihateyou'\n", errors, "...with correct error"); errors_uncapture(); /* * Check GPUT ACLs, or make sure they behave sanely when GPUT support is * not compiled. */ server_config_set_gput_file((char *) "data/gput"); acls[0] = "gput:test"; acls[1] = NULL; #ifdef HAVE_GPUT ok(acl_permit(&rule, "*****@*****.**"), "GPUT 1"); ok(!acl_permit(&rule, "*****@*****.**"), "GPUT 2"); ok(!acl_permit(&rule, "*****@*****.**"), "GPUT 3"); acls[0] = "gput:test[%@EXAMPLE.NET]"; ok(acl_permit(&rule, "*****@*****.**"), "GPUT with transform 1"); ok(!acl_permit(&rule, "*****@*****.**"), "GPUT with transform 2"); ok(!acl_permit(&rule, "*****@*****.**"), "GPUT with transform 3"); #else errors_capture(); ok(!acl_permit(&rule, "*****@*****.**"), "GPUT"); is_string("TEST:0: ACL scheme 'gput' is not supported\n", errors, "...with not supported error"); errors_uncapture(); skip_block(4, "GPUT support not configured"); #endif /* * Check PCRE ACLs, or make sure they behave as they should when not * supported. */ acls[0] = "deny:pcre:host/foo.+\\.org@EXAMPLE\\.ORG"; acls[1] = "pcre:host/.+\\.org@EXAMPLE\\.ORG"; acls[2] = NULL; #ifdef HAVE_PCRE ok(acl_permit(&rule, "host/[email protected]"), "PCRE 1"); ok(!acl_permit(&rule, "host/[email protected]"), "PCRE 2"); ok(!acl_permit(&rule, "host/[email protected]"), "PCRE 3"); ok(!acl_permit(&rule, "host/[email protected]"), "PCRE 4 (plus operator)"); ok(!acl_permit(&rule, "host/[email protected]"), "PCRE 5 (escaped period)"); acls[1] = "pcre:+host/.*"; errors_capture(); ok(!acl_permit(&rule, "host/[email protected]"), "PCRE invalid regex"); is_string("TEST:0: compilation of regex '+host/.*' failed around 0\n", errors, "...with invalid regex error"); errors_uncapture(); #else errors_capture(); ok(!acl_permit(&rule, "host/[email protected]"), "PCRE"); is_string("TEST:0: ACL scheme 'pcre' is not supported\n", errors, "...with not supported error"); errors_uncapture(); skip_block(5, "PCRE support not configured"); #endif /* * Check POSIX regex ACLs, or make sure they behave as they should when * not supported. */ acls[0] = "deny:regex:host/foo.*\\.org@EXAMPLE\\.ORG"; acls[1] = "regex:host/.*\\.org@EXAMPLE\\.ORG"; acls[2] = NULL; #ifdef HAVE_REGCOMP ok(acl_permit(&rule, "host/[email protected]"), "regex 1"); ok(!acl_permit(&rule, "host/[email protected]"), "regex 2"); ok(!acl_permit(&rule, "host/[email protected]"), "regex 3"); ok(acl_permit(&rule, "host/[email protected]"), "regex 4"); ok(!acl_permit(&rule, "host/[email protected]"), "regex 5 (escaped period)"); acls[1] = "regex:*host/.*"; errors_capture(); ok(!acl_permit(&rule, "host/[email protected]"), "regex invalid regex"); ok(strncmp(errors, "TEST:0: compilation of regex '*host/.*' failed:", strlen("TEST:0: compilation of regex '*host/.*' failed:")) == 0, "...with invalid regex error"); errors_uncapture(); free(errors); errors = NULL; #else errors_capture(); ok(!acl_permit(&rule, "host/[email protected]"), "regex"); is_string("TEST:0: ACL scheme 'regex' is not supported\n", errors, "...with not supported error"); errors_uncapture(); skip_block(5, "regex support not available"); free(errors); errors = NULL; #endif /* Test for valid characters in ACL files. */ acls[0] = "file:data/acls"; acls[1] = NULL; ok(acl_permit(&rule, "*****@*****.**"), "valid chars 1"); ok(acl_permit(&rule, "*****@*****.**"), "valid chars 2"); ok(acl_permit(&rule, "*****@*****.**"), "valid chars 3"); ok(!acl_permit(&rule, "*****@*****.**"), "invalid chars 1"); ok(!acl_permit(&rule, "*****@*****.**"), "invalid chars 2"); ok(!acl_permit(&rule, "*****@*****.**"), "invalid chars 3"); /* Check anyuser:*. */ acls[0] = "anyuser:auth"; acls[1] = NULL; ok(acl_permit(&rule, "*****@*****.**"), "anyuser:auth"); acls[0] = "anyuser:anonymous"; ok(acl_permit(&rule, "*****@*****.**"), "anyuser:anonymous"); acls[0] = "ANYUSER"; ok(acl_permit(&rule, "*****@*****.**"), "ANYUSER"); /* * Ensure that anyuser:auth and ANYUSER don't allow the anonymous * identity, but anyuser:anonymous does. */ acls[0] = "anyuser:auth"; acls[1] = NULL; ok(!acl_permit_anonymous(&rule), "anyuser:auth disallows anonymous"); acls[0] = "ANYUSER"; ok(!acl_permit_anonymous(&rule), "ANYUSER disallows anonymous"); acls[0] = "anyuser:anonymous"; ok(acl_permit_anonymous(&rule), "anyuser:anonymous allows anonymous"); /* Check error handling of unknown anyuser ACLs. */ acls[0] = "anyuser:foo"; acls[1] = NULL; errors_capture(); ok(!acl_permit(&rule, "*****@*****.**"), "invalid anyuser ACL"); is_string("TEST:0: invalid ACL value 'anyuser:foo'\n", errors, "...with correct error."); errors_uncapture(); free(errors); errors = NULL; return 0; }
int main(void) { struct script_config config; struct kerberos_password *password; DIR *tmpdir; struct dirent *file; char *tmppath, *path; /* Load the Kerberos principal and password from a file. */ password = kerberos_config_password(); if (password == NULL) skip_all("Kerberos tests not configured"); memset(&config, 0, sizeof(config)); config.user = password->username; config.password = password->password; config.extra[0] = password->principal; /* Generate a testing krb5.conf file. */ kerberos_generate_conf(password->realm); /* Get the temporary directory and store that as the %1 substitution. */ tmppath = test_tmpdir(); config.extra[1] = tmppath; plan_lazy(); /* * We need to ensure that the only thing in the test temporary directory * is the krb5.conf file that we generated, since we're going to check for * cleanup by looking for any out-of-place files. */ tmpdir = opendir(tmppath); if (tmpdir == NULL) sysbail("cannot open directory %s", tmppath); while ((file = readdir(tmpdir)) != NULL) { if (strcmp(file->d_name, ".") == 0 || strcmp(file->d_name, "..") == 0) continue; if (strcmp(file->d_name, "krb5.conf") == 0) continue; basprintf(&path, "%s/%s", tmppath, file->d_name); if (unlink(path) < 0) sysbail("cannot delete temporary file %s", path); free(path); } closedir(tmpdir); /* * Authenticate only, call pam_end, and be sure the ticket cache is * gone. The auth-only script sets ccache_dir to the temporary directory, * so the module will create a temporary ticket cache there and then * should clean it up. */ run_script("data/scripts/cache-cleanup/auth-only", &config); path = NULL; tmpdir = opendir(tmppath); if (tmpdir == NULL) sysbail("cannot open directory %s", tmppath); while ((file = readdir(tmpdir)) != NULL) { if (strcmp(file->d_name, ".") == 0 || strcmp(file->d_name, "..") == 0) continue; if (strcmp(file->d_name, "krb5.conf") == 0) continue; if (path == NULL) basprintf(&path, "%s/%s", tmppath, file->d_name); } closedir(tmpdir); if (path != NULL) diag("found stray temporary file %s", path); ok(path == NULL, "ticket cache cleaned up"); if (path != NULL) free(path); test_tmpdir_free(tmppath); kerberos_config_password_free(password); return 0; }
/* * We found a section delimiter while parsing another section. Rewind our * input file back before the section delimiter so that we'll read it again. * Takes the length of the line we read, which is used to determine how far to * rewind. */ static void rewind_section(FILE *script, size_t length) { if (fseek(script, -length - 1, SEEK_CUR) != 0) sysbail("cannot rewind file"); }