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