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.util; 028 029import static org.forgerock.util.Utils.closeSilently; 030 031import java.io.*; 032import java.net.InetSocketAddress; 033import java.net.ServerSocket; 034import java.net.Socket; 035import java.net.UnknownHostException; 036import java.security.KeyStoreException; 037import java.security.cert.Certificate; 038import java.security.cert.CertificateEncodingException; 039import java.util.HashSet; 040import java.util.LinkedList; 041import java.util.Random; 042import java.util.Set; 043 044import com.forgerock.opendj.util.OperatingSystem; 045 046/** 047 * This class provides a number of utility methods that may be used during the 048 * graphical or command-line setup process. 049 */ 050@org.opends.server.types.PublicAPI( 051 stability=org.opends.server.types.StabilityLevel.VOLATILE, 052 mayInstantiate=false, 053 mayExtend=false, 054 mayInvoke=true) 055public class SetupUtils 056{ 057 /** 058 * Specific environment variable used by the scripts to find java. 059 */ 060 public static final String OPENDJ_JAVA_HOME = "OPENDJ_JAVA_HOME"; 061 062 /** 063 * Specific environment variable used by the scripts to set java arguments. 064 */ 065 public static final String OPENDJ_JAVA_ARGS = "OPENDJ_JAVA_ARGS"; 066 067 /** 068 * Java property used to know which are the jar files that must be downloaded 069 * lazily. The current code in WebStartDownloader that uses this property 070 * assumes that the URL are separated with an space. 071 */ 072 public static final String LAZY_JAR_URLS = 073 "org.opends.quicksetup.lazyjarurls"; 074 075 /** 076 * Java property used to know which is the name of the zip file that must 077 * be unzipped and whose contents must be extracted during the Web Start 078 * based setup. 079 */ 080 public static final String ZIP_FILE_NAME = 081 "org.opends.quicksetup.zipfilename"; 082 083 /** 084 * The relative path where all the libraries (jar files) are. 085 */ 086 public static final String LIBRARIES_PATH_RELATIVE = "lib"; 087 088 /** 089 * The relative path where the setup stores the name of the host the user 090 * provides. This is used for instance to generate the self-signed admin 091 * certificate the first time the server starts. 092 */ 093 public static final String HOST_NAME_FILE = "config" + File.separatorChar 094 + "hostname"; 095 096 /* These string values must be synchronized with Directory Server's main 097 * method. These string values are considered stable by the server team and 098 * not candidates for internationalization. */ 099 /** Product name. */ 100 public static final String NAME = "Name"; 101 /** Build ID. */ 102 public static final String BUILD_ID = "Build ID"; 103 /** Major version. */ 104 public static final String MAJOR_VERSION = "Major Version"; 105 /** Minor version. */ 106 public static final String MINOR_VERSION = "Minor Version"; 107 /** Point version of the product. */ 108 public static final String POINT_VERSION = "Point Version"; 109 /** Revision in VCS. */ 110 public static final String REVISION = "Revision Number"; 111 /** The VCS url repository. */ 112 public static final String URL_REPOSITORY = "URL Repository"; 113 /** The version qualifier. */ 114 public static final String VERSION_QUALIFIER = "Version Qualifier"; 115 /** Incompatibilities found between builds (used by the upgrade tool). */ 116 public static final String INCOMPATIBILITY_EVENTS = "Upgrade Event IDs"; 117 /** Fix IDs associated with the build. */ 118 public static final String FIX_IDS = "Fix IDs"; 119 /** Debug build identifier. */ 120 public static final String DEBUG_BUILD = "Debug Build"; 121 /** The OS used during the build. */ 122 public static final String BUILD_OS = "Build OS"; 123 /** The user that generated the build. */ 124 public static final String BUILD_USER = "Build User"; 125 /** The java version used to generate the build. */ 126 public static final String BUILD_JAVA_VERSION = "Build Java Version"; 127 /** The java vendor of the JVM used to build. */ 128 public static final String BUILD_JAVA_VENDOR = "Build Java Vendor"; 129 /** The version of the JVM used to create the build. */ 130 public static final String BUILD_JVM_VERSION = "Build JVM Version"; 131 /** The vendor of the JVM used to create the build. */ 132 public static final String BUILD_JVM_VENDOR = "Build JVM Vendor"; 133 /** The build number. */ 134 public static final String BUILD_NUMBER = "Build Number"; 135 136 /** 137 * A variable used to keep the latest read host name from the file written 138 * by the setup. 139 */ 140 private static String lastReadHostName; 141 142 /** 143 * Creates a MakeLDIF template file using the provided information. 144 * 145 * @param baseDN The base DN for the data in the template file. 146 * @param numEntries The number of user entries the template file should 147 * create. 148 * 149 * @return The {@code File} object that references the created template file. 150 * 151 * @throws IOException If a problem occurs while writing the template file. 152 */ 153 public static File createTemplateFile(String baseDN, int numEntries) 154 throws IOException 155 { 156 Set<String> baseDNs = new HashSet<>(1); 157 baseDNs.add(baseDN); 158 return createTemplateFile(baseDNs, numEntries); 159 } 160 161 /** 162 * Creates a MakeLDIF template file using the provided information. 163 * 164 * @param baseDNs The base DNs for the data in the template file. 165 * @param numEntries The number of user entries the template file should 166 * create. 167 * 168 * @return The {@code File} object that references the created template file. 169 * 170 * @throws IOException If a problem occurs while writing the template file. 171 */ 172 public static File createTemplateFile(Set<String> baseDNs, 173 int numEntries) 174 throws IOException 175 { 176 File templateFile = File.createTempFile("opendj-install", ".template"); 177 templateFile.deleteOnExit(); 178 179 LinkedList<String> lines = new LinkedList<>(); 180 int i = 0; 181 for (String baseDN : baseDNs) 182 { 183 i++; 184 lines.add("define suffix"+i+"=" + baseDN); 185 } 186 if (numEntries > 0) 187 { 188 lines.add("define numusers=" + numEntries); 189 } 190 191 for (i=1; i<=baseDNs.size(); i++) 192 { 193 lines.add(""); 194 lines.add("branch: [suffix"+i+"]"); 195 lines.add(""); 196 lines.add("branch: ou=People,[suffix"+i+"]"); 197 198 if (numEntries > 0) 199 { 200 lines.add("subordinateTemplate: person:[numusers]"); 201 lines.add(""); 202 } 203 } 204 205 if (!baseDNs.isEmpty() && numEntries > 0) 206 { 207 lines.add("template: person"); 208 lines.add("rdnAttr: uid"); 209 lines.add("objectClass: top"); 210 lines.add("objectClass: person"); 211 lines.add("objectClass: organizationalPerson"); 212 lines.add("objectClass: inetOrgPerson"); 213 lines.add("givenName: <first>"); 214 lines.add("sn: <last>"); 215 lines.add("cn: {givenName} {sn}"); 216 lines.add("initials: {givenName:1}" + 217 "<random:chars:ABCDEFGHIJKLMNOPQRSTUVWXYZ:1>{sn:1}"); 218 lines.add("employeeNumber: <sequential:0>"); 219 lines.add("uid: user.{employeeNumber}"); 220 lines.add("mail: {uid}@maildomain.net"); 221 lines.add("userPassword: password"); 222 lines.add("telephoneNumber: <random:telephone>"); 223 lines.add("homePhone: <random:telephone>"); 224 lines.add("pager: <random:telephone>"); 225 lines.add("mobile: <random:telephone>"); 226 lines.add("street: <random:numeric:5> <file:streets> Street"); 227 lines.add("l: <file:cities>"); 228 lines.add("st: <file:states>"); 229 lines.add("postalCode: <random:numeric:5>"); 230 lines.add("postalAddress: {cn}${street}${l}, {st} {postalCode}"); 231 lines.add("description: This is the description for {cn}."); 232 } 233 234 BufferedWriter writer = new BufferedWriter(new FileWriter(templateFile)); 235 for (String line : lines) 236 { 237 writer.write(line); 238 writer.newLine(); 239 } 240 241 writer.flush(); 242 writer.close(); 243 244 return templateFile; 245 } 246 247 /** 248 * Returns {@code true} if the provided port is free and we can use it, 249 * {@code false} otherwise. 250 * @param hostname the host name we are analyzing. Use <CODE>null</CODE> 251 * to connect to any address. 252 * @param port the port we are analyzing. 253 * @return {@code true} if the provided port is free and we can use it, 254 * {@code false} otherwise. 255 */ 256 public static boolean canUseAsPort(String hostname, int port) 257 { 258 boolean canUseAsPort = false; 259 ServerSocket serverSocket = null; 260 try 261 { 262 InetSocketAddress socketAddress; 263 if (hostname != null) 264 { 265 socketAddress = new InetSocketAddress(hostname, port); 266 } 267 else 268 { 269 socketAddress = new InetSocketAddress(port); 270 } 271 serverSocket = new ServerSocket(); 272 if (!OperatingSystem.isWindows()) 273 { 274 serverSocket.setReuseAddress(true); 275 } 276 serverSocket.bind(socketAddress); 277 canUseAsPort = true; 278 279 serverSocket.close(); 280 281 /* Try to create a socket because sometimes even if we can create a server 282 * socket there is already someone listening to the port (is the case 283 * of products as Sun DS 6.0). 284 */ 285 Socket s = null; 286 try 287 { 288 s = new Socket(); 289 s.connect(socketAddress, 1000); 290 canUseAsPort = false; 291 } catch (Throwable t) 292 { 293 } 294 finally 295 { 296 if (s != null) 297 { 298 try 299 { 300 s.close(); 301 } 302 catch (Throwable t) 303 { 304 } 305 } 306 } 307 } catch (IOException ex) 308 { 309 canUseAsPort = false; 310 } finally 311 { 312 try 313 { 314 if (serverSocket != null) 315 { 316 serverSocket.close(); 317 } 318 } catch (Exception ex) 319 { 320 } 321 } 322 323 return canUseAsPort; 324 } 325 326 /** 327 * Returns {@code true} if the provided port is free and we can use it, 328 * {@code false} otherwise. 329 * @param port the port we are analyzing. 330 * @return {@code true} if the provided port is free and we can use it, 331 * {@code false} otherwise. 332 */ 333 public static boolean canUseAsPort(int port) 334 { 335 return canUseAsPort(null, port); 336 } 337 338 /** 339 * Returns {@code true} if the provided port is a privileged port, 340 * {@code false} otherwise. 341 * @param port the port we are analyzing. 342 * @return {@code true} if the provided port is a privileged port, 343 * {@code false} otherwise. 344 */ 345 public static boolean isPrivilegedPort(int port) 346 { 347 return port <= 1024 && !OperatingSystem.isWindows(); 348 } 349 350 /** 351 * Returns the String that can be used to launch an script using Runtime.exec. 352 * This method is required because in Windows the script that contain a "=" 353 * in their path must be quoted. 354 * @param script the script name 355 * @return the absolute path for the given parentPath and relativePath. 356 */ 357 public static String getScriptPath(String script) 358 { 359 String s = script; 360 if (OperatingSystem.isWindows() 361 && s != null && (!s.startsWith("\"") || !s.endsWith("\""))) 362 { 363 return "\"" + script + "\""; 364 } 365 return s; 366 } 367 368 /** 369 * Returns a randomly generated password for a self-signed certificate 370 * keystore. 371 * @return a randomly generated password for a self-signed certificate 372 * keystore. 373 */ 374 public static char[] createSelfSignedCertificatePwd() { 375 int pwdLength = 50; 376 char[] pwd = new char[pwdLength]; 377 Random random = new Random(); 378 for (int pos=0; pos < pwdLength; pos++) { 379 int type = getRandomInt(random,3); 380 char nextChar = getRandomChar(random,type); 381 pwd[pos] = nextChar; 382 } 383 return pwd; 384 } 385 386 /** 387 * Export a certificate in a file. If the certificate alias to export is null, 388 * It will export the first certificate defined. 389 * 390 * @param certManager 391 * Certificate manager to use. 392 * @param alias 393 * Certificate alias to export. If {@code null} the first certificate 394 * defined will be exported. 395 * @param path 396 * Path of the output file. 397 * @throws CertificateEncodingException 398 * If the certificate manager cannot encode the certificate. 399 * @throws IOException 400 * If a problem occurs while creating or writing in the output file. 401 * @throws KeyStoreException 402 * If the certificate manager cannot retrieve the certificate to be 403 * exported. 404 */ 405 public static void exportCertificate(CertificateManager certManager, String alias, String path) 406 throws CertificateEncodingException, IOException, KeyStoreException 407 { 408 final Certificate certificate = 409 certManager.getCertificate(alias != null ? alias : certManager.getCertificateAliases()[0]); 410 byte[] certificateBytes = certificate.getEncoded(); 411 412 FileOutputStream outputStream = new FileOutputStream(path, false); 413 try 414 { 415 outputStream.write(certificateBytes); 416 } 417 finally 418 { 419 closeSilently(outputStream); 420 } 421 } 422 423 424 /** 425 * The next two methods are used to generate the random password for the 426 * self-signed certificate. 427 */ 428 private static char getRandomChar(Random random, int type) 429 { 430 char generatedChar; 431 int next = random.nextInt(); 432 int d; 433 434 switch (type) 435 { 436 case 0: 437 // Will return a digit 438 d = next % 10; 439 if (d < 0) 440 { 441 d = d * -1; 442 } 443 generatedChar = (char) (d+48); 444 break; 445 case 1: 446 // Will return a lower case letter 447 d = next % 26; 448 if (d < 0) 449 { 450 d = d * -1; 451 } 452 generatedChar = (char) (d + 97); 453 break; 454 default: 455 // Will return a capital letter 456 d = next % 26; 457 if (d < 0) 458 { 459 d = d * -1; 460 } 461 generatedChar = (char) (d + 65) ; 462 } 463 464 return generatedChar; 465 } 466 467 private static int getRandomInt(Random random,int modulo) 468 { 469 return random.nextInt() & modulo; 470 } 471 472 /** 473 * Returns the host name to be used to create self-signed certificates. <br> 474 * The method will first try to read the host name file written by the setup 475 * where the user provided the host name where OpenDJ has been installed. If 476 * the file cannot be read, the class {@link java.net.InetAddress} is used. 477 * 478 * @param installationRoot the path where the server is installed. 479 * @return the host name to be used to create self-signed certificates. 480 * @throws UnknownHostException 481 * if a host name could not be used. 482 */ 483 public static String getHostNameForCertificate( 484 String installationRoot) throws UnknownHostException 485 { 486 String hostName = null; 487 File f = new File(installationRoot + File.separator + HOST_NAME_FILE); 488 BufferedReader br = null; 489 try 490 { 491 br = new BufferedReader(new FileReader(f)); 492 String s = br.readLine(); 493 s = s.trim(); 494 495 if (s.length() > 0) 496 { 497 hostName = s; 498 lastReadHostName = hostName; 499 } 500 } 501 catch (IOException ioe) 502 { 503 } 504 finally 505 { 506 closeSilently(br); 507 } 508 if (hostName == null) 509 { 510 hostName = lastReadHostName; 511 } 512 if (hostName == null) 513 { 514 hostName = java.net.InetAddress.getLocalHost().getHostName(); 515 } 516 return hostName; 517 } 518}