diff options
Diffstat (limited to 'libpamc/test/regress')
-rw-r--r-- | libpamc/test/regress/Makefile | 7 | ||||
-rwxr-xr-x | libpamc/test/regress/run_test.sh | 6 | ||||
-rw-r--r-- | libpamc/test/regress/test.libpamc.c | 343 | ||||
-rwxr-xr-x | libpamc/test/regress/test.secret@here | 151 |
4 files changed, 507 insertions, 0 deletions
diff --git a/libpamc/test/regress/Makefile b/libpamc/test/regress/Makefile new file mode 100644 index 0000000..cba474f --- /dev/null +++ b/libpamc/test/regress/Makefile @@ -0,0 +1,7 @@ +CFLAGS = -g -I ../../include + +test.libpamc: test.libpamc.o + $(CC) -o $@ $(CFLAGS) $< -L ../.. -lpamc + +clean: + rm -f test.libpamc test.libpamc.o diff --git a/libpamc/test/regress/run_test.sh b/libpamc/test/regress/run_test.sh new file mode 100755 index 0000000..6922f03 --- /dev/null +++ b/libpamc/test/regress/run_test.sh @@ -0,0 +1,6 @@ +#!/bin/sh + +LD_LIBRARY_PATH=../.. ; export LD_LIBRARY_PATH +PAMC_AGENT_PATH="../agents" ; export PAMC_AGENT_PATH + +./test.libpamc diff --git a/libpamc/test/regress/test.libpamc.c b/libpamc/test/regress/test.libpamc.c new file mode 100644 index 0000000..4251b4f --- /dev/null +++ b/libpamc/test/regress/test.libpamc.c @@ -0,0 +1,343 @@ +/* + * This is a small test program for testing libpamc against the + * secret@here agent. It does the same as the test.secret@here perl + * script in this directory, but via the libpamc API. + */ + +#include <stdio.h> +#include <stdint.h> +#include <string.h> +#include <security/pam_client.h> +#include <ctype.h> + +struct internal_packet { + int length; + int at; + char *buffer; +}; + + +void append_data(struct internal_packet *packet, int extra, const char *data) +{ + if ((extra + packet->at) >= packet->length) { + if (packet->length == 0) { + packet->length = 1000; + } + /* make sure we have at least a char extra space available */ + while (packet->length <= (extra + packet->at)) { + packet->length <<= 1; + } + packet->buffer = realloc(packet->buffer, packet->length); + if (packet->buffer == NULL) { + fprintf(stderr, "out of memory\n"); + exit(1); + } + } + + if (data != NULL) { + memcpy(packet->at + packet->buffer, data, extra); + } + packet->at += extra; + + /* assisting string manipulation */ + packet->buffer[packet->at] = '\0'; +} + +void append_string(struct internal_packet *packet, const char *string, + int with_nul) +{ + append_data(packet, strlen(string) + (with_nul ? 1:0), string); +} + +char *identify_secret(char *identity) +{ + struct internal_packet temp_packet; + FILE *secrets; + int length_id; + + temp_packet.length = temp_packet.at = 0; + temp_packet.buffer = NULL; + + append_string(&temp_packet, "/home/", 0); + append_string(&temp_packet, getlogin(), 0); + append_string(&temp_packet, "/.secret@here", 1); + + secrets = fopen(temp_packet.buffer, "r"); + if (secrets == NULL) { + fprintf(stderr, "server: failed to open\n [%s]\n", + temp_packet.buffer); + exit(1); + } + + length_id = strlen(identity); + for (;;) { + char *secret = NULL; + temp_packet.at = 0; + + if (fgets(temp_packet.buffer, temp_packet.length, secrets) == NULL) { + fclose(secrets); + return NULL; + } + + if (memcmp(temp_packet.buffer, identity, length_id)) { + continue; + } + + fclose(secrets); + for (secret=temp_packet.buffer; *secret; ++secret) { + if (*secret == ' ' || *secret == '\n' || *secret == '\t') { + break; + } + } + for (; *secret; ++secret) { + if (!(*secret == ' ' || *secret == '\n' || *secret == '\t')) { + break; + } + } + + for (temp_packet.buffer=secret; *temp_packet.buffer; + ++temp_packet.buffer) { + if (*temp_packet.buffer == ' ' || *temp_packet.buffer == '\n' + || *temp_packet.buffer == '\t') { + break; + } + } + if (*temp_packet.buffer) { + *temp_packet.buffer = '\0'; + } + + return secret; + } + + /* NOT REACHED */ +} + +/* + * This is a hack, and is fundamentally insecure. All our secrets will be + * displayed on the command line for someone doing 'ps' to see. This + * is just for programming convenience in this instance, since this + * program is simply a regression test. The pam_secret module should + * not do this, but make use of md5 routines directly. + */ + +char *create_digest(int length, const char *raw) +{ + struct internal_packet temp_packet; + FILE *pipe; + + temp_packet.length = temp_packet.at = 0; + temp_packet.buffer = NULL; + + append_string(&temp_packet, "echo -n '", 0); + append_string(&temp_packet, raw, 0); + append_string(&temp_packet, "'|/usr/bin/md5sum -", 1); + + fprintf(stderr, "am attempting to run [%s]\n", temp_packet.buffer); + + pipe = popen(temp_packet.buffer, "r"); + if (pipe == NULL) { + fprintf(stderr, "server: failed to run\n [%s]\n", temp_packet.buffer); + exit(1); + } + + temp_packet.at = 0; + append_data(&temp_packet, 32, NULL); + + if (fgets(temp_packet.buffer, 33, pipe) == NULL) { + fprintf(stderr, "server: failed to read digest\n"); + exit(1); + } + if (strlen(temp_packet.buffer) != 32) { + fprintf(stderr, "server: digest was not 32 chars?? [%s]\n", + temp_packet.buffer); + exit(1); + } + + fclose(pipe); + + return temp_packet.buffer; +} + +void packet_to_prompt(pamc_bp_t *prompt_p, uint8_t control, + struct internal_packet *packet) +{ + PAM_BP_RENEW(prompt_p, control, packet->at); + PAM_BP_FILL(*prompt_p, 0, packet->at, packet->buffer); + packet->at = 0; +} + +void prompt_to_packet(pamc_bp_t prompt, struct internal_packet *packet) +{ + int data_length; + + data_length = PAM_BP_LENGTH(prompt); + packet->at = 0; + append_data(packet, data_length, NULL); + + PAM_BP_EXTRACT(prompt, 0, data_length, packet->buffer); + + fprintf(stderr, "server received[%d]: {%d|0x%.2x|%s}\n", + data_length, + PAM_BP_SIZE(prompt), PAM_BP_RCONTROL(prompt), + PAM_BP_RDATA(prompt)); +} + +int main(int argc, char **argv) +{ + pamc_handle_t pch; + pamc_bp_t prompt = NULL; + struct internal_packet packet_data, *packet; + char *temp_string, *secret, *user, *a_cookie, *seqid, *digest; + const char *cookie = "123451234512345"; + int retval; + + packet = &packet_data; + packet->length = 0; + packet->at = 0; + packet->buffer = NULL; + + pch = pamc_start(); + if (pch == NULL) { + fprintf(stderr, "server: unable to get a handle from libpamc\n"); + exit(1); + } + + temp_string = getlogin(); + if (temp_string == NULL) { + fprintf(stderr, "server: who are you?\n"); + exit(1); + } +#define DOMAIN "@local.host" + user = malloc(1+strlen(temp_string)+strlen(DOMAIN)); + if (user == NULL) { + fprintf(stderr, "server: out of memory for user id\n"); + exit(1); + } + sprintf(user, "%s%s", temp_string, DOMAIN); + + append_string(packet, "secret@here/", 0); + append_string(packet, user, 0); + append_string(packet, "|", 0); + append_string(packet, cookie, 0); + packet_to_prompt(&prompt, PAM_BPC_SELECT, packet); + + /* get the library to accept the first packet (which should load + the secret@here agent) */ + + retval = pamc_converse(pch, &prompt); + fprintf(stderr, "server: after conversation\n"); + if (PAM_BP_RCONTROL(prompt) != PAM_BPC_OK) { + fprintf(stderr, "server: prompt had unexpected control type: %u\n", + PAM_BP_RCONTROL(prompt)); + exit(1); + } + + fprintf(stderr, "server: got a prompt back\n"); + + prompt_to_packet(prompt, packet); + + temp_string = strtok(packet->buffer, "|"); + if (temp_string == NULL) { + fprintf(stderr, "server: prompt does not contain anything"); + exit(1); + } + seqid = strdup(temp_string); + if (seqid == NULL) { + fprintf(stderr, "server: unable to store sequence id\n"); + } + + temp_string = strtok(NULL, "|"); + if (temp_string == NULL) { + fprintf(stderr, "server: no cookie from agent\n"); + exit(1); + } + a_cookie = strdup(temp_string); + if (a_cookie == NULL) { + fprintf(stderr, "server: no memory to store agent cookie\n"); + exit(1); + } + + fprintf(stderr, "server: agent responded with {%s|%s}\n", seqid, a_cookie); + secret = identify_secret(user); + fprintf(stderr, "server: secret=%s\n", secret); + + /* now, we construct the response */ + packet->at = 0; + append_string(packet, a_cookie, 0); + append_string(packet, "|", 0); + append_string(packet, cookie, 0); + append_string(packet, "|", 0); + append_string(packet, secret, 0); + + fprintf(stderr, "server: get digest of %s\n", packet->buffer); + + digest = create_digest(packet->at, packet->buffer); + + fprintf(stderr, "server: secret=%s, digest=%s\n", secret, digest); + + packet->at = 0; + append_string(packet, seqid, 0); + append_string(packet, "|", 0); + append_string(packet, digest, 0); + packet_to_prompt(&prompt, PAM_BPC_OK, packet); + + retval = pamc_converse(pch, &prompt); + fprintf(stderr, "server: after 2nd conversation\n"); + if (PAM_BP_RCONTROL(prompt) != PAM_BPC_DONE) { + fprintf(stderr, "server: 2nd prompt had unexpected control type: %u\n", + PAM_BP_RCONTROL(prompt)); + exit(1); + } + + prompt_to_packet(prompt, packet); + PAM_BP_RENEW(&prompt, 0, 0); + + temp_string = strtok(packet->buffer, "|"); + if (temp_string == NULL) { + fprintf(stderr, "no digest from agent\n"); + exit(1); + } + temp_string = strdup(temp_string); + + packet->at = 0; + append_string(packet, secret, 0); + append_string(packet, "|", 0); + append_string(packet, cookie, 0); + append_string(packet, "|", 0); + append_string(packet, a_cookie, 0); + + fprintf(stderr, "server: get digest of %s\n", packet->buffer); + + digest = create_digest(packet->at, packet->buffer); + + fprintf(stderr, "server: digest=%s\n", digest); + + if (strcmp(digest, temp_string)) { + fprintf(stderr, "server: agent doesn't know the secret\n"); + fprintf(stderr, "server: agent says: [%s]\n" + "server: server says: [%s]\n", temp_string, digest); + exit(1); + } else { + fprintf(stderr, "server: agent seems to know the secret\n"); + + packet->at = 0; + append_string(packet, cookie, 0); + append_string(packet, "|", 0); + append_string(packet, secret, 0); + append_string(packet, "|", 0); + append_string(packet, a_cookie, 0); + + digest = create_digest(packet->at, packet->buffer); + + fprintf(stderr, "server: putenv(\"AUTH_SESSION_TICKET=%s\")\n", + digest); + } + + + retval = pamc_end(&pch); + + fprintf(stderr, "server: agent(s) were %shappy to terminate\n", + retval == PAM_BPC_TRUE ? "":"un"); + + exit(!retval); +} diff --git a/libpamc/test/regress/test.secret@here b/libpamc/test/regress/test.secret@here new file mode 100755 index 0000000..67fe22e --- /dev/null +++ b/libpamc/test/regress/test.secret@here @@ -0,0 +1,151 @@ +#!/usr/bin/perl + +## +## this is a test script for regressing changes to the secret@here PAM +## agent +## + +$^W = 1; +use strict; +use IPC::Open2; + +$| = 1; + +my $whoami = `/usr/bin/whoami`; chomp $whoami; +my $cookie = "12345"; +my $user_domain = "$whoami\@local.host"; + +my $pid = open2(\*Reader, \*Writer, "../agents/secret\@here blah") + or die "failed to load secret\@here agent"; + +unless (-f (getpwuid($<))[7]."/.secret\@here") { + print STDERR "server: ". "no " .(getpwuid($<))[7]. "/.secret\@here file\n"; + die "no config file"; +} + +WriteBinaryPrompt(\*Writer, 0x02, "secret\@here/$user_domain|$cookie"); + +my ($control, $data) = ReadBinaryPrompt(\*Reader); + +print STDERR "server: ". "reply: control=$control, data=$data\n"; +if ($control != 1) { + die "expected 1 (OK) for the first agent reply; got $control"; +} +my ($seqid, $a_cookie) = split '\|', $data; + +# server needs to convince agent that it knows the secret before +# agent will give a valid response +my $secret = IdentifyLocalSecret($user_domain); +my $digest = CreateDigest($a_cookie."|".$cookie."|".$secret); + +print STDERR "server: ". "digest = $digest\n"; +WriteBinaryPrompt(\*Writer, 0x01, "$seqid|$digest"); + +# The agent will authenticate us and then reply with its +# authenticating digest. we check that before we're done. + +($control, $data) = ReadBinaryPrompt(\*Reader); +if ($control != 0x03) { + die "server: agent did not reply with a 'done' prompt ($control)\n"; +} + +unless ($data eq CreateDigest($secret."|".$cookie."|".$a_cookie)) { + die "server: agent is not authenticated\n"; +} + +print STDERR "server: agent appears to know secret\n"; + +my $session_authenticated_ticket + = CreateDigest($cookie."|".$secret."|".$a_cookie); + +print STDERR "server: should putenv(" + ."\"AUTH_SESSION_TICKET=$session_authenticated_ticket\")\n"; + +exit 0; + +sub CreateDigest ($) { + my ($data) = @_; + + my $pid = open2(\*MD5out, \*MD5in, "/usr/bin/md5sum -") + or die "you'll need /usr/bin/md5sum installed"; + + my $oldfd = select MD5in; $|=1; select $oldfd; + print MD5in "$data"; + close MD5in; + my $reply = <MD5out>; + ($reply) = split /\s/, $reply; + print STDERR "server: ". "md5 said: <$reply>\n"; + close MD5out; + + return $reply; +} + +sub ReadBinaryPrompt ($) { + my ($fd) = @_; + + my $buffer = " "; + my $count = read($fd, $buffer, 5); + if ($count == 0) { + # no more packets to read + return (0, ""); + } + + if ($count != 5) { + # broken packet header + return (-1, ""); + } + + my ($length, $control) = unpack("N C", $buffer); + if ($length < 5) { + # broken packet length + return (-1, ""); + } + + my $data = ""; + $length -= 5; + while ($count = read($fd, $buffer, $length)) { + $data .= $buffer; + if ($count != $length) { + $length -= $count; + next; + } + + print STDERR "server: ". "data is [$data]\n"; + + return ($control, $data); + } + + # broken packet data + return (-1, ""); +} + +sub WriteBinaryPrompt ($$$) { + my ($fd, $control, $data) = @_; + + my $length = 5 + length($data); + printf STDERR "server: ". "{%d|0x%.2x|%s}\n", $length, $control, $data; + my $bp = pack("N C a*", $length, $control, $data); + print $fd $bp; + + print STDERR "server: ". "control passed to agent\@here\n"; +} + +sub IdentifyLocalSecret ($) { + my ($identifier) = @_; + my $secret; + + my $whoami = `/usr/bin/whoami` ; chomp $whoami; + if (open SECRETS, "< " .(getpwuid($<))[7]. "/.secret\@here") { + my $line; + while (defined ($line = <SECRETS>)) { + my ($id, $sec) = split /[\s]/, $line; + if ((defined $id) && ($id eq $identifier)) { + $secret = $sec; + last; + } + } + close SECRETS; + } + + return $secret; +} |