// // Copyright (c) SAS Institute Inc. // // 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 starman import ( "archive/tar" "crypto" "crypto/hmac" "encoding/hex" "encoding/json" "fmt" "io" "os" "strings" ) type TarMD struct { Name string `json:"name"` Arch string `json:"arch"` Version Version `json:"version"` Files []File `json:"files"` FileCheckSumType string `json:"file_checksum_type"` } type Version struct { Epoch string `json:"epoch"` Version string `json:"version"` Release string `json:"release"` } type File struct { Name string `json:"name"` Size int64 `json:"size"` Mode int `json:"mode"` Mtime int `json:"mtime"` Digest string `json:"digest"` Flags int `json:"flags"` UserName string `json:"username"` GroupName string `json:"groupname"` } var hashMap = map[string]crypto.Hash{ "md5": crypto.MD5, "sha1": crypto.SHA1, "sha224": crypto.SHA224, "sha256": crypto.SHA256, "sha384": crypto.SHA384, "sha512": crypto.SHA512, } func (i *starmanInfo) verifyFiles(tr *tar.Reader) error { if err := json.Unmarshal(i.mdblob, &i.md); err != nil { return err } md := &i.md hash := hashMap[md.FileCheckSumType] if !hash.Available() { return fmt.Errorf("unknown digest type %s", md.FileCheckSumType) } else if hash < crypto.SHA224 { return fmt.Errorf("digest type %s is too weak", md.FileCheckSumType) } filemap := make(map[string]*File, len(md.Files)) for i := range md.Files { filemd := &md.Files[i] filemap[filemd.Name] = filemd } for { hdr, err := tr.Next() if err == io.EOF { break } else if err != nil { return err } name := strings.TrimLeft(hdr.Name, ".") filemd := filemap[name] if filemd == nil { return fmt.Errorf("file %s is missing from metadata", name) } delete(filemap, name) if hdr.Typeflag == tar.TypeLink { // has a digest but no contents continue } else if filemd.Digest != "" { d := hash.New() if hdr.FileInfo().Mode()&os.ModeSymlink != 0 { d.Write([]byte(hdr.Linkname)) } else { if _, err := io.Copy(d, tr); err != nil { return err } } sum := d.Sum(nil) expect, err := hex.DecodeString(filemd.Digest) if err != nil { return err } if !hmac.Equal(expect, sum) { return fmt.Errorf("digest mismatch for %s: given %x, calculated %x", name, expect, sum) } } else { switch hdr.FileInfo().Mode() & os.ModeType { case 0, os.ModeSymlink: return fmt.Errorf("file %s has no digest", name) } } } for k := range filemap { return fmt.Errorf("file %s missing from tar", k) } return nil }