/*
* 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 <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, u_int8_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);
}