001    /**
002     * Copyright (c) 2000-2012 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.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    /**
083     * @author Amos Fong
084     */
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://www.liferay.com");
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    }