001    /**
002     * Copyright (c) 2000-2012 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.security.pacl.checker;
016    
017    import com.liferay.portal.kernel.log.Log;
018    import com.liferay.portal.kernel.log.LogFactoryUtil;
019    import com.liferay.portal.kernel.util.GetterUtil;
020    import com.liferay.portal.kernel.util.StringPool;
021    import com.liferay.portal.kernel.util.Validator;
022    
023    import java.net.InetAddress;
024    import java.net.UnknownHostException;
025    
026    import java.security.Permission;
027    
028    import java.util.HashMap;
029    import java.util.HashSet;
030    import java.util.Map;
031    import java.util.Set;
032    
033    /**
034     * @author Brian Wing Shun Chan
035     */
036    public class SocketChecker extends BaseChecker {
037    
038            public void afterPropertiesSet() {
039                    initAcceptHostsAndPorts();
040                    initConnectHostsAndPorts();
041                    initListenPorts();
042            }
043    
044            public void checkPermission(Permission permission) {
045                    String actions = permission.getActions();
046    
047                    String name = permission.getName();
048    
049                    int pos = name.indexOf(StringPool.COLON);
050    
051                    String host = "localhost";
052    
053                    if (pos != -1) {
054                            host = name.substring(0, pos);
055                    }
056    
057                    int port = GetterUtil.getInteger(name.substring(pos + 1));
058    
059                    // resolve
060    
061                    if (port == -1) {
062                            if (_log.isDebugEnabled()) {
063                                    _log.debug("Always allow resolving of host " + host);
064                            }
065    
066                            return;
067                    }
068    
069                    if (actions.contains(SOCKET_PERMISSION_ACCEPT)) {
070                            if (!hasAccept(host, port)) {
071                                    throwSecurityException(
072                                            _log,
073                                            "Attempted to accept from host " + host + " on port " +
074                                                    port);
075                            }
076                    }
077                    else if (actions.contains(SOCKET_PERMISSION_CONNECT)) {
078                            if (!hasConnect(host, port)) {
079                                    throwSecurityException(
080                                            _log,
081                                            "Attempted to connect to host " + host + " on port " +
082                                                    port);
083                            }
084                    }
085                    else if (actions.contains(SOCKET_PERMISSION_LISTEN)) {
086                            if (!hasListen(port)) {
087                                    throwSecurityException(
088                                            _log, "Attempted to listen on port " + port);
089                            }
090                    }
091            }
092    
093            protected boolean hasAccept(String host, int port) {
094                    Set<Integer> ports = _acceptHostsAndPorts.get(host);
095    
096                    if (ports == null) {
097                            return false;
098                    }
099    
100                    return ports.contains(port);
101            }
102    
103            protected boolean hasConnect(String host, int port) {
104                    Set<Integer> ports = _connectHostsAndPorts.get(host);
105    
106                    if (ports == null) {
107                            return false;
108                    }
109    
110                    return ports.contains(port);
111            }
112    
113            protected boolean hasListen(int port) {
114                    return _listenPorts.contains(port);
115            }
116    
117            protected void initAcceptHostsAndPorts() {
118                    String[] networkParts = getPropertyArray(
119                            "security-manager-sockets-accept");
120    
121                    for (String networkPart : networkParts) {
122                            initHostsAndPorts(networkPart, true);
123                    }
124            }
125    
126            protected void initConnectHostsAndPorts() {
127                    String[] networkParts = getPropertyArray(
128                            "security-manager-sockets-connect");
129    
130                    for (String networkPart : networkParts) {
131                            initHostsAndPorts(networkPart, false);
132                    }
133            }
134    
135            protected void initHostsAndPorts(String networkPart, boolean accept) {
136                    String action = "accept";
137    
138                    if (!accept) {
139                            action = "connect";
140                    }
141    
142                    int pos = networkPart.indexOf(StringPool.COLON);
143    
144                    if (pos == -1) {
145                            if (_log.isWarnEnabled()) {
146                                    _log.warn(
147                                            "Unable to determine socket " + action +
148                                                    " host and port from " + networkPart +
149                                                            " because it is missing a colon delimeter");
150                            }
151    
152                            return;
153                    }
154    
155                    String host = networkPart.substring(0, pos);
156    
157                    if (!Validator.isDomain(host)) {
158                            if (_log.isWarnEnabled()) {
159                                    _log.warn(
160                                            "Socket " + action + " host " + host +
161                                                    " is not a valid domain");
162                            }
163    
164                            return;
165                    }
166    
167                    String portString = networkPart.substring(pos + 1);
168    
169                    int port = GetterUtil.getInteger(portString);
170    
171                    if (port <= 0) {
172                            if (_log.isWarnEnabled()) {
173                                    _log.warn(
174                                            "Socket " + action + " port " + portString +
175                                                    " is less than or equal to 0");
176                            }
177    
178                            return;
179                    }
180    
181                    InetAddress[] inetAddresses = null;
182    
183                    try {
184                            inetAddresses = InetAddress.getAllByName(host);
185                    }
186                    catch (UnknownHostException uhe) {
187                            if (_log.isWarnEnabled()) {
188                                    _log.warn("Unable to resolve host " + host);
189                            }
190    
191                            return;
192                    }
193    
194                    Map<String, Set<Integer>> hostsAndPorts = _acceptHostsAndPorts;
195    
196                    if (!accept) {
197                            hostsAndPorts = _connectHostsAndPorts;
198                    }
199    
200                    for (InetAddress inetAddress : inetAddresses) {
201                            Set<Integer> ports = hostsAndPorts.get(
202                                    inetAddress.getHostAddress());
203    
204                            if (ports == null) {
205                                    ports = new HashSet<Integer>();
206    
207                                    if (_log.isDebugEnabled()) {
208                                            _log.debug(
209                                                    "Allowing socket " + action + " host " + host +
210                                                            " with IP " + inetAddress.getHostAddress() +
211                                                                    " on port " + port);
212                                    }
213    
214                                    hostsAndPorts.put(inetAddress.getHostAddress(), ports);
215                            }
216    
217                            ports.add(port);
218                    }
219            }
220    
221            protected void initListenPorts() {
222                    String[] listenParts = getPropertyArray(
223                            "security-manager-sockets-listen");
224    
225                    for (String listenPart : listenParts) {
226                            initListenPorts(listenPart);
227                    }
228            }
229    
230            protected void initListenPorts(String listenPart) {
231                    int pos = listenPart.indexOf(StringPool.DASH);
232    
233                    if (pos == -1) {
234                            if (!Validator.isNumber(listenPart)) {
235                                    if (_log.isWarnEnabled()) {
236                                            _log.warn(
237                                                    "Socket listen port " + listenPart +
238                                                            " is not a number");
239                                    }
240    
241                                    return;
242                            }
243    
244                            int port = GetterUtil.getInteger(listenPart);
245    
246                            if (_log.isDebugEnabled()) {
247                                    _log.debug("Allowing socket listen port " + port);
248                            }
249    
250                            _listenPorts.add(port);
251                    }
252                    else {
253                            String portString1 = listenPart.substring(0, pos);
254                            String portString2 = listenPart.substring(pos + 1);
255    
256                            if (!Validator.isNumber(portString1)) {
257                                    if (_log.isWarnEnabled()) {
258                                            _log.warn(
259                                                    "Socket listen port " + portString1 +
260                                                            " is not a number");
261                                    }
262    
263                                    return;
264                            }
265    
266                            if (!Validator.isNumber(portString2)) {
267                                    if (_log.isWarnEnabled()) {
268                                            _log.warn(
269                                                    "Socket listen port " + portString2 +
270                                                            " is not a number");
271                                    }
272    
273                                    return;
274                            }
275    
276                            int port1 = GetterUtil.getInteger(portString1);
277                            int port2 = GetterUtil.getInteger(portString2);
278    
279                            if (port1 >= port2) {
280                                    if (_log.isWarnEnabled()) {
281                                            _log.warn(
282                                                    "Socket listen port range " + listenPart +
283                                                            " is invalid");
284                                    }
285    
286                                    return;
287                            }
288    
289                            for (int i = port1; i <= port2; i++) {
290                                    if (_log.isDebugEnabled()) {
291                                            _log.debug("Allowing socket listen port " + i);
292                                    }
293    
294                                    _listenPorts.add(i);
295                            }
296                    }
297            }
298    
299            private static Log _log = LogFactoryUtil.getLog(SocketChecker.class);
300    
301            private Map<String, Set<Integer>> _acceptHostsAndPorts =
302                    new HashMap<String, Set<Integer>>();
303            private Map<String, Set<Integer>> _connectHostsAndPorts =
304                    new HashMap<String, Set<Integer>>();
305            private Set<Integer> _listenPorts = new HashSet<Integer>();
306    
307    }