/* * 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 #include #include #include 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); }