001: /*
002: * JBoss, Home of Professional Open Source.
003: * Copyright 2006, Red Hat Middleware LLC, and individual contributors
004: * as indicated by the @author tags. See the copyright.txt file in the
005: * distribution for a full listing of individual contributors.
006: *
007: * This is free software; you can redistribute it and/or modify it
008: * under the terms of the GNU Lesser General Public License as
009: * published by the Free Software Foundation; either version 2.1 of
010: * the License, or (at your option) any later version.
011: *
012: * This software is distributed in the hope that it will be useful,
013: * but WITHOUT ANY WARRANTY; without even the implied warranty of
014: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
015: * Lesser General Public License for more details.
016: *
017: * You should have received a copy of the GNU Lesser General Public
018: * License along with this software; if not, write to the Free
019: * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
020: * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
021: */
022: package org.jboss.iiop.csiv2;
023:
024: /***************************************
025: * *
026: * JBoss: The OpenSource J2EE WebOS *
027: * *
028: * Distributable under LGPL license. *
029: * See terms of license at gnu.org. *
030: * *
031: ***************************************/
032:
033: import org.omg.CORBA.Any;
034: import org.omg.CORBA.BAD_PARAM;
035: import org.omg.CORBA.MARSHAL;
036: import org.omg.CORBA.NO_PERMISSION;
037: import org.omg.CORBA.LocalObject;
038: import org.omg.CORBA.ORB;
039: import org.omg.CSI.CompleteEstablishContext;
040: import org.omg.CSI.ContextError;
041: import org.omg.CSI.EstablishContext;
042: import org.omg.CSI.GSS_NT_ExportedNameHelper;
043: import org.omg.CSI.ITTPrincipalName;
044: import org.omg.CSI.IdentityToken;
045: import org.omg.CSI.MTEstablishContext;
046: import org.omg.CSI.MTMessageInContext;
047: import org.omg.CSI.SASContextBody;
048: import org.omg.CSI.SASContextBodyHelper;
049: import org.omg.GSSUP.ErrorToken;
050: import org.omg.GSSUP.ErrorTokenHelper;
051: import org.omg.GSSUP.GSS_UP_S_G_UNSPECIFIED;
052: import org.omg.GSSUP.InitialContextToken;
053: import org.omg.IOP.Codec;
054: import org.omg.IOP.CodecPackage.FormatMismatch;
055: import org.omg.IOP.CodecPackage.TypeMismatch;
056: import org.omg.IOP.CodecPackage.InvalidTypeForEncoding;
057: import org.omg.IOP.ServiceContext;
058: import org.omg.PortableInterceptor.ServerRequestInfo;
059: import org.omg.PortableInterceptor.ServerRequestInterceptor;
060:
061: import org.jboss.iiop.CorbaORBService;
062: import org.jboss.logging.Logger;
063:
064: /**
065: * This implementation of
066: * <code>org.omg.PortableInterceptor.ServerRequestInterceptor</code>
067: * extracts the security attribute service (SAS) context from incoming IIOP
068: * and inserts SAS messages into the SAS context of outgoing IIOP replies.
069: *
070: * @author <a href="mailto:reverbel@ime.usp.br">Francisco Reverbel</a>
071: * @version $Revision: 57194 $
072: */
073: public class SASTargetInterceptor extends LocalObject implements
074: ServerRequestInterceptor {
075:
076: // Static fields and initializers ---------------------------------
077: static final long serialVersionUID = 3352317126682935627L;
078:
079: private static final Logger log = Logger
080: .getLogger(SASTargetInterceptor.class);
081: private static final boolean traceEnabled = log.isTraceEnabled();
082:
083: private static final int sasContextId = org.omg.IOP.SecurityAttributeService.value;
084:
085: private static final byte[] empty = new byte[0];
086: private static final IdentityToken absent;
087:
088: /** Scratch field for <code>CompleteEstablishContext<code> messages */
089: private static final SASContextBody msgBodyCtxAccepted;
090:
091: /** Ready-to-go <code>CompleteEstablishContext<code> message
092: with context id set to zero */
093: private static final Any msgCtx0Accepted;
094:
095: static {
096: // initialize absent
097: absent = new IdentityToken();
098: absent.absent(true);
099:
100: // initialize msgBodyCtxAccepted
101: // (Note that "context stateful" is always set to false. Even if the
102: // client wants a stateful context, we negotiate the context down to
103: // stateless.)
104: CompleteEstablishContext ctxAccepted = new CompleteEstablishContext(
105: 0, /* context id */
106: false, /* context stateful */
107: new byte[0] /* no final token */);
108:
109: msgBodyCtxAccepted = new SASContextBody();
110: msgBodyCtxAccepted.complete_msg(ctxAccepted);
111:
112: // initialize msgCtx0Accepted
113: msgCtx0Accepted = createMsgCtxAccepted(0);
114: }
115:
116: // Static methods ------------------------------------------------
117:
118: private static Any createMsgCtxAccepted(long contextId) {
119: Any any = ORB.init().create_any();
120: synchronized (msgBodyCtxAccepted) {
121: msgBodyCtxAccepted.complete_msg().client_context_id = contextId;
122: SASContextBodyHelper.insert(any, msgBodyCtxAccepted);
123: }
124: return any;
125: }
126:
127: // Fields ---------------------------------------------------------
128:
129: private final Codec codec;
130:
131: /** Scratch field for <code>ContextError<code> messages */
132: private final SASContextBody msgBodyCtxError;
133:
134: /** Ready-to-go <code>ContextError<code> message with context id set to
135: zero and major status "invalid evidence" */
136: private final Any msgCtx0Rejected;
137:
138: private ThreadLocal threadLocalData = new ThreadLocal() {
139: protected synchronized Object initialValue() {
140: return new CurrentRequestInfo(); // see nested class below
141: }
142: };
143:
144: // Nested class -------------------------------------------------
145:
146: /**
147: * The <code>CurrentRequestInfo</code> class holds SAS information
148: * associated with IIOP request handled by the current thread.
149: */
150: private static class CurrentRequestInfo {
151: boolean sasContextReceived;
152: boolean authenticationTokenReceived;
153: byte[] incomingUsername;
154: byte[] incomingPassword;
155: byte[] incomingTargetName;
156: IdentityToken incomingIdentity;
157: byte[] incomingPrincipalName;
158: long contextId;
159: Any sasReply;
160: boolean sasReplyIsAccept; // true if sasReply is
161:
162: // CompleteEstablishContext (for
163: // interoperability with IONA's ASP 6.0)
164: CurrentRequestInfo() {
165: }
166: }
167:
168: // Private method ------------------------------------------------
169:
170: private Any createMsgCtxError(long contextId, int majorStatus) {
171: Any any = ORB.init().create_any();
172: synchronized (msgBodyCtxError) {
173: msgBodyCtxError.error_msg().client_context_id = contextId;
174: msgBodyCtxError.error_msg().major_status = majorStatus;
175: SASContextBodyHelper.insert(any, msgBodyCtxError);
176: }
177: return any;
178: }
179:
180: // Constructor ---------------------------------------------------
181:
182: public SASTargetInterceptor(Codec codec) {
183: this .codec = codec;
184:
185: // build encapsulated GSSUP error token for ContextError messages
186: // (the error code within the error token is GSS_UP_S_G_UNSPECIFIED,
187: // which says nothing about the cause of the error)
188: ErrorToken errorToken = new ErrorToken(
189: GSS_UP_S_G_UNSPECIFIED.value);
190: Any any = ORB.init().create_any();
191: byte[] encapsulatedErrorToken;
192:
193: ErrorTokenHelper.insert(any, errorToken);
194: try {
195: encapsulatedErrorToken = codec.encode_value(any);
196: } catch (InvalidTypeForEncoding e) {
197: throw new RuntimeException("Unexpected exception: " + e);
198: }
199:
200: // initialize msgBodyCtxError
201: ContextError ctxError = new ContextError(0, /* context id */
202: 1, /* major status: invalid evidence */
203: 1, /* minor status (always 1) */
204: encapsulatedErrorToken);
205:
206: msgBodyCtxError = new SASContextBody();
207: msgBodyCtxError.error_msg(ctxError);
208:
209: // initialize msgCtx0Rejected (major status: invalid evidence)
210: msgCtx0Rejected = createMsgCtxError(0, 1);
211:
212: }
213:
214: // Methods -------------------------------------------------------
215:
216: /**
217: * Returns true if an SAS context arrived with the current IIOP request.
218: */
219: boolean sasContextReceived() {
220: CurrentRequestInfo threadLocal = (CurrentRequestInfo) threadLocalData
221: .get();
222: return threadLocal.sasContextReceived;
223: }
224:
225: /**
226: * Returns true if a client authentication token arrived with the
227: * current IIOP request.
228: */
229: boolean authenticationTokenReceived() {
230: CurrentRequestInfo threadLocal = (CurrentRequestInfo) threadLocalData
231: .get();
232: return threadLocal.authenticationTokenReceived;
233: }
234:
235: /**
236: * Returns the username that arrived in the current IIOP request.
237: */
238: byte[] getIncomingUsername() {
239: CurrentRequestInfo threadLocal = (CurrentRequestInfo) threadLocalData
240: .get();
241: return threadLocal.incomingUsername;
242: }
243:
244: /**
245: * Returns the password that arrived in the current IIOP request.
246: */
247: byte[] getIncomingPassword() {
248: CurrentRequestInfo threadLocal = (CurrentRequestInfo) threadLocalData
249: .get();
250: return threadLocal.incomingPassword;
251: }
252:
253: /**
254: * Returns the target name that arrived in the current IIOP request.
255: */
256: byte[] getIncomingTargetName() {
257: CurrentRequestInfo threadLocal = (CurrentRequestInfo) threadLocalData
258: .get();
259: return threadLocal.incomingTargetName;
260: }
261:
262: /**
263: * Returns the <code>org.omg.CSI.IdentityToken<code> that arrived in
264: * the current IIOP request.
265: */
266: IdentityToken getIncomingIdentity() {
267: CurrentRequestInfo threadLocal = (CurrentRequestInfo) threadLocalData
268: .get();
269: return threadLocal.incomingIdentity;
270: }
271:
272: /**
273: * Returns the principal name that arrived in the current IIOP request.
274: */
275: byte[] getIncomingPrincipalName() {
276: CurrentRequestInfo threadLocal = (CurrentRequestInfo) threadLocalData
277: .get();
278: return threadLocal.incomingPrincipalName;
279: }
280:
281: /**
282: * Sets the outgoing SAS reply to <code>ContextError</code>, with major
283: * status "invalid evidence".
284: */
285: void rejectIncomingContext() {
286: CurrentRequestInfo threadLocal = (CurrentRequestInfo) threadLocalData
287: .get();
288:
289: if (threadLocal.sasContextReceived) {
290: threadLocal.sasReply = (threadLocal.contextId == 0) ? msgCtx0Rejected
291: : createMsgCtxError(threadLocal.contextId, 1 /* major status: invalid evidence */);
292: threadLocal.sasReplyIsAccept = false;
293: }
294: }
295:
296: // org.omg.PortableInterceptor.Interceptor operations ------------
297:
298: public String name() {
299: return "SASTargetInterceptor";
300: }
301:
302: public void destroy() {
303: // do nothing
304: }
305:
306: // ServerRequestInterceptor operations ---------------------------
307:
308: public void receive_request_service_contexts(ServerRequestInfo ri) {
309: // do nothing
310: }
311:
312: // ServerRequestInterceptor operations ---------------------------
313:
314: public void receive_request(ServerRequestInfo ri) {
315: if (traceEnabled)
316: log.trace("receive_request " + ri.operation());
317: CurrentRequestInfo threadLocal = (CurrentRequestInfo) threadLocalData
318: .get();
319:
320: threadLocal.sasContextReceived = false;
321: threadLocal.authenticationTokenReceived = false;
322: threadLocal.incomingUsername = empty;
323: threadLocal.incomingPassword = empty;
324: threadLocal.incomingTargetName = empty;
325: threadLocal.incomingIdentity = absent;
326: threadLocal.incomingPrincipalName = empty;
327: threadLocal.sasReply = null;
328: threadLocal.sasReplyIsAccept = false;
329:
330: try {
331: ServiceContext sc = ri
332: .get_request_service_context(sasContextId);
333: Any any = codec.decode_value(sc.context_data,
334: SASContextBodyHelper.type());
335: SASContextBody contextBody = SASContextBodyHelper
336: .extract(any);
337:
338: if (contextBody == null) {
339: // we're done
340: return;
341: } else if (contextBody.discriminator() == MTMessageInContext.value) {
342: // should not happen, as stateful context requests are always
343: // negotiated down to stateless in this implementation
344: long contextId = contextBody.in_context_msg().client_context_id;
345: threadLocal.sasReply = createMsgCtxError(contextId, 4 /* major status: no context */);
346: throw new NO_PERMISSION("SAS context does not exist.");
347: } else if (contextBody.discriminator() == MTEstablishContext.value) {
348: EstablishContext message = contextBody.establish_msg();
349: threadLocal.contextId = message.client_context_id;
350: threadLocal.sasContextReceived = true;
351:
352: if (message.client_authentication_token != null
353: && message.client_authentication_token.length > 0) {
354: if (traceEnabled)
355: log
356: .trace("received client authentication token");
357: InitialContextToken authToken = CSIv2Util
358: .decodeInitialContextToken(
359: message.client_authentication_token,
360: codec);
361: if (authToken == null) {
362: threadLocal.sasReply = createMsgCtxError(
363: message.client_context_id, 2 /* major status:
364: invalid mechanism */);
365: throw new NO_PERMISSION("Could not decode "
366: + "initial context token.");
367: }
368: threadLocal.incomingUsername = authToken.username;
369: threadLocal.incomingPassword = authToken.password;
370: threadLocal.incomingTargetName = CSIv2Util
371: .decodeGssExportedName(authToken.target_name);
372: if (threadLocal.incomingTargetName == null) {
373: threadLocal.sasReply = createMsgCtxError(
374: message.client_context_id, 2 /* major status:
375: invalid mechanism */);
376: throw new NO_PERMISSION(
377: "Could not decode target name "
378: + "in initial context token.");
379: }
380:
381: threadLocal.authenticationTokenReceived = true;
382: }
383: if (message.identity_token != null) {
384: if (traceEnabled)
385: log.trace("received identity token");
386: threadLocal.incomingIdentity = message.identity_token;
387: if (message.identity_token.discriminator() == ITTPrincipalName.value) {
388: // Extract the RFC2743-encoded name
389: // from CDR encapsulation
390: Any a = codec
391: .decode_value(message.identity_token
392: .principal_name(),
393: GSS_NT_ExportedNameHelper
394: .type());
395: byte[] encodedName = GSS_NT_ExportedNameHelper
396: .extract(a);
397:
398: // Decode the principal name
399: threadLocal.incomingPrincipalName = CSIv2Util
400: .decodeGssExportedName(encodedName);
401:
402: if (threadLocal.incomingPrincipalName == null) {
403: threadLocal.sasReply = createMsgCtxError(
404: message.client_context_id, 2 /* major status:
405: invalid mechanism */);
406: throw new NO_PERMISSION("Could not decode "
407: + "incoming principal name.");
408: }
409: }
410: }
411: threadLocal.sasReply = (threadLocal.contextId == 0) ? msgCtx0Accepted
412: : createMsgCtxAccepted(threadLocal.contextId);
413: threadLocal.sasReplyIsAccept = true;
414: }
415: } catch (BAD_PARAM e) {
416: // no service context with sasContextId: do nothing
417: } catch (FormatMismatch e) {
418: throw new MARSHAL("Exception decoding context data in "
419: + "SASTargetInterceptor: " + e);
420: } catch (TypeMismatch e) {
421: throw new MARSHAL("Exception decoding context data in "
422: + "SASTargetInterceptor: " + e);
423: }
424: }
425:
426: public void send_reply(ServerRequestInfo ri) {
427: if (traceEnabled)
428: log.trace("send_reply " + ri.operation());
429: CurrentRequestInfo threadLocal = (CurrentRequestInfo) threadLocalData
430: .get();
431:
432: if (threadLocal.sasReply != null) {
433: try {
434: ServiceContext sc = new ServiceContext(sasContextId,
435: codec.encode_value(threadLocal.sasReply));
436: ri.add_reply_service_context(sc, true);
437: } catch (InvalidTypeForEncoding e) {
438: throw new MARSHAL("Unexpected exception: " + e);
439: }
440: }
441: }
442:
443: public void send_exception(ServerRequestInfo ri) {
444: if (traceEnabled)
445: log.trace("send_exception " + ri.operation() + ": ");
446: CurrentRequestInfo threadLocal = (CurrentRequestInfo) threadLocalData
447: .get();
448:
449: // The check for sasReplyIsAccept below was added for interoperability
450: // with IONA's ASP 6.0, which throws an ArrayIndexOutOfBoundsException
451: // when it receives an IIOP reply carrying both an application exception
452: // and a SAS reply CompleteEstablishContext. The sasReplyIsAccept flag
453: // serves the purpose of refraining from sending an SAS accept
454: // (CompleteEstablishContext) reply together with an exception.
455: //
456: // The CSIv2 spec does not explicitly disallow an SAS accept in an
457: // IIOP exception reply.
458: //
459: if (threadLocal.sasReply != null
460: && (!threadLocal.sasReplyIsAccept || CorbaORBService
461: .getSendSASAcceptWithExceptionEnabledFlag() == true)) {
462: try {
463: ServiceContext sc = new ServiceContext(sasContextId,
464: codec.encode_value(threadLocal.sasReply));
465: ri.add_reply_service_context(sc, true);
466: } catch (InvalidTypeForEncoding e) {
467: throw new MARSHAL("Unexpected exception: " + e);
468: }
469: }
470: }
471:
472: public void send_other(ServerRequestInfo ri) {
473: // Do nothing. According to the SAS spec, LOCATION_FORWARD reply
474: // carries no SAS message.
475: }
476: }
|