| 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 */  | 
 | 
 | 
 | 
/*  | 
 | 
 *  | 
 | 
 *  (C) Copyright IBM Corp. 1999 All Rights Reserved.  | 
 | 
 *  Copyright 1997 The Open Group Research Institute.  All rights reserved.  | 
 | 
 */  | 
 | 
 | 
 | 
package sun.security.krb5.internal.ktab;  | 
 | 
 | 
 | 
import sun.security.krb5.*;  | 
 | 
import sun.security.krb5.internal.*;  | 
 | 
import sun.security.krb5.internal.crypto.*;  | 
 | 
import java.util.ArrayList;  | 
 | 
import java.util.Arrays;  | 
 | 
import java.io.IOException;  | 
 | 
import java.io.FileInputStream;  | 
 | 
import java.io.FileOutputStream;  | 
 | 
import java.io.File;  | 
 | 
import java.io.FileNotFoundException;  | 
 | 
import java.util.Comparator;  | 
 | 
import java.util.HashMap;  | 
 | 
import java.util.Map;  | 
 | 
import java.util.StringTokenizer;  | 
 | 
import java.util.Vector;  | 
 | 
import sun.security.jgss.krb5.ServiceCreds;  | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 */  | 
 | 
public class KeyTab implements KeyTabConstants { | 
 | 
 | 
 | 
    private static final boolean DEBUG = Krb5.DEBUG;  | 
 | 
    private static String defaultTabName = null;  | 
 | 
 | 
 | 
    // Attention: Currently there is no way to remove a keytab from this map,  | 
 | 
      | 
 | 
    private static Map<String,KeyTab> map = new HashMap<>();  | 
 | 
 | 
 | 
      | 
 | 
    private boolean isMissing = false;  | 
 | 
 | 
 | 
      | 
 | 
    private boolean isValid = true;  | 
 | 
 | 
 | 
    private final String tabName;  | 
 | 
    private long lastModified;  | 
 | 
    private int kt_vno = KRB5_KT_VNO;  | 
 | 
 | 
 | 
    private Vector<KeyTabEntry> entries = new Vector<>();  | 
 | 
 | 
 | 
      | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
     */  | 
 | 
    private KeyTab(String filename) { | 
 | 
        tabName = filename;  | 
 | 
        try { | 
 | 
            lastModified = new File(tabName).lastModified();  | 
 | 
            try (KeyTabInputStream kis =  | 
 | 
                    new KeyTabInputStream(new FileInputStream(filename))) { | 
 | 
                load(kis);  | 
 | 
            }  | 
 | 
        } catch (FileNotFoundException e) { | 
 | 
            entries.clear();  | 
 | 
            isMissing = true;  | 
 | 
        } catch (Exception ioe) { | 
 | 
            entries.clear();  | 
 | 
            isValid = false;  | 
 | 
        }  | 
 | 
    }  | 
 | 
 | 
 | 
      | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
     */  | 
 | 
    private synchronized static KeyTab getInstance0(String s) { | 
 | 
        long lm = new File(s).lastModified();  | 
 | 
        KeyTab old = map.get(s);  | 
 | 
        if (old != null && old.isValid() && old.lastModified == lm) { | 
 | 
            return old;  | 
 | 
        }  | 
 | 
        KeyTab ktab = new KeyTab(s);  | 
 | 
        if (ktab.isValid()) {                | 
 | 
            map.put(s, ktab);  | 
 | 
            return ktab;  | 
 | 
        } else if (old != null) {            | 
 | 
            return old;  | 
 | 
        } else { | 
 | 
            return ktab;                      | 
 | 
        }  | 
 | 
    }  | 
 | 
 | 
 | 
      | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
     */  | 
 | 
    public static KeyTab getInstance(String s) { | 
 | 
        if (s == null) { | 
 | 
            return getInstance();  | 
 | 
        } else { | 
 | 
            return getInstance0(normalize(s));  | 
 | 
        }  | 
 | 
    }  | 
 | 
 | 
 | 
      | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
     */  | 
 | 
    public static KeyTab getInstance(File file) { | 
 | 
        if (file == null) { | 
 | 
            return getInstance();  | 
 | 
        } else { | 
 | 
            return getInstance0(file.getPath());  | 
 | 
        }  | 
 | 
    }  | 
 | 
 | 
 | 
      | 
 | 
 | 
 | 
 | 
 | 
     */  | 
 | 
    public static KeyTab getInstance() { | 
 | 
        return getInstance(getDefaultTabName());  | 
 | 
    }  | 
 | 
 | 
 | 
    public boolean isMissing() { | 
 | 
        return isMissing;  | 
 | 
    }  | 
 | 
 | 
 | 
    public boolean isValid() { | 
 | 
        return isValid;  | 
 | 
    }  | 
 | 
 | 
 | 
      | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
     */  | 
 | 
    private static String getDefaultTabName() { | 
 | 
        if (defaultTabName != null) { | 
 | 
            return defaultTabName;  | 
 | 
        } else { | 
 | 
            String kname = null;  | 
 | 
            try { | 
 | 
                String keytab_names = Config.getInstance().get  | 
 | 
                        ("libdefaults", "default_keytab_name"); | 
 | 
                if (keytab_names != null) { | 
 | 
                    StringTokenizer st = new StringTokenizer(keytab_names, " ");  | 
 | 
                    while (st.hasMoreTokens()) { | 
 | 
                        kname = normalize(st.nextToken());  | 
 | 
                        if (new File(kname).exists()) { | 
 | 
                            break;  | 
 | 
                        }  | 
 | 
                    }  | 
 | 
                }  | 
 | 
            } catch (KrbException e) { | 
 | 
                kname = null;  | 
 | 
            }  | 
 | 
 | 
 | 
            if (kname == null) { | 
 | 
                String user_home =  | 
 | 
                        java.security.AccessController.doPrivileged(  | 
 | 
                        new sun.security.action.GetPropertyAction("user.home")); | 
 | 
 | 
 | 
                if (user_home == null) { | 
 | 
                    user_home =  | 
 | 
                        java.security.AccessController.doPrivileged(  | 
 | 
                        new sun.security.action.GetPropertyAction("user.dir")); | 
 | 
                }  | 
 | 
 | 
 | 
                kname = user_home + File.separator  + "krb5.keytab";  | 
 | 
            }  | 
 | 
            defaultTabName = kname;  | 
 | 
            return kname;  | 
 | 
        }  | 
 | 
    }  | 
 | 
 | 
 | 
    /**  | 
 | 
     * Normalizes some common keytab name formats into the bare file name.  | 
 | 
     * For example, FILE:/etc/krb5.keytab to /etc/krb5.keytab  | 
 | 
     * @param name never null  | 
 | 
     * @return never null  | 
 | 
     */  | 
 | 
      | 
 | 
    public static String normalize(String name) { | 
 | 
        String kname;  | 
 | 
        if ((name.length() >= 5) &&  | 
 | 
            (name.substring(0, 5).equalsIgnoreCase("FILE:"))) { | 
 | 
            kname = name.substring(5);  | 
 | 
        } else if ((name.length() >= 9) &&  | 
 | 
                (name.substring(0, 9).equalsIgnoreCase("ANY:FILE:"))) { | 
 | 
              | 
 | 
            kname = name.substring(9);  | 
 | 
        } else if ((name.length() >= 7) &&  | 
 | 
                (name.substring(0, 7).equalsIgnoreCase("SRVTAB:"))) { | 
 | 
              | 
 | 
            kname = name.substring(7);  | 
 | 
        } else  | 
 | 
            kname = name;  | 
 | 
        return kname;  | 
 | 
    }  | 
 | 
 | 
 | 
    private void load(KeyTabInputStream kis)  | 
 | 
        throws IOException, RealmException { | 
 | 
 | 
 | 
        entries.clear();  | 
 | 
        kt_vno = kis.readVersion();  | 
 | 
        if (kt_vno == KRB5_KT_VNO_1) { | 
 | 
            kis.setNativeByteOrder();  | 
 | 
        }  | 
 | 
        int entryLength = 0;  | 
 | 
        KeyTabEntry entry;  | 
 | 
        while (kis.available() > 0) { | 
 | 
            entryLength = kis.readEntryLength();  | 
 | 
            entry = kis.readEntry(entryLength, kt_vno);  | 
 | 
            if (DEBUG) { | 
 | 
                System.out.println(">>> KeyTab: load() entry length: " + | 
 | 
                        entryLength + "; type: " +  | 
 | 
                        (entry != null? entry.keyType : 0));  | 
 | 
            }  | 
 | 
            if (entry != null)  | 
 | 
                entries.addElement(entry);  | 
 | 
        }  | 
 | 
    }  | 
 | 
 | 
 | 
      | 
 | 
 | 
 | 
 | 
 | 
     */  | 
 | 
    public PrincipalName getOneName() { | 
 | 
        int size = entries.size();  | 
 | 
        return size > 0 ? entries.elementAt(size-1).service : null;  | 
 | 
    }  | 
 | 
 | 
 | 
      | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
     */  | 
 | 
    public EncryptionKey[] readServiceKeys(PrincipalName service) { | 
 | 
        KeyTabEntry entry;  | 
 | 
        EncryptionKey key;  | 
 | 
        int size = entries.size();  | 
 | 
        ArrayList<EncryptionKey> keys = new ArrayList<>(size);  | 
 | 
        if (DEBUG) { | 
 | 
            System.out.println("Looking for keys for: " + service); | 
 | 
        }  | 
 | 
        for (int i = size-1; i >= 0; i--) { | 
 | 
            entry = entries.elementAt(i);  | 
 | 
            if (entry.service.match(service)) { | 
 | 
                if (EType.isSupported(entry.keyType)) { | 
 | 
                    key = new EncryptionKey(entry.keyblock,  | 
 | 
                                        entry.keyType,  | 
 | 
                                        new Integer(entry.keyVersion));  | 
 | 
                    keys.add(key);  | 
 | 
                    if (DEBUG) { | 
 | 
                        System.out.println("Added key: " + entry.keyType + | 
 | 
                            "version: " + entry.keyVersion);  | 
 | 
                    }  | 
 | 
                } else if (DEBUG) { | 
 | 
                    System.out.println("Found unsupported keytype (" + | 
 | 
                        entry.keyType + ") for " + service);  | 
 | 
                }  | 
 | 
            }  | 
 | 
        }  | 
 | 
        size = keys.size();  | 
 | 
        EncryptionKey[] retVal = keys.toArray(new EncryptionKey[size]);  | 
 | 
 | 
 | 
        // Sort the keys by kvno. Sometimes we must choose a single key (say,  | 
 | 
        // generate encrypted timestamp in AS-REQ). A key with a higher KVNO  | 
 | 
          | 
 | 
        Arrays.sort(retVal, new Comparator<EncryptionKey>() { | 
 | 
            @Override  | 
 | 
            public int compare(EncryptionKey o1, EncryptionKey o2) { | 
 | 
                return o2.getKeyVersionNumber().intValue()  | 
 | 
                        - o1.getKeyVersionNumber().intValue();  | 
 | 
            }  | 
 | 
        });  | 
 | 
 | 
 | 
        return retVal;  | 
 | 
    }  | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
      | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
     */  | 
 | 
    public boolean findServiceEntry(PrincipalName service) { | 
 | 
        KeyTabEntry entry;  | 
 | 
        for (int i = 0; i < entries.size(); i++) { | 
 | 
            entry = entries.elementAt(i);  | 
 | 
            if (entry.service.match(service)) { | 
 | 
                if (EType.isSupported(entry.keyType)) { | 
 | 
                    return true;  | 
 | 
                } else if (DEBUG) { | 
 | 
                    System.out.println("Found unsupported keytype (" + | 
 | 
                        entry.keyType + ") for " + service);  | 
 | 
                }  | 
 | 
            }  | 
 | 
        }  | 
 | 
        return false;  | 
 | 
    }  | 
 | 
 | 
 | 
    public String tabName() { | 
 | 
        return tabName;  | 
 | 
    }  | 
 | 
 | 
 | 
    /////////////////// THE WRITE SIDE ///////////////////////  | 
 | 
    /////////////// only used by ktab tool //////////////////  | 
 | 
 | 
 | 
      | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
     */  | 
 | 
    public void addEntry(PrincipalName service, char[] psswd,  | 
 | 
            int kvno, boolean append) throws KrbException { | 
 | 
        addEntry(service, service.getSalt(), psswd, kvno, append);  | 
 | 
    }  | 
 | 
 | 
 | 
      | 
 | 
    public void addEntry(PrincipalName service, String salt, char[] psswd,  | 
 | 
            int kvno, boolean append) throws KrbException { | 
 | 
 | 
 | 
        EncryptionKey[] encKeys = EncryptionKey.acquireSecretKeys(  | 
 | 
            psswd, salt);  | 
 | 
 | 
 | 
        // There should be only one maximum KVNO value for all etypes, so that  | 
 | 
        // all added keys can have the same KVNO.  | 
 | 
 | 
 | 
        int maxKvno = 0;      | 
 | 
        for (int i = entries.size()-1; i >= 0; i--) { | 
 | 
            KeyTabEntry e = entries.get(i);  | 
 | 
            if (e.service.match(service)) { | 
 | 
                if (e.keyVersion > maxKvno) { | 
 | 
                    maxKvno = e.keyVersion;  | 
 | 
                }  | 
 | 
                if (!append || e.keyVersion == kvno) { | 
 | 
                    entries.removeElementAt(i);  | 
 | 
                }  | 
 | 
            }  | 
 | 
        }  | 
 | 
        if (kvno == -1) { | 
 | 
            kvno = maxKvno + 1;  | 
 | 
        }  | 
 | 
 | 
 | 
        for (int i = 0; encKeys != null && i < encKeys.length; i++) { | 
 | 
            int keyType = encKeys[i].getEType();  | 
 | 
            byte[] keyValue = encKeys[i].getBytes();  | 
 | 
 | 
 | 
            KeyTabEntry newEntry = new KeyTabEntry(service,  | 
 | 
                            service.getRealm(),  | 
 | 
                            new KerberosTime(System.currentTimeMillis()),  | 
 | 
                                               kvno, keyType, keyValue);  | 
 | 
            entries.addElement(newEntry);  | 
 | 
        }  | 
 | 
    }  | 
 | 
 | 
 | 
      | 
 | 
 | 
 | 
 | 
 | 
     */  | 
 | 
    public KeyTabEntry[] getEntries() { | 
 | 
        KeyTabEntry[] kentries = new KeyTabEntry[entries.size()];  | 
 | 
        for (int i = 0; i < kentries.length; i++) { | 
 | 
            kentries[i] = entries.elementAt(i);  | 
 | 
        }  | 
 | 
        return kentries;  | 
 | 
    }  | 
 | 
 | 
 | 
      | 
 | 
 | 
 | 
     */  | 
 | 
    public synchronized static KeyTab create()  | 
 | 
        throws IOException, RealmException { | 
 | 
        String dname = getDefaultTabName();  | 
 | 
        return create(dname);  | 
 | 
    }  | 
 | 
 | 
 | 
      | 
 | 
 | 
 | 
     */  | 
 | 
    public synchronized static KeyTab create(String name)  | 
 | 
        throws IOException, RealmException { | 
 | 
 | 
 | 
        try (KeyTabOutputStream kos =  | 
 | 
                new KeyTabOutputStream(new FileOutputStream(name))) { | 
 | 
            kos.writeVersion(KRB5_KT_VNO);  | 
 | 
        }  | 
 | 
        return new KeyTab(name);  | 
 | 
    }  | 
 | 
 | 
 | 
      | 
 | 
 | 
 | 
     */  | 
 | 
    public synchronized void save() throws IOException { | 
 | 
        try (KeyTabOutputStream kos =  | 
 | 
                new KeyTabOutputStream(new FileOutputStream(tabName))) { | 
 | 
            kos.writeVersion(kt_vno);  | 
 | 
            for (int i = 0; i < entries.size(); i++) { | 
 | 
                kos.writeEntry(entries.elementAt(i));  | 
 | 
            }  | 
 | 
        }  | 
 | 
    }  | 
 | 
 | 
 | 
      | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
     */  | 
 | 
    public int deleteEntries(PrincipalName service, int etype, int kvno) { | 
 | 
        int count = 0;  | 
 | 
 | 
 | 
          | 
 | 
        Map<Integer,Integer> highest = new HashMap<>();  | 
 | 
 | 
 | 
        for (int i = entries.size()-1; i >= 0; i--) { | 
 | 
            KeyTabEntry e = entries.get(i);  | 
 | 
            if (service.match(e.getService())) { | 
 | 
                if (etype == -1 || e.keyType == etype) { | 
 | 
                    if (kvno == -2) { | 
 | 
                        // Two rounds for kvno == -2. In the first round (here),  | 
 | 
                          | 
 | 
                        if (highest.containsKey(e.keyType)) { | 
 | 
                            int n = highest.get(e.keyType);  | 
 | 
                            if (e.keyVersion > n) { | 
 | 
                                highest.put(e.keyType, e.keyVersion);  | 
 | 
                            }  | 
 | 
                        } else { | 
 | 
                            highest.put(e.keyType, e.keyVersion);  | 
 | 
                        }  | 
 | 
                    } else if (kvno == -1 || e.keyVersion == kvno) { | 
 | 
                        entries.removeElementAt(i);  | 
 | 
                        count++;  | 
 | 
                    }  | 
 | 
                }  | 
 | 
            }  | 
 | 
        }  | 
 | 
 | 
 | 
          | 
 | 
        if (kvno == -2) { | 
 | 
            for (int i = entries.size()-1; i >= 0; i--) { | 
 | 
                KeyTabEntry e = entries.get(i);  | 
 | 
                if (service.match(e.getService())) { | 
 | 
                    if (etype == -1 || e.keyType == etype) { | 
 | 
                        int n = highest.get(e.keyType);  | 
 | 
                        if (e.keyVersion != n) { | 
 | 
                            entries.removeElementAt(i);  | 
 | 
                            count++;  | 
 | 
                        }  | 
 | 
                    }  | 
 | 
                }  | 
 | 
            }  | 
 | 
        }  | 
 | 
        return count;  | 
 | 
    }  | 
 | 
 | 
 | 
      | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
     */  | 
 | 
    public synchronized void createVersion(File file) throws IOException { | 
 | 
        try (KeyTabOutputStream kos =  | 
 | 
                new KeyTabOutputStream(new FileOutputStream(file))) { | 
 | 
            kos.write16(KRB5_KT_VNO);  | 
 | 
        }  | 
 | 
    }  | 
 | 
}  |