Last active
April 22, 2024 08:03
-
-
Save rmohr/a131ae3b095095aff134 to your computer and use it in GitHub Desktop.
Absolutely minimalistic pam authentication example
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#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; | |
} |
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(...)
?
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
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.