All Packages This Package Class Hierarchy Class Search Index
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.
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:
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:
- The returned FILE * points to a bi-directional pipe, which inextricably couples stdin and stdout of the child process, so you can't close its stdin to signal EOF without also closing its stdout and thereby being unable to read results. Hence the presence of HAS_PROCESS_COUPLED_STREAMS.
- The privileged Process's stderr is simply inherited from the parent process, so it's typically just going to spew error-text onto the system console, rather than let the parent process parse it or decode error-results from it. No, that might be too useful. Hence the absence of HAS_PROCESS_ERRORSTREAM.
- There is no way to know or discover the process-ID (pid) of the privileged child Process. Who would want to know that? After all, just because it's a required parameter for wait(), or wait4(), or kill(), who'd want to wait for the child or send it signals? Naah. Hence the absence of HAS_PROCESS_WAITFOR, HAS_PROCESS_EXITVALUE, and HAS_PROCESS_DESTROY.
- 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 IndexFreshly brewed Java API Documentation automatically generated with polardoc Version 1.0.7