Many times in our applications we have to authenticate users and provide interfaces to them according to the privileges granted to them. A typical solution would be to maintain a list of usernames and their encrypted passwords in a file or as a table in a database, which we may look up for each user who logs in. In this approach we have to encrypt and decrypt data ourselves, and is the most useful solution in most cases. However, if the application demands that we use the user accounts already present on the Linux machine, we have to use a different solution. This is the content of this article-authentication of existing users under Linux using the Linux PAM (Pluggable Authentication Modules). A prerequisite for this article is a
background in programming in C/C++ under Linux and some experience with gcc.
Before we get into a description of the PAM libraries, let's look at the way Linux stores usernames and passwords. We could say that the core of the Linux login management system is the file/etc/passwd. This file contains various fields relating to the username, account management information, the UID (user id), GID (group id), home path and shell path. The actual passwords are stored in an encrypted form in the /etc/shadow file. Two common encryption techniques used by most Linux distributions are the shadow and md5 techniques. So under the traditional technique, we would have to process the passwords ourselves and compare them with the stored versions. This, however, depends totally upon the encryption technique used. Here is where the Linux PAM library scores its point.
Using PAM
The Linux PAM library provides us with a set of easy to use routines that let us manage authentication of existing Linux logins without having to encrypt and decrypt data ourselves. So if the encryption technique changes, we do not have to modify our application, the libraries present on the new Linux system will handle the encryption, thus, improving the portability of applications. To demonstrate the power of this set of libraries, we write a simple program that mimics the 'su' or switch user command under Linux. This command allows the user to invoke a shell with the privileges of another user by entering the correct login information.
|
To accomplish this task, we follow the following steps. First, we get the username from the user, which is provided on the command line. Next we invoke the PAM library, which prompts us for a password. After this, we check whether or not the credentials supplied are valid and whether the user is allowed to maintain a session on the machine. If the user passes all these tests, we get the uid of that particular user from the etc/passwd file using system calls. We then change our effective uid to that of the user and execute the bash shell. This in effect completes the operation of the 'su' command.
The complete source code for the following program can be found in the file
pam.c.
Programming example
#define DEBUG
#include
#include
#include
#include
char *pass;
struct pam_response *temp;
extern int convert(int num_msg,const struct pam_message **msg,struct pam_response **resp,void *appdata_ptr)
{
printf("\n*Callback activated with num_msg=%d\t",num_msg);
//printf("\n*msg passed =%s",msg<0>->msg);
resp<0>=temp;
.
.
return PAM_SUCCESS;
}
static struct pam_conv conv = {
convert,
NULL
};
int main(int argc, char *argv<>)
{
pam_handle_t *pamh=NULL;
int retval;
struct pam_response *pp=NULL;
.
.
pass=argv<2>;
user = argv<1>;
.
.
retval = pam_start("check_user", user, &conv, &pamh);
if(retval==PAM_SUCCESS)
printf("\n*Pam started successfully");
if (retval == PAM_SUCCESS)
retval = pam_authenticate(pamh, 0);
if (retval == PAM_SUCCESS)
{
printf("\n*pam_authenticate returned success");
retval = pam_acct_mgmt(pamh, 0);
}
err=pam_strerror(pamh,retval);
.
.
if (pam_end(pamh,retval) != PAM_SUCCESS)
{
pamh = NULL;
fprintf(stderr, "check_user: failed to release authenticator\n");
exit(1);
}
if (retval == PAM_SUCCESS)
{
p=getpwnam(user);
uid=p->pw_uid;
printf("\n* Switching to a shell with uid %d",uid);
setuid(uid);
execlp("bash","bash",(char *)NULL);
}
else
return 0;
}
This program consists of two parts: one is the main function and the other is the callback provided to PAM. The functions pam_start(), pam_authenticate() and pam_acct_mgmt() start the PAM subsystem, check a particular set of credentials and check whether or not the user is allowed to maintain a session, respectively. Pam_strerror() is analogous to the function perror() and pam_end() shuts down the PAM subsystem. These calls reside in the main() function. Once we have the user authenticated, we obtain his account information using the getpwnam() system call that searches the etc/passwd file searching for a matching name. The uid is returned in passwd structure returned as a pointer. After this we change the effective uid of the program by the use of the setuid call and then execute the bash shell.
The other function here is the callback function. This callback is activated whenever the PAM subsystem requires any data-for example, a password in this case. While the Linux PAM library does provide a built in callback or conversion function, we choose to write our own for the sake of gaining more power and flexibility in our programs. The callback function registered here is convert() and it receives data through the parameters num_msg and msg and returns an array of data using resp. This function is registered during the call of the pam_start() function and is passed as a function pointer in a structure of type
pam_conv.
To run this program we first compile it with the command gcc -lpam -ldl -lpam_misc pam.c. As usual the output file is called a.out, and can be run with the command ./a.out
#%PAM-1.0
auth required pam_stack.so service=system-auth
account required pam_stack.so service=system-auth
password required pam_stack.so service=system-auth
session required pam_stack.so service=system-auth
Under PCQLinux 2004 we have to save this file in the etc/pam.d directory along with the other configuration scripts. A point to be noted here is that this program has to be running with root privileges so either it has to be created and run by the root login or has to be made a setuid program so that it can gain the appropriate privileges when it is executed.
To conclude, we would like to state how widely used this particular library is. As you may have noticed while creating the configuration script, there are a lot of very popular programs like the login process and the x server to name a few. Even the configuration script for the actual su command is present in the etc/pam.d directory. Practically this library can be used while creating programs such as ftp servers where user credentials are required. The callback function provides us with a lot of flexibility and hence with a little work can be coupled with a qt front end enabling us to use these functions in applications that utilize the facilities of the x server, be it gnome or kde. Finally, for people more interested in the minute details of the PAM library, the official documentation can be found in the /usr/docs directory under PCQLinux 2004, and contains a comprehensive set of manuals in postscript, pdf and html format. More information can be found at
www.kernel.org/pub/linux/libs/pam/.
Rakesh Iyer