All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.elasticsearch.plugins.PluginSecurity Maven / Gradle / Ivy

There is a newer version: 8.15.1
Show newest version
/*
 * Licensed to Elasticsearch under one or more contributor
 * license agreements. See the NOTICE file distributed with
 * this work for additional information regarding copyright
 * ownership. Elasticsearch licenses this file to you under
 * the Apache License, Version 2.0 (the "License"); you may
 * not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */

package org.elasticsearch.plugins;

import org.elasticsearch.common.cli.Terminal;
import org.elasticsearch.common.cli.Terminal.Verbosity;
import org.elasticsearch.env.Environment;

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.security.NoSuchAlgorithmException;
import java.security.Permission;
import java.security.PermissionCollection;
import java.security.Permissions;
import java.security.Policy;
import java.security.URIParameter;
import java.security.UnresolvedPermission;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;

class PluginSecurity {
    
    /**
     * Reads plugin policy, prints/confirms exceptions
     */
    static void readPolicy(Path file, Terminal terminal, Environment environment, boolean batch) throws IOException {
        PermissionCollection permissions = parsePermissions(terminal, file, environment.tmpFile());
        List requested = Collections.list(permissions.elements());
        if (requested.isEmpty()) {
            terminal.print(Verbosity.VERBOSE, "plugin has a policy file with no additional permissions");
            return;
        }
        
        // sort permissions in a reasonable order
        Collections.sort(requested, new Comparator() {
            @Override
            public int compare(Permission o1, Permission o2) {
                int cmp = o1.getClass().getName().compareTo(o2.getClass().getName());
                if (cmp == 0) {
                    String name1 = o1.getName();
                    String name2 = o2.getName();
                    if (name1 == null) {
                        name1 = "";
                    }
                    if (name2 == null) {
                        name2 = "";
                    }
                    cmp = name1.compareTo(name2);
                    if (cmp == 0) {
                        String actions1 = o1.getActions();
                        String actions2 = o2.getActions();
                        if (actions1 == null) {
                            actions1 = "";
                        }
                        if (actions2 == null) {
                            actions2 = "";
                        }
                        cmp = actions1.compareTo(actions2);
                    }
                }
                return cmp;
            }
        });
        
        terminal.println(Verbosity.NORMAL, "@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@");
        terminal.println(Verbosity.NORMAL, "@     WARNING: plugin requires additional permissions     @");
        terminal.println(Verbosity.NORMAL, "@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@");
        // print all permissions:
        for (Permission permission : requested) {
            terminal.println(Verbosity.NORMAL, "* %s", formatPermission(permission));
        }
        terminal.println(Verbosity.NORMAL, "See http://docs.oracle.com/javase/8/docs/technotes/guides/security/permissions.html");
        terminal.println(Verbosity.NORMAL, "for descriptions of what these permissions allow and the associated risks.");
        if (!batch) {
            terminal.println(Verbosity.NORMAL);
            String text = terminal.readText("Continue with installation? [y/N]");
            if (!text.equalsIgnoreCase("y")) {
                throw new RuntimeException("installation aborted by user");
            }
        }
    }
    
    /** Format permission type, name, and actions into a string */
    static String formatPermission(Permission permission) {
        StringBuilder sb = new StringBuilder();
        
        String clazz = null;
        if (permission instanceof UnresolvedPermission) {
            clazz = ((UnresolvedPermission) permission).getUnresolvedType();
        } else {
            clazz = permission.getClass().getName();
        }
        sb.append(clazz);
        
        String name = null;
        if (permission instanceof UnresolvedPermission) {
            name = ((UnresolvedPermission) permission).getUnresolvedName();
        } else {
            name = permission.getName();
        }
        if (name != null && name.length() > 0) {
            sb.append(' ');
            sb.append(name);
        }
        
        String actions = null;
        if (permission instanceof UnresolvedPermission) {
            actions = ((UnresolvedPermission) permission).getUnresolvedActions();
        } else {
            actions = permission.getActions();
        }
        if (actions != null && actions.length() > 0) {
            sb.append(' ');
            sb.append(actions);
        }
        return sb.toString();
    }
    
    /**
     * Parses plugin policy into a set of permissions
     */
    static PermissionCollection parsePermissions(Terminal terminal, Path file, Path tmpDir) throws IOException {
        // create a zero byte file for "comparison"
        // this is necessary because the default policy impl automatically grants two permissions:
        // 1. permission to exitVM (which we ignore)
        // 2. read permission to the code itself (e.g. jar file of the code)

        Path emptyPolicyFile = Files.createTempFile(tmpDir, "empty", "tmp");
        final Policy emptyPolicy;
        try {
            emptyPolicy = Policy.getInstance("JavaPolicy", new URIParameter(emptyPolicyFile.toUri()));
        } catch (NoSuchAlgorithmException e) {
            throw new RuntimeException(e);
        }
        PluginManager.tryToDeletePath(terminal, emptyPolicyFile);
        
        // parse the plugin's policy file into a set of permissions
        final Policy policy;
        try {
            policy = Policy.getInstance("JavaPolicy", new URIParameter(file.toUri()));
        } catch (NoSuchAlgorithmException e) {
            throw new RuntimeException(e);
        }
        PermissionCollection permissions = policy.getPermissions(PluginSecurity.class.getProtectionDomain());
        // this method is supported with the specific implementation we use, but just check for safety.
        if (permissions == Policy.UNSUPPORTED_EMPTY_COLLECTION) {
            throw new UnsupportedOperationException("JavaPolicy implementation does not support retrieving permissions");
        }
        PermissionCollection actualPermissions = new Permissions();
        for (Permission permission : Collections.list(permissions.elements())) {
            if (!emptyPolicy.implies(PluginSecurity.class.getProtectionDomain(), permission)) {
                actualPermissions.add(permission);
            }
        }
        actualPermissions.setReadOnly();
        return actualPermissions;
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy