The conversation function presented below is a greatly simplified version of OpenPAM's openpam_ttyconv(3). It is fully functional, and should give the reader a good idea of how a conversation function should behave, but it is far too simple for real-world use. Even if you are not using OpenPAM, feel free to download the source code and adapt openpam_ttyconv(3) to your uses; we believe it to be as robust as a tty-oriented conversation function can reasonably get.
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <security/pam_appl.h> int converse(int n, const struct pam_message **msg, struct pam_response **resp, void *data) { struct pam_response *aresp; char buf[PAM_MAX_RESP_SIZE]; int i; data = data; if (n <= 0 || n > PAM_MAX_NUM_MSG) return (PAM_CONV_ERR); if ((aresp = calloc(n, sizeof *aresp)) == NULL) return (PAM_BUF_ERR); for (i = 0; i < n; ++i) { aresp[i].resp_retcode = 0; aresp[i].resp = NULL; switch (msg[i]->msg_style) { case PAM_PROMPT_ECHO_OFF: aresp[i].resp = strdup(getpass(msg[i]->msg)); if (aresp[i].resp == NULL) goto fail; break; case PAM_PROMPT_ECHO_ON: fputs(msg[i]->msg, stderr); if (fgets(buf, sizeof buf, stdin) == NULL) goto fail; aresp[i].resp = strdup(buf); if (aresp[i].resp == NULL) goto fail; break; case PAM_ERROR_MSG: fputs(msg[i]->msg, stderr); if (strlen(msg[i]->msg) > 0 && msg[i]->msg[strlen(msg[i]->msg) - 1] != '\n') fputc('\n', stderr); break; case PAM_TEXT_INFO: fputs(msg[i]->msg, stdout); if (strlen(msg[i]->msg) > 0 && msg[i]->msg[strlen(msg[i]->msg) - 1] != '\n') fputc('\n', stdout); break; default: goto fail; } } *resp = aresp; return (PAM_SUCCESS); fail: for (i = 0; i < n; ++i) { if (aresp[i].resp != NULL) { memset(aresp[i].resp, 0, strlen(aresp[i].resp)); free(aresp[i].resp); } } memset(aresp, 0, n * sizeof *aresp); *resp = NULL; return (PAM_CONV_ERR); }