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