/*******************************************************************************
*Copyright (c) 2009 Eucalyptus Systems, Inc.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, only version 3 of the License.
*
*
* This file is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program. If not, see <http://www.gnu.org/licenses/>.
*
* Please contact Eucalyptus Systems, Inc., 130 Castilian
* Dr., Goleta, CA 93101 USA or visit <http://www.eucalyptus.com/licenses/>
* if you need additional information or have any questions.
*
* This file may incorporate work covered under the following copyright and
* permission notice:
*
* Software License Agreement (BSD License)
*
* Copyright (c) 2008, Regents of the University of California
* All rights reserved.
*
* Redistribution and use of this software in source and binary forms, with
* or without modification, are permitted provided that the following
* conditions are met:
*
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
* IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
* OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. USERS OF
* THIS SOFTWARE ACKNOWLEDGE THE POSSIBLE PRESENCE OF OTHER OPEN SOURCE
* LICENSED MATERIAL, COPYRIGHTED MATERIAL OR PATENTED MATERIAL IN THIS
* SOFTWARE, AND IF ANY SUCH MATERIAL IS DISCOVERED THE PARTY DISCOVERING
* IT MAY INFORM DR. RICH WOLSKI AT THE UNIVERSITY OF CALIFORNIA, SANTA
* BARBARA WHO WILL THEN ASCERTAIN THE MOST APPROPRIATE REMEDY, WHICH IN
* THE REGENTS’ DISCRETION MAY INCLUDE, WITHOUT LIMITATION, REPLACEMENT
* OF THE CODE SO IDENTIFIED, LICENSING OF THE CODE SO IDENTIFIED, OR
* WITHDRAWAL OF THE CODE CAPABILITY TO THE EXTENT NEEDED TO COMPLY WITH
* ANY SUCH LICENSES OR RIGHTS.
*******************************************************************************/
/*
*/
package edu.ucsb.eucalyptus.admin.server;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.math.BigInteger;
import java.security.KeyPair;
import java.security.KeyStore;
import java.security.cert.X509Certificate;
import java.util.Calendar;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.log4j.Logger;
import com.eucalyptus.auth.SystemCredentialProvider;
import com.eucalyptus.auth.Users;
import com.eucalyptus.auth.crypto.Certs;
import com.eucalyptus.auth.principal.User;
import com.eucalyptus.auth.util.PEMFiles;
import com.eucalyptus.bootstrap.Component;
import edu.ucsb.eucalyptus.cloud.entities.SystemConfiguration;
public class X509Download extends HttpServlet {
private static Logger LOG = Logger.getLogger( X509Download.class );
public static String NAME_SHORT = "euca2";
public static String PARAMETER_USERNAME = "user";
public static String PARAMETER_KEYNAME = "keyName";
public static String PARAMETER_CODE = "code";
public void doGet( HttpServletRequest request, HttpServletResponse response ) {
String code = request.getParameter( PARAMETER_CODE );
String userName = request.getParameter( PARAMETER_USERNAME );
String keyName = request.getParameter( PARAMETER_KEYNAME );
String mimetype = "application/zip";
Calendar now = Calendar.getInstance( );
keyName = ( keyName == null || "".equals( keyName ) ) ? "default" : keyName;
keyName = userName + String.format( "-%1$ty%1$tm%1$te%1$tk%1$tM%1$tS", now );
if ( userName == null || "".equals( userName ) ) {
hasError( "No user name provided", response );
return;
}
if ( code == null || "".equals( code ) ) {
hasError( "Wrong confirmation code", response );
return;
}
User user = null;
try {
user = Users.lookupUser( userName );
} catch ( Exception e ) {
hasError( "User does not exist", response );
return;
}
if ( !user.checkToken( code ) ) {
hasError( "Confirmation code is invalid", response );
return;
}
response.setContentType( mimetype );
response.setHeader( "Content-Disposition", "attachment; filename=\"" + X509Download.NAME_SHORT + "-" + userName + "-x509.zip\"" );
LOG.info( "pushing out the X509 certificate for user " + userName );
try {
byte[] x509zip = getX509Zip( userName, keyName );
ServletOutputStream op = response.getOutputStream( );
response.setContentLength( x509zip.length );
op.write( x509zip );
op.flush( );
} catch ( Throwable e ) {
LOG.error( e, e );
}
}
public static void hasError( String message, HttpServletResponse response ) {
try {
response.getWriter( ).print( EucalyptusManagement.getError( message ) );
response.getWriter( ).flush( );
} catch ( IOException e ) {
e.printStackTrace( );
}
}
public static byte[] getX509Zip( String userName, String newKeyName ) throws Exception {
X509Certificate cloudCert = null;
final X509Certificate x509;
User u = Users.lookupUser( userName );
String userAccessKey = u.getQueryId( );
String userSecretKey = u.getSecretKey( );
KeyPair keyPair = null;
try {
keyPair = Certs.generateKeyPair( );
x509 = Certs.generateCertificate( keyPair, userName );
x509.checkValidity( );
cloudCert = SystemCredentialProvider.getCredentialProvider( Component.eucalyptus ).getCertificate( );
u.revokeX509Certificate( );
u.setX509Certificate( x509 );
// Transactions.one( new UserEntity( userName ), new Tx<UserEntity>() {
// public void fire( UserEntity user ) throws Throwable {
// user.revokeX509Certificate( );
// user.setCertificate( B64.url.encString( PEMFiles.getBytes( x509 ) ) );
// }});
User now = Users.lookupUser( userName );
LOG.info( "Current user certificate: " + now.getX509Certificate( ) != null ? now.getX509Certificate( ).getSerialNumber( ) : null );
} catch ( Exception e ) {
LOG.fatal( e, e );
throw e;
}
ByteArrayOutputStream byteOut = new ByteArrayOutputStream( );
ZipOutputStream zipOut = new ZipOutputStream( byteOut );
String fingerPrint = Certs.getFingerPrint( keyPair.getPublic( ) );
if ( fingerPrint != null ) {
String baseName = X509Download.NAME_SHORT + "-" + userName + "-" + fingerPrint.replaceAll( ":", "" ).toLowerCase( ).substring( 0, 8 );
zipOut.setComment( "To setup the environment run: source /path/to/eucarc" );
StringBuffer sb = new StringBuffer( );
BigInteger number = Users.lookupUser( userName ).getNumber( );
String userNumber = null;
if ( number != null ) {
userNumber = number.toString( );
}
sb.append( "EUCA_KEY_DIR=$(dirname $(readlink -f ${BASH_SOURCE}))" );
try {
sb.append( "\nexport S3_URL=" + SystemConfiguration.getWalrusUrl( ) );
} catch ( Exception e ) {
sb.append( "\necho WARN: Walrus URL is not configured." );
}
sb.append( "\nexport EC2_URL=" + SystemConfiguration.getCloudUrl( ) );
sb.append( "\nexport EC2_PRIVATE_KEY=${EUCA_KEY_DIR}/" + baseName + "-pk.pem" );
sb.append( "\nexport EC2_CERT=${EUCA_KEY_DIR}/" + baseName + "-cert.pem" );
sb.append( "\nexport EC2_JVM_ARGS=-Djavax.net.ssl.trustStore=${EUCA_KEY_DIR}/jssecacerts" );
sb.append( "\nexport EUCALYPTUS_CERT=${EUCA_KEY_DIR}/cloud-cert.pem" );
sb.append( "\nexport EC2_ACCESS_KEY='" + userAccessKey + "'" );
sb.append( "\nexport EC2_SECRET_KEY='" + userSecretKey + "'" );
if ( userNumber != null ) {
sb.append( "\n# This is a bogus value; Eucalyptus does not need this but client tools do.\nexport EC2_USER_ID='" + userNumber + "'" );
sb.append( "\nalias ec2-bundle-image=\"ec2-bundle-image --cert ${EC2_CERT} --privatekey ${EC2_PRIVATE_KEY} --user " + userNumber
+ " --ec2cert ${EUCALYPTUS_CERT}\"" );
}
sb.append( "\nalias ec2-upload-bundle=\"ec2-upload-bundle -a ${EC2_ACCESS_KEY} -s ${EC2_SECRET_KEY} --url ${S3_URL} --ec2cert ${EUCALYPTUS_CERT}\"" );
sb.append( "\n" );
zipOut.putNextEntry( new ZipEntry( "eucarc" ) );
zipOut.write( sb.toString( ).getBytes( ) );
zipOut.closeEntry( );
/** write the private key to the zip stream **/
zipOut.putNextEntry( new ZipEntry( "cloud-cert.pem" ) );
zipOut.write( PEMFiles.getBytes( cloudCert ) );
zipOut.closeEntry( );
zipOut.putNextEntry( new ZipEntry( "jssecacerts" ) );
KeyStore tempKs = KeyStore.getInstance( "jks" );
tempKs.load( null );
tempKs.setCertificateEntry( "eucalyptus", cloudCert );
ByteArrayOutputStream bos = new ByteArrayOutputStream( );
tempKs.store( bos, "changeit".toCharArray( ) );
zipOut.write( bos.toByteArray( ) );
zipOut.closeEntry( );
/** write the private key to the zip stream **/
zipOut.putNextEntry( new ZipEntry( baseName + "-pk.pem" ) );
zipOut.write( PEMFiles.getBytes( keyPair.getPrivate( ) ) );
zipOut.closeEntry( );
/** write the X509 certificate to the zip stream **/
zipOut.putNextEntry( new ZipEntry( baseName + "-cert.pem" ) );
zipOut.write( PEMFiles.getBytes( x509 ) );
zipOut.closeEntry( );
}
/** close the zip output stream and return the bytes **/
zipOut.close( );
return byteOut.toByteArray( );
}
}