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