Securing Applications on Linux with PAM

The basic concepts of PAM (Pluggable Authentication Module), developing a PAM-enabled application, a PAM authentication module and writing the PAM configuration file.
Developing a Linux PAM-Enabled Application

To PAM-enable an application, we need to invoke the appropriate authentication routines in the PAM library. We also need to provide a “conversation function” that the module can use to communicate directly with the application.

The authentication routines of the PAM API consist of the following three primary functions:

  1. pam_start(): the first of the PAM functions that must be called by an application. It initializes the PAM library, reads the PAM configuration file and loads the desired authentication modules in the order in which they are mentioned in the configuration file. It returns a handle to the PAM library that the application can make use of for all further interactions with the library.

  2. pam_end(): the last function an application should call in the PAM library. Upon its return, the handle to the PAM library is no longer valid, and all memory associated with it will be invalidated.

  3. pam_authenticate(): this function serves as an interface to the authentication mechanisms of the loaded modules. It is called by the application when it needs to authenticate a user who is requesting service.

In addition to the authentication routines, the PAM API also provides the following functions, which the application can invoke:

  • pam_acct_mgmt(): checks whether the current user's account is valid.

  • pam_open_session(): begins a new session.

  • pam_close_session(): closes the current session.

  • pam_setcred(): manages user credentials.

  • pam_chauthtok(): changes the user's authentication token.

  • pam_set_item(): writes state information for the PAM session.

  • pam_get_item(): retrieves state information for PAM session.

  • pam_strerror(): returns an error string.

These PAM API routines are made available to the application by the security/pam_appl.h interface.

The conversation function facilitates direct communication between a loaded module and the application. It typically provides a means for the module to prompt the user for a user name, password and so on. The signature of the conversation function, conv_func, is as follows:

int conv_func (int,const struct pam_message **,
                struct pam_response **,void *);

The loaded authentication module prompts the application for some input via the pam_message structure. The application sends the requested information to the module through the pam_response structure.

But, how does the module get a pointer to the conversation function? The answer is the conversation structure: struct pam_conv. The conversation structure needs to be initialized by the application with the pointer to the conversation function. After initialization, the conversation structure is passed as an argument to the PAM library during the call to pam_start(). Using this pointer, the module can then begin communication with the conversation function.

Putting Them Together

Now, let's develop an application that returns the current time. This application is one that is required to authenticate the user before providing service.

First, include the necessary headers. The header file security/pam_appl.h is the interface to the PAM API. Then, initialize the conversation structure:

static struct pam_conv conv = {
        my_conv,        //function pointer to the
                        //conversation function
        NULL
};

Then write the main() method. To do this, first load the PAM library. We know that an application needs to call the methods in the PAM library in order to delegate the required authentication tasks. But how does the application get a handle to the PAM library, libpam? A call to pam_start() initializes the libpam with the service_name of the application requiring the authentication service, the user name of the individual to be authenticated and a pointer to the pam_conv structure. This function returns a handle to the libpam, *pamh, that provides continuity for successive calls to the PAM library:

pam_handle_t *pamh = NULL;
int retval = 0;
retval =  pam_start("check_user",NULL,&conv,&pamh);
if(retval != PAM_SUCCESS)
        exit(0);
If we do not want to pass the user name to pam_start(), we can pass NULL. The loaded authentication module then will prompt the user for it at a later point in time with the conversation function.

The second step in writing the main() method is to authenticate the user. Now comes the moment of truth where we decide whether the user is who he claims to be. How do we discover this? The function pam_authenticate() serves as an interface to the authentication mechanisms of the loaded modules. It verifies the user name and password supplied by the user by interacting with the appropriate authentication module. It returns PAM_SUCCESS on success, and if there is no match, some error value indicating the nature of failure is returned:

retval = pam_authenticate(pamh,0);
if(retval == PAM_SUCCESS)
        printf("%s\n","Authenticated.");
else
        printf("%s\n","Authentication Failed.");

You may notice we pass the handle pamh, which we have obtained from the earlier call to pam_start().

The third step in this process is providing access to the desired service. Now that the user is authenticated, he will be provided with access to the requested service. As an example, our service displays the current time:

return current_time();

Finally, unload the PAM library. After the user has finished using the application, the PAM library needs to be unloaded. Also, the memory associated with the handle pamh needs to be invalidated. We achieve this with a call to pam_end():

int pam_ status = 0; if(pam_end(pamh,pam_status) !=
PAM_SUCCESS) { pamh = NULL;
        exit(1);
}
The value is taken by the second argument to pam_end(). pam_status is used as an argument to the module-specific callback function, cleanup(). In this way, the module can perform any last-minute tasks that are appropriate to the module before it is unlinked. On successful return of the function, all memory associated with the handle pamh is released.

______________________

Comments

Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.

getPassword function is incorrect

prashanth's picture

Hi
your getPassword function is incorrect

while((i = getch()) != '\n')
buf[i++] = i;
is incorrect

Very crisp and to the point.

Sunil's picture

Very crisp and to the point. Great work !

Webinar
One Click, Universal Protection: Implementing Centralized Security Policies on Linux Systems

As Linux continues to play an ever increasing role in corporate data centers and institutions, ensuring the integrity and protection of these systems must be a priority. With 60% of the world's websites and an increasing share of organization's mission-critical workloads running on Linux, failing to stop malware and other advanced threats on Linux can increasingly impact an organization's reputation and bottom line.

Learn More

Sponsored by Bit9

Webinar
Linux Backup and Recovery Webinar

Most companies incorporate backup procedures for critical data, which can be restored quickly if a loss occurs. However, fewer companies are prepared for catastrophic system failures, in which they lose all data, the entire operating system, applications, settings, patches and more, reducing their system(s) to “bare metal.” After all, before data can be restored to a system, there must be a system to restore it to.

In this one hour webinar, learn how to enhance your existing backup strategies for better disaster recovery preparedness using Storix System Backup Administrator (SBAdmin), a highly flexible bare-metal recovery solution for UNIX and Linux systems.

Learn More

Sponsored by Storix