001/* 002 * The contents of this file are subject to the terms of the Common Development and 003 * Distribution License (the License). You may not use this file except in compliance with the 004 * License. 005 * 006 * You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the 007 * specific language governing permission and limitations under the License. 008 * 009 * When distributing Covered Software, include this CDDL Header Notice in each file and include 010 * the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL 011 * Header, with the fields enclosed by brackets [] replaced by your own identifying 012 * information: "Portions Copyright [year] [name of copyright owner]". 013 * 014 * Copyright 2006-2008 Sun Microsystems, Inc. 015 * Portions Copyright 2011-2016 ForgeRock AS. 016 */ 017package org.opends.server.controls; 018 019import java.io.IOException; 020import org.forgerock.i18n.LocalizableMessage; 021import org.opends.server.api.AuthenticationPolicyState; 022import org.opends.server.api.IdentityMapper; 023import org.opends.server.core.DirectoryServer; 024import org.opends.server.core.PasswordPolicyState; 025import org.forgerock.i18n.slf4j.LocalizedLogger; 026import org.forgerock.opendj.io.ASN1; 027import org.forgerock.opendj.io.ASN1Reader; 028import org.forgerock.opendj.io.ASN1Writer; 029import org.opends.server.types.*; 030import org.forgerock.opendj.ldap.DN; 031import org.forgerock.opendj.ldap.ResultCode; 032import org.forgerock.opendj.ldap.ByteString; 033import static org.opends.messages.ProtocolMessages.*; 034import static org.opends.server.util.ServerConstants.*; 035import static org.opends.server.util.StaticUtils.*; 036import static org.forgerock.util.Reject.*; 037 038/** 039 * This class implements version 2 of the proxied authorization control as 040 * defined in RFC 4370. It makes it possible for one user to request that an 041 * operation be performed under the authorization of another. The target user 042 * is specified using an authorization ID, which may be in the form "dn:" 043 * immediately followed by the DN of that user, or "u:" followed by a user ID 044 * string. 045 */ 046public class ProxiedAuthV2Control 047 extends Control 048{ 049 /** 050 * ControlDecoder implementation to decode this control from a ByteString. 051 */ 052 private static final class Decoder 053 implements ControlDecoder<ProxiedAuthV2Control> 054 { 055 /** {@inheritDoc} */ 056 @Override 057 public ProxiedAuthV2Control decode(boolean isCritical, ByteString value) 058 throws DirectoryException 059 { 060 if (!isCritical) 061 { 062 LocalizableMessage message = ERR_PROXYAUTH2_CONTROL_NOT_CRITICAL.get(); 063 throw new DirectoryException(ResultCode.PROTOCOL_ERROR, message); 064 } 065 066 if (value == null) 067 { 068 LocalizableMessage message = ERR_PROXYAUTH2_NO_CONTROL_VALUE.get(); 069 throw new DirectoryException(ResultCode.PROTOCOL_ERROR, message); 070 } 071 072 ASN1Reader reader = ASN1.getReader(value); 073 ByteString authorizationID; 074 try 075 { 076 // Try the legacy encoding where the value is wrapped by an 077 // extra octet string 078 authorizationID = reader.readOctetString(); 079 } 080 catch (Exception e) 081 { 082 // Try just getting the value. 083 authorizationID = value; 084 String lowerAuthZIDStr = toLowerCase(authorizationID.toString()); 085 if (!lowerAuthZIDStr.startsWith("dn:") && 086 !lowerAuthZIDStr.startsWith("u:")) 087 { 088 logger.traceException(e); 089 090 LocalizableMessage message = 091 ERR_PROXYAUTH2_INVALID_AUTHZID.get(lowerAuthZIDStr); 092 throw new DirectoryException(ResultCode.PROTOCOL_ERROR, message, 093 e); 094 } 095 } 096 097 return new ProxiedAuthV2Control(isCritical, authorizationID); 098 } 099 100 @Override 101 public String getOID() 102 { 103 return OID_PROXIED_AUTH_V2; 104 } 105 106 } 107 108 /** 109 * The Control Decoder that can be used to decode this control. 110 */ 111 public static final ControlDecoder<ProxiedAuthV2Control> DECODER = 112 new Decoder(); 113 private static final LocalizedLogger logger = LocalizedLogger.getLoggerForThisClass(); 114 115 116 117 118 /** The authorization ID from the control value. */ 119 private ByteString authorizationID; 120 121 122 123 /** 124 * Creates a new instance of the proxied authorization v2 control with the 125 * provided information. 126 * 127 * @param authorizationID The authorization ID from the control value. 128 */ 129 public ProxiedAuthV2Control(ByteString authorizationID) 130 { 131 this(true, authorizationID); 132 } 133 134 135 136 /** 137 * Creates a new instance of the proxied authorization v2 control with the 138 * provided information. 139 * 140 * @param isCritical Indicates whether support for this control 141 * should be considered a critical part of the 142 * server processing. 143 * @param authorizationID The authorization ID from the control value. 144 */ 145 public ProxiedAuthV2Control(boolean isCritical, ByteString authorizationID) 146 { 147 super(OID_PROXIED_AUTH_V2, isCritical); 148 149 ifNull(authorizationID); 150 151 this.authorizationID = authorizationID; 152 } 153 154 155 156 /** 157 * Writes this control's value to an ASN.1 writer. The value (if any) must be 158 * written as an ASN1OctetString. 159 * 160 * @param writer The ASN.1 writer to use. 161 * @throws IOException If a problem occurs while writing to the stream. 162 */ 163 @Override 164 protected void writeValue(ASN1Writer writer) throws IOException { 165 writer.writeOctetString(authorizationID); 166 } 167 168 169 170 /** 171 * Retrieves the authorization ID for this proxied authorization V2 control. 172 * 173 * @return The authorization ID for this proxied authorization V2 control. 174 */ 175 public ByteString getAuthorizationID() 176 { 177 return authorizationID; 178 } 179 180 181 182 /** 183 * Retrieves the authorization entry for this proxied authorization V2 184 * control. It will also perform any necessary password policy checks to 185 * ensure that the associated user account is suitable for use in performing 186 * this processing. 187 * 188 * @return The entry for user specified as the authorization identity in this 189 * proxied authorization V1 control, or {@code null} if the 190 * authorization DN is the null DN. 191 * 192 * @throws DirectoryException If the target user does not exist or is not 193 * available for use, or if a problem occurs 194 * while making the determination. 195 */ 196 public Entry getAuthorizationEntry() 197 throws DirectoryException 198 { 199 // Check for a zero-length value, which would be for an anonymous user. 200 if (authorizationID.length() == 0) 201 { 202 return null; 203 } 204 205 206 // Get a lowercase string representation. It must start with either "dn:" 207 // or "u:". 208 String lowerAuthzID = toLowerCase(authorizationID.toString()); 209 if (lowerAuthzID.startsWith("dn:")) 210 { 211 // It's a DN, so decode it and see if it exists. If it's the null DN, 212 // then just assume that it does. 213 DN authzDN = DN.valueOf(lowerAuthzID.substring(3)); 214 if (authzDN.isRootDN()) 215 { 216 return null; 217 } 218 else 219 { 220 // See if the authorization DN is one of the alternate bind DNs for one 221 // of the root users and if so then map it accordingly. 222 DN actualDN = DirectoryServer.getActualRootBindDN(authzDN); 223 if (actualDN != null) 224 { 225 authzDN = actualDN; 226 } 227 228 Entry userEntry = DirectoryServer.getEntry(authzDN); 229 if (userEntry == null) 230 { 231 // The requested user does not exist. 232 LocalizableMessage message = ERR_PROXYAUTH2_NO_SUCH_USER.get(lowerAuthzID); 233 throw new DirectoryException(ResultCode.AUTHORIZATION_DENIED, message); 234 } 235 236 // FIXME -- We should provide some mechanism for enabling debug 237 // processing. 238 checkAccountIsUsable(userEntry); 239 240 // If we've made it here, then the user is acceptable. 241 return userEntry; 242 } 243 } 244 else if (lowerAuthzID.startsWith("u:")) 245 { 246 // If the authorization ID is just "u:", then it's an anonymous request. 247 if (lowerAuthzID.length() == 2) 248 { 249 return null; 250 } 251 252 253 // Use the proxied authorization identity mapper to resolve the username 254 // to an entry. 255 IdentityMapper<?> proxyMapper = 256 DirectoryServer.getProxiedAuthorizationIdentityMapper(); 257 if (proxyMapper == null) 258 { 259 LocalizableMessage message = ERR_PROXYAUTH2_NO_IDENTITY_MAPPER.get(); 260 throw new DirectoryException(ResultCode.AUTHORIZATION_DENIED, message); 261 } 262 263 Entry userEntry = proxyMapper.getEntryForID(lowerAuthzID.substring(2)); 264 if (userEntry == null) 265 { 266 LocalizableMessage message = ERR_PROXYAUTH2_NO_SUCH_USER.get(lowerAuthzID); 267 throw new DirectoryException(ResultCode.AUTHORIZATION_DENIED, message); 268 } 269 else 270 { 271 // FIXME -- We should provide some mechanism for enabling debug 272 // processing. 273 checkAccountIsUsable(userEntry); 274 275 return userEntry; 276 } 277 } 278 else 279 { 280 LocalizableMessage message = ERR_PROXYAUTH2_INVALID_AUTHZID.get(lowerAuthzID); 281 throw new DirectoryException(ResultCode.PROTOCOL_ERROR, message); 282 } 283 } 284 285 286 287 private void checkAccountIsUsable(Entry userEntry) 288 throws DirectoryException 289 { 290 AuthenticationPolicyState state = AuthenticationPolicyState.forUser( 291 userEntry, false); 292 293 if (state.isDisabled()) 294 { 295 LocalizableMessage message = ERR_PROXYAUTH2_ACCOUNT_DISABLED.get(userEntry.getName()); 296 throw new DirectoryException(ResultCode.AUTHORIZATION_DENIED, message); 297 } 298 299 if (state.isPasswordPolicy()) 300 { 301 PasswordPolicyState pwpState = (PasswordPolicyState) state; 302 if (pwpState.isAccountExpired()) 303 { 304 LocalizableMessage message = ERR_PROXYAUTH2_ACCOUNT_EXPIRED.get(userEntry.getName()); 305 throw new DirectoryException(ResultCode.AUTHORIZATION_DENIED, message); 306 } 307 if (pwpState.isLocked()) 308 { 309 LocalizableMessage message = ERR_PROXYAUTH2_ACCOUNT_LOCKED.get(userEntry.getName()); 310 throw new DirectoryException(ResultCode.AUTHORIZATION_DENIED, message); 311 } 312 if (pwpState.isPasswordExpired()) 313 { 314 LocalizableMessage message = ERR_PROXYAUTH2_PASSWORD_EXPIRED.get(userEntry.getName()); 315 throw new DirectoryException(ResultCode.AUTHORIZATION_DENIED, message); 316 } 317 } 318 } 319 320 321 322 /** 323 * Appends a string representation of this proxied authorization v2 control 324 * to the provided buffer. 325 * 326 * @param buffer The buffer to which the information should be appended. 327 */ 328 @Override 329 public void toString(StringBuilder buffer) 330 { 331 buffer.append("ProxiedAuthorizationV2Control(authzID=\""); 332 buffer.append(authorizationID); 333 buffer.append("\")"); 334 } 335} 336