001    /**
002     * Copyright (c) 2000-2013 Liferay, Inc. All rights reserved.
003     *
004     * The contents of this file are subject to the terms of the Liferay Enterprise
005     * Subscription License ("License"). You may not use this file except in
006     * compliance with the License. You can obtain a copy of the License by
007     * contacting Liferay, Inc. See the License for the specific language governing
008     * permissions and limitations under the License, including but not limited to
009     * distribution rights of the Software.
010     *
011     *
012     *
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.ArrayUtil;
028    import com.liferay.portal.kernel.util.Base64;
029    import com.liferay.portal.kernel.util.CharPool;
030    import com.liferay.portal.kernel.util.Constants;
031    import com.liferay.portal.kernel.util.ContentTypes;
032    import com.liferay.portal.kernel.util.FileUtil;
033    import com.liferay.portal.kernel.util.GetterUtil;
034    import com.liferay.portal.kernel.util.Http;
035    import com.liferay.portal.kernel.util.MethodHandler;
036    import com.liferay.portal.kernel.util.MethodKey;
037    import com.liferay.portal.kernel.util.ParamUtil;
038    import com.liferay.portal.kernel.util.ReleaseInfo;
039    import com.liferay.portal.kernel.util.StringPool;
040    import com.liferay.portal.kernel.util.StringUtil;
041    import com.liferay.portal.kernel.util.Validator;
042    import com.liferay.portal.util.ClassLoaderUtil;
043    import com.liferay.portal.util.PortalUtil;
044    import com.liferay.portal.util.PropsUtil;
045    import com.liferay.portal.util.PropsValues;
046    import com.liferay.util.Encryptor;
047    
048    import java.io.File;
049    import java.io.InputStream;
050    
051    import java.net.Inet4Address;
052    import java.net.InetAddress;
053    import java.net.NetworkInterface;
054    import java.net.URI;
055    import java.net.URL;
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    
074    import javax.crypto.KeyGenerator;
075    
076    import javax.servlet.http.HttpServletRequest;
077    
078    import org.apache.commons.io.IOUtils;
079    import org.apache.http.HttpEntity;
080    import org.apache.http.HttpHost;
081    import org.apache.http.HttpResponse;
082    import org.apache.http.auth.AuthScope;
083    import org.apache.http.auth.UsernamePasswordCredentials;
084    import org.apache.http.client.CredentialsProvider;
085    import org.apache.http.client.methods.HttpPost;
086    import org.apache.http.conn.ClientConnectionManager;
087    import org.apache.http.conn.params.ConnRoutePNames;
088    import org.apache.http.entity.ByteArrayEntity;
089    import org.apache.http.impl.client.DefaultHttpClient;
090    import org.apache.http.params.HttpParams;
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            public static String getHostName() {
154                    return PortalUtil.getComputerName();
155            }
156    
157            public static Set<String> getIpAddresses() {
158                    return _ipAddresses;
159            }
160    
161            public static Set<String> getMacAddresses() {
162                    return _macAddresses;
163            }
164    
165            public static byte[] getServerIdBytes() throws Exception {
166                    if (_serverIdBytes != null) {
167                            return _serverIdBytes;
168                    }
169    
170                    File serverIdFile = new File(
171                            LICENSE_REPOSITORY_DIR + "/server/serverId");
172    
173                    if (!serverIdFile.exists()) {
174                            return new byte[0];
175                    }
176    
177                    _serverIdBytes = FileUtil.getBytes(serverIdFile);
178    
179                    return _serverIdBytes;
180            }
181    
182            public static Map<String, String> getServerInfo() {
183                    Map<String, String> serverInfo = new HashMap<String, String>();
184    
185                    serverInfo.put("hostName", PortalUtil.getComputerName());
186                    serverInfo.put("ipAddresses", StringUtil.merge(getIpAddresses()));
187                    serverInfo.put("macAddresses", StringUtil.merge(getMacAddresses()));
188    
189                    return serverInfo;
190            }
191    
192            public static void registerOrder(HttpServletRequest request) {
193                    String orderUuid = ParamUtil.getString(request, "orderUuid");
194                    String productEntryName = ParamUtil.getString(
195                            request, "productEntryName");
196                    int maxServers = ParamUtil.getInteger(request, "maxServers");
197    
198                    List<ClusterNode> clusterNodes = ClusterExecutorUtil.getClusterNodes();
199    
200                    if ((clusterNodes.size() <= 1) || Validator.isNull(productEntryName) ||
201                            Validator.isNull(orderUuid)) {
202    
203                            Map<String, Object> attributes = registerOrder(
204                                    orderUuid, productEntryName, maxServers);
205    
206                            for (Map.Entry<String, Object> entry : attributes.entrySet()) {
207                                    request.setAttribute(entry.getKey(), entry.getValue());
208                            }
209                    }
210                    else {
211                            for (ClusterNode clusterNode : clusterNodes) {
212                                    boolean register = ParamUtil.getBoolean(
213                                            request, clusterNode.getClusterNodeId() + "_register");
214    
215                                    if (!register) {
216                                            continue;
217                                    }
218    
219                                    try {
220                                            _registerClusterOrder(
221                                                    request, clusterNode, orderUuid, productEntryName,
222                                                    maxServers);
223                                    }
224                                    catch (Exception e) {
225                                            _log.error(e, e);
226    
227                                            InetAddress inetAddress = clusterNode.getInetAddress();
228    
229                                            String message =
230                                                    "Error contacting " + inetAddress.getHostName();
231    
232                                            if (clusterNode.getPort() != -1) {
233                                                    message += StringPool.COLON + clusterNode.getPort();
234                                            }
235    
236                                            request.setAttribute(
237                                                    clusterNode.getClusterNodeId() + "_ERROR_MESSAGE",
238                                                    message);
239                                    }
240                            }
241                    }
242            }
243    
244            public static Map<String, Object> registerOrder(
245                    String orderUuid, String productEntryName, int maxServers) {
246    
247                    Map<String, Object> attributes = new HashMap<String, Object>();
248    
249                    if (Validator.isNull(orderUuid)) {
250                            return attributes;
251                    }
252    
253                    try {
254                            JSONObject jsonObject = _createRequest(
255                                    orderUuid, productEntryName, maxServers);
256    
257                            String response = sendRequest(jsonObject.toString());
258    
259                            JSONObject responseJSONObject = JSONFactoryUtil.createJSONObject(
260                                    response);
261    
262                            attributes.put(
263                                    "ORDER_PRODUCT_ID", responseJSONObject.getString("productId"));
264                            attributes.put(
265                                    "ORDER_PRODUCTS", _getOrderProducts(responseJSONObject));
266    
267                            String errorMessage = responseJSONObject.getString("errorMessage");
268    
269                            if (Validator.isNotNull(errorMessage)) {
270                                    attributes.put("ERROR_MESSAGE", errorMessage);
271    
272                                    return attributes;
273                            }
274    
275                            String licenseXML = responseJSONObject.getString("licenseXML");
276    
277                            if (Validator.isNotNull(licenseXML)) {
278                                    LicenseManagerUtil.registerLicense(responseJSONObject);
279    
280                                    attributes.clear();
281                                    attributes.put(
282                                            "SUCCESS_MESSAGE",
283                                            "Your license has been successfully registered.");
284                            }
285                    }
286                    catch (Exception e) {
287                            _log.error(e, e);
288    
289                            attributes.put(
290                                    "ERROR_MESSAGE",
291                                    "There was an error contacting " + LICENSE_SERVER_URL);
292                    }
293    
294                    return attributes;
295            }
296    
297            public static String sendRequest(String request) throws Exception {
298                    DefaultHttpClient defaultHttpClient = new DefaultHttpClient();
299    
300                    try {
301                            String serverURL = LICENSE_SERVER_URL;
302    
303                            if (!serverURL.endsWith(StringPool.SLASH)) {
304                                    serverURL += StringPool.SLASH;
305                            }
306    
307                            serverURL += "osb-portlet/license";
308    
309                            URI uri = new URI(serverURL);
310    
311                            HttpPost httpPost = new HttpPost(uri);
312    
313                            if (Validator.isNotNull(_PROXY_URL)) {
314                                    if (_log.isInfoEnabled()) {
315                                            _log.info(
316                                                    "Using proxy " + _PROXY_URL + StringPool.COLON +
317                                                            _PROXY_PORT);
318                                    }
319    
320                                    HttpHost httpHost = new HttpHost(_PROXY_URL, _PROXY_PORT);
321    
322                                    HttpParams httpParams = defaultHttpClient.getParams();
323    
324                                    httpParams.setParameter(
325                                            ConnRoutePNames.DEFAULT_PROXY, httpHost);
326    
327                                    if (Validator.isNotNull(_PROXY_USER_NAME)) {
328                                            CredentialsProvider credentialsProvider =
329                                                    defaultHttpClient.getCredentialsProvider();
330    
331                                            credentialsProvider.setCredentials(
332                                                    new AuthScope(_PROXY_URL, _PROXY_PORT),
333                                                    new UsernamePasswordCredentials(
334                                                            _PROXY_USER_NAME, _PROXY_PASSWORD));
335                                    }
336                            }
337    
338                            ByteArrayEntity byteArrayEntity = new ByteArrayEntity(
339                                    _encryptRequest(serverURL, request));
340    
341                            byteArrayEntity.setContentType(ContentTypes.APPLICATION_JSON);
342    
343                            httpPost.setEntity(byteArrayEntity);
344    
345                            HttpResponse httpResponse = defaultHttpClient.execute(httpPost);
346    
347                            HttpEntity httpEntity = httpResponse.getEntity();
348    
349                            String response = _decryptResponse(
350                                    serverURL, httpEntity.getContent());
351    
352                            if (_log.isDebugEnabled()) {
353                                    _log.debug("Server response: " + response);
354                            }
355    
356                            if (Validator.isNull(response)) {
357                                    throw new Exception("Server response is null");
358                            }
359    
360                            return response;
361                    }
362                    finally {
363                            ClientConnectionManager clientConnectionManager =
364                                    defaultHttpClient.getConnectionManager();
365    
366                            clientConnectionManager.shutdown();
367                    }
368            }
369    
370            public static void writeServerProperties(byte[] serverIdBytes)
371                    throws Exception {
372    
373                    File serverIdFile = new File(
374                            LICENSE_REPOSITORY_DIR + "/server/serverId");
375    
376                    FileUtil.write(serverIdFile, serverIdBytes);
377            }
378    
379            private static JSONObject _createRequest(
380                            String orderUuid, String productEntryName, int maxServers)
381                    throws Exception {
382    
383                    JSONObject jsonObject = JSONFactoryUtil.createJSONObject();
384    
385                    jsonObject.put("version", 2);
386                    jsonObject.put("orderUuid", orderUuid);
387                    jsonObject.put("liferayVersion", ReleaseInfo.getBuildNumber());
388    
389                    if (Validator.isNull(productEntryName)) {
390                            jsonObject.put(Constants.CMD, "QUERY");
391                    }
392                    else {
393                            jsonObject.put(Constants.CMD, "REGISTER");
394    
395                            if (productEntryName.startsWith("basic")) {
396                                    jsonObject.put("productEntryName", "basic");
397    
398                                    if (productEntryName.equals("basic-cluster")) {
399                                            jsonObject.put("cluster", true);
400                                            jsonObject.put("maxServers", maxServers);
401                                    }
402                                    else if (productEntryName.startsWith("basic-")) {
403                                            String[] productNameArray = StringUtil.split(
404                                                    productEntryName, StringPool.DASH);
405    
406                                            if (productNameArray.length >= 3) {
407                                                    jsonObject.put("offeringEntryId", productNameArray[1]);
408                                                    jsonObject.put("clusterId", productNameArray[2]);
409                                            }
410                                    }
411                            }
412                            else {
413                                    jsonObject.put("productEntryName", productEntryName);
414                            }
415    
416                            jsonObject.put("hostName", PortalUtil.getComputerName());
417                            jsonObject.put("ipAddresses", StringUtil.merge(getIpAddresses()));
418                            jsonObject.put("macAddresses", StringUtil.merge(getMacAddresses()));
419                            jsonObject.put("serverId", Arrays.toString(getServerIdBytes()));
420                    }
421    
422                    return jsonObject;
423            }
424    
425            private static String _decryptResponse(
426                            String serverURL, InputStream inputStream)
427                    throws Exception {
428    
429                    if (serverURL.startsWith(Http.HTTPS)) {
430                            return StringUtil.read(inputStream);
431                    }
432    
433                    byte[] bytes = IOUtils.toByteArray(inputStream);
434    
435                    if ((bytes == null) || (bytes.length <= 0)) {
436                            return null;
437                    }
438    
439                    bytes = Encryptor.decryptUnencodedAsBytes(_symmetricKey, bytes);
440    
441                    return new String(bytes, StringPool.UTF8);
442            }
443    
444            private static byte[] _encryptRequest(String serverURL, String request)
445                    throws Exception {
446    
447                    byte[] bytes = request.getBytes(StringPool.UTF8);
448    
449                    if (serverURL.startsWith(Http.HTTPS)) {
450                            return bytes;
451                    }
452    
453                    JSONObject jsonObject = JSONFactoryUtil.createJSONObject();
454    
455                    bytes = Encryptor.encryptUnencoded(_symmetricKey, bytes);
456    
457                    jsonObject.put("content", Base64.objectToString(bytes));
458                    jsonObject.put("key", _encryptedSymmetricKey);
459    
460                    return jsonObject.toString().getBytes(StringPool.UTF8);
461            }
462    
463            private static Set<String> _getIPAddresses() {
464                    Set<String> ipAddresses = new HashSet<String>();
465    
466                    try {
467                            List<NetworkInterface> networkInterfaces = Collections.list(
468                                    NetworkInterface.getNetworkInterfaces());
469    
470                            for (NetworkInterface networkInterface : networkInterfaces) {
471                                    List<InetAddress> inetAddresses = Collections.list(
472                                            networkInterface.getInetAddresses());
473    
474                                    for (InetAddress inetAddress : inetAddresses) {
475                                            if (inetAddress.isLinkLocalAddress() ||
476                                                    inetAddress.isLoopbackAddress() ||
477                                                    !(inetAddress instanceof Inet4Address)) {
478    
479                                                    continue;
480                                            }
481    
482                                            ipAddresses.add(inetAddress.getHostAddress());
483                                    }
484                            }
485                    }
486                    catch (Exception e) {
487                            _log.error("Unable to read local server's IP addresses", e);
488                    }
489    
490                    return ipAddresses;
491            }
492    
493            private static Set<String> _getMACAddresses() {
494                    Set<String> macAddresses = new HashSet<String>();
495    
496                    try {
497                            List<NetworkInterface> networkInterfaces = Collections.list(
498                                    NetworkInterface.getNetworkInterfaces());
499    
500                            for (NetworkInterface networkInterface : networkInterfaces) {
501                                    byte[] hardwareAddress = networkInterface.getHardwareAddress();
502    
503                                    if (ArrayUtil.isEmpty(hardwareAddress)) {
504                                            continue;
505                                    }
506    
507                                    StringBuilder sb = new StringBuilder(
508                                            (hardwareAddress.length * 3) - 1);
509    
510                                    String hexString = StringUtil.bytesToHexString(hardwareAddress);
511    
512                                    for (int i = 0; i < hexString.length(); i += 2) {
513                                            if (i != 0) {
514                                                    sb.append(CharPool.COLON);
515                                            }
516    
517                                            sb.append(Character.toLowerCase(hexString.charAt(i)));
518                                            sb.append(Character.toLowerCase(hexString.charAt(i + 1)));
519                                    }
520    
521                                    macAddresses.add(sb.toString());
522                            }
523                    }
524                    catch (Exception e) {
525                            _log.error("Unable to read local server's MAC addresses", e);
526                    }
527    
528                    return macAddresses;
529            }
530    
531            private static Map<String, String> _getOrderProducts(
532                    JSONObject jsonObject) {
533    
534                    JSONObject productsJSONObject = jsonObject.getJSONObject(
535                            "productsJSONObject");
536    
537                    if (productsJSONObject == null) {
538                            return null;
539                    }
540    
541                    Map<String, String> sortedMap = new TreeMap<String, String>(
542                            String.CASE_INSENSITIVE_ORDER);
543    
544                    Iterator<String> itr = productsJSONObject.keys();
545    
546                    while (itr.hasNext()) {
547                            String key = itr.next();
548    
549                            sortedMap.put(key, productsJSONObject.getString(key));
550                    }
551    
552                    return sortedMap;
553            }
554    
555            private static void _initKeys() {
556                    ClassLoader classLoader = ClassLoaderUtil.getPortalClassLoader();
557    
558                    if ((classLoader == null) || (_encryptedSymmetricKey != null)) {
559                            return;
560                    }
561    
562                    try {
563                            URL url = classLoader.getResource(
564                                    "com/liferay/portal/license/public.key");
565    
566                            byte[] bytes = IOUtils.toByteArray(url.openStream());
567    
568                            X509EncodedKeySpec x509EncodedKeySpec = new X509EncodedKeySpec(
569                                    bytes);
570    
571                            KeyFactory keyFactory = KeyFactory.getInstance("RSA");
572    
573                            PublicKey publicKey = keyFactory.generatePublic(x509EncodedKeySpec);
574    
575                            KeyGenerator keyGenerator = KeyGenerator.getInstance("AES");
576    
577                            keyGenerator.init(128, new SecureRandom());
578    
579                            _symmetricKey = keyGenerator.generateKey();
580    
581                            byte[] encryptedSymmetricKey = Encryptor.encryptUnencoded(
582                                    publicKey, _symmetricKey.getEncoded());
583    
584                            _encryptedSymmetricKey = Base64.objectToString(
585                                    encryptedSymmetricKey);
586                    }
587                    catch (Exception e) {
588                            _log.error(e, e);
589                    }
590            }
591    
592            private static void _registerClusterOrder(
593                            HttpServletRequest request, ClusterNode clusterNode,
594                            String orderUuid, String productEntryName, int maxServers)
595                    throws Exception {
596    
597                    MethodHandler methodHandler = new MethodHandler(
598                            _registerOrderMethodKey, orderUuid, productEntryName, maxServers);
599    
600                    ClusterRequest clusterRequest = ClusterRequest.createUnicastRequest(
601                            methodHandler, clusterNode.getClusterNodeId());
602    
603                    FutureClusterResponses futureClusterResponses =
604                            ClusterExecutorUtil.execute(clusterRequest);
605    
606                    ClusterNodeResponses clusterNodeResponses = futureClusterResponses.get(
607                            20000, TimeUnit.MILLISECONDS);
608    
609                    ClusterNodeResponse clusterNodeResponse =
610                            clusterNodeResponses.getClusterResponse(clusterNode);
611    
612                    Map<String, Object> attributes =
613                            (Map<String, Object>)clusterNodeResponse.getResult();
614    
615                    for (Map.Entry<String, Object> entry : attributes.entrySet()) {
616                            request.setAttribute(
617                                    clusterNode.getClusterNodeId() + StringPool.UNDERLINE +
618                                            entry.getKey(),
619                                    entry.getValue());
620                    }
621            }
622    
623            private static final String _PROXY_PASSWORD = GetterUtil.getString(
624                    PropsUtil.get("license.proxy.password"));
625    
626            private static final int _PROXY_PORT = GetterUtil.getInteger(
627                    PropsUtil.get("license.proxy.port"), 80);
628    
629            private static final String _PROXY_URL = PropsUtil.get("license.proxy.url");
630    
631            private static final String _PROXY_USER_NAME = GetterUtil.getString(
632                    PropsUtil.get("license.proxy.username"));
633    
634            private static Log _log = LogFactoryUtil.getLog(LicenseUtil.class);
635    
636            private static String _encryptedSymmetricKey;
637            private static MethodHandler _getServerInfoMethodHandler =
638                    new MethodHandler(new MethodKey(LicenseUtil.class, "getServerInfo"));
639            private static final Set<String> _ipAddresses;
640            private static final Set<String> _macAddresses;
641            private static MethodKey _registerOrderMethodKey = new MethodKey(
642                    LicenseUtil.class, "registerOrder", String.class, String.class,
643                    int.class);
644            private static byte[] _serverIdBytes;
645            private static Key _symmetricKey;
646    
647            static {
648                    _initKeys();
649    
650                    _ipAddresses = _getIPAddresses();
651                    _macAddresses = _getMACAddresses();
652            }
653    
654    }