-
Notifications
You must be signed in to change notification settings - Fork 138
/
ServerSecurityPipe.java
261 lines (220 loc) · 10.9 KB
/
ServerSecurityPipe.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
/*
* Copyright (c) 2023 Contributors to the Eclipse Foundation.
* Copyright (c) 1997, 2018 Oracle and/or its affiliates. All rights reserved.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0, which is available at
* http://www.eclipse.org/legal/epl-2.0.
*
* This Source Code may also be made available under the following Secondary
* Licenses when the conditions for such availability set forth in the
* Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
* version 2 with the GNU Classpath Exception, which is available at
* https://www.gnu.org/software/classpath/license.html.
*
* SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
*/
package com.sun.enterprise.security.webservices.server;
import static com.sun.enterprise.security.webservices.LogUtils.ERROR_RESPONSE_SECURING;
import static com.sun.xml.wss.provider.wsit.PipeConstants.CLIENT_SUBJECT;
import static com.sun.xml.wss.provider.wsit.PipeConstants.SECURITY_PIPE;
import static com.sun.xml.wss.provider.wsit.PipeConstants.SOAP_LAYER;
import static java.util.logging.Level.FINE;
import static java.util.logging.Level.INFO;
import static java.util.logging.Level.SEVERE;
import com.sun.enterprise.security.ee.jmac.callback.ServerContainerCallbackHandler;
import com.sun.enterprise.security.webservices.LogUtils;
import com.sun.enterprise.security.webservices.SoapAuthenticationService;
import com.sun.enterprise.util.LocalStringManagerImpl;
import com.sun.xml.ws.api.message.Packet;
import com.sun.xml.ws.api.pipe.Pipe;
import com.sun.xml.ws.api.pipe.PipeCloner;
import com.sun.xml.ws.api.pipe.helper.AbstractFilterPipeImpl;
import com.sun.xml.wss.provider.wsit.PacketMapMessageInfo;
import com.sun.xml.wss.provider.wsit.PacketMessageInfo;
import com.sun.xml.wss.provider.wsit.PipeConstants;
import jakarta.security.auth.message.AuthException;
import jakarta.security.auth.message.AuthStatus;
import jakarta.security.auth.message.config.ServerAuthContext;
import jakarta.xml.ws.WebServiceException;
import java.security.PrivilegedActionException;
import java.security.PrivilegedExceptionAction;
import java.util.Map;
import java.util.logging.Logger;
import javax.security.auth.Subject;
/**
* This pipe uses Jakarta Authentication to authenticate messages / packages.
*/
@SuppressWarnings("deprecation")
public class ServerSecurityPipe extends AbstractFilterPipeImpl {
protected static final Logger _logger = LogUtils.getLogger();
protected static final LocalStringManagerImpl localStrings = new LocalStringManagerImpl(ServerSecurityPipe.class);
private static final String WSIT_SERVER_AUTH_CONTEXT = "com.sun.xml.wss.provider.wsit.WSITServerAuthContext";
private final boolean isHttpBinding;
private final SoapAuthenticationService authenticationService;
public ServerSecurityPipe(Map<String, Object> props, final Pipe next, boolean isHttpBinding) {
super(next);
props.put(SECURITY_PIPE, this);
this.authenticationService = new SoapAuthenticationService(SOAP_LAYER, props, new ServerContainerCallbackHandler());
this.isHttpBinding = isHttpBinding;
}
protected ServerSecurityPipe(ServerSecurityPipe that, PipeCloner cloner) {
super(that, cloner);
// We can share the soapAuthenticationService for all pipes so that the remove registration (in server side) can be
// done properly
this.authenticationService = that.authenticationService;
this.isHttpBinding = that.isHttpBinding;
}
/**
* This method is called once in server side and at most one in client side.
*/
@Override
public void preDestroy() {
authenticationService.disable();
try {
Packet request = new Packet();
PacketMessageInfo messageInfo = new PacketMapMessageInfo(request, new Packet());
Subject subject = new Subject();
ServerAuthContext serverAuthContext = authenticationService.getServerAuthContext(messageInfo, subject);
if (serverAuthContext != null && WSIT_SERVER_AUTH_CONTEXT.equals(serverAuthContext.getClass().getName())) {
serverAuthContext.cleanSubject(messageInfo, subject);
}
} catch (Exception ex) {
// ignore exceptions
}
next.preDestroy();
}
/**
* This is used in creating subsequent pipes.
*/
@Override
public Pipe copy(PipeCloner cloner) {
return new ServerSecurityPipe(this, cloner);
}
@Override
public Packet process(Packet request) {
if (isHttpBinding) {
return next.process(request);
}
Packet response;
try {
response = processRequest(request);
} catch (Exception e) {
_logger.log(FINE, "Failure in security pipe process", e);
response = authenticationService.makeFaultResponse(null, e);
}
return response;
}
private Packet processRequest(Packet request) throws Exception {
AuthStatus status = AuthStatus.SUCCESS;
PacketMessageInfo info = new PacketMapMessageInfo(request, new Packet());
// XXX at this time, we expect the server subject to be null
Subject serverSubject = (Subject) request.invocationProperties.get(PipeConstants.SERVER_SUBJECT);
// Could change the request packet
ServerAuthContext serverAuthContext = authenticationService.getServerAuthContext(info, serverSubject);
Subject clientSubject = getClientSubject(request);
final Packet validatedRequest;
try {
if (serverAuthContext != null) {
// Client subject must not be null and when return status is SUCCESS, module
// must have called handler.handle(CallerPrincipalCallback)
status = serverAuthContext.validateRequest(info, clientSubject, serverSubject);
}
} catch (Exception e) {
_logger.log(SEVERE, LogUtils.ERROR_REQUEST_VALIDATION, e);
WebServiceException wse = new WebServiceException(localStrings.getLocalString("enterprise.webservice.cantValidateRequest",
"Cannot validate request for {0}", new Object[] { authenticationService.getModelName() }), e);
// Set status for audit
status = AuthStatus.SEND_FAILURE;
// If unable to determine if two-way will return empty response
return authenticationService.getFaultResponse(info.getRequestPacket(), info.getResponsePacket(), wse);
} finally {
validatedRequest = info.getRequestPacket();
authenticationService.auditInvocation(validatedRequest, status);
}
_logger.log(FINE, "Validation status: {0}", status);
Packet response = null;
if (status == AuthStatus.SUCCESS) {
boolean authorized = false;
try {
authenticationService.authorize(validatedRequest);
authorized = true;
} catch (Exception e) {
// Not authorized, construct fault and proceded
response = authenticationService.getFaultResponse(validatedRequest, info.getResponsePacket(), e);
}
if (authorized) {
// only do doAdPriv if SecurityManager is in effect
if (System.getSecurityManager() == null) {
try {
// proceed to invoke the endpoint
response = next.process(validatedRequest);
} catch (Exception e) {
_logger.log(SEVERE, LogUtils.NEXT_PIPE, e);
response = authenticationService.getFaultResponse(validatedRequest, info.getResponsePacket(), e);
}
} else {
try {
response = Subject.doAsPrivileged(clientSubject, new PrivilegedExceptionAction<Packet>() {
@Override
public Packet run() throws Exception {
// proceed to invoke the endpoint
return next.process(validatedRequest);
}
}, null);
} catch (PrivilegedActionException pae) {
Throwable cause = pae.getCause();
_logger.log(SEVERE, LogUtils.NEXT_PIPE, cause);
response = authenticationService.getFaultResponse(validatedRequest, info.getResponsePacket(), cause);
}
}
}
// Pipes are not supposed to return a null response packet
if (response == null) {
WebServiceException wse = new WebServiceException(localStrings.getLocalString("enterprise.webservice.nullResponsePacket",
"Invocation of Service {0} returned null response packet", new Object[] { authenticationService.getModelName() }));
response = authenticationService.getFaultResponse(validatedRequest, info.getResponsePacket(), wse);
_logger.log(SEVERE, LogUtils.EXCEPTION_THROWN, wse);
}
// Secure response, including if it is a fault
if (serverAuthContext != null && response.getMessage() != null) {
info.setResponsePacket(response);
response = processResponse(info, serverAuthContext, serverSubject);
}
} else {
// ValidateRequest did not return success
// Even for one-way mep, may return response with non-empty message
response = info.getResponsePacket();
}
return response;
}
// Called when secureResponse is to be called
private Packet processResponse(PacketMessageInfo info, ServerAuthContext serverAuthContext, Subject serverSubject) throws Exception {
AuthStatus status;
try {
status = serverAuthContext.secureResponse(info, serverSubject);
} catch (Exception e) {
if (e instanceof AuthException) {
_logger.log(INFO, ERROR_RESPONSE_SECURING, e);
} else {
_logger.log(SEVERE, ERROR_RESPONSE_SECURING, e);
}
return authenticationService.makeFaultResponse(info.getResponsePacket(), e);
}
_logger.log(FINE, "ws.status_secure_response", status);
return info.getResponsePacket();
}
private static Subject getClientSubject(Packet packet) {
Subject clientSubject = null;
if (packet != null) {
clientSubject = (Subject) packet.invocationProperties.get(CLIENT_SUBJECT);
}
if (clientSubject == null) {
clientSubject = SoapAuthenticationService.getClientSubject();
if (packet != null) {
packet.invocationProperties.put(CLIENT_SUBJECT, clientSubject);
}
}
return clientSubject;
}
}