-
-
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