001: /*
002: * Portions Copyright 2000-2006 Sun Microsystems, Inc. All Rights Reserved.
003: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
004: *
005: * This code is free software; you can redistribute it and/or modify it
006: * under the terms of the GNU General Public License version 2 only, as
007: * published by the Free Software Foundation. Sun designates this
008: * particular file as subject to the "Classpath" exception as provided
009: * by Sun in the LICENSE file that accompanied this code.
010: *
011: * This code is distributed in the hope that it will be useful, but WITHOUT
012: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
013: * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
014: * version 2 for more details (a copy is included in the LICENSE file that
015: * accompanied this code).
016: *
017: * You should have received a copy of the GNU General Public License version
018: * 2 along with this work; if not, write to the Free Software Foundation,
019: * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
020: *
021: * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
022: * CA 95054 USA or visit www.sun.com if you need additional information or
023: * have any questions.
024: */
025:
026: /*
027: * @(#)KeyTab.java 1.26 07/05/05
028: *
029: * (C) Copyright IBM Corp. 1999 All Rights Reserved.
030: * Copyright 1997 The Open Group Research Institute. All rights reserved.
031: */
032:
033: package sun.security.krb5.internal.ktab;
034:
035: import sun.security.krb5.*;
036: import sun.security.krb5.internal.*;
037: import sun.security.krb5.internal.crypto.*;
038: import java.util.Vector;
039: import java.util.ArrayList;
040: import java.util.Arrays;
041: import java.io.IOException;
042: import java.io.FileInputStream;
043: import java.io.FileOutputStream;
044: import java.io.File;
045: import java.util.StringTokenizer;
046:
047: /**
048: * This class represents key table. The key table functions deal with storing
049: * and retrieving service keys for use in authentication exchanges.
050: *
051: * @author Yanni Zhang
052: * @version 1.00 09 Mar 2000
053: */
054: public class KeyTab implements KeyTabConstants {
055: int kt_vno;
056: private static KeyTab singleton = null;
057: private static final boolean DEBUG = Krb5.DEBUG;
058: private static String name;
059: private Vector<KeyTabEntry> entries = new Vector<KeyTabEntry>();
060:
061: private KeyTab(String filename) throws IOException, RealmException {
062: init(filename);
063: }
064:
065: public static KeyTab getInstance(String s) {
066: name = parse(s);
067: if (name == null) {
068: return getInstance();
069: }
070: return getInstance(new File(name));
071: }
072:
073: /**
074: * Gets the single instance of KeyTab class.
075: * @param file the key tab file.
076: * @return single instance of KeyTab;
077: * return null if error occurs while reading data out of the file.
078: */
079: public static KeyTab getInstance(File file) {
080: try {
081: if (!(file.exists())) {
082: singleton = null;
083: } else {
084: String fname = file.getAbsolutePath();
085: // Since this class deals with file I/O operations,
086: // we want only one class instance existing.
087: if (singleton != null) {
088: File kfile = new File(singleton.name);
089: String kname = kfile.getAbsolutePath();
090: if (kname.equalsIgnoreCase(fname)) {
091: if (DEBUG) {
092: System.out
093: .println("KeyTab instance already exists");
094: }
095: }
096: } else {
097: singleton = new KeyTab(fname);
098: }
099: }
100: } catch (Exception e) {
101: singleton = null;
102: if (DEBUG) {
103: System.out
104: .println("Could not obtain an instance of KeyTab"
105: + e.getMessage());
106: }
107: }
108: return singleton;
109: }
110:
111: /**
112: * Gets the single instance of KeyTab class.
113: * @return single instance of KeyTab; return null if default keytab file
114: * does not exist, or error occurs while reading data from the file.
115: */
116: public static KeyTab getInstance() {
117: try {
118: name = getDefaultKeyTab();
119: if (name != null) {
120: singleton = getInstance(new File(name));
121: }
122: } catch (Exception e) {
123: singleton = null;
124: if (DEBUG) {
125: System.out
126: .println("Could not obtain an instance of KeyTab"
127: + e.getMessage());
128: }
129: }
130: return singleton;
131: }
132:
133: /**
134: * The location of keytab file will be read from the configuration file
135: * If it is not specified, consider user.home as the keytab file's
136: * default location.
137: */
138: private static String getDefaultKeyTab() {
139: if (name != null) {
140: return name;
141: } else {
142: String kname = null;
143: try {
144: String keytab_names = Config.getInstance().getDefault(
145: "default_keytab_name", "libdefaults");
146: if (keytab_names != null) {
147: StringTokenizer st = new StringTokenizer(
148: keytab_names, " ");
149: while (st.hasMoreTokens()) {
150: kname = parse(st.nextToken());
151: if (kname != null) {
152: break;
153: }
154: }
155: }
156: } catch (KrbException e) {
157: kname = null;
158: }
159:
160: if (kname == null) {
161: String user_home = java.security.AccessController
162: .doPrivileged(new sun.security.action.GetPropertyAction(
163: "user.home"));
164:
165: if (user_home == null) {
166: user_home = java.security.AccessController
167: .doPrivileged(new sun.security.action.GetPropertyAction(
168: "user.dir"));
169: }
170:
171: if (user_home != null) {
172: kname = user_home + File.separator + "krb5.keytab";
173: }
174: }
175: return kname;
176: }
177: }
178:
179: private static String parse(String name) {
180: String kname = null;
181: if (name == null) {
182: return null;
183: }
184: if ((name.length() >= 5)
185: && (name.substring(0, 5).equalsIgnoreCase("FILE:"))) {
186: kname = name.substring(5);
187: } else if ((name.length() >= 9)
188: && (name.substring(0, 9).equalsIgnoreCase("ANY:FILE:"))) {
189: // this format found in MIT's krb5.ini.
190: kname = name.substring(9);
191: } else if ((name.length() >= 7)
192: && (name.substring(0, 7).equalsIgnoreCase("SRVTAB:"))) {
193: // this format found in MIT's krb5.ini.
194: kname = name.substring(7);
195: } else
196: kname = name;
197: return kname;
198: }
199:
200: private synchronized void init(String filename) throws IOException,
201: RealmException {
202:
203: if (filename != null) {
204: KeyTabInputStream kis = new KeyTabInputStream(
205: new FileInputStream(filename));
206: load(kis);
207: kis.close();
208: name = filename;
209: }
210: }
211:
212: private void load(KeyTabInputStream kis) throws IOException,
213: RealmException {
214:
215: entries.clear();
216: kt_vno = kis.readVersion();
217: if (kt_vno == KRB5_KT_VNO_1) {
218: kis.setNativeByteOrder();
219: }
220: int entryLength = 0;
221: KeyTabEntry entry;
222: while (kis.available() > 0) {
223: entryLength = kis.readEntryLength();
224: entry = kis.readEntry(entryLength, kt_vno);
225: if (DEBUG) {
226: System.out.println(">>> KeyTab: load() entry length: "
227: + entryLength + "; type: "
228: + (entry != null ? entry.keyType : 0));
229: }
230: if (entry != null)
231: entries.addElement(entry);
232: }
233: }
234:
235: /**
236: * Reads the service key from the keytab file.
237: * @param service the PrincipalName of the requested service.
238: * @return the last service key in the keytab
239: */
240: public EncryptionKey readServiceKey(PrincipalName service) {
241: KeyTabEntry entry = null;
242: if (entries != null) {
243: // Find latest entry for this service that has an etype
244: // that has been configured for use
245: for (int i = entries.size() - 1; i >= 0; i--) {
246: entry = entries.elementAt(i);
247: if (entry.service.match(service)) {
248: if (EType.isSupported(entry.keyType)) {
249: return new EncryptionKey(entry.keyblock,
250: entry.keyType, new Integer(
251: entry.keyVersion));
252: } else if (DEBUG) {
253: System.out
254: .println("Found unsupported keytype ("
255: + entry.keyType + ") for "
256: + service);
257: }
258: }
259: }
260: }
261: return null;
262: }
263:
264: /**
265: * Reads all keys for a service from the keytab file that have
266: * etypes that have been configured for use.
267: * @param service the PrincipalName of the requested service
268: * @return an array containing all the service keys
269: */
270: public EncryptionKey[] readServiceKeys(PrincipalName service) {
271: KeyTabEntry entry;
272: EncryptionKey key;
273: int size = entries.size();
274: ArrayList<EncryptionKey> keys = new ArrayList<EncryptionKey>(
275: size);
276: if (entries != null) {
277: for (int i = size - 1; i >= 0; i--) {
278: entry = entries.elementAt(i);
279: if (entry.service.match(service)) {
280: if (EType.isSupported(entry.keyType)) {
281: key = new EncryptionKey(entry.keyblock,
282: entry.keyType, new Integer(
283: entry.keyVersion));
284: keys.add(key);
285: if (DEBUG) {
286: System.out.println("Added key: "
287: + entry.keyType + "version: "
288: + entry.keyVersion);
289: }
290: } else if (DEBUG) {
291: System.out
292: .println("Found unsupported keytype ("
293: + entry.keyType + ") for "
294: + service);
295: }
296: }
297: }
298: }
299:
300: size = keys.size();
301: if (size == 0)
302: return null;
303: EncryptionKey[] retVal = new EncryptionKey[size];
304:
305: // Sort keys according to default_tkt_enctypes
306: int pos = 0;
307: EncryptionKey k;
308: if (DEBUG) {
309: System.out
310: .println("Ordering keys wrt default_tkt_enctypes list");
311: }
312: int[] etypes = EType.getDefaults("default_tkt_enctypes");
313: if (etypes == null || etypes == EType.getBuiltInDefaults()) {
314: // Either no supported types specified in default_tkt_enctypes
315: // or no default_tkt_enctypes entry at all. For both cases,
316: // just return supported keys in the order retrieved
317: for (int i = 0; i < size; i++) {
318: retVal[pos++] = keys.get(i);
319: }
320: } else {
321: for (int j = 0; j < etypes.length && pos < size; j++) {
322: int target = etypes[j];
323: for (int i = 0; i < size && pos < size; i++) {
324: k = keys.get(i);
325: if (k != null && k.getEType() == target) {
326: if (DEBUG) {
327: System.out.println(pos + ": " + k);
328: }
329: retVal[pos++] = k;
330: keys.set(i, null); // Cleared from consideration
331: }
332: }
333: }
334: // copy the rest
335: for (int i = 0; i < size && pos < size; i++) {
336: k = keys.get(i);
337: if (k != null) {
338: retVal[pos++] = k;
339: }
340: }
341: }
342: if (pos != size) {
343: throw new RuntimeException(
344: "Internal Error: did not copy all keys;expecting "
345: + size + "; got " + pos);
346: }
347: return retVal;
348: }
349:
350: /**
351: * Searches for the service entry in the keytab file.
352: * The etype of the key must be one that has been configured
353: * to be used.
354: * @param service the PrincipalName of the requested service.
355: * @return true if the entry is found, otherwise, return false.
356: */
357: public boolean findServiceEntry(PrincipalName service) {
358: KeyTabEntry entry;
359: if (entries != null) {
360: for (int i = 0; i < entries.size(); i++) {
361: entry = entries.elementAt(i);
362: if (entry.service.match(service)) {
363: if (EType.isSupported(entry.keyType)) {
364: return true;
365: } else if (DEBUG) {
366: System.out
367: .println("Found unsupported keytype ("
368: + entry.keyType + ") for "
369: + service);
370: }
371: }
372: }
373: }
374: return false;
375: }
376:
377: public static String tabName() {
378: return name;
379: }
380:
381: /**
382: * Adds a new entry in the key table.
383: * @param service the service which will have a new entry in the key table.
384: * @param psswd the password which generates the key.
385: */
386: public void addEntry(PrincipalName service, char[] psswd)
387: throws KrbException {
388:
389: EncryptionKey[] encKeys = EncryptionKey.acquireSecretKeys(
390: psswd, service.getSalt());
391:
392: for (int i = 0; encKeys != null && i < encKeys.length; i++) {
393: int keyType = encKeys[i].getEType();
394: byte[] keyValue = encKeys[i].getBytes();
395: int result = retrieveEntry(service, keyType);
396: int kvno = 1;
397: if (result != -1) {
398: KeyTabEntry oldEntry = entries.elementAt(result);
399: kvno = oldEntry.keyVersion;
400: entries.removeElementAt(result);
401: kvno += 1;
402: } else
403: kvno = 1;
404:
405: KeyTabEntry newEntry = new KeyTabEntry(service, service
406: .getRealm(), new KerberosTime(System
407: .currentTimeMillis()), kvno, keyType, keyValue);
408: if (entries == null)
409: entries = new Vector<KeyTabEntry>();
410: entries.addElement(newEntry);
411: }
412: }
413:
414: /**
415: * Retrieves the key table entry with the specified service name.
416: * @param service the service which may have an entry in the key table.
417: * @return -1 if the entry is not found, else return the entry index
418: * in the list.
419: */
420: private int retrieveEntry(PrincipalName service, int keyType) {
421: int found = -1;
422: KeyTabEntry e;
423: if (entries != null) {
424: for (int i = 0; i < entries.size(); i++) {
425: e = entries.elementAt(i);
426: if (service.match(e.getService())
427: && (keyType == -1 || e.keyType == keyType)) {
428: return i;
429: }
430: }
431: }
432: return found;
433: }
434:
435: /**
436: * Gets the list of service entries in key table.
437: * @return array of <code>KeyTabEntry</code>.
438: */
439: public KeyTabEntry[] getEntries() {
440: if (entries != null) {
441: KeyTabEntry[] kentries = new KeyTabEntry[entries.size()];
442: for (int i = 0; i < kentries.length; i++) {
443: kentries[i] = entries.elementAt(i);
444: }
445: return kentries;
446: } else {
447: return null;
448: }
449: }
450:
451: /**
452: * Creates a new default key table.
453: */
454: public synchronized static KeyTab create() throws IOException,
455: RealmException {
456: String dname = getDefaultKeyTab();
457: return create(dname);
458: }
459:
460: /**
461: * Creates a new default key table.
462: */
463: public synchronized static KeyTab create(String name)
464: throws IOException, RealmException {
465:
466: KeyTabOutputStream kos = new KeyTabOutputStream(
467: new FileOutputStream(name));
468: kos.writeVersion(KRB5_KT_VNO);
469: kos.close();
470: singleton = new KeyTab(name);
471: return singleton;
472: }
473:
474: /**
475: * Saves the file at the directory.
476: */
477: public synchronized void save() throws IOException {
478: KeyTabOutputStream kos = new KeyTabOutputStream(
479: new FileOutputStream(name));
480: kos.writeVersion(kt_vno);
481: for (int i = 0; i < entries.size(); i++) {
482: kos.writeEntry(entries.elementAt(i));
483: }
484: kos.close();
485: }
486:
487: /**
488: * Removes an entry from the key table.
489: * @param service the service <code>PrincipalName</code>.
490: */
491: public void deleteEntry(PrincipalName service) {
492: int result = retrieveEntry(service, -1);
493: if (result != -1) {
494: entries.removeElementAt(result);
495: }
496: }
497:
498: /**
499: * Creates key table file version.
500: * @param file the key table file.
501: * @exception IOException.
502: */
503: public synchronized void createVersion(File file)
504: throws IOException {
505: KeyTabOutputStream kos = new KeyTabOutputStream(
506: new FileOutputStream(file));
507: kos.write16(KRB5_KT_VNO);
508: kos.close();
509: }
510:
511: public static void refresh() {
512: if (singleton != null) {
513: if (DEBUG) {
514: System.out.println("Refreshing Keytab");
515: }
516: singleton = null;
517: }
518: }
519: }
|