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