| /** |
| * 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.hadoop.security.alias; |
| |
| import org.apache.hadoop.classification.InterfaceAudience; |
| import org.apache.hadoop.conf.Configuration; |
| import org.apache.hadoop.fs.FileUtil; |
| import org.apache.hadoop.fs.permission.FsPermission; |
| import org.apache.hadoop.util.Shell; |
| |
| import java.io.File; |
| import java.io.FileInputStream; |
| import java.io.FileOutputStream; |
| import java.io.IOException; |
| import java.io.InputStream; |
| import java.io.OutputStream; |
| import java.net.URI; |
| import java.net.URISyntaxException; |
| import java.nio.file.Files; |
| import java.nio.file.Path; |
| import java.nio.file.Paths; |
| import java.nio.file.attribute.PosixFilePermission; |
| import java.nio.file.attribute.PosixFilePermissions; |
| import java.util.Set; |
| import java.util.StringTokenizer; |
| import java.util.EnumSet; |
| |
| /** |
| * CredentialProvider based on Java's KeyStore file format. The file may be |
| * stored only on the local filesystem using the following name mangling: |
| * localjceks://file/home/larry/creds.jceks -> file:///home/larry/creds.jceks |
| */ |
| @InterfaceAudience.Private |
| public final class LocalJavaKeyStoreProvider extends |
| AbstractJavaKeyStoreProvider { |
| public static final String SCHEME_NAME = "localjceks"; |
| private File file; |
| private Set<PosixFilePermission> permissions; |
| |
| private LocalJavaKeyStoreProvider(URI uri, Configuration conf) |
| throws IOException { |
| super(uri, conf); |
| } |
| |
| @Override |
| protected String getSchemeName() { |
| return SCHEME_NAME; |
| } |
| |
| @Override |
| protected OutputStream getOutputStreamForKeystore() throws IOException { |
| if (LOG.isDebugEnabled()) { |
| LOG.debug("using '" + file + "' for output stream."); |
| } |
| FileOutputStream out = new FileOutputStream(file); |
| return out; |
| } |
| |
| @Override |
| protected boolean keystoreExists() throws IOException { |
| /* The keystore loader doesn't handle zero length files. */ |
| return file.exists() && (file.length() > 0); |
| } |
| |
| @Override |
| protected InputStream getInputStreamForFile() throws IOException { |
| FileInputStream is = new FileInputStream(file); |
| return is; |
| } |
| |
| @Override |
| protected void createPermissions(String perms) throws IOException { |
| int mode = 700; |
| try { |
| mode = Integer.parseInt(perms, 8); |
| } catch (NumberFormatException nfe) { |
| throw new IOException("Invalid permissions mode provided while " |
| + "trying to createPermissions", nfe); |
| } |
| permissions = modeToPosixFilePermission(mode); |
| } |
| |
| @Override |
| protected void stashOriginalFilePermissions() throws IOException { |
| // save off permissions in case we need to |
| // rewrite the keystore in flush() |
| if (!Shell.WINDOWS) { |
| Path path = Paths.get(file.getCanonicalPath()); |
| permissions = Files.getPosixFilePermissions(path); |
| } else { |
| // On Windows, the JDK does not support the POSIX file permission APIs. |
| // Instead, we can do a winutils call and translate. |
| String[] cmd = Shell.getGetPermissionCommand(); |
| String[] args = new String[cmd.length + 1]; |
| System.arraycopy(cmd, 0, args, 0, cmd.length); |
| args[cmd.length] = file.getCanonicalPath(); |
| String out = Shell.execCommand(args); |
| StringTokenizer t = new StringTokenizer(out, Shell.TOKEN_SEPARATOR_REGEX); |
| // The winutils output consists of 10 characters because of the leading |
| // directory indicator, i.e. "drwx------". The JDK parsing method expects |
| // a 9-character string, so remove the leading character. |
| String permString = t.nextToken().substring(1); |
| permissions = PosixFilePermissions.fromString(permString); |
| } |
| } |
| |
| @Override |
| protected void initFileSystem(URI uri) |
| throws IOException { |
| super.initFileSystem(uri); |
| try { |
| file = new File(new URI(getPath().toString())); |
| if (LOG.isDebugEnabled()) { |
| LOG.debug("initialized local file as '" + file + "'."); |
| if (file.exists()) { |
| LOG.debug("the local file exists and is size " + file.length()); |
| if (LOG.isTraceEnabled()) { |
| if (file.canRead()) { |
| LOG.trace("we can read the local file."); |
| } |
| if (file.canWrite()) { |
| LOG.trace("we can write the local file."); |
| } |
| } |
| } else { |
| LOG.debug("the local file does not exist."); |
| } |
| } |
| } catch (URISyntaxException e) { |
| throw new IOException(e); |
| } |
| } |
| |
| @Override |
| public void flush() throws IOException { |
| super.flush(); |
| if (LOG.isDebugEnabled()) { |
| LOG.debug("Resetting permissions to '" + permissions + "'"); |
| } |
| if (!Shell.WINDOWS) { |
| Files.setPosixFilePermissions(Paths.get(file.getCanonicalPath()), |
| permissions); |
| } else { |
| // FsPermission expects a 10-character string because of the leading |
| // directory indicator, i.e. "drwx------". The JDK toString method returns |
| // a 9-character string, so prepend a leading character. |
| FsPermission fsPermission = FsPermission.valueOf( |
| "-" + PosixFilePermissions.toString(permissions)); |
| FileUtil.setPermission(file, fsPermission); |
| } |
| } |
| |
| /** |
| * The factory to create JksProviders, which is used by the ServiceLoader. |
| */ |
| public static class Factory extends CredentialProviderFactory { |
| @Override |
| public CredentialProvider createProvider(URI providerName, |
| Configuration conf) throws IOException { |
| if (SCHEME_NAME.equals(providerName.getScheme())) { |
| return new LocalJavaKeyStoreProvider(providerName, conf); |
| } |
| return null; |
| } |
| } |
| |
| private static Set<PosixFilePermission> modeToPosixFilePermission( |
| int mode) { |
| Set<PosixFilePermission> perms = EnumSet.noneOf(PosixFilePermission.class); |
| if ((mode & 0001) != 0) { |
| perms.add(PosixFilePermission.OTHERS_EXECUTE); |
| } |
| if ((mode & 0002) != 0) { |
| perms.add(PosixFilePermission.OTHERS_WRITE); |
| } |
| if ((mode & 0004) != 0) { |
| perms.add(PosixFilePermission.OTHERS_READ); |
| } |
| if ((mode & 0010) != 0) { |
| perms.add(PosixFilePermission.GROUP_EXECUTE); |
| } |
| if ((mode & 0020) != 0) { |
| perms.add(PosixFilePermission.GROUP_WRITE); |
| } |
| if ((mode & 0040) != 0) { |
| perms.add(PosixFilePermission.GROUP_READ); |
| } |
| if ((mode & 0100) != 0) { |
| perms.add(PosixFilePermission.OWNER_EXECUTE); |
| } |
| if ((mode & 0200) != 0) { |
| perms.add(PosixFilePermission.OWNER_WRITE); |
| } |
| if ((mode & 0400) != 0) { |
| perms.add(PosixFilePermission.OWNER_READ); |
| } |
| return perms; |
| } |
| } |