Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
8282730: LdapLoginModule throw NPE from logout method after login fai…
…lure

Reviewed-by: mullan
  • Loading branch information
wangweij committed Aug 1, 2022
1 parent f714ac5 commit 554f44e
Show file tree
Hide file tree
Showing 11 changed files with 221 additions and 72 deletions.
8 changes: 4 additions & 4 deletions src/java.base/share/classes/javax/security/auth/Subject.java
Expand Up @@ -144,8 +144,8 @@ public final class Subject implements java.io.Serializable {
* has been set read-only before permitting subsequent modifications.
* The newly created Sets also prevent illegal modifications
* by ensuring that callers have sufficient permissions. These Sets
* also prohibit null elements, and attempts to add or query a null
* element will result in a {@code NullPointerException}.
* also prohibit null elements, and attempts to add, query, or remove
* a null element will result in a {@code NullPointerException}.
*
* <p> To modify the Principals Set, the caller must have
* {@code AuthPermission("modifyPrincipals")}.
Expand Down Expand Up @@ -174,8 +174,8 @@ public Subject() {
* has been set read-only before permitting subsequent modifications.
* The newly created Sets also prevent illegal modifications
* by ensuring that callers have sufficient permissions. These Sets
* also prohibit null elements, and attempts to add or query a null
* element will result in a {@code NullPointerException}.
* also prohibit null elements, and attempts to add, query, or remove
* a null element will result in a {@code NullPointerException}.
*
* <p> To modify the Principals Set, the caller must have
* {@code AuthPermission("modifyPrincipals")}.
Expand Down
@@ -1,5 +1,5 @@
/*
* Copyright (c) 1998, 2021, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 1998, 2022, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
Expand Down Expand Up @@ -691,13 +691,13 @@ private void invoke(String methodName) throws LoginException {
// - this can only be non-zero if methodName is LOGIN_METHOD

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

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

// locate and instantiate the LoginModule
//
String name = moduleStack[i].entry.getLoginModuleName();
Set<Provider<LoginModule>> lmProviders;
synchronized(providersCache){
lmProviders = providersCache.get(contextClassLoader);
Expand Down Expand Up @@ -780,16 +780,16 @@ private void invoke(String methodName) throws LoginException {
clearState();

if (debug != null)
debug.println(methodName + " SUFFICIENT success");
debug.println(name + " " + methodName + " SUFFICIENT success");
return;
}

if (debug != null)
debug.println(methodName + " success");
debug.println(name + " " + methodName + " success");
success = true;
} else {
if (debug != null)
debug.println(methodName + " ignored");
debug.println(name + " " + methodName + " ignored");
}
} catch (Exception ite) {

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

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

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

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

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

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

// mark down that an OPTIONAL module failed
if (firstError == null)
Expand Down
@@ -1,5 +1,5 @@
/*
* Copyright (c) 1998, 2015, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 1998, 2022, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
Expand All @@ -26,7 +26,6 @@
package javax.security.auth.spi;

import javax.security.auth.Subject;
import javax.security.auth.AuthPermission;
import javax.security.auth.callback.*;
import javax.security.auth.login.*;
import java.util.Map;
Expand All @@ -50,13 +49,13 @@
* a {@code Subject}, a {@code CallbackHandler}, shared
* {@code LoginModule} state, and LoginModule-specific options.
*
* The {@code Subject} represents the
* <p> The {@code Subject} represents the
* {@code Subject} currently being authenticated and is updated
* with relevant Credentials if authentication succeeds.
* LoginModules use the {@code CallbackHandler} to
* communicate with users. The {@code CallbackHandler} may be
* used to prompt for usernames and passwords, for example.
* Note that the {@code CallbackHandler} may be null. LoginModules
* Note that the {@code CallbackHandler} may be {@code null}. LoginModules
* which absolutely require a {@code CallbackHandler} to authenticate
* the {@code Subject} may throw a {@code LoginException}.
* LoginModules optionally use the shared state to share information
Expand Down Expand Up @@ -129,7 +128,7 @@
public interface LoginModule {

/**
* Initialize this LoginModule.
* Initialize this {@code LoginModule}.
*
* <p> This method is called by the {@code LoginContext}
* after this {@code LoginModule} has been instantiated.
Expand Down Expand Up @@ -163,12 +162,12 @@ void initialize(Subject subject, CallbackHandler callbackHandler,
* {@code Subject} information such
* as a username and password and then attempt to verify the password.
* This method saves the result of the authentication attempt
* as private state within the LoginModule.
* as private state within the {@code LoginModule}.
*
* @exception LoginException if the authentication fails
*
* @return true if the authentication succeeded, or false if this
* {@code LoginModule} should be ignored.
* @return {@code true} if the authentication succeeded, or {@code false}
* if this {@code LoginModule} should be ignored.
*/
boolean login() throws LoginException;

Expand All @@ -190,8 +189,8 @@ void initialize(Subject subject, CallbackHandler callbackHandler,
*
* @exception LoginException if the commit fails
*
* @return true if this method succeeded, or false if this
* {@code LoginModule} should be ignored.
* @return {@code true} if this method succeeded, or {@code false}
* if this {@code LoginModule} should be ignored.
*/
boolean commit() throws LoginException;

Expand All @@ -210,8 +209,8 @@ void initialize(Subject subject, CallbackHandler callbackHandler,
*
* @exception LoginException if the abort fails
*
* @return true if this method succeeded, or false if this
* {@code LoginModule} should be ignored.
* @return {@code true} if this method succeeded, or {@code false}
* if this {@code LoginModule} should be ignored.
*/
boolean abort() throws LoginException;

Expand All @@ -223,8 +222,15 @@ void initialize(Subject subject, CallbackHandler callbackHandler,
*
* @exception LoginException if the logout fails
*
* @return true if this method succeeded, or false if this
* {@code LoginModule} should be ignored.
* @return {@code true} if this method succeeded, or {@code false}
* if this {@code LoginModule} should be ignored.
*
* @implSpec Implementations should check if a variable is {@code null}
* before removing it from the Principals or Credentials set
* of a {@code Subject}, otherwise a {@code NullPointerException}
* will be thrown as these sets {@linkplain Subject#Subject()
* prohibit null elements}. This is especially important if
* this method is called after a login failure.
*/
boolean logout() throws LoginException;
}
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2004, 2021, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2004, 2022, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
Expand Down Expand Up @@ -422,7 +422,9 @@ public boolean logout() throws LoginException {
cleanState();
throw new LoginException ("Subject is read-only");
}
subject.getPrincipals().remove(user);
if (user != null) {
subject.getPrincipals().remove(user);
}

// clean out state
cleanState();
Expand Down
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2000, 2021, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2000, 2022, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
Expand Down Expand Up @@ -471,11 +471,18 @@ public boolean logout() throws LoginException {
cleanState();
throw new LoginException ("Subject is Readonly");
}
subject.getPrincipals().remove(userPrincipal);
subject.getPrincipals().remove(UIDPrincipal);
subject.getPrincipals().remove(GIDPrincipal);
for (int i = 0; i < supplementaryGroups.size(); i++) {
subject.getPrincipals().remove(supplementaryGroups.get(i));
if (userPrincipal != null) {
subject.getPrincipals().remove(userPrincipal);
}
if (UIDPrincipal != null) {
subject.getPrincipals().remove(UIDPrincipal);
}
if (GIDPrincipal != null) {
subject.getPrincipals().remove(GIDPrincipal);
}
for (UnixNumericGroupPrincipal gp : supplementaryGroups) {
// gp is never null
subject.getPrincipals().remove(gp);
}


Expand Down
Expand Up @@ -851,23 +851,25 @@ private void logoutInternal() throws LoginException {
certP = null;
status = INITIALIZED;
// destroy the private credential
Iterator<Object> it = subject.getPrivateCredentials().iterator();
while (it.hasNext()) {
Object obj = it.next();
if (privateCredential.equals(obj)) {
privateCredential = null;
try {
((Destroyable)obj).destroy();
if (debug)
debugPrint("Destroyed private credential, " +
obj.getClass().getName());
break;
} catch (DestroyFailedException dfe) {
LoginException le = new LoginException
("Unable to destroy private credential, "
+ obj.getClass().getName());
le.initCause(dfe);
throw le;
if (privateCredential != null) {
Iterator<Object> it = subject.getPrivateCredentials().iterator();
while (it.hasNext()) {
Object obj = it.next();
if (privateCredential.equals(obj)) {
privateCredential = null;
try {
((Destroyable) obj).destroy();
if (debug)
debugPrint("Destroyed private credential, " +
obj.getClass().getName());
break;
} catch (DestroyFailedException dfe) {
LoginException le = new LoginException
("Unable to destroy private credential, "
+ obj.getClass().getName());
le.initCause(dfe);
throw le;
}
}
}
}
Expand Down
Expand Up @@ -1202,8 +1202,10 @@ public boolean logout() throws LoginException {
throw new LoginException("Subject is Readonly");
}

subject.getPrincipals().remove(kerbClientPrinc);
// Let us remove all Kerberos credentials stored in the Subject
if (kerbClientPrinc != null) {
subject.getPrincipals().remove(kerbClientPrinc);
}
// Let us remove all Kerberos credentials stored in the Subject
Iterator<Object> it = subject.getPrivateCredentials().iterator();
while (it.hasNext()) {
Object o = it.next();
Expand Down
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2005, 2020, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2005, 2022, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
Expand Down Expand Up @@ -696,8 +696,12 @@ public boolean logout() throws LoginException {
throw new LoginException ("Subject is read-only");
}
Set<Principal> principals = subject.getPrincipals();
principals.remove(ldapPrincipal);
principals.remove(userPrincipal);
if (ldapPrincipal != null) {
principals.remove(ldapPrincipal);
}
if (userPrincipal != null) {
principals.remove(userPrincipal);
}
if (authzIdentity != null) {
principals.remove(authzPrincipal);
}
Expand Down
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2000, 2020, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2000, 2022, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
Expand Down Expand Up @@ -349,29 +349,30 @@ public boolean logout() throws LoginException {
throw new LoginException ("Subject is ReadOnly");
}
Set<Principal> principals = subject.getPrincipals();
if (principals.contains(userPrincipal)) {
if (userPrincipal != null && principals.contains(userPrincipal)) {
principals.remove(userPrincipal);
}
if (principals.contains(userSID)) {
if (userSID != null && principals.contains(userSID)) {
principals.remove(userSID);
}
if (principals.contains(userDomain)) {
if (userDomain != null && principals.contains(userDomain)) {
principals.remove(userDomain);
}
if (principals.contains(domainSID)) {
if (domainSID != null && principals.contains(domainSID)) {
principals.remove(domainSID);
}
if (principals.contains(primaryGroup)) {
if (primaryGroup != null && principals.contains(primaryGroup)) {
principals.remove(primaryGroup);
}
for (int i = 0; groups != null && i < groups.length; i++) {
if (principals.contains(groups[i])) {
principals.remove(groups[i]);
if (groups != null) {
for (NTSidGroupPrincipal gp : groups) {
// gp is never null
principals.remove(gp);
}
}

Set<Object> pubCreds = subject.getPublicCredentials();
if (pubCreds.contains(iToken)) {
if (iToken != null && pubCreds.contains(iToken)) {
pubCreds.remove(iToken);
}

Expand Down
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2000, 2020, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2000, 2022, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
Expand Down Expand Up @@ -277,11 +277,18 @@ public boolean logout() throws LoginException {
("logout Failed: Subject is Readonly");
}
// remove the added Principals from the Subject
subject.getPrincipals().remove(userPrincipal);
subject.getPrincipals().remove(UIDPrincipal);
subject.getPrincipals().remove(GIDPrincipal);
for (int i = 0; i < supplementaryGroups.size(); i++) {
subject.getPrincipals().remove(supplementaryGroups.get(i));
if (userPrincipal != null) {
subject.getPrincipals().remove(userPrincipal);
}
if (UIDPrincipal != null) {
subject.getPrincipals().remove(UIDPrincipal);
}
if (GIDPrincipal != null) {
subject.getPrincipals().remove(GIDPrincipal);
}
for (UnixNumericGroupPrincipal gp : supplementaryGroups) {
// gp is never null
subject.getPrincipals().remove(gp);
}

// clean out state
Expand Down

5 comments on commit 554f44e

@openjdk-notifier
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@GoeLin
Copy link
Member

@GoeLin GoeLin commented on 554f44e Oct 17, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

/backport jdk11u-dev

@GoeLin
Copy link
Member

@GoeLin GoeLin commented on 554f44e Oct 17, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

/backport jdk17u-dev

@openjdk
Copy link

@openjdk openjdk bot commented on 554f44e Oct 17, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@GoeLin Could not automatically backport 554f44ec to openjdk/jdk11u-dev due to conflicts in the following files:

  • src/java.base/share/classes/javax/security/auth/login/LoginContext.java
  • src/java.management/share/classes/com/sun/jmx/remote/security/FileLoginModule.java
  • src/jdk.security.auth/share/classes/com/sun/security/auth/module/JndiLoginModule.java
  • src/jdk.security.auth/share/classes/com/sun/security/auth/module/LdapLoginModule.java
  • src/jdk.security.auth/share/classes/com/sun/security/auth/module/NTLoginModule.java
  • src/jdk.security.auth/share/classes/com/sun/security/auth/module/UnixLoginModule.java

Please fetch the appropriate branch/commit and manually resolve these conflicts by using the following commands in your personal fork of openjdk/jdk11u-dev. Note: these commands are just some suggestions and you can use other equivalent commands you know.

# Fetch the up-to-date version of the target branch
$ git fetch --no-tags https://git.openjdk.org/jdk11u-dev master:master

# Check out the target branch and create your own branch to backport
$ git checkout master
$ git checkout -b GoeLin-backport-554f44ec

# Fetch the commit you want to backport
$ git fetch --no-tags https://git.openjdk.org/jdk 554f44ecb1134acff3eaf02e2e1c0e01158ab7e5

# Backport the commit
$ git cherry-pick --no-commit 554f44ecb1134acff3eaf02e2e1c0e01158ab7e5
# Resolve conflicts now

# Commit the files you have modified
$ git add files/with/resolved/conflicts
$ git commit -m 'Backport 554f44ecb1134acff3eaf02e2e1c0e01158ab7e5'

Once you have resolved the conflicts as explained above continue with creating a pull request towards the openjdk/jdk11u-dev with the title Backport 554f44ecb1134acff3eaf02e2e1c0e01158ab7e5.

@openjdk
Copy link

@openjdk openjdk bot commented on 554f44e Oct 17, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@GoeLin the backport was successfully created on the branch GoeLin-backport-554f44ec in my personal fork of openjdk/jdk17u-dev. To create a pull request with this backport targeting openjdk/jdk17u-dev:master, just click the following link:

➡️ Create pull request

The title of the pull request is automatically filled in correctly and below you find a suggestion for the pull request body:

Hi all,

This pull request contains a backport of commit 554f44ec from the openjdk/jdk repository.

The commit being backported was authored by Weijun Wang on 1 Aug 2022 and was reviewed by Sean Mullan.

Thanks!

If you need to update the source branch of the pull then run the following commands in a local clone of your personal fork of openjdk/jdk17u-dev:

$ git fetch https://github.com/openjdk-bots/jdk17u-dev GoeLin-backport-554f44ec:GoeLin-backport-554f44ec
$ git checkout GoeLin-backport-554f44ec
# make changes
$ git add paths/to/changed/files
$ git commit --message 'Describe additional changes made'
$ git push https://github.com/openjdk-bots/jdk17u-dev GoeLin-backport-554f44ec

Please sign in to comment.