1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 package org.apache.directory.server.changepw.service;
21
22
23 import java.util.ArrayList;
24 import java.util.List;
25
26 import javax.security.auth.kerberos.KerberosPrincipal;
27
28 import org.apache.directory.server.changepw.ChangePasswordServer;
29 import org.apache.directory.server.changepw.exceptions.ChangePasswordException;
30 import org.apache.directory.server.changepw.exceptions.ErrorType;
31 import org.apache.directory.server.kerberos.shared.messages.components.Authenticator;
32 import org.apache.mina.common.IoSession;
33 import org.apache.mina.handler.chain.IoHandlerCommand;
34 import org.slf4j.Logger;
35 import org.slf4j.LoggerFactory;
36
37
38
39
40
41
42
43
44 public class CheckPasswordPolicy implements IoHandlerCommand
45 {
46
47 private static final Logger log = LoggerFactory.getLogger( CheckPasswordPolicy.class );
48
49 private String contextKey = "context";
50
51
52 public void execute( NextCommand next, IoSession session, Object message ) throws Exception
53 {
54 ChangePasswordContext changepwContext = ( ChangePasswordContext ) session.getAttribute( getContextKey() );
55
56 ChangePasswordServer config = changepwContext.getConfig();
57 Authenticator authenticator = changepwContext.getAuthenticator();
58 KerberosPrincipal clientPrincipal = authenticator.getClientPrincipal();
59
60 String password = changepwContext.getPassword();
61 String username = clientPrincipal.getName();
62
63 int passwordLength = config.getPasswordLengthPolicy();
64 int categoryCount = config.getCategoryCountPolicy();
65 int tokenSize = config.getTokenSizePolicy();
66
67 if ( !isValid( username, password, passwordLength, categoryCount, tokenSize ) )
68 {
69 String explanation = buildErrorMessage( username, password, passwordLength, categoryCount, tokenSize );
70 log.error( explanation );
71
72 byte[] explanatoryData = explanation.getBytes( "UTF-8" );
73
74 throw new ChangePasswordException( ErrorType.KRB5_KPASSWD_SOFTERROR, explanatoryData );
75 }
76
77 next.execute( session, message );
78 }
79
80
81
82
83
84
85
86
87 boolean isValid( String username, String password, int passwordLength, int categoryCount, int tokenSize )
88 {
89 return isValidPasswordLength( password, passwordLength ) && isValidCategoryCount( password, categoryCount )
90 && isValidUsernameSubstring( username, password, tokenSize );
91 }
92
93
94
95
96
97 boolean isValidPasswordLength( String password, int passwordLength )
98 {
99 return password.length() >= passwordLength;
100 }
101
102
103
104
105
106
107
108
109
110 boolean isValidCategoryCount( String password, int categoryCount )
111 {
112 int uppercase = 0;
113 int lowercase = 0;
114 int digit = 0;
115 int nonAlphaNumeric = 0;
116
117 char[] characters = password.toCharArray();
118
119 for ( int ii = 0; ii < characters.length; ii++ )
120 {
121 if ( Character.isLowerCase( characters[ii] ) )
122 {
123 lowercase = 1;
124 }
125 else
126 {
127 if ( Character.isUpperCase( characters[ii] ) )
128 {
129 uppercase = 1;
130 }
131 else
132 {
133 if ( Character.isDigit( characters[ii] ) )
134 {
135 digit = 1;
136 }
137 else
138 {
139 if ( !Character.isLetterOrDigit( characters[ii] ) )
140 {
141 nonAlphaNumeric = 1;
142 }
143 }
144 }
145 }
146 }
147
148 return ( uppercase + lowercase + digit + nonAlphaNumeric ) >= categoryCount;
149 }
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164 boolean isValidUsernameSubstring( String username, String password, int tokenSize )
165 {
166 String[] tokens = username.split( "[^a-zA-Z]" );
167
168 for ( int ii = 0; ii < tokens.length; ii++ )
169 {
170 if ( tokens[ii].length() >= tokenSize )
171 {
172 if ( password.matches( "(?i).*" + tokens[ii] + ".*" ) )
173 {
174 return false;
175 }
176 }
177 }
178
179 return true;
180 }
181
182
183 private String buildErrorMessage( String username, String password, int passwordLength, int categoryCount,
184 int tokenSize )
185 {
186 List<String> violations = new ArrayList<String>();
187
188 if ( !isValidPasswordLength( password, passwordLength ) )
189 {
190 violations.add( "length too short" );
191 }
192
193 if ( !isValidCategoryCount( password, categoryCount ) )
194 {
195 violations.add( "insufficient character mix" );
196 }
197
198 if ( !isValidUsernameSubstring( username, password, tokenSize ) )
199 {
200 violations.add( "contains portions of username" );
201 }
202
203 StringBuffer sb = new StringBuffer( "Password violates policy: " );
204
205 boolean isFirst = true;
206
207 for ( String violation : violations )
208 {
209 if ( isFirst )
210 {
211 isFirst = false;
212 }
213 else
214 {
215 sb.append( ", " );
216 }
217
218 sb.append( violation );
219 }
220
221 return sb.toString();
222 }
223
224
225 protected String getContextKey()
226 {
227 return ( this.contextKey );
228 }
229 }