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