blob: 9ea9a579655e32533d1e5038d6694a64b1df4b58 [file] [log] [blame]
/**
* 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;
}
}