github.com/kata-containers/runtime@v0.0.0-20210505125100-04f29832a923/virtcontainers/types/asset.go (about)

     1  // Copyright (c) 2017 Intel Corporation
     2  //
     3  // SPDX-License-Identifier: Apache-2.0
     4  //
     5  
     6  package types
     7  
     8  import (
     9  	"crypto/sha512"
    10  	"encoding/hex"
    11  	"fmt"
    12  	"io/ioutil"
    13  	"path/filepath"
    14  
    15  	"github.com/kata-containers/runtime/virtcontainers/pkg/annotations"
    16  )
    17  
    18  // AssetType describe a type of assets.
    19  type AssetType string
    20  
    21  const (
    22  	// KernelAsset is a kernel asset.
    23  	KernelAsset AssetType = "kernel"
    24  
    25  	// ImageAsset is an image asset.
    26  	ImageAsset AssetType = "image"
    27  
    28  	// InitrdAsset is an initrd asset.
    29  	InitrdAsset AssetType = "initrd"
    30  
    31  	// HypervisorAsset is an hypervisor asset.
    32  	HypervisorAsset AssetType = "hypervisor"
    33  
    34  	// HypervisorCtlAsset is a hypervisor control asset.
    35  	HypervisorCtlAsset AssetType = "hypervisorctl"
    36  
    37  	// JailerAsset is a jailer asset.
    38  	JailerAsset AssetType = "jailer"
    39  
    40  	// FirmwareAsset is a firmware asset.
    41  	FirmwareAsset AssetType = "firmware"
    42  )
    43  
    44  // AssetTypes returns a list of all known asset types.
    45  //
    46  // XXX: New asset types *MUST* be added here.
    47  func AssetTypes() []AssetType {
    48  	return []AssetType{
    49  		FirmwareAsset,
    50  		HypervisorAsset,
    51  		HypervisorCtlAsset,
    52  		ImageAsset,
    53  		InitrdAsset,
    54  		JailerAsset,
    55  		KernelAsset,
    56  	}
    57  }
    58  
    59  // AssetAnnotations returns all annotations for all asset types.
    60  func AssetAnnotations() ([]string, error) {
    61  	var result []string
    62  
    63  	for _, at := range AssetTypes() {
    64  		aPath, aHash, err := at.Annotations()
    65  		if err != nil {
    66  			return []string{}, err
    67  		}
    68  
    69  		result = append(result, []string{aPath, aHash}...)
    70  	}
    71  
    72  	return result, nil
    73  }
    74  
    75  // Annotations returns the path and hash annotations for a given Asset type.
    76  func (t AssetType) Annotations() (string, string, error) {
    77  	switch t {
    78  	case KernelAsset:
    79  		return annotations.KernelPath, annotations.KernelHash, nil
    80  	case ImageAsset:
    81  		return annotations.ImagePath, annotations.ImageHash, nil
    82  	case InitrdAsset:
    83  		return annotations.InitrdPath, annotations.InitrdHash, nil
    84  	case HypervisorAsset:
    85  		return annotations.HypervisorPath, annotations.HypervisorHash, nil
    86  	case HypervisorCtlAsset:
    87  		return annotations.HypervisorCtlPath, annotations.HypervisorCtlHash, nil
    88  	case JailerAsset:
    89  		return annotations.JailerPath, annotations.JailerHash, nil
    90  	case FirmwareAsset:
    91  		return annotations.FirmwarePath, annotations.FirmwareHash, nil
    92  	}
    93  
    94  	return "", "", fmt.Errorf("Wrong asset type %s", t)
    95  }
    96  
    97  // Asset represents a virtcontainers asset.
    98  type Asset struct {
    99  	path         string
   100  	computedHash string
   101  	kind         AssetType
   102  }
   103  
   104  // Path returns an asset path.
   105  func (a Asset) Path() string {
   106  	return a.path
   107  }
   108  
   109  // Type returns an asset type.
   110  func (a Asset) Type() AssetType {
   111  	return a.kind
   112  }
   113  
   114  // Valid checks if an asset is valid or not.
   115  func (a *Asset) Valid() bool {
   116  	if !filepath.IsAbs(a.path) {
   117  		return false
   118  	}
   119  
   120  	for _, at := range AssetTypes() {
   121  		if at == a.kind {
   122  			return true
   123  		}
   124  	}
   125  
   126  	return false
   127  }
   128  
   129  // Hash returns the hex encoded string for the asset hash
   130  func (a *Asset) Hash(hashType string) (string, error) {
   131  	var hashEncodedLen int
   132  	var hash string
   133  
   134  	// We read the actual asset content
   135  	bytes, err := ioutil.ReadFile(a.path)
   136  	if err != nil {
   137  		return "", err
   138  	}
   139  
   140  	if len(bytes) == 0 {
   141  		return "", fmt.Errorf("Empty asset file at %s", a.path)
   142  	}
   143  
   144  	// Build the asset hash and convert it to a string.
   145  	// We only support SHA512 for now.
   146  	switch hashType {
   147  	case annotations.SHA512:
   148  		hashComputed := sha512.Sum512(bytes)
   149  		hashEncodedLen = hex.EncodedLen(len(hashComputed))
   150  		hashEncoded := make([]byte, hashEncodedLen)
   151  		hex.Encode(hashEncoded, hashComputed[:])
   152  		hash = string(hashEncoded[:])
   153  	default:
   154  		return "", fmt.Errorf("Invalid hash type %s", hashType)
   155  	}
   156  
   157  	a.computedHash = hash
   158  
   159  	return hash, nil
   160  }
   161  
   162  // NewAsset returns a new asset from a slice of annotations.
   163  func NewAsset(anno map[string]string, t AssetType) (*Asset, error) {
   164  	pathAnnotation, hashAnnotation, err := t.Annotations()
   165  	if err != nil {
   166  		return nil, err
   167  	}
   168  
   169  	if pathAnnotation == "" || hashAnnotation == "" {
   170  		return nil, fmt.Errorf("Missing annotation paths for %s", t)
   171  	}
   172  
   173  	path, ok := anno[pathAnnotation]
   174  	if !ok || path == "" {
   175  		return nil, nil
   176  	}
   177  
   178  	if !filepath.IsAbs(path) {
   179  		return nil, fmt.Errorf("%s is not an absolute path", path)
   180  	}
   181  
   182  	a := &Asset{path: path, kind: t}
   183  
   184  	hash, ok := anno[hashAnnotation]
   185  	if !ok || hash == "" {
   186  		return a, nil
   187  	}
   188  
   189  	// We have a hash annotation, we need to verify the asset against it.
   190  	hashType, ok := anno[annotations.AssetHashType]
   191  	if !ok {
   192  		hashType = annotations.SHA512
   193  	}
   194  
   195  	hashComputed, err := a.Hash(hashType)
   196  	if err != nil {
   197  		return a, err
   198  	}
   199  
   200  	// If our computed asset hash does not match the passed annotation, we must exit.
   201  	if hashComputed != hash {
   202  		return nil, fmt.Errorf("Invalid hash for %s: computed %s, expecting %s]", a.path, hashComputed, hash)
   203  	}
   204  
   205  	return a, nil
   206  }