Previous Next Contents

3   Authorization

Once a principal has been authenticated, it may have access to and usage of services corresponding to its level of privilege. This implies that the node must be able to map some set of protected services to each principal--this is the security policy of the node--and keep track of how services may be used by that principal.

For access, we define a way to formulate such as policy based on sets in [7, 1]. Each level of privilege is defined by the set of principals which have the same privilege level, and each service is associated with some privilege level. Privilege levels may be overlapping. For instance, in the government, those with top secret clearance may look at all classified documents, those with secret clearance may see a subset of these, and finally those with confidential clearance may see a subset of these. In our model, top secret clearance would be defined by some set of users TS, secret clearance would be TS È S, and confidential clearance would be TS È S È C. This way, a service which is associated with top secret clearance may only be accessed by those in the set TS; however, a service with secret clearance may be accessed by those in S and TS. In this case, the sets are concentric, but other approaches are also possible.

For the latter, we leave it to the service to define sets indicating how the service may be used. This may be formed in a way similar to the access policy, but rather than storing which services may be access, we store parameters indicating how that service may be used. The form of these parameters depends on the implementation of the service.

Using this architecture implies that the node must be able to determine to which privilege set a user belongs, and the node must be able to determine the association between services (or usage parameters) and privilege sets. How these determinations are made is a matter of implementation. Authorization data for a node could be stored monolithically---that is, the node maintains all security information internally to itself---or, hierarchically---it shares the task of storing the information with other, trusted nodes---or, ephemerally---it doesn't store any information directly, but instead relies on certificates from trusted sources which describe privilege on a per-use basis. We have structured the PLAN implementation to be independent of the approach taken, or the means by which the approach is implemented.

In addition to access to privileged services, the use of those services may also be a matter of privilege. For example, a service which provides persistent storage on the node may be available to privileged users, and the amount of storage allowed to those users may also vary according to privilege.

To implement the security policy of the node, one must write some code that implements the POLICY signature (in the file security/policy.ml):
(* Stuff particular to an implementation *)
module type POLICY =
  sig
    (* Type for the user's key *)
    type key = Dsa.public_key
    type privkey = Dsa.private_key
    type cert = string
 
    (* For default policies *)
    val default_principal : key
    val default_principal_privkey : privkey

    (* Namespace authorization action *)
    val construct_all_service_lists : unit -> string list
    val construct_service_lists : key -> (cert list) option -> 
      (string list * string list)

    (* Per-service authorization action *)
    val query_service_parameter: key -> string -> string ->
      (cert list) option -> string

    (* Initialization *)
    val init : string -> int -> key -> privkey -> unit
  end
The implementation of this signature provides privilege-based service access and use. The first three type definitions indicate that public and private keys are implemented using DSA, and that certificates are in string form. The next two definitions define the principal corresponding to the default (unauthenticated) user. The next three definitions indicate the authorization actions. The first two functions deal with service access management while the third is for per-usage queries. The first function, construct_all_service_lists, returns all services which are privileged beyond the default user; this is used by the node to prevent these services from being added to the default service symbol table at startup.3 The second function, construct_service_lists, is called to alter the namespace with the additional services allowed to the authenticated principal and those restricted from him (usually this will be one or the other, but not both). This function may optionally take a list of certificates to provide further information. The third function, query_service_parameter, is used to return service parameters: given a user's public key, service name, and service parameter name, it returns, in string form, the value of the specified parameter. Finally, the initialization function, init, initializes the node policy, given the policy file, port number, and public and private keys of the node.

In the PLAN distribution, we provide two implementations of this signature, one using a monolithic approach, and another allowing all possible policy storage strategies, using the Query Certificate Manager (QCM).

3.1   Monolithic Approach

By default, PLAN is compiled to use our monolithic, set-based approach to policy management. It is not expected that this approach scales well to real-world situations; it was designed as a testbed for ideas. In fact, we have also developed a translator to QCM for policy files written for the monolithic approach. We present a basic example here.

The form of the policy file is as a list of set definitions with key associations. For example:
p1 = ( print ) + { "public.key.3324" };
creates a set named p1 that specifies that the print service routine is privileged and may be accessed by the prinicipal whose public key is stored in file public.key.3324, implying that it is unavailable by default. By contrast:
p1 = ( print ) - { "public.key.3324" };
indicates that the print service is not available to the given principal, implying that it is available by default. This is useful for our active firewall, discussed in Section 3.3.2. Finally, sets can be specified in relation to each other, as in
p1 = ( print ) + { "public.key" };
privileged = ( thisHost ) union p1 + 
  { "public.key.3325", "public.key.3324" };
This indicates that the privilege afforded to prinicipals associated with set privileged is a superset of the privilege afforded to those associated with set p1.

This approach does not currently implement usage parameters; thus the function query_service_parameter is not implemented. This is, however, implemented with QCM as explained below.

3.2   Query Certificate Manager

Also provided with the distribution is some code which implements the POLICY signature using the Query Certificate Manager (QCM). QCM is a natural choice for a policy implementation because it is based on sets, and supports all three implementation approaches described above (monolithic, hierarchical, and ephemeral). Please see the QCM documentation in the distribution for more specific information about QCM; it's in plan/qcmlib/docs. By default, plan is built with QCM support; if you wish to not use QCM, you should comment out the line
USE_QCM = true
in the Makefile before building plan.

The QCM policy checker is implemented in the file security/policy_qcm.ml. The implementation strategy for access is to define a QCM program with a set named acl. This set consists of a list of three-tuples consisting of
  1. a set of keys indicating the principals this tuple applies to
  2. a thicken set of privileged services to add to the principals' environment
  3. a thin set of services to subtract from the principals' environment
Using this approach, the last example in the previous section could be encoded in QCM as:
online
{
  p1 = {
    Principal(<PublicKey="...">)
  };
  p10 = {"print"};

  privileged = {
    Principal(<PublicKey="...">)
  };
  privileged0 = {"thisHost"};

  acl = {
    ( p1, p10, {} ),
    ( privileged, union ( privileged0, p10 ), {} )
  };
}
Note that QCM programs may refer to QCM programs on other nodes to implement a distributed, hierarchical program. Details are omitted here; see the QCM documentation for more.

For usage-based security, you simply add set definitions such that the name of the set is the name of the service to parameterize. The elements of the set are two-tuples of the form
  1. a set of principals (as in the acl)
  2. a labeled record of length 1 with the label corresponding to the parameter name
To have multiple parameters per service, you add multiple records.4

As an example, the resident state package implements the ability for packets to store soft state on the router. PLAN 3.2 allows you to create a policy about how much particular users are allowed to store. For example, the above program might be extended as follows:
  default = {
    Principal(<PublicKey="...">)
  };
  resident = { ( default, <amount=100> ),
               ( privileged, <amount=1000> ) };
This indicates that all of the principals in default and privileged are allowed to store 100 and 1000 words of information, respectively, at any one time. The value of the default key should match Policy_impl.default_principal (see security/policy_qcm.ml for the exact string value). This policy is enforced by the resident state implementation itself. If we look in the file resident/resident_svc_impl.ml at the implementation of the function put, we see5
exception TooMuch

(* check_put: 'a -> string *)
(* Code to implement restriction of service policy *)
let check_put v =
  let whokey =
      (match (Auth.get_principal()) with
        Some k -> k
      | None -> Policy_impl.default_principal) in
  let who = Dsa.string_of_public_key whokey in
  let max_amount = int_of_string
      (Policy_impl.query_service_parameter 
        whokey "resident" "amount" None) in
  let current_amount = space_used who in
  let requested_amount = Obj.size (Obj.repr v) in
  if (current_amount + requested_amount) > max_amount then
    raise TooMuch
  else
    who

let put key v timeout =
  let who = check_put v in
  (* Actual service code ... *)
The first thing that happens in put is that the function check_put is called to determine whether the action is permitted. It first checks who the current principal is from the Auth.get_principal() call; this value is stored after a packet does an authEval(). If no authEval has taken place, get_principal returns None and so the default principal is used. The next thing is that the policy is queried using Policy_impl.query_service_parameter to determine the maximum allowable amount for this prinicpal. Then, the code looks up in the state table how much this principal currently has stored and if that combined with the requested amount is less than the value dictated by policy, then the operation is permitted (returning the current principal to be used by put). Otherwise, an exception is raised.

In the next subsection we provide an example implementation and service for use with authapp to demonstrate the use of QCM in the monolithic setting.

3.3   Authorization Tutorial

We present three demonstrations of authorization. First, we present the use of privilege-expansion so that a privileged packet may have access to a greater number of services. Next we present the use of privilege-restriction to limit the default services available to a packet; this allows us to implement an active firewall. Finally, we present usage-based security using resident state, as described above.

3.3.1   Application Privilege Expansion

As a demonstration of namespace-expansion authorization, we have provided a sample QCM program in the file expand.qcm in the plan directory:
online
{
  enhancements = {
    Principal(<PublicKey="P: iuWJZ7KVoRbhoCDrDqUSo7pd/hsYFvs9E5rk2
JYB2W7M06F2ughbk8zaIzgiVriopSMhZziJXyvs6fla/6QzRIwMwhLtTUzAIQZYDUD
duJjClHZzU8lN7FM4jGNyq3wOn/uH/L9i3tJGm/V9ywfqqUFNF0O2amlxhM1s1JMci
kM=, Q: ledWdPQsmKTqyynTCBn0vWlhFeM=, ALPHA: D7CquhERwI22+bW0JfY9o
D+tLiV7+0p+5y12Mvx/RYsQgwmA1BUD6PqyZ54lXRh3hm6k/AM9zvYXbcG8Dx49uq6
jQXHdjQCK2c8rlztrjplJvPdG5bSMTZzbVJAY1oDM9sugFGmMJLvizqFsZfJsrE13P
pOTNc7LRS8b26NodUM=, Y: Ow++XUNYt5JnxuQ4WgjvESddl+CKV6vEE6SGSu+mbI
ubArSzYUV2OBYdMU6uqZSaPxX0lSIDrHC+Tvq4ckNU+U5LciYDydgfUxFQQXbLInwS
UI2iaus9jGVP5Px8gASIkhy0eKQg45qZHySalBpuA6AyDlHqRMIR7IK8zV2XKcQ=">)
  };
  enhancements0 = {"authSvc"};
  acl = {
    ( enhancements, enhancements0, {} )
  };
}
It defines a single set named enhancements with a single member (which is the pair of keys in the file key used for authapp in the last tutorial). Then this user authenticates on the node using this policy, the service authSvc will be added to his namespace. This service is defined in security/crypto_svc_impl.ml as the function auth_svc, and always returns the value true.

We can try this service out with a slight modification to authapp. In the file security/authapp.ml, on line 266 you'll find three blocks of PLAN code:
      (let chunk_code = Printf.sprintf
        "svc print : 'a -> unit
         svc toString : 'a -> string
         svc getHostByName : string -> host
         svc defaultRoute : host -> host * dev
         svc getRB : void -> int
         svc thisHost : void -> host list
         fun doit() = 
           OnRemote(|print|(toString(hd thisHost()) ^
                            \" authenticated successfully\"),
                    getHostByName(\"%s\"),getRB(),defaultRoute)"
(*        "svc print : 'a -> unit
         svc getHostByName : string -> host
         svc defaultRoute : host -> host * dev
         svc getRB : void -> int
         svc authSvc : void -> bool
         fun doit() = OnRemote(|print|(authSvc()),
                               getHostByName(\"%s\"),getRB(),
                               defaultRoute)"
        "svc print : 'a -> unit
         svc toString : 'a -> string
         svc put : (string,key,'a,int) -> unit
         svc generateKey : void -> key
         svc thisHost : void -> host list
         svc getHostByName : string -> host
         svc defaultRoute : host -> host * dev
         svc getRB : void -> int
         fun store(i:int list) =
           let val session = generateKey()
               fun mark(a: int, f:unit) =
             put(\"mark\"^toString(a),session,true,20)
           in
             (foldr(mark,i,());
             OnRemote(|print|(toString(hd thisHost()) ^
                              \" stored data successfully\"),
                      getHostByName(\"%s\"),getRB(),defaultRoute))
           end
         fun doit() = store([1;2;3;4;5])" *)
             (Activehost.activeHostToString my_host_addr) in 
Comment out the first block of PLAN code (a string) and uncomment the second:
      (let chunk_code = Printf.sprintf
(*        "svc print : 'a -> unit
         svc toString : 'a -> string
         svc getHostByName : string -> host
         svc defaultRoute : host -> host * dev
         svc getRB : void -> int
         svc thisHost : void -> host list
         fun doit() = 
           OnRemote(|print|(toString(hd thisHost()) ^
                            \" authenticated successfully\"),
                    getHostByName(\"%s\"),getRB(),defaultRoute)" *)
        "svc print : 'a -> unit
         svc getHostByName : string -> host
         svc defaultRoute : host -> host * dev
         svc getRB : void -> int
         svc authSvc : void -> bool
         fun doit() = OnRemote(|print|(authSvc()),
                               getHostByName(\"%s\"),getRB(),
                               defaultRoute)"
(*        "svc print : 'a -> unit
         svc toString : 'a -> string
         svc put : (string,key,'a,int) -> unit
         svc generateKey : void -> key
         svc thisHost : void -> host list
         svc getHostByName : string -> host
         svc defaultRoute : host -> host * dev
         svc getRB : void -> int
         fun store(i:int list) =
           let val session = generateKey()
               fun mark(a: int, f:unit) =
             put(\"mark\"^toString(a),session,true,20)
           in
             (foldr(mark,i,());
             OnRemote(|print|(toString(hd thisHost()) ^
                              \" stored data successfully\"),
                      getHostByName(\"%s\"),getRB(),defaultRoute))
           end
         fun doit() = store([1;2;3;4;5])" *)
             (Activehost.activeHostToString my_host_addr) in 
Then recompile authapp.

Now, we must start node m1 slightly differently, so that it will use expand.qcm. To start it, do:


bin/pland -l log3324 -ip 3324 -policy expand.qcm m1


Now start authapp as before. After it has authenticated with the remote node, rather than simply authenticating and printing the message ``authenticated successfully,'' the program will access this service which requires privilege. If the principal authenticated with is in the privileged set, the message true will be printed as the last of authapp's output; this should happen if you specify key as the app's keys as in the last tutorial:
bin/authapp -p 3324 m key

To see the authorization fail, have authapp use the keys in key.3325, also present in the plan directory:

bin/authapp -p 3324 m key.3325

In this case, ERROR: will be printed in authapp, and the log file for m1 will contain:
Call: No such function authSvc
Eval thread 4 exiting with failure:
  Uncaught packet exception ServiceNotPresent

3.3.2   An Active Firewall

The active firewall is described in more detail in [7]. The basic idea is that it wraps packets coming in from a protected interface with a chunk that evaluates the original chunk associated with a guest principal. The local policy restricts the namespace of this guest principal, thus limiting the effect of external packets.



Figure 1: Firewall Tutorial: Shows three nodes: m1 is the source node, inside the trusted network; m2 is the firewall; and m3 is the unstrusted node. Each connection is labelled by the device name and that device's address, as specified in the interface configuration files for the three respective nodes.

To demonstrate the use of the firewall, we need to set up a three-node network, as depicted in Figure 1. Node m3 in the untrusted network is started as normal:6


bin/pland -l log3326 -ip 3326 m3


where m3 contains:
1
ip0 ip m:3326 m:3327


The middle node, m2, serves as the firewall. It must be started with one additional parameter:


bin/pland -l log3325 -ip 3325 -firewall ip0 m2


where m2 contains:
2
ip0 ip m:3325 m:3324
ip1 ip m:3327 m:3326


The -firewall parameter indicates that pland should function as a firewall rather than a router. The arguments given to this option specify which devices are being protected by the firewall. All packets that are emitted via these devices are first wrapped with a privilege-reducing chunk. Here we specify ip0 as that is the device that leads to the trusted network (see the Figure).

Finally, the trusted node m1 is started as:


bin/pland -l log3324 -ip 3324 -policy firewall.qcm -authlist m:3325 m1


where m1 contains:
1
ip0 ip m:3324 m:3325


There are two additional parameters. The first parameter -policy takes a policy file that specifies which services should be removed from the the guest environment. For this tutorial, we use the file firewall.qcm:
online
{
  restrictions = {
    Principal(<PublicKey="...">)
  };
  restrictions0 = {"DevNotLocal", "FlowNoRoute", "getToll", "setTo
ll", "flowSet", "flow", "RIPNoRoute", "RIPGetRoutes", "RIPRecv", "
getNeighbors", "RIPGetNeighbors", "RIP", "DHmessageThree", "DHmess
ageTwo", "DHmessageOne", "authSvc", "uncompress", "compress", "set
Percent", "setUncompDelay", "setCompDelay", "getHostByName", "Load
GenNotStarted", "completeFlow", "startFlow", "stopSender", "demoSh
owHop", "flowUseNewFlow", "flowNewFlowReady", "flowNewScout", "flo
wGo", "flowKeyReady", "flowStartToDemo", "flowStartToPort", "flowS
tart", "startSenderToDemo", "startSenderToPort", "startSender", "s
tartReceiverDemo", "NonLocalHost", "delete", "genKeyHost", "genera
teKey", "setGT", "setLT", "put", "get", "BindFailed", "NoBinding",
 "retrieveBinding", "bind", "openPort", "abort", "evalBlob", "eval
", "getMTU", "getDevs", "getSrcDev", "getStaticRoutes", "staticRou
te", "remove", "member", "length", "gettimeofday", "timestamp", "t
hisHost", "getSessionKey", "getSrc", "getSource", "logGC", "queryG
C", "exit"};
  acl = {
    ( restrictions, {}, restrictions0 ),
  };
}
This defines s set of principals called restrictions. The sole member of the restrictions set is the firewall's public key (as stored in the file key.3325. This set is stored in the ACL with the thin set as defined by restrictions0. To protect the trusted node using this policy, the firewall wraps incoming packets with an authEval that changes the identity to that of the firewall (serving as the guest identity). Packets running with this principal will have the thinned environment. In order for the firewall to sign incoming packets, the trusted host must authenticate with the firewall. This is done via the -authlist parameter, which takes a comma-separated list of nodes to be authenticated with. The firewall is specified in this list.

Once all of these nodes are started, m1 will authenticate with the m2, just as we saw in Section 2.3.2. Once this authentication is complete, the stage is set to try running some packets. To show how privilege is restricted, we send the Helloworld program from m3 to m1 through the firewall. The policy specified in firewall.qcm indicates that the thisHost service should be removed. Indeed, when we do:


bin/inject -p 3326 -ed m:3324 interp_tests/Helloworld.plan 10
doit();


The result is
1.1-1.6: Warning: service doit not declared.
ERROR: <chunk: 
doit[]> raised ServiceNotPresent on (127.0.0.1,3324)
and at the end of the log file for m1 we see
Call: No such function thisHost
Eval thread 1 exiting with failure:
  Uncaught packet exception ServiceNotPresent
On the other hand, if we run a program, such as ping, that does not use any thinned services (e.g. thisHost) on m1, things should work OK. Indeed, if we run bin/planping:


bin/planping m:3326
PING 0 483.964920 ms
PING 1 21.622062 ms
PING 2 21.566033 ms
...

3.3.3   Privileged Usage

As described in the previous section, we have implemented privileged usage for the resident state package. In this section we demonstrate the how privileged service usage works using this service suite. The policy file that we use for m1 in this case is usage.qcm:
online
{
  privileged = {
    Principal(<PublicKey="P: iuWJZ7KVoRbhoCDrDqUSo7pd/hsYFvs9E5rk2
JYB2W7M06F2ughbk8zaIzgiVriopSMhZziJXyvs6fla/6QzRIwMwhLtTUzAIQZYDUD
duJjClHZzU8lN7FM4jGNyq3wOn/uH/L9i3tJGm/V9ywfqqUFNF0O2amlxhM1s1JMci
kM=, Q: ledWdPQsmKTqyynTCBn0vWlhFeM=, ALPHA: D7CquhERwI22+bW0JfY9o
D+tLiV7+0p+5y12Mvx/RYsQgwmA1BUD6PqyZ54lXRh3hm6k/AM9zvYXbcG8Dx49uq6
jQXHdjQCK2c8rlztrjplJvPdG5bSMTZzbVJAY1oDM9sugFGmMJLvizqFsZfJsrE13P
pOTNc7LRS8b26NodUM=, Y: Ow++XUNYt5JnxuQ4WgjvESddl+CKV6vEE6SGSu+mbI
ubArSzYUV2OBYdMU6uqZSaPxX0lSIDrHC+Tvq4ckNU+U5LciYDydgfUxFQQXbLInwS
UI2iaus9jGVP5Px8gASIkhy0eKQg45qZHySalBpuA6AyDlHqRMIR7IK8zV2XKcQ=">)
  };

  acl = {
    ( privileged, {}, {} )
  };

  # usage-based security
  # this key matches Policy_qcm.Policy_impl.default_principal
  default = {
    Principal(<PublicKey="P: 6e+1xVU3D5dHFrZo+Cd2YsR/CxvFVcDyAImn5
nESz17GjBurur3xrEVj7XV+wpS7N2XtZ0QPeNoUId1RRXAY2qDK8Cc7WyofsomqCzW
5Sdz4d4sa45J2ur/+pdiZtQEnfGoYd15mUPhShE5BtfaYOVrBzrfX7pqIPLhq476zo
bs=, Q: 2dfTnTvli0m4HzCh9rlIaKT2alk=, ALPHA: OXmMrRFwHdRY2i/ya7d1m
urBUkhJwc+H4lJTpZ7LJNKcOs3+HolntEwtWiO5B4OwwSO2Pl5B7azZxm23zFXUJVX
QATGEH13XsX+BEaxrIMj6Vh+dOw5B3286wjhAm/lgyglrSsl20BNqNeW42zTMCoYQe
Frs+vI4Z10Zx8yCMmY=, Y: D+4Mymn0id6KMo0gZjappc1Azd/qM3LI2A8aaTERym
YQe0bqULkLxX/SiYkGaLZNJ9LJ2Iy9EQGiG2NPEaC+nENHnYYDfSKOXjaaeCOaulRE
kyOQVK5qvvXql3lYAbinnBMRrJx5nZoL2Te1VAW/KY5JNM9riUSp6KdKKnT+dE4=">)
  };
  resident = { ( default, <amount=4> ),
               ( privileged, <amount=1000> ) };
}
In this file, two principal sets are defined: one contains the default principal (matching that defined in Policy_qcm.Policy_impl.default_principal) and the other contains a privileged principal. The policy is implemented by the resident set: it specifies that the default principal may store (at any one time) at most 4 words of soft state, while the privileged user may store 1000 words.7

To demonstrate the use of this policy, we've provided a PLAN program interp_tests/store.plan which stores the value true on the node 5 times, exceeding the policy for the default user, but well under it for the privileged one. This code has also been incorporated into authapp; you need to uncomment the last region of code:
      (let chunk_code = Printf.sprintf
(*        "svc print : 'a -> unit
         svc toString : 'a -> string
         svc getHostByName : string -> host
         svc defaultRoute : host -> host * dev
         svc getRB : void -> int
         svc thisHost : void -> host list
         fun doit() = 
           OnRemote(|print|(toString(hd thisHost()) ^
                            \" authenticated successfully\"),
                    getHostByName(\"%s\"),getRB(),defaultRoute)" *)
        "svc print : 'a -> unit
         svc getHostByName : string -> host
         svc defaultRoute : host -> host * dev
         svc getRB : void -> int
         svc authSvc : void -> bool
         fun doit() = OnRemote(|print|(authSvc()),
                               getHostByName(\"%s\"),getRB(),
                               defaultRoute)" *)
        "svc print : 'a -> unit
         svc toString : 'a -> string
         svc put : (string,key,'a,int) -> unit
         svc generateKey : void -> key
         svc thisHost : void -> host list
         svc getHostByName : string -> host
         svc defaultRoute : host -> host * dev
         svc getRB : void -> int
         fun store(i:int list) =
           let val session = generateKey()
               fun mark(a: int, f:unit) =
             put(\"mark\"^toString(a),session,true,20)
           in
             (foldr(mark,i,());
             OnRemote(|print|(toString(hd thisHost()) ^
                              \" stored data successfully\"),
                      getHostByName(\"%s\"),getRB(),defaultRoute))
           end
         fun doit() = store([1;2;3;4;5])"
             (Activehost.activeHostToString my_host_addr) in 
Let us first run this as the default user. Start up the node, using the usage.qcm policy:


bin/pland -l log3324 -ip 3324 -policy usage.qcm m1


Then, inject the store.plan program with initial invocation doit:


bin/inject -p 3326 -ed m:3324 interp_tests/Helloworld.plan 10
doit();


The result is
1.1-1.6: Warning: service doit not declared.
ERROR: <chunk: 
doit[]> raised InsufficientPrivilege on (127.0.0.1,3324)
and at the end of the log file for m1 we see
Principal |P: 6e+1xVU3D5dHFrZo+Cd2YsR/CxvFVcDyAImn5nESz17GjBurur3x
rEVj7XV+wpS7N2XtZ0QPeNoUId1RRXAY2qDK8Cc7WyofsomqCzW5Sdz4d4sa45J2ur
/+pdiZtQEnfGoYd15mUPhShE5BtfaYOVrBzrfX7pqIPLhq476zobs=, Q: 2dfTnTv
li0m4HzCh9rlIaKT2alk=, ALPHA: OXmMrRFwHdRY2i/ya7d1murBUkhJwc+H4lJT
pZ7LJNKcOs3+HolntEwtWiO5B4OwwSO2Pl5B7azZxm23zFXUJVXQATGEH13XsX+BEa
xrIMj6Vh+dOw5B3286wjhAm/lgyglrSsl20BNqNeW42zTMCoYQeFrs+vI4Z10Zx8yC
MmY=, Y: D+4Mymn0id6KMo0gZjappc1Azd/qM3LI2A8aaTERymYQe0bqULkLxX/Si
YkGaLZNJ9LJ2Iy9EQGiG2NPEaC+nENHnYYDfSKOXjaaeCOaulREkyOQVK5qvvXql3l
YAbinnBMRrJx5nZoL2Te1VAW/KY5JNM9riUSp6KdKKnT+dE4=|
  Allowed=4
  Current=4
  Request=1
Eval thread 4 exiting with failure:
  Uncaught packet exception InsufficientPrivilege
Now let's try this program with greater privilege. Start authapp as before, using the keyfile key. Now, after it has authenticated with the remote node, it will run the store program, but it will complete successfully, printing on authapp:
127.0.0.1:3324 stored data successfully

3.4   Implementation Notes

To use QCM in a hierarchical fashion, it must be possible for multiple PLAN evaluations to execute concurrently. Due to overheads in the runtime system, this causes a significant performance hit, so this functionality is not enabled by default. To enable it, you need to recompile plan with the -DMULTI_EVAL flag added to the CAMLP4FLAGS variable in the Makefile.

QCM has its own notion of the node's identity as specified in the QCM program at startup. If a public/private key pair is specified in the QCM program, it will be overridden by any keys given explicitly to pland at startup.


Previous Next Contents