001    /**
002     * Copyright (c) 2000-present Liferay, Inc. All rights reserved.
003     *
004     * This library is free software; you can redistribute it and/or modify it under
005     * the terms of the GNU Lesser General Public License as published by the Free
006     * Software Foundation; either version 2.1 of the License, or (at your option)
007     * any later version.
008     *
009     * This library is distributed in the hope that it will be useful, but WITHOUT
010     * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
011     * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
012     * details.
013     */
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    
073    import javax.crypto.KeyGenerator;
074    
075    import javax.servlet.http.HttpServletRequest;
076    
077    import org.apache.commons.io.IOUtils;
078    import org.apache.http.HttpEntity;
079    import org.apache.http.HttpHost;
080    import org.apache.http.HttpResponse;
081    import org.apache.http.auth.AuthScope;
082    import org.apache.http.auth.UsernamePasswordCredentials;
083    import org.apache.http.client.CredentialsProvider;
084    import org.apache.http.client.HttpClient;
085    import org.apache.http.client.methods.HttpPost;
086    import org.apache.http.conn.HttpClientConnectionManager;
087    import org.apache.http.entity.ByteArrayEntity;
088    import org.apache.http.impl.client.BasicCredentialsProvider;
089    import org.apache.http.impl.client.HttpClientBuilder;
090    import org.apache.http.impl.conn.BasicHttpClientConnectionManager;
091    
092    /**
093     * @author Amos Fong
094     */
095    public class LicenseUtil {
096    
097            public static final String LICENSE_REPOSITORY_DIR =
098                    PropsValues.LIFERAY_HOME.concat("/data/license");
099    
100            public static final String LICENSE_SERVER_URL = GetterUtil.get(
101                    PropsUtil.get("license.server.url"), "https://www.liferay.com");
102    
103            public static Map<String, String> getClusterServerInfo(String clusterNodeId)
104                    throws Exception {
105    
106                    List<ClusterNode> clusterNodes = ClusterExecutorUtil.getClusterNodes();
107    
108                    ClusterNode clusterNode = null;
109    
110                    for (ClusterNode curClusterNode : clusterNodes) {
111                            String curClusterNodeId = curClusterNode.getClusterNodeId();
112    
113                            if (curClusterNodeId.equals(clusterNodeId)) {
114                                    clusterNode = curClusterNode;
115    
116                                    break;
117                            }
118                    }
119    
120                    if (clusterNode == null) {
121                            return null;
122                    }
123    
124                    try {
125                            if (clusterNode.equals(ClusterExecutorUtil.getLocalClusterNode())) {
126                                    return getServerInfo();
127                            }
128    
129                            ClusterRequest clusterRequest = ClusterRequest.createUnicastRequest(
130                                    _getServerInfoMethodHandler, clusterNodeId);
131    
132                            FutureClusterResponses futureClusterResponses =
133                                    ClusterExecutorUtil.execute(clusterRequest);
134    
135                            ClusterNodeResponses clusterNodeResponses =
136                                    futureClusterResponses.get(20000, TimeUnit.MILLISECONDS);
137    
138                            ClusterNodeResponse clusterNodeResponse =
139                                    clusterNodeResponses.getClusterResponse(clusterNode);
140    
141                            return (Map<String, String>)clusterNodeResponse.getResult();
142                    }
143                    catch (Exception e) {
144                            _log.error(e, e);
145    
146                            throw e;
147                    }
148            }
149    
150            /**
151             * @deprecated As of 6.2.0, replaced by {@link PortalUtil#getComputerName()}
152             */
153            @Deprecated
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                    _macAddresses = new HashSet<String>();
200    
201                    try {
202                            List<NetworkInterface> networkInterfaces = Collections.list(
203                                    NetworkInterface.getNetworkInterfaces());
204    
205                            for (NetworkInterface networkInterface : networkInterfaces) {
206                                    byte[] hardwareAddress = networkInterface.getHardwareAddress();
207    
208                                    if (hardwareAddress == null) {
209                                            continue;
210                                    }
211    
212                                    StringBuilder sb = new StringBuilder(
213                                            (hardwareAddress.length * 3) - 1);
214    
215                                    String hexString = StringUtil.bytesToHexString(hardwareAddress);
216    
217                                    for (int i = 0; i < hexString.length(); i += 2) {
218                                            if (i != 0) {
219                                                    sb.append(CharPool.COLON);
220                                            }
221    
222                                            sb.append(Character.toLowerCase(hexString.charAt(i)));
223                                            sb.append(Character.toLowerCase(hexString.charAt(i + 1)));
224                                    }
225    
226                                    _macAddresses.add(sb.toString());
227                            }
228                    }
229                    catch (Exception e) {
230                            _log.error("Unable to read local server's MAC addresses");
231    
232                            _log.error(e, e);
233                    }
234    
235                    return new HashSet<String>(_macAddresses);
236            }
237    
238            public static byte[] getServerIdBytes() throws Exception {
239                    if (_serverIdBytes != null) {
240                            return _serverIdBytes;
241                    }
242    
243                    File serverIdFile = new File(
244                            LICENSE_REPOSITORY_DIR + "/server/serverId");
245    
246                    if (!serverIdFile.exists()) {
247                            return new byte[0];
248                    }
249    
250                    _serverIdBytes = FileUtil.getBytes(serverIdFile);
251    
252                    return _serverIdBytes;
253            }
254    
255            public static Map<String, String> getServerInfo() {
256                    Map<String, String> serverInfo = new HashMap<String, String>();
257    
258                    serverInfo.put("hostName", PortalUtil.getComputerName());
259                    serverInfo.put("ipAddresses", StringUtil.merge(getIpAddresses()));
260                    serverInfo.put("macAddresses", StringUtil.merge(getMacAddresses()));
261    
262                    return serverInfo;
263            }
264    
265            public static void registerOrder(HttpServletRequest request) {
266                    String orderUuid = ParamUtil.getString(request, "orderUuid");
267                    String productEntryName = ParamUtil.getString(
268                            request, "productEntryName");
269                    int maxServers = ParamUtil.getInteger(request, "maxServers");
270    
271                    List<ClusterNode> clusterNodes = ClusterExecutorUtil.getClusterNodes();
272    
273                    if ((clusterNodes.size() <= 1) || Validator.isNull(productEntryName) ||
274                            Validator.isNull(orderUuid)) {
275    
276                            Map<String, Object> attributes = registerOrder(
277                                    orderUuid, productEntryName, maxServers);
278    
279                            for (Map.Entry<String, Object> entry : attributes.entrySet()) {
280                                    request.setAttribute(entry.getKey(), entry.getValue());
281                            }
282                    }
283                    else {
284                            for (ClusterNode clusterNode : clusterNodes) {
285                                    boolean register = ParamUtil.getBoolean(
286                                            request, clusterNode.getClusterNodeId() + "_register");
287    
288                                    if (!register) {
289                                            continue;
290                                    }
291    
292                                    try {
293                                            _registerClusterOrder(
294                                                    request, clusterNode, orderUuid, productEntryName,
295                                                    maxServers);
296                                    }
297                                    catch (Exception e) {
298                                            _log.error(e, e);
299    
300                                            InetAddress inetAddress = clusterNode.getBindInetAddress();
301    
302                                            String message =
303                                                    "Error contacting " + inetAddress.getHostName();
304    
305                                            if (clusterNode.getPortalPort() != -1) {
306                                                    message +=
307                                                            StringPool.COLON + clusterNode.getPortalPort();
308                                            }
309    
310                                            request.setAttribute(
311                                                    clusterNode.getClusterNodeId() + "_ERROR_MESSAGE",
312                                                    message);
313                                    }
314                            }
315                    }
316            }
317    
318            public static Map<String, Object> registerOrder(
319                    String orderUuid, String productEntryName, int maxServers) {
320    
321                    Map<String, Object> attributes = new HashMap<String, Object>();
322    
323                    if (Validator.isNull(orderUuid)) {
324                            return attributes;
325                    }
326    
327                    try {
328                            JSONObject jsonObject = _createRequest(
329                                    orderUuid, productEntryName, maxServers);
330    
331                            String response = sendRequest(jsonObject.toString());
332    
333                            JSONObject responseJSONObject = JSONFactoryUtil.createJSONObject(
334                                    response);
335    
336                            attributes.put(
337                                    "ORDER_PRODUCT_ID", responseJSONObject.getString("productId"));
338                            attributes.put(
339                                    "ORDER_PRODUCTS", _getOrderProducts(responseJSONObject));
340    
341                            String errorMessage = responseJSONObject.getString("errorMessage");
342    
343                            if (Validator.isNotNull(errorMessage)) {
344                                    attributes.put("ERROR_MESSAGE", errorMessage);
345    
346                                    return attributes;
347                            }
348    
349                            String licenseXML = responseJSONObject.getString("licenseXML");
350    
351                            if (Validator.isNotNull(licenseXML)) {
352                                    LicenseManagerUtil.registerLicense(responseJSONObject);
353    
354                                    attributes.clear();
355                                    attributes.put(
356                                            "SUCCESS_MESSAGE",
357                                            "Your license has been successfully registered.");
358                            }
359                    }
360                    catch (Exception e) {
361                            _log.error(e, e);
362    
363                            attributes.put(
364                                    "ERROR_MESSAGE",
365                                    "There was an error contacting " + LICENSE_SERVER_URL);
366                    }
367    
368                    return attributes;
369            }
370    
371            public static String sendRequest(String request) throws Exception {
372                    HttpClient httpClient = null;
373    
374                    HttpClientConnectionManager httpClientConnectionManager =
375                            new BasicHttpClientConnectionManager();
376    
377                    try {
378                            HttpClientBuilder httpClientBuilder = HttpClientBuilder.create();
379    
380                            httpClientBuilder.setConnectionManager(httpClientConnectionManager);
381    
382                            String serverURL = LICENSE_SERVER_URL;
383    
384                            if (!serverURL.endsWith(StringPool.SLASH)) {
385                                    serverURL += StringPool.SLASH;
386                            }
387    
388                            serverURL += "osb-portlet/license";
389    
390                            URI uri = new URI(serverURL);
391    
392                            HttpPost httpPost = new HttpPost(uri);
393    
394                            CredentialsProvider credentialsProvider =
395                                    new BasicCredentialsProvider();
396    
397                            HttpHost proxyHttpHost = null;
398    
399                            if (Validator.isNotNull(_PROXY_URL)) {
400                                    if (_log.isInfoEnabled()) {
401                                            _log.info(
402                                                    "Using proxy " + _PROXY_URL + StringPool.COLON +
403                                                            _PROXY_PORT);
404                                    }
405    
406                                    proxyHttpHost = new HttpHost(_PROXY_URL, _PROXY_PORT);
407    
408                                    if (Validator.isNotNull(_PROXY_USER_NAME)) {
409                                            credentialsProvider.setCredentials(
410                                                    new AuthScope(_PROXY_URL, _PROXY_PORT),
411                                                    new UsernamePasswordCredentials(
412                                                            _PROXY_USER_NAME, _PROXY_PASSWORD));
413                                    }
414                            }
415    
416                            httpClientBuilder.setDefaultCredentialsProvider(
417                                    credentialsProvider);
418                            httpClientBuilder.setProxy(proxyHttpHost);
419    
420                            httpClient = httpClientBuilder.build();
421    
422                            ByteArrayEntity byteArrayEntity = new ByteArrayEntity(
423                                    _encryptRequest(serverURL, request));
424    
425                            byteArrayEntity.setContentType(ContentTypes.APPLICATION_JSON);
426    
427                            httpPost.setEntity(byteArrayEntity);
428    
429                            HttpResponse httpResponse = httpClient.execute(httpPost);
430    
431                            HttpEntity httpEntity = httpResponse.getEntity();
432    
433                            String response = _decryptResponse(
434                                    serverURL, httpEntity.getContent());
435    
436                            if (_log.isDebugEnabled()) {
437                                    _log.debug("Server response: " + response);
438                            }
439    
440                            if (Validator.isNull(response)) {
441                                    throw new Exception("Server response is null");
442                            }
443    
444                            return response;
445                    }
446                    finally {
447                            if (httpClient != null) {
448                                    httpClientConnectionManager.shutdown();
449                            }
450                    }
451            }
452    
453            public static void writeServerProperties(byte[] serverIdBytes)
454                    throws Exception {
455    
456                    File serverIdFile = new File(
457                            LICENSE_REPOSITORY_DIR + "/server/serverId");
458    
459                    FileUtil.write(serverIdFile, serverIdBytes);
460            }
461    
462            private static JSONObject _createRequest(
463                            String orderUuid, String productEntryName, int maxServers)
464                    throws Exception {
465    
466                    JSONObject jsonObject = JSONFactoryUtil.createJSONObject();
467    
468                    jsonObject.put("version", 2);
469                    jsonObject.put("orderUuid", orderUuid);
470                    jsonObject.put("liferayVersion", ReleaseInfo.getBuildNumber());
471    
472                    if (Validator.isNull(productEntryName)) {
473                            jsonObject.put(Constants.CMD, "QUERY");
474                    }
475                    else {
476                            jsonObject.put(Constants.CMD, "REGISTER");
477    
478                            if (productEntryName.startsWith("basic")) {
479                                    jsonObject.put("productEntryName", "basic");
480    
481                                    if (productEntryName.equals("basic-cluster")) {
482                                            jsonObject.put("cluster", true);
483                                            jsonObject.put("maxServers", maxServers);
484                                    }
485                                    else if (productEntryName.startsWith("basic-")) {
486                                            String[] productNameArray = StringUtil.split(
487                                                    productEntryName, StringPool.DASH);
488    
489                                            if (productNameArray.length >= 3) {
490                                                    jsonObject.put("offeringEntryId", productNameArray[1]);
491                                                    jsonObject.put("clusterId", productNameArray[2]);
492                                            }
493                                    }
494                            }
495                            else {
496                                    jsonObject.put("productEntryName", productEntryName);
497                            }
498    
499                            jsonObject.put("hostName", PortalUtil.getComputerName());
500                            jsonObject.put("ipAddresses", StringUtil.merge(getIpAddresses()));
501                            jsonObject.put("macAddresses", StringUtil.merge(getMacAddresses()));
502                            jsonObject.put("serverId", Arrays.toString(getServerIdBytes()));
503                    }
504    
505                    return jsonObject;
506            }
507    
508            private static String _decryptResponse(
509                            String serverURL, InputStream inputStream)
510                    throws Exception {
511    
512                    if (serverURL.startsWith(Http.HTTPS)) {
513                            return StringUtil.read(inputStream);
514                    }
515    
516                    byte[] bytes = IOUtils.toByteArray(inputStream);
517    
518                    if ((bytes == null) || (bytes.length <= 0)) {
519                            return null;
520                    }
521    
522                    bytes = Encryptor.decryptUnencodedAsBytes(_symmetricKey, bytes);
523    
524                    return new String(bytes, StringPool.UTF8);
525            }
526    
527            private static byte[] _encryptRequest(String serverURL, String request)
528                    throws Exception {
529    
530                    byte[] bytes = request.getBytes(StringPool.UTF8);
531    
532                    if (serverURL.startsWith(Http.HTTPS)) {
533                            return bytes;
534                    }
535    
536                    JSONObject jsonObject = JSONFactoryUtil.createJSONObject();
537    
538                    bytes = Encryptor.encryptUnencoded(_symmetricKey, bytes);
539    
540                    jsonObject.put("content", Base64.objectToString(bytes));
541                    jsonObject.put("key", _encryptedSymmetricKey);
542    
543                    return jsonObject.toString().getBytes(StringPool.UTF8);
544            }
545    
546            private static Map<String, String> _getOrderProducts(
547                    JSONObject jsonObject) {
548    
549                    JSONObject productsJSONObject = jsonObject.getJSONObject(
550                            "productsJSONObject");
551    
552                    if (productsJSONObject == null) {
553                            return null;
554                    }
555    
556                    Map<String, String> sortedMap = new TreeMap<String, String>(
557                            String.CASE_INSENSITIVE_ORDER);
558    
559                    Iterator<String> itr = productsJSONObject.keys();
560    
561                    while (itr.hasNext()) {
562                            String key = itr.next();
563    
564                            sortedMap.put(key, productsJSONObject.getString(key));
565                    }
566    
567                    return sortedMap;
568            }
569    
570            private static void _initKeys() {
571                    ClassLoader classLoader = ClassLoaderUtil.getPortalClassLoader();
572    
573                    if ((classLoader == null) || (_encryptedSymmetricKey != null)) {
574                            return;
575                    }
576    
577                    try {
578                            URL url = classLoader.getResource(
579                                    "com/liferay/portal/license/public.key");
580    
581                            byte[] bytes = IOUtils.toByteArray(url.openStream());
582    
583                            X509EncodedKeySpec x509EncodedKeySpec = new X509EncodedKeySpec(
584                                    bytes);
585    
586                            KeyFactory keyFactory = KeyFactory.getInstance("RSA");
587    
588                            PublicKey publicKey = keyFactory.generatePublic(x509EncodedKeySpec);
589    
590                            KeyGenerator keyGenerator = KeyGenerator.getInstance("AES");
591    
592                            keyGenerator.init(128, new SecureRandom());
593    
594                            _symmetricKey = keyGenerator.generateKey();
595    
596                            byte[] encryptedSymmetricKey = Encryptor.encryptUnencoded(
597                                    publicKey, _symmetricKey.getEncoded());
598    
599                            _encryptedSymmetricKey = Base64.objectToString(
600                                    encryptedSymmetricKey);
601                    }
602                    catch (Exception e) {
603                            _log.error(e, e);
604                    }
605            }
606    
607            private static void _registerClusterOrder(
608                            HttpServletRequest request, ClusterNode clusterNode,
609                            String orderUuid, String productEntryName, int maxServers)
610                    throws Exception {
611    
612                    MethodHandler methodHandler = new MethodHandler(
613                            _registerOrderMethodKey, orderUuid, productEntryName, maxServers);
614    
615                    ClusterRequest clusterRequest = ClusterRequest.createUnicastRequest(
616                            methodHandler, clusterNode.getClusterNodeId());
617    
618                    FutureClusterResponses futureClusterResponses =
619                            ClusterExecutorUtil.execute(clusterRequest);
620    
621                    ClusterNodeResponses clusterNodeResponses = futureClusterResponses.get(
622                            20000, TimeUnit.MILLISECONDS);
623    
624                    ClusterNodeResponse clusterNodeResponse =
625                            clusterNodeResponses.getClusterResponse(clusterNode);
626    
627                    Map<String, Object> attributes =
628                            (Map<String, Object>)clusterNodeResponse.getResult();
629    
630                    for (Map.Entry<String, Object> entry : attributes.entrySet()) {
631                            request.setAttribute(
632                                    clusterNode.getClusterNodeId() + StringPool.UNDERLINE +
633                                            entry.getKey(),
634                                    entry.getValue());
635                    }
636            }
637    
638            private static final String _PROXY_PASSWORD = GetterUtil.getString(
639                    PropsUtil.get("license.proxy.password"));
640    
641            private static final int _PROXY_PORT = GetterUtil.getInteger(
642                    PropsUtil.get("license.proxy.port"), 80);
643    
644            private static final String _PROXY_URL = PropsUtil.get("license.proxy.url");
645    
646            private static final String _PROXY_USER_NAME = GetterUtil.getString(
647                    PropsUtil.get("license.proxy.username"));
648    
649            private static Log _log = LogFactoryUtil.getLog(LicenseUtil.class);
650    
651            private static String _encryptedSymmetricKey;
652            private static MethodHandler _getServerInfoMethodHandler =
653                    new MethodHandler(new MethodKey(LicenseUtil.class, "getServerInfo"));
654            private static Set<String> _ipAddresses;
655            private static Set<String> _macAddresses;
656            private static MethodKey _registerOrderMethodKey = new MethodKey(
657                    LicenseUtil.class, "registerOrder", String.class, String.class,
658                    int.class);
659            private static byte[] _serverIdBytes;
660            private static Key _symmetricKey;
661    
662            static {
663                    _initKeys();
664            }
665    
666    }