Security Guide : Authorization

Authorization
Authorization is the process of verifying that the user has the authority to perform the requested operations on the server. For example, when a client accesses an enterprise bean method the server must verify that the user of the client has the authority to perform such an access. Authorization occurs after authentication (confirming the user's identity).
Access Control
Authorization occurs after the user proves who he or she is (Authentication). Authorization is the process of making access control decisions on requested resources for an authenticated entity based on certain security attributes or privileges. Following Java Security Architecture, VisiBroker adopts the notion of permission in authorization. In VisiSecure, resource authorization decisions are based on permissions. Micro Focus uses a proprietary authorization framework based on users and roles to accomplish authorization. For example, when a client accesses a CORBA or Web request enterprise bean method, the application server must verify that the user has the necessary permissions to perform such an access. This process is called access control or authorization.
Access Control List
Authorization is based on the user's identity and an access control list (ACL), which is a list of roles. Typically, an access control list specifies a set of roles that can use a particular resource. It also designates the set of people whose attributes match those of particular roles, and thus are allowed to perform those roles.
Roles-based access control
VisiSecure uses a role database (a file whose default name is roles.db) to associate user identities with roles. If a user is associated with at least one allowed role, the user may access the method. For more information, see “Configuring authorization using the rolemap file”.
Pluggable Authorization
VisiSecure provides the ability to plug-in an authorization service that can map users to roles. The implementer of the Authorization Service provides the collection of permissions that are granted access to certain resources.
The class RolePermission is defined to represent “role” as permission. The Authorization Services Provider in turn provides the implementation on the homogeneous collection of RolePermissions contained for an association between given privileges and a particular resource.
For more information on the RolePermission class, see “vbsec::RolePermission”.
The Authorization Service is tightly connected with the concept of the Authorization domain—each domain has exactly one Authorization Services Provider implementation. The Authorization domain is the bridge between the VisiSecure system and the authorization service implementation. During the initialization of the ORB itself, the authorization domains defined by the property vbroker.security.authDomains are constructed, while the Authorization Services Provider implementation is instantiated during the construction of the Authorization domain itself.
The Authorization Domain defines the set of rules that determine whether a user belongs to a logical “role” or not.
The implementer of the Authorization Service provides the collection of permission objects that are granted access to certain resources. Whenever an access decision is going to be made, the AuthorizationServicesProvider is consulted. The Authorization Service is closely associated with the Authorization domain concept. An Authorization Service is installed for each Authorization domain implementation, and functions only for that particular Authorization domain.
The AuthorizationServicesProvider is initialized during the construction of its corresponding Authorization domain. Use the following property to set the implementing class for the AuthorizationServicesProvider:
vbroker.security.domain.<domain-name>.provider
During run time, this property is loaded by way of Java reflection.
Another important functionality of the Authorization Service is to return the run-as alias for a particular role given. The security service is configured with a set of identities, identified by aliases. When resources request to “run-as” a given role the AuthorizationServices is again consulted to return the alias that must be used to “run-as” in the context of the rules specified for this authorization domain.
Configuring authorization using the rolemap file
You can configure authorization by creating your own Authorization Rolemap by hand.
What is a rolemap
The authorization rolemap is captured in a rolemap file. Typically, you would name this file after your authorization domain. The rolemap file, also called Role DB, is a map of users to roles. The rolemap designates the set of people whose attributes match the rules, and who are therefore associated with the corresponding role.
VisiSecure provides a mechanism for specifying role names and a set of attributes which define the role.
Syntax of Role DB
The Role DB file itself has the following form, and can contain multiple role entries:
role-name {
assertion1 [, assertion2, ... ]
...
[assertion-n]
...
}
role-name2 {
assertion3 [, assertion4, ... ]
...
[assertion-n]
...
}
A role entry is made up of a role name and a list of rules within curly braces (“{}”). A role must be made up of one or more rules. Each rule is a single line containing a list of comma-separated assertions for proper access identifications. Similarly, each rule must contain one or more assertions.
Each line in the Role Entry is a rule. Rules are read top-to-bottom, and authorization proceeds until one or none succeeds. That is, each rule is read as though separated by an “OR” operator. Assertions are separated on the same line by a comma (“,”). Assertions are read left-to-right, and all assertions must succeed in order for the rule to succeed. That is, each assertions in a rule is read as though separated by an “AND” operator.
Each rule must contain all necessary security information for a given Principal's security credentials. That is, each principal must have at least those attributes required from the rule—or exactly all the listed attributes. Otherwise authorization will not succeed.
For more information on specifying rules, see “Assertion syntax”.
For example, the contents of Role DB could be:
ServerAdministrator {
CN=*, OU=Security, O=Borland, L=San Mateo, S=California, C=US
*(CN=admin)
*(GROUP=administrators)
}
 
Customer {
role=ServerAdministrator
*(CN=borland)
*(CN=pclare)
*(CN=jeeves)
*(GROUP=RegularUsers)
}
This defines two roles, ServerAdministrator and Customer along with a set of rules and attributes which define them.
Once the rolemap file is complete, it can be referenced using the property vbroker.security.domain.<authorization-domain>.rolemap_path.
Modifying the authorization rolemap file
You can modify the authorization rolemap files by editing the rolemap file using properties given in the example directory. You can specify rolenames and attributes and thus associate users with roles. A role must be made up of one or more rules. For more information on rules and role entries, refer to “Assertion syntax”.
For configuring the database to store users, credentials and attributes, refer to “Using the userdbadmin tool”.
Assertion syntax
There is a variety of ways to specify rules using logical operators with attribute/value pairs that represent the access identifications necessary for authorization. There is also a simplified syntax using the wildcard character (“*”) to give your rules more flexibility. Both of these are discussed below.
Using logical operators with assertions
Two logical operators are available in specifying attribute/value pairs.
attribute = value
equals: attribute must equal value for authorization rule to succeed.
attribute != value
not equal: attribute must not equal value for authorization rule to succeed.
A value can be any string, but the wildcard character, “*” has special uses. For example, the attribute/value pair GROUP=* matches for all GROUPs. The following role has two associated rules:
manager {
CN=Kitty, GROUP=*
GROUP=SalesForce1, CN=*
}
The role manager has two rules associated with it. In the first rule, anyone named Kitty is authorized for manager, regardless of Kitty's associated group at the time. The second rule authorizes anyone in the group SalesForce1, regardless of their common-name (CN).
Wildcard assertions
For complicated security hierarchies, it may be prudent to look for only one or two attributes from the hierarchy in order to authorize a principal. VisiSecure’s security hierarchy starts with GROUPs at the top, then branching out into ORGANIZATIONs (O) and ORGANIZATIONAL UNITS (OU), and finally settling on COMMON NAMEs (CN).
For example, you may want to define a security role called SalesSupervisor that allows method permissions for managers of the sales force. (For this example, “sales” is the ORGANIZATION and “managers” is the ORGANIZATIONAL UNIT. You could do so with the following rule:
SalesSupervisor {
GROUP=*, O=sales, OU=managers, CN=*
}
This rule does not specify values for GROUP or for COMMON NAME (presumably because they are not necessary). But remember, each rule must represent all possible values for a Principal's credentials. There are other means of representing this same information in a smaller space using wildcard assertions.
You make a wildcard assertion by placing the wildcard character (“*”) in front of the assertion(s) in one of two ways. You may place the wildcard character in front of a single assertion, meaning that all possible security attributes are accepted but they must contain the single assertion. Or, you may place the wildcard character in front of a list of assertions separated by commas within parentheses. This means all possible security attributes are accepted but they must contain the assertions listed in the parentheses.
Making use of wildcard assertions, the role could also look like this:
SalesSupervisor {
*O=sales, *OU=managers
}
Or, even more simply:
SalesSupervisor {
*(O=sales, OU=managers)
}
All three code samples are different versions of the same rule.
Other assertions
Each role provides limited extensibility to others. You may, as a part of a role entry, specify a role=existing-role-name assertion that can extend an earlier role. You can also use customized code as your authorization mechanism rather than Role DB syntax by using the Authorization Provider Interface.
Recycling an existing role
You can refer to the rules from an existing role by using the rule-reference assertion—role=role-name. For example, let's say we have a group of marketers who are also sales supervisors that need to be authorized to the same code as Sales Supervisors. Building upon the SalesSupervisor code sample, we can create a new role entry as follows:
MarketSales {
role=SalesSupervisor
*(OU=marketing)
}
Now, everyone in role SalesSupervisor has access to the MarketSales role, as does anyone in the “marketing” OU.
Authorization domains
The authorization domain defines the set of rules that determine whether a user belongs to a logical “role” or not.
The authorization domain is the bridge between VisiSecure system and the authorization service implementation.
During the initialization of the ORB itself, the authorization domain is defined by the property vbroker.security.authDomains. Each Role DB file is associated with an authorization domain. An authorization domain is a security context that is used to separate role DBs and hence their authorization permissions. For more information on the authorization domain in the context of the basic security model, see “Basic security model” on page 7.
Specifying names to authorization domain
Each Role DB file is associated with an authorization domain. An authorization domain is a security context that is used to separate role DBs and hence their authorization permissions. For more information on the authorization domain in the context of the basic security model, see “Basic security model”.
You may use as many authorization domains as you wish, provided they are all registered with the VisiBroker ORB. You must do the following for each of your authorization domains:
To accomplish these items, the properties described in the following sections must be set. For more information about these properties, see “Security Properties for Java” or “Security Properties for C++”.
Naming authorization domains
You can give each authorization domain a name and list them using the property:
vbroker.security.authDomains=<domain1> [, <domain2>, <domain3>, ...]
Setting up default access
You can set up the default access and decide whether or not to grant access to the domain in the absence of security roles for <domain-name>.
The property used to set up the default access is
vbroker.security.domain.<domain-name>.defaultAccessRole=grant|deny
Setting up RoleDB
The path of the Role DB file is associated with the authorization domain <domain-name>. Although this can be a relative path, Micro Focus recommends you make this path fully-qualified.
The property you use to set up the RoleDB is:
vbroker.security.domain.<domain-name>.rolemap_path=<path>
Configuring authorization domains to run-as alias
Authorization domains are then configured to run-as a given alias for a role in that domain.
A Run-as Alias is a string identifying an authentication identity. It is defined in the vault and scoped within the VisiBroker ORB. This alias then represents a particular user. The identity is mapped to the alias using either the Context APIs or by defining it in the vault. The vault can contain a list of run-as aliases and the corresponding authenticating credentials for the identity to run-as. In both cases, the authenticating credentials (from the vault or wallet) are passed to the LoginModules, which authenticate those credentials and set them as fully authenticated identities corresponding to those credentials in the run-as map.
Note
Run-as aliases are not available under C++.
When a request is made to run-as a given role, then the authorization domain for that context is consulted to get the corresponding run-as alias. The run-as map is then consulted to get the identity corresponding to that alias, and this identity is used.
Run-as identities can also be configured to be certificate identities and not just username/password identities.
Run-as alias is useful in particular when there are clients, middle-tier servers and end-tier servers.
To set up run-as alias on CORBA application level:
1
Set the property in the server.properties file. This property specifies the name of the run-as role. The value can either:
a
be use-caller-identity to have the caller principal itself as the principal identity for the run-as role, or
b
vbroker.security.domain.<domain-name>.runas.<role-name>=<alias>|use-caller-identity
2
3
vbroker.security.assertions.trust.all
Setting up authorization for CORBA objects
Authorization in the CORBA environment allows only identities in specific roles for a given object to access that object. An object's access policy is specified by means of a Quality of Protection policy for the Portable Object Adapter (POA) hosting the object in question. Note that access policies can only be applied at the POA level.
Rolemaps are also used to implement authorization for CORBA objects. Similarly, the J2EE roles and concepts therein are also used in the CORBA environment.
In order to set up authorization for an object, you need to perform the following:
1
Create a ServerQopPolicy.
2
Initialize the ServerQopPolicy with a ServerQopConfig object.
3
Implement an AccessPolicyManager interface, which takes the following form:
Java
interface AccessPolicyManager {
public java.lang.String domain();
public com.borland.security.csiv2.ObjectAccessPolicy getAccessPolicy(
org.omg.PortableServer.Servant servant, byte[] object_id byte [] adapter_id);
}
C++
class AccessPolicyManager {
public:
virtual char* domain() =0;
ObjectAccessPolicy_ptr getAccessPolicy(PortableServer_ServantBase* _servant,
const ::PortableServer::ObjectId& id,
const::CORBA::OctetSequence& _adapter_id) =0;
}
Setting up the name
This interface returns the authorization domain from the domain() method and uses it to set the access manager in the ServerQopConfig object. The domain specifies the name of the authorization domain associated with the proper rolemap. You set the location and name of the rolemap by setting the property:
vbroker.security.domain.<authorization-domain-name>.<rolemap-path>
where <authorization-domain-name> is a tautology, and <rolemap-path> is a relative path to the rolemap file.
Setting up default access
The getAccessPolicy() method takes an instance of the servant, the object identity, and the adapter identity and returns an implementation of the ObjectAccessPolicy interface.
1
Implement the ObjectAccessPolicy interface that returns the required roles and a run-as role for accessing a method of the object. There is no difference between J2EE and CORBA run-as roles in Micro Focus's implementation. The ObjectAccessPolicy interface takes the following form:
Java
interface ObjectAccessPolicy {
public java.lang.String[] getRequiredRoles(java.lang.String method);
public java.lang,String getRunAsRole(java.lang.String method);
}
C++
class ObjectAccessPolicy {
public:
getRequiredRoles (const char* _method) =0;
}
The getRequiredRoles() method takes a method name as its argument and returns a sequence of roles. The getRunAsRole() method returns a run-as role, if any, for accessing the method.
Identities can be supplied using Callback Handlers. For more details, see “Authentication”.
Configuring authorization requirements
You must configure authorization requirements for the components in the server, as the client needs to have these authorizations in order to access these components in the server.
In the corbaauthz example in the <install_dir>\examples\vbroker\security folder, the authorization requirement for the BankManager object is that the clients should be a member of the "Manager" role and for the Account it is "Customer" or "Teller" role.
The rolemap file contains the authorization data from the Role DB file. Members of the roles Manager, Customer and Teller are described in the bank.rolemap file, a snippet of which is shown below:
Example for bank.rolemap file for Java
Manager {
*CN=admin
*group=user
}
Customer {
*CN=admin
}
Teller {
*CN=admin
*group=user
}
Example for bank.rolemap file for C++
Manager {
*group=cceng
}
Customer {
*group=cceng
}
Teller {
*group=cceng
}
Any authenticated user with username=Administrator is a member of the role ‘Manager’.
Any authenticated user with group=cceng is a member of both role ‘Customer’ and role ‘Teller’.
You can use this example and change the username and group to use a valid, existing username and group in your system as required.
The example illustrates the use of VisiBroker properties and JAAS configuration file to secure your application. The example client and server uses username/password authentication of the client on the server and also for the server's self authentication.
Look at the different properties files (server.properties, client.properties) and config files (server.config and client.config) in the <install_dir>\examples\vbroker\security/corbaauthz folder.
The server or the client configuration file is the JAAS configuration file which defines the login modules.
To enable security, you must set up the following properties in the server or client properties file:
The default value is false. If set to true, disables all security services.
If this property is set to true, during initialization, this property tries to log on to all the realms listed by the property vbroker.security.login.realms.
When set to none, Authentication is not required. During handshake, no certificate request will be sent to the peer. Regardless of whether the peer has certificates, a connection will be accepted. There will be no transport identity for the peer.
CmdLineCallbackHandler has password echo on, while HostCallbackHandler has password echo off.
The properties allow you to customize the behavior of VisiSecure. Depending on whether your application is Java, C++, or both, you may have to set different properties with different types of values. See “Security Properties for C++” and “Security Properties for Java” for all the properties you can set in this file.
Java Example: Authorization Using a Vault
To set up authorization for CORBA objects by using a vault, modify the following files in the corbaauthz example in the <install_dir>\examples\vbroker\security\example folder:
You may need to add the following files:
In the java server properties file, add the following properties:
vbroker.security.domain.bank.runas.jeeves_runasrole=jeeves_alias
vbroker.security.assertions.trust.all=true
In the Bank.idl file, make the following changes:
module Bank {
interface Converter {
float toSGD( in float USD);
};
interface Account {
float balance();
};
interface AccountManager {
Account open(in string name);
};
};
In the AccountImpl.java file, modify as shown in bold below.
// AccountImpl.java
import com.borland.security.csiv2.ObjectAccessPolicy;
import org.omg.CORBA.BAD_OPERATION;
import org.omg.CORBA.ORB;
 
public class AccountImpl extends Bank.AccountPOA implements ObjectAccessPolicy {
 
public String[] getRequiredRoles(String op) {
if (op.equals("balance")) {
return new String[] {"Customer", "Teller"};
}
throw new BAD_OPERATION("No operation named " + op);
}
 
public String getRunAsRole(String op) {
// return "jeeves_runasrole";
return null;
}
 
public AccountImpl(float balance) {
_balance = balance;
}
public float balance() {
return _converter.toSGD( _balance );
}
private float _balance;
 
public static Bank.Converter _converter;
}
In the server.java file, to write the AccountManager’s reference to a file for the client to access, add the following commands:
FileWriter output = new FileWriter("bank.ior");
output.write(orb.object_to_string(object));
output.close();
System.out.println(object + " is ready.");
// Wait for incoming requests
orb.run();
}
 
// AccountManagerImpl.java
import org.omg.PortableServer.*;
import com.borland.security.csiv2.ObjectAccessPolicy;
import org.omg.CORBA.BAD_OPERATION;
import org.omg.CORBA.ORB;
 
import java.util.*;
 
public class ConverterImpl extends Bank.ConverterPOA implements ObjectAccessPolicy {
 
public String[] getRequiredRoles(String op) {
if (op.equals("toSGD")) {
return new String[] {"Manager"};
}
throw new BAD_OPERATION("No operation named " + op);
}
 
public String getRunAsRole(String op) {
return null;
}
 
public float toSGD( float USD) {
System.out.println( "*** Converter.toSGD is called");
javax.security.auth.Subject caller = _current.getCallerSubject();
System.out.println( "THE CALLER = " + caller.toString() );
return _rate * USD;
}
 
// Use const for now
private static final float _rate = 1.65676f;
com.borland.security.Current _current;
 
ConverterImpl( com.borland.security.Current current) {
_current = current;
}
 
}
Add a new file ConverterServer.java. This file is the same server.java file that is in the corba authz example. Add the following that is given in bold:
// Server.java
import org.omg.PortableServer.*;
import java.io.*;
import org.omg.CORBA.Any;
import org.omg.CORBA.Policy;
import org.omg.CORBA.PolicyManager;
import org.omg.CORBA.PolicyManagerHelper;
import org.omg.CORBA.SetOverrideType;
import com.borland.security.csiv2.SERVER_QOP_CONFIG_TYPE;
import com.borland.security.csiv2.ServerQoPConfigDefaultFactory;
import com.borland.security.csiv2.ServerQoPConfig;
import com.borland.security.csiv2.ServerQoPConfigHelper;
import com.borland.security.csiv2.ServerQoPPolicy;
import com.borland.security.csiv2.AccessPolicyManager;
import com.borland.security.csiv2.ObjectAccessPolicy;
 
public class ConverterServer {
 
public static void main(String[] args) {
try {
// Initialize the ORB.
org.omg.CORBA.ORB orb = org.omg.CORBA.ORB.init(args,null);
// get a reference to the root POA
POA rootPOA = POAHelper.narrow(orb.resolve_initial_references("RootPOA"));
 
ServerQoPConfig config =
new ServerQoPConfigDefaultFactory().create(false,
ServerQoPPolicy.ALL,
true,
new AccessPolicyManager() {
public String domain () {
return "bank";
}
 
public ObjectAccessPolicy getAccessPolicy (Servant servant,
byte[] id,
byte[] adapter_id) {
return (ObjectAccessPolicy) servant;
}
});
Any any = orb.create_any();
ServerQoPConfigHelper.insert(any, config);
Policy qop = orb.create_policy(SERVER_QOP_CONFIG_TYPE.value, any);
 
PolicyManager polmgr =
PolicyManagerHelper.narrow(orb.resolve_initial_references ("ORBPolicyManager"));
polmgr.set_policy_overrides(new Policy[] {qop},SetOverrideType.SET_OVERRIDE);
 
// Create policies for our persistent POA
org.omg.CORBA.Policy[] policies = {
rootPOA.create_lifespan_policy(LifespanPolicyValue.PERSISTENT),
};
// Create myPOA with the right policies
POA myPOA = rootPOA.create_POA( "converter_poa", rootPOA.the_POAManager(),
policies );
// Create the servant
com.borland.security.Current current =
(com.borland.security.Current) orb.resolve_initial_references (
"VBSecurityCurrent"
);
ConverterImpl converterServant = new ConverterImpl(current);
 
// Decide on the ID for the servant
byte[] converterId = "CurrencyConverter".getBytes();
 
// Activate the servant with the ID on myPOA
myPOA.activate_object_with_id( converterId, converterServant);
 
// Activate the POA manager
rootPOA.the_POAManager().activate();
 
// convert servant to an object reference
org.omg.CORBA.Object object = myPOA.servant_to_reference(converterServant);
 
// Write the AccountManager's reference to a file,
// so clients can access it.
FileWriter output = new FileWriter("converter.ior");
output.write(orb.object_to_string(object));
output.close();
System.out.println(object + " is ready.");
// Wait for incoming requests
orb.run();
}
catch (Exception e) {
e.printStackTrace();
}
}
 
}
In this example, we are creating a vault called fault:
1
> vaultgen -vault fault -config java_server.config -interactive
2
3
> login myrealm
JDataStore: Developer's License (no connection limit)
JDataStore: Copyright (c) 1996-2004 Borland Software Corporation. All rights reserved.
JDataStore: License for JDataStore development only - not for redistribution
JDataStore: Registered to:
JDataStore: JDataStore
JDataStore: Developer's license with unlimited connections
4
5
6
You are logged into realm myrealm
7
> runas jeeves_alias myrealm
8
9
10
Added runas alias jeeves_alias
11
> quit
You are now generating a Vault to “fault”.
To launch run-as alias:
Open three different command-line consoles (DOS prompts on Windows NT).
Start the osagent.
1
vbj -DORBpropStorage=java_client.properties Client
2
When prompted, enter borland/borland.
3
vbj -DORBpropStorage=java_server.properties Server
4
vbj -DORBpropStorage=java_server.properties ConverterServer
5
*** Converter.toSGD is called
THE CALLER = Subject:
Principal: jeeves@myrealm
Public Credential: Privileges for jeeves@myrealm
Private Credential: Destroyed authentication context for null
Private Credential: com.borland.security.provider.ATSCodec$EncoderCache@e93999
6
public String getRunAsRole(String op) {
return "jeeves_runasrole";
 
}
to
public String getRunAsRole(String op) {
return null;
}
7
vbj -DORBpropStorage=java_client.properties Client
8
When prompted, login as borland/borland.
See console3 again:
*** Converter.toSGD is called
THE CALLER = Subject:
Principal: borland@myrealm
Public Credential: Privileges for borland@myrealm
Private Credential: Destroyed authentication context for null
Private Credential: com.borland.security.provider.ATSCodec$EncoderCache@e9399
9
There are three things that together specify jeeves@myrealm:
1
AccountImpl.java getRunAsRole() returns "jeeves_runasrole"
2
java_server.properties translates "jeeves_runasrole" into "jeeves_alias" using:
vbroker.security.domain.bank.runas.jeeves_runasrole=jeeves_alias
3
Run-as mapping
Note
Run-as mapping is not available under C++.
Setting the vbroker.security.domain.<domain-name>.runas.<role-name> property effectively maps an alias to a bean's run-as role. Upon successful authorization, but before method invocation, the container checks the Run-as role specified in the EJB's deployment descriptor for the called method. If a run-as role exists, the container checks to see if there is an alias as well. If there is, when the bean makes an outgoing invocation it switches to the identity for that alias.
If, however, no alias is specified (that is, the run-as role name is set to use-caller-identity), the caller principal name is used.
Using a vault for a domain
If you are using a vault to store system identities, you associate it with a domain so that it can be used. You do this by setting the domain's vbroker.security.vault property in the domain's orb.properties file.
Set the property to the location of the domain's vault. For example:
vbroker.security.vault=c:/BDP/var/domains/base/adm/security/MyVault
Similar to the vault are other properties which only belong to the orb.properties file. These include secure listener ports, thread monitoring, and so forth.
As a general rule, add only those properties that can be shared by multiple applications. Otherwise, use the appropriate process-specific ORB properties file to specify the property.
Context Propagation
In addition to ensuring the confidentiality and integrity of transmitted messages, you need to communicate caller identity and authentication information between clients and servers. This is called delegation. The caller identity also needs to be maintained in the presence of multiple tiers in an invocation path. This is because a single call to a mid-tier system may result in further calls being invoked on other systems which must be executed based on the privileges attributed to the original caller.
In a distributed environment, it is common for a mid-tier server to make identity assertions and act on behalf of the caller. The end-tier server must make decision on whether the assertion is trusted or not. When propagating context, the client transfers the following information:
Authentication token—client's identity and authentication credentials.
Identity token—any identity assertion made by this client.
Authorization elements—privilege information that a client may push about the caller and/or itself.
Identity assertions
Identity assertion occurs when several servers with secure components are involved in a client request. At times, it is necessary for a server to act on behalf of its clients—when a client request is passed from one server to another. This is typical in the case where a client calls a mid-tier server, and the server further needs to call an end-tier server to perform a part of the service requested by the client. At such times, the mid-tier server typically needs to act on behalf of the client. In other words, it needs to let the end-tier server know that while it (the mid-tier server) is communicating with the end-tier server, access control decisions must be based on the original caller's privileges and not its privileges.
For example, a client request goes to Server 1, and Server 1 performs the authentication of the identity of the client. However, Server 1 passes the client request to Server 2, which may in turn pass the request to Server 3, and so forth. See the following diagram:
Each subsequent server (Server 2 and Server 3) can assume that the client identity has been verified by Server 1 and thus the identity is trusted. The server that ultimately fulfills the client request, such as Server 3, need only perform the access control authorization.
By default the identity is authenticated only at the first tier server and is asserted. It is the asserted identity that propagates to other tiers.
Impersonation
Impersonation is the form of identity assertion where there is no restriction on what resources the mid-tier server can access on the end-tier server. The mid-tier server can perform any task on behalf of the client.
Delegation
The inverse of impersonation, delegation is the form of identity assertion where the client explicitly delegates certain privileges to the server. In this case, the server is allowed to perform only certain actions as dictated by the client. VisiSecure performs only simple delegation.
Asserting identity of the caller
The identity assertion example in the \\VisiBroker\examples\vbroker\security\assertion example folder illustrates the use of identity assertion APIs which can be used to explicitly assert an identity as caller.
This example uses APIs provided by security context to create a new identity and assert the identity as caller before making the invocation. The server first checks if the assertion is made by the trusted peer and then checks if the asserted identity is authorized to make the invocation.
You can change the attribute to suit your own environment before running the example. In this example, a server is setup with assertion trust and authorization rules.
To make the assertion, use the following command:
for (int i=0; i<argc; ++i) {
 
if (strcmp(argv[i], "-assert") == 0) {
CORBA::Object_var obj1 = orb->resolve_initial_references("VBSecurityContext");
Context* context = dynamic_cast<Context*>(obj1.in());
 
CORBA::Object_var obj2 = orb->resolve_initial_references("VBSecurityCurrent");
Current* current = dynamic_cast<Current*>(obj2.in());
 
CORBA::Object_var obj3 = orb->resolve_initial_references("VBWalletFactory");
WalletFactory* factory = dynamic_cast<WalletFactory*>(obj3.in());
 
Wallet* wallet = factory->createIdentityWallet("asserted", "password", "myrealm");
Subject* subject = context->importIdentity(*wallet);
current->asserting(subject);
cout << "New caller identity asserted." << endl;
break;
}
}
The assertion trust rule on the server requires the asserter, which is the client in this example. This asserter must be a member of the Asserter role for the authorization domain bank.
Members of the role Asserter are described in the bank.rolemap as follows:
Asserter {
*group=cceng
}
This means that any authenticated user that belongs to group cceng must be a member of role Asserter.
The authorization rule on the server requires the caller, which is the identity asserted by the client in this example, to be of Manager role for authorization domain bank.
Members of the role Manager are described in the bank.rolemap as follows:
Manager {
*cn=asserted
}
The client code asserts an identity that user name is asserted, thus the asserted identity is a member of role Manager and access will be granted.
1
prompt> Server -DORBpropStorage=cpp_server.properties &
(start Server -DORBpropStorage=cpp_server.properties on Windows)
2
prompt> Client -DORBpropStorage=cpp_client.properties -assert
3
The program runs successfully.
To run the client without assertion:
1
prompt> Client -DORBpropStorage=cpp_client.properties
2
The exception CORBA::NO_PERMISSION is thrown because only the asserted identity is authorized to make the invocation under the server configuration.
In this example, the following properties are set on the server side:
If set to true, at initialization time this property tries to log on to all realms listed by property vbroker.security.login.realms.
none—Authentication is not required. During handshake, no certificate request will be sent to the peer. Regardless of whether the peer has certificates, a connection will be accepted. There will be no transport identity for the peer.
This property is used to specify a list of trusted roles (specify with the format <role>@<authorization_domain>). <n> is uniquely identified for each trust assertion rule as a list of digits.
For example, setting vbroker.security.assertions.trust.1=ServerAdmin@default means this process trusts any assertion made by the ServerAdmin role in the default authorization domain.
The following properties are set on the client side:
Note: To use secure transport only, the secureTransport property must also be set to true.
If set to true, at initialization-time this property tries to login to all the realms listed by property vbroker.security.login.realms.
Note: To use secure transport only, the secureTransport property must also be set to true.
none—Authentication is not required. During handshake, no certificate request will be sent to the peer. Regardless of whether the peer has certificates, a connection will be accepted. There will be no transport identity for the peer.
Trusting Assertions
A server (end-tier) may choose to accept or not accept identity assertions. In the case where it chooses to accept identity assertions, there are trust issues that present themselves. While the server may know that the peer is authentic, it must also confirm that the peer has the privilege to assert another caller or act on behalf of the caller. Since the caller itself is not authenticated by the end-tier, and the end-tier accepts the mid-tier's assertion, the end-tier needs to ensure that it trusts the mid-tier to have performed proper authentication of the original caller. It, in turn, trusts the mid-tier's trust in the authenticity of the caller.
There may be many peers to an end-tier system, some of which are trusted as mid-tiers, while others are just clients. Therefore, the privilege to speak for other callers must be granted only to certain peers.
Trust assertions and plug-ins
When a remote peer (server or process) makes identity assertions while acting on behalf of the callers, the end-tier server needs to trust the peer to make such assertions. The Service Provider Interface (SPI) allows you to plug in a Trust Services Provider to determine whether the assertion is allowed (trusted) for a given caller and a given set of privileges for the asserter. Specifically, you use the TrustProvider class to implement trust rules that determine whether the server will accept identity assertions from a given asserting subject. For more information, see sec-api-doc in the Help system, and the “Security SPI for C++”.
Backward trust
Backward trust is provided “out of the box”, and is the form of trust where the server has rules to decide who it trusts to perform assertions. With backward trust, the client has no say whether the mid-tier server has the privilege to act on its behalf.
Forward trust
Forward trust is similar to delegation in that the client explicitly provides certain mid-tier servers the privilege to act on its behalf.
Temporary privileges
At times, a server needs to access a privileged resource to perform a service for a client. However, the client itself may not have access to that privileged resource. Typically, in the context of an invocation, access to all resources are evaluated based on the original caller's identity. Therefore, it would not be possible to allow this scenario, as the original caller does not have access to such privileged resource. To support this scenario, the application may choose to assume an identity different from that of the caller, temporarily while performing that service. Usually, this identity is described as a logical role, as the application effectively needs to assume an identity that has access to all resources that require the user to be in that role.