KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > mx4j > remote > MX4JRemoteUtils


1 /*
2  * Copyright (C) MX4J.
3  * All rights reserved.
4  *
5  * This software is distributed under the terms of the MX4J License version 1.0.
6  * See the terms of the MX4J License in the documentation provided with this software.
7  */

8
9 package mx4j.remote;
10
11 import java.io.ByteArrayOutputStream JavaDoc;
12 import java.io.IOException JavaDoc;
13 import java.io.ObjectOutputStream JavaDoc;
14 import java.io.Serializable JavaDoc;
15 import java.lang.reflect.Constructor JavaDoc;
16 import java.security.AccessControlContext JavaDoc;
17 import java.security.AccessController JavaDoc;
18 import java.security.CodeSource JavaDoc;
19 import java.security.DomainCombiner JavaDoc;
20 import java.security.Permission JavaDoc;
21 import java.security.PermissionCollection JavaDoc;
22 import java.security.Principal JavaDoc;
23 import java.security.PrivilegedAction JavaDoc;
24 import java.security.PrivilegedActionException JavaDoc;
25 import java.security.PrivilegedExceptionAction JavaDoc;
26 import java.security.ProtectionDomain JavaDoc;
27 import java.util.HashMap JavaDoc;
28 import java.util.Iterator JavaDoc;
29 import java.util.Map JavaDoc;
30 import java.util.Set JavaDoc;
31
32 import javax.management.remote.SubjectDelegationPermission JavaDoc;
33 import javax.security.auth.AuthPermission JavaDoc;
34 import javax.security.auth.Policy JavaDoc;
35 import javax.security.auth.Subject JavaDoc;
36
37 /**
38  *
39  * @author <a HREF="mailto:biorn_steedom@users.sourceforge.net">Simone Bordet</a>
40  * @version $Revision: 1.12 $
41  */

42 public class MX4JRemoteUtils
43 {
44    private static int connectionNumber;
45
46    /**
47     * Returns a copy of the given Map that does not contain non-serializable entries
48     */

49    public static Map JavaDoc removeNonSerializableEntries(Map JavaDoc map)
50    {
51       Map JavaDoc newMap = new HashMap JavaDoc(map.size());
52       for (Iterator JavaDoc i = map.entrySet().iterator(); i.hasNext();)
53       {
54          Map.Entry JavaDoc entry = (Map.Entry JavaDoc)i.next();
55          if (isSerializable(entry)) newMap.put(entry.getKey(), entry.getValue());
56       }
57       return newMap;
58    }
59
60    private static boolean isSerializable(Object JavaDoc object)
61    {
62       if (object instanceof Map.Entry JavaDoc) return isSerializable(((Map.Entry JavaDoc)object).getKey()) && isSerializable(((Map.Entry JavaDoc)object).getValue());
63       if (object == null) return true;
64       if (object instanceof String JavaDoc) return true;
65       if (object instanceof Number JavaDoc) return true;
66       if (!(object instanceof Serializable JavaDoc)) return false;
67
68       return isTrulySerializable(object);
69    }
70
71    public static boolean isTrulySerializable(Object JavaDoc object)
72    {
73       // Give up and serialize the object
74
try
75       {
76          ByteArrayOutputStream JavaDoc baos = new ByteArrayOutputStream JavaDoc();
77          ObjectOutputStream JavaDoc oos = new ObjectOutputStream JavaDoc(baos);
78          oos.writeObject(object);
79          oos.close();
80          return true;
81       }
82       catch (IOException JavaDoc ignored)
83       {
84       }
85       return false;
86    }
87
88    public static String JavaDoc createConnectionID(String JavaDoc protocol, String JavaDoc callerAddress, int callerPort, Subject JavaDoc subject)
89    {
90       // See JSR 160 specification at javax/management/remote/package-summary.html
91

92       StringBuffer JavaDoc buffer = new StringBuffer JavaDoc(protocol);
93       buffer.append(':');
94       if (callerAddress != null) buffer.append("//").append(callerAddress);
95       if (callerPort >= 0) buffer.append(':').append(callerPort);
96       buffer.append(' ');
97
98       if (subject != null)
99       {
100          Set JavaDoc principals = subject.getPrincipals();
101          for (Iterator JavaDoc i = principals.iterator(); i.hasNext();)
102          {
103             Principal JavaDoc principal = (Principal JavaDoc)i.next();
104             String JavaDoc name = principal.getName();
105             name = name.replace(' ', '_');
106             buffer.append(name);
107             if (i.hasNext()) buffer.append(';');
108          }
109       }
110       buffer.append(' ');
111
112       buffer.append("0x").append(Integer.toHexString(getNextConnectionNumber()));
113
114       return buffer.toString();
115    }
116
117    private static synchronized int getNextConnectionNumber()
118    {
119       return ++connectionNumber;
120    }
121
122    public static Object JavaDoc subjectInvoke(Subject JavaDoc subject, Subject JavaDoc delegate, AccessControlContext JavaDoc context, PrivilegedExceptionAction JavaDoc action) throws Exception JavaDoc
123    {
124       if (delegate != null)
125       {
126          if (subject == null) throw new SecurityException JavaDoc("There is no authenticated subject to delegate to");
127          checkSubjectDelegationPermission(delegate, getSubjectContext(subject, context));
128       }
129
130       if (subject == null)
131       {
132          if (context == null) return action.run();
133          try
134          {
135             return AccessController.doPrivileged(action, context);
136          }
137          catch (PrivilegedActionException JavaDoc x)
138          {
139             throw x.getException();
140          }
141       }
142
143       // The precedent stack frames have normally AllPermission, since - for example in RMI - they
144
// are JDK domains or JMX/MX4J domains. Below I take the context at start() moment, and I
145
// inject the JSR 160 domain with the authenticated Subject, then call Subject.doAsPrivileged()
146
// with, eventually, the delegate Subject.
147
try
148       {
149          AccessControlContext JavaDoc subjectContext = getSubjectContext(subject, context);
150          if (delegate == null)
151             return Subject.doAsPrivileged(subject, action, subjectContext);
152          else
153             return Subject.doAsPrivileged(delegate, action, subjectContext);
154       }
155       catch (PrivilegedActionException JavaDoc x)
156       {
157          throw x.getException();
158       }
159    }
160
161    private static void checkSubjectDelegationPermission(final Subject JavaDoc delegate, AccessControlContext JavaDoc context) throws SecurityException JavaDoc
162    {
163       final SecurityManager JavaDoc sm = System.getSecurityManager();
164       if (sm != null)
165       {
166          AccessController.doPrivileged(new PrivilegedAction JavaDoc()
167          {
168             public Object JavaDoc run()
169             {
170                StringBuffer JavaDoc buffer = new StringBuffer JavaDoc();
171                Set JavaDoc principals = delegate.getPrincipals();
172                for (Iterator JavaDoc i = principals.iterator(); i.hasNext();)
173                {
174                   Principal JavaDoc principal = (Principal JavaDoc)i.next();
175                   buffer.setLength(0);
176                   String JavaDoc permission = buffer.append(principal.getClass().getName()).append(".").append(principal.getName()).toString();
177                   sm.checkPermission(new SubjectDelegationPermission JavaDoc(permission));
178                }
179                return null;
180             }
181          }, context);
182       }
183    }
184
185    /**
186     * Returns a suitable AccessControlContext that restricts access in a {@link Subject#doAsPrivileged} call
187     * based on the current JAAS authorization policy, and combined with the given context.
188     *
189     * This is needed because the server stack frames in a call to a JMXConnectorServer are,
190     * for example for RMI, like this:
191     * <pre>
192     * java.lang.Thread.run()
193     * [rmi runtime classes]
194     * javax.management.remote.rmi.RMIConnectionImpl
195     * [mx4j JSR 160 implementation code]
196     * javax.security.auth.Subject.doAsPrivileged()
197     * [mx4j JSR 160 implementation code]
198     * [mx4j JSR 3 implementation code]
199     * </pre>
200     * All protection domains in this stack frames have AllPermission, normally, and the Subject.doAsPrivileged()
201     * call stops the checks very early. <br>
202     *
203     * So we need a restricting context (created at the start() of the connector server), and furthermore we need
204     * to combine the restricting context with a "special" context that does not have the same location as the
205     * JSR 3 and 160 classes and implementation (in particular will have a null location). <br>
206     * The "injection" of this synthetic ProtectionDomain allows to give AllPermission to the JSR 3 and 160 classes
207     * and implementation, but still have the possibility to specify a JAAS policy with MBeanPermissions in this way:
208     * <pre>
209     * grant principal javax.management.remote.JMXPrincipal "mx4j"
210     * {
211     * permission javax.management.MBeanPermission "*", "getAttribute";
212     * };
213     * </pre>
214     */

215    private static AccessControlContext JavaDoc getSubjectContext(final Subject JavaDoc subject, final AccessControlContext JavaDoc context)
216    {
217       final SecurityManager JavaDoc sm = System.getSecurityManager();
218       if (sm == null)
219       {
220          return context;
221       }
222       else
223       {
224          return (AccessControlContext JavaDoc)AccessController.doPrivileged(new PrivilegedAction JavaDoc()
225          {
226             public Object JavaDoc run()
227             {
228                InjectingDomainCombiner combiner = new InjectingDomainCombiner(subject);
229                AccessControlContext JavaDoc acc = new AccessControlContext JavaDoc(context, combiner);
230                AccessController.doPrivileged(new PrivilegedAction JavaDoc()
231                {
232                   public Object JavaDoc run()
233                   {
234                      // Check this permission, that is required anyway, to combine the domains
235
sm.checkPermission(new AuthPermission JavaDoc("doAsPrivileged"));
236                      return null;
237                   }
238                }, acc);
239                ProtectionDomain JavaDoc[] combined = combiner.getCombinedDomains();
240                return new AccessControlContext JavaDoc(combined);
241             }
242          });
243       }
244    }
245
246    private static class InjectingDomainCombiner implements DomainCombiner JavaDoc
247    {
248       private static Constructor JavaDoc domainConstructor;
249
250       static
251       {
252          try
253          {
254             domainConstructor = ProtectionDomain JavaDoc.class.getConstructor(new Class JavaDoc[]{CodeSource JavaDoc.class, PermissionCollection JavaDoc.class, ClassLoader JavaDoc.class, Principal JavaDoc[].class});
255          }
256          catch (Exception JavaDoc x)
257          {
258          }
259       }
260
261       private ProtectionDomain JavaDoc domain;
262       private ProtectionDomain JavaDoc[] combined;
263
264       public InjectingDomainCombiner(Subject JavaDoc subject)
265       {
266          if (domainConstructor != null)
267          {
268             Principal JavaDoc[] principals = (Principal JavaDoc[])subject.getPrincipals().toArray(new Principal JavaDoc[0]);
269             try
270             {
271                domain = (ProtectionDomain JavaDoc)domainConstructor.newInstance(new Object JavaDoc[]{new CodeSource JavaDoc(null, null), null, null, principals});
272             }
273             catch (Exception JavaDoc x)
274             {
275             }
276          }
277
278          if (domain == null)
279          {
280             // This is done for JDK 1.3 compatibility.
281
domain = new SubjectProtectionDomain(new CodeSource JavaDoc(null, null), subject);
282          }
283       }
284
285       public ProtectionDomain JavaDoc[] combine(ProtectionDomain JavaDoc[] current, ProtectionDomain JavaDoc[] assigned)
286       {
287          int length = current.length;
288          ProtectionDomain JavaDoc[] result = null;
289          if (assigned == null || assigned.length == 0)
290          {
291             result = new ProtectionDomain JavaDoc[length + 1];
292             System.arraycopy(current, 0, result, 0, length);
293          }
294          else
295          {
296             result = new ProtectionDomain JavaDoc[length + assigned.length + 1];
297             System.arraycopy(current, 0, result, 0, length);
298             System.arraycopy(assigned, 0, result, length, assigned.length);
299          }
300          result[result.length - 1] = domain;
301          this.combined = result;
302          return result;
303       }
304
305       public ProtectionDomain JavaDoc[] getCombinedDomains()
306       {
307          return combined;
308       }
309
310       private static class SubjectProtectionDomain extends ProtectionDomain JavaDoc
311       {
312          private final Subject JavaDoc subject;
313
314          public SubjectProtectionDomain(CodeSource JavaDoc codesource, Subject JavaDoc subject)
315          {
316             super(codesource, null);
317             this.subject = subject;
318          }
319
320          public boolean implies(Permission JavaDoc permission)
321          {
322             Policy JavaDoc policy = (Policy JavaDoc)AccessController.doPrivileged(new PrivilegedAction JavaDoc()
323             {
324                public Object JavaDoc run()
325                {
326                   return Policy.getPolicy();
327                }
328             });
329             PermissionCollection JavaDoc permissions = policy.getPermissions(subject, getCodeSource());
330             return permissions.implies(permission);
331          }
332       }
333    }
334 }
335
Popular Tags