[omniORB] Security/Authentication

JHJE (Jan Holst Jensen) jhje@novonordisk.com
Thu Feb 27 13:21:02 2003


> Can you post an example of how you do this? It sounds interesting.

Sure. Although this code is in Object Pascal, it should be general enough
CORBA-wise to translate well to C++. "High(sc.context_data) + 1" returns the
length of a service context octet sequence, and "Move(....)" is a mem -> mem
copy function.

This is still a work in progress, but I have the authenticator running and
the interceptors shown here work fine. The authentication service has an IDL
like

interface authenticator {
  boolean authenticate(in string user_name, in string password, out long
session_token);
  boolean is_valid_session_token(in string user_name, in long
session_token);
};

Register a client request interceptor and have it piggy-back user name and
session token onto IIOP messages after having called the authenticator
interface and getting a session_token in return:

const
  // Both client and server must agree on the service request IDs used.
  USERNAME_CTXID = 100;
  SESSION_CTXID  = 101;

var
  // Client sets UserName and gets SessionToken from authenticator service.
  UserName: String;
  SessionToken: Integer;

procedure TMyClientInterceptor.send_request(const ri: IClientRequestInfo);
var
  sc: ServiceContext;
begin
  sc.context_id := USERNAME_CTXID;
  // Allocate service request context data buffer.
  SetLength(sc.context_data, Length(UserName));
  // Copy string data to service context buffer.
  Move(UserName[1], sc.context_data[0], Length(UserName));
  ri.add_request_service_context(sc, false);

  sc.context_id := SESSION_CTXID;
  SetLength(sc.context_data, SizeOf(SessionToken));
  Move(SessionToken, sc.context_data[0], SizeOf(SessionToken));
  ri.add_request_service_context(sc, false);
end;

On the server side, register a server request interceptor and do:

procedure TMyServerInterceptor.receive_request_service_contexts(const ri:
IServerRequestInfo);
var
  sc: ServiceContext;
  UserName: String;
  SessionToken: Integer;
begin
  UserName := '';
  SessionToken := -1;

  try
    sc := ri.get_request_service_context(USERNAME_CTXID);
    { Allocate string buffer based on length of service context octet
sequence. }
    SetLength(UserName, High(sc.context_data) + 1);
    Move(sc.context_data[0], UserName[1], High(sc.context_data) + 1);
    Writeln('User name "', UserName, '".');
  except
    on E: BAD_PARAM do
      writeln(E.ClassName, ': No user name embedded in request.');
  end;

  try
    sc := ri.get_request_service_context(SESSION_CTXID);
    // Potential Access Violation lurking here if sc.context_data is not at
least
    // 4 bytes long, so use min() function.
    Move(sc.context_data[0], SessionToken, 
         min(SizeOf(SessionToken), High(sc.context_data) + 1));
    Writeln('  with session token ', SessionToken);
  except
    on E: BAD_PARAM do
      writeln(E.ClassName, ': No session token embedded in request.');
  end;

  // Do access check using is_valid_session_token, raise
CORBA::NO_PERMISSION if it fails.
end;

Remaining issues:

* I suppose I should encode/decode the UserName string so it transfers well
between different architectures and platforms.
* Same goes for SessionToken, at least making sure that it it encoded like a
CORBA::long is encoded in an IIOP message.
* The session token is really a password proxy in this respect so you must
keep it secret (use e.g. SSL) and have it time out some way.

I assume these issues would be readily solved by buying a CORBA security
service, but so far I found the security service docs a bit overwhelming so
I just threw this code together. At least it gave me a good grip on the
issues involved.

Has anyone tried embedding a Kerberos ticket like this ? Could that be used
by the server to impersonate the client (really useful for database-apps) ?

-- Jan