Introduction

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      pam_unix.so             nullok #set_secrpc
        auth     required       pam_securetty.so
        auth     required       pam_nologin.so
        auth     required       pam_env.so
        auth     required       pam_mail.so
        account  required       pam_unix.so
        password required       pam_pwcheck.so          nullok use_cracklib
        password required       pam_unix.so             nullok use_first_pass use_authtok
        session  required       pam_unix.so             none # debug or trace
        session  required       pam_limits.so        
      

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 java.io.BufferedReader;
        import java.io.IOException;
        import java.io.InputStreamReader;
        
        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));
                }
                pam.end();
            }
        
            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(System.in));
                    for (int i=0; i<messages.length; i++) {
                        System.out.println(messages[i].getMsg());
                        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 libjaaspam.so to /usr/local/lib and run ldconfig or set LD_LIBRARY_PATH=./target/native to the directory where libjaaspam.so resides.

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