You may read the original PAM documentation first. While PAM provides a native C API, the docs give you an idea about the PAM architecture. If you prefer no to delve into PAM specifics, you may access PAM indirectly through JAAS, using the classes from the package ch.odi.jaaspam. To use PAM directly, you only need the classes from the ch.odi.pam package.

Creating the PAM object

To create the PAM object two things are required:

  1. The PAM service name
  2. A custom callback handler

The PAM service name is the name of a PAM configuration file that typically resides in /etc/pam.d. A typical Linux "login" configuration looks like this:

        /etc/pam.d> cat login
        auth     requisite             nullok #set_secrpc
        auth     required
        auth     required
        auth     required
        auth     required
        account  required
        password required          nullok use_cracklib
        password required             nullok use_first_pass use_authtok
        session  required             none # debug or trace
        session  required        

You may choose an existing service name or define your own.

The callback handler is a class that implements the PamCallback interface. The callback handler will receive all PAM events and the feed the results back to PAM. Typically the callback handler will present a dialog to the user and retrieve the authenticaion credentials requested by PAM. It will then hand the credentials back to PAM.

Sample code

        package ch.odi.jaaspam;
        import ch.odi.pam.Pam;
        import ch.odi.pam.PamCallback;
        import ch.odi.pam.PamConstants;
        import ch.odi.pam.PamMessage;
        import ch.odi.pam.PamResponse;
         * On Linux call this as:
         * LD_LIBRARY_PATH=./target/native java -cp target/classes ch.odi.jaaspam.TestPam
         * @author Ortwin Glück
        public class TestPam {
            public TestPam() {
                Pam pam = new Pam("login", null, new MyPamCallback());
                pam.setItem(PamConstants.PAM_USER_PROMPT, "Enter username");
                System.out.println("User: "+ pam.getItem(PamConstants.PAM_USER));
                System.out.println("Service: "+ pam.getItem(PamConstants.PAM_SERVICE));
                int status = pam.authenticate();
                if (status == PamConstants.PAM_SUCCESS) { 
                    status = pam.accountManagement();
                    if ((status == PamConstants.PAM_SUCCESS) 
                            || (status == PamConstants.PAM_AUTHINFO_UNAVAIL)) {
                        System.out.println("User successfully authenticated");
                    } else {
                        System.out.println("Account has insufficient rights: "+ pam.getError(status));
                } else {
                    System.out.println("Authentication failed: "+ pam.getError(status));
            public static void main(String[] args) {
                new TestPam();
            private class MyPamCallback implements PamCallback {
                public int handle(PamMessage[] messages, PamResponse[] responses) {
                    BufferedReader r = new BufferedReader(new InputStreamReader(;
                    for (int i=0; i<messages.length; i++) {
                        try {
                            responses[i] = new PamResponse(r.readLine());
                        } catch (IOException e) {
                            return PamConstants.PAM_CONV_ERR;
                    return PamConstants.PAM_SUCCESS;

The above program will use the login service to authenticate a user. The source code is in src/test/.

The dynamic loader must also be able to find our native library that connects PAM to Java. You can either copy to /usr/local/lib and run ldconfig or set LD_LIBRARY_PATH=./target/native to the directory where resides.

The complete call of the sample program is then: LD_LIBRARY_PATH=./target/native java -cp target/classes ch.odi.jaaspam.TestPam