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