001/** 002 * Licensed to the Apache Software Foundation (ASF) under one 003 * or more contributor license agreements. See the NOTICE file 004 * distributed with this work for additional information 005 * regarding copyright ownership. The ASF licenses this file 006 * to you under the Apache License, Version 2.0 (the 007 * "License"); you may not use this file except in compliance 008 * with the License. You may obtain a copy of the License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, software 013 * distributed under the License is distributed on an "AS IS" BASIS, 014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 015 * See the License for the specific language governing permissions and 016 * limitations under the License. 017 */ 018 019package org.apache.hadoop.crypto.key; 020 021import java.io.IOException; 022import java.net.URI; 023import java.util.ArrayList; 024import java.util.Date; 025import java.util.HashMap; 026import java.util.List; 027import java.util.Map; 028 029import org.apache.hadoop.classification.InterfaceAudience; 030import org.apache.hadoop.conf.Configuration; 031import org.apache.hadoop.io.Text; 032import org.apache.hadoop.security.Credentials; 033import org.apache.hadoop.security.UserGroupInformation; 034 035/** 036 * A KeyProvider factory for UGIs. It uses the credentials object associated 037 * with the current user to find keys. This provider is created using a 038 * URI of "user:///". 039 */ 040@InterfaceAudience.Private 041public class UserProvider extends KeyProvider { 042 public static final String SCHEME_NAME = "user"; 043 private final UserGroupInformation user; 044 private final Credentials credentials; 045 private final Map<String, Metadata> cache = new HashMap<String, Metadata>(); 046 047 private UserProvider(Configuration conf) throws IOException { 048 super(conf); 049 user = UserGroupInformation.getCurrentUser(); 050 credentials = user.getCredentials(); 051 } 052 053 @Override 054 public boolean isTransient() { 055 return true; 056 } 057 058 @Override 059 public synchronized KeyVersion getKeyVersion(String versionName) 060 throws IOException { 061 byte[] bytes = credentials.getSecretKey(new Text(versionName)); 062 if (bytes == null) { 063 return null; 064 } 065 return new KeyVersion(getBaseName(versionName), versionName, bytes); 066 } 067 068 @Override 069 public synchronized Metadata getMetadata(String name) throws IOException { 070 if (cache.containsKey(name)) { 071 return cache.get(name); 072 } 073 byte[] serialized = credentials.getSecretKey(new Text(name)); 074 if (serialized == null) { 075 return null; 076 } 077 Metadata result = new Metadata(serialized); 078 cache.put(name, result); 079 return result; 080 } 081 082 @Override 083 public synchronized KeyVersion createKey(String name, byte[] material, 084 Options options) throws IOException { 085 Text nameT = new Text(name); 086 if (credentials.getSecretKey(nameT) != null) { 087 throw new IOException("Key " + name + " already exists in " + this); 088 } 089 if (options.getBitLength() != 8 * material.length) { 090 throw new IOException("Wrong key length. Required " + 091 options.getBitLength() + ", but got " + (8 * material.length)); 092 } 093 Metadata meta = new Metadata(options.getCipher(), options.getBitLength(), 094 options.getDescription(), options.getAttributes(), new Date(), 1); 095 cache.put(name, meta); 096 String versionName = buildVersionName(name, 0); 097 credentials.addSecretKey(nameT, meta.serialize()); 098 credentials.addSecretKey(new Text(versionName), material); 099 return new KeyVersion(name, versionName, material); 100 } 101 102 @Override 103 public synchronized void deleteKey(String name) throws IOException { 104 Metadata meta = getMetadata(name); 105 if (meta == null) { 106 throw new IOException("Key " + name + " does not exist in " + this); 107 } 108 for(int v=0; v < meta.getVersions(); ++v) { 109 credentials.removeSecretKey(new Text(buildVersionName(name, v))); 110 } 111 credentials.removeSecretKey(new Text(name)); 112 cache.remove(name); 113 } 114 115 @Override 116 public synchronized KeyVersion rollNewVersion(String name, 117 byte[] material) throws IOException { 118 Metadata meta = getMetadata(name); 119 if (meta == null) { 120 throw new IOException("Key " + name + " not found"); 121 } 122 if (meta.getBitLength() != 8 * material.length) { 123 throw new IOException("Wrong key length. Required " + 124 meta.getBitLength() + ", but got " + (8 * material.length)); 125 } 126 int nextVersion = meta.addVersion(); 127 credentials.addSecretKey(new Text(name), meta.serialize()); 128 String versionName = buildVersionName(name, nextVersion); 129 credentials.addSecretKey(new Text(versionName), material); 130 return new KeyVersion(name, versionName, material); 131 } 132 133 @Override 134 public String toString() { 135 return SCHEME_NAME + ":///"; 136 } 137 138 @Override 139 public synchronized void flush() { 140 user.addCredentials(credentials); 141 } 142 143 public static class Factory extends KeyProviderFactory { 144 145 @Override 146 public KeyProvider createProvider(URI providerName, 147 Configuration conf) throws IOException { 148 if (SCHEME_NAME.equals(providerName.getScheme())) { 149 return new UserProvider(conf); 150 } 151 return null; 152 } 153 } 154 155 @Override 156 public synchronized List<String> getKeys() throws IOException { 157 List<String> list = new ArrayList<String>(); 158 List<Text> keys = credentials.getAllSecretKeys(); 159 for (Text key : keys) { 160 if (key.find("@") == -1) { 161 list.add(key.toString()); 162 } 163 } 164 return list; 165 } 166 167 @Override 168 public synchronized List<KeyVersion> getKeyVersions(String name) throws IOException { 169 List<KeyVersion> list = new ArrayList<KeyVersion>(); 170 Metadata km = getMetadata(name); 171 if (km != null) { 172 int latestVersion = km.getVersions(); 173 for (int i = 0; i < latestVersion; i++) { 174 KeyVersion v = getKeyVersion(buildVersionName(name, i)); 175 if (v != null) { 176 list.add(v); 177 } 178 } 179 } 180 return list; 181 } 182}