ObjectStore C++ API User Guide

Chapter 7

Database Access Control

ObjectStore offers a choice of methods for controlling who can access a database. The information describing techniques you can use is organized in the following manner:

Access Control Methods

ObjectStore provides two general approaches to database access control. With one approach, you can set read and write permissions for various categories of users, at various granularities. With the other approach, you can require that applications supply a key in order to access a particular database.

ObjectStore also supports Server authentication services. See Chapter 2, Server Parameters, of ObjectStore Management for more information about access control methods.

Setting User Category Permissions

For rawfs databases, you can specify a combination of types of access to a given directory, database, or segment for a given category of users.

To specify access restrictions on a rawfs database or directory, use os_dbutil::chmod() or the utility oschmod. To specify access restrictions on a segment in a rawfs database, use os_segment::set_access_control(). See Chapter 2, Class Library, of the ObjectStore C++ API Reference, as well as Chapter 4, Utilities, of ObjectStore Management.

For file databases, you can specify a combination of types of access to a given directory or database for a given category of users. To do this, use commands of the native operating system. With file databases, a segment always has the same protections as the database that contains it; see the discussions beginning with Categories of Users.

Restricting Database Access Using Schema Keys

If you want to restrict access to a database's data and metadata, use ObjectStore's schema protection facility. This facility allows you to associate a schema key (a pair of integers) with a database. Once a database has been given a schema key, an application must supply the key in order to access data in the database. See the discussions beginning with Schema Keys.

Categories of Users

For a given directory, database, or segment there are three categories of users:

Owner of a Directory, Database, or Segment

For file databases and the directories they occupy, the owner is determined by the native operating system and its commands. For rawfs databases and the directories they occupy, the owner is initially the creator, and is subsequently determined by os_dbutil::chown() and the utility oschown. The owner of a segment is the owner of the database that contains it.

Only the owner of a segment can modify its protections with os_segment::set_access_control().

Group of a Directory, Database, or Segment

For file databases, the primary group of a directory or database is determined by the native operating system and its commands. For rawfs databases, the primary group of a directory or database is initially the owner's group, and is subsequently determined by os_dbutil::chgrp() and the utility oschgrp. The primary group of a segment is initially the group of the containing database, and is subsequently determined by os_segment::set_access_control().

Group of a User

A user's group membership is determined by the native operating system and its commands.

Permissions

For rawfs databases, you can specify access permissions at three levels of granularity: directory, database, and segment.

For file databases, you can specify access permissions at two levels of granularity: directory and database.

Directory Permissions

At any time, each user has zero or more of the following two types of access to a given directory:

Database Permissions

At any time, each user has zero or more of the following two types of access to a given database:

Segment Permissions

For a given segment in a rawfs database, each user has zero or more of the following two types of access to the segment:

Each new rawfs segment grants both read and write permissions to all three categories of users.

Permission Checks

Directory-Level Access

To create or delete databases in a given directory, you must have write access to the directory. To list the contents of a directory, you must have read access to the directory.

Database-Level Access

For an application to open a database for update, the user that launched the application must have write permission for the database. For file databases, the user must also have write permission for the directory containing the database.

For an application to open a database for read, the user must have read permission for the database. For file databases, the user must also have read permission for the directory containing the database.

If a user does not have the appropriate permissions when opening a database for read or update, ObjectStore signals err_permission_denied.

Segment-Level Access

Consider a segment in a rawfs database. In each transaction, the first time an application attempts to lock (for read or write) data in the segment, ObjectStore checks the access permissions granted to the user that launched the application.

For the application to perform write access on the segment, the user must have write permission for the segment. For file databases, the user must also have write permission for the directory containing the database that contains the segment, as well as write permission for the database.

For the application to perform read access on the segment, the user must have read permission for the segment. For file databases, the user must also have read permission for the directory containing the database that contains the segment, as well as read permission for the database.

If a user does not have the appropriate permissions when attempting initial read or write access to a segment, ObjectStore signals err_permission_denied.

Segment-Level Permissions API

The programming interface for rawfs database segment-level permissions is provided by the following functions:

These and other ObjectStore functions are described in detail in Chapter 2, Class Library, of ObjectStore Management.

Establishing Access Permissions with os_segment_access

Instances of the class os_segment_access serve to associate zero or more access types with a group of a specified name, as well as with the default group (see Categories of Users).

By associating an os_segment_access with a segment (using os_segment::set_access_control()), you specify the segment's associated primary group and the segment's permissions.

The owner of a segment always has both read and write access to it.

Access type enumerators
The possible combinations of access types are represented by the following enumerators:

Note that write access without read access cannot be specified.

These enumerators are used as arguments to some of the members of os_segment_access.

You must be the owner of a database to set the permissions on its segments.

For more information, see os_segment_access in Chapter 2, Class Library, of the ObjectStore C++ API Reference.

os_segment_access::set_primary_group()

There are two overloadings of this function. The first is declared as follows:

      void set_primary_group(
            const char* group_name, 
            os_int32 access_type
      ) ;
This function associates a specified combination of access types with a group of a specified name. group_name is the name of the group. access_type is os_segment_access::no_access, os_segment_access::read_access, or os_segment_access::read_write_access.

The second overloading is declared as follows:

      void set_primary_group(
            os_int32 access_type
            );
This function associates a specified combination of access types with a group of an unspecified name. access_type is os_segment_access::no_access, os_segment_access::read_access, or os_segment_access::read_write_access.

For more information, see os_segment_access::set_primary_group() in Chapter 2, Class Library, of the ObjectStore C++ API Reference.

os_segment_access::get_primary_group()

This function is declared as follows:

      os_int32 get_primary_group(
                  const char** group_name = 0
      ) const ;
It returns the types of access associated with the primary group of the os_segment_access. The function sets group_name, if supplied, to point to the name of that group.

For more information, see os_segment_access::get_primary_group() in Chapter 2, Class Library, of the ObjectStore C++ API Reference.

os_segment_access::set_default()

This function is declared as follows:

      void set_default(
                  os_int32 access_type
      );
This function associates a specified combination of access types with the default group. access_type is os_segment_access::no_access, os_segment_access::read_access, or os_segment_access::read_write_access.

For more information, see os_segment_access::set_default() in Chapter 2, Class Library, of the ObjectStore C++ API Reference.

os_segment_access::get_default()

This function is declared as follows:

      os_int32 get_default() const ;
It returns the types of access associated with the default group for the os_segment_access.

For more information, see os_segment_access::get_default() in Chapter 2, Class Library, of the ObjectStore C++ API Reference.

os_segment_access::os_segment_access()

This function has three overloadings. The first is declared as follows:

      os_segment_access() ;
This creates an os_segment_access that associates no_access with both the default group and the group named group_name.

The second overloading is declared as follows:

      os_segment_access(
             const char* primary_group,
             os_int32 primary_group_access_type,
             os_int32 default_access_type
      ) ;
This creates an instance of os_segment_access that associates primary_group_access_type with the group named primary_group, and associates default_access_type with the default group. primary_group_access_type and default_access_type are each os_segment_access::no_access, os_segment_access::read_access, or os_segment_access::read_write_access.

The third overloading is declared as follows:

      os_segment_access(
             const os_segment_access& source
      ) ;
This creates a copy of source; that is, it creates an os_segment_access that stores the same group name, and associates the same combinations of access types with the same groups.

For more information, see os_segment_access::os_segment_access() in Chapter 2, Class Library, of the ObjectStore C++ API Reference.

os_segment_access::operator =()

This function is declared as follows:

      os_segment_access& operator= (
            const os_segment_access& source
      ) ;
This function modifies the os_segment_access pointed to by this so that it is a copy of source, that is, so that it stores the same group name as source, and associates the same combinations of access types with the same groups. It returns a reference to the modified os_segment_access.

For more information, see os_segment_access::operator =() in Chapter 2, Class Library, of the ObjectStore C++ API Reference.

os_segment_access::~os_segment_access()

The destructor frees memory associated with the deleted instance of os_segment_access.

For more information, see os_segment_access::~os_segment_access() in Chapter 2, Class Library, of the ObjectStore C++ API Reference.

os_segment::set_access_control()

This function is declared as follows:

      void set_access_control( const os_segment_access
*new_access) ;
This associates the specified os_segment_access with the specified segment. The os_segment_access determines the primary group and permissions for the os_segment. You must be the owner of a database to set the permissions on its segments.

If you are not the owner of a database but nevertheless have write access to it, you have the ability to create a segment in the database but not to modify its permissions. Since newly created segments allow all types of access to all categories of users, segments created by nonowners necessarily have a period of vulnerability between creation time and the time at which the owner restricts access with os_segment::set_access_control().

For more information, see os_segment::set_access_control() in Chapter 2, Class Library, of the ObjectStore C++ API Reference.

os_segment::get_access_control()

This function is declared as follows:

      os_segment_access *get_access_control() const ;
It returns a pointer to the segment's associated os_segment_access, which indicates the segment's primary group and permissions.

For more information, see os_segment::get_access_control() in Chapter 2, Class Library, of the ObjectStore C++ API Reference.

os_database::get_all_segments_and_permissions()

This member of os_database is declared as follows:

      void get_all_segments_and_permissions(
            os_int32 max_to_return,
            os_segment** segs,
            os_segment_access** controls,
            os_int32 &n_returned
      ) ;
Provides access to all the segments in the specified database, together with each segment's associated os_segment_access. The nth element of controls points to the os_segment_access associated with the segment pointed to by the nth element of segs. The arrays controls and segs must be allocated by the user. max_to_return is specified by the user.

For more information, see os_database::get_all_segments_and_permissions() in Chapter 2, Class Library, of the ObjectStore C++ API Reference.

Segment-Level Permissions and Locking

When you change a segment's permissions, ObjectStore locks the entire segment for write. This means that permission changes at the segment level are transaction consistent (database-level changes are not transaction consistent). But this can also adversely affect the performance of the application performing the change, as well as of concurrent applications, if there is a lot of contention for the segment's pages.

Permissions and Related Segments

Remember to set the permissions on related segments in a compatible way. That is, if an operation on one segment triggers access to another segment, do not forget that both segments must have the appropriate permissions.

For example, consider a collection in one segment that has an index in another segment. If you perform a query on the collection that uses the index, do not forget that both segments must be accessible for read.

Similarly, given a configuration that spans segments, unexpected permission failures can occur if all the configuration's segments do not have the same protections.

Schema Keys

If you want to restrict access to a database's data and metadata, use ObjectStore's schema protection facility. This facility allows you to associate a schema key (a pair of integers) with a database. Once a database has been given a schema key, an application must supply the key in order to access data in the database.

Database Schema Keys

At any time a database can have a schema key that consists of a pair of four-byte unsigned integers. By default, a database has no schema key. You specify a key for a database with os_database::change_schema_key(). You can also freeze the schema key of a given database, preventing any change to the key, even by applications with a matching key. You do this with os_database::freeze_schema_key(). See Schema Key API.

If a database has a schema key, it can only be accessed by an application that supplies a matching schema key. See Application Schema Keys as well as Key Mismatch.

Application Schema Keys

As is the case with ObjectStore databases, ObjectStore applications can have a schema key that consists of a pair of four-byte unsigned integers. By default, an application has no current schema key. You specify a key for an application with objectstore::set_current_schema_key(). See Schema Key API. For ObjectStore tools and for ObjectStore utilities executed from the command line, you specify the schema key with environment variables. See Schema Key Environment Variables.

Key Mismatch

When an application first attempts to access the data or metadata of a protected database (one with a schema key), an err_schema_key is signaled if the application has no key or has a different key from the database. ObjectStore issues an error message like the following:

      Error using schema keys
      <err-0025-0151>The schema is protected and the key, if provided,
      did not match the one in the schema of database db1.
      (err_schema_key)
If the database's open count goes to 0 and then is increased to 1 again, the schema protection key is checked again at the first attempted access. In general, the schema protection key is checked at the first attempted access after each database open that increments the open count from 0 to 1.

If an application is linked with a version of ObjectStore that does not support schema protection, and the application tries to access a database with a schema key, err_uninitialized is signaled. ObjectStore issues an error message like the following:

      The database is uninitialized.
      <err-0025-0508>The database db1 is corrupted or uninitialized.
      Possibly the transaction that created this database aborted.
      Use osrm to remove the database, and try again. (err_uninitialized)
Permissible operations on a protected database
You can perform certain operations on a database that are not considered to access the database, since they do not require use of the database's schema. These operations include opening and copying a database. You can perform these operations on a protected database without knowing its key.

Caution when using the oscp utility

In the case of the oscp utility , however, specifying the correct key can nevertheless affect the operation's result. If you specify the correct key, the copy has a different db_id from the original; otherwise, the copy has the same db_id as the original. In either case, the copy has the same key as the original, and the copy's key is frozen if and only if the original's is. For more information on the oscp utility, see oscp: Copying Databases in Chapter 4, Utilities, of ObjectStore Management.

Schema Key API

The programming interface for schema protection is provided by the following functions:

Setting a Database Schema Key with change_schema_key()

You set the schema key of a database with os_database::change_schema_key(). This function is declared as follows:

      void change_schema_key(
            os_unsigned_int32 old_key_low, 
            os_unsigned_int32 old_key_high,
            os_unsigned_int32 new_key_low, 
            os_unsigned_int32 new_key_high
      ) ;
Call this function from within an update transaction. The specified database must be opened for update, otherwise ObjectStore signals err_opened_read_only, and issues an error message like the following:

      <err-0025-0155> Attempt to change the schema key of database
db1, but it is opened for read only.
If the database has had its key frozen, err_schema_key is signaled, and ObjectStore issues an error message like the following:

      err_schema_key
      <err-0025-0152> The schema key of database db1 is frozen 
and may not be changed.
old_key_low and old_key_high
If the database already has a schema key at the time of the call, old_key_low must be the first component of the key and old_key_high must be the second component, or err_schema_key is signaled, and ObjectStore issues an error message like the following:

      Error using schema keys
      <err-0025-0158>Unable to change schema key of database db1.
      The schema is already protected and the key provided did not match
      the old key in the schema. (err_schema_key)
If the database has no schema key, old_key_low and old_key_high are ignored.

new_key_low and new_key_high
new_key_low specifies the first component of the database's new schema key, and new_key_high specifies the second component. If both these arguments are 0, calling this function causes the database to have no schema key.

For more information, see os_database::change_schema_key() in Chapter 2, Class Library, of the ObjectStore C++ API Reference.

Setting Application Schema Keys with set_current_schema_key()

The function objectstore::set_current_schema_key() can be used to set or unset the schema key of the current application. This function is declared as follows:

      void objectstore::set_current_schema_key(
            os_unsigned_int32 key_low, 
            os_unsigned_int32 key_high
      ) ;
Call this function only after calling objectstore::initialize(), otherwise err_schema_key is signaled and ObjectStore issues an error message like the following:

      <err-0025-0153> The schema key may not be set until after
objectstore::initialize has been called.
key_low and key_high
key_low specifies the first component of the schema key, and key_high specifies the second component. If both these arguments are 0, calling this function causes the application's schema key to be determined as for an application that has not called this function.

If an application has not called this function, its key is determined by the values of OS_SCHEMA_KEY_HIGH and OS_SCHEMA_KEY_LOW (see Schema Key Environment Variables). If neither variable is set, the application has no current schema key.

For more information, see objectstore::set_current_schema_key() in Chapter 2, Class Library, of the ObjectStore C++ API Reference.

Freezing a Database Key with freeze_schema_key()

Use os_database::freeze_schema_key() to freeze a database's key, preventing any change to the key, even by applications with a matching key. This function is declared as follows:

      void os_database::freeze_schema_key(
            os_unsigned_int32 key_low, 
            os_unsigned_int32 key_high
      ) ;
Call this function from within an update transaction. The specified database must be opened for update, otherwise ObjectStore signals err_opened_read_only, and issues an error message like the following:

      <err-0025-0156> Attempt to freeze the schema key of database db1,
      but it is opened for read only.
If the database is schema protected and has not been accessed since the last time its open count was incremented from 0 to 1, the application's schema key must match the database's schema key. If it does not, err_schema_key is signaled, and ObjectStore issues an error message like the following:

      <err-0025-0159>Unable to freeze the schema key of database db1.
      The schema is protected and the key provided did not match the key
      in the schema.
key_low and key_high
key_low and key_high must also match the database's schema key, or else err_schema_key is signaled.

If the database's schema key is already frozen, and you specify the correct key, the call has no effect.

For more information, see os_database::freeze_schema_key() in Chapter 2, Class Library, of the ObjectStore C++ API Reference.

Schema Key Environment Variables

If you run certain ObjectStore tools and utilities on schema-protected databases, set the ObjectStore client environment variables OS_SCHEMA_KEY_LOW and OS_SCHEMA_KEY_HIGH to specify the schema key of the databases to be accessed.

Normally, you specify a key for an application with objectstore::set_current_schema_key(). These environment variables are provided since it is not possible for you to set the schema key of a tool or utility programmatically. ObjectStore environment variables are described in Chapter 3, Environment Variables, of ObjectStore Management.

The tools and utilities for which these variables must be set include the following:
UtilityFunctionRefer to Chapter 4, Utilities, inObjectStore Management
oscompact Removes deleted space in specified databases or segments.oscompact: Compacting Databases
osexschm Lists the names of all classes in the schema referenced by the specified database.

osexschm: Displaying Class Names in a Schema
ossevol Modifies a database and its schema so that it matches a revised application schema.ossevol: Evolving Schemas
(also Schema Evolution with ossevol of this publication)
ossg ObjectStore schema generator.ossg: Generating Schemas
ossize Displays the size of the specified database and the sizes of its segments.

ossize: Displaying Database Size
osverifydb Verifies all pointers and references in a database.

osverifydb: Verifying Pointers and References in a Database (also Using osverifydb to Verify Pointers and References of this publication)

These environment variables determine an application's schema key when an ObjectStore application attempts to access data in a schema-protected database, and either one of the following is true:

Keep in mind that when the environment variables determine an application's schema key, all schema-protected databases that the application accesses must have the same schema key.

By default, neither OS_SCHEMA_KEY_LOW nor OS_SCHEMA_KEY_HIGH is set.

Building applications that allow utility use on protected databases
To allow your customers to use an ObjectStore utility on a database that you have protected, build an application that calls the member of the class os_dbutil that corresponds to the utility. This application can specify the schema key with objectstore::set_current_schema_key() (see Chapter 2, Class Library, in ObjectStore C++ API Reference).

Some ObjectStore tools (such as the ossg utility) cannot be invoked from the ObjectStore API. To allow your customers to use such a tool on a database that you have protected, build an application that spawns the tool as a child process, and specify the key of the child process by setting the environment variables from within the application.



[previous] [next]

Copyright © 1997 Object Design, Inc. All rights reserved.

Updated: 03/31/98 17:03:02