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.
To create the PAM object two things are required:
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.
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