Author: erodriguez Date: Wed Dec 29 03:13:03 2004 New Revision: 123624 URL: http://svn.apache.org/viewcvs?view=rev&rev=123624 Log: Infrastructure for using changepw protocol with embedded Eve store. Added: incubator/directory/changepw/trunk/core/src/java/org/apache/kerberos/changepw/store/EmbeddedEveStore.java incubator/directory/changepw/trunk/core/src/java/org/apache/kerberos/changepw/store/PasswordStoreEntry.java incubator/directory/changepw/trunk/core/src/java/org/apache/kerberos/changepw/store/PasswordStoreEntryModifier.java
Added: incubator/directory/changepw/trunk/core/src/java/org/apache/kerberos/changepw/store/EmbeddedEveStore.java Url: http://svn.apache.org/viewcvs/incubator/directory/changepw/trunk/core/src/java/org/apache/kerberos/changepw/store/EmbeddedEveStore.java?view=auto&rev=123624 ============================================================================== --- (empty file) +++ incubator/directory/changepw/trunk/core/src/java/org/apache/kerberos/changepw/store/EmbeddedEveStore.java Wed Dec 29 03:13:03 2004 @@ -0,0 +1,339 @@ +/* + * Copyright 2004 The Apache Software Foundation + * + * Licensed 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.kerberos.changepw.store; + + +import java.io.File; +import java.io.FileInputStream; +import java.util.Hashtable; + +import javax.naming.Context; +import javax.naming.Name; +import javax.naming.NamingEnumeration; +import javax.naming.NamingException; +import javax.naming.directory.Attributes; +import javax.naming.directory.InitialDirContext; +import javax.naming.directory.SearchResult; +import javax.security.auth.kerberos.KerberosKey; +import javax.security.auth.kerberos.KerberosPrincipal; + +import org.apache.ldap.common.ldif.LdifIterator; +import org.apache.ldap.common.ldif.LdifParser; +import org.apache.ldap.common.ldif.LdifParserImpl; +import org.apache.ldap.common.message.LockableAttributesImpl; +import org.apache.ldap.common.name.LdapName; +import org.apache.ldap.common.util.NestableRuntimeException; + + +/** + * ChangePassword backing store implementation using an embedded Eve DIT + * + * @author <a href="mailto:[EMAIL PROTECTED]">Apache Directory Project</a> + * @version $Rev$ + */ +public class EmbeddedEveStore implements PasswordStore +{ + + // ------------------------------------------------------------------------ + // Krb5 Schema Attributes + // ------------------------------------------------------------------------ + + /** + * The key of the property specifying where Kerberos users are stored. If this + * property is not set the store defaults to performing a subtree search + * from the DN in the [EMAIL PROTECTED] Context#PROVIDER_URL}. If it is present a more + * efficient search is conducted on the more specific DN. + */ + public static final String KDC_ENTRY_BASEDN_KEY = "kdc.entry.basedn"; + public static final String KDC_ENTRY_LDIF_FILE = "kdc.entry.ldif.file"; + + /** the krb5kdc schema key for a krb5KDCEntry */ + private static final String KEY_ATTR = "krb5Key"; + /** the krb5kdc schema key encryption type for a krb5KDCEntry */ + private static final String TYPE_ATTR = "krb5EncryptionType"; + /** the krb5kdc schema principal name for a krb5KDCEntry */ + private static final String PRINCIPAL_ATTR = "krb5PrincipalName"; + /** the krb5kdc schema key version identifier for a krb5KDCEntry */ + private static final String VERSION_ATTR = "krb5KeyVersionNumber"; + + /** JNDI environment properties and more */ + private final Hashtable env; + /** a handle on the top initial context: get new context from this */ + private InitialDirContext ctx; + /** the search base relative to provider URL to use for reading entries */ + private Name searchBaseDn; + + + /** + * Creates the embedded Eve principal store. + * + * @param env the environment properties used to initialize the Eve + * Kerberos environment + */ + public EmbeddedEveStore( Hashtable env ) + { + this.env = ( Hashtable ) env.clone(); + this.env.put( Context.INITIAL_CONTEXT_FACTORY, "org.apache.eve.jndi.EveContextFactory" ); + } + + + /** + * Fires up the Eve backing store using the environment properties supplied to the + * constructor. The JNDI default context factor and some other parameters + * are automatically set though. This method then checks to see if the + * suffix is installed and setup according to the needed DIT structure. + * + * Create some additional indices for the Kerberos schema, namely on + * krb5PrincipalName. Also objectClass should be indexed as well. If these + * indices are not specified we should add them here. + */ + public void init() + { + LdapName ctxRoot; + + try + { + ctx = new InitialDirContext( env ); + } + catch ( NamingException e ) + { + // @todo for now until we can find a better means of error handling + e.printStackTrace(); + String msg = "Failed to create initial context for Eve provider"; + NestableRuntimeException fault; + fault = new NestableRuntimeException( msg, e ); + throw fault; + } + + // get the seach base if it has been set ------------------------------ + if ( env.containsKey( KDC_ENTRY_BASEDN_KEY ) ) + { + try + { + ctxRoot = new LdapName( ctx.getNameInNamespace() ); + searchBaseDn = new LdapName( ( String ) env.get( KDC_ENTRY_BASEDN_KEY ) ); + + if ( searchBaseDn.startsWith( ctxRoot ) ) + { + for ( int ii = 0; ii < ctxRoot.size(); ii++ ) + { + searchBaseDn.remove( 0 ); + } + } + else + { + String msg = "Failed to create initial context for Eve provider"; + IllegalArgumentException fault; + fault = new IllegalArgumentException( msg ); + throw fault; + } + } + catch ( NamingException e ) + { + // @todo for now until we can find a better means of error handling + e.printStackTrace(); + String msg = "Failed to find search base for Eve store"; + NestableRuntimeException fault; + fault = new NestableRuntimeException( msg, e ); + throw fault; + } + } + + // An LDIF must be loaded if this is the first time we're starting + // otherwise we're done and ready to serve lookup requests + if ( ! env.containsKey( KDC_ENTRY_LDIF_FILE ) ) + { + return; + } + + try + { + File file = new File( ( ( String ) env.get( KDC_ENTRY_LDIF_FILE ) ).trim() ); + + if ( ! file.exists() ) + { + System.err.println( "LDIF file '" + file.getAbsolutePath() + "' does not exit!" ); + + System.exit( 4 ); + } + + FileInputStream in = new FileInputStream( file ); + LdifIterator iterator = new LdifIterator( in ); + LdifParser ldifParser = new LdifParserImpl(); + + while ( iterator.hasNext() ) + { + String ldif = ( String ) iterator.next(); + Attributes attributes = new LockableAttributesImpl(); + ldifParser.parse( attributes, ldif ); + String dn = ( String ) attributes.remove( "dn" ).get(); + + if ( attributes.get( "objectClass" ).contains( "krb5KDCEntry" ) ) + { + String pw = ( String ) attributes.get( "userpassword" ).get(); + String krbPrincipal = ( String ) attributes.get( PRINCIPAL_ATTR ).get(); + KerberosPrincipal principal = new KerberosPrincipal( krbPrincipal ); + KerberosKey key = new KerberosKey( principal, pw.toCharArray(), "DES" ) ; + byte[] encodedKey = key.getEncoded(); + attributes.put( KEY_ATTR, encodedKey ); + attributes.put( VERSION_ATTR, Integer.toString( key.getVersionNumber() ) ); + attributes.put( TYPE_ATTR, Integer.toString( key.getKeyType() ) ); + } + + try + { + if ( ctx.lookup( dn ) == null ) + { + System.out.println( "Entry " + dn + " from LDIF exists." ); + continue; + } + } + catch( Exception e ) + { + System.out.println( "Entry " + dn + + " from LDIF does not exist. Creating it ..." ); + + } + + ctx.createSubcontext( getRelativeName( ctx, dn ), attributes ); + } + } + catch( Exception e ) + { + // @todo for now until we can find a better means of error handling + e.printStackTrace(); + String msg = "Failed to import initial LDIF into Eve store"; + NestableRuntimeException fault; + fault = new NestableRuntimeException( msg, e ); + throw fault; + } + + + try + { + String ldifFile = ( String ) env.get( KDC_ENTRY_LDIF_FILE ); + FileInputStream in = new FileInputStream( ldifFile ); + LdifIterator iterator = new LdifIterator( in ); + LdifParser ldifParser = new LdifParserImpl(); + + while ( iterator.hasNext() ) + { + String ldif = ( String ) iterator.next(); + Attributes attributes = new LockableAttributesImpl(); + ldifParser.parse( attributes, ldif ); + String dn = ( String ) attributes.remove( "dn" ).get(); + + Context stored = ( Context ) ctx.lookup( getRelativeName( ctx, dn ) ); + + if ( stored == null ) + { + throw new IllegalStateException( "LDIF entries not being pushed to disk" ); + } + } + } + catch( Exception e ) + { + // @todo for now until we can find a better means of error handling + e.printStackTrace(); + String msg = "Failed to import initial LDIF into Eve store"; + NestableRuntimeException fault; + fault = new NestableRuntimeException( msg, e ); + throw fault; + } + } + + + public Name getRelativeName( Context base, String dn ) throws NamingException + { + LdapName rdn = new LdapName( dn ); + LdapName baseDn = new LdapName( base.getNameInNamespace() ); + + if ( rdn.startsWith( baseDn ) ) + { + for ( int ii = 0; ii < baseDn.size(); ii++ ) + { + rdn.remove( 0 ); + } + } + else + { + throw new NamingException( dn + " is not a subordinate of context:" + + baseDn.toString() ); + } + + return rdn; + } + + + public String changePassword( KerberosPrincipal principal, byte[] key ) + { + if ( principal == null ) + { + return null; + } + + Attributes attributes = new LockableAttributesImpl(); + attributes.put( PRINCIPAL_ATTR, principal.getName() ); + try + { + Attributes attrs = null; + NamingEnumeration list = ctx.search( searchBaseDn, attributes ); + if ( list.hasMore() ) + { + SearchResult result = ( SearchResult ) list.next(); + attrs = result.getAttributes(); + } + list.close(); + + if ( attrs == null ) + { + return null; + } + + return getEntry( attrs ); + } + catch ( NamingException e ) + { + e.printStackTrace(); + return null; + } + } + + + /** + * Marshals an a String from an Attributes object. + * + * @param attrs the attributes of the Kerberos principal + * @return the entry for the principal + * @throws NamingException if there are any access problems + */ + private String getEntry( Attributes attrs ) throws NamingException + { + PasswordStoreEntryModifier modifier = new PasswordStoreEntryModifier(); + String principal = (String) attrs.get( PRINCIPAL_ATTR ).get(); + String encryptionType = (String) attrs.get( TYPE_ATTR ).get(); + String keyVersionNumber = (String) attrs.get( VERSION_ATTR ).get(); + byte[] keyBytes = (byte[]) attrs.get( KEY_ATTR ).get(); + + modifier.setPrincipal( new KerberosPrincipal( principal ) ); + modifier.setEncryptionType( Integer.parseInt( encryptionType ) ); + modifier.setKeyVersionNumber( Integer.parseInt( keyVersionNumber ) ); + modifier.setKey( keyBytes ); + return modifier.getEntry().getCommonName(); + } +} + Added: incubator/directory/changepw/trunk/core/src/java/org/apache/kerberos/changepw/store/PasswordStoreEntry.java Url: http://svn.apache.org/viewcvs/incubator/directory/changepw/trunk/core/src/java/org/apache/kerberos/changepw/store/PasswordStoreEntry.java?view=auto&rev=123624 ============================================================================== --- (empty file) +++ incubator/directory/changepw/trunk/core/src/java/org/apache/kerberos/changepw/store/PasswordStoreEntry.java Wed Dec 29 03:13:03 2004 @@ -0,0 +1,90 @@ +/* + * Copyright 2004 The Apache Software Foundation + * + * Licensed 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.kerberos.changepw.store; + +import org.apache.kerberos.crypto.encryption.*; +import org.apache.kerberos.messages.value.*; + +import javax.security.auth.kerberos.*; + +public class PasswordStoreEntry { + + // 'Principal' + private String _commonName; + private KerberosPrincipal _principal; + + // 'KDCEntry' + private KerberosTime _validStart; + private KerberosTime _validEnd; + private KerberosTime _passwordEnd; + private int _maxLife; + private int _maxRenew; + private int _kdcFlags; + private EncryptionKey _key; + + private String _realmName; + + PasswordStoreEntry(String commonName, KerberosPrincipal principal, + int keyVersionNumber, KerberosTime validStart, KerberosTime validEnd, + KerberosTime passwordEnd, int maxLife, int maxRenew, int kdcFlags, + int keyType, byte[] key, String realmName) { + + _commonName = commonName; + _principal = principal; + _validStart = validStart; + _validEnd = validEnd; + _passwordEnd = passwordEnd; + _maxLife = maxLife; + _maxRenew = maxRenew; + _kdcFlags = kdcFlags; + _realmName = realmName; + + _key = new EncryptionKey(EncryptionType.getTypeByOrdinal(keyType), key, keyVersionNumber); + } + + public String getCommonName() { + return _commonName; + } + public EncryptionKey getEncryptionKey() { + return _key; + } + public int getKDCFlags() { + return _kdcFlags; + } + public int getMaxLife() { + return _maxLife; + } + public int getMaxRenew() { + return _maxRenew; + } + public KerberosTime getPasswordEnd() { + return _passwordEnd; + } + public KerberosPrincipal getPrincipal() { + return _principal; + } + public String getRealmName() { + return _realmName; + } + public KerberosTime getValidEnd() { + return _validEnd; + } + public KerberosTime getValidStart() { + return _validStart; + } +} + Added: incubator/directory/changepw/trunk/core/src/java/org/apache/kerberos/changepw/store/PasswordStoreEntryModifier.java Url: http://svn.apache.org/viewcvs/incubator/directory/changepw/trunk/core/src/java/org/apache/kerberos/changepw/store/PasswordStoreEntryModifier.java?view=auto&rev=123624 ============================================================================== --- (empty file) +++ incubator/directory/changepw/trunk/core/src/java/org/apache/kerberos/changepw/store/PasswordStoreEntryModifier.java Wed Dec 29 03:13:03 2004 @@ -0,0 +1,86 @@ +/* + * Copyright 2004 The Apache Software Foundation + * + * Licensed 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.kerberos.changepw.store; + +import org.apache.kerberos.messages.value.*; + +import javax.security.auth.kerberos.*; + +public class PasswordStoreEntryModifier { + + // 'Principal' + private String _commonName; + private KerberosPrincipal _principal; + + // 'KDCEntry' + private int _keyVersionNumber; // must + // may + private KerberosTime _validStart; + private KerberosTime _validEnd; + private KerberosTime _passwordEnd; + private int _maxLife; + private int _maxRenew; + private int _kdcFlags; + private int _encryptionType; + private byte[] _key; + + private String _realmName; + + public PasswordStoreEntry getEntry() { + return new PasswordStoreEntry(_commonName, _principal, _keyVersionNumber, + _validStart, _validEnd, _passwordEnd, _maxLife, _maxRenew, + _kdcFlags, _encryptionType, _key, _realmName); + } + + public void setCommonName(String commonName) { + _commonName = commonName; + } + public void setEncryptionType(int encryptionType) { + _encryptionType = encryptionType; + } + public void setKDCFlags(int kdcFlags) { + _kdcFlags = kdcFlags; + } + public void setKey(byte[] key) { + _key = key; + } + public void setKeyVersionNumber(int keyVersionNumber) { + _keyVersionNumber = keyVersionNumber; + } + public void setMaxLife(int maxLife) { + _maxLife = maxLife; + } + public void setMaxRenew(int maxRenew) { + _maxRenew = maxRenew; + } + public void setPasswordEnd(KerberosTime passwordEnd) { + _passwordEnd = passwordEnd; + } + public void setPrincipal(KerberosPrincipal principal) { + _principal = principal; + } + public void setRealmName(String realmName) { + _realmName = realmName; + } + public void setValidEnd(KerberosTime validEnd) { + _validEnd = validEnd; + } + public void setValidStart(KerberosTime validStart) { + _validStart = validStart; + } +} +
