All Packages  This Package  Class Hierarchy  Class Search  Index

Class glguerin.authkit.imp.macosx.MacOSXAuthorization
java.lang.Object
   |
   +----glguerin.authkit.Authorization
           |
           +----glguerin.authkit.imp.macosx.MacOSXAuthorization

  Summary

public final class  MacOSXAuthorization
     extends glguerin.authkit.Authorization
{
          // Constructors 1
     public MacOSXAuthorization();

          // Methods 13
     public synchronized void attach(byte[]);
     public synchronized void attachPrivileged();
     public void authorize(Privilege, boolean);
     public synchronized void detach(boolean);
     public Process execPrivileged(String[]);
     public int getCapabilities();
     public Date getPastGrantedDate(Privilege, int);
     public Enumeration getPastGrantedPrivileges();
     public synchronized byte[] getSecretIdentifier();
     public int getSecretLength();
     public boolean isAvailable(Privilege);
     public Privilege makeExecPrivilege(String);
     public void preauthorize(Privilege);
}

MacOSXAuthorization is an implementation of Authorization for Mac OS X, based on the native Authorization Services API.

The policies and rules for MacOSXAuthorization are declared in the file /etc/authorization on the local host. It is an XML file, readable by members of the admin group, but writable only by root. The policies and rules are system-wide for the local host. There are no per-user policies or rules, as far as I know.

The constraints on the privileged Process are largely a consequence of the Authorization Services functionality for executing a privileged process. See the getCapabilities() doc-comment for details.

IMPLEMENTATION NOTES

A single Authorization may be called from multiple Threads. It must therefore be inherently thread-safe. All methods that manipulate state or depend on it are implemented thread-safely. In short, multiple Threads can safely share a single MacOSXAuthorization instance. Whether your program will deadlock if it shares a MacOSXAuthorization among Threads is another question entirely, and one only you can answer.

The authorize(), preauthorize(), and isAvailable() methods are all synchronized on the MacOSXAuthorization. In addition to ensuring the thread-safety of the instance's state, this also ensures that one Thread which is blocked in interactive authentication will prevent all other calling Threads from progressing until the user authenticates correctly or the authentication fails. This makes sense not only from a thread-safety perspective, but from the security perspective as well. You don't want multiple password dialogs, but you also don't want an authentication in-progress to return indeterminate or transitory results just because it's from another thread.

All the native functions are static and synchronized. That is, they are synchronized on the Class object.

The functions are static because we don't need the instance object in the native code. All the necessary state is communicated and managed by the opaque session-token.

The functions are synchronized because I don't know whether the Authorization Services API is re-entrant for a process. The Auth Services API will definitely block the calling thread when interacting with the user. By acquiring the class lock before calling the native code, we ensure that Java threads will properly block before making any Auth Services API call, preventing re-entrancy issues, but possibly introducing deadlock issues. If the blocked calling thread is the AWT event-thread, this may have AWT or Swing repurcussions, which may in turn affect program responsiveness.

For obvious security reasons that have nothing to do with Auth Services re-entrancy, the current thread must be blocked while obtaining interactive authentication. It's the length of time that the thread could be blocked that poses a potential problem.

As a rule, don't call anything from the AWT event-thread that may cause user interaction, and therefore lead to long or indefinite thread blocking.

REFERENCES:

On security in Mac OS X --
Developer -- Mac OS X Security
Contains links to sample code, mailing-list, API and reference documentation, etc.
On the Authorization Services concepts and principles --
Performing Privileged Operations With Authorization Services (HTML and PDF available).
On the Authorization Services C API --
Authorization Services Reference (HTML and PDF available).
On synchronized static native methods --
JNI Programmers's Guide & Specification: section 8.1.2, p.95 available as HTML or as PDF.




  Constructors

· MacOSXAuthorization

Summary  |  Top

   public MacOSXAuthorization() 

Vanilla constructor.



  Methods

· getCapabilities

Summary  |  Top
   public int getCapabilities() 

This imp has the following notable capabilities and/or constraints:

HAS_PROCESS_COUPLED_STREAMS
An execPrivileged() child Process has interdependent input and output streams.
HAS_PROCESS_INPUTSTREAM
An execPrivileged() child Process implements getInputStream().
HAS_PROCESS_OUTPUTSTREAM
An execPrivileged() child Process implements getOutputStream().
HAS_PROCESS_ELEVATED
An execPrivileged() child Process runs with an effective user-ID of root (superuser). Its real user-ID is unaffected, as are its real and effective group-ID.
HAS_SYNTHETIC_PREAUTH
The preauthorize() method is exactly the same as calling authorize() with true for interactionAllowed. Real preauthorization is not provided.
HAS_SESSION_MULTIPROCESS
Secret identifiers work across process boundaries. This makes them much more valuable as secrets. They are still transitory, however, so are valuable only as long as the session exists.

According to Quinn (the Eskimo!), in the "Read Me About MoreAuthSample" documentation for MoreAuthSample v1.0b2:

... the preauthorization flag (kAuthorizationFlagPreAuthorize) does nothing on current versions of Mac OS X [2907852].
This statement coincides with my own experimental results. Earlier trials showed that trying to preauthorize a rule having timeout=0 simply did not work as Apple's documentation described. Whew, I thought it was just me.

The Auth-Services API function that actually executes a program as a privileged process is notable for its limitations:

Overrides:
getCapabilities in class Authorization


· authorize

Summary  |  Top
   public void authorize(Privilege toGrant, 
                         boolean interactionAllowed) 

Authorize the given Privilege, granting approval to exercise it immediately. If the Privilege is granted or was already available, this method returns normally. If the Privilege is not granted, an UnauthorizedException is thrown.

Interaction with the user is allowed or forbidden according to 'interactionAllowed'. The current thread is blocked while any user-interaction is occurring. If interaction is allowed, and the user does not authenticate properly, this method fails. If interaction is forbidden, and the privilege is not already available, this method fails.

Throws: UnauthorizedException
thrown when the requested Privilege is not granted.
Throws: IllegalArgumentException
thrown when the Privilege is malformed, or some other structural error occurs.
Overrides:
authorize in class Authorization


· preauthorize

Summary  |  Top
   public void preauthorize(Privilege toGrant) 

This method is a synthetic implementation. It simply calls authorize(), since the PREAUTH option-flag isn't distinguished as of Mac OS X v10.2.*. Since this isn't a real preauthorize(), getCapabilities() returns a bit-mask value containing HAS_SYNTHETIC_PREAUTH.

Throws: UnauthorizedException
thrown when the requested Privilege is not granted.
Throws: IllegalArgumentException
thrown when the Privilege is malformed, or some other structural error occurs.
Overrides:
preauthorize in class Authorization

See Also: authorize, getCapabilities



· isAvailable

Summary  |  Top
   public boolean isAvailable(Privilege toCheck) 

Is the given Privilege currently available (authorized, preauthorized, or implied)?

Overrides:
isAvailable in class Authorization


· detach

Summary  |  Top
   public synchronized void detach(boolean revokeShared) 

Detach this Authorization from any underlying session, acting on shared authorizations in the manner given by the boolean.

In this implementation, acquire this instance's lock for its own thread-safety in assigning a value to mySession.

Overrides:
detach in class Authorization


· getPastGrantedPrivileges

Summary  |  Top
   public Enumeration getPastGrantedPrivileges() 

Return an Enumeration (possibly empty, but never null) representing all past Privileges granted by authorize() or preauthorize(). The Enumeration's nextElement() method returns a Privilege.

This method DOES NOT create a new session if one is not attached. This method never throws an UnauthorizedException.

Overrides:
getPastGrantedPrivileges in class Authorization


· getPastGrantedDate

Summary  |  Top
   public Date getPastGrantedDate(Privilege privilege, 
                                  int when) 

If the given Privilege was previously granted, return a Date representing the timestamp of when that last occurred.

Throws: IllegalArgumentException
thrown if 'when' is an invalid value.
Overrides:
getPastGrantedDate in class Authorization


· makeExecPrivilege

Summary  |  Top
   public Privilege makeExecPrivilege(String cmdName) 

Make and return a Privilege representing the ability to execute a program with elevated privileges by way of execPrivileged().

The name of the returned Privilege is "system.privilege.admin" (see "AuthorizationTags.h"). Its value is the UTF-8 encoding of the cmdName.

Overrides:
makeExecPrivilege in class Authorization


· execPrivileged

Summary  |  Top
   public Process execPrivileged(String[] progArray) 

Execute a program as if run by a privileged user, or with temporarily elevated privileges. If the execution privilege is denied, an UnauthorizedException is thrown and no program is executed. If authorization succeeds, a Privilege is added to the past granted Privileges.

In this implementation, the program is run with an effective user-ID of root. The real user-ID, along with the real and effective group-ID's, are unchanged.

The program named in progArray[0] must be accessible to the current unprivileged user-ID. That is, the current process must have search-permission on all directories leading to the executable, and have execute-permission on the file itself. It isn't good enough if root has access, since the privileges are not elevated to root until AFTER the executable has been loaded into memory.

The program named in progArray[0] must be an absolute pathname. The PATH environment-variable is not used to search for a command, as it is for Runtime.exec(). This is done for security reasons, so an attacker can't forge or taint a PATH that would cause an unexpected program to run as root.

If no session is active, one is established first. If the necessary Privilege is not already available or preauthorized, an interactive dialog will be presented, blocking the current Thread. If your program absolutely cannot tolerate blocking the current Thread, then call this method on its own Thread, or arrange to preauthorize() or authorize() first, or qualify the call to this method with a call to isAvailable().

Throws: UnauthorizedException
thrown when the necessary Privilege is not available or preauthorized, or the program is inaccessible, malformed, or otherwise unexecutable.
Throws: IllegalArgumentException
thrown when some malformation or structural error occurs.
Overrides:
execPrivileged in class Authorization


· getSecretLength

Summary  |  Top
   public int getSecretLength() 

Return the required length of a buffer that can hold the secret session identifier.

This is the size in bytes of the AuthorizationExternalForm type defined by the Authorization Services API.

Overrides:
getSecretLength in class Authorization


· getSecretIdentifier

Summary  |  Top
   public synchronized byte[] getSecretIdentifier() 

Return a secret session ID underlying this Authorization. If no session is active, one IS NOT established first, and a zero-length byte[] is returned. Otherwise the secret identifier is put into a new byte[], which is returned.

The returned secret identifier is identical with the AuthorizationExternalForm type defined by the Authorization Services API. Therefore, it can be sent to any other program that uses Authorization Services, and the available credentials and privileges will be shared.

Throws: UnauthorizedException
thrown when the secret identifier cannot be obtained.
Throws: IllegalArgumentException
thrown when something really bad or stupid happens.
Overrides:
getSecretIdentifier in class Authorization


· attach

Summary  |  Top
   public synchronized void attach(byte[] secretIdentifier) 

Attach this Authorization to the underlying session identified by the secret session identifier. This Authorization must be newly constructed or recently detach()'ed. The underlying session identified by the secretIdentifier must still be active (alive), or an UnauthorizedException will be thrown.

The supplied "secret identifier" is identical with the AuthorizationExternalForm type defined by the Authorization Services API. Therefor, any other program that uses Authorization Services can send those bytes to a Java program using a MacOSXAuthorization, and the Java program will have access to all the same credentials and privileges.

Throws: UnauthorizedException
thrown when the session identified by the secret cannot be attached because it has vanished.
Throws: IllegalArgumentException
thrown when some malformation or structural error occurs.
Throws: IllegalStateException
thrown when this Authorization already represents an active session. That is, when this Authorization is not newly created or recently detach()'ed.
Overrides:
attach in class Authorization


· attachPrivileged

Summary  |  Top
   public synchronized void attachPrivileged() 

Attach this Authorization to the same session used by execPrivileged() in the parent process. That session is only available to child processes executed by execPrivileged(), and needs no secret identifier. If the current process is not a child by execPrivileged(), then this method fails.

This implementation eventually calls the Authorization Services API function AuthorizationCopyPrivilegedReference(). Therefore, any other program that uses Authorization Services can execute a Java program using AuthorizationExecuteWithPrivileges(), and the privileged session will be available to the Java program after calling this method.

Throws: UnauthorizedException
thrown when there is no privileged session to attach to.
Throws: IllegalArgumentException
thrown when some malformation or structural error occurs.
Throws: IllegalStateException
thrown when this Authorization already represents an active session. That is, when this Authorization is not newly created or recently detach()'ed.
Overrides:
attachPrivileged in class Authorization


All Packages  This Package  Class Hierarchy  Class Search  Index
Freshly brewed Java API Documentation automatically generated with polardoc Version 1.0.7