1 /* 2 * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. 8 * 9 * This code is distributed in the hope that it will be useful, but WITHOUT 10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 12 * version 2 for more details (a copy is included in the LICENSE file that 13 * accompanied this code). 14 * 15 * You should have received a copy of the GNU General Public License version 16 * 2 along with this work; if not, write to the Free Software Foundation, 17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 18 * 19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 20 * or visit www.oracle.com if you need additional information or have any 21 * questions. 22 */ 23 24 import com.sun.security.auth.UnixPrincipal; 25 26 import javax.security.auth.Subject; 27 import javax.security.auth.callback.*; 28 import javax.security.auth.login.FailedLoginException; 29 import javax.security.auth.login.LoginContext; 30 import javax.security.auth.login.LoginException; 31 import javax.security.auth.spi.LoginModule; 32 import java.io.IOException; 33 import java.security.Principal; 34 import java.util.ArrayList; 35 import java.util.List; 36 import java.util.Map; 37 38 /* 39 * @test 40 * @bug 8050460 41 * @summary Test checks that proper methods associated with login/logout process 42 * of LoginContext are called for different configurations and circumstances. 43 * @modules jdk.security.auth 44 * 45 * @run main/othervm LCTest EmptyModuleConfig false 46 * @run main/othervm LCTest IncorrectName false 47 * @run main/othervm LCTest AbortRequisite false abort 48 * @run main/othervm LCTest AbortSufficient false abort 49 * @run main/othervm LCTest AbortRequired false abort 50 * @run main/othervm LCTest LogoutRequisite false logout 51 * @run main/othervm LCTest LogoutSufficient true logout 52 * @run main/othervm LCTest LogoutRequired false logout 53 * @run main/othervm LCTest LoginRequisite false login 54 * @run main/othervm LCTest LoginSufficient true login 55 * @run main/othervm LCTest LoginRequired false login 56 */ 57 58 public class LCTest { 59 60 private static final String USER_NAME = "testUser"; 61 private static final String PASSWORD = "testPassword"; 62 private static final List<String> loggedActions = new ArrayList<>(); 63 64 static { 65 System.setProperty("java.security.auth.login.config", 66 System.getProperty("test.src") 67 + System.getProperty("file.separator") 68 + "LCTest.jaas.config"); 69 } 70 main(String[] args)71 public static void main(String[] args) { 72 if (args.length < 2) { 73 throw new RuntimeException("Incorrect test params"); 74 } 75 String nameOfContext = args[0]; 76 boolean isPositive = Boolean.parseBoolean(args[1]); 77 String actionName = null; 78 if (args.length == 3) { 79 actionName = args[2]; 80 } 81 try { 82 LoginContext lc = new LoginContext(nameOfContext, 83 new MyCallbackHandler()); 84 lc.login(); 85 checkPrincipal(lc, true); 86 lc.logout(); 87 checkPrincipal(lc, false); 88 if (!isPositive) { 89 throw new RuntimeException("Test failed. Exception expected."); 90 } 91 } catch (LoginException le) { 92 if (isPositive) { 93 throw new RuntimeException("Test failed. Unexpected " + 94 "exception", le); 95 } 96 System.out.println("Expected exception: " 97 + le.getMessage()); 98 } 99 checkActions(actionName); 100 System.out.println("Test passed."); 101 } 102 103 /* 104 * Log action from login modules 105 */ logAction(String actionName)106 private static void logAction(String actionName) { 107 loggedActions.add(actionName); 108 } 109 110 /* 111 * Check if logged actions are as expected. We always expected 3 actions 112 * if any. 113 */ checkActions(String actionName)114 private static void checkActions(String actionName) { 115 if (actionName == null) { 116 if (loggedActions.size() != 0) { 117 throw new RuntimeException("No logged actions expected"); 118 } 119 } else { 120 int loggedActionsFound = 0; 121 System.out.println("Logged actions : " + loggedActions); 122 for (String s : loggedActions) { 123 if (s.equals(actionName)) { 124 loggedActionsFound++; 125 } 126 } 127 if (loggedActionsFound != 3) { 128 throw new RuntimeException("Incorrect number of actions " + 129 actionName + " : " + loggedActionsFound); 130 } 131 } 132 } 133 134 /* 135 * Check context for principal of the test user. 136 */ checkPrincipal(LoginContext loginContext, boolean principalShouldExist)137 private static void checkPrincipal(LoginContext loginContext, boolean 138 principalShouldExist) { 139 if (!principalShouldExist) { 140 if (loginContext.getSubject().getPrincipals().size() != 0) { 141 throw new RuntimeException("Test failed. Principal was not " + 142 "cleared."); 143 } 144 } else { 145 for (Principal p : loginContext.getSubject().getPrincipals()) { 146 if (p instanceof UnixPrincipal && 147 USER_NAME.equals(p.getName())) { 148 //Proper principal was found, return. 149 return; 150 } 151 } 152 throw new RuntimeException("Test failed. UnixPrincipal " 153 + USER_NAME + " expected."); 154 } 155 } 156 157 private static class MyCallbackHandler implements CallbackHandler { 158 159 @Override handle(Callback[] callbacks)160 public void handle(Callback[] callbacks) throws IOException, 161 UnsupportedCallbackException { 162 for (Callback callback : callbacks) { 163 if (callback instanceof NameCallback) { 164 ((NameCallback) callback).setName(USER_NAME); 165 } else if (callback instanceof PasswordCallback) { 166 ((PasswordCallback) callback).setPassword( 167 PASSWORD.toCharArray()); 168 } else { 169 throw new UnsupportedCallbackException(callback); 170 } 171 } 172 } 173 } 174 175 /* ------------------------------------------------------------------------- 176 * Test login modules 177 * ------------------------------------------------------------------------- 178 */ 179 180 /* 181 * Login module that should pass through all phases. 182 */ 183 public static class LoginModuleAllPass extends LoginModuleBase { 184 185 } 186 187 /* 188 * Login module that throws Exception in abort method. 189 */ 190 public static class LoginModuleWithAbortException extends LoginModuleBase { 191 192 @Override abort()193 public boolean abort() throws LoginException { 194 super.abort(); 195 throw new LoginException("Abort failed!"); 196 } 197 } 198 199 /* 200 * Login module that throws Exception in login method. 201 */ 202 public static class LoginModuleWithLoginException extends LoginModuleBase { 203 204 @Override login()205 public boolean login() throws LoginException { 206 super.login(); 207 throw new FailedLoginException("Login failed!"); 208 } 209 } 210 211 /* 212 * Login module that throws Exception in logout method. 213 */ 214 public static class LoginModuleWithLogoutException extends LoginModuleBase { 215 216 @Override logout()217 public boolean logout() throws LoginException { 218 super.logout(); 219 throw new FailedLoginException("Logout failed!"); 220 } 221 } 222 223 /* 224 * Base class for login modules 225 */ 226 public static abstract class LoginModuleBase implements LoginModule { 227 // initial state 228 private Subject subject; 229 private CallbackHandler callbackHandler; 230 private Map sharedState; 231 private Map options; 232 private UnixPrincipal userPrincipal; 233 234 // username and password 235 private String username; 236 private String password; 237 238 // the authentication status 239 private boolean succeeded = false; 240 private boolean commitSucceeded = false; 241 242 @Override initialize(Subject subject, CallbackHandler callbackHandler, Map<String, ?> sharedState, Map<String, ?> options)243 public void initialize(Subject subject, CallbackHandler callbackHandler, 244 Map<String, ?> sharedState, Map<String, ?> options) { 245 246 this.subject = subject; 247 this.callbackHandler = callbackHandler; 248 this.sharedState = sharedState; 249 this.options = options; 250 System.out.println("Login module initialized."); 251 } 252 253 /* 254 * Authenticate the user by prompting for a username and password. 255 */ 256 @Override login()257 public boolean login() throws LoginException { 258 LCTest.logAction("login"); 259 if (callbackHandler == null) { 260 throw new LoginException("No CallbackHandler available"); 261 } 262 263 Callback[] callbacks = new Callback[2]; 264 callbacks[0] = new NameCallback("Username: "); 265 callbacks[1] = new PasswordCallback("Password: ", false); 266 267 try { 268 callbackHandler.handle(callbacks); 269 username = ((NameCallback) callbacks[0]).getName(); 270 password = new String(((PasswordCallback) callbacks[1]) 271 .getPassword()); 272 if (username.equals(LCTest.USER_NAME) && 273 password.equals(LCTest.PASSWORD)) { 274 succeeded = true; 275 return true; 276 } 277 throw new FailedLoginException("Incorrect username/password!"); 278 } catch (IOException | UnsupportedCallbackException e) { 279 throw new LoginException("Login failed: " + e.getMessage()); 280 } 281 } 282 283 @Override commit()284 public boolean commit() throws LoginException { 285 LCTest.logAction("commit"); 286 if (succeeded == false) { 287 return false; 288 } 289 userPrincipal = new UnixPrincipal(username); 290 final Subject s = subject; 291 final UnixPrincipal up = userPrincipal; 292 java.security.AccessController.doPrivileged 293 ((java.security.PrivilegedAction) () -> { 294 if (!s.getPrincipals().contains(up)) { 295 s.getPrincipals().add(up); 296 } 297 return null; 298 }); 299 password = null; 300 commitSucceeded = true; 301 return true; 302 } 303 304 @Override abort()305 public boolean abort() throws LoginException { 306 LCTest.logAction("abort"); 307 if (succeeded == false) { 308 return false; 309 } 310 clearState(); 311 return true; 312 } 313 314 @Override logout()315 public boolean logout() throws LoginException { 316 LCTest.logAction("logout"); 317 clearState(); 318 return true; 319 } 320 clearState()321 private void clearState() { 322 if (commitSucceeded) { 323 final Subject s = subject; 324 final UnixPrincipal up = userPrincipal; 325 java.security.AccessController.doPrivileged 326 ((java.security.PrivilegedAction) () -> { 327 s.getPrincipals().remove(up); 328 return null; 329 }); 330 } 331 username = null; 332 password = null; 333 userPrincipal = null; 334 } 335 } 336 337 } 338