Skip to content

Commit f942ead

Browse files
committed
Null safety via JSpecify spring-security-kerberos-core
Closes gh-18549
1 parent b970746 commit f942ead

11 files changed

Lines changed: 149 additions & 43 deletions

kerberos/kerberos-core/spring-security-kerberos-core.gradle

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
plugins {
2+
id 'security-nullability'
23
id 'io.spring.convention.spring-module'
34
id 'javadoc-warnings-error'
45
}

kerberos/kerberos-core/src/main/java/org/springframework/security/kerberos/authentication/JaasSubjectHolder.java

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@
2222

2323
import javax.security.auth.Subject;
2424

25+
import org.jspecify.annotations.Nullable;
26+
2527
import org.springframework.security.kerberos.authentication.sun.SunJaasKerberosClient;
2628

2729
/**
@@ -40,7 +42,7 @@ public class JaasSubjectHolder implements Serializable {
4042

4143
private Subject jaasSubject;
4244

43-
private String username;
45+
private @Nullable String username;
4446

4547
private Map<String, byte[]> savedTokens = new HashMap<String, byte[]>();
4648

@@ -53,7 +55,7 @@ public JaasSubjectHolder(Subject jaasSubject, String username) {
5355
this.username = username;
5456
}
5557

56-
public String getUsername() {
58+
public @Nullable String getUsername() {
5759
return this.username;
5860
}
5961

@@ -65,7 +67,7 @@ public void addToken(String targetService, byte[] outToken) {
6567
this.savedTokens.put(targetService, outToken);
6668
}
6769

68-
public byte[] getToken(String principalName) {
70+
public byte @Nullable [] getToken(String principalName) {
6971
return this.savedTokens.get(principalName);
7072
}
7173

kerberos/kerberos-core/src/main/java/org/springframework/security/kerberos/authentication/KerberosAuthenticationProvider.java

Lines changed: 21 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@
1616

1717
package org.springframework.security.kerberos.authentication;
1818

19+
import org.jspecify.annotations.Nullable;
20+
1921
import org.springframework.security.authentication.AuthenticationProvider;
2022
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
2123
import org.springframework.security.core.Authentication;
@@ -32,17 +34,31 @@
3234
*/
3335
public class KerberosAuthenticationProvider implements AuthenticationProvider {
3436

35-
private KerberosClient kerberosClient;
37+
private @Nullable KerberosClient kerberosClient;
3638

37-
private UserDetailsService userDetailsService;
39+
private @Nullable UserDetailsService userDetailsService;
3840

3941
@Override
4042
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
4143
UsernamePasswordAuthenticationToken auth = (UsernamePasswordAuthenticationToken) authentication;
42-
JaasSubjectHolder subjectHolder = this.kerberosClient.login(auth.getName(), auth.getCredentials().toString());
43-
UserDetails userDetails = this.userDetailsService.loadUserByUsername(subjectHolder.getUsername());
44+
if (this.kerberosClient == null) {
45+
throw new IllegalStateException("kerberosClient must be set");
46+
}
47+
if (this.userDetailsService == null) {
48+
throw new IllegalStateException("userDetailsService must be set");
49+
}
50+
Object credentials = auth.getCredentials();
51+
if (credentials == null) {
52+
throw new IllegalArgumentException("credentials cannot be null");
53+
}
54+
JaasSubjectHolder subjectHolder = this.kerberosClient.login(auth.getName(), credentials.toString());
55+
String username = subjectHolder.getUsername();
56+
if (username == null) {
57+
throw new IllegalStateException("username cannot be null");
58+
}
59+
UserDetails userDetails = this.userDetailsService.loadUserByUsername(username);
4460
KerberosUsernamePasswordAuthenticationToken output = new KerberosUsernamePasswordAuthenticationToken(
45-
userDetails, auth.getCredentials(), userDetails.getAuthorities(), subjectHolder);
61+
userDetails, credentials, userDetails.getAuthorities(), subjectHolder);
4662
output.setDetails(authentication.getDetails());
4763
return output;
4864

kerberos/kerberos-core/src/main/java/org/springframework/security/kerberos/authentication/KerberosMultiTier.java

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
import org.ietf.jgss.GSSManager;
2727
import org.ietf.jgss.GSSName;
2828
import org.ietf.jgss.Oid;
29+
import org.jspecify.annotations.Nullable;
2930

3031
import org.springframework.security.authentication.BadCredentialsException;
3132
import org.springframework.security.core.Authentication;
@@ -59,9 +60,9 @@ public static Authentication authenticateService(Authentication authentication,
5960
final JaasSubjectHolder jaasSubjectHolder = kerberosAuthentication.getJaasSubjectHolder();
6061
Subject subject = jaasSubjectHolder.getJaasSubject();
6162

62-
Subject.doAs(subject, new PrivilegedAction<Object>() {
63+
Subject.doAs(subject, new PrivilegedAction<@Nullable Object>() {
6364
@Override
64-
public Object run() {
65+
public @Nullable Object run() {
6566
runAuthentication(jaasSubjectHolder, username, lifetimeInSeconds, targetService);
6667

6768
return null;
@@ -71,7 +72,7 @@ public Object run() {
7172
return authentication;
7273
}
7374

74-
public static byte[] getTokenForService(Authentication authentication, String principalName) {
75+
public static byte @Nullable [] getTokenForService(Authentication authentication, String principalName) {
7576
KerberosAuthentication kerberosAuthentication = (KerberosAuthentication) authentication;
7677
final JaasSubjectHolder jaasSubjectHolder = kerberosAuthentication.getJaasSubjectHolder();
7778

kerberos/kerberos-core/src/main/java/org/springframework/security/kerberos/authentication/KerberosServiceAuthenticationProvider.java

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818

1919
import org.apache.commons.logging.Log;
2020
import org.apache.commons.logging.LogFactory;
21+
import org.jspecify.annotations.Nullable;
2122

2223
import org.springframework.beans.factory.InitializingBean;
2324
import org.springframework.security.authentication.AccountStatusUserDetailsChecker;
@@ -55,9 +56,9 @@ public class KerberosServiceAuthenticationProvider implements AuthenticationProv
5556

5657
private static final Log LOG = LogFactory.getLog(KerberosServiceAuthenticationProvider.class);
5758

58-
private KerberosTicketValidator ticketValidator;
59+
private @Nullable KerberosTicketValidator ticketValidator;
5960

60-
private UserDetailsService userDetailsService;
61+
private @Nullable UserDetailsService userDetailsService;
6162

6263
private UserDetailsChecker userDetailsChecker = new AccountStatusUserDetailsChecker();
6364

@@ -66,6 +67,12 @@ public Authentication authenticate(Authentication authentication) throws Authent
6667
KerberosServiceRequestToken auth = (KerberosServiceRequestToken) authentication;
6768
byte[] token = auth.getToken();
6869
LOG.debug("Try to validate Kerberos Token");
70+
if (this.ticketValidator == null) {
71+
throw new IllegalStateException("ticketValidator must be set");
72+
}
73+
if (this.userDetailsService == null) {
74+
throw new IllegalStateException("userDetailsService must be set");
75+
}
6976
KerberosTicketValidation ticketValidation = this.ticketValidator.validateTicket(token);
7077
LOG.debug("Successfully validated " + ticketValidation.username());
7178
UserDetails userDetails = this.userDetailsService.loadUserByUsername(ticketValidation.username());

kerberos/kerberos-core/src/main/java/org/springframework/security/kerberos/authentication/KerberosServiceRequestToken.java

Lines changed: 31 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626

2727
import org.ietf.jgss.GSSContext;
2828
import org.ietf.jgss.MessageProp;
29+
import org.jspecify.annotations.Nullable;
2930

3031
import org.springframework.security.authentication.AbstractAuthenticationToken;
3132
import org.springframework.security.core.GrantedAuthority;
@@ -56,11 +57,11 @@ public class KerberosServiceRequestToken extends AbstractAuthenticationToken imp
5657

5758
private final byte[] token;
5859

59-
private final Object principal;
60+
private final @Nullable Object principal;
6061

61-
private final transient KerberosTicketValidation ticketValidation;
62+
private final transient @Nullable KerberosTicketValidation ticketValidation;
6263

63-
private JaasSubjectHolder jaasSubjectHolder;
64+
private @Nullable JaasSubjectHolder jaasSubjectHolder;
6465

6566
/**
6667
* Creates an authenticated token, normally used as an output of an authentication
@@ -127,12 +128,12 @@ public int hashCode() {
127128
}
128129

129130
@Override
130-
public Object getCredentials() {
131+
public @Nullable Object getCredentials() {
131132
return null;
132133
}
133134

134135
@Override
135-
public Object getPrincipal() {
136+
public @Nullable Object getPrincipal() {
136137
return this.principal;
137138
}
138139

@@ -148,7 +149,7 @@ public byte[] getToken() {
148149
* Gets the ticket validation
149150
* @return the ticket validation (which will be null if the token is unauthenticated)
150151
*/
151-
public KerberosTicketValidation getTicketValidation() {
152+
public @Nullable KerberosTicketValidation getTicketValidation() {
152153
return this.ticketValidation;
153154
}
154155

@@ -168,6 +169,9 @@ public String getEncodedResponseToken() {
168169
if (!hasResponseToken()) {
169170
throw new IllegalStateException("Unauthenticated or no response token");
170171
}
172+
if (this.ticketValidation == null) {
173+
throw new IllegalStateException("Ticket validation is not available");
174+
}
171175
return Base64.getEncoder().encodeToString(this.ticketValidation.responseToken());
172176
}
173177

@@ -180,9 +184,16 @@ public String getEncodedResponseToken() {
180184
* @throws PrivilegedActionException if jaas throws and error
181185
*/
182186
public byte[] decrypt(final byte[] data, final int offset, final int length) throws PrivilegedActionException {
183-
return Subject.doAs(getTicketValidation().subject(), new PrivilegedExceptionAction<byte[]>() {
187+
KerberosTicketValidation validation = getTicketValidation();
188+
if (validation == null) {
189+
throw new IllegalStateException("Cannot decrypt without ticket validation");
190+
}
191+
return Subject.doAs(validation.subject(), new PrivilegedExceptionAction<byte[]>() {
184192
public byte[] run() throws Exception {
185-
final GSSContext context = getTicketValidation().getGssContext();
193+
final GSSContext context = validation.getGssContext();
194+
if (context == null) {
195+
throw new IllegalStateException("GSSContext is not available");
196+
}
186197
return context.unwrap(data, offset, length, new MessageProp(true));
187198
}
188199
});
@@ -207,9 +218,16 @@ public byte[] decrypt(final byte[] data) throws PrivilegedActionException {
207218
* @throws PrivilegedActionException if jaas throws and error
208219
*/
209220
public byte[] encrypt(final byte[] data, final int offset, final int length) throws PrivilegedActionException {
210-
return Subject.doAs(getTicketValidation().subject(), new PrivilegedExceptionAction<byte[]>() {
221+
KerberosTicketValidation validation = getTicketValidation();
222+
if (validation == null) {
223+
throw new IllegalStateException("Cannot encrypt without ticket validation");
224+
}
225+
return Subject.doAs(validation.subject(), new PrivilegedExceptionAction<byte[]>() {
211226
public byte[] run() throws Exception {
212-
final GSSContext context = getTicketValidation().getGssContext();
227+
final GSSContext context = validation.getGssContext();
228+
if (context == null) {
229+
throw new IllegalStateException("GSSContext is not available");
230+
}
213231
return context.wrap(data, offset, length, new MessageProp(true));
214232
}
215233
});
@@ -227,6 +245,9 @@ public byte[] encrypt(final byte[] data) throws PrivilegedActionException {
227245

228246
@Override
229247
public JaasSubjectHolder getJaasSubjectHolder() {
248+
if (this.jaasSubjectHolder == null) {
249+
throw new IllegalStateException("JaasSubjectHolder is not available for unauthenticated token");
250+
}
230251
return this.jaasSubjectHolder;
231252
}
232253

kerberos/kerberos-core/src/main/java/org/springframework/security/kerberos/authentication/KerberosTicketValidation.java

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323

2424
import org.ietf.jgss.GSSContext;
2525
import org.ietf.jgss.GSSCredential;
26+
import org.jspecify.annotations.Nullable;
2627

2728
/**
2829
* Result of ticket validation
@@ -35,17 +36,17 @@ public final class KerberosTicketValidation {
3536

3637
private final byte[] responseToken;
3738

38-
private final GSSContext gssContext;
39+
private final @Nullable GSSContext gssContext;
3940

40-
private final GSSCredential delegationCredential;
41+
private final @Nullable GSSCredential delegationCredential;
4142

4243
public KerberosTicketValidation(String username, String servicePrincipal, byte[] responseToken,
43-
GSSContext gssContext) {
44+
@Nullable GSSContext gssContext) {
4445
this(username, servicePrincipal, responseToken, gssContext, null);
4546
}
4647

4748
public KerberosTicketValidation(String username, String servicePrincipal, byte[] responseToken,
48-
GSSContext gssContext, GSSCredential delegationCredential) {
49+
@Nullable GSSContext gssContext, @Nullable GSSCredential delegationCredential) {
4950
final HashSet<KerberosPrincipal> princs = new HashSet<KerberosPrincipal>();
5051
princs.add(new KerberosPrincipal(servicePrincipal));
5152

@@ -56,12 +57,13 @@ public KerberosTicketValidation(String username, String servicePrincipal, byte[]
5657
this.delegationCredential = delegationCredential;
5758
}
5859

59-
public KerberosTicketValidation(String username, Subject subject, byte[] responseToken, GSSContext gssContext) {
60+
public KerberosTicketValidation(String username, Subject subject, byte[] responseToken,
61+
@Nullable GSSContext gssContext) {
6062
this(username, subject, responseToken, gssContext, null);
6163
}
6264

63-
public KerberosTicketValidation(String username, Subject subject, byte[] responseToken, GSSContext gssContext,
64-
GSSCredential delegationCredential) {
65+
public KerberosTicketValidation(String username, Subject subject, byte[] responseToken,
66+
@Nullable GSSContext gssContext, @Nullable GSSCredential delegationCredential) {
6567
this.username = username;
6668
this.subject = subject;
6769
this.responseToken = responseToken;
@@ -77,15 +79,15 @@ public byte[] responseToken() {
7779
return this.responseToken;
7880
}
7981

80-
public GSSContext getGssContext() {
82+
public @Nullable GSSContext getGssContext() {
8183
return this.gssContext;
8284
}
8385

8486
public Subject subject() {
8587
return this.subject;
8688
}
8789

88-
public GSSCredential getDelegationCredential() {
90+
public @Nullable GSSCredential getDelegationCredential() {
8991
return this.delegationCredential;
9092
}
9193

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
/*
2+
* Copyright 2004-present the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
@NullMarked
18+
package org.springframework.security.kerberos.authentication;
19+
20+
import org.jspecify.annotations.NullMarked;

kerberos/kerberos-core/src/main/java/org/springframework/security/kerberos/authentication/sun/GlobalSunJaasKerberosConfig.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@
1616

1717
package org.springframework.security.kerberos.authentication.sun;
1818

19+
import org.jspecify.annotations.Nullable;
20+
1921
import org.springframework.beans.BeansException;
2022
import org.springframework.beans.factory.InitializingBean;
2123
import org.springframework.beans.factory.config.BeanPostProcessor;
@@ -30,7 +32,7 @@ public class GlobalSunJaasKerberosConfig implements BeanPostProcessor, Initializ
3032

3133
private boolean debug = false;
3234

33-
private String krbConfLocation;
35+
private @Nullable String krbConfLocation;
3436

3537
@Override
3638
public void afterPropertiesSet() throws Exception {

0 commit comments

Comments
 (0)