Skip to content

Commit 554f44e

Browse files
committed
8282730: LdapLoginModule throw NPE from logout method after login failure
Reviewed-by: mullan
1 parent f714ac5 commit 554f44e

File tree

11 files changed

+221
-72
lines changed

11 files changed

+221
-72
lines changed

src/java.base/share/classes/javax/security/auth/Subject.java

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -144,8 +144,8 @@ public final class Subject implements java.io.Serializable {
144144
* has been set read-only before permitting subsequent modifications.
145145
* The newly created Sets also prevent illegal modifications
146146
* by ensuring that callers have sufficient permissions. These Sets
147-
* also prohibit null elements, and attempts to add or query a null
148-
* element will result in a {@code NullPointerException}.
147+
* also prohibit null elements, and attempts to add, query, or remove
148+
* a null element will result in a {@code NullPointerException}.
149149
*
150150
* <p> To modify the Principals Set, the caller must have
151151
* {@code AuthPermission("modifyPrincipals")}.
@@ -174,8 +174,8 @@ public Subject() {
174174
* has been set read-only before permitting subsequent modifications.
175175
* The newly created Sets also prevent illegal modifications
176176
* by ensuring that callers have sufficient permissions. These Sets
177-
* also prohibit null elements, and attempts to add or query a null
178-
* element will result in a {@code NullPointerException}.
177+
* also prohibit null elements, and attempts to add, query, or remove
178+
* a null element will result in a {@code NullPointerException}.
179179
*
180180
* <p> To modify the Principals Set, the caller must have
181181
* {@code AuthPermission("modifyPrincipals")}.

src/java.base/share/classes/javax/security/auth/login/LoginContext.java

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 1998, 2021, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 1998, 2022, Oracle and/or its affiliates. All rights reserved.
33
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44
*
55
* This code is free software; you can redistribute it and/or modify it
@@ -691,13 +691,13 @@ private void invoke(String methodName) throws LoginException {
691691
// - this can only be non-zero if methodName is LOGIN_METHOD
692692

693693
for (int i = moduleIndex; i < moduleStack.length; i++, moduleIndex++) {
694+
String name = moduleStack[i].entry.getLoginModuleName();
694695
try {
695696

696697
if (moduleStack[i].module == null) {
697698

698699
// locate and instantiate the LoginModule
699700
//
700-
String name = moduleStack[i].entry.getLoginModuleName();
701701
Set<Provider<LoginModule>> lmProviders;
702702
synchronized(providersCache){
703703
lmProviders = providersCache.get(contextClassLoader);
@@ -780,16 +780,16 @@ private void invoke(String methodName) throws LoginException {
780780
clearState();
781781

782782
if (debug != null)
783-
debug.println(methodName + " SUFFICIENT success");
783+
debug.println(name + " " + methodName + " SUFFICIENT success");
784784
return;
785785
}
786786

787787
if (debug != null)
788-
debug.println(methodName + " success");
788+
debug.println(name + " " + methodName + " success");
789789
success = true;
790790
} else {
791791
if (debug != null)
792-
debug.println(methodName + " ignored");
792+
debug.println(name + " " + methodName + " ignored");
793793
}
794794
} catch (Exception ite) {
795795

@@ -854,7 +854,7 @@ private void invoke(String methodName) throws LoginException {
854854
AppConfigurationEntry.LoginModuleControlFlag.REQUISITE) {
855855

856856
if (debug != null)
857-
debug.println(methodName + " REQUISITE failure");
857+
debug.println(name + " " + methodName + " REQUISITE failure");
858858

859859
// if REQUISITE, then immediately throw an exception
860860
if (methodName.equals(ABORT_METHOD) ||
@@ -869,7 +869,7 @@ private void invoke(String methodName) throws LoginException {
869869
AppConfigurationEntry.LoginModuleControlFlag.REQUIRED) {
870870

871871
if (debug != null)
872-
debug.println(methodName + " REQUIRED failure");
872+
debug.println(name + " " + methodName + " REQUIRED failure");
873873

874874
// mark down that a REQUIRED module failed
875875
if (firstRequiredError == null)
@@ -878,7 +878,7 @@ private void invoke(String methodName) throws LoginException {
878878
} else {
879879

880880
if (debug != null)
881-
debug.println(methodName + " OPTIONAL failure");
881+
debug.println(name + " " + methodName + " OPTIONAL failure");
882882

883883
// mark down that an OPTIONAL module failed
884884
if (firstError == null)

src/java.base/share/classes/javax/security/auth/spi/LoginModule.java

Lines changed: 20 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 1998, 2015, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 1998, 2022, Oracle and/or its affiliates. All rights reserved.
33
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44
*
55
* This code is free software; you can redistribute it and/or modify it
@@ -26,7 +26,6 @@
2626
package javax.security.auth.spi;
2727

2828
import javax.security.auth.Subject;
29-
import javax.security.auth.AuthPermission;
3029
import javax.security.auth.callback.*;
3130
import javax.security.auth.login.*;
3231
import java.util.Map;
@@ -50,13 +49,13 @@
5049
* a {@code Subject}, a {@code CallbackHandler}, shared
5150
* {@code LoginModule} state, and LoginModule-specific options.
5251
*
53-
* The {@code Subject} represents the
52+
* <p> The {@code Subject} represents the
5453
* {@code Subject} currently being authenticated and is updated
5554
* with relevant Credentials if authentication succeeds.
5655
* LoginModules use the {@code CallbackHandler} to
5756
* communicate with users. The {@code CallbackHandler} may be
5857
* used to prompt for usernames and passwords, for example.
59-
* Note that the {@code CallbackHandler} may be null. LoginModules
58+
* Note that the {@code CallbackHandler} may be {@code null}. LoginModules
6059
* which absolutely require a {@code CallbackHandler} to authenticate
6160
* the {@code Subject} may throw a {@code LoginException}.
6261
* LoginModules optionally use the shared state to share information
@@ -129,7 +128,7 @@
129128
public interface LoginModule {
130129

131130
/**
132-
* Initialize this LoginModule.
131+
* Initialize this {@code LoginModule}.
133132
*
134133
* <p> This method is called by the {@code LoginContext}
135134
* after this {@code LoginModule} has been instantiated.
@@ -163,12 +162,12 @@ void initialize(Subject subject, CallbackHandler callbackHandler,
163162
* {@code Subject} information such
164163
* as a username and password and then attempt to verify the password.
165164
* This method saves the result of the authentication attempt
166-
* as private state within the LoginModule.
165+
* as private state within the {@code LoginModule}.
167166
*
168167
* @exception LoginException if the authentication fails
169168
*
170-
* @return true if the authentication succeeded, or false if this
171-
* {@code LoginModule} should be ignored.
169+
* @return {@code true} if the authentication succeeded, or {@code false}
170+
* if this {@code LoginModule} should be ignored.
172171
*/
173172
boolean login() throws LoginException;
174173

@@ -190,8 +189,8 @@ void initialize(Subject subject, CallbackHandler callbackHandler,
190189
*
191190
* @exception LoginException if the commit fails
192191
*
193-
* @return true if this method succeeded, or false if this
194-
* {@code LoginModule} should be ignored.
192+
* @return {@code true} if this method succeeded, or {@code false}
193+
* if this {@code LoginModule} should be ignored.
195194
*/
196195
boolean commit() throws LoginException;
197196

@@ -210,8 +209,8 @@ void initialize(Subject subject, CallbackHandler callbackHandler,
210209
*
211210
* @exception LoginException if the abort fails
212211
*
213-
* @return true if this method succeeded, or false if this
214-
* {@code LoginModule} should be ignored.
212+
* @return {@code true} if this method succeeded, or {@code false}
213+
* if this {@code LoginModule} should be ignored.
215214
*/
216215
boolean abort() throws LoginException;
217216

@@ -223,8 +222,15 @@ void initialize(Subject subject, CallbackHandler callbackHandler,
223222
*
224223
* @exception LoginException if the logout fails
225224
*
226-
* @return true if this method succeeded, or false if this
227-
* {@code LoginModule} should be ignored.
225+
* @return {@code true} if this method succeeded, or {@code false}
226+
* if this {@code LoginModule} should be ignored.
227+
*
228+
* @implSpec Implementations should check if a variable is {@code null}
229+
* before removing it from the Principals or Credentials set
230+
* of a {@code Subject}, otherwise a {@code NullPointerException}
231+
* will be thrown as these sets {@linkplain Subject#Subject()
232+
* prohibit null elements}. This is especially important if
233+
* this method is called after a login failure.
228234
*/
229235
boolean logout() throws LoginException;
230236
}

src/java.management/share/classes/com/sun/jmx/remote/security/FileLoginModule.java

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2004, 2021, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2004, 2022, Oracle and/or its affiliates. All rights reserved.
33
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44
*
55
* This code is free software; you can redistribute it and/or modify it
@@ -422,7 +422,9 @@ public boolean logout() throws LoginException {
422422
cleanState();
423423
throw new LoginException ("Subject is read-only");
424424
}
425-
subject.getPrincipals().remove(user);
425+
if (user != null) {
426+
subject.getPrincipals().remove(user);
427+
}
426428

427429
// clean out state
428430
cleanState();

src/jdk.security.auth/share/classes/com/sun/security/auth/module/JndiLoginModule.java

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2000, 2021, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2000, 2022, Oracle and/or its affiliates. All rights reserved.
33
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44
*
55
* This code is free software; you can redistribute it and/or modify it
@@ -471,11 +471,18 @@ public boolean logout() throws LoginException {
471471
cleanState();
472472
throw new LoginException ("Subject is Readonly");
473473
}
474-
subject.getPrincipals().remove(userPrincipal);
475-
subject.getPrincipals().remove(UIDPrincipal);
476-
subject.getPrincipals().remove(GIDPrincipal);
477-
for (int i = 0; i < supplementaryGroups.size(); i++) {
478-
subject.getPrincipals().remove(supplementaryGroups.get(i));
474+
if (userPrincipal != null) {
475+
subject.getPrincipals().remove(userPrincipal);
476+
}
477+
if (UIDPrincipal != null) {
478+
subject.getPrincipals().remove(UIDPrincipal);
479+
}
480+
if (GIDPrincipal != null) {
481+
subject.getPrincipals().remove(GIDPrincipal);
482+
}
483+
for (UnixNumericGroupPrincipal gp : supplementaryGroups) {
484+
// gp is never null
485+
subject.getPrincipals().remove(gp);
479486
}
480487

481488

src/jdk.security.auth/share/classes/com/sun/security/auth/module/KeyStoreLoginModule.java

Lines changed: 19 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -851,23 +851,25 @@ private void logoutInternal() throws LoginException {
851851
certP = null;
852852
status = INITIALIZED;
853853
// destroy the private credential
854-
Iterator<Object> it = subject.getPrivateCredentials().iterator();
855-
while (it.hasNext()) {
856-
Object obj = it.next();
857-
if (privateCredential.equals(obj)) {
858-
privateCredential = null;
859-
try {
860-
((Destroyable)obj).destroy();
861-
if (debug)
862-
debugPrint("Destroyed private credential, " +
863-
obj.getClass().getName());
864-
break;
865-
} catch (DestroyFailedException dfe) {
866-
LoginException le = new LoginException
867-
("Unable to destroy private credential, "
868-
+ obj.getClass().getName());
869-
le.initCause(dfe);
870-
throw le;
854+
if (privateCredential != null) {
855+
Iterator<Object> it = subject.getPrivateCredentials().iterator();
856+
while (it.hasNext()) {
857+
Object obj = it.next();
858+
if (privateCredential.equals(obj)) {
859+
privateCredential = null;
860+
try {
861+
((Destroyable) obj).destroy();
862+
if (debug)
863+
debugPrint("Destroyed private credential, " +
864+
obj.getClass().getName());
865+
break;
866+
} catch (DestroyFailedException dfe) {
867+
LoginException le = new LoginException
868+
("Unable to destroy private credential, "
869+
+ obj.getClass().getName());
870+
le.initCause(dfe);
871+
throw le;
872+
}
871873
}
872874
}
873875
}

src/jdk.security.auth/share/classes/com/sun/security/auth/module/Krb5LoginModule.java

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1202,8 +1202,10 @@ public boolean logout() throws LoginException {
12021202
throw new LoginException("Subject is Readonly");
12031203
}
12041204

1205-
subject.getPrincipals().remove(kerbClientPrinc);
1206-
// Let us remove all Kerberos credentials stored in the Subject
1205+
if (kerbClientPrinc != null) {
1206+
subject.getPrincipals().remove(kerbClientPrinc);
1207+
}
1208+
// Let us remove all Kerberos credentials stored in the Subject
12071209
Iterator<Object> it = subject.getPrivateCredentials().iterator();
12081210
while (it.hasNext()) {
12091211
Object o = it.next();

src/jdk.security.auth/share/classes/com/sun/security/auth/module/LdapLoginModule.java

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2005, 2020, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2005, 2022, Oracle and/or its affiliates. All rights reserved.
33
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44
*
55
* This code is free software; you can redistribute it and/or modify it
@@ -696,8 +696,12 @@ public boolean logout() throws LoginException {
696696
throw new LoginException ("Subject is read-only");
697697
}
698698
Set<Principal> principals = subject.getPrincipals();
699-
principals.remove(ldapPrincipal);
700-
principals.remove(userPrincipal);
699+
if (ldapPrincipal != null) {
700+
principals.remove(ldapPrincipal);
701+
}
702+
if (userPrincipal != null) {
703+
principals.remove(userPrincipal);
704+
}
701705
if (authzIdentity != null) {
702706
principals.remove(authzPrincipal);
703707
}

src/jdk.security.auth/share/classes/com/sun/security/auth/module/NTLoginModule.java

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2000, 2020, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2000, 2022, Oracle and/or its affiliates. All rights reserved.
33
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44
*
55
* This code is free software; you can redistribute it and/or modify it
@@ -349,29 +349,30 @@ public boolean logout() throws LoginException {
349349
throw new LoginException ("Subject is ReadOnly");
350350
}
351351
Set<Principal> principals = subject.getPrincipals();
352-
if (principals.contains(userPrincipal)) {
352+
if (userPrincipal != null && principals.contains(userPrincipal)) {
353353
principals.remove(userPrincipal);
354354
}
355-
if (principals.contains(userSID)) {
355+
if (userSID != null && principals.contains(userSID)) {
356356
principals.remove(userSID);
357357
}
358-
if (principals.contains(userDomain)) {
358+
if (userDomain != null && principals.contains(userDomain)) {
359359
principals.remove(userDomain);
360360
}
361-
if (principals.contains(domainSID)) {
361+
if (domainSID != null && principals.contains(domainSID)) {
362362
principals.remove(domainSID);
363363
}
364-
if (principals.contains(primaryGroup)) {
364+
if (primaryGroup != null && principals.contains(primaryGroup)) {
365365
principals.remove(primaryGroup);
366366
}
367-
for (int i = 0; groups != null && i < groups.length; i++) {
368-
if (principals.contains(groups[i])) {
369-
principals.remove(groups[i]);
367+
if (groups != null) {
368+
for (NTSidGroupPrincipal gp : groups) {
369+
// gp is never null
370+
principals.remove(gp);
370371
}
371372
}
372373

373374
Set<Object> pubCreds = subject.getPublicCredentials();
374-
if (pubCreds.contains(iToken)) {
375+
if (iToken != null && pubCreds.contains(iToken)) {
375376
pubCreds.remove(iToken);
376377
}
377378

src/jdk.security.auth/share/classes/com/sun/security/auth/module/UnixLoginModule.java

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2000, 2020, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2000, 2022, Oracle and/or its affiliates. All rights reserved.
33
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44
*
55
* This code is free software; you can redistribute it and/or modify it
@@ -277,11 +277,18 @@ public boolean logout() throws LoginException {
277277
("logout Failed: Subject is Readonly");
278278
}
279279
// remove the added Principals from the Subject
280-
subject.getPrincipals().remove(userPrincipal);
281-
subject.getPrincipals().remove(UIDPrincipal);
282-
subject.getPrincipals().remove(GIDPrincipal);
283-
for (int i = 0; i < supplementaryGroups.size(); i++) {
284-
subject.getPrincipals().remove(supplementaryGroups.get(i));
280+
if (userPrincipal != null) {
281+
subject.getPrincipals().remove(userPrincipal);
282+
}
283+
if (UIDPrincipal != null) {
284+
subject.getPrincipals().remove(UIDPrincipal);
285+
}
286+
if (GIDPrincipal != null) {
287+
subject.getPrincipals().remove(GIDPrincipal);
288+
}
289+
for (UnixNumericGroupPrincipal gp : supplementaryGroups) {
290+
// gp is never null
291+
subject.getPrincipals().remove(gp);
285292
}
286293

287294
// clean out state

0 commit comments

Comments
 (0)