001: /**
002: * JOnAS: Java(TM) Open Application Server
003: * Copyright (C) 2004 Bull S.A.
004: * Contact: jonas-team@objectweb.org
005: *
006: * This library is free software; you can redistribute it and/or
007: * modify it under the terms of the GNU Lesser General Public
008: * License as published by the Free Software Foundation; either
009: * version 2.1 of the License, or any later version.
010: *
011: * This library is distributed in the hope that it will be useful,
012: * but WITHOUT ANY WARRANTY; without even the implied warranty of
013: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
014: * Lesser General Public License for more details.
015: *
016: * You should have received a copy of the GNU Lesser General Public
017: * License along with this library; if not, write to the Free Software
018: * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
019: * USA
020: *
021: * --------------------------------------------------------------------------
022: * $Id: Csiv2ClientInterceptor.java 6058 2005-01-07 13:28:28Z joaninh $
023: * --------------------------------------------------------------------------
024: */package org.objectweb.jonas.security.iiop;
025:
026: import java.io.IOException;
027: import java.io.UnsupportedEncodingException;
028:
029: import org.omg.CORBA.Any;
030: import org.omg.CORBA.BAD_PARAM;
031: import org.omg.CSI.AuthorizationElement;
032: import org.omg.CSI.EstablishContext;
033: import org.omg.CSI.GSS_NT_ExportedNameHelper;
034: import org.omg.CSI.IdentityToken;
035: import org.omg.CSI.SASContextBody;
036: import org.omg.CSI.SASContextBodyHelper;
037: import org.omg.CSIIOP.CompoundSecMech;
038: import org.omg.CSIIOP.CompoundSecMechList;
039: import org.omg.CSIIOP.CompoundSecMechListHelper;
040: import org.omg.CSIIOP.EstablishTrustInClient;
041: import org.omg.CSIIOP.IdentityAssertion;
042: import org.omg.CSIIOP.TAG_CSI_SEC_MECH_LIST;
043: import org.omg.GSSUP.InitialContextToken;
044: import org.omg.GSSUP.InitialContextTokenHelper;
045: import org.omg.IOP.Codec;
046: import org.omg.IOP.SecurityAttributeService;
047: import org.omg.IOP.ServiceContext;
048: import org.omg.IOP.TaggedComponent;
049: import org.omg.IOP.CodecPackage.FormatMismatch;
050: import org.omg.IOP.CodecPackage.InvalidTypeForEncoding;
051: import org.omg.IOP.CodecPackage.TypeMismatch;
052: import org.omg.PortableInterceptor.ClientRequestInfo;
053: import org.omg.PortableInterceptor.ClientRequestInterceptor;
054: import org.omg.PortableInterceptor.ForwardRequest;
055:
056: import org.objectweb.carol.util.csiv2.gss.GSSHelper;
057:
058: import org.objectweb.util.monolog.api.BasicLevel;
059: import org.objectweb.util.monolog.api.Logger;
060:
061: /**
062: * SAS context interceptor on client side.
063: * @see Csiv2 spec : A client security service (CSS) is the security service
064: * associated with the ORB that is used by the client to invoke the target
065: * object.
066: * @see client state machine (fig 16-3)
067: * @see Common Secure Interoperability V2 Specification (July 23,2001)
068: * @author Florent Benoit
069: */
070: public class Csiv2ClientInterceptor extends org.omg.CORBA.LocalObject
071: implements ClientRequestInterceptor {
072:
073: /**
074: * Name
075: */
076: private static final String NAME = "Csiv2ClientInterceptor";
077:
078: /**
079: * Codec to use
080: */
081: private Codec codec = null;
082:
083: /**
084: * Logger to use
085: */
086: private Logger logger = null;
087:
088: /**
089: * Logger details (On catching exception)
090: */
091: private Logger loggerDetails = null;
092:
093: /**
094: * Constructor
095: * @param codec used for encoding any objects
096: * @param logger used for logging useful information
097: * @param loggerDetails for all information (useless for most time :)
098: */
099: public Csiv2ClientInterceptor(Codec codec, Logger logger,
100: Logger loggerDetails) {
101: this .codec = codec;
102: this .logger = logger;
103: this .loggerDetails = loggerDetails;
104: }
105:
106: /**
107: * Indicates to the interceptor that an exception occurred. Allows
108: * an Interceptor to query the exception's information before it is
109: * thrown to the client.
110: * @param ri Information about the current request being intercepted.
111: * @exception ForwardRequest If thrown, indicates to the ORB that a
112: * retry of the request should occur with the new object given in
113: * the exception.
114: */
115: public void receive_exception(ClientRequestInfo ri)
116: throws ForwardRequest {
117:
118: }
119:
120: /**
121: * Allows an Interceptor to query the information available when a
122: * request results in something other than a normal reply or an
123: * exception.
124: * @param ri Information about the current request being intercepted.
125: * @exception ForwardRequest If thrown, indicates to the ORB that a
126: * retry of the request should occur with the new object given in
127: * the exception.
128: */
129: public void receive_other(ClientRequestInfo ri)
130: throws ForwardRequest {
131:
132: }
133:
134: /**
135: * Allows an Interceptor to query the information on a reply after it
136: * is returned from the server and before control is returned to the
137: * client.
138: * <p>
139: * @param ri Information about the current request being intercepted.
140: */
141: public void receive_reply(ClientRequestInfo ri) {
142:
143: }
144:
145: /**
146: * Allows an Interceptor to query information during a Time-Independent
147: * Invocation (TII) polling get reply sequence.
148: * @param ri Information about the current request being intercepted.
149: */
150: public void send_poll(ClientRequestInfo ri) {
151:
152: }
153:
154: /**
155: * Need to send an establish context as described in the CSS state machine
156: * Compliance with level 0, so stateless context
157: * @see fig 16-3 of spec. [109] <br>
158: */
159: public void send_request(ClientRequestInfo ri)
160: throws ForwardRequest {
161:
162: // Is there a TAG_CSI_SEC_MECH tagged component in the request ?
163: TaggedComponent taggedComponent = null;
164: try {
165: taggedComponent = ri
166: .get_effective_component(TAG_CSI_SEC_MECH_LIST.value);
167: if (logger.isLoggable(BasicLevel.DEBUG)) {
168: logger
169: .log(BasicLevel.DEBUG,
170: "There is a TAG_CSI_SEC_MECH_LIST tagged component");
171: }
172:
173: } catch (BAD_PARAM e) {
174: if (loggerDetails.isLoggable(BasicLevel.DEBUG)) {
175: loggerDetails.log(BasicLevel.DEBUG,
176: "No tagged component with id "
177: + TAG_CSI_SEC_MECH_LIST.value);
178: }
179: return;
180:
181: }
182:
183: // Nothing to do if the component is not here
184: if (taggedComponent == null) {
185: return;
186: }
187:
188: // Extract infos from the received TaggedComponent
189: Any pAny = null;
190: try {
191: pAny = codec.decode_value(taggedComponent.component_data,
192: CompoundSecMechListHelper.type());
193: } catch (FormatMismatch fm) {
194: logger.log(BasicLevel.ERROR,
195: "Format mismatch while decoding value :"
196: + fm.getMessage());
197: return;
198: } catch (TypeMismatch tm) {
199: logger.log(BasicLevel.ERROR,
200: "Type mismatch while decoding value :"
201: + tm.getMessage());
202: return;
203: }
204:
205: // TODO : there can have several compound sech mech
206: // For now, take first
207: CompoundSecMechList compoundSecMechList = CompoundSecMechListHelper
208: .extract(pAny);
209: CompoundSecMech compoundSecMech = null;
210: if (compoundSecMechList.mechanism_list.length > 0) {
211: compoundSecMech = compoundSecMechList.mechanism_list[0];
212: } else {
213: // no compound sec mech received !
214: if (logger.isLoggable(BasicLevel.DEBUG)) {
215: logger.log(BasicLevel.DEBUG,
216: "No coumpound sec mech in the list.");
217: }
218: return;
219: }
220:
221: /**
222: * Now, build the EstablishContext message with stateless mode [17]
223: * EstablishContextMessage format Interoperability EstablishContext <br>
224: * <br>
225: * An EstablishContext message is sent by a CSS to establish a SAS
226: * context with a TSS. The SAS context and the context identifier
227: * allocated by the CSS to refer to it are scoped to the transport layer
228: * connection or association over which the CSS and TSS are
229: * communicating. When an association is dismantled, all SAS contexts
230: * scoped to the connection shall be invalidated and may be discarded.
231: * The EstablishContext message contains the following fields: <br>
232: * <ul>
233: * <li>client_context_id The CSS allocated identifier for the security
234: * attribute service context. A stateless CSS shall set the
235: * client_context_id to 0, indicating to the TSS that it is stateless. A
236: * stateful CSS may allocate a nonzero client_context_id.</li>
237: * <li>authorization_token May be used by a CSS to push privilege
238: * information to a TSS. A CSS may use this token to send proxy
239: * privileges to a TSS as a means to enable the target to issue calls as
240: * the client.</li>
241: * <li>identity_token Carries a representation of the invocation
242: * identity for the call (that is, the identity under which the call is
243: * to be authorized). The identity_token carries a representation of the
244: * invocation identity in one of the following forms:
245: * <ol>
246: * <li>A typed mechanism-specific representation of a principal name
247: * </li>
248: * <li>A chain of identity certificates representing the subject and a
249: * chain of verifying authorities</li>
250: * <li>A distinguished name</li>
251: * <li>The anonymous principal identity (a type, not a name)</li>
252: * </ol>
253: * An identity_token is used to assert a caller identity when that
254: * identity differs from the identity proven by authentication in the
255: * authentication layer(s). If the caller identity is intended to be the
256: * same as that established in the authentication layer(s), then it does
257: * not need to be asserted in an identity_token.</li>
258: * <li>client_authentication_token Carries a mechanism-specific GSS
259: * initial context token that authenticates the client to the TSS. It
260: * contains a mechanism type identifier and the mechanism-specific
261: * evidence (that is, the authenticator) required by the TSS to
262: * authenticate the client. When an initial context token contains
263: * private credentials, such as a password, this message may be safely
264: * sent only after a confidential connection with a trusted TSS has been
265: * established. The determination of when it is safe to send a client
266: * authentication token in an EstablishContext message shall be
267: * considered in the context of the CORBA location-binding paradigm for
268: * persistent objects (where an invocation may be location forwarded by
269: * a location daemon to the target object).</li>
270: * </ul>
271: */
272:
273: long clientContextId = Csiv2Const.STATELESS_CONTEXT_ID;
274: AuthorizationElement[] withoutAuthorizationToken = new AuthorizationElement[0];
275:
276: IdentityToken identityToken = null;
277:
278: // Anonymous
279: IdentityToken anonymousIdentityToken = new IdentityToken();
280: anonymousIdentityToken.anonymous(true);
281:
282: // Absent
283: IdentityToken absentIdentityToken = new IdentityToken();
284: absentIdentityToken.absent(true);
285:
286: byte[] clientAuthenticationToken = Csiv2Const.EMPTY_BYTES;
287:
288: // Test what we need to send (depending of the support)
289: // see 16-5.2 section
290:
291: // Client authentication token
292: if ((compoundSecMech.as_context_mech.target_requires & EstablishTrustInClient.value) == EstablishTrustInClient.value) {
293: pAny = null;
294: try {
295: pAny = ORBHelper.getOrb().create_any();
296: } catch (Csiv2InterceptorException csie) {
297: logger
298: .log(BasicLevel.ERROR,
299: "Cannot get orb for any = "
300: + csie.getMessage());
301: return;
302: }
303: InitialContextToken initialContextToken = null;
304: try {
305: initialContextToken = SecurityContextHelper
306: .getInstance().getInitialContextToken();
307: } catch (UnsupportedEncodingException uee) {
308: logger.log(BasicLevel.ERROR,
309: "Unsupported encoding for UTF8"
310: + uee.getMessage());
311: return;
312: }
313: InitialContextTokenHelper.insert(pAny, initialContextToken);
314: byte[] contextData = null;
315:
316: try {
317: contextData = codec.encode_value(pAny);
318: } catch (InvalidTypeForEncoding itfe) {
319: logger.log(BasicLevel.ERROR,
320: "Cannot encode a given any corba object : "
321: + itfe.getMessage());
322: return;
323: }
324:
325: try {
326: clientAuthenticationToken = GSSHelper
327: .encodeToken(contextData);
328: } catch (IOException ioe) {
329: logger.log(BasicLevel.ERROR,
330: "Cannot encode client authentication token : "
331: + ioe.getMessage());
332: return;
333: }
334: }
335:
336: // Identity token case
337: if ((compoundSecMech.sas_context_mech.target_supports & IdentityAssertion.value) == IdentityAssertion.value) {
338: pAny = null;
339: try {
340: pAny = ORBHelper.getOrb().create_any();
341: } catch (Csiv2InterceptorException csie) {
342: logger
343: .log(BasicLevel.ERROR,
344: "Cannot get orb for any = "
345: + csie.getMessage());
346: return;
347: }
348:
349: // Insert username
350: String identity = SecurityContextHelper.getInstance()
351: .getIdentityToken();
352: byte[] name = GSSHelper.encodeExported(identity);
353: byte[] principalName = null;
354: GSS_NT_ExportedNameHelper.insert(pAny, name);
355: try {
356: principalName = codec.encode_value(pAny);
357: } catch (InvalidTypeForEncoding itfe) {
358: logger.log(BasicLevel.ERROR,
359: "Cannot encode a given any corba object : "
360: + itfe.getMessage());
361: return;
362: }
363:
364: // Put name in the token
365: identityToken = new IdentityToken();
366: identityToken.principal_name(principalName);
367:
368: }
369:
370: // No identity was set (principal name), so use absent identity
371: if (identityToken == null) {
372: identityToken = absentIdentityToken;
373: }
374:
375: // if absent and no client auth token, don't do anything
376: if (identityToken == absentIdentityToken
377: && clientAuthenticationToken == Csiv2Const.EMPTY_BYTES) {
378: return;
379: }
380:
381: EstablishContext establishContext = new EstablishContext(
382: clientContextId, withoutAuthorizationToken,
383: identityToken, clientAuthenticationToken);
384:
385: /**
386: * And then, this message should be added. see 16.2.1 The Security
387: * Attribute Service Context Element [10] This specification defines a
388: * new GIOP service context element type, the security attribute service
389: * (SAS) element. <br>
390: * [11] The SAS context element may be used to associate any or all of
391: * the following contexts with GIOP request and reply messages: "
392: * Identity context, to be accepted based on trust " Authorization
393: * context, including authorization-based delegation context " Client
394: * authentication context <br>
395: * [12] A new context_id has been defined for the SAS element. const
396: * ServiceId SecurityAttributeService = 15
397: */
398: try {
399: pAny = ORBHelper.getOrb().create_any();
400: } catch (Csiv2InterceptorException csie) {
401: logger.log(BasicLevel.ERROR, "Cannot get orb for any = "
402: + csie.getMessage());
403: return;
404: }
405:
406: // Generate contextData of service context with EstablishContext
407: SASContextBody sasContextBody = new SASContextBody();
408: sasContextBody.establish_msg(establishContext);
409: SASContextBodyHelper.insert(pAny, sasContextBody);
410: byte[] contextData = null;
411:
412: try {
413: contextData = codec.encode_value(pAny);
414: } catch (InvalidTypeForEncoding itfe) {
415: logger.log(BasicLevel.ERROR,
416: "Cannot encode a given any corba object : "
417: + itfe.getMessage());
418: return;
419: }
420:
421: // build service context and add it
422: ServiceContext serviceContext = new ServiceContext(
423: SecurityAttributeService.value, contextData);
424: ri.add_request_service_context(serviceContext,
425: Csiv2Const.REPLACE_SECURITY_ATTRIBUTE_SERVICE);
426:
427: }
428:
429: /**
430: * Provides an opportunity to destroy this interceptor.
431: */
432: public void destroy() {
433: // TODO Auto-generated method stub
434:
435: }
436:
437: /**
438: * Returns the name of the interceptor.
439: * @return the name of the interceptor.
440: */
441: public String name() {
442: return NAME;
443: }
444:
445: }
|