001/* 002 * Licensed to the Apache Software Foundation (ASF) under one 003 * or more contributor license agreements. See the NOTICE file 004 * distributed with this work for additional information 005 * regarding copyright ownership. The ASF licenses this file 006 * to you under the Apache License, Version 2.0 (the 007 * "License"); you may not use this file except in compliance 008 * with the License. You may obtain a copy of the License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, 013 * software distributed under the License is distributed on an 014 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 015 * KIND, either express or implied. See the License for the 016 * specific language governing permissions and limitations 017 * under the License. 018 * 019 */ 020 021package org.apache.directory.server.core.hash; 022 023 024import java.util.List; 025 026import org.apache.directory.api.ldap.model.constants.LdapSecurityConstants; 027import org.apache.directory.api.ldap.model.constants.SchemaConstants; 028import org.apache.directory.api.ldap.model.entry.Attribute; 029import org.apache.directory.api.ldap.model.entry.DefaultAttribute; 030import org.apache.directory.api.ldap.model.entry.Entry; 031import org.apache.directory.api.ldap.model.entry.Modification; 032import org.apache.directory.api.ldap.model.entry.ModificationOperation; 033import org.apache.directory.api.ldap.model.entry.Value; 034import org.apache.directory.api.ldap.model.exception.LdapException; 035import org.apache.directory.api.ldap.model.password.PasswordUtil; 036import org.apache.directory.api.util.Strings; 037import org.apache.directory.server.core.api.interceptor.BaseInterceptor; 038import org.apache.directory.server.core.api.interceptor.context.AddOperationContext; 039import org.apache.directory.server.core.api.interceptor.context.ModifyOperationContext; 040 041 042/** 043 * An interceptor to hash plain text password according to the configured 044 * hashing algorithm. 045 * 046 * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a> 047 */ 048public abstract class PasswordHashingInterceptor extends BaseInterceptor 049{ 050 051 /** the hashing algorithm to be used, if null then the password won't be changed */ 052 private LdapSecurityConstants algorithm; 053 054 055 /** 056 * 057 * Creates a new instance of PasswordHashingInterceptor which hashes the 058 * incoming non-hashed password using the given algorithm. 059 * If the password is found already hashed then it will skip hashing it. 060 * 061 * @param name The instance's name 062 * @param algorithm the name of the algorithm to be used 063 */ 064 protected PasswordHashingInterceptor( String name, LdapSecurityConstants algorithm ) 065 { 066 super( name ); 067 this.algorithm = algorithm; 068 } 069 070 071 /** 072 * {@inheritDoc} 073 */ 074 @Override 075 public void add( AddOperationContext addContext ) throws LdapException 076 { 077 if ( algorithm == null ) 078 { 079 next( addContext ); 080 return; 081 } 082 083 Entry entry = addContext.getEntry(); 084 085 Attribute pwdAt = entry.get( SchemaConstants.USER_PASSWORD_AT ); 086 087 Attribute hashedPwdAt = includeHashedPassword( pwdAt ); 088 089 if ( hashedPwdAt != null ) 090 { 091 entry.remove( pwdAt ); 092 entry.add( hashedPwdAt ); 093 } 094 095 next( addContext ); 096 } 097 098 099 /** 100 * {@inheritDoc} 101 */ 102 @Override 103 public void modify( ModifyOperationContext modifyContext ) throws LdapException 104 { 105 if ( algorithm == null ) 106 { 107 next( modifyContext ); 108 return; 109 } 110 111 List<Modification> mods = modifyContext.getModItems(); 112 113 for ( Modification mod : mods ) 114 { 115 String oid = mod.getAttribute().getAttributeType().getOid(); 116 117 // check for modification on 'userPassword' AT 118 if ( SchemaConstants.USER_PASSWORD_AT_OID.equals( oid ) ) 119 { 120 if ( mod.getOperation() == ModificationOperation.REMOVE_ATTRIBUTE ) 121 { 122 continue; 123 } 124 125 Attribute newPwd = includeHashedPassword( mod.getAttribute() ); 126 127 if ( newPwd != null ) 128 { 129 mod.setAttribute( newPwd ); 130 } 131 } 132 } 133 134 next( modifyContext ); 135 } 136 137 138 /** 139 * hash the password if it was <i>not</i> already hashed 140 * 141 * @param pwdAt the password attribute 142 */ 143 private Attribute includeHashedPassword( Attribute pwdAt ) throws LdapException 144 { 145 if ( pwdAt == null ) 146 { 147 return null; 148 } 149 150 Attribute newPwd = new DefaultAttribute( pwdAt.getAttributeType() ); 151 152 // Special case : deal with a potential empty value. We may have more than one 153 for ( Value userPassword : pwdAt ) 154 { 155 if ( Strings.isEmpty( userPassword.getString() ) ) 156 { 157 continue; 158 } 159 160 // check if the given password is already hashed 161 LdapSecurityConstants existingAlgo = PasswordUtil.findAlgorithm( userPassword.getBytes() ); 162 163 // if there exists NO algorithm, then hash the password 164 if ( existingAlgo == null ) 165 { 166 byte[] hashedPassword = PasswordUtil.createStoragePassword( userPassword.getBytes(), algorithm ); 167 168 newPwd.add( hashedPassword ); 169 } 170 else 171 { 172 newPwd.add( userPassword.getBytes() ); 173 } 174 } 175 176 return newPwd; 177 } 178}