/* * Copyright (c) 2014, Facebook, Inc. * All rights reserved. * * This source code is licensed under the BSD-style license found in the * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * */ #include #include #include #include #include #include #include #include #include #include #include namespace osquery { namespace tables { // Maximum number of files per RPM. #define MAX_RPM_FILES 2048 /** * @brief Return a string representation of the RPM tag type. * * @param header A librpm header. * @param tag A librpm rpmTag_t name. * @param td A librpm rpmtd. * * Given a librpm iterator header and a requested tag name: * 1. Determine the type of the tag (the class of value). * 2. Request a const pointer or cast of numerate to that class. * 3. Lexical-cast the value for SQL. * * @return The string representation of the tag type. */ static std::string getRpmAttribute(const Header& header, rpmTag tag, const rpmtd& td) { std::string result; if (headerGet(header, tag, td, HEADERGET_DEFAULT) == 0) { // Intentional check for a 0 = failure. TLOG << "Could not get RPM header flag."; return result; } if (rpmTagGetClass(tag) == RPM_NUMERIC_CLASS) { long long int attr = rpmtdGetNumber(td); result = BIGINT(attr); } else if (rpmTagGetClass(tag) == RPM_STRING_CLASS) { const char* attr = rpmtdGetString(td); if (attr != nullptr) { result = TEXT(attr); } } return result; } QueryData genRpmPackages(QueryContext& context) { QueryData results; // The following implementation uses http://rpm.org/api/4.11.1/ rpmInitCrypto(); if (rpmReadConfigFiles(nullptr, nullptr) != 0) { TLOG << "Cannot read RPM configuration files."; return results; } rpmts ts = rpmtsCreate(); rpmdbMatchIterator matches; if (context.constraints["name"].exists(EQUALS)) { auto name = (*context.constraints["name"].getAll(EQUALS).begin()); matches = rpmtsInitIterator(ts, RPMTAG_NAME, name.c_str(), name.size()); } else { matches = rpmtsInitIterator(ts, RPMTAG_NAME, nullptr, 0); } Header header; while ((header = rpmdbNextIterator(matches)) != nullptr) { Row r; rpmtd td = rpmtdNew(); r["name"] = getRpmAttribute(header, RPMTAG_NAME, td); r["version"] = getRpmAttribute(header, RPMTAG_VERSION, td); r["release"] = getRpmAttribute(header, RPMTAG_RELEASE, td); r["source"] = getRpmAttribute(header, RPMTAG_SOURCERPM, td); r["size"] = getRpmAttribute(header, RPMTAG_SIZE, td); r["sha1"] = getRpmAttribute(header, RPMTAG_SHA1HEADER, td); r["arch"] = getRpmAttribute(header, RPMTAG_ARCH, td); rpmtdFree(td); results.push_back(r); } rpmdbFreeIterator(matches); rpmtsFree(ts); rpmFreeCrypto(); rpmFreeRpmrc(); return results; } QueryData genRpmPackageFiles(QueryContext& context) { QueryData results; if (rpmReadConfigFiles(nullptr, nullptr) != 0) { TLOG << "Cannot read RPM configuration files."; return results; } rpmts ts = rpmtsCreate(); rpmdbMatchIterator matches; if (context.constraints["package"].exists(EQUALS)) { auto name = (*context.constraints["package"].getAll(EQUALS).begin()); matches = rpmtsInitIterator(ts, RPMTAG_NAME, name.c_str(), name.size()); } else { matches = rpmtsInitIterator(ts, RPMTAG_NAME, nullptr, 0); } Header header; while ((header = rpmdbNextIterator(matches)) != nullptr) { rpmtd td = rpmtdNew(); rpmfi fi = rpmfiNew(ts, header, RPMTAG_BASENAMES, RPMFI_NOHEADER); auto file_count = rpmfiFC(fi); if (file_count <= 0 || file_count > MAX_RPM_FILES) { // This package contains no or too many files. rpmfiFree(fi); continue; } // Iterate over every file in this package. for (size_t i = 0; rpmfiNext(fi) >= 0 && i < file_count; i++) { Row r; r["package"] = getRpmAttribute(header, RPMTAG_NAME, td); auto path = rpmfiFN(fi); r["path"] = (path != nullptr) ? path : ""; auto username = rpmfiFUser(fi); r["username"] = (username != nullptr) ? username : ""; auto groupname = rpmfiFGroup(fi); r["groupname"] = (groupname != nullptr) ? groupname : ""; r["mode"] = lsperms(rpmfiFMode(fi)); r["size"] = BIGINT(rpmfiFSize(fi)); #if defined(CENTOS_CENTOS6) || defined(RHEL_RHEL6) // Older versions of rpmlib/rpmip use a hash algorithm enum. pgpHashAlgo digest_algo; #else int digest_algo; #endif auto digest = rpmfiFDigestHex(fi, &digest_algo); if (digest_algo == PGPHASHALGO_SHA256) { r["sha256"] = (digest != nullptr) ? digest : ""; } results.push_back(r); } rpmfiFree(fi); rpmtdFree(td); } rpmdbFreeIterator(matches); rpmtsFree(ts); rpmFreeRpmrc(); return results; } } }