Currently I have a graphical application that has two levels of access, operator and administrator. The login and authentication is all homebrewed and I'd like to switch the application to use PAM instead. I'm not sure what the right way to do that is.
Correct me if I'm wrong, but it seems that PAM boils down to a "yes" or "no" check--yes you can access this service, or no you can't. There's no provision for having various levels of access based on which user is logging in. I need to be able to tell who's an operator and who's an admin, though, and I want to be able to do it strictly through PAM if possible.
So my thought is that I'd set up two services with two different configurations, /etc/pam.d/pamdemo
for operators and /etc/pam.d/pamdemo-admin
for administrators. My application would then try to authenticate against pamdemo-admin
first, and if that fails then pamdemo
. If both fails, access is denied. Am I on the right track or am I completely off the rails?
Here's some sample C code I've written up as a proof of concept. When I do the login I don't want to prompt the user for his credentials twice. I've got it so it remembers the username across the two pam_start()
calls but I can't access pam_get_item(PAM_AUTHTOK)
from the application level to do the same caching for the password. And it was in trying to do so that I realized that there might be a totally different way to do this. I would like this application to work no matter what the authentication method, be it username/password or Kerberos tickets or fingerprints, whatever.
pam_handle_t *try_login(const char *service, int *retval)
{
static char * username = NULL;
struct pam_conv pam_conversation = { conv, NULL };
pam_handle_t * pamh;
*retval = pam_start(service, username, &pam_conversation, &pamh);
if (*retval == PAM_SUCCESS) *retval = pam_authenticate(pamh, 0);
if (*retval == PAM_SUCCESS) *retval = pam_acct_mgmt (pamh, 0);
if (*retval == PAM_SUCCESS) *retval = pam_open_session(pamh, 0);
if (username == NULL) {
if (pam_get_item(pamh, PAM_USER, (const void **) &username) == PAM_SUCCESS) {
username = strdup(username);
}
}
if (*retval != PAM_SUCCESS) {
fprintf(stderr, "%s: %s\n", service, pam_strerror(pamh, *retval));
pam_end(pamh, *retval);
pamh = NULL;
}
return pamh;
}
int main(void)
{
pam_handle_t *pamh = NULL;
int retval;
const char *service, *username;
if (!pamh) pamh = try_login("pamdemo-admin", &retval);
if (!pamh) pamh = try_login("pamdemo", &retval);
if (!pamh) {
fprintf(stderr, "Access denied.\n");
return 1;
}
pam_get_item(pamh, PAM_SERVICE, (const void **) &service);
pam_get_item(pamh, PAM_USER, (const void **) &username);
printf("Logged into %s as %s.\n", service, username);
pam_close_session(pamh, 0);
pam_end (pamh, retval);
return 0;
}
As written this demo program repeats the "password:" prompt. I don't want it to ask twice!