rpmutils

package module
v0.4.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: May 7, 2024 License: Apache-2.0 Imports: 25 Imported by: 55

README

Go RPM Utils

Go Reference

go-rpmutils is a library written in go for parsing and extracting content from RPMs.

Overview

go-rpmutils provides a few interfaces for handling RPM packages. There is a highlevel Rpm struct that provides access to the RPM header and CPIO payload. The CPIO payload can be extracted to a filesystem location via the ExpandPayload function or through a Reader interface, similar to the tar implementation in the go standard library.

Example

// Opening a RPM file
f, err := os.Open("foo.rpm")
if err != nil {
    panic(err)
}
rpm, err := rpmutils.ReadRpm(f)
if err != nil {
    panic(err)
}
// Getting metadata
nevra, err := rpm.Header.GetNEVRA()
if err != nil {
    panic(err)
}
fmt.Println(nevra)
provides, err := rpm.Header.GetStrings(rpmutils.PROVIDENAME)
if err != nil {
    panic(err)
}
fmt.Println("Provides:")
for _, p := range provides {
    fmt.Println(p)
}
// Extracting payload
if err := rpm.ExpandPayload("destdir"); err != nil {
    panic(err)
}

Validating Signatures

rpmutils supports validating PGP signatures embedded in RPM files.

import (
    "github.com/sassoftware/go-rpmutils"
    "github.com/ProtonMail/go-crypto/openpgp"
)

func main() {
    kf, err := os.Open("trusted.pgp")
    keyring, err := openpgp.ReadArmoredKeyRing(kf)
    f, err := os.Open("foo.rpm")
    hdr, sigs, err := rpmutils.Verify(f, keyring)
}

Passing nil as the keyring will parse the signature without validating it, so that the signers' key ID can be inspected.

By default rpmutils uses the ProtonMail PGP implementation, which supports PGP v4 and later signatures. PGP v4 was released in 1998, and yet some still-supported Linux distributions contain RPMs with v3 signatures.

Depending on your needs you may want to use the pgpkeys-eu soft fork, which re-adds v3 signature support. To consume it, the binary being built must have a replace directive, and must set the pgp3 tag to enable the related validation code in rpmutils:

go mod edit -replace github.com/ProtonMail/go-crypto=github.com/pgpkeys-eu/go-crypto@main
go build -tags pgp3
Upgrading from versions before v0.4.0

Previous versions of rpmutils used the standard library golang.org/x/crypto/openpgp implementation, which has been deprecated for some time. Most callers that are verifying or signing RPMs will just need to change imports to github.com/ProtonMail/go-crypto/openpgp .

There are two known regressions with the ProtonMail implementation. The first is that PGP v3 signatures are no longer supported. If this is important to you, then see the above note about using the pgpkeys-eu fork instead.

The second is that signing with a HSM-bound private key (crypto.Signer) of type other than RSA is currently not supported by ProtonMail. Hopefully a future release will restore this functionality.

Contributing

  1. Read contributor agreement
  2. Fork it
  3. Create your feature branch (git checkout -b my-new-feature)
  4. Commit your changes (git commit -a). Make sure to include a Signed-off-by line per the contributor agreement.
  5. Push to the branch (git push origin my-new-feature)
  6. Create new Pull Request

License

go-rpmutils is released under the Apache 2.0 license. See LICENSE.

Documentation

Index

Constants

View Source
const (
	PGPHASHALGO_MD5         = 1  // MD5
	PGPHASHALGO_SHA1        = 2  // SHA1
	PGPHASHALGO_RIPEMD160   = 3  // RIPEMD160
	PGPHASHALGO_MD2         = 5  // MD2
	PGPHASHALGO_TIGER192    = 6  // TIGER192
	PGPHASHALGO_HAVAL_5_160 = 7  // HAVAL-5-160
	PGPHASHALGO_SHA256      = 8  // SHA256
	PGPHASHALGO_SHA384      = 9  // SHA384
	PGPHASHALGO_SHA512      = 10 // SHA512
	PGPHASHALGO_SHA224      = 11 // SHA224
)

PGP Hash Algorithms

View Source
const (
	RPM_NULL_TYPE         = 0
	RPM_CHAR_TYPE         = 1
	RPM_INT8_TYPE         = 2
	RPM_INT16_TYPE        = 3
	RPM_INT32_TYPE        = 4
	RPM_INT64_TYPE        = 5
	RPM_STRING_TYPE       = 6
	RPM_BIN_TYPE          = 7
	RPM_STRING_ARRAY_TYPE = 8
	RPM_I18NSTRING_TYPE   = 9
)

tag data types

View Source
const (
	NAME              = 1000
	VERSION           = 1001
	RELEASE           = 1002
	EPOCH             = 1003
	SUMMARY           = 1004
	DESCRIPTION       = 1005
	BUILDTIME         = 1006
	BUILDHOST         = 1007
	SIZE              = 1009
	DISTRIBUTION      = 1010
	VENDOR            = 1011
	GIF               = 1012
	XPM               = 1013
	LICENSE           = 1014
	PACKAGER          = 1015
	GROUP             = 1016
	CHANGELOG         = 1017
	SOURCE            = 1018
	PATCH             = 1019
	URL               = 1020
	OS                = 1021
	ARCH              = 1022
	PREIN             = 1023
	POSTIN            = 1024
	PREUN             = 1025
	POSTUN            = 1026
	OLDFILENAMES      = 1027
	FILESIZES         = 1028
	FILEMODES         = 1030
	FILERDEVS         = 1033
	FILEMTIMES        = 1034
	FILEDIGESTS       = 1035 // AKA FILEMD5S
	FILELINKTOS       = 1036
	FILEFLAGS         = 1037 // bitmask: RPMFILE_* are bitmasks to interpret
	FILEUSERNAME      = 1039
	FILEGROUPNAME     = 1040
	ICON              = 1043
	SOURCERPM         = 1044
	FILEVERIFYFLAGS   = 1045 // bitmask: RPMVERIFY_* are bitmasks to interpret
	ARCHIVESIZE       = 1046
	PROVIDENAME       = 1047
	REQUIREFLAGS      = 1048
	REQUIRENAME       = 1049
	REQUIREVERSION    = 1050
	CONFLICTFLAGS     = 1053
	CONFLICTNAME      = 1054
	CONFLICTVERSION   = 1055
	RPMVERSION        = 1064
	TRIGGERSCRIPTS    = 1065
	TRIGGERNAME       = 1066
	TRIGGERVERSION    = 1067
	TRIGGERFLAGS      = 1068 // bitmask: RPMSENSE_* are bitmasks to interpret
	TRIGGERINDEX      = 1069
	VERIFYSCRIPT      = 1079
	CHANGELOGTIME     = 1080
	CHANGELOGNAME     = 1081
	CHANGELOGTEXT     = 1082
	PREINPROG         = 1085
	POSTINPROG        = 1086
	PREUNPROG         = 1087
	POSTUNPROG        = 1088
	OBSOLETENAME      = 1090
	FILEDEVICES       = 1095
	FILEINODES        = 1096
	PROVIDEFLAGS      = 1112
	PROVIDEVERSION    = 1113
	OBSOLETEFLAGS     = 1114
	OBSOLETEVERSION   = 1115
	VERIFYSCRIPTPROG  = 1091
	TRIGGERSCRIPTPROG = 1092
	DIRINDEXES        = 1116
	BASENAMES         = 1117
	DIRNAMES          = 1118
	PAYLOADFORMAT     = 1124
	PAYLOADCOMPRESSOR = 1125
	FILECOLORS        = 1140
	// BLINK*, FLINK*, and TRIGGERPREIN included from SUSE fork of RPM
	BLINKPKGID   = 1164
	BLINKHDRID   = 1165
	BLINKNEVRA   = 1166
	FLINKPKGID   = 1167
	FLINKHDRID   = 1168
	FLINKNEVRA   = 1169
	TRIGGERPREIN = 1170

	LONGFILESIZES  = 5008
	LONGSIZE       = 5009
	FILECAPS       = 5010
	FILEDIGESTALGO = 5011
	BUGURL         = 5012
	VCS            = 5034
	ENCODING       = 5062

	PAYLOADDIGEST     = 5092
	PAYLOADDIGESTALGO = 5093
)

RPM header tags found in the general header

View Source
const (
	SIG_BASE            = 256
	SIG_DSA             = SIG_BASE + 11 // DSA signature over header only
	SIG_RSA             = SIG_BASE + 12 // RSA signature over header only
	SIG_SHA1            = SIG_BASE + 13 // SHA1 over header only (hex)
	SIG_LONGSIGSIZE     = SIG_BASE + 14 // header + compressed payload (uint64)
	SIG_LONGARCHIVESIZE = SIG_BASE + 15 // uncompressed payload bytes (uint64)
	SIG_SHA256          = SIG_BASE + 17 // SHA256 over header only (hex)

	SIG_SIZE          = _SIGHEADER_TAG_BASE + 1000 // Header + Payload size
	SIG_PGP           = _SIGHEADER_TAG_BASE + 1002 // Signature over header + payload
	SIG_MD5           = _SIGHEADER_TAG_BASE + 1004 // MD5SUM of header + payload
	SIG_GPG           = _SIGHEADER_TAG_BASE + 1005 // (same as SIG_PGP)
	SIG_PAYLOADSIZE   = _SIGHEADER_TAG_BASE + 1007 // uncompressed payload bytes (uint32)
	SIG_RESERVEDSPACE = _SIGHEADER_TAG_BASE + 1008 // blank space that can be replaced by a signature
)

RPM header tags found in the signature header

View Source
const (
	RPMFILE_NONE      = 0
	RPMFILE_CONFIG    = 1 << 0
	RPMFILE_DOC       = 1 << 1
	RPMFILE_ICON      = 1 << 2
	RPMFILE_MISSINGOK = 1 << 3
	RPMFILE_NOREPLACE = 1 << 4
	RPMFILE_SPECFILE  = 1 << 5
	RPMFILE_GHOST     = 1 << 6
	RPMFILE_LICENSE   = 1 << 7
	RPMFILE_README    = 1 << 8
	RPMFILE_EXCLUDE   = 1 << 9
	RPMFILE_UNPATCHED = 1 << 10
	RPMFILE_PUBKEY    = 1 << 11
	RPMFILE_POLICY    = 1 << 12
)

FILEFLAGS bitmask elements

View Source
const (
	RPMVERIFY_NONE       = 0
	RPMVERIFY_MD5        = 1 << 0
	RPMVERIFY_FILEDIGEST = 1 << 0
	RPMVERIFY_FILESIZE   = 1 << 1
	RPMVERIFY_LINKTO     = 1 << 2
	RPMVERIFY_USER       = 1 << 3
	RPMVERIFY_GROUP      = 1 << 4
	RPMVERIFY_MTIME      = 1 << 5
	RPMVERIFY_MODE       = 1 << 6
	RPMVERIFY_RDEV       = 1 << 7
	RPMVERIFY_CAPS       = 1 << 8
	RPMVERIFY_CONTEXTS   = 1 << 15
)

FILEVERIFYFLAGS bitmask elements

View Source
const (
	RPMSENSE_ANY           = 0
	RPMSENSE_LESS          = 1 << 1
	RPMSENSE_GREATER       = 1 << 2
	RPMSENSE_EQUAL         = 1 << 3
	RPMSENSE_TRIGGERIN     = 1 << 16
	RPMSENSE_TRIGGERUN     = 1 << 17
	RPMSENSE_TRIGGERPOSTUN = 1 << 18
	RPMSENSE_TRIGGERPREIN  = 1 << 25
)

TRIGGERFLAGS bitmask elements -- not all rpmsenseFlags make sense in TRIGGERFLAGS

View Source
const (
	RPMTAG_HEADERSIGNATURES = 62
	RPMTAG_HEADERIMMUTABLE  = 63
	RPMTAG_HEADERREGIONS    = 64
)

Header region tags

View Source
const (
	HASH_MD5         = 1
	HASH_SHA1        = 2
	HASH_RIPEMD160   = 3
	HASH_MD2         = 5
	HASH_TIGER192    = 6
	HASH_HAVAL_5_160 = 7
	HASH_SHA256      = 8
	HASH_SHA384      = 9
	HASH_SHA512      = 10
	HASH_SHA224      = 11
)

Crypto algos

Variables

View Source
var (
	R_NONALNUMTILDE = regexp.MustCompile(`^([^a-zA-Z0-9~]*)(.*)$`)
	R_NUM           = regexp.MustCompile(`^([\d]+)(.*)$`)
	R_ALPHA         = regexp.MustCompile(`^([a-zA-Z]+)(.*)$`)
)
View Source
var (
	ErrNoPGPSignature  = errors.New("no supported PGP signature packet found")
	ErrTrailingGarbage = errors.New("trailing garbage after PGP signature packet")
)

Functions

func GetFileAlgoName

func GetFileAlgoName(algo int) string

GetFileAlgoName returns the name of a digest algorithm

func NEVRAcmp

func NEVRAcmp(a NEVRA, b NEVRA) int

NEVRAcmp compares two RPM versions. It returns -1 if a < b, 1 if a > b, and 0 if a == b

func Vercmp

func Vercmp(first, second string) int

Vercmp compares two version strings using the same algorithm as rpm uses. Returns -1 if first < second, 1 if first > second, and 0 if first == second.

func Verify

func Verify(stream io.Reader, knownKeys openpgp.EntityList) (header *RpmHeader, sigs []*Signature, err error)

Verify the PGP signature over a RPM file. knownKeys should enumerate public keys to check against, otherwise the signature validity cannot be verified. If knownKeys is nil then digests will be checked but only the raw key ID will be available.

Types

type FileInfo

type FileInfo interface {
	Name() string
	Size() int64
	UserName() string
	GroupName() string
	Flags() int
	Mtime() int
	Digest() string
	Mode() int
	Linkname() string
	Device() int
	Inode() int
}

FileInfo describes a file in the RPM payload

type HeaderRange added in v0.2.0

type HeaderRange struct {
	// Start is the byte offset of the signature header
	Start int
	// End is the byte offset of the end of the general header and start of the payload
	End int
}

HeaderRange indicates the byte offsets that the RPM header spans

type KeyNotFoundError added in v0.4.0

type KeyNotFoundError struct {
	KeyID       uint64
	Fingerprint []byte
}

func (KeyNotFoundError) Error added in v0.4.0

func (e KeyNotFoundError) Error() string

type NEVRA

type NEVRA struct {
	Name    string
	Epoch   string
	Version string
	Release string
	Arch    string
}

NEVRA holds the name, epoch, version, release and arch of the RPM, which uniquely identifies it

func (*NEVRA) String

func (nevra *NEVRA) String() string

type NEVRASlice

type NEVRASlice []NEVRA

NEVRASlice is used to sort a list of NEVRAs and implements sort.Interface

func (NEVRASlice) Len

func (s NEVRASlice) Len() int

func (NEVRASlice) Less

func (s NEVRASlice) Less(i, j int) bool

func (NEVRASlice) Sort

func (s NEVRASlice) Sort()

Sort a list of NEVRAs in-place

func (NEVRASlice) Swap

func (s NEVRASlice) Swap(i, j int)

type NoSuchTagError

type NoSuchTagError struct {
	Tag int
}

NoSuchTagError is returned when a tag does not exist in the header

func NewNoSuchTagError

func NewNoSuchTagError(tag int) NoSuchTagError

NewNoSuchTagError creates a NoSuchTagError for a given tag

func (NoSuchTagError) Error

func (err NoSuchTagError) Error() string

type PayloadReader

type PayloadReader interface {
	Next() (FileInfo, error)
	Read([]byte) (int, error)
	IsLink() bool
}

PayloadReader is used to sequentially access the file contents of a RPM payload

type Rpm

type Rpm struct {
	Header *RpmHeader
	// contains filtered or unexported fields
}

Rpm is an open RPM header and payload

func ReadRpm

func ReadRpm(f io.Reader) (*Rpm, error)

ReadRpm reads the header from a RPM file and prepares to read payload contents

func (*Rpm) ExpandPayload

func (rpm *Rpm) ExpandPayload(dest string) error

ExpandPayload extracts the payload of a RPM to the specified directory

func (*Rpm) PayloadReader

func (rpm *Rpm) PayloadReader() (*cpio.Reader, error)

PayloadReader accesses the payload cpio archive within the RPM.

DEPRECATED: Use PayloadReaderExtended instead in order to handle files larger than 4GiB.

func (*Rpm) PayloadReaderExtended

func (rpm *Rpm) PayloadReaderExtended() (PayloadReader, error)

PayloadReaderExtended accesses payload file contents sequentially

type RpmHeader

type RpmHeader struct {
	// contains filtered or unexported fields
}

RpmHeader holds the signature header and general header of a RPM

Tags are drawn from both header areas, with IDs between _GENERAL_TAG_BASE and _SIGHEADER_TAG_BASE coming from the general header and all others coming from the signature header.

func ReadHeader

func ReadHeader(f io.Reader) (*RpmHeader, error)

ReadHeader reads the signature and general headers from a RPM.

The stream is positioned for reading the compressed payload following the headers.

func RewriteWithSignatures

func RewriteWithSignatures(infile *os.File, outpath string, sigPgp, sigRsa []byte) (*RpmHeader, error)

RewriteWithSignatures inserts raw signatures into a RPM header.

DEPRECATED: To perform a detached signature, use SignRpmStream and call DumpSignatureHeader to export the result.

func SignRpmFile

func SignRpmFile(infile *os.File, outpath string, key *packet.PrivateKey, opts *SignatureOptions) (header *RpmHeader, err error)

SignRpmFile signs infile and writes it to outpath, which may be the same file

func SignRpmStream

func SignRpmStream(stream io.Reader, key *packet.PrivateKey, opts *SignatureOptions) (header *RpmHeader, err error)

SignRpmStream reads an RPM and signs it, returning the set of headers updated with the new signature.

func (*RpmHeader) DumpSignatureHeader

func (hdr *RpmHeader) DumpSignatureHeader(sameSize bool) ([]byte, error)

DumpSignatureHeader dumps the lead and signature header, optionally adding or changing padding to make it the same size as when it was originally read. Otherwise padding is removed to make it as small as possible.

A RPM can be signed by removing the first OriginalSignatureHeaderSize() bytes of the file and replacing it with the result of DumpSignatureHeader().

func (*RpmHeader) Get

func (hdr *RpmHeader) Get(tag int) (interface{}, error)

Get the value of a tag. Returns whichever type most closely represents how the tag was stored, or NoSuchTagError if the tag was not found. If tag is OLDFILENAMES, special handling is provided to splice together DIRNAMES and BASENAMES if it is not present.

func (*RpmHeader) GetBytes

func (hdr *RpmHeader) GetBytes(tag int) ([]byte, error)

GetBytes gets a tag as a byte array.

func (*RpmHeader) GetFiles

func (hdr *RpmHeader) GetFiles() ([]FileInfo, error)

GetFiles returns an array of FileInfo objects holding file-related attributes held in parallel arrays of tags

func (*RpmHeader) GetInt

func (hdr *RpmHeader) GetInt(tag int) (int, error)

GetInt gets an integer using the default 'int' type.

DEPRECATED: large int32s and int64s can overflow. Use GetUint32s or GetUint64s instead.

func (*RpmHeader) GetInts

func (hdr *RpmHeader) GetInts(tag int) ([]int, error)

GetInts gets an integer array using the default 'int' type.

DEPRECATED: large int32s and int64s can overflow. Use GetUint32s or GetUint64s instead.

func (*RpmHeader) GetNEVRA

func (hdr *RpmHeader) GetNEVRA() (*NEVRA, error)

GetNEVRA gets the name, epoch, version, release and arch of the RPM.

func (*RpmHeader) GetRange added in v0.2.0

func (hdr *RpmHeader) GetRange() HeaderRange

GetRange returns the byte offsets that the RPM header spans within the original RPM file

func (*RpmHeader) GetString

func (hdr *RpmHeader) GetString(tag int) (string, error)

GetString returns the value of a tag holding a single string

func (*RpmHeader) GetStrings

func (hdr *RpmHeader) GetStrings(tag int) ([]string, error)

GetStrings fetches the given tag holding a string or array of strings. If tag is OLDFILENAMES, special handling is provided to splice together DIRNAMES and BASENAMES if it is not present.

func (*RpmHeader) GetUint32s added in v0.2.0

func (hdr *RpmHeader) GetUint32s(tag int) ([]uint32, error)

GetUint32s gets an int array as a uint32 slice. This can accomodate any int type other than INT64. Returns an error in case of overflow.

func (*RpmHeader) GetUint64Fallback

func (hdr *RpmHeader) GetUint64Fallback(intTag, longTag int) (uint64, error)

GetUint64Fallback gets longTag if it exists, otherwise intTag, and returns the value as an array of uint64s. This can accomodate all int types and is normally used when a int32 tag was later replaced with a int64 tag.

func (*RpmHeader) GetUint64s added in v0.2.0

func (hdr *RpmHeader) GetUint64s(tag int) ([]uint64, error)

GetUint64s gets an int array as a uint64 slice. This can accomodate all int types

func (*RpmHeader) HasTag

func (hdr *RpmHeader) HasTag(tag int) bool

HasTag returns true if the given tag exists in the header

func (*RpmHeader) InstalledSize

func (hdr *RpmHeader) InstalledSize() (int64, error)

InstalledSize returns the approximate disk space needed to install the package

func (*RpmHeader) OriginalSignatureHeaderSize

func (hdr *RpmHeader) OriginalSignatureHeaderSize() int

OriginalSignatureHeaderSize returns the size of the lead and signature header area as originally read from the file.

func (*RpmHeader) PayloadSize

func (hdr *RpmHeader) PayloadSize() (int64, error)

PayloadSize returns the size of the uncompressed payload in bytes

type Signature

type Signature struct {
	// Signer is the PGP identity that created the signature. It may be nil if
	// the public key is not available at verification time, but KeyId will
	// always be set.
	Signer *openpgp.Entity
	// Hash is the algorithm used to digest the signature contents
	Hash crypto.Hash
	// CreationTime is when the signature was created
	CreationTime time.Time
	// HeaderOnly is true for signatures that only cover the general RPM header,
	// and false for signatures that cover the general header plus the payload
	HeaderOnly bool
	// KeyId is the PGP key that created the signature.
	KeyId uint64
	// KeyFingerprint is the fingerprint of the public key that created the
	// signature, if available.
	KeyFingerprint []byte
	// PrimaryName is the primary identity of the signing key, if available.
	PrimaryName string
	// contains filtered or unexported fields
}

Signature describes a PGP signature found within a RPM while verifying it.

type SignatureOptions

type SignatureOptions struct {
	// Hash algorithm for the signature. If not set, defaults to SHA-256
	Hash crypto.Hash
	// CreationTime for the signature. If not set, defaults to the current time
	CreationTime time.Time
}

SignatureOptions describes additional configuration for SignRpm methods

type VersionSlice

type VersionSlice []string

VersionSlice provides the Sort interface for sorting version strings.

func (VersionSlice) Len

func (vs VersionSlice) Len() int

Len is the number of elements in the collection.

func (VersionSlice) Less

func (vs VersionSlice) Less(i, j int) bool

Less reports wheather the element with index i should sort before the element with index j.

func (VersionSlice) Swap

func (vs VersionSlice) Swap(i, j int)

Swap swaps the elements with indexes i and j.

Directories

Path Synopsis
pmpgp module

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL