opends/src/server/org/opends/server/authorization/dseecompat/AciContainer.java
@@ -30,10 +30,10 @@ import org.opends.server.types.*; import org.opends.server.api.ClientConnection; import org.opends.server.api.Group; import org.opends.server.api.ConnectionSecurityProvider; import org.opends.server.core.AddOperation; import org.opends.server.core.Operation; import org.opends.server.extensions.TLSConnectionSecurityProvider; import org.opends.server.util.ServerConstants; import java.net.InetAddress; import java.util.LinkedList; @@ -353,88 +353,63 @@ } /** * Try to determine the authentication information from the current * client connection. The checks are for simple and SASL, anything else * is not a match. If the bind rule requires any SSL client authentication * information then the "wantSSL" flag should be set. This code is used by * the authmethod bind rule keyword. * * @param wantSSL True if the bind rule needs the ssl client auth check. * @return Return an enumeration containing the authentication method * type for this client connection. * {@inheritDoc} */ public EnumAuthMethod getAuthenticationMethod(boolean wantSSL) { EnumAuthMethod method=EnumAuthMethod.AUTHMETHOD_NOMATCH; public EnumEvalResult hasAuthenticationMethod(EnumAuthMethod authMethod, String saslMech) { EnumEvalResult matched=EnumEvalResult.FALSE; if(authMethod==EnumAuthMethod.AUTHMETHOD_NONE) { /** * None actually means any, in that we don't care what method was used. * This doesn't seem very intuitive or useful, but that's the way it is. */ matched = EnumEvalResult.TRUE; } else { /* * Some kind of authentication is required. */ AuthenticationInfo authInfo=clientConnection.getAuthenticationInfo(); if(authInfo.isAuthenticated()) { if(authInfo.hasAuthenticationType(AuthenticationType.SIMPLE)) method=EnumAuthMethod.AUTHMETHOD_SIMPLE; else if(authInfo.hasAuthenticationType(AuthenticationType.SASL)) method=getSaslAuthenticationMethod(authInfo, wantSSL); else method=EnumAuthMethod.AUTHMETHOD_NOMATCH; if(authMethod==EnumAuthMethod.AUTHMETHOD_SIMPLE) { if(authInfo.hasAuthenticationType(AuthenticationType.SIMPLE)) { matched = EnumEvalResult.TRUE; } } else if(authMethod == EnumAuthMethod.AUTHMETHOD_SSL) { /* * This means authentication using a certificate over TLS. * * We check the following: * - SASL EXTERNAL has been used, and * - TLS is the security provider, and * - The client provided a certificate. */ if (authInfo.hasAuthenticationType(AuthenticationType.SASL) && authInfo.hasSASLMechanism(saslMech)) { ConnectionSecurityProvider provider = clientConnection.getConnectionSecurityProvider(); if (provider instanceof TLSConnectionSecurityProvider) { TLSConnectionSecurityProvider tlsProvider = (TLSConnectionSecurityProvider) provider; if (tlsProvider.getClientCertificateChain() != null) { matched = EnumEvalResult.TRUE; } } } } else { // A particular SASL mechanism. if (authInfo.hasAuthenticationType(AuthenticationType.SASL) && authInfo.hasSASLMechanism(saslMech)) { matched = EnumEvalResult.TRUE; } } } return method; } /* * TODO This method needs to be tested. * TODO Investigate multi-factor authentication. * Second, OpenDS is devised so that it could be possible to use * multi-factor or step-up authentication, in which the same client * has provided multiple forms of credentials, but this method * expects only a single authentication type. */ /** * This method attempts to figure out what the SASL method was/is or * what the client auth is. * @param authInfo The authentication information to use. * @param wantSSL The bin drule wants the SSL client auth status. * @return An enumeration containing the SASL bind information. */ private EnumAuthMethod getSaslAuthenticationMethod(AuthenticationInfo authInfo, boolean wantSSL) { EnumAuthMethod method=EnumAuthMethod.AUTHMETHOD_NOMATCH; if(authInfo.hasAuthenticationType(AuthenticationType.SASL)) { if(authInfo.hasSASLMechanism(ServerConstants. SASL_MECHANISM_DIGEST_MD5)) method=EnumAuthMethod.AUTHMETHOD_SASL_MD5; else if(authInfo.hasSASLMechanism(ServerConstants. SASL_MECHANISM_GSSAPI)) method=EnumAuthMethod.AUTHMETHOD_SASL_GSSAPI; else if(authInfo.hasSASLMechanism(ServerConstants. SASL_MECHANISM_EXTERNAL)) { /* * The bind rule wants ssl client auth information. Need the * security provider to see if the clientAuthPolicy is * required. If it is optional, we really can't determine if * the client auth. */ if(wantSSL) { String mechName= clientConnection.getConnectionSecurityProvider(). getSecurityMechanismName(); if(mechName.equalsIgnoreCase("TLS")) { TLSConnectionSecurityProvider tlsProv= (TLSConnectionSecurityProvider)clientConnection. getConnectionSecurityProvider(); SSLClientAuthPolicy clientAuthPolicy= tlsProv.getSSLClientAuthPolicy(); if(clientAuthPolicy == SSLClientAuthPolicy.REQUIRED) method=EnumAuthMethod.AUTHMETHOD_SSL; } else method=EnumAuthMethod.AUTHMETHOD_NOMATCH; } else { method=EnumAuthMethod.AUTHMETHOD_SASL_EXTERNAL; } } else method=EnumAuthMethod.AUTHMETHOD_NOMATCH; } return method; } return matched; } /** * Convienance method that checks if the the clientDN is a member of the * Convenience method that checks if the the clientDN is a member of the * specified group. * @param group The group to check membership in. * @return True if the clientDN is a member of the specified group. opends/src/server/org/opends/server/authorization/dseecompat/AciEvalContext.java
@@ -110,12 +110,18 @@ public String getHostName(); /** * Get the authentication method. * @param wantSSL The authmethod bind rule needs the SSL client auth * status. * @return An Enumeration of the auth method bound as. * Determine whether the client connection has been authenticated using * a specified authentication method. This method is used for the * authmethod bind rule keyword. * * @param authMethod The required authentication method. * @param saslMech The required SASL mechanism if the authentication method * is SASL. * @return An evaluation result indicating whether the client connection * has been authenticated using the required authentication method. */ public EnumAuthMethod getAuthenticationMethod(boolean wantSSL); public EnumEvalResult hasAuthenticationMethod(EnumAuthMethod authMethod, String saslMech); /** * Get the address of the bound connection. opends/src/server/org/opends/server/authorization/dseecompat/AciMessages.java
@@ -610,6 +610,16 @@ int MSGID_ACI_SYNTAX_INVALID_ATTRIBUTE_TYPE_NAME = CATEGORY_MASK_ACCESS_CONTROL | SEVERITY_MASK_SEVERE_WARNING | 59; /** * The message ID for the message that will be used if a bind rule * authmethod expression contains a SASL mechanism that is not currrently * registered in the server. This takes one argument, which is the * SASL mechanism string parsed from the authmethod expression. */ public static final int MSGID_ACI_SYNTAX_DUBIOUS_AUTHMETHOD_SASL_MECHANISM = CATEGORY_MASK_ACCESS_CONTROL | SEVERITY_MASK_NOTICE | 60; /** * Associates a set of generic messages with the message IDs defined in * this class. @@ -773,7 +783,14 @@ "The provided Access Control Instruction (ACI) bind rule " + "authmethod expression value \"%s\" is invalid. A valid " + "authmethod value is one of the following: none, simple," + "SSL, sasl EXTERNAL, sasl DIGEST-MD5, or sasl GSSAPI."); "SSL, or \"sasl mechanism\", where mechanism is one of the" + "supported SASL mechanisms including CRAM-MD5, DIGEST-MD5, " + "and GSSAPI."); registerMessage(MSGID_ACI_SYNTAX_DUBIOUS_AUTHMETHOD_SASL_MECHANISM, "The SASL mechanism \"%s\" provided in the Access Control " + "Instruction (ACI) bind rule authmethod expression is not " + "one of the currently registered mechanisms in the server"); registerMessage(MSGID_ACI_SYNTAX_INVALID_USERATTR_EXPRESSION, "The provided Access Control Instruction (ACI) bind rule " + opends/src/server/org/opends/server/authorization/dseecompat/AuthMethod.java
@@ -29,6 +29,10 @@ import static org.opends.server.authorization.dseecompat.AciMessages.*; import static org.opends.server.messages.MessageHandler.getMessage; import org.opends.server.core.DirectoryServer; import static org.opends.server.loggers.Error.logError; import org.opends.server.types.ErrorLogCategory; import org.opends.server.types.ErrorLogSeverity; /** * The AuthMethod class represents an authmethod bind rule keyword expression. @@ -40,6 +44,11 @@ */ private EnumAuthMethod authMethod=null; /** * The SASL mechanism if the authentication method is SASL. */ private String saslMech = null; /* * Enumeration representing the bind rule operation type. */ @@ -48,73 +57,62 @@ /** * Create a class representing an authmethod bind rule keyword from the * provided method and bind rule type. * @param method An enumeration representing the method of the expression. * @param type An enumeration representing the type of the expression. */ private AuthMethod(EnumAuthMethod method, EnumBindRuleType type) { private AuthMethod(EnumAuthMethod method, String saslMech, EnumBindRuleType type) { this.authMethod=method; this.saslMech = saslMech; this.type=type; } /** * Decode a string representing a authmethod bind rule. * Decode a string representing an authmethod bind rule. * @param expr The string representing the bind rule. * @param type An enumeration representing the bind rule type. * @return An keyword bind rule class that can be used to evaluate the * @return A keyword bind rule class that can be used to evaluate the * bind rule. * @throws AciException If the expression string is invalid. */ public static KeywordBindRule decode(String expr, EnumBindRuleType type) throws AciException { EnumAuthMethod method=EnumAuthMethod.createAuthmethod(expr); if (method == null) { int msgID = MSGID_ACI_SYNTAX_INVALID_AUTHMETHOD_EXPRESSION; String message = getMessage(msgID, expr); throw new AciException(msgID, message); String lowerExpr = expr.toLowerCase(); if (lowerExpr.equals("none")) { return new AuthMethod(EnumAuthMethod.AUTHMETHOD_NONE, null, type); } else if (lowerExpr.equals("simple")) { return new AuthMethod(EnumAuthMethod.AUTHMETHOD_SIMPLE, null, type); } else if (lowerExpr.equals("ssl")) { return new AuthMethod(EnumAuthMethod.AUTHMETHOD_SSL, "EXTERNAL", type); } else if (expr.length() > 5 && lowerExpr.startsWith("sasl ")) { String saslMech = expr.substring(5); if (DirectoryServer.getSASLMechanismHandler(saslMech) == null) { int msgID = MSGID_ACI_SYNTAX_DUBIOUS_AUTHMETHOD_SASL_MECHANISM; logError(ErrorLogCategory.ACCESS_CONTROL, ErrorLogSeverity.NOTICE, msgID, saslMech); } return new AuthMethod(method, type); return new AuthMethod(EnumAuthMethod.AUTHMETHOD_SASL, saslMech, type); } int msgID = MSGID_ACI_SYNTAX_INVALID_AUTHMETHOD_EXPRESSION; String message = getMessage(msgID, expr); throw new AciException(msgID, message); } /* * TODO Evaluate if AUTHMETHOD_NONE processing is correct. This was fixed * prior to Neil's review. Verify in a unit test. * * I'm not sure that the evaluate() method handles AUTHMETHOD_NONE * correctly. My understanding is that it should only match in cases * in which no authentication has been performed, but you have it * always matching. */ /** * Evaluate authmethod bind rule using the provided evaluation context. * @param evalCtx An evaluation context to use. * @return An enumeration evaluation result. */ public EnumEvalResult evaluate(AciEvalContext evalCtx) { EnumEvalResult matched=EnumEvalResult.FALSE; if(authMethod==EnumAuthMethod.AUTHMETHOD_NONE) { matched=EnumEvalResult.TRUE; } else if(authMethod==EnumAuthMethod.AUTHMETHOD_SIMPLE) { if(evalCtx.getAuthenticationMethod(false) == EnumAuthMethod.AUTHMETHOD_SIMPLE){ matched=EnumEvalResult.TRUE; } } else if(authMethod == EnumAuthMethod.AUTHMETHOD_SSL) { /* * TODO Verfiy that SSL authemethod is correctly handled in a * unit test. * I'm not sure that the evaluate() method correctly handles * SASL EXTERNAL in all cases. My understanding is that in * DS 5/6, an authmethod of SSL is the same as an authmethod of * SASL EXTERNAL. If that's true, then you don't properly handle * that condition. */ if(authMethod == evalCtx.getAuthenticationMethod(true)) matched=EnumEvalResult.TRUE; } else { if(authMethod ==evalCtx.getAuthenticationMethod(false)) matched=EnumEvalResult.TRUE; } EnumEvalResult matched = evalCtx.hasAuthenticationMethod(authMethod, saslMech); return matched.getRet(type, false); } } opends/src/server/org/opends/server/authorization/dseecompat/EnumAuthMethod.java
@@ -26,17 +26,7 @@ */ package org.opends.server.authorization.dseecompat; /* * TODO Evaluate moving this to a non-enumeration class that can add * SASL mechanisms dynamically. * * Given our previous discussion about needing to support any kind of SASL * mechanism that may be registered with the server, perhaps an enum isn't * the right way to handle this because we don't know ahead of time what * auth methods might be available (certainly not at compile time, but * potentially not even at runtime since I can add support for a new SASL * mechanism on the fly without restarting the server). */ /** * This class provides an enumeration of the allowed authmethod types. */ @@ -47,35 +37,24 @@ * none. */ AUTHMETHOD_NONE ("none"), /** * The enumeration type when the bind rule has specified authentication of * simple. * The enumeration type when the bind rule has specified authentication of * simple. */ AUTHMETHOD_SIMPLE ("simple"), /** * The enumeration type when the bind rule has specified authentication of * ssl client auth. * The enumeration type when the bind rule has specified authentication of * ssl client auth. */ AUTHMETHOD_SSL ("ssl"), /** * The enumeration type when the bind rule has specified authentication of * sasl DIGEST-MD5. * a sasl mechanism. */ AUTHMETHOD_SASL_MD5 ("sasl DIGEST-MD5"), /** * The enumeration type when the bind rule has specified authentication of * sasl EXTERNAL. */ AUTHMETHOD_SASL_EXTERNAL ("sasl EXTERNAL"), /** * The enumeration type when the bind rule has specified authentication of * sasl GSSAPI. */ AUTHMETHOD_SASL_GSSAPI ("sasl GSSAPI"), /** * Special internal enumeration for when there is no match. */ AUTHMETHOD_NOMATCH ("nomatch"); AUTHMETHOD_SASL ("sasl"); /* * The name of the authmethod. @@ -90,28 +69,4 @@ this.authmethod = authmethod; } /** * Checks if a authmethod name is equal to this enumeration. * @param myauthmethod The name to test for. * @return True if the names match. */ public boolean isAuthMethod(String myauthmethod){ return myauthmethod.equalsIgnoreCase(this.authmethod); } /** * Creates an authmethod enumeration from the name passed in. * @param myauthmethod The name to create. * @return An authmethod enumeration if the name was found or null if not. */ public static EnumAuthMethod createAuthmethod(String myauthmethod){ if (myauthmethod != null){ for (EnumAuthMethod t : EnumAuthMethod.values()){ if (t.isAuthMethod(myauthmethod)){ return t; } } } return null; } } opends/tests/unit-tests-testng/src/server/org/opends/server/authorization/dseecompat/AciTests.java
@@ -208,7 +208,7 @@ private static final String BIND_RULE_AUTHMETHOD_SIMPLE = "authmethod=\"simple\""; private static final String BIND_RULE_AUTHMETHOD_SSL = "authmethod=\"ssl\""; private static final String BIND_RULE_AUTHMETHOD_SASL = "authmethod=\"sasl\""; private static final String BIND_RULE_AUTHMETHOD_SASL_DIGEST_MD5 = "authmethod=\"sasl DIGEST-MD5\""; // Admin, but not anonymous private static final String BIND_RULE_USERDN_NOT_ADMIN = and(not(BIND_RULE_USERDN_ADMIN), BIND_RULE_AUTHMETHOD_SIMPLE); @@ -373,6 +373,9 @@ private static final String ALLOW_ALL_TO_SSL = buildAciValue("name", "allow all to ssl", "targetattr", "*", "allow(all)", BIND_RULE_AUTHMETHOD_SSL); private static final String ALLOW_ALL_TO_SASL_DIGEST_MD5 = buildAciValue("name", "allow all to sasl DIGEST-MD5", "targetattr", "*", "allow(all)", BIND_RULE_AUTHMETHOD_SASL_DIGEST_MD5); private static final String DENY_ALL_TO_SIMPLE = buildAciValue("name", "deny all to simple", "targetattr", "*", "deny(all)", BIND_RULE_AUTHMETHOD_SIMPLE); @@ -581,6 +584,7 @@ DENY_ALL_TO_DNS_LOCALHOST, buildAciValue("name", "deny all to example.com", "targetattr", "*", "deny(all)", "dns=\"*.example.com\""), ALLOW_ALL_TO_SSL, ALLOW_ALL_TO_SASL_DIGEST_MD5, DENY_ALL_TO_SIMPLE, DENY_ALL_TODAY, DENY_ALL_TODAY_AND_TOMORROW, @@ -1043,6 +1047,9 @@ private static final String ALLOW_ALL_BASE_TO_SSL_AUTH = makeAddAciLdif(OU_BASE_DN, ALLOW_ALL_TO_SSL); private static final String ALLOW_ALL_BASE_TO_SASL_DIGEST_MD5_AUTH = makeAddAciLdif(OU_BASE_DN, ALLOW_ALL_TO_SASL_DIGEST_MD5); private static final String ALLOW_ALL_BASE_DENY_ALL_TO_SIMPLE_AUTH = makeAddAciLdif(OU_BASE_DN, ALLOW_ALL_TO_ALL) + makeAddAciLdif(OU_INNER_DN, DENY_ALL_TO_SIMPLE); @@ -1215,6 +1222,7 @@ ALLOW_ALL_BASE_DENY_ALL_TO_MISC_AND_LOCALHOST, ALLOW_ALL_BASE_TO_NON_DNS_LOCALHOST, ALLOW_ALL_BASE_TO_SSL_AUTH, ALLOW_ALL_BASE_TO_SASL_DIGEST_MD5_AUTH, ALLOW_ALL_BASE_DENY_ALL_TO_SIMPLE_AUTH, ALLOW_ALL_BASE_DENY_ALL_TODAY, ALLOW_ALL_BASE_DENY_ALL_TODAY_AND_TOMORROW,