View Javadoc

1   package ch.odi.pam;
2   
3   
4   /**
5    * Java native interface to PAM.
6    * Loads the native libjaaspam.so.
7    *  
8    * @author Ortwin Gl?ck
9    */
10  public class Pam {
11      // keeps the pam handle returned by libpam.so    
12      private long pam_handle = 0L;
13      private PamCallback callback;
14      
15      static {
16          System.loadLibrary("jaas-pam");
17      }
18      
19      /**
20       * 
21       * @param serviceName The service name corresponding to a PAM service in /etc/pam.d
22       * @param user The username or <code>null</code>.
23       * @param callback The callback that will get messages from PAM during authenticate.
24       */
25      public Pam(String serviceName, String user, PamCallback callback) {
26          this.callback = callback;
27          // exactly one thread is allowed to call pam_start() of libpam.so
28          synchronized (Pam.class) {
29              int code = pam_start(serviceName, user);
30              if (code != PamConstants.PAM_SUCCESS) throw new PamError("PAM error code: "+ code);
31          }
32      }
33  
34      /**
35       * Frees all resources associated with this objects. Calling methods on this
36       * object after a call to end() will lead to undefined results!
37       */
38      public void end() {
39          // exactly one thread is allowed to call pam_end() of libpam.so
40          synchronized (Pam.class) {
41              if (pam_handle != 0L) {
42                  pam_end(pam_handle);
43                  pam_handle = 0L;
44              }
45          }
46      }
47      
48      protected void finalize() throws Throwable {
49          end();
50      }
51  
52      /**
53       * This function is used to obtain the value of the indicated item_type. Upon success returns
54       * the value of the corresponding item. 
55       *
56       * @param item_type see setStringItem
57       * @return The item or <code>null</code> on error
58       */
59      public String getItem(int item_type) {
60          return pam_get_str_item(pam_handle, item_type);
61      }
62      
63      /**
64       * This function is used to (re)set the value of an item.
65       * 
66       * @param item_type
67       * PAM_SERVICE
68       *  The service name (which identifies that PAM stack that libpam will use to authenticate the 
69       *  program).
70       *
71       * PAM_USER
72       *  The username of the entity under who's identity service will be given. That is, following
73       *  authentication, PAM_USER identifies the local entity that gets to use the service. 
74       *  Note, this value can be mapped from something (eg., "anonymous") to something else
75       *  (eg. "guest119") by any module in the PAM stack. As such an application should consult
76       *  the value of PAM_USER after each call to a pam_*() function.
77       *
78       * PAM_USER_PROMPT
79       *  The string used when prompting for a user's name. The default value for this string 
80       *  is "Please enter username: ".
81       *
82       * PAM_TTY
83       *  The terminal name: prefixed by /dev/ if it is a device file; for graphical, X-based, 
84       *  applications the value for this item should be the $DISPLAY variable.
85       *
86       * PAM_RUSER
87       *  The requesting entity: user's username for a locally requesting user or a remote requesting
88       *  user - generally an application or module will attempt to supply the value that is most
89       *  strongly authenticated (a local account before a remote one. The level of trust in this
90       *  value is embodied in the actual authentication stack associated with the application, so
91       *  it is ultimately at the discretion of the system administrator. It should generally match
92       *  the current PAM_RHOST value. That is, "PAM_RUSER@PAM_RHOST" should always identify the
93       *  requesting user. In some cases, PAM_RUSER may be NULL. In such situations, it is unclear
94       *  who the requesting entity is.
95       *
96       * PAM_RHOST
97       *  The requesting hostname (the hostname of the machine from which the PAM_RUSER entity is
98       *  requesting service). That is "PAM_RUSER@PAM_RHOST" does identify the requesting user.
99       *  "luser@localhost" or "evil@evilcom.com" are valid "PAM_RUSER@PAM_RHOST" examples. 
100      *  In some applications, PAM_RHOST may be NULL. In such situations, it is unclear where the
101      *  authentication request is originating from.
102      * @param item the value to set
103      * @return
104      * A successful call to this function returns PAM_SUCCESS. However, the application should
105      * expect at least one the following errors:
106      *
107      * PAM_SYSTEM_ERR
108      *  The pam_handle_t passed as a first argument to this function was invalid.
109      *  
110      * PAM_PERM_DENIED
111      *  An attempt was made to replace the conversation structure with a NULL value.
112      *  
113      * PAM_BUF_ERR
114      *  The function ran out of memory making a copy of the item.
115      *  
116      * PAM_BAD_ITEM
117      *  The application attempted to set an undefined or inaccessible item. 
118      */
119     public int setItem(int item_type, String item) {
120         return pam_set_str_item(pam_handle, item_type, item);
121     }
122     
123     /**
124      * This function serves as an interface to the authentication mechanisms of the loaded modules.
125      * 
126      * @param flags The single optional flag, which may be logically OR'd with PAM_SILENT, 
127      * takes the following value, PAM_DISALLOW_NULL_AUTHTOK.
128      * @return The value returned by this function is one of the following:
129      * PAM_SUCCESS
130      *  The user was authenticated 
131      * 
132      * PAM_AUTH_ERR
133      *  The user was not authenticated
134      *  
135      * PAM_CRED_INSUFFICIENT
136      *  For some reason the application does not have sufficient credentials to authenticate the user.
137      *  
138      * PAM_AUTHINFO_UNAVAIL
139      *  The modules were not able to access the authentication information. This might be due to a 
140      *  network or hardware failure etc.
141      *  
142      * PAM_USER_UNKNOWN
143      *  The supplied username is not known to the authentication service
144      *  
145      * PAM_MAXTRIES
146      *  One or more of the authentication modules has reached its limit of tries authenticating
147      *  the user. Do not try again.
148      *
149      * If one or more of the authentication modules fails to load, for whatever reason, this 
150      * function will return PAM_ABORT. 
151      */
152     public int authenticate(int flags) {
153         /*
154          * The authenticate method isn't synchronized because it blocks
155          * all other threads on PAM_FAIL_DELAY function (about 2 seconds).
156          * Take care that the pam auth modules are thread-safe.
157          * e.g.
158          * pam_unix.so alias pam_unix_auth.so can make trouble
159          * if old password encryption is used.
160          * 
161          * <pre>
162          * # SIGSEGV (0xb) at pc=0x400e8423, pid=24690, tid=12697890
163          * C [libc.so.6+0x67423] strlen+0x33
164          * C [pam_unix_auth.so+0x572d] _unix_verify_password+0x1dd
165          * </pre>
166          */
167         return pam_authenticate(pam_handle, flags);
168     }
169     
170     /**
171      * The same as calling authencicate(0).
172      * 
173      * @return
174      */
175     public int authenticate() {
176         return authenticate(0);
177     }
178     
179     /**
180      * This function is typically called after the user has been authenticated. It establishes
181      * whether the user's account is healthy. That is to say, whether the user's account is still
182      * active and whether the user is permitted to gain access to the system at this time. 
183      * @param flags Valid flags, any one of which, may be logically OR'd with PAM_SILENT, and are 
184      * the same as those applicable to the flags argument of authenticate.
185      * @return The normal response from this function is PAM_SUCCESS, however, specific failures are
186      * indicated by the following error returns:
187      * PAM_AUTHTOK_EXPIRED
188      *  The user is valid but their authentication token has expired. The correct response to this
189      *  return-value is to require that the user satisfies the pam_chauthtok() function before
190      *  obtaining service. It may not be possible for some applications to do this. In such cases,
191      *  the user should be denied access until such time as they can update their password.
192      *
193      * PAM_ACCT_EXPIRED
194      *  The user is no longer permitted to access the system.
195      *  
196      * PAM_AUTH_ERR
197      *  There was an authentication error.
198      *
199      * PAM_PERM_DENIED
200      *  The user is not permitted to gain access at this time.
201      *  
202      * PAM_USER_UNKNOWN
203      *  The user is not known to a module's account management component.
204      */
205     public int accountManagement(int flags) {
206         return pam_acct_mgmt(pam_handle, flags);
207     }
208     
209     /**
210      * The same as calling accountManagement(0).
211      * @return
212      */
213     public int accountManagement() {
214         return accountManagement(0);
215     }
216     
217     /**
218      * This function is used to set the module-specific credentials of the user. It is usually 
219      * called after the user has been authenticated, after the account management function has 
220      * been called but before a session has been opened for the user.
221      * 
222      * A credential is something that the user possesses. It is some property, such as a 
223      * Kerberos ticket, or a supplementary group membership that make up the uniqueness of a 
224      * given user. On a Linux (or UN*X system) the user's UID and GID's are credentials too. 
225      * However, it has been decided that these properties (along with the default supplementary 
226      * groups of which the user is a member) are credentials that should be set directly by the 
227      * application and not by PAM.
228      * 
229      * This function simply calls the pam_sm_setcred functions of each of the loaded modules. 
230      * @param flags Valid flags, any one of which, may be logically OR'd with PAM_SILENT, are:
231      * PAM_ESTABLISH_CRED
232      *  Set the credentials for the authentication service,
233      *  
234      * PAM_DELETE_CRED
235      *  Delete the credentials associated with the authentication service,
236      *  
237      * PAM_REINITIALIZE_CRED
238      * Reinitialize the user credentials, and
239      *  
240      * PAM_REFRESH_CRED
241      *  Extend the lifetime of the user credentials. 
242      *
243      * @return A successful return is signalled with PAM_SUCCESS. Errors that are especially 
244      * relevant to this function are the following:
245      *
246      * PAM_CRED_UNAVAIL
247      *  A module cannot retrieve the user's credentials.
248      *  
249      * PAM_CRED_EXPIRED
250      *  The user's credentials have expired.
251      *  
252      * PAM_USER_UNKNOWN
253      *  The user is not known to an authentication module.
254      *  
255      * PAM_CRED_ERR
256      *  A module was unable to set the credentials of the user. 
257      */
258     public int setCredentials(int flags) {
259         return pam_setcred(pam_handle, flags);
260     }
261     
262     /**
263      * This function is used to change the authentication token for a given user (as indicated by
264      * the state of this object). 
265      *
266      * @param flags The following is a valid but optional flag which may be logically
267      * OR'd with PAM_SILENT,
268      *
269      * PAM_CHANGE_EXPIRED_AUTHTOK
270      *  This argument indicates to the modules that the users authentication token (password)
271      *  should only be changed if it has expired. 
272      *  Note, if this argument is not passed, the application requires that all authentication 
273      *  tokens are to be changed.
274      * @return PAM_SUCCESS is the only successful return value, valid error-returns are:
275      *
276      * PAM_AUTHTOK_ERR
277      *  A module was unable to obtain the new authentication token.
278      *
279      * PAM_AUTHTOK_RECOVERY_ERR
280      *  A module was unable to obtain the old authentication token.
281      *
282      * PAM_AUTHTOK_LOCK_BUSY
283      *  One or more of the modules was unable to change the authentication token since it is currently locked.
284      *
285      * PAM_AUTHTOK_DISABLE_AGING
286      *  Authentication token aging has been disabled for at least one of the modules.
287      *
288      * PAM_PERM_DENIED
289      *  Permission denied.
290      *
291      * PAM_TRY_AGAIN
292      *  Not all of the modules were in a position to update the authentication token(s). In such a case none of the user's authentication tokens are updated.
293      *
294      * PAM_USER_UNKNOWN
295      *  The user is not known to the authentication token changing service. 
296      */
297     public int changeAuthToken(int flags) {
298         return pam_chauthtok(pam_handle, flags);
299     }
300     
301     /**
302      * This function is used to indicate that an authenticated session has begun. It is used to 
303      * inform the modules that the user is currently in a session. It should be possible for the 
304      * Linux-PAM library to open a session and close the same session (see section below) from 
305      * different applications.
306      *
307      * Currently, this function simply calls each of the corresponding functions of the loaded 
308      * modules. 
309      * 
310      * @param flags The only valid flag is PAM_SILENT and this is, of course, optional.
311      * @return If any of the required loaded modules are unable to open a session for the user, 
312      * this function will return PAM_SESSION_ERR.
313      */
314     public int openSession(int flags) {
315         return pam_open_session(pam_handle, flags);
316     }
317     
318     /**
319      * This function is used to indicate that an authenticated session has ended. It is used to 
320      * inform the modules that the user is exiting a session. It should be possible for the 
321      * Linux-PAM library to open a session and close the same session from different applications.
322      * 
323      * This function simply calls each of the corresponding functions of the loaded modules in the
324      * same order that they were invoked with openSession. 
325      * 
326      * @param flags The only valid flag is PAM_SILENT and this is, of course, optional.
327      * @return If any of the required loaded modules are unable to close a session for the user, 
328      * this function will return PAM_SESSION_ERR.
329      */
330     public int closeSession(int flags) {
331         return pam_close_session(pam_handle, flags);
332     }
333     
334     public String getError(int errorCode) {
335         return pam_strerror(pam_handle, errorCode);
336     }
337     
338     // Called back by the native library
339     private int conversation(PamMessage[] messages, PamResponse[] responses) {
340         try {
341             int ret = callback.handle(messages, responses);
342             return ret;
343         } catch (Throwable th) {
344             // do not propagate exceptions back to native code, but use a nice
345             // return code
346             th.printStackTrace();
347             return PamConstants.PAM_CONV_ERR;
348         }
349     }
350      
351     // native interface
352     private native int pam_start(String serviceName, String user);
353     private native void pam_end(long handle);
354     private native String pam_get_str_item(long handle, int item);
355     private native int pam_set_str_item(long handle, int item, String value);
356     private native int pam_authenticate(long handle, int flags);
357     private native String pam_strerror(long handle, int errnum);
358     private native int pam_acct_mgmt(long handle, int flags);
359     private native int pam_setcred(long handle, int flags);
360     private native int pam_chauthtok(long handle, int flags);
361     private native int pam_open_session(long handle, int flags);
362     private native int pam_close_session(long handle, int flags);
363 }