n-Tier Authentication

advertisement
n-Tier Authentication
n-Tier, or proxy, authentication is the ability for middle tier software to log onto the database using its
'own' credentials on another user's behalf, in order to perform some operation in the database. This
allows us to build middle tier applications that use their own authentication scheme, perhaps via X509
certificates, or by some other single sign-on process, to securely log into a database on your behalf
without having to know your database password. As far as the database is concerned, you are logged in.
However, the credentials that are used to log in are not yours; they are those of the middle tier.
In this chapter, we'll take a look at the features of n-Tier authentication and at how to implement this
new Oracle 8i functionality in your applications. Specifically, we will:
❑
Introduce the feature and look at why you might want to use it in your applications.
❑
Develop an OCI program that will allow you to use this proxy authentication scheme to log in
to the database.
❑
Investigate the ALTER USER command that enables this feature in the database.
❑
Discuss the auditing options that allow you to track the operations of the proxy account.
Currently, in Oracle 8i, n-tier authentication is limited to Oracle Call Interface (OCI) programs written
in C or C++. Looking ahead to Oracle 9i, this feature will be available in JDBC as well, thus greatly
expanding the audience that can make use of this feature.
5254ch22.pdf 1
2/28/2005 6:52:55 PM
Chapter 22
Why Use n-Tier Authentication?
In the days of client-server and host-based systems, authentication was easy. The client (your
application) prompted the end user for their credentials (username and password) and presented these
credentials to the database server. The database verified those credentials, and you were connected:
Scott/Tiger
Database
Application
Now we have the Web and with it an n-Tier architecture where, for example, a client (your browser)
presents credentials to a middle tier application server running a JavaServer Page (JSP), which in turn
makes a callout to a CORBA object that finally accesses the database. The credentials presented to the
middle tier may or may not be the same as the database username and password from the client server
days – it might be credentials that permit access to a directory service, so the middle tier can discover
who you are and what access privileges you have. It may be credentials presented in the form of an
X.509 certificate that carries your identity and privileges. In any case, they are not necessarily
credentials that the middle tier can use to log you into the database.
The fact is that the client is no longer talking to the database directly; there are one, two, or more layers
in between. You could of course, make the end user pass their database username and password to the
JSP, which would pass it to the CORBA object, which would pass it to the database but that thwarts the
use of other technologies and authentication mechanisms, especially single sign-on mechanisms.
Consider the following example – a fairly typical web-based application today:
Directory
Service
Bug
DB
2
3
Scheduler
DB
1
Web Browser
4
Expense
Report
DB
Application Server
Meta-Link
DB
964
5254ch22.pdf 2
2/28/2005 6:52:56 PM
n-Tier Authentication
The client is really just a web browser, displaying HTML, which submits requests to a web/application
server over HTTP (1). The application itself resides in the web/application server, perhaps as a Java
servlet, Apache module, and so on. In the above diagram, the middle tier application server uses a
directory service, perhaps running LDAP, to which it transmits the credentials you supplied (2). The
directory service is used as a means to authenticate the browser session. If authentication is successful,
the application server is notified (3) and, finally, the application server logs in to one of many existing
databases (4) to retrieve data and process transactions.
The 'credentials' passed from browser to application server in (1) can take many forms – a
username/password, a cookie from a single sign-on server, a digital certificate of some sorts – anything.
The only thing that is generally true is that we did not pass our database username and password.
The problem is, of course, that the application server needs a database login and password to
authenticate the user to a back end database. Furthermore the username/password combination will be
different in each case. In the above example, we have four databases:
❑
A Bug database, which might recognize me as, say, TKYTE.
❑
An Expense Report database which might recognize me as TKYTE_US.
❑
A Scheduler database which might recognize me as WEB$TKYTE.
❑
...And so on...
Stop and think for a moment – how many usernames and passwords do you have? I have at least 15 that
I can remember off of the top of my head. Furthermore, although my identity in the database is never
changing, my password changes frequently. Now, wouldn't it be nice if we could authenticate ourselves
once – to the application server – and then the App server itself could access each of the backend
databases on our behalf (in other words, by proxy), without needing to be given the specific password
for each of these databases? This is what n-Tier authentication is all about.
The feature that enables this in the database is a simple connection option. In Oracle 8i the ALTER
USER command has been modified to support the GRANT CONNECT THROUGH clause (this is discussed in
full detail later, in the Granting the Privilege section). Consider access to the Expense Report database
in the above application:
Directory
Service
TomKyte/
TomKyte_password
Web Browser
App_Server/pass
word as TomKyte_US
Expense
Report
DB
Application Server
965
5254ch22.pdf 3
2/28/2005 6:52:56 PM
Chapter 22
The directory service stores the mapping information that associates TomKyte with the database client
TKYTE_US. Once that information has been successfully retrieved, the application server account (the
proxy) can then log into the database, using its own credentials, on behalf of the client in the
database, TKYTE_US. The application server does not need to know TKYTE_US's password.
To allow this to happen, the administrator of the Expense Report database would need to grant the
schema APP_SERVER permission to connect as the client:
alter user tykte_us grant connect through app_server
The application server will execute in the database with the identity and privileges of TKYTE_US, and it
will be as if TKYTE_US logged directly into the database.
In this manner, Oracle 8i extends the security model so that the application server can securely act on
behalf of the client, without requiring the database password of the client, or requiring many privileges
that allow it to access objects or procedures that it has no need to access. In addition, auditing has been
extended to include operations performed by the application server on behalf of a client. That is, we can
see that the application server, on behalf of a client, performed some operation (see the Auditing Proxy
Accounts section for full details).
We'll now move on to discuss how to implement this feature. As indicated in the introduction, this
feature is currently only accessible via the Oracle Call Interface (OCI) programs written in C or C++.
The Mechanics of n-Tier Authentication
In this section, we'll take one of the supplied OCI demonstration programs that implements a miniSQL*PLUS, and modify it to use the proxy authentication scheme to log in to the database. This will
give us a small, interactive SQL tool that will let us explore how this works, and what side effects we can
expect to see. As a bonus, you'll have a tool that, provided you have been granted the appropriate
privileges, will allow you to log in as another user and perform ad-hoc operations on their behalf, all the
while being audited as yourself. You could use this, for example, to GRANT SELECT on another user's
table without having to know their password.
The necessary C code that we will graft into the cdemo2.c example (found in
[ORACLE_HOME]\rdbms\demo) will just be a login routine. Once we've done that, we'll be able to login
using OS Authentication to the SCOTT account and enable the CONNECT role, as follows:
C:\> cdemo2 / scott CONNECT
Alternatively, we can login using a username and password to a database remote to the SCOTT account
and enable the roles RESOURCE and PLUSTRACE:
C:\> cdemo2 user/pass@database scott RESOURCE,PLUSTRACE
The C routine that we need to build n-Tier demonstrates how to login using this n-Tier authentication –
we'll take a look at it piecebypiece. Once we are logged in, the rest of the application is just standard
OCI code, no different from any other OCI program.
966
5254ch22.pdf 4
2/28/2005 6:52:56 PM
n-Tier Authentication
The beginning of the C code includes the standard oci.h header file found in
ORACLE_HOME]\rdbms\demo. This include file holds the necessary function prototypes and defines
all OCI programs. Next, we declare a couple of variables local to our login routine. Specifically, we
have the normal OCI handles for a connection – but notice that we have two OCISession handles.
One is for the account we will present the credentials for (login with) and the other will be for the
account we want to 'become'. The remaining local variables are pretty self-explanatory – they hold the
username, password, database, and all of the roles we want enabled for us:
#include <oci.h>
void checkerr( OCIError * errhp, sword status);
Lda_Def connect8i( int argc, char * argv[] )
{
OCIEnv
*environment_handle;
OCIServer *data_server_handle;
OCIError
*error_handle;
OCISvcCtx *application_server_service_handle;
OCISession *first_client_session_handle;
OCISession *application_server_session_handle;
char
char
char
char
char
char
int
*username;
*password;
*database;
temp[255];
role_buffer[1024];
*roles[255];
nroles;
Next, we validate the command line arguments passed to this routine. If the number of arguments is not
equal to four, we have not been passed enough information and we'll just print out a usage and exit.
Otherwise, we'll parse (using the standard C function strtok) the inputs. Since strtok is destructive
(it changes the string it is parsing), we copy the arguments into local variables before breaking them up:
if ( argc != 4 )
{
printf( "usage: %s proxy_user/proxy_pass real_account_name role1,…\n",
argv[0] );
printf( "
proxy_user/proxy_pass can just be /\n" );
printf( "
real_account_name is what you want to connect to\n" );
exit(1);
}
strcpy( temp, argv[1] );
username = strtok( temp, "/" );
password = strtok( NULL, "@" );
database = strtok( NULL, "" );
strcpy( role_buffer, argv[3] );
for( nroles = 0, roles[nroles] = strtok(role_buffer,",");
roles[nroles] != NULL;
nroles++, roles[nroles] = strtok(NULL,",") );
967
5254ch22.pdf 5
2/28/2005 6:52:57 PM
Chapter 22
Now we do some general initialization and allocation of contexts. This is standard for all OCI routines:
OCIInitialize( OCI_DEFAULT, NULL, NULL, NULL, NULL );
OCIEnvInit( &environment_handle, OCI_DEFAULT, 0, NULL );
OCIHandleAlloc( (dvoid *) environment_handle,
(dvoid **) &error_handle,
OCI_HTYPE_ERROR, 0, NULL );
Next, we allocate and initialize the server and service contexts used by the 'application server'. In this
case, the application server is going to be our mini-SQL*PLUS tool cdemo2. This code attaches us to
the server but does not yet begin a session:
checkerr(error_handle,
OCIHandleAlloc(environment_handle,
(dvoid **)&data_server_handle,
OCI_HTYPE_SERVER,
0, NULL)
);
checkerr(error_handle,
OCIHandleAlloc((dvoid *) environment_handle,
(dvoid **) &application_server_service_handle,
OCI_HTYPE_SVCCTX, 0, NULL)
);
checkerr(error_handle,
OCIServerAttach(data_server_handle,
error_handle,
(text *)database?database:"",
strlen(database?database:""), 0)
);
checkerr(error_handle,
OCIAttrSet((dvoid *) application_server_service_handle,
OCI_HTYPE_SVCCTX,
(dvoid *) data_server_handle,
(ub4) 0,
OCI_ATTR_SERVER,
error_handle)
);
Now we are ready to initialize and then authenticate the application server session handle. In this case,
either external authentication or username/password is being used:
checkerr(error_handle,
OCIHandleAlloc((dvoid *) environment_handle,
(dvoid **)&application_server_session_handle,
(ub4) OCI_HTYPE_SESSION,
(size_t) 0,
(dvoid **) 0)
);
968
5254ch22.pdf 6
2/28/2005 6:52:57 PM
n-Tier Authentication
Having initialized our session handle, we now need to add the authentication information. We permit
either OS-based authentication (which would not require our application server to pass any username
and password at all to the server) or standard username/password authentication. We'll put the code for
username/password-based authentication first:
if (username != NULL && password != NULL && *username && *password)
{
checkerr(error_handle,
OCIAttrSet((dvoid *) application_server_session_handle,
(ub4) OCI_HTYPE_SESSION,
(dvoid *) username, (ub4) strlen((char *)username),
(ub4) OCI_ATTR_USERNAME, error_handle)
);
checkerr( error_handle,
OCIAttrSet((dvoid *) application_server_session_handle,
(ub4) OCI_HTYPE_SESSION,
(dvoid *) password,
(ub4) strlen((char *)password),
(ub4) OCI_ATTR_PASSWORD,
error_handle)
);
checkerr(error_handle,
OCISessionBegin (application_server_service_handle,
error_handle,
application_server_session_handle,
OCI_CRED_RDBMS,
(ub4) OCI_DEFAULT)
);
}
Now, the code to handle OS authentication:
else
{
checkerr(error_handle,
OCISessionBegin(application_server_service_handle,
error_handle,
application_server_session_handle,
OCI_CRED_EXT,
OCI_DEFAULT)
);
}
We are now ready to initialize the session for our client (the user who is logging in, and on whose behalf
we have been trusted to execute processes). First we initialize the session:
checkerr(error_handle,
OCIHandleAlloc((dvoid *) environment_handle,
(dvoid **)&first_client_session_handle,
(ub4) OCI_HTYPE_SESSION,
(size_t) 0,
(dvoid **) 0)
);
969
5254ch22.pdf 7
2/28/2005 6:52:57 PM
Chapter 22
Then we set the username to be associated with this session:
checkerr( error_handle,
OCIAttrSet((dvoid *) first_client_session_handle,
(ub4) OCI_HTYPE_SESSION,
(dvoid *) argv[2],
(ub4) strlen(argv[2]),
OCI_ATTR_USERNAME,
error_handle)
);
Next, we add the list of roles we want enabled for this session – if we skip this call, all default roles for
that user would be enabled:
checkerr( error_handle,
OCIAttrSet((dvoid *) first_client_session_handle,
(ub4) OCI_HTYPE_SESSION,
(dvoid *) roles,
(ub4) nroles,
OCI_ATTR_INITIAL_CLIENT_ROLES,
error_handle)
);
Now we are ready to begin our actual session. First, we associate our client session (who we want to be
seen as in the database) with our application server session (our proxy account):
checkerr(error_handle,
OCIAttrSet((dvoid *) first_client_session_handle,
(ub4) OCI_HTYPE_SESSION,
(dvoid *) application_server_session_handle,
(ub4) 0,
OCI_ATTR_PROXY_CREDENTIALS,
error_handle)
);
checkerr(error_handle,
OCIAttrSet((dvoid *)application_server_service_handle,
(ub4) OCI_HTYPE_SVCCTX,
(dvoid *)first_client_session_handle,
(ub4)0,
(ub4)OCI_ATTR_SESSION,
error_handle)
);
And then we begin the session:
checkerr(error_handle,
OCISessionBegin(application_server_service_handle,
error_handle,
first_client_session_handle,
OCI_CRED_PROXY,
OCI_DEFAULT)
);
970
5254ch22.pdf 8
2/28/2005 6:52:57 PM
n-Tier Authentication
Now, since this is a version 7 OCI program (cdemo2.c is a v.7 program), we need to convert our
Oracle 8i login data into something usable. Here we convert a version 8 connection into a version 7
OCI LDA (Login Data Area) and return it:
checkerr(error_handle,
OCISvcCtxToLda( application_server_service_handle,
error_handle,
&lda )
);
return lda;
}
The last bit of code is the checkerr routine that we referenced many times above. It is used to verify
that the return codes from the OCI functions above indicate success, otherwise it prints out the error
message and exits:
void checkerr(OCIError * errhp, sword status)
{
text
errbuf[512];
sb4
errcode = 0;
switch (status)
{
case OCI_SUCCESS:
break;
case OCI_SUCCESS_WITH_INFO:
(void) printf("Error - OCI_SUCCESS_WITH_INFO\n");
break;
case OCI_NEED_DATA:
(void) printf("Error - OCI_NEED_DATA\n");
break;
case OCI_NO_DATA:
(void) printf("Error - OCI_NODATA\n");
break;
case OCI_ERROR:
(void) OCIErrorGet((dvoid *)errhp, (ub4) 1, (text *) NULL, &errcode,
errbuf, (ub4) sizeof(errbuf), OCI_HTYPE_ERROR);
(void) printf("Error - %.*s\n", 512, errbuf);
exit(1);
break;
case OCI_INVALID_HANDLE:
(void) printf("Error - OCI_INVALID_HANDLE\n");
break;
case OCI_STILL_EXECUTING:
(void) printf("Error - OCI_STILL_EXECUTE\n");
break;
case OCI_CONTINUE:
(void) printf("Error - OCI_CONTINUE\n");
break;
default:
break;
}
}
971
5254ch22.pdf 9
2/28/2005 6:52:57 PM
Chapter 22
Now, we need to modify cdemo2.c and inject our new code. The existing code in that sample program
looks like this:
...
static sword numwidth = 8;
main()
{
sword col, errno, n, ncols;
text *cp;
/* Connect to ORACLE. */
if (connect_user())
exit(-1);
...
The modification is very straightforward, we simply need to add the code in bold:
...
static sword numwidth = 8;
Lda_Def connect8i( int argc, char * argv[] );
main( int argc, char * argv[] )
{
sword col, errno, n, ncols;
text *cp;
/* Connect to ORACLE. */
/*
if (connect_user())
exit(-1);
*/
lda = connect8i( argc, argv );
...
Next, add the entire contents of the code from above (the connect8i and checkerr subroutines) to
the bottom of the source code file. Save that file and compile.
On UNIX, the command to compile this will be:
$ make -f $ORACLE_HOME/rdbms/demo/demo_rdbms.mk cdemo2
On Windows NT, I used this makefile:
CPU=i386
WORK_DIR
= .\
!include <\msdev\include\win32.mak>
OBJDIR
EXEDIR
=
= $(WORK_DIR)\
$(WORK_DIR)\
#
#
dir where all the .exe will be put
972
5254ch22.pdf 10
2/28/2005 6:52:57 PM
n-Tier Authentication
ORACLE_HOME = \oracle
TARGET
= $(EXEDIR)cdemo2.exe
SAMPLEOBJS
= cdemo2.obj
LOCAL_DEFINE = -DWIN_NT
SYSLIBS = \msdev\lib\msvcrt.lib \
\msdev\lib\oldnames.lib \
\msdev\lib\kernel32.lib \
\msdev\lib\advapi32.lib \
\msdev\lib\wsock32.lib
NTUSER32LIBS
SQLLIB
= \msdev\lib\user32.lib \
\msdev\lib\advapi32.lib \
\msdev\lib\libc.lib
= $(ORACLE_HOME)\oci\lib\msvc\oci.lib
INCLS
= -I\msdev\include \
-I$(ORACLE_HOME)\oci\include
CFLAGS = $(cdebug) $(cflags) $(INCLS) $(LOCAL_DEFINE)
LINKOPT = /nologo /subsystem:console /machine:I386 /nodefaultlib
$(TARGET): $(SAMPLEOBJS) $(SQLLIB)
$(link) $(LINKOPT) \
-out:$(TARGET) $(SAMPLEOBJS) \
$(NTUSER32LIBS) \
$(SYSLIBS) \
$(SQLLIB)
And then just used nmake to compile it:
c:\oracle\rdbms\demo>nmake
Now we are ready to try it:
c:\oracle\rdbms\demo>cdemo2 tkyte/tkyte scott connect,resource
Error - ORA-28150: proxy not authorized to connect as client
So, we do not have success yet but we are very close. We need to give our proxy (TKYTE) the authority
to connect as the database client (SCOTT). Log into SQL*PLUS and issue:
sys@TKYTE816> alter user scott grant connect through tkyte;
User altered.
We'll explain more about that new command in a moment, along with all of its options. Now we just
want to see this work. Notice the bolded prompt – I am not in SQL*PLUS here, I am in the cdemo2
application, it just looks a lot like SQL*PLUS:
973
5254ch22.pdf 11
2/28/2005 6:52:57 PM
Chapter 22
c:\oracle\rdbms\demo>cdemo2 tkyte/tkyte scott connect,resource
OCISQL> SELECT user, substr(sys_context('userenv','proxy_user'),1,30)
2
FROM dual;
USER
SUBSTR(SYS_CONTEXT('USERENV','PRO
------------------------------ -----------------------------SCOTT
TKYTE
1 row processed.
OCISQL> select * from session_roles;
ROLE
-----------------------------CONNECT
RESOURCE
2 rows processed.
OCISQL> select distinct authentication_type from v$session_connect_info
2
where sid = ( select sid from v$mystat where rownum =1 );
AUTHENTICATION_TYPE
-------PROXY
1 row processed.
OCISQL> exit
C:\oracle\RDBMS\demo>
That's it. We've successfully logged in as SCOTT without knowing SCOTT's password. Additionally,
we've seen how we can verify that we are proxied in, by comparing USER to SYS_CONTEXT's
PROXY_USER, or by looking in the V$SESSION_CONNECT_INFO view. Also, we can clearly see that the
roles CONNECT and RESOURCE are enabled. If we connected like this:
c:\oracle\rdbms\demo>cdemo2 tkyte/tkyte scott connect
OCISQL> select * from session_roles;
ROLE
-----------------------------CONNECT
1 row processed.
We can see that only CONNECT is enabled – we control what roles the 'application server' enables.
974
5254ch22.pdf 12
2/28/2005 6:52:57 PM
n-Tier Authentication
Granting the Privilege
The basic syntax of the ALTER USER command is as follows:
Alter user <username> grant connect through <proxy user><,proxy user>...
This gives the usernames listed in the proxy user list the ability to connect as username. By default,
these listed users will have all of that user's roles available to them. There is a variation of this
command:
Alter user <username> grant connect through <proxy> WITH NONE;
This allows the proxy account to connect as username but only with their base privileges – no roles
will be enabled. Additionally, we can use:
Alter user <username> grant connect through <proxy> ROLE rolename,rolename,...
or:
Alter user <username> grant connect through <proxy> ROLE ALL EXCEPT
rolename,rolename,...
The purpose of the above two statements would be to give a proxy account the ability to connect as a
given user, but only with certain application roles enabled. You do not have to give the application
server proxy account all of the privileges, just the necessary roles to accomplish the job. By default,
Oracle tries to enable all default roles for the user and the PUBLIC role. This would be appropriate if
the application server was only allowed to assume the HR role for a given user, and none of the other
application roles that user was able to use.
There is, of course, also a method to revoke:
Alter user <username> REVOKE connect through <proxy user><,proxy user>...
There is an administrative view, PROXY_USERS, which you may use to review all granted proxy
accounts. Right now, given that we issued the ALTER user SCOTT GRANT CONNECT through tkyte;
command – our PROXY_USERS view has:
TKYTE@TKYTE816> select * from proxy_users;
PROXY
CLIENT
ROLE
FLAGS
-------- -------- ---------- ----------------------------------TKYTE
SCOTT
PROXY MAY ACTIVATE ALL CLIENT ROLES
975
5254ch22.pdf 13
2/28/2005 6:52:58 PM
Chapter 22
Auditing Proxy Accounts
The new syntax of the audit command with regards to proxy accounts is:
AUDIT <operation> BY <proxy>, <proxy> ... ON BEHALF OF <client>, <client>..;
or:
AUDIT <operation> BY <proxy>, <proxy> ON BEHALF OF ANY;
The new part is the BY <proxy> and ON BEHALF OF. It allows us to specifically audit operations
performed by specific proxy users on behalf of any specific or all accounts.
For example, suppose you enabled auditing by setting AUDIT_TRAIL=TRUE in your init.ora and
restarting the instance. We could then use:
sys@TKYTE816> audit connect by tkyte on behalf of scott;
Audit succeeded.
Now, if I use our modified cdemo2.c to connect:
C:\oracle\RDBMS\demo>cdemo2 tkyte/tkyte scott connect
OCISQL> exit
I'll find this record in DBA_AUDIT_TRAIL:
OS_USERNAME
USERNAME
USERHOST
TERMINAL
TIMESTAMP
OWNER
OBJ_NAME
ACTION
ACTION_NAME
NEW_OWNER
NEW_NAME
OBJ_PRIVILEGE
SYS_PRIVILEGE
ADMIN_OPTION
GRANTEE
AUDIT_OPTION
SES_ACTIONS
LOGOFF_TIME
LOGOFF_LREAD
LOGOFF_PREAD
LOGOFF_LWRITE
LOGOFF_DLOCK
COMMENT_TEXT
SESSIONID
ENTRYID
STATEMENTID
RETURNCODE
PRIV_USED
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
Thomas?Kyte
SCOTT
TKYTE-DELL
08-may-2001 19:19:29
101
LOGOFF
08-may-2001 19:19:30
23
0
6
0
Authenticated by: PROXY: TKYTE
8234
1
1
0
CREATE SESSION
976
5254ch22.pdf 14
2/28/2005 6:52:58 PM
n-Tier Authentication
It is interesting to note that if either SCOTT or TKYTE connects via SQL*PLUS, no audit trail record is
created. Auditing is strictly limited to:
connect by tkyte on behalf of scott;
I can still audit connects by TKYTE or SCOTT if I want, I just chose not to in this case. This just shows
that we can still have accountability in the database (we can tell it was SCOTT that performed some
action), but we can also tell when it was an application server executing on behalf of SCOTT.
Caveats
In general, n-Tier authentication works exactly like you might expect it to. If you connect:
C:\oracle\RDBMS\demo>cdemo2 tkyte/tkyte scott connect
It will be as if SCOTT logged in directly. Features like Invoker and Definer Rights (Chapter 23) work as if
SCOTT logged in. Fine Grained Access Control (Chapter 21) work as if SCOTT logged in. Login triggers for
SCOTT will fire as expected. And so on. I have not found any feature to be negatively affected by the
use of n-Tier authentication.
There is one implementation detail that might be an issue, however. When using n-Tier authentication,
the server will enable a set of roles for you. If you use the OCI_ATTR_INITIAL_CLIENT_ROLES
attribute that we used above, you might expect this set of roles to be limited only to those you specify.
However, the roles that have been granted to PUBLIC are always enabled as well. For example, if we
grant the PLUSTRACE role to PUBLIC (PLUSTRACE is the AUTOTRACE role we've been making use of
throughout this book to do performance tracing in SQL*PLUS):
sys@TKYTE816> grant plustrace to public;
Grant succeeded.
Now, when we connect with our mini-SQL*PLUS:
c:\oracle\rdbms\demo>cdemo2 tkyte/tkyte scott connect
OCISQL> select * from session_roles;
ROLE
-----------------------------CONNECT
PLUSTRACE
2 rows processed.
You'll notice that in addition to the CONNECT role being enabled, the PLUSTRACE role is also enabled.
This might not seem like a bad thing at first. However, if you've used the ALTER USER command to pass
on only a few roles to some user:
sys@TKYTE816> alter user scott grant connect through tkyte with role CONNECT;
User altered.
977
5254ch22.pdf 15
2/28/2005 6:52:58 PM
Chapter 22
You'll now discover that:
c:\oracle\rdbms\demo>cdemo2 tkyte/tkyte scott connect
Error - ORA-28156: Proxy user 'TKYTE' not authorized to set role 'PLUSTRACE' for
client 'SCOTT'
The user, TKYTE, is not allowed to enable this role when connecting on SCOTT's behalf. The only
solutions to this issue are:
1.
Do not grant roles to PUBLIC
2.
Or, always add that role to the list of roles in the ALTER USER command.
For example, if we issue:
sys@TKYTE816> alter user scott grant connect through tkyte with role
2 connect, plustrace;
User altered.
Then the following works as expected:
c:\oracle\rdbms\demo>cdemo2 tkyte/tkyte scott connect
OCISQL> select * from session_roles;
ROLE
-----------------------------CONNECT
PLUSTRACE
Summary
In this chapter, we learned about the proxy authentication or n-Tier authentication capabilities available
to us when programming in OCI. This feature allows a middle tier application server to act as a trusted
agent into the database, on behalf of a client known to the application. We have seen how Oracle allows
us to restrict the set of roles available to the application server proxy account, so that the proxy account
can only perform application specific operations. Further, we have seen how auditing has been
enhanced to support this new feature. We can audit actions specifically performed by proxy accounts on
behalf of any given user or all users. We can clearly see when a given user, via the application proxy
account, performed an action, and when it was done by the user directly.
We modified one of Oracle's simple demonstration programs to provide us with a simple SQL*PLUSlike environment to test this feature. The environment it provides is optimal for testing out various
pieces of this functionality, and will help you see how it works interactively.
978
5254ch22.pdf 16
2/28/2005 6:52:58 PM
n-Tier Authentication
979
5254ch22.pdf 17
2/28/2005 6:52:58 PM
Download