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 2012-2016 ForgeRock AS. 016 */ 017package org.opends.server.schema; 018import static org.opends.messages.SchemaMessages.*; 019import static org.opends.server.schema.SchemaConstants.*; 020 021import org.forgerock.i18n.LocalizableMessage; 022import org.forgerock.opendj.ldap.ByteSequence; 023import org.forgerock.opendj.ldap.ResultCode; 024import org.forgerock.opendj.ldap.schema.Schema; 025import org.forgerock.opendj.ldap.schema.Syntax; 026import org.forgerock.opendj.server.config.server.AttributeSyntaxCfg; 027import org.opends.server.api.AttributeSyntax; 028import org.opends.server.types.DirectoryException; 029 030/** 031 * This class defines the auth password attribute syntax, which is defined in 032 * RFC 3112 and is used to hold authentication information. Only equality 033 * matching will be allowed by default. 034 */ 035public class AuthPasswordSyntax 036 extends AttributeSyntax<AttributeSyntaxCfg> 037{ 038 039 /** 040 * Creates a new instance of this syntax. Note that the only thing that 041 * should be done here is to invoke the default constructor for the 042 * superclass. All initialization should be performed in the 043 * <CODE>initializeSyntax</CODE> method. 044 */ 045 public AuthPasswordSyntax() 046 { 047 super(); 048 } 049 050 @Override 051 public Syntax getSDKSyntax(Schema schema) 052 { 053 return schema.getSyntax(SchemaConstants.SYNTAX_AUTH_PASSWORD_OID); 054 } 055 056 /** 057 * Retrieves the common name for this attribute syntax. 058 * 059 * @return The common name for this attribute syntax. 060 */ 061 @Override 062 public String getName() 063 { 064 return SYNTAX_AUTH_PASSWORD_NAME; 065 } 066 067 /** 068 * Retrieves the OID for this attribute syntax. 069 * 070 * @return The OID for this attribute syntax. 071 */ 072 @Override 073 public String getOID() 074 { 075 return SYNTAX_AUTH_PASSWORD_OID; 076 } 077 078 /** 079 * Retrieves a description for this attribute syntax. 080 * 081 * @return A description for this attribute syntax. 082 */ 083 @Override 084 public String getDescription() 085 { 086 return SYNTAX_AUTH_PASSWORD_DESCRIPTION; 087 } 088 089 /** 090 * Decodes the provided authentication password value into its component parts. 091 * <p> 092 * FIXME this is a duplicate of {@link org.forgerock.opendj.ldap.schema.AuthPasswordSyntaxImpl} 093 * 094 * @param authPasswordValue The authentication password value to be decoded. 095 * @return A three-element array, containing the scheme, authInfo, and 096 * authValue components of the given string, in that order. 097 * @throws DirectoryException If a problem is encountered while attempting 098 * to decode the value. 099 */ 100 public static String[] decodeAuthPassword(String authPasswordValue) throws DirectoryException 101 { 102 // Create placeholders for the values to return. 103 StringBuilder scheme = new StringBuilder(); 104 StringBuilder authInfo = new StringBuilder(); 105 StringBuilder authValue = new StringBuilder(); 106 107 108 // First, ignore any leading whitespace. 109 int length = authPasswordValue.length(); 110 int pos = 0; 111 while (pos < length && authPasswordValue.charAt(pos) == ' ') 112 { 113 pos++; 114 } 115 116 117 // The next set of characters will be the scheme, which must consist only 118 // of digits, uppercase alphabetic characters, dash, period, slash, and 119 // underscore characters. It must be immediately followed by one or more 120 // spaces or a dollar sign. 121readScheme: 122 while (pos < length) 123 { 124 char c = authPasswordValue.charAt(pos); 125 126 switch (c) 127 { 128 case '0': 129 case '1': 130 case '2': 131 case '3': 132 case '4': 133 case '5': 134 case '6': 135 case '7': 136 case '8': 137 case '9': 138 case 'A': 139 case 'B': 140 case 'C': 141 case 'D': 142 case 'E': 143 case 'F': 144 case 'G': 145 case 'H': 146 case 'I': 147 case 'J': 148 case 'K': 149 case 'L': 150 case 'M': 151 case 'N': 152 case 'O': 153 case 'P': 154 case 'Q': 155 case 'R': 156 case 'S': 157 case 'T': 158 case 'U': 159 case 'V': 160 case 'W': 161 case 'X': 162 case 'Y': 163 case 'Z': 164 case '-': 165 case '.': 166 case '/': 167 case '_': 168 scheme.append(c); 169 pos++; 170 break; 171 case ' ': 172 case '$': 173 break readScheme; 174 default: 175 LocalizableMessage message = ERR_ATTR_SYNTAX_AUTHPW_INVALID_SCHEME_CHAR.get(pos); 176 throw new DirectoryException(ResultCode.INVALID_ATTRIBUTE_SYNTAX, 177 message); 178 } 179 } 180 181 182 // The scheme must consist of at least one character. 183 if (scheme.length() == 0) 184 { 185 LocalizableMessage message = ERR_ATTR_SYNTAX_AUTHPW_NO_SCHEME.get(); 186 throw new DirectoryException(ResultCode.INVALID_ATTRIBUTE_SYNTAX, 187 message); 188 } 189 190 191 // Ignore any spaces before the dollar sign separator. Then read the dollar 192 // sign and ignore any trailing spaces. 193 while (pos < length && authPasswordValue.charAt(pos) == ' ') 194 { 195 pos++; 196 } 197 198 if (pos < length && authPasswordValue.charAt(pos) == '$') 199 { 200 pos++; 201 } 202 else 203 { 204 LocalizableMessage message = ERR_ATTR_SYNTAX_AUTHPW_NO_SCHEME_SEPARATOR.get(); 205 throw new DirectoryException(ResultCode.INVALID_ATTRIBUTE_SYNTAX, 206 message); 207 } 208 209 while (pos < length && authPasswordValue.charAt(pos) == ' ') 210 { 211 pos++; 212 } 213 214 215 // The next component must be the authInfo element, containing only 216 // printable characters other than the dollar sign and space character. 217readAuthInfo: 218 while (pos < length) 219 { 220 char c = authPasswordValue.charAt(pos); 221 if (c == ' ' || c == '$') 222 { 223 break readAuthInfo; 224 } 225 else if (PrintableString.isPrintableCharacter(c)) 226 { 227 authInfo.append(c); 228 pos++; 229 } 230 else 231 { 232 LocalizableMessage message = 233 ERR_ATTR_SYNTAX_AUTHPW_INVALID_AUTH_INFO_CHAR.get(pos); 234 throw new DirectoryException(ResultCode.INVALID_ATTRIBUTE_SYNTAX, 235 message); 236 } 237 } 238 239 240 // The authInfo element must consist of at least one character. 241 if (authInfo.length() == 0) 242 { 243 LocalizableMessage message = ERR_ATTR_SYNTAX_AUTHPW_NO_AUTH_INFO.get(); 244 throw new DirectoryException( 245 ResultCode.INVALID_ATTRIBUTE_SYNTAX, message); 246 } 247 248 249 // Ignore any spaces before the dollar sign separator. Then read the dollar 250 // sign and ignore any trailing spaces. 251 while (pos < length && authPasswordValue.charAt(pos) == ' ') 252 { 253 pos++; 254 } 255 256 if (pos < length && authPasswordValue.charAt(pos) == '$') 257 { 258 pos++; 259 } 260 else 261 { 262 LocalizableMessage message = ERR_ATTR_SYNTAX_AUTHPW_NO_AUTH_INFO_SEPARATOR.get(); 263 throw new DirectoryException( 264 ResultCode.INVALID_ATTRIBUTE_SYNTAX, message); 265 } 266 267 while (pos < length && authPasswordValue.charAt(pos) == ' ') 268 { 269 pos++; 270 } 271 272 273 // The final component must be the authValue element, containing only 274 // printable characters other than the dollar sign and space character. 275 while (pos < length) 276 { 277 char c = authPasswordValue.charAt(pos); 278 if (c == ' ' || c == '$') 279 { 280 break ; 281 } 282 else if (PrintableString.isPrintableCharacter(c)) 283 { 284 authValue.append(c); 285 pos++; 286 } 287 else 288 { 289 LocalizableMessage message = 290 ERR_ATTR_SYNTAX_AUTHPW_INVALID_AUTH_VALUE_CHAR.get(pos); 291 throw new DirectoryException(ResultCode.INVALID_ATTRIBUTE_SYNTAX, 292 message); 293 } 294 } 295 296 297 // The authValue element must consist of at least one character. 298 if (authValue.length() == 0) 299 { 300 LocalizableMessage message = ERR_ATTR_SYNTAX_AUTHPW_NO_AUTH_VALUE.get(); 301 throw new DirectoryException( 302 ResultCode.INVALID_ATTRIBUTE_SYNTAX, message); 303 } 304 305 306 // The only characters remaining must be whitespace. 307 while (pos < length) 308 { 309 char c = authPasswordValue.charAt(pos); 310 if (c == ' ') 311 { 312 pos++; 313 } 314 else 315 { 316 LocalizableMessage message = ERR_ATTR_SYNTAX_AUTHPW_INVALID_TRAILING_CHAR.get(pos); 317 throw new DirectoryException(ResultCode.INVALID_ATTRIBUTE_SYNTAX, 318 message); 319 } 320 } 321 322 323 // If we've gotten here, then everything must be OK. 324 return new String[] 325 { 326 scheme.toString(), 327 authInfo.toString(), 328 authValue.toString() 329 }; 330 } 331 332 /** 333 * Indicates whether the provided value is encoded using the auth password 334 * syntax. 335 * 336 * @param value The value for which to make the determination. 337 * 338 * @return <CODE>true</CODE> if the value appears to be encoded using the 339 * auth password syntax, or <CODE>false</CODE> if not. 340 */ 341 public static boolean isEncoded(ByteSequence value) 342 { 343 // FIXME -- Make this more efficient, and don't use exceptions for flow control. 344 try 345 { 346 decodeAuthPassword(value.toString()); 347 return true; 348 } 349 catch (Exception e) 350 { 351 return false; 352 } 353 } 354} 355