From a84e2a7da6a27da65b9f521a6daaf172ca0cb04f Mon Sep 17 00:00:00 2001 From: Christoph Wurm Date: Wed, 20 Feb 2019 11:59:17 +0000 Subject: [PATCH] [Auditbeat] Package dataset: Make librpm code compatible across CentOS 6.x, 7.x, and Fedora 29 (#10796) Librpm version 4.14.2.1 on Fedora 29 no longer contains the `headerGetEntry` method we are currently using. It was deprecated and then removed in version 4.14 (https://github.com/rpm-software-management/rpm/commit/c68fa9ab0bac6e3c3a9b826b5a208447ec16da33). Also, the much older version 4.8.0 of librpm on CentOS 6.10 (Final) does not yet contain newer data structures for tags like `rpm_tag_t/rpmTag/rpmTagVal`. This PR makes two changes that should allow this code to work on all three distros (CentOS 6.x, 7.x, Fedora 29 - and hopefully anything in between): 1. Use `headerGetString/headerGetNumber` instead of `headerGetEntry`. 2. Use `int32_t` instead of `rpm_tag_t/rpmTag/rpmTagVal`. Luckily, this seems to work on all three distros. I'd prefer something like a typedef, but unfortunately, C99 does not allow repeating a typedef (C11 does) and so backporting them is not easily possible. It also makes the code more lenient with errors during data collection: Only when no package name can be found do we return an error. Together with https://github.com/elastic/beats/pull/10694 this will hopefully allow RPM package collection to work well. (cherry picked from commit e7ea5d75437dea48f1827de852ccce0afec20d1c) --- .../module/system/package/rpm_linux.go | 110 +++++++----------- 1 file changed, 44 insertions(+), 66 deletions(-) diff --git a/x-pack/auditbeat/module/system/package/rpm_linux.go b/x-pack/auditbeat/module/system/package/rpm_linux.go index 292db1d5ed6..89596b1d785 100644 --- a/x-pack/auditbeat/module/system/package/rpm_linux.go +++ b/x-pack/auditbeat/module/system/package/rpm_linux.go @@ -7,6 +7,7 @@ package pkg import ( + "errors" "fmt" "runtime" "time" @@ -65,28 +66,32 @@ my_headerLink(void *f, Header h) { return headerLink(h); } -int -my_headerGetEntry(void *f, Header h, rpm_tag_t tag, char **p) { - int (*headerGetEntry)(Header, rpm_tag_t, rpm_tagtype_t*, rpm_data_t*, rpm_count_t*); - headerGetEntry = (int (*)(Header, rpm_tag_t, rpm_tagtype_t*, rpm_data_t*, rpm_count_t*))f; +// Note: Using int32_t instead of rpmTag/rpmTagVal in definitions +// to make it work on CentOS 6.x, 7.x, and Fedora 29. +const char * +my_headerGetString(void *f, Header h, int32_t tag) { + const char * (*headerGetString)(Header, int32_t); + headerGetString = (const char * (*)(Header, int32_t))f; - return headerGetEntry(h, tag, NULL, (void**)p, NULL); + return headerGetString(h, tag); } -int -my_headerGetEntryInt(void *f, Header h, rpm_tag_t tag, int **p) { - int (*headerGetEntry)(Header, rpm_tag_t, rpm_tagtype_t*, rpm_data_t*, rpm_count_t*); - headerGetEntry = (int (*)(Header, rpm_tag_t, rpm_tagtype_t*, rpm_data_t*, rpm_count_t*))f; +// Note: Using int32_t instead of rpmTag/rpmTagVal in definitions +// to make it work on CentOS 6.x, 7.x, and Fedora 29. +uint64_t +my_headerGetNumber(void *f, Header h, int32_t tag) { + uint64_t (*headerGetNumber)(Header, int32_t); + headerGetNumber = (uint64_t (*)(Header, int32_t))f; - return headerGetEntry(h, tag, NULL, (void**)p, NULL); + return headerGetNumber(h, tag); } void my_headerFree(void *f, Header h) { - Header (*headerFree)(Header); + Header (*headerFree)(Header); headerFree = (Header (*)(Header))f; - headerFree(h); + headerFree(h); } void @@ -160,7 +165,8 @@ type cFunctions struct { rpmtsInitIterator unsafe.Pointer rpmdbNextIterator unsafe.Pointer headerLink unsafe.Pointer - headerGetEntry unsafe.Pointer + headerGetString unsafe.Pointer + headerGetNumber unsafe.Pointer headerFree unsafe.Pointer rpmdbFreeIterator unsafe.Pointer rpmtsFree unsafe.Pointer @@ -206,7 +212,12 @@ func dlopenCFunctions() (*cFunctions, error) { return nil, err } - cFun.headerGetEntry, err = librpm.GetSymbolPointer("headerGetEntry") + cFun.headerGetString, err = librpm.GetSymbolPointer("headerGetString") + if err != nil { + return nil, err + } + + cFun.headerGetNumber, err = librpm.GetSymbolPointer("headerGetNumber") if err != nil { return nil, err } @@ -306,65 +317,32 @@ func packageFromHeader(header C.Header, cFun *cFunctions) (*Package, error) { pkg := Package{} - var name *C.char - res := C.my_headerGetEntry(cFun.headerGetEntry, header, RPMTAG_NAME, &name) - if res != 1 { - return nil, fmt.Errorf("Failed to call headerGetEntry(name): %d", res) - } - pkg.Name = C.GoString(name) - - var version *C.char - res = C.my_headerGetEntry(cFun.headerGetEntry, header, RPMTAG_VERSION, &version) - if res != 1 { - return nil, fmt.Errorf("Failed to call headerGetEntry(version): %d", res) + name := C.my_headerGetString(cFun.headerGetString, header, RPMTAG_NAME) + if name != nil { + pkg.Name = C.GoString(name) + } else { + return nil, errors.New("Failed to get package name") } - pkg.Version = C.GoString(version) - var release *C.char - res = C.my_headerGetEntry(cFun.headerGetEntry, header, RPMTAG_RELEASE, &release) - if res != 1 { - return nil, fmt.Errorf("Failed to call headerGetEntry(release): %d", res) + version := C.my_headerGetString(cFun.headerGetString, header, RPMTAG_VERSION) + if version != nil { + pkg.Version = C.GoString(version) + } else { + pkg.Error = errors.New("Failed to get package version") } - pkg.Release = C.GoString(release) - var license *C.char - res = C.my_headerGetEntry(cFun.headerGetEntry, header, RPMTAG_LICENSE, &license) - if res != 1 { - return nil, fmt.Errorf("Failed to call headerGetEntry(license): %d", res) - } - pkg.License = C.GoString(license) - - var arch *C.char - res = C.my_headerGetEntry(cFun.headerGetEntry, header, RPMTAG_ARCH, &arch) - if res == 1 { // not always successful - pkg.Arch = C.GoString(arch) - } - - var url *C.char - res = C.my_headerGetEntry(cFun.headerGetEntry, header, RPMTAG_URL, &url) - if res == 1 { // not always successful - pkg.URL = C.GoString(url) - } - - var summary *C.char - res = C.my_headerGetEntry(cFun.headerGetEntry, header, RPMTAG_SUMMARY, &summary) - if res == 1 { // not always successful - pkg.Summary = C.GoString(summary) - } + pkg.Release = C.GoString(C.my_headerGetString(cFun.headerGetString, header, RPMTAG_RELEASE)) + pkg.License = C.GoString(C.my_headerGetString(cFun.headerGetString, header, RPMTAG_LICENSE)) + pkg.Arch = C.GoString(C.my_headerGetString(cFun.headerGetString, header, RPMTAG_ARCH)) + pkg.URL = C.GoString(C.my_headerGetString(cFun.headerGetString, header, RPMTAG_URL)) + pkg.Summary = C.GoString(C.my_headerGetString(cFun.headerGetString, header, RPMTAG_SUMMARY)) - var size *C.int - res = C.my_headerGetEntryInt(cFun.headerGetEntry, header, RPMTAG_SIZE, &size) - if res != 1 { - return nil, fmt.Errorf("Failed to call headerGetEntry(size): %d", res) - } - pkg.Size = uint64(*size) + pkg.Size = uint64(C.my_headerGetNumber(cFun.headerGetNumber, header, RPMTAG_SIZE)) - var installTime *C.int - res = C.my_headerGetEntryInt(cFun.headerGetEntry, header, RPMTAG_INSTALLTIME, &installTime) - if res != 1 { - return nil, fmt.Errorf("Failed to call headerGetEntry(installTime): %d", res) + installTime := C.my_headerGetNumber(cFun.headerGetNumber, header, RPMTAG_INSTALLTIME) + if installTime != 0 { + pkg.InstallTime = time.Unix(int64(installTime), 0) } - pkg.InstallTime = time.Unix(int64(*installTime), 0) return &pkg, nil }