/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF 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.apache.felix.framework; import java.io.File; import java.io.IOException; import java.util.StringTokenizer; import org.apache.felix.framework.ext.SecurityProvider; import org.apache.felix.framework.security.SecurityConstants; import org.apache.felix.framework.security.condpermadmin.ConditionalPermissionAdminImpl; import org.apache.felix.framework.security.permissionadmin.PermissionAdminImpl; import org.apache.felix.framework.security.util.Conditions; import org.apache.felix.framework.security.util.LocalPermissions; import org.apache.felix.framework.security.util.Permissions; import org.apache.felix.framework.security.util.PropertiesCache; import org.apache.felix.framework.util.SecureAction; import org.osgi.framework.BundleActivator; import org.osgi.framework.BundleContext; import org.osgi.framework.BundleException; import org.osgi.framework.Constants; import org.osgi.service.condpermadmin.ConditionalPermissionAdmin; import org.osgi.service.permissionadmin.PermissionAdmin; /** * <p> * This Felix specific activator installs a security provider with the Felix * framework. The security settings can be changed via the * {@link PermissionAdmin} and/or the {@link ConditionalPermissionAdmin} * services that may be published by this class. * </p> * <p> * Permission informations as well as caching data will be stored in several * files in a directory called <tt>security</tt> obtained by a call to * {@link BundleContext#getDataFile(String))}. * </p> * <p> * The following properties are recognized: * <p> * {@link SecurityConstants#ENABLE_PERMISSIONADMIN_PROP} - Whether or not ( * <tt>true</tt>|<tt>false</tt>) to publish a{@link ConditionalPermissionAdmin} * service. The default is * {@link SecurityConstants#ENABLE_PERMISSIONADMIN_VALUE}. * </p> * <p> * {@link SecurityConstants#ENABLE_CONDPERMADMIN_PROP} - Whether or not ( * <tt>true</tt>|<tt>false</tt>) to publish a{@link ConditionalPermissionAdmin} * service. The default is {@link SecurityConstants#ENABLE_CONDPERMADMIN_VALUE}. * </p> * <p> * {@link SecurityConstants#KEYSTORE_FILE_PROP} - The keystore URL(s) to use as * trusted CA stores. The urls must be separated by a guard (i.e., <tt>|</tt>). * The default is {@link SecurityConstants#KEYSTORE_FILE_VALUE}. * </p> * <p> * {@link SecurityConstants#KEYSTORE_PASS_PROP} - The keystore password(s) to * use for the given keystores. The passwords must be separated by a guard * (i.e., <tt>|</tt>).The default is * {@link SecurityConstants#KEYSTORE_PASS_VALUE}. * </p> * <p> * {@link SecurityConstants#KEYSTORE_TYPE_PROP} - The keystore type(s) to use * for the given keystores. The types must be separated by a guard (i.e., * <tt>|</tt>).The default is {@link SecurityConstants#KEYSTORE_TYPE_VALUE}. * </p> * <p> * {@link SecurityConstants#CRL_FILE_PROP} - The CRL URL(s) to use for revoked * certificates. The urls must be separated by a guard (i.e., <tt>|</tt>). The * default is {@link SecurityConstants#CRL_FILE_VALUE}. * </p> * </p> */ /* * TODO: using a string for passwords is bad. We need to investigate * alternatives. * * TODO: we might want to allow for the recognized properties to change without * a restart. This is trick because we can not publish a managed service due to * not being able to import as we are an extension bundle. */ public final class SecurityActivator implements BundleActivator { public synchronized void start(BundleContext context) throws Exception { PermissionAdminImpl pai = null; SecureAction action = new SecureAction(); Permissions permissions = new Permissions(context, action); File tmp = context.getDataFile("security" + File.separator + "tmp"); if ((tmp == null) || (!tmp.isDirectory() && !tmp.mkdirs())) { throw new IOException("Can't create tmp dir."); } // TODO: log something if we can not clean-up the tmp dir File[] old = tmp.listFiles(); if (old != null) { for (int i = 0; i < old.length; i++) { old[i].delete(); } } if ("TRUE".equalsIgnoreCase(getProperty(context, SecurityConstants.ENABLE_PERMISSIONADMIN_PROP, SecurityConstants.ENABLE_PERMISSIONADMIN_VALUE))) { File cache = context.getDataFile("security" + File.separator + "pa.txt"); if ((cache == null) || (!cache.isFile() && !cache.createNewFile())) { throw new IOException("Can't create cache file"); } pai = new PermissionAdminImpl(permissions, new PropertiesCache( cache, tmp, action)); } ConditionalPermissionAdminImpl cpai = null; if ("TRUE".equalsIgnoreCase(getProperty(context, SecurityConstants.ENABLE_CONDPERMADMIN_PROP, SecurityConstants.ENABLE_CONDPERMADMIN_VALUE))) { File cpaCache = context.getDataFile("security" + File.separator + "cpa.txt"); if ((cpaCache == null) || (!cpaCache.isFile() && !cpaCache.createNewFile())) { throw new IOException("Can't create cache file"); } LocalPermissions localPermissions = new LocalPermissions( permissions); cpai = new ConditionalPermissionAdminImpl(permissions, new Conditions(action), localPermissions, new PropertiesCache( cpaCache, tmp, action), pai); } if ((pai != null) || (cpai != null)) { String crlList = getProperty(context, SecurityConstants.CRL_FILE_PROP, SecurityConstants.CRL_FILE_VALUE); String storeList = getProperty(context, SecurityConstants.KEYSTORE_FILE_PROP, SecurityConstants.KEYSTORE_FILE_VALUE); String passwdList = getProperty(context, SecurityConstants.KEYSTORE_PASS_PROP, SecurityConstants.KEYSTORE_PASS_VALUE); String typeList = getProperty(context, SecurityConstants.KEYSTORE_TYPE_PROP, SecurityConstants.KEYSTORE_TYPE_VALUE); String osgi_keystores = getProperty(context, Constants.FRAMEWORK_TRUST_REPOSITORIES, null); if (osgi_keystores != null) { StringTokenizer tok = new StringTokenizer(osgi_keystores, File.pathSeparator); if (storeList.length() == 0) { storeList += "file:" + tok.nextToken(); passwdList += " "; typeList += "JKS"; } while (tok.hasMoreTokens()) { storeList += "|file:" + tok.nextToken(); passwdList += "| "; typeList += "|JKS"; } } StringTokenizer storeTok = new StringTokenizer(storeList, "|"); StringTokenizer passwdTok = new StringTokenizer(passwdList, "|"); StringTokenizer typeTok = new StringTokenizer(typeList, "|"); if ((storeTok.countTokens() != typeTok.countTokens()) || (passwdTok.countTokens() != storeTok.countTokens())) { throw new BundleException( "Each CACerts keystore must have one type and one passwd entry and vice versa."); } SecurityProvider provider = new SecurityProviderImpl(crlList, typeList, passwdList, storeList, pai, cpai, action, ((Felix) context.getBundle(0)).getLogger()); ((Felix) context.getBundle(0)).setSecurityProvider(provider); } if (pai != null) { context.registerService(PermissionAdmin.class.getName(), pai, null); } if (cpai != null) { context.registerService(ConditionalPermissionAdmin.class.getName(), cpai, null); } } public synchronized void stop(BundleContext context) throws Exception { ((Felix) context.getBundle(0)).setSecurityProvider(null); } private String getProperty(BundleContext context, String key, String defaultValue) { String result = context.getProperty(key); return (result != null) ? result : defaultValue; } }