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