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