1 /* 2 * Copyright (c) 2015, 2019, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package sun.security.provider.certpath.ldap; 27 28 import java.io.ByteArrayInputStream; 29 import java.net.URI; 30 import java.util.*; 31 import javax.naming.CompositeName; 32 import javax.naming.Context; 33 import javax.naming.InvalidNameException; 34 import javax.naming.NamingEnumeration; 35 import javax.naming.NamingException; 36 import javax.naming.NameNotFoundException; 37 import javax.naming.directory.Attribute; 38 import javax.naming.directory.Attributes; 39 import javax.naming.directory.BasicAttributes; 40 41 import java.security.*; 42 import java.security.cert.Certificate; 43 import java.security.cert.*; 44 import javax.naming.CommunicationException; 45 import javax.naming.ldap.InitialLdapContext; 46 import javax.naming.ldap.LdapContext; 47 import javax.security.auth.x500.X500Principal; 48 49 import com.sun.jndi.ldap.LdapReferralException; 50 import sun.security.util.HexDumpEncoder; 51 import sun.security.provider.certpath.X509CertificatePair; 52 import sun.security.util.Cache; 53 import sun.security.util.Debug; 54 55 /** 56 * Core implementation of a LDAP Cert Store. 57 * @see java.security.cert.CertStore 58 * 59 * @since 9 60 */ 61 final class LDAPCertStoreImpl { 62 63 private static final Debug debug = Debug.getInstance("certpath"); 64 65 /** 66 * LDAP attribute identifiers. 67 */ 68 private static final String USER_CERT = "userCertificate;binary"; 69 private static final String CA_CERT = "cACertificate;binary"; 70 private static final String CROSS_CERT = "crossCertificatePair;binary"; 71 private static final String CRL = "certificateRevocationList;binary"; 72 private static final String ARL = "authorityRevocationList;binary"; 73 74 // Constants for various empty values 75 private final static String[] STRING0 = new String[0]; 76 77 private final static byte[][] BB0 = new byte[0][]; 78 79 private final static Attributes EMPTY_ATTRIBUTES = new BasicAttributes(); 80 81 // cache related constants 82 private final static int DEFAULT_CACHE_SIZE = 750; 83 private final static int DEFAULT_CACHE_LIFETIME = 30; 84 85 private final static int LIFETIME; 86 87 private final static String PROP_LIFETIME = 88 "sun.security.certpath.ldap.cache.lifetime"; 89 90 /* 91 * Internal system property, that when set to "true", disables the 92 * JNDI application resource files lookup to prevent recursion issues 93 * when validating signed JARs with LDAP URLs in certificates. 94 */ 95 private final static String PROP_DISABLE_APP_RESOURCE_FILES = 96 "sun.security.certpath.ldap.disable.app.resource.files"; 97 98 static { 99 String s = AccessController.doPrivileged( 100 (PrivilegedAction<String>) () -> System.getProperty(PROP_LIFETIME)); 101 if (s != null) { 102 LIFETIME = Integer.parseInt(s); // throws NumberFormatException 103 } else { 104 LIFETIME = DEFAULT_CACHE_LIFETIME; 105 } 106 } 107 108 /** 109 * The CertificateFactory used to decode certificates from 110 * their binary stored form. 111 */ 112 private CertificateFactory cf; 113 114 /** 115 * The JNDI directory context. 116 */ 117 private LdapContext ctx; 118 119 /** 120 * Flag indicating that communication error occurred. 121 */ 122 private boolean communicationError = false; 123 124 /** 125 * Flag indicating whether we should prefetch CRLs. 126 */ 127 private boolean prefetchCRLs = false; 128 129 private final Cache<String, byte[][]> valueCache; 130 131 private int cacheHits = 0; 132 private int cacheMisses = 0; 133 private int requests = 0; 134 135 /** 136 * Creates a <code>CertStore</code> with the specified parameters. 137 */ LDAPCertStoreImpl(String serverName, int port)138 LDAPCertStoreImpl(String serverName, int port) 139 throws InvalidAlgorithmParameterException { 140 createInitialDirContext(serverName, port); 141 // Create CertificateFactory for use later on 142 try { 143 cf = CertificateFactory.getInstance("X.509"); 144 } catch (CertificateException e) { 145 throw new InvalidAlgorithmParameterException( 146 "unable to create CertificateFactory for X.509"); 147 } 148 if (LIFETIME == 0) { 149 valueCache = Cache.newNullCache(); 150 } else if (LIFETIME < 0) { 151 valueCache = Cache.newSoftMemoryCache(DEFAULT_CACHE_SIZE); 152 } else { 153 valueCache = Cache.newSoftMemoryCache(DEFAULT_CACHE_SIZE, LIFETIME); 154 } 155 } 156 157 /** 158 * Create InitialDirContext. 159 * 160 * @param server Server DNS name hosting LDAP service 161 * @param port Port at which server listens for requests 162 * @throws InvalidAlgorithmParameterException if creation fails 163 */ createInitialDirContext(String server, int port)164 private void createInitialDirContext(String server, int port) 165 throws InvalidAlgorithmParameterException { 166 String url = "ldap://" + server + ":" + port; 167 Hashtable<String,Object> env = new Hashtable<>(); 168 env.put(Context.INITIAL_CONTEXT_FACTORY, 169 "com.sun.jndi.ldap.LdapCtxFactory"); 170 env.put(Context.PROVIDER_URL, url); 171 172 // If property is set to true, disable application resource file lookup. 173 boolean disableAppResourceFiles = AccessController.doPrivileged( 174 (PrivilegedAction<Boolean>) () -> Boolean.getBoolean(PROP_DISABLE_APP_RESOURCE_FILES)); 175 if (disableAppResourceFiles) { 176 if (debug != null) { 177 debug.println("LDAPCertStore disabling app resource files"); 178 } 179 env.put("com.sun.naming.disable.app.resource.files", "true"); 180 } 181 182 try { 183 ctx = new InitialLdapContext(env, null); 184 /* 185 * Always deal with referrals here. 186 */ 187 ctx.addToEnvironment(Context.REFERRAL, "throw"); 188 } catch (NamingException e) { 189 if (debug != null) { 190 debug.println("LDAPCertStore.engineInit about to throw " 191 + "InvalidAlgorithmParameterException"); 192 e.printStackTrace(); 193 } 194 Exception ee = new InvalidAlgorithmParameterException 195 ("unable to create InitialDirContext using supplied parameters"); 196 ee.initCause(e); 197 throw (InvalidAlgorithmParameterException)ee; 198 } 199 } 200 201 /** 202 * Private class encapsulating the actual LDAP operations and cache 203 * handling. Use: 204 * 205 * LDAPRequest request = new LDAPRequest(dn); 206 * request.addRequestedAttribute(CROSS_CERT); 207 * request.addRequestedAttribute(CA_CERT); 208 * byte[][] crossValues = request.getValues(CROSS_CERT); 209 * byte[][] caValues = request.getValues(CA_CERT); 210 * 211 * At most one LDAP request is sent for each instance created. If all 212 * getValues() calls can be satisfied from the cache, no request 213 * is sent at all. If a request is sent, all requested attributes 214 * are always added to the cache irrespective of whether the getValues() 215 * method is called. 216 */ 217 private class LDAPRequest { 218 219 private final String name; 220 private Map<String, byte[][]> valueMap; 221 private final List<String> requestedAttributes; 222 LDAPRequest(String name)223 LDAPRequest(String name) throws CertStoreException { 224 this.name = checkName(name); 225 requestedAttributes = new ArrayList<>(5); 226 } 227 checkName(String name)228 private String checkName(String name) throws CertStoreException { 229 if (name == null) { 230 throw new CertStoreException("Name absent"); 231 } 232 try { 233 if (new CompositeName(name).size() > 1) { 234 throw new CertStoreException("Invalid name: " + name); 235 } 236 } catch (InvalidNameException ine) { 237 throw new CertStoreException("Invalid name: " + name, ine); 238 } 239 return name; 240 } 241 addRequestedAttribute(String attrId)242 void addRequestedAttribute(String attrId) { 243 if (valueMap != null) { 244 throw new IllegalStateException("Request already sent"); 245 } 246 requestedAttributes.add(attrId); 247 } 248 249 /** 250 * Gets one or more binary values from an attribute. 251 * 252 * @param attrId the attribute identifier 253 * @return an array of binary values (byte arrays) 254 * @throws NamingException if a naming exception occurs 255 */ getValues(String attrId)256 byte[][] getValues(String attrId) throws NamingException { 257 if (debug != null && Debug.isVerbose() && ((cacheHits + cacheMisses) % 50 == 0)) { 258 debug.println("LDAPRequest Cache hits: " + cacheHits + 259 "; misses: " + cacheMisses); 260 } 261 String cacheKey = name + "|" + attrId; 262 byte[][] values = valueCache.get(cacheKey); 263 if (values != null) { 264 cacheHits++; 265 return values; 266 } 267 cacheMisses++; 268 Map<String, byte[][]> attrs = getValueMap(); 269 values = attrs.get(attrId); 270 return values; 271 } 272 273 /** 274 * Get a map containing the values for this request. The first time 275 * this method is called on an object, the LDAP request is sent, 276 * the results parsed and added to a private map and also to the 277 * cache of this LDAPCertStore. Subsequent calls return the private 278 * map immediately. 279 * 280 * The map contains an entry for each requested attribute. The 281 * attribute name is the key, values are byte[][]. If there are no 282 * values for that attribute, values are byte[0][]. 283 * 284 * @return the value Map 285 * @throws NamingException if a naming exception occurs 286 */ getValueMap()287 private Map<String, byte[][]> getValueMap() throws NamingException { 288 if (valueMap != null) { 289 return valueMap; 290 } 291 if (debug != null && Debug.isVerbose()) { 292 debug.println("LDAPRequest: " + name + ":" + requestedAttributes); 293 requests++; 294 if (requests % 5 == 0) { 295 debug.println("LDAP requests: " + requests); 296 } 297 } 298 valueMap = new HashMap<>(8); 299 String[] attrIds = requestedAttributes.toArray(STRING0); 300 Attributes attrs; 301 302 if (communicationError) { 303 ctx.reconnect(null); 304 communicationError = false; 305 } 306 307 try { 308 attrs = ctx.getAttributes(name, attrIds); 309 } catch (LdapReferralException lre) { 310 // LdapCtx has a hopCount field to avoid infinite loop 311 while (true) { 312 try { 313 String newName = (String) lre.getReferralInfo(); 314 URI newUri = new URI(newName); 315 if (!newUri.getScheme().equalsIgnoreCase("ldap")) { 316 throw new IllegalArgumentException("Not LDAP"); 317 } 318 String newDn = newUri.getPath(); 319 if (newDn != null && newDn.charAt(0) == '/') { 320 newDn = newDn.substring(1); 321 } 322 checkName(newDn); 323 } catch (Exception e) { 324 throw new NamingException("Cannot follow referral to " 325 + lre.getReferralInfo()); 326 } 327 LdapContext refCtx = 328 (LdapContext)lre.getReferralContext(); 329 330 // repeat the original operation at the new context 331 try { 332 attrs = refCtx.getAttributes(name, attrIds); 333 break; 334 } catch (LdapReferralException re) { 335 lre = re; 336 continue; 337 } finally { 338 // Make sure we close referral context 339 refCtx.close(); 340 } 341 } 342 } catch (CommunicationException ce) { 343 communicationError = true; 344 throw ce; 345 } catch (NameNotFoundException e) { 346 // name does not exist on this LDAP server 347 // treat same as not attributes found 348 attrs = EMPTY_ATTRIBUTES; 349 } 350 for (String attrId : requestedAttributes) { 351 Attribute attr = attrs.get(attrId); 352 byte[][] values = getAttributeValues(attr); 353 cacheAttribute(attrId, values); 354 valueMap.put(attrId, values); 355 } 356 return valueMap; 357 } 358 359 /** 360 * Add the values to the cache. 361 */ cacheAttribute(String attrId, byte[][] values)362 private void cacheAttribute(String attrId, byte[][] values) { 363 String cacheKey = name + "|" + attrId; 364 valueCache.put(cacheKey, values); 365 } 366 367 /** 368 * Get the values for the given attribute. If the attribute is null 369 * or does not contain any values, a zero length byte array is 370 * returned. NOTE that it is assumed that all values are byte arrays. 371 */ getAttributeValues(Attribute attr)372 private byte[][] getAttributeValues(Attribute attr) 373 throws NamingException { 374 byte[][] values; 375 if (attr == null) { 376 values = BB0; 377 } else { 378 values = new byte[attr.size()][]; 379 int i = 0; 380 NamingEnumeration<?> enum_ = attr.getAll(); 381 while (enum_.hasMore()) { 382 Object obj = enum_.next(); 383 if (debug != null) { 384 if (obj instanceof String) { 385 debug.println("LDAPCertStore.getAttrValues() " 386 + "enum.next is a string!: " + obj); 387 } 388 } 389 byte[] value = (byte[])obj; 390 values[i++] = value; 391 } 392 } 393 return values; 394 } 395 396 } 397 398 /* 399 * Gets certificates from an attribute id and location in the LDAP 400 * directory. Returns a Collection containing only the Certificates that 401 * match the specified CertSelector. 402 * 403 * @param name the location holding the attribute 404 * @param id the attribute identifier 405 * @param sel a CertSelector that the Certificates must match 406 * @return a Collection of Certificates found 407 * @throws CertStoreException if an exception occurs 408 */ getCertificates(LDAPRequest request, String id, X509CertSelector sel)409 private Collection<X509Certificate> getCertificates(LDAPRequest request, 410 String id, X509CertSelector sel) throws CertStoreException { 411 412 /* fetch encoded certs from storage */ 413 byte[][] encodedCert; 414 try { 415 encodedCert = request.getValues(id); 416 } catch (NamingException namingEx) { 417 throw new CertStoreException(namingEx); 418 } 419 420 int n = encodedCert.length; 421 if (n == 0) { 422 return Collections.emptySet(); 423 } 424 425 List<X509Certificate> certs = new ArrayList<>(n); 426 /* decode certs and check if they satisfy selector */ 427 for (int i = 0; i < n; i++) { 428 ByteArrayInputStream bais = new ByteArrayInputStream(encodedCert[i]); 429 try { 430 Certificate cert = cf.generateCertificate(bais); 431 if (sel.match(cert)) { 432 certs.add((X509Certificate)cert); 433 } 434 } catch (CertificateException e) { 435 if (debug != null) { 436 debug.println("LDAPCertStore.getCertificates() encountered " 437 + "exception while parsing cert, skipping the bad data: "); 438 HexDumpEncoder encoder = new HexDumpEncoder(); 439 debug.println( 440 "[ " + encoder.encodeBuffer(encodedCert[i]) + " ]"); 441 } 442 } 443 } 444 445 return certs; 446 } 447 448 /* 449 * Gets certificate pairs from an attribute id and location in the LDAP 450 * directory. 451 * 452 * @param name the location holding the attribute 453 * @param id the attribute identifier 454 * @return a Collection of X509CertificatePairs found 455 * @throws CertStoreException if an exception occurs 456 */ getCertPairs( LDAPRequest request, String id)457 private Collection<X509CertificatePair> getCertPairs( 458 LDAPRequest request, String id) throws CertStoreException { 459 460 /* fetch the encoded cert pairs from storage */ 461 byte[][] encodedCertPair; 462 try { 463 encodedCertPair = request.getValues(id); 464 } catch (NamingException namingEx) { 465 throw new CertStoreException(namingEx); 466 } 467 468 int n = encodedCertPair.length; 469 if (n == 0) { 470 return Collections.emptySet(); 471 } 472 473 List<X509CertificatePair> certPairs = new ArrayList<>(n); 474 /* decode each cert pair and add it to the Collection */ 475 for (int i = 0; i < n; i++) { 476 try { 477 X509CertificatePair certPair = 478 X509CertificatePair.generateCertificatePair(encodedCertPair[i]); 479 certPairs.add(certPair); 480 } catch (CertificateException e) { 481 if (debug != null) { 482 debug.println( 483 "LDAPCertStore.getCertPairs() encountered exception " 484 + "while parsing cert, skipping the bad data: "); 485 HexDumpEncoder encoder = new HexDumpEncoder(); 486 debug.println( 487 "[ " + encoder.encodeBuffer(encodedCertPair[i]) + " ]"); 488 } 489 } 490 } 491 492 return certPairs; 493 } 494 495 /* 496 * Looks at certificate pairs stored in the crossCertificatePair attribute 497 * at the specified location in the LDAP directory. Returns a Collection 498 * containing all X509Certificates stored in the forward component that match 499 * the forward X509CertSelector and all Certificates stored in the reverse 500 * component that match the reverse X509CertSelector. 501 * <p> 502 * If either forward or reverse is null, all certificates from the 503 * corresponding component will be rejected. 504 * 505 * @param name the location to look in 506 * @param forward the forward X509CertSelector (or null) 507 * @param reverse the reverse X509CertSelector (or null) 508 * @return a Collection of X509Certificates found 509 * @throws CertStoreException if an exception occurs 510 */ getMatchingCrossCerts( LDAPRequest request, X509CertSelector forward, X509CertSelector reverse)511 private Collection<X509Certificate> getMatchingCrossCerts( 512 LDAPRequest request, X509CertSelector forward, 513 X509CertSelector reverse) 514 throws CertStoreException { 515 // Get the cert pairs 516 Collection<X509CertificatePair> certPairs = 517 getCertPairs(request, CROSS_CERT); 518 519 // Find Certificates that match and put them in a list 520 ArrayList<X509Certificate> matchingCerts = new ArrayList<>(); 521 for (X509CertificatePair certPair : certPairs) { 522 X509Certificate cert; 523 if (forward != null) { 524 cert = certPair.getForward(); 525 if ((cert != null) && forward.match(cert)) { 526 matchingCerts.add(cert); 527 } 528 } 529 if (reverse != null) { 530 cert = certPair.getReverse(); 531 if ((cert != null) && reverse.match(cert)) { 532 matchingCerts.add(cert); 533 } 534 } 535 } 536 return matchingCerts; 537 } 538 539 /** 540 * Returns a <code>Collection</code> of <code>X509Certificate</code>s that 541 * match the specified selector. If no <code>X509Certificate</code>s 542 * match the selector, an empty <code>Collection</code> will be returned. 543 * <p> 544 * It is not practical to search every entry in the LDAP database for 545 * matching <code>X509Certificate</code>s. Instead, the 546 * <code>X509CertSelector</code> is examined in order to determine where 547 * matching <code>Certificate</code>s are likely to be found (according 548 * to the PKIX LDAPv2 schema, RFC 2587). 549 * If the subject is specified, its directory entry is searched. If the 550 * issuer is specified, its directory entry is searched. If neither the 551 * subject nor the issuer are specified (or the selector is not an 552 * <code>X509CertSelector</code>), a <code>CertStoreException</code> is 553 * thrown. 554 * 555 * @param xsel a <code>X509CertSelector</code> used to select which 556 * <code>Certificate</code>s should be returned. 557 * @return a <code>Collection</code> of <code>X509Certificate</code>s that 558 * match the specified selector 559 * @throws CertStoreException if an exception occurs 560 */ getCertificates(X509CertSelector xsel, String ldapDN)561 synchronized Collection<X509Certificate> getCertificates 562 (X509CertSelector xsel, String ldapDN) throws CertStoreException { 563 564 if (ldapDN == null) { 565 ldapDN = xsel.getSubjectAsString(); 566 } 567 int basicConstraints = xsel.getBasicConstraints(); 568 String issuer = xsel.getIssuerAsString(); 569 HashSet<X509Certificate> certs = new HashSet<>(); 570 if (debug != null) { 571 debug.println("LDAPCertStore.engineGetCertificates() basicConstraints: " 572 + basicConstraints); 573 } 574 575 // basicConstraints: 576 // -2: only EE certs accepted 577 // -1: no check is done 578 // 0: any CA certificate accepted 579 // >1: certificate's basicConstraints extension pathlen must match 580 if (ldapDN != null) { 581 if (debug != null) { 582 debug.println("LDAPCertStore.engineGetCertificates() " 583 + " subject is not null"); 584 } 585 LDAPRequest request = new LDAPRequest(ldapDN); 586 if (basicConstraints > -2) { 587 request.addRequestedAttribute(CROSS_CERT); 588 request.addRequestedAttribute(CA_CERT); 589 request.addRequestedAttribute(ARL); 590 if (prefetchCRLs) { 591 request.addRequestedAttribute(CRL); 592 } 593 } 594 if (basicConstraints < 0) { 595 request.addRequestedAttribute(USER_CERT); 596 } 597 598 if (basicConstraints > -2) { 599 certs.addAll(getMatchingCrossCerts(request, xsel, null)); 600 if (debug != null) { 601 debug.println("LDAPCertStore.engineGetCertificates() after " 602 + "getMatchingCrossCerts(subject,xsel,null),certs.size(): " 603 + certs.size()); 604 } 605 certs.addAll(getCertificates(request, CA_CERT, xsel)); 606 if (debug != null) { 607 debug.println("LDAPCertStore.engineGetCertificates() after " 608 + "getCertificates(subject,CA_CERT,xsel),certs.size(): " 609 + certs.size()); 610 } 611 } 612 if (basicConstraints < 0) { 613 certs.addAll(getCertificates(request, USER_CERT, xsel)); 614 if (debug != null) { 615 debug.println("LDAPCertStore.engineGetCertificates() after " 616 + "getCertificates(subject,USER_CERT, xsel),certs.size(): " 617 + certs.size()); 618 } 619 } 620 } else { 621 if (debug != null) { 622 debug.println 623 ("LDAPCertStore.engineGetCertificates() subject is null"); 624 } 625 if (basicConstraints == -2) { 626 throw new CertStoreException("need subject to find EE certs"); 627 } 628 if (issuer == null) { 629 throw new CertStoreException("need subject or issuer to find certs"); 630 } 631 } 632 if (debug != null) { 633 debug.println("LDAPCertStore.engineGetCertificates() about to " 634 + "getMatchingCrossCerts..."); 635 } 636 if ((issuer != null) && (basicConstraints > -2)) { 637 LDAPRequest request = new LDAPRequest(issuer); 638 request.addRequestedAttribute(CROSS_CERT); 639 request.addRequestedAttribute(CA_CERT); 640 request.addRequestedAttribute(ARL); 641 if (prefetchCRLs) { 642 request.addRequestedAttribute(CRL); 643 } 644 645 certs.addAll(getMatchingCrossCerts(request, null, xsel)); 646 if (debug != null) { 647 debug.println("LDAPCertStore.engineGetCertificates() after " 648 + "getMatchingCrossCerts(issuer,null,xsel),certs.size(): " 649 + certs.size()); 650 } 651 certs.addAll(getCertificates(request, CA_CERT, xsel)); 652 if (debug != null) { 653 debug.println("LDAPCertStore.engineGetCertificates() after " 654 + "getCertificates(issuer,CA_CERT,xsel),certs.size(): " 655 + certs.size()); 656 } 657 } 658 if (debug != null) { 659 debug.println("LDAPCertStore.engineGetCertificates() returning certs"); 660 } 661 return certs; 662 } 663 664 /* 665 * Gets CRLs from an attribute id and location in the LDAP directory. 666 * Returns a Collection containing only the CRLs that match the 667 * specified X509CRLSelector. 668 * 669 * @param name the location holding the attribute 670 * @param id the attribute identifier 671 * @param sel a X509CRLSelector that the CRLs must match 672 * @return a Collection of CRLs found 673 * @throws CertStoreException if an exception occurs 674 */ getCRLs(LDAPRequest request, String id, X509CRLSelector sel)675 private Collection<X509CRL> getCRLs(LDAPRequest request, String id, 676 X509CRLSelector sel) throws CertStoreException { 677 678 /* fetch the encoded crls from storage */ 679 byte[][] encodedCRL; 680 try { 681 encodedCRL = request.getValues(id); 682 } catch (NamingException namingEx) { 683 throw new CertStoreException(namingEx); 684 } 685 686 int n = encodedCRL.length; 687 if (n == 0) { 688 return Collections.emptySet(); 689 } 690 691 List<X509CRL> crls = new ArrayList<>(n); 692 /* decode each crl and check if it matches selector */ 693 for (int i = 0; i < n; i++) { 694 try { 695 CRL crl = cf.generateCRL(new ByteArrayInputStream(encodedCRL[i])); 696 if (sel.match(crl)) { 697 crls.add((X509CRL)crl); 698 } 699 } catch (CRLException e) { 700 if (debug != null) { 701 debug.println("LDAPCertStore.getCRLs() encountered exception" 702 + " while parsing CRL, skipping the bad data: "); 703 HexDumpEncoder encoder = new HexDumpEncoder(); 704 debug.println("[ " + encoder.encodeBuffer(encodedCRL[i]) + " ]"); 705 } 706 } 707 } 708 709 return crls; 710 } 711 712 /** 713 * Returns a <code>Collection</code> of <code>X509CRL</code>s that 714 * match the specified selector. If no <code>X509CRL</code>s 715 * match the selector, an empty <code>Collection</code> will be returned. 716 * <p> 717 * It is not practical to search every entry in the LDAP database for 718 * matching <code>X509CRL</code>s. Instead, the <code>X509CRLSelector</code> 719 * is examined in order to determine where matching <code>X509CRL</code>s 720 * are likely to be found (according to the PKIX LDAPv2 schema, RFC 2587). 721 * If issuerNames or certChecking are specified, the issuer's directory 722 * entry is searched. If neither issuerNames or certChecking are specified 723 * (or the selector is not an <code>X509CRLSelector</code>), a 724 * <code>CertStoreException</code> is thrown. 725 * 726 * @param xsel A <code>X509CRLSelector</code> used to select which 727 * <code>CRL</code>s should be returned. Specify <code>null</code> 728 * to return all <code>CRL</code>s. 729 * @return A <code>Collection</code> of <code>X509CRL</code>s that 730 * match the specified selector 731 * @throws CertStoreException if an exception occurs 732 */ getCRLs(X509CRLSelector xsel, String ldapDN)733 synchronized Collection<X509CRL> getCRLs(X509CRLSelector xsel, 734 String ldapDN) throws CertStoreException { 735 736 HashSet<X509CRL> crls = new HashSet<>(); 737 738 // Look in directory entry for issuer of cert we're checking. 739 Collection<Object> issuerNames; 740 X509Certificate certChecking = xsel.getCertificateChecking(); 741 if (certChecking != null) { 742 issuerNames = new HashSet<>(); 743 X500Principal issuer = certChecking.getIssuerX500Principal(); 744 issuerNames.add(issuer.getName(X500Principal.RFC2253)); 745 } else { 746 // But if we don't know which cert we're checking, try the directory 747 // entries of all acceptable CRL issuers 748 if (ldapDN != null) { 749 issuerNames = new HashSet<>(); 750 issuerNames.add(ldapDN); 751 } else { 752 issuerNames = xsel.getIssuerNames(); 753 if (issuerNames == null) { 754 throw new CertStoreException("need issuerNames or" 755 + " certChecking to find CRLs"); 756 } 757 } 758 } 759 for (Object nameObject : issuerNames) { 760 String issuerName; 761 if (nameObject instanceof byte[]) { 762 try { 763 X500Principal issuer = new X500Principal((byte[])nameObject); 764 issuerName = issuer.getName(X500Principal.RFC2253); 765 } catch (IllegalArgumentException e) { 766 continue; 767 } 768 } else { 769 issuerName = (String)nameObject; 770 } 771 // If all we want is CA certs, try to get the (probably shorter) ARL 772 Collection<X509CRL> entryCRLs = Collections.emptySet(); 773 if (certChecking == null || certChecking.getBasicConstraints() != -1) { 774 LDAPRequest request = new LDAPRequest(issuerName); 775 request.addRequestedAttribute(CROSS_CERT); 776 request.addRequestedAttribute(CA_CERT); 777 request.addRequestedAttribute(ARL); 778 if (prefetchCRLs) { 779 request.addRequestedAttribute(CRL); 780 } 781 try { 782 entryCRLs = getCRLs(request, ARL, xsel); 783 if (entryCRLs.isEmpty()) { 784 // no ARLs found. We assume that means that there are 785 // no ARLs on this server at all and prefetch the CRLs. 786 prefetchCRLs = true; 787 } else { 788 crls.addAll(entryCRLs); 789 } 790 } catch (CertStoreException e) { 791 if (debug != null) { 792 debug.println("LDAPCertStore.engineGetCRLs non-fatal error " 793 + "retrieving ARLs:" + e); 794 e.printStackTrace(); 795 } 796 } 797 } 798 // Otherwise, get the CRL 799 // if certChecking is null, we don't know if we should look in ARL or CRL 800 // attribute, so check both for matching CRLs. 801 if (entryCRLs.isEmpty() || certChecking == null) { 802 LDAPRequest request = new LDAPRequest(issuerName); 803 request.addRequestedAttribute(CRL); 804 entryCRLs = getCRLs(request, CRL, xsel); 805 crls.addAll(entryCRLs); 806 } 807 } 808 return crls; 809 } 810 } 811