001/* 002 * CDDL HEADER START 003 * 004 * The contents of this file are subject to the terms of the 005 * Common Development and Distribution License, Version 1.0 only 006 * (the "License"). You may not use this file except in compliance 007 * with the License. 008 * 009 * You can obtain a copy of the license at legal-notices/CDDLv1_0.txt 010 * or http://forgerock.org/license/CDDLv1.0.html. 011 * See the License for the specific language governing permissions 012 * and limitations under the License. 013 * 014 * When distributing Covered Code, include this CDDL HEADER in each 015 * file and include the License file at legal-notices/CDDLv1_0.txt. 016 * If applicable, add the following below this CDDL HEADER, with the 017 * fields enclosed by brackets "[]" replaced with your own identifying 018 * information: 019 * Portions Copyright [yyyy] [name of copyright owner] 020 * 021 * CDDL HEADER END 022 * 023 * 024 * Copyright 2006-2010 Sun Microsystems, Inc. 025 * Portions Copyright 2011-2015 ForgeRock AS 026 */ 027package org.opends.server.admin; 028 029import static org.opends.messages.AdminMessages.*; 030 031import java.io.File; 032import java.io.FileWriter; 033import java.io.PrintWriter; 034import java.net.InetAddress; 035import java.util.List; 036import java.util.SortedSet; 037import java.util.TreeSet; 038 039import javax.naming.ldap.Rdn; 040 041import org.forgerock.i18n.LocalizableMessage; 042import org.forgerock.i18n.slf4j.LocalizedLogger; 043import org.forgerock.opendj.config.server.ConfigChangeResult; 044import org.forgerock.opendj.config.server.ConfigException; 045import org.forgerock.opendj.ldap.AddressMask; 046import org.opends.server.admin.server.ConfigurationChangeListener; 047import org.opends.server.admin.server.ServerManagementContext; 048import org.opends.server.admin.std.meta.LDAPConnectionHandlerCfgDefn.SSLClientAuthPolicy; 049import org.opends.server.admin.std.server.AdministrationConnectorCfg; 050import org.opends.server.admin.std.server.ConnectionHandlerCfg; 051import org.opends.server.admin.std.server.FileBasedKeyManagerProviderCfg; 052import org.opends.server.admin.std.server.FileBasedTrustManagerProviderCfg; 053import org.opends.server.admin.std.server.KeyManagerProviderCfg; 054import org.opends.server.admin.std.server.LDAPConnectionHandlerCfg; 055import org.opends.server.admin.std.server.RootCfg; 056import org.opends.server.admin.std.server.TrustManagerProviderCfg; 057import org.opends.server.core.DirectoryServer; 058import org.opends.server.core.ServerContext; 059import org.opends.server.core.SynchronousStrategy; 060import org.opends.server.protocols.ldap.LDAPConnectionHandler; 061import org.opends.server.types.DN; 062import org.opends.server.types.DirectoryException; 063import org.opends.server.types.FilePermission; 064import org.opends.server.types.InitializationException; 065import org.opends.server.util.CertificateManager; 066import org.opends.server.util.Platform.KeyType; 067import org.opends.server.util.SetupUtils; 068 069/** 070 * This class is a wrapper on top of LDAPConnectionHandler to manage 071 * the administration connector, which is an LDAPConnectionHandler 072 * with specific (limited) configuration properties. 073 */ 074public final class AdministrationConnector implements 075 ConfigurationChangeListener<AdministrationConnectorCfg> 076{ 077 078 /** Default Administration Connector port. */ 079 public static final int DEFAULT_ADMINISTRATION_CONNECTOR_PORT = 4444; 080 /** Validity (in days) of the generated certificate. */ 081 public static final int ADMIN_CERT_VALIDITY = 20 * 365; 082 083 /** Friendly name of the administration connector. */ 084 private static final String FRIENDLY_NAME = "Administration Connector"; 085 private static final LocalizedLogger logger = LocalizedLogger.getLoggerForThisClass(); 086 087 private LDAPConnectionHandler adminConnectionHandler; 088 private AdministrationConnectorCfg config; 089 090 /** Predefined values for Administration Connector configuration. */ 091 private static final String ADMIN_CLASS_NAME = 092 "org.opends.server.protocols.ldap.LDAPConnectionHandler"; 093 094 private static final boolean ADMIN_ALLOW_LDAP_V2 = false; 095 private static final boolean ADMIN_ALLOW_START_TLS = false; 096 097 private static final SortedSet<AddressMask> ADMIN_ALLOWED_CLIENT = new TreeSet<>(); 098 private static final SortedSet<AddressMask> ADMIN_DENIED_CLIENT = new TreeSet<>(); 099 100 private static final boolean ADMIN_ENABLED = true; 101 private static final boolean ADMIN_KEEP_STATS = true; 102 private static final boolean ADMIN_USE_SSL = true; 103 104 private static final int ADMIN_ACCEPT_BACKLOG = 128; 105 private static final boolean ADMIN_ALLOW_TCP_REUSE_ADDRESS = true; 106 107 /** 2mn. */ 108 private static final long ADMIN_MAX_BLOCKED_WRITE_TIME_LIMIT = 120000; 109 /** 5 Mb. */ 110 private static final int ADMIN_MAX_REQUEST_SIZE = 5000000; 111 private static final int ADMIN_WRITE_BUFFER_SIZE = 4096; 112 private static final int ADMIN_NUM_REQUEST_HANDLERS = 1; 113 private static final boolean ADMIN_SEND_REJECTION_NOTICE = true; 114 private static final boolean ADMIN_USE_TCP_KEEP_ALIVE = true; 115 private static final boolean ADMIN_USE_TCP_NO_DELAY = true; 116 private static final SSLClientAuthPolicy ADMIN_SSL_CLIENT_AUTH_POLICY = 117 SSLClientAuthPolicy.DISABLED; 118 119 private final ServerContext serverContext; 120 121 /** 122 * Initializes this administration connector provider based on the 123 * information in the provided administration connector 124 * configuration. 125 * 126 * @param configuration 127 * The connection handler configuration that contains the 128 * information to use to initialize this connection 129 * handler. 130 * @throws ConfigException 131 * If an unrecoverable problem arises in the process of 132 * performing the initialization as a result of the server 133 * configuration. 134 * @throws InitializationException 135 * If a problem occurs during initialization that is not 136 * related to the server configuration. 137 */ 138 public void initializeAdministrationConnector( 139 AdministrationConnectorCfg configuration) throws ConfigException, 140 InitializationException 141 { 142 this.config = configuration; 143 144 // Administration Connector uses the LDAP connection handler implementation 145 adminConnectionHandler = new LDAPConnectionHandler( 146 new SynchronousStrategy(), FRIENDLY_NAME); 147 adminConnectionHandler.initializeConnectionHandler(serverContext, new LDAPConnectionCfgAdapter(config)); 148 adminConnectionHandler.setAdminConnectionHandler(); 149 150 // Register this as a change listener. 151 config.addChangeListener(this); 152 } 153 154 155 /** 156 * Creates an instance of the administration connector. 157 * 158 * @param serverContext 159 * The server context. 160 **/ 161 public AdministrationConnector(ServerContext serverContext) 162 { 163 this.serverContext = serverContext; 164 } 165 166 /** 167 * Retrieves the connection handler linked to this administration connector. 168 * 169 * @return The connection handler linked to this administration connector. 170 */ 171 public LDAPConnectionHandler getConnectionHandler() 172 { 173 return adminConnectionHandler; 174 } 175 176 /** {@inheritDoc} */ 177 @Override 178 public boolean isConfigurationChangeAcceptable( 179 AdministrationConnectorCfg configuration, 180 List<LocalizableMessage> unacceptableReasons) 181 { 182 return adminConnectionHandler.isConfigurationAcceptable(new LDAPConnectionCfgAdapter(configuration), 183 unacceptableReasons); 184 } 185 186 /** {@inheritDoc} */ 187 @Override 188 public ConfigChangeResult applyConfigurationChange( 189 AdministrationConnectorCfg configuration) 190 { 191 return adminConnectionHandler.applyConfigurationChange(new LDAPConnectionCfgAdapter(configuration)); 192 } 193 194 195 196 /** 197 * This private class implements a fake LDAP connection Handler configuration. 198 * This allows to re-use the LDAPConnectionHandler as it is. 199 */ 200 private static class LDAPConnectionCfgAdapter implements 201 LDAPConnectionHandlerCfg 202 { 203 private final AdministrationConnectorCfg config; 204 205 public LDAPConnectionCfgAdapter(AdministrationConnectorCfg config) 206 { 207 this.config = config; 208 } 209 210 /** {@inheritDoc} */ 211 @Override 212 public Class<? extends LDAPConnectionHandlerCfg> configurationClass() 213 { 214 return LDAPConnectionHandlerCfg.class; 215 } 216 217 /** {@inheritDoc} */ 218 @Override 219 public void addLDAPChangeListener( 220 ConfigurationChangeListener<LDAPConnectionHandlerCfg> listener) 221 { 222 // do nothing. change listener already added. 223 } 224 225 /** {@inheritDoc} */ 226 @Override 227 public void removeLDAPChangeListener( 228 ConfigurationChangeListener<LDAPConnectionHandlerCfg> listener) 229 { 230 // do nothing. change listener already added. 231 } 232 233 /** {@inheritDoc} */ 234 @Override 235 public int getAcceptBacklog() 236 { 237 return ADMIN_ACCEPT_BACKLOG; 238 } 239 240 /** {@inheritDoc} */ 241 @Override 242 public boolean isAllowLDAPV2() 243 { 244 return ADMIN_ALLOW_LDAP_V2; 245 } 246 247 /** {@inheritDoc} */ 248 @Override 249 public boolean isAllowStartTLS() 250 { 251 return ADMIN_ALLOW_START_TLS; 252 } 253 254 /** {@inheritDoc} */ 255 @Override 256 public boolean isAllowTCPReuseAddress() 257 { 258 return ADMIN_ALLOW_TCP_REUSE_ADDRESS; 259 } 260 261 /** {@inheritDoc} */ 262 @Override 263 public String getJavaClass() 264 { 265 return ADMIN_CLASS_NAME; 266 } 267 268 /** {@inheritDoc} */ 269 @Override 270 public boolean isKeepStats() 271 { 272 return ADMIN_KEEP_STATS; 273 } 274 275 /** {@inheritDoc} */ 276 @Override 277 public String getKeyManagerProvider() 278 { 279 return config.getKeyManagerProvider(); 280 } 281 282 /** {@inheritDoc} */ 283 @Override 284 public DN getKeyManagerProviderDN() 285 { 286 return config.getKeyManagerProviderDN(); 287 } 288 289 /** {@inheritDoc} */ 290 @Override 291 public SortedSet<InetAddress> getListenAddress() 292 { 293 return config.getListenAddress(); 294 } 295 296 /** {@inheritDoc} */ 297 @Override 298 public int getListenPort() 299 { 300 return config.getListenPort(); 301 } 302 303 /** {@inheritDoc} */ 304 @Override 305 public long getMaxBlockedWriteTimeLimit() 306 { 307 return ADMIN_MAX_BLOCKED_WRITE_TIME_LIMIT; 308 } 309 310 /** {@inheritDoc} */ 311 @Override 312 public long getMaxRequestSize() 313 { 314 return ADMIN_MAX_REQUEST_SIZE; 315 } 316 317 /** {@inheritDoc} */ 318 @Override 319 public long getBufferSize() 320 { 321 return ADMIN_WRITE_BUFFER_SIZE; 322 } 323 324 /** {@inheritDoc} */ 325 @Override 326 public Integer getNumRequestHandlers() 327 { 328 return ADMIN_NUM_REQUEST_HANDLERS; 329 } 330 331 /** {@inheritDoc} */ 332 @Override 333 public boolean isSendRejectionNotice() 334 { 335 return ADMIN_SEND_REJECTION_NOTICE; 336 } 337 338 /** {@inheritDoc} */ 339 @Override 340 public SortedSet<String> getSSLCertNickname() 341 { 342 return config.getSSLCertNickname(); 343 } 344 345 /** {@inheritDoc} */ 346 @Override 347 public SortedSet<String> getSSLCipherSuite() 348 { 349 return config.getSSLCipherSuite(); 350 } 351 352 /** {@inheritDoc} */ 353 @Override 354 public SSLClientAuthPolicy getSSLClientAuthPolicy() 355 { 356 return ADMIN_SSL_CLIENT_AUTH_POLICY; 357 } 358 359 /** {@inheritDoc} */ 360 @Override 361 public SortedSet<String> getSSLProtocol() 362 { 363 return config.getSSLProtocol(); 364 } 365 366 /** {@inheritDoc} */ 367 @Override 368 public String getTrustManagerProvider() 369 { 370 return config.getTrustManagerProvider(); 371 } 372 373 /** {@inheritDoc} */ 374 @Override 375 public DN getTrustManagerProviderDN() 376 { 377 return config.getTrustManagerProviderDN(); 378 } 379 380 /** {@inheritDoc} */ 381 @Override 382 public boolean isUseSSL() 383 { 384 return ADMIN_USE_SSL; 385 } 386 387 /** {@inheritDoc} */ 388 @Override 389 public boolean isUseTCPKeepAlive() 390 { 391 return ADMIN_USE_TCP_KEEP_ALIVE; 392 } 393 394 /** {@inheritDoc} */ 395 @Override 396 public boolean isUseTCPNoDelay() 397 { 398 return ADMIN_USE_TCP_NO_DELAY; 399 } 400 401 /** {@inheritDoc} */ 402 @Override 403 public void addChangeListener( 404 ConfigurationChangeListener<ConnectionHandlerCfg> listener) 405 { 406 // do nothing. change listener already added. 407 } 408 409 /** {@inheritDoc} */ 410 @Override 411 public void removeChangeListener( 412 ConfigurationChangeListener<ConnectionHandlerCfg> listener) 413 { 414 // do nothing. change listener already added. 415 } 416 417 /** {@inheritDoc} */ 418 @Override 419 public SortedSet<AddressMask> getAllowedClient() 420 { 421 return ADMIN_ALLOWED_CLIENT; 422 } 423 424 /** {@inheritDoc} */ 425 @Override 426 public SortedSet<AddressMask> getDeniedClient() 427 { 428 return ADMIN_DENIED_CLIENT; 429 } 430 431 /** {@inheritDoc} */ 432 @Override 433 public boolean isEnabled() 434 { 435 return ADMIN_ENABLED; 436 } 437 438 /** {@inheritDoc} */ 439 @Override 440 public DN dn() 441 { 442 return config.dn(); 443 } 444 } 445 446 447 448 /** 449 * Creates a self-signed JKS certificate if needed. 450 * 451 * @param serverContext 452 * The server context. 453 * @throws InitializationException 454 * If an unexpected error occurred whilst trying to create the 455 * certificate. 456 */ 457 public static void createSelfSignedCertificateIfNeeded(ServerContext serverContext) 458 throws InitializationException 459 { 460 try 461 { 462 RootCfg root = ServerManagementContext.getInstance() 463 .getRootConfiguration(); 464 AdministrationConnectorCfg config = root.getAdministrationConnector(); 465 466 // Check if certificate generation is needed 467 final SortedSet<String> certAliases = config.getSSLCertNickname(); 468 KeyManagerProviderCfg keyMgrConfig = root.getKeyManagerProvider(config 469 .getKeyManagerProvider()); 470 TrustManagerProviderCfg trustMgrConfig = root 471 .getTrustManagerProvider(config.getTrustManagerProvider()); 472 473 if (hasDefaultConfigChanged(keyMgrConfig, trustMgrConfig)) 474 { 475 // nothing to do 476 return; 477 } 478 479 FileBasedKeyManagerProviderCfg fbKeyManagerConfig = 480 (FileBasedKeyManagerProviderCfg) keyMgrConfig; 481 String keystorePath = getFullPath(fbKeyManagerConfig.getKeyStoreFile()); 482 FileBasedTrustManagerProviderCfg fbTrustManagerConfig = 483 (FileBasedTrustManagerProviderCfg) trustMgrConfig; 484 String truststorePath = getFullPath(fbTrustManagerConfig 485 .getTrustStoreFile()); 486 String pinFilePath = getFullPath(fbKeyManagerConfig.getKeyStorePinFile()); 487 488 // Check that either we do not have any file, 489 // or we have the 3 required files (keystore, truststore, pin 490 // file) 491 boolean keystore = false; 492 boolean truststore = false; 493 boolean pinFile = false; 494 int nbFiles = 0; 495 if (new File(keystorePath).exists()) 496 { 497 keystore = true; 498 nbFiles++; 499 } 500 if (new File(truststorePath).exists()) 501 { 502 truststore = true; 503 nbFiles++; 504 } 505 if (new File(pinFilePath).exists()) 506 { 507 pinFile = true; 508 nbFiles++; 509 } 510 if (nbFiles == 3) 511 { 512 // nothing to do 513 return; 514 } 515 if (nbFiles != 0) 516 { 517 // 1 or 2 files are missing : error 518 String err = ""; 519 if (!keystore) 520 { 521 err += keystorePath + " "; 522 } 523 if (!truststore) 524 { 525 err += truststorePath + " "; 526 } 527 if (!pinFile) 528 { 529 err += pinFilePath + " "; 530 } 531 LocalizableMessage message = ERR_ADMIN_CERTIFICATE_GENERATION_MISSING_FILES 532 .get(err); 533 logger.error(message); 534 throw new InitializationException(message); 535 } 536 537 // Generate a password 538 String pwd = new String(SetupUtils.createSelfSignedCertificatePwd()); 539 540 // Generate a self-signed certificate 541 CertificateManager certManager = new CertificateManager( 542 getFullPath(fbKeyManagerConfig.getKeyStoreFile()), fbKeyManagerConfig 543 .getKeyStoreType(), pwd); 544 String hostName = 545 SetupUtils.getHostNameForCertificate(DirectoryServer.getServerRoot()); 546 547 // Temporary exported certificate's file 548 String tempCertPath = getFullPath("config" + File.separator 549 + "admin-cert.txt"); 550 551 // Create a new trust store and import the server certificate 552 // into it 553 CertificateManager trustManager = new CertificateManager(truststorePath, 554 CertificateManager.KEY_STORE_TYPE_JKS, pwd); 555 for (String certAlias : certAliases) 556 { 557 final KeyType keyType = KeyType.getTypeOrDefault(certAlias); 558 final String subjectDN = 559 "cn=" + Rdn.escapeValue(hostName) + ",O=" + FRIENDLY_NAME + " " + keyType + " Self-Signed Certificate"; 560 certManager.generateSelfSignedCertificate(keyType, certAlias, subjectDN, ADMIN_CERT_VALIDITY); 561 562 SetupUtils.exportCertificate(certManager, certAlias, tempCertPath); 563 564 // import the server certificate into it 565 final File tempCertFile = new File(tempCertPath); 566 trustManager.addCertificate(certAlias, tempCertFile); 567 tempCertFile.delete(); 568 } 569 570 // Generate a password file 571 if (!new File(pinFilePath).exists()) 572 { 573 try (final FileWriter file = new FileWriter(pinFilePath); 574 final PrintWriter out = new PrintWriter(file)) 575 { 576 out.println(pwd); 577 out.flush(); 578 } 579 } 580 581 // Change the password file permission if possible 582 try 583 { 584 if (!FilePermission.setPermissions(new File(pinFilePath), 585 new FilePermission(0600))) 586 { 587 // Log a warning that the permissions were not set. 588 logger.warn(WARN_ADMIN_SET_PERMISSIONS_FAILED, pinFilePath); 589 } 590 } 591 catch (DirectoryException e) 592 { 593 // Log a warning that the permissions were not set. 594 logger.warn(WARN_ADMIN_SET_PERMISSIONS_FAILED, pinFilePath); 595 } 596 } 597 catch (InitializationException e) 598 { 599 throw e; 600 } 601 catch (Exception e) 602 { 603 throw new InitializationException(ERR_ADMIN_CERTIFICATE_GENERATION.get(e.getMessage()), e); 604 } 605 } 606 607 /** 608 * Check if default configuration for administrator's key manager and trust 609 * manager provider has changed. 610 * 611 * @param keyConfig 612 * key manager provider configuration 613 * @param trustConfig 614 * trust manager provider configuration 615 * @return true if default configuration has changed, false otherwise 616 */ 617 private static boolean hasDefaultConfigChanged( 618 KeyManagerProviderCfg keyConfig, TrustManagerProviderCfg trustConfig) 619 { 620 if (keyConfig.isEnabled() 621 && keyConfig instanceof FileBasedKeyManagerProviderCfg 622 && trustConfig.isEnabled() 623 && trustConfig instanceof FileBasedTrustManagerProviderCfg) 624 { 625 FileBasedKeyManagerProviderCfg fileKeyConfig = 626 (FileBasedKeyManagerProviderCfg) keyConfig; 627 boolean pinIsProvidedByFileOnly = 628 fileKeyConfig.getKeyStorePinFile() != null 629 && fileKeyConfig.getKeyStorePin() == null 630 && fileKeyConfig.getKeyStorePinEnvironmentVariable() == null 631 && fileKeyConfig.getKeyStorePinProperty() == null; 632 return !pinIsProvidedByFileOnly; 633 } 634 return true; 635 } 636 637 private static String getFullPath(String path) 638 { 639 File file = new File(path); 640 if (!file.isAbsolute()) 641 { 642 path = DirectoryServer.getInstanceRoot() + File.separator + path; 643 } 644 645 return path; 646 } 647}