From 139c40de1bc595ccd4b8ca952da9e2a37bc8a18e Mon Sep 17 00:00:00 2001
From: dugan <dugan@localhost>
Date: Wed, 05 Nov 2008 13:22:43 +0000
Subject: [PATCH] These fixes add confidentiality/integrity to the SASL GSSAPI and DIGEST-MD5 mechanisms. The issue links:
---
opends/src/admin/defn/org/opends/server/admin/std/DigestMD5SASLMechanismHandlerConfiguration.xml | 131 +
opends/src/server/org/opends/server/core/AccessControlConfigManager.java | 2
opends/tests/unit-tests-testng/src/server/org/opends/server/extensions/DigestMD5SASLMechanismHandlerTestCase.java | 35
opends/src/server/org/opends/server/util/ServerConstants.java | 33
opends/src/server/org/opends/server/authorization/dseecompat/AciEvalContext.java | 2
opends/src/admin/defn/org/opends/server/admin/std/GSSAPISASLMechanismHandlerConfiguration.xml | 81
opends/src/server/org/opends/server/extensions/GSSAPISASLMechanismHandler.java | 534 ++---
opends/src/server/org/opends/server/extensions/NullConnectionSecurityProvider.java | 9
opends/tests/unit-tests-testng/src/server/org/opends/server/tools/LDAPSearchTestCase.java | 17
opends/src/messages/messages/extension.properties | 353 +---
opends/src/server/org/opends/server/protocols/ldap/LDAPClientConnection.java | 25
opends/resource/schema/02-config.ldif | 19
opends/src/server/org/opends/server/authorization/dseecompat/Aci.java | 2
opends/src/server/org/opends/server/extensions/SASLSecurityProvider.java | 457 +++++
opends/src/server/org/opends/server/extensions/SASLContext.java | 894 ++++++++++
opends/src/server/org/opends/server/authorization/dseecompat/AciContainer.java | 51
opends/src/server/org/opends/server/api/AccessControlHandler.java | 17
opends/src/server/org/opends/server/api/ConnectionSecurityProvider.java | 10
opends/src/server/org/opends/server/authorization/dseecompat/AciHandler.java | 36
opends/src/server/org/opends/server/extensions/DigestMD5SASLMechanismHandler.java | 1631 ++-----------------
opends/src/server/org/opends/server/extensions/TLSConnectionSecurityProvider.java | 9
opends/tests/unit-tests-testng/src/server/org/opends/server/tools/LDAPCompareTestCase.java | 21
opends/tests/unit-tests-testng/src/server/org/opends/server/tools/LDAPAuthenticationHandlerTestCase.java | 38
opends/tests/unit-tests-testng/src/server/org/opends/server/tools/LDAPModifyTestCase.java | 17
opends/src/server/org/opends/server/api/ClientConnection.java | 28
opends/src/server/org/opends/server/core/DefaultAccessControlHandler.java | 10
/dev/null | 531 ------
opends/tests/unit-tests-testng/src/server/org/opends/server/types/PrivilegeTestCase.java | 24
opends/src/server/org/opends/server/authorization/dseecompat/AciLDAPOperationContainer.java | 20
29 files changed, 2,378 insertions(+), 2,659 deletions(-)
diff --git a/opends/resource/schema/02-config.ldif b/opends/resource/schema/02-config.ldif
index 00e4eeb..79bf5ae 100644
--- a/opends/resource/schema/02-config.ldif
+++ b/opends/resource/schema/02-config.ldif
@@ -2354,6 +2354,21 @@
NAME 'ds-cfg-prohibited-subtrees'
SYNTAX 1.3.6.1.4.1.1466.115.121.1.12
X-ORIGIN 'OpenDS Directory Server' )
+attributeTypes: ( 1.3.6.1.4.1.26027.1.1.511
+ NAME 'ds-cfg-quality-of-protection'
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.15
+ SINGLE-VALUE
+ X-ORIGIN 'OpenDS Directory Server' )
+attributeTypes: ( 1.3.6.1.4.1.26027.1.1.512
+ NAME 'ds-cfg-cipher-strength'
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.15
+ SINGLE-VALUE
+ X-ORIGIN 'OpenDS Directory Server' )
+attributeTypes: ( 1.3.6.1.4.1.26027.1.1.513
+ NAME 'ds-cfg-principal-name'
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.15
+ SINGLE-VALUE
+ X-ORIGIN 'OpenDS Directory Server' )
objectClasses: ( 1.3.6.1.4.1.26027.1.2.1
NAME 'ds-cfg-access-control-handler'
SUP top
@@ -2801,6 +2816,8 @@
STRUCTURAL
MUST ds-cfg-identity-mapper
MAY ( ds-cfg-realm $
+ ds-cfg-cipher-strength $
+ ds-cfg-quality-of-protection $
ds-cfg-server-fqdn )
X-ORIGIN 'OpenDS Directory Server' )
objectClasses: ( 1.3.6.1.4.1.26027.1.2.47
@@ -2811,6 +2828,8 @@
ds-cfg-realm $
ds-cfg-kdc-address $
ds-cfg-keytab $
+ ds-cfg-principal-name $
+ ds-cfg-quality-of-protection $
ds-cfg-server-fqdn )
X-ORIGIN 'OpenDS Directory Server' )
objectClasses: ( 1.3.6.1.4.1.26027.1.2.48
diff --git a/opends/src/admin/defn/org/opends/server/admin/std/DigestMD5SASLMechanismHandlerConfiguration.xml b/opends/src/admin/defn/org/opends/server/admin/std/DigestMD5SASLMechanismHandlerConfiguration.xml
index e52079d..ceb4cc0 100644
--- a/opends/src/admin/defn/org/opends/server/admin/std/DigestMD5SASLMechanismHandlerConfiguration.xml
+++ b/opends/src/admin/defn/org/opends/server/admin/std/DigestMD5SASLMechanismHandlerConfiguration.xml
@@ -33,18 +33,18 @@
<adm:synopsis>
The DIGEST-MD5 SASL mechanism
is used to perform all processing related to SASL DIGEST-MD5
- authentication.
+ authentication.
</adm:synopsis>
<adm:description>
- The DIGEST-MD5 SASL mechanism is very similar
- to the CRAM-MD5 mechanism in that it allows for password-based
- authentication without exposing the password in the clear
- (although it does require that both the client and the server
- have access to the clear-text password). Like the CRAM-MD5
- mechanism, it uses data that is randomly generated by the server
- to make it resistant to replay attacks, but it also includes
- randomly-generated data from the client, which makes it also
- resistant to problems resulting from weak server-side random
+ The DIGEST-MD5 SASL mechanism is very similar
+ to the CRAM-MD5 mechanism in that it allows for password-based
+ authentication without exposing the password in the clear
+ (although it does require that both the client and the server
+ have access to the clear-text password). Like the CRAM-MD5
+ mechanism, it uses data that is randomly generated by the server
+ to make it resistant to replay attacks, but it also includes
+ randomly-generated data from the client, which makes it also
+ resistant to problems resulting from weak server-side random
number generation.
</adm:description>
<adm:profile name="ldap">
@@ -64,18 +64,18 @@
</adm:property-override>
<adm:property name="realm">
<adm:synopsis>
- Specifies the realm that is to be used by the server for
+ Specifies the realms that is to be used by the server for
DIGEST-MD5 authentication.
</adm:synopsis>
<adm:description>
- If this value is not provided, then the server defaults to use a
- set of realm names that correspond to the defined suffixes.
+ If this value is not provided, then the server defaults to use the fully
+ qualified hostname of the machine.
</adm:description>
<adm:default-behavior>
<adm:alias>
<adm:synopsis>
- The server defaults to a set of realm names that
- correspond to the defined suffixes.
+ If this value is not provided, then the server defaults to use the fully
+ qualified hostname of the machine.
</adm:synopsis>
</adm:alias>
</adm:default-behavior>
@@ -85,8 +85,7 @@
<adm:regex>.*</adm:regex>
<adm:usage>STRING</adm:usage>
<adm:synopsis>
- Any realm string. As needed, it be a DN or matched
- to a realm already in use for another service.
+ Any realm string that does not contain a comma.
</adm:synopsis>
</adm:pattern>
</adm:string>
@@ -96,7 +95,80 @@
<ldap:name>ds-cfg-realm</ldap:name>
</ldap:attribute>
</adm:profile>
- </adm:property> <adm:property name="identity-mapper" mandatory="true">
+ </adm:property>
+ <adm:property name="quality-of-protection">
+ <adm:synopsis>
+ The name of a property that specifies the quality of protection
+ the server will support.
+ </adm:synopsis>
+ <adm:default-behavior>
+ <adm:defined>
+ <adm:value>none</adm:value>
+ </adm:defined>
+ </adm:default-behavior>
+ <adm:syntax>
+ <adm:enumeration>
+ <adm:value name="none">
+ <adm:synopsis>
+ QOP equals authentication only.
+ </adm:synopsis>
+ </adm:value>
+ <adm:value name="integrity">
+ <adm:synopsis>
+ Quality of protection equals authentication with integrity
+ protection.
+ </adm:synopsis>
+ </adm:value>
+ <adm:value name="confidentiality">
+ <adm:synopsis>
+ Quality of protection equals authentication with integrity and
+ confidentiality protection.
+ </adm:synopsis>
+ </adm:value>
+ </adm:enumeration>
+ </adm:syntax>
+ <adm:profile name="ldap">
+ <ldap:attribute>
+ <ldap:name>ds-cfg-quality-of-protection</ldap:name>
+ </ldap:attribute>
+ </adm:profile>
+ </adm:property>
+ <adm:property name="cipher-strength">
+ <adm:synopsis>
+ The name of a property that specifies the minimum cipher strength that the
+ server will support.
+ </adm:synopsis>
+ <adm:default-behavior>
+ <adm:defined>
+ <adm:value>low</adm:value>
+ </adm:defined>
+ </adm:default-behavior>
+ <adm:syntax>
+ <adm:enumeration>
+ <adm:value name="low">
+ <adm:synopsis>
+ Cipher strength suported is high, medium or low.
+ </adm:synopsis>
+ </adm:value>
+ <adm:value name="medium">
+ <adm:synopsis>
+ Cipher strength suported is medium,high.
+ </adm:synopsis>
+ </adm:value>
+ <adm:value name="high">
+ <adm:synopsis>
+ Cipher strength suported is high only.
+ </adm:synopsis>
+ </adm:value>
+ </adm:enumeration>
+ </adm:syntax>
+ <adm:profile name="ldap">
+ <ldap:attribute>
+ <ldap:name>ds-cfg-cipher-strength</ldap:name>
+ </ldap:attribute>
+ </adm:profile>
+ </adm:property>
+ <adm:property name="identity-mapper" mandatory="true">
<adm:synopsis>
Specifies the name of the identity mapper that is to be used
with this SASL mechanism handler to match the authentication
@@ -128,20 +200,21 @@
</ldap:attribute>
</adm:profile>
</adm:property>
+
<adm:property name="server-fqdn">
<adm:synopsis>
Specifies the DNS-resolvable fully-qualified domain name for the
- server that is used when validating the digest-uri parameter during
- the authentication process.
+ server that is used when validating the digest-uri parameter during
+ the authentication process.
</adm:synopsis>
<adm:description>
- If this configuration attribute is
- present, then the server expects that clients use a digest-uri equal
- to "ldap/" followed by the value of this attribute. For example, if
- the attribute has a value of "directory.example.com", then the
- server expects clients to use a digest-uri of
- "ldap/directory.example.com". If no value is provided, then the
- server does not attempt to validate the digest-uri provided by the
+ If this configuration attribute is
+ present, then the server expects that clients use a digest-uri equal
+ to "ldap/" followed by the value of this attribute. For example, if
+ the attribute has a value of "directory.example.com", then the
+ server expects clients to use a digest-uri of
+ "ldap/directory.example.com". If no value is provided, then the
+ server does not attempt to validate the digest-uri provided by the
client and accepts any value.
</adm:description>
<adm:default-behavior>
@@ -158,12 +231,12 @@
<adm:regex>.*</adm:regex>
<adm:usage>STRING</adm:usage>
<adm:synopsis>
- The fully-qualified address that is expected for clients to use
+ The fully-qualified address that is expected for clients to use
when connecting to the server and authenticating via DIGEST-MD5.
</adm:synopsis>
</adm:pattern>
</adm:string>
- </adm:syntax>
+ </adm:syntax>
<adm:profile name="ldap">
<ldap:attribute>
<ldap:name>ds-cfg-server-fqdn</ldap:name>
diff --git a/opends/src/admin/defn/org/opends/server/admin/std/GSSAPISASLMechanismHandlerConfiguration.xml b/opends/src/admin/defn/org/opends/server/admin/std/GSSAPISASLMechanismHandlerConfiguration.xml
index b76f7fb..2579301 100644
--- a/opends/src/admin/defn/org/opends/server/admin/std/GSSAPISASLMechanismHandlerConfiguration.xml
+++ b/opends/src/admin/defn/org/opends/server/admin/std/GSSAPISASLMechanismHandlerConfiguration.xml
@@ -31,15 +31,15 @@
xmlns:adm="http://www.opends.org/admin"
xmlns:ldap="http://www.opends.org/admin-ldap">
<adm:synopsis>
- The GSSAPI SASL mechanism
+ The GSSAPI SASL mechanism
performs all processing related to SASL GSSAPI
authentication using Kerberos V5.
</adm:synopsis>
<adm:description>
- The GSSAPI SASL mechanism provides the ability for clients
- to authenticate themselves to the server using existing
- authentication in a Kerberos environment. This mechanism
- provides the ability to achieve single sign-on for
+ The GSSAPI SASL mechanism provides the ability for clients
+ to authenticate themselves to the server using existing
+ authentication in a Kerberos environment. This mechanism
+ provides the ability to achieve single sign-on for
Kerberos-based clients.
</adm:description>
<adm:profile name="ldap">
@@ -85,7 +85,7 @@
</adm:synopsis>
<adm:description>
If provided, this property must be a fully-qualified DNS-resolvable name.
- If this property is not provided, then the server attempts to determine it
+ If this property is not provided, then the server attempts to determine it
from the system-wide Kerberos configuration.
</adm:description>
<adm:default-behavior>
@@ -105,6 +105,71 @@
</ldap:attribute>
</adm:profile>
</adm:property>
+ <adm:property name="quality-of-protection">
+ <adm:synopsis>
+ The name of a property that specifies the quality of protection
+ the server will support.
+ </adm:synopsis>
+ <adm:default-behavior>
+ <adm:defined>
+ <adm:value>none</adm:value>
+ </adm:defined>
+ </adm:default-behavior>
+ <adm:syntax>
+ <adm:enumeration>
+ <adm:value name="none">
+ <adm:synopsis>
+ QOP equals authentication only.
+ </adm:synopsis>
+ </adm:value>
+ <adm:value name="integrity">
+ <adm:synopsis>
+ Quality of protection equals authentication with integrity
+ protection.
+ </adm:synopsis>
+ </adm:value>
+ <adm:value name="confidentiality">
+ <adm:synopsis>
+ Quality of protection equals authentication with integrity and
+ confidentiality protection.
+ </adm:synopsis>
+ </adm:value>
+ </adm:enumeration>
+ </adm:syntax>
+ <adm:profile name="ldap">
+ <ldap:attribute>
+ <ldap:name>ds-cfg-quality-of-protection</ldap:name>
+ </ldap:attribute>
+ </adm:profile>
+ </adm:property>
+ <adm:property name="principal-name">
+ <adm:synopsis>
+ Specifies the principal name.
+ </adm:synopsis>
+ <adm:description>
+ It can either be a simple user name or a
+ service name such as host/example.com.
+ If this property is not provided, then the server attempts to build the
+ principal name by appending the fully qualified domain name to the string
+ "ldap/".
+ </adm:description>
+ <adm:default-behavior>
+ <adm:alias>
+ <adm:synopsis>
+ The server attempts to determine the principal name from the
+ underlying system configuration.
+ </adm:synopsis>
+ </adm:alias>
+ </adm:default-behavior>
+ <adm:syntax>
+ <adm:string />
+ </adm:syntax>
+ <adm:profile name="ldap">
+ <ldap:attribute>
+ <ldap:name>ds-cfg-principal-name</ldap:name>
+ </ldap:attribute>
+ </adm:profile>
+ </adm:property>
<adm:property name="keytab">
<adm:synopsis>
Specifies the path to the keytab file that should be used for
@@ -155,8 +220,8 @@
<adm:property name="identity-mapper" mandatory="true">
<adm:synopsis>
Specifies the name of the identity mapper that is to be used
- with this SASL mechanism handler
- to match the Kerberos principal
+ with this SASL mechanism handler
+ to match the Kerberos principal
included in the SASL bind request to the corresponding
user in the directory.
</adm:synopsis>
diff --git a/opends/src/messages/messages/extension.properties b/opends/src/messages/messages/extension.properties
index ade0c5e..9811b59 100644
--- a/opends/src/messages/messages/extension.properties
+++ b/opends/src/messages/messages/extension.properties
@@ -400,7 +400,7 @@
security provider is required for clients that wish to use SASL EXTERNAL \
authentication
MILD_ERR_SASLEXTERNAL_NO_CLIENT_CERT_126=The SASL EXTERNAL bind request could \
- not be processed because the client did not present a certificate chain \
+ not be processed because the client did not present an certificate chain \
during SSL/TLS negotiation
MILD_ERR_SASLEXTERNAL_NO_MAPPING_127=The SASL EXTERNAL bind request failed \
because the certificate chain presented by the client during SSL/TLS \
@@ -614,271 +614,76 @@
INFO_SASLCRAMMD5_UPDATED_USER_BASE_DN_191=Attribute ds-cfg-user-base-dn in \
configuration entry %s has been updated. The DN %s will now be used as the \
search base when looking up user entries based on their username
-SEVERE_ERR_SASLDIGESTMD5_CANNOT_GET_MESSAGE_DIGEST_192=An unexpected error \
- occurred while attempting to obtain an MD5 digest engine for use by the \
- DIGEST-MD5 SASL handler: %s
-INFO_SASLDIGESTMD5_DESCRIPTION_USERNAME_ATTRIBUTE_193=Name of \
- the attribute that will be used to identify user entries based on the \
- username provided during SASL DIGEST-MD5 authentication. This must specify \
- the name of a valid attribute type defined in the server schema. Changes to \
- this configuration attribute will take effect immediately
-SEVERE_ERR_SASLDIGESTMD5_CANNOT_GET_USERNAME_ATTR_194=An unexpected error \
- occurred while attempting to determine the value of the \
- ds-cfg-user-name-attribute attribute in configuration entry %s: %s
-SEVERE_ERR_SASLDIGESTMD5_UNKNOWN_USERNAME_ATTR_195=The attribute %s \
- referenced in configuration attribute ds-cfg-user-name-attribute in \
- configuration entry %s does not exist in the Directory Server schema. The \
- attribute that is to be used for username lookups during SASL DIGEST-MD5 \
- authentication must be defined in the server schema
-INFO_SASLDIGESTMD5_DESCRIPTION_USER_BASE_DN_196=Base DN that \
- should be used when searching for entries based on the username provided \
- during SASL DIGEST-MD5 authentication. Changes to this configuration \
- attribute will take effect immediately
-SEVERE_ERR_SASLDIGESTMD5_CANNOT_GET_USER_BASE_DN_197=An unexpected error \
- occurred while attempting to determine the value of the ds-cfg-user-base-dn \
- attribute in configuration entry %s: %s
-INFO_SASLDIGESTMD5_DESCRIPTION_REALM_198=Realm that should be \
- used by the server for DIGEST-MD5 authentication. If this is not provided, \
- then the server will default to using a set of realm names that correspond to \
- the defined suffixes. Changes to this configuration attribute will take \
- effect immediately
-SEVERE_ERR_SASLDIGESTMD5_CANNOT_GET_REALM_199=An unexpected error occurred \
- while attempting to determine the value of the ds-cfg-realm attribute in \
- configuration entry %s: %s
-SEVERE_WARN_SASLDIGESTMD5_CHALLENGE_TOO_LONG_200=The initial DIGEST-MD5 must \
- be less than 2048 bytes, but the generated challenge was %d bytes
-MILD_ERR_SASLDIGESTMD5_NO_CREDENTIALS_201=The client connection included \
- DIGEST-MD5 state information, indicating that the client was in the process \
- of performing a DIGEST-MD5 bind, but the bind request did not include any \
+ INFO_SASL_UNSUPPORTED_CALLBACK_192=An unsupported or unexpected callback was \
+ provided to the SASL server for use during %s authentication: %s
+MILD_ERR_SASL_NO_CREDENTIALS_193=The client connection included \
+ %s state information, indicating that the client was in the process \
+ of performing a %s bind, but the bind request did not include any \
credentials
-MILD_ERR_SASLDIGESTMD5_INVALID_STORED_STATE_202=The SASL DIGEST-MD5 bind \
- request contained SASL credentials, but the stored SASL state information for \
- this client connection is not in an appropriate form for the challenge
-SEVERE_WARN_SASLDIGESTMD5_CANNOT_PARSE_ISO_CREDENTIALS_203=An error occurred \
- while attempting to parse the DIGEST-MD5 credentials as a string using the %s \
- character set: %s. The server will re-try using UTF-8
-SEVERE_WARN_SASLDIGESTMD5_CANNOT_PARSE_UTF8_CREDENTIALS_204=An error occurred \
- while attempting to parse the DIGEST-MD5 credentials as a string using the \
- UTF-8 character set: %s
-MILD_ERR_SASLDIGESTMD5_INVALID_TOKEN_IN_CREDENTIALS_205=The DIGEST-MD5 \
- credentials provided by the client contained an invalid token of "%s" \
- starting at position %d
-MILD_ERR_SASLDIGESTMD5_INVALID_CHARSET_206=The DIGEST-MD5 credentials \
- provided by the client specified an invalid character set of %s. Only a \
- value of 'utf-8' is acceptable for this parameter
-MILD_ERR_SASLDIGESTMD5_CANNOT_DECODE_REALM_AS_DN_207=An error occurred while \
- attempting to parse the provided response realm "%s" as a DN: %s
-MILD_ERR_SASLDIGESTMD5_INVALID_REALM_208=The DIGEST-MD5 credentials provided \
- by the client included an invalid realm of "%s"
-SEVERE_ERR_SASLDIGESTMD5_INVALID_NONCE_209=The DIGEST-MD5 credentials \
- provided by the client included a nonce that was different from the nonce \
- supplied by the server. This could indicate a replay attack or a chosen \
- plaintext attack, and as a result the client connection will be terminated
-MILD_ERR_SASLDIGESTMD5_CANNOT_DECODE_NONCE_COUNT_210=The DIGEST-MD5 \
- credentials provided by the client included a nonce count "%s" that could not \
- be decoded as a hex-encoded integer
-SEVERE_ERR_SASLDIGESTMD5_CANNOT_DECODE_STORED_NONCE_COUNT_211=An unexpected \
- error occurred while attempting to decode the nonce count stored by the \
- server for this client connection: %s
-SEVERE_ERR_SASLDIGESTMD5_INVALID_NONCE_COUNT_212=The DIGEST-MD5 credentials \
- provided by the client included a nonce count that was different from the \
- count expected by the server. This could indicate a replay attack, and as a \
- result the client connection will be terminated
-MILD_ERR_SASLDIGESTMD5_INTEGRITY_NOT_SUPPORTED_213=The client requested the \
- auth-int quality of protection but integrity protection is not currently \
- supported by the Directory Server
-MILD_ERR_SASLDIGESTMD5_CONFIDENTIALITY_NOT_SUPPORTED_214=The client requested \
- the auth-conf quality of protection but confidentiality protection is not \
- currently supported by the Directory Server
-MILD_ERR_SASLDIGESTMD5_INVALID_QOP_215=The DIGEST-MD5 credentials provided by \
- the client requested an invalid quality of protection mechanism of %s
-MILD_ERR_SASLDIGESTMD5_CANNOT_PARSE_RESPONSE_DIGEST_216=The DIGEST-MD5 \
- credentials provided by the client included a digest that could not be \
- decoded as a hex-encoded byte sequence: %s
-MILD_ERR_SASLDIGESTMD5_INVALID_RESPONSE_TOKEN_217=The DIGEST-MD5 credentials \
- provided by the client included an invalid token named "%s"
-MILD_ERR_SASLDIGESTMD5_NO_USERNAME_IN_RESPONSE_218=The DIGEST-MD5 credentials \
- provided by the client did not contain the required "username" token
-MILD_ERR_SASLDIGESTMD5_NO_NONCE_IN_RESPONSE_219=The DIGEST-MD5 credentials \
- provided by the client did not contain the required "nonce" token
-MILD_ERR_SASLDIGESTMD5_NO_CNONCE_IN_RESPONSE_220=The DIGEST-MD5 credentials \
- provided by the client did not contain the required "cnonce" token
-MILD_ERR_SASLDIGESTMD5_NO_NONCE_COUNT_IN_RESPONSE_221=The DIGEST-MD5 \
- credentials provided by the client did not contain the required "nc" token
-MILD_ERR_SASLDIGESTMD5_NO_DIGEST_IN_RESPONSE_223=The DIGEST-MD5 credentials \
- provided by the client did not contain the required "response" token
-MILD_ERR_SASLDIGESTMD5_CANNOT_DECODE_USERNAME_AS_DN_224=An error occurred \
- while attempting to decode the SASL DIGEST-MD5 username "%s" because it \
- appeared to contain a DN but DN decoding failed: %s
-MILD_ERR_SASLDIGESTMD5_USERNAME_IS_NULL_DN_225=The username in the SASL \
- DIGEST-MD5 bind request appears to be an empty DN. This is not allowed
-INFO_SASLDIGESTMD5_CANNOT_LOCK_ENTRY_226=The Directory Server was unable to \
- obtain a read lock on user entry %s in order to retrieve that entry
-MILD_ERR_SASLDIGESTMD5_CANNOT_GET_ENTRY_BY_DN_227=An error occurred while \
- attempting to retrieve user entry %s as specified in the DN-based username of \
- a SASL DIGEST-MD5 bind request: %s
-MILD_ERR_SASLDIGESTMD5_ZERO_LENGTH_USERNAME_228=The username contained in the \
- SASL DIGEST-MD5 bind request had a length of zero characters, which is not \
- allowed. DIGEST-MD5 authentication does not allow an empty string for use as \
- the username
-MILD_ERR_SASLDIGESTMD5_CANNOT_PERFORM_INTERNAL_SEARCH_229=An error occurred \
- while trying to perform an internal search to retrieve the user entry \
- associated with the SASL DIGEST-MD5 username %s. The result of that search \
- was %s with a message of %s
-MILD_ERR_SASLDIGESTMD5_MULTIPLE_MATCHING_ENTRIES_230=The internal search \
- attempting to resolve SASL DIGEST-MD5 username %s matched multiple entries. \
- Authentication cannot succeed unless the username is mapped to exactly one \
- user entry
-MILD_ERR_SASLDIGESTMD5_NO_MATCHING_ENTRIES_231=The server was not able to \
- find any user entries for the provided username of %s
-MILD_ERR_SASLDIGESTMD5_NO_PW_ATTR_232=The SASL DIGEST-MD5 authentication \
- failed because the mapped user entry did not contain any values for the %s \
- attribute
-MILD_ERR_SASLDIGESTMD5_UNKNOWN_STORAGE_SCHEME_233=A password in the target \
- user entry %s could not be processed via SASL DIGEST-MD5 because that \
- password has an unknown storage scheme of %s
-MILD_ERR_SASLDIGESTMD5_CANNOT_GET_CLEAR_PASSWORD_234=An error occurred while \
- attempting to obtain the clear-text password for user %s from the value with \
- storage scheme %s: %s
-MILD_ERR_SASLDIGESTMD5_INVALID_CREDENTIALS_235=The DIGEST-MD5 credentials \
- provided by the client are not appropriate for any password in the associated \
- user account
-MILD_ERR_SASLDIGESTMD5_NO_REVERSIBLE_PASSWORDS_236=SASL DIGEST-MD5 \
- authentication is not possible for user %s because none of the passwords in \
- the user entry are stored in a reversible form
-SEVERE_WARN_SASLDIGESTMD5_CANNOT_GENERATE_RESPONSE_DIGEST_237=An error \
- occurred while attempting to generate a server-side digest to compare with \
- the client response: %s
-SEVERE_ERR_SASLDIGESTMD5_CANNOT_GENERATE_RESPONSE_AUTH_DIGEST_238=An error \
- occurred while trying to generate the response auth digest to include in the \
- server SASL credentials: %s
-MILD_ERR_SASLDIGESTMD5_INVALID_CLOSING_QUOTE_POS_239=The DIGEST-MD5 response \
- challenge could not be parsed because it had an invalid quotation mark at \
- position %d
-INFO_SASLDIGESTMD5_UPDATED_USERNAME_ATTR_240=Attribute \
- ds-cfg-user-name-attribute in configuration entry %s has been updated. The \
- %s attribute will now be used when looking up user entries based on their \
- username
-INFO_SASLDIGESTMD5_UPDATED_USER_BASE_DN_241=Attribute ds-cfg-user-base-dn in \
- configuration entry %s has been updated. The DN %s will now be used as the \
- search base when looking up user entries based on their username
-INFO_SASLDIGESTMD5_UPDATED_NEW_REALM_242=Attribute ds-cfg-realm in \
- configuration entry %s has been updated. The realm "%s" will now be \
- advertised by the server in the challenge response
-INFO_SASLDIGESTMD5_UPDATED_NO_REALM_243=Attribute ds-cfg-realm in \
- configuration entry %s has been updated. The realm(s) advertised by the \
- server in the challenge response will be the DNs of the server suffixes
-INFO_SASLGSSAPI_DESCRIPTION_USERNAME_ATTRIBUTE_244=Name of the \
- attribute that will be used to identify user entries based on the username \
- provided during SASL GSSAPI authentication. This must specify the name of a \
- valid attribute type defined in the server schema. Changes to this \
- configuration attribute will take effect immediately
-SEVERE_ERR_SASLGSSAPI_CANNOT_GET_USERNAME_ATTR_245=An unexpected error \
- occurred while attempting to determine the value of the \
- ds-cfg-user-name-attribute attribute in configuration entry %s: %s
-SEVERE_ERR_SASLGSSAPI_UNKNOWN_USERNAME_ATTR_246=The attribute %s referenced \
- in configuration attribute ds-cfg-user-name-attribute in configuration entry \
- %s does not exist in the Directory Server schema. The attribute that is to \
- be used for username lookups during SASL GSSAPI authentication must be \
- defined in the server schema
-INFO_SASLGSSAPI_DESCRIPTION_USER_BASE_DN_247=Base DN that \
- should be used when searching for entries based on the username provided \
- during SASL GSSAPI authentication. Changes to this configuration attribute \
- will take effect immediately
-SEVERE_ERR_SASLGSSAPI_CANNOT_GET_USER_BASE_DN_248=An unexpected error \
- occurred while attempting to determine the value of the ds-cfg-user-base-dn \
- attribute in configuration entry %s: %s
-INFO_SASLGSSAPI_DESCRIPTION_SERVER_FQDN_249=Fully-qualified \
- domain name that should be used for the server during SASL GSSAPI \
- authentication. Changes to this configuration attribute will take effect \
- immediately
-SEVERE_ERR_SASLGSSAPI_CANNOT_GET_SERVER_FQDN_250=An unexpected error occurred \
+SEVERE_ERR_SASL_CANNOT_GET_SERVER_FQDN_194=An unexpected error occurred \
while attempting to determine the value of the ds-cfg-server-fqdn attribute \
in configuration entry %s: %s
-INFO_SASLGSSAPI_UPDATED_USERNAME_ATTR_251=Attribute \
- ds-cfg-user-name-attribute in configuration entry %s has been updated. The \
- %s attribute will now be used when looking up user entries based on their \
- username
-INFO_SASLGSSAPI_UPDATED_USER_BASE_DN_252=Attribute ds-cfg-user-base-dn in \
- configuration entry %s has been updated. The DN %s will now be used as the \
- search base when looking up user entries based on their username
-INFO_SASLGSSAPI_UPDATED_NEW_SERVER_FQDN_253=Attribute ds-cfg-server-fqdn in \
- configuration entry %s has been updated. The value "%s" will now be used as \
- the fully-qualified name of the Directory Server for GSSAPI authentication
-INFO_SASLGSSAPI_UPDATED_NO_SERVER_FQDN_254=Attribute ds-cfg-server-fqdn in \
- configuration entry %s has been updated. The Directory Server will attempt \
- to determine its own FQDN for use in GSSAPI authentication
-INFO_SASLGSSAPI_UNEXPECTED_CALLBACK_255=An unexpected callback was provided \
- for the SASL server for use during GSSAPI authentication: %s
-INFO_SASLGSSAPI_DESCRIPTION_KDC_ADDRESS_256=Address of the KDC \
- that should be used during SASL GSSAPI authentication. If this is not \
- specified, then an attempt will be made to obtain it from the system-wide \
- Kerberos configuration. Changes to this configuration attribute will take \
- effect immediately for subsequent GSSAPI bind attempts
-MILD_ERR_SASLGSSAPI_CANNOT_GET_KDC_ADDRESS_257=An unexpected error occurred \
- while attempting to determine the value of the ds-cfg-kdc-address attribute \
- in configuration entry %s: %s
-INFO_SASLGSSAPI_DESCRIPTION_REALM_258=Default realm that should \
- be used during SASL GSSAPI authentication. If this is not specified, then an \
- attempt will be made to obtain it from the system-wide Kerberos \
- configuration. Changes to this configuration attribute will take effect \
- immediately for subsequent GSSAPI bind attempts
-MILD_ERR_SASLGSSAPI_CANNOT_GET_REALM_259=An unexpected error occurred while \
- attempting to determine the value of the ds-cfg-realm attribute in \
- configuration entry %s: %s
-MILD_ERR_SASLGSSAPI_NO_CLIENT_CONNECTION_260=No client connection was \
- available for use in processing the GSSAPI bind request
-MILD_ERR_SASLGSSAPI_CANNOT_CREATE_SASL_SERVER_261=An error occurred while \
- attempting to create the SASL server instance to process the GSSAPI bind \
- request: %s
-MILD_ERR_SASLGSSAPI_CANNOT_EVALUATE_RESPONSE_262=An error occurred while \
- attempting to evaluate the challenge response provided by the client in the \
- GSSAPI bind request: %s
-MILD_ERR_SASLGSSAPI_NO_AUTHZ_ID_263=The GSSAPI authentication process appears \
- to have completed but no authorization ID is available for mapping to a \
- directory user
-MILD_ERR_SASLGSSAPI_CANNOT_PERFORM_INTERNAL_SEARCH_264=An error occurred \
- while attempting to perform an internal search to map the GSSAPI \
- authorization ID %s to a Directory Server user (result code %d, error message \
- "%s")
-MILD_ERR_SASLGSSAPI_MULTIPLE_MATCHING_ENTRIES_265=The GSSAPI authorization ID \
- %s appears to have multiple matches in the Directory Server
-MILD_ERR_SASLGSSAPI_CANNOT_MAP_AUTHZID_266=The GSSAPI authorization ID %s \
- could not be mapped to any user in the Directory Server
-INFO_SASLGSSAPI_UPDATED_KDC_267=Attribute ds-cfg-kdc-address in configuration \
- entry %s has been updated. The value "%s" will now be used as the address of \
- the KDC for GSSAPI authentication
-INFO_SASLGSSAPI_UNSET_KDC_268=Attribute ds-cfg-kdc-address in configuration \
- entry %s has been un-set as a system property. Any further GSSAPI \
- authentication attempts will rely on the Kerberos configuration in the \
- underlying operating system to determine the KDC address
-INFO_SASLGSSAPI_UPDATED_REALM_269=Attribute ds-cfg-realm in configuration \
- entry %s has been updated. The value "%s" will now be used as the default \
- realm for GSSAPI authentication
-INFO_SASLGSSAPI_UNSET_REALM_270=Attribute ds-cfg-realm in configuration entry \
- %s has been un-set as a system property. Any further GSSAPI authentication \
- attempts will rely on the Kerberos configuration in the underlying operating \
- system to determine the default realm
-MILD_ERR_SASLGSSAPI_CANNOT_CREATE_LOGIN_CONTEXT_271=An error occurred while \
- attempting to create the JAAS login context for GSSAPI authentication: %s
-MILD_ERR_SASLGSSAPI_CANNOT_AUTHENTICATE_SERVER_272=An error occurred while \
- attempting to perform server-side Kerberos authentication to support a GSSAPI \
- bind operation: %s
-INFO_SASLGSSAPI_DESCRIPTION_KEYTAB_FILE_273=Path to the keytab \
- file containing the secret key for the Kerberos principal to use when \
- processing GSSAPI authentication. If this is not specified, then the \
- system-wide default keytab file will be used. Changes to this configuration \
- attribute will not take effect until the GSSAPI SASL mechanism handler is \
- disabled and re-enabled or the Directory Server is restarted
-MILD_ERR_SASLGSSAPI_CANNOT_GET_KEYTAB_FILE_274=An unexpected error occurred \
- while attempting to determine the value of the ds-cfg-keytab attribute in \
- configuration entry %s: %s
-SEVERE_ERR_SASLGSSAPI_CANNOT_CREATE_JAAS_CONFIG_275=An error occurred while \
+ SEVERE_ERR_SASL_CONTEXT_CREATE_ERROR_195=An unexpected error occurred while \
+ trying to create an %s context: %s
+MILD_ERR_SASL_CANNOT_DECODE_USERNAME_AS_DN_196=An error occurred \
+ while attempting to decode the SASL %s username "%s" because it \
+ appeared to contain a DN but DN decoding failed: %s
+MILD_ERR_SASL_USERNAME_IS_NULL_DN_197=The username in the SASL \
+ %s bind request appears to be an empty DN. This is not allowed
+INFO_SASL_CANNOT_LOCK_ENTRY_198=The Directory Server was unable to \
+ obtain a read lock on user entry %s in order to retrieve that entry
+MILD_ERR_SASL_CANNOT_GET_ENTRY_BY_DN_199=An error occurred while \
+ attempting to retrieve user entry %s as specified in the DN-based username of \
+ a SASL %s bind request: %s
+MILD_ERR_SASL_ZERO_LENGTH_USERNAME_200=The username contained in the \
+ SASL %s bind request had a length of zero characters, which is not \
+ allowed. %s authentication does not allow an empty string for use as \
+ the username
+MILD_ERR_SASL_NO_MATCHING_ENTRIES_201=The server was not able to \
+ find any user entries for the provided username of %s
+MILD_ERR_SASL_AUTHZID_INVALID_DN_202=The provided authorization ID \
+ %s contained an invalid DN: %s
+ MILD_ERR_SASL_AUTHZID_NO_SUCH_ENTRY_203=The entry %s specified as \
+ the authorization identity does not exist
+MILD_ERR_SASL_AUTHZID_CANNOT_GET_ENTRY_204=The entry %s specified as \
+ the authorization identity could not be retrieved: %s
+MILD_ERR_SASL_AUTHZID_NO_MAPPED_ENTRY_205=The server was unable to \
+ find any entry corresponding to authorization ID %s
+MILD_ERR_SASL_CANNOT_MAP_AUTHZID_206=An error occurred while \
+ attempting to map authorization ID %s to a user entry: %s
+MILD_ERR_SASL_CANNOT_GET_REVERSIBLE_PASSWORDS_207=An error occurred \
+ while attempting to retrieve the clear-text password(s) for user %s in order \
+ to perform SASL %s authentication: %s
+MILD_ERR_SASL_NO_REVERSIBLE_PASSWORDS_208=SASL %s \
+ authentication is not possible for user %s because none of the passwords in \
+ the user entry are stored in a reversible form
+SEVERE_ERR_SASL_PROTOCOL_ERROR_209=SASL %s protocol error: %s
+MILD_ERR_SASL_AUTHZID_INSUFFICIENT_PRIVILEGES_210=The authenticating \
+ user %s does not have sufficient privileges to assume a different \
+ authorization identity
+MILD_ERR_SASL_AUTHZID_INSUFFICIENT_ACCESS_211=The authenticating \
+ user %s does not have sufficient access to assume a different \
+ authorization identity
+MILD_ERR_SASL_AUTHENTRY_NO_MAPPED_ENTRY_212=The server was unable to \
+ find any entry corresponding to authentication ID %s
+SEVERE_ERR_SASLGSSAPI_KDC_REALM_NOT_DEFINED_213=The server was unable to \
+ because both the ds-cfg-kdc-address and ds-cfg-realm attributes must be \
+ defined or neither defined
+MILD_ERR_SASL_CANNOT_MAP_AUTHENTRY_214=An error occurred while \
+ attempting to map authorization ID %s to a user entry: %s
+SEVERE_ERR_SASLGSSAPI_CANNOT_CREATE_JAAS_CONFIG_215=An error occurred while \
attempting to write a temporary JAAS configuration file for use during GSSAPI \
processing: %s
-SEVERE_ERR_SASLGSSAPI_DIFFERENT_AUTHID_AND_AUTHZID_276=The authentication ID \
- %s was not equal to the authorization ID %s. This is not supported for \
- GSSAPI authentication
+ SEVERE_ERR_SASLGSSAPI_CANNOT_CREATE_LOGIN_CONTEXT_216=An error occurred while \
+ attempting to create the JAAS login context for GSSAPI authentication: %s
+ MILD_ERR_SASLGSSAPI_NO_CLIENT_CONNECTION_217=No client connection was \
+ available for use in processing the GSSAPI bind request
+ INFO_GSSAPI_PRINCIPAL_NAME_218=GSSAPI mechanism using a principal name of: %s
+ INFO_GSSAPI_SERVER_FQDN_219=GSSAPI SASL mechanism using a server fully \
+ qualified domain name of: %s
+ INFO_DIGEST_MD5_REALM_220=DIGEST-MD5 SASL mechanism using a realm of: %s
+ INFO_DIGEST_MD5_SERVER_FQDN_221=DIGEST-MD5 SASL mechanism using a server \
+ fully qualified domain name of: %s
SEVERE_ERR_EXTOP_WHOAMI_PROXYAUTH_INSUFFICIENT_PRIVILEGES_277=You do not have \
sufficient privileges to use the proxied authorization control
INFO_EXACTMAP_DESCRIPTION_MATCH_ATTR_298=Name or OID of the \
@@ -1123,9 +928,11 @@
from configuration entry %s: %s
NOTICE_ERRORLOG_ACCTNOTHANDLER_NOTIFICATION_375=Account-Status-Notification \
type='%s' userdn='%s' id=%d msg='%s'
-MILD_ERR_SASLDIGESTMD5_CANNOT_GET_REVERSIBLE_PASSWORDS_376=An error occurred \
- while attempting to retrieve the clear-text password(s) for user %s in order \
- to perform SASL DIGEST-MD5 authentication: %s
+
+
+
+
+
MILD_ERR_SASLCRAMMD5_CANNOT_GET_REVERSIBLE_PASSWORDS_377=An error occurred \
while attempting to retrieve the clear-text password(s) for user %s in order \
to perform SASL CRAM-MD5 authentication: %s
@@ -1169,13 +976,13 @@
perform an internal modification to update the group: %s
MILD_ERR_EXTOP_PASSMOD_INSUFFICIENT_PRIVILEGES_392=You do not have sufficient \
privileges to perform password reset operations
+
+
+
MILD_ERR_SASLDIGESTMD5_EMPTY_AUTHZID_393=The provided authorization ID was \
empty, which is not allowed for DIGEST-MD5 authentication
MILD_ERR_SASLDIGESTMD5_AUTHZID_INVALID_DN_394=The provided authorization ID \
%s contained an invalid DN: %s
-MILD_ERR_SASLDIGESTMD5_AUTHZID_INSUFFICIENT_PRIVILEGES_395=The authenticating \
- user %s does not have sufficient privileges to assume a different \
- authorization identity
MILD_ERR_SASLDIGESTMD5_AUTHZID_NO_SUCH_ENTRY_396=The entry %s specified as \
the authorization identity does not exist
MILD_ERR_SASLDIGESTMD5_AUTHZID_CANNOT_GET_ENTRY_397=The entry %s specified as \
@@ -1184,6 +991,14 @@
find any entry corresponding to authorization ID %s
MILD_ERR_SASLDIGESTMD5_CANNOT_MAP_AUTHZID_399=An error occurred while \
attempting to map authorization ID %s to a user entry: %s
+
+
+
+
+
+
+
+
MILD_ERR_SASLPLAIN_AUTHZID_INVALID_DN_400=The provided authorization ID %s \
contained an invalid DN: %s
MILD_ERR_SASLPLAIN_AUTHZID_INSUFFICIENT_PRIVILEGES_401=The authenticating \
@@ -1625,6 +1440,6 @@
SEVERE_ERR_SDTUACM_ATTR_UNINDEXED_569=The subject DN to user attribute \
certificate mapper defined in configuration entry %s references attribute \
type %s which is does not have an equality index defined in backend %s
-INFO_LOG_EXTENSION_INFORMATION_570=Loaded extension from file '%s' (build %s, \
+SEVERE_ERR_SASLDIGESTMD5_PROTOCOL_ERROR_570=SASL DIGEST MD5 protocol error: %s
+INFO_LOG_EXTENSION_INFORMATION_571=Loaded extension from file '%s' (build %s, \
revision %s)
-
diff --git a/opends/src/server/org/opends/server/api/AccessControlHandler.java b/opends/src/server/org/opends/server/api/AccessControlHandler.java
index b1a1d63..d0cb74e 100644
--- a/opends/src/server/org/opends/server/api/AccessControlHandler.java
+++ b/opends/src/server/org/opends/server/api/AccessControlHandler.java
@@ -331,5 +331,22 @@
SearchOperation searchOperation,
SearchResultReference searchReference);
+ /**
+ * Indicates if the specified proxy user entry can proxy, or act on
+ * the behalf of the specified proxied user entry. The operation
+ * parameter is used in the evaluation.
+ *
+ * @param proxyUser The entry to use as the proxy user.
+ * @param proxiedUser The entry to be proxied by the proxy user.
+ * @param operation The operation to use in the evaluation.
+ *
+ * @return {@code true} if the access control configuration allows
+ * the proxy user to proxy the proxied user, or
+ * {@code false} if not.
+ */
+ public abstract boolean mayProxy(Entry proxyUser,
+ Entry proxiedUser,
+ Operation operation);
+
}
diff --git a/opends/src/server/org/opends/server/api/ClientConnection.java b/opends/src/server/org/opends/server/api/ClientConnection.java
index a9f8b5a..c56d565 100644
--- a/opends/src/server/org/opends/server/api/ClientConnection.java
+++ b/opends/src/server/org/opends/server/api/ClientConnection.java
@@ -306,7 +306,7 @@
* @return The connection handler that accepted this client
* connection.
*/
- public abstract ConnectionHandler getConnectionHandler();
+ public abstract ConnectionHandler<?> getConnectionHandler();
@@ -1113,6 +1113,26 @@
}
+ /**
+ * Indicate whether the specified authorization entry parameter
+ * has the specified privilege. The method can be used to perform
+ * a "what-if" scenario.
+ *
+ * @param authorizationEntry The authentication entry to use.
+ * @param privilege The privilege to check for.
+ *
+ * @return {@code true} if the authentication entry has the
+ * specified privilege, or {@code false} if not.
+ */
+ public static boolean hasPrivilege(Entry authorizationEntry,
+ Privilege privilege) {
+ boolean isRoot =
+ DirectoryServer.isRootDN(authorizationEntry.getDN());
+ return getPrivileges(authorizationEntry,
+ isRoot).contains(privilege) ||
+ DirectoryServer.isDisabled(privilege);
+ }
+
/**
* Indicates whether the authenticated client has the specified
@@ -1298,7 +1318,7 @@
*
* @return A set of the privileges that should be assigned.
*/
- private HashSet<Privilege> getPrivileges(Entry entry,
+ private static HashSet<Privilege> getPrivileges(Entry entry,
boolean isRoot)
{
if (entry == null)
@@ -1579,7 +1599,7 @@
* @throws DirectoryException If a problem occurs while attempting
* to make the determination.
*/
- public boolean isMemberOf(Group group, Operation operation)
+ public boolean isMemberOf(Group<?> group, Operation operation)
throws DirectoryException
{
if (operation == null)
@@ -1668,7 +1688,7 @@
* Retrieves the DN of the key manager provider that should be used
* for operations requiring access to a key manager. The default
* implementation returns {@code null} to indicate that no key
- * manager provider is avaialble, but subclasses should override
+ * manager provider is available, but subclasses should override
* this method to return a valid DN if they perform operations which
* may need access to a key manager.
*
diff --git a/opends/src/server/org/opends/server/api/ConnectionSecurityProvider.java b/opends/src/server/org/opends/server/api/ConnectionSecurityProvider.java
index 9ad8c1b..a2a510d 100644
--- a/opends/src/server/org/opends/server/api/ConnectionSecurityProvider.java
+++ b/opends/src/server/org/opends/server/api/ConnectionSecurityProvider.java
@@ -98,6 +98,16 @@
*/
public abstract boolean isSecure();
+ /**
+ * Indicates whether the security provider is active or not. Some
+ * security providers (DIGEST-MD5, GSSAPI) perform
+ * confidentiality/integrity processing of messages and require
+ * several handshakes to setup.
+ *
+ * @return {@code true} if the security provider is active, or,
+ * {@code false} if not.
+ */
+ public abstract boolean isActive();
/**
diff --git a/opends/src/server/org/opends/server/authorization/dseecompat/Aci.java b/opends/src/server/org/opends/server/authorization/dseecompat/Aci.java
index 3fafa70..96df8da 100644
--- a/opends/src/server/org/opends/server/authorization/dseecompat/Aci.java
+++ b/opends/src/server/org/opends/server/authorization/dseecompat/Aci.java
@@ -538,7 +538,7 @@
* @param expr A string representing the OID expression.
* @param msg A message to be used if there is an exception.
*
- * @return Return a hash set of verfied OID strings parsed from the OID
+ * @return Return a hash set of verified OID strings parsed from the OID
* expression.
*
* @throws AciException If the specified expression string is invalid.
diff --git a/opends/src/server/org/opends/server/authorization/dseecompat/AciContainer.java b/opends/src/server/org/opends/server/authorization/dseecompat/AciContainer.java
index 7e301bc..b661486 100644
--- a/opends/src/server/org/opends/server/authorization/dseecompat/AciContainer.java
+++ b/opends/src/server/org/opends/server/authorization/dseecompat/AciContainer.java
@@ -93,9 +93,9 @@
private boolean isAddOp=false;
/*
- * The rights to use in the evaluation of the LDAP operation.
+ * The right mask to use in the evaluation of the LDAP operation.
*/
- private int rights;
+ private int rightsMask;
/*
* The entry being evaluated (resource entry).
@@ -246,8 +246,15 @@
*/
private String extOpOID;
+ /*
+ * AuthenticationInfo class to use.
+ */
+ private AuthenticationInfo authInfo;
+
/**
- * This constructor is used by all currently supported LDAP operations.
+ * This constructor is used by all currently supported LDAP operations
+ * except the generic access control check that can be used by
+ * plugins.
*
* @param operation The Operation object being evaluated and target
* matching.
@@ -262,6 +269,7 @@
this.clientConnection=operation.getClientConnection();
if(operation instanceof AddOperationBasis)
this.isAddOp=true;
+ this.authInfo = clientConnection.getAuthenticationInfo();
//If the proxied authorization control was processed, then the operation
//will contain an attachment containing the original authorization entry.
@@ -313,9 +321,29 @@
//if an access proxy check was performed.
this.saveAuthorizationEntry=this.authorizationEntry;
this.saveResourceEntry=this.resourceEntry;
- this.rights = rights;
+ this.rightsMask = rights;
}
+ /**
+ * This constructor is used by the generic access control check.
+ *
+ * @param operation The operation to use in the access evaluation.
+ * @param e The entry to check access for.
+ * @param authInfo The authentication information to use in the evaluation.
+ * @param rights The rights to check access of.
+ */
+ protected AciContainer(Operation operation, Entry e,
+ AuthenticationInfo authInfo,
+ int rights) {
+ this.resourceEntry=e;
+ this.operation=operation;
+ this.clientConnection=operation.getClientConnection();
+ this.authInfo = authInfo;
+ this.authorizationEntry = authInfo.getAuthorizationEntry();
+ this.saveAuthorizationEntry=this.authorizationEntry;
+ this.saveResourceEntry=this.resourceEntry;
+ this.rightsMask = rights;
+ }
/**
* Returns true if an entry has already been processed by an access proxy
* check.
@@ -655,7 +683,7 @@
* {@inheritDoc}
*/
public boolean isAnonymousUser() {
- return !clientConnection.getAuthenticationInfo().isAuthenticated();
+ return !authInfo.isAuthenticated();
}
/**
@@ -689,21 +717,21 @@
* {@inheritDoc}
*/
public boolean hasRights(int rights) {
- return (this.rights & rights) != 0;
+ return (this.rightsMask & rights) != 0;
}
/**
* {@inheritDoc}
*/
public int getRights() {
- return this.rights;
+ return this.rightsMask;
}
/**
* {@inheritDoc}
*/
public void setRights(int rights) {
- this.rights=rights;
+ this.rightsMask=rights;
}
/**
@@ -791,7 +819,6 @@
/*
* Some kind of authentication is required.
*/
- AuthenticationInfo authInfo=clientConnection.getAuthenticationInfo();
if(authInfo.isAuthenticated()) {
if(authMethod==EnumAuthMethod.AUTHMETHOD_SIMPLE) {
if(authInfo.hasAuthenticationType(AuthenticationType.SIMPLE)) {
@@ -833,7 +860,7 @@
/**
* {@inheritDoc}
*/
- public boolean isMemberOf(Group group) {
+ public boolean isMemberOf(Group<?> group) {
boolean ret;
try {
if(useAuthzid) {
@@ -884,7 +911,7 @@
* {@inheritDoc}
*/
public void setEvalUserAttributes(int v) {
- if(operation instanceof SearchOperation && (rights == ACI_READ)) {
+ if(operation instanceof SearchOperation && (rightsMask == ACI_READ)) {
if(v == ACI_FOUND_USER_ATTR_RULE) {
evalAllAttributes |= ACI_FOUND_USER_ATTR_RULE;
evalAllAttributes &= ~ACI_USER_ATTR_STAR_MATCHED;
@@ -897,7 +924,7 @@
* {@inheritDoc}
*/
public void setEvalOpAttributes(int v) {
- if(operation instanceof SearchOperation && (rights == ACI_READ)) {
+ if(operation instanceof SearchOperation && (rightsMask == ACI_READ)) {
if(v == ACI_FOUND_OP_ATTR_RULE) {
evalAllAttributes |= ACI_FOUND_OP_ATTR_RULE;
evalAllAttributes &= ~ACI_OP_ATTR_PLUS_MATCHED;
diff --git a/opends/src/server/org/opends/server/authorization/dseecompat/AciEvalContext.java b/opends/src/server/org/opends/server/authorization/dseecompat/AciEvalContext.java
index 9eee358..e9b437c 100644
--- a/opends/src/server/org/opends/server/authorization/dseecompat/AciEvalContext.java
+++ b/opends/src/server/org/opends/server/authorization/dseecompat/AciEvalContext.java
@@ -146,7 +146,7 @@
* @return True if the authorization DN of the operation is a
* member of the specified group.
*/
- public boolean isMemberOf(Group group);
+ public boolean isMemberOf(Group<?> group);
/**
* Returns true if the hashtable of ACIs that matched the targattrfilters
diff --git a/opends/src/server/org/opends/server/authorization/dseecompat/AciHandler.java b/opends/src/server/org/opends/server/authorization/dseecompat/AciHandler.java
index f2f197c..d505d25 100644
--- a/opends/src/server/org/opends/server/authorization/dseecompat/AciHandler.java
+++ b/opends/src/server/org/opends/server/authorization/dseecompat/AciHandler.java
@@ -28,8 +28,6 @@
package org.opends.server.authorization.dseecompat;
import org.opends.messages.Message;
-
-
import static org.opends.server.authorization.dseecompat.Aci.*;
import static org.opends.server.config.ConfigConstants.ATTR_AUTHZ_GLOBAL_ACI;
import static org.opends.server.loggers.ErrorLogger.logError;
@@ -39,12 +37,11 @@
import static org.opends.server.schema.SchemaConstants.SYNTAX_DN_OID;
import static org.opends.server.util.ServerConstants.*;
import static org.opends.server.util.StaticUtils.toLowerCase;
-
import java.util.*;
import java.util.concurrent.locks.Lock;
-
import org.opends.server.admin.std.server.DseeCompatAccessControlHandlerCfg;
import org.opends.server.api.AccessControlHandler;
+import org.opends.server.api.ClientConnection;
import org.opends.server.config.ConfigException;
import org.opends.server.core.*;
import org.opends.server.loggers.debug.DebugTracer;
@@ -535,6 +532,17 @@
}
/**
+ * Check to see if the specified entry has the specified privilege.
+ *
+ * @param e The entry to check privileges on.
+ * @return {@code true} if the entry has the
+ * specified privilege, or {@code false} if not.
+ */
+ private boolean skipAccessCheck(Entry e) {
+ return ClientConnection.hasPrivilege(e, Privilege.BYPASS_ACL);
+ }
+
+ /**
* Check access using the specified container. This container will have all
* of the information to gather applicable ACIs and perform evaluation on
* them.
@@ -1242,6 +1250,26 @@
* {@inheritDoc}
*/
@Override
+ public boolean
+ mayProxy(Entry proxyUser, Entry proxiedUser, Operation op) {
+ boolean ret;
+ if(!(ret=skipAccessCheck(proxyUser))) {
+ AuthenticationInfo authInfo =
+ new AuthenticationInfo(proxyUser,
+ DirectoryServer.isRootDN(proxyUser.getDN()));
+ AciLDAPOperationContainer operationContainer =
+ new AciLDAPOperationContainer(op, proxiedUser,
+ authInfo, ACI_PROXY);
+ ret=accessAllowedEntry(operationContainer);
+ }
+ return ret;
+ }
+
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
public boolean isAllowed(LocalBackendBindOperation bindOperation) {
//Not planned to be implemented.
return true;
diff --git a/opends/src/server/org/opends/server/authorization/dseecompat/AciLDAPOperationContainer.java b/opends/src/server/org/opends/server/authorization/dseecompat/AciLDAPOperationContainer.java
index 149ded5..8354477 100644
--- a/opends/src/server/org/opends/server/authorization/dseecompat/AciLDAPOperationContainer.java
+++ b/opends/src/server/org/opends/server/authorization/dseecompat/AciLDAPOperationContainer.java
@@ -28,7 +28,6 @@
package org.opends.server.authorization.dseecompat;
import java.util.List;
-
import org.opends.server.core.*;
import org.opends.server.types.*;
import org.opends.server.workflowelement.localbackend.*;
@@ -62,6 +61,23 @@
super(operation, rights, operation.getEntryToCompare());
}
+
+ /**
+ * Constructor interface for evaluation general purpose Operation, entry and
+ * rights..
+ *
+ * @param operation The operation to use in the evaluation.
+ * @param e The entry for evaluation.
+ * @param authInfo The authentication information to use in the evaluation.
+ * @param rights The rights of the operation.
+ */
+ public AciLDAPOperationContainer(Operation operation, Entry e,
+ AuthenticationInfo authInfo,
+ int rights) {
+ super(operation, e, authInfo, rights);
+ }
+
+
/**
* Constructor interface for evaluation of a control.
*
@@ -126,7 +142,7 @@
* Constructor interface for the modify DN operation.
* @param operation The modify DN operation.
* @param rights The rights of the modify DN operation.
- * @param entry The entry to evalauted for this modify DN.
+ * @param entry The entry to evaluated for this modify DN.
*/
public AciLDAPOperationContainer(LocalBackendModifyDNOperation operation,
int rights,
diff --git a/opends/src/server/org/opends/server/core/AccessControlConfigManager.java b/opends/src/server/org/opends/server/core/AccessControlConfigManager.java
index fc9e793..1cd92dc 100644
--- a/opends/src/server/org/opends/server/core/AccessControlConfigManager.java
+++ b/opends/src/server/org/opends/server/core/AccessControlConfigManager.java
@@ -143,7 +143,7 @@
*
* @return The active access control handler (never {@code null}).
*/
- public AccessControlHandler getAccessControlHandler()
+ public AccessControlHandler<?> getAccessControlHandler()
{
return accessControlHandler.get();
}
diff --git a/opends/src/server/org/opends/server/core/DefaultAccessControlHandler.java b/opends/src/server/org/opends/server/core/DefaultAccessControlHandler.java
index 6655a60..c634bd2 100644
--- a/opends/src/server/org/opends/server/core/DefaultAccessControlHandler.java
+++ b/opends/src/server/org/opends/server/core/DefaultAccessControlHandler.java
@@ -222,5 +222,15 @@
return true;
}
+
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public boolean mayProxy(Entry proxyUser, Entry proxiedUser,
+ Operation operation) {
+ return true;
+ }
}
diff --git a/opends/src/server/org/opends/server/extensions/DigestMD5SASLMechanismHandler.java b/opends/src/server/org/opends/server/extensions/DigestMD5SASLMechanismHandler.java
index c1e1b9e..56106d5 100644
--- a/opends/src/server/org/opends/server/extensions/DigestMD5SASLMechanismHandler.java
+++ b/opends/src/server/org/opends/server/extensions/DigestMD5SASLMechanismHandler.java
@@ -26,95 +26,65 @@
*/
package org.opends.server.extensions;
-
-
-import java.io.UnsupportedEncodingException;
-import java.security.MessageDigest;
-import java.security.SecureRandom;
-import java.text.ParseException;
+import java.net.InetAddress;
+import java.net.UnknownHostException;
import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Iterator;
+import java.util.HashMap;
import java.util.List;
-import java.util.Map;
-import java.util.concurrent.locks.Lock;
-
+import javax.security.sasl.*;
import org.opends.messages.Message;
import org.opends.server.admin.server.ConfigurationChangeListener;
+import org.opends.server.admin.std.meta.DigestMD5SASLMechanismHandlerCfgDefn.*;
import org.opends.server.admin.std.server.DigestMD5SASLMechanismHandlerCfg;
import org.opends.server.admin.std.server.SASLMechanismHandlerCfg;
-import org.opends.server.api.Backend;
import org.opends.server.api.ClientConnection;
import org.opends.server.api.IdentityMapper;
import org.opends.server.api.SASLMechanismHandler;
import org.opends.server.config.ConfigException;
import org.opends.server.core.BindOperation;
import org.opends.server.core.DirectoryServer;
-import org.opends.server.core.PasswordPolicyState;
-import org.opends.server.loggers.debug.DebugTracer;
-import org.opends.server.protocols.asn1.ASN1OctetString;
-import org.opends.server.protocols.internal.InternalClientConnection;
-import org.opends.server.types.AuthenticationInfo;
-import org.opends.server.types.ByteString;
+import org.opends.server.loggers.debug.*;
import org.opends.server.types.ConfigChangeResult;
-import org.opends.server.types.DebugLogLevel;
-import org.opends.server.types.DirectoryException;
-import org.opends.server.types.DisconnectReason;
import org.opends.server.types.DN;
-import org.opends.server.types.Entry;
+import org.opends.server.types.DebugLogLevel;
import org.opends.server.types.InitializationException;
-import org.opends.server.types.LockManager;
-import org.opends.server.types.Privilege;
import org.opends.server.types.ResultCode;
-import org.opends.server.util.Base64;
-
-import static org.opends.messages.ExtensionMessages.*;
-import static org.opends.server.loggers.ErrorLogger.*;
import static org.opends.server.loggers.debug.DebugLogger.*;
+import static org.opends.server.loggers.ErrorLogger.logError;
+import static org.opends.messages.ExtensionMessages.*;
import static org.opends.server.util.ServerConstants.*;
import static org.opends.server.util.StaticUtils.*;
-
/**
- * This class provides an implementation of a SASL mechanism that uses digest
- * authentication via DIGEST-MD5. This is a password-based mechanism that does
- * not expose the password itself over the wire but rather uses an MD5 hash that
- * proves the client knows the password. This is similar to the CRAM-MD5
- * mechanism, and the primary differences are that CRAM-MD5 only obtains random
- * data from the server whereas DIGEST-MD5 uses random data from both the
- * server and the client, CRAM-MD5 does not allow for an authorization ID in
- * addition to the authentication ID where DIGEST-MD5 does, and CRAM-MD5 does
- * not define any integrity and confidentiality mechanisms where DIGEST-MD5
- * does. This implementation is based on the specification in RFC 2831 and
- * updates from draft-ietf-sasl-rfc2831bis-06.
+ * This class provides an implementation of a SASL mechanism that authenticates
+ * clients through DIGEST-MD5.
*/
public class DigestMD5SASLMechanismHandler
- extends SASLMechanismHandler<DigestMD5SASLMechanismHandlerCfg>
- implements ConfigurationChangeListener<
- DigestMD5SASLMechanismHandlerCfg>
-{
- /**
- * The tracer object for the debug logger.
- */
+ extends SASLMechanismHandler<DigestMD5SASLMechanismHandlerCfg>
+ implements ConfigurationChangeListener<DigestMD5SASLMechanismHandlerCfg> {
+
+ //The tracer object for the debug logger.
private static final DebugTracer TRACER = getTracer();
// The current configuration for this SASL mechanism handler.
- private DigestMD5SASLMechanismHandlerCfg currentConfig;
+ private DigestMD5SASLMechanismHandlerCfg configuration;
// The identity mapper that will be used to map ID strings to user entries.
private IdentityMapper<?> identityMapper;
- // The message digest engine that will be used to create the MD5 digests.
- private MessageDigest md5Digest;
+ //Properties to use when creating a SASL server to process the authentication.
+ private HashMap<String,String> saslProps;
- // The lock that will be used to provide threadsafe access to the message
- // digest.
- private Object digestLock;
+//The fully qualified domain name used when creating the SASL server.
+ private String serverFQDN;
- // The random number generator that we will use to create the nonce.
- private SecureRandom randomGenerator;
+ // The DN of the configuration entry for this SASL mechanism handler.
+ private DN configEntryDN;
+ //Property used to set the realm in the environment.
+ private static final String REALM_PROPERTY =
+ "com.sun.security.sasl.digest.realm";
/**
@@ -128,1409 +98,95 @@
}
-
/**
* {@inheritDoc}
*/
@Override()
public void initializeSASLMechanismHandler(
- DigestMD5SASLMechanismHandlerCfg configuration)
- throws ConfigException, InitializationException
- {
- configuration.addDigestMD5ChangeListener(this);
- currentConfig = configuration;
-
-
- // Initialize the variables needed for the MD5 digest creation.
- digestLock = new Object();
- randomGenerator = new SecureRandom();
-
- try
- {
- md5Digest = MessageDigest.getInstance("MD5");
- }
- catch (Exception e)
- {
- if (debugEnabled())
- {
- TRACER.debugCaught(DebugLogLevel.ERROR, e);
+ DigestMD5SASLMechanismHandlerCfg configuration)
+ throws ConfigException, InitializationException {
+ configuration.addDigestMD5ChangeListener(this);
+ configEntryDN = configuration.dn();
+ try {
+ DN identityMapperDN = configuration.getIdentityMapperDN();
+ identityMapper = DirectoryServer.getIdentityMapper(identityMapperDN);
+ serverFQDN = getFQDN(configuration);
+ Message msg= INFO_DIGEST_MD5_SERVER_FQDN.get(serverFQDN);
+ logError(msg);
+ String QOP = getQOP(configuration);
+ saslProps = new HashMap<String,String>();
+ saslProps.put(Sasl.QOP, QOP);
+ if(QOP.equalsIgnoreCase(SASL_MECHANISM_CONFIDENTIALITY)) {
+ saslProps.put(Sasl.STRENGTH, getStrength(configuration));
+ }
+ String realm=getRealm(configuration);
+ if(realm != null) {
+ msg = INFO_DIGEST_MD5_REALM.get(realm);
+ logError(msg);
+ saslProps.put(REALM_PROPERTY, getRealm(configuration));
+ }
+ this.configuration = configuration;
+ DirectoryServer.registerSASLMechanismHandler(SASL_MECHANISM_DIGEST_MD5,
+ this);
+ } catch (UnknownHostException unhe) {
+ if (debugEnabled()) {
+ TRACER.debugCaught(DebugLogLevel.ERROR, unhe);
+ }
+ Message message = ERR_SASL_CANNOT_GET_SERVER_FQDN.get(
+ String.valueOf(configEntryDN), getExceptionMessage(unhe));
+ throw new InitializationException(message, unhe);
}
-
- Message message = ERR_SASLDIGESTMD5_CANNOT_GET_MESSAGE_DIGEST.get(
- getExceptionMessage(e));
- throw new InitializationException(message, e);
- }
-
-
- // Get the identity mapper that should be used to find users.
- DN identityMapperDN = configuration.getIdentityMapperDN();
- identityMapper = DirectoryServer.getIdentityMapper(identityMapperDN);
-
-
- DirectoryServer.registerSASLMechanismHandler(SASL_MECHANISM_DIGEST_MD5,
- this);
}
-
/**
* {@inheritDoc}
*/
@Override()
- public void finalizeSASLMechanismHandler()
- {
- currentConfig.removeDigestMD5ChangeListener(this);
+ public void finalizeSASLMechanismHandler() {
+ configuration.removeDigestMD5ChangeListener(this);
DirectoryServer.deregisterSASLMechanismHandler(SASL_MECHANISM_DIGEST_MD5);
}
-
-
/**
* {@inheritDoc}
*/
@Override()
- public void processSASLBind(BindOperation bindOperation)
- {
- DigestMD5SASLMechanismHandlerCfg config = currentConfig;
- IdentityMapper<?> identityMapper = this.identityMapper;
- String realm = config.getRealm();
-
-
- // The DIGEST-MD5 bind process uses two stages. See if we have any state
- // information from the first stage to determine whether this is a
- // continuation of an existing bind or an initial authentication. Note that
- // this implementation does not support subsequent authentication, so even
- // if the client provided credentials for the bind, it will be treated as an
- // initial authentication if there is no existing state.
- boolean initialAuth = true;
- ClientConnection clientConnection = bindOperation.getClientConnection();
- Object saslStateInfo = clientConnection.getSASLAuthStateInfo();
- if ((saslStateInfo != null) &&
- (saslStateInfo instanceof DigestMD5StateInfo))
- {
- initialAuth = false;
- }
-
- if (initialAuth)
- {
- // Create a buffer to hold the challenge.
- StringBuilder challengeBuffer = new StringBuilder();
-
-
- // Add the realm to the challenge. If we have a configured realm, then
- // use it. Otherwise, add a realm for each suffix defined in the server.
- if (realm == null)
- {
- Map<DN,Backend> suffixes = DirectoryServer.getPublicNamingContexts();
- if (! suffixes.isEmpty())
- {
- Iterator<DN> iterator = suffixes.keySet().iterator();
- challengeBuffer.append("realm=\"");
- challengeBuffer.append(iterator.next().toNormalizedString());
- challengeBuffer.append("\"");
-
- while (iterator.hasNext())
- {
- challengeBuffer.append(",realm=\"");
- challengeBuffer.append(iterator.next().toNormalizedString());
- challengeBuffer.append("\"");
- }
- }
- }
- else
- {
- challengeBuffer.append("realm=\"");
- challengeBuffer.append(realm);
- challengeBuffer.append("\"");
- }
-
-
- // Generate the nonce. Add it to the challenge and remember it for future
- // use.
- String nonce = generateNonce();
- if (challengeBuffer.length() > 0)
- {
- challengeBuffer.append(",");
- }
- challengeBuffer.append("nonce=\"");
- challengeBuffer.append(nonce);
- challengeBuffer.append("\"");
-
-
- // Generate the qop-list and add it to the challenge.
- // FIXME -- Add support for integrity and confidentiality. Once we do,
- // we'll also want to add the maxbuf and cipher options.
- challengeBuffer.append(",qop=\"auth\"");
-
-
- // Add the charset option to indicate that we support UTF-8 values.
- challengeBuffer.append(",charset=utf-8");
-
-
- // Add the algorithm, which will always be "md5-sess".
- challengeBuffer.append(",algorithm=md5-sess");
-
-
- // Encode the challenge as an ASN.1 element. The total length of the
- // encoded value must be less than 2048 bytes, which should not be a
- // problem, but we'll add a safety check just in case.... In the event
- // that it does happen, we'll also log an error so it is more noticeable.
- ASN1OctetString challenge =
- new ASN1OctetString(challengeBuffer.toString());
- if (challenge.value().length >= 2048)
- {
- bindOperation.setResultCode(ResultCode.INVALID_CREDENTIALS);
-
- Message message = WARN_SASLDIGESTMD5_CHALLENGE_TOO_LONG.get(
- challenge.value().length);
- bindOperation.setAuthFailureReason(message);
-
- logError(message);
- return;
- }
-
-
- // Store the state information with the client connection so we can use it
- // for later validation.
- DigestMD5StateInfo stateInfo = new DigestMD5StateInfo(nonce, "00000000");
- clientConnection.setSASLAuthStateInfo(stateInfo);
-
-
- // Prepare the response and return so it will be sent to the client.
- bindOperation.setResultCode(ResultCode.SASL_BIND_IN_PROGRESS);
- bindOperation.setServerSASLCredentials(challenge);
- return;
- }
-
-
- // If we've gotten here, then we have existing SASL state information for
- // this client. Make sure that the client also provided credentials.
- ASN1OctetString clientCredentials = bindOperation.getSASLCredentials();
- if ((clientCredentials == null) || (clientCredentials.value().length == 0))
- {
- bindOperation.setResultCode(ResultCode.INVALID_CREDENTIALS);
-
- Message message = ERR_SASLDIGESTMD5_NO_CREDENTIALS.get();
- bindOperation.setAuthFailureReason(message);
- return;
- }
-
-
- // Parse the SASL state information. Also, since there are only ever two
- // stages of a DIGEST-MD5 bind, clear the SASL state information stored in
- // the client connection because it shouldn't be used anymore regardless of
- // whether the bind succeeds or fails. Note that if we do add support for
- // subsequent authentication in the future, then we will probably need to
- // keep state information in the client connection, but even then it will
- // be different from what's already there.
- DigestMD5StateInfo stateInfo = (DigestMD5StateInfo) saslStateInfo;
- clientConnection.setSASLAuthStateInfo(null);
-
-
- // Create variables to hold values stored in the client's response. We'll
- // also store the base DN because we might need to override it later.
- String responseUserName = null;
- String responseRealm = null;
- String responseNonce = null;
- String responseCNonce = null;
- int responseNonceCount = -1;
- String responseNonceCountStr = null;
- String responseQoP = "auth";
- String responseDigestURI = null;
- byte[] responseDigest = null;
- String responseCharset = "ISO-8859-1";
- String responseAuthzID = null;
-
-
- // Get a temporary string representation of the SASL credentials using the
- // ISO-8859-1 encoding and see if it contains "charset=utf-8". If so, then
- // re-parse the credentials using that character set.
- byte[] credBytes = clientCredentials.value();
- String credString = null;
- String lowerCreds = null;
- try
- {
- credString = new String(credBytes, responseCharset);
- lowerCreds = toLowerCase(credString);
- }
- catch (Exception e)
- {
- if (debugEnabled())
- {
- TRACER.debugCaught(DebugLogLevel.ERROR, e);
- }
-
- // This isn't necessarily fatal because we're going to retry using UTF-8,
- // but we want to log it anyway.
- logError(WARN_SASLDIGESTMD5_CANNOT_PARSE_ISO_CREDENTIALS.get(
- responseCharset, getExceptionMessage(e)));
- }
-
- if ((credString == null) ||
- (lowerCreds.indexOf("charset=utf-8") >= 0))
- {
- try
- {
- credString = new String(credBytes, "UTF-8");
- lowerCreds = toLowerCase(credString);
- }
- catch (Exception e)
- {
- if (debugEnabled())
- {
- TRACER.debugCaught(DebugLogLevel.ERROR, e);
- }
-
- // This is fatal because either we can't parse the credentials as a
- // string at all, or we know we need to do so using UTF-8 and can't.
- bindOperation.setResultCode(ResultCode.INVALID_CREDENTIALS);
-
- Message message = WARN_SASLDIGESTMD5_CANNOT_PARSE_UTF8_CREDENTIALS.get(
- getExceptionMessage(e));
- bindOperation.setAuthFailureReason(message);
- return;
- }
- }
-
-
- // Iterate through the credentials string, parsing the property names and
- // their corresponding values.
- int pos = 0;
- int length = credString.length();
- while (pos < length)
- {
- int equalPos = credString.indexOf('=', pos+1);
- if (equalPos < 0)
- {
- // This is bad because we're not at the end of the string but we don't
- // have a name/value delimiter.
- bindOperation.setResultCode(ResultCode.INVALID_CREDENTIALS);
-
- Message message = ERR_SASLDIGESTMD5_INVALID_TOKEN_IN_CREDENTIALS.get(
- credString, pos);
- bindOperation.setAuthFailureReason(message);
- return;
- }
-
-
- String tokenName = lowerCreds.substring(pos, equalPos);
-
- String tokenValue;
- try
- {
- StringBuilder valueBuffer = new StringBuilder();
- pos = readToken(credString, equalPos+1, length, valueBuffer);
- tokenValue = valueBuffer.toString();
- }
- catch (DirectoryException de)
- {
- // We couldn't parse the token value, so it must be malformed.
- bindOperation.setResultCode(ResultCode.INVALID_CREDENTIALS);
- bindOperation.setAuthFailureReason(
- de.getMessageObject());
- return;
- }
-
- if (tokenName.equals("charset"))
- {
- // The value must be the string "utf-8". If not, that's an error.
- if (! tokenValue.equalsIgnoreCase("utf-8"))
- {
- bindOperation.setResultCode(ResultCode.INVALID_CREDENTIALS);
-
- Message message = ERR_SASLDIGESTMD5_INVALID_CHARSET.get(tokenValue);
- bindOperation.setAuthFailureReason(message);
+ public void processSASLBind(BindOperation bindOp) {
+ ClientConnection clientConnection = bindOp.getClientConnection();
+ if (clientConnection == null) {
+ Message message = ERR_SASLGSSAPI_NO_CLIENT_CONNECTION.get();
+ bindOp.setAuthFailureReason(message);
+ bindOp.setResultCode(ResultCode.INVALID_CREDENTIALS);
return;
- }
}
- else if (tokenName.equals("username"))
- {
- responseUserName = tokenValue;
- }
- else if (tokenName.equals("realm"))
- {
- responseRealm = tokenValue;
- if (realm != null)
- {
- if (! responseRealm.equals(realm))
- {
- bindOperation.setResultCode(ResultCode.INVALID_CREDENTIALS);
-
- Message message =
- ERR_SASLDIGESTMD5_INVALID_REALM.get(responseRealm);
- bindOperation.setAuthFailureReason(message);
- return;
- }
- }
- }
- else if (tokenName.equals("nonce"))
- {
- responseNonce = tokenValue;
- String requestNonce = stateInfo.getNonce();
- if (! responseNonce.equals(requestNonce))
- {
- // The nonce provided by the client is incorrect. This could be an
- // attempt at a replay or chosen plaintext attack, so we'll close the
- // connection. We will put a message in the log but will not send it
- // to the client.
- Message message = ERR_SASLDIGESTMD5_INVALID_NONCE.get();
- clientConnection.disconnect(DisconnectReason.SECURITY_PROBLEM, false,
- message);
- return;
- }
- }
- else if (tokenName.equals("cnonce"))
- {
- responseCNonce = tokenValue;
- }
- else if (tokenName.equals("nc"))
- {
- try
- {
- responseNonceCountStr = tokenValue;
- responseNonceCount = Integer.parseInt(responseNonceCountStr, 16);
- }
- catch (Exception e)
- {
- if (debugEnabled())
- {
- TRACER.debugCaught(DebugLogLevel.ERROR, e);
- }
-
- bindOperation.setResultCode(ResultCode.INVALID_CREDENTIALS);
-
- Message message = ERR_SASLDIGESTMD5_CANNOT_DECODE_NONCE_COUNT.get(
- tokenValue);
- bindOperation.setAuthFailureReason(message);
- return;
- }
-
- int storedNonce;
- try
- {
- storedNonce = Integer.parseInt(stateInfo.getNonceCount(), 16);
- }
- catch (Exception e)
- {
- if (debugEnabled())
- {
- TRACER.debugCaught(DebugLogLevel.ERROR, e);
- }
-
- bindOperation.setResultCode(ResultCode.INVALID_CREDENTIALS);
-
- Message message =
- ERR_SASLDIGESTMD5_CANNOT_DECODE_STORED_NONCE_COUNT.get(
- getExceptionMessage(e));
- bindOperation.setAuthFailureReason(message);
- return;
- }
-
- if (responseNonceCount != (storedNonce + 1))
- {
- // The nonce count provided by the client is incorrect. This
- // indicates a replay attack, so we'll close the connection. We will
- // put a message in the log but we will not send it to the client.
- Message message = ERR_SASLDIGESTMD5_INVALID_NONCE_COUNT.get();
- clientConnection.disconnect(DisconnectReason.SECURITY_PROBLEM, false,
- message);
- return;
- }
- }
- else if (tokenName.equals("qop"))
- {
- responseQoP = tokenValue;
-
- if (responseQoP.equals("auth"))
- {
- // No action necessary.
- }
- else if (responseQoP.equals("auth-int"))
- {
- // FIXME -- Add support for integrity protection.
- bindOperation.setResultCode(ResultCode.INVALID_CREDENTIALS);
-
- Message message = ERR_SASLDIGESTMD5_INTEGRITY_NOT_SUPPORTED.get();
- bindOperation.setAuthFailureReason(message);
- return;
- }
- else if (responseQoP.equals("auth-conf"))
- {
- // FIXME -- Add support for confidentiality protection.
- bindOperation.setResultCode(ResultCode.INVALID_CREDENTIALS);
-
- Message message =
- ERR_SASLDIGESTMD5_CONFIDENTIALITY_NOT_SUPPORTED.get();
- bindOperation.setAuthFailureReason(message);
- return;
- }
- else
- {
- // This is an invalid QoP value.
- bindOperation.setResultCode(ResultCode.INVALID_CREDENTIALS);
-
- Message message = ERR_SASLDIGESTMD5_INVALID_QOP.get(responseQoP);
- bindOperation.setAuthFailureReason(message);
- return;
- }
- }
- else if (tokenName.equals("digest-uri"))
- {
- responseDigestURI = tokenValue;
-
- String serverFQDN = config.getServerFqdn();
- if ((serverFQDN != null) && (serverFQDN.length() > 0))
- {
- // If a server FQDN is populated, then we'll use it to validate the
- // digest-uri, which should be in the form "ldap/serverfqdn".
- String expectedDigestURI = "ldap/" + serverFQDN;
- if (! expectedDigestURI.equalsIgnoreCase(responseDigestURI))
- {
- bindOperation.setResultCode(ResultCode.INVALID_CREDENTIALS);
-
- Message message = ERR_SASLDIGESTMD5_INVALID_DIGEST_URI.get(
- responseDigestURI, expectedDigestURI);
- bindOperation.setAuthFailureReason(message);
- return;
- }
- }
- }
- else if (tokenName.equals("response"))
- {
- try
- {
- responseDigest = hexStringToByteArray(tokenValue);
- }
- catch (ParseException pe)
- {
- if (debugEnabled())
- {
- TRACER.debugCaught(DebugLogLevel.ERROR, pe);
- }
-
- Message message =
- ERR_SASLDIGESTMD5_CANNOT_PARSE_RESPONSE_DIGEST.get(
- getExceptionMessage(pe));
- bindOperation.setAuthFailureReason(message);
- return;
- }
- }
- else if (tokenName.equals("authzid"))
- {
- responseAuthzID = tokenValue;
-
- // FIXME -- This must always be parsed in UTF-8 even if the charset for
- // other elements is ISO 8859-1.
- }
- else if (tokenName.equals("maxbuf") || tokenName.equals("cipher"))
- {
- // FIXME -- Add support for confidentiality and integrity protection.
- }
- else
- {
- bindOperation.setResultCode(ResultCode.INVALID_CREDENTIALS);
-
- Message message = ERR_SASLDIGESTMD5_INVALID_RESPONSE_TOKEN.get(
- tokenName);
- bindOperation.setAuthFailureReason(message);
- return;
- }
- }
-
-
- // Make sure that all required properties have been specified.
- if ((responseUserName == null) || (responseUserName.length() == 0))
- {
- bindOperation.setResultCode(ResultCode.INVALID_CREDENTIALS);
-
- Message message = ERR_SASLDIGESTMD5_NO_USERNAME_IN_RESPONSE.get();
- bindOperation.setAuthFailureReason(message);
- return;
- }
- else if (responseNonce == null)
- {
- bindOperation.setResultCode(ResultCode.INVALID_CREDENTIALS);
-
- Message message = ERR_SASLDIGESTMD5_NO_NONCE_IN_RESPONSE.get();
- bindOperation.setAuthFailureReason(message);
- return;
- }
- else if (responseCNonce == null)
- {
- bindOperation.setResultCode(ResultCode.INVALID_CREDENTIALS);
-
- Message message = ERR_SASLDIGESTMD5_NO_CNONCE_IN_RESPONSE.get();
- bindOperation.setAuthFailureReason(message);
- return;
- }
- else if (responseNonceCount < 0)
- {
- bindOperation.setResultCode(ResultCode.INVALID_CREDENTIALS);
-
- Message message = ERR_SASLDIGESTMD5_NO_NONCE_COUNT_IN_RESPONSE.get();
- bindOperation.setAuthFailureReason(message);
- return;
- }
- else if (responseDigest == null)
- {
- bindOperation.setResultCode(ResultCode.INVALID_CREDENTIALS);
-
- Message message = ERR_SASLDIGESTMD5_NO_DIGEST_IN_RESPONSE.get();
- bindOperation.setAuthFailureReason(message);
- return;
- }
-
-
- // Slight departure from draft-ietf-sasl-rfc2831bis-06 in order to
- // support legacy/broken client implementations, such as Solaris
- // Native LDAP Client, which omit digest-uri directive. the presence
- // of digest-uri directive erroneously read "may" in the RFC and has
- // been fixed later in the DRAFT to read "must". if the client does
- // not include digest-uri directive use the empty string instead.
- if (responseDigestURI == null)
- {
- responseDigestURI = "";
- }
-
-
- // If a realm has not been specified, then use the empty string.
- // FIXME -- Should we reject this if a specific realm is defined?
- if (responseRealm == null)
- {
- responseRealm = "";
- }
-
-
- // Get the user entry for the authentication ID. Allow for an
- // authentication ID that is just a username (as per the DIGEST-MD5 spec),
- // but also allow a value in the authzid form specified in RFC 2829.
- Entry userEntry = null;
- String lowerUserName = toLowerCase(responseUserName);
- if (lowerUserName.startsWith("dn:"))
- {
- // Try to decode the user DN and retrieve the corresponding entry.
- DN userDN;
- try
- {
- userDN = DN.decode(responseUserName.substring(3));
- }
- catch (DirectoryException de)
- {
- if (debugEnabled())
- {
- TRACER.debugCaught(DebugLogLevel.ERROR, de);
- }
-
- bindOperation.setResultCode(ResultCode.INVALID_CREDENTIALS);
-
- Message message = ERR_SASLDIGESTMD5_CANNOT_DECODE_USERNAME_AS_DN.get(
- responseUserName, de.getMessageObject());
- bindOperation.setAuthFailureReason(message);
- return;
- }
-
- if (userDN.isNullDN())
- {
- bindOperation.setResultCode(ResultCode.INVALID_CREDENTIALS);
-
- Message message = ERR_SASLDIGESTMD5_USERNAME_IS_NULL_DN.get();
- bindOperation.setAuthFailureReason(message);
- return;
- }
-
- DN rootDN = DirectoryServer.getActualRootBindDN(userDN);
- if (rootDN != null)
- {
- userDN = rootDN;
- }
-
- // Acquire a read lock on the user entry. If this fails, then so will the
- // authentication.
- Lock readLock = null;
- for (int i=0; i < 3; i++)
- {
- readLock = LockManager.lockRead(userDN);
- if (readLock != null)
- {
- break;
- }
- }
-
- if (readLock == null)
- {
- bindOperation.setResultCode(DirectoryServer.getServerErrorResultCode());
-
- Message message = INFO_SASLDIGESTMD5_CANNOT_LOCK_ENTRY.get(
- String.valueOf(userDN));
- bindOperation.setAuthFailureReason(message);
- return;
- }
-
- try
- {
- userEntry = DirectoryServer.getEntry(userDN);
- }
- catch (DirectoryException de)
- {
- if (debugEnabled())
- {
- TRACER.debugCaught(DebugLogLevel.ERROR, de);
- }
-
- bindOperation.setResultCode(ResultCode.INVALID_CREDENTIALS);
-
- Message message = ERR_SASLDIGESTMD5_CANNOT_GET_ENTRY_BY_DN.get(
- String.valueOf(userDN), de.getMessageObject());
- bindOperation.setAuthFailureReason(message);
- return;
- }
- finally
- {
- LockManager.unlock(userDN, readLock);
- }
- }
- else
- {
- // Use the identity mapper to resolve the username to an entry.
- String userName = responseUserName;
- if (lowerUserName.startsWith("u:"))
- {
- if (lowerUserName.equals("u:"))
- {
- bindOperation.setResultCode(ResultCode.INVALID_CREDENTIALS);
-
- Message message = ERR_SASLDIGESTMD5_ZERO_LENGTH_USERNAME.get();
- bindOperation.setAuthFailureReason(message);
- return;
- }
-
- userName = responseUserName.substring(2);
- }
-
-
- try
- {
- userEntry = identityMapper.getEntryForID(userName);
- }
- catch (DirectoryException de)
- {
- if (debugEnabled())
- {
- TRACER.debugCaught(DebugLogLevel.ERROR, de);
- }
-
- bindOperation.setResultCode(ResultCode.INVALID_CREDENTIALS);
-
- Message message = ERR_SASLDIGESTMD5_CANNOT_MAP_USERNAME.get(
- String.valueOf(responseUserName), de.getMessageObject());
- bindOperation.setAuthFailureReason(message);
- return;
- }
- }
-
-
- // At this point, we should have a user entry. If we don't then fail.
- if (userEntry == null)
- {
- bindOperation.setResultCode(ResultCode.INVALID_CREDENTIALS);
-
- Message message =
- ERR_SASLDIGESTMD5_NO_MATCHING_ENTRIES.get(responseUserName);
- bindOperation.setAuthFailureReason(message);
- return;
- }
- else
- {
- bindOperation.setSASLAuthUserEntry(userEntry);
- }
-
-
- Entry authZEntry = userEntry;
- if (responseAuthzID != null)
- {
- if (responseAuthzID.length() == 0)
- {
- // The authorization ID must not be an empty string.
- bindOperation.setResultCode(ResultCode.INVALID_CREDENTIALS);
-
- Message message = ERR_SASLDIGESTMD5_EMPTY_AUTHZID.get();
- bindOperation.setAuthFailureReason(message);
- return;
- }
- else if (! responseAuthzID.equals(responseUserName))
- {
- String lowerAuthzID = toLowerCase(responseAuthzID);
-
- if (lowerAuthzID.startsWith("dn:"))
- {
- DN authzDN;
- try
- {
- authzDN = DN.decode(responseAuthzID.substring(3));
- }
- catch (DirectoryException de)
- {
- if (debugEnabled())
- {
- TRACER.debugCaught(DebugLogLevel.ERROR, de);
- }
-
- bindOperation.setResultCode(ResultCode.INVALID_CREDENTIALS);
-
- Message message = ERR_SASLDIGESTMD5_AUTHZID_INVALID_DN.get(
- responseAuthzID, de.getMessageObject());
- bindOperation.setAuthFailureReason(message);
- return;
- }
-
- DN actualAuthzDN = DirectoryServer.getActualRootBindDN(authzDN);
- if (actualAuthzDN != null)
- {
- authzDN = actualAuthzDN;
- }
-
- if (! authzDN.equals(userEntry.getDN()))
- {
- AuthenticationInfo tempAuthInfo =
- new AuthenticationInfo(userEntry,
- DirectoryServer.isRootDN(userEntry.getDN()));
- InternalClientConnection tempConn =
- new InternalClientConnection(tempAuthInfo);
- if (! tempConn.hasPrivilege(Privilege.PROXIED_AUTH, bindOperation))
- {
- bindOperation.setResultCode(ResultCode.INVALID_CREDENTIALS);
-
- Message message =
- ERR_SASLDIGESTMD5_AUTHZID_INSUFFICIENT_PRIVILEGES.get(
- String.valueOf(userEntry.getDN()));
- bindOperation.setAuthFailureReason(message);
+ ClientConnection clientConn = bindOp.getClientConnection();
+ SASLContext saslContext =
+ (SASLContext) clientConn.getSASLAuthStateInfo();
+ if(saslContext == null) {
+ try {
+ saslContext = SASLContext.createSASLContext(saslProps, serverFQDN,
+ SASL_MECHANISM_DIGEST_MD5, identityMapper);
+ } catch (SaslException ex) {
+ if (debugEnabled()) {
+ TRACER.debugCaught(DebugLogLevel.ERROR, ex);
+ }
+ Message msg =
+ ERR_SASL_CONTEXT_CREATE_ERROR.get(SASL_MECHANISM_DIGEST_MD5,
+ getExceptionMessage(ex));
+ clientConn.setSASLAuthStateInfo(null);
+ bindOp.setAuthFailureReason(msg);
+ bindOp.setResultCode(ResultCode.INVALID_CREDENTIALS);
return;
- }
-
- if (authzDN.isNullDN())
- {
- authZEntry = null;
- }
- else
- {
- try
- {
- authZEntry = DirectoryServer.getEntry(authzDN);
- if (authZEntry == null)
- {
- bindOperation.setResultCode(ResultCode.INVALID_CREDENTIALS);
-
- Message message = ERR_SASLDIGESTMD5_AUTHZID_NO_SUCH_ENTRY.get(
- String.valueOf(authzDN));
- bindOperation.setAuthFailureReason(message);
- return;
- }
- }
- catch (DirectoryException de)
- {
- if (debugEnabled())
- {
- TRACER.debugCaught(DebugLogLevel.ERROR, de);
- }
-
- bindOperation.setResultCode(ResultCode.INVALID_CREDENTIALS);
-
- Message message = ERR_SASLDIGESTMD5_AUTHZID_CANNOT_GET_ENTRY
- .get(String.valueOf(authzDN), de.getMessageObject());
- bindOperation.setAuthFailureReason(message);
- return;
- }
- }
}
- }
- else
- {
- String idStr;
- if (lowerAuthzID.startsWith("u:"))
- {
- idStr = responseAuthzID.substring(2);
- }
- else
- {
- idStr = responseAuthzID;
- }
-
- if (idStr.length() == 0)
- {
- authZEntry = null;
- }
- else
- {
- try
- {
- authZEntry = identityMapper.getEntryForID(idStr);
- if (authZEntry == null)
- {
- bindOperation.setResultCode(ResultCode.INVALID_CREDENTIALS);
-
- Message message = ERR_SASLDIGESTMD5_AUTHZID_NO_MAPPED_ENTRY.get(
- responseAuthzID);
- bindOperation.setAuthFailureReason(message);
- return;
- }
- }
- catch (DirectoryException de)
- {
- if (debugEnabled())
- {
- TRACER.debugCaught(DebugLogLevel.ERROR, de);
- }
-
- bindOperation.setResultCode(ResultCode.INVALID_CREDENTIALS);
-
- Message message = ERR_SASLDIGESTMD5_CANNOT_MAP_AUTHZID.get(
- responseAuthzID, de.getMessageObject());
- bindOperation.setAuthFailureReason(message);
- return;
- }
- }
-
- if ((authZEntry == null) ||
- (! authZEntry.getDN().equals(userEntry.getDN())))
- {
- AuthenticationInfo tempAuthInfo =
- new AuthenticationInfo(userEntry,
- DirectoryServer.isRootDN(userEntry.getDN()));
- InternalClientConnection tempConn =
- new InternalClientConnection(tempAuthInfo);
- if (! tempConn.hasPrivilege(Privilege.PROXIED_AUTH, bindOperation))
- {
- bindOperation.setResultCode(ResultCode.INVALID_CREDENTIALS);
-
- Message message =
- ERR_SASLDIGESTMD5_AUTHZID_INSUFFICIENT_PRIVILEGES.get(
- String.valueOf(userEntry.getDN()));
- bindOperation.setAuthFailureReason(message);
- return;
- }
- }
- }
+ saslContext.evaluateInitialStage(bindOp);
+ } else {
+ saslContext.evaluateFinalStage(bindOp);
}
- }
-
-
- // Get the clear-text passwords from the user entry, if there are any.
- List<ByteString> clearPasswords;
- try
- {
- PasswordPolicyState pwPolicyState =
- new PasswordPolicyState(userEntry, false);
- clearPasswords = pwPolicyState.getClearPasswords();
- if ((clearPasswords == null) || clearPasswords.isEmpty())
- {
- bindOperation.setResultCode(ResultCode.INVALID_CREDENTIALS);
-
- Message message = ERR_SASLDIGESTMD5_NO_REVERSIBLE_PASSWORDS.get(
- String.valueOf(userEntry.getDN()));
- bindOperation.setAuthFailureReason(message);
- return;
- }
- }
- catch (Exception e)
- {
- bindOperation.setResultCode(ResultCode.INVALID_CREDENTIALS);
-
- Message message = ERR_SASLDIGESTMD5_CANNOT_GET_REVERSIBLE_PASSWORDS.get(
- String.valueOf(userEntry.getDN()),
- String.valueOf(e));
- bindOperation.setAuthFailureReason(message);
- return;
- }
-
-
- // Iterate through the clear-text values and see if any of them can be used
- // in conjunction with the challenge to construct the provided digest.
- boolean matchFound = false;
- byte[] passwordBytes = null;
- for (ByteString clearPassword : clearPasswords)
- {
- byte[] generatedDigest;
- try
- {
- generatedDigest =
- generateResponseDigest(responseUserName, responseAuthzID,
- clearPassword.value(), responseRealm,
- responseNonce, responseCNonce,
- responseNonceCountStr, responseDigestURI,
- responseQoP, responseCharset);
- }
- catch (Exception e)
- {
- if (debugEnabled())
- {
- TRACER.debugCaught(DebugLogLevel.ERROR, e);
- }
-
- logError(WARN_SASLDIGESTMD5_CANNOT_GENERATE_RESPONSE_DIGEST.get(
- getExceptionMessage(e)));
- continue;
- }
-
- if (Arrays.equals(responseDigest, generatedDigest))
- {
- matchFound = true;
- passwordBytes = clearPassword.value();
- break;
- }
- }
-
- if (! matchFound)
- {
- bindOperation.setResultCode(ResultCode.INVALID_CREDENTIALS);
-
- Message message = ERR_SASLDIGESTMD5_INVALID_CREDENTIALS.get();
- bindOperation.setAuthFailureReason(message);
- return;
- }
-
-
- // Generate the response auth element to include in the response to the
- // client.
- byte[] responseAuth;
- try
- {
- responseAuth =
- generateResponseAuthDigest(responseUserName, responseAuthzID,
- passwordBytes, responseRealm,
- responseNonce, responseCNonce,
- responseNonceCountStr, responseDigestURI,
- responseQoP, responseCharset);
- }
- catch (Exception e)
- {
- if (debugEnabled())
- {
- TRACER.debugCaught(DebugLogLevel.ERROR, e);
- }
-
- bindOperation.setResultCode(ResultCode.INVALID_CREDENTIALS);
-
- Message message =
- ERR_SASLDIGESTMD5_CANNOT_GENERATE_RESPONSE_AUTH_DIGEST.get(
- getExceptionMessage(e));
- bindOperation.setAuthFailureReason(message);
- return;
- }
-
- ASN1OctetString responseAuthStr =
- new ASN1OctetString("rspauth=" + getHexString(responseAuth));
-
-
- // Make sure to store the updated nonce count with the client connection to
- // allow for correct subsequent authentication.
- stateInfo.setNonceCount(responseNonceCountStr);
-
-
- // If we've gotten here, then the authentication was successful. We'll also
- // need to include the response auth string in the server SASL credentials.
- bindOperation.setResultCode(ResultCode.SUCCESS);
- bindOperation.setServerSASLCredentials(responseAuthStr);
-
-
- AuthenticationInfo authInfo =
- new AuthenticationInfo(userEntry, authZEntry,
- SASL_MECHANISM_DIGEST_MD5,
- DirectoryServer.isRootDN(userEntry.getDN()));
- bindOperation.setAuthenticationInfo(authInfo);
- return;
}
-
- /**
- * Generates a new nonce value to use during the DIGEST-MD5 authentication
- * process.
- *
- * @return The nonce that should be used for DIGEST-MD5 authentication.
- */
- private String generateNonce()
- {
- byte[] nonceBytes = new byte[16];
- randomGenerator.nextBytes(nonceBytes);
- return Base64.encode(nonceBytes);
- }
-
-
-
- /**
- * Reads the next token from the provided credentials string using the
- * provided information. If the token is surrounded by quotation marks, then
- * the token returned will not include those quotation marks.
- *
- * @param credentials The credentials string from which to read the token.
- * @param startPos The position of the first character of the token to
- * read.
- * @param length The total number of characters in the credentials
- * string.
- * @param token The buffer into which the token is to be placed.
- *
- * @return The position at which the next token should start, or a value
- * greater than or equal to the length of the string if there are no
- * more tokens.
- *
- * @throws DirectoryException If a problem occurs while attempting to read
- * the token.
- */
- private int readToken(String credentials, int startPos, int length,
- StringBuilder token)
- throws DirectoryException
- {
- // If the position is greater than or equal to the length, then we shouldn't
- // do anything.
- if (startPos >= length)
- {
- return startPos;
- }
-
-
- // Look at the first character to see if it's an empty string or the string
- // is quoted.
- boolean isEscaped = false;
- boolean isQuoted = false;
- int pos = startPos;
- char c = credentials.charAt(pos++);
-
- if (c == ',')
- {
- // This must be a zero-length token, so we'll just return the next
- // position.
- return pos;
- }
- else if (c == '"')
- {
- // The string is quoted, so we'll ignore this character, and we'll keep
- // reading until we find the unescaped closing quote followed by a comma
- // or the end of the string.
- isQuoted = true;
- }
- else if (c == '\\')
- {
- // The next character is escaped, so we'll take it no matter what.
- isEscaped = true;
- }
- else
- {
- // The string is not quoted, and this is the first character. Store this
- // character and keep reading until we find a comma or the end of the
- // string.
- token.append(c);
- }
-
-
- // Enter a loop, reading until we find the appropriate criteria for the end
- // of the token.
- while (pos < length)
- {
- c = credentials.charAt(pos++);
-
- if (isEscaped)
- {
- // The previous character was an escape, so we'll take this no matter
- // what.
- token.append(c);
- isEscaped = false;
- }
- else if (c == ',')
- {
- // If this is a quoted string, then this comma is part of the token.
- // Otherwise, it's the end of the token.
- if (isQuoted)
- {
- token.append(c);
- }
- else
- {
- break;
- }
- }
- else if (c == '"')
- {
- if (isQuoted)
- {
- // This should be the end of the token, but in order for it to be
- // valid it must be followed by a comma or the end of the string.
- if (pos >= length)
- {
- // We have hit the end of the string, so this is fine.
- break;
- }
- else
- {
- char c2 = credentials.charAt(pos++);
- if (c2 == ',')
- {
- // We have hit the end of the token, so this is fine.
- break;
- }
- else
- {
- // We found the closing quote before the end of the token. This
- // is not fine.
- Message message =
- ERR_SASLDIGESTMD5_INVALID_CLOSING_QUOTE_POS.get((pos-2));
- throw new DirectoryException(ResultCode.INVALID_CREDENTIALS,
- message);
- }
- }
- }
- else
- {
- // This must be part of the value, so we'll take it.
- token.append(c);
- }
- }
- else if (c == '\\')
- {
- // The next character is escaped. We'll set a flag so we know to
- // accept it, but will not include the backspace itself.
- isEscaped = true;
- }
- else
- {
- token.append(c);
- }
- }
-
-
- return pos;
- }
-
-
-
- /**
- * Generates the appropriate DIGEST-MD5 response for the provided set of
- * information.
- *
- * @param userName The username from the authentication request.
- * @param authzID The authorization ID from the request, or
- * <CODE>null</CODE> if there is none.
- * @param password The clear-text password for the user.
- * @param realm The realm for which the authentication is to be
- * performed.
- * @param nonce The random data generated by the server for use in the
- * digest.
- * @param cnonce The random data generated by the client for use in the
- * digest.
- * @param nonceCount The 8-digit hex string indicating the number of times
- * the provided nonce has been used by the client.
- * @param digestURI The digest URI that specifies the service and host for
- * which the authentication is being performed.
- * @param qop The quality of protection string for the
- * authentication.
- * @param charset The character set used to encode the information.
- *
- * @return The DIGEST-MD5 response for the provided set of information.
- *
- * @throws UnsupportedEncodingException If the specified character set is
- * invalid for some reason.
- */
- public byte[] generateResponseDigest(String userName, String authzID,
- byte[] password, String realm,
- String nonce, String cnonce,
- String nonceCount, String digestURI,
- String qop, String charset)
- throws UnsupportedEncodingException
- {
- synchronized (digestLock)
- {
- // First, get a hash of "username:realm:password".
- StringBuilder a1String1 = new StringBuilder();
- a1String1.append(userName);
- a1String1.append(':');
- a1String1.append(realm);
- a1String1.append(':');
-
- byte[] a1Bytes1a = a1String1.toString().getBytes(charset);
- byte[] a1Bytes1 = new byte[a1Bytes1a.length + password.length];
- System.arraycopy(a1Bytes1a, 0, a1Bytes1, 0, a1Bytes1a.length);
- System.arraycopy(password, 0, a1Bytes1, a1Bytes1a.length,
- password.length);
- byte[] urpHash = md5Digest.digest(a1Bytes1);
-
-
- // Next, get a hash of "urpHash:nonce:cnonce[:authzid]".
- StringBuilder a1String2 = new StringBuilder();
- a1String2.append(':');
- a1String2.append(nonce);
- a1String2.append(':');
- a1String2.append(cnonce);
- if (authzID != null)
- {
- a1String2.append(':');
- a1String2.append(authzID);
- }
- byte[] a1Bytes2a = a1String2.toString().getBytes(charset);
- byte[] a1Bytes2 = new byte[urpHash.length + a1Bytes2a.length];
- System.arraycopy(urpHash, 0, a1Bytes2, 0, urpHash.length);
- System.arraycopy(a1Bytes2a, 0, a1Bytes2, urpHash.length,
- a1Bytes2a.length);
- byte[] a1Hash = md5Digest.digest(a1Bytes2);
-
-
- // Next, get a hash of "AUTHENTICATE:digesturi".
- byte[] a2Bytes = ("AUTHENTICATE:" + digestURI).getBytes(charset);
- byte[] a2Hash = md5Digest.digest(a2Bytes);
-
-
- // Get hex string representations of the last two hashes.
- String a1HashHex = getHexString(a1Hash);
- String a2HashHex = getHexString(a2Hash);
-
-
- // Put together the final string to hash, consisting of
- // "a1HashHex:nonce:nonceCount:cnonce:qop:a2HashHex" and get its digest.
- StringBuilder kdString = new StringBuilder();
- kdString.append(a1HashHex);
- kdString.append(':');
- kdString.append(nonce);
- kdString.append(':');
- kdString.append(nonceCount);
- kdString.append(':');
- kdString.append(cnonce);
- kdString.append(':');
- kdString.append(qop);
- kdString.append(':');
- kdString.append(a2HashHex);
- return md5Digest.digest(kdString.toString().getBytes(charset));
- }
- }
-
-
-
- /**
- * Generates the appropriate DIGEST-MD5 rspauth digest using the provided
- * information.
- *
- * @param userName The username from the authentication request.
- * @param authzID The authorization ID from the request, or
- * <CODE>null</CODE> if there is none.
- * @param password The clear-text password for the user.
- * @param realm The realm for which the authentication is to be
- * performed.
- * @param nonce The random data generated by the server for use in the
- * digest.
- * @param cnonce The random data generated by the client for use in the
- * digest.
- * @param nonceCount The 8-digit hex string indicating the number of times
- * the provided nonce has been used by the client.
- * @param digestURI The digest URI that specifies the service and host for
- * which the authentication is being performed.
- * @param qop The quality of protection string for the
- * authentication.
- * @param charset The character set used to encode the information.
- *
- * @return The DIGEST-MD5 response for the provided set of information.
- *
- * @throws UnsupportedEncodingException If the specified character set is
- * invalid for some reason.
- */
- public byte[] generateResponseAuthDigest(String userName, String authzID,
- byte[] password, String realm,
- String nonce, String cnonce,
- String nonceCount, String digestURI,
- String qop, String charset)
- throws UnsupportedEncodingException
- {
- synchronized (digestLock)
- {
- // First, get a hash of "username:realm:password".
- StringBuilder a1String1 = new StringBuilder();
- a1String1.append(userName);
- a1String1.append(':');
- a1String1.append(realm);
- a1String1.append(':');
-
- byte[] a1Bytes1a = a1String1.toString().getBytes(charset);
- byte[] a1Bytes1 = new byte[a1Bytes1a.length + password.length];
- System.arraycopy(a1Bytes1a, 0, a1Bytes1, 0, a1Bytes1a.length);
- System.arraycopy(password, 0, a1Bytes1, a1Bytes1a.length,
- password.length);
- byte[] urpHash = md5Digest.digest(a1Bytes1);
-
-
- // Next, get a hash of "urpHash:nonce:cnonce[:authzid]".
- StringBuilder a1String2 = new StringBuilder();
- a1String2.append(':');
- a1String2.append(nonce);
- a1String2.append(':');
- a1String2.append(cnonce);
- if (authzID != null)
- {
- a1String2.append(':');
- a1String2.append(authzID);
- }
- byte[] a1Bytes2a = a1String2.toString().getBytes(charset);
- byte[] a1Bytes2 = new byte[urpHash.length + a1Bytes2a.length];
- System.arraycopy(urpHash, 0, a1Bytes2, 0, urpHash.length);
- System.arraycopy(a1Bytes2a, 0, a1Bytes2, urpHash.length,
- a1Bytes2a.length);
- byte[] a1Hash = md5Digest.digest(a1Bytes2);
-
-
- // Next, get a hash of "AUTHENTICATE:digesturi".
- String a2String = ":" + digestURI;
- if (qop.equals("auth-int") || qop.equals("auth-conf"))
- {
- a2String += ":00000000000000000000000000000000";
- }
- byte[] a2Bytes = a2String.getBytes(charset);
- byte[] a2Hash = md5Digest.digest(a2Bytes);
-
-
- // Get hex string representations of the last two hashes.
- String a1HashHex = getHexString(a1Hash);
- String a2HashHex = getHexString(a2Hash);
-
-
- // Put together the final string to hash, consisting of
- // "a1HashHex:nonce:nonceCount:cnonce:qop:a2HashHex" and get its digest.
- StringBuilder kdString = new StringBuilder();
- kdString.append(a1HashHex);
- kdString.append(':');
- kdString.append(nonce);
- kdString.append(':');
- kdString.append(nonceCount);
- kdString.append(':');
- kdString.append(cnonce);
- kdString.append(':');
- kdString.append(qop);
- kdString.append(':');
- kdString.append(a2HashHex);
- return md5Digest.digest(kdString.toString().getBytes(charset));
- }
- }
-
-
-
- /**
- * Retrieves a hexadecimal string representation of the contents of the
- * provided byte array.
- *
- * @param byteArray The byte array for which to obtain the hexadecimal
- * string representation.
- *
- * @return The hexadecimal string representation of the contents of the
- * provided byte array.
- */
- private String getHexString(byte[] byteArray)
- {
- StringBuilder buffer = new StringBuilder(2*byteArray.length);
- for (byte b : byteArray)
- {
- buffer.append(byteToLowerHex(b));
- }
-
- return buffer.toString();
- }
-
-
-
/**
* {@inheritDoc}
*/
@@ -1554,7 +210,6 @@
}
-
/**
* {@inheritDoc}
*/
@@ -1569,7 +224,6 @@
}
-
/**
* {@inheritDoc}
*/
@@ -1581,23 +235,116 @@
}
-
/**
* {@inheritDoc}
*/
public ConfigChangeResult applyConfigurationChange(
- DigestMD5SASLMechanismHandlerCfg configuration)
+ DigestMD5SASLMechanismHandlerCfg configuration)
{
- ResultCode resultCode = ResultCode.SUCCESS;
- boolean adminActionRequired = false;
- ArrayList<Message> messages = new ArrayList<Message>();
+ ResultCode resultCode = ResultCode.SUCCESS;
+ boolean adminActionRequired = false;
- // Get the identity mapper that should be used to find users.
- DN identityMapperDN = configuration.getIdentityMapperDN();
- identityMapper = DirectoryServer.getIdentityMapper(identityMapperDN);
- currentConfig = configuration;
+ ArrayList<Message> messages = new ArrayList<Message>();
+ try {
+ DN identityMapperDN = configuration.getIdentityMapperDN();
+ identityMapper = DirectoryServer.getIdentityMapper(identityMapperDN);
+ serverFQDN = getFQDN(configuration);
+ Message msg = INFO_DIGEST_MD5_SERVER_FQDN.get(serverFQDN);
+ logError(msg);
+ String QOP = getQOP(configuration);
+ saslProps = new HashMap<String,String>();
+ saslProps.put(Sasl.QOP, QOP);
+ if(QOP.equalsIgnoreCase(SASL_MECHANISM_CONFIDENTIALITY)) {
+ saslProps.put(Sasl.STRENGTH, getStrength(configuration));
+ }
+ String realm=getRealm(configuration);
+ if(realm != null) {
+ msg = INFO_DIGEST_MD5_REALM.get(realm);
+ logError(msg);
+ saslProps.put(REALM_PROPERTY, getRealm(configuration));
+ }
+ this.configuration = configuration;
+ } catch (UnknownHostException unhe) {
+ if (debugEnabled()) {
+ TRACER.debugCaught(DebugLogLevel.ERROR, unhe);
+ }
+ resultCode = ResultCode.OPERATIONS_ERROR;
+ messages.add(ERR_SASL_CANNOT_GET_SERVER_FQDN.get(
+ String.valueOf(configEntryDN), getExceptionMessage(unhe)));
+ return new ConfigChangeResult(resultCode,adminActionRequired,
+ messages);
+ }
+ return new ConfigChangeResult(resultCode, adminActionRequired, messages);
+ }
- return new ConfigChangeResult(resultCode, adminActionRequired, messages);
+
+ /**
+ * Retrieves the cipher strength string to use if confidentiality is enforce.
+ * This determination is the lowest value that the server can use.
+ *
+ * @param configuration The configuration to examine.
+ * @return The cipher strength string.
+ */
+ private String
+ getStrength(DigestMD5SASLMechanismHandlerCfg configuration) {
+ CipherStrength strength = configuration.getCipherStrength();
+ if(strength.equals(CipherStrength.HIGH)) {
+ return "high";
+ } else if(strength.equals(CipherStrength.MEDIUM)) {
+ return "high,medium";
+ } else {
+ return "high,medium,low";
+ }
+ }
+
+
+ /**
+ * Retrieves the QOP (quality-of-protection) from the specified
+ * configuration.
+ *
+ * @param configuration The new configuration to use.
+ * @return A string representing the quality-of-protection.
+ */
+ private String
+ getQOP(DigestMD5SASLMechanismHandlerCfg configuration) {
+ QualityOfProtection QOP = configuration.getQualityOfProtection();
+ if(QOP.equals(QualityOfProtection.CONFIDENTIALITY))
+ return "auth-conf";
+ else if(QOP.equals(QualityOfProtection.INTEGRITY))
+ return "auth-int";
+ else
+ return "auth";
+ }
+
+
+ /**
+ * Returns the fully qualified name either defined in the configuration, or,
+ * determined by examining the system configuration.
+ *
+ * @param configuration The configuration to check.
+ * @return The fully qualified hostname of the server.
+ *
+ * @throws UnknownHostException If the name cannot be determined from the
+ * system configuration.
+ */
+ private String getFQDN(DigestMD5SASLMechanismHandlerCfg configuration)
+ throws UnknownHostException {
+ String serverName = configuration.getServerFqdn();
+ if (serverName == null) {
+ serverName = InetAddress.getLocalHost().getCanonicalHostName();
+ }
+ return serverName;
+ }
+
+
+ /**
+ * Retrieve the realm either defined in the specified configuration. If this
+ * isn't defined, the SaslServer internal code uses the server name.
+ *
+ * @param configuration The configuration to check.
+ * @return A string representing the realm.
+ */
+ private String getRealm(DigestMD5SASLMechanismHandlerCfg configuration) {
+ return configuration.getRealm();
}
}
-
diff --git a/opends/src/server/org/opends/server/extensions/DigestMD5StateInfo.java b/opends/src/server/org/opends/server/extensions/DigestMD5StateInfo.java
deleted file mode 100644
index b51f9d7..0000000
--- a/opends/src/server/org/opends/server/extensions/DigestMD5StateInfo.java
+++ /dev/null
@@ -1,104 +0,0 @@
-/*
- * CDDL HEADER START
- *
- * The contents of this file are subject to the terms of the
- * Common Development and Distribution License, Version 1.0 only
- * (the "License"). You may not use this file except in compliance
- * with the License.
- *
- * You can obtain a copy of the license at
- * trunk/opends/resource/legal-notices/OpenDS.LICENSE
- * or https://OpenDS.dev.java.net/OpenDS.LICENSE.
- * See the License for the specific language governing permissions
- * and limitations under the License.
- *
- * When distributing Covered Code, include this CDDL HEADER in each
- * file and include the License file at
- * trunk/opends/resource/legal-notices/OpenDS.LICENSE. If applicable,
- * add the following below this CDDL HEADER, with the fields enclosed
- * by brackets "[]" replaced with your own identifying information:
- * Portions Copyright [yyyy] [name of copyright owner]
- *
- * CDDL HEADER END
- *
- *
- * Copyright 2006-2008 Sun Microsystems, Inc.
- */
-package org.opends.server.extensions;
-
-
-/**
- * This class defines a data structure that will hold state information for use
- * in the DIGEST-MD5 authentication process. This information will support both
- * the two-stage initial authentication as well as subsequent authentication.
- */
-public class DigestMD5StateInfo
-{
-
-
-
- // The nonce generated by the server for this authentication session.
- private String nonce;
-
- // The hex string representation of the nonce count used by the last
- // successful authentication.
- private String nonceCount;
-
-
-
- /**
- * Creates a new instance of this DIGEST-MD5 state info structure.
- *
- * @param nonce The nonce generated by the server for this
- * authentication session.
- * @param nonceCount The hex string representation of the nonce count used
- * by the last successful authentication.
- */
- public DigestMD5StateInfo(String nonce, String nonceCount)
- {
- this.nonce = nonce;
- this.nonceCount = nonceCount;
- }
-
-
-
- /**
- * Retrieves the nonce generated by the server for this authentication
- * session.
- *
- * @return The nonce generated by the server for this authentication session.
- */
- public String getNonce()
- {
- return nonce;
- }
-
-
-
- /**
- * Retrieves the hex string representation of the nonce count used by the last
- * successful authentication.
- *
- * @return The hex string representation of the nonce count used by the last
- * successful authentication.
- */
- public String getNonceCount()
- {
- return nonceCount;
- }
-
-
-
- /**
- * Specifies the hex string representation of the nonce count used by the last
- * successful authentication.
- *
- * @param nonceCount The hex string representation of the nonce count used
- * by the last successful authentication.
- */
- public void setNonceCount(String nonceCount)
- {
- this.nonceCount = nonceCount;
- }
-}
-
diff --git a/opends/src/server/org/opends/server/extensions/GSSAPISASLMechanismHandler.java b/opends/src/server/org/opends/server/extensions/GSSAPISASLMechanismHandler.java
index e45809b..6e3ff5f 100644
--- a/opends/src/server/org/opends/server/extensions/GSSAPISASLMechanismHandler.java
+++ b/opends/src/server/org/opends/server/extensions/GSSAPISASLMechanismHandler.java
@@ -32,11 +32,21 @@
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
+import java.io.IOException;
import java.net.InetAddress;
+import java.net.UnknownHostException;
import java.util.ArrayList;
+import java.util.HashMap;
import java.util.List;
-
+import javax.security.auth.callback.Callback;
+import javax.security.auth.callback.CallbackHandler;
+import javax.security.auth.callback.UnsupportedCallbackException;
+import javax.security.auth.login.LoginContext;
+import javax.security.auth.login.LoginException;
+import javax.security.sasl.Sasl;
+import javax.security.sasl.SaslException;
import org.opends.server.admin.server.ConfigurationChangeListener;
+import org.opends.server.admin.std.meta.GSSAPISASLMechanismHandlerCfgDefn.*;
import org.opends.server.admin.std.server.GSSAPISASLMechanismHandlerCfg;
import org.opends.server.admin.std.server.SASLMechanismHandlerCfg;
import org.opends.server.api.ClientConnection;
@@ -45,19 +55,17 @@
import org.opends.server.config.ConfigException;
import org.opends.server.core.BindOperation;
import org.opends.server.core.DirectoryServer;
-import org.opends.server.types.AuthenticationInfo;
import org.opends.server.types.ConfigChangeResult;
import org.opends.server.types.DirectoryException;
import org.opends.server.types.DN;
import org.opends.server.types.Entry;
import org.opends.server.types.InitializationException;
import org.opends.server.types.ResultCode;
-
import static org.opends.server.loggers.debug.DebugLogger.*;
import org.opends.server.loggers.debug.DebugTracer;
import org.opends.server.types.DebugLogLevel;
+import static org.opends.server.loggers.ErrorLogger.logError;
import static org.opends.messages.ExtensionMessages.*;
-
import static org.opends.server.util.ServerConstants.*;
import static org.opends.server.util.StaticUtils.*;
@@ -69,28 +77,30 @@
*/
public class GSSAPISASLMechanismHandler
extends SASLMechanismHandler<GSSAPISASLMechanismHandlerCfg>
- implements ConfigurationChangeListener<
- GSSAPISASLMechanismHandlerCfg>
-{
- /**
- * The tracer object for the debug logger.
- */
+ implements ConfigurationChangeListener< GSSAPISASLMechanismHandlerCfg>,
+ CallbackHandler {
+
+ //The tracer object for the debug logger.
private static final DebugTracer TRACER = getTracer();
// The DN of the configuration entry for this SASL mechanism handler.
private DN configEntryDN;
// The current configuration for this SASL mechanism handler.
- private GSSAPISASLMechanismHandlerCfg currentConfig;
+ private GSSAPISASLMechanismHandlerCfg configuration;
- // The identity mapper that will be used to map the Kerberos principal to a
- // directory user.
+ // The identity mapper that will be used to map identities.
private IdentityMapper<?> identityMapper;
- // The fully-qualified domain name for the server system.
+ //The properties to use when creating a SASL server to process the GSSAPI
+ //authentication.
+ private HashMap<String,String> saslProps;
+
+ //The fully qualified domain name used when creating the SASL server.
private String serverFQDN;
-
+ //The login context used to perform server-side authentication.
+ private LoginContext loginContext;
/**
* Creates a new instance of this SASL mechanism handler. No initialization
@@ -103,221 +113,237 @@
}
-
/**
* {@inheritDoc}
*/
@Override()
- public void initializeSASLMechanismHandler(
- GSSAPISASLMechanismHandlerCfg configuration)
- throws ConfigException, InitializationException
- {
- configuration.addGSSAPIChangeListener(this);
-
- currentConfig = configuration;
- configEntryDN = configuration.dn();
-
-
- // Get the identity mapper that should be used to find users.
- DN identityMapperDN = configuration.getIdentityMapperDN();
- identityMapper = DirectoryServer.getIdentityMapper(identityMapperDN);
-
-
- // Determine the fully-qualified hostname for this system. It may be
- // provided, but if not, then try to determine it programmatically.
- serverFQDN = configuration.getServerFqdn();
- if (serverFQDN == null)
- {
- try
- {
- serverFQDN = InetAddress.getLocalHost().getCanonicalHostName();
+ public void
+ initializeSASLMechanismHandler(GSSAPISASLMechanismHandlerCfg configuration)
+ throws ConfigException, InitializationException {
+ configuration.addGSSAPIChangeListener(this);
+ this.configuration = configuration;
+ configEntryDN = configuration.dn();
+ try {
+ DN identityMapperDN = configuration.getIdentityMapperDN();
+ identityMapper = DirectoryServer.getIdentityMapper(identityMapperDN);
+ serverFQDN = getFQDN(configuration);
+ Message msg= INFO_GSSAPI_SERVER_FQDN.get(serverFQDN);
+ logError(msg);
+ saslProps = new HashMap<String,String>();
+ saslProps.put(Sasl.QOP, getQOP(configuration));
+ saslProps.put(Sasl.REUSE, "false");
+ String configFileName=configureLoginConfFile(configuration);
+ System.setProperty(JAAS_PROPERTY_CONFIG_FILE, configFileName);
+ System.setProperty(JAAS_PROPERTY_SUBJECT_CREDS_ONLY, "false");
+ getKdcRealm(configuration);
+ DirectoryServer.registerSASLMechanismHandler(SASL_MECHANISM_GSSAPI,
+ this);
+ login();
+ } catch (UnknownHostException unhe) {
+ if (debugEnabled()) {
+ TRACER.debugCaught(DebugLogLevel.ERROR, unhe);
+ }
+ Message message = ERR_SASL_CANNOT_GET_SERVER_FQDN.get(
+ String.valueOf(configEntryDN), getExceptionMessage(unhe));
+ throw new InitializationException(message, unhe);
+ } catch(IOException ioe) {
+ if (debugEnabled()) {
+ TRACER.debugCaught(DebugLogLevel.ERROR, ioe);
+ }
+ Message message = ERR_SASLGSSAPI_CANNOT_CREATE_JAAS_CONFIG.get(
+ getExceptionMessage(ioe));
+ throw new InitializationException(message, ioe);
+ } catch (LoginException le) {
+ if (debugEnabled()) {
+ TRACER.debugCaught(DebugLogLevel.ERROR, le);
+ }
+ Message message = ERR_SASLGSSAPI_CANNOT_CREATE_LOGIN_CONTEXT.get(
+ getExceptionMessage(le));
+ throw new InitializationException(message, le);
}
- catch (Exception e)
- {
- if (debugEnabled())
- {
- TRACER.debugCaught(DebugLogLevel.ERROR, e);
- }
+ }
- Message message = ERR_SASLGSSAPI_CANNOT_GET_SERVER_FQDN.get(
- String.valueOf(configEntryDN), getExceptionMessage(e));
- throw new InitializationException(message, e);
+
+ /**
+ * Checks to make sure that the ds-cfg-kdc-address and dc-cfg-realm are
+ * both defined in the configuration. If only one is set, then that is an
+ * error. If both are defined, or, both are null that is fine.
+ *
+ * @param configuration The configuration to use.
+ * @throws InitializationException If the properties violate the requirements.
+ */
+ private void getKdcRealm(GSSAPISASLMechanismHandlerCfg configuration)
+ throws InitializationException {
+ String kdcAddress = configuration.getKdcAddress();
+ String realm = configuration.getRealm();
+ if((kdcAddress != null && realm == null) ||
+ (kdcAddress == null && realm != null)) {
+ Message message = ERR_SASLGSSAPI_KDC_REALM_NOT_DEFINED.get();
+ throw new InitializationException(message);
+ } else if(kdcAddress != null && realm != null) {
+ System.setProperty(KRBV_PROPERTY_KDC, kdcAddress);
+ System.setProperty(KRBV_PROPERTY_REALM, realm);
+
}
- }
+ }
- // Since we're going to be using JAAS behind the scenes, we need to have a
- // JAAS configuration. Rather than always requiring the user to provide it,
- // we'll write one to a temporary file that will be deleted when the JVM
- // exits.
- String configFileName;
- try
- {
+ /**
+ * During login, callbacks are usually used to prompt for passwords. All of
+ * the GSSAPI login information is provided in the properties and login.conf
+ * file, so callbacks are ignored.
+ *
+ * @param callbacks An array of callbacks to process.
+ * @throws UnsupportedCallbackException if an error occurs.
+ */
+ public void handle(Callback[] callbacks)
+ throws UnsupportedCallbackException {
+ }
+
+
+ /**
+ * Returns the fully qualified name either defined in the configuration, or,
+ * determined by examining the system configuration.
+ *
+ * @param configuration The configuration to check.
+ * @return The fully qualified hostname of the server.
+ *
+ * @throws UnknownHostException If the name cannot be determined from the
+ * system configuration.
+ */
+ private String getFQDN(GSSAPISASLMechanismHandlerCfg configuration)
+ throws UnknownHostException {
+ String serverName = configuration.getServerFqdn();
+ if (serverName == null) {
+ serverName = InetAddress.getLocalHost().getCanonicalHostName();
+ }
+ return serverName;
+ }
+
+
+ /**
+ * Create a login context or login using the principal and keytab information
+ * specified in the configuration.
+ *
+ * @throws LoginException If a login context cannot be created.
+ */
+ private void login() throws LoginException {
+ loginContext =
+ new LoginContext(GSSAPISASLMechanismHandler.class.getName(), this);
+ loginContext.login();
+ }
+
+
+ /**
+ * Logout of the current login context.
+ *
+ */
+ private void logout() {
+ try {
+ loginContext.logout();
+ } catch (LoginException e) {
+ if (debugEnabled()) {
+ TRACER.debugCaught(DebugLogLevel.ERROR, e);
+ }
+ }
+ }
+
+
+ /**
+ * Creates an login.conf file from information in the specified configuration.
+ * This file is used during the login phase.
+ *
+ * @param configuration The new configuration to use.
+ * @return The filename of the new configuration file.
+ *
+ * @throws IOException If the configuration file cannot be created.
+ */
+ private String
+ configureLoginConfFile(GSSAPISASLMechanismHandlerCfg configuration)
+ throws IOException {
+ String configFileName;
File tempFile = File.createTempFile("login", "conf");
configFileName = tempFile.getAbsolutePath();
tempFile.deleteOnExit();
BufferedWriter w = new BufferedWriter(new FileWriter(tempFile, false));
-
w.write(getClass().getName() + " {");
w.newLine();
-
w.write(" com.sun.security.auth.module.Krb5LoginModule required " +
- "storeKey=true useKeyTab=true ");
-
+ "storeKey=true useKeyTab=true ");
String keyTabFile = configuration.getKeytab();
- if (keyTabFile != null)
- {
- w.write("keyTab=\"" + keyTabFile + "\" ");
+ if (keyTabFile != null) {
+ w.write("keyTab=\"" + keyTabFile + "\" ");
}
-
- // FIXME -- Should we add the ability to include "debug=true"?
-
- // FIXME -- Can we get away from hard-coding a protocol here?
- w.write("principal=\"ldap/" + serverFQDN);
-
+ StringBuilder principal= new StringBuilder();
+ String principalName = configuration.getPrincipalName();
String realm = configuration.getRealm();
- if (realm != null)
- {
- w.write("@" + realm);
+ if(principalName != null) {
+ principal.append("principal=\"" + principalName);
+ } else {
+ principal.append("principal=\"ldap/" + serverFQDN);
}
+ if (realm != null) {
+ principal.append("@" + realm);
+ }
+ w.write(principal.toString());
+ Message msg = INFO_GSSAPI_PRINCIPAL_NAME.get(principal.toString());
+ logError(msg);
w.write("\";");
-
w.newLine();
-
w.write("};");
w.newLine();
-
w.flush();
w.close();
- }
- catch (Exception e)
- {
- if (debugEnabled())
- {
- TRACER.debugCaught(DebugLogLevel.ERROR, e);
- }
-
- Message message =
- ERR_SASLGSSAPI_CANNOT_CREATE_JAAS_CONFIG.get(getExceptionMessage(e));
- throw new InitializationException(message, e);
- }
-
- System.setProperty(JAAS_PROPERTY_CONFIG_FILE, configFileName);
- System.setProperty(JAAS_PROPERTY_SUBJECT_CREDS_ONLY, "false");
-
-
- DirectoryServer.registerSASLMechanismHandler(SASL_MECHANISM_GSSAPI, this);
+ return configFileName;
}
-
/**
* {@inheritDoc}
*/
@Override()
- public void finalizeSASLMechanismHandler()
- {
- currentConfig.removeGSSAPIChangeListener(this);
- DirectoryServer.deregisterSASLMechanismHandler(SASL_MECHANISM_GSSAPI);
+ public void finalizeSASLMechanismHandler() {
+ logout();
+ configuration.removeGSSAPIChangeListener(this);
+ DirectoryServer.deregisterSASLMechanismHandler(SASL_MECHANISM_GSSAPI);
}
-
-
/**
* {@inheritDoc}
*/
@Override()
- public void processSASLBind(BindOperation bindOperation)
- {
- // GSSAPI binds use multiple stages, so we need to determine whether this is
- // the first stage or a subsequent one. To do that, see if we have SASL
- // state information in the client connection.
- ClientConnection clientConnection = bindOperation.getClientConnection();
- if (clientConnection == null)
- {
- Message message = ERR_SASLGSSAPI_NO_CLIENT_CONNECTION.get();
-
- bindOperation.setAuthFailureReason(message);
- bindOperation.setResultCode(ResultCode.INVALID_CREDENTIALS);
- return;
- }
-
- GSSAPIStateInfo stateInfo = null;
- Object saslBindState = clientConnection.getSASLAuthStateInfo();
- if ((saslBindState != null) && (saslBindState instanceof GSSAPIStateInfo))
- {
- stateInfo = (GSSAPIStateInfo) saslBindState;
- }
- else
- {
- try
- {
- stateInfo = new GSSAPIStateInfo(this, bindOperation, serverFQDN);
+ public void processSASLBind(BindOperation bindOp) {
+ ClientConnection clientConnection = bindOp.getClientConnection();
+ if (clientConnection == null) {
+ Message message = ERR_SASLGSSAPI_NO_CLIENT_CONNECTION.get();
+ bindOp.setAuthFailureReason(message);
+ bindOp.setResultCode(ResultCode.INVALID_CREDENTIALS);
+ return;
}
- catch (InitializationException ie)
- {
- if (debugEnabled())
- {
- TRACER.debugCaught(DebugLogLevel.ERROR, ie);
- }
-
- bindOperation.setAuthFailureReason(ie.getMessageObject());
- bindOperation.setResultCode(ResultCode.INVALID_CREDENTIALS);
- clientConnection.setSASLAuthStateInfo(null);
- return;
+ ClientConnection clientConn = bindOp.getClientConnection();
+ SASLContext saslContext = (SASLContext) clientConn.getSASLAuthStateInfo();
+ if(saslContext == null) {
+ try {
+ saslContext = SASLContext.createSASLContext(saslProps, serverFQDN,
+ SASL_MECHANISM_GSSAPI, identityMapper);
+ } catch (SaslException ex) {
+ if (debugEnabled()) {
+ TRACER.debugCaught(DebugLogLevel.ERROR, ex);
+ }
+ Message msg =
+ ERR_SASL_CONTEXT_CREATE_ERROR.get(SASL_MECHANISM_GSSAPI,
+ getExceptionMessage(ex));
+ clientConn.setSASLAuthStateInfo(null);
+ bindOp.setAuthFailureReason(msg);
+ bindOp.setResultCode(ResultCode.INVALID_CREDENTIALS);
+ return;
+ }
}
- }
-
- stateInfo.setBindOperation(bindOperation);
- stateInfo.processAuthenticationStage();
-
-
- if (bindOperation.getResultCode() == ResultCode.SUCCESS)
- {
- // The authentication was successful, so set the proper state information
- // in the client connection and return success.
- Entry userEntry = stateInfo.getUserEntry();
- AuthenticationInfo authInfo =
- new AuthenticationInfo(userEntry, SASL_MECHANISM_GSSAPI,
- DirectoryServer.isRootDN(userEntry.getDN()));
- bindOperation.setAuthenticationInfo(authInfo);
- bindOperation.setResultCode(ResultCode.SUCCESS);
-
- // FIXME -- If we're using integrity or confidentiality, then we can't do
- // this.
- clientConnection.setSASLAuthStateInfo(null);
-
- try
- {
- stateInfo.dispose();
- }
- catch (Exception e)
- {
- if (debugEnabled())
- {
- TRACER.debugCaught(DebugLogLevel.ERROR, e);
- }
- }
- }
- else if (bindOperation.getResultCode() == ResultCode.SASL_BIND_IN_PROGRESS)
- {
- // We need to store the SASL auth state with the client connection so we
- // can resume authentication the next time around.
- clientConnection.setSASLAuthStateInfo(stateInfo);
- }
- else
- {
- // The authentication failed. We don't want to keep the SASL state
- // around.
- // FIXME -- Are there other result codes that we need to check for and
- // preserve the auth state?
- clientConnection.setSASLAuthStateInfo(null);
- }
+ saslContext.performAuthentication(loginContext, bindOp);
}
-
/**
* Retrieves the user account for the user associated with the provided
* authorization ID.
@@ -328,7 +354,7 @@
* associated user.
*
* @return The user entry for the user with the specified authorization ID,
- * or <CODE>null</CODE> if none is identified.
+ * or {@code null} if none is identified.
*
* @throws DirectoryException If a problem occurs while searching the
* directory for the associated user, or if
@@ -392,119 +418,41 @@
}
-
/**
* {@inheritDoc}
*/
public ConfigChangeResult applyConfigurationChange(
- GSSAPISASLMechanismHandlerCfg configuration)
- {
- ResultCode resultCode = ResultCode.SUCCESS;
- boolean adminActionRequired = false;
- ArrayList<Message> messages = new ArrayList<Message>();
-
-
- // Get the identity mapper that should be used to find users.
- DN identityMapperDN = configuration.getIdentityMapperDN();
- IdentityMapper<?> newIdentityMapper =
- DirectoryServer.getIdentityMapper(identityMapperDN);
-
-
- // Determine the fully-qualified hostname for this system. It may be
- // provided, but if not, then try to determine it programmatically.
- String newFQDN = configuration.getServerFqdn();
- if (newFQDN == null)
- {
- try
- {
- newFQDN = InetAddress.getLocalHost().getCanonicalHostName();
- }
- catch (Exception e)
- {
- if (debugEnabled())
- {
- TRACER.debugCaught(DebugLogLevel.ERROR, e);
- }
-
- if (resultCode == ResultCode.SUCCESS)
- {
- resultCode = DirectoryServer.getServerErrorResultCode();
- }
-
-
- messages.add(ERR_SASLGSSAPI_CANNOT_GET_SERVER_FQDN.get(
- String.valueOf(configEntryDN),
- getExceptionMessage(e)));
- }
- }
-
-
- if (resultCode == ResultCode.SUCCESS)
- {
- String configFileName;
- try
- {
- File tempFile = File.createTempFile("login", "conf");
- configFileName = tempFile.getAbsolutePath();
- tempFile.deleteOnExit();
- BufferedWriter w = new BufferedWriter(new FileWriter(tempFile, false));
-
- w.write(getClass().getName() + " {");
- w.newLine();
-
- w.write(" com.sun.security.auth.module.Krb5LoginModule required " +
- "storeKey=true useKeyTab=true ");
-
- String keyTabFile = configuration.getKeytab();
- if (keyTabFile != null)
- {
- w.write("keyTab=\"" + keyTabFile + "\" ");
- }
-
- // FIXME -- Should we add the ability to include "debug=true"?
-
- // FIXME -- Can we get away from hard-coding a protocol here?
- w.write("principal=\"ldap/" + serverFQDN);
-
- String realm = configuration.getRealm();
- if (realm != null)
- {
- w.write("@" + realm);
- }
- w.write("\";");
-
- w.newLine();
-
- w.write("};");
- w.newLine();
-
- w.flush();
- w.close();
- }
- catch (Exception e)
- {
- if (debugEnabled())
- {
- TRACER.debugCaught(DebugLogLevel.ERROR, e);
- }
-
- resultCode = DirectoryServer.getServerErrorResultCode();
-
- messages.add(ERR_SASLGSSAPI_CANNOT_CREATE_JAAS_CONFIG.get(
- getExceptionMessage(e)));
-
- return new ConfigChangeResult(resultCode, adminActionRequired, messages);
- }
-
- System.setProperty(JAAS_PROPERTY_CONFIG_FILE, configFileName);
-
+ GSSAPISASLMechanismHandlerCfg configuration) {
+ ResultCode resultCode = ResultCode.SUCCESS;
+ boolean adminActionRequired = false;
+ ArrayList<Message> messages = new ArrayList<Message>();
+ DN identityMapperDN = configuration.getIdentityMapperDN();
+ IdentityMapper<?> newIdentityMapper =
+ DirectoryServer.getIdentityMapper(identityMapperDN);
identityMapper = newIdentityMapper;
- serverFQDN = newFQDN;
- currentConfig = configuration;
- }
+ saslProps = new HashMap<String,String>();
+ saslProps.put(Sasl.QOP, getQOP(configuration));
+ saslProps.put(Sasl.REUSE, "false");
+ this.configuration = configuration;
+ return new ConfigChangeResult(resultCode, adminActionRequired, messages);
+ }
- return new ConfigChangeResult(resultCode, adminActionRequired, messages);
+ /**
+ * Retrieves the QOP (quality-of-protection) from the specified
+ * configuration.
+ *
+ * @param configuration The new configuration to use.
+ * @return A string representing the quality-of-protection.
+ */
+ private String
+ getQOP(GSSAPISASLMechanismHandlerCfg configuration) {
+ QualityOfProtection QOP = configuration.getQualityOfProtection();
+ if(QOP.equals(QualityOfProtection.CONFIDENTIALITY))
+ return "auth-conf";
+ else if(QOP.equals(QualityOfProtection.INTEGRITY))
+ return "auth-int";
+ else
+ return "auth";
}
}
-
diff --git a/opends/src/server/org/opends/server/extensions/GSSAPIStateInfo.java b/opends/src/server/org/opends/server/extensions/GSSAPIStateInfo.java
deleted file mode 100644
index ff34857..0000000
--- a/opends/src/server/org/opends/server/extensions/GSSAPIStateInfo.java
+++ /dev/null
@@ -1,531 +0,0 @@
-/*
- * CDDL HEADER START
- *
- * The contents of this file are subject to the terms of the
- * Common Development and Distribution License, Version 1.0 only
- * (the "License"). You may not use this file except in compliance
- * with the License.
- *
- * You can obtain a copy of the license at
- * trunk/opends/resource/legal-notices/OpenDS.LICENSE
- * or https://OpenDS.dev.java.net/OpenDS.LICENSE.
- * See the License for the specific language governing permissions
- * and limitations under the License.
- *
- * When distributing Covered Code, include this CDDL HEADER in each
- * file and include the License file at
- * trunk/opends/resource/legal-notices/OpenDS.LICENSE. If applicable,
- * add the following below this CDDL HEADER, with the fields enclosed
- * by brackets "[]" replaced with your own identifying information:
- * Portions Copyright [yyyy] [name of copyright owner]
- *
- * CDDL HEADER END
- *
- *
- * Copyright 2006-2008 Sun Microsystems, Inc.
- */
-package org.opends.server.extensions;
-import org.opends.messages.Message;
-
-
-
-import java.security.PrivilegedExceptionAction;
-import java.util.HashMap;
-import javax.security.auth.Subject;
-import javax.security.auth.callback.Callback;
-import javax.security.auth.callback.CallbackHandler;
-import javax.security.auth.callback.NameCallback;
-import javax.security.auth.callback.UnsupportedCallbackException;
-import javax.security.auth.login.LoginContext;
-import javax.security.sasl.AuthorizeCallback;
-import javax.security.sasl.Sasl;
-import javax.security.sasl.SaslServer;
-
-import org.opends.server.api.ClientConnection;
-import org.opends.server.core.BindOperation;
-import org.opends.server.core.DirectoryServer;
-import org.opends.server.protocols.asn1.ASN1OctetString;
-import org.opends.server.types.AuthenticationInfo;
-import org.opends.server.types.ByteString;
-import org.opends.server.types.DirectoryException;
-import org.opends.server.types.Entry;
-import org.opends.server.types.InitializationException;
-import org.opends.server.types.ResultCode;
-
-import static org.opends.server.loggers.debug.DebugLogger.*;
-import org.opends.server.loggers.debug.DebugTracer;
-import org.opends.server.types.DebugLogLevel;
-import static org.opends.messages.ExtensionMessages.*;
-import static org.opends.server.util.ServerConstants.*;
-import static org.opends.server.util.StaticUtils.*;
-
-
-
-/**
- * This class defines a data structure that holds state information needed for
- * processing a SASL GSSAPI bind from a client.
- */
-public class GSSAPIStateInfo
- implements PrivilegedExceptionAction<Boolean>, CallbackHandler
-{
- /**
- * The tracer object for the debug logger.
- */
- private static final DebugTracer TRACER = getTracer();
-
-
-
-
- // The bind operation with which this state is associated.
- private BindOperation bindOperation;
-
- // The client connection with which this state is associated.
- private ClientConnection clientConnection;
-
- // The entry of the user that authenticated in this session.
- private Entry userEntry;
-
- // The GSSAPI authentication handler that created this state information.
- private GSSAPISASLMechanismHandler gssapiHandler;
-
- // The login context used to perform server-side authentication.
- private LoginContext loginContext;
-
- // The SASL server that will be used to actually perform the authentication.
- private SaslServer saslServer;
-
- // The protocol that the client is using to communicate with the server.
- private String protocol;
-
- // The FQDN of this system to use in the authentication process.
- private String serverFQDN;
-
-
-
-
- /**
- * Creates a new GSSAPI state info structure with the provided information.
- *
- * @param gssapiHandler The GSSAPI authentication handler that created this
- * state information.
- * @param bindOperation The bind operation with which this state is
- * associated.
- * @param serverFQDN The fully-qualified domain name for the server to
- * use in the authentication process.
- *
- * @throws InitializationException If it is not possible to authenticate to
- * the KDC to verify the client credentials.
- */
- public GSSAPIStateInfo(GSSAPISASLMechanismHandler gssapiHandler,
- BindOperation bindOperation, String serverFQDN)
- throws InitializationException
- {
- this.gssapiHandler = gssapiHandler;
- this.bindOperation = bindOperation;
- this.serverFQDN = serverFQDN;
-
- clientConnection = bindOperation.getClientConnection();
- protocol = toLowerCase(clientConnection.getProtocol());
- userEntry = null;
-
-
- // Create the LoginContext and do the server-side authentication.
- // FIXME -- Can this be moved to a one-time call in the GSSAPI handler
- // rather than once per GSSAPI bind attempt?
- try
- {
- loginContext =
- new LoginContext(GSSAPISASLMechanismHandler.class.getName(), this);
- }
- catch (Exception e)
- {
- if (debugEnabled())
- {
- TRACER.debugCaught(DebugLogLevel.ERROR, e);
- }
-
- Message message = ERR_SASLGSSAPI_CANNOT_CREATE_LOGIN_CONTEXT.get(
- getExceptionMessage(e));
- throw new InitializationException(message, e);
- }
-
- try
- {
- loginContext.login();
- }
- catch (Exception e)
- {
- if (debugEnabled())
- {
- TRACER.debugCaught(DebugLogLevel.ERROR, e);
- }
-
- Message message =
- ERR_SASLGSSAPI_CANNOT_AUTHENTICATE_SERVER.get(getExceptionMessage(e));
- throw new InitializationException(message, e);
- }
-
-
- saslServer = null;
- }
-
-
-
- /**
- * Sets the bind operation for the next stage of processing in the GSSAPI
- * authentication. This must be called before the processing is performed so
- * that the appropriate response may be sent to the client.
- *
- * @param bindOperation The bind operation for the next stage of processing
- * in the GSSAPI authentication.
- */
- public void setBindOperation(BindOperation bindOperation)
- {
- this.bindOperation = bindOperation;
- }
-
-
-
- /**
- * Retrieves the entry of the user that has authenticated on this GSSAPI
- * session. This should only be available after a successful GSSAPI
- * authentication. The return value of this method should be considered
- * unreliable if GSSAPI authentication has not yet completed successfully.
- *
- * @return x
- */
- public Entry getUserEntry()
- {
- return userEntry;
- }
-
-
-
- /**
- * Destroys any sensitive information that might be associated with the SASL
- * server instance.
- */
- public void dispose()
- {
- try
- {
- saslServer.dispose();
- }
- catch (Exception e)
- {
- if (debugEnabled())
- {
- TRACER.debugCaught(DebugLogLevel.ERROR, e);
- }
- }
- }
-
-
-
- /**
- * Processes the next stage of the GSSAPI bind process. This may be used for
- * the first stage or any stage thereafter until the authentication is
- * complete. It will automatically take care of the JAAS processing behind
- * the scenes as necessary.
- */
- public void processAuthenticationStage()
- {
- try
- {
- Subject.doAs(loginContext.getSubject(), this);
- }
- catch (Exception e)
- {
- if (debugEnabled())
- {
- TRACER.debugCaught(DebugLogLevel.ERROR, e);
- }
- }
- }
-
-
-
- /**
- * Processes a stage of the SASL GSSAPI bind request. The
- * <CODE>setBindOperation</CODE> method must have been called to update the
- * reference to the latest bind request before invoking this method through
- * <CODE>doAs</CODE> or <CODE>doAsPrivileged</CODE>.
- *
- * @return <CODE>true</CODE> if there was no error during this stage of the
- * bind and processing can continue, or <CODE>false</CODE> if an
- * error occurred and and processing should not continue.
- */
- public Boolean run()
- {
- if (saslServer == null)
- {
- // Create the SASL server instance for use with this authentication
- // attempt.
- try
- {
- HashMap<String,String> saslProperties = new HashMap<String,String>();
-
- // FIXME -- We need to add support for auth-int and auth-conf.
- // propertyMap.put(Sasl.QOP, "auth,auth-int,auth-conf");
- saslProperties.put(Sasl.QOP, "auth");
-
- saslProperties.put(Sasl.REUSE, "false");
-
- saslServer = Sasl.createSaslServer(SASL_MECHANISM_GSSAPI, "ldap",
- serverFQDN, saslProperties, this);
- }
- catch (Exception e)
- {
- if (debugEnabled())
- {
- TRACER.debugCaught(DebugLogLevel.ERROR, e);
- }
-
- Message message = ERR_SASLGSSAPI_CANNOT_CREATE_SASL_SERVER.get(
- getExceptionMessage(e));
-
- clientConnection.setSASLAuthStateInfo(null);
- bindOperation.setAuthFailureReason(message);
- bindOperation.setResultCode(ResultCode.INVALID_CREDENTIALS);
- return false;
- }
- }
-
-
- // Get the SASL credentials from the bind request.
- byte[] clientCredBytes;
- ByteString clientCredentials = bindOperation.getSASLCredentials();
- if (clientCredentials == null)
- {
- clientCredBytes = new byte[0];
- }
- else
- {
- clientCredBytes = clientCredentials.value();
- }
-
-
- // Process the client SASL credentials and get the data to include in the
- // server SASL credentials of the response.
- ASN1OctetString serverSASLCredentials;
- try
- {
- byte[] serverCredBytes = saslServer.evaluateResponse(clientCredBytes);
-
- if (serverCredBytes == null)
- {
- serverSASLCredentials = null;
- }
- else
- {
- serverSASLCredentials = new ASN1OctetString(serverCredBytes);
- }
- }
- catch (Exception e)
- {
- if (debugEnabled())
- {
- TRACER.debugCaught(DebugLogLevel.ERROR, e);
- }
-
- try
- {
- saslServer.dispose();
- }
- catch (Exception e2)
- {
- if (debugEnabled())
- {
- TRACER.debugCaught(DebugLogLevel.ERROR, e2);
- }
- }
-
- Message message = ERR_SASLGSSAPI_CANNOT_EVALUATE_RESPONSE.get(
- getExceptionMessage(e));
-
- clientConnection.setSASLAuthStateInfo(null);
- bindOperation.setAuthFailureReason(message);
- bindOperation.setResultCode(ResultCode.INVALID_CREDENTIALS);
- return false;
- }
-
-
- // If the authentication is not yet complete, then send a "SASL bind in
- // progress" response to the client.
- if (! saslServer.isComplete())
- {
- clientConnection.setSASLAuthStateInfo(saslServer);
- bindOperation.setResultCode(ResultCode.SASL_BIND_IN_PROGRESS);
- bindOperation.setServerSASLCredentials(serverSASLCredentials);
- return true;
- }
-
-
- // If the authentication is complete, then get the authorization ID from the
- // SASL server and map that to a user in the directory.
- String authzID = saslServer.getAuthorizationID();
- if ((authzID == null) || (authzID.length() == 0))
- {
- try
- {
- saslServer.dispose();
- }
- catch (Exception e)
- {
- if (debugEnabled())
- {
- TRACER.debugCaught(DebugLogLevel.ERROR, e);
- }
- }
-
- Message message = ERR_SASLGSSAPI_NO_AUTHZ_ID.get();
-
- clientConnection.setSASLAuthStateInfo(null);
- bindOperation.setAuthFailureReason(message);
- bindOperation.setResultCode(ResultCode.INVALID_CREDENTIALS);
- return false;
- }
-
-
- try
- {
- userEntry = gssapiHandler.getUserForAuthzID(bindOperation, authzID);
- }
- catch (DirectoryException de)
- {
- if (debugEnabled())
- {
- TRACER.debugCaught(DebugLogLevel.ERROR, de);
- }
-
- try
- {
- saslServer.dispose();
- }
- catch (Exception e)
- {
- if (debugEnabled())
- {
- TRACER.debugCaught(DebugLogLevel.ERROR, e);
- }
- }
-
- bindOperation.setResultCode(ResultCode.INVALID_CREDENTIALS);
- bindOperation.setAuthFailureReason(de.getMessageObject());
- clientConnection.setSASLAuthStateInfo(null);
- return false;
- }
-
-
- // If the user entry is null, then we couldn't map the authorization ID to
- // a user.
- if (userEntry == null)
- {
- try
- {
- saslServer.dispose();
- }
- catch (Exception e)
- {
- if (debugEnabled())
- {
- TRACER.debugCaught(DebugLogLevel.ERROR, e);
- }
- }
-
- Message message = ERR_SASLGSSAPI_CANNOT_MAP_AUTHZID.get(authzID);
-
- clientConnection.setSASLAuthStateInfo(null);
- bindOperation.setAuthFailureReason(message);
- bindOperation.setResultCode(ResultCode.INVALID_CREDENTIALS);
- return false;
- }
- else
- {
- bindOperation.setSASLAuthUserEntry(userEntry);
- }
-
-
- // The authentication was successful, so set the proper state information
- // in the client connection and return success.
- AuthenticationInfo authInfo =
- new AuthenticationInfo(userEntry, SASL_MECHANISM_GSSAPI,
- DirectoryServer.isRootDN(userEntry.getDN()));
- bindOperation.setAuthenticationInfo(authInfo);
- bindOperation.setResultCode(ResultCode.SUCCESS);
-
- // FIXME -- If we're using integrity or confidentiality, then we can't do
- // this.
- clientConnection.setSASLAuthStateInfo(null);
- try
- {
- saslServer.dispose();
- }
- catch (Exception e)
- {
- if (debugEnabled())
- {
- TRACER.debugCaught(DebugLogLevel.ERROR, e);
- }
- }
-
- return true;
- }
-
-
-
- /**
- * Handles any callbacks that might be required in order to process a SASL
- * GSSAPI bind on the server. In this case, if an authorization ID was
- * provided, then a callback may be used to determine whether it is
- * acceptable.
- *
- * @param callbacks The callbacks needed to provide information for the
- * GSSAPI authentication process.
- *
- * @throws UnsupportedCallbackException If an unexpected callback is
- * included in the provided set.
- */
- public void handle(Callback[] callbacks)
- throws UnsupportedCallbackException
- {
- for (Callback callback : callbacks)
- {
- if (callback instanceof NameCallback)
- {
- String authID = toLowerCase(clientConnection.getProtocol()) + "/" +
- serverFQDN;
- ((NameCallback) callback).setName(authID);
- }
- else if (callback instanceof AuthorizeCallback)
- {
- // FIXME -- Should we allow an authzID different from the authID?
- // FIXME -- Do we need to do anything else here?
- AuthorizeCallback authzCallback = (AuthorizeCallback) callback;
- String authID = authzCallback.getAuthenticationID();
- String authzID = authzCallback.getAuthorizationID();
-
- if (authID.equals(authzID))
- {
- authzCallback.setAuthorizedID(authzID);
- authzCallback.setAuthorized(true);
- }
- else
- {
- Message message = ERR_SASLGSSAPI_DIFFERENT_AUTHID_AND_AUTHZID.get(
- authID, authzID);
- bindOperation.setAuthFailureReason(message);
- authzCallback.setAuthorized(false);
- }
- }
- else
- {
- // We weren't prepared for this type of callback.
- Message message =
- INFO_SASLGSSAPI_UNEXPECTED_CALLBACK.get(String.valueOf(callback));
- throw new UnsupportedCallbackException(callback, message.toString());
- }
- }
- }
-}
-
diff --git a/opends/src/server/org/opends/server/extensions/NullConnectionSecurityProvider.java b/opends/src/server/org/opends/server/extensions/NullConnectionSecurityProvider.java
index 0564cad..0aa9ae0 100644
--- a/opends/src/server/org/opends/server/extensions/NullConnectionSecurityProvider.java
+++ b/opends/src/server/org/opends/server/extensions/NullConnectionSecurityProvider.java
@@ -173,6 +173,15 @@
/**
* {@inheritDoc}
*/
+ @Override
+ public boolean isActive() {
+ //This provider is always active.
+ return true;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
@Override()
public ConnectionSecurityProvider newInstance(ClientConnection
clientConnection,
diff --git a/opends/src/server/org/opends/server/extensions/SASLContext.java b/opends/src/server/org/opends/server/extensions/SASLContext.java
new file mode 100644
index 0000000..2ccc27f
--- /dev/null
+++ b/opends/src/server/org/opends/server/extensions/SASLContext.java
@@ -0,0 +1,894 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at
+ * trunk/opends/resource/legal-notices/OpenDS.LICENSE
+ * or https://OpenDS.dev.java.net/OpenDS.LICENSE.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at
+ * trunk/opends/resource/legal-notices/OpenDS.LICENSE. If applicable,
+ * add the following below this CDDL HEADER, with the fields enclosed
+ * by brackets "[]" replaced with your own identifying information:
+ * Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ *
+ *
+ * Copyright 2008 Sun Microsystems, Inc.
+ */
+
+package org.opends.server.extensions;
+
+import java.security.PrivilegedActionException;
+import java.security.PrivilegedExceptionAction;
+import java.util.HashMap;
+import java.util.List;
+import java.util.concurrent.locks.Lock;
+import javax.security.auth.Subject;
+import javax.security.auth.callback.Callback;
+import javax.security.auth.callback.CallbackHandler;
+import javax.security.auth.callback.NameCallback;
+import javax.security.auth.callback.PasswordCallback;
+import javax.security.auth.callback.UnsupportedCallbackException;
+import javax.security.auth.login.LoginContext;
+import javax.security.sasl.AuthorizeCallback;
+import javax.security.sasl.RealmCallback;
+import javax.security.sasl.Sasl;
+import javax.security.sasl.SaslException;
+import javax.security.sasl.SaslServer;
+import org.opends.server.loggers.debug.DebugTracer;
+import org.opends.messages.Message;
+import org.opends.server.api.ClientConnection;
+import org.opends.server.api.IdentityMapper;
+import org.opends.server.core.AccessControlConfigManager;
+import org.opends.server.core.BindOperation;
+import org.opends.server.core.DirectoryServer;
+import org.opends.server.core.PasswordPolicyState;
+import org.opends.server.protocols.asn1.ASN1OctetString;
+import org.opends.server.protocols.internal.InternalClientConnection;
+import org.opends.server.protocols.ldap.LDAPClientConnection;
+import org.opends.server.types.*;
+import static org.opends.messages.ExtensionMessages.*;
+import static org.opends.server.loggers.debug.DebugLogger.*;
+import static org.opends.server.util.ServerConstants.*;
+import static org.opends.server.util.StaticUtils.*;
+
+/**
+ * This class defines the SASL context needed to process GSSAPI and DIGEST-MD5
+ * bind requests from clients.
+ *
+ */
+public class
+SASLContext implements CallbackHandler, PrivilegedExceptionAction<Boolean> {
+
+
+ // The tracer object for the debug logger.
+ private static final DebugTracer TRACER = getTracer();
+
+ // The SASL server to use in the authentication.
+ private SaslServer saslServer=null;
+
+ // The identity mapper to use when mapping identities.
+ private final IdentityMapper<?> identityMapper;
+
+ //The property set to use when creating the SASL server.
+ private HashMap<String, String>saslProps;
+
+ //The fully qualified domain name to use when creating the SASL server.
+ private String serverFQDN;
+
+ //The SASL mechanism name.
+ private final String mech;
+
+ //The authorization entry used in the authentication.
+ private Entry authEntry=null;
+
+ //The authorization entry used in the authentication.
+ private Entry authzEntry=null;
+
+ //The user name used in the authentication taken from the name callback.
+ private String userName;
+
+ //Error message used by callbacks.
+ private Message cbMsg;
+
+ //The current bind operation used by the callbacks.
+ private BindOperation bindOp;
+
+ //Used to check if negotiated QOP is confidentiality or integrity.
+ private final String confidentiality = "auth-conf";
+ private final String integrity = "auth-int";
+
+
+ /**
+ * Create a SASL context using the specified parameters. A SASL server will
+ * be instantiated only for the DIGEST-MD5 mechanism. The GSSAPI mechanism
+ * must instantiate the SASL server as the login context in a separate step.
+ *
+ * @param saslProps The properties to use in creating the SASL server.
+ * @param serverFQDN The fully qualified domain name to use in creating the
+ * SASL server.
+ * @param mech The SASL mechanism name.
+ * @param identityMapper The identity mapper to use in mapping identities.
+ *
+ * @throws SaslException If the SASL server can not be instantiated.
+ */
+ private SASLContext(HashMap<String, String>saslProps, String serverFQDN,
+ String mech, IdentityMapper<?> identityMapper)
+ throws SaslException {
+ this.identityMapper = identityMapper;
+ this.mech = mech;
+ this.saslProps = saslProps;
+ this.serverFQDN = serverFQDN;
+ if(mech.equals(SASL_MECHANISM_DIGEST_MD5)) {
+ initSASLServer();
+ }
+ }
+
+
+ /**
+ * Instantiate a GSSAPI/DIGEST-MD5 SASL context using the specified
+ * parameters.
+ *
+ * @param saslProps The properties to use in creating the SASL server.
+ * @param serverFQDN The fully qualified domain name to use in creating the
+ * SASL server.
+ * @param mech The SASL mechanism name.
+ * @param identityMapper The identity mapper to use in mapping identities.
+ * @return A fully instantiated SASL context to use in processing a SASL
+ * bind for the GSSAPI or DIGEST-MD5 mechanisms.
+ *
+ * @throws SaslException If the SASL server can not be instantiated.
+ */
+ public static
+ SASLContext createSASLContext(HashMap<String,String>saslProps,
+ String serverFQDN, String mech,
+ IdentityMapper<?> identityMapper) throws SaslException {
+ return (new SASLContext(saslProps,serverFQDN, mech, identityMapper));
+ }
+
+
+ /**
+ * Initialize the SASL server using parameters specified in the
+ * constructor.
+ *
+ * @throws SaslException If the SASL server can not be instantiated.
+ */
+ private void initSASLServer() throws SaslException {
+ this.saslServer = Sasl.createSaslServer(mech, SASL_DEFAULT_PROTOCOL,
+ serverFQDN, saslProps, this);
+ }
+
+
+ /**
+ * Wrap the specified clear byte array using the provided offset and length
+ * values. Used only when the SASL server has negotiated
+ * confidentiality/integrity processing.
+ *
+ * @param clearBytes The clear byte array to wrap.
+ * @param offset The offset into the clear byte array..
+ * @param len The length from the offset of the number of bytes to wrap.
+ * @return A byte array containing the wrapped bytes.
+ *
+ * @throws SaslException If the clear bytes cannot be wrapped.
+ */
+ byte[] wrap(byte[] clearBytes, int offset, int len)
+ throws SaslException {
+ return saslServer.wrap(clearBytes, offset, len);
+ }
+
+
+ /**
+ * Unwrap the specified byte array using the provided offset and length
+ * values. Used only when the SASL server has negotiated
+ * confidentiality/integrity processing.
+ *
+ * @param bytes The byte array to unwrap.
+ * @param offset The offset in the array.
+ * @param len The length from the offset of the number of bytes to unwrap.
+ * @return A byte array containing the clear or unwrapped bytes.
+ *
+ * @throws SaslException If the bytes cannot be unwrapped.
+ */
+ byte[] unwrap(byte[] bytes, int offset, int len)
+ throws SaslException {
+ return saslServer.unwrap(bytes, offset, len);
+ }
+
+
+ /**
+ * Dispose of the SASL server instance.
+ */
+ void dispose() {
+ try {
+ saslServer.dispose();
+ } catch (SaslException e) {
+ if (debugEnabled()) {
+ TRACER.debugCaught(DebugLogLevel.ERROR, e);
+ }
+ }
+ }
+
+
+ /**
+ * Return the negotiated buffer size.
+ *
+ * @param prop The buffer size property to return.
+ * @return The value of the negotiated buffer size.
+ */
+ int getBufSize(String prop) {
+ String sizeStr =
+ (String) saslServer.getNegotiatedProperty(prop);
+ return Integer.parseInt(sizeStr);
+ }
+
+
+ /**
+ * Return true if the bind has been completed. If the context is supporting
+ * confidentiality/integrity, the security provider will need to check
+ * if the context has completed its handshakes with the client and is
+ * ready to process confidentiality/integrity messages.
+ *
+ * @return {@code true} if the handshaking is complete,
+ * {@code false} if further handshaking is needed.
+ */
+ boolean isBindComplete() {
+ return saslServer.isComplete();
+ }
+
+
+ /**
+ * Return true if the SASL server has negotiated with the client to support
+ * confidentiality/integrity.
+ *
+ * @return {@code true} if the context support
+ * confidentiality/integrity, or, {@code false} otherwise.
+ */
+ private boolean isConfidentialIntegrity() {
+ boolean ret = false;
+ String qop = (String) saslServer.getNegotiatedProperty(Sasl.QOP);
+ if(qop.equalsIgnoreCase(confidentiality) ||
+ qop.equalsIgnoreCase(integrity))
+ ret = true;
+ return ret;
+ }
+
+
+ /**
+ * Helper routine to call the SASL server evaluateResponse method with the
+ * specified byte array.
+ *
+ * @param bytes The byte array to pass to the SASL server.
+ * @return A byte array containing the result of the evaluation.
+ *
+ * @throws SaslException If the SASL server cannot evaluate the byte array.
+ */
+ private byte[] evaluateResponse(byte[] bytes) throws SaslException {
+ return saslServer.evaluateResponse(bytes);
+ }
+
+
+ /**
+ * This method is used to process an exception that is thrown during bind
+ * processing. It will try to determine if the exception is a result of
+ * callback processing, and if it is, will try to use a more informative
+ * failure message set by the callback.
+ *
+ * If the exception is a result of a error during the the SASL server
+ * processing, the callback message will be null, and
+ * the method will use the specified message parameter as the
+ * failure reason. This is a more cryptic exception message hard-coded
+ * in the SASL server internals.
+ *
+ * The method also disposes of the SASL server, clears the authentication
+ * state and sets the result code to INVALID_CREDENTIALs
+ *
+ * @param msg The message to use if the callback message is not null.
+ */
+ private void handleError(Message msg) {
+ dispose();
+ ClientConnection clientConn = bindOp.getClientConnection();
+ clientConn.setSASLAuthStateInfo(null);
+ //Check if the callback message is null and use that message if not.
+ if(cbMsg != null)
+ bindOp.setAuthFailureReason(cbMsg);
+ else
+ bindOp.setAuthFailureReason(msg);
+ bindOp.setResultCode(ResultCode.INVALID_CREDENTIALS);
+ }
+
+
+ /**
+ * Checks the specified authentication information parameter against the
+ * privilege subsystem to see if it has PROXIED_AUTH privileges.
+ *
+ * @param authInfo The authentication information to use in the check.
+ * @return {@code true} if the authentication information has
+ * PROXIED_AUTH privileges, {@code false} otherwise.
+ */
+ private boolean
+ hasPrivilege(AuthenticationInfo authInfo) {
+ boolean ret = true;
+ InternalClientConnection tempConn =
+ new InternalClientConnection(authInfo);
+ if (! tempConn.hasPrivilege(Privilege.PROXIED_AUTH, bindOp)) {
+ setCallbackMsg(ERR_SASL_AUTHZID_INSUFFICIENT_PRIVILEGES.get(
+ String.valueOf(authEntry.getDN())));
+ ret = false;
+ }
+ return ret;
+ }
+
+
+ /**
+ * Checks the specified authentication information parameter against the
+ * access control subsystem to see if it has the "proxy" right.
+ *
+ * @param authInfo The authentication information to check access on.
+ * @return {@code true} if the authentication information has
+ * proxy access, {@code false} otherwise.
+ */
+ private boolean
+ hasPermission(AuthenticationInfo authInfo) {
+ boolean ret = true;
+ Entry e = authzEntry;
+ //If the authz entry is null, use the entry associated with the NULL DN.
+ if(e == null) {
+ try {
+ e = DirectoryServer.getEntry(DN.nullDN());
+ } catch (DirectoryException ex) {
+ return false;
+ }
+ }
+ if (AccessControlConfigManager.getInstance().getAccessControlHandler().
+ mayProxy(authInfo.getAuthenticationEntry(), e,
+ bindOp) == false) {
+ setCallbackMsg(ERR_SASL_AUTHZID_INSUFFICIENT_ACCESS.get(
+ String.valueOf(authEntry.getDN())));
+ ret = false;
+ }
+ return ret;
+ }
+
+
+ /**
+ * Sets the callback message to the specified message.
+ *
+ * @param cbMsg The message to set the callback message to.
+ */
+ private void setCallbackMsg(Message cbMsg) {
+ this.cbMsg = cbMsg;
+ }
+
+
+ /**
+ * Process the specified callback array.
+ *
+ *@param callbacks An array of callbacks that need processing.
+ *@throws UnsupportedCallbackException If a callback is not supported.
+ */
+ public void handle(Callback[] callbacks)
+ throws UnsupportedCallbackException {
+ for (Callback callback : callbacks) {
+ if (callback instanceof NameCallback) {
+ nameCallback((NameCallback) callback);
+ } else if (callback instanceof PasswordCallback) {
+ passwordCallback((PasswordCallback) callback);
+ } else if (callback instanceof RealmCallback) {
+ realmCallback((RealmCallback) callback);
+ } else if (callback instanceof AuthorizeCallback) {
+ authorizeCallback((AuthorizeCallback) callback);
+ } else {
+ Message message =
+ INFO_SASL_UNSUPPORTED_CALLBACK.get(mech,
+ String.valueOf(callback));
+ throw new UnsupportedCallbackException(callback,
+ message.toString());
+ }
+ }
+ }
+
+
+ /**
+ * This callback is used to process realm information. It is not used.
+ *
+ * @param callback The realm callback instance to process.
+ */
+ private void realmCallback(RealmCallback callback) {
+ }
+
+
+ /**
+ * This callback is used to process the authorize callback. It is used
+ * during both GSSAPI and DIGEST-MD5 processing. When processing the GSSAPI
+ * mechanism, this is the only callback invoked. When processing the
+ * DIGEST-MD5 mechanism, it is the last callback invoked after the name
+ * and password callbacks respectively.
+ *
+ * @param callback The authorize callback instance to process.
+ */
+ private void authorizeCallback(AuthorizeCallback callback) {
+ String responseAuthzID = callback.getAuthorizationID();
+ //If the authEntry is null, then we are processing a GSSAPI SASL bind,
+ //and first need to try to map the authentication ID to an user entry.
+ //The authEntry is never null, when processing a DIGEST-MD5 SASL bind.
+ if(authEntry == null) {
+ String authid = callback.getAuthenticationID();
+ try {
+ authEntry = identityMapper.getEntryForID(authid);
+ if (authEntry == null) {
+ setCallbackMsg(ERR_SASL_AUTHENTRY_NO_MAPPED_ENTRY.get(authid));
+ callback.setAuthorized(false);
+ return;
+ }
+ } catch (DirectoryException de) {
+ if (debugEnabled()) {
+ TRACER.debugCaught(DebugLogLevel.ERROR, de);
+ }
+ setCallbackMsg(ERR_SASL_CANNOT_MAP_AUTHENTRY.get(authid,
+ de.getMessage()));
+ callback.setAuthorized(false);
+ return;
+ }
+ userName=authid;
+ }
+ if (responseAuthzID.length() == 0) {
+ setCallbackMsg(ERR_SASLDIGESTMD5_EMPTY_AUTHZID.get());
+ callback.setAuthorized(false);
+ return;
+ } else if (!responseAuthzID.equals(userName)) {
+ String lowerAuthzID = toLowerCase(responseAuthzID);
+ //Process the callback differently depending on if the authzid
+ //string begins with the string "dn:" or not.
+ if (lowerAuthzID.startsWith("dn:")) {
+ authzDNCheck(callback);
+ } else {
+ authzIDCheck(callback);
+ }
+ } else {
+ authzEntry = authEntry;
+ callback.setAuthorized(true);
+ }
+ }
+
+
+ /**
+ * Process the specified authorize callback. This method is called if the
+ * callback's authorization ID does not begin with the string "dn:".
+ *
+ * @param callback The authorize callback to process.
+ */
+ private void authzIDCheck(AuthorizeCallback callback) {
+ String authzid = callback.getAuthorizationID();
+ String lowerAuthzID = toLowerCase(authzid);
+ String idStr;
+ callback.setAuthorized(true);
+ if (lowerAuthzID.startsWith("u:")) {
+ idStr = authzid.substring(2);
+ } else {
+ idStr = authzid;
+ }
+ if (idStr.length() == 0) {
+ authzEntry = null;
+ } else {
+ try {
+ authzEntry = identityMapper.getEntryForID(idStr);
+ if (authzEntry == null) {
+ setCallbackMsg(ERR_SASL_AUTHZID_NO_MAPPED_ENTRY.get(authzid));
+ callback.setAuthorized(false);
+ return;
+ }
+ } catch (DirectoryException e) {
+ if (debugEnabled()) {
+ TRACER.debugCaught(DebugLogLevel.ERROR, e);
+ }
+ setCallbackMsg(ERR_SASL_AUTHZID_NO_MAPPED_ENTRY.get(authzid));
+ callback.setAuthorized(false);
+ return;
+ }
+ }
+ if ((authzEntry == null) ||
+ (!authzEntry.getDN().equals(authEntry.getDN()))) {
+ //Create temporary authorization information and run it both
+ //through the privilege and then the access control subsystems.
+ AuthenticationInfo authInfo = new AuthenticationInfo(authEntry,
+ DirectoryServer.isRootDN(authEntry.getDN()));
+ if(!hasPrivilege(authInfo)) {
+ callback.setAuthorized(false);
+ } else {
+ callback.setAuthorized(hasPermission(authInfo));
+ }
+ }
+ }
+
+ /**
+ * Process the specified authorize callback. This method is called if the
+ * callback's authorization ID begins with the string "dn:".
+ *
+ * @param callback The authorize callback to process.
+ */
+ private void authzDNCheck(AuthorizeCallback callback) {
+ String responseAuthzID = callback.getAuthorizationID();
+ DN authzDN;
+ callback.setAuthorized(true);
+ try {
+ authzDN = DN.decode(responseAuthzID.substring(3));
+ } catch (DirectoryException e) {
+ if (debugEnabled()) {
+ TRACER.debugCaught(DebugLogLevel.ERROR, e);
+ }
+ setCallbackMsg(ERR_SASL_AUTHZID_INVALID_DN.get(responseAuthzID,
+ e.getMessageObject()));
+ callback.setAuthorized(false);
+ return;
+ }
+ DN actualAuthzDN =
+ DirectoryServer.getActualRootBindDN(authzDN);
+ if (actualAuthzDN != null) {
+ authzDN = actualAuthzDN;
+ }
+ if (!authzDN.equals(authEntry.getDN())) {
+ if (authzDN.isNullDN()) {
+ authzEntry = null;
+ } else {
+ try {
+ if((authzEntry = DirectoryServer.getEntry(authzDN)) == null) {
+ setCallbackMsg(ERR_SASL_AUTHZID_NO_SUCH_ENTRY.get(
+ String.valueOf(authzDN)));
+ callback.setAuthorized(false);
+ return;
+ }
+ } catch (DirectoryException e) {
+ if (debugEnabled()) {
+ TRACER.debugCaught(DebugLogLevel.ERROR, e);
+ }
+ setCallbackMsg(ERR_SASL_AUTHZID_CANNOT_GET_ENTRY
+ .get(String.valueOf(authzDN), e.getMessageObject()));
+ callback.setAuthorized(false);
+ return;
+ }
+ }
+ AuthenticationInfo authInfo = new AuthenticationInfo(authEntry,
+ DirectoryServer.isRootDN(authEntry.getDN()));
+ if(!hasPrivilege(authInfo)) {
+ callback.setAuthorized(false);
+ } else
+ callback.setAuthorized(hasPermission(authInfo));
+ }
+ }
+
+ /**
+ * Process the specified password callback. Used only for the DIGEST-MD5
+ * SASL mechanism. The password callback is processed after the name
+ * callback.
+ *
+ * @param passwordCallback The password callback to process.
+ */
+ private void passwordCallback(PasswordCallback passwordCallback) {
+ //If there is no authEntry this is an error.
+ if(authEntry == null) {
+ setCallbackMsg(ERR_SASL_NO_MATCHING_ENTRIES.get(userName));
+ return;
+ }
+ //Try to get a clear password to use.
+ List<ByteString> clearPasswords;
+ try {
+ PasswordPolicyState pwPolicyState =
+ new PasswordPolicyState(authEntry, false);
+ clearPasswords = pwPolicyState.getClearPasswords();
+ if ((clearPasswords == null) || clearPasswords.isEmpty()) {
+ setCallbackMsg(
+ ERR_SASL_NO_REVERSIBLE_PASSWORDS.get(mech,
+ String.valueOf(authEntry.getDN())));
+ return;
+ }
+ }
+ catch (Exception e) {
+ if (debugEnabled()) {
+ TRACER.debugCaught(DebugLogLevel.ERROR, e);
+ }
+ setCallbackMsg(ERR_SASL_CANNOT_GET_REVERSIBLE_PASSWORDS.get(
+ String.valueOf(authEntry.getDN()),mech,
+ String.valueOf(e)));
+ return;
+ }
+ //Use the first password.
+ char[] password = clearPasswords.get(0).toString().toCharArray();
+ passwordCallback.setPassword(password);
+ return;
+ }
+
+ /**
+ * Process the specified name callback. Used only for DIGEST-MD5 SASL
+ * mechanism.
+ *
+ * @param nameCallback The name callback to process.
+ */
+ private void nameCallback(NameCallback nameCallback) {
+ userName= nameCallback.getDefaultName();
+ String lowerUserName = toLowerCase(userName);
+ //Process the user name differently if it starts with the string "dn:".
+ if (lowerUserName.startsWith("dn:")) {
+ DN userDN;
+ try {
+ userDN = DN.decode(userName.substring(3));
+ } catch (DirectoryException e) {
+ if (debugEnabled()) {
+ TRACER.debugCaught(DebugLogLevel.ERROR, e);
+ }
+ setCallbackMsg(ERR_SASL_CANNOT_DECODE_USERNAME_AS_DN.get(
+ mech,
+ userName, e.getMessageObject()));
+ return;
+ }
+ if (userDN.isNullDN()) {
+ setCallbackMsg(ERR_SASL_USERNAME_IS_NULL_DN.get(
+ mech));
+ return;
+ }
+ DN rootDN = DirectoryServer.getActualRootBindDN(userDN);
+ if (rootDN != null) {
+ userDN = rootDN;
+ }
+ getAuthEntry(userDN);
+ } else {
+ //The entry name is not a DN, try to map it using the identity
+ //mapper.
+ String entryID = userName;
+ if (lowerUserName.startsWith("u:")) {
+ if (lowerUserName.equals("u:")) {
+ setCallbackMsg(ERR_SASL_ZERO_LENGTH_USERNAME.get(
+ mech,mech));
+ return;
+ }
+ entryID = userName.substring(2);
+ }
+ try {
+ authEntry = identityMapper.getEntryForID(entryID);
+ } catch (DirectoryException e) {
+ if (debugEnabled()) {
+ TRACER.debugCaught(DebugLogLevel.ERROR, e);
+ }
+ setCallbackMsg(ERR_SASLDIGESTMD5_CANNOT_MAP_USERNAME.get(
+ String.valueOf(userName), e.getMessageObject()));
+ return;
+ }
+ }
+ if (authEntry == null) {
+ //The authEntry is null, this is an error. The password callback
+ //will catch this error. There is no way to stop the processing
+ //from the name callback.
+ return;
+ }
+ }
+
+ /**
+ * Try to get a entry from the directory using the specified DN. Used only
+ * for DIGEST-MD5 SASL mechanism.
+ *
+ * @param userDN The DN of the entry to retrieve from the server.
+ */
+ private void getAuthEntry(DN userDN) {
+ Lock readLock = null;
+ for (int i=0; i < 3; i++) {
+ readLock = LockManager.lockRead(userDN);
+ if (readLock != null) {
+ break;
+ }
+ }
+ if (readLock == null) {
+ setCallbackMsg(INFO_SASL_CANNOT_LOCK_ENTRY.get(
+ String.valueOf(userDN)));
+ return;
+ } try {
+ authEntry = DirectoryServer.getEntry(userDN);
+ } catch (DirectoryException e) {
+ if (debugEnabled()) {
+ TRACER.debugCaught(DebugLogLevel.ERROR, e);
+ }
+ setCallbackMsg(ERR_SASL_CANNOT_GET_ENTRY_BY_DN.get(
+ String.valueOf(userDN), SASL_MECHANISM_DIGEST_MD5,
+ e.getMessageObject()));
+ return;
+ } finally {
+ LockManager.unlock(userDN, readLock);
+ }
+ }
+
+ /**
+ * The method performs all GSSAPI processing. It is run as the context of
+ * the login context performed by the GSSAPI mechanism handler. See comments
+ * for processing overview.
+ * @return {@code true} if the authentication processing was successful,
+ * or, {@code false} otherwise.
+ */
+ public Boolean run() {
+ ClientConnection clientConn = bindOp.getClientConnection();
+ //If the SASL server is null then this is the first handshake and the
+ //server needs to be initialized before any processing can be performed.
+ //If the SASL server cannot be created then all processing is abandoned
+ //and INVALID_CREDENTIALS is returned to the client.
+ if(saslServer == null) {
+ try {
+ initSASLServer();
+ } catch (SaslException e) {
+ if (debugEnabled()) {
+ TRACER.debugCaught(DebugLogLevel.ERROR, e);
+ }
+ Message msg =
+ ERR_SASL_CONTEXT_CREATE_ERROR.get(SASL_MECHANISM_DIGEST_MD5,
+ getExceptionMessage(e));
+ clientConn.setSASLAuthStateInfo(null);
+ bindOp.setAuthFailureReason(msg);
+ bindOp.setResultCode(ResultCode.INVALID_CREDENTIALS);
+ return false;
+ }
+ }
+ byte[] clientCredBytes = new byte[0];
+ ASN1OctetString clientCredentials = bindOp.getSASLCredentials();
+ if(clientCredentials != null) {
+ clientCredBytes = clientCredentials.value();
+ }
+ clientConn.setSASLAuthStateInfo(null);
+ try {
+ byte[] responseBytes =
+ evaluateResponse(clientCredBytes);
+ ASN1OctetString responseAuthStr =
+ new ASN1OctetString(responseBytes);
+ //If the bind has not been completed,then
+ //more handshake is needed and SASL_BIND_IN_PROGRESS is returned back
+ //to the client.
+ if (isBindComplete()) {
+ bindOp.setResultCode(ResultCode.SUCCESS);
+ bindOp.setSASLAuthUserEntry(authEntry);
+ AuthenticationInfo authInfo =
+ new AuthenticationInfo(authEntry, authzEntry,
+ mech,
+ DirectoryServer.isRootDN(authEntry.getDN()));
+ bindOp.setAuthenticationInfo(authInfo);
+ //If confidentiality/integrity has been negotiated then
+ //create a SASL security provider and save it in the client
+ //connection. If confidentiality/integrity has not been
+ //negotiated, dispose of the SASL server.
+ if(isConfidentialIntegrity()) {
+ SASLSecurityProvider secProvider =
+ new SASLSecurityProvider(clientConn, mech, this);
+ LDAPClientConnection ldapConn =
+ (LDAPClientConnection) clientConn;
+ ldapConn.setSASLConnectionSecurityProvider(secProvider);
+ } else {
+ dispose();
+ clientConn.setSASLAuthStateInfo(null);
+ }
+ } else {
+ bindOp.setServerSASLCredentials(responseAuthStr);
+ clientConn.setSASLAuthStateInfo(this);
+ bindOp.setResultCode(ResultCode.SASL_BIND_IN_PROGRESS);
+ }
+ } catch (SaslException e) {
+ if (debugEnabled()) {
+ TRACER.debugCaught(DebugLogLevel.ERROR, e);
+ }
+ Message msg =
+ ERR_SASL_PROTOCOL_ERROR.get(mech, getExceptionMessage(e));
+ handleError(msg);
+ return false;
+ }
+ return true;
+ }
+
+
+ /**
+ * Perform the authentication as the specified login context. The specified
+ * bind operation needs to be saved so the callbacks have access to it.
+ * Only used by the GSSAPI mechanism.
+ *
+ * @param loginContext The login context to perform the authentication
+ * as.
+ * @param bindOp The bind operation needed by the callbacks to process the
+ * authentication.
+ */
+ void
+ performAuthentication(LoginContext loginContext, BindOperation bindOp) {
+ this.bindOp = bindOp;
+ try {
+ Subject.doAs(loginContext.getSubject(), this);
+ } catch (PrivilegedActionException e) {
+ if (debugEnabled()) {
+ TRACER.debugCaught(DebugLogLevel.ERROR, e);
+ }
+ Message msg =
+ ERR_SASL_PROTOCOL_ERROR.get(mech, getExceptionMessage(e));
+ handleError(msg);
+ }
+ }
+
+ /**
+ * Process the initial stage of a DIGEST-MD5 SASL bind using the specified
+ * bind operation.
+ *
+ * @param bindOp The bind operation to use in processing.
+ */
+ void
+ evaluateInitialStage(BindOperation bindOp) {
+ this.bindOp = bindOp;
+ ClientConnection clientConn = bindOp.getClientConnection();
+ try {
+ byte[] challengeBuffer = evaluateResponse(new byte[0]);
+ ASN1OctetString challenge = new ASN1OctetString(challengeBuffer);
+ bindOp.setResultCode(ResultCode.SASL_BIND_IN_PROGRESS);
+ bindOp.setServerSASLCredentials(challenge);
+ clientConn.setSASLAuthStateInfo(this);
+ } catch (SaslException e) {
+ if (debugEnabled()) {
+ TRACER.debugCaught(DebugLogLevel.ERROR, e);
+ }
+ Message msg =
+ ERR_SASL_PROTOCOL_ERROR.get(mech,getExceptionMessage(e));
+ handleError(msg);
+ }
+ }
+
+ /**
+ * Evaluate the final stage of a DIGEST-MD5 SASL bind using the specified
+ * bind operation.
+ *
+ * @param bindOp The bind operation to use in processing.
+ */
+ void
+ evaluateFinalStage(BindOperation bindOp) {
+ this.bindOp = bindOp;
+ ASN1OctetString clientCredentials = bindOp.getSASLCredentials();
+ if ((clientCredentials == null) ||
+ (clientCredentials.value().length == 0)) {
+ Message msg =
+ ERR_SASL_NO_CREDENTIALS.get(mech, mech);
+ handleError(msg);
+ return;
+ }
+ ClientConnection clientConn = bindOp.getClientConnection();
+ clientConn.setSASLAuthStateInfo(null);
+ try {
+ byte[] responseBytes =
+ evaluateResponse(clientCredentials.value());
+ ASN1OctetString responseAuthStr =
+ new ASN1OctetString(responseBytes);
+ bindOp.setResultCode(ResultCode.SUCCESS);
+ bindOp.setServerSASLCredentials(responseAuthStr);
+ bindOp.setSASLAuthUserEntry(authEntry);
+ AuthenticationInfo authInfo =
+ new AuthenticationInfo(authEntry, authzEntry,
+ mech,
+ DirectoryServer.isRootDN(authEntry.getDN()));
+ bindOp.setAuthenticationInfo(authInfo);
+ //If confidentiality/integrity has been negotiated, then create a
+ //SASL security provider and save it in the client connection for
+ //use in later processing.
+ if(isConfidentialIntegrity()) {
+ SASLSecurityProvider secProvider =
+ new SASLSecurityProvider(clientConn, mech, this);
+ LDAPClientConnection ldapConn =
+ (LDAPClientConnection) clientConn;
+ ldapConn.setSASLConnectionSecurityProvider(secProvider);
+ } else {
+ dispose();
+ clientConn.setSASLAuthStateInfo(null);
+ }
+ } catch (SaslException e) {
+ if (debugEnabled()) {
+ TRACER.debugCaught(DebugLogLevel.ERROR, e);
+ }
+ Message msg =
+ ERR_SASL_PROTOCOL_ERROR.get(mech, getExceptionMessage(e));
+ handleError(msg);
+ }
+ }
+}
\ No newline at end of file
diff --git a/opends/src/server/org/opends/server/extensions/SASLSecurityProvider.java b/opends/src/server/org/opends/server/extensions/SASLSecurityProvider.java
new file mode 100644
index 0000000..0ee7e0c
--- /dev/null
+++ b/opends/src/server/org/opends/server/extensions/SASLSecurityProvider.java
@@ -0,0 +1,457 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at
+ * trunk/opends/resource/legal-notices/OpenDS.LICENSE
+ * or https://OpenDS.dev.java.net/OpenDS.LICENSE.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at
+ * trunk/opends/resource/legal-notices/OpenDS.LICENSE. If applicable,
+ * add the following below this CDDL HEADER, with the fields enclosed
+ * by brackets "[]" replaced with your own identifying information:
+ * Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ *
+ *
+ * Copyright 2008 Sun Microsystems, Inc.
+ */
+
+package org.opends.server.extensions;
+
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.nio.channels.SelectionKey;
+import java.nio.channels.Selector;
+import java.nio.channels.SocketChannel;
+import java.util.Iterator;
+import javax.security.sasl.Sasl;
+import javax.security.sasl.SaslException;
+import org.opends.server.api.ClientConnection;
+import org.opends.server.api.ConnectionSecurityProvider;
+import org.opends.server.config.ConfigEntry;
+import org.opends.server.config.ConfigException;
+import org.opends.server.loggers.debug.DebugTracer;
+import org.opends.server.protocols.ldap.LDAPClientConnection;
+import org.opends.server.types.DebugLogLevel;
+import org.opends.server.types.DirectoryException;
+import org.opends.server.types.DisconnectReason;
+import org.opends.server.types.InitializationException;
+import static org.opends.server.loggers.debug.DebugLogger.*;
+
+/**
+ * This class provides an implementation of a connection security provider that
+ * provides SASL confidentiality/integrity between the server and client.
+ *
+ */
+public class SASLSecurityProvider extends ConnectionSecurityProvider {
+
+ // The tracer object for the debug logger.
+ private static final DebugTracer TRACER = getTracer();
+
+ //The client connection associated with this provider.
+ private ClientConnection connection;
+
+ //The socket channel associated with this provider.
+ private SocketChannel sockChannel;
+
+ //The SASL context associated with the provider
+ private SASLContext saslContext;
+
+ //The number of bytes in the length buffer.
+ private final int lengthSize = 4;
+
+ //A byte buffer used to hold the length of the clear buffer.
+ private ByteBuffer lengthBuf = ByteBuffer.allocate(lengthSize);
+
+ //The SASL mechanism name.
+ private String name;
+
+ /**
+ * Create a SASL security provider with the specified parameters that is
+ * capable of processing a confidentiality/integrity SASL connection.
+ *
+ * @param connection The client connection to read/write the bytes.
+ * @param name The SASL mechanism name.
+ * @param saslContext The SASL context to process the data through.
+ */
+ public SASLSecurityProvider(ClientConnection connection, String name,
+ SASLContext saslContext) {
+ super();
+ this.connection = connection;
+ this.name = name;
+ this.saslContext = saslContext;
+ this.sockChannel = ((LDAPClientConnection) connection).getSocketChannel();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void disconnect(boolean connectionValid) {
+ this.saslContext.dispose();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void finalizeConnectionSecurityProvider() {
+
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public int getClearBufferSize() {
+ return 0;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public int getEncodedBufferSize() {
+ return 0;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public String getSecurityMechanismName() {
+ return name;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void initializeConnectionSecurityProvider(ConfigEntry configEntry)
+ throws ConfigException, InitializationException {
+ this.connection = null;
+ this.sockChannel = null;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public boolean isSecure() {
+ return true;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public ConnectionSecurityProvider newInstance(ClientConnection clientConn,
+ SocketChannel socketChannel) {
+ return new SASLSecurityProvider(clientConn, null,null);
+ }
+
+ /**
+ * Return the clear buffer length as determined by processing the first
+ * 4 bytes of the specified buffer.
+ *
+ * @param byteBuf The buffer to examine the first 4 bytes of.
+ * @return The size of the clear buffer.
+ */
+ private int getBufLength(ByteBuffer byteBuf) {
+ int answer = 0;
+ byte[] buf = byteBuf.array();
+
+ for (int i = 0; i < lengthSize; i++) {
+ answer <<= 8;
+ answer |= ((int)buf[i] & 0xff);
+ }
+ return answer;
+ }
+
+ /**
+ * Read from the socket channel into the specified byte buffer the
+ * number of bytes specified in the total parameter.
+ *
+ * @param byteBuf The byte buffer to put the bytes in.
+ * @param total The total number of bytes to read from the socket channel.
+ * @return The number of bytes read, 0 or -1.
+ * @throws IOException If an error occurred reading the socket channel.
+ */
+ private int readAll(ByteBuffer byteBuf, int total) throws IOException {
+ int count = 0;
+ while(sockChannel.isOpen() && total > 0) {
+ count = sockChannel.read(byteBuf);
+ if(count == -1)
+ return -1;
+ if(count == 0)
+ return 0;
+ total -= count;
+ }
+ if(total > 0)
+ return -1;
+ else
+ return byteBuf.position();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public boolean readData() throws DirectoryException {
+ int recvBufSize = saslContext.getBufSize(Sasl.MAX_BUFFER);
+ try {
+ while(true) {
+ lengthBuf.clear();
+ int readResult = readAll(lengthBuf, lengthSize);
+ if (readResult == -1) {
+ //Client connection has been closed. Disconnect and return.
+ connection.disconnect(DisconnectReason.CLIENT_DISCONNECT,
+ false, null);
+ return false;
+ } else if(readResult == 0)
+ return true;
+ int bufLength = getBufLength(lengthBuf);
+ if(bufLength > recvBufSize) {
+ connection.disconnect(DisconnectReason.CLIENT_DISCONNECT,
+ false, null);
+ return false;
+ }
+ ByteBuffer readBuf = ByteBuffer.allocate(bufLength);
+ readResult = readAll(readBuf, bufLength);
+ if (readResult == -1) {
+ //Client connection has been closed. Disconnect and return.
+ connection.disconnect(DisconnectReason.CLIENT_DISCONNECT,
+ false, null);
+ return false;
+ } else if (readResult == 0)
+ return true;
+ byte[] inBytes = readBuf.array();
+ byte[] clearBytes =
+ saslContext.unwrap(inBytes, 0, inBytes.length);
+ ByteBuffer clearBuffer = ByteBuffer.wrap(clearBytes);
+ if (!connection.processDataRead(clearBuffer))
+ return false;
+ }
+ } catch (SaslException saslEx) {
+ if (debugEnabled()) {
+ TRACER.debugCaught(DebugLogLevel.ERROR, saslEx);
+ }
+ //Error trying to unwrap the data.
+ connection.disconnect(DisconnectReason.IO_ERROR, false, null);
+ return false;
+ } catch (IOException ioe) {
+ // An error occurred while trying to communicate with the client.
+ // Disconnect and return.
+ if (debugEnabled()) {
+ TRACER.debugCaught(DebugLogLevel.ERROR, ioe);
+ }
+ connection.disconnect(DisconnectReason.IO_ERROR, false, null);
+ return false;
+ }
+ }
+
+ /**
+ * Creates a buffer suitable to send to the client using the specified
+ * clear byte array, offset and length of the bytes to wrap.
+ *
+ * @param clearBytes The clear byte array to send to the client.
+ * @param offSet An offset into the byte array to start the wrap at.
+ * @param len The length of the bytes to wrap in the byte array.
+ * @throws SaslException If the wrap of the bytes fails.
+ */
+ private ByteBuffer
+ createSendBuffer(byte[] clearBytes, int offSet, int len)
+ throws SaslException {
+ byte[] wrapBytes = saslContext.wrap(clearBytes, offSet, len);
+ byte[] outBuf = new byte[wrapBytes.length + lengthSize];
+ writeBufLen(outBuf, wrapBytes.length);
+ System.arraycopy(wrapBytes, 0, outBuf, lengthSize, wrapBytes.length);
+ return ByteBuffer.wrap(outBuf);
+ }
+
+ /**
+ * Writes the specified len parameter into the buffer in a form that can
+ * be sent over a network to the client.
+ *
+ * @param buf The buffer to hold the length bytes.
+ * @param len The length to encode.
+ */
+ private void writeBufLen(byte[] buf, int len) {
+ for (int i = 3; i >= 0; i--) {
+ buf[i] = (byte)(len & 0xff);
+ len >>>= 8;
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override()
+ public boolean writeData(ByteBuffer clearData) {
+ int maxSendBufSize = saslContext.getBufSize(Sasl.RAW_SEND_SIZE);
+ int clearLength = clearData.limit();
+ try {
+ if(clearLength < maxSendBufSize) {
+ ByteBuffer sendBuffer =
+ createSendBuffer(clearData.array(),0,clearLength);
+ return writeChannel(sendBuffer);
+ } else {
+ byte[] clearBytes = clearData.array();
+ for(int i=0; i < clearLength; i += maxSendBufSize) {
+ int totLength = (clearLength - i) < maxSendBufSize ?
+ (clearLength - i) : maxSendBufSize;
+ ByteBuffer sendBuffer =
+ createSendBuffer(clearBytes, i, totLength);
+ if(!writeChannel(sendBuffer))
+ return false;
+ }
+ }
+ } catch (SaslException e) {
+ if (debugEnabled()) {
+ TRACER.debugCaught(DebugLogLevel.ERROR, e);
+ }
+ connection.disconnect(DisconnectReason.IO_ERROR, false, null);
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * Write the specified byte buffer to the socket channel.
+ *
+ * @param buffer The byte buffer to write to the socket channel.
+ * @return {@code true} if the byte buffer was successfully written to the
+ * socket channel, or, {@code false} if not.
+ */
+ private boolean writeChannel(ByteBuffer buffer) {
+ try {
+ while (buffer.hasRemaining()) {
+ int bytesWritten = sockChannel.write(buffer);
+ if (bytesWritten < 0) {
+ connection.disconnect(DisconnectReason.CLIENT_DISCONNECT,
+ false, null);
+ return false;
+ } else if (bytesWritten == 0) {
+ return writeWithTimeout(buffer);
+ }
+ }
+ } catch (IOException ioe) {
+ if (debugEnabled()) {
+ TRACER.debugCaught(DebugLogLevel.ERROR, ioe);
+ }
+ connection.disconnect(DisconnectReason.IO_ERROR, false, null);
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * Writes the specified byte buffer parameter to the socket channel waiting
+ * for a period of time if the buffer cannot be written immediately.
+ *
+ * @param buffer The byte buffer to write to the channel.
+ * @return {@code true} if the bytes were sent, or, {@code false} otherwise.
+ * @throws IOException If an IO error occurs while writing the bytes.
+ */
+ private boolean writeWithTimeout(ByteBuffer buffer) throws IOException {
+ long startTime = System.currentTimeMillis();
+ long waitTime = connection.getMaxBlockedWriteTimeLimit();
+ if (waitTime <= 0) {
+ // We won't support an infinite time limit, so fall back to using
+ // five minutes, which is a very long timeout given that we're
+ // blocking a worker thread.
+ waitTime = 300000L;
+ }
+ long stopTime = startTime + waitTime;
+ Selector selector = connection.getWriteSelector();
+ if (selector == null) {
+ // The client connection does not provide a selector, so we'll
+ // fall back to a more inefficient way that will work without a
+ // selector.
+ while (buffer.hasRemaining() &&
+ (System.currentTimeMillis() < stopTime)) {
+ if (sockChannel.write(buffer) < 0) {
+ // The client connection has been closed
+ connection.disconnect(DisconnectReason.CLIENT_DISCONNECT,
+ false, null);
+ return false;
+ }
+ }
+ if (buffer.hasRemaining()) {
+ // If we've gotten here, then the write timed out.
+ // Terminate the client connection.
+ connection.disconnect(DisconnectReason.IO_TIMEOUT, false, null);
+ return false;
+ }
+ return true;
+ }
+ // Register with the selector for handling write operations.
+ SelectionKey key =
+ sockChannel.register(selector, SelectionKey.OP_WRITE);
+ try
+ {
+ selector.select(waitTime);
+ while (buffer.hasRemaining())
+ {
+ long currentTime = System.currentTimeMillis();
+ if (currentTime >= stopTime) {
+ // We've been blocked for too long
+ connection.disconnect(DisconnectReason.IO_TIMEOUT,
+ false, null);
+ return false;
+ }
+ else {
+ waitTime = stopTime - currentTime;
+ }
+ Iterator<SelectionKey> iterator =
+ selector.selectedKeys().iterator();
+ while (iterator.hasNext()) {
+ SelectionKey k = iterator.next();
+ if (k.isWritable()) {
+ int bytesWritten = sockChannel.write(buffer);
+ if (bytesWritten < 0) {
+ // The client connection has been closed.
+ connection.disconnect(
+ DisconnectReason.CLIENT_DISCONNECT,
+ false, null);
+ return false;
+ }
+ iterator.remove();
+ }
+ }
+ if (buffer.hasRemaining()) {
+ selector.select(waitTime);
+ }
+ }
+ return true;
+ } finally {
+ if (key.isValid()) {
+ key.cancel();
+ selector.selectNow();
+ }
+ }
+ }
+
+ /**
+ * Return if the underlying SASL context is active or not. The SASL context
+ * may still be negotiating a multi-stage SASL bind and is not ready to
+ * process confidentiality or integrity data yet.
+ *
+ * @return {@code true} if the underlying SASL context is active or ready
+ * to process confidentiality/integrity messages, or, {@code false}
+ * if not.
+ */
+
+ public boolean isActive() {
+ return saslContext.isBindComplete();
+ }
+}
diff --git a/opends/src/server/org/opends/server/extensions/TLSConnectionSecurityProvider.java b/opends/src/server/org/opends/server/extensions/TLSConnectionSecurityProvider.java
index 20f857b..7b26abc 100644
--- a/opends/src/server/org/opends/server/extensions/TLSConnectionSecurityProvider.java
+++ b/opends/src/server/org/opends/server/extensions/TLSConnectionSecurityProvider.java
@@ -339,6 +339,15 @@
/**
* {@inheritDoc}
*/
+ @Override
+ public boolean isActive() {
+ //This provider is always active.
+ return true;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
@Override()
public ConnectionSecurityProvider newInstance(ClientConnection
clientConnection,
diff --git a/opends/src/server/org/opends/server/protocols/ldap/LDAPClientConnection.java b/opends/src/server/org/opends/server/protocols/ldap/LDAPClientConnection.java
index 60e3443..c4e0248 100644
--- a/opends/src/server/org/opends/server/protocols/ldap/LDAPClientConnection.java
+++ b/opends/src/server/org/opends/server/protocols/ldap/LDAPClientConnection.java
@@ -59,7 +59,6 @@
import org.opends.server.core.SearchOperation;
import org.opends.server.core.SearchOperationBasis;
import org.opends.server.core.UnbindOperationBasis;
-import org.opends.server.core.networkgroups.NetworkGroup;
import org.opends.server.extensions.NullConnectionSecurityProvider;
import org.opends.server.extensions.TLSCapableConnection;
import org.opends.server.extensions.TLSConnectionSecurityProvider;
@@ -220,7 +219,9 @@
// if StartTLS is requested.
private TLSConnectionSecurityProvider tlsSecurityProvider;
-
+ //The SASL connection provider used if confidentiality/integrity is negotiated
+ //during a SASL bind (GSSAPI and DIGEST-MD5 only).
+ private ConnectionSecurityProvider saslSecurityProvider;
/**
* Creates a new LDAP client connection with the provided information.
@@ -237,9 +238,6 @@
this.connectionHandler = connectionHandler;
- if (connectionHandler.isAdminConnectionHandler()) {
- setNetworkGroup(NetworkGroup.getAdminNetworkGroup());
- }
this.clientChannel = clientChannel;
this.securityProvider = null;
this.clearSecurityProvider = null;
@@ -479,12 +477,27 @@
*/
public ConnectionSecurityProvider getConnectionSecurityProvider()
{
- return securityProvider;
+ if(saslSecurityProvider != null && saslSecurityProvider.isActive())
+ securityProvider = saslSecurityProvider;
+ return securityProvider;
}
/**
+ * Set the security provider to be used to process SASL (DIGEST-MD5, GSSAPI)
+ * confidentiality/integrity messages.
+ *
+ * @param secProvider The security provider to use.
+ */
+ public void
+ setSASLConnectionSecurityProvider(ConnectionSecurityProvider secProvider) {
+ saslSecurityProvider = secProvider;
+ }
+
+
+
+ /**
* Specifies the connection security provider for this client connection.
*
* @param securityProvider The connection security provider to use for
diff --git a/opends/src/server/org/opends/server/util/ServerConstants.java b/opends/src/server/org/opends/server/util/ServerConstants.java
index 954c585..fdbe328 100644
--- a/opends/src/server/org/opends/server/util/ServerConstants.java
+++ b/opends/src/server/org/opends/server/util/ServerConstants.java
@@ -1873,6 +1873,12 @@
"Kerberos Confidentiality";
+ /**
+ * The name of the default protocol used.
+ */
+ public static final String SASL_DEFAULT_PROTOCOL = "ldap";
+
+
/**
* The name of the security mechanism that will be used for connections
@@ -1934,6 +1940,33 @@
/**
+ * The name of the security mechanism that will be used for connections whose
+ * communication only SASL authenticated.
+ */
+ public static final String SASL_MECHANISM_AUTHENTICATION_ONLY =
+ "none";
+
+
+
+ /**
+ * The name of the security mechanism that will be used for connections whose
+ * communication is protected using the confidentiality features SASL.
+ */
+ public static final String SASL_MECHANISM_CONFIDENTIALITY =
+ "confidentiality";
+
+
+
+ /**
+ * The name of the security mechanism that will be used for connections whose
+ * communication is verified using SASL integrity.
+ */
+ public static final String SASL_MECHANISM_INTEGRITY =
+ "integrity";
+
+
+
+ /**
* The OID for the account usable request and response controls.
*/
public static final String OID_ACCOUNT_USABLE_CONTROL =
diff --git a/opends/tests/unit-tests-testng/src/server/org/opends/server/extensions/DigestMD5SASLMechanismHandlerTestCase.java b/opends/tests/unit-tests-testng/src/server/org/opends/server/extensions/DigestMD5SASLMechanismHandlerTestCase.java
index a0f63dc..4488593 100644
--- a/opends/tests/unit-tests-testng/src/server/org/opends/server/extensions/DigestMD5SASLMechanismHandlerTestCase.java
+++ b/opends/tests/unit-tests-testng/src/server/org/opends/server/extensions/DigestMD5SASLMechanismHandlerTestCase.java
@@ -31,6 +31,7 @@
import java.util.ArrayList;
import java.util.List;
+import org.testng.annotations.AfterClass;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.DataProvider;
import org.testng.annotations.Test;
@@ -79,9 +80,25 @@
throws Exception
{
TestCaseUtils.startServer();
+
+
+ TestCaseUtils.dsconfig(
+ "set-sasl-mechanism-handler-prop",
+ "--handler-name", "DIGEST-MD5",
+ "--set", "server-fqdn:" + "127.0.0.1");
+
}
+ @AfterClass
+ public void tearDown() throws Exception {
+
+ TestCaseUtils.dsconfig(
+ "set-sasl-mechanism-handler-prop",
+ "--handler-name", "DIGEST-MD5",
+ "--remove", "server-fqdn:" + "127.0.0.1");
+ }
+
/**
* Retrieves a set of invvalid configuration entries.
@@ -234,7 +251,6 @@
"-o", "mech=DIGEST-MD5",
"-o", "authid=u:test.user",
"-o", "authzid=u:test.user",
- "-o", "realm=o=test",
"-w", "password",
"-b", "",
"-s", "base",
@@ -286,7 +302,6 @@
"-o", "mech=DIGEST-MD5",
"-o", "authid=dn:uid=test.user,o=test",
"-o", "authzid=dn:uid=test.user,o=test",
- "-o", "realm=o=test",
"-w", "password",
"-b", "",
"-s", "base",
@@ -338,7 +353,6 @@
"-o", "mech=DIGEST-MD5",
"-o", "authid=u:test.user",
"-o", "authzid=u:test.user",
- "-o", "realm=o=test",
"-w", "wrongpassword",
"-b", "",
"-s", "base",
@@ -390,7 +404,6 @@
"-o", "mech=DIGEST-MD5",
"-o", "authid=dn:uid=test.user,o=test",
"-o", "authzid=dn:uid=test.user,o=test",
- "-o", "realm=o=test",
"-w", "wrongpassword",
"-b", "",
"-s", "base",
@@ -440,7 +453,6 @@
"-o", "mech=DIGEST-MD5",
"-o", "authid=u:test.user",
"-o", "authzid=u:test.user",
- "-o", "realm=o=test",
"-w", "password",
"-b", "",
"-s", "base",
@@ -490,7 +502,6 @@
"-o", "mech=DIGEST-MD5",
"-o", "authid=dn:uid=test.user,o=test",
"-o", "authzid=dn:uid=test.user,o=test",
- "-o", "realm=o=test",
"-w", "password",
"-b", "",
"-s", "base",
@@ -540,7 +551,6 @@
"-o", "mech=DIGEST-MD5",
"-o", "authid=dn:invaliddn",
"-o", "authzid=dn:invaliddn",
- "-o", "realm=o=test",
"-w", "password",
"-b", "",
"-s", "base",
@@ -590,7 +600,6 @@
"-o", "mech=DIGEST-MD5",
"-o", "authid=u:doesntexist",
"-o", "authzid=u:doesntexist",
- "-o", "realm=o=test",
"-w", "password",
"-b", "",
"-s", "base",
@@ -640,7 +649,6 @@
"-o", "mech=DIGEST-MD5",
"-o", "authid=dn:uid=doesntexist,o=test",
"-o", "authzid=dn:uid=doesntexist,o=test",
- "-o", "realm=o=test",
"-w", "password",
"-b", "",
"-s", "base",
@@ -671,7 +679,6 @@
"-o", "mech=DIGEST-MD5",
"-o", "authid=u:",
"-o", "authzid=u:",
- "-o", "realm=o=test",
"-w", "",
"-b", "",
"-s", "base",
@@ -702,7 +709,6 @@
"-o", "mech=DIGEST-MD5",
"-o", "authid=dn:",
"-o", "authzid=dn:",
- "-o", "realm=o=test",
"-w", "",
"-b", "",
"-s", "base",
@@ -733,7 +739,6 @@
"-o", "mech=DIGEST-MD5",
"-o", "authid=",
"-o", "authzid=",
- "-o", "realm=o=test",
"-w", "",
"-b", "",
"-s", "base",
@@ -778,7 +783,6 @@
"-o", "mech=DIGEST-MD5",
"-o", "authid=dn:uid=test.user,o=test",
"-o", "authzid=",
- "-o", "realm=o=test",
"-w", "password",
"-b", "",
"-s", "base",
@@ -808,7 +812,6 @@
"-o", "mech=DIGEST-MD5",
"-o", "authid=dn:cn=Directory Manager",
"-o", "authzid=dn:cn=Directory Manager",
- "-o", "realm=o=test",
"-w", "password",
"-b", "",
"-s", "base",
@@ -860,7 +863,6 @@
"-o", "mech=DIGEST-MD5",
"-o", "authid=dn:cn=Second Root DN",
"-o", "authzid=dn:cn=Second Root DN",
- "-o", "realm=o=test",
"-w", "password",
"-b", "",
"-s", "base",
@@ -909,7 +911,6 @@
"-o", "mech=DIGEST-MD5",
"-o", "authid=dn:uid=test.user,o=test",
"-o", "authzid=dn:uid=nonexistent,o=test",
- "-o", "realm=o=test",
"-w", "password",
"-b", "",
"-s", "base",
@@ -954,7 +955,6 @@
"-o", "mech=DIGEST-MD5",
"-o", "authid=dn:uid=test.user,o=test",
"-o", "authzid=u:nonexistent",
- "-o", "realm=o=test",
"-w", "password",
"-b", "",
"-s", "base",
@@ -999,7 +999,6 @@
"-o", "mech=DIGEST-MD5",
"-o", "authid=dn:uid=test.user,o=test",
"-o", "authzid=dn:malformed",
- "-o", "realm=o=test",
"-w", "password",
"-b", "",
"-s", "base",
diff --git a/opends/tests/unit-tests-testng/src/server/org/opends/server/tools/LDAPAuthenticationHandlerTestCase.java b/opends/tests/unit-tests-testng/src/server/org/opends/server/tools/LDAPAuthenticationHandlerTestCase.java
index d77c72c..aebf22d 100644
--- a/opends/tests/unit-tests-testng/src/server/org/opends/server/tools/LDAPAuthenticationHandlerTestCase.java
+++ b/opends/tests/unit-tests-testng/src/server/org/opends/server/tools/LDAPAuthenticationHandlerTestCase.java
@@ -29,7 +29,9 @@
import java.io.File;
+import java.net.InetAddress;
import java.net.Socket;
+import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
@@ -41,6 +43,7 @@
import org.opends.server.TestCaseUtils;
import org.opends.messages.Message;
+import org.opends.server.admin.std.server.DigestMD5SASLMechanismHandlerCfg;
import org.opends.server.api.SASLMechanismHandler;
import org.opends.server.controls.PasswordPolicyRequestControl;
import org.opends.server.core.AddOperation;
@@ -63,6 +66,7 @@
public class LDAPAuthenticationHandlerTestCase
extends ToolsTestCase
{
+ String hostname;
/**
* Ensures that the Directory Server is running.
*
@@ -73,6 +77,7 @@
throws Exception
{
TestCaseUtils.startServer();
+ getFQDN();
}
@@ -1412,11 +1417,9 @@
saslProperties.put("authid", propList);
propList = new ArrayList<String>();
- propList.add("o=test");
- saslProperties.put("realm", propList);
LDAPAuthenticationHandler authHandler =
- new LDAPAuthenticationHandler(r, w, "localhost", messageID);
+ new LDAPAuthenticationHandler(r, w, this.hostname, messageID);
authHandler.doSASLBind(new ASN1OctetString(),
new ASN1OctetString("password"),
"DIGEST-MD5", saslProperties, requestControls,
@@ -1476,11 +1479,9 @@
saslProperties.put("authid", propList);
propList = new ArrayList<String>();
- propList.add("o=test");
- saslProperties.put("realm", propList);
-
+
LDAPAuthenticationHandler authHandler =
- new LDAPAuthenticationHandler(r, w, "localhost", messageID);
+ new LDAPAuthenticationHandler(r, w, this.hostname, messageID);
authHandler.doSASLBind(new ASN1OctetString(),
new ASN1OctetString("password"),
"DIGEST-MD5", saslProperties, requestControls,
@@ -1793,15 +1794,11 @@
saslProperties.put("authid", propList);
propList = new ArrayList<String>();
- propList.add("o=test");
- saslProperties.put("realm", propList);
-
- propList = new ArrayList<String>();
propList.add("auth");
saslProperties.put("qop", propList);
LDAPAuthenticationHandler authHandler =
- new LDAPAuthenticationHandler(r, w, "localhost", messageID);
+ new LDAPAuthenticationHandler(r, w, this.hostname, messageID);
authHandler.doSASLBind(new ASN1OctetString(),
new ASN1OctetString("password"),
"DIGEST-MD5", saslProperties, requestControls,
@@ -2412,11 +2409,9 @@
saslProperties.put("authid", propList);
propList = new ArrayList<String>();
- propList.add("o=test");
- saslProperties.put("realm", propList);
LDAPAuthenticationHandler authHandler =
- new LDAPAuthenticationHandler(r, w, "localhost", messageID);
+ new LDAPAuthenticationHandler(r, w, this.hostname, messageID);
authHandler.doSASLBind(new ASN1OctetString(),
new ASN1OctetString("password"),
"DIGEST-MD5", saslProperties, requestControls,
@@ -4154,11 +4149,9 @@
saslProperties.put("authid", propList);
propList = new ArrayList<String>();
- propList.add("o=test");
- saslProperties.put("realm", propList);
LDAPAuthenticationHandler authHandler =
- new LDAPAuthenticationHandler(r, w, "localhost", messageID);
+ new LDAPAuthenticationHandler(r, w, this.hostname, messageID);
authHandler.doSASLBind(new ASN1OctetString(),
new ASN1OctetString("password"),
"DIGEST-MD5", saslProperties, requestControls,
@@ -4290,5 +4283,14 @@
s.close();
}
+
+ private void getFQDN() {
+ try {
+ this.hostname = InetAddress.getLocalHost().getCanonicalHostName();
+ } catch(UnknownHostException ex) {
+ this.hostname = "localhost";
+ }
+ }
+
}
diff --git a/opends/tests/unit-tests-testng/src/server/org/opends/server/tools/LDAPCompareTestCase.java b/opends/tests/unit-tests-testng/src/server/org/opends/server/tools/LDAPCompareTestCase.java
index d9afd2b..75c8a45 100644
--- a/opends/tests/unit-tests-testng/src/server/org/opends/server/tools/LDAPCompareTestCase.java
+++ b/opends/tests/unit-tests-testng/src/server/org/opends/server/tools/LDAPCompareTestCase.java
@@ -32,6 +32,7 @@
import java.io.FileWriter;
import java.util.ArrayList;
+import org.testng.annotations.AfterClass;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.DataProvider;
import org.testng.annotations.Test;
@@ -44,7 +45,7 @@
import org.opends.server.types.OperatingSystem;
import org.opends.server.types.ResultCode;
import org.opends.server.util.Base64;
-
+import org.opends.server.tools.dsconfig.DSConfig;
import static org.testng.Assert.*;
import static org.opends.server.util.ServerConstants.*;
@@ -77,6 +78,12 @@
{
TestCaseUtils.startServer();
+
+ TestCaseUtils.dsconfig(
+ "set-sasl-mechanism-handler-prop",
+ "--handler-name", "DIGEST-MD5",
+ "--set", "server-fqdn:" + "127.0.0.1");
+
File pwFile = File.createTempFile("valid-bind-password-", ".txt");
pwFile.deleteOnExit();
FileWriter fileWriter = new FileWriter(pwFile);
@@ -92,6 +99,14 @@
invalidPasswordFile = pwFile.getAbsolutePath();
}
+ @AfterClass
+ public void tearDown() throws Exception {
+
+ TestCaseUtils.dsconfig(
+ "set-sasl-mechanism-handler-prop",
+ "--handler-name", "DIGEST-MD5",
+ "--remove", "server-fqdn:" + "127.0.0.1");
+ }
/**
@@ -883,6 +898,7 @@
"sn: User",
"cn: Test User",
"ds-privilege-name: bypass-acl",
+ "ds-privilege-name: proxied-auth",
"userPassword: password",
"ds-pwp-password-policy-dn: cn=Clear UserPassword Policy," +
"cn=Password Policies,cn=config");
@@ -894,7 +910,7 @@
e.getOperationalAttributes());
assertEquals(addOperation.getResultCode(), ResultCode.SUCCESS);
-
+
String[] args =
{
"-h", "127.0.0.1",
@@ -902,7 +918,6 @@
"-o", "mech=DIGEST-MD5",
"-o", "authid=u:test.user",
"-o", "authzid=u:test.user",
- "-o", "realm=o=test",
"-w", "password",
"--noPropertiesFile",
"givenName:Test",
diff --git a/opends/tests/unit-tests-testng/src/server/org/opends/server/tools/LDAPModifyTestCase.java b/opends/tests/unit-tests-testng/src/server/org/opends/server/tools/LDAPModifyTestCase.java
index 5ba2057..a746aa2 100644
--- a/opends/tests/unit-tests-testng/src/server/org/opends/server/tools/LDAPModifyTestCase.java
+++ b/opends/tests/unit-tests-testng/src/server/org/opends/server/tools/LDAPModifyTestCase.java
@@ -32,6 +32,7 @@
import java.io.FileWriter;
import java.util.ArrayList;
+import org.testng.annotations.AfterClass;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.DataProvider;
import org.testng.annotations.Test;
@@ -78,7 +79,12 @@
throws Exception
{
TestCaseUtils.startServer();
-
+
+ TestCaseUtils.dsconfig(
+ "set-sasl-mechanism-handler-prop",
+ "--handler-name", "DIGEST-MD5",
+ "--set", "server-fqdn:" + "127.0.0.1");
+
File pwFile = File.createTempFile("valid-bind-password-", ".txt");
pwFile.deleteOnExit();
FileWriter fileWriter = new FileWriter(pwFile);
@@ -100,6 +106,14 @@
}
+ @AfterClass
+ public void tearDown() throws Exception {
+
+ TestCaseUtils.dsconfig(
+ "set-sasl-mechanism-handler-prop",
+ "--handler-name", "DIGEST-MD5",
+ "--remove", "server-fqdn:" + "127.0.0.1");
+ }
/**
* Retrieves sets of invalid arguments that may not be used to initialize
@@ -824,7 +838,6 @@
"-o", "mech=DIGEST-MD5",
"-o", "authid=u:test.user",
"-o", "authzid=u:test.user",
- "-o", "realm=o=test",
"-w", "password",
"--noPropertiesFile",
"-f", modifyFilePath
diff --git a/opends/tests/unit-tests-testng/src/server/org/opends/server/tools/LDAPSearchTestCase.java b/opends/tests/unit-tests-testng/src/server/org/opends/server/tools/LDAPSearchTestCase.java
index 11250af..b385a40 100644
--- a/opends/tests/unit-tests-testng/src/server/org/opends/server/tools/LDAPSearchTestCase.java
+++ b/opends/tests/unit-tests-testng/src/server/org/opends/server/tools/LDAPSearchTestCase.java
@@ -32,6 +32,7 @@
import java.io.FileWriter;
import java.util.ArrayList;
+import org.testng.annotations.AfterClass;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.DataProvider;
import org.testng.annotations.Test;
@@ -74,7 +75,12 @@
throws Exception
{
TestCaseUtils.startServer();
-
+
+ TestCaseUtils.dsconfig(
+ "set-sasl-mechanism-handler-prop",
+ "--handler-name", "DIGEST-MD5",
+ "--set", "server-fqdn:" + "127.0.0.1");
+
File pwFile = File.createTempFile("valid-bind-password-", ".txt");
pwFile.deleteOnExit();
FileWriter fileWriter = new FileWriter(pwFile);
@@ -91,6 +97,14 @@
}
+ @AfterClass
+ public void tearDown() throws Exception {
+
+ TestCaseUtils.dsconfig(
+ "set-sasl-mechanism-handler-prop",
+ "--handler-name", "DIGEST-MD5",
+ "--remove", "server-fqdn:" + "127.0.0.1");
+ }
/**
* Retrieves sets of invalid arguments that may not be used to initialize
@@ -1054,7 +1068,6 @@
"-o", "mech=DIGEST-MD5",
"-o", "authid=u:test.user",
"-o", "authzid=u:test.user",
- "-o", "realm=o=test",
"-w", "password",
"-b", "",
"-s", "base",
diff --git a/opends/tests/unit-tests-testng/src/server/org/opends/server/types/PrivilegeTestCase.java b/opends/tests/unit-tests-testng/src/server/org/opends/server/types/PrivilegeTestCase.java
index 9a06dfc..e174069 100644
--- a/opends/tests/unit-tests-testng/src/server/org/opends/server/types/PrivilegeTestCase.java
+++ b/opends/tests/unit-tests-testng/src/server/org/opends/server/types/PrivilegeTestCase.java
@@ -114,7 +114,12 @@
throws Exception
{
TestCaseUtils.startServer();
-
+
+ TestCaseUtils.dsconfig(
+ "set-sasl-mechanism-handler-prop",
+ "--handler-name", "DIGEST-MD5",
+ "--set", "server-fqdn:" + "127.0.0.1");
+
TestCaseUtils.initializeTestBackend(true);
TestCaseUtils.addEntries(
"dn: cn=Unprivileged Root,cn=Root DNs,cn=config",
@@ -303,6 +308,11 @@
public void cleanUp()
throws Exception
{
+ TestCaseUtils.dsconfig(
+ "set-sasl-mechanism-handler-prop",
+ "--handler-name", "DIGEST-MD5",
+ "--remove", "server-fqdn:" + "127.0.0.1");
+
InternalClientConnection conn =
InternalClientConnection.getRootConnection();
@@ -1595,7 +1605,6 @@
"-o", "mech=DIGEST-MD5",
"-o", "authid=dn:cn=Privileged User,o=test",
"-o", "authzid=dn:",
- "-o", "realm=o=test",
"-w", "password",
"-b", "o=test",
"-s", "base",
@@ -1627,7 +1636,6 @@
"-o", "mech=DIGEST-MD5",
"-o", "authid=dn:cn=Privileged User,o=test",
"-o", "authzid=dn:cn=Privileged User,o=test",
- "-o", "realm=o=test",
"-w", "password",
"-b", "o=test",
"-s", "base",
@@ -1659,7 +1667,6 @@
"-o", "mech=DIGEST-MD5",
"-o", "authid=dn:cn=Privileged User,o=test",
"-o", "authzid=dn:cn=Unprivileged User,o=test",
- "-o", "realm=o=test",
"-w", "password",
"-b", "o=test",
"-s", "base",
@@ -1691,7 +1698,6 @@
"-o", "mech=DIGEST-MD5",
"-o", "authid=u:privileged.user",
"-o", "authzid=u:",
- "-o", "realm=o=test",
"-w", "password",
"-b", "o=test",
"-s", "base",
@@ -1723,7 +1729,6 @@
"-o", "mech=DIGEST-MD5",
"-o", "authid=u:privileged.user",
"-o", "authzid=u:privileged.user",
- "-o", "realm=o=test",
"-w", "password",
"-b", "o=test",
"-s", "base",
@@ -1755,7 +1760,6 @@
"-o", "mech=DIGEST-MD5",
"-o", "authid=u:privileged.user",
"-o", "authzid=u:unprivileged.user",
- "-o", "realm=o=test",
"-w", "password",
"-b", "o=test",
"-s", "base",
@@ -1787,7 +1791,6 @@
"-o", "mech=DIGEST-MD5",
"-o", "authid=dn:cn=Unprivileged User,o=test",
"-o", "authzid=dn:",
- "-o", "realm=o=test",
"-w", "password",
"-b", "o=test",
"-s", "base",
@@ -1819,7 +1822,6 @@
"-o", "mech=DIGEST-MD5",
"-o", "authid=dn:cn=Unprivileged User,o=test",
"-o", "authzid=dn:cn=Unprivileged User,o=test",
- "-o", "realm=o=test",
"-w", "password",
"-b", "o=test",
"-s", "base",
@@ -1851,7 +1853,6 @@
"-o", "mech=DIGEST-MD5",
"-o", "authid=dn:cn=Unprivileged User,o=test",
"-o", "authzid=dn:cn=Privileged User,o=test",
- "-o", "realm=o=test",
"-w", "password",
"-b", "o=test",
"-s", "base",
@@ -1883,7 +1884,6 @@
"-o", "mech=DIGEST-MD5",
"-o", "authid=u:unprivileged.user",
"-o", "authzid=u:",
- "-o", "realm=o=test",
"-w", "password",
"-b", "o=test",
"-s", "base",
@@ -1915,7 +1915,6 @@
"-o", "mech=DIGEST-MD5",
"-o", "authid=u:unprivileged.user",
"-o", "authzid=u:unprivileged.user",
- "-o", "realm=o=test",
"-w", "password",
"-b", "o=test",
"-s", "base",
@@ -1947,7 +1946,6 @@
"-o", "mech=DIGEST-MD5",
"-o", "authid=u:unprivileged.user",
"-o", "authzid=u:privileged.user",
- "-o", "realm=o=test",
"-w", "password",
"-b", "o=test",
"-s", "base",
--
Gitblit v1.10.0