函数内存清理

内存清理  时间:2021-01-16  阅读:()

文件号码E540652014年7月面向开发者的OracleSolaris11安全性指南版权所有2000,2014,Oracle和/或其附属公司.
保留所有权利.
本软件和相关文档是根据许可证协议提供的,该许可证协议中规定了关于使用和公开本软件和相关文档的各种限制,并受知识产权法的保护.
除非在许可证协议中明确许可或适用法律明确授权,否则不得以任何形式、任何方式使用、拷贝、复制、翻译、广播、修改、授权、传播、分发、展示、执行、发布或显示本软件和相关文档的任何部分.
除非法律要求实现互操作,否则严禁对本软件进行逆向工程设计、反汇编或反编译.

此文档所含信息可能随时被修改,恕不另行通知,我们不保证该信息没有错误.
如果贵方发现任何问题,请书面通知我们.

如果将本软件或相关文档交付给美国政府,或者交付给以美国政府名义获得许可证的任何机构,必须符合以下规定:U.
S.
GOVERNMENTENDUSERS:Oracleprograms,includinganyoperatingsystem,integratedsoftware,anyprogramsinstalledonthehardware,and/ordocumentation,deliveredtoU.
S.
Governmentendusersare"commercialcomputersoftware"pursuanttotheapplicableFederalAcquisitionRegulationandagency-specificsupplementalregulations.
Assuch,use,duplication,disclosure,modification,andadaptationoftheprograms,includinganyoperatingsystem,integratedsoftware,anyprogramsinstalledonthehardware,and/ordocumentation,shallbesubjecttolicensetermsandlicenserestrictionsapplicabletotheprograms.
NootherrightsaregrantedtotheU.
S.
Government.
本软件或硬件是为了在各种信息管理应用领域内的一般使用而开发的.
它不应被应用于任何存在危险或潜在危险的应用领域,也不是为此而开发的,其中包括可能会产生人身伤害的应用领域.
如果在危险应用领域内使用本软件或硬件,贵方应负责采取所有适当的防范措施,包括备份、冗余和其它确保安全使用本软件或硬件的措施.
对于因在危险应用领域内使用本软件或硬件所造成的一切损失或损害,OracleCorporation及其附属公司概不负责.
Oracle和Java是Oracle和/或其附属公司的注册商标.
其他名称可能是各自所有者的商标.
Intel和IntelXeon是IntelCorporation的商标或注册商标.
所有SPARC商标均是SPARCInternational,Inc的商标或注册商标,并应按照许可证的规定使用.
AMD、Opteron、AMD徽标以及AMDOpteron徽标是AdvancedMicroDevices的商标或注册商标.
UNIX是TheOpenGroup的注册商标.
本软件或硬件以及文档可能提供了访问第三方内容、产品和服务的方式或有关这些内容、产品和服务的信息.
对于第三方内容、产品和服务,OracleCorporation及其附属公司明确表示不承担任何种类的担保,亦不对其承担任何责任.
对于因访问或使用第三方内容、产品或服务所造成的任何损失、成本或损害,OracleCorporation及其附属公司概不负责.
3目录使用此文档151面向开发者的OracleSolaris安全(概述)17面向开发者的OracleSolaris安全功能概述17系统安全17地址空间布局随机化(AddressSpaceLayoutRandomization,ASLR)18网络安全体系结构202开发特权应用程序21特权应用程序21关于特权22管理员如何指定特权22如何实现特权22超级用户与特权模型之间的兼容性23特权类别24使用特权进行编程24特权数据类型25特权接口25特权编码示例27特权应用程序开发指南30关于授权313编写PAM应用程序和服务33PAM框架介绍33PAM服务模块34PAM库35PAM验证过程35PAM使用者的要求36PAM配置36通过/etc/pam.
d配置PAM37目录4面向开发者的OracleSolaris11安全性指南2014年7月编写使用PAM服务的应用程序37简单PAM使用者示例38其他有用的PAM函数41编写对话函数42编写提供PAM服务的模块46PAM服务提供者要求46PAM提供者服务模块样例474编写使用GSS-API的应用程序51GSS-API介绍51使用GSS-API的应用程序的可移植性52GSS-API中的安全服务53GSS-API中的可用机制53使用GSS-API的远程过程调用53GSS-API的限制54GSS-API的语言绑定55有关GSS-API的更多参考信息55GSS-API的重要元素55GSS-API数据类型55GSS-API状态码65GSS-API令牌66开发使用GSS-API的应用程序68GSS-API的一般用法68在GSS-API中使用凭证68在GSS-API中使用上下文69在GSS-API中发送受保护的数据78清除GSS-API会话885GSS-API客户机示例89GSSAPI客户机示例概述89GSSAPI客户机示例结构89运行GSSAPI客户机示例90GSSAPI客户机示例:main()函数90打开与服务器的连接92建立与服务器的安全上下文93将服务名称转换为GSS-API格式94为GSS-API建立安全上下文94客户端上的各种GSSAPI上下文操作97包装和发送消息98目录5读取和验证GSS-API客户机中的签名块101删除安全上下文1026GSS-API服务器示例103GSSAPI服务器示例概述103GSSAPI服务器示例结构103运行GSSAPI服务器示例104GSSAPI服务器示例:main()函数104获取凭证106检查inetd109从客户机接收数据109接受上下文111展开消息115消息的签名和返回116使用test_import_export_context()函数116在GSSAPI服务器示例中清除1177编写使用SASL的应用程序119简单验证和安全层(SimpleAuthenticationandSecurityLayer,SASL)介绍.
.
.
.
119SASL库基础119SASL周期中的步骤123SASL示例131服务提供者的SASL134SASL插件概述135SASL插件开发指南1398OracleSolaris加密框架介绍141OracleSolaris加密术语141加密框架概述142加密框架的组件143加密开发者需要了解的内容144用户级使用者的开发要求145内核级使用者的开发要求1459编写用户级加密应用程序147Cryptoki库概述147PKCS#11函数列表147使用PKCS#11的函数148目录6面向开发者的OracleSolaris11安全性指南2014年7月扩展的PKCS#11函数154用户级加密应用程序示例155消息摘要示例155对称加密示例158签名和验证示例162随机字节生成示例16910OracleSolaris密钥管理框架介绍173OracleSolaris密钥管理框架功能173OracleSolaris密钥管理框架组件174KMF密钥管理工具174KMF策略实施机制175KMF应用编程接口176OracleSolaris密钥管理框架示例应用程序176KMF头和库177KMF基本数据类型177KMF应用程序结果验证177完整的KMF应用程序源代码178A适用于开发者的安全编码准则185B基于C的GSS-API样例程序187客户端应用程序187服务器端应用程序198各种GSS-API样例函数208CGSS-API参考信息215GSS-API函数215早期GSS-API版本中的函数217GSS-API状态码217GSS-API主状态码值218显示状态码220状态码宏220GSS-API数据类型和值221基本GSS-API数据类型221名称类型222通道绑定的地址类型223GSS-API中特定于实现的功能224目录7特定于OracleSolaris的函数224人工可读的名称语法224实现选定数据类型224删除上下文和存储数据225保护通道绑定信息225上下文导出和进程间令牌225支持的凭证类型225凭证到期225上下文到期225回绕大小限制和QOP值226使用minor_status参数226Kerberosv5状态码226Kerberosv5中状态码1的返回消息226Kerberosv5中状态码2的返回消息227Kerberosv5中状态码3的返回消息228Kerberosv5中状态码4的返回消息229Kerberosv5中状态码5的返回消息230Kerberosv5中状态码6的返回消息231Kerberosv5中状态码7的返回消息232D指定OID235包含OID值的文件235/etc/gss/mech文件235/etc/gss/qop文件236gss_str_to_oid()函数237构造机制OID237createMechOid()函数238指定非缺省机制239ESASL示例的源代码241SASL客户机示例241SASL服务器示例250通用代码259FSASL参考信息表263SASL接口摘要263G使用C函数时的安全注意事项267目录8面向开发者的OracleSolaris11安全性指南2014年7月术语表277索引2839图图3-1PAM体系结构34图4-1GSS-API层52图4-2RPCSEC_GSS和GSS-API54图4-3内部名称和机制名称59图4-4比较名称(慢速)61图4-5比较名称(快速)63图4-6导出上下文:多线程接受器示例77图4-7gss_get_mic()与gss_wrap(79图4-8消息重放和消息失序83图4-9确认MIC数据85图4-10确认已包装的数据87图7-1SASL体系结构120图7-2SASL生命周期124图7-3SASL会话初始化127图7-4SASL验证:发送客户机数据129图7-5SASL验证:处理服务器数据130图8-1OracleSolaris加密框架概述143图C-1主状态编码21810面向开发者的OracleSolaris11安全性指南2014年7月11表表2-1使用特权的接口25表2-2特权集合转换29表C-1GSS-API调用错误218表C-2GSS-API例程错误219表C-3GSS-API补充信息代码219表C-4通道绑定地址类型223表C-5Kerberosv5状态码1226表C-6Kerberosv5状态码2227表C-7Kerberosv5状态码3228表C-8Kerberosv5状态码4229表C-9Kerberosv5状态码5230表C-10Kerberosv5状态码6231表C-11Kerberosv5状态码7232表F-1通用于客户机和服务器的SASL函数263表F-2仅限于客户机的基本SASL函数263表F-3基本的SASL服务器函数(客户机可选的)264表F-4用于配置基本服务的SASL函数264表F-5SASL实用程序函数264表F-6SASL属性函数264表F-7回调数据类型265表F-8SASL包含文件265表F-9SASL返回码:常规265表F-10SASL返回码:仅限客户机266表F-11SASL返回码:仅限服务器266表F-12SASL返回码-口令操作266表G-1使用C函数时的安全注意事项26712面向开发者的OracleSolaris11安全性指南2014年7月13示例例2-1超级用户特权包围示例27例2-2最小特权包围示例28例2-3检查授权32例3-1PAM使用者应用程序样例39例3-2PAM对话函数42例3-3PAM服务模块样例48例4-1在GSS-API中使用字符串56例4-2使用gss_import_name(57例4-3OID结构64例4-4OID集合结构64例5-1gss-client示例:main(91例5-2connect_to_server()函数92例5-3client_establish_context()-转换服务名称94例5-4用于建立上下文的循环96例5-5gss-client:call_server()建立上下文98例5-6gss-client示例:call_server()-包装消息99例5-7gss-client示例-读取和验证签名块101例5-8gss-client示例:call_server()-删除上下文102例6-1gss-server示例:main(104例6-2server_acquire_creds()函数的样例代码108例6-3sign_server()函数110例6-4server_establish_context()函数112例6-5test_import_export_context(117例9-1使用PKCS#11函数创建消息摘要156例9-2使用PKCS#11函数创建加密密钥对象159例9-3使用PKCS#11函数对文本进行签名和验证163例9-4使用PKCS#11函数生成随机数169例B-1gss-client.
c样例程序的完整列表187例B-2gss-server.
c样例程序的完整代码列表198例B-3各种GSS-API函数的代码列表208示例14面向开发者的OracleSolaris11安全性指南2014年7月例C-1使用gss_display_status()显示状态码220例D-1/etc/gss/mech文件235例D-2/etc/gss/qop文件236例D-3createMechOid()函数238例D-4parse_oid()函数239使用此文档15使用此文档概述-《面向开发者的OracleSolaris11安全性指南》介绍了用于OracleSolaris操作环境中的安全功能的公共应用编程接口(applicationprogramminginterface,API)和服务提供者接口(serviceproviderinterface,SPI).
术语服务提供者指插入框架以提供安全服务的组件,如加密算法和安全协议.
目标读者-《面向开发者的OracleSolaris11安全性指南》适用于编写以下类型程序的C语言开发者:可以覆盖系统控制的特权应用程序使用验证和相关安全服务的应用程序需要保护网络通信的应用程序使用加密服务的应用程序提供或使用安全服务的库、共享目标文件和插件注-有关OracleSolaris功能的java语言等效对象,请参见http://www.
oracle.
com/technetwork/java/javase/tech/index-jsp-136007.
html.
必备知识-本指南的读者应熟悉C编程.
了解安全机制的基础知识是有帮助的,但不是必需的.
使用本书无需网络编程方面的专业知识.
产品文档库有关本产品的最新信息和已知问题均包含在文档库中,网址为:http://www.
oracle.
com/pls/topic/lookupctx=E36784.
获得Oracle支持Oracle客户可通过MyOracleSupport获得电子支持.
有关信息,请访问http://www.
oracle.
com/pls/topic/lookupctx=acc&id=info;如果您听力受损,请访问http://www.
oracle.
com/pls/topic/lookupctx=acc&id=trs.
反馈16面向开发者的OracleSolaris11安全性指南2014年7月反馈可以在http://www.
oracle.
com/goto/docfeedback上提供有关此文档的反馈.
第1章面向开发者的OracleSolaris安全(概述)171第1章面向开发者的OracleSolaris安全(概述)该手册介绍了用于OracleSolaris操作系统(OracleSolarisOS)中安全功能的公共应用编程接口(applicationprogramminginterface,API)和服务提供者接口(serviceproviderinterface,SPI).
本章介绍以下两个方面的内容:"系统安全"[17]"网络安全体系结构"[20]面向开发者的OracleSolaris安全功能概述本手册介绍了OracleSolaris操作系统中安全功能的公共API和公共SPI.
有关如何从系统管理员的角度执行这些安全功能的信息,请参见《SecuringUsersandProcessesinOracleSolaris11.
2》.
OracleSolarisOS提供了一个基于标准行业接口的网络安全体系结构.
通过使用标准化的接口,应当无需随着安全技术的演变对使用或提供加密服务的应用程序进行任何修改.
系统安全为了实现系统安全,OracleSolarisOS提供了进程特权.
进程特权是基于超级用户的UNIX标准模型的替换模型,可用来授予对特权应用程序的访问权限.
系统管理员需要为用户指定一组允许其访问特权应用程序的进程特权.
用户不必成为超级用户即可使用特权应用程序.
特权使得系统管理员可以将有限的权限委托给用户而不是授予用户完全的root用户权限,以忽略系统安全.
相应地,创建新特权应用程序的开发者应该测试特定的特权,而不是检查UID是否等于0.
请参见第2章开发特权应用程序.
为了实现非常严格的系统安全,OracleSolarisOS提供了TrustedExtensions功能,该功能不在本书的讲述范围内.
TrustedExtensions功能允许系统管理员指定能够由特定系统安全18面向开发者的OracleSolaris11安全性指南2014年7月用户访问的应用程序和文件.
有关更多信息,请参见《TrustedExtensionsDeveloper'sGuide》和《TrustedExtensionsUser'sGuide》.
为安全起见,OracleSolaris提供了以下公共接口:加密框架-加密框架是OracleSolarisOS中加密服务的主干.
该框架提供了标准的扩展PKCS#11v2.
20修订3库(今后称为PKCS#11)、适用于加密服务使用者和提供者的接口.
该框架有两个部分:用户级应用程序的用户加密框架和内核级模块的内核加密框架.
连接到框架的使用者无需了解有关所安装加密机制的专业知识.
插入到框架中的提供者不包括针对不同类型使用者的特殊代码.
加密框架的使用者包括安全协议、某些机制和需要执行加密的应用程序.
框架的提供者可以是加密机制,也可以是硬件和软件插件中的其他机制.
有关加密框架的概述,请参见第8章OracleSolaris加密框架介绍.
要了解如何编写从框架使用服务的用户级应用程序,请参见第9章编写用户级加密应用程序.
加密框架的库是基于RSAPKCS#11规范实现的.
使用者和提供者都可以通过标准的PKCS#11调用来与用户级加密框架进行通信.
JavaAPI-Java安全技术包含大量的API、工具以及常用安全算法、机制和协议的实现.
Java安全API跨越多个领域,包括加密、公钥基础结构、安全通信、验证和访问控制.
Java安全技术为开发者提供了一个全面的用于编写应用程序的安全框架,还为用户或管理员提供了一组用于安全管理应用程序的工具.
请参见http://www.
oracle.
com/technetwork/java/javase/tech/index-jsp-136007.
html.
地址空间布局随机化(AddressSpaceLayoutRandomization,ASLR)ASLR是OracleSolaris系统的一种功能,利用此功能可以随机生成进程地址空间(例如栈、库和基于brk的堆)的关键部分的起始地址.
缺省情况下,为显式标记为需要ASLR的二进制文件启用ASLR.
以下命令提供有关ASLR状态的信息:%sxadminfoEXTENSIONSTATUSCONFIGURATIONaslrenable(tagged-files)enable(tagged-files)ld(1)命令的-z选项用于标记需要使用ASLR的新建目标文件.
用法如下所示:ld-zaslr[=mode]其中mode可设置为enable或disable.
如果未指定mode,则假定使用enable.
以下示例说明了如何使用-z选项创建启用了ASLR的可执行文件:%cathello.
c#includeintmain(intargc,char**argv){系统安全第1章面向开发者的OracleSolaris安全(概述)19(void)printf("HelloWorld!
\n");return(0);}%cchello.
c-zaslrASLR标记由目标文件动态部分中的条目提供,该条目可以使用elfdump(1)进行检查.
%elfdump-da.
out|grepASLR[28]SUNW_ASLR0x2ENABLEelfedit(1)命令可用于在现有目标文件中添加或修改ASLR动态条目.
%cchello.
c%elfedit-e'dyn:sunw_aslrenable'a.
out%elfdump-da.
out|grepASLR[29]SUNW_ASLR0x2ENABLE%elfedit-e'dyn:sunw_aslrdisable'a.
out%elfdump-da.
out|grepASLR[29]SUNW_ASLR0x1DISABLE给定进程对ASLR的要求是在该进程启动时建立的,该进程一旦启动就无法对此要求进行修改.
因此,ASLR标记仅对进程中的主要可执行目标文件有意义.
pmap(1)实用程序可用于检查进程的地址映射.
使用该实用程序观察启用了ASLR的可执行文件的映射时,会看到用于栈、库映射和基于brk的堆的具体地址在每次调用时均不同.
sxadm(1)命令用于控制系统的ASLR缺省行为.
显式标记为禁用ASLR的二进制文件优先于sxadm建立的系统缺省行为.
调试和ASLR调试期间,地址空间随机化可能会出现问题.
某些调试需要重复调用同一程序时使用相同的地址映射.
您可以采用以下方式之一禁用ASLR:临时在系统范围内禁用ASLR%sxadmexec-saslr=disable/bin/bash使用ld或elfedit命令将相关二进制文件标记为禁用ASLR建立禁用ASLR的shell,从中执行调试操作%sxadmexec-saslr=disable/bin/bash注-此ASLR修改不会应用到SUID或特权二进制文件.
网络安全体系结构20面向开发者的OracleSolaris11安全性指南2014年7月有关更多信息,请参见sxadm(1M)手册页和Chapter2,"ConfiguringOracleSolarisSecurity,"in《OracleSolaris11SecurityGuidelines》.
网络安全体系结构网络安全体系结构使用行业标准接口,如PAM、GSS-API、SASL和RSASecurityInc.
的PKCS#11加密令牌接口(CryptographicTokenInterface,Cryptoki).
通过使用标准化的协议和接口,开发者可以编写无需随安全技术演变而进行修改的使用者和提供者.

使用安全服务的应用程序、库或内核模块称作使用者.
向使用者提供安全服务的应用程序称作提供者,或称作插件.
用来实现加密操作的软件称作机制.
机制不只是算法,还包括算法的应用方式.
例如,将DES算法应用于验证功能是一种机制,而将DES应用于逐块加密的数据保护功能是另外一种机制.
使用网络安全体系结构,使用者的开发者将不再需要编写、维护和优化加密算法.
可以将经过优化的加密机制作为体系结构的一部分提供.
OracleSolarisOS提供了以下几个公共安全接口:PAM-可插拔验证模块(Pluggableauthenticationmodule).
PAM模块主要用于系统对用户进行初始验证.
用户可以通过GUI、命令行或其他方法进入系统.
除了验证服务以外,PAM还提供用来管理帐户、会话和口令的服务.
诸如login、rlogin和telnet之类的应用程序便是典型的PAM服务使用者.
PAMSPI服务由安全提供者(如Kerberosv5)提供.
请参见第3章编写PAM应用程序和服务.
GSS-API-通用安全服务应用编程接口(Genericsecurityserviceapplicationprograminterface).
GSS-API在对等应用程序之间提供安全通信.
GSS-API还提供验证、完整性和保密性服务.
在OracleSolaris中实现的GSS-API可以使用Kerberosv5、SPNEGO和Diffie-Hellman加密.
GSS-API主要用于设计或实现安全的应用程序协议.
GSS-API可以向其他类型的协议(如SASL)提供服务.
GSS-API通过SASL来向LDAP提供服务.
GSS-API通常由两个对等应用程序在最初建立了凭证之后通过网络进行通信时使用.
GSS-API由登录应用程序NFS、ftp在其他应用程序之间使用.
有关GSS-API的介绍,请参见第4章编写使用GSS-API的应用程序.
第5章GSS-API客户机示例和第6章GSS-API服务器示例介绍了两个典型GSS-API应用程序的源代码.
附录B,基于C的GSS-API样例程序介绍了GSS-API示例的代码列表.
附录C,GSS-API参考信息提供了有关GSS-API的参考信息.
附录D,指定OID介绍了如何指定缺省机制之外的机制.
SASL-简单验证和安全层(Simpleauthenticationandsecuritylayer).
SASL主要由协议使用,用来进行验证并提供保密性和数据完整性服务.
SASL适用于基于网络的较高级别的应用程序,这些应用程序使用动态安全协商机制来保护会话.
LDAP是SASL的一个众所周知的使用者.
SASL与GSS-API相似,但SASL的级别比GSS-API略高一些.
SASL使用GSS-API服务.
请参见第7章编写使用SASL的应用程序.
第2章开发特权应用程序212第2章开发特权应用程序本章说明如何开发特权应用程序.
本章涵盖以下主题:"特权应用程序"[21]"关于特权"[22]"使用特权进行编程"[24]"关于授权"[31]特权应用程序特权应用程序是可以覆盖系统控制并检查特定用户ID(userID,UID)、组ID(groupID,GID)、授权或特权的应用程序.
这些访问控制元素是由系统管理员指定的.
有关管理员如何使用这些访问控制元素的概要讨论,请参见"AssigningRightstoUsersandRoles"in《SecuringUsersandProcessesinOracleSolaris11.
2》.
OracleSolaris操作系统为开发者提供了两个元素,使用这两个元素可以授予更为精细的特权:特权-特权是一项可授予给应用程序的独立的权限.
被授予特权后,一个进程可以执行原本会被OracleSolarisOS禁止的操作.
例如,没有正确的文件权限,进程通常无法打开数据文件.
file_dac_read特权可向进程提供覆盖UNIX文件和读取文件的权限.
在内核级别执行操作必须具备特权.
授权-授权是可以执行某一类操作的权限,如果不具备授权,则安全策略会禁止这类操作.
授权可指定给某个角色或某位用户.
在用户级别执行操作必须具备授权.

授权和特权之间的区别在于其级别,在某个级别允许谁可以执行某项操作的策略.
在内核级别执行操作必须具备特权.
如果不具备正确的特权,某项进程无法在特权应用程序中执行特定的操作.
授权在用户应用程序级别强制执行策略.
可能需要具备某项特权才能访问某个特权应用程序或者才能在特权应用程序中执行特定的操作.

关于特权22面向开发者的OracleSolaris11安全性指南2014年7月关于特权特权是授予某个进程的独立权限,允许该进程执行某项不具备该特权就会被OracleSolaris操作系统禁止的操作.
大部分程序不使用特权,因为程序通常在系统安全策略的界限内执行操作.
特权由管理员进行指定.
将根据程序的设计启用特权.
登录或进入某配置文件shell时,管理员所具备的特权指定适用于在该shell中执行的所有命令.
应用程序运行后,将以编程方式打开或关闭特权.
如果使用exec(1)命令启动了一项新程序,则该程序可能可以使用所有可从父进程继承的特权.
但是,该程序不能新增任何特权.

管理员如何指定特权系统管理员负责将特权指定给各个命令.
有关特权分配的更多信息,请参见"MoreAboutPrivileges"in《SecuringUsersandProcessesinOracleSolaris11.
2》.
如何实现特权每项进程具有4组特权,这些特权决定了某项进程是否可以使用某个特定的特权:允许特权集合可继承特权集合有限特权集合有效特权集合允许特权集合进程可能会使用到的所有特权都必须包含在允许特权集合中.
相反,从来不会用到的特权应从该程序的允许特权集合中排除.
进程启动后,该进程会从其父进程中集成允许特权集合.
通常在登录时或在新的配置文件shell中,所有特权都包含在初始的允许特权集合中.
该集合中的特权是由管理员指定的.
每项子进程都可以从该允许特权集合中删除特权,但是子进程无法向允许特权集合中添加其他特权.
作为一个安全事项,您应该将程序从不使用的特权从允许特权集合中删除.
这样,可以通过避免使用指定有误的或继承有误的特权来保护程序.

从允许特权集合中删除的特权也会从有效特权集合中自动删除.
关于特权第2章开发特权应用程序23可继承特权集合在登录时或在新的配置文件shell中,可继承特权集合包含已由管理员指定的特权.
调用exec(1)后,这些特权可能会传递到子进程中.
进程应删除不必要的特权来避免将这些特权传递到子进程中.
允许特权集合通常与可继承特权集合相同.
但是,在某些情况下,从可继承特权集合中删除的特权还保留在允许特权集合中.
有限特权集合通过有限特权集合,开发者可控制某个进程可以使用哪些特权或可将哪些特权传递到子进程.
子进程和子孙进程只能从有限特权集合中获取特权.
执行setuid(0)函数时,有限特权集合负责确定允许应用程序使用的特权.
在执行exec(1)时,将强制使用限制特权集.
只有在执行exec(1)后,从有限特权集合中删除特权的操作才会影响其他集合.
有效特权集合进程实际可以使用的特权位于该进程的有效特权集合中.
启动程序时,有效特权集合与允许特权集合是等同的.
然后,有效特权集合既不是允许特权集合的子集也不与其等同.
将有效特权集合减少到基本特权是一个好的做法.
基本特权集合包含核心特权,"特权类别"[24]对其进行了说明.
将程序不需要的所有特权完全删除.
将所有不需要的基本特权切换为禁用,直到程序需要该进程.
例如,file_dac_read特权,可以使所有文件被读取.
一个程序可以具备多个读取文件的例程.
程序起初将所有特权关闭,然后会打开file_dac_read特权执行相应的读取例程.
这样,开发者就可以确保程序不会执行file_dac_read特权执行错误的读取例程.
此做法称为特权包围.
"特权编码示例"[27]对特权包围进行了演示.
超级用户与特权模型之间的兼容性为适应原有应用程序,特权的实施要同时与超级用户和特权模型兼容.
该适应过程是通过使用PRIV_AWARE标志实现的,该标志表明程序与特权兼容.
PRIV_AWARE标志由操作系统自动处理.
请考虑不能识别特权的子进程.
该进程将不具有PRIV_AWARE标志.
从父进程继承来的所有特权在允许特权集合和有效特权集合中都是可用的.
如果该子进程将UID设置为0,则该进程的有效特权集合和允许特权集合被限制为有效特权集合中的那些特权.
子进程无法获取超级用户的完整权限.
这样,感知特权进程的有限特权集合将对所有未感知特权的子进程限制超级用户特权.
如果子进程对任何特权集合进行了修改,则该进程的PRIV_AWARE标志将设置为true.
使用特权进行编程24面向开发者的OracleSolaris11安全性指南2014年7月特权类别特权是根据其作用域进行逻辑分组的,如下所示:基本特权-基本特权是授予进程(在之前的OracleSolaris发行版中未被授予特权)的特权.
缺省情况下,为每个进程和每个用户都分配了所有基本特权;但是,可以取消这些特权以进一步限制进程.
PRIV_FILE_LINK_ANY-允许进程创建硬链接,指向由某UID所有(而非该进程有效UID所有)的文件.
PRIV_PROC_EXEC-允许进程调用execve().
PRIV_PROC_FORK-允许进程调用fork()、fork1()或vfork().
PRIV_PROC_SESSION-允许进程发送信号或跟踪其会话外的进程.
PRIV_PROC_INFO-允许进程检查特定进程(查询进程可以将信号发送给这些进程)以外的进程的状态.
没有此特权,就无法检查在/proc中不能看到的进程.
PRIV_FILE_READ-允许进程读取文件系统中的对象.
PRIV_FILE_WRITE-允许进程修改文件系统中的对象.
PRIV_NET_ACCESS-允许进程打开TCP、UDP、SDP或SCTP网络端点.
最初,对于某个程序,应该将基本特权作为集合来指定,而不是分别指定.
此方法将确保指定中包括OracleSolarisOS更新版本中发布的所有基本特权.
但是,在计算程序的所需特权集时,一定要删除不需要的基本特权并添加程序所需的其他特权.
例如,如果程序不适用于exec(1)子进程,则禁用proc_exec特权.
文件系统特权.
系统V进程间通信(InterprocessCommunication,IPC)特权.
网络特权.
进程特权.
系统特权.
有关OracleSolaris特权的完整列表及其说明,请参见privileges(5)手册页.
注-OracleSolaris提供区域功能,通过此功能,管理员可以为运行的应用程序设置隔离环境.
有关更多信息,请参见zones(5).
由于一个区域中的进程无法监视或干扰该区域外系统中的其他活动,因此该进程的所有特权也限于该区域.
但是,如果需要,PRIV_PROC_ZONE特权可应用到全局区域中需要特权才能在非全局区域中运行的进程.
使用特权进行编程本节讨论使用特权的接口.
要使用特权编程接口,需要以下头文件.
使用特权进行编程第2章开发特权应用程序25#include本节还提供了说明如何在特权应用程序中使用特权接口的示例.
特权数据类型以下是特权接口使用的主要数据类型:特权类型-单个特权由priv_t类型定义表示.
可以使用下列方式使用特权ID字符串初始化priv_t类型的变量:priv_tpriv_id=PRIV_FILE_DAC_WRITE;特权集合类型-特权集合由priv_set_t数据结构表示.
请使用表2-1"使用特权的接口"中列出的一个特权处理函数初始化priv_set_t类型的变量.
特权操作类型-对文件或进程特权集合执行的操作类型由priv_op_t类型定义表示.
并不是所有的操作对每种类型的特权集合都有效.
有关详细信息,请阅读"使用特权进行编程"[24]中的特权集合说明.
特权操作可以具有下列各值:PRIV_ON-在指定的文件或进程特权集合中启用在priv_set_t结构中声明的特权.
PRIV_OFF-在指定的文件或进程特权集合中禁用在priv_set_t结构中声明的特权.
PRIV_SET-将指定文件或进程特权集合中的特权设置为在priv_set_t结构中声明的特权.
如果将该结构初始化为空,则PRIV_SET会将特权集合设置为none.
特权接口下表列出了使用特权的接口.
表后面提供了一些主要特权接口的说明.
表2-1使用特权的接口用途函数其他注释获取和设置特权集合setppriv(2)、getppriv(2)、priv_set(3C)、priv_ineffect(3C)setppriv()和getppriv()是系统调用.
priv_ineffect()和priv_set()是为方便而使用的包装函数.
识别和转换特权priv_str_to_set(3C)、priv_set_to_str(3C)、priv_getbyname(3C)、priv_getbynum(3C)、priv_getsetbyname(3C)、priv_getsetbynum(3C)这些函数将指定的特权或特权集合映射到名称或编号.
使用特权进行编程26面向开发者的OracleSolaris11安全性指南2014年7月用途函数其他注释处理特权集合priv_allocset(3C)、priv_freeset(3C)、priv_emptyset(3C)、priv_fillset(3C)、priv_isemptyset(3C)、priv_isfullset(3C)、priv_isequalset(3C)、priv_issubset(3C)、priv_intersect(3C)、priv_union(3C)、priv_inverse(3C)、priv_addset(3C)、priv_copyset(3C)、priv_delset(3C)、priv_ismember(3C)、priv_basicset(3C)这些函数与特权内存分配、测试和各种设置操作有关.
获取和设置进程标志getpflags(2)、setpflags(2)PRIV_AWARE进程标志指示进程是否了解特权或是否在超级用户模型下运行.
PRIV_DEBUG用于特权调试.
低级凭证处理ucred_get(3C)这些例程用于调试、底层系统调用和内核调用.
setppriv():用于设置特权用于设置特权的主要函数为setppriv(),该函数具有以下语法:intsetppriv(priv_op_top,priv_ptype_twhich,\constpriv_set_t*set);op表示要执行的特权操作.
op参数具有以下三个可能值之一:PRIV_ON-将set变量指定的特权添加到which指定的特权集合类型中PRIV_OFF-从which指定的特权集合类型中删除set变量指定的特权PRIV_SET-使用set变量指定的特权替换which指定的特权集合类型中的特权which用于指定要更改的特权集合类型:PRIV_PERMITTEDPRIV_EFFECTIVEPRIV_INHERITABLEPRIV_LIMITset指定要在更改操作中使用的特权.
此外,还提供了便利函数:priv_set().
使用特权进行编程第2章开发特权应用程序27用于映射特权的priv_str_to_set()这些函数便于使用其数值映射特权名称.
priv_str_to_set()是此系列中的典型函数.
priv_str_to_set()具有以下语法:priv_set_t*priv_str_to_set(constchar*buf,constchar*set,\constchar**endptr);priv_str_to_set()采用buf中指定的特权名字符串.
priv_str_to_set()返回可以与四个特权集合之一组合的一组特权值.
**endptr可用于调试解析错误.
请注意,可以在buf中包括以下关键字:"all"表示所有已定义的特权.
使用"all,!
priv_name,.
.
.
"可以指定除指示特权以外的所有特权.
注-使用"priv_set,"!
priv_name,.
.
.
"的构造将从指定的特权集合中删除指定的特权.
如果事先没有指定集合,请不要使用"!
priv_name,.
.
.
",因为如果没有从中删除特权的特权集合,该构造将从空的特权集合中删除指定的特权,并有效指示无特权.

"none"表示无特权.
"basic"表示执行登录标准UNIX操作系统的所有用户一般都可以执行的操作所需的特权集合.
特权编码示例本节对使用超级用户模型和最小特权模型包围特权的方式进行比较.
包围在超级用户模型中的特权以下示例说明如何在超级用户模型中包围特权操作.
例2-1超级用户特权包围示例/*Programstart*/uid=getuid();seteuid(uid);/*Privilegebracketing*/seteuid(0);/*Coderequiringsuperusercapability*/.
.
.
/*Endofcoderequiringsuperusercapability*/seteuid(uid);使用特权进行编程28面向开发者的OracleSolaris11安全性指南2014年7月.
.
.
/*Giveupsuperuserabilitypermanently*/setreuid(uid,uid);包围在最小特权模型中的特权此示例说明如何在最小特权模型中包围特权操作.
此示例使用以下假定:该程序为setuid0.
由于setuid0,允许集合和有效集合最初设置为所有特权.
可继承集合最初设置为基本特权.
有限集合最初设置为所有特权.
代码后面是该示例的说明.
注-此示例的源代码也可以通过Oracle下载中心获取.
请参见http://www.
oracle.
com/technetwork/indexes/downloads/sdlc-decommission-333274.
html.
例2-2最小特权包围示例1#include2/*Alwaysusethebasicset.
TheBasicsetmightgrowinfuture3*releasesandpotentiallyretrictactionsthatarecurrently4*unrestricted*/5priv_set_t*temp=priv_str_to_set("basic",",",NULL);6/*PRIV_FILE_DAC_READisneededinthisexample*/7(void)priv_addset(temp,PRIV_FILE_DAC_READ);8/*PRIV_PROC_EXECisnolongerneededafterprogramstarts*/9(void)priv_delset(temp,PRIV_PROC_EXEC);10/*Computethesetofprivilegesthatareneverneeded*/11priv_inverse(temp);12/*RemovethesetofunneededprivsfromPermitted(andby13*implicationfromEffective)*/14(void)setppriv(PRIV_OFF,PRIV_PERMITTED,temp);15/*RemoveunneededprivsetfromLimittobesafe*/16(void)setppriv(PRIV_OFF,PRIV_LIMIT,temp);17/*Donewithtemp*/18priv_freeset(temp);19/*Nowgetridoftheeuidthatbroughtusextraprivs*/20(void)seteuid(getuid());21/*TogglePRIV_FILE_DAC_READoffwhileitisunneeded*/使用特权进行编程第2章开发特权应用程序2922priv_set(PRIV_OFF,PRIV_EFFECTIVE,PRIV_FILE_DAC_READ,NULL);23/*TogglePRIV_FILE_DAC_READonwhenspecialprivilegeisneeded*/24priv_set(PRIV_ON,PRIV_EFFECTIVE,PRIV_FILE_DAC_READ,NULL);25fd=open("/some/retricted/file",O_RDONLY);26/*TogglePRIV_FILE_DAC_READoffafterithasbeenused*/27priv_set(PRIV_OFF,PRIV_EFFECTIVE,PRIV_FILE_DAC_READ,NULL);28/*RemovePRIV_FILE_DAC_READwhenitisnolongerneeded*/29priv_set(PRIV_OFF,PRIV_ALLSETS,PRIV_FILE_DAC_READ,NULL);该程序定义了名为temp的变量.
temp变量确定此程序不需要的特权集合.
最初,在第5行将temp定义为包含基本特权集合.
在第7行将file_dac_read特权添加到temp中.
proc_exec特权对exec(1)新进程(在此程序中不允许)是必需的.
因此,在第9行中从temp中删除了proc_exec,从而使exec(1)命令无法执行新进程.
此时,temp仅包含该程序所需的那些特权,即基本集合加上file_dac_read,再删除proc_exec.
在第11行中,priv_inverse()函数将计算temp的逆向值,并将temp的值重置为逆向值.
逆向值是从所有可能特权集合中删除指定集合(在本例中为temp)所得的结果.
作为第11行的结果,temp现在包含该程序永不使用的那些特权.
在第14行中,从允许集合中删除了temp定义的不需要的特权.
此删除操作还从有效集合中有效地删除了这些特权.
在第16行中,从有限集合中删除了不需要的特权.
在第18行中,因为不再需要temp,因而释放了temp变量.
该程序可以识别特权.
因此,该程序不使用setuid,但可以将有效的UID重置为第20行中的用户的实际UID.
在第22行中,通过从有效集合中删除file_dac_read特权禁用了该特权.
在实际的程序中,需要file_dac_read特权之前,还将发生其他活动.
在该样例程序中,读取第25行中的文件需要file_dac_read.
因此,在第24行中,启用了file_dac_read.
读取文件后,将再次从有效集合中立即删除file_dac_read.
读取所有文件后,通过在所有特权集合中禁用file_dac_read,可永久地删除file_dac_read.
下表说明了随着程序的运行如何转换特权集合.
已指出了行号.
表2-2特权集合转换步骤temp集合允许特权集合有效特权集合有限特权集合最初–所有所有所有第5行-将temp设置为基本特权基本所有所有所有第7行-将file_dac_read添加到temp中.
基本+file_dac_read所有所有所有第9行-从temp中删除了proc_exec.
基本+file_dac_read–proc_exec所有所有所有特权应用程序开发指南30面向开发者的OracleSolaris11安全性指南2014年7月步骤temp集合允许特权集合有效特权集合有限特权集合第11行-temp重置为逆向值.
所有-(基本+file_dac_read–proc_exec)所有所有所有第14行-在允许集合中禁用不需要的特权.
所有-(基本+file_dac_read–proc_exec)基本+file_dac_read–proc_exec基本+file_dac_read–proc_exec所有第16行-在有限集合中禁用不需要的特权.
所有-(基本+file_dac_read–proc_exec)基本+file_dac_read–proc_exec基本+file_dac_read–proc_exec基本+file_dac_read–proc_exec第18行-释放了temp文件.
–基本+file_dac_read–proc_exec基本+file_dac_read–proc_exec基本+file_dac_read–proc_exec第22行-禁用file_dac_read直到需要时再启用.
–基本-proc_exec基本-proc_exec基本+file_dac_read–proc_exec第24行-需要时启用file_dac_read.
–基本+file_dac_read–proc_exec基本+file_dac_read–proc_exec基本+file_dac_read–proc_exec第27行-执行read()操作后禁用file_dac_read.
–基本-proc_exec基本-proc_exec基本+file_dac_read–proc_exec第29行-不再需要file_dac_read时,从所有集合中删除该特权.
–基本-proc_exec基本-proc_exec基本-proc_exec特权应用程序开发指南本节为开发特权应用程序提供了以下建议:使用隔离系统.
绝不应在生产系统中调试特权应用程序,因为不完整的特权应用程序可能会危及安全.
正确设置ID.
调用进程的有效集合中需要具备proc_setid特权,以更改其用户ID、组ID或补充组ID.
使用特权包围.
应用程序使用特权时,将覆盖系统安全策略.
应该包围特权任务并仔细进行控制,以确保不会破坏敏感信息.
有关如何包围特权的信息,请参见"特权编码示例"[27].
使用基本特权启动.
基本特权是最基本的操作所必需的.
特权应用程序应该使用基本集合启动.
然后,该应用程序应该适当地删除和添加特权.
以下是典型的启动方案.
1.
守护进程以root身份启动.
2.
守护进程启用基本特权集合.
3.
守护进程禁用不必要的所有基本特权,例如PRIV_FILE_LINK_ANY.
关于授权第2章开发特权应用程序314.
守护进程添加需要的所有其他特权,例如PRIV_FILE_DAC_READ.
5.
守护进程切换到守护进程UID.
请避免shell转义序列.
shell转义序列中的新进程可以使用父进程的可继承集合中的任何特权.
因此,最终用户可以通过shell转义序列违反信任.
例如,某些邮件应用程序可能会将!
command行解释为命令并执行该行.
因而,最终用户可以创建脚本,以充分利用所有邮件应用程序特权.
建议删除不必要的shell转义序列.
关于授权授权存储在/etc/security/auth_attr文件中.
要创建使用授权的应用程序,请执行以下步骤:1.
使用getent命令扫描auth_attr数据库中的条目,如下所示:%getentauth_attr|sort|moregetent命令会检索auth_attr数据库中的授权列表,并对名称类似的授权一起进行排序.
将按照配置授权的顺序来检索授权.
有关使用getent命令的信息,请参见getent(1M)手册页.
2.
在程序开始时使用chkauthattr(3C)函数检查所需的授权.
chkauthattr()函数将在以下位置按顺序搜索授权:policy.
conf(4)数据库中的AUTHS_GRANTED键-AUTHS_GRANTED指示缺省情况下指定的授权.
policy.
conf(4)数据库中的PROFS_GRANTED键-PROFS_GRANTED指示缺省情况下指定的权限配置文件.
chkauthattr()将针对指定授权检查这些权限配置文件.
user_attr(4)数据库-此数据库存储为用户指定的安全属性.
prof_attr(4)数据库-此数据库存储为用户指定的权限配置文件.
chkauthattr()在上述任何位置中都找不到权限授权,则将拒绝用户访问该程序.
如果chkauthattr()函数遇到Stop配置文件,则将忽略其他授权和配置文件,包括AUTHS_GRANTED、PROFS_GRANTED以及在/etc/security/policy.
conf中找到的那些.
因此,可以使用Stop配置文件来覆盖使用/etc/security/policy.
conf文件中的PROFS_GRANTED和AUTHS_GRANTED密钥列出的配置文件.
有关如何使用提供的安全属性,添加新的安全属性以及将它们指定给用户和进程的信息,请参见Chapter3,"AssigningRightsinOracleSolaris,"in《SecuringUsersandProcessesinOracleSolaris11.
2》.
注-用户可以将条目添加到auth_attr()、exec_attr()和prof_attr()数据库.
但是,OracleSolaris授权并未存储在这些数据库中.
关于授权32面向开发者的OracleSolaris11安全性指南2014年7月例2-3检查授权以下代码段说明如何使用chkauthattr()函数检查用户的授权.
在本例中,该程序将检查solaris.
job.
admin授权.
如果用户具有此授权,则该用户可以读取或写入其他用户的文件.
如果没有此授权,则用户只能对其拥有的文件执行操作.
/*Defineoverrideprivileges*/priv_set_t*override_privs=priv_allocset();/*Clearprivilegesetbeforeaddingprivileges.
*/priv_set(PRIV_OFF,PRIV_EFFECTIVE,PRIV_FILE_DAC_READ,priv_FILE_DAC_WRITE,NULL);priv_addset(override_privs,PRIV_FILE_DAC_READ);priv_addset(override_privs,PRIV_FILE_DAC_WRITE);if(!
chkauthattr("solaris.
jobs.
admin",username)){/*turnoffprivileges*/setppriv(PRIV_OFF,PRIV_EFFECTIVE,override_privs);}/*Authorizeduserscontinuetorunwithprivileges*//*Otheruserscanreadorwritetotheirownfilesonly*/第3章编写PAM应用程序和服务333第3章编写PAM应用程序和服务可插拔验证模块(PluggableAuthenticationModule,PAM)为系统登录应用程序提供了验证和相关的安全服务.
本章适用于希望通过PAM模块提供验证、帐户管理、会话管理和口令管理的系统登录应用程序开发者.
此外,还为PAM服务模块的设计者提供了相关的信息.
本章包含以下主题:"PAM框架介绍"[33]"PAM配置"[36]"编写使用PAM服务的应用程序"[37]"编写提供PAM服务的模块"[46]PAM最初是由Oracle开发的.
自此以后PAM规范提交给了X/Open,即现在的"OpenGroup".
PAM规范可在《X/OpenSingleSign-OnService(XSSO)-PluggableAuthentication》(OpenGroup,英国出版,ISBN:1-85912-144-6,1997年6月)中找到.
pam(3PAM)、libpam(3LIB)和pam_sm(3PAM)手册页中介绍了OracleSolaris的PAM实现.
PAM框架介绍PAM框架由四个部分组成:PAM使用者PAM库pam.
conf(4)配置文件PAM服务模块,也称为提供者该框架为与验证相关的活动提供了统一的执行方式.
采用该方式,应用程序开发者不必了解策略的语义即可使用PAM服务.
算法是集中提供的.
可以独立于各个应用程序对算法进行修改.
借助PAM,管理员可以根据特定系统的需要调整验证过程,而不必更改任何应用程序.
通过pam.
conf、PAM配置文件或/etc/pam.
d文件进行调整,这些文件均可从OracleSolaris11.
1发行版以及更高发行版中获得.
下图说明了PAM体系结构.
应用程序通过PAM应用编程接口(ApplicationProgrammingInterface,API)与PAM库进行通信.
PAM模块通过PAM服务提供者接PAM框架介绍34面向开发者的OracleSolaris11安全性指南2014年7月口(ServiceProviderInterface,SPI)与PAM库进行通信.
通过这种方式,PAM库可使应用程序和模块相互进行通信.
图3-1PAM体系结构PAM服务模块PAM服务模块是一个共享库,用于为系统登录应用程序(如login、rlogin和telnet)提供验证和其他安全服务.
PAM服务的四种类型是:PAM框架介绍第3章编写PAM应用程序和服务35验证服务模块-用于授予用户访问帐户或服务的权限.
提供此服务的模块可以验证用户并设置用户凭证.
帐户管理模块-用于确定当前用户的帐户是否有效.
提供此服务的模块可以检查口令或帐户的失效期以及限时访问.
会话管理模块-用于设置和终止登录会话.
口令管理模块-用于强制实施口令强度规则并执行验证令牌更新.
PAM模块可以实现其中的一项或多项服务.
将简单模块用于明确定义的任务可以增加配置灵活性.
因此,应该在不同的模块中实现PAM服务.
然后,可以根据需要按照PAM配置中的定义使用这些服务.
请参见pam.
conf(4).
例如,OracleSolarisOS为系统管理员提供了用于配置站点口令策略的pam_authtok_check(5)模块.
pam_authtok_check(5)模块可以检查建议的口令是否符合各种强度条件.
有关OracleSolarisPAM模块的完整列表,请参见手册页第5节:标准、环境与宏.
PAM模块的前缀为pam_.
此发行版中对PAM模块的更改OracleSolaris11.
1发行版提供了新的PAM模块pam_user_policy(5),该模块添加了对每用户PAM配置的支持.
该模块调用pam_eval(3PAM)函数评估指定的PAM配置.
PAM库libpam(3LIB)中的pam_eval()例程也是OracleSolaris11.
1中新增的例程.
PAM库PAM库libpam(3LIB)是PAM体系结构中的中心元素:libpam可以导出APIpam(3PAM).
应用程序可以调用此API以执行验证、帐户管理、凭证建立、会话管理以及口令更改.
libpam在/etc/pam.
conf中查找PAM配置,或在/etc/pam.
d中查找每服务PAM策略文件.
PAM配置为每个可用服务指定PAM模块要求并由系统管理员管理.
libpam可以导入SPIpam_sm(3PAM),而导出则由服务模块完成.
PAM验证过程以使用者如何使用PAM库进行用户验证为例,考虑login如何验证用户:1.
login应用程序通过调用pam_start(3PAM)并指定login服务来启动PAM会话.
PAM配置36面向开发者的OracleSolaris11安全性指南2014年7月2.
该应用程序将调用pam_authenticate(3PAM),它是PAM库libpam(3LIB)导出的PAMAPI的一部分.
3.
PAM库在PAM配置中搜索与验证(auth)服务模块类型对应的login条目.
4.
对于pam.
conf中为login服务配置的每个模块,PAM库将调用pam_sm_authenticate(3PAM).
pam_sm_authenticate()函数是PAMSPI的一部分.
pam.
conf控制标志和每个调用的结果将确定是否允许用户访问系统.
在"ConfiguringPAM"in《ManagingKerberosandOtherAuthenticationServicesinOracleSolaris11.
2》中详细介绍了该过程.
通过此方式,PAM库可以将PAM应用程序与系统管理员已配置的PAM模块连接起来.
PAM使用者的要求PAM使用者必须与PAM库libpam链接.
应用程序使用模块提供的任何服务之前,必须通过调用pam_start(3PAM)初始化其PAM库的实例.
调用pam_start()可初始化必须传递给所有后续PAM调用的句柄.
应用程序完成使用PAM服务后,系统将调用pam_end()以清除PAM库已使用的任何数据.
PAM应用程序与PAM模块之间的通信是通过项进行的.
例如,以下各项有助于进行初始化:PAM_USER-当前验证的用户PAM_AUTHTOK-口令PAM_USER_PROMPT-用户名提示PAM_TTY-用户借此进行通信的终端PAM_RHOST-用户借此进入系统的远程主机PAM_REPOSITORY-对用户帐户系统信息库的任何限制PAM_RESOURCE-对资源的任何控制有关可用项的完整列表,请参见pam_set_item(3PAM).
应用程序可以通过pam_set_item(3PAM)对项进行设置.
应用程序可以通过pam_get_item(3PAM)检索模块已设置的值.
但是,应用程序不能检索PAM_AUTHTOK和PAM_OLDAUTHTOK.
无法设置PAM_SERVICE项.
注-PAM使用者必须拥有唯一的PAM服务名,该名称传递到pam_start(3PAM).
PAM配置/etc/pam.
conf中的PAM配置或/etc/pam.
d中的每服务策略文件用于为系统服务(例如login、rlogin、su和cron)配置PAM服务模块.
由系统管理员管理PAM配置.
如编写使用PAM服务的应用程序第3章编写PAM应用程序和服务37果/etc/pam.
conf中的条目顺序有误或/etc/pam.
d中的每服务策略文件顺序有误,可能导致无法预料的负面影响.
例如,如果pam.
conf配置错误,则会将多个用户锁定在外,这样必须采用单用户模式才能进行修复.
有关PAM配置的信息,请参见"ConfiguringPAM"in《ManagingKerberosandOtherAuthenticationServicesinOracleSolaris11.
2》.
通过/etc/pam.
d配置PAM从OracleSolaris11.
1发行版开始,除可通过pam.
conf文件配置PAM外,还可以通过/etc/pam.
d目录中的每服务PAM策略文件配置PAM.
/etc/pam.
d目录包含使用PAM_SERVICE的值命名的文件.
例如,/etc/pam.
d/telnet是从telnet服务读取的文件.
/etc/pam.
d文件的语法与/etc/pam.
conf的相同,但/etc/pam.
conf文件的第一列(服务名称)除外,/etc/pam.
d中省略了这一列.
使用/etc/pam.
d文件配置PAM具有以下优点:每服务PAM策略文件中的错误仅影响该服务.
添加新的PAM服务非常简单,因为只需要在/etc/pam.
d中创建文件.
由于许多其他PAM实现(例如Linux-PAM和OpenPAM)支持/etc/pam.
d,因此使用该文件可改善跨平台PAM应用程序的互操作性.
系统管理员还可以通过覆盖供应商提供的/etc/pam.
d文件来定制其站点的安全策略.
在搜索配置时,将遵循以下提供的顺序:1.
/etc/pam.
conf,搜索指定的服务条目2.
/etc/pam.
d/servicename3.
/etc/pam.
conf,搜索其他条目4.
/etc/pam.
d/other此搜索顺序能够确保在系统通过pkg(5)进行升级时会保留对/etc/pam.
conf文件所做的任何定制并确保策略仍然有效.
有关其他信息,请参见"ProcessRightsManagement"in《SecuringUsersandProcessesinOracleSolaris11.
2》.
编写使用PAM服务的应用程序本节提供了使用多个PAM函数的应用程序样例.
编写使用PAM服务的应用程序38面向开发者的OracleSolaris11安全性指南2014年7月简单PAM使用者示例以下将以PAM使用者应用程序作为示例.
该示例是一个基本的终端锁定应用程序,用于检验尝试访问终端的用户.
此示例执行以下步骤:1.
初始化PAM会话.
PAM会话通过调用pam_start(3PAM)函数来启动.
调用任何其他PAM函数之前,PAM使用者应用程序必须首先建立PAM会话.
pam_start(3PAM)函数采用以下参数:plock-服务名,即应用程序的名称.
PAM框架使用服务名称来确定配置文件、/etc/pam.
conf或/etc/pam.
d中的哪些规则可用.
服务名通常用于日志记录和错误报告.
pw->pw_name-用户名,即PAM框架所作用的用户的名称.
&conv-对话函数conv,用于提供PAM与用户或应用程序进行通信的通用方法.
对话函数是必需的,因为PAM模块无法了解如何进行通信.
通信可以采用GUI、命令行、智能读卡器或其他设备等方式进行.
有关更多信息,请参见"编写对话函数"[42].
&pamh-PAM句柄pamh,即PAM框架用于存储有关当前操作信息的不透明句柄.
成功调用pam_start()后将返回此句柄.
注-调用PAM接口的应用程序必须具有足够的特权才能执行任何所需的操作,如验证、口令更改、进程凭证处理或审计状态初始化.
在该示例中,应用程序必须可以读取/etc/shadow才能验证本地用户的口令.
2.
验证用户.
应用程序将调用pam_authenticate(3PAM)来验证当前用户.
通常,系统会要求用户输入口令或其他验证令牌,具体取决于验证服务的类型.
PAM框架会调用/etc/pam.
conf或/etc/pam.
d/plock(使用OracleSolaris11.
1OS时)中为服务名称plock(对应于验证服务模块类型(auth))配置的模块.
如果在/etc/pam.
conf或/etc/pam.
d/plock中不存在与plock服务对应的auth条目,则会在/etc/pam.
conf中搜索与other服务对应的auth条目,最后在/etc/pam.
d/other文件中进行搜索.
3.
检查帐户有效性.
该示例使用pam_acct_mgmt(3PAM)函数检查已验证的用户帐户的有效性.
在该示例中,pam_acct_mgmt()用于检查口令是否到期.
pam_acct_mgmt()函数还会使用PAM_DISALLOW_NULL_AUTHTOK标志.
如果pam_acct_mgmt()返回PAM_NEW_AUTHTOK_REQD,则应调用pam_chauthtok(3PAM)以允许已验证的用户更改口令.
编写使用PAM服务的应用程序第3章编写PAM应用程序和服务394.
如果系统发现口令已到期,则会强制用户更改口令.
该示例使用循环调用pam_chauthtok(),直到返回成功信息为止.
如果用户成功更改其验证信息(通常为口令),则pam_chauthtok()函数将返回成功信息.
在该示例中,循环将继续直到返回成功信息为止.
通常,应用程序会设置终止前应尝试的最多次数.
5.
调用pam_setcred(3PAM).
pam_setcred(3PAM)函数用于建立、修改或删除用户凭证.
pam_setcred()通常在验证用户之后进行调用.
调用是在检验帐户后和打开会话前进行的.
将pam_setcred()函数与PAM_ESTABLISH_CRED标志结合使用可建立新的用户会话.
如果该会话是对现有会话(如对于lockscreen)的更新,则应调用带有PAM_REFRESH_CRED标志的pam_setcred().
如果会话要更改凭证(如使用su或承担角色),则应调用带有PAM_REINITIALIZE_CRED标志的pam_setcred().
6.
关闭PAM会话.
PAM会话通过调用pam_end(3PAM)函数进行关闭.
pam_end()还将释放所有的PAM资源.
以下示例给出了PAM使用者应用程序样例的源代码.
注-此示例的源代码也可以通过Oracle下载中心获取.
请参见http://www.
oracle.
com/technetwork/indexes/downloads/sdlc-decommission-333274.
html.
例3-1PAM使用者应用程序样例/**Copyright(c)2005,2012,Oracleand/oritsaffiliates.
Allrightsreserved.
*/#include#include#include#include#include#include#include#include#includeexternintpam_tty_conv(intnum_msg,structpam_message**msg,structpam_response**response,void*appdata_ptr);/*Disablekeyboardinterrupts(Ctrl-C,Ctrl-Z,Ctrl-\)*/staticvoiddisable_kbd_signals(void)编写使用PAM服务的应用程序40面向开发者的OracleSolaris11安全性指南2014年7月{(void)signal(SIGINT,SIG_IGN);(void)signal(SIGTSTP,SIG_IGN);(void)signal(SIGQUIT,SIG_IGN);}/*Terminatecurrentusersession,i.
e.
,logout*/staticvoidlogout(){pid_tpgroup=getpgrp();(void)signal(SIGTERM,SIG_IGN);(void)fprintf(stderr,"Sorry,yoursessioncan'tberestored.
\n");(void)fprintf(stderr,"Pressreturntoterminatethissession.
\n");(void)getchar();(void)kill(-pgroup,SIGTERM);(void)sleep(2);(void)kill(-pgroup,SIGKILL);exit(-1);}int/*ARGSUSED*/main(intargc,char*argv){structpam_convconv={pam_tty_conv,NULL};pam_handle_t*pamh;structpasswd*pw;interr;disable_kbd_signals();if((pw=getpwuid(getuid()))==NULL){(void)fprintf(stderr,"plock:Can'tgetusername:%s\n",strerror(errno));exit(1);}/*InitializePAMframework*/err=pam_start("plock",pw->pw_name,&conv,&pamh);if(err!
=PAM_SUCCESS){(void)fprintf(stderr,"plock:pam_startfailed:%s\n",pam_strerror(pamh,err));exit(1);}/*Authenticateuserinordertounlockscreen*/do{(void)fprintf(stderr,"Terminallockedfor%s.
",pw->pw_name);err=pam_authenticate(pamh,0);if(err==PAM_USER_UNKNOWN){logout();}elseif(err!
=PAM_SUCCESS){(void)fprintf(stderr,"Invalidpassword.
\n");编写使用PAM服务的应用程序第3章编写PAM应用程序和服务41}}while(err!
=PAM_SUCCESS);/*Makesureaccountandpasswordarestillvalid*/switch(err=pam_acct_mgmt(pamh,0)){casePAM_SUCCESS:break;casePAM_USER_UNKNOWN:casePAM_ACCT_EXPIRED:/*Usernotallowedinanymore*/logout();break;casePAM_NEW_AUTHTOK_REQD:/*Theuser'spasswordhasexpired.
Getanewone*/do{err=pam_chauthtok(pamh,0);}while(err==PAM_AUTHTOK_ERR);if(err!
=PAM_SUCCESS)logout();break;default:logout();}if(pam_setcred(pamh,PAM_REFRESH_CRED)!
=PAM_SUCCESS){logout();}(void)pam_end(pamh,0);return(0);/*NOTREACHED*/}其他有用的PAM函数前面的例3-1"PAM使用者应用程序样例"是一个简单的应用程序,它仅说明了几个主要的PAM函数.
本节将介绍一些其他有用的PAM函数.
成功验证用户后,将会调用pam_open_session(3PAM)函数打开新会话.
调用pam_getenvlist(3PAM)函数可以建立新环境.
pam_getenvlist()将返回要与现有环境合并的新环境.
pam_eval(3PAM)函数装入并评估调用方指定的文件中存储的PAM配置.
此函数由pam_user_policy(5)PAM模块调用.
编写对话函数42面向开发者的OracleSolaris11安全性指南2014年7月编写对话函数PAM模块或应用程序可以采用多种方式与用户进行通信:命令行、对话框等.
因此,与用户通信的PAM使用者的设计者需要编写对话函数.
对话函数用于在用户与模块之间传递消息,而与通信方式无关.
对话函数可从对话函数回调pam_message参数中的msg_style参数派生消息类型.
请参见pam_start(3PAM).
开发者不应对PAM如何与用户进行通信做出假设.
而应用程序应该与用户交换消息,直到操作完成为止.
应用程序会显示与对话函数对应的消息字符串,但不进行解释或修改.
一条单独的消息中可以包含多行、控制字符或额外的空格.
请注意,服务模块负责本地化发送给对话函数的任何字符串.
下面提供了对话函数样例pam_tty_conv().
pam_tty_conv()采用以下参数:num_msg-传递给函数的消息数.
**mess-指向缓冲区的指针,该缓冲区包含来自用户的消息.
**resp-指向缓冲区的指针,该缓冲区包含对用户所做的响应.
*my_data-指向应用程序数据的指针.
函数样例从stdin获取用户输入.
例程需要为响应缓冲区分配内存.
可以设置最大值PAM_MAX_NUM_MSG以限制消息的数量.
如果对话函数返回错误,则对话函数会负责清除并释放为响应分配的任何内存.
此外,对话函数必须将响应指针设置为NULL.
请注意,应使用零填充方法来完成内存清除.
对话函数的调用者负责释放返回给它的所有响应.
要进行对话,该函数将循环处理来自用户应用程序的消息.
有效的消息将写入stdout,所有错误将写入stderr.
注-此示例的源代码也可以通过Oracle下载中心获取.
请参见http://www.
oracle.
com/technetwork/indexes/downloads/sdlc-decommission-333274.
html.
例3-2PAM对话函数/**Copyright(c)2005,2012,Oracleand/oritsaffiliates.
*Allrightsreserved.
#pragmaident"@(#)pam_tty_conv.
c1.
405/02/12SMI"#define__EXTENSIONS__/*toexposeflockfileandfriendsinstdio.
h*/#include#include#include编写对话函数第3章编写PAM应用程序和服务43#include#include#include#include#include#include#include#includestaticintctl_c;/*wastheconversationinterrupted*//*ARGSUSED1*/staticvoidinterrupt(intx){ctl_c=1;}/*getinput--readuserinputfromstdinaborton^C*Entrynoecho==TRUE,don'techoinput.
*ExitUser'sinput.
*Ifinterrupted,sendSIGINTtocallerforprocessing.
*/staticchar*getinput(intnoecho){structtermiotty;unsignedshorttty_flags;charinput[PAM_MAX_RESP_SIZE];intc;inti=0;void(*sig)(int);ctl_c=0;sig=signal(SIGINT,interrupt);if(noecho){(void)ioctl(fileno(stdin),TCGETA,&tty);tty_flags=tty.
c_lflag;tty.
c_lflag&=~(ECHO|ECHOE|ECHOK|ECHONL);(void)ioctl(fileno(stdin),TCSETAF,&tty);}/*gotoend,butdon'toverflowPAM_MAX_RESP_SIZE*/flockfile(stdin);while(ctl_c==0&&(c=getchar_unlocked(n'&&c!
='\r'&&c!
=EOF){if(iresp){/*clearbeforefreeing--maybeapassword*/bzero(r->resp,strlen(r->resp));free(r->resp);r->resp=NULL;}}free(pr);}/*ARGSUSED*/intpam_tty_conv(intnum_msg,structpam_message**mess,structpam_response**resp,void*my_data){structpam_message*m=*mess;structpam_response*r;inti;if(num_msg=PAM_MAX_NUM_MSG){(void)fprintf(stderr,"badnumberofmessages%d""=%d\n",num_msg,PAM_MAX_NUM_MSG);*resp=NULL;return(PAM_CONV_ERR);}if((*resp=r=calloc(num_msg,sizeof(structpam_response)))==NULL)编写对话函数第3章编写PAM应用程序和服务45return(PAM_BUF_ERR);errno=0;/*don'tpropogatepossibleEINTR*//*Loopthroughmessages*/for(i=0;imsg==NULL){(void)fprintf(stderr,"message[%d]:%d/NULL\n",i,m->msg_style);gotoerr;}/**fixupfinalnewline:*removedforprompts*addedbackformessages*/if(m->msg[strlen(m->msg)]=='\n')m->msg[strlen(m->msg)]='\0';r->resp=NULL;r->resp_retcode=0;echo_off=0;switch(m->msg_style){casePAM_PROMPT_ECHO_OFF:echo_off=1;/*FALLTHROUGH*/casePAM_PROMPT_ECHO_ON:(void)fputs(m->msg,stdout);r->resp=getinput(echo_off);break;casePAM_ERROR_MSG:(void)fputs(m->msg,stderr);(void)fputc('\n',stderr);break;casePAM_TEXT_INFO:(void)fputs(m->msg,stdout);(void)fputc('\n',stdout);break;default:(void)fprintf(stderr,"message[%d]:unknowntype""%d/val=\"%s\"\n",i,m->msg_style,m->msg);编写提供PAM服务的模块46面向开发者的OracleSolaris11安全性指南2014年7月/*error,servicemodulewon'tcleanup*/gotoerr;}if(errno==EINTR)gotoerr;/*nextmessage/response*/m++;r++;}return(PAM_SUCCESS);err:free_resp(i,r);*resp=NULL;return(PAM_CONV_ERR);}编写提供PAM服务的模块本节介绍如何编写PAM服务模块.
PAM服务提供者要求PAM服务模块使用pam_get_item(3PAM)和pam_set_item(3PAM)与应用程序进行通信.
要相互进行通信,服务模块需要使用pam_get_data(3PAM)和pam_set_data(3PAM).
如果同一项目的服务模块需要交换数据,则应建立该项目的唯一数据名称.
然后,服务模块即可通过pam_get_data()和pam_set_data()函数共享此数据.
服务模块必须返回以下三类PAM返回码之一:如果模块在所请求的策略中做出了明确决定,则返回PAM_SUCCESS.
如果模块未做出策略决定,则返回PAM_IGNORE.
如果模块参与的决定导致失败,则返回PAM_error.
error可以是常规错误代码或特定于服务模块类型的代码.
错误不能是其他服务模块类型的错误代码.
有关错误代码,请参见特定的pam_sm_module-type手册页.
如果服务模块执行多个函数,则应将这些函数分成单独的模块.
使用此方法,系统管理员可对策略配置进行更为精细的控制.
应该为任何新的服务模块提供手册页.
手册页应该包括以下各项:模块接受的参数.
模块实现的所有函数.
标志对算法的影响.
编写提供PAM服务的模块第3章编写PAM应用程序和服务47任何所需的PAM项.
特定于此模块的错误返回信息.
服务模块必须支持PAM_SILENT标志,以防止显示消息.
建议使用debug参数将调试信息记录到syslog中.
请将syslog(3C)与LOG_AUTH和LOG_DEBUG结合使用来记录调试.
其他消息应发送到具有LOG_AUTH和相应优先级的syslog().
决不能使用openlog(3C)、closelog(3C)和setlogmask(3C),因为这些函数会干扰应用程序设置.
PAM提供者服务模块样例以下是一个PAM服务模块示例.
此示例将查看用户是否是允许访问此服务的组中的成员.
如果成功,则提供者随后将授予访问权限,如果失败则记录错误消息.

此示例执行以下步骤:1.
解析从PAM配置传递给此模块的选项.
请参见pam.
conf(4).
此模块可接受nowarn和debug选项以及特定的选项group.
使用group选项可以配置模块,允许其访问除缺省情况下使用的root组以外的特定组.
有关示例,请参见源代码中DEFAULT_GROUP的定义.
例如,要允许属于组staff的用户访问telnet(1),用户可以使用以下行(位于/etc/pam.
conf中的telnet栈中):telnetaccountrequiredpam_members_only.
so.
1group=staff等效的/etc/pam.
d配置在/etc/pam.
d/telnet中可能具有以下行:accountrequiredpam_members_only.
so.
1group=staff2.
获取用户名、服务名和主机名.
用户名通过调用pam_get_user(3PAM)(用于从PAM句柄中检索当前用户名)获取.
如果未设置用户名,则会拒绝访问.
服务名和主机名通过调用pam_get_item(3PAM)获取.
3.
验证要使用的信息.
如果未设置用户名,则拒绝访问.
如果未定义要使用的组,则拒绝访问.
4.
检验当前用户是否是允许访问此主机并且授予访问权限的特殊组的成员.

如果该特殊组已定义但根本不包含任何成员,则将返回PAM_IGNORE,表示此模块不参与任何帐户验证过程.
该决策将留给栈中的其他模块.
5.
如果用户不是特殊组的成员,则会显示一条消息,通知用户访问被拒绝.
记录消息以记录此事件.
以下示例给出了PAM提供者样例的源代码.
注-此示例的源代码也可以通过Oracle下载中心获取.
请参见http://www.
oracle.
com/technetwork/indexes/downloads/sdlc-decommission-333274.
html.
编写提供PAM服务的模块48面向开发者的OracleSolaris11安全性指南2014年7月例3-3PAM服务模块样例/**Copyright(c)2005,2012,Oracleand/oritsaffiliates.
Allrightsreserved.
*/#include#include#include#include#include#include#include/**bydefault,onlyuserswhoareamemberofgroup"root"areallowedaccess*/#defineDEFAULT_GROUP"root"staticchar*NOMSG="Sorry,youarenotontheaccesslistforthishost-accessdenied.
";intpam_sm_acct_mgmt(pam_handle_t*pamh,intflags,intargc,constchar**argv){char*user=NULL;char*host=NULL;char*service=NULL;constchar*allowed_grp=DEFAULT_GROUP;chargrp_buf[4096];structgroupgrp;structpam_conv*conversation;structpam_messagemessage;structpam_message*pmessage=&message;structpam_response*res=NULL;inti;intnowarn=0;intdebug=0;/*Setflagstodisplaywarningsifindebugmode.
*/for(i=0;iconv(1,&pmessage,&res,conversation->appdata_ptr);if(debug&&err!
=PAM_SUCCESS)syslog(LOG_AUTH|LOG_DEBUG,"%s:members_only:conversationreturned""error%d(%s).
",service,err,pam_strerror(pamh,err));/*freeresponse(ifany)*/if(res!
=NULL){if(res->resp)free(res->resp);free(res);}}/*Reportdenialtosystemlogandreturnerrortocaller.
*/syslog(LOG_NOTICE|LOG_AUTH,"%s:members_only:""Connectionfor%snotallowedfrom%s",service,user,host);return(PAM_PERM_DENIED);}第4章编写使用GSS-API的应用程序514第4章编写使用GSS-API的应用程序通用安全服务应用编程接口(GenericSecurityServiceApplicationProgrammingInterface,GSS-API)为应用程序提供了一种用于保护发送到对等应用程序的数据的方法.
通常,连接是指从一台计算机上的客户机到另一台计算机上的服务器.

本章介绍有关以下主题的信息:"GSS-API介绍"[51]"GSS-API的重要元素"[55]"开发使用GSS-API的应用程序"[68]GSS-API介绍使用GSS-API,程序员在编写应用程序时,可以应用通用的安全机制.
开发者不必针对任何特定的平台、安全机制、保护类型或传输协议来定制安全实现.
使用GSS-API,程序员可忽略保护网络数据方面的细节.
使用GSS-API编写的程序在网络安全方面具有更高的可移植性.
这种可移植性是通用安全服务API的一个特点.
GSS-API是一个以通用方式为调用者提供安全服务的框架.
许多底层机制和技术(如Kerberosv5或公钥技术)都支持GSS-API框架,如下图所示.
GSS-API介绍52面向开发者的OracleSolaris11安全性指南2014年7月图4-1GSS-API层从广义上讲,GSS-API主要具有以下两种功能:1.
GSS–API可创建一个安全上下文,应用程序可在该上下文中相互传递数据.
上下文是指两个应用程序之间的信任状态.
由于共享同一个上下文的应用程序可相互识别,因此可以允许在上下文存在期间进行数据传送.
2.
GSS-API可向要传送的数据应用一种或多种类型的保护,称为安全服务.
"GSS-API中的安全服务"[53]介绍了安全服务.
此外,GSS-API还可执行以下功能:转换数据错误检查授予用户特权显示信息比较标识GSS-API中包括许多支持函数和便利函数.
使用GSS-API的应用程序的可移植性GSS-API为应用程序提供了以下几种类型的可移植性:GSS-API介绍第4章编写使用GSS-API的应用程序53机制无关性.
GSS-API提供了一个用于实现安全性的通用接口.
通过指定缺省的安全机制,应用程序无需了解要应用的机制以及该机制的任何详细信息.
协议无关性.
GSS-API与任何通信协议或协议套件均无关.
例如,GSS-API可用于使用套接字、RCP或TCP/IP的应用程序.
RPCSEC_GSS是用于将GSS-API与RPC顺利集成的附加层.
有关更多信息,请参见"使用GSS-API的远程过程调用"[53].
平台无关性.
GSS-API与运行应用程序的操作系统的类型无关.
保护质量无关性.
保护质量(QualityofProtection,QOP)是指一种算法类型,用于加密数据或生成加密标记.
通过GSS-API,程序员可使用GSS-API所提供的缺省设置忽略QOP.
另一方面,应用程序可以根据需要指定QOP.
GSS-API中的安全服务GSS-API提供了三种类型的安全服务:验证-验证是GSS-API提供的基本安全性.
验证是指对身份进行验证.
如果用户通过了验证,则系统会假设其有权以该用户名进行操作.
完整性-完整性是指对数据的有效性进行验证.
即使数据来自有效用户,数据本身也可能会损坏或遭到破坏.
完整性可确保消息与预期的一样完整(未增减任何内容).
GSS-API提供的数据附带有一个名为消息完整性代码(MessageIntegrityCode,MIC)的加密标记.
MIC可用于证明收到的数据与发送者所传送的数据是否相同.
保密性-保密性可确保拦截了消息的第三方难以阅读消息内容.
验证和完整性机制都不会修改数据.
如果数据由于某种原因而被拦截,则其他人可以阅读该数据.
因此,可通过GSS-API对数据进行加密,前提是提供了支持加密的基础机制.
这种数据加密称为保密性.
GSS-API中的可用机制当前的GSS-API实现可使用以下机制:Kerberosv5、Diffie-Hellman和SPNEGO.
有关Kerberos实现的更多信息,请参见《ManagingKerberosandOtherAuthenticationServicesinOracleSolaris11.
2》.
如果任何系统上运行了能够识别GSS-API的程序,则应在该系统上安装和运行Kerberosv5.
使用GSS-API的远程过程调用针对网络应用程序使用RPC(RemoteProcedureCall,远程过程调用)协议的程序员可以使用RPCSEC_GSS来提供安全性.
RPCSEC_GSS是位于GSS-API上面的一个独立层.
RPCSEC_GSS可提供GSS-API的所有功能,但其方式是针对RPC进行GSS-API介绍54面向开发者的OracleSolaris11安全性指南2014年7月了调整的.
实际上,RPCSC_GSS可用于向程序员隐藏GSS-API的许多方面,从而使RPC安全性具有更强的可访问性和可移植性.
有关RPCSEC_GSS的更多信息,请参见"AuthenticationUsingRPCSEC_GSS"in《ONC+RPCDeveloper'sGuide》.
下图说明了RPCSEC_GSS层在应用程序和GSS-API之间的位置.
图4-2RPCSEC_GSS和GSS-APIGSS-API的限制虽然GSS-API使数据保护变得很简单,但是它回避某些任务的做法并不符合GSS-API的一般本性.
因此,GSS-API不执行以下活动:为用户或应用程序提供安全凭证.
凭证必须由基础安全机制提供.
GSS-API确实允许应用程序自动或显式获取凭证.
在应用程序之间传送数据.
应用程序负责处理对等应用程序之间的所有数据传送,无论数据是与安全有关的数据还是明文数据.
区分不同类型的传送数据.
例如,GSS-API无法识别数据包是明文数据还是经过加密的数据.
指示由于异步错误而导致的状态.
缺省情况下保护多进程程序的不同进程之间已发送的信息.
分配要传递到GSS-API函数的字符串缓冲区.
请参见"GSS-API中的字符串和类似数据"[56].
GSS-API的重要元素第4章编写使用GSS-API的应用程序55取消分配GSS-API数据空间.
此内存必须通过gss_release_buffer()和gss_delete_name()等函数显式取消分配.
GSS-API的语言绑定本文档目前仅介绍GSS-API的C语言绑定,即函数和数据类型.
现已推出Java绑定版本的GSS-API.
JavaGSS-API包含通用安全服务应用编程接口(GenericSecurityServiceApplicationProgramInterface,GSS-API)的Java绑定,如RFC2853中所定义.
有关GSS-API的更多参考信息以下两个文档提供有关GSS-API的更多信息:GenericSecurityServiceApplicationProgramInterface(通用安全服务应用编程接口)文档(http://www.
ietf.
org/rfc/rfc2743.
txt)概述了GSS-API的概念.
GenericSecurityServiceAPIVersion2:C-Bindings(通用安全服务API版本2:C绑定)文档(http://www.
ietf.
org/rfc/rfc2744.
txt)介绍了基于C语言的GSS-API的具体信息.
GSS-API的重要元素本节介绍了以下GSS-API的重要概念:主体、GSS-API数据类型、状态码和令牌.
"GSS-API数据类型"[55]"GSS-API状态码"[65]"GSS-API令牌"[66]GSS-API数据类型以下几节介绍了主要的GSS-API数据类型.
有关所有GSS-API数据类型的信息,请参见"GSS-API数据类型和值"[221].
GSS-API整数由于int的长度因平台而异,因此GSS-API提供了以下整数数据类型:OM_uint32,此为32位无符号整数.
GSS-API的重要元素56面向开发者的OracleSolaris11安全性指南2014年7月GSS-API中的字符串和类似数据由于GSS-API以内部格式处理所有数据,因此在将字符串传递到GSS-API函数之前必须将其转换为GSS-API格式.
GSS-API可处理具有gss_buffer_desc结构的字符串:typedefstructgss_buffer_desc_struct{size_tlength;void*value;}gss_buffer_desc*gss_buffer_t;gss_buffer_t是指向此类结构的指针.
在将字符串传递到使用它们的函数之前,必须先将其放到gss_buffer_desc结构中.
在以下示例中,通用的GSS-API函数在发送消息之前会先对此消息应用保护机制.
例4-1在GSS-API中使用字符串char*message_string;gss_buffer_descinput_msg_buffer;input_msg_buffer.
value=message_string;input_msg_buffer.
length=strlen(input_msg_buffer.
value)+1;gss_generic_function(arg1,&input_msg_buffer,arg2.
.
.
);gss_release_buffer(input_msg_buffer);请注意,完成对input_msg_buffer的操作之后,必须通过gss_release_buffer()取消对input_msg_buffer的分配.
gss_buffer_desc对象不只是用于字符串.
例如,令牌可以作为gss_buffer_desc对象进行处理.
有关更多信息,请参见"GSS-API令牌"[66].
GSS-API中的名称名称是指主体.
在网络安全术语中,主体是指用户、程序或计算机.
主体可以是客户机或服务器.
下面是主体的一些示例:登录到另一台计算机的用户,如user@machine网络服务,如nfs@machine运行应用程序的计算机,如myHost@example.
com在GSS-API中,名称会存储为gss_name_t对象,该对象对于应用程序是不透明的.
名称可以通过gss_import_name()函数从gss_buffer_t对象转换为gss_name_t形式.
所GSS-API的重要元素第4章编写使用GSS-API的应用程序57导入的每个名称都有一个指示名称格式的相关名称类型.
有关名称类型的更多信息,请参见"GSS-APIOID"[64].
有关有效名称类型的列表,请参见"名称类型"[222].
gss_import_name()具有以下语法:OM_uint32gss_import_name(OM_uint32*minor-status,constgss_buffer_tinput-name-buffer,constgss_OIDinput-name-type,gss_name_t*output-name)minor-status基础机制返回的状态码.
请参见"GSS-API状态码"[65].
input-name-buffer包含要导入的名称的gss_buffer_desc结构.
应用程序必须显式分配此结构.
请参见"GSS-API中的字符串和类似数据"[56]以及例4-2"使用gss_import_name()".
应用程序不再使用为该参数分配的空间时,必须通过gss_release_buffer()取消分配该参数.
input-name-type用于指定input-name-buffer的格式的gss_OID.
请参见"GSS-API中的名称类型"[65].
另外,"名称类型"[222]中还包含一个有效名称类型表.
output-name接收名称的gss_name_t结构.
例4-1"在GSS-API中使用字符串"中所示的通用示例进行了小修改,说明如何使用gss_import_name().
首先,将规则字符串插入到gss_buffer_desc结构中.
然后,使用gss_import_name()将该字符串放到gss_name_t结构中.
例4-2使用gss_import_name()char*name_string;gss_buffer_descinput_name_buffer;gss_name_toutput_name_buffer;input_name_buffer.
value=name_string;input_name_buffer.
length=strlen(input_name_buffer.
value)+1;gss_import_name(&minor_status,input_name_buffer,GSS_C_NT_HOSTBASED_SERVICE,&output_name);gss_release_buffer(input_name_buffer);使用gss_display_name()可以将所导入的名称放回到gss_buffer_t对象中,以便能够以可读格式显示.
但是,鉴于基础机制存储名称的方式,gss_display_name()不能保证所得到的字符串与原来的字符串相同.
GSS-API包括若干个用于处理名称的其他函数.
请参见"GSS-API函数"[215].
gss_name_t结构可以包含一个名称的多个版本.
可以为GSS-API所支持的每个机制都生成一个版本.
也即是说,user@company的gss_name_t结构GSS-API的重要元素58面向开发者的OracleSolaris11安全性指南2014年7月可能会包含该名称的两个版本:一个版本由Kerberosv5提供,另一个版本由其他机制提供.
gss_canonicalize_name()函数将内部名称和机制用作输入.
gss_canonicalize_name()可生成另一个内部名称,其中包含一个特定于该机制的名称版本.
此类特定于机制的名称称为机制名称(MechanismName,MN).
机制名称是指指定机制所生成的主体名称,而不是指机制的名称.
下图对此流程进行了说明.
GSS-API的重要元素第4章编写使用GSS-API的应用程序59图4-3内部名称和机制名称GSS-API的重要元素60面向开发者的OracleSolaris11安全性指南2014年7月在GSS-API中比较名称请考虑以下情况:服务器从客户机收到一个名称,并且需要在访问控制列表中查找该名称.
访问控制列表(即ACL)是指具有特定访问权限的主体的列表.
下面是一种用于进行查找的方法:1.
使用gss_import_name()以GSS-API内部格式导入客户机名称(如果该名称尚未导入).
在某些情况下,服务器将以内部格式接收名称,因此可不必执行该步骤.
例如,服务器可能会查找客户机本身的名称.
在启动上下文的过程中,客户机本身的名称以内部格式进行传递.
2.
使用gss_import_name()将每个名称导入到ACL中.
3.
使用gss_compare_name()将所导入的每个ACL名称与所导入的客户机名称进行比较.
此过程如下图所示.
在本示例中,假设步骤1是必需的.
GSS-API的重要元素第4章编写使用GSS-API的应用程序61图4-4比较名称(慢速)GSS-API的重要元素62面向开发者的OracleSolaris11安全性指南2014年7月如果只有少数名称,则可以接受使用上述单独比较名称的方法.
如果有大量名称,则使用gss_canonicalize_name()函数会更高效.
此方法会执行以下步骤:1.
使用gss_import_name()导入客户机的名称(如果该名称尚未导入).
和上述比较名称的方法一样,如果名称已经采用内部格式,则不必执行此步骤.

2.
使用gss_canonicalize_name()生成机制名称版本的客户机名称.
3.
使用gss_export_name()生成所导出的名称,这是连续字符串形式的客户机名称.
4.
使用memcmp()对所导出的客户机名称与ACL中的每个名称进行快速比较,该函数的开销较低.
此过程如下图所示.
同样,再次假设服务器需要导入从客户机收到的名称.

GSS-API的重要元素第4章编写使用GSS-API的应用程序63图4-5比较名称(快速)由于gss_export_name()需要使用机制名称(MechanismName,MN),因此必须首先针对客户机名称运行gss_canonicalize_name().
有关更多信息,请参见gss_export_name(3GSS)、gss_import_name(3GSS)和gss_canonicalize_name(3GSS).
GSS-API的重要元素64面向开发者的OracleSolaris11安全性指南2014年7月GSS-APIOID对象标识符(ObjectIdentifier,OID)用于存储以下几种数据:安全机制QOP-保护质量的值名称类型OID存储在GSS-APIgss_OID_desc结构中.
GSS-API提供了指向gss_OID结构的指针,如以下示例中所示.
例4-3OID结构typedefstructgss_OID_desc_struct{OM_uint32length;void*elements;}gss_OID_desc,*gss_OID;并且,gss_OID_set_desc结构中可能会包含一个或多个OID.
例4-4OID集合结构typedefstructgss_OID_set_desc_struct{size_tcount;gss_OIDelements;}gss_OID_set_desc,*gss_OID_set;注意-应用程序不应尝试使用free()取消分配OID.
GSS-API中的机制和QOP尽管GSS-API允许应用程序选择基础安全机制,但是应用程序还是应当尽可能使用GSS-API已选择的缺省机制.
同样,尽管GSS-API允许应用程序指定用于保护数据的保护质量级别,但还是应当尽可能使用缺省QOP.
可以通过向需要使用机制或QOP作为参数的函数传递值GSS_C_NULL_OID来表示接受缺省机制.
注意-显式指定安全机制或QOP会背离使用GSS-API的初衷.
类似的特定选择会限制应用程序的可移植性.
其他GSS-API实现可能无法以预期方式支持QOP或机制.
尽管如此,附录D,指定OID简要介绍了如何查找可用的机制和QOP以及如何从中选择一个机制或QOP.
GSS-API的重要元素第4章编写使用GSS-API的应用程序65GSS-API中的名称类型除了QOP和安全机制以外,OID还用于指示名称类型,从而指明关联名称的格式.
例如,gss_import_name()函数(用于将主体的名称从字符串转换为gss_name_t类型)将需要转换的字符串的格式用作一个参数.
例如,如果名称类型为GSS_C_NT_HOSTBASED_SERVICE,则该函数便会知道所输入的名称是采用service@host形式.
如果名称类型为GSS_C_NT_EXPORT_NAME,则该函数需要使用GSS-API导出的名称.
应用程序可以使用gss_inquire_names_for_mech()函数来确定指定机制可用的名称类型.
"名称类型"[222]中提供了GSS-API所使用的名称类型的列表.
GSS-API状态码所有GSS-API函数都会返回两种类型的代码,其中提供了有关该函数执行成败与否的信息.
这两种类型的状态码都以OM_uint32值的形式返回.
这两种类型的返回码如下所示:主状态码主状态码指示以下错误:常规的GSS-API例程错误,如为例程提供无效机制特定于指定GSS-API语言绑定的调用错误,如函数参数无法读取、无法写入或格式有误以上两种类型的错误此外,主状态码还可以提供有关例程状态的补充信息.
例如,代码可能会指示操作尚未完成或者令牌未按顺序发送.
如果未出现任何错误,例程将返回主状态值GSS_S_COMPLETE.
主状态码按如下方式返回:OM_uint32major_status;/*statusreturnedbyGSS-API*/major_status=gss_generic_function(arg1,arg2.
.
.
);主状态返回码的处理方式与任何其他OM_uint32的处理方式相同.
例如,请考虑以下代码:OM_uint32maj_stat;maj_sta=gss_generic_function(arg1,arg2.
.
.
);if(maj_stat==GSS_CREDENTIALS_EXPIRED)GSS-API的重要元素66面向开发者的OracleSolaris11安全性指南2014年7月主状态码可以通过GSS_ROUTINE_ERROR()、GSS_CALLING_ERROR()和GSS_SUPPLEMENTARY_INFO()宏进行处理.
"GSS-API状态码"[217]介绍了如何读取主状态码并提供了GSS-API状态码的列表.
次状态码基础机制返回次状态码.
这些代码未在本手册中具体说明.
每个GSS-API函数都将次状态码的OM_uint32类型作为第一个参数.
该函数返回到调用函数时,次状态码将存储到OM_uint32参数中.
请考虑以下代码:OM_uint32*minor_status;/*statusreturnedbymech*/major_status=gss_generic_function(&minor_status,arg1,arg2.
.
.
);即使返回了致命的主状态码错误,minor_status参数也始终由GSS-API例程设置.
请注意,大多数其他输出参数均可以保持未设置状态.
但是,需要将应当返回特定指针的输出参数设置为NULL,这些指针指向该例程已分配的存储空间.
NULL表示并未实际分配存储空间.
正如在gss_buffer_desc结构中一样,与此类指针相关联的任何长度字段均设置为零.
在此类情况下,应用程序无需释放这些缓冲区.
GSS-API令牌GSS-API中的基本"流通"(currency)单位是令牌(token).
使用GSS-API的应用程序可以借助令牌来相互通信.
使用令牌可以交换数据并创建安全布局.
令牌声明为gss_buffer_t数据类型.
令牌对于应用程序是不透明的.
令牌的类型包括上下文级别的令牌和每消息令牌两种.
上下文级别的令牌主要在建立(即启动和接受)上下文时使用.
另外,还可以在以后通过传递上下文级别的令牌来管理上下文.
每消息令牌在建立了上下文之后使用.
每消息令牌用于为数据提供保护服务.
例如,请考虑一个希望将消息发送到另一个应用程序的应用程序.
该应用程序可能会使用GSS-API来生成随该消息传递的加密标识符.
该标识符可存储在令牌中.
可以考虑按如下方式针对消息使用每消息令牌.
消息是应用程序发送到其对等应用程序的一段数据.
例如,ls命令可以是发送到ftp服务器的消息.
每消息令牌是GSS-API为该消息生成的对象.
每消息令牌可以是加密标记,也可以是消息的加密形式.
请注意,最后一个示例不太准确.
加密消息仍是消息,而不是令牌.
令牌仅是指GSS-API生成的信息.
但是,在非正式情况下,消息和每消息令牌通常可以互换使用.

应用程序负责进行以下活动:1.
收发令牌.
开发者通常需要编写一般性的读写函数来执行这些操作.
send_token()和recv_token()函数在"各种GSS-API样例函数"[208]中进行介绍.
2.
区分不同类型的令牌并相应地处理这些令牌.
GSS-API的重要元素第4章编写使用GSS-API的应用程序67由于令牌对于应用程序来说是不透明的,因此应用程序不会对不同的令牌加以区分.
如果不知道令牌的内容,则应用程序必须能够区分令牌的类型才能将令牌传递到相应的GSS-API函数.
应用程序可以通过以下方法来区分令牌类型:按状态.
通过程序的控制流程.
例如,等待接受上下文的应用程序可能会假设收到的任何令牌都与所建立的上下文有关.
对等应用程序应当等到上下文完全建立之后才发送消息令牌(即数据).
建立上下文之后,正在等待接受上下文的应用程序会假设新令牌即是消息令牌.
这是处理令牌的相当常见的方法.
本书中的样例程序会使用此方法.
按标志.
例如,如果某个应用程序包含用于向对等应用程序发送令牌的函数,则该应用程序可以引入一个标志,用于指示令牌类型.
请考虑以下代码:gss_buffer_ttoken;/*declarethetoken*/OM_uint32token_flag/*flagfordescribingthetypeoftoken*/token_flag=MIC_TOKEN;/*specifywhatkindoftokenitis*/send_a_token(&token,token_flag);接收应用程序可包含用于检查token_flag参数的接收函数,例如get_a_token().
通过显式使用标记.
应用程序可以使用元令牌.
元令牌是一种用户定义的结构,其中包含从GSS-API函数收到的令牌.
元令牌包括用户定义的字段,这些字段指示如何使用GSS-API提供的令牌.
GSS-API中的进程间令牌GSS-API允许在多进程应用程序中的进程之间传递安全上下文.
通常,应用程序已经接受了客户机的上下文.
然后,应用程序将在其进程之间共享该上下文.
有关多进程应用程序的信息,请参见"在GSS-API中导出和导入上下文"[75].
gss_export_context()函数可用于创建进程间令牌.
使用此令牌中包含的信息,另一个进程可以重建该上下文.
应用程序负责在进程之间传递进程间令牌.
这与应用程序负责将令牌传递到其他应用程序情况相似.
进程间令牌可能包含密钥或其他敏感信息.
并非所有的GSS-API实现都以加密方式保护进程间令牌.
因此,在进行交换之前,应用程序必须保护进程间令牌.
这种保护可能涉及到使用gss_wrap()对令牌进行加密(如果加密机制可用).
注-请勿假设进程间令牌可以在不同的GSS-API实现之间传送.
开发使用GSS-API的应用程序68面向开发者的OracleSolaris11安全性指南2014年7月开发使用GSS-API的应用程序本节说明如何使用GSS-API来实现安全的数据交换.
本节将重点介绍那些对于使用GSS-API至关重要的函数.
有关更多信息,请参见附录C,GSS-API参考信息,其中包含所有GSS-API函数、状态代码和数据类型的列表.
要查找有关任何GSS-API函数的更多信息,请检查相应的手册页.
本手册中的示例遵循一个简单的模型.
客户机应用程序将数据直接发送到远程服务器.

无需通过传输协议层(如RPC)进行中间调用.
GSS-API的一般用法下面是使用GSS-API的一般步骤:1.
每个应用程序(无论是发送者还是接受者)都显式获取凭证,除非已经自动获取凭证.
2.
发送者启动一个安全上下文.
接受者接受该上下文.
3.
发送者向要传送的数据应用安全保护机制.
发送者会对消息进行加密或者使用标识标记对数据进行标记.
发送者随后将传送受保护的消息.
注-发送者可以选择不应用安全保护机制,在这种情况下,消息仅具有缺省的GSS-API安全服务,即验证.
4.
接受者根据需要对消息进行解密,并在适当的情况下对消息进行验证.
5.
(可选)接受者将标识标记返回到发送者进行确认.
6.
这两个应用程序都会销毁共享的安全上下文.
如有必要,分配功能还可以取消分配其余任何GSS-API数据.
注意-调用应用程序负责释放已经分配的所有数据空间.
使用GSS-API的应用程序需要包含文件gssapi.
h.
在GSS-API中使用凭证凭证是指向主体名称提供应用程序声明证明的数据结构.
应用程序使用凭证来建立其全局标识.
此外,凭证还可用于确认实体的特权.
GSS-API不提供凭证.
凭证由那些以GSS-API为基础的安全机制在调用GSS-API函数之前创建.
在许多情况下,用户会在登录时接收凭证.
开发使用GSS-API的应用程序第4章编写使用GSS-API的应用程序69指定的GSS-API凭证对于单个主体有效.
单个凭证可以包含该主体的多个元素,每个元素由不同的机制创建.
如果某个凭证是在包含多个安全机制的计算机上获取的,则该凭证在传输到包含其中部分机制的计算机上时有效.
GSS-API通过gss_cred_id_t结构访问凭证.
此结构称为凭证句柄.
凭证对于应用程序是不透明的.
因此,应用程序无需知道指定凭证的具体信息.
凭证采用以下三种形式:GSS_C_INITIATE-标识仅启动安全上下文的应用程序GSS_C_ACCEPT-标识仅接受安全上下文的应用程序GSS_C_BOTH-标识可启动或接受安全上下文的应用程序在GSS-API中获取凭证服务器和客户机都必须获取各自的凭证,然后才能建立安全上下文.
应用程序可以在凭证到期之前重用它,在失效之后必须重新获取凭证.
客户机使用的凭证与服务器使用的凭证可以具有不同的生命周期.
基于GSS-API的应用程序可以通过以下两种方法获取凭证:使用gss_acquire_cred()或gss_add_cred()函数在建立上下文时指定值GSS_C_NO_CREDENTIAL,该值表示使用缺省凭证大多数情况下,只有上下文接受器(即服务器)才能调用gss_acquire_cred().
上下文启动器(即客户机)通常在登录时接收凭证.
因此,客户机通常会指定缺省凭证.
服务器也可以跳过gss_acquire_cred()而使用其缺省凭证.
客户机的凭证用于向其他进程证明该客户机的身份.
服务器在获取凭证后即可接受安全上下文.
因此,如果某个客户机向服务器发出ftp请求,则表明该客户机可能已经在登录时获取了凭证.
客户机尝试启动上下文时,GSS-API会自动检索凭证.
但是,服务器程序可以显式获取所请求服务(ftp)的凭证.
如果gss_acquire_cred()成功完成,将返回GSS_S_COMPLETE.
如果不能返回有效的凭证,将返回GSS_S_NO_CRED.
有关其他错误代码,请参见gss_acquire_cred(3GSS)手册页.
有关示例,请参见第8章中的"获取凭证".
gss_add_cred()与gss_acquire_cred()类似.
但是,gss_add_cred()允许应用程序使用现有的凭证来创建新句柄或者添加新的凭证元素.
如果将GSS_C_NO_CREDENTIAL指定为现有的凭证,则gss_add_cred()会根据缺省行为创建新凭证.
有关更多信息,请参见gss_add_cred(3GSS)手册页.
在GSS-API中使用上下文在提供安全性方面,GSS-API最重要的两个任务就是创建安全上下文和保护数据.
应用程序获取必要的凭证之后,必须建立安全上下文.
建立上下文时会涉及到两个应用开发使用GSS-API的应用程序70面向开发者的OracleSolaris11安全性指南2014年7月程序,一个应用程序(通常是客户机)用于启动上下文,另一个应用程序(通常是服务器)用于接受上下文.
允许在对等应用程序之间使用多个上下文.
相互通信的应用程序通过交换验证令牌来建立共同的安全上下文.
安全上下文是一对GSS-API数据结构,其中包含要在两个应用程序之间共享的信息.
这些信息从安全的角度描述了每个应用程序的状态.
安全上下文是保护数据所必需的.
在GSS-API中启动上下文gss_init_sec_context()函数用于启动应用程序和远程服务器之间的安全上下文.
如果成功,该函数将返回一个上下文句柄以建立上下文,还将返回一个要发送到接受器的上下文级别的令牌.
调用gss_init_sec_context()之前,客户机应当执行以下任务:1.
使用gss_acquire_cred()获取凭证(如有必要).
通常,客户机会在登录时接收凭证.
gss_acquire_cred()只能从正在运行的操作系统中检索初始凭证.
2.
使用gss_import_name()以GSS-API内部格式导入服务器的名称.
有关名称和"GSS-API中的名称"[56]的更多信息,请参见NamesinGSS-API().
调用gss_init_sec_context()时,客户机通常会传递以下参数值:GSS_C_NO_CREDENTIAL(用于cred_handle参数),用于表示缺省凭证.
GSS_C_NULL_OID(用于mech_type参数),用于表示缺省机制.
GSS_C_NO_CONTEXT(用于context_handle参数),用于表示初始的空上下文.
由于gss_init_sec_context()通常会循环调用,因此后续的调用应传递以前的调用所返回的上下文句柄.
GSS_C_NO_BUFFER(用于input_token参数),用于表示最初为空的令牌.
或者,应用程序可以传递一个指向gss_buffer_desc对象的指针,该对象的长度字段已经设置为零.
使用gss_import_name()以GSS-API内部格式导入的服务器名称.
应用程序不一定要使用这些缺省值.
此外,客户机还可以使用req_flags参数指定其他安全参数的要求.
下面描述了完整的gss_init_sec_context()参数集.
上下文接受器可能需要多次握手才能建立上下文.
也就是说,接受器会要求启动器先发送多段上下文信息,然后再建立完整的上下文.
因此,为了实现可移植性,应始终在检查上下文是否已完全建立的循环过程中启动上下文.
如果上下文不完整,则gss_init_sec_context()将返回一个主状态码GSS_C_CONTINUE_NEEDED.
因此,循环应当使用gss_init_sec_context()的返回值来测试是否继续执行启动循环操作.
客户机将上下文信息以输出令牌形式传递到服务器,输出令牌通过gss_init_sec_context()返回.
客户机可接收从服务器返回的输入令牌形式的信息.
开发使用GSS-API的应用程序第4章编写使用GSS-API的应用程序71以后调用gss_init_sec_context()时,输入令牌可以作为参数传递.
但是,如果所收到的输入令牌的长度为零,则表明服务器不再需要其他输出令牌.
因此,除了检查gss_init_sec_context()的返回状态以外,该循环还应当检查输入令牌的长度.
如果长度是非零值,则需要向服务器发送其他令牌.
开始循环之前,应将输入令牌的长度初始化为零.
请将输入令牌设置为GSS_C_NO_BUFFER或者将结构的长度字段设置为零值.
下面的伪代码举例说明如何从客户端建立上下文:context=GSS_C_NO_CONTEXTinputtoken=GSS_C_NO_BUFFERdocallgss_init_sec_context(credential,context,name,inputtoken,outputtoken,otherargs.
.
.
)if(there'sanoutputtokentosendtotheacceptor)sendtheoutputtokentotheacceptorreleasetheoutputtokenif(thecontextisnotcomplete)receiveaninputtokenfromtheacceptorif(there'saGSS-APIerror)deletethecontextuntilthecontextiscomplete错误检查范围越大,实际循环将越完整.
有关此类上下文启动循环的实际示例,请参见"建立与服务器的安全上下文"[93].
此外,gss_init_sec_context(3GSS)手册页还提供了比较特殊的示例.
通常,在上下文未完全建立时返回的参数值即是那些会在上下文完整建立时返回的值.

有关更多信息,请参见gss_init_sec_context()手册页.
如果gss_init_sec_context()成功完成,将返回GSS_S_COMPLETE.
如果对等应用程序需要上下文建立令牌,将返回GSS_S_CONTINUE_NEEDED.
如果出现错误,则将返回gss_init_sec_context(3GSS)手册页中所示的错误代码.
如果上下文启动失败,则客户机会断开与服务器的连接.
在GSS-API中接受上下文建立上下文的另一方面是接受上下文,这可通过gss_accept_sec_context()函数来完成.
通常情况下,服务器接受客户机使用gss_init_sec_context()已启动的上下文.
开发使用GSS-API的应用程序72面向开发者的OracleSolaris11安全性指南2014年7月gss_accept_sec_context()的主要输入是来自启动器的输入令牌.
启动器会返回一个上下文句柄,以及一个要返回到启动器的输出令牌.
但是,服务器应当首先获取客户机所请求服务的凭证,然后才能调用gss_accept_sec_context().
服务器使用gss_acquire_cred()函数获取这些凭证.
或者,服务器可以指定在调用gss_accept_sec_context()时的缺省凭证(即GSS_C_NO_CREDENTIAL),从而无需显式获取凭证.
调用gss_accept_sec_context()时,服务器可以按如下方式设置以下参数:cred_handle-gss_acquire_cred()返回的凭证句柄.
或者,可以使用GSS_C_NO_CREDENTIAL来表示缺省凭证.
context_handle-GSS_C_NO_CONTEXT表示最初为空的上下文.
由于gss_init_sec_context()通常会循环调用,因此后续的调用应传递以前的调用所返回的上下文句柄.
input_token-从客户机接收的上下文令牌.
以下几段将介绍完整的gss_accept_sec_context()参数集.
建立安全上下文可能需要几次握手.
启动器和接受器通常需要先发送多段上下文信息,然后才能建立完整的上下文.
因此,为了实现可移植性,应始终在检查上下文是否已完全建立的循环过程中接受上下文.
如果上下文尚未建立,则gss_accept_sec_context()将返回一个主状态码GSS_C_CONTINUE_NEEDED.
因此,循环应当使用gss_accept_sec_context()返回的值来测试是否继续执行接受循环操作.
上下文接受器将上下文信息以输出令牌形式返回到启动器,输出令牌通过gss_accept_sec_context()返回.
以后,接受器可以从启动器接收输入令牌形式的其他信息.
以后调用gss_accept_sec_context()时,输入令牌将作为参数传递.
gss_accept_sec_context()不再向启动器发送令牌时,将返回一个长度为零的输出令牌.
除了检查返回状态gss_accept_sec_context()以外,循环还应当检查输出令牌的长度,查看是否必须发送其他令牌.
开始循环之前,应将输出令牌的长度初始化为零.
请将输出令牌设置为GSS_C_NO_BUFFER或者将结构的长度字段设置为零值.
下面的伪代码举例说明如何从服务器端建立上下文:context=GSS_C_NO_CONTEXToutputtoken=GSS_C_NO_BUFFERdoreceiveaninputtokenfromtheinitiatorcallgss_accept_sec_context(context,credhandle,inputtoken,outputtoken,otherargs.
.
.
)if(there'sanoutputtokentosendtotheinitiator)sendtheoutputtokentotheinitiatorreleasetheoutputtokenif(there'saGSS-APIerror)开发使用GSS-API的应用程序第4章编写使用GSS-API的应用程序73deletethecontextuntilthecontextiscomplete错误检查范围越大,实际循环将越完整.
有关此类上下文接受循环的实际示例,请参见"建立与服务器的安全上下文"[93].
此外,gss_accept_sec_context()手册页还提供了一个示例.
同样,GSS-API不会收发令牌.
令牌必须由应用程序进行处理.
有关令牌传送函数的示例可以在"各种GSS-API样例函数"[208]中找到.
gss_accept_sec_context()成功完成时将返回GSS_S_COMPLETE.
如果上下文不完整,该函数将返回GSS_S_CONTINUE_NEEDED.
如果出现错误,该函数将返回错误代码.
有关更多信息,请参见gss_accept_sec_context(3GSS)手册页.
在GSS-API中使用其他上下文服务gss_init_sec_context()函数允许应用程序请求超出所建立基本上下文范围的其他数据保护服务.
可通过gss_init_sec_context()的req_flags参数来请求这些服务.
并非所有的机制都提供所有这些服务.
gss_init_sec_context()的ret_flags参数用于指示指定上下文中可用的服务.
同样,上下文接受器也会通过检查gss_accept_sec_context()返回的ret_flags值来确定可用的服务.
这些服务将在以下几节中进行介绍.
在GSS-API中委托凭证如果允许的话,上下文启动器可以请求由上下文接受器充当代理.
在此类情况下,接受器可以代表启动器启动进一步的上下文.
假设计算机A上的某个用户希望通过rlogin登录到计算机B,然后通过rlogin从计算机B登录到计算机C.
根据所使用的机制,所授予的凭证会将B标识为A,或者将B标识为A的代理.
如果允许委托的话,可以将ret_flags设置为GSS_C_DELEG_FLAG.
接受器可接收委托凭证并将其作为gss_accept_sec_context()的delegated_cred_handle参数.
委托凭证不同于导出上下文.
请参见"在GSS-API中导出和导入上下文"[75].
二者的区别是应用程序可以将其凭证同时委托多次,而上下文一次只能由一个进程持有.

在GSS-API中的对等应用程序之间执行相互验证将文件传送到ftp站点的用户通常无需站点标识的证明.
另一方面,需要向应用程序提供信用卡号的用户则需要接收者身份的确切证明.
在此类情况下,需要进行相互验证.

上下文启动器和上下文接受器都需要证明各自的身份.
开发使用GSS-API的应用程序74面向开发者的OracleSolaris11安全性指南2014年7月上下文启动器可以通过将gss_init_sec_context()req_flags参数设置为值GSS_C_MUTUAL_FLAG来请求相互验证.
如果已经对相互验证进行了授权,则函数会通过将ret_flags参数设置为该值来表示授权.
如果所请求的相互验证不可用,则启动应用程序会负责进行相应的响应.
在此情况下GSS-API不会自动终止上下文.
另外,即使没有特定的请求,某些机制也会始终执行相互验证.
在GSS-API中执行匿名验证正常使用GSS-API时,启动器的身份会在建立上下文的过程中提供给接受器.
但是,上下文启动器会请求不将其身份显示给上下文接受器.
例如,请考虑提供对医疗数据库进行无限制访问的应用程序.
此类服务的客户机可能需要对该服务进行验证.
此方法会信任从该数据库中检索的所有信息.
例如,出于保密性方面的考虑,客户机可能不希望暴露其身份.
要请求进行匿名验证,请将gss_init_sec_context()的req_flags参数设置为GSS_C_ANON_FLAG.
要检验匿名验证是否可用,请检查gss_init_sec_context()或gss_accept_sec_context()的ret_flags参数,查看是否返回了GSS_C_ANON_FLAG.
如果匿名验证有效,则针对gss_accept_sec_context()或gss_inquire_context()返回的客户机名称调用gss_display_name()时会生成通用的匿名名称.
注-应用程序负责在所请求的匿名验证不被许可时采取相应的措施.
在此类情况下,GSS-API不会终止上下文.
在GSS-API中使用通道绑定对于许多应用程序来说,建立基本的上下文足以确保对上下文启动器进行正确的验证.

如果需要实现额外的安全性,则可以使用GSS-API所提供的通道绑定功能.
通道绑定是用于标识所使用的特定数据通道的标记.
具体来说,通道绑定可标识起点和终点,即上下文的启动器和接受器.
由于标记特定于始发者应用程序和接受者应用程序,因此此类标记可为有效身份提供更多证明.
gss_channel_bindings_t数据类型是一个指针,它指向如下所示的作为通道绑定的gss_channel_bindings_struct结构.
typedefstructgss_channel_bindings_struct{OM_uint32initiator_addrtype;gss_buffer_descinitiator_address;OM_uint32acceptor_addrtype;gss_buffer_descacceptor_address;gss_buffer_descapplication_data;}*gss_channel_bindings_t;开发使用GSS-API的应用程序第4章编写使用GSS-API的应用程序75第一个字段表示地址类型,用于标识所发送的启动器地址采用的格式.
第二个字段表示启动器的地址.
例如,initiator_addrtype可能会发送到GSS_C_AF_INET,表示initiator_address采用的是Internet地址(即IP地址)形式.
同样,第三和第四个字段分别表示接受器的地址类型和接受器的地址.
应用程序可以根据需要使用最后一个字段application_data.
如果不打算使用application_data,请将application_data设置为GSS_C_NO_BUFFER.
如果没有为某个应用程序指定地址,则应当将该应用程序的地址类型字段设置为GSS_C_AF_NULLADDR.
"通道绑定的地址类型"[223]一节提供了有效地址类型值的列表.
地址类型用于表示地址族,而不是表示特定的寻址格式.
对于包含多种替换地址形式的地址族,initiator_address和acceptor_address字段中必须包含足够的信息才能确定所使用的形式.
除非另行指定,否则应当以网络字节顺序(即地址族的本机字节排序)指定地址.
要建立使用通道绑定的上下文,gss_init_sec_context()的input_chan_bindings参数应当指向所分配的通道绑定结构.
该结构的字段将连接到一个八位字节字符串,并将派生一个MIC.
该MIC随后会绑定到输出令牌.
然后,应用程序会将该令牌发送到上下文接受器.
接受器在收到该令牌之后将调用gss_accept_sec_context().
有关更多信息,请参见"在GSS-API中接受上下文"[71].
gss_accept_sec_context()会针对所收到的通道绑定计算MIC.
如果MIC不匹配,gss_accept_sec_context()随后将返回GSS_C_BAD_BINDINGS.
由于gss_accept_sec_context()会返回已传送的通道绑定,因此接受器可以使用这些值来执行安全检查.
例如,接受器可以针对保留在安全数据库中的代码字检查application_data的值.
注-基础机制可能不会为通道绑定信息提供保密性.
因此,除非确保能够实现保密性,否则应用程序不应当在通道绑定中包括敏感信息.
要测试保密性,应用程序可以检查gss_init_sec_context()或gss_accept_sec_context()的ret_flags参数.
值GSS_C_CONF_FLAG和GSS_C_PROT_READY_FLAG可用于指示保密性.
有关"在GSS-API中启动上下文"[70]的信息,请参见"在GSS-API中接受上下文"[71]或AcceptingaContextinGSS-API.
单个机制可以针对出现在通道绑定中的地址和地址类型施加额外的约束.

例如,一个机制可能会检验通道绑定的initiator_address字段是否返回到gss_init_sec_context().
因此,具有可移植性的应用程序应当为地址字段提供正确的信息.
如果无法确定正确的信息,则应当将GSS_C_AF_NULLADDR指定为地址类型.
在GSS-API中导出和导入上下文GSS-API提供了用于导出和导入上下文的方法.
此方法允许多进程应用程序(通常是上下文接受器)在进程之间传送上下文.
例如,接受器可以有两个进程,一个进程侦听上下文启动器,另一个进程使用在上下文中发送的数据.
"使用开发使用GSS-API的应用程序76面向开发者的OracleSolaris11安全性指南2014年7月test_import_export_context()函数"[116]一节说明了如何使用这些函数来保存和恢复上下文.
gss_export_sec_context()函数可创建进程间令牌,其中包含有关所导出上下文的信息.
有关更多信息,请参见"GSS-API中的进程间令牌"[67].
调用gss_export_sec_context()之前,应当将接收令牌的缓冲区设置为GSS_C_NO_BUFFER.
应用程序随后会将令牌传递到另一个进程.
新进程将接受该令牌并将其传递到gss_import_sec_context().
用于在应用程序之间传递令牌的函数通常也可用于在进程之间传递令牌.
一次只能存在一个安全进程实例.
gss_export_sec_context()可取消激活所导出的上下文并将上下文句柄设置为GSS_C_NO_CONTEXT.
gss_export_sec_context()还可取消分配与该上下文相关联的任何进程范围内的资源.
如果无法完成对上下文的导出,gss_export_sec_context()会保持现有的安全上下文不变,并且不返回进程间令牌.
并非所有机制都允许导出上下文.
应用程序可以通过检查gss_accept_sec_context()或gss_init_sec_context()的ret_flags参数来确定上下文是否可以导出.
如果此标志设置为GSS_C_TRANS_FLAG,则可以导出上下文.
(请参见"在GSS-API中接受上下文"[71]和"在GSS-API中启动上下文"[70].
)图4-6"导出上下文:多线程接受器示例"说明多进程接受器如何将上下文导出到多个任务.
在这种情况下,进程1接收和处理令牌.
此步骤会将上下文级别的令牌与数据令牌分开,并将令牌传递到进程2.
进程2按照应用程序特定的方式处理数据.
在该图中,客户机已经从gss_init_sec_context()获取了导出令牌.
客户机将令牌传递到用户定义的函数send_a_token(),该函数指示要传送的令牌是上下文级别的令牌还是消息令牌.
send_a_token()将这些令牌传送到服务器.
send_a_token()可能会用于在线程之间传递令牌,但是该图中未显示这一点.
开发使用GSS-API的应用程序第4章编写使用GSS-API的应用程序77图4-6导出上下文:多线程接受器示例开发使用GSS-API的应用程序78面向开发者的OracleSolaris11安全性指南2014年7月在GSS-API中获取上下文信息GSS-API提供了一个gss_inquire_context(3GSS)函数,该函数可用于获取有关指定的安全上下文的信息.
请注意,上下文无需是完整的上下文.
如果提供了上下文句柄,则gss_inquire_context()将提供以下有关上下文的信息:上下文启动器的名称.
上下文接受器的名称.
上下文保持有效的秒数.
要用于上下文的安全机制.
若干个上下文参数标志.
这些标志与gss_accept_sec_context(3GSS)函数的ret_flags参数相同.
这些标志涉及委托、相互验证等.
请参见"在GSS-API中接受上下文"[71].
指示查询应用程序是否为上下文启动器的标志.
指示上下文是否已完全建立的标志.
在GSS-API中发送受保护的数据在两个对等应用程序之间建立上下文之后,即可在发送消息之前对其进行保护.

建立上下文时使用的只是最基本的GSS-API保护:验证.
根据基础安全机制,GSS-API提供了另外两个保护级别:完整性-消息的消息完整性代码(MessageIntegrityCode,MIC)由gss_get_mic()函数生成.
接受者通过检查MIC来确保所收到的消息与所发送的消息完全相同.
保密性-除了使用MIC以外,还会对消息进行加密.
GSS-API函数gss_wrap()可用于执行加密.
gss_get_mic()和gss_wrap()之间的区别如下图所示.
使用gss_get_mic(),接收者可获取用于指示消息是否保持不变的标记;使用gss_wrap(),接收者除了获取标记外还可获取经过加密的消息.
开发使用GSS-API的应用程序第4章编写使用GSS-API的应用程序79图4-7gss_get_mic()与gss_wrap()要使用哪个函数取决于具体情况.
由于gss_wrap()包括完整性服务,因此许多程序都使用gss_wrap().
程序可以测试保密性服务的可用性.
随后程序可以根据保密性服务的可用情况,以应用或不应用保密性的方式调用gss_wrap().
"包装和发送消息"[98]即是一个示例.
但是,由于无需展开使用gss_get_mic()的消息,因此所使用的CPU周期比gss_wrap()所使用的要少.
所以,不需要保密性的程序可能会使用gss_get_mic()来保护消息.
使用gss_get_mic()标记消息程序可以使用gss_get_mic()向消息添加加密MIC.
接受者可以通过调用gss_verify_mic()检查消息的MIC.
开发使用GSS-API的应用程序80面向开发者的OracleSolaris11安全性指南2014年7月与gss_wrap()相比,gss_get_mic()会针对消息和MIC生成单独的输出.
这意味着发送者应用程序必须既能发送消息又能发送随附的MIC.
更重要的是,接受者必须能够区分消息和MIC.
以下方法可确保对消息和MIC进行正确的处理:通过程序控制(即状态).
接受者应用程序可能知道对接收函数调用了两次,第一次用于获取消息,第二次用于获取消息的MIC.
通过标志.
发送者和接收者可以标记所包含令牌的类型.
通过用户定义的同时包含消息和MIC的令牌结构.
如果gss_get_mic()成功完成,则返回GSS_S_COMPLETE.
如果指定的QOP无效,则返回GSS_S_BAD_QOP.
有关更多信息,请参见gss_get_mic(3GSS).
使用gss_wrap()包装消息可以使用gss_wrap()函数来包装消息.
与gss_get_mic()一样,gss_wrap()也提供了MIC.
如果基础机制允许使用所请求的保密性,则gss_wrap()也会对指定的消息进行加密.
消息的接收者通过使用gss_unwrap()来展开消息.
与gss_get_mic()不同,gss_wrap()会将消息和MIC一起包装到传出消息中.
用于传送该包的函数只需调用一次.
在另一端,可以使用gss_unwrap()提取消息.
MIC对于应用程序是不可见的.
如果消息已成功包装,则gss_wrap()将返回GSS_S_COMPLETE.
如果所请求的QOP无效,则返回GSS_S_BAD_QOP.
有关gss_wrap()的示例,请参见"包装和发送消息"[98].
在GSS-API中处理包装大小问题使用gss_wrap()来包装消息会增加要发送的数据量.
由于所保护的消息包需要适应指定的传输协议,因此GSS-API提供了gss_wrap_size_limit()函数.
gss_wrap_size_limit()用于计算可以包装的消息的最大大小,使其不至于因变得过大而不适用于该协议.
应用程序可以在调用gss_wrap()之前截断超出该大小的消息.
在实际包装消息之前,请始终检查包装大小限制.
包装大小的增加取决于以下两个因素:用于进行转换的QOP算法是否调用了保密性缺省的QOP会因不同的GSS-API实现而异.
因此,即使指定了缺省的QOP,所包装消息的大小也会有所不同.
下图说明了这种可能性:开发使用GSS-API的应用程序第4章编写使用GSS-API的应用程序81无论是否应用了保密性,gss_wrap()仍然会增加消息的大小.
gss_wrap()可将MIC嵌入到所传送的消息中.
但是,对消息进行加密会进一步增加消息的大小.
此过程如下图所示.
开发使用GSS-API的应用程序82面向开发者的OracleSolaris11安全性指南2014年7月如果gss_wrap_size_limit()成功完,则返回GSS_S_COMPLETE.
如果指定的QOP无效,则返回GSS_S_BAD_QOP.
"包装和发送消息"[98]中提供了一个说明如何使用gss_wrap_size_limit()返回原始消息最大大小的示例.
成功完成此调用并不一定保证gss_wrap()可以保护长度为max-input-size个字节的消息.
能否保护消息取决于调用gss_wrap()时系统资源是否可用.
有关更多信息,请参见gss_wrap_size_limit(3GSS)手册页.
在GSS-API中检测顺序问题由于上下文启动器会向接受器传送顺序的数据包,因此某些机制允许上下文接受器检查顺序是否正确.
这些检查包括数据包是否按正确的顺序到达以及是否存在不需要的数据包重复.
请参见下图.
接受器可以在检验数据包和展开数据包的过程中检查这两种情况.
有关更多信息,请参见"展开消息"[115].
开发使用GSS-API的应用程序第4章编写使用GSS-API的应用程序83图4-8消息重放和消息失序开发使用GSS-API的应用程序84面向开发者的OracleSolaris11安全性指南2014年7月启动器可以使用gss_init_sec_context()来检查顺序,方法是通过GSS_C_REPLAY_FLAG或GSS_C_SEQUENCE_FLAG对req_flags参数应用逻辑OR.
在GSS-API中确认消息传送接受者展开或检验所传送的消息之后,会向发送者返回确认信息.
这意味着会发回该消息的MIC.
请考虑以下情况:消息没有由发送者进行包装,而只是通过gss_get_mic()使用MIC进行了标记.
图4-9"确认MIC数据"中说明的过程如下所示:1.
启动器使用gss_get_mic()对消息进行标记.
2.
启动器将该消息和MIC发送到接受器.
3.
接受器使用gss_verify_mic()检验该消息.
4.
接受器将该MIC发回到启动器.
5.
启动器使用gss_verify_mic()针对原始消息检验所收到的MIC.
开发使用GSS-API的应用程序第4章编写使用GSS-API的应用程序85图4-9确认MIC数据对于已包装的数据,gss_unwrap()函数从不生成单独的MIC,因此,接受者必须根据所收到的未包装的消息生成它.
图4-10"确认已包装的数据"中说明的过程如下所示:1.
启动器使用gss_wrap()包装消息.
2.
启动器发送已包装的消息.
3.
接受器使用gss_unwrap()展开该消息.
开发使用GSS-API的应用程序86面向开发者的OracleSolaris11安全性指南2014年7月4.
接受器调用gss_get_mic()以生成未包装的消息的MIC.
5.
接受器将派生的MIC发送到启动器.
6.
启动器使用gss_verify_mic()将所收到的MIC与原始消息进行比较.
对于已经为GSS-API数据分配的任何数据空间,应用程序应当取消对其进行分配.
相关函数为gss_release_buffer(3GSS)、gss_release_cred(3GSS)、gss_release_name(3GSS)和gss_release_oid_set(3GSS).
开发使用GSS-API的应用程序第4章编写使用GSS-API的应用程序87图4-10确认已包装的数据开发使用GSS-API的应用程序88面向开发者的OracleSolaris11安全性指南2014年7月清除GSS-API会话最后,发送和接收所有的消息,之后启动器应用程序和接受器应用程序就将完成操作.

此时,这两个应用程序都应当调用gss_delete_sec_context()以销毁共享的上下文.
gss_delete_sec_context()可删除与该上下文相关的局部数据结构.
对于已经为GSS-API数据分配的任何数据空间,确保应用程序取消对其进行分配是不错的方法.
这可以通过gss_release_buffer()、gss_release_cred()、gss_release_name()和gss_release_oid_set()函数来完成.
第5章GSS-API客户机示例895第5章GSS-API客户机示例本章将对典型的GSS-API客户机应用程序进行详细介绍,本章包含以下主题:"GSSAPI客户机示例概述"[89]"GSSAPI客户机示例:main()函数"[90]"打开与服务器的连接"[92]"建立与服务器的安全上下文"[93]"客户端上的各种GSSAPI上下文操作"[97]"包装和发送消息"[98]"读取和验证GSS-API客户机中的签名块"[101]"删除安全上下文"[102]GSSAPI客户机示例概述样例客户端程序gss-client可创建与服务器之间的安全上下文,建立安全参数,并将消息字符串发送到服务器.
此程序使用基于TCP的简单套接字连接机制来建立连接.
以下几节将提供有关gss-client工作方式的逐步说明.
由于gss-client是一个用于说明GSSAPI功能的样例程序,因此仅详细讨论该程序的相关部分.
这两个应用程序的完整源代码可在附录中找到,也可以从Oracle下载中心下载.
请参见http://www.
oracle.
com/technetwork/indexes/downloads/sdlc-decommission-333274.
html.
GSSAPI客户机示例结构gss-client应用程序执行以下步骤:1.
解析命令行.
2.
如果指定了机制,则为机制创建一个对象ID(ObjectID,OID).
否则,使用缺省机制,此为最常见的情况.
GSSAPI客户机示例:main()函数90面向开发者的OracleSolaris11安全性指南2014年7月3.
创建与服务器的连接.
4.
建立安全上下文.
5.
包装和发送消息.
6.
验证服务器是否已对消息进行正确的"签名".
7.
删除安全上下文.
运行GSSAPI客户机示例gss-client示例在命令行中采用以下形式:gss-client[-portport][-d][-mechmech]hostservice-name[-f]msgport-用于与host所指定的远程计算机建立连接的端口号.
-d标志-用于将安全凭证委托给服务器.
具体来说,deleg-flag变量会设置为GSS-API值GSS_C_DELEG_FLAG.
否则,deleg-flag设置为零.
mech-要使用的安全机制的名称,如Kerberosv5.
如果未指定任何机制,GSS-API将使用缺省机制.
host-服务器的名称.
service-name-客户机所请求的网络服务的名称.
telnet、ftp和login服务便是一些典型的示例.
msg-作为受保护的数据发送到服务器的字符串.
如果指定了-f选项,则msg是指从中读取字符串的文件名.
客户机应用程序的典型命令行可能如以下示例所示:%gss-client-port8080-d-mechkerberos_v5erebos.
engnfs"ls"以下示例不指定机制、端口或委托:%gss-clienterebos.
engnfs"ls"GSSAPI客户机示例:main()函数与所有C程序一样,该程序的外部shell也包含在入口点函数main()中.
main()可执行以下四种功能:解析命令行参数并为变量指定参数.
如果要使用缺省机制以外的机制,则调用parse_oid()来创建GSS-APIOID(即对象标识符).
对象标识符来自安全机制的名称,前提是已经提供了机制名称.

调用call_server(),这会实际创建上下文并发送数据.
GSSAPI客户机示例:main()函数第5章GSS-API客户机示例91发送数据之后,根据需要释放OID的存储空间.
main()例程的源代码如以下示例所示.
注-此示例的源代码也可以通过Oracle下载中心获取.
请参见http://www.
oracle.
com/technetwork/indexes/downloads/sdlc-decommission-333274.
html.
例5-1gss-client示例:main()intmain(argc,argv)intargc;char**argv;{char*msg;charservice_name[128];charhostname[128];char*mechanism=0;u_shortport=4444;intuse_file=0;OM_uint32deleg_flag=0,min_stat;display_file=stdout;/*Parsecommand-linearguments.
*/argc--;argv++;while(argc){if(strcmp(*argv,"-port")==0){argc--;argv++;if(!
argc)usage();port=atoi(*argv);}elseif(strcmp(*argv,"-mech")==0){argc--;argv++;if(!
argc)usage();mechanism=*argv;}elseif(strcmp(*argv,"-d")==0){deleg_flag=GSS_C_DELEG_FLAG;}elseif(strcmp(*argv,"-f")==0){use_file=1;}elsebreak;argc--;argv++;}if(argc!
=3)usage();if(argc>1){strcpy(hostname,argv[0]);}elseif(gethostname(hostname,sizeof(hostname))==-1){perror("gethostname");exit(1);}打开与服务器的连接92面向开发者的OracleSolaris11安全性指南2014年7月if(argc>2){strcpy(service_name,argv[1]);strcat(service_name,"@");strcat(service_name,hostname);}msg=argv[2];/*CreateGSSAPIobjectID.
*/if(mechanism)parse_oid(mechanism,&g_mechOid);/*Callservertocreatecontextandsenddata.
*/if(call_server(hostname,port,g_mechOid,service_name,deleg_flag,msg,use_file)h_addrtype;memcpy((char*)&saddr.
sin_addr,hp->h_addr,sizeof(saddr.
sin_addr));saddr.
sin_port=htons(port);if((s=socket(AF_INET,SOCK_STREAM,0))string",maj_stat,min_stat);return-1;}fprintf(stderr,"Nametypeofsourcenameis%.
*s.
\n",(int)oid_name.
length,(char*)oid_name.
value);(void)gss_release_buffer(&min_stat,&oid_name);/*Nowgetthenamessupportedbythemechanism*/maj_stat=gss_inquire_names_for_mech(&min_stat,mechanism,&mech_names);if(maj_stat!
=GSS_S_COMPLETE){display_status("inquiringmechnames",maj_stat,min_stat);return-1;}maj_stat=gss_oid_to_str(&min_stat,mechanism,&oid_name);if(maj_stat!
=GSS_S_COMPLETE){display_status("convertingoid->string",maj_stat,min_stat);return-1;}mechStr=(char*)__gss_oid_to_mech(mechanism);fprintf(stderr,"Mechanism%.
*s(%s)supports%dnames\n",(int)oid_name.
length,(char*)oid_name.
value,(mechStr==NULL"NULL":mechStr),mech_names->count);(void)gss_release_buffer(&min_stat,&oid_name);for(i=0;icount;i++){maj_stat=gss_oid_to_str(&min_stat,&mech_names->elements[i],&oid_name);if(maj_stat!
=GSS_S_COMPLETE){display_status("convertingoid->string",maj_stat,min_stat);return-1;}fprintf(stderr,"%d:%.
*s\n",i,(int)oid_name.
length,(char*)oid_name.
value);(void)gss_release_buffer(&min_stat,&oid_name);}(void)gss_release_oid_set(&min_stat,&mech_names);if(use_file){read_file(msg,&in_buf);}else{/*Wrapthemessage*/in_buf.
value=msg;in_buf.
length=strlen(msg)+1;}if(ret_flag&GSS_C_CONF_FLAG){state=1;elsestate=0;}maj_stat=gss_wrap(&min_stat,context,1,GSS_C_QOP_DEFAULT,&in_buf,&state,&out_buf);读取和验证GSS-API客户机中的签名块第5章GSS-API客户机示例101if(maj_stat!
=GSS_S_COMPLETE){display_status("wrappingmessage",maj_stat,min_stat);(void)close(s);(void)gss_delete_sec_context(&min_stat,&context,GSS_C_NO_BUFFER);return-1;}elseif(!
state){fprintf(stderr,"Warning!
Messagenotencrypted.
\n");}/*Sendtoserver*/if(send_token(s,&out_buf)=0){do{/*AcceptaTCPconnection*/if((s=accept(stmp,NULL,0))string",maj_stat,min_stat);从客户机接收数据114面向开发者的OracleSolaris11安全性指南2014年7月return-1;}fprintf(log,"AcceptedconnectionusingmechanismOID%.
*s.
\n",(int)oid_name.
length,(char*)oid_name.
value);(void)gss_release_buffer(&min_stat,&oid_name);}maj_stat=gss_display_name(&min_stat,client,client_name,&doid);if(maj_stat!
=GSS_S_COMPLETE){display_status("displayingname",maj_stat,min_stat);return-1;}maj_stat=gss_release_name(&min_stat,&client);if(maj_stat!
=GSS_S_COMPLETE){display_status("releasingname",maj_stat,min_stat);return-1;}return0;}sign_server()函数使用以下源代码,通过调用server_establish_context()来接受上下文.
/*Establishacontextwiththeclient*/if(server_establish_context(s,server_creds,&context,&client_name,&ret_flags)1-支持验证、完整性和保密性.
数字表示加密密钥长度.
保密性和完整性操作是通过安全机制执行的.
libsasl可以协调这些请求.
注-在协商过程中,SASL客户机会选择具有最大SSF的机制.
但是,实际所选的SASL机制可能随后会协商较小的SSF.
SASL中的通信应用程序通过libsaslAPI与libsasl进行通信.
libsasl可通过应用程序注册的回调方式请求其他信息.
应用程序不会直接调用插件,而只是通过libsasl进行调用.
一般情况下,插件会调用libsasl框架的插件,随后这些插件调用应用程序的回调.
SASL插件还可以直接调用应用程序,不过应用程序不知道调用来自插件还是来自libsasl.
回调在以下几个方面非常有用.
libsasl可以使用回调获取完成验证所需的信息.
libsasl使用者应用程序可以使用回调更改插件和配置数据的搜索路径、验证文件以及更改各种缺省行为.
服务器可以使用回调来更改授权策略、提供不同的口令验证方法以及获取口令更改信息.
客户机和服务器可以使用回调来指定错误消息的语言.
应用程序可以注册两种回调:全局回调和会话回调.
此外,libsasl定义了大量用于为不同种类的回调注册的回调标识符.
如果未注册给定类型的回调,则libsasl将执行缺省操作.
会话回调将覆盖全局回调.
如果为给定ID指定了会话回调,则不会为该会话调用全局回调.
某些回调必须是全局的,因为这些回调发生在会话之外.
简单验证和安全层(SimpleAuthenticationandSecurityLayer,SASL)介绍122面向开发者的OracleSolaris11安全性指南2014年7月以下实例要求使用全局回调:确定要装入的插件的搜索路径验证插件定位配置数据记录错误消息对libsasl或其插件的其他全局配置可以使用给定SASL回调ID的NULL回调函数来注册SASL回调.
NULL回调函数指示配备客户机的目的是为了提供所需的数据.
所有的SASL回调ID都以前缀SASL_CB_开头.
SASL提供以下可供客户机或服务器使用的回调:SASL_CB_GETOPT获取SASL选项.
选项用于修改libsasl(3LIB)和相关插件的行为.
可由客户机或服务器使用.
SASL_CB_LOG设置libsasl及其插件的日志记录函数.
缺省行为是使用syslog.
SASL_CB_GETPATH获取以冒号分隔的SASL插件搜索路径列表.
缺省SASL插件搜索路径取决于以下体系结构:32位SPARC体系结构:/usr/lib/sasl32位x86体系结构:/usr/lib/sasl64位SPARC体系结构:/usr/lib/sasl/sparcv9x64体系结构:/usr/lib/sasl/amd64SASL_CB_GETCONF获取SASL服务器的配置目录的路径.
缺省设置为/etc/sasl.
SASL_CB_LANGUAGE为客户机和服务器错误消息和客户机提示指定以逗号分隔的RFC1766语言代码列表(按优先级顺序).
缺省设置为i-default.
SASL_CB_VERIFYFILE验证配置文件和插件文件.
SASL提供以下仅限客户机使用的回调:SASL_CB_USER获取客户机用户名.
用户名与授权ID相同.
LOGNAME环境变量为缺省设置.
SASL_CB_AUTHNAME获取客户机验证名称.
SASL_CB_PASS获取基于客户机口令短语的机密.
SASL_CB_ECHOPROMPT获取给定质询提示的结果.
可以回显来自客户机的输入.
简单验证和安全层(SimpleAuthenticationandSecurityLayer,SASL)介绍第7章编写使用SASL的应用程序123SASL_CB_NOECHOPROMPT获取给定质询提示的结果.
不应回显来自客户机的输入.
SASL_CB_GETREALM设置用于验证的领域.
SASL提供以下仅限服务器使用的回调:SASL_CB_PROXY_POLICY检查是否授权经过验证的用户代表指定用户执行操作.
如果未注册此回调,则经过验证的用户与要授权的用户必须是同一用户.
如果这些ID不同,则验证将失败.
请使用服务器应用程序来维护非标准授权策略.
SASL_CB_SERVER_USERDB_CHECKPASS针对调用方提供的用户数据库验证纯文本口令.
SASL_CB_SERVER_USERDB_SETPASS在用户数据库中存储纯文本口令SASL_CB_CANON_USER调用应用程序提供的用户标准化函数.
首次初始化SASL库时,服务器和客户机会声明所有必要的全局回调.
执行SASL会话之前或期间可以使用全局回调.
初始化之前,回调将执行诸如装入插件、记录数据和读取配置文件之类的任务.
SASL会话开始时,可以声明其他回调.
这类回调可以根据需要覆盖全局回调.
SASL连接上下文libsasl使用SASL连接上下文维护SASL客户机和SASL服务器的每个SASL会话的状态.
每个上下文一次只能用于一个验证和安全会话.
维护的状态包括以下信息:连接信息,如服务、命名和地址信息以及协议标志特定于连接的回调用于协商SASLSSF的安全属性验证状态以及安全层信息SASL周期中的步骤下图显示了SASL生命周期中的步骤.
客户机操作显示在图的左侧,服务器操作显示在右侧.
中间的箭头显示客户机与服务器之间通过外部连接执行的交互操作.

简单验证和安全层(SimpleAuthenticationandSecurityLayer,SASL)介绍124面向开发者的OracleSolaris11安全性指南2014年7月图7-2SASL生命周期以下各节说明了生命周期中的步骤.
简单验证和安全层(SimpleAuthenticationandSecurityLayer,SASL)介绍第7章编写使用SASL的应用程序125libsasl初始化客户机调用sasl_client_init()来初始化libsasl以供客户机使用.
服务器调用sasl_server_init()来初始化libsasl以供服务器使用.
运行sasl_client_init()时,将装入SASL客户机、该客户机的机制以及该客户机的标准化插件.
同样,调用sasl_server_init()时,将装入SASL服务器、该服务器的机制、该服务器的标准化插件以及该服务器的auxprop插件.
调用sasl_client_init()后,可以使用sasl_client_add_plugin()和sasl_canonuser_add_plugin()来添加其他客户机插件.
在服务器端,调用sasl_server_init()后,可以通过sasl_server_add_plugin()、sasl_canonuser_add_plugin()和sasl_auxprop_add_plugin()来添加其他服务器插件.
依据体系结构,我们在OracleSolaris软件的以下目录中提供了SASL机制:32位SPARC体系结构:/usr/lib/sasl32位x86体系结构:/usr/lib/sasl64位SPARC体系结构:/usr/lib/sasl/sparcv9x64体系结构:/usr/lib/sasl/amd64可以使用SASL_CB_GETPATH回调覆盖缺省位置.
此时,可以设置所有必需的全局回调.
SASL客户机和服务器可能包括以下回调:SASL_CB_GETOPTSASL_CB_LOGSASL_CB_GETPATHSASL_CB_VERIFYFILE此外,SASL服务器还可能包括SASL_CB_GETCONF回调.
SASL会话初始化服务器和客户机通过协议建立连接.
要使用SASL执行验证,服务器和客户机可以分别使用sasl_server_new()和sasl_client_new()来创建SASL连接上下文.
SASL客户机和服务器可以使用sasl_setprop()来设置对机制强制执行安全限制的属性.
此方法使SASL使用者应用程序可以决定指定SASL连接上下文的最小SSF、最大SSF和安全属性.
#defineSASL_SEC_NOPLAINTEXT0x0001#defineSASL_SEC_NOACTIVE0x0002#defineSASL_SEC_NODICTIONARY0x0004#defineSASL_SEC_FORWARD_SECRECY0x0008#defineSASL_SEC_NOANONYMOUS0x0010简单验证和安全层(SimpleAuthenticationandSecurityLayer,SASL)介绍126面向开发者的OracleSolaris11安全性指南2014年7月#defineSASL_SEC_PASS_CREDENTIALS0x0020#defineSASL_SEC_MUTUAL_AUTH0x0040注-验证和安全层可由客户机/服务器协议提供,也可由libsasl外部的某些其他机制提供.
在这类情况下,可以使用sasl_setprop()来设置外部验证ID或外部SSF.
例如,请考虑协议对服务器结合使用SSL和客户机验证的情况.
在这种情况下,外部验证标识可以为客户机的主题名称.
外部SSF可以为密钥大小.
对于服务器,libsasl可以依据安全属性和外部SSF来确定可用的SASL机制.
客户机可以通过协议从SASL服务器获取可用的SASL机制.
为使SASL服务器可以创建SASL连接上下文,服务器应该调用sasl_server_new().
可以重复使用不再使用的现有SASL连接上下文.
但是,可能需要重置以下参数:#defineSASL_DEFUSERREALM3/*defaultrealmpassedtoserver_neworsetwithsetprop*/#defineSASL_IPLOCALPORT8/*iplocalportstringpassedtoserver_new*/#defineSASL_IPREMOTEPORT9/*ipremoteportstringpassedtoserver_new*/#defineSASL_SERVICE12/*servicepassedtosasl_*_new*/#defineSASL_SERVERFQDN13/*serverFQDNpassedtosasl_*_new*/可以修改sasl_client_new()和sasl_server_new()的任何参数,但回调和协议标志除外.
服务器和客户机还可以使用sasl_setprop()来指定以下属性,从而建立安全策略并设置连接特定参数:#defineSASL_SSF_EXTERNAL100/*externalSSFactive(sasl_ssf_t*)*/#defineSASL_SEC_PROPS101/*sasl_security_properties_t*/#defineSASL_AUTH_EXTERNAL102/*externalauthenticationID(constchar*)*/SASL_SSF_EXTERNAL-用于设置强度因子,即密钥中的位数SASL_SEC_PROPS-用于定义安全策略SASL_AUTH_EXTERNAL-外部验证ID服务器可以调用sasl_listmech()来获取满足安全策略的可用SASL机制的列表.
一般情况下,客户机可以采用与协议相关的方式从服务器获取可用机制的列表.

下图说明了SASL会话的初始化过程.
在此图和后续的图中,为了简单起见,省略了通过协议进行传输后的数据检查.
简单验证和安全层(SimpleAuthenticationandSecurityLayer,SASL)介绍第7章编写使用SASL的应用程序127图7-3SASL会话初始化SASL验证验证根据使用的安全机制采用不同数量的客户机和服务器步骤.
SASL客户机将调用sasl_client_start()和要使用的安全机制列表.
此列表通常来自服务器.
libsasl将依据可用机制和客户机的安全策略选择要用于此SASL会话的最佳机制.
客户机的安全策略控制允许的机制.
选定的机制由sasl_client_start()返回.
有时,客户机的安全机制需要其他验证信息.
对于已注册的回调,libsasl将调用指定的回调,除简单验证和安全层(SimpleAuthenticationandSecurityLayer,SASL)介绍128面向开发者的OracleSolaris11安全性指南2014年7月非回调函数为NULL.
如果回调函数为NULL,则libsasl将返回SASL_INTERACT和对所需信息的请求.
如果返回SASL_INTERACT,则应使用请求的信息调用sasl_client_start().
如果sasl_client_start()返回SASL_CONTINUE或SASL_OK,则客户机应向服务器发送包含生成的所有验证数据的选定机制.
如果返回任何其他值,则表示出现了错误.
例如,任何机制可能都不可用.
服务器将接收客户机选定的机制,以及所有的验证数据.
随后,服务器将调用sasl_server_start()来初始化此会话的机制数据.
sasl_server_start()还将处理所有的验证数据.
如果sasl_server_start()返回SASL_CONTINUE或SASL_OK,则服务器将发送验证数据.
如果sasl_server_start()返回任何其他值,则表示出现了错误,如不可接受的机制或验证失败.
必须中止验证.
应该释放或重复使用SASL上下文.
下图说明了此部分的验证过程.
简单验证和安全层(SimpleAuthenticationandSecurityLayer,SASL)介绍第7章编写使用SASL的应用程序129图7-4SASL验证:发送客户机数据如果服务器对sasl_server_start()的调用返回SASL_CONTINUE,则服务器将继续与客户机进行通信,以获取所有必要的验证信息.
后续步骤的数目取决于机制.
如果需要,客户机可以调用sasl_client_step()来处理来自服务器的验证数据并生成回复.
同样,服务器可以调用sasl_server_step()来处理来自客户机的验证并相应地生成回复.
此交换将持续进行下去,直到验证完成或出现错误为止.
如果返回SASL_OK,则指示对客户机或服务器的验证已成功完成.
SASL机制可能仍然具有要发送给另一端的其他数据,因此另一端可以完成验证.
在两端都完成验证后,服务器和客户机便可查询彼此的属性.
下图显示了服务器与客户机之间为传输其他验证数据所执行的交互.
简单验证和安全层(SimpleAuthenticationandSecurityLayer,SASL)介绍130面向开发者的OracleSolaris11安全性指南2014年7月图7-5SASL验证:处理服务器数据SASL示例第7章编写使用SASL的应用程序131SASL保密性和完整性要检查安全层,请使用sasl_getprop(3SASL)函数查看安全强度因子(securitystrengthfactor,SSF)的值是否大于0.
如果已协商安全层,则成功验证后客户机和服务器必须使用生成的SSF.
在客户机与服务器之间交换数据的方式与验证的方式相似.
通过协议向客户机或服务器发送数据之前,将sasl_encode()应用于数据.
在接收端,使用sasl_decode()对数据进行解码.
如果未协商安全层,则无需SASL连接上下文.
随后,即可处理或重复使用该上下文.
释放SASL会话只有在不会重复使用会话时,才应释放SASL连接上下文.
sasl_dispose()将释放SASL连接上下文以及所有关联的资源和机制.
调用sasl_done()之前,必须处理SASL连接上下文.
sasl_done()不负责释放SASL连接的上下文资源.
请参见"libsasl清除"[131].
释放SASL会话时,将通知关联的机制所有状态均可释放.
只有在不会重复使用SASL会话时,才应释放该会话.
否则,其他会话可以重复使用SASL状态.
客户机和服务器都使用sasl_dispose()释放SASL连接上下文.
libsasl清除此步骤可释放SASL库和插件中的所有资源.
客户机和服务器可以调用sasl_done()来释放libsasl()资源并卸载所有的SASL插件.
sasl_done()不会释放SASL连接上下文.
请注意,如果应用程序同时为SASL客户机和SASL服务器,则sasl_done()将同时释放SASL客户机和SASL服务器资源.
您不能仅释放客户机或服务器的资源.
注意-库不应调用sasl_done().
应用程序在调用sasl_done()时务必要谨慎,避免与可能使用libsasl的任何库发生干扰.
SASL示例本节说明客户机应用程序与服务器应用程序之间的典型SASL会话.
该示例包含以下步骤:1.
该客户机应用程序初始化libsasl.
该客户机应用程序设置以下全局回调:SASL示例132面向开发者的OracleSolaris11安全性指南2014年7月SASL_CB_GETREALMSASL_CB_USERSASL_CB_AUTHNAMESASL_CB_PASSSASL_CB_GETPATHSASL_CB_LIST_END2.
该服务器应用程序初始化libsasl.
该服务器应用程序设置以下全局回调:SASL_CB_LOGSASL_CB_LIST_END3.
客户机将创建SASL连接上下文,设置安全属性并从服务器请求可用机制的列表.
4.
服务器将创建SASL连接上下文,设置安全属性,获取适当SASL机制的列表并向客户机发送该列表.
5.
客户机将接收可用机制的列表,选择一种机制,并向服务器发送所选择的机制以及所有验证数据.
6.
随后,客户机和服务器将交换SASL数据,直到验证和安全层协商完成为止.
7.
验证完成后,客户机和服务器将确定是否已协商安全层.
客户机将对测试消息进行编码.
然后,会将该消息发送给服务器.
服务器也会确定经过验证的用户的用户名和该用户的领域.
8.
服务器将接收、解码和显示编码的消息.
9.
客户机将调用sasl_dispose()以释放客户机的SASL连接上下文.
随后,客户机将调用sasl_done()以释放libsasl资源.
10.
服务器将调用sasl_dispose()以释放客户机连接上下文.
以下是客户机与服务器之间的对话.
执行调用时,会显示对libsasl的每个调用.
每次数据传输都由发送者和接收者指明.
数据采用编码的形式显示,并在前面加上表示来源的字符:C:表示来自于客户机,S:表示来自于服务器.
附录E,SASL示例的源代码提供了这两种应用程序的源代码.
客户机%doc-sample-client***Callingsasl_client_init()toinitializelibsaslforclientuse******Callingsasl_client_new()tocreateclientSASLconnectioncontext******Callingsasl_setprop()tosetsaslcontextsecurityproperties***Waitingformechanismlistfromserver.
.
.
服务器%doc-sample-serverdigest-md5***Callingsasl_server_init()toinitializelibsaslforserveruse******Callingsasl_server_new()tocreateserverSASLconnectioncontext******Callingsasl_setprop()tosetsaslcontextsecurityproperties***Forcinguseofmechanismdigest-md5Sendinglistof1mechanism(s)S:ZGlnZXN0LW1kNQ==SASL示例第7章编写使用SASL的应用程序133客户机S:ZGlnZXN0LW1kNQ==received10bytemessagegot'digest-md5'Choosingbestmechanismfrom:digest-md5***Callingsasl_client_start()***UsingmechanismDIGEST-MD5Sendinginitialresponse.
.
.
C:RElHRVNULU1ENQ==Waitingforserverreply.
.
.
服务器C:RElHRVNULU1ENQ==got'DIGEST-MD5'***Callingsasl_server_start()***Sendingresponse.
.
.
S:bm9uY2U9IklicGxhRHJZNE4Z1gyVm5lQzl5MTZOYWxUOVcvanUrcmp5YmRqaHM\sbT0iam0xMTQxNDIiLHFvcD0iYXV0aCxhdXRoLWludCxhdXRoLWNvbmYiLGNpcGhlcj0ic\QwLHJjNC01NixyYzQiLG1heGJ1Zj0yMDQ4LGNoYXJzZXQ9dXRmLTgsYWxnb3JpdGhtPW1k\XNzWaitingforclientreply.
.
.
客户机S:bm9uY2U9IklicGxhRHJZNE4Z1gyVm5lQzl5MTZOYWxUOVcvanUrcmp5YmRqaHM\sbT0iam0xMTQxNDIiLHFvcD0iYXV0aCxhdXRoLWludCxhdXRoLWNvbmYiLGNpcGhlcj0ic\QwLHJjNC01NixyYzQiLG1heGJ1Zj0yMDQ4LGNoYXJzZXQ9dXRmLTgsYWxnb3JpdGhtPW1k\XNzreceived171bytemessagegot'nonce="IbplaDrY4N4szhgX2VneC9y16NalT9W/ju+rjybdjhs=",\realm="jm114142",qop="auth,auth-int,auth-conf",cipher="rc4-40,rc4-56,\rc4",maxbuf=2048,charset=utf-8,algorithm=md5-sess'***Callingsasl_client_step()***Pleaseenteryourauthorizationname:zzzzPleaseenteryourauthenticationname:zzzzPleaseenteryourpassword:zz***Callingsasl_client_step()***Sendingresponse.
.
.
C:dXNlcm5hbWU9Inp6enoiLHJlYWxtPSJqbTExNDE0MiIsbm9uY2U9IklicGxhRHJZNE4\yVm5lQzl5MTZOYWxUOVcvanUrcmp5YmRqaHM9Iixjbm9uY2U9InlqZ2hMVmhjRFJMa0Fob\tDS0p2WVUxMUM4V1NycjJVWm5IR2Vkclk9IixuYz0wMDAwMDAwMSxxb3A9YXV0aC1jb25m\Ghlcj0icmM0IixtYXhidWY9MjA0OCxkaWdlc3QtdXJpPSJyY21kLyIscmVzcG9uc2U9OTY\ODI1MmRmNzY4YTJjYzkxYjJjZDMyYTk0ZWM=Waitingforserverreply.
.
.
服务器C:dXNlcm5hbWU9Inp6enoiLHJlYWxtPSJqbTExNDE0MiIsbm9uY2U9IklicGxhRHJZNE4\yVm5lQzl5MTZOYWxUOVcvanUrcmp5YmRqaHM9Iixjbm9uY2U9InlqZ2hMVmhjRFJMa0Fob\tDS0p2WVUxMUM4V1NycjJVWm5IR2Vkclk9IixuYz0wMDAwMDAwMSxxb3A9YXV0aC1jb25m\Ghlcj0icmM0IixtYXhidWY9MjA0OCxkaWdlc3QtdXJpPSJyY21kLyIscmVzcG9uc2U9OTY\ODI1MmRmNzY4YTJjYzkxYjJjZDMyYTk0ZWM=got'username="zzzz",realm="jm114142",\nonce="IbplaDrY4N4szhgX2VneC9y16NalT9W/ju+rjybdjhs=",\cnonce="yjghLVhcDRLkAhoirwKCKJvYU11C8WSrr2UZnHGedrY=",\nc=00000001,qop=auth-conf,cipher="rc4",maxbuf=2048,digest-uri="rcmd/",\response=966e978252df768a2cc91b2cd32a94ec'***Callingsasl_server_step()***Sendingresponse.
.
.
S:cnNwYXV0aD0yYjEzMzRjYzU4NTE4MTEwOWM3OTdhMjUwYjkwMzk3OQ==服务提供者的SASL134面向开发者的OracleSolaris11安全性指南2014年7月Waitingforclientreply.
.
.
客户机S:cnNwYXV0aD0yYjEzMzRjYzU4NTE4MTEwOWM3OTdhMjUwYjkwMzk3OQ==received40bytemessagegot'rspauth=2b1334cc585181109c797a250b903979'***Callingsasl_client_step()***C:Negotiationcomplete***Callingsasl_getprop()***Username:zzzzSSF:128Waitingforencodedmessage.
.
.
服务器Waitingforclientreply.
.
.
C:got''***Callingsasl_server_step()***Negotiationcomplete***Callingsasl_getprop()togetusername,realm,ssf***Username:zzzzRealm:22c38SSF:128***Callingsasl_encode()***sendingencryptedmessage'srvmessage1'S:AAAAHvArjnAvDFuMBqAAxkqdumzJB6VD1oajiwABAAAAAA==客户机S:AAAAHvArjnAvDFuMBqAAxkqdumzJB6VD1oajiwABAAAAAA==received34bytemessagegot''***Callingsasl_decode()***receiveddecodedmessage'srvmessage1'***Callingsasl_encode()***sendingencryptedmessage'clientmessage1'C:AAAAIRdkTEMYOn9X4NXkxPc3OTFvAZUnLbZANqzn6gABAAAAAA==***Callingsasl_dispose()toreleaseclientSASLconnectioncontext******Callingsasl_done()toreleaselibsaslresources***服务器Waitingforencryptedmessage.
.
.
C:AAAAIRdkTEMYOn9X4NXkxPc3OTFvAZUnLbZANqzn6gABAAAAAA==got''***Callingsasl_decode()***receiveddecodedmessage'clientmessage1'***Callingsasl_dispose()toreleaseclientSASLconnectioncontext***服务提供者的SASL本节介绍如何创建用于为SASL应用程序提供机制和其他服务的插件.
注-由于导出规程,对于非OracleSolaris客户机/服务器机制插件,OracleSolarisSASLSPI不支持安全层.
因此,非OracleSolaris客户机/服务器机制插件不能提供完整性和保密性服务.
OracleSolaris客户机/服务器机制插件没有该限制.
服务提供者的SASL第7章编写使用SASL的应用程序135SASL插件概述SASL服务提供者接口(ServiceProviderInterface,SPI)可实现插件与libsasl库之间的通信.
SASL插件通常作为共享库来实现.
单个共享库可以具有一个或多个不同类型的SASL插件.
位于共享库中的插件是由libsasl通过dlopen(3C)函数动态打开的.
还可以将插件静态绑定至调用libsasl的应用程序.
使用sasl_client_add_plugin()函数或sasl_server_add_plugin()函数装入这些种类的插件,具体视应用程序是客户机还是服务器而定.
OracleSolaris操作系统中的SASL插件具有以下要求:共享库中的插件必须位于有效的可执行目标文件(文件扩展名最好是.
so)中.
插件必须位于可验证的位置中.
SASL_CB_VERIFYFILE回调用于验证插件.
插件必须包含正确的入口点.
SASL客户机的插件版本必须与SASL服务器的对应插件版本匹配.
插件需要能够成功初始化.
二进制类型的插件必须与libsasl的二进制类型匹配.
SASL插件分为四种类别:客户机机制插件服务器机制插件标准化插件Auxprop插件sasl_client_init()函数导致SASL客户机装入所有可用的客户机插件.
sasl_server_init()函数导致SASL服务器装入服务器插件、标准化插件和auxprop插件.
调用sasl_done()时将卸载所有插件.
为查找插件,libsasl使用SASL_CB_GETPATH回调函数或缺省路径.
SASL_CB_GETPATH返回以冒号分隔的目录列表,以供在其中查找插件.
如果SASL使用者指定了SASL_CB_GETPATH回调,则libsasl将使用返回的搜索路径.
否则,SASL使用者可以使用与二进制类型对应的缺省路径.
下面列出了缺省路径和二进制类型对应关系:64位SPARC体系结构:/usr/lib/sasl/sparcv9x64体系结构:/usr/lib/sasl/amd6432位SPARC体系结构:/usr/lib/sasl32位x86体系结构:/usr/lib/sasl在装入过程中,libsasl将调用最新支持的插件版本.
插件将返回该版本以及描述该插件的结构.
如果该版本合格,则libsasl即可装入该插件.
当前版本号为SASL_UTILS_VERSION.
服务提供者的SASL136面向开发者的OracleSolaris11安全性指南2014年7月初始化插件后,插件与libsasl之间的后续通信通过必须建立的结构执行.
插件使用sasl_utils_t结构来调用libsasl.
libsasl库使用以下结构中的入口点与插件进行通信:sasl_out_params_tsasl_client_params_tsasl_server_params_tsasl_client_plug_tsasl_server_plug_tsasl_canonuser_plug_tsasl_auxprop_plug_t可以在SASL头文件中找到这些结构的源代码.
这些结构将在下一节中进行介绍.
SASL插件的重要结构libsasl与插件之间的通信是通过以下结构完成的:sasl_utils_t-sasl_utils_t结构包含大量实用程序函数以及三种上下文.
此结构包含大量可为插件编写人员提供方便的实用程序函数.
许多函数是指向libsasl中的公共接口的指针.
插件不需要直接调用libsasl,除非出于某种原因,插件需要是SASL使用者.
libsasl将为sasl_utils_t创建三种上下文:sasl_conn_t*connsasl_rand_t*rpoolvoid*getopt_context在某些情况(如装入插件)下,sasl_utils_t中的conn变量实际上与连接没有关联.
在另外一些情况下,conn是SASL使用者的SASL连接上下文.
rpool变量用于随机数生成函数.
getopt_context是应该与getopt()函数结合使用的上下文.
请参见sasl_getopt_t(3SASL)、sasl_log_t(3SASL)和sasl_getcallback_t(3SASL).
sasl_out_params_t-libsasl用于创建sasl_out_params_t结构并将该结构传递给客户机或服务器中的mech_step().
此结构可以将以下信息传达给libsasl:验证状态、authid、authzid、maxbuf、协商的ssf以及数据编码和解码信息.
sasl_client_params_t-libsasl使用sasl_client_params_t结构将客户机状态传递给SASL客户机机制.
客户机机制的mech_new()、mech_step()和mech_idle()入口点用于发送此状态数据.
canon_user_client()入口点还需要同时传递客户机状态.
服务提供者的SASL第7章编写使用SASL的应用程序137sasl_server_params_t-sasl_server_params_t结构在服务器端执行和sasl_client_params_t类似的功能.
客户机插件客户机插件用于管理SASL协商的客户端.
客户机插件通常随对应的服务器插件一同打包.
客户机插件包含一种或多种客户端SASL机制.
每种SASL客户机机制都支持验证、完整性和保密性(后两者是可选的).
每种SASL客户机机制都提供有关该机制的功能的信息:最大SSF最大安全标志插件功能用于使用插件的回调和提示ID客户机插件必须导出sasl_client_plug_init().
libsasl将调用sasl_client_plug_init()来初始化客户机的插件.
插件将返回sasl_client_plug_t结构.
sasl_client_plug_t将为libsasl提供以下用于调用机制的入口点:mech_new()-客户机通过调用sasl_client_start()(使用mech_new())来启动连接.
mech_new()将执行特定于机制的初始化.
如有必要,将分配连接上下文.
mech_step()-mech_step()可由sasl_client_start()和sasl_client_step()进行调用.
调用mech_new()后,mech_step()将对客户端执行验证.
如果验证成功,mech_step()将返回SASL_OK.
如果需要更多数据,则将返回SASL_CONTINUE.
如果验证失败,则将返回SASL错误代码.
如果出现错误,则将调用seterror().
如果验证成功,则mech_step()必须返回包含相关安全层信息和回调的sasl_out_params_t结构.
canon_user()函数是此结构的一部分.
客户机收到验证和授权ID时,必须调用canon_user().
mech_dispose()-mech_dispose()是在安全关闭上下文时进行调用的.
mech_dispose()由sasl_dispose()进行调用.
mech_free()-mech_free()是在libsasl关闭时进行调用的.
插件的所有其余全局状态都是通过mech_free()释放的.
服务器插件服务器插件用于管理SASL协商的服务器端.
服务器插件通常随对应的客户机插件一同打包.
服务器插件包含一种或多种服务器端SASL机制.
每种SASL服务器机制都支持验证、完整性和保密性(后两者是可选的).
每种SASL服务器机制都提供有关该机制的功能的信息:最大SSF服务提供者的SASL138面向开发者的OracleSolaris11安全性指南2014年7月最大安全标志插件功能用于使用插件的回调和提示ID服务器插件必须导出sasl_server_plug_init().
libsasl将调用sasl_server_plug_init()来初始化服务器的插件.
插件将返回sasl_server_plug_t结构.
sasl_server_plug_t结构将为libsasl提供以下用于调用机制的入口点:mech_new()-服务器通过调用sasl_server_start()(使用mech_new())来启动连接.
mech_new()将执行特定于机制的初始化.
如有必要,mech_new()将分配连接上下文.
mech_step()-mech_step()可由sasl_server_start()和sasl_server_step()进行调用.
调用mech_new()后,mech_step()将对服务器端执行验证.
如果验证成功,mech_step()将返回SASL_OK.
如果需要更多数据,则将返回SASL_CONTINUE.
如果验证失败,则将返回SASL错误代码.
如果出现错误,则将调用seterror().
如果验证成功,则mech_step()必须返回包含相关安全层信息和回调的sasl_out_params_t结构.
canon_user()函数是此结构的一部分.
服务器收到验证和授权ID时,必须调用canon_user().
调用canon_user()函数将导致propctx被填充.
标准化验证之前,应执行所有必需的辅助属性请求.
标准化验证后,应执行授权ID查找.
返回SASL_OK之前,mech_step()函数必须填充所有相关的sasl_out_params_t字段.
这些字段可以执行以下函数:doneflag-指示完整的交换maxoutbuf-指示安全层的最大输出大小mech_ssf-为安全层提供SSFencode()-由sasl_encode()、sasl_encodev()和sasl_decode()进行调用decode()-由sasl_encode()、sasl_encodev()和sasl_decode()进行调用encode_context()-由sasl_encode()、sasl_encodev()和sasl_decode()进行调用decode_context()-由sasl_encode()、sasl_encodev()和sasl_decode()进行调用mech_dispose()-mech_dispose()是在安全关闭上下文时进行调用的.
mech_dispose()由sasl_dispose()进行调用.
mech_free()-mech_free()是在libsasl关闭时进行调用的.
插件的所有其余全局状态都是通过mech_free()释放的.
setpass()用于设置用户的口令.
setpass()使机制可以具有内部口令.
mech_avail()由sasl_listmech()进行调用,以检查机制是否可用于给定用户.
mech_avail()可以创建新的上下文,进而避免对mech_new()的调用.
只要性能不受影响,就应使用此方法创建上下文.
服务提供者的SASL第7章编写使用SASL的应用程序139用户标准化插件标准化插件为客户端和服务器端的验证和授权名称的备用标准化提供支持.
sasl_canonuser_plug_init()用于装入标准化插件.
标准化插件具有以下要求:必须将标准化的名称复制到输出缓冲区.
可以将同一输入缓冲区用作输出缓冲区.
当仅存在一个验证ID或授权ID时,标准化插件必须发挥作用.
用户标准化插件必须导出sasl_canonuser_init()函数.
sasl_canonuser_init()函数必须返回sasl_canonuser_plug_t以建立必要的入口点.
用户标准化插件必须至少实现sasl_canonuser_plug_t结构的一个canon_user_client()成员或canon_user_server()成员.
辅助属性(auxprop)插件Auxprop插件为查找SASL服务器的authid和authzid的辅助属性提供支持.
例如,应用程序可能需要查找内部验证的用户口令.
sasl_auxprop_plug_init()函数用于初始化auxprop插件并返回sasl_auxpropr_plug_t结构.
要成功实现auxprop插件,必须实现sasl_auxprop_plug_t结构的auxprop_lookup成员.
标准化用户名后,应使用标准化的用户名来调用auxprop_lookup()函数.
随后,插件即可执行请求的辅助属性所需的所有查询.
注-OracleCorporation当前不提供auxprop插件.
SASL插件开发指南本节提供了一些用于开发SASL插件的其他建议.
SASL插件中的错误报告适当的错误报告可以帮助跟踪验证问题和其他调试问题.
建议插件开发者使用sasl_utils_t结构中的sasl_seterror()回调为给定连接提供详细的错误信息.
SASL插件中的内存分配在SASL中分配内存的一般规则是在不再需要已分配的任何内存时将其释放.
遵循此规则可以提高性能和可移植性,而且可以避免内存泄漏.
服务提供者的SASL140面向开发者的OracleSolaris11安全性指南2014年7月设置SASL协商顺序插件机制可以设置客户机和服务器通过以下标志执行SASL会话的顺序:SASL_FEAT_WANT_CLIENT_FIRST-客户端将开始交换.
SASL_FEAT_WANT_SERVER_LAST-服务器将最终数据发送到客户机.
如果未设置任何标志,则机制插件将在内部设置顺序.
在这种情况下,机制必须同时检查客户机和服务器中需要发送的数据.
请注意,只有在协议允许初始响应时,才有可能出现客户机首先发送的情况.
服务器最后发送的情况要求在步骤函数返回SASL_OK时插件可设置*serverout.
永远不让服务器最后发送的那些机制必须将*serverout设置为NULL.
始终让服务器最后发送的那些机制需要将*serverout指向成功数据.
第8章OracleSolaris加密框架介绍1418第8章OracleSolaris加密框架介绍OracleSolaris加密框架是一种体系结构,可使OracleSolaris操作系统中的应用程序使用或提供加密服务.
与框架的所有交互都基于RSASecurityInc.
的PKCS#11加密令牌接口(CryptographicTokenInterface,Cryptoki).
PKCS#11是RSALaboratories(RSASecurity,Inc.
的研究机构)的产品.
本章提供了以下有关OracleSolaris加密框架的主题:"加密框架概述"[142]"加密框架的组件"[143]"加密开发者需要了解的内容"[144]OracleSolaris加密术语获取加密服务的应用程序、库或内核模块称作使用者.
通过框架向使用者提供加密服务的应用程序称作提供者,或称作插件.
用来实现加密操作的软件称作机制.
机制不只是算法,还包括算法的应用方式.
例如,应用于验证机制的DES(数据加密标准)算法被视为一个单独的机制,应用于逐块加密的DES则是另一个机制.
令牌是可以执行加密的设备的抽象术语.
此外,令牌可以存储要用在加密操作中的信息.
一个令牌可以支持一个或多个机制.
令牌可以代表硬件,例如加速器板中的部件.

仅代表软件的令牌称作软令牌.
令牌可以插入到插槽中,插槽也是一种物理比喻.
插槽是使用加密服务的应用程序的连接点.
除了提供者的特定插槽以外,OracleSolaris实现还提供一个名为metaslot的特殊插槽.
metaslot是OracleSolaris加密框架库(libpkcs11.
so)的一个组件.
metaslot起着一个虚拟插槽的作用,该插槽具有已经安装在框架中的所有令牌和插槽的组合功能.
使用metaslot,应用程序可以通过单个插槽与任何可用加密服务之间有效地实现透明连接.
当应用程序请求加密服务时,metaslot将指向最适合的插槽,这简化了插槽的选择过程.
在某些情况下可能需要一个不同的插槽,此时,应用程序必须显式地执行单独的搜索.
metaslot由系统自动启用,而且只能由系统管理员通过显式的操作来禁用.
会话是令牌与使用加密服务的应用程序之间的连接.
PKCS#11标准使用两种对象:令牌对象和会话对象.
会话对象是暂时的对象,即它只在会话期间存在.
在会话结束之后仍存在的对象称作令牌对象.
加密框架概述142面向开发者的OracleSolaris11安全性指南2014年7月令牌对象的缺省位置是$HOME/.
sunw/pkcs11_softtoken.
或者,可以将令牌对象存储到$SOFTTOKEN_DIR/pkcs11_softtoken中.
专用令牌对象由个人识别码(personalidentificationnumber,PIN)保护.
要创建或更改令牌对象,需要对用户进行验证,除非用户访问的是专用令牌对象.
加密框架概述加密框架是OracleSolarisOS中OracleCorporation和第三方供应商提供加密服务的部分.
加密框架提供以下各种服务:消息加密和消息摘要消息验证代码(MessageAuthenticationCode,MAC)数字签名用来访问加密服务的应用编程接口(ApplicationProgrammerInterface,API)用来提供加密服务的服务提供者接口(ServiceProviderInterface,SPI)用来管理加密资源的管理命令下图提供了加密框架的概述.
该图中的浅灰色阴影表示加密框架的用户级部分,深灰色阴影表示加密框架的内核级部分,带有斜条纹的背景表示专用软件部分.

加密框架的组件第8章OracleSolaris加密框架介绍143图8-1OracleSolaris加密框架概述加密框架的组件加密框架的组件如下所述:加密开发者需要了解的内容144面向开发者的OracleSolaris11安全性指南2014年7月libpkcs11.
so-该框架通过RSASecurityInc.
的PKCS#11加密令牌接口(CryptographicTokenInterface,Cryptoki)提供访问权限.
应用程序需要链接到libpkcs11.
so库,该库实现RSAPKCS#11标准.
可插接式接口-可插接式接口是PKCS#11加密服务的服务提供者接口(ServiceProviderInterface,SPI),这些服务由OracleCorporation和第三方开发者提供.
提供者是用户级库,可以通过能够从硬件或软件使用的加密服务实现.
pkcs11_softtoken.
so-一个专用共享目标文件,其中包含OracleCorporation提供的用户级加密机制.
pkcs11_softtoken(5)库实现RSAPKCS#11v2.
11标准.
pkcs11_kernel.
so-用来访问内核级加密机制的专用共享目标文件.
pkcs11_kernel(5)可用来实现RSAPKCS#11v2.
11规范.
pkcs11_kernel.
so为插入到内核的服务提供者接口中的加密服务提供一个PKCS#11用户接口.
/dev/crypto伪设备驱动程序-使用内核级加密机制的专用伪设备驱动程序.
提供此信息的目的在于避免意外删除伪设备驱动程序.
调度程序/负载平衡器-一种内核软件,负责协调对加密服务请求的使用并对这些请求进行负载平衡和分发.
内核编程接口-用于加密服务的内核级使用者的接口.
IPSec(Internet协议安全)协议和KerberosGSS机制是典型的加密使用者.
注-只有通过与OracleCorporation签订特殊合同才会提供该接口.
有关更多信息,请向solaris-crypto-req_ww@oracle.
com发送电子邮件.
OracleHW和SW加密提供者-由OracleCorporation提供的内核级加密服务.
HW是指硬件加密服务,如加速器板.
SW是指提供加密服务的内核模块(如加密算法的实现).
内核加密框架守护进程-一种专用守护进程,负责管理用于加密操作的系统资源.
还负责验证加密提供者.
模块验证库-一种专用库,用于验证OracleSolaris加密框架所导入的所有库的完整性和真实性.
elfsign-可验证插入到OracleSolaris加密框架中的二进制文件(即elf目标文件)签名的实用程序.
/dev/cryptoadm伪设备驱动程序-一种专用的伪设备驱动程序,由cryptoadm(1M)用来管理内核级加密机制.
提供此信息的目的在于避免意外删除伪设备驱动程序.

cryptoadm-供管理员管理加密服务的用户级命令.
使用cryptoadm执行的典型任务就是列出加密提供者及其功能,使用cryptoadm还可以根据安全策略禁用和启用加密机制.
加密开发者需要了解的内容本节介绍可插入到OracleSolaris加密框架中的四种应用程序的开发要求.
加密开发者需要了解的内容第8章OracleSolaris加密框架介绍145用户级使用者的开发要求要开发用户级使用者,请执行以下所有操作:包括.
所有的调用都只通过PKCS#11接口执行.
链接到libpkcs11.
so.
库不应当调用C_Finalize()函数.
有关更多信息,请参见第9章编写用户级加密应用程序.
内核级使用者的开发要求要开发内核级使用者,请执行以下所有操作:包括和.
所有的调用都通过内核编程接口执行.
146面向开发者的OracleSolaris11安全性指南2014年7月第9章编写用户级加密应用程序1479第9章编写用户级加密应用程序本章介绍如何开发使用PKCS#11函数进行加密的用户级应用程序和提供者.
本章包含以下主题:"PKCS#11函数列表"[147]"使用PKCS#11的函数"[148]"消息摘要示例"[155]"对称加密示例"[158]"签名和验证示例"[162]"随机字节生成示例"[169]有关加密框架的更多信息,请参见第8章OracleSolaris加密框架介绍.
Cryptoki库概述OracleSolaris加密框架中的用户级应用程序会通过libpkcs11.
so模块中提供的cryptoki库访问PKCS#11功能.
pkcs11_softtoken.
so模块是由OracleCorporation提供的PKCS#11软令牌实现,用于提供加密机制.
软令牌插件是缺省的机制源.
加密机制还可以通过第三方插件提供.
本节列出了软令牌所支持的PKCS#11函数和返回值,返回代码根据插入到框架中的提供者而异.
本节还介绍了一些常见的函数.
有关cryptoki库中所有元素的完整说明,请参阅libpkcs11(3LIB)或者RSALaboratories网站上的PKCS#11:CryptographicTokenInterfaceStandard(PKCS#11:加密令牌接口标准).
确保对所有提供者使用直接绑定.
有关更多信息,请参见ld(1)和《OracleSolaris11.
2LinkersandLibrariesGuide》.
PKCS#11函数列表下面列表列出了OracleSolaris加密框架中的pkcs11_softtoken.
so所支持的PKCS#11函数类别以及相关函数:Cryptoki库概述148面向开发者的OracleSolaris11安全性指南2014年7月通用-C_Initialize()、C_Finalize()、C_GetInfo()和C_GetFunctionList()会话管理-C_OpenSession()、C_CloseSession()、C_GetSessionInfo()、C_CloseAllSessions()、C_和C_Logout()插槽和令牌管理-C_GetSlotList()、C_GetSlotInfo()、C_GetMechanismList()、C_GetMechanismInfo()和C_SetPIN()加密和解密-C_EncryptInit()、C_Encrypt()、C_EncryptUpdate()、C_EncryptFinal()、C_DecryptIni和C_DecryptFinal()消息摘要-C_DigestInit()、C_Digest()、C_DigestKey()、C_DigestUpdate()和C_DigestFinal()MAC的签名和应用-C_Sign()、C_SignInit()、C_SignUpdate()、C_SignFinal()、C_SignRecoverInit()和C_SignRecover()签名验证-C_Verify()、C_VerifyInit()、C_VerifyUpdate()、C_VerifyFinal()、C_VerifyRecoverI和C_VerifyRecover()双重用途加密函数-C_DigestEncryptUpdate()、C_DecryptDigestUpdate()、C_SignEncryptUpdate()和C_DecryptVerifyUpdate()随机数生成-C_SeedRandom()和C_GenerateRandom()对象管理-C_CreateObject()、C_DestroyObject()、C_CopyObject()、C_FindObjects()、C_FindObj和C_SetAttributeValue()密钥管理-C_GenerateKey()、C_GenerateKeyPair()和C_DeriveKey()使用PKCS#11的函数本节提供了以下使用PKCS#11的函数的说明:"PKCS#11函数:C_Initialize()"[149]"PKCS#11函数:C_GetInfo()"[149]"PKCS#11函数:C_GetSlotList()"[150]"PKCS#11函数:C_GetTokenInfo()"[150]"PKCS#11函数:C_OpenSession()"[151]"PKCS#11函数:C_GetMechanismList()"[152]Cryptoki库概述第9章编写用户级加密应用程序149注-所有的PKCS#11函数都可以从libpkcs11.
so库中获取.
不必使用C_GetFunctionList()函数来获取可用函数的列表.
PKCS#11函数:C_Initialize()C_Initialize()可用于初始化PKCS#11库.
C_Initialize()语法如下:C_Initialize(CK_VOID_PTRpInitArgs);pInitArgs是空值NULL_PTR或是指向CK_C_INITIALIZE_ARGS结构的指针.
通过NULL_PTR,该库可以将OracleSolaris互斥锁用作锁定原语,在多个线程之间仲裁对内部共享结构的访问.
请注意,OracleSolaris加密框架不接受互斥锁.
由于cryptoki库的此实现可以安全高效地处理多线程,因此建议使用NULL_PTR.
应用程序还可以使用pInitArgs来设置诸如CKF_LIBRARY_CANT_CREATE_OS_THREADS之类的标志.
C_Finalize()表示应用程序使用PKCS#11库结束会话.
注-C_Finalize()绝不应当通过库进行调用.
按照惯例,应用程序负责调用C_Finalize()来关闭会话.
除了CKR_FUNCTION_FAILED、CKR_GENERAL_ERROR、CKR_HOST_MEMORY和CKR_OK以外,C_Initialize()还会使用以下返回值:CKR_ARGUMENTS_BADCKR_CANT_LOCKCKR_CRYPTOKI_ALREADY_INITIALIZED(非致命)PKCS#11函数:C_GetInfo()C_GetInfo()使用的是有关cryptoki库的制造商和版本信息.
C_GetInfo()的语法如下所示:C_GetInfo(CK_INFO_PTRpInfo);C_GetInfo()会返回以下值:cryptokiVersion=2,11manufacturerID=OracleCorporation.
除了CKR_FUNCTION_FAILED、CKR_GENERAL_ERROR、CKR_HOST_MEMORY和CKR_OK以外,C_GetInfo()还可以获取以下返回值:CKR_ARGUMENTS_BADCryptoki库概述150面向开发者的OracleSolaris11安全性指南2014年7月CKR_CRYPTOKI_NOT_INITIALIZEDPKCS#11函数:C_GetSlotList()C_GetSlotList()使用的是可用插槽的列表.
如果除了pkcs11_softtoken.
so以外尚未安装任何其他加密提供者,则C_GetSlotList()仅返回缺省插槽.
C_GetSlotList()使用以下语法:C_GetSlotList(CK_BBOOLtokenPresent,CK_SLOT_ID_PTRpSlotList,CK_ULONG_PTRpulCount);如果tokenPresent设置为TRUE,则会将搜索限制在那些存在令牌的插槽.
如果pSlotList设置为NULL_PTR,则C_GetSlotlist()仅返回插槽的数量.
pulCount是指向用于接收插槽计数的位置的指针.
如果pSlotList指向用于接收插槽的缓冲区,则*pulCount将设置为CK_SLOT_ID元素的最大预期数量.
在返回时,*pulCount将设置为CK_SLOT_ID元素的实际数量.
通常,PKCS#11应用程序会调用C_GetSlotList()两次.
第一次调用C_GetSlotList()用于获取进行内存分配的插槽数量,第二次调用C_GetSlotList()用于检索插槽.
注-不保证插槽的顺序,它会因PKCS#11库的每次装入而异.
除了CKR_FUNCTION_FAILED、CKR_GENERAL_ERROR、CKR_HOST_MEMORY和CKR_OK以外,C_GetSlotlist()还可以获取以下返回值:CKR_ARGUMENTS_BADCKR_BUFFER_TOO_SMALLCKR_CRYPTOKI_NOT_INITIALIZEDPKCS#11函数:C_GetTokenInfo()C_GetTokenInfo()可用于获取有关特定令牌的信息.
C_GetTokenInfo()使用以下语法:C_GetTokenInfo(CK_SLOT_IDslotID,CK_TOKEN_INFO_PTRpInfo);slotID用于标识令牌的插槽.
slotID必须是由C_GetSlotList()返回的有效ID.
pInfo是指向用于接收令牌信息的位置的指针.
如果pkcs11_softtoken.
so是所安装的唯一提供者,C_GetTokenInfo()将返回以下字段和值:Cryptoki库概述第9章编写用户级加密应用程序151标签-SunSoftwarePKCS#11软令牌.
标志-CKF_DUAL_CRYPTO_OPERATIONS、CKF_TOKEN_INITIALIZED、CKF_RNG、CKF_USER_PIN_INITIALIZED和CKF_LOGIN_REQUIRED,这些标志设置为1.
ulMaxSessionCount-设置为CK_EFFECTIVELY_INFINITE.
ulMaxRwSessionCount-设置为CK_EFFECTIVELY_INFINITE.
ulMaxPinLen-设置为256.
ulMinPinLen-设置为1.
ulTotalPublicMemory-设置为CK_UNAVAILABLE_INFORMATION.
ulFreePublicMemory-设置为CK_UNAVAILABLE_INFORMATION.
ulTotalPrivateMemory-设置为CK_UNAVAILABLE_INFORMATION.
ulFreePrivateMemory-设置为CK_UNAVAILABLE_INFORMATION.
除了CKR_FUNCTION_FAILED、CKR_GENERAL_ERROR、CKR_HOST_MEMORY和CKR_OK以外,C_GetSlotlist()还可以获取以下返回值:CKR_ARGUMENTS_BADCKR_BUFFER_TOO_SMALLCKR_CRYPTOKI_NOT_INITIALIZEDCKR_SLOT_ID_INVALID以下返回值与具有硬件令牌的插件相关:CKR_DEVICE_ERRORCKR_DEVICE_MEMORYCKR_DEVICE_REMOVEDCKR_TOKEN_NOT_PRESENTCKR_TOKEN_NOT_RECOGNIZEDPKCS#11函数:C_OpenSession()应用程序可使用C_OpenSession()来启动特定插槽中具有特定令牌的加密会话.
C_OpenSession()语法如下:C_OpenSession(CK_SLOT_IDslotID,CK_FLAGSflags,CK_VOID_PTRpApplication,CK_NOTIFYNotify,CK_SESSION_HANDLE_PTRphSession);slotID用于标识插槽.
flags用于指示会话是可读写的还是只读的.
pApplication是应用程序所定义的用于回调的指针.
Notify用于存放可选回调函数的地址.
phSession是指向会话句柄的位置的指针.
除了CKR_FUNCTION_FAILED、CKR_GENERAL_ERROR、CKR_HOST_MEMORY和CKR_OK以外,C_OpenSession()还可以获取以下返回值:Cryptoki库概述152面向开发者的OracleSolaris11安全性指南2014年7月CKR_ARGUMENTS_BADCKR_CRYPTOKI_NOT_INITIALIZEDCKR_SLOT_ID_INVALIDCKR_TOKEN_WRITE_PROTECTED(随受写保护的令牌出现)以下返回值与具有硬件令牌的插件相关:CKR_DEVICE_ERRORCKR_DEVICE_MEMORYCKR_DEVICE_REMOVEDCKR_SESSION_COUNTCKR_SESSION_PARALLEL_NOT_SUPPORTEDCKR_SESSION_READ_WRITE_SO_EXISTSCKR_TOKEN_NOT_PRESENTCKR_TOKEN_NOT_RECOGNIZEDPKCS#11函数:C_GetMechanismList()C_GetMechanismList()用于获取指定令牌所支持的机制类型的列表.
C_GetMechanismList()的语法如下所示:C_GetMechanismList(CK_SLOT_IDslotID,CK_MECHANISM_TYPE_PTRpMechanismList,CK_ULONG_PTRpulCount);slotID用于标识令牌的插槽.
pulCount是指向用于接收机制数量的位置的指针.
如果pMechanismList设置为NULL_PTR,则*pulCount将返回机制的数量.
否则,必须将*pulCount设置为列表的大小,pMechanismList必须指向用于存放列表的缓冲区.
如果已插入PKCS#11软令牌,C_GetMechanismList()将返回以下列出的支持的机制:CKM_AES_CBCCKM_AES_CBC_PADCKM_AES_ECBCKM_AES_KEY_GENCKM_DES_CBCCKM_DES_CBC_PADCKM_DES_ECBCKM_DES_KEY_GENCKM_DES_MACCKM_DES_MAC_GENERALCKM_DES3_CBCCryptoki库概述第9章编写用户级加密应用程序153CKM_DES3_CBC_PADCKM_DES3_ECBCKM_DES3_KEY_GENCKM_DH_PKCS_DERIVECKM_DH_PKCS_KEY_PAIR_GENCKM_DSACKM_DSA_KEY_PAIR_GENCKM_DSA_SHA_1CKM_MD5CKM_MD5_KEY_DERIVATIONCKM_MD5_RSA_PKCSCKM_MD5_HMACCKM_MD5_HMAC_GENERALCKM_PBE_SHA1_RC4_128CKM_PKCS5_PBKD2CKM_RC4CKM_RC4_KEY_GENCKM_RSA_PKCSCKM_RSA_X_509CKM_RSA_PKCS_KEY_PAIR_GENCKM_SHA_1CKM_SHA_1_HMAC_GENERALCKM_SHA_1_HMACCKM_SHA_1_KEY_DERIVATIONCKM_SHA_1_RSA_PKCSCKM_SSL3_KEY_AND_MAC_DERIVECKM_SSL3_MASTER_KEY_DERIVECKM_SSL3_MASTER_KEY_DERIVE_DHCKM_SSL3_MD5_MACCKM_SSL3_PRE_MASTER_KEY_GENCKM_SSL3_SHA1_MACCKM_TLS_KEY_AND_MAC_DERIVECKM_TLS_MASTER_KEY_DERIVECKM_TLS_MASTER_KEY_DERIVE_DHCKM_TLS_PRE_MASTER_KEY_GEN除了CKR_FUNCTION_FAILED、CKR_GENERAL_ERROR、CKR_HOST_MEMORY和CKR_OK以外,C_GetSlotlist()还使用以下返回值:Cryptoki库概述154面向开发者的OracleSolaris11安全性指南2014年7月CKR_ARGUMENTS_BADCKR_BUFFER_TOO_SMALLCKR_CRYPTOKI_NOT_INITIALIZEDCKR_SLOT_ID_INVALID以下返回值与具有硬件令牌的插件相关:CKR_DEVICE_ERRORCKR_DEVICE_MEMORYCKR_DEVICE_REMOVEDCKR_TOKEN_NOT_PRESENTCKR_TOKEN_NOT_RECOGNIZED扩展的PKCS#11函数除了标准的PKCS#11函数以外,OracleSolaris加密框架还附带了两个便利函数:"扩展的PKCS#11函数:SUNW_C_GetMechSession()()"[154]"扩展的PKCS#11函数:SUNW_C_KeyToObject"[154]扩展的PKCS#11函数:SUNW_C_GetMechSession()()SUNW_C_GetMechSession()是一个便利函数,用于初始化OracleSolaris加密框架.
该函数随后会使用指定的机制启动会话.
SUNW_C_GetMechSession()的语法如下所示:SUNW_C_GetMechSession(CK_MECHANISM_TYPEmech,C\K_SESSION_HANDLE_PTRhSession)mech参数用于指定要使用的机制.
hSession是指向会话位置的指针.
SUNW_C_GetMechSession()在内部调用C_Initialize()以初始化cryptoki库.
SUNW_C_GetMechSession()接着会使用指定的机制调用C_GetSlotList()和C_GetMechanismInfo(),在可用插槽中搜索令牌.
如果找到了机制,SUNW_C_GetMechSession()会调用C_OpenSession()来打开会话.
SUNW_C_GetMechSession()只需要调用一次.
不过,多次调用SUNW_C_GetMechSession()也不会造成任何问题.
扩展的PKCS#11函数:SUNW_C_KeyToObjectSUNW_C_KeyToObject()用于创建私钥对象.
调用程序必须指定要使用的机制以及原始密钥数据.
SUNW_C_KeyToObject()可在内部确定指定机制的密钥类用户级加密应用程序示例第9章编写用户级加密应用程序155型.
通用密钥对象是通过C_CreateObject()创建的.
SUNW_C_KeyToObject()接着会调用C_GetSessionInfo()和C_GetMechanismInfo()来获取插槽和机制.
C_SetAttributeValue()随后会根据机制的类型为密钥对象设置属性标志.
用户级加密应用程序示例本节包含以下示例:"消息摘要示例"[155]"对称加密示例"[158]"签名和验证示例"[162]"随机字节生成示例"[169]消息摘要示例此示例使用PKCS#11函数基于输入文件创建摘要.
此示例执行以下步骤:1.
指定摘要机制.
此示例中使用的是CKM_MD5摘要机制.
2.
查找适用于指定摘要算法的插槽.
此示例使用OracleSolaris的便利函数SUNW_C_GetMechSession().
SUNW_C_GetMechSession()用于打开cryptoki库,该库存放着OracleSolaris加密框架中所使用的全部PKCS#11函数.
SUNW_C_GetMechSession()随后使用所需的机制来查找插槽.
然后,将会启动会话.
这个便利函数可有效地替换C_Initialize()调用、C_OpenSession()调用以及查找支持指定机制的插槽所需的任何代码.
3.
获取cryptoki信息.
本部分实际上不是创建消息摘要所必需的,之所以将其包括在内是为了说明C_GetInfo()函数的用法.
此示例中将获取制造商ID.
其他信息选项用于检索版本和库数据.
4.
针对插槽执行摘要操作.
此任务中的消息摘要可通过以下几个步骤创建:a.
打开输入文件.
b.
通过调用C_DigestInit()来初始化摘要操作.
c.
使用C_DigestUpdate()逐段处理数据.
d.
使用C_DigestFinal()获取完整的摘要,从而结束摘要操作过程.
5.
结束会话.
用户级加密应用程序示例156面向开发者的OracleSolaris11安全性指南2014年7月程序使用C_CloseSession()关闭会话,使用C_Finalize()关闭库.
以下示例中显示了消息摘要示例的源代码.
例9-1使用PKCS#11函数创建消息摘要#include#include#include#include#include#include#defineBUFFERSIZ8192#defineMAXDIGEST64/*Calculatethedigestofausersuppliedfile.
*/voidmain(intargc,char**argv){CK_BYTEdigest[MAXDIGEST];CK_INFOinfo;CK_MECHANISMmechanism;CK_SESSION_HANDLEhSession;CK_SESSION_INFOInfo;CK_ULONGulDatalen=BUFFERSIZ;CK_ULONGulDigestLen=MAXDIGEST;CK_RVrv;CK_SLOT_IDSlotID;inti,bytes_read=0;charinbuf[BUFFERSIZ];FILE*fs;interror=0;/*SpecifytheCKM_MD5digestmechanismasthetarget*/mechanism.
mechanism=CKM_MD5;mechanism.
pParameter=NULL_PTR;mechanism.
ulParameterLen=0;/*UseSUNWconveniencefunctiontoinitializethecryptoki*library,andopenasessionwithaslotthatsupports*themechanismweplanonusing.
*/rv=SUNW_C_GetMechSession(mechanism.
mechanism,&hSession);if(rv!
=CKR_OK){fprintf(stderr,"SUNW_C_GetMechSession:rv=0x%.
8X\n",rv);exit(1);}/*Getcryptokiinformation,themanufacturerID*/rv=C_GetInfo(&info);if(rv!
=CKR_OK){fprintf(stderr,"WARNING:C_GetInfo:rv=0x%.
8X\n",rv);用户级加密应用程序示例第9章编写用户级加密应用程序157}fprintf(stdout,"ManufacturerID=%s\n",info.
manufacturerID);/*Opentheinputfile*/if((fs=fopen(argv[1],"r"))==NULL){perror("fopen");fprintf(stderr,"\n\tusage:%sfilename>\n",argv[0]);error=1;gotoexit_session;}/*Initializethedigestsession*/if((rv=C_DigestInit(hSession,&mechanism))!
=CKR_OK){fprintf(stderr,"C_DigestInit:rv=0x%.
8X\n",rv);error=1;gotoexit_digest;}/*Readinthedataandcreatedigestofthisportion*/while(!
feof(fs)&&(ulDatalen=fread(inbuf,1,BUFFERSIZ,fs))>0){if((rv=C_DigestUpdate(hSession,(CK_BYTE_PTR)inbuf,ulDatalen))!
=CKR_OK){fprintf(stderr,"C_DigestUpdate:rv=0x%.
8X\n",rv);error=1;gotoexit_digest;}bytes_read+=ulDatalen;}fprintf(stdout,"%dbytesreadanddigested!
!
!
\n\n",bytes_read);/*Getcompletedigest*/ulDigestLen=sizeof(digest);if((rv=C_DigestFinal(hSession,(CK_BYTE_PTR)digest,&ulDigestLen))!
=CKR_OK){fprintf(stderr,"C_DigestFinal:rv=0x%.
8X\n",rv);error=1;gotoexit_digest;}/*Printtheresults*/fprintf(stdout,"Thevalueofthedigestis:");for(i=0;i#include#include#include#include#include#defineBUFFERSIZ8192/*Declarevaluesforthekeymaterials.
DONOTdeclareinitialization*vectorsstaticallylikethisinreallife!
!
*/uchar_tdes_key[]={0x01,0x23,0x45,0x67,0x89,0xab,0xcd,0xef};uchar_tdes_cbc_iv[]={0x12,0x34,0x56,0x78,0x90,0xab,0xcd,0xef};/*Keytemplaterelateddefinitions.
*/staticCK_BBOOLtruevalue=TRUE;staticCK_BBOOLfalsevalue=FALSE;staticCK_OBJECT_CLASSclass=CKO_SECRET_KEY;staticCK_KEY_TYPEkeyType=CKK_DES;/*Exampleencryptsanddecryptsafileprovidedbytheuser.
*/voidmain(intargc,char**argv){CK_RVrv;CK_MECHANISMmechanism;CK_OBJECT_HANDLEhKey;CK_SESSION_HANDLEhSession;CK_ULONGciphertext_len=64,lastpart_len=64;longciphertext_space=BUFFERSIZ;CK_ULONGdecrypttext_len;CK_ULONGtotal_encrypted=0;CK_ULONGulDatalen=BUFFERSIZ;inti,bytes_read=0;interror=0;charinbuf[BUFFERSIZ];FILE*fs;uchar_tciphertext[BUFFERSIZ],*pciphertext,decrypttext[BUFFERSIZ];/*Setthekeyobject*/CK_ATTRIBUTEtemplate[]={{CKA_CLASS,&class,sizeof(class)},{CKA_KEY_TYPE,&keyType,sizeof(keyType)},{CKA_TOKEN,&falsevalue,sizeof(falsevalue)},{CKA_ENCRYPT,&truevalue,sizeof(truevalue)},{CKA_VALUE,&des_key,sizeof(des_key)}};/*SettheencryptionmechanismtoCKM_DES_CBC_PAD*/mechanism.
mechanism=CKM_DES_CBC_PAD;用户级加密应用程序示例160面向开发者的OracleSolaris11安全性指南2014年7月mechanism.
pParameter=des_cbc_iv;mechanism.
ulParameterLen=8;/*UseSUNWconveniencefunctiontoinitializethecryptoki*library,andopenasessionwithaslotthatsupports*themechanismweplanonusing.
*/rv=SUNW_C_GetMechSession(mechanism.
mechanism,&hSession);if(rv!
=CKR_OK){fprintf(stderr,"SUNW_C_GetMechSession:rv=0x%.
8X\n",rv);exit(1);}/*Opentheinputfile*/if((fs=fopen(argv[1],"r"))==NULL){perror("fopen");fprintf(stderr,"\n\tusage:%sfilename>\n",argv[0]);error=1;gotoexit_session;}/*Createanobjecthandleforthekey*/rv=C_CreateObject(hSession,template,sizeof(template)/sizeof(CK_ATTRIBUTE),&hKey);if(rv!
=CKR_OK){fprintf(stderr,"C_CreateObject:rv=0x%.
8X\n",rv);error=1;gotoexit_session;}/*Initializetheencryptionoperationinthesession*/rv=C_EncryptInit(hSession,&mechanism,hKey);if(rv!
=CKR_OK){fprintf(stderr,"C_EncryptInit:rv=0x%.
8X\n",rv);error=1;gotoexit_session;}/*Readinthedataandencryptthisportion*/pciphertext=&ciphertext[0];while(!
feof(fs)&&(ciphertext_space>0)&&(ulDatalen=fread(inbuf,1,ciphertext_space,fs))>0){ciphertext_len=ciphertext_space;/*C_EncryptUpdateisonlybeingsentonebyteata*time,sowearenotcheckingforCKR_BUFFER_TOO_SMALL.
*Also,wearecheckingtomakesurewedonotgo*overtheallotedbuffersize.
Amorerobustprogram*couldincorporaterealloctoenlargethebuffer*dynamically.
*/用户级加密应用程序示例第9章编写用户级加密应用程序161rv=C_EncryptUpdate(hSession,(CK_BYTE_PTR)inbuf,ulDatalen,pciphertext,&ciphertext_len);if(rv!
=CKR_OK){fprintf(stderr,"C_EncryptUpdate:rv=0x%.
8X\n",rv);error=1;gotoexit_encrypt;}pciphertext+=ciphertext_len;total_encrypted+=ciphertext_len;ciphertext_space-=ciphertext_len;bytes_read+=ulDatalen;}if(!
feof(fs)||(ciphertext_space#include#include#include#include#include#defineBUFFERSIZ8192/*Definekeytemplate*/staticCK_BBOOLtruevalue=TRUE;staticCK_BBOOLfalsevalue=FALSE;staticCK_ULONGmodulusbits=1024;staticCK_BYTEpublic_exponent[]={3};boolean_tGetMySlot(CK_MECHANISM_TYPEsv_mech,CK_MECHANISM_TYPEkpgen_mech,CK_SLOT_ID_PTRpslot);/*Examplesignsandverifiesasimplestring,usingapublic/private*keypair.
*/voidmain(intargc,char**argv){CK_RVrv;CK_MECHANISMgenmech,smech;CK_SESSION_HANDLEhSession;CK_SESSION_INFOsessInfo;CK_SLOT_IDslotID;interror,i=0;CK_OBJECT_HANDLEprivatekey,publickey;/*Setpublickey.
*/CK_ATTRIBUTEpublickey_template[]={{CKA_VERIFY,&truevalue,sizeof(truevalue)},{CKA_MODULUS_BITS,&modulusbits,sizeof(modulusbits)},{CKA_PUBLIC_EXPONENT,&public_exponent,sizeof(public_exponent)}};用户级加密应用程序示例164面向开发者的OracleSolaris11安全性指南2014年7月/*Setprivatekey.
*/CK_ATTRIBUTEprivatekey_template[]={{CKA_SIGN,&truevalue,sizeof(truevalue)},{CKA_TOKEN,&falsevalue,sizeof(falsevalue)},{CKA_SENSITIVE,&truevalue,sizeof(truevalue)},{CKA_EXTRACTABLE,&truevalue,sizeof(truevalue)}};/*Createsamplemessage.
*/CK_ATTRIBUTEgetattributes[]={{CKA_MODULUS_BITS,NULL_PTR,0},{CKA_MODULUS,NULL_PTR,0},{CKA_PUBLIC_EXPONENT,NULL_PTR,0}};CK_ULONGmessagelen,slen,template_size;boolean_tfound_slot=B_FALSE;uchar_t*message=(uchar_t*)"Simplemessageforsigning&verifying.
";uchar_t*modulus,*pub_exponent;charsign[BUFFERSIZ];slen=BUFFERSIZ;messagelen=strlen((char*)message);/*Setupmechanismforgeneratingkeypair*/genmech.
mechanism=CKM_RSA_PKCS_KEY_PAIR_GEN;genmech.
pParameter=NULL_PTR;genmech.
ulParameterLen=0;/*Setupthesigningmechanism*/smech.
mechanism=CKM_RSA_PKCS;smech.
pParameter=NULL_PTR;smech.
ulParameterLen=0;/*InitializetheCRYPTOKIlibrary*/rv=C_Initialize(NULL_PTR);if(rv!
=CKR_OK){fprintf(stderr,"C_Initialize:Error=0x%.
8X\n",rv);exit(1);}found_slot=GetMySlot(smech.
mechanism,genmech.
mechanism,&slotID);if(!
found_slot){fprintf(stderr,"Nousableslotwasfound.
\n");gotoexit_program;}fprintf(stdout,"selectedslot:%d\n",slotID);/*Openasessionontheslotfound*/rv=C_OpenSession(slotID,CKF_SERIAL_SESSION,NULL_PTR,NULL_PTR,用户级加密应用程序示例第9章编写用户级加密应用程序165&hSession);if(rv!
=CKR_OK){fprintf(stderr,"C_OpenSession:rv=0x%.
8X\n",rv);error=1;gotoexit_program;}fprintf(stdout,"Generatingkeypair.
.
.
.
\n");/*GenerateKeypairforsigning/verifying*/rv=C_GenerateKeyPair(hSession,&genmech,publickey_template,(sizeof(publickey_template)/sizeof(CK_ATTRIBUTE)),privatekey_template,(sizeof(privatekey_template)/sizeof(CK_ATTRIBUTE)),&publickey,&privatekey);if(rv!
=CKR_OK){fprintf(stderr,"C_GenerateKeyPair:rv=0x%.
8X\n",rv);error=1;gotoexit_session;}/*Displaythepublickey.
*/template_size=sizeof(getattributes)/sizeof(CK_ATTRIBUTE);rv=C_GetAttributeValue(hSession,publickey,getattributes,template_size);if(rv!
=CKR_OK){/*notfatal,wecanstillsign/verifyifthisfailed*/fprintf(stderr,"C_GetAttributeValue:rv=0x%.
8X\n",rv);error=1;}else{/*Allocatememorytoholdthedatawewant*/for(i=0;i0)){fprintf(stdout,"slotCount=%d\n",ulSlotCount);pSlotList=malloc(ulSlotCount*sizeof(CK_SLOT_ID));if(pSlotList==NULL){fprintf(stderr,"Systemerror:unabletoallocate""memory\n");return(returnval);}/*Gettheslotlistforprocessing*/rv=C_GetSlotList(0,pSlotList,&ulSlotCount);用户级加密应用程序示例168面向开发者的OracleSolaris11安全性指南2014年7月if(rv!
=CKR_OK){fprintf(stderr,"GetSlotListfailed:unabletoget""slotcount.
\n");gotocleanup;}}else{fprintf(stderr,"GetSlotListfailed:unabletogetslot""list.
\n");return(returnval);}/*Findaslotcapableofspecifiedmechanism*/for(i=0;i#include#include#include#include#include#defineRANDSIZE64boolean_tGetRandSlot(CK_SLOT_ID_PTRpslot);/*Examplegeneratesrandombytes.
*/voidmain(intargc,char**argv){CK_RVrv;CK_MECHANISMmech;CK_SESSION_HANDLEhSession;CK_SESSION_INFOsessInfo;CK_SLOT_IDslotID;用户级加密应用程序示例170面向开发者的OracleSolaris11安全性指南2014年7月CK_BYTErandBytes[RANDSIZE];boolean_tfound_slot=B_FALSE;interror;inti;/*InitializetheCRYPTOKIlibrary*/rv=C_Initialize(NULL_PTR);if(rv!
=CKR_OK){fprintf(stderr,"C_Initialize:Error=0x%.
8X\n",rv);exit(1);}found_slot=GetRandSlot(&slotID);if(!
found_slot){gotoexit_program;}/*Openasessionontheslotfound*/rv=C_OpenSession(slotID,CKF_SERIAL_SESSION,NULL_PTR,NULL_PTR,&hSession);if(rv!
=CKR_OK){fprintf(stderr,"C_OpenSession:rv=0x%.
8x\n",rv);error=1;gotoexit_program;}/*Generaterandombytes*/rv=C_GenerateRandom(hSession,randBytes,RANDSIZE);if(rv!
=CKR_OK){fprintf(stderr,"C_GenerateRandom:rv=0x%.
8x\n",rv);error=1;gotoexit_session;}fprintf(stdout,"Randomvalue:");for(i=0;i0)){fprintf(stdout,"slotCount=%d\n",(int)ulSlotCount);pSlotList=malloc(ulSlotCount*sizeof(CK_SLOT_ID));if(pSlotList==NULL){fprintf(stderr,"Systemerror:unabletoallocatememory\n");return(result);}/*Gettheslotlistforprocessing*/rv=C_GetSlotList(0,pSlotList,&ulSlotCount);if(rv!
=CKR_OK){fprintf(stderr,"GetSlotListfailed:unabletoget""slotlist.
\n");free(pSlotList);return(result);}}else{fprintf(stderr,"GetSlotListfailed:unabletogetslot""count.
\n");return(result);}/*Findaslotcapableofdoingrandomnumbergeneration*/for(i=0;i#include请确保在链接步骤中包含KMF库.
$cc-okmftestkmftest.
c-lkmfKMF基本数据类型有关结果和类型的定义,请参见kmftypes.
h文件.
此示例使用以下KMF类型的变量.
KMF_HANDLE_TKMF调用的会话句柄KMF_RETURN所有KMF调用的返回代码KMF_KEY_HANDLEKMF密钥的句柄KMF_CREDENTIALKMF凭证KMF_ATTRIBUTE请确保足够大KMF_KEYSTORE_TYPE密钥库类型,如KMF_KEYSTORE_PK11TOKENKMF_KEY_ALG密钥类型,如KMF_RSAKMF_X509_CERTIFICATE获得签名的数据记录KMF_X509_NAME标识名记录KMF_DATA最终证书数据记录KMF_BIGINT变量长度整数KMF应用程序结果验证通过使用pktool(1M)实用程序,用户可以验证程序是否已成功创建证书和密钥对.
$pktoollistobjtype=bothOracleSolaris密钥管理框架示例应用程序178面向开发者的OracleSolaris11安全性指南2014年7月EnterpinforSunSoftwarePKCS#11softtoken:Found1certificates.
1.
(X.
509certificate)Label:admin@example.
comID:09:ac:7f:1a:01:f7:fc:a9:1a:cd:fd:8f:d4:92:4c:25:bf:b1:97:feSubject:C=US,ST=CA,L=MenloPark,O=FoobarInc.
,OU=FoobarITOffice,CN=admin@example.
comIssuer:C=US,ST=CA,L=MenloPark,O=FoobarInc.
,OU=FoobarITOffice,CN=admin@example.
comSerial:0x452BF693X509v3SubjectAlternativeName:email:admin@example.
comFound1keys.
Key#1-RSAprivatekey:admin@example.
com完整的KMF应用程序源代码有关KMFAPI的定义,请参见libkmf(3LIB)手册页.
此应用程序执行以下步骤:1.
在可以调用任何KMF函数之前,应用程序必须首先使用kmf_initialize()来初始化KMF会话的句柄.
此句柄用作大多数KMF函数调用的第一个参数.
它是一个不透明的数据类型,用于保留该会话的内部状态和上下文信息.
2.
此示例应用程序使用PKCS#11密钥库.
使用kmf_configure_keystore()来定义将来操作要使用的令牌.
3.
创建证书或PKCS#10CSR的第一步是生成密钥对.
使用kmf_create_keypair()来创建所需的公钥和私钥,并将私钥存储在指定密钥库中.
该函数会将句柄返回到应用程序,以便调用者可以在将来的操作中引用公钥和私钥对象(如有必要).

4.
建立密钥对后,使用kmf_set_cert_pubkey()和kmf_set_cert_version()来填充用于生成最终证书的模板记录.
KMF可提供不同的API来设置X.
509v3证书的各种字段(包括扩展).
使用kmf_hexstr_to_bytes()、kmf_set_cert_serial()、kmf_set_cert_validity()和kmf_set_cert_sig_alg()来设置序列号.
该序列号是KMF_BIGINT记录.
使用kmf_dn_parser()、kmf_set_cert_subject()和kmf_set_cert_issuer()来创建KMF_X509_NAME结构.
5.
由于这是自签名证书创建练习,所以该应用程序使用随证书自身的公钥提供的私钥对上面创建的证书模板进行签名.
该kmf_sign_cert()操作将导致生成KMF_DATA记录,该记录包含ASN.
1编码的X.
509v3证书数据.
6.
现在,已对证书进行签名并且为其最终格式,可以将其存储在任何密钥库中.
使用kmf_store_cert()将证书存储在该应用程序开始时定义的PKCS#11令牌中.
此时,也可以将证书存储在NSS或OpenSSL文件中.
7.
不再需要KMF生成的数据结构时,应清除分配给该数据结构的内存.
KMF提供了便于使用的API,以便正确地解除与这些对象关联的内存的分配.
为了节省资源,OracleSolaris密钥管理框架示例应用程序第10章OracleSolaris密钥管理框架介绍179强烈建议正确地清除内存.
清除接口包括kmf_free_data()、kmf_free_dn()和kmf_finalize().
下面是此示例应用程序的完整源代码,包括所有数据类型和辅助(helper)函数.
编译时,请确保包含KMF库.
/**KMFExamplecodeforgeneratingaself-signedX.
509certificate.
*Thisiscompletelyunsupportedandisjusttobeusedasanexample.
**Compile:*$cc-okeytestkeytest.
c-lkmf**Run:*$.
/keytest**Oncecomplete,theresultscanbeverifiedusingthepktool(1)command:**$pktoollist*ThisshouldshowanRSApublickeylabeled"keytest"andacertlabeled"keytest".
**Theobjectscreatedbythisprogramcanbedeletedfromthekeystore*usingpktool(1)also:**$pktooldeletelabel=keytest**/#include#include#include#include#include#include#includeintmain(intargc,char*argv[]){KMF_HANDLE_Tkmfhandle;KMF_RETURNret;charopt,*str=NULL;externchar*optarg;KMF_KEY_HANDLEprikey,pubkey;KMF_CREDENTIALcred;KMF_ATTRIBUTEattrlist[16];/*thisneedstobebigenough*/KMF_KEYSTORE_TYPEkstype;KMF_KEY_ALGkeytype;KMF_KEY_HANDLEprik,pubk;KMF_X509_CERTIFICATEcertstruct;KMF_X509_NAMEcertsubject,certissuer;KMF_DATArawcert;KMF_BIGINTserno;char*token="SunSoftwarePKCS#11softtoken";OracleSolaris密钥管理框架示例应用程序180面向开发者的OracleSolaris11安全性指南2014年7月char*keylabel="keytest";boolean_treadonly=B_FALSE;uint32_tkeylen=1024;uint32_tltime=SECSPERDAY*DAYSPERNYEAR;/*secondsinayear(seetzfile.
h)*/charprompt[1024];intnumattrs;(void)memset(&certstruct,0,sizeof(certstruct));(void)memset(&rawcert,0,sizeof(rawcert));(void)memset(&certissuer,0,sizeof(certissuer));(void)memset(&certsubject,0,sizeof(certsubject));/**InitializeaKMFhandleforuseinfuturecalls.
*/ret=kmf_initialize(&kmfhandle,NULL,NULL);if(ret!
=KMF_OK){printf("kmf_initializefailed:0x%0x\n",ret);exit(1);}/*WewanttousethePKCS11keystore*/kstype=KMF_KEYSTORE_PK11TOKEN;numattrs=0;kmf_set_attr_at_index(attrlist,numattrs,KMF_KEYSTORE_TYPE_ATTR,&kstype,sizeof(kstype));numattrs++;/*IndicatewhichPKCS11tokenwillbeused*/kmf_set_attr_at_index(attrlist,numattrs,KMF_TOKEN_LABEL_ATTR,token,strlen(token));numattrs++;kmf_set_attr_at_index(attrlist,numattrs,KMF_READONLY_ATTR,&readonly,sizeof(readonly));numattrs++;ret=kmf_configure_keystore(kmfhandle,numattrs,attrlist);if(ret!
=KMF_OK)exit(ret);/*Resettheattributecountforanewcommand*/numattrs=0;/**GetthePINtoaccessthetoken.
*/(void)snprintf(prompt,sizeof(prompt),"EnterPINfor%s:",token);cred.
cred=getpassphrase(prompt);if(cred.
cred!
=NULL){cred.
credlen=strlen(cred.
cred);kmf_set_attr_at_index(attrlist,numattrs,KMF_CREDENTIAL_ATTR,&cred,sizeof(cred));OracleSolaris密钥管理框架示例应用程序第10章OracleSolaris密钥管理框架介绍181numattrs++;}kmf_set_attr_at_index(attrlist,numattrs,KMF_KEYSTORE_TYPE_ATTR,&kstype,sizeof(kstype));numattrs++;keytype=KMF_RSA;keylen=1024;keylabel="keytest";kmf_set_attr_at_index(attrlist,numattrs,KMF_KEYALG_ATTR,&keytype,sizeof(keytype));numattrs++;kmf_set_attr_at_index(attrlist,numattrs,KMF_KEYLENGTH_ATTR,&keylen,sizeof(keylen));numattrs++;kmf_set_attr_at_index(attrlist,numattrs,KMF_KEYLABEL_ATTR,keylabel,strlen(keylabel));numattrs++;kmf_set_attr_at_index(attrlist,numattrs,KMF_CREDENTIAL_ATTR,&cred,sizeof(cred));numattrs++;/**Setthehandlessotheycanbeusedlater.
*/kmf_set_attr_at_index(attrlist,numattrs,KMF_PRIVKEY_HANDLE_ATTR,&prik,sizeof(prik));numattrs++;kmf_set_attr_at_index(attrlist,numattrs,KMF_PUBKEY_HANDLE_ATTR,&pubk,sizeof(pubk));numattrs++;ret=kmf_create_keypair(kmfhandle,numattrs,attrlist);if(ret!
=KMF_OK){printf("kmf_create_keypairerror:0x%02x\n",ret);gotocleanup;}/**Nowthekeyshavebeencreated,generateanX.
509certificate*bypopulatingthetemplateandsigningit.
*/if((ret=kmf_set_cert_pubkey(kmfhandle,&pubk,&certstruct))){printf("kmf_set_cert_pubkeyerror:0x%02x\n",ret);gotocleanup;}/*Version"2"isforanx509.
v3certificate*/OracleSolaris密钥管理框架示例应用程序182面向开发者的OracleSolaris11安全性指南2014年7月if((ret=kmf_set_cert_version(&certstruct,2))){printf("kmf_set_cert_versionerror:0x%02x\n",ret);gotocleanup;}/**Setuptheserialnumber,itmustbeaKMF_BIGINTrecord.
*/if((ret=kmf_hexstr_to_bytes((uchar_t*)"0x010203",&serno.
val,\&serno.
len))){printf("kmf_hexstr_to_byteserror:0x%02x\n",ret);gotocleanup;}if((ret=kmf_set_cert_serial(&certstruct,&serno))){printf("kmf_set_cert_serialerror:0x%02x\n",ret);gotocleanup;}if((ret=kmf_set_cert_validity(&certstruct,NULL,ltime))){printf("kmf_set_cert_validityerror:0x%02x\n",ret);gotocleanup;}if((ret=kmf_set_cert_sig_alg(&certstruct,KMF_ALGID_SHA1WithRSA))){printf("kmf_set_cert_sig_algerror:0x%02x\n",ret);gotocleanup;}/**CreateaKMF_X509_NAMEstructbyparsingadistinguishedname.
*/if((ret=kmf_dn_parser("cn=testcert",&certsubject))){printf("kmf_dn_parsererror:0x%02x\n",ret);gotocleanup;}if((ret=kmf_dn_parser("cn=testcert",&certissuer))){printf("kmf_dn_parsererror:0x%02x\n",ret);gotocleanup;}if((ret=kmf_set_cert_subject(&certstruct,&certsubject))){printf("kmf_set_cert_sig_algerror:0x%02x\n",ret);gotocleanup;}if((ret=kmf_set_cert_issuer(&certstruct,&certissuer))){printf("kmf_set_cert_sig_algerror:0x%02x\n",ret);gotocleanup;}/**NowwehavethecertstructsetupwiththeminimalamountneededOracleSolaris密钥管理框架示例应用程序第10章OracleSolaris密钥管理框架介绍183*togenerateaself-signedcert.
Puttogethertheattributesto*callkmf_sign_cert.
*/numattrs=0;kmf_set_attr_at_index(attrlist,numattrs,KMF_KEYSTORE_TYPE_ATTR,&kstype,sizeof(kstype));numattrs++;kmf_set_attr_at_index(attrlist,numattrs,KMF_KEY_HANDLE_ATTR,&prik,sizeof(KMF_KEY_HANDLE_ATTR));numattrs++;/*TheX509templatestructuretobesignedgoeshere.
*/kmf_set_attr_at_index(attrlist,numattrs,KMF_X509_CERTIFICATE_ATTR,&certstruct,sizeof(KMF_X509_CERTIFICATE));numattrs++;/**Settheoutputbufferforthesignedcert.
*ThiswillbeablockofrawASN.
1data.
*/kmf_set_attr_at_index(attrlist,numattrs,KMF_CERT_DATA_ATTR,&rawcert,sizeof(KMF_DATA));numattrs++;if((ret=kmf_sign_cert(kmfhandle,numattrs,attrlist))){printf("kmf_sign_certerror:0x%02x\n",ret);gotocleanup;}/**Nowwehavethecertificateandwewanttostoreitinthe*keystore(whichisthePKCS11tokeninthisexample).
*/numattrs=0;kmf_set_attr_at_index(attrlist,numattrs,KMF_KEYSTORE_TYPE_ATTR,&kstype,sizeof(kstype));numattrs++;kmf_set_attr_at_index(attrlist,numattrs,KMF_CERT_DATA_ATTR,&rawcert,sizeof(KMF_DATA));numattrs++;/*Usethesamelabelasthepublickey*/kmf_set_attr_at_index(attrlist,numattrs,KMF_CERT_LABEL_ATTR,keylabel,strlen(keylabel));numattrs++;if((ret=kmf_store_cert(kmfhandle,numattrs,attrlist))){printf("kmf_store_certerror:0x%02x\n",ret);gotocleanup;}cleanup:kmf_free_data(&rawcert);OracleSolaris密钥管理框架示例应用程序184面向开发者的OracleSolaris11安全性指南2014年7月kmf_free_dn(&certissuer);kmf_free_dn(&certsubject);kmf_finalize(kmfhandle);return(ret);}附录A.
适用于开发者的安全编码准则185A附录A适用于开发者的安全编码准则为OracleSolaris操作系统编写应用程序的开发者需要遵循以下安全编码准则.
在使用常规的、特定于语言的编码和特定于OracleSolaris的编码以及工具时,存在一些安全编码准则.
以下Web站点跟踪编码漏洞并促进安全的编码做法:CommonWeaknessEnumeration(常见漏洞列举)NationalVulnerabilityDatabaseVersion2.
2(国家级漏洞数据库2.
2版)CERTSecureCodingStandards(CERT安全编码标准)ISO/IECJTC1/SC22/WG23ProgrammingLanguageVulnerabilities(ISO/IECJTC1/SC22/WG23编程语言漏洞)CERTWeb站点包含有关安全编码做法的计算机语言参考信息.
这些参考信息可能包含有关POSIXAPI(OracleSolarisAPI集的一部分)的章节.
C-CERTCSecureCodingStandard(《CERTC安全编码标准》)附录G,使用C函数时的安全注意事项提供了在OracleSolaris中安全使用标准C库函数的其他准则C++-CERTC++SecureCodingStandard(《CERTC++安全编码标准》)Java-CERTOracleSecureCodingStandardforJava(《CERT针对Java的Oracle安全编码标准》)Perl-CERTPerlSecureCodingStandard(CERTPerl安全编码标准)开放式Web应用程序安全项目(OpenWebApplicationSecurityProject,OWASP)拥有两种Web脚本语言的安全准则:PHP-OWASPPHPSecurityCheatSheet(OWASPPHP安全速查表)Python-OWASPPythonSecurity(OWASPPython安全)Web站点OracleSolaris提供了特定的API,可用于编写更安全的代码和利用OracleSolaris操作系统和OracleSun硬件系统的安全和加密功能.
此外,OracleSolarisStudio的文档集包含有关安全使用工具的讨论.
来自OracleSolaris的以下指南可以解决安全编码:《OracleSolaris11.
2LinkersandLibrariesGuide》《OracleSolaris11.
2DynamicTracingGuide》186面向开发者的OracleSolaris11安全性指南2014年7月《ResourceManagementandOracleSolarisZonesDeveloper'sGuide》《Studio12.
3SecurityGuide》(《Studio12.
3安全指南》)附录B.
基于C的GSS-API样例程序187B附录B基于C的GSS-API样例程序本附录给出了使用GSS-API建立安全网络连接的两个样例应用程序的源代码.
第一个应用程序为典型客户机.
第二个应用程序说明服务器如何在GSS-API中工作.
在运行的过程中,这两个程序都会显示基准测试程序.
因此,用户可以查看使用中的GSS-API.
此外,还提供了可供客户机和服务器应用程序使用的各种特定函数.
本章包含以下主题:"客户端应用程序"[187]"服务器端应用程序"[198]"各种GSS-API样例函数"[208]第5章GSS-API客户机示例和第6章GSS-API服务器示例中详细检查了这些程序.
客户端应用程序以下示例提供了客户端程序gss_client的源代码.
注-此示例的源代码也可以通过Oracle下载中心获取.
请参见http://www.
oracle.
com/technetwork/indexes/downloads/sdlc-decommission-333274.
html.
例B-1gss-client.
c样例程序的完整列表/**Copyright1994byOpenVisionTechnologies,Inc.
**Permissiontouse,copy,modify,distribute,andsellthissoftware*anditsdocumentationforanypurposeisherebygrantedwithoutfee,*providedthattheabovecopyrightnoticeappearsinallcopiesand*thatboththatcopyrightnoticeandthispermissionnoticeappearin*supportingdocumentation,andthatthenameofOpenVisionnotbeused*inadvertisingorpublicitypertainingtodistributionofthesoftware*withoutspecific,writtenpriorpermission.
OpenVisionmakesno*representationsaboutthesuitabilityofthissoftwareforany*purpose.
Itisprovided"asis"withoutexpressorimpliedwarranty.
**OPENVISIONDISCLAIMSALLWARRANTIESWITHREGARDTOTHISSOFTWARE,客户端应用程序188面向开发者的OracleSolaris11安全性指南2014年7月*INCLUDINGALLIMPLIEDWARRANTIESOFMERCHANTABILITYANDFITNESS,INNO*EVENTSHALLOPENVISIONBELIABLEFORANYSPECIAL,INDIRECTOR*CONSEQUENTIALDAMAGESORANYDAMAGESWHATSOEVERRESULTINGFROMLOSSOF*USE,DATAORPROFITS,WHETHERINANACTIONOFCONTRACT,NEGLIGENCEOR*OTHERTORTIOUSACTION,ARISINGOUTOFORINCONNECTIONWITHTHEUSEOR*PERFORMANCEOFTHISSOFTWARE.
*/#if!
defined(lint)&&!
defined(__CODECENTER__)staticchar*rcsid=\"$Header:/cvs/krbdev/krb5/src/appl/gss-sample/gss-client.
c,\v1.
161998/10/3002:52:03marcExp$";#endif#include#include#include#include#include#include#include#include#include#include#include#include#include#include#includevoidusage(){fprintf(stderr,"Usage:gss-client[-portport][-d]hostservice\msg\n");exit(1);}/**Function:connect_to_server**Purpose:OpensaTCPconnectiontothenamehostandport.
**Arguments:**host(r)thetargethostname*port(r)thetargetport,inhostbyteorder**Returns:theestablishedsocketfiledescriptor,or-1onfailure**Effects:**Thehostnameisresolvedwithgethostbyname(),andthesocketis*openedandconnected.
Ifanerroroccurs,anerrormessageis客户端应用程序附录B.
基于C的GSS-API样例程序189*displayedand-1isreturned.
*/intconnect_to_server(host,port)char*host;u_shortport;{structsockaddr_insaddr;structhostent*hp;ints;if((hp=gethostbyname(host))==NULL){fprintf(stderr,"Unknownhost:%s\n",host);return-1;}saddr.
sin_family=hp->h_addrtype;memcpy((char*)&saddr.
sin_addr,hp->h_addr,sizeof(saddr.
sin_addr));saddr.
sin_port=htons(port);if((s=socket(AF_INET,SOCK_STREAM,0))length=stat_buf.
st_size;if(in_buf->length==0){in_buf->value=NULL;return;}if((in_buf->value=malloc(in_buf->length))==0){fprintf(stderr,\"Couldn'tallocate%dbytebufferforreadingfile\n",in_buf->length);exit(1);}/*thiscodeusedtocheckforincompletereads,butyoucan'tgetanincompletereadonanyfileforwhichfstat()ismeaningful*/count=read(fd,in_buf->value,in_buf->length);if(countlength)fprintf(stderr,"Warning,onlyreadin%dbytes,expected%d\n",count,in_buf->length);}/**Function:call_server**Purpose:Callthe"sign"service.
**Arguments:**host(r)thehostprovidingtheservice*port(r)theporttoconnecttoonhost*service_name(r)theGSS-APIservicenametoauthenticateto*msg(r)themessagetohave"signed"**Returns:0onsuccess,-1onfailure*客户端应用程序附录B.
基于C的GSS-API样例程序193*Effects:**call_serveropensaTCPconnectiontoandestablishesa*GSS-APIcontextwithservice_nameovertheconnection.
Itthen*sealsmsginaGSS-APItokenwithgss_seal,sendsittotheserver,*readsbackaGSS-APIsignatureblockformsgfromtheserver,and*verifiesitwithgss_verify.
-1isreturnedifanystepfails,*otherwise0isreturned.
*/intcall_server(host,port,oid,service_name,deleg_flag,msg,use_file)char*host;u_shortport;gss_OIDoid;char*service_name;OM_uint32deleg_flag;char*msg;intuse_file;{gss_ctx_id_tcontext;gss_buffer_descin_buf,out_buf;ints,state;OM_uint32ret_flags;OM_uint32maj_stat,min_stat;gss_name_tsrc_name,targ_name;gss_buffer_descsname,tname;OM_uint32lifetime;gss_OIDmechanism,name_type;intis_local;OM_uint32context_flags;intis_open;gss_qop_tqop_state;gss_OID_setmech_names;gss_buffer_descoid_name;size_ti;/*Openconnection*/if((s=connect_to_server(host,port))string",maj_stat,min_stat);return-1;}fprintf(stderr,"Nametypeofsourcenameis%.
*s.
\n",(int)oid_name.
length,(char*)oid_name.
value);(void)gss_release_buffer(&min_stat,&oid_name);/*Nowgetthenamessupportedbythemechanism*/maj_stat=gss_inquire_names_for_mech(&min_stat,mechanism,&mech_names);if(maj_stat!
=GSS_S_COMPLETE){display_status("inquiringmechnames",maj_stat,min_stat);return-1;}maj_stat=gss_oid_to_str(&min_stat,mechanism,&oid_name);if(maj_stat!
=GSS_S_COMPLETE){display_status("convertingoid->string",maj_stat,min_stat);客户端应用程序附录B.
基于C的GSS-API样例程序195return-1;}fprintf(stderr,"Mechanism%.
*ssupports%dnames\n",(int)oid_name.
length,(char*)oid_name.
value,mech_names->count);(void)gss_release_buffer(&min_stat,&oid_name);for(i=0;icount;i++){maj_stat=gss_oid_to_str(&min_stat,&mech_names->elements[i],&oid_name);if(maj_stat!
=GSS_S_COMPLETE){display_status("convertingoid->string",maj_stat,min_stat);return-1;}fprintf(stderr,"%d:%.
*s\n",i,(int)oid_name.
length,(char*)oid_name.
value);(void)gss_release_buffer(&min_stat,&oid_name);}(void)gss_release_oid_set(&min_stat,&mech_names);if(use_file){read_file(msg,&in_buf);}else{/*Sealthemessage*/in_buf.
value=msg;in_buf.
length=strlen(msg);}maj_stat=gss_wrap(&min_stat,context,1,GSS_C_QOP_DEFAULT,&in_buf,&state,&out_buf);if(maj_stat!
=GSS_S_COMPLETE){display_status("sealingmessage",maj_stat,min_stat);(void)close(s);(void)gss_delete_sec_context(&min_stat,&context,GSS_C_NO_BUFFER);return-1;}elseif(!
state){fprintf(stderr,"Warning!
Messagenotencrypted.
\n");}/*Sendtoserver*/if(send_token(s,&out_buf)#include#include#include#include服务器端应用程序附录B.
基于C的GSS-API样例程序199#include#include#include#include#include#include#includevoidusage(){fprintf(stderr,"Usage:gss-server[-portport][-verbose]\n");fprintf(stderr,"[-inetd][-logfilefile][service_name]\n");exit(1);}FILE*log;intverbose=0;/**Function:server_acquire_creds**Purpose:importsaservicenameandacquirescredentialsforit**Arguments:**service_name(r)theASCIIservicename*server_creds(w)theGSS-APIservicecredentials**Returns:0onsuccess,-1onfailure**Effects:**Theservicenameisimportedwithgss_import_name,andservice*credentialsareacquiredwithgss_acquire_cred.
Ifeitheroperation*fails,anerrormessageisdisplayedand-1isreturned;otherwise,*0isreturned.
*/intserver_acquire_creds(service_name,server_creds)char*service_name;gss_cred_id_t*server_creds;{gss_buffer_descname_buf;gss_name_tserver_name;OM_uint32maj_stat,min_stat;name_buf.
value=service_name;name_buf.
length=strlen(name_buf.
value)+1;maj_stat=gss_import_name(&min_stat,&name_buf,(gss_OID)GSS_C_NT_HOSTBASED_SERVICE,&server_name);if(maj_stat!
=GSS_S_COMPLETE){display_status("importingname",maj_stat,min_stat);服务器端应用程序200面向开发者的OracleSolaris11安全性指南2014年7月return-1;}maj_stat=gss_acquire_cred(&min_stat,server_name,0,GSS_C_NULL_OID_SET,GSS_C_ACCEPT,server_creds,NULL,NULL);if(maj_stat!
=GSS_S_COMPLETE){display_status("acquiringcredentials",maj_stat,min_stat);return-1;}(void)gss_release_name(&min_stat,&server_name);return0;}/**Function:server_establish_context**Purpose:establishesaGSS-APIcontextasaspecifiedservicewith*anincomingclient,andreturnsthecontexthandleandassociated*clientname**Arguments:**s(r)anestablishedTCPconnectiontotheclient*service_creds(r)servercredentials,fromgss_acquire_cred*context(w)theestablishedGSS-APIcontext*client_name(w)theclient'sASCIIname**Returns:0onsuccess,-1onfailure**Effects:**Anyvalidclientrequestisaccepted.
Ifacontextisestablished,*itshandleisreturnedincontextandtheclientnameisreturned*inclient_nameand0isreturned.
Ifunsuccessful,anerror*messageisdisplayedand-1isreturned.
*/intserver_establish_context(s,server_creds,context,client_name,\ret_flags)ints;gss_cred_id_tserver_creds;gss_ctx_id_t*context;gss_buffer_tclient_name;OM_uint32*ret_flags;{gss_buffer_descsend_tok,recv_tok;gss_name_tclient;gss_OIDdoid;OM_uint32maj_stat,min_stat,acc_sec_min_stat;gss_buffer_descoid_name;服务器端应用程序附录B.
基于C的GSS-API样例程序201*context=GSS_C_NO_CONTEXT;do{if(recv_token(s,&recv_tok)string",maj_stat,min_stat);return-1;}fprintf(log,"AcceptedconnectionusingmechanismOID%.
*s.
\n",(int)oid_name.
length,(char*)oid_name.
value);(void)gss_release_buffer(&min_stat,&oid_name);}maj_stat=gss_display_name(&min_stat,client,client_name,&doid);if(maj_stat!
=GSS_S_COMPLETE){display_status("displayingname",maj_stat,min_stat);return-1;}maj_stat=gss_release_name(&min_stat,&client);if(maj_stat!
=GSS_S_COMPLETE){display_status("releasingname",maj_stat,min_stat);return-1;}return0;}/**Function:create_socket**Purpose:OpensalisteningTCPsocket.
**Arguments:**port(r)theportnumberonwhichtolisten**Returns:thelisteningsocketfiledescriptor,or-1onfailure**Effects:**Alisteningsocketonthespecifiedportiscreatedandreturned.
*Onerror,anerrormessageisdisplayedand-1isreturned.
*/intcreate_socket(port)u_shortport;{structsockaddr_insaddr;ints;inton=1;服务器端应用程序附录B.
基于C的GSS-API样例程序203saddr.
sin_family=AF_INET;saddr.
sin_port=htons(port);saddr.
sin_addr.
s_addr=INADDR_ANY;if((s=socket(AF_INET,SOCK_STREAM,0))tv_sec-tv2->tv_sec)+((float)(tv1->tv_usec-tv2->tv_usec))/1000000);}/**Yes,yes,thisisn'tthebestplacefordoingthistest.
*DONOTREMOVETHISUNTILABETTERTESTHASBEENWRITTEN,THOUGH.
*-TYT*/inttest_import_export_context(context)gss_ctx_id_t*context;{OM_uint32min_stat,maj_stat;gss_buffer_desccontext_token,copied_token;structtimevaltm1,tm2;/**Attempttosaveandthenrestorethecontext.
*/gettimeofday(&tm1,(structtimezone*)0);maj_stat=gss_export_sec_context(&min_stat,context,\&context_token);if(maj_stat!
=GSS_S_COMPLETE){display_status("exportingcontext",maj_stat,min_stat);return1;}gettimeofday(&tm2,(structtimezone*)0);服务器端应用程序204面向开发者的OracleSolaris11安全性指南2014年7月if(verbose&&log)fprintf(log,"Exportedcontext:%dbytes,%7.
4fseconds\n",context_token.
length,timeval_subtract(&tm2,&tm1));copied_token.
length=context_token.
length;copied_token.
value=malloc(context_token.
length);if(copied_token.
value==0){fprintf(log,"Couldn'tallocatememorytocopycontext\token.
\n");return1;}memcpy(copied_token.
value,context_token.
value,\copied_token.
length);maj_stat=gss_import_sec_context(&min_stat,&copied_token,\context);if(maj_stat!
=GSS_S_COMPLETE){display_status("importingcontext",maj_stat,min_stat);return1;}free(copied_token.
value);gettimeofday(&tm1,(structtimezone*)0);if(verbose&&log)fprintf(log,"Importingcontext:%7.
4fseconds\n",timeval_subtract(&tm1,&tm2));(void)gss_release_buffer(&min_stat,&context_token);return0;}/**Function:sign_server**Purpose:Performsthe"sign"service.
**Arguments:**s(r)aTCPsocketonwhichaconnectionhasbeen*accept()ed*service_name(r)theASCIInameoftheGSS-APIserviceto*establishacontextas**Returns:-1onerror**Effects:**sign_serverestablishesacontext,andperformsasinglesignrequest.
**AsignrequestisasingleGSS-APIsealedtoken.
Thetokenis*unsealedandasignatureblock,producedwithgss_sign,isreturned*tothesender.
Thecontextisthendestroyedandtheconnection*closed.
**Ifanyerroroccurs,-1isreturned.
*/intsign_server(s,server_creds)ints;服务器端应用程序附录B.
基于C的GSS-API样例程序205gss_cred_id_tserver_creds;{gss_buffer_descclient_name,xmit_buf,msg_buf;gss_ctx_id_tcontext;OM_uint32maj_stat,min_stat;inti,conf_state,ret_flags;char*cp;/*Establishacontextwiththeclient*/if(server_establish_context(s,server_creds,&context,&client_name,&ret_flags)=0){do{/*AcceptaTCPconnection*/if((s=accept(stmp,NULL,0))#include#include#include#include#include#include#include#include#includeFILE*display_file;staticvoiddisplay_status_1(char*m,OM_uint32code,inttype);staticintwrite_all(intfildes,char*buf,unsignedintnbyte){intret;char*ptr;for(ptr=buf;nbyte;ptr+=ret,nbyte-=ret){ret=write(fildes,ptr,nbyte);if(retlength);ret=write_all(s,(char*)&len,4);if(retvalue,tok->length);if(retlength){if(display_file)fprintf(display_file,"sendingtokendata:%dof%dbyteswritten\n",ret,tok->length);return-1;}return0;}各种GSS-API样例函数附录B.
基于C的GSS-API样例程序211/**Function:recv_token**Purpose:Readsatokenfromafiledescriptor.
**Arguments:**s(r)anopenfiledescriptor*tok(w)thereadtoken**Returns:0onsuccess,-1onfailure**Effects:**recv_tokenreadsthetokenlength(asanetworklong),allocates*memorytoholdthedata,andthenreadsthetokendatafromthe*filedescriptors.
Itblockstoreadthelengthanddata,if*necessary.
Onasuccessfulreturn,thetokenshouldbefreedwith*gss_release_buffer.
Itreturns0onsuccess,and-1ifanerror*occursorifitcouldnotreadallthedata.
*/intrecv_token(s,tok)ints;gss_buffer_ttok;{intret;ret=read_all(s,(char*)&tok->length,4);if(retlength=ntohl(tok->length);tok->value=(char*)malloc(tok->length);if(tok->value==NULL){if(display_file)fprintf(display_file,"Outofmemoryallocatingtokendata\n");return-1;}ret=read_all(s,(char*)tok->value,tok->length);if(retvalue);return-1;各种GSS-API样例函数212面向开发者的OracleSolaris11安全性指南2014年7月}elseif(ret!
=tok->length){fprintf(stderr,"sendingtokendata:%dof%dbyteswritten\n",ret,tok->length);free(tok->value);return-1;}return0;}staticvoiddisplay_status_1(m,code,type)char*m;OM_uint32code;inttype;{OM_uint32maj_stat,min_stat;gss_buffer_descmsg;OM_uint32msg_ctx;msg_ctx=0;while(1){maj_stat=gss_display_status(&min_stat,code,type,GSS_C_NULL_OID,&msg_ctx,&msg);if(display_file)fprintf(display_file,"GSS-APIerror%s:%s\n",m,(char*)msg.
value);(void)gss_release_buffer(&min_stat,&msg);if(!
msg_ctx)break;}}/**Function:display_status**Purpose:displaysGSS-APImessages**Arguments:**msgastringtobedisplayedwiththemessage*maj_stattheGSS-APImajorstatuscode*min_stattheGSS-APIminorstatuscode**Effects:**TheGSS-APImessagesassociatedwithmaj_statandmin_statare*displayedonstderr,eachprecededby"GSS-APIerror:"and*followedbyanewline.
*/voiddisplay_status(msg,maj_stat,min_stat)char*msg;OM_uint32maj_stat;各种GSS-API样例函数附录B.
基于C的GSS-API样例程序213OM_uint32min_stat;{display_status_1(msg,maj_stat,GSS_C_GSS_CODE);display_status_1(msg,min_stat,GSS_C_MECH_CODE);}/**Function:display_ctx_flags**Purpose:displaystheflagsreturnedbycontextinitiationin*ahuman-readableform**Arguments:**intret_flags**Effects:**Stringscorrespondingtothecontextflagsareprintedon*stdout,precededby"contextflag:"andfollowedbyanewline*/voiddisplay_ctx_flags(flags)OM_uint32flags;{if(flags&GSS_C_DELEG_FLAG)fprintf(display_file,"contextflag:GSS_C_DELEG_FLAG\n");if(flags&GSS_C_MUTUAL_FLAG)fprintf(display_file,"contextflag:GSS_C_MUTUAL_FLAG\n");if(flags&GSS_C_REPLAY_FLAG)fprintf(display_file,"contextflag:GSS_C_REPLAY_FLAG\n");if(flags&GSS_C_SEQUENCE_FLAG)fprintf(display_file,"contextflag:GSS_C_SEQUENCE_FLAG\n");if(flags&GSS_C_CONF_FLAG)fprintf(display_file,"contextflag:GSS_C_CONF_FLAG\n");if(flags&GSS_C_INTEG_FLAG)fprintf(display_file,"contextflag:GSS_C_INTEG_FLAG\n");}voidprint_token(tok)gss_buffer_ttok;{inti;unsignedchar*p=tok->value;if(!
display_file)return;for(i=0;ilength;i++,p++){fprintf(display_file,"%02x",*p);if((i%16)==15){fprintf(display_file,"\n");}}fprintf(display_file,"\n");各种GSS-API样例函数214面向开发者的OracleSolaris11安全性指南2014年7月fflush(display_file);}附录C.
GSS-API参考信息215C附录CGSS-API参考信息本附录包括以下几节:"GSS-API函数"[215]提供了GSS-API函数表.
"GSS-API状态码"[217]讨论GSS-API函数返回的状态码,并提供这些状态码的列表.
"GSS-API数据类型和值"[221]讨论GSS-API使用的各种数据类型.
"GSS-API中特定于实现的功能"[224]介绍OracleSolarisGSS-API实现独有的功能.
"Kerberosv5状态码"[226]列出了Kerberosv5机制可以返回的状态码.
其他GSS-API定义可在gssapi.
h文件中找到.
GSS-API函数OracleSolaris软件实现了GSS-API函数.
有关每个函数的更多信息,请参见其手册页.
另请参见"早期GSS-API版本中的函数"[217].
gss_acquire_cred()通过获取预先存在凭证的GSS-API凭证句柄来建立全局标识gss_add_cred()以增量方式构造凭证gss_inquire_cred()获取有关凭证的信息gss_inquire_cred_by_mech()获取有关凭证的每机制信息gss_release_cred()放弃凭证句柄gss_init_sec_context()使用对等应用程序启动安全上下文gss_accept_sec_context()接受对等应用程序启动的安全上下文gss_delete_sec_context()放弃安全上下文GSS-API函数216面向开发者的OracleSolaris11安全性指南2014年7月gss_process_context_token()在对等应用程序的安全上下文中处理令牌gss_context_time()确定上下文处于有效状态的时间gss_inquire_context()获取有关安全上下文的信息gss_wrap_size_limit()确定上下文中gss_wrap()的令牌大小限制gss_export_sec_context()将安全上下文传输给其他进程gss_import_sec_context()导入传输的上下文gss_get_mic()计算消息的加密消息完整性代码(MessageIntegrityCode,MIC)gss_verify_mic()根据消息检查MIC,以验证收到消息的完整性gss_wrap()将MIC附加到消息中,并有选择地加密消息内容gss_unwrap()使用附加的MIC验证消息.
解密消息内容(如果必要)gss_import_name()将连续的字符串名称转换为内部格式名称gss_display_name()将内部格式名称转换为文本gss_compare_name()比较两个内部格式名称gss_release_name()放弃内部格式名称gss_inquire_names_for_mech()列出指定机制支持的名称类型gss_inquire_mechs_for_name()列出支持指定名称类型的机制gss_canonicalize_name()将内部名称转换为机制名称(MechanismName,MN)gss_export_name()将MN转换为导出格式gss_duplicate_name()创建内部名称的副本gss_add_oid_set_member()向组中添加对象标识符gss_display_status()将GSS-API状态码转换为文本gss_indicate_mechs()确定可用的基础验证机制gss_release_buffer()放弃缓冲区GSS-API状态码附录C.
GSS-API参考信息217gss_release_oid_set()放弃一组对象标识符gss_create_empty_oid_set()创建不含对象标识符的组gss_test_oid_set_member()确定对象标识符是否为组的成员早期GSS-API版本中的函数本节说明了早期GSS-API版本中包含的函数.
用于处理OID的函数OracleSolaris的GSS-API实现提供了下列函数,这些函数满足了便捷性和向后兼容性的需要.
但是,其他GSS-API实现可能不支持这些函数.
gss_delete_oid()gss_oid_to_str()gss_str_to_oid()尽管可以将机制的名称从字符串转换为OID,但程序员应尽可能地使用缺省GSS-API机制.
重命名的函数以下函数已被更新的函数所替代.
在所有情况下,新函数的功能与较早的函数等效.
尽管支持较早的函数,但开发者应尽可能地将这些函数替换为更新的函数.

gss_sign()已被替换为gss_get_mic().
gss_verify()已被替换为gss_verify_mic().
gss_seal()已被替换为gss_wrap().
gss_unseal()已被替换为gss_unwrap().
GSS-API状态码主状态码是按下图所示方式在OM_uint32中进行编码的.
GSS-API状态码218面向开发者的OracleSolaris11安全性指南2014年7月图C-1主状态编码如果GSS-API例程返回的GSS状态码的高16位包含非零值,则调用失败.
如果调用错误字段为非零值,则应用程序的例程调用是错误的.
TableC–1中列出了表C-1"GSS-API调用错误".
如果例程错误字段为非零,例程将因例程特定错误(在表C-2"GSS-API例程错误"中列出)而失败.
无论高16位指示失败还是成功,都可以设置状态码的补充信息字段中的位.
表C-3"GSS-API补充信息代码"中列出了各个位的含义.
GSS-API主状态码值下表列出了GSS-API返回的调用错误.
这些错误特定于特定语言绑定(在本例中为C).
表C-1GSS-API调用错误错误字段中的值含义GSS_S_CALL_INACCESSIBLE_READ1不能读取所需的输入参数GSS_S_CALL_INACCESSIBLE_WRITE2不能写入所需的输出参数GSS_S_CALL_BAD_STRUCTURE3参数格式错误下表列出了GSS-API例程错误,即GSS-API函数返回的一般错误.
GSS-API状态码附录C.
GSS-API参考信息219表C-2GSS-API例程错误错误字段中的值含义GSS_S_BAD_MECH1请求不受支持的机制.
GSS_S_BAD_NAME2提供无效名称.
GSS_S_BAD_NAMETYPE3提供的名称属于不受支持的类型.
GSS_S_BAD_BINDINGS4提供不正确的通道绑定.
GSS_S_BAD_STATUS5提供无效状态码.
GSS_S_BAD_MIC、GSS_S_BAD_SIG6令牌具有无效的MIC.
GSS_S_NO_CRED7凭证不可用、不可访问或不受支持.
GSS_S_NO_CONTEXT8未建立上下文.
GSS_S_DEFECTIVE_TOKEN9令牌无效.
GSS_S_DEFECTIVE_CREDENTIAL10凭证无效.
GSS_S_CREDENTIALS_EXPIRED11引用的凭证已到期.
GSS_S_CONTEXT_EXPIRED12上下文已到期.
GSS_S_FAILURE13各种故障.
基础机制检测到未定义特定GSS–API状态码的错误.
机制特定状态码(即次状态码)提供了有关错误的更多详细信息.
GSS_S_BAD_QOP14无法提供请求的保护质量.
GSS_S_UNAUTHORIZED15本地安全策略禁止该操作.
GSS_S_UNAVAILABLE16操作或选项不可用.
GSS_S_DUPLICATE_ELEMENT17请求的凭证元素已存在.
GSS_S_NAME_NOT_MN18提供的名称不是机制名称(MechanismName,MN).
名称GSS_S_COMPLETE(零值)指示缺少API错误或补充信息位.
下表列出了GSS-API函数返回的补充信息值.
表C-3GSS-API补充信息代码Code(代码)位数含义GSS_S_CONTINUE_NEEDED0(LSB)仅由gss_init_sec_context()或gss_accept_sec_context()返回.
必须再次调用例程才能完成其函数.
GSS_S_DUPLICATE_TOKEN1该令牌是早期令牌的副本.
GSS_S_OLD_TOKEN2令牌的有效期已到期.
GSS_S_UNSEQ_TOKEN3已处理后面的令牌.
GSS_S_GAP_TOKEN4未收到预期的每条消息令牌.
有关状态码的更多信息,请参见"GSS-API状态码"[65].
GSS-API状态码220面向开发者的OracleSolaris11安全性指南2014年7月显示状态码函数gss_display_status()将GSS-API状态码转换为文本格式.
采用此格式,可以向用户显示代码或将代码置于文本日志中.
gss_display_status()一次仅显示一个状态码,而且某些函数可以返回多个状态条件.
因此,应该将gss_display_status()作为循环的一部分进行调用.
如果gss_display_status()指示非零状态码,则函数可以提取其他状态码.
例C-1使用gss_display_status()显示状态码OM_uint32message_context;OM_uint32status_code;OM_uint32maj_status;OM_uint32min_status;gss_buffer_descstatus_string;.
.
.
message_context=0;do{maj_status=gss_display_status(&min_status,status_code,GSS_C_GSS_CODE,GSS_C_NO_OID,&message_context,&status_string);fprintf(stderr,"%.
*s\n",\(int)status_string.
length,\(char*)status_string.
value);gss_release_buffer(&min_status,&status_string,);}while(message_context!
=0);状态码宏宏GSS_CALLING_ERROR()、GSS_ROUTINE_ERROR()和GSS_SUPPLEMENTARY_INFO()使用GSS状态码.
这些宏可以删除相关字段以外的所有信息.
例如,可以将GSS_ROUTINE_ERROR()应用于状态码,以删除调用错误和补充信息字段.
此操作仅保留例程错误字段.
可以将这些宏提供的值与相应类型的GSS_S_xxx符号直接进行比较.
如果状态码指示调用错误或例程错误,则宏GSS_ERROR()返回非零值,否则将返回零值.
由GSS-API定义的所有宏一次可以评估多个参数.
GSS-API数据类型和值附录C.
GSS-API参考信息221GSS-API数据类型和值本节介绍各种类型的GSS-API数据类型和值.
某些数据类型(如gss_cred_id_t或gss_name_t)对用户是不透明的.
这些数据类型无需介绍.
本节介绍以下主题:"基本GSS-API数据类型"[221]-给出OM_uint32、gss_buffer_desc、gss_OID_desc、gss_OID_set_desc_struct和gss_channel_bindings_struct数据类型的定义.
"名称类型"[222]-给出为指定名称由GSS-API识别的各种名称格式.
"通道绑定的地址类型"[223]-给出可用作gss_channel_bindings_t结构的initiator_addrtype和acceptor_addrtype字段的各种值.
基本GSS-API数据类型本节介绍GSS-API使用的数据类型.
OM_uint32OM_uint32是与平台无关的32位无符号整数.
gss_buffer_desc包含gss_buffer_t指针的gss_buffer_desc的定义采用以下格式:typedefstructgss_buffer_desc_struct{size_tlength;void*value;}gss_buffer_desc,*gss_buffer_t;gss_OID_desc包含gss_OID指针的gss_OID_desc的定义采用以下格式:typedefstructgss_OID_desc_struct{OM_uint32length;void*elements;}gss_OID_desc,*gss_OID;GSS-API数据类型和值222面向开发者的OracleSolaris11安全性指南2014年7月gss_OID_set_desc包含gss_OID_set指针的gss_OID_set_desc的定义采用以下格式:typedefstructgss_OID_set_desc_struct{size_tcount;gss_OIDelements;}gss_OID_set_desc,*gss_OID_set;gss_channel_bindings_structgss_channel_bindings_struct结构和gss_channel_bindings_t指针的定义的格式如下:typedefstructgss_channel_bindings_struct{OM_uint32initiator_addrtype;gss_buffer_descinitiator_address;OM_uint32acceptor_addrtype;gss_buffer_descacceptor_address;gss_buffer_descapplication_data;}*gss_channel_bindings_t;名称类型名称类型指示关联名称的格式.
有关名称和名称类型的更多信息,请参见"GSS-API中的名称"[56]和"GSS-APIOID"[64].
GSS-API支持下表中的gss_OID名称类型.
GSS_C_NO_NAME建议将符号名称GSS_C_NO_NAME作为参数值,以指示在名称传输中未提供任何值.
GSS_C_NO_OID此值对应于空输入值,而不是实际的对象标识符.
如果指定了该值,则该值基于机制特定缺省可列显语法指示关联名称的解释.
GSS_C_NT_ANONYMOUS标识匿名名称的方式.
可以比较此值,以与机制无关的方式确定名称是否引用匿名主体.
GSS_C_NT_EXPORT_NAME使用gss_export_name()函数导出的名称.
GSS_C_NT_HOSTBASED_SERVICE用于表示与主机关联的服务.
此名称格式是按照以下方式使用两个元素(服务和主机名)构造的:service@hostname.
GSS_C_NT_MACHINE_UID_NAME用于指示与本地系统中的用户对应的数字用户标识符.
该值的解释特定于OS.
gss_import_name()函数将此UID解析为用户名,之后该UID就表示为用户名形式.
GSS-API数据类型和值附录C.
GSS-API参考信息223GSS_C_NT_STRING_STRING_UID_NAME用于指示一个数字字符串,该字符串表示本地系统中的用户的数字用户标识符.
该值的解释特定于OS.
此名称类型与计算机UID格式类似,不同的是缓冲区包含表示用户ID的字符串.
GSS_C_NT_USER_NAME本地系统中的命名用户.
该值的解释特定于OS.
该值采用以下格式:username.
通道绑定的地址类型下表给出了gss_channel_bindings_struct结构的initiator_addrtype和acceptor_addrtype字段的可能值.
这些字段指示名称可以采用的格式,例如ARPAnetIMP地址或AppleTalk地址.
通道绑定将在"在GSS-API中使用通道绑定"[74]中讨论.
表C-4通道绑定地址类型字段值(十进制)地址类型GSS_C_AF_UNSPEC0未指定的地址类型GSS_C_AF_LOCAL1本地主机GSS_C_AF_INET2Internet地址类型,例如IPGSS_C_AF_IMPLINK3ARPAnetIMPGSS_C_AF_PUP4pup协议,例如BSPGSS_C_AF_CHAOS5MITCHAOS协议GSS_C_AF_NS6XEROXNSGSS_C_AF_NBS7nbsGSS_C_AF_ECMA8ECMAGSS_C_AF_DATAKIT9Datakit协议GSS_C_AF_CCITT10CCITTGSS_C_AF_SNA11IBMSNAGSS_C_AF_DECnet12DECnetGSS_C_AF_DLI13直接数据链接接口GSS_C_AF_LAT14LATGSS_C_AF_HYLINK15NSC超级通道GSS_C_AF_APPLETALK16AppleTalkGSS_C_AF_BSC17BISYNCGSS_C_AF_DSS18分布式系统服务GSS_C_AF_OSI19OSITP4GSS_C_AF_X2521X.
25GSS_C_AF_NULLADDR255未指定任何地址GSS-API中特定于实现的功能224面向开发者的OracleSolaris11安全性指南2014年7月GSS-API中特定于实现的功能在API的实现之间GSS-API的某些方面可能有所不同.
大多数情况下,实现之间的差异对程序只有很小的影响.
在所有情况下,开发者都可以不依赖任何特定于给定实现(包括OracleSolaris实现)的操作来最大化可移植性.
特定于OracleSolaris的函数OracleSolaris实现中没有定制的GSS-API函数.
人工可读的名称语法GSS-API实现在与名称对应的可列显语法中可能有所不同.
对于可移植性,应用程序不应该比较使用人工可读(即可列显)格式的名称.
相反,这些应用程序应该使用gss_compare_name()来确定内部格式名称是否与任何其他名称匹配.
OracleSolarisgss_display_name()实现按以下方式显示名称.
如果input_name参数表示用户主体,则gss_display_name()将返回user_principal@realm作为output_name_buffer,返回gss_OID值作为output_name_type.
如果Kerberosv5是基础机制,则gss_OID为1.
2.
840.
11354.
1.
2.
2.
如果gss_display_name()接收到的名称是gss_import_name()使用GSS_C_NO_OID名称类型创建的,则gss_display_name()将在output_name_type参数中返回GSS_C_NO_OID.
匿名名称的格式gss_display_name()函数将输出字符串"",以指示匿名GSS-API主体.
与此名称关联的名称类型OID为GSS_C_NT_ANONYMOUS.
OracleSolaris实现支持的其他有效可列显名称不应以尖括号()括起.
实现选定数据类型以下数据类型已作为指针实现,但某些实现可能将这些类型指定为算术类型:gss_cred_t、gss_ctx_id_t和gss_name_t.
GSS-API中特定于实现的功能附录C.
GSS-API参考信息225删除上下文和存储数据如果上下文建立失败,则OracleSolaris实现不会自动删除部分生成的上下文.
因此,应用程序应该通过使用gss_delete_sec_context()删除上下文来处理此事件.
OracleSolaris实现将通过内存管理自动释放存储的数据(如内部名称).
但是,当不再需要数据元素时,应用程序仍然应该调用相应的函数(如gss_release_name()).

保护通道绑定信息对通道绑定的支持随机制而变化.
Diffie-Hellman机制与Kerberosv5机制都支持通道绑定.
开发者应该假设通道绑定数据没有保密性保护.
尽管Kerberosv5机制提供此保护,但通道绑定数据的保密性对于Diffie-Hellman机制不可用.
上下文导出和进程间令牌OracleSolaris实现检测并拒绝相同上下文的多次导入尝试.
支持的凭证类型OracleSolaris的GSS-API实现支持通过gss_acquire_cred()获取GSS_C_INITIATE、GSS_C_ACCEPT和GSS_C_BOTH凭证.
凭证到期OracleSolaris的GSS-API实现支持凭证到期.
因此,程序员可以在函数(如gss_acquire_cred()和gss_add_cred())中使用与凭证生命周期相关的参数.
上下文到期OracleSolaris的GSS-API实现支持上下文到期.
因此,程序员可以在函数(如gss_init_sec_context()和gss_inquire_context())中使用与上下文生命周期相关的参数.
Kerberosv5状态码226面向开发者的OracleSolaris11安全性指南2014年7月回绕大小限制和QOP值OracleSolaris的GSS-API实现(与任何基础机制相反)不对gss_wrap()处理的消息强加最大大小.
应用程序可以使用gss_wrap_size_limit()确定最大消息大小.
调用gss_wrap_size_limit()时,OracleSolaris的GSS-API实现可以检测无效QOP值.
使用minor_status参数在OracleSolaris的GSS-API实现中,函数在minor_status参数中仅返回特定于机制的信息.
其他实现可能在返回的次状态码中包括特定于实现的返回值.
Kerberosv5状态码每个GSS-API函数都会返回两个状态码:主状态码和次状态码.
主状态码与GSS-API的行为相关.
例如,如果应用程序尝试在安全上下文到期后传输消息,则GSS-API将返回GSS_S_CONTEXT_EXPIRED的主状态码.
"GSS-API状态码"[217]中列出了主状态码.
次状态码是由给定的GSS-API实现支持的基础安全机制返回的.
每个GSS-API函数都采用minor_status或minor_stat参数作为第一个变量.
无论函数是否成功返回,应用程序都可以检查此参数,以了解基础机制返回的状态.
下表列出了minor_status参数中的Kerberosv5返回的状态消息.
有关GSS-API状态码的更多信息,请参见"GSS-API状态码"[65].
Kerberosv5中状态码1的返回消息下表列出了Kerberosv5中状态码1的返回的次状态消息.
表C-5Kerberosv5状态码1次状态值含义KRB5KDC_ERR_NONE-1765328384L无错误KRB5KDC_ERR_NAME_EXP-1765328383L数据库中的客户机项已到期KRB5KDC_ERR_SERVICE_EXP-1765328382L数据库中的服务器项已到期Kerberosv5状态码附录C.
GSS-API参考信息227次状态值含义KRB5KDC_ERR_BAD_PVNO-1765328381L请求的协议版本不受支持KRB5KDC_ERR_C_OLD_MAST_KVNO-1765328380L客户机的密钥用旧的主密钥加密KRB5KDC_ERR_S_OLD_MAST_KVNO-1765328379L服务器的密钥用旧的主密钥加密KRB5KDC_ERR_C_PRINCIPAL_UNKNOWN-1765328378L在Kerberos数据库中找不到客户机KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN-1765328377L在Kerberos数据库中找不到服务器KRB5KDC_ERR_PRINCIPAL_NOT_UNIQUE-1765328376L主体具有Kerberos数据库中的多项KRB5KDC_ERR_NULL_KEY-1765328375L客户机或服务器具有空密钥KRB5KDC_ERR_CANNOT_POSTDATE-1765328374L票证的生效期不能延后KRB5KDC_ERR_NEVER_VALID-1765328373L请求的有效生命周期为负数或太短KRB5KDC_ERR_POLICY-1765328372LKDC策略拒绝请求KRB5KDC_ERR_BADOPTION-1765328371LKDC无法实现请求的选项KRB5KDC_ERR_ETYPE_NOSUPP-1765328370LKDC不支持加密类型KRB5KDC_ERR_SUMTYPE_NOSUPP-1765328369LKDC不支持校验和类型KRB5KDC_ERR_PADATA_TYPE_NOSUPP-1765328368LKDC不支持padata类型KRB5KDC_ERR_TRTYPE_NOSUPP-1765328367LKDC不支持传输的类型KRB5KDC_ERR_CLIENT_REVOKED-1765328366L已撤销客户机的凭证KRB5KDC_ERR_SERVICE_REVOKED-1765328365L已撤销服务器的凭证Kerberosv5中状态码2的返回消息下表列出了Kerberosv5中状态码2的返回的次状态消息.
表C-6Kerberosv5状态码2次状态值含义KRB5KDC_ERR_TGT_REVOKED-1765328364L已撤销TGTKRB5KDC_ERR_CLIENT_NOTYET-1765328363L客户机尚无效,请稍后重试KRB5KDC_ERR_SERVICE_NOTYET-1765328362L服务器尚无效,请稍后重试KRB5KDC_ERR_KEY_EXP-1765328361L口令已到期Kerberosv5状态码228面向开发者的OracleSolaris11安全性指南2014年7月次状态值含义KRB5KDC_ERR_PREAUTH_FAILED-1765328360L预验证失败KRB5KDC_ERR_PREAUTH_REQUIRED-1765328359L需要其他预验证KRB5KDC_ERR_SERVER_NOMATCH-1765328358L请求的服务器和票证不匹配KRB5PLACEHOLD_27到KRB5PLACEHOLD_30-1765328357L到-1765328354LKRB5错误码27到30(保留)KRB5KRB_AP_ERR_BAD_INTEGRITY-1765328353L解密完整性检查失败KRB5KRB_AP_ERR_TKT_EXPIRED-1765328352L票证已到期KRB5KRB_AP_ERR_TKT_NYV-1765328351L票证尚无效KRB5KRB_AP_ERR_REPEAT-1765328350L请求为重放KRB5KRB_AP_ERR_NOT_US-1765328349L票证不是供我们使用KRB5KRB_AP_ERR_BADMATCH-1765328348L票证/验证者不匹配KRB5KRB_AP_ERR_SKEW-1765328347L时钟相位差太大KRB5KRB_AP_ERR_BADADDR-1765328346L网络地址不正确KRB5KRB_AP_ERR_BADVERSION-1765328345L协议版本不匹配KRB5KRB_AP_ERR_MSG_TYPE-1765328344L消息类型无效KRB5KRB_AP_ERR_MODIFIED-1765328343L消息流已经过修改KRB5KRB_AP_ERR_BADORDER-1765328342L消息无序KRB5KRB_AP_ERR_ILL_CR_TKT-1765328341L非法的跨领域票证KRB5KRB_AP_ERR_BADKEYVER-1765328340L密钥版本不可用Kerberosv5中状态码3的返回消息下表列出了Kerberosv5中状态码3的返回的次状态消息.
表C-7Kerberosv5状态码3次状态值含义KRB5KRB_AP_ERR_NOKEY-1765328339L服务密钥不可用KRB5KRB_AP_ERR_MUT_FAIL-1765328338L相互验证失败KRB5KRB_AP_ERR_BADDIRECTION-1765328337L消息方向不正确KRB5KRB_AP_ERR_METHOD-1765328336L需要替换验证方法KRB5KRB_AP_ERR_BADSEQ-1765328335L消息中的序列号不正确KRB5KRB_AP_ERR_INAPP_CKSUM-1765328334L消息中的校验和类型不适合KRB5PLACEHOLD_51到KRB5PLACEHOLD_59-1765328333L到-1765328325LKRB5错误码51到59(保留)KRB5KRB_ERR_GENERIC-1765328324L一般性错误KRB5KRB_ERR_FIELD_TOOLONG-1765328323L字段对于此实现太长Kerberosv5状态码附录C.
GSS-API参考信息229次状态值含义KRB5PLACEHOLD_62到KRB5PLACEHOLD_127-1765328322L到-1765328257LKRB5错误码62到127(保留)未返回值-1765328256L仅供内部使用KRB5_LIBOS_BADLOCKFLAG-1765328255L文件锁定模式的标志无效KRB5_LIBOS_CANTREADPWD-1765328254L无法读取口令KRB5_LIBOS_BADPWDMATCH-1765328253L口令不匹配KRB5_LIBOS_PWDINTR-1765328252L口令读取中断KRB5_PARSE_ILLCHAR-1765328251L组件名称存在非法字符KRB5_PARSE_MALFORMED-1765328250L主体的错误格式表示KRB5_CONFIG_CANTOPEN-1765328249L无法打开/找到Kerberos/etc/krb5/krb5配置文件KRB5_CONFIG_BADFORMAT-1765328248LKerberos/etc/krb5/krb5配置文件的格式不正确KRB5_CONFIG_NOTENUFSPACE-1765328247L空间不足,无法返回完整信息KRB5_BADMSGTYPE-1765328246L为编码指定了无效的消息类型KRB5_CC_BADNAME-1765328245L凭证高速缓存名称格式错误Kerberosv5中状态码4的返回消息下表列出了Kerberosv5中状态码4的返回的次状态消息.
表C-8Kerberosv5状态码4次状态值含义KRB5_CC_UNKNOWN_TYPE-1765328244L未知的凭证高速缓存类型KRB5_CC_NOTFOUND-1765328243L未找到匹配的凭证KRB5_CC_END-1765328242L到达凭证高速缓存的结尾KRB5_NO_TKT_SUPPLIED-1765328241L请求未提供票证KRB5KRB_AP_WRONG_PRINC-1765328240L请求中的主体错误KRB5KRB_AP_ERR_TKT_INVALID-1765328239L票证具有无效的标志集KRB5_PRINC_NOMATCH-1765328238L请求的主体和票证不匹配KRB5_KDCREP_MODIFIED-1765328237LKDC回复与预期情况不匹配Kerberosv5状态码230面向开发者的OracleSolaris11安全性指南2014年7月次状态值含义KRB5_KDCREP_SKEW-1765328236LKDC回复中的时钟相位差太大KRB5_IN_TKT_REALM_MISMATCH-1765328235L初始票证请求中的客户机/服务器领域不匹配KRB5_PROG_ETYPE_NOSUPP-1765328234L程序缺少加密类型支持KRB5_PROG_KEYTYPE_NOSUPP-1765328233L程序缺少密钥类型支持KRB5_WRONG_ETYPE-1765328232L消息中未使用请求的加密类型KRB5_PROG_SUMTYPE_NOSUPP-1765328231L程序缺少校验和类型支持KRB5_REALM_UNKNOWN-1765328230L找不到请求领域的KDCKRB5_SERVICE_UNKNOWN-1765328229LKerberos服务未知KRB5_KDC_UNREACH-1765328228L无法访问请求领域的任何KDCKRB5_NO_LOCALNAME-1765328227L未找到主体名称的本地名称KRB5_MUTUAL_FAILED-1765328226L相互验证失败KRB5_RC_TYPE_EXISTS-1765328225L已注册重放高速缓存类型KRB5_RC_MALLOC-1765328224L无更多内存可供分配(用重放高速缓存代码)KRB5_RC_TYPE_NOTFOUND-1765328223L重放高速缓存类型未知Kerberosv5中状态码5的返回消息下表列出了Kerberosv5中状态码5的返回的次状态消息.
表C-9Kerberosv5状态码5次状态值含义KRB5_RC_UNKNOWN-1765328222L一般性未知RC错误KRB5_RC_REPLAY-1765328221L消息为重放KRB5_RC_IO-1765328220L重放I/O操作失败KRB5_RC_NOIO-1765328219L重放高速缓存类型不支持非易失性存储KRB5_RC_PARSE-1765328218L重放高速缓存名称解析和格式错误KRB5_RC_IO_EOF-1765328217L重放高速缓存I/O的文件结束Kerberosv5状态码附录C.
GSS-API参考信息231次状态值含义KRB5_RC_IO_MALLOC-1765328216L无更多内存可供分配(用重放高速缓存I/O代码)KRB5_RC_IO_PERM-1765328215L重放高速缓存代码中权限被拒绝KRB5_RC_IO_IO-1765328214L重放高速缓存I/O代码中的I/O错误KRB5_RC_IO_UNKNOWN-1765328213L一般性未知RC/IO错误KRB5_RC_IO_SPACE-1765328212L存储重放信息的系统空间不足KRB5_TRANS_CANTOPEN-1765328211L无法打开/找到领域转换文件KRB5_TRANS_BADFORMAT-1765328210L领域转换文件的格式不正确KRB5_LNAME_CANTOPEN-1765328209L无法打开或找到lname转换数据库KRB5_LNAME_NOTRANS-1765328208L不能对请求的主体进行转换KRB5_LNAME_BADFORMAT-1765328207L转换数据库项的格式不正确KRB5_CRYPTO_INTERNAL-1765328206L密码系统内部错误KRB5_KT_BADNAME-1765328205L密钥表名称格式错误KRB5_KT_UNKNOWN_TYPE-1765328204L未知的密钥表类型KRB5_KT_NOTFOUND-1765328203L未找到密钥表项KRB5_KT_END-1765328202L到达密钥表的结尾KRB5_KT_NOWRITE-1765328201L无法写入指定的密钥表Kerberosv5中状态码6的返回消息下表列出了Kerberosv5中状态码6的返回的次状态消息.
表C-10Kerberosv5状态码6次状态值含义KRB5_KT_IOERR-1765328200L写入密钥表时出错KRB5_NO_TKT_IN_RLM-1765328199L找不到请求领域的票证KRB5DES_BAD_KEYPAR-1765328198LDES密钥包含错误的奇偶校验KRB5DES_WEAK_KEY-1765328197LDES密钥为弱密钥KRB5_BAD_ENCTYPE-1765328196L错误的加密类型Kerberosv5状态码232面向开发者的OracleSolaris11安全性指南2014年7月次状态值含义KRB5_BAD_KEYSIZE-1765328195L密钥大小与加密类型不兼容KRB5_BAD_MSIZE-1765328194L消息大小与加密类型不兼容KRB5_CC_TYPE_EXISTS-1765328193L已注册凭证高速缓存类型KRB5_KT_TYPE_EXISTS-1765328192L已注册密钥表类型KRB5_CC_IO-1765328191L凭证高速缓存I/O操作失败KRB5_FCC_PERM-1765328190L凭证高速缓存文件权限不正确KRB5_FCC_NOFILE-1765328189L未找到凭证高速缓存文件KRB5_FCC_INTERNAL-1765328188L内部文件凭证高速缓存错误KRB5_CC_WRITE-1765328187L写入凭证高速缓存文件时出错KRB5_CC_NOMEM-1765328186L没有更多内存可供分配(用凭证高速缓存代码)KRB5_CC_FORMAT-1765328185L凭证高速缓存中的格式错误KRB5_INVALID_FLAGS-1765328184L无效的KDC选项组合,即内部库错误KRB5_NO_2ND_TKT-1765328183L请求缺失的第二个票证KRB5_NOCREDS_SUPPLIED-1765328182L未向库例程提供凭证KRB5_SENDAUTH_BADAUTHVERS-1765328181L发送了错误的sendauth版本KRB5_SENDAUTH_BADAPPLVERS-1765328180Lsendauth发送了错误的应用程序版本KRB5_SENDAUTH_BADRESPONSE-1765328179L错误响应(sendauth交换期间)KRB5_SENDAUTH_REJECTED-1765328178L服务器在执行sendauth交换期间拒绝验证Kerberosv5中状态码7的返回消息下表列出了Kerberosv5中状态码7的返回的次状态消息.
表C-11Kerberosv5状态码7次状态值含义KRB5_PREAUTH_BAD_TYPE-1765328177L预验证类型不受支持Kerberosv5状态码附录C.
GSS-API参考信息233次状态值含义KRB5_PREAUTH_NO_KEY-1765328176L未提供所需的预验证密钥KRB5_PREAUTH_FAILED-1765328175L一般性预验证故障KRB5_RCACHE_BADVNO-1765328174L重放高速缓存的格式版本号不受支持KRB5_CCACHE_BADVNO-1765328173L凭证高速缓存格式版本号不受支持KRB5_KEYTAB_BADVNO-1765328172L密钥表格式的版本号不受支持KRB5_PROG_ATYPE_NOSUPP-1765328171L程序缺少地址类型支持KRB5_RC_REQUIRED-1765328170L消息重放检测需要rcache参数KRB5_ERR_BAD_HOSTNAME-1765328169L主机名无法规范KRB5_ERR_HOST_REALM_UNKNOWN-1765328168L主机名无法规范KRB5_SNAME_UNSUPP_NAMETYPE-1765328167L未针对名称类型定义转换到服务主体KRB5KRB_AP_ERR_V4_REPLY-1765328166L最初的票证响应似乎为版本4错误KRB5_REALM_CANT_RESOLVE-1765328165L无法解析请求领域的KDCKRB5_TKT_NOT_FORWARDABLE-1765328164L请求票证无法获取可转发票证KRB5_FWD_BAD_PRINCIPAL-1765328163L尝试转发凭证时的错误主体名称KRB5_GET_IN_TKT_LOOP-1765328162L在krb5_get_in_tkt中检测到循环KRB5_CONFIG_NODEFREALM-1765328161L配置文件/etc/krb5/krb5.
conf未指定缺省领域KRB5_SAM_UNSUPPORTED-1765328160Lobtain_sam_padata中的SAM标志错误KRB5_KT_NAME_TOOLONG-1765328159L密钥表名字太长KRB5_KT_KVNONOTFOUND-1765328158L密钥表中的主体的密钥版本号不正确KRB5_CONF_NOT_CONFIGURED-1765328157L未配置Kerberos/etc/krb5/krb5.
conf配置文件ERROR_TABLE_BASE_krb5-1765328384L缺省值234面向开发者的OracleSolaris11安全性指南2014年7月附录D.
指定OID235D附录D指定OID应尽可能使用GSS-API所提供的缺省QOP和机制.
请参见"GSS-APIOID"[64].
但是,您可能会出于自己的考虑指定OID.
本附录介绍了如何指定OID.
本章包含以下主题:"包含OID值的文件"[235]"构造机制OID"[237]"指定非缺省机制"[239]包含OID值的文件为方便起见,GSS-API允许以可读方式显示机制和QOP.
在OracleSolaris系统上,有两个文件(/etc/gss/mech和/etc/gss/qop)中包含有关可用机制和可用QOP的信息.
如果无权访问这些文件,则必须提供来自其他源代码的串文字.
针对该机制或QOP发布的Internet标准应当可以实现此目的.
/etc/gss/mech文件/etc/gss/mech文件列出了可用的机制.
/etc/gss/mech包含数值和字母两种格式的名称.
/etc/gss/mech将显示以下格式的信息:ASCII形式的机制名称机制的OID用于实现此机制所提供的服务的共享库也可以是用于实现服务的内核模块/etc/gss/mech样例可能与例D-1"/etc/gss/mech文件"类似.
例D-1/etc/gss/mech文件#包含OID值的文件236面向开发者的OracleSolaris11安全性指南2014年7月#Copyright(c)2005,2012,Oracleand/oritsaffiliates.
Allrightsreserved.
##ident"@(#)mech1.
1203/10/20SMI"##ThisfilecontainstheGSS-APIbasedsecuritymechanismnames,#theassociatedobjectidentifiers(OID)andasharedlibrarythat#implementstheservicesforthemechanismsunderGSS-API.
##MechanismNameObjectIdentifierSharedLibraryKernelModule[Options]#kerberos_v51.
2.
840.
113554.
1.
2.
2mech_krb5.
sokmech_krb5spnego1.
3.
6.
1.
5.
5.
2mech_spnego.
so.
1[msinterop]diffie_hellman_640_01.
3.
6.
4.
1.
42.
2.
26.
2.
4dh640-0.
so.
1diffie_hellman_1024_01.
3.
6.
4.
1.
42.
2.
26.
2.
5dh1024-0.
so.
1/etc/gss/qop文件对于安装的所有机制,/etc/gss/qop文件存储每个机制所支持的所有QOP,这些机制均以ASCII字符串和对应的32位整数两种形式提供.
样例/etc/gss/qop可能如以下示例所示:例D-2/etc/gss/qop文件##Copyright(c)2000,2012byOracleand/oritsaffiliates.
Allrightsreserved.
#Allrightsreserved.
##ident"@(#)qop1.
300/11/09SMI"##ThisfilecontainsinformationabouttheGSS-APIbasedqualityof#protection(QOP),itsstringnameanditsvalue(32-bitinteger).
##QOPstringQOPValueMechanismName#GSS_KRB5_INTEG_C_QOP_DES_MD50kerberos_v5GSS_KRB5_CONF_C_QOP_DES0kerberos_v5gss_str_to_oid()函数附录D.
指定OID237gss_str_to_oid()函数为了与早期版本的GSS-API向下兼容,此GSS-API实现支持函数gss_str_to_oid().
gss_str_to_oid()可用于将表示机制或QOP的字符串转换为OID.
字符串可以采用数字或字的形式.
注意-某些GSS-API实现不支持gss_str_to_oid()、gss_oid_to_str()和gss_release_oid(),因此不建议使用显式的非缺省机制和QOP.
机制字符串可以硬编码到应用程序中,也可以来自用户输入的内容.
但是,并非所有的GSS-API实现都支持gss_str_to_oid(),因此应用程序不应当依赖此函数.
表示机制的数字可以采用两种不同的格式.
第一种格式{1234}是GSS-API规范要求使用的正式格式.
第二种格式1.
2.
3.
4的使用更为广泛,但不是正式的标准格式.
gss_str_to_oid()应当使用第一种格式的机制数字,因此,在调用gss_str_to_oid()之前,必须对采用第二种格式的字符串进行转换.
ExampleD–3()中显示了例D-3"createMechOid()函数"的示例.
如果机制无效,gss_str_to_oid()将返回GSS_S_BAD_MECH.
由于gss_str_to_oid()会分配GSS-API数据空间,因此在您完成操作时会提供已存在的gss_release_oid()函数来删除所分配的OID.
与gss_str_to_oid()一样,gss_release_oid()也不是广泛支持的函数,因此对于希望具有可移植性的通用程序来说,不应依赖此函数.
构造机制OID由于无法始终使用gss_str_to_oid(),因此提供了多种用于查找和选择机制的备用方法.
一种方法是手动构造机制OID,然后将该机制与一组可用的机制进行比较.
另一种方法是获取一组可用机制并从其中选择一个机制.
gss_OID类型的格式如下:typedefstructgss_OID_descstruct{OM_uint32length;void*elements;}gss_OID_desc,*gss_OID;其中,此结构的elements字段指向八位字节字符串的第一个字节,该字符串中包含gss_OID的常规BERTLV编码的值部分的ASN.
1BER编码.
length字段包含该值中的字节数.
例如,对应于DASSX.
509验证机制的gss_OID值包含一个值为7的length字段和一个指向以下八位字节值的elements字段:53,14,2,207,163,7,5.
构造机制OID238面向开发者的OracleSolaris11安全性指南2014年7月构造机制OID的一种方法是声明gss_OID,然后手动初始化表示指定机制的元素.
如上所述,elements值的输入可以是从表中获取的硬编码值,也可以由用户输入.
此方法比使用gss_str_to_oid()稍微麻烦些,但是二者的效果相同.
然后,可以将所构造的gss_OID与gss_indicate_mechs()或gss_inquire_mechs_for_name()函数已返回的一组可用机制进行比较.
应用程序可以使用gss_test_oid_set_member()函数来检查这组可用机制中是否存在所构造的机制OID.
如果gss_test_oid_set_member()没有返回错误,则表明可以将所构造的OID用作GSS-API事务的机制.
构造预设OID的另一种方法是使应用程序使用gss_indicate_mechs()或gss_inquire_mechs_for_name()来获取可用机制的gss_OID_set.
gss_OID_set具有以下格式:typedefstructgss_OID_set_desc_struct{OM_uint32length;void*elements;}gss_OID_set_desc,*gss_OID_set;其中,每个元素都是一个表示相应机制的gss_OID.
应用程序随后会解析每个机制并显示其数值表示形式.
用户可以使用所显示的数值来选择机制.
然后,应用程序会将该机制设置为gss_OID_set的相应成员.
应用程序还可以将所需的机制与首选机制的列表进行比较.
createMechOid()函数说明此函数的目的是为了保持代码完整.
通常,应当使用GSS_C_NULL_OID所指定的缺省机制.
例D-3createMechOid()函数gss_OIDcreateMechOid(constchar*mechStr){gss_buffer_descmechDesc;gss_OIDmechOid;OM_uint32minor;if(mechStr==NULL)return(GSS_C_NULL_OID);mechDesc.
length=strlen(mechStr);mechDesc.
value=(void*)mechStr;if(gss_str_to_oid(&minor,&mechDesc,&mechOid)!
=GSS_S_COMPLETE){fprintf(stderr,"Invalidmechanismoidspecified",mechStr);指定非缺省机制附录D.
指定OID239return(GSS_C_NULL_OID);}return(mechOid);}指定非缺省机制parse_oid()可用于将命令行中的安全机制名称转换为兼容的OID.
例D-4parse_oid()函数staticvoidparse_oid(char*mechanism,gss_OID*oid){char*mechstr=0,*cp;gss_buffer_desctok;OM_uint32maj_stat,min_stat;if(isdigit(mechanism[0])){mechstr=malloc(strlen(mechanism)+5);if(!
mechstr){printf("Couldn'tallocatemechanismscratch!
\n");return;}sprintf(mechstr,"{%s}",mechanism);for(cp=mechstr;*cp;cp++)if(*cp=='.
')*cp='';tok.
value=mechstr;}elsetok.
value=mechanism;tok.
length=strlen(tok.
value);maj_stat=gss_str_to_oid(&min_stat,&tok,oid);if(maj_stat!
=GSS_S_COMPLETE){display_status("str_to_oid",maj_stat,min_stat);return;}if(mechstr)free(mechstr);}240面向开发者的OracleSolaris11安全性指南2014年7月附录E.
SASL示例的源代码241E附录ESASL示例的源代码本附录包含"SASL示例"[131]中所介绍示例的源代码.
本附录包含以下主题:"SASL客户机示例"[241]"SASL服务器示例"[250]"通用代码"[259]SASL客户机示例以下是"SASL示例"[131]中所介绍样例客户机的代码列表.
注-此示例的源代码也可以通过Oracle下载中心获取.
请参见http://www.
oracle.
com/technetwork/indexes/downloads/sdlc-decommission-333274.
html.
#pragmaident"@(#)client.
c1.
403/04/07SMI"/*$Id:client.
c,v1.
32002/09/0315:11:59rjs3Exp$*//**Copyright(c)2001CarnegieMellonUniversity.
Allrightsreserved.
**Redistributionanduseinsourceandbinaryforms,withorwithout*modification,arepermittedprovidedthatthefollowingconditions*aremet:**1.
Redistributionsofsourcecodemustretaintheabovecopyright*notice,thislistofconditionsandthefollowingdisclaimer.
**2.
Redistributionsinbinaryformmustreproducetheabovecopyright*notice,thislistofconditionsandthefollowingdisclaimerin*thedocumentationand/orothermaterialsprovidedwiththe*distribution.
**3.
Thename"CarnegieMellonUniversity"mustnotbeusedto*endorseorpromoteproductsderivedfromthissoftwarewithout*priorwrittenpermission.
Forpermissionoranyotherlegal*details,pleasecontact*OfficeofTechnologyTransfer*CarnegieMellonUniversity*5000ForbesAvenueSASL客户机示例242面向开发者的OracleSolaris11安全性指南2014年7月*Pittsburgh,PA15213-3890*(412)268-4387,fax:(412)268-7395*tech-transfer@andrew.
cmu.
edu**4.
Redistributionsofanyformwhatsoevermustretainthefollowing*acknowledgment:*"ThisproductincludessoftwaredevelopedbyComputingServices*atCarnegieMellonUniversity(http://www.
cmu.
edu/computing/).
"**CARNEGIEMELLONUNIVERSITYDISCLAIMSALLWARRANTIESWITHREGARDTO*THISSOFTWARE,INCLUDINGALLIMPLIEDWARRANTIESOFMERCHANTABILITY*ANDFITNESS,INNOEVENTSHALLCARNEGIEMELLONUNIVERSITYBELIABLE*FORANYSPECIAL,INDIRECTORCONSEQUENTIALDAMAGESORANYDAMAGES*WHATSOEVERRESULTINGFROMLOSSOFUSE,DATAORPROFITS,WHETHERIN*ANACTIONOFCONTRACT,NEGLIGENCEOROTHERTORTIOUSACTION,ARISING*OUTOFORINCONNECTIONWITHTHEUSEORPERFORMANCEOFTHISSOFTWARE.
*/#include#include#include#include#include#include#include#ifdefHAVE_UNISTD_H#include#endif#include#include#include#include#ifdef_SUN_SDK_#include#endif/*_SUN_SDK_*/#include#include#include"common.
h"/*remove\r\natendoftheline*/staticvoidchop(char*s){char*p;assert(s);p=s+strlen(s)-1;if(p[0]=='\n'){*p--='\0';SASL客户机示例附录E.
SASL示例的源代码243}if(p>=s&&p[0]=='\r'){*p--='\0';}}staticintgetrealm(void*context__attribute__((unused)),intid,constchar**availrealms,constchar**result){staticcharbuf[1024];/*Double-checktheID*/if(id!
=SASL_CB_GETREALM)returnSASL_BADPARAM;if(!
result)returnSASL_BADPARAM;printf("pleasechoosearealm(available:");while(*availrealms){printf("%s",*availrealms);availrealms++;}printf("):");fgets(buf,sizeofbuf,stdin);chop(buf);*result=buf;returnSASL_OK;}staticintsimple(void*context__attribute__((unused)),intid,constchar**result,unsigned*len){staticcharbuf[1024];/*Double-checktheconnection*/if(!
result)returnSASL_BADPARAM;switch(id){caseSASL_CB_USER:printf("pleaseenteranauthorizationid:");break;caseSASL_CB_AUTHNAME:printf("pleaseenteranauthenticationid:");break;default:returnSASL_BADPARAM;}fgets(buf,sizeofbuf,stdin);SASL客户机示例244面向开发者的OracleSolaris11安全性指南2014年7月chop(buf);*result=buf;if(len)*len=strlen(buf);returnSASL_OK;}#ifndefHAVE_GETPASSPHRASEstaticchar*getpassphrase(constchar*prompt){returngetpass(prompt);}#endif/*!
HAVE_GETPASSPHRASE*/staticintgetsecret(sasl_conn_t*conn,void*context__attribute__((unused)),intid,sasl_secret_t**psecret){char*password;size_tlen;staticsasl_secret_t*x;/*paranoiacheck*/if(!
conn||!
psecret||id!
=SASL_CB_PASS)returnSASL_BADPARAM;password=getpassphrase("Password:");if(!
password)returnSASL_FAIL;len=strlen(password);x=(sasl_secret_t*)realloc(x,sizeof(sasl_secret_t)+len);if(!
x){memset(password,0,len);returnSASL_NOMEM;}x->len=len;#ifdef_SUN_SDK_strcpy((char*)x->data,password);#elsestrcpy(x->data,password);#endif/*_SUN_SDK_*/memset(password,0,len);*psecret=x;returnSASL_OK;}SASL客户机示例附录E.
SASL示例的源代码245staticintgetpath(void*context__attribute__((unused)),constchar**path){*path=getenv("SASL_PATH");if(*path==NULL)*path=PLUGINDIR;returnSASL_OK;}/*callbackswesupport*/staticsasl_callback_tcallbacks[]={{SASL_CB_GETREALM,&getrealm,NULL},{SASL_CB_USER,&simple,NULL},{SASL_CB_AUTHNAME,&simple,NULL},{SASL_CB_PASS,&getsecret,NULL},{SASL_CB_GETPATH,&getpath,NULL},{SASL_CB_LIST_END,NULL,NULL}};intgetconn(constchar*host,constchar*port){structaddrinfohints,*ai,*r;interr,sock=-1;memset(&hints,0,sizeof(hints));hints.
ai_family=PF_UNSPEC;hints.
ai_socktype=SOCK_STREAM;if((err=getaddrinfo(host,port,&hints,&ai))!
=0){fprintf(stderr,"getaddrinfo:%s\n",gai_strerror(err));exit(EX_UNAVAILABLE);}for(r=ai;r;r=r->ai_next){sock=socket(r->ai_family,r->ai_socktype,r->ai_protocol);if(sockai_addr,r->ai_addrlen)>=0)break;close(sock);sock=-1;}freeaddrinfo(ai);if(sockargc-1){#ifdef_SUN_SDK_usage(argv[0]);#elseusage();#endif/*_SUN_SDK_*/SASL客户机示例附录E.
SASL示例的源代码249}if(optind==argc-1){host=argv[optind];}/*initializethesasllibrary*/r=sasl_client_init(callbacks);if(r!
=SASL_OK)saslfail(r,"initializinglibsasl");/*connecttoremoteserver*/fd=getconn(host,port);/*setipaddresses*/salen=sizeof(local_ip);if(getsockname(fd,(structsockaddr*)&local_ip,&salen)#include#include#include#include#include#include#ifdefHAVE_UNISTD_H#include#endif#include#include#include#include#ifdef_SUN_SDK_#include#endif/*_SUN_SDK_*/#include#include"common.
h"#if!
defined(IPV6_BINDV6ONLY)&&defined(IN6P_IPV6_V6ONLY)#defineIPV6_BINDV6ONLYIN6P_BINDV6ONLY#endif#if!
defined(IPV6_V6ONLY)&&defined(IPV6_BINDV6ONLY)#defineIPV6_V6ONLYIPV6_BINDV6ONLY#endif#ifndefIPV6_BINDV6ONLY#undefIPV6_V6ONLY#endifstaticintgetpath(void*context__attribute__((unused)),SASL服务器示例252面向开发者的OracleSolaris11安全性指南2014年7月constchar**path){*path=getenv("SASL_PATH");if(*path==NULL)*path=PLUGINDIR;returnSASL_OK;}/*callbackswesupport*/staticsasl_callback_tcallbacks[]={{SASL_CB_GETPATH,&getpath,NULL},{SASL_CB_LIST_END,NULL,NULL}};/*createasocketlisteningonport'port'*//*ifafisPF_UNSPECmorethanonesocketmightbereturned*//*thereturnedlistisdynamicallyallocated,socallerneedstofreeit*/int*listensock(constchar*port,constintaf){structaddrinfohints,*ai,*r;interr,maxs,*sock,*socks;constinton=1;memset(&hints,0,sizeof(hints));hints.
ai_flags=AI_PASSIVE;hints.
ai_family=af;hints.
ai_socktype=SOCK_STREAM;err=getaddrinfo(NULL,port,&hints,&ai);if(err){fprintf(stderr,"%s\n",gai_strerror(err));exit(EX_USAGE);}/*Countmaxnumberofsocketswecanopen*/for(maxs=0,r=ai;r;r=r->ai_next,maxs++);socks=malloc((maxs+1)*sizeof(int));if(!
socks){fprintf(stderr,"couldn'tallocatememoryforsockets\n");freeaddrinfo(ai);exit(EX_OSERR);}socks[0]=0;/*numofsocketscounteratstartofarray*/sock=socks+1;for(r=ai;r;r=r->ai_next){fprintf(stderr,"trying%d,%d,%d\n",r->ai_family,r->ai_socktype,r->ai_protocol);*sock=socket(r->ai_family,r->ai_socktype,r->ai_protocol);SASL服务器示例附录E.
SASL示例的源代码253if(*sockai_family==AF_INET6){if(setsockopt(*sock,IPPROTO_IPV6,IPV6_BINDV6ONLY,(void*)&on,sizeof(on))ai_addr,r->ai_addrlen)maxfd)maxfd=l[i];}for(;;){charlocaladdr[NI_MAXHOST|NI_MAXSERV],remoteaddr[NI_MAXHOST|NI_MAXSERV];charmyhostname[1024+1];charhbuf[NI_MAXHOST],pbuf[NI_MAXSERV];structsockaddr_storagelocal_ip,remote_ip;intsalen;intnfds,fd=-1;FILE*in,*out;fd_setreadfds;FD_ZERO(&readfds);for(i=1;i#include#include#include#ifdef_SUN_SDK_#include#endif/*_SUN_SDK_*/#include/*send/recvlibraryforIMAP4styleliterals.
reallynotimportant;justonewayofdoinglengthcodedstrings*/intsend_string(FILE*f,constchar*s,intl){intal;al=fprintf(f,"{%d}\r\n",l);fwrite(s,1,l,f);fflush(f);printf("send:{%d}\n",l);while(l--){if(isprint((unsignedchar)*s)){printf("%c",*s);}else{printf("[%X]",(unsignedchar)*s);}s++;}printf("\n");returnal;}intrecv_string(FILE*f,char*buf,intbuflen){intc;intlen,l;char*s;c=fgetc(f);if(c!
='{')return-1;/*readlength*/len=0;c=fgetc(f);while(isdigit(c)){len=len*10+(c-'0');c=fgetc(f);}通用代码附录E.
SASL示例的源代码261if(c!
='}')return-1;c=fgetc(f);if(c!
='\r')return-1;c=fgetc(f);if(c!
='\n')return-1;/*readstring*/if(buflen=lvl){va_start(ap,fmt);ret=vfprintf(stdout,fmt,ap);va_end(ap);}returnret;}voidsaslerr(intwhy,constchar*what){通用代码262面向开发者的OracleSolaris11安全性指南2014年7月fprintf(stderr,"%s:%s",what,sasl_errstring(why,NULL,NULL));}voidsaslfail(intwhy,constchar*what){saslerr(why,what);exit(EX_TEMPFAIL);}附录F.
SASL参考信息表263F附录FSASL参考信息表本附录提供了SASL(SimpleAuthenticationandSecurityLayer,简单验证和安全层)的参考信息.
SASL接口摘要下表提供了一些SASL接口的简要说明.
表F-1通用于客户机和服务器的SASL函数函数描述sasl_version获取SASL库的版本信息.
sasl_done释放所有的SASL全局状态.
sasl_dispose完成连接以后,处置sasl_conn_t.
sasl_getprop获取属性,例如用户名、安全层信息.
sasl_setprop设置SASL属性.
sasl_errdetail根据上次出现的连接错误生成字符串.
sasl_errstring将SASL错误代码转换为字符串.
sasl_encode使用安全层对要发送的数据进行编码.
sasl_encodev对通过安全层传输的数据块进行编码.
使用iovec*作为输入参数.
sasl_listmech创建可用机制的列表.
sasl_global_listmech返回所有可能机制的数组.
请注意,此接口已过时.
sasl_seterror设置将由sasl_errdetail()返回的错误字符串.
sasl_idle配置saslib以便在空闲期间或网络往返期间执行计算.
sasl_decode对使用安全层接收的数据进行解码.
表F-2仅限于客户机的基本SASL函数函数描述sasl_client_init最初调用一次,以装入和初始化客户机插件.
sasl_client_new初始化客户机连接.
设置sasl_conn_t上下文.
sasl_client_start选择连接机制.
SASL接口摘要264面向开发者的OracleSolaris11安全性指南2014年7月函数描述sasl_client_step执行一个验证步骤.
表F-3基本的SASL服务器函数(客户机可选的)函数描述sasl_server_init最初调用一次,以装入和初始化服务器插件.
sasl_server_new初始化服务器连接.
设置sasl_conn_t上下文.
sasl_server_start开始验证交换.
sasl_server_step执行一个验证交换步骤.
sasl_checkpass检查纯文本口令短语.
sasl_checkapop检查APOP质询/响应.
使用类似于CRAM-MD5机制的伪APOP机制(可选的).
请注意,此接口已过时.
sasl_user_exists检查用户是否存在.
sasl_setpass更改口令.
(可选)添加用户项.
sasl_auxprop_request请求辅助属性.
sasl_auxprop_getctx获取连接的辅助属性上下文.
表F-4用于配置基本服务的SASL函数函数描述sasl_set_alloc指定内存分配函数.
请注意,此接口已过时.
sasl_set_mutex指定互斥函数.
请注意,此接口已过时.
sasl_client_add_plugin添加客户机插件.
sasl_server_add_plugin添加服务器插件.
sasl_canonuser_add_plugin添加用户标准化插件.
sasl_auxprop_add_plugin添加辅助属性插件.
表F-5SASL实用程序函数函数描述sasl_decode64使用base64解码.
sasl_encode64使用base64编码.
sasl_utf8verify验证字符串是否为有效的UTF-8.
sasl_erasebuffer删除与安全性相关的缓冲区或口令.
实现可能会使用阻止恢复的删除逻辑.

RackNerd :美国大硬盘服务器促销/洛杉矶multacom数据中心/双路e5-2640v2/64G内存/256G SSD+160T SAS/$389/月

大硬盘服务器、存储服务器、Chia矿机。RackNerd,2019年末成立的商家,主要提供各类KVM VPS主机、独立服务器和站群服务器等。当前RackNerd正在促销旗下几款美国大硬盘服务器,位于洛杉矶multacom数据中心,亚洲优化线路,非常适合存储、数据备份等应用场景,双路e5-2640v2,64G内存,56G SSD系统盘,160T SAS数据盘,流量是每月200T,1Gbps带宽,配5...

云如故枣庄高防(49元)大内存2H2G49元8H8G109元

云如故是一家成立于2018年的国内企业IDC服务商,由山东云如故网络科技有限公司运营,IDC ICP ISP CDN VPN IRCS等证件齐全!合法运营销售,主要从事自营高防独立服务器、物理机、VPS、云服务器,虚拟主机等产品销售,适合高防稳定等需求的用户,可用于建站、游戏、商城、steam、APP、小程序、软件、资料存储等等各种个人及企业级用途。机房可封UDP 海外 支持策略定制 双层硬件(傲...

MineServer:洛杉矶CN2 GIA VPS/512MB内存/20GB NVME/800GB流量/200Mbps/KVM,58元/季

mineserver怎么样?mineserver是一家国人商家,主要提供香港CN2 KVM VPS、香港CMI KVM VPS、日本CN2 KVM VPS、洛杉矶cn2 gia端口转发等服务,之前介绍过几次,最近比较活跃。这家新推出了洛杉矶CN2 GIA VPS,512MB内存/20GB NVME/800GB流量/200Mbps/KVM,58元/季,并且进行了带宽升级,同时IP更改为美国IP。点击...

内存清理为你推荐
独立ip空间独立ip主机空间有什么用?免费国内空间跪求国内最好的免费空间!网站域名一个网站要几个域名个人虚拟主机个人网站该购买什么类型虚拟主机?jsp虚拟空间请问如何卖掉JSP虚拟主机jsp虚拟空间java虚拟主机空间怎么选择,国内jsp虚拟主机比较稳定java项目做好后需要推荐一下吧网站空间购买哪里买网站空间好?apache虚拟主机linux apache虚拟主机有几种方式华众虚拟主机管理系统华众虚拟主机管理系统请问。华众 虚拟主机管理系统 这个问题 怎么解决 。就是后台可以开通虚拟主机 没有问题,但是 删除虚拟主机 后台显示删除成功的,但是实际在服务器上 文件夹 ftp iis站点 都没有被删除 是什么问题顶级域名什么是顶级域名
圣迭戈 创宇云 ssh帐号 全能主机 qq数据库 台湾谷歌网址 京东商城0元抢购 idc是什么 中国电信测速网 常州联通宽带 web服务器搭建 帽子云排名 广州虚拟主机 百度云空间 金主 江苏徐州移动 杭州电信 cdn服务 512内存 免费服务器 更多