Skip to main content

Introduction to PAM

posted onJune 29, 2000
by hitbsecnews

This
article first appeared in Phrack
Issue #56

----|
INTRODUCTION

The
Pluggable Authentication Module (PAM) system is a means by which
programs can perform services relating to user authentication and
account maintenance. The authentication part is usually done
through a challenge-response interaction. Using PAM, an
administrator can customize the methods used by authenticating
programs without recompilation of those programs.

The
PAM system is comprised of four parts. The first part, libpam, is
the library which implements the PAM API. The second part is the
PAM configuration file, /etc/pam.conf. The third consists of a
suite of dynamically loadable binary objects, often called the
service modules, which handle the actual work of authentication.
The final part is comprised of the system commands which use (or
should use) the PAM API, such as login, su, ftp, telnet, etc...

----|
LIBPAM

The
authentication routines of the PAM API consist of three primary
functions: pam_start( const char *service_name, const char
*username, const struct pam_conv *conv, pam_handle_t **pamh_p );
pam_end( pam_handle_t *pamh, int exit_status ); pam_authenticate(
pam_handle_t *pamh, int flags ); The pam_start() and pam_end()
functions begin and end a PAM session. The arguments to
pam_start() are as follows:

+
service_name: a string specifying a particular service as defined
in the pam.conf file (see below)

+
username: the login name of the user to be authenticated

+
conv: a pointer to a pam_conv structure (more on this in a minute)

+
pamh_p: a double pointer to a pam_handle_t structure. The PAM
framework will allocate and deallocate the memory for the
structure, and an application should never access it directly.
Itis basically used by the PAM framework to deal with multiple
concurrent PAM sessions.

The
pam_conv structure looks like this:

struct
pam_conv {

int
(*conv)(int num_msg, const struct pam_message **msg,

struct
pam_response **resp, void *appdata_ptr);

void
*appdata_ptr;

}

*conv
is a pointer to a function in the application known as the PAM
conversation function. It will be discussed below. The appdata_ptr
points to application-specific data, and is not often used.

The
pam_end() function's arguments consist of the same pam_handle_t*
that was filled in by pam_start(), and an exit status. The exit
status is normally PAM_SUCCESS, but can be different in the event
of an unsuccessful PAM session. pam_end() will deallocate the
memory associated with the pam_handle_t*, and any attempt to
re-use the handle will likely result in a seg fault.

The
pam_authenticate() function again consists of the pam_handle_t*
filled in by pam_start(), and optional flags that can be passed to
the framework.

Some
other functions in the PAM API available to applications are as
follows (consult your system's documentation for a complete
description of its PAM API):

+
pam_set_item() - write state information for PAM session

+ pam_get_item() - retrieve state information for PAM session

+
pam_acct_mgmt() - checks whether the current user's account is

valid

+
pam_open_session() - begin a new session

+
pam_close_session() - close current session

+
pam_setcred() - manage user credentials

+
pam_chauthtok() - change user's authentication token

+
pam_strerror() - returns an error string, similar to perror()

----|
PAM.CONF

The
PAM configuration file is usually located in /etc/pam.conf. It is
divided into four sections: authentication, account management,
session management, and password management. A typical line looks
like this:

login
auth required /usr/lib/security/pam_unix.so.1 try_first_pass

The
first field is the service name. This is the service referred to
in the first argument to pam_start(). If the service requested by
pam_start() is not listed in pam.conf, the default service "other"
will be used. Other service names might be "su" and
"rlogin". If the service name is specified more than
once, the modules are said to be "stacked", and the
behavior of the framework will be determined by the value of the
third field, as discussed below.

The
second field denotes what action this particular service will
perform. The valid values are "auth" for authentication,
"account" for account management, "session"
for session management, and "password" for password
management. Not all applications will need to access every action.
For example, su will need only to access the "auth"
action, while "passwd" should need only the "password"
action.

The
third field is known as the control field, and will require some
discussion. It indicates the behavior of the PAM framework if the
user should fail the authentication. Valid values for this field
are "requisite", "required", "sufficient",
and "optional":

+
"requisite" means that if the user fails authentication
for this particular module, the framework will immediately return
a failure, and no other modules will be invoked.

+
"required" denotes that if a user fails authentication,
the framework will return a failure only after all other modules have been invoked. This is done so that the user will not know for
which module authentication was denied. For a user to successfully
authenticate, all "required" modules have to return
success.

+
"optional" means that the user will be allowed access
even if authentication fails. In the event of failure, the next
module on the stack will be processed.

+
"sufficient" means that if a user passes this particular
module, the framework will immediately return success, even if
subsequent modules have "requisite" or "required"
control values. Like "optional", "sufficient"
will allow access even if authentication fails.

Note
that if any module returns success, the user will succeed
authentication with the only exception being if the user
previously failed to authenticate with a "required"
module.

The
fourth field in pam.conf is the path to the authentication module.
The path can differ between systems. For example, the PAM modules
are located in /usr/lib in the Linux-PAM implementation, while
Solaris maintains the modules in /usr/lib/security.

The
fifth field is a space-separated list of module-dependent options,
which are passed to the authentication module whenever it is
invoked. Consult the specific module's man page for details.

----|
MODULES

Each
PAM module is essentially a library which must export specified
functions.These functions are called by the PAM framework. The
functions exported by the library are:

+
pam_sm_authenticate()

+
pam_sm_setcred()

+
pam_sm_acct_mgmt()

+
pam_sm_open_session()

+
pam_sm_close_session()

+
pam_sm_chauthtok()

If
an implementer decides not to support a particular action within a
module, the module should return PAM_SUCCESS for that action. For
example, if a module is not designed to support account
management, the pam_sm_acct_mgmt() function should simply return
PAM_SUCCESS.

The
declaration for pam_sm_authenticate() is as follows:

extern
int pam_sm_authenticate( pam_handle_t *pamh, int flags,

int
argc, char **argv); where pamh is a pointer to a PAM handle which has been filled in by the framework, flags is the set of flags
passed to the framework by the application's call to
pam_authenticate(), and argc and argv are the number and values of
the optional arguments for this service in pam.conf.

A
simple pam_sm_authenticate() for the pam_unix module might look
like this:

#include

#include
<...>

extern
int

pam_sm_authenticate(
pam_handle_t *pamh, int flgs, int c, char **v )

{

char
*user;

char
*passwd;

struct
passwd *pwd;

int
ret;

/*
ignore flags and optional arguments */

if
( (ret = pam_get_user( ..., &user )) != PAM_SUCCESS )

return
ret;

if
( (ret = pam_get_pass( ..., &passwd )) != PAM_SUCCESS )

return
ret;

if
( (pwd = getpwnam(user)) != NULL ) {

if
( !strcmp(pwd->pw_passwd, crypt(passwd)) )

return
PAM_SUCCESS;

else

return
PAM_AUTH_ERR;

}

return
PAM_AUTH_ERR;

}

Of
course, this function is grossly oversimplified, but it
demonstratesthe basic functionality of pam_sm_authenticate(). It
retrieves the user's login name and password from the framework,
then retrieves the user's encrypted password, and finally calls
crypt() on the user's password and compares the result with the
encrypted system password. Success or failure is determined on
this comparison. The functions pam_get_*() are calls to the
framework, and may not have the same declaration between
implementations.

----| THE APPLICATION

A
PAM application is fairly simple to implement. The portions that
deal with PAM must consist of a pam_start() and pam_end() pair,
and a PAM conversation function. Fortunately, the user-space PAM
API is well-defined and stable, and so the conversation function
will pretty much be boilerplate code (at least for a command-line
application). A simple implementation of su might look like this:

#include

#include
<...>

int
su_conv(int, const struct pam_message **,

struct
pam_response **, void *);

static
struct pam_conv pam_conv = { su_conv, NULL };

int

main(
int argc, char **argv )

{

pam_handle_t
*pamh;

int
ret;

struct
passwd *pwd;

/*
assume arguments are correct and argv[1] is the username */

ret
= pam_start("su", argv[1], &pam_conv, &pamh);

if
( ret == PAM_SUCCESS )

ret
= pam_authenticate(pamh, 0);

if
( ret == PAM_SUCCESS )

ret
= pam_acct_mgmt(pamh, 0);

if
( ret == PAM_SUCCESS ) {

if
( (pwd = getpwnam(argv[1])) != NULL )

setuid(pwd->pw_uid);

else
{

pam_end(pamh,
PAM_AUTH_ERR);

exit(1);

}

}

pam_end(pamh,
PAM_SUCCESS);

/*
return 0 on success, !0 on failure */

return
( ret == PAM_SUCCESS ? 0 : 1 );

}

int

su_conv(int
num_msg, const struct pam_message **msg,

struct
pam_response **resp, void *appdata)

{

struct
pam_message *m = *msg;

struct
pam_message *r = *resp;

while
( num_msg-- )

{

switch(m->msg_style)
{

case
PAM_PROMPT_ECHO_ON:

fprintf(stdout,
"%s", m->msg);

r->resp
= (char *)malloc(PAM_MAX_RESP_SIZE);

fgets(r->resp,
PAM_MAX_RESP_SIZE-1, stdin);

m++;
r++;

break;

case
PAM_PROMPT_ECHO_OFF:

r->resp
= getpass(m->msg);

m++;
r++;

break;

case
PAM_ERROR_MSG:

fprintf(stderr,
"%sn", m->msg);

m++;
r++;

break;

case
PAM_TEXT_MSG:

fprintf(stdout,
"%sn", m->msg);

m++;
r++;

break;

default:

break;

}

}

return
PAM_SUCCESS;

}

The
su_conv() function is the conversation function - it allows the
module to "converse" with the user. Each pam_message
struct has a message style, which indicates what type of data the
module wants. The PAM_PROMPT_ECHO_ON and PAM_PROMPT_ECHO_OFF cases
indicate that the module needs more information from the user. The
prompt used will be supplied by the module. In the case of
PAM_PROMPT_ECHO_OFF, the module usually wants a password. It is up
to the application to disable echoing of the characters. The *_MSG
cases are used for displaying messages on the user's terminal.

The
beauty of the PAM conversation is that all of the character-based
output can be replaced with calls to different display systems
without changing the authentication module. For example, the
getpass() could be replaced with get_gui_passwd() (or whatever) if
we want to implement a gui-based su-like command.

Note
that a real conversation function should be much more robust.
Also, the Linux-PAM implementation supplies the misc_conv()
conversation function for command-line interactions, which should
be used if a standard conversation function is all that is
required. Finally, it is usually the application's responsibility
to free() the memory allocated for the responses.

----|
FUN WITH MODULES

Now
that you have a familiarity with PAM, we can briefly discuss
custom authentication routines. For example, it is easy to modify
our earlier module so that, when authenticating the root user, a
second password must be typed:

extern
int

pam_sm_authenticate(
pam_handle_t *pamh, int flgs, int c, char **v )

{

char
*user;

char
*passwd;

struct
passwd *pwd;

int
ret;

/*
ignore flags and optional arguments */

if
( (ret = pam_get_user( ..., &user )) != PAM_SUCCESS )

return
ret;

if
( (ret = pam_get_pass( ..., &passwd )) != PAM_SUCCESS )

return
ret;

if
( (pwd = getpwnam(user)) != NULL ) {

if
( !strcmp(pwd->pw_passwd, crypt(passwd)) )

ret
= PAM_SUCCESS;

else

ret
= PAM_AUTH_ERR;

}

if
( !strcmp(user, "root") ) {

pam_display_message("root
user must enter secondary password");

if
( (ret = pam_get_pass( ..., &passwd )) != PAM_SUCCESS )

return
ret;

if
( !strcmp(get_second_root_pwd(), crypt(passwd)) )

ret
= PAM_SUCCESS;

else

ret
= PAM_AUTH_ERR;

}

return
ret;

}

Here
we assume there is a function get_second_root_pwd() which returns
some secret encrypted password. Of course, this example is a
little silly, but it demonstrates that we can be as free as we
want to be when designing our PAM modules. Also, because the
modules live in user space, they have access to all library
functions. If you have some sort of biometric scanner hooked up to
your machine and a library function that can access it, you could
write a PAM module that does the following:

thumbprint_t
*tp;

tp
= scan_thumbprint();

/*
or scan_retina() if you like James Bond */

if
( match_print_to_user(tp, user) )

return
PAM_SUCCESS;

----|
CONCLUSION

The
point is, the PAM modules are not limited to calling crypt() or
some similar function on a user's password. You are limited only
by what you can think of.

----|
REFERENCES

"Making
Login Services Independent of Authentication Technologies".

Samar,
Vipin and Charlie Lai.

http://www.sun.com/software/solaris/pam/pam.external.pd$

"The Linux-PAM System Administrator's Guide". Morgan, Andrew G.

http://www.kernel.org/pub/linux/libs/pam/Linux-PAM-html$

"The
Linux-PAM Module Writers' Guide". Morgan, Andrew G.

http://www.kernel.org/pub/linux/libs/pam/Linux-PAM-html$

"The
Linux-PAM Application Developers' Guide". Morgan, Andrew G.

http://www.kernel.org/pub/linux/libs/pam/Linux-PAM-html$

Linux-PAM
source code from FreeBSD 3.3 source packages.

FreeBSD

1.)
Advanced
Password Generator *CRACK*
-
metaray!abrams

2.)
News
Generator v3.0.17 *KEYGEN*
-
metaray!abrams

3.)
Introduction
to PAM
- Bryan Ericson

4.)
Taxonomy
of Communications Intelligence

- Psyops

5.)
A
look into Wiretapping
- Psyops

Source

Tags

Intel

You May Also Like

Recent News

Friday, November 29th

Tuesday, November 19th

Friday, November 8th

Friday, November 1st

Tuesday, July 9th

Wednesday, July 3rd

Friday, June 28th

Thursday, June 27th

Thursday, June 13th

Wednesday, June 12th

Tuesday, June 11th