1 //
2 // Copyright (c) ZeroC, Inc. All rights reserved.
3 //
4 
5 package com.zeroc.IceSSL;
6 
7 class TrustManager
8 {
TrustManager(com.zeroc.Ice.Communicator communicator)9     TrustManager(com.zeroc.Ice.Communicator communicator)
10     {
11         assert communicator != null;
12         _communicator = communicator;
13         com.zeroc.Ice.Properties properties = communicator.getProperties();
14         _traceLevel = properties.getPropertyAsInt("IceSSL.Trace.Security");
15         String key = null;
16         try
17         {
18             key = "IceSSL.TrustOnly";
19             parse(properties.getProperty(key), _rejectAll, _acceptAll);
20             key = "IceSSL.TrustOnly.Client";
21             parse(properties.getProperty(key), _rejectClient, _acceptClient);
22             key = "IceSSL.TrustOnly.Server";
23             parse(properties.getProperty(key), _rejectAllServer, _acceptAllServer);
24             java.util.Map<String, String> dict = properties.getPropertiesForPrefix("IceSSL.TrustOnly.Server.");
25             for(java.util.Map.Entry<String, String> p : dict.entrySet())
26             {
27                 key = p.getKey();
28                 String name = key.substring("IceSSL.TrustOnly.Server.".length());
29                 java.util.List<java.util.List<RFC2253.RDNPair> > reject =
30                     new java.util.LinkedList<java.util.List<RFC2253.RDNPair> >();
31                 java.util.List<java.util.List<RFC2253.RDNPair> > accept =
32                     new java.util.LinkedList<java.util.List<RFC2253.RDNPair> >();
33                 parse(p.getValue(), reject, accept);
34                 if(!reject.isEmpty())
35                 {
36                     _rejectServer.put(name, reject);
37                 }
38                 if(!accept.isEmpty())
39                 {
40                     _acceptServer.put(name, accept);
41                 }
42             }
43         }
44         catch(RFC2253.ParseException e)
45         {
46             com.zeroc.Ice.PluginInitializationException ex = new com.zeroc.Ice.PluginInitializationException();
47             ex.reason = "IceSSL: invalid property " + key  + ":\n" + e.reason;
48             throw ex;
49         }
50     }
51 
52     boolean
verify(ConnectionInfo info, String desc)53     verify(ConnectionInfo info, String desc)
54     {
55         java.util.List<java.util.List<java.util.List<RFC2253.RDNPair> > >
56             reject = new java.util.LinkedList<java.util.List<java.util.List<RFC2253.RDNPair> > >(),
57             accept = new java.util.LinkedList<java.util.List<java.util.List<RFC2253.RDNPair> > >();
58 
59         if(!_rejectAll.isEmpty())
60         {
61             reject.add(_rejectAll);
62         }
63         if(info.incoming)
64         {
65             if(!_rejectAllServer.isEmpty())
66             {
67                 reject.add(_rejectAllServer);
68             }
69             if(info.adapterName.length() > 0)
70             {
71                 java.util.List<java.util.List<RFC2253.RDNPair> > p = _rejectServer.get(info.adapterName);
72                 if(p != null)
73                 {
74                     reject.add(p);
75                 }
76             }
77         }
78         else
79         {
80             if(!_rejectClient.isEmpty())
81             {
82                 reject.add(_rejectClient);
83             }
84         }
85 
86         if(!_acceptAll.isEmpty())
87         {
88             accept.add(_acceptAll);
89         }
90         if(info.incoming)
91         {
92             if(!_acceptAllServer.isEmpty())
93             {
94                 accept.add(_acceptAllServer);
95             }
96             if(info.adapterName.length() > 0)
97             {
98                 java.util.List<java.util.List<RFC2253.RDNPair> > p = _acceptServer.get(info.adapterName);
99                 if(p != null)
100                 {
101                     accept.add(p);
102                 }
103             }
104         }
105         else
106         {
107             if(!_acceptClient.isEmpty())
108             {
109                 accept.add(_acceptClient);
110             }
111         }
112 
113         //
114         // If there is nothing to match against, then we accept the cert.
115         //
116         if(reject.isEmpty() && accept.isEmpty())
117         {
118             return true;
119         }
120 
121         //
122         // If there is no certificate then we match false.
123         //
124         if(info.certs != null && info.certs.length > 0)
125         {
126             javax.security.auth.x500.X500Principal subjectDN =
127                 ((java.security.cert.X509Certificate)info.certs[0]).getSubjectX500Principal();
128             String subjectName = subjectDN.getName(javax.security.auth.x500.X500Principal.RFC2253);
129             assert subjectName != null;
130             try
131             {
132                 //
133                 // Decompose the subject DN into the RDNs.
134                 //
135                 if(_traceLevel > 0)
136                 {
137                     if(info.incoming)
138                     {
139                         _communicator.getLogger().trace("Security", "trust manager evaluating client:\n" +
140                             "subject = " + subjectName + "\n" +
141                             "adapter = " + info.adapterName + "\n" +
142                             desc);
143                     }
144                     else
145                     {
146                         _communicator.getLogger().trace("Security", "trust manager evaluating server:\n" +
147                             "subject = " + subjectName + "\n" + desc);
148                     }
149                 }
150                 java.util.List<RFC2253.RDNPair> dn = RFC2253.parseStrict(subjectName);
151 
152                 //
153                 // Fail if we match anything in the reject set.
154                 //
155                 for(java.util.List<java.util.List<RFC2253.RDNPair>> matchSet : reject)
156                 {
157                     if(_traceLevel > 1)
158                     {
159                         StringBuilder s = new StringBuilder("trust manager rejecting PDNs:\n");
160                         stringify(matchSet, s);
161                         _communicator.getLogger().trace("Security", s.toString());
162                     }
163                     if(match(matchSet, dn))
164                     {
165                         return false;
166                     }
167                 }
168 
169                 //
170                 // Succeed if we match anything in the accept set.
171                 //
172                 for(java.util.List<java.util.List<RFC2253.RDNPair>> matchSet : accept)
173                 {
174                     if(_traceLevel > 1)
175                     {
176                         StringBuilder s = new StringBuilder("trust manager accepting PDNs:\n");
177                         stringify(matchSet, s);
178                         _communicator.getLogger().trace("Security", s.toString());
179                     }
180                     if(match(matchSet, dn))
181                     {
182                         return true;
183                     }
184                 }
185             }
186             catch(RFC2253.ParseException e)
187             {
188                 _communicator.getLogger().warning(
189                     "IceSSL: unable to parse certificate DN `" + subjectName + "'\nreason: " + e.reason);
190             }
191 
192             //
193             // At this point we accept the connection if there are no explicit accept rules.
194             //
195             return accept.isEmpty();
196         }
197 
198         return false;
199     }
200 
201     private boolean
match(java.util.List<java.util.List<RFC2253.RDNPair> > matchSet, java.util.List<RFC2253.RDNPair> subject)202     match(java.util.List<java.util.List<RFC2253.RDNPair> > matchSet, java.util.List<RFC2253.RDNPair> subject)
203     {
204         for(java.util.List<RFC2253.RDNPair> r : matchSet)
205         {
206             if(matchRDNs(r, subject))
207             {
208                 return true;
209             }
210         }
211         return false;
212     }
213 
214     private boolean
matchRDNs(java.util.List<RFC2253.RDNPair> match, java.util.List<RFC2253.RDNPair> subject)215     matchRDNs(java.util.List<RFC2253.RDNPair> match, java.util.List<RFC2253.RDNPair> subject)
216     {
217         for(RFC2253.RDNPair matchRDN : match)
218         {
219             boolean found = false;
220             for(RFC2253.RDNPair subjectRDN : subject)
221             {
222                 if(matchRDN.key.equals(subjectRDN.key))
223                 {
224                     found = true;
225                     if(!matchRDN.value.equals(subjectRDN.value))
226                     {
227                         return false;
228                     }
229                 }
230             }
231             if(!found)
232             {
233                 return false;
234             }
235         }
236         return true;
237     }
238 
239     void
parse(String value, java.util.List<java.util.List<RFC2253.RDNPair> > reject, java.util.List<java.util.List<RFC2253.RDNPair> > accept)240     parse(String value, java.util.List<java.util.List<RFC2253.RDNPair> > reject,
241           java.util.List<java.util.List<RFC2253.RDNPair> > accept)
242         throws RFC2253.ParseException
243     {
244         //
245         // Java X500Principal.getName says:
246         //
247         // If "RFC2253" is specified as the format, this method emits
248         // the attribute type keywords defined in RFC 2253 (CN, L, ST,
249         // O, OU, C, STREET, DC, UID). Any other attribute type is
250         // emitted as an OID. Under a strict reading, RFC 2253 only
251         // specifies a UTF-8 string representation. The String
252         // returned by this method is the Unicode string achieved by
253         // decoding this UTF-8 representation.
254         //
255         // This means that things like emailAddress and such will be turned into
256         // something like:
257         //
258         // 1.2.840.113549.1.9.1=#160e696e666f407a65726f632e636f6d
259         //
260         // The left hand side is the OID (see
261         // http://www.columbia.edu/~ariel/ssleay/asn1-oids.html) for a
262         // list. The right hand side is a BER encoding of the value.
263         //
264         // This means that the user input, unless it uses the
265         // unfriendly OID format, will not directly match the
266         // principal.
267         //
268         // Two possible solutions:
269         //
270         // Have the RFC2253 parser convert anything that is not CN, L,
271         // ST, O, OU, C, STREET, DC, UID into OID format, and have it
272         // convert the values into a BER encoding.
273         //
274         // Send the user data through X500Principal to string form and
275         // then through the RFC2253 encoder. This uses the
276         // X500Principal to do the encoding for us.
277         //
278         // The latter is much simpler, however, it means we need to
279         // send the data through the parser twice because we split the
280         // DNs on ';' which cannot be blindly split because of quotes,
281         // \ and such.
282         //
283         java.util.List<RFC2253.RDNEntry> l = RFC2253.parse(value);
284         for(RFC2253.RDNEntry e : l)
285         {
286             StringBuilder v = new StringBuilder();
287             boolean first = true;
288             for(RFC2253.RDNPair pair : e.rdn)
289             {
290                 if(!first)
291                 {
292                     v.append(",");
293                 }
294                 first = false;
295                 v.append(pair.key);
296                 v.append("=");
297                 v.append(pair.value);
298             }
299             javax.security.auth.x500.X500Principal princ = new javax.security.auth.x500.X500Principal(v.toString());
300             String subjectName = princ.getName(javax.security.auth.x500.X500Principal.RFC2253);
301             if(e.negate)
302             {
303                 reject.add(RFC2253.parseStrict(subjectName));
304             }
305             else
306             {
307                 accept.add(RFC2253.parseStrict(subjectName));
308             }
309         }
310     }
311 
312     private static void
stringify(java.util.List<java.util.List<RFC2253.RDNPair>> matchSet, StringBuilder s)313     stringify(java.util.List<java.util.List<RFC2253.RDNPair>> matchSet, StringBuilder s)
314     {
315         boolean addSemi = false;
316         for(java.util.List<RFC2253.RDNPair> rdnSet : matchSet)
317         {
318             if(addSemi)
319             {
320                 s.append(';');
321             }
322             addSemi = true;
323             boolean addComma = false;
324             for(RFC2253.RDNPair rdn : rdnSet)
325             {
326                 if(addComma)
327                 {
328                     s.append(',');
329                 }
330                 addComma = true;
331                 s.append(rdn.key);
332                 s.append('=');
333                 s.append(rdn.value);
334             }
335         }
336     }
337 
338     private com.zeroc.Ice.Communicator _communicator;
339     private int _traceLevel;
340 
341     private java.util.List<java.util.List<RFC2253.RDNPair> > _rejectAll =
342         new java.util.LinkedList<java.util.List<RFC2253.RDNPair> >();
343     private java.util.List<java.util.List<RFC2253.RDNPair> > _rejectClient =
344         new java.util.LinkedList<java.util.List<RFC2253.RDNPair> >();
345     private java.util.List<java.util.List<RFC2253.RDNPair> > _rejectAllServer =
346         new java.util.LinkedList<java.util.List<RFC2253.RDNPair> >();
347     private java.util.Map<String, java.util.List<java.util.List<RFC2253.RDNPair> > > _rejectServer =
348         new java.util.HashMap<String, java.util.List<java.util.List<RFC2253.RDNPair> > >();
349 
350     private java.util.List<java.util.List<RFC2253.RDNPair> > _acceptAll =
351         new java.util.LinkedList<java.util.List<RFC2253.RDNPair> >();
352     private java.util.List<java.util.List<RFC2253.RDNPair> > _acceptClient =
353         new java.util.LinkedList<java.util.List<RFC2253.RDNPair> >();
354     private java.util.List<java.util.List<RFC2253.RDNPair> > _acceptAllServer =
355         new java.util.LinkedList<java.util.List<RFC2253.RDNPair> >();
356     private java.util.Map<String, java.util.List<java.util.List<RFC2253.RDNPair> > > _acceptServer =
357         new java.util.HashMap<String, java.util.List<java.util.List<RFC2253.RDNPair> > >();
358 }
359