1
/*******************************************************************************
2
*Copyright (c) 2009 Eucalyptus Systems, Inc.
4
* This program is free software: you can redistribute it and/or modify
5
* it under the terms of the GNU General Public License as published by
6
* the Free Software Foundation, only version 3 of the License.
9
* This file is distributed in the hope that it will be useful, but WITHOUT
10
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14
* You should have received a copy of the GNU General Public License along
15
* with this program. If not, see <http://www.gnu.org/licenses/>.
17
* Please contact Eucalyptus Systems, Inc., 130 Castilian
18
* Dr., Goleta, CA 93101 USA or visit <http://www.eucalyptus.com/licenses/>
19
* if you need additional information or have any questions.
21
* This file may incorporate work covered under the following copyright and
24
* Software License Agreement (BSD License)
26
* Copyright (c) 2008, Regents of the University of California
27
* All rights reserved.
29
* Redistribution and use of this software in source and binary forms, with
30
* or without modification, are permitted provided that the following
33
* Redistributions of source code must retain the above copyright notice,
34
* this list of conditions and the following disclaimer.
36
* Redistributions in binary form must reproduce the above copyright
37
* notice, this list of conditions and the following disclaimer in the
38
* documentation and/or other materials provided with the distribution.
40
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
41
* IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
42
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
43
* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
44
* OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
45
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
46
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
47
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
48
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
49
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
50
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. USERS OF
51
* THIS SOFTWARE ACKNOWLEDGE THE POSSIBLE PRESENCE OF OTHER OPEN SOURCE
52
* LICENSED MATERIAL, COPYRIGHTED MATERIAL OR PATENTED MATERIAL IN THIS
53
* SOFTWARE, AND IF ANY SUCH MATERIAL IS DISCOVERED THE PARTY DISCOVERING
54
* IT MAY INFORM DR. RICH WOLSKI AT THE UNIVERSITY OF CALIFORNIA, SANTA
55
* BARBARA WHO WILL THEN ASCERTAIN THE MOST APPROPRIATE REMEDY, WHICH IN
56
* THE REGENTS' DISCRETION MAY INCLUDE, WITHOUT LIMITATION, REPLACEMENT
57
* OF THE CODE SO IDENTIFIED, LICENSING OF THE CODE SO IDENTIFIED, OR
58
* WITHDRAWAL OF THE CODE CAPABILITY TO THE EXTENT NEEDED TO COMPLY WITH
59
* ANY SUCH LICENSES OR RIGHTS.
60
*******************************************************************************/
62
* Author: chris grzegorczyk <grze@eucalyptus.com>
64
package com.eucalyptus.images;
66
import java.security.PublicKey;
67
import java.security.Signature;
68
import java.security.cert.X509Certificate;
69
import java.util.ArrayList;
70
import java.util.List;
71
import java.util.zip.Adler32;
72
import javax.xml.xpath.XPath;
73
import javax.xml.xpath.XPathConstants;
74
import javax.xml.xpath.XPathExpressionException;
75
import javax.xml.xpath.XPathFactory;
76
import org.apache.log4j.Logger;
77
import org.w3c.dom.DOMException;
78
import org.w3c.dom.Document;
79
import org.w3c.dom.NodeList;
80
import com.eucalyptus.auth.Accounts;
81
import com.eucalyptus.auth.AuthException;
82
import com.eucalyptus.auth.util.Hashes;
83
import com.eucalyptus.blockstorage.WalrusUtil;
84
import com.eucalyptus.cloud.ImageMetadata;
85
import com.eucalyptus.cloud.ImageMetadata.Architecture;
86
import com.eucalyptus.cloud.ImageMetadata.StaticDiskImage;
87
import com.eucalyptus.component.ComponentIds;
88
import com.eucalyptus.component.id.Eucalyptus;
89
import com.eucalyptus.context.Context;
90
import com.eucalyptus.context.Contexts;
91
import com.eucalyptus.crypto.Crypto;
92
import com.eucalyptus.entities.EntityWrapper;
93
import com.eucalyptus.util.EucalyptusCloudException;
94
import com.eucalyptus.util.FullName;
95
import com.eucalyptus.util.RestrictedTypes;
96
import com.google.common.collect.Lists;
97
import edu.ucsb.eucalyptus.msgs.BlockDeviceMappingItemType;
98
import edu.ucsb.eucalyptus.msgs.GetBucketAccessControlPolicyResponseType;
99
import edu.ucsb.eucalyptus.msgs.LaunchPermissionItemType;
100
import edu.ucsb.eucalyptus.msgs.ModifyImageAttributeType;
101
import edu.ucsb.eucalyptus.msgs.RegisterImageType;
103
public class ImageUtil {
104
private static Logger LOG = Logger.getLogger( ImageUtil.class );
106
public static String newImageId( final String imagePrefix, final String imageLocation ) {
107
EntityWrapper<ImageInfo> db = EntityWrapper.get( ImageInfo.class );
108
String testId = Crypto.generateId( imageLocation, imagePrefix );
109
ImageInfo query = Images.exampleWithImageId( testId );
110
LOG.info( "Trying to lookup using created AMI id=" + query.getDisplayName( ) );
111
for ( ; db.query( query ).size( ) != 0; query.setDisplayName( Crypto.generateId( imageLocation, imagePrefix ) ) );
113
LOG.info( "Assigning imageId=" + query.getDisplayName( ) );
114
return query.getDisplayName( );
117
public static boolean verifyManifestSignature( final X509Certificate cert, final String signature, String pad ) {
118
Signature sigVerifier;
120
sigVerifier = Signature.getInstance( "SHA1withRSA" );
121
PublicKey publicKey = cert.getPublicKey( );
122
sigVerifier.initVerify( publicKey );
123
sigVerifier.update( ( pad ).getBytes( ) );
124
return sigVerifier.verify( Hashes.hexToBytes( signature ) );
125
} catch ( Exception ex ) {
131
public static ArrayList<String> getAncestors( String userId, String manifestPath ) {
132
ArrayList<String> ancestorIds = Lists.newArrayList( );
134
String[] imagePathParts = manifestPath.split( "/" );
135
Document inputSource = WalrusUtil.getManifestData( ComponentIds.lookup( Eucalyptus.class ).getFullName( ), imagePathParts[0], imagePathParts[1] );
136
XPath xpath = XPathFactory.newInstance( ).newXPath( );
137
NodeList ancestors = null;
139
ancestors = ( NodeList ) xpath.evaluate( "/manifest/image/ancestry/ancestor_ami_id/text()", inputSource, XPathConstants.NODESET );
140
if ( ancestors == null ) return ancestorIds;
141
for ( int i = 0; i < ancestors.getLength( ); i++ ) {
142
for ( String ancestorId : ancestors.item( i ).getNodeValue( ).split( "," ) ) {
143
ancestorIds.add( ancestorId );
146
} catch ( XPathExpressionException e ) {
149
} catch ( EucalyptusCloudException e ) {
151
} catch ( DOMException e ) {
157
public static Long getSize( String manifestPath ) {
160
String[] imagePathParts = manifestPath.split( "/" );
161
Document inputSource = WalrusUtil.getManifestData( ComponentIds.lookup( Eucalyptus.class ).getFullName( ), imagePathParts[0], imagePathParts[1] );
162
XPath xpath = XPathFactory.newInstance( ).newXPath( );
163
String rootSize = "0";
165
rootSize = ( String ) xpath.evaluate( "/manifest/image/size/text()", inputSource, XPathConstants.STRING );
167
size = Long.parseLong( rootSize );
168
} catch ( NumberFormatException e ) {
171
} catch ( XPathExpressionException e ) {
174
} catch ( EucalyptusCloudException e ) {
180
public static void checkStoredImage( final ImageMetadata.StaticDiskImage imgInfo ) throws EucalyptusCloudException {
181
if ( imgInfo != null ) try {
182
Document inputSource = null;
184
String[] imagePathParts = imgInfo.getManifestLocation( ).split( "/" );
185
inputSource = WalrusUtil.getManifestData( imgInfo.getOwner( ), imagePathParts[0], imagePathParts[1] );
186
} catch ( EucalyptusCloudException e ) {
190
xpath = XPathFactory.newInstance( ).newXPath( );
191
String signature = null;
193
signature = ( String ) xpath.evaluate( "/manifest/signature/text()", inputSource, XPathConstants.STRING );
194
} catch ( XPathExpressionException e ) {}
195
if ( imgInfo.getSignature( ) != null && !imgInfo.getSignature( ).equals( signature ) ) throw new EucalyptusCloudException( "Manifest signature has changed since registration." );
196
LOG.info( "Triggering caching: " + imgInfo.getManifestLocation( ) );
198
if( imgInfo instanceof ImageMetadata.StaticDiskImage ) {
199
WalrusUtil.triggerCaching( ( StaticDiskImage ) imgInfo );
201
} catch ( Exception e ) {}
202
} catch ( EucalyptusCloudException e ) {
204
LOG.error( "Failed bukkit check! Invalidating registration: " + imgInfo.getManifestLocation( ) );
205
//TODO: we need to consider if this is a good semantic or not, it can have ugly side effects
206
// invalidateImageById( imgInfo.getImageId() );
207
throw new EucalyptusCloudException( "Failed check! Invalidating registration: " + imgInfo.getManifestLocation( ) );
211
public static boolean isSet( String id ) {
212
return id != null && !"".equals( id );
216
private static boolean userHasImagePermission( final UserInfo user, final ImageInfo img ) {
218
if ( !user.getUserName( ).equals( img.getImageOwnerId( ) )
219
&& !Users.lookupUser( user.getUserName( ) ).isAdministrator( ) && !img.getPermissions( ).contains( user ) ) return true;
220
} catch ( NoSuchUserException e ) {
226
// private static void invalidateImageById( String searchId ) throws EucalyptusCloudException {
227
// EntityWrapper<ImageInfo> db = EntityWrapper.get( ImageInfo.class );
228
// if ( isSet( searchId ) ) try {
229
// ImageInfo img = db.getUnique( Images.exampleWithImageId( searchId ) );
230
// WalrusUtil.invalidate( img );
232
// } catch ( EucalyptusCloudException e ) {
234
// throw new EucalyptusCloudException( "Failed to find registered image with id " + searchId, e );
238
public static ImageInfo getImageInfobyId( String searchId ) throws EucalyptusCloudException {
239
EntityWrapper<ImageInfo> db = EntityWrapper.get( ImageInfo.class );
240
if ( isSet( searchId ) ) try {
241
ImageInfo imgInfo = db.getUnique( Images.exampleWithImageId( searchId ) );
244
} catch ( EucalyptusCloudException e ) {
247
throw new EucalyptusCloudException( "Failed to find registered image with id " + searchId, e );
248
} catch ( Exception t ) {
252
LOG.error( "Failed to find registered image with id " + searchId );
253
throw new EucalyptusCloudException( "Failed to find registered image with id " + searchId );
256
public static String getImageInfobyId( String userSuppliedId, String imageDefaultId, String systemDefaultId ) {
257
String searchId = null;
258
if ( isSet( userSuppliedId ) )
259
searchId = userSuppliedId;
260
else if ( isSet( imageDefaultId ) )
261
searchId = imageDefaultId;
262
else if ( isSet( systemDefaultId ) ) searchId = systemDefaultId;
266
public static BlockDeviceMappingItemType EMI = new BlockDeviceMappingItemType( "emi", "sda1" );
267
public static BlockDeviceMappingItemType EPHEMERAL = new BlockDeviceMappingItemType( "ephemeral0", "sda2" );
268
public static BlockDeviceMappingItemType SWAP = new BlockDeviceMappingItemType( "swap", "sda3" );
269
public static BlockDeviceMappingItemType ROOT = new BlockDeviceMappingItemType( "root", "/dev/sda1" );
271
public static Architecture extractArchitecture( Document inputSource, XPath xpath ) {
274
arch = ( String ) xpath.evaluate( "/manifest/machine_configuration/architecture/text()", inputSource, XPathConstants.STRING );
275
} catch ( XPathExpressionException e ) {
276
LOG.warn( e.getMessage( ) );
278
String architecture = ( ( arch == null )
281
return ImageMetadata.Architecture.valueOf( architecture );
284
public static String extractRamdiskId( Document inputSource, XPath xpath ) {
285
String ramdiskId = null;
287
ramdiskId = ( String ) xpath.evaluate( "/manifest/machine_configuration/ramdisk_id/text()", inputSource, XPathConstants.STRING );
288
} catch ( XPathExpressionException e ) {
289
LOG.warn( e.getMessage( ) );
291
if ( !isSet( ramdiskId ) ) ramdiskId = null;
295
public static String extractKernelId( Document inputSource, XPath xpath ) {
296
String kernelId = null;
298
kernelId = ( String ) xpath.evaluate( "/manifest/machine_configuration/kernel_id/text()", inputSource, XPathConstants.STRING );
299
} catch ( XPathExpressionException e ) {
300
LOG.warn( e.getMessage( ) );
302
if ( !isSet( kernelId ) ) kernelId = null;
306
public static void checkBucketAcl( RegisterImageType request, String[] imagePathParts ) throws EucalyptusCloudException {
307
String userName = null;
308
Context ctx = Contexts.lookup( );
309
if ( !ctx.hasAdministrativePrivileges( ) ) {
310
GetBucketAccessControlPolicyResponseType reply = WalrusUtil.getBucketAcl( request, imagePathParts );
311
if ( reply != null ) {
312
if ( !Contexts.lookup( ).getUserFullName( ).getUserId( ).equals( reply.getAccessControlPolicy( ).getOwner( ).getDisplayName( ) ) ) throw new EucalyptusCloudException(
313
"Image registration failed: you must own the bucket containing the image." );
314
userName = reply.getAccessControlPolicy( ).getOwner( ).getDisplayName( );
319
public static void applyImageAttributes( final EntityWrapper<ImageInfo> db, final ImageInfo imgInfo, final List<LaunchPermissionItemType> changeList, final boolean adding ) throws EucalyptusCloudException {
320
for ( LaunchPermissionItemType perm : changeList ) {
321
if ( perm.isGroup( ) ) {
324
//TODO:GRZE:RESTORE imgInfo.grantPermission( new ImageUserGroup( perm.getGroup( ) ) );
326
//TODO:GRZE:RESTORE imgInfo.revokePermission( new ImageUserGroup( perm.getGroup( ) ) );
328
} catch ( Exception e ) {
330
throw new EucalyptusCloudException( "Modify image attribute failed because of: " + e.getMessage( ) );
332
} else if ( perm.isUser( ) ) {
335
imgInfo.grantPermission( Accounts.lookupAccountById( perm.getUserId( ) ) );
337
imgInfo.revokePermission( Accounts.lookupAccountById( perm.getUserId( ) ) );
339
} catch ( AuthException e ) {
341
throw new EucalyptusCloudException( "Modify image attribute failed because of: " + e.getMessage( ) );
347
public static boolean modifyImageInfo( ModifyImageAttributeType request ) {
348
final String imageId = request.getImageId( );
349
final List<LaunchPermissionItemType> addList = request.getAdd( );
350
final List<LaunchPermissionItemType> remList = request.getRemove( );
351
EntityWrapper<ImageInfo> db = EntityWrapper.get( ImageInfo.class );
352
ImageInfo imgInfo = null;
354
imgInfo = db.getUnique( Images.exampleWithImageId( imageId ) );
355
} catch ( EucalyptusCloudException e ) {
359
if ( RestrictedTypes.filterPrivileged( ).apply( imgInfo ) ) {
363
applyImageAttributes( db, imgInfo, addList, true );
364
applyImageAttributes( db, imgInfo, remList, false );
367
} catch ( EucalyptusCloudException e ) {
374
public static Document getManifestDocument( String[] imagePathParts, FullName userName ) throws EucalyptusCloudException {
375
Document inputSource = null;
377
inputSource = WalrusUtil.getManifestData( userName, imagePathParts[0], imagePathParts[1] );
378
} catch ( EucalyptusCloudException e ) {
384
public static void cleanDeregistered( ) {
385
EntityWrapper<ImageInfo> db = EntityWrapper.get( ImageInfo.class );
387
List<ImageInfo> imgList = db.query( Images.exampleWithImageState( ImageMetadata.State.deregistered ) );
388
for ( ImageInfo deregImg : imgList ) {
390
db.delete( deregImg );
391
} catch ( Exception e1 ) {}
394
} catch ( Exception e1 ) {
399
public static int countByAccount( String accountId ) throws AuthException {
400
EntityWrapper<ImageInfo> db = EntityWrapper.get( ImageInfo.class );
402
List<ImageInfo> images = db.query( new ImageInfo( ) );
404
for ( ImageInfo img : images ) {
405
if ( img.getOwnerAccountNumber( ).equals( accountId ) ) {
411
} catch ( Exception e ) {
413
throw new AuthException( "Image database query failed", e );
417
public static int countByUser( String userId ) throws AuthException {
418
EntityWrapper<ImageInfo> db = EntityWrapper.get( ImageInfo.class );
420
List<ImageInfo> images = db.query( new ImageInfo( ) );
422
for ( ImageInfo img : images ) {
423
if ( img.getOwnerUserId( ).equals( userId ) ) {
429
} catch ( Exception e ) {
431
throw new AuthException( "Image database query failed", e );