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