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