当前位置: 首页 > 图文教程 > 数据库 > DB2 > DB2 9部署定制的安全性插件

DB2
IBM DB2 日常维护汇总(八)
IBM DB2 日常维护汇总(七)
IBM DB2 日常维护汇总(五)
IBM DB2 日常维护汇总(四)
IBM DB2 日常维护汇总(三)
IBM DB2 日常维护汇总(二)
IBM DB2 日常维护汇总(一)
DB2常用傻瓜问题1000问(六)
DB2常用傻瓜问题1000问(五)
MySQL数据库结构和数据的导出和导入
DB2编程序小小技巧
在DB2中提高INSERT性能的技巧(1)
db2v8的pdf文档资料
DB2 9数据服务器发展3部曲
对比DB2 9和DB2 V8.x中的XML功能
用shell抽取,更新db2的数据
IBM DB2 Connect简介(1)
使用XQuery查询DB2 XML数据
DB2 9产品说明书在线参考地址(http)
使用SQL查询DB2 9中的XML数据

DB2 9部署定制的安全性插件


出处:互联网   整理: 软晨网(RuanChen.com)   发布: 2009-09-30   浏览: 124 ::
收藏到网摘: n/a

  介绍

  在获知用户是否被授权进行特定操作之前,DB 2 必须能够获知外部用户的有效数据库授权 ID 及其所属的组。为了找到有效数据库授权 ID,需要确保外部用户就是所声称的用户,然后将其外部用户 ID 映射到内部 DB2 授权 ID。此过程被称为身份验证。DB2 没有用于维护用户 ID 和密码,或用户 ID 组成员关系的机制。

  安全性插件的作用是什么?

  有两个事件需要使用(客户端或服务器端)身份验证插件或组成员查找插件:

  • 连接或实例连接时
  • 实例级操作授权时

  实例级操作是涉及 DB2 实例维护的任何操作。实例级操作的典型例子为更新数据库管理器配置参数和启动数据库管理器。

  首先,我们看一下实例级操作。大多数实例级操作命令不会接受用户 ID 或密码作为输入部分。在这种情况下,DB2 使用客户端身份验证插件(即 client auth 插件)找到发出命令的当前登录用户。(这就是所谓的获得默认登录上下文。登录上下文可以是用户 ID/密码对或 Generic Security Services API [GSS-API] 凭证,如 Kerberos 票据)。一旦获取用户的身份,将使用 DB2 组成员查找插件(即 group plug-in)为用户生成组授权 ID 列表。

  在连接建立或实例连接中,用户 ID 和密码是可选的。如果用户选择不指定用户 ID 和密码,则 DB2 使用客户端身份验证插件来获得默认登录上下文。然后,将登录上下文发送到服务器以获取用户的组授权 ID。如果用户选择指定用户 ID 和密码,且未将身份验证类型数据库管理器配置参数指定为 CLIENT,则 DB2 执行下面的步骤:

表 1. 除 CLIENT 之外的身份验证的信息流

客户机 信息流(如果可用) 服务器
(客户端身份验证插件)
如果已实现 db2secRemapUserid 且身份验证插件是基于用户 ID/密码的,则重新映射用户 ID、密码和名称空间。请注意,该过程也适用于默认登录上下文情况。
   
发送登录上下文到服务器。 登录上下文 -> 服务器接收了登录上下文。
    (服务器端身份验证插件)
验证登录上下文(验证用户 ID 和密码或 GSS-API 凭证,如 Kerberos 票据),并获取用户的授权 ID。
    获取正在使用服务器上组成员查找插件的用户的组授权 ID。

  如果在数据库服务器上将身份验证类型指定为 CLIENT 身份验证,则执行下面的步骤:

表 2. CLIENT 身份验证的信息流

客户机 信息流(如果可用) 服务器
(客户端身份验证插件)
如果已实现 db2secRemapUserid 且身份验证插件是基于用户 ID/密码的,则重新映射用户 ID、密码和名称空间。请注意,该过程也适用于默认登录上下文情况。
   
(客户端身份验证插件)
验证登录上下文(验证用户 ID 和密码或 GSS-API 凭证,如 Kerberos 票据)。
   
发送用户 ID 到服务器。 user ID -> (服务器端身份验证插件)服务器接收了用户 ID 并获取用户的授权 ID。
    获取正在使用服务器上组成员查找插件的用户的组授权 ID。

  建议的最小完备测试

  基于前文所述的安全性插件职责,建议安全性插件至少应进行下面的测试:

  1. 使用未授权的用户,尝试执行实例级操作,如获得数据库管理器配置参数。此操作应该失败。
  2. 使用授权的用户,尝试执行实例级操作。此操作应该成功。
  3. 使用有效的用户 ID 和密码,建立数据库连接,并尝试使用授予其中一个组授权 ID 的特权。此操作应该成功。
  4. 尝试使用无效的用户 ID 或无效的密码进行连接。此操作应该失败。
  5. 尝试不指定用户 ID 和密码而进行连接。此操作仅当客户机和服务器处于同一个机器上时才能进行,且在定制的安全性插件中没有执行任何特殊的限制逻辑。这样确保定制的安全性插件将正确的登录用户凭证返回到 DB2。
  6. 如果正在实现定制的组成员查找插件,则请确保 db2secGetGroupForUser API 了解授权 ID,并且始终是大写的,无论定制的身份验证插件可能返回大小写混合或小写授权 ID。

  安全性插件 loader

  安全性插件 loader 程序允许模拟 DB2 对上述建议测试的响应。这对于断定某个特定问题是由插件中的代码造成的,还是由 DB2 引起的是非常有用的。

  程序输入为:

  • -c option:客户端身份验证插件的全限定路径和插件名称。
  • -s option:服务器端身份验证插件的全限定路径和插件名称。
  • -g option:组成员查找插件的全限定路径和插件名称。
  • -u option:用户 ID。
  • -p option:密码。(对于 GSS-API 或 Kerberos,这是用于获得用户凭证的密码。)
  • -d option:数据库名称。
  • -n option:新密码。
  • -a option:用户授权 ID。

  启动或执行 loader 时,根据需要加入三个指定的安全性插件(客户端身份验证插件、服务器身份验证插件和组成员查找插件),并且确定身份验证插件类型(例如基于用户 ID/密码、基于 Kerberos 或基于 GSS-API)。调用每个插件的初始化函数,且检索每个插件的全部函数指针。

  基于所指定的选项组合,将执行下面四个测试类型。

  为了简单起见,该 loader 不支持客户端身份验证测试,且不为定制的插件(例如 db2secGetConDetails)提供 get Client connection details 回调函数。为插件提供了 db2secLogMessage,但输出直接到达标准输出(屏幕)。

  这个 loader 提供了下面四个测试功能,用来模拟安全性插件在 DB2 环境中调用序列完全相同的 API 的用法。

  测试功能 1

 

  用给定的用户 ID 和密码建立连接,且验证密码或凭据。输出是 authid 和组 authid 列表。

  1. 如果正在使用 Kerberos 或基于 GSS-API 的身份验证插件,则 API 调用序列如下:
  • 客户机插件:db2secProcessServerPrincipalName
  • 客户机插件:db2secGenerateInitialCred
  • 客户机插件:gss_init_sec_context
  • 服务器插件:gss_accept_sec_context

  在客户端 API:gss_init_sec_context 与服务器端 API:gss_accept_sec_context 之间进行循环,直到其中一端返回 GSS_S_COMPLETE 为止。如果希望从另一端得到更多令牌,则 API 应返回 GSS_S_CONTINUE_NEEDED。

  • 客户机插件:gss_release_buffer、gss_release_name、gss_release_cred、db2secFreeInitInfo、gss_delete_sec_context
  • 服务器插件:ss_release_buffer、db2secGetAuthIDs
  • 组插件:db2secGetGroupForUser
  • 客户机插件:db2secFreeToken
  • 服务器插件:gss_delete_sec_context
  • 组插件:db2secFreeGroupListMemory

  图 1. 使用 Kerberos 或基于 GSS-API 的身份验证插件的 API 调用序列

  使用 Kerberos 或基于 GSS-API 的身份验证插件的 API 调用序列

  2、如果正在使用基于用户 ID/密码的身份验证插件,则 API 调用序列如下:

  • 客户机插件:db2secRemapUserid(如果它是可选实现的)
  • 服务器插件:db2secValidatePassword、db2secGetAuthIDs
  • 组插件:db2secGetGroupForUser
  • 服务器插件:db2secFreeToken
  • 组插件:db2secFreeGroupListMemory

  图 2. 使用基于用户 ID/密码的身份验证插件的 API 调用序列

  使用基于用户 ID/密码的身份验证插件的 API 调用序列

  测试功能 2

  不提供用户 ID 和密码(默认的登录上下文)而建立连接。输出是 authid 和组 authid 列表。

  1、如果正在使用 Kerberos 或基于 GSS-API 的身份验证插件,则 API 调用序列如下:

  • 客户机插件:db2secGetDefaultLoginContext
  • 客户机插件:db2secProcessServerPrincipalName
  • 客户机插件:gss_init_sec_context
  • 服务器插件:gss_accept_sec_context

  在客户端 API:gss_init_sec_context 和服务器端 API:gss_accept_sec_context 之间进行循环,直到其中一端返回 GSS_S_COMPLETE 为止。如果希望从另一端得到更多令牌,则 API 应返回 GSS_S_CONTINUE_NEEDED。

  • 客户机插件:gss_release_buffer、gss_release_name、gss_delete_sec_context
  • 服务器插件:gss_release_buffer、db2secGetAuthIDs
  • 组插件:db2secGetGroupForUser
  • 客户机插件:db2secFreeToken
  • 服务器插件:gss_delete_sec_context
  • 组插件:db2secFreeGroupListMemory

  图 3. 使用 Kerberos 或基于 GSS-API 的身份验证插件的 API 调用序列

  使用 Kerberos 或基于 GSS-API 的身份验证插件的 API 调用序列

  2、如果正在使用基于用户 ID/密码的身份验证插件,则 API 调用序列如下:

  • 客户机插件:db2secGetDefaultLoginContext、db2secFreeToken
  • 服务器插件:db2secGetAuthIDs
  • 组插件:db2secGetGroupForUser
  • 服务器插件:db2secFreeToken
  • 组插件:db2secFreeGroupListMemory

  图 4. 使用基于用户 ID/密码的身份验证插件的 API 调用序列

  使用基于用户 ID/密码的身份验证插件的 API 调用序列

  测试功能 3

  给定用户授权 ID,输出是组 authid 列表。

  API 调用序列如下:

  • 组插件:db2secGetGroupsForUser

  图 5. API 调用序列

  API 调用序列

  测试功能 4

  模拟实例级操作(没有用户 ID 和密码)的本地授权测试。输出是组 authid 列表。

  API 调用序列如下:

  • 客户机插件:db2secGetDefaultLoginContext
  • 组插件:db2secGetGroupForUser
  • 客户机插件:db2secFreeToken
  • 组插件:db2secFreeGroupListMemory

  图 6. API 调用序列

  API 调用序列

  DB2 9 中 DB2 安全性插件的增强

 

  DB2 9 中对 DB2 安全性插件基础设施进行了很多增强,包括基于轻量级目录访问协议(Lightweight Directory Access Protocol,LDAP)的身份验证插件的输送、基于 LDAP 的组成员查找插件的输送、及 client connection details 的增强以包括对 TCP/IP 版本 6 和客户机操作系统平台信息的支持。

  可以从 DB2 LDAP 验证插件 下载受 IBM 支持的基于 LDAP 的身份验证插件和组成员查找插件。

  如果已经通过 TCP/IP 地址实现了连接限定,且您的公司正准备实现 TCP/IP 版本 6,则需要修改安全性插件,从而进行对应的操作。

  首先,需要从使用下面所列的版本 2 或 3 的连接详细信息结构(节选自 IBM 发布的头文件 db2secPlugin.h)开始。版本 2 和 3 的区别在于版本 3 还给出了客户机操作系统平台。

  清单 1. 客户机连接详细信息结构的新版本

  typedef struct db2sec_con_details_2

  {

  db2int32 clientProtocol; /* See SQL_PROTOCOL_ in sqlenv.h */

  db2Uint32 clientIPAddress; /* Set if protocol is TCPIP4 */

  db2Uint32 connect_info_bitmap;

  db2int32 dbnameLen;

  char dbname[DB2SEC_MAX_DBNAME_LENGTH + 1];

  db2Uint32 clientIP6Address[4];/* Set if protocol is TCPIP6 */

  } db2sec_con_details_2;

  typedef struct db2sec_con_details_3

  {

  db2int32 clientProtocol; /* See SQL_PROTOCOL_ in sqlenv.h */

  db2Uint32 clientIPAddress; /* Set if protocol is TCPIP4 */

  db2Uint32 connect_info_bitmap;

  db2int32 dbnameLen;

  char dbname[DB2SEC_MAX_DBNAME_LENGTH + 1];

  db2Uint32 clientIP6Address[4];/* Set if protocol is TCPIP6 */

  db2Uint32 clientPlatform; /* SQLM_PLATFORM_* from sqlmon.h */

  db2Uint32 _reserved[16];

  } db2sec_con_details_3;

  然后,在调用回调函数时,使用 DB2SEC_CON_DETAILS_VERSION_2 或 DB2SEC_CON_DETAILS_VERSION_3 来代替 DB2 V8.2 文档所建议的 DB2SEC_API_VERSION_1 或数字 1。例如,假定要使用连接详细信息版本 3;可以在插件中进行下面的回调:

  struct db2sec_con_details_3 con_struct3;

  rc = (getConDetails_fn)(DB2SEC_CON_DETAILS_VERSION_3, &con_struct3);

  由于在整个机构上使用 TCP/IP 版本 6 是一个较长的过程,因此 DB2 可能会遇到一些仍使用 TCP/IP 版本 4 的客户机或其他切换到 TCP/IP 版本 6 的客户机。为了帮助使用者区分客户机,结构中 clientProtocol 字段现在可以返回 SQL_PROTOCOL_TCPIP4 或 SQL_PROTOCOL_TCPIP6,用来指示客户机正在使用的 TCP/IP 协议是版本 4,还是版本 6。

  如果需要找到客户机的操作系统,则可以检查 DB2 通过回调函数返回到字段 clientPlatform 中的整数。整数到相应平台的映射如下(节选自 IBM 发布的头文件 sqlmon.h):

  #define SQLM_PLATFORM_UNKNOWN 0 /* Unknown platform */

  #define SQLM_PLATFORM_OS2 1 /* OS/2 */

  #define SQLM_PLATFORM_DOS 2 /* DOS */

  #define SQLM_PLATFORM_WINDOWS 3 /* Windows */

  #define SQLM_PLATFORM_AIX 4 /* AIX */

  #define SQLM_PLATFORM_NT 5 /* NT */

  #define SQLM_PLATFORM_HP 6 /* HP */

  #define SQLM_PLATFORM_SUN 7 /* Sun */

  #define SQLM_PLATFORM_MVS_DRDA 8 /* MVS (client via DRDA) */

  #define SQLM_PLATFORM_AS400_DRDA 9 /* AS400 (client via DRDA) */

  #define SQLM_PLATFORM_VM_DRDA 10 /* VM (client via DRDA) */

  #define SQLM_PLATFORM_VSE_DRDA 11 /* VSE (client via DRDA) */

  #define SQLM_PLATFORM_UNKNOWN_DRDA 12 /* Unknown DRDA Client */

  #define SQLM_PLATFORM_SNI 13 /* Siemens Nixdorf */

  #define SQLM_PLATFORM_MAC 14 /* Macintosh Client */

  #define SQLM_PLATFORM_WINDOWS95 15 /* Windows 95 */

  #define SQLM_PLATFORM_SCO 16 /* SCO */

  #define SQLM_PLATFORM_SGI 17 /* Silicon Graphic */

  #define SQLM_PLATFORM_LINUX 18 /* Linux */

  #define SQLM_PLATFORM_DYNIX 19 /* DYNIX/ptx */

  #define SQLM_PLATFORM_AIX64 20 /* AIX 64 bit */

  #define SQLM_PLATFORM_SUN64 21 /* Sun 64 bit */

  #define SQLM_PLATFORM_HP64 22 /* HP 64 bit */

  #define SQLM_PLATFORM_NT64 23 /* NT 64 bit */

  #define SQLM_PLATFORM_LINUX390 24 /* Linux for S/390 */

  #define SQLM_PLATFORM_LINUXZ64 25 /* Linux for z900 */

  #define SQLM_PLATFORM_LINUXIA64 26 /* Linux for IA64 */

  #define SQLM_PLATFORM_LINUXPPC 27 /* Linux for PPC */

  #define SQLM_PLATFORM_LINUXPPC64 28 /* Linux for PPC64 */

  #define SQLM_PLATFORM_OS390 29 /* OS/390 Tools (CC, DW) */

  #define SQLM_PLATFORM_LINUXX8664 30 /* Linux for x86-64 */

  #define SQLM_PLATFORM_HPIA 31 /* HP-UX Itanium 32bit */

  #define SQLM_PLATFORM_HPIA64 32 /* HP-UX Itanium 64bit */

  下面是示例代码片断,其中阻塞了数据库服务器上的回路连接:

  清单 2. 来自本系列第 2 部分的连接限定代码

  /* If the connection is not local */

  if (!(con_struct.connect_info_bitmap & DB2SEC_CONNECTION_ISLOCAL))

  {

  struct hostent* hostinfo;

  char hostname[256];

  int* firstaddr;

  /* Get the address of the machine restricted */

  gethostname(hostname, sizeof(hostname));

  hostinfo = gethostbyname(hostname);

  firstaddr = (int *)*(hostinfo->h_addr_list);

  /* If the client IP address is the restricted one */

  if (*firstaddr == con_struct.clientIPAddress)

  {

  /* We won't allow behavoir to connect through this IP address */

  if (0 == strcmp(userid, "beauvoir"))

  return DB2SEC_PLUGIN_CONNECTION_DISALLOWED;

  }

  }

  为了使用 TCP/IP 版本 6,需要修改为以下代码:

  清单 3. 使用 IP V6 的连接限定代码

  if (!(con_struct3.connect_info_bitmap & DB2SEC_CONNECTION_ISLOCAL))

  {

  struct addrinfo* pAddr;

  /* find the local IP address to compare it with the one retrieved */

  if ((con_struct3.clientProtocol == SQL_PROTOCOL_TCPIP6) &&

  (IN6_IS_ADDR_V4MAPPED((struct in6_addr *)con_struct3.clientIP6Address)))

  {

  /* Comment out the following line of code if your database server is not */

  /* running on MS Windows */

  aiHints.ai_flags = AI_ALL | AI_V4MAPPED;

  }

  else

  {

  aiHints.ai_flags = AI_PASSIVE;

  }

  aiHints.ai_family = (con_struct3.clientProtocol == SQL_PROTOCOL_TCPIP6)?

  AF_INET6 : AF_INET;

  aiHints.ai_socktype = SOCK_STREAM;

  aiHints.ai_protocol = IPPROTO_TCP;

  err = getaddrinfo(hostname, NULL, &aiHints, &aiList);

  if (err)

  {

  Print("getaddrinfo error: %d\n", err);

  return DB2SEC_PLUGIN_CONNECTION_DISALLOWED;

  }

  /* compare the IPs */

  if (SQL_PROTOCOL_TCPIP6 == con_struct3.clientProtocol)

  {

  pAddr = aiList;

  while (pAddr)

  {

  struct sockaddr_in6* s = (struct sockaddr_in6*)pAddr->ai_addr;

  struct in6_addr *src = (struct in6_addr*)&s->sin6_addr;

  struct in6_addr *dst =

  (struct in6_addr*)&con_struct3.clientIP6Address;

  /* check if address verified is the same format as the address

  * sent by the client: compare only IPv6 format address with

  * IPv6 ones or IPv6 mapped IPv4 with the same type */

  if ((*(db2Uint32*)(&s->sin6_addr.s6_addr[0]) ==

  con_struct3.clientIP6Address[0]) &&

  (*(db2Uint32*)(&s->sin6_addr.s6_addr[4]) ==

  con_struct3.clientIP6Address[1]) &&

  (*(db2Uint32*)(&s->sin6_addr.s6_addr[8]) ==

  con_struct3.clientIP6Address[2]) &&

  (*(db2Uint32*)(&s->sin6_addr.s6_addr[12])==

  con_struct3.clientIP6Address[3]))

  {

  /* don't allow beauvoir to connect through this ip address */

  if (0 == strcmp(userid, "beauvoir"))

  {

  return DB2SEC_PLUGIN_CONNECTION_DISALLOWED;

  }

  else

  {

  break;

  }

  }

  pAddr = pAddr->ai_next;

  } /* while (pAddr) */

  }

  else /* This else clause is need if you have a mix v4 and v6 environment */

  {

  /* TCPIP4 */

  struct sockaddr_in* s = (struct sockaddr_in*)aiList->ai_addr;

  if (s->sin_addr.s_addr == con_struct3.clientIPAddress)

  {

  /* don't allow beauvoir to connect through this ip address */

  if (0 == strcmp(userid, "beauvoir"))

  return DB2SEC_PLUGIN_CONNECTION_DISALLOWED;

  }

  }

  freeaddrinfo(aiList);

  }

  结束语

  本文给出了 DB2 9 中 DB2 安全插件的增强信息,详细阐述了安全性插件的职责。本文还提出了基于这些职责的最小完备测试列表。现在您应知道如何使用安全性插件 loader 来测试定制的安全性插件,该 loader 可以在 下载 部分中找到。