SQL Roles: Users and Security in InterBase

by Markus Kemper and Brett Bandy, Borland Developers Conference 1998

Databases need security. Data stored in database files must be secure. InterBase provides two levels of security for data; user validation and database privileges. This paper will discuss how to setup security for both levels to maintain security for your database. Topics covered:

  • What an InterBase user is and how to create them
  • Basic SQL privileges and how to apply them to a database
  • How SQL Roles can make a Database Administrator's life easier
  • How to use SQL Roles in a database

What is an InterBase user?

InterBase security is based on the concept of a user. A user is the identity that all database security is validated against. Authorized InterBase users are stored in a security database, which is called ISC4.GDB. Each InterBase server has its own security database, which means that a user definition is bound to the server where it is stored. The same user may exist on several servers, but it must be created on all servers where it is required. The security database also stores an encrypted password for each user. Users in the security database are authorized for all databases that reside on that server.

The username can have a maximum of 31 characters. The username is NOT case-sensitive. The password can have up to 8 characters. The password IS case-sensitive.

Common Mistake

This is a common mistake for a lot of new InterBase users. They create a user and password, but when they try to use it they get an error message saying that they don't have a valid username and password. Make sure that you have specified the password EXACTLY how it was defined.


User table fields
Column Required? Data Type Description
User Name Yes String The name the user supplies when logging in.
Password Yes String The user's password
UID No Integer Optional UserID. Currently not used by InterBase
GID No Integer Optional GroupID. Currently not used by InterBase
Full Name No String User's real name

Extending InterBase users with Operating System users

All versions of InterBase use the security database to authenticate users. Some versions of InterBase allow access based on operating system permissions. All InterBase kits running on Unix will allow the use of either operating system users or InterBase users when logging into an InterBase server or database. InterBase kits running on Win95 or NT do not make use of operating system security database permissions. A Win95 or NT user is NOT a valid InterBase user. InterBase relies solely on the security database for user authentication on Win95 and NT.

InterBase will treat a Unix user the same as a user stored in the InterBase security database, as long as the server sees the client as a trusted host. The user account must exist on both the client and the server. To establish a trusted host relationship between the client and the server, an entry must be in the server's /etc/hosts.equiv or /etc/gds_hosts.equiv file. The hosts.equiv file will setup an OS trusted host relationship, which extends to other services (for example rlogin, rsh, rcp). The gds_hosts.equiv will setup an InterBase only trusted host relationship. The format of an entry is identical for the two files. The format of an entry in either file is:

hostname [username]

It is not possible to setup a trusted host relationship between a Unix machine and a Windows machine. To use Unix usernames, the client and the server must both be Unix machines.

Unix usernames are used only if no InterBase username is specified at database login time.

SYSDBA user

When InterBase is installed there is only one user authorized, SYSDBA. SYSDBA is a special user that is above security restrictions and has full access to all databases on the server. The default password for SYSDBA is masterkey. It is strongly recommended to change the SYSDBA password, because this is the default for all InterBase kits and can easily be guessed by anyone who has ever used InterBase. Only SYSDBA can add, modify, or delete new users

On Unix systems the root superuser can be used as the SYSDBA user. InterBase treats the root username as SYSDBA. When using root you will have all privileges on all databases residing on the server. Managing users

InterBase provides three interfaces for SYSDBA to manage users.

  • GSEC
  • Server Manager
  • InterBase API function calls

All three methods provide the same functionality. They just differ in how they are used.

Using GSEC InterBase's command-line security utility

GSEC is a command-line utility that provides an interface to InterBase's security database. You must be SYSDBA or the superuser (on Unix) to use GSEC. GSEC can be used interactively or from the command-line. The following table summarizes GSEC's commands.

GSEC commands
Command Description
di[splay] Displays all users stored in ISC4.GDB
di[splay] name Displays the information for user name
a[dd] name -pw passwd [option argument option argument ...] Adds user name with password passwd and optional information
mo[dify] name [options] Modify a user's attributes
de[lete] name Deletes user name from ISC4.GDB
h[elp] Display's GSEC's commands and syntax
q[uit] Quits interactive GSEC

Displaying users - The DISPLAY command will show all authorized users:

GSEC display

User name uid gid full name
--------------------------------------------------------------------------
SYSDBA    0   0
TEST      0   0
BBANDY    0   0
MKEMPER   0   0

Adding a user - Use the ADD command to add new users to the security database.

The following table lists all options that can be specified when adding or modifying a user.

GSEC command line switches
Option Description
-pw User's password
-u[id] User ID
-g[id] Group ID
-f[name] User's first name
-mn[ame] User's middle name
-l[name] User's last name

This example will add user BJONES with specified password. It also stores information for first and last names:

GSEC add BJONES -pw blah -fname Bobby -lname Jones
GSEC display BJONES

user name uid gid full name
-------------------------------------------------------------------------
BJONES    0   0   Bobby Jones

Modifying a user - The MODIFY command is used to change existing user information.

This example updates the UserID and lastname for user BJONES:

GSEC modify BJONES -uid 22 -fname Brad
GSEC display BJONES

user name uid gid full name
-------------------------------------------------------------------------
BJONES    22  0   Brad Jones

Deleting a user - This example will use the DELETE command to delete the user BJONES. To verify that the user has been deleted the DISPLAY command will be used to show all users.

GSEC delete BJONES
GSEC display

user name uid gid full name
-------------------------------------------------------------------------
SYSDBA    0   0
TEST      0   0
BBANDY    0   0
MKEMPER   0   0

Using Server Manager to manage users

InterBase's Server Manager provides a Graphical Interface for many common database administration tasks. The Server Manager is only available on Windows platforms. You can manage an InterBase server on any platform InterBase supports, but the Server Manager must be run on a windows platform. The InterBase Client for Windows must be installed on the machine where Server Manager is being used.

Among the many tasks it performs is user management. Server Manager will perform the same user management tasks that GSEC does, but in a Graphical Environment. Since each InterBase server has its own security database, you must login to the server as SYSDBA before you can manage the server's users.

InterBase Security Dialog

There are two main windows dealing with user security. The first form, InterBase Security, presents a list of current users in the security database, analogous to GSEC's DISPLAY command. This form has buttons to add, modify, or delete users.

InterBase Security Dialog

User Configuration Dialog

The second Server Manager window is the User Configuration Dialog. This dialog will display when you perform an "Add User" or "Modify User". The dialog groups all the required fields separate from the optional information. When adding a new user the User Configuration Dialog will display with all its fields empty. For a new user you must enter a username and password. Additional user information can be entered as well.

User Configuration Dialog

Using the InterBase API to Manage Users

InterBase provides a native Application Programming Interface (API) to access databases and perform administration functions. Applications can use these API functions to provide connectivity to InterBase databases. Among these API functions are three that allow the application programmer to manage users. The following table summarizes the three API functions available for user management.

API functions for User Management
API Function Description
isc_add_user Adds a new user to security database
isc_modify_user Modifies a users record in security database
isc_delete_user Deletes a user from security database

For each of the three functions you will setup a USER_SEC_DATA structure. This structure is defined in ibase.h as:

typedef struct {
short sec_flags; /* which fields are specified */
int uid; /* the user's id */
int gid; /* the user's group id */
int protocol; /* protocol to use for connection */
char ISC_FAR *server; /* server to administer */
char ISC_FAR *user_name; /* the user's name */
char ISC_FAR *password; /* the user's password */
char ISC_FAR *group_name; /* the group name */
char ISC_FAR *first_name; /* the user's first name */
char ISC_FAR *middle_name; /* the user's middle name */
char ISC_FAR *last_name; /* the user's last name */
char ISC_FAR *dba_user_name; /* the dba user name */
char ISC_FAR *dba_password; /* the dba password */
} USER_SEC_DATA;

This structure has members for each of the user attributes that can be stored in the security database. There are additional members in the structure that are required for the server to know which server's security database is being modified and which fields in the database are being modified. Examples using the USER_SEC_DATA structure with the three API functions are provided below.

For the user management functions to succeed the SYSDBA password must be supplied. This can be done in two different ways. When setting up the USER_SEC_DATA structure, there are two fields that can be used to specify the SYSDBA username and password. The two fields used to specify the SYSDBA user and password are:

  • dba_user_name
  • dba_password

The dba_user_name should be set to SYSDBA, while the dba_password should be set to the SYSDBA password (default for InterBase is masterkey).

Common Mistake

You should not hardcode the SYSDBA password into the application. Any user can run a grep utility or hex editor on the binary and retrieve the static string. Instead you should have a provision to pass in the SYSDBA password so that it is not statically stored with the binary.

Following are several examples using these user management API functions. The examples are provided in both C and Delphi.

Adding a new user in C - This example will add user BJONES with password bjones into the security database. This example will also store the user's first and lastnames.

File: adduser.c

#include "ibase.h"
#include <stdio.h>

main()
{ ISC_STATUS isc_status[20];
  USER_SEC_DATA user_data;
  user_data.user_name = "BJONES";
  user_data.password = "bjones";
  user_data.protocol = sec_protocol_local;
  user_data.dba_user_name = "SYSDBA";
  user_data.dba_password = "masterkey"; /* Don't hardcode this */
  user_data.first_name = "Bobby";
  user_data.last_name = "Jones";
  user_data.sec_flags = sec_password_spec | sec_dba_user_name_spec |
                        sec_dba_password_spec | sec_first_name_spec |
                        sec_last_name_spec;
  isc_add_user(isc_status, &user_data);
  if(isc_status[0]==1 && isc_status[1])
  {
    isc_print_status(isc_status);
    return;
  }
  printf("Successfully added usern");
  return;
}

Adding a new user in Delphi - This example does the same thing as the previous add user example written in C.

File: Adduser.dpr

program Adduser;

uses
   ibase in 'ibase.pas',
   ib_externals in 'ib_externals.pas';

var
  userData: TUserSecData;
  userDataPtr: PUserSecData;
  status: array[0..19] of ISC_STATUS;
  isc_status: PISC_STATUS;

begin
   { setup isc_status pointer }
   isc_status :=@status;
   { setup user data pointer to point to user data structure }
   userDataPtr :=@userData;

   { setup user data structure }
   userData.user_name :='BJONES';
   userData.password :='bjones';
   userData.protocol :=sec_protocol_local;
   userData.dba_user_name :='SYSDBA';
   userData.dba_password :='masterkey'; { Don't hardcode this }
   userData.first_name :='Bobby';
   userData.last_name :='Jones';
   userData.sec_flags :=sec_password_spec or sec_dba_user_name_spec or
   sec_dba_password_spec or sec_first_name_spec or sec_last_name_spec;
   { add user to security database }
   isc_add_user(isc_status, userDataPtr);
end.

Modifying a user in C - This example will modify the user BJONES's password to bjones.

File: modifyuser.c

#include "ibase.h"
#include <stdio.h>

main()
{ ISC_STATUS isc_status[20];
  USER_SEC_DATA user_data;
  user_data.user_name="BJONES";
  user_data.password="bjones";
  user_data.protocol=sec_protocol_local;
  user_data.dba_user_name="SYSDBA";
  user_data.dba_password="masterkey"; /* Don't hardcode this */
  user_data.sec_flags=sec_password_spec | sec_dba_user_name_spec |
                      sec_dba_password_spec;
  isc_modify_user(isc_status, &user_data);
  if(isc_status[0]==1 && isc_status[1])
  {
    isc_print_status(isc_status);
    return;
  }
  printf("Successfully modified usern");
  return;
}

Modifying a user in Delphi - This example will modify the user BJONES's password to newpass and also change the UID to 22.

File: ModifyUser.dpr

Program ModifyUser;

uses
  ib_externals in 'ib_externals.pas',
  ibase in 'ibase.pas';

var
  userData: TUserSecData;
  userDataPtr: PUserSecData;
  status: array[0..19] of ISC_STATUS;
  isc_status: PISC_STATUS;

begin
  { setup isc_status pointer }
  isc_status :=@status;
  { setup user data pointer to point to user data structure }
  userDataPtr :=@userData;
  { setup user data structure }
  userData.user_name :='BJONES';
  userData.password :='newpass';
  userData.uid :=22;
  userData.protocol :=sec_protocol_local;
  userData.dba_user_name :='SYSDBA';
  userData.dba_password :='masterkey'; { Don't hardcode this }
  userData.sec_flags :=sec_uid_spec or sec_password_spec or
  sec_dba_user_name_spec or sec_dba_password_spec;
  { add user to security database }
  isc_modify_user(isc_status, userDataPtr);
end.

Deleting a user in C - This example will delete the user BJONES from the security database. As shown in the examples, the only fields required are the user to be deleted and the SYSDBA user and password.

File: deleteuser.c

#include "ibase.h"
#include <stdio.h>

main()
{ ISC_STATUS isc_status[20];
  USER_SEC_DATA user_data;
  user_data.user_name="BJONES";
  user_data.protocol=sec_protocol_local;
  user_data.dba_user_name="SYSDBA";
  user_data.dba_password="masterkey"; /* Don't hardcode this */
  user_data.sec_flags=sec_dba_user_name_spec | sec_dba_password_spec;
  isc_delete_user(isc_status, &user_data);
  if(isc_status[0]==1 && isc_status[1])
  {
    isc_print_status(isc_status);
    return;
  }
  printf("Successfully deleted usern");
  return;
}

Deleting a user in Delphi - This example will delete the user BJONES from the security database.

File: DeleteUser.dpr

program DeleteUser;

uses
  ibase in 'ibase.pas',
  ib_externals in 'ib_externals.pas';

var
  userData: TUserSecData;
  userDataPtr: PUserSecData;
  status: array[0..19] of ISC_STATUS;
  isc_status: PISC_STATUS;
  errorBuffer: array[0..256] of char;
  buffer: PChar;

begin
  { setup isc_status pointer }
  isc_status :=@status;
  { setup user data pointer to point to user data structure }
  userDataPtr :=@userData;
  { setup pointer for error buffer }
  buffer :=errorBuffer;
  { setup user data structure }
  userData.user_name :='BJONES';
  userData.protocol :=sec_protocol_local;
  userData.dba_user_name :='SYSDBA';
  userData.dba_password :='masterkey'; { Don't hardcode this }
  userData.sec_flags :=sec_dba_user_name_spec and sec_dba_password_spec;
  { add user to security database }
  isc_delete_user(isc_status, userDataPtr);
end.

SQL Privileges: The second level of security

InterBase implements two levels of security. The first is user validation. This is done at database connection time. Users are validated against InterBase's security database (See previous section for discussion of authorized users). The second level of security is implemented at the database level. All privileges for this level are stored in the database itself. An authorized user does not have privileges to data stored in the database unless that user is explicitly assigned privileges. Authorized users are allowed to connect to databases, but unless they have privileges they cannot access any of the objects or data stored in the database. SQL privileges are controlled on a table level. Each user has a list of operations that he or she is allowed to perform on a given table or view. This list of operations makes up that user's access privileges.

Additionally, InterBase puts restrictions on the use of stored procedures as well. A user cannot execute a stored procedure unless that user has been assigned the privilege to do so. When a database object is created only the SYSDBA user and the owner of the object have privileges to access that object. The user that creates the database object is the owner of that object. SYSDBA or the owner of the object can explicitly grant privileges to other users. Additionally, the privilege of assigning privileges to other users can also be assigned to a user other than SYSDBA or the object's owner.

How to Assign Privileges to Users

As previously stated, only SYSDBA and the owner of the object initially have privileges to access the object. If SQL privileges stopped here, they would be useless. There needs to be a way to allow additional users to access the database objects. With SQL privileges this is done with the GRANT and REVOKE statements. The GRANT statement is used to assign privileges on tables or views to authorized users. Conversely, the REVOKE statement is used to take away privileges that were previously assigned to a user with the GRANT statement.

Common Mistake

A common mistake developers make is to create database objects and not assign privileges to any users. Since the developer is the owner of the object there are no privilege problems. Being the owner allows the developer to perform any and all operations on the database object. When it comes time to deploy the application, no other users are allowed access, because they have not been granted privileges on the database object. For all security operations, SYSDBA always has the right to grant or revoke privileges from users. The SYSDBA user always assumes total control over a database. SYSDBA is the superuser, there is no way to deny access from the SYSDBA user. The GRANT and REVOKE statements have no effect on the SYSDBA user.

GRANT Statement

The GRANT statement assigns a new privilege or privileges on a database object to a user. The types of privileges that can be assigned to a user are listed in the following table.

Privileges
Privilege Definition of Privileges
Insert Allows insertion of new rows into table
Update Allows existing rows to be updated
Delete Allows existing rows to be deleted
Select Allows user to view/query the rows in a table
Execute Allows a user to execute a procedure
Reference Allows server to lookup rows in a primary/foreign key relationship
All Shortcut to assign insert, update, delete, select, references to a user

Granting privileges to PUBLIC

SQL defines the special user PUBLIC to represent all uses. If an operation is granted to PUBLIC, then any valid user can perform the operation on the specified database object. Care should be taken when granting privileges to PUBLIC. All current and future users will be able to perform the operations that have been granted to PUBLIC.

Granting privileges to execute a Stored Procedure

To use a stored procedure a user must have the privilege to execute that stored procedure. There is a form of the GRANT statement that assigns the privilege to execute a stored procedure. The form of the statement is:

GRANT EXECUTE ON PROCEDURE procname to user;

Additionally, the stored procedure must have privileges on all relevant tables for all operations that it performs. The stored procedure must have been given the privilege to access all the tables that it uses. An example assigning the select privilege to a stored procedure would take this form:

GRANT SELECT on tablename to PROCEDURE procname;

Granting access to Views

For the most part, assigning privileges on views is the same as for tables. There are a few differences that are very important and need pointing out.

First, a view is a virtual table. A view is a query against base tables. The data that is queried from the table is NOT stored, only the query definition is stored. The data is retrieved from the base tables every time the view is queried. So, for any INSERT, UPDATE, or DELETE statement the operation is performed on the base tables. It is meaningful to grant INSERT, UPDATE, or DELETE privileges to a view only if that view is updatable. An updatable view is one that generally does not involve joins or aggregate functions. For a more thorough explanation of views see the InterBase Data Definition Guide's chapter Working with Views.

While it is possible to grant INSERT, UPDATE, or DELETE privileges to a read-only view without receiving an error, any actual write operation attempted through the view will fail because it is read-only. SELECT privileges, however, can be granted to a read-only view to control which users have read access on the view.

Granting privileges to execute a Stored Procedure

Views can also be used to control access to base table data. If a user is only required to have access to a subset of rows for a table a view can be defined to query that subset of rows from the base table. The user can then be given privileges only on the view, not the base table. This allows the user privileges to the subset of rows that are required, but no privileges to the other rows in the table.

For example, imagine a table called TEST_SCORES that contains the columns, LASTNAME, FIRSTNAME, TESTNAME, SCORE. This table contains information that can be viewed by anyone, LASTNAME, FIRSTNAME and TESTNAME. This information is made available so that everyone can query to see who has taken which test. There is also information that should not be made available to all users, SCORE. There is a need to allow everyone access to a subset of the columns in the TEST_SCORES table, while only allowing access to the SCORE column to an exclusive set of users. This can be accomplished by creating a view that queries the rows available to all users and only granting everyone privileges to the view and not the base table:

CREATE VIEW TESTTAKERS AS
  SELECT LASTNAME, FIRSTNAME, TESTNAME FROM TEST_SCORES;

GRANT SELECT ON TESTTAKERS TO PUBLIC;
GRANT ALL ON TEST_SCORES TO INSTRUCTOR;

Examples using Grant Statement

This example grants ALL privileges for the table TEST_SCORES to the user JJOHNSON. The ALL privilege grants JJOHNSON the right to insert, update, select, delete from this table:

GRANT ALL ON TEST_SCORES TO JJOHNSON;

This example will grant the ALL privilege to the special user PUBLIC. The PUBLIC user will grant all existing and future users ALL privileges on table TEST_SCORES:

GRANT ALL ON TEST_SCORES TO PUBLIC;

The following example will grant ALL privileges to a stored procedure. This will give the procedure rights to access the TEST_SCORES base table. For users to be able to use the stored procedure they must be granted EXECUTE privileges to the stored procedure (see later example).

GRANT ALL ON TEST_SCORES TO PROCEDURE GET_PASSING_SCORES;

This example will grant EXECUTE privileges to the special user PUBLIC. This will allow all current and future users to execute this procedure. The procedure must be given the privileges to access any base tables that it requires.

GRANT EXECUTE ON PROCEDURE GET_PASSING_SCORES TO PUBLIC;

This example will grant INSERT privileges on table TEST_SCORES to the user JJOHNSON. JJOHNSON will be able to add new rows to the table, but will not be able to query the table.

GRANT INSERT ON TEST_SCORES TO JJOHNSON;

This example will grant UPDATE privileges on a subset of columns to the user JJOHNSON. JJOHNSON will be able to update only those rows explicitly listed in the grant statement.

GRANT UPDATE (FIRST_NAME, LAST_NAME) ON TEST_SCORES TO JJOHNSON;

Granting Right to Grant to Others

Initially, only the SYSDBA and owner can grant privileges to other users. The grant statement has a clause that allows the SYSDBA or owner to grant the right for a user to grant privileges to other users. The WITH GRANT OPTION is used to allow other users the right to grant privileges to other users. Because the grant statement works on individual tables, the WITH GRANT OPTION can only be used on one table at a time. A grant statement with the WITH GRANT OPTION clause will have to be used for every table that the SYSDBA or owner wish to allow others to grant privileges for.

Users who have be given the right to grant privileges to other users can only grant the privileges that they have been granted themselves. For instance, a user who has been granted select privilege can not grant the update privilege to other uses. That user can only grant the select privilege to other users.

The following example grant's select privilege on table employee to the user BJONES and allows BJONES to grant the select privilege for table employee to other users:

Grant SELECT on employee to BJONES WITH GRANT OPTION;

Once a user has been given the right to grant privileges, that user can give the right to grant privileges to other users. This can cause a security breach unless controlled. Every user that has been given the right to grant privileges can also pass that right on to other users. There is no way to give a user the right to grant privileges to others, but not be able to pass on that right. Because of this fact, caution should be used when using the WITH GRANT OPTION clause.

REVOKE Statement

While the GRANT statement allows privileges to be given to a user, the REVOKE statement allows privileges to be removed. Only privileges that have been previously granted can be revoked. The basic syntax of the revoke statement is of the following form:

Revoke <operation list> on tablename from user;

Only the user who originally grants a privilege can revoke that privilege. For example, if userA grants a privilege to userB, then userC does not have the right to revoke that privilege from userB. Only userA can revoke that privilege. The SYSDBA user, as previously stated, always has the right to grant and/or revoke any privilege from any user.

The ALL privilege combines the SELECT, INSERT, UPDATE, and DELETE privileges. The ALL privilege can be used as a shorthanded way of revoking multiple privileges from a user. To use the ALL privilege in a revoke statement the targeted user does not have to have all the privileges that the ALL privilege encompasses. When a user revokes the ALL privilege it will revoke any subset of the ALL privilege. For example, assume a user has insert and select privileges on a table. If SYSDBA executes a REVOKE ALL statement, the user will no longer have any privileges to that table.

As stated before, the WITH GRANT OPTION clause allows the right to grant privileges to be passed to other users. Also, users who have been given the right to grant privileges can pass on that right to other users. This can lead to a hole in database security. The right to grant may start out with only a few users having the privilege, but quickly grow to an enormous number of users who have the right to grant privileges. The revoke command can be used to stop this chain of users. Revoking privileges from a user who was granted privileges with the WITH GRANT OPTION clause also revokes privileges from all additional users that the user has granted privileges to others. Here is a sequence of events that demonstrate this scenario:

  1. Grant privileges to USERA WITH GRANT OPTION
  2. USERA grants privileges to USERB
  3. Revoke privileges from USERA
  4. Neither USERA or USERB will have privileges

Here is the sequence of GRANT and REVOKE statements that demonstrate this scenario:

Connect to database as SYSDBA:
GRANT ALL ON TEST_SOURCE TO USERA WITH GRANT OPTION;
Connect to database As USERA:
GRANT ALL ON TEST_SOURCE TO USERB;
Connect to database as SYSDBA:
REVOKE ALL ON TEST_SOURCE FROM USERA;
Connect to database as USERA:
SELECT * FROM TEST_SOURCE;
Statement failed, SQLCODE=-551
no permission for read/select access to table TEST_SOURCE
Connect to database as USERB:
SELECT * FROM TEST_SOURCE;
Statement failed, SQLCODE=-551
no permission for read/select access to table TEST_SOURCE

Revoking privileges from PUBLIC

If privileges are assigned to PUBLIC, those privileges cannot be revoked from a specific user, they must revoked from the PUBLIC user. The PUBLIC special user is used to allow any user to access a table. It does not, however, act as a group of users. Once the PUBLIC user has been assigned privileges, those privileges can only be revoked from the PUBLIC user as a whole. For example, assume that PUBLIC has been granted the ALL privilege on table TEST_SOURCE. SYSDBA cannot then revoke the ALL privilege specifically from user BJONES. If it is desired to have only a subset of users with privileges then the PUBLIC user cannot be granted rights to the table.

Revoke Examples

This example will revoke the ALL privilege from the special user PUBLIC. This revoke statement will only affect the privileges that were specifically granted to the PUBLIC user. All privileges that were granted to individual users are still assigned.

REVOKE ALL ON TEST_SOURCE FROM PUBLIC;

This example will revoke the INSERT privilege from an individual user, BJONES. Any additional privileges that BJONES has on this table will still be assigned.

REVOKE INSERT ON TEST_SOURCE FROM BJONES;

This example will revoke the EXECUTE privilege from an individual user, BJONES. BJONES will no longer have the right to execute this procedure.

REVOKE EXECUTE ON PROCEDURE GET_PASSING_SCORES FROM BJONES;

Revoking Grant Authority

The REVOKE statement will also allow a user's grant privilege to be revoked. The following example will revoke the user BJONES's authority to grant privileges to other users. BJONES will still have his assigned privileges to that table, but will no longer be able to grant privileges to other users.

REVOKE GRANT OPTION FOR ALL ON TEST_SOURCE FROM BJONES;

You can also revoke grant authority for selective operations. For example, if BJONES had grant authority for the ALL privilege, the REVOKE command can be used to revoke BJONES's grant authority for the INSERT operation:

REVOKE GRANT OPTION FOR INSERT ON TEST_SOURCE FROM BJONES;

BJONES will still have insert privileges into table TEST_SOURCE, the revoke statement just revokes BJONES's right to grant the INSERT privilege to other users.

SQL Security is inefficient

The SQL security mechanism will get the job done. It is, however, inefficient for database administrators to setup and manage. The SQL security mechanism works on individual users. Outside of the PUBLIC user, there is no way to assign privileges at a group level. Additionally, SQL security works on a table by table basis. Privileges must be assigned for each table in the database.

For example, assume a database administrator is trying to setup SQL security for 100 users on a database with 100 tables in it. With SQL security the database administrator will have to execute 10000 grant statements to setup the security for the users on this database. For each user there is one grant statement per table. This comes out to 100 grant statements per user. For 100 users that totals the 10000 grant statements required to setup SQL security. For each table you can grant privileges to a list of users, but this approach leads to many errors. The database administrator has to partition the users among the grant statements and ensure that each user is granted privileges for each table. In most cases, database administrators usually stick to one user per grant statement.

SQL Security makes it hard to manage users

The SQL security mechanism does not make it easy for the database administrator (DBA) to maintain an existing database either. There is no provision to help the DBA add new users to an existing database. Likewise, there are no provisions to help the DBA modify existing user privileges for a set of users.

For each new user that must be added to an existing system the DBA must reuse the same grant statements that were executed for all other users. This must be done for each table in the database. This administration operation is very repetitive. SQL security has no provision for simplifying the operation. Every time a new user is added the DBA has to go through the same routine to assign privileges to the new user.

The lack of group security means that for any little change to security, privileges for each individual user must be changed to reflect the new privileges. This requires revoking the privileges that are no longer available to users and granting the new privileges to the same users.

SQL Security is not flexible

SQL security does not allow for flexibility of user privileges. It is an all or nothing mechanism. Either you have privileges or you don't. SQL security doesn't have the flexibility to allow changing user privileges without the overhead of managing each individual user change, via a grant or revoke statement. Also, there is no way to assign a set of privileges to a user without assigning them one table at a time.

What are SQL Roles?

InterBase 5.0 introduced an extension to SQL security called SQL Roles. SQL Roles implement the concept of group level security. Roles also act as templates for predefined sets of privileges. SQL Roles are an extension to the standard SQL security mechanism implemented in InterBase. A Role defines a set of SQL privileges on one or more tables in a database. Roles work with normal SQL security, but add the benefit of group level security.

For example, a programmer must assume many roles during the life cycle of a product. Initially, the programmer must assume the role of a marketing person to define what the product is and what its capabilities are. Next, the programmer must assume the role of the developer while engineering the product. Finally, the programmer must assume the role of the end user and QA the product to assure its quality. SQL Roles do just this for database security.

A role acts as a group template for users. In the above example, the programmer can be one user or many users. Roles also allow users to assume different roles (different sets of privileges). The programmer in the example can take on different privileges as he is assuming a different role. The marketing role has a set of privileges defined for it, while the QA role has an entirely different set of privileges defined for it.

Roles are an identity

To reiterate, a role is like a template, or set of privileges. The role is first created, then it is granted privileges to database objects. Users are then granted the right to assume a role upon connection. When a connection is attempted the user can specify a role that the user is to assume when connected. When a user assumes a role, that user is given all the privileges that are assigned to that role.

Roles are additive

As stated earlier, roles are an extension of basic SQL security. Privileges assigned when assuming a role are added to the privileges that are granted explicitly to a user. For example, assume the role TEST_ADMIN has been granted the INSERT privilege on table TEST_SCORES. USERA has explicitly been granted the SELECT privilege, and has been granted the right to assume the role TEST_ADMIN. When USERA connects specifying role TEST_ADMIN, USERA will have the accumulated privileges: INSERT and SELECT on table TEST_SCORES.

Which InterBase kits support Roles?

SQL Roles are available with InterBase v5.0. If upgrading from a previous version of InterBase, you must backup your database and restore onto the server which is running InterBase v5.0. It is not enough to move your database to the v5.0 server, you must also backup and restore to enable the database to support SQL Roles.

Here are some general role requirements and conditions:

  • A role is bound to the database that it is created in.
  • Role names must be unique with respect to other roles and usernames.
  • Roles must be assumed at connection time. A user cannot switch roles without disconnecting and reconnecting to the database.
  • A database must be backed up and restored with a V5.0 server to use roles. The InterBase V5.0 server can access V4.x databases, but unless the database is restored with a V5.0 server roles cannot be used in that database.

Advantages of Roles

Roles have several advantages over the basic SQL security model. These advantages make it easier for a DBA to administer a database's security. As stated earlier, Roles provide much needed group level security for InterBase. This makes it easy for the DBA to add security for multiple users. Roles also provide a degree of flexibility that is not easy attainable with the basic SQL security model.

Lets revisit the example of the DBA setting up security for 100 users on a database with 100 tables. With basic SQL security we concluded that the DBA would have to execute 10000 grant statements to fully setup security for these 100 users on all 100 tables in the database. Using roles, the best case scenario would only require 200 grant statements to be executed. The best case would be where every user requires the same privileges on all tables. In this scenario the DBA could setup one role, which would require 100 grant statements. To add the 100 users to the role would require an additional 100 grant statements, which equals the 200 grant statements stated above. The more general case would require 100x + 100 grant statements, where x equals the number of different roles that are required. For example, if 5 roles were required then a total of 600 grant statements would have to be executed by the DBA to setup security. Clearly, using Roles has made setting up security for this database much easier for the DBA.

Roles make it easier to manage users

Roles also make database security easier to maintain for an existing database. Since roles extend basic SQL security there is no need to redesign the database's security to use roles. Roles will allow the DBA to keep the already defined privileges intact and build new privileges along side these. There are a couple key areas where Roles really make the DBA's job easier when it comes to maintaining security for an existing database. One, roles allow a new user's privileges to be easily added to the database. Second, Roles implement group level security, which makes the job of modifying existing privileges for a large number of users manageable.

With basic SQL security adding a new user's privileges to a database requires the DBA to grant privileges to the user for each table in the database. This is a very repetitive task, because user privileges fall into categories and the same set of privileges are defined for several users. Roles can act as templates when setting up privileges for new users. A role can be defined in the database for a common set of privileges. When a new user is added the DBA must only grant the new user privileges to assume the role that was defined for the user's required set of privileges.

One common method DBA's use to work around the basic SQL security model's lack of groups is to grant privileges to one generic user and allow all users to connect with this same username. This makes setting up and maintaining privileges easy to manage. The major drawback to this method is that when connected, every user has the same username. There is no way to differentiate between the users, because they are all using the same username. To InterBase, they are all effectively the same user, because the username is the only way to differentiate users. Using roles as templates alleviates this issue. The DBA still only has to setup privileges once, this time for the role instead of the generic user. When users connect they still provide their unique username, but also add the role that they are assuming. This allows InterBase to maintain security privileges, but still differentiate between the individual users. There are only two differences between using roles and using the generic user. One, users must specify the role when connecting. Two, the DBA must grant to all users the right to assume the role.

The second major advantage with using roles is the management of user groups. Roles are defined around the concept of group level security. Roles allow DBAs to manage the privileges for entire groups of users, not one user at a time like the basic SQL security model. Roles allow the DBA to modify privileges at a group level instead of an individual user level. When the DBA modifies the privileges of a role, the effective privileges of all users is modified as well. The privileges are modified for the entire group, everyone who is granted privileges to the role.

For example, suppose a role FULL_ACCESS has been defined with the ALL privilege on table TEST_SCORES.

create table test_scores (i1 integer);
create role FULL_ACCESS;
grant all on test_scores to full_access;
show grant test_scores;
GRANT DELETE, INSERT, SELECT, UPDATE, REFERENCES
  ON TEST_SCORES TO FULL_ACCESS

Users TEST and TEST2 have been granted the privilege to assume that role

grant FULL_ACCESS to TEST;
grant FULL_ACCESS to TEST2;

Users TEST and TEST2 now have the ALL privilege for table TEST_SCORES when they connect assuming the FULL_ACCESS role. If TEST or TEST2 tries to connect to the database without specifying the FULL_ACCESS role they will not have privileges on table TEST_SCORES.

connect tempemployee.gdb user test password test;
Database: tempemployee.gdb, User: test
select * from test_scores;
Statement failed, SQLCODE=-551
no permission for read/select access to table TEST_SCORES

connect tempemployee.gdb user test password test role full_access;
Database: tempemployee.gdb, User: test, Role: full_access
SQL select * from test_scores;

Now, for the DBA to modify the privileges for both TEST and TEST2 all that is required is to modify the role FULL_ACCESS. In this example the INSERT privilege is taken away from the role. TEST and TEST2 can assume the role and have the UPDATE, DELETE, SELECT, and, REFERENCES privileges on table TEST_SCORES.

show grant test_scores;
GRANT DELETE, INSERT, SELECT, UPDATE, REFERENCES
  ON TEST_SCORES TO FULL_ACCESS

revoke insert on test_scores from full_access;

show grant test_scores;
GRANT DELETE, SELECT, UPDATE, REFERENCES ON TEST_SCORES TO FULL_ACCESS

connect tempemployee.gdb user test password test role full_access;
Database: tempemployee.gdb, User: test, Role: full_access
select * from test_scores;
insert into test_scores values (96);
Statement failed, SQLCODE=-551
no permission for insert/write access to table TEST_SCORES

The select, while not returning any rows, was successful. The insert, however, received the no permission error message. While this example demonstrates a group with only 2 users, the same example can easily be extended to incorporate hundreds of users.

Roles are flexible

Roles also add much needed flexibility to the basic SQL security model. To reiterate, the SQL model doesn't have the flexibility to allow changing user privileges without the overhead of managing each individual user change, via a grant or revoke statement. Roles allow a user to change the privileges assigned by assuming a different role when connecting to the database. This allows for a secure database, while still having the flexibility to accommodate a users changing needs. Revisiting the product life cycle example presented earlier will demonstrate how roles add the flexibility required for this scenario. For each role the programmer assumes, there are a predefined set of privileges that are assigned. When the programmer is assuming the marketing role, he should not have any privileges that are granted to a developer. Likewise, when the programmer is working as the developer role, he should not have any of the privileges granted to the QA role. The point is each role has its own predefined set of privileges, and the programmer when assuming that role should only have those privileges granted to that role.

Moving this example into the database realm, there should be three roles defined on the database; Marketing, development, and QA. Each role will have its own set of privileges to the tables in the database. When a user connects to the database assuming one of the roles, that user will only have the privileges granted to that role.

Roles also provide the flexibility, because they work in conjunction with the basic SQL security model. As explained earlier, the total privileges for a user are cumulative:

Total Privileges=Privileges explicitly granted to user +
Privileges granted to role being assumed

Continuing the product life cycle example, assume user BJONES has been granted privileges to assume the QA role. Each month BJONES is responsible for accumulating a count of all the developer source code changes that have occurred. Since BJONES has only been granted privileges to assume the QA role, he cannot access the tables that are used during development. The DBA does not want to give BJONES the privileges to assume the developer role, because the privileges BJONES requires are a subset of the total privileges that have been granted to the developer role. The DBA can explicitly grant privileges on the subset of tables directly to BJONES. When BJONES connects assuming the QA role he will have privileges to access the subset of development tables as well as the tables for QA, because of the conjunctive nature of roles with the basic SQL security model. BJONES has privileges granted to him through the role, plus all privileges explicitly granted to him by the DBA.

Using Roles

Creating a Role

To create a role you must use the CREATE ROLE statement. By default a role has no privileges when it is created. The role must be granted privileges to database objects before it can access them. Any user can create a role, since creating a role assigns no privileges. Also, since roles are defined and stored in a database, you must be connected to a database before you can define a role. Here is an example of creating a role:

CREATE ROLE FULL_ACCESS;

Granting Privileges to a Role

When a role is created it has no privileges to any objects in the database. This follows the SQL security model of providing no access unless explicitly granted. To assign privileges to a role you must use the grant statement. The basic syntax for granting privileges to a role is:

GRANT <priviliges> ON [TABLE] {tablename | viewname} TO rolename;

To grant privileges to a role you must be:

  • SYSDBA
  • The owner of the table or view
  • A user that has been granted the right to assign privileges for this table or view (i.e. WITH GRANT OPTION)

Here are some examples of granting privileges to a role:

GRANT ALL ON TEST_SCORES TO FULL_ACCESS;
GRANT INSERT, SELECT ON TABLE EMPLOYEE TO BJONES;

Granting a Role to a User

After a role has been created and privileges have been assigned to it, you must grant the role to users. A user can only assume a role if that user has been granted the privilege to use it. When a user connects to a database and specifies a role, that user acquires all the privileges that have been granted to the role.

The syntax for granting a role to a user is:

GRANT {rolename [, rolename ...]} TO {PUBLIC| {[USER] username
[, [USER] username ...]} } [WITH ADMIN OPTION];

The WITH ADMIN OPTION clause permits users to grant the role to other users. If it is added to the end of the grant role statement then that user that has just been granted the role can grant the role to other users. For example, if USERA is granted the role DEVELOPER with the WITH ADMIN OPTION clause, then USERA can grant the DEVELOPER role to other users. The following example creates the role FULL_ACCESS, grants ALL privileges on the TEST_SCORES table to this role, and grants the role to user BJONES.

CREATE ROLE FULL_ACCESS;
GRANT ALL ON TEST_SCORES TO FULL_ACCESS;
GRANT FULL_ACCESS TO BJONES;

BJones is connecting to the database without specifying the role FULL_ACCESS. BJones is denied access to the table TEST_SCORES, because only the role was granted privileges on table TEST_SCORES.

connect tempemployee.gdb user bjones password bjones;
Database: tempemployee.gdb, User: bjones
select * from test_scores;
Statement failed, SQLCODE=-551
no permission for read/select access to table TEST_SCORES

BJones now connects and specifies the FULL_ACCESS role. Since the role was granted ALL privileges on table TEST_SCORES, Bjones is allowed to select from the table.

connect tempemployee.gdb user bjones password bjones role full_access;
Database: tempemployee.gdb, User: bjones, Role: full_access
select * from test_scores;

Assuming a Role

We have already touched on a user assuming a role. When a user connects to a database he can assume a role by specifying the role in the connection string. This is the only place where a user can specify a role to assume. InterBase does not provide a way for a user to switch roles while staying connected to a database. The user must disconnect and reconnect with the new role.

Common Mistake

The most common misconception regarding roles is that when a role has been granted to a user, that user will immediately have all the privileges that the role has been granted. This is incorrect. For a user to acquire the privileges that have been granted to a role, the user must specify the role when connecting to the database. If a user does not specify the role when connecting, that user will have none of the privileges that have been granted to the role.

Here is an example of the user BJONES connecting to a database and assuming the role FULL_ACCESS. In this example the user is using ISQL, InterBase's interactive SQL processing utility.

connect tempemployee.gdb user bjones password bjones role full_access;
Database: tempemployee.gdb, User: bjones, Role: full_access

Here is an example of the same user connecting, but using DSQL instead if the ISQL utility.

File: sampleapi.c

#include <string.h>
#include <stdio.h>
#include "ibase.h"

int main()
{ ISC_STATUS isc_status[20];
  isc_db_handle db_handle;
  char *dpb;
  short dpb_length;
  int i;

  /* clear database handle */
  db_handle=0L;

  /* add dpb version to dpb */
  dpb=(char *)0;
  dpb_length=0;

  /* add user, password, and role to dpb */
  isc_expand_dpb(&dpb, &dpb_length,
    isc_dpb_user_name, "bjones",
    isc_dpb_password, "bjones",
    isc_dpb_sql_role_name, "full_access",
    NULL);

  /* attach to database */
  isc_attach_database(isc_status, 0, "testdb.gdb", &db_handle,
    dpb_length, dpb);
}

Revoking Privileges from a Role

For roles that have been granted privileges, you can revoke those privileges using the REVOKE command. If privileges are revoked from a role, all users who have been granted the role will no longer have the privileges that are being revoked. The syntax for revoking privileges from a role are:

REVOKE <priviliges> ON [TABLE] tablename FROM rolename;

Here is an example that revokes the INSERT privilege on table TEST_SCORES from the role FULL_ACCESS.

REVOKE INSERT ON TEST_SCORES FROM FULL_ACCESS;

Revoking a Role from a User

Just as a role can be granted to a user, a role can also be revoked from a user. When a role is revoked from a user it means that user can no longer connect and specify that role. That user no longer has the privileges that were assigned to the role. The syntax for revoking a role from a user is:

REVOKE <rolename> FROM username;

In this example the user Bjones is being revoked from the role FULL_ACCESS. BJones will no longer be able to connect specifying the role FULL_ACCESS.

REVOKE FULL_ACCESS FROM BJONES;

Dropping a Role

When a role is no longer needed it can be dropped from the database. A role can be dropped by either SYSDBA or the creator of the role. When a role is dropped all the privileges that the role has been granted are removed from the database. The syntax for dropping a role is:

DROP ROLE rolename;

The following example drops the role FULL_ACCESS.

DROP ROLE FULL_ACCESS;

Where and How Roles are Supported

For roles to be of value, client tools must be able to specify a role upon database connection. The first step is for InterBase to surface the ability to use roles while connecting to databases. The second step is for development tools to use one of the methods that InterBase surfaced to implement roles in the client application. It is a two step process for end users to use roles with their database applications.

InterBase provides three interfaces to roles

InterBase allows client applications and development tools to use SQL Roles. InterBase surfaces roles via the native API, embedded SQL, and the interactive query tools. All tools that are built using one of these access methods will be able to support roles.

The InterBase native API is a set of functions that allow developers to programmatically construct and send SQL statements to the InterBase engine and receive results back. All database work can be performed through calls to the API. The InterBase API provides the most flexibility and power of any access method. This flexibility and power comes at the expense of using a lower level interface. The API relies on the developer to manage more of the burden when communicating with the InterBase engine. The developer is required to allocate and populate underlying data structures, which are hidden in the other higher-level interfaces. The following example will use InterBase's native API to attach to a database. The role full_access will be used for the connection so that user bjones will have the privileges that have been granted to the role.

File: sampleapi.c

#include <stdio.h>
#include "ibase.h"

int main()
{ ISC_STATUS isc_status[20];
  isc_db_handle db_handle;
  char *dpb;
  short dpb_length;
  int i;

  /* clear database handle */
  db_handle=0L;

  /* add dpb version to dbp */
  dpb=(char *)0;
  dpb_length=0;

  /* add user, password, and role to dpb */
  isc_expand_dpb(&dpb, &dpb_length,
    isc_dpb_user_name, "bjones",
    isc_dpb_password, "bjones",
    isc_dpb_sql_role_name, "full_access",
    NULL);

  /* attach to database */
  isc_attach_database(isc_status, 0, "testdb.gdb", &db_handle,
    dpb_length, dpb);
}

Embedded SQL allows the developer to embed SQL statements in an application. Embedded SQL provides an easy route for developers to execute SQL statements without having to learn the complexities of the InterBase API. Much of the burden of dealing with memory allocation and data structures is hidden from the developer. The compromise for using embedded SQL is that the developer does not have as much flexibility or control when communicating with the InterBase engine.

The following example will connect to the database testdb.gdb using the role full_access. Once it is connected it will insert into the table test_scores.

File: sampleesql.e

#include <stdio.h>

EXEC SQL
SET DATABASE db="testdb.gdb";
int main()
{ EXEC SQL
  CONNECT db USER "bjones" PASSWORD "bjones" ROLE "full_access";
  EXEC SQL
  INSERT INTO TEST_SCORES VALUES ("History", 83, "Bobby","Jones");
  EXEC SQL
  COMMIT;
  return;
}

InterBase provides interactive utilities for a developer to work with databases. ISQL and WISQL both provide an interface for developers to easily enter SQL statements to execute against databases. ISQL is a command-line utility, while WISQL is the graphical based version of the same utility. Both ISQL and WISQL handle the details of managing transactions and statements. The user must only enter the SQL statement to be executed. Both versions of the tool also allow scripts containing SQL statements to be executed against a database. This is an example ISQL script that connects to the database testdb.gdb using the role full_access.

File: sampleisql.sql

connect "testdb.gdb" user "bjones" password
  "bjones" role "full_access";
select * from test_scores;
commit;

Here is a screen shot showing the WISQL connection dialog. There is a separate box to specify a role upon connection to a database.

Connection dialog with role specification

See the InterBase Operations Guide for more details regarding the InterBase API, embedded SQL, and the InterBase interactive utilities.

InterBase clients must do their part

The second step in the process of using roles is the responsibility of the client tools. The client tools must allow applications to use roles. It is not enough for the InterBase engine to provide an interface to roles. The client tool must also support roles. For an end-user application to work with roles, both parts must comply. Since SQL roles are a new feature in InterBase v5.0, many client tools do not currently support InterBase roles.

Roles make security manageable

In summary, database security is a must. The standard SQL security model is adequate for securing your data. Roles build on the standard model and add efficiency to security management. Roles provide group level security, which is missing in the standard SQL security model. Using roles makes your life easier.