-
-
Save rmohr/a131ae3b095095aff134 to your computer and use it in GitHub Desktop.
#include <stdio.h> | |
#include <stdlib.h> | |
#include <assert.h> | |
#include <string.h> | |
#include <unistd.h> /* getpass */ | |
#include <security/pam_appl.h> /* pam_start, pam_conv, pam_end, ... */ | |
#define TRY(x) ret = (x); printf("PAM: %s\n", pam_strerror(handle, ret)); if (ret != PAM_SUCCESS) goto finally | |
int test_conv(int num_msg, const struct pam_message **msg, | |
struct pam_response **resp, void *appdata_ptr){ | |
const struct pam_message* msg_ptr = *msg; | |
struct pam_response * resp_ptr = NULL; | |
int x = 0; | |
*resp = calloc(sizeof(struct pam_response), num_msg); | |
for (x = 0; x < num_msg; x++, msg_ptr++){ | |
char* resp_str; | |
switch (msg_ptr->msg_style){ | |
case PAM_PROMPT_ECHO_OFF: | |
case PAM_PROMPT_ECHO_ON: | |
resp_str = getpass(msg_ptr->msg); | |
resp[x]->resp= strdup(resp_str); | |
break; | |
case PAM_ERROR_MSG: | |
case PAM_TEXT_INFO: | |
printf("PAM: %s\n", msg_ptr->msg); | |
break; | |
default: | |
assert(0); | |
} | |
} | |
return PAM_SUCCESS; | |
} | |
int main(int argc, char** argv){ | |
int len = 1000; | |
const char service[] = "passwd"; | |
char user[len]; | |
const char *changed_username = NULL; | |
struct pam_conv conv; | |
char resp_str[len]; | |
conv.conv=test_conv; | |
pam_handle_t* handle; | |
int ret; | |
printf("Username: "); | |
fgets(user,len, stdin); | |
strtok(user, "\n"); | |
TRY( pam_start(service, user, &conv, &handle )); | |
TRY( pam_authenticate(handle, 0)); | |
TRY( pam_acct_mgmt(handle, 0)); | |
TRY( pam_get_item(handle, PAM_USER,(const void**) &changed_username )); | |
if (changed_username != NULL) { | |
printf("PAM: %s\n", changed_username); | |
} | |
finally: | |
pam_end(handle, ret); | |
return ret; | |
} |
I believe the following line is wrong;
resp[x]->resp= strdup(resp_str);
The problem is resp is a pointer to a pointer to an array of pam_response structs, not a pointer to an array of pointers to pam_response structs. The difference is subtle, and the result is exactly the same if the array contains only one entry, but will break if num_msg is ever greater than 1.
Fortunately there is confusion between the Sun and Linux PAM implementations around this interpretation for the **msg argument. This has resulted in nearly everyone always using num_msg=1 to avoid compatibility errors. However, some people work around this confusion by making sure that **msg works for both interpretations by setting it to be a pointer to an array of pointers to pam_response structs, where the pam_response structs are themselves in an array. For things that use this approach, num_msg can be greater than 1, which will break this.
On Debian/Ubuntu, the package pam-devel
was not found, but the package libpam0g-dev
worked for me.
Doesn't the pam_conv man page say to release the resources pam_message and pam_response structures, where does that fit in this code ?
It is the caller's responsibility to release both, this array and the responses themselves
"both" here means the pam_message's and pam_response's
"caller" is the pam module, this is the application code. So for example if you were implementing your own module side pam_sm_authenticate and you'd call pam_get_item(handle, PAM_CONV, &conv)
and then conv->conv( ... )
should't it be
(*resp)[idx].resp=strdup(...)
?
Dependencies
Fedora:
Debian/Ubuntu:
Compilation