001
014
015 package com.liferay.portal.license.util;
016
017 import com.liferay.portal.kernel.cluster.ClusterExecutorUtil;
018 import com.liferay.portal.kernel.cluster.ClusterNode;
019 import com.liferay.portal.kernel.cluster.ClusterNodeResponse;
020 import com.liferay.portal.kernel.cluster.ClusterNodeResponses;
021 import com.liferay.portal.kernel.cluster.ClusterRequest;
022 import com.liferay.portal.kernel.cluster.FutureClusterResponses;
023 import com.liferay.portal.kernel.json.JSONFactoryUtil;
024 import com.liferay.portal.kernel.json.JSONObject;
025 import com.liferay.portal.kernel.log.Log;
026 import com.liferay.portal.kernel.log.LogFactoryUtil;
027 import com.liferay.portal.kernel.util.Base64;
028 import com.liferay.portal.kernel.util.CharPool;
029 import com.liferay.portal.kernel.util.Constants;
030 import com.liferay.portal.kernel.util.FileUtil;
031 import com.liferay.portal.kernel.util.GetterUtil;
032 import com.liferay.portal.kernel.util.Http;
033 import com.liferay.portal.kernel.util.MethodHandler;
034 import com.liferay.portal.kernel.util.MethodKey;
035 import com.liferay.portal.kernel.util.ParamUtil;
036 import com.liferay.portal.kernel.util.ReleaseInfo;
037 import com.liferay.portal.kernel.util.StringPool;
038 import com.liferay.portal.kernel.util.StringUtil;
039 import com.liferay.portal.kernel.util.Validator;
040 import com.liferay.portal.util.ClassLoaderUtil;
041 import com.liferay.portal.util.PortalUtil;
042 import com.liferay.portal.util.PropsUtil;
043 import com.liferay.portal.util.PropsValues;
044 import com.liferay.util.Encryptor;
045
046 import java.io.File;
047 import java.io.InputStream;
048 import java.io.OutputStream;
049
050 import java.net.Inet4Address;
051 import java.net.InetAddress;
052 import java.net.InetSocketAddress;
053 import java.net.NetworkInterface;
054 import java.net.Proxy;
055 import java.net.URL;
056 import java.net.URLConnection;
057
058 import java.security.Key;
059 import java.security.KeyFactory;
060 import java.security.PublicKey;
061 import java.security.SecureRandom;
062 import java.security.spec.X509EncodedKeySpec;
063
064 import java.util.Arrays;
065 import java.util.Collections;
066 import java.util.HashMap;
067 import java.util.HashSet;
068 import java.util.Iterator;
069 import java.util.List;
070 import java.util.Map;
071 import java.util.Set;
072 import java.util.TreeMap;
073 import java.util.concurrent.TimeUnit;
074 import java.util.regex.Matcher;
075 import java.util.regex.Pattern;
076
077 import javax.crypto.KeyGenerator;
078
079 import javax.servlet.http.HttpServletRequest;
080
081 import org.apache.commons.io.IOUtils;
082
083
086 public class LicenseUtil {
087
088 public static final String LICENSE_REPOSITORY_DIR =
089 PropsValues.LIFERAY_HOME.concat("/data/license");
090
091 public static final String LICENSE_SERVER_URL = GetterUtil.get(
092 PropsUtil.get("license.server.url"), "https:
093
094 public static Map<String, String> getClusterServerInfo(String clusterNodeId)
095 throws Exception {
096
097 List<ClusterNode> clusterNodes = ClusterExecutorUtil.getClusterNodes();
098
099 ClusterNode clusterNode = null;
100
101 for (ClusterNode curClusterNode : clusterNodes) {
102 String curClusterNodeId = curClusterNode.getClusterNodeId();
103
104 if (curClusterNodeId.equals(clusterNodeId)) {
105 clusterNode = curClusterNode;
106
107 break;
108 }
109 }
110
111 if (clusterNode == null) {
112 return null;
113 }
114
115 try {
116 if (clusterNode.equals(ClusterExecutorUtil.getLocalClusterNode())) {
117 return getServerInfo();
118 }
119
120 ClusterRequest clusterRequest = ClusterRequest.createUnicastRequest(
121 _getServerInfoMethodHandler, clusterNodeId);
122
123 FutureClusterResponses futureClusterResponses =
124 ClusterExecutorUtil.execute(clusterRequest);
125
126 ClusterNodeResponses clusterNodeResponses =
127 futureClusterResponses.get(20000, TimeUnit.MILLISECONDS);
128
129 ClusterNodeResponse clusterNodeResponse =
130 clusterNodeResponses.getClusterResponse(clusterNode);
131
132 return (Map<String, String>)clusterNodeResponse.getResult();
133 }
134 catch (Exception e) {
135 _log.error(e, e);
136
137 throw e;
138 }
139 }
140
141
144 public static String getHostName() {
145 return PortalUtil.getComputerName();
146 }
147
148 public static Set<String> getIpAddresses() {
149 if (_ipAddresses != null) {
150 return new HashSet<String>(_ipAddresses);
151 }
152
153 _ipAddresses = new HashSet<String>();
154
155 try {
156 List<NetworkInterface> networkInterfaces = Collections.list(
157 NetworkInterface.getNetworkInterfaces());
158
159 for (NetworkInterface networkInterface : networkInterfaces) {
160 List<InetAddress> inetAddresses = Collections.list(
161 networkInterface.getInetAddresses());
162
163 for (InetAddress inetAddress : inetAddresses) {
164 if (inetAddress.isLinkLocalAddress() ||
165 inetAddress.isLoopbackAddress() ||
166 !(inetAddress instanceof Inet4Address)) {
167
168 continue;
169 }
170
171 _ipAddresses.add(inetAddress.getHostAddress());
172 }
173 }
174 }
175 catch (Exception e) {
176 _log.error("Unable to read local server's IP addresses");
177
178 _log.error(e, e);
179 }
180
181 return new HashSet<String>(_ipAddresses);
182 }
183
184 public static Set<String> getMacAddresses() {
185 if (_macAddresses != null) {
186 return new HashSet<String>(_macAddresses);
187 }
188
189 Set<String> macAddresses = new HashSet<String>();
190
191 String osName = System.getProperty("os.name");
192
193 String executable = null;
194 String arguments = null;
195
196 if (StringUtil.startsWith(osName, "win")) {
197 executable = "ipconfig";
198 arguments = "/all";
199 }
200 else {
201 if (StringUtil.startsWith(osName, "aix")) {
202 executable = "netstat";
203 arguments = "-ina";
204 }
205 else {
206 executable = "ifconfig";
207 arguments = "-a";
208 }
209
210 File sbinDir = new File("/sbin", executable);
211
212 if (sbinDir.exists()) {
213 executable = "/sbin/".concat(executable);
214 }
215 }
216
217 try {
218 Runtime runtime = Runtime.getRuntime();
219
220 Process process = runtime.exec(
221 new String[] {executable, arguments});
222
223 macAddresses = getMacAddresses(osName, process.getInputStream());
224 }
225 catch (Exception e) {
226 _log.error(e, e);
227 }
228
229 _macAddresses = macAddresses;
230
231 return new HashSet<String>(macAddresses);
232 }
233
234 public static Set<String> getMacAddresses(
235 String osName, InputStream processInputStream)
236 throws Exception {
237
238 Set<String> macAddresses = new HashSet<String>();
239
240 Pattern macAddressPattern = _macAddressPattern1;
241
242 if (StringUtil.startsWith(osName, "aix")) {
243 macAddressPattern = _macAddressPattern2;
244 }
245
246 String processOutput = StringUtil.read(processInputStream);
247
248 String[] lines = StringUtil.split(processOutput, CharPool.NEW_LINE);
249
250 for (String line : lines) {
251 Matcher matcher = macAddressPattern.matcher(line);
252
253 if (!matcher.find()) {
254 continue;
255 }
256
257 String macAddress = matcher.group(1);
258
259 macAddress = macAddress.toLowerCase();
260 macAddress = macAddress.replace(CharPool.DASH, CharPool.COLON);
261 macAddress = macAddress.replace(CharPool.PERIOD, CharPool.COLON);
262
263 StringBuilder sb = new StringBuilder(17);
264
265 sb.append(macAddress);
266
267 for (int i = 1; i < 5; ++i) {
268 int pos = (i * 3) - 1;
269
270 if (sb.charAt(pos) != CharPool.COLON) {
271 sb.insert((i - 1) * 3, CharPool.NUMBER_0);
272 }
273 }
274
275 if (sb.length() < 17) {
276 sb.insert(15, CharPool.NUMBER_0);
277 }
278
279 macAddress = sb.toString();
280
281 macAddresses.add(macAddress);
282 }
283
284 return macAddresses;
285 }
286
287 public static byte[] getServerIdBytes() throws Exception {
288 if (_serverIdBytes != null) {
289 return _serverIdBytes;
290 }
291
292 File serverIdFile = new File(
293 LICENSE_REPOSITORY_DIR + "/server/serverId");
294
295 if (!serverIdFile.exists()) {
296 return new byte[0];
297 }
298
299 _serverIdBytes = FileUtil.getBytes(serverIdFile);
300
301 return _serverIdBytes;
302 }
303
304 public static Map<String, String> getServerInfo() {
305 Map<String, String> serverInfo = new HashMap<String, String>();
306
307 serverInfo.put("hostName", PortalUtil.getComputerName());
308 serverInfo.put("ipAddresses", StringUtil.merge(getIpAddresses()));
309 serverInfo.put("macAddresses", StringUtil.merge(getMacAddresses()));
310
311 return serverInfo;
312 }
313
314 public static void registerOrder(HttpServletRequest request) {
315 String orderUuid = ParamUtil.getString(request, "orderUuid");
316 String productEntryName = ParamUtil.getString(
317 request, "productEntryName");
318 int maxServers = ParamUtil.getInteger(request, "maxServers");
319
320 List<ClusterNode> clusterNodes = ClusterExecutorUtil.getClusterNodes();
321
322 if ((clusterNodes.size() <= 1) || Validator.isNull(productEntryName) ||
323 Validator.isNull(orderUuid)) {
324
325 Map<String, Object> attributes = registerOrder(
326 orderUuid, productEntryName, maxServers);
327
328 for (Map.Entry<String, Object> entry : attributes.entrySet()) {
329 request.setAttribute(entry.getKey(), entry.getValue());
330 }
331 }
332 else {
333 for (ClusterNode clusterNode : clusterNodes) {
334 boolean register = ParamUtil.getBoolean(
335 request, clusterNode.getClusterNodeId() + "_register");
336
337 if (!register) {
338 continue;
339 }
340
341 try {
342 _registerClusterOrder(
343 request, clusterNode, orderUuid, productEntryName,
344 maxServers);
345 }
346 catch (Exception e) {
347 _log.error(e, e);
348
349 InetAddress inetAddress = clusterNode.getInetAddress();
350
351 String message =
352 "Error contacting " + inetAddress.getHostName();
353
354 if (clusterNode.getPort() != -1) {
355 message += StringPool.COLON + clusterNode.getPort();
356 }
357
358 request.setAttribute(
359 clusterNode.getClusterNodeId() + "_ERROR_MESSAGE",
360 message);
361 }
362 }
363 }
364 }
365
366 public static Map<String, Object> registerOrder(
367 String orderUuid, String productEntryName, int maxServers) {
368
369 Map<String, Object> attributes = new HashMap<String, Object>();
370
371 if (Validator.isNull(orderUuid)) {
372 return attributes;
373 }
374
375 try {
376 JSONObject jsonObject = _createRequest(
377 orderUuid, productEntryName, maxServers);
378
379 String response = sendRequest(jsonObject.toString());
380
381 JSONObject responseJSONObject = JSONFactoryUtil.createJSONObject(
382 response);
383
384 attributes.put(
385 "ORDER_PRODUCT_ID", responseJSONObject.getString("productId"));
386 attributes.put(
387 "ORDER_PRODUCTS", _getOrderProducts(responseJSONObject));
388
389 String errorMessage = responseJSONObject.getString("errorMessage");
390
391 if (Validator.isNotNull(errorMessage)) {
392 attributes.put("ERROR_MESSAGE", errorMessage);
393
394 return attributes;
395 }
396
397 String licenseXML = responseJSONObject.getString("licenseXML");
398
399 if (Validator.isNotNull(licenseXML)) {
400 LicenseManagerUtil.registerLicense(responseJSONObject);
401
402 attributes.clear();
403 attributes.put(
404 "SUCCESS_MESSAGE",
405 "Your license has been successfully registered.");
406 }
407 }
408 catch (Exception e) {
409 _log.error(e, e);
410
411 attributes.put(
412 "ERROR_MESSAGE",
413 "There was an error contacting " + LICENSE_SERVER_URL);
414 }
415
416 return attributes;
417 }
418
419 public static String sendRequest(String request) throws Exception {
420 InputStream inputStream = null;
421 OutputStream outputStream = null;
422
423 try {
424 String serverURL = LICENSE_SERVER_URL;
425
426 if (!serverURL.endsWith(StringPool.SLASH)) {
427 serverURL += StringPool.SLASH;
428 }
429
430 serverURL += "osb-portlet/license";
431
432 URL url = new URL(serverURL);
433
434 URLConnection connection = null;
435
436 if (Validator.isNotNull(_PROXY_URL)) {
437 if (_log.isInfoEnabled()) {
438 _log.info(
439 "Using proxy " + _PROXY_URL + StringPool.COLON +
440 _PROXY_PORT);
441 }
442
443 Proxy proxy = new Proxy(
444 Proxy.Type.HTTP,
445 new InetSocketAddress(_PROXY_URL, _PROXY_PORT));
446
447 connection = url.openConnection(proxy);
448
449 if (Validator.isNotNull(_PROXY_USER_NAME)) {
450 String login =
451 _PROXY_USER_NAME + StringPool.COLON + _PROXY_PASSWORD;
452
453 String encodedLogin = Base64.encode(login.getBytes());
454
455 connection.setRequestProperty(
456 "Proxy-Authorization", "Basic " + encodedLogin);
457 }
458 }
459 else {
460 connection = url.openConnection();
461 }
462
463 connection.setDoOutput(true);
464
465 outputStream = connection.getOutputStream();
466
467 outputStream.write(_encryptRequest(serverURL, request));
468
469 String response = _decryptResponse(
470 serverURL, connection.getInputStream());
471
472 if (_log.isDebugEnabled()) {
473 _log.debug("Server response: " + response);
474 }
475
476 if (Validator.isNull(response)) {
477 throw new Exception("Server response is null");
478 }
479
480 return response;
481 }
482 finally {
483 try {
484 if (inputStream != null) {
485 inputStream.close();
486 }
487 }
488 catch (Exception e) {
489 }
490
491 try {
492 if (outputStream != null) {
493 outputStream.close();
494 }
495 }
496 catch (Exception e) {
497 }
498 }
499 }
500
501 public static void writeServerProperties(byte[] serverIdBytes)
502 throws Exception {
503
504 File serverIdFile = new File(
505 LICENSE_REPOSITORY_DIR + "/server/serverId");
506
507 FileUtil.write(serverIdFile, serverIdBytes);
508 }
509
510 private static JSONObject _createRequest(
511 String orderUuid, String productEntryName, int maxServers)
512 throws Exception {
513
514 JSONObject jsonObject = JSONFactoryUtil.createJSONObject();
515
516 jsonObject.put("version", 2);
517 jsonObject.put("orderUuid", orderUuid);
518 jsonObject.put("liferayVersion", ReleaseInfo.getBuildNumber());
519
520 if (Validator.isNull(productEntryName)) {
521 jsonObject.put(Constants.CMD, "QUERY");
522 }
523 else {
524 jsonObject.put(Constants.CMD, "REGISTER");
525
526 if (productEntryName.startsWith("basic")) {
527 jsonObject.put("productEntryName", "basic");
528
529 if (productEntryName.equals("basic-cluster")) {
530 jsonObject.put("cluster", true);
531 jsonObject.put("maxServers", maxServers);
532 }
533 else if (productEntryName.startsWith("basic-")) {
534 String[] productNameArray = StringUtil.split(
535 productEntryName, StringPool.DASH);
536
537 if (productNameArray.length >= 3) {
538 jsonObject.put("offeringEntryId", productNameArray[1]);
539 jsonObject.put("clusterId", productNameArray[2]);
540 }
541 }
542 }
543 else {
544 jsonObject.put("productEntryName", productEntryName);
545 }
546
547 jsonObject.put("hostName", PortalUtil.getComputerName());
548 jsonObject.put("ipAddresses", StringUtil.merge(getIpAddresses()));
549 jsonObject.put("macAddresses", StringUtil.merge(getMacAddresses()));
550 jsonObject.put("serverId", Arrays.toString(getServerIdBytes()));
551 }
552
553 return jsonObject;
554 }
555
556 private static String _decryptResponse(
557 String serverURL, InputStream inputStream)
558 throws Exception {
559
560 if (serverURL.startsWith(Http.HTTPS)) {
561 return StringUtil.read(inputStream);
562 }
563
564 byte[] bytes = IOUtils.toByteArray(inputStream);
565
566 if ((bytes == null) || (bytes.length <= 0)) {
567 return null;
568 }
569
570 bytes = Encryptor.decryptUnencodedAsBytes(_symmetricKey, bytes);
571
572 return new String(bytes, StringPool.UTF8);
573 }
574
575 private static byte[] _encryptRequest(String serverURL, String request)
576 throws Exception {
577
578 byte[] bytes = request.getBytes(StringPool.UTF8);
579
580 if (serverURL.startsWith(Http.HTTPS)) {
581 return bytes;
582 }
583
584 JSONObject jsonObject = JSONFactoryUtil.createJSONObject();
585
586 bytes = Encryptor.encryptUnencoded(_symmetricKey, bytes);
587
588 jsonObject.put("content", Base64.objectToString(bytes));
589 jsonObject.put("key", _encryptedSymmetricKey);
590
591 return jsonObject.toString().getBytes(StringPool.UTF8);
592 }
593
594 private static Map<String, String> _getOrderProducts(
595 JSONObject jsonObject) {
596
597 JSONObject productsJSONObject = jsonObject.getJSONObject(
598 "productsJSONObject");
599
600 if (productsJSONObject == null) {
601 return null;
602 }
603
604 Map<String, String> sortedMap = new TreeMap<String, String>(
605 String.CASE_INSENSITIVE_ORDER);
606
607 Iterator<String> itr = productsJSONObject.keys();
608
609 while (itr.hasNext()) {
610 String key = itr.next();
611
612 sortedMap.put(key, productsJSONObject.getString(key));
613 }
614
615 return sortedMap;
616 }
617
618 private static void _initKeys() {
619 ClassLoader classLoader = ClassLoaderUtil.getPortalClassLoader();
620
621 if ((classLoader == null) || (_encryptedSymmetricKey != null)) {
622 return;
623 }
624
625 try {
626 URL url = classLoader.getResource(
627 "com/liferay/portal/license/public.key");
628
629 byte[] bytes = IOUtils.toByteArray(url.openStream());
630
631 X509EncodedKeySpec x509EncodedKeySpec = new X509EncodedKeySpec(
632 bytes);
633
634 KeyFactory keyFactory = KeyFactory.getInstance("RSA");
635
636 PublicKey publicKey = keyFactory.generatePublic(x509EncodedKeySpec);
637
638 KeyGenerator keyGenerator = KeyGenerator.getInstance("AES");
639
640 keyGenerator.init(128, new SecureRandom());
641
642 _symmetricKey = keyGenerator.generateKey();
643
644 byte[] encryptedSymmetricKey = Encryptor.encryptUnencoded(
645 publicKey, _symmetricKey.getEncoded());
646
647 _encryptedSymmetricKey = Base64.objectToString(
648 encryptedSymmetricKey);
649 }
650 catch (Exception e) {
651 _log.error(e, e);
652 }
653 }
654
655 private static void _registerClusterOrder(
656 HttpServletRequest request, ClusterNode clusterNode,
657 String orderUuid, String productEntryName, int maxServers)
658 throws Exception {
659
660 MethodHandler methodHandler = new MethodHandler(
661 _registerOrderMethodKey, orderUuid, productEntryName, maxServers);
662
663 ClusterRequest clusterRequest = ClusterRequest.createUnicastRequest(
664 methodHandler, clusterNode.getClusterNodeId());
665
666 FutureClusterResponses futureClusterResponses =
667 ClusterExecutorUtil.execute(clusterRequest);
668
669 ClusterNodeResponses clusterNodeResponses = futureClusterResponses.get(
670 20000, TimeUnit.MILLISECONDS);
671
672 ClusterNodeResponse clusterNodeResponse =
673 clusterNodeResponses.getClusterResponse(clusterNode);
674
675 Map<String, Object> attributes =
676 (Map<String, Object>)clusterNodeResponse.getResult();
677
678 for (Map.Entry<String, Object> entry : attributes.entrySet()) {
679 request.setAttribute(
680 clusterNode.getClusterNodeId() + StringPool.UNDERLINE +
681 entry.getKey(),
682 entry.getValue());
683 }
684 }
685
686 private static final String _PROXY_PASSWORD = GetterUtil.getString(
687 PropsUtil.get("license.proxy.password"));
688
689 private static final int _PROXY_PORT = GetterUtil.getInteger(
690 PropsUtil.get("license.proxy.port"), 80);
691
692 private static final String _PROXY_URL = PropsUtil.get("license.proxy.url");
693
694 private static final String _PROXY_USER_NAME = GetterUtil.getString(
695 PropsUtil.get("license.proxy.username"));
696
697 private static Log _log = LogFactoryUtil.getLog(LicenseUtil.class);
698
699 private static String _encryptedSymmetricKey;
700 private static MethodHandler _getServerInfoMethodHandler =
701 new MethodHandler(new MethodKey(LicenseUtil.class, "getServerInfo"));
702 private static Set<String> _ipAddresses;
703 private static Set<String> _macAddresses;
704 private static Pattern _macAddressPattern1 = Pattern.compile(
705 "\\s((\\p{XDigit}{2}(-|:)){5}(\\p{XDigit}{2}))(?:\\s|$)");
706 private static Pattern _macAddressPattern2 = Pattern.compile(
707 "\\s((\\p{XDigit}{1,2}(\\.)){5}(\\p{XDigit}{1,2}))(?:\\s|$)");
708 private static MethodKey _registerOrderMethodKey = new MethodKey(
709 LicenseUtil.class, "registerOrder", String.class, String.class,
710 int.class);
711 private static byte[] _serverIdBytes;
712 private static Key _symmetricKey;
713
714 static {
715 _initKeys();
716 }
717
718 }