Annotation of embedaddon/strongswan/src/libimcv/swima/swima_collector.c, revision 1.1.1.1

1.1       misho       1: /*
                      2:  * Copyright (C) 2017 Andreas Steffen
                      3:  * HSR Hochschule fuer Technik Rapperswil
                      4:  *
                      5:  * This program is free software; you can redistribute it and/or modify it
                      6:  * under the terms of the GNU General Public License as published by the
                      7:  * Free Software Foundation; either version 2 of the License, or (at your
                      8:  * option) any later version.  See <http://www.fsf.org/copyleft/gpl.txt>.
                      9:  *
                     10:  * This program is distributed in the hope that it will be useful, but
                     11:  * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
                     12:  * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
                     13:  * for more details.
                     14:  */
                     15: 
                     16: #define _GNU_SOURCE /* for asprintf() */
                     17: 
                     18: #include "swima_collector.h"
                     19: 
                     20: #include <swid_gen/swid_gen.h>
                     21: 
                     22: #include <collections/linked_list.h>
                     23: #include <utils/debug.h>
                     24: 
                     25: #include <stdio.h>
                     26: #include <fcntl.h>
                     27: #include <unistd.h>
                     28: #include <sys/stat.h>
                     29: #include <libgen.h>
                     30: #include <errno.h>
                     31: 
                     32: #define SOURCE_ID_GENERATOR            1
                     33: #define SOURCE_ID_COLLECTOR            2
                     34: 
                     35: #ifndef SWID_DIRECTORY
                     36: #define SWID_DIRECTORY NULL
                     37: #endif
                     38: 
                     39: /**
                     40:  * Directories to be skipped by collector
                     41:  */
                     42: static const char* skip_directories[] = {
                     43:        "/usr/share/doc",
                     44:        "/usr/share/help",
                     45:        "/usr/share/icons",
                     46:        "/usr/share/gnome/help"
                     47: };
                     48: 
                     49: typedef struct private_swima_collector_t private_swima_collector_t;
                     50: 
                     51: /**
                     52:  * Private data of a swima_collector_t object.
                     53:  *
                     54:  */
                     55: struct private_swima_collector_t {
                     56: 
                     57:        /**
                     58:         * Public swima_collector_t interface.
                     59:         */
                     60:        swima_collector_t public;
                     61: 
                     62:        /**
                     63:         * Collect Software Identifiers only
                     64:         */
                     65:        bool sw_id_only;
                     66: 
                     67:        /**
                     68:         * Software Collector Database [if it exists]
                     69:         */
                     70:        database_t *db;
                     71: 
                     72:        /**
                     73:         * List of Software [Identifier] records
                     74:         */
                     75:        swima_inventory_t *inventory;
                     76: 
                     77:        /**
                     78:         * List of Software [Identifier] events
                     79:         */
                     80:        swima_events_t *events;
                     81: 
                     82: };
                     83: 
                     84: /**
                     85:  * Extract Software Identifier from SWID tag
                     86:  */
                     87: static status_t extract_sw_id(chunk_t swid_tag, chunk_t *sw_id)
                     88: {
                     89:        char *pos, *tag, *tagid, *regid;
                     90:        size_t len, tagid_len, regid_len;
                     91:        status_t status = NOT_FOUND;
                     92: 
                     93:        /* Copy at most 1023 bytes of the SWID tag and null-terminate it */
                     94:        len = min(1023, swid_tag.len);
                     95:        pos = tag = strndup(swid_tag.ptr, len);
                     96: 
                     97:        tagid= strstr(pos, "tagId=\"");
                     98:        if (tagid == NULL)
                     99:        {
                    100:                goto end;
                    101:        }
                    102:        tagid += 7;
                    103:        len -= tagid - pos - 7;
                    104: 
                    105:        pos = strchr(tagid, '"');
                    106:        if (pos == NULL)
                    107:        {
                    108:                goto end;
                    109:        }
                    110:        tagid_len = pos - tagid;
                    111: 
                    112:        regid= strstr(pos, "regid=\"");
                    113:        if (regid == NULL)
                    114:        {
                    115:                goto end;
                    116:        }
                    117:        regid += 7;
                    118:        len -= regid - pos - 7;
                    119: 
                    120:        pos = strchr(regid, '"');
                    121:        if (pos == NULL)
                    122:        {
                    123:                goto end;
                    124:        }
                    125:        regid_len = pos - regid;
                    126: 
                    127:        *sw_id = chunk_cat("ccc", chunk_create(regid, regid_len),
                    128:                                                          chunk_from_chars('_','_'),
                    129:                                                          chunk_create(tagid, tagid_len));
                    130:        status = SUCCESS;
                    131: end:
                    132:        free(tag);
                    133: 
                    134:        return status;
                    135: }
                    136: 
                    137: static status_t retrieve_inventory(private_swima_collector_t *this,
                    138:                                                                   swima_inventory_t *targets)
                    139: {
                    140:        char *name;
                    141:        uint32_t record_id, source;
                    142:        swima_record_t *sw_record;
                    143:        chunk_t sw_id;
                    144:        enumerator_t *e;
                    145: 
                    146:        /* Retrieve complete software identifier inventory */
                    147:        e = this->db->query(this->db,
                    148:                        "SELECT id, name, source FROM sw_identifiers WHERE installed = 1 "
                    149:                        "ORDER BY name ASC", DB_UINT, DB_TEXT, DB_UINT);
                    150:        if (!e)
                    151:        {
                    152:                DBG1(DBG_IMC, "database query for installed sw_identifiers failed");
                    153:                return FAILED;
                    154:        }
                    155:        while (e->enumerate(e, &record_id, &name, &source))
                    156:        {
                    157:                sw_id = chunk_from_str(name);
                    158:                sw_record = swima_record_create(record_id, sw_id, chunk_empty);
                    159:                sw_record->set_source_id(sw_record, source);
                    160:                this->inventory->add(this->inventory, sw_record);
                    161:        }
                    162:        e->destroy(e);
                    163: 
                    164:        return SUCCESS;
                    165: }
                    166: 
                    167: static status_t retrieve_events(private_swima_collector_t *this,
                    168:                                                                swima_inventory_t *targets)
                    169: {
                    170:        enumerator_t *e;
                    171:        char *name, *timestamp;
                    172:        uint32_t record_id, source, action, eid, earliest_eid;
                    173:        chunk_t sw_id, ev_ts;
                    174:        swima_record_t *sw_record;
                    175:        swima_event_t *sw_event;
                    176: 
                    177:        earliest_eid = targets->get_eid(targets, NULL);
                    178: 
                    179:        /* Retrieve complete software identifier inventory */
                    180:        e = this->db->query(this->db,
                    181:                "SELECT e.id, e.timestamp, i.id, i.name, i.source, s.action "
                    182:                "FROM sw_events as s JOIN events AS e ON s.eid = e.id "
                    183:                "JOIN sw_identifiers as i ON s.sw_id = i.id WHERE s.eid >= ?"
                    184:                "ORDER BY s.eid, i.name, s.action ASC", DB_UINT, earliest_eid,
                    185:                 DB_UINT, DB_TEXT, DB_UINT, DB_TEXT, DB_UINT, DB_UINT);
                    186:        if (!e)
                    187:        {
                    188:                DBG1(DBG_IMC, "database query for sw_events failed");
                    189:                return FAILED;
                    190:        }
                    191:        while (e->enumerate(e, &eid, &timestamp, &record_id, &name, &source, &action))
                    192:        {
                    193:                sw_id = chunk_from_str(name);
                    194:                ev_ts = chunk_from_str(timestamp);
                    195:                sw_record = swima_record_create(record_id, sw_id, chunk_empty);
                    196:                sw_record->set_source_id(sw_record, source);
                    197:                sw_event = swima_event_create(eid, ev_ts, action, sw_record);
                    198:                this->events->add(this->events, sw_event);
                    199:        }
                    200:        e->destroy(e);
                    201: 
                    202:        return SUCCESS;
                    203: }
                    204: 
                    205: static status_t generate_tags(private_swima_collector_t *this,
                    206:                                                          swima_inventory_t *targets, bool pretty, bool full)
                    207: {
                    208:        swid_gen_t *swid_gen;
                    209:        swima_record_t *target, *sw_record;
                    210:        enumerator_t *enumerator;
                    211:        status_t status = SUCCESS;
                    212: 
                    213:        swid_gen = swid_gen_create();
                    214: 
                    215:        if (targets->get_count(targets) == 0)
                    216:        {
                    217:                chunk_t out, sw_id, swid_tag = chunk_empty;
                    218: 
                    219:                DBG2(DBG_IMC, "SWID tag%s generation by package manager",
                    220:                                           this->sw_id_only ? " ID" : "");
                    221: 
                    222:                enumerator = swid_gen->create_tag_enumerator(swid_gen, this->sw_id_only,
                    223:                                                                                                         full, pretty);
                    224:                if (enumerator)
                    225:                {
                    226:                        while (enumerator->enumerate(enumerator, &out))
                    227:                        {
                    228:                                if (this->sw_id_only)
                    229:                                {
                    230:                                        sw_id = out;
                    231:                                }
                    232:                                else
                    233:                                {
                    234:                                        swid_tag = out;
                    235:                                        status = extract_sw_id(swid_tag, &sw_id);
                    236:                                        if (status != SUCCESS)
                    237:                                        {
                    238:                                                DBG1(DBG_IMC, "software id could not be extracted "
                    239:                                                                          "from tag");
                    240:                                                chunk_free(&swid_tag);
                    241:                                                break;
                    242:                                        }
                    243:                                }
                    244:                                sw_record = swima_record_create(0, sw_id, chunk_empty);
                    245:                                sw_record->set_source_id(sw_record, SOURCE_ID_GENERATOR);
                    246:                                if (!this->sw_id_only)
                    247:                                {
                    248:                                        sw_record->set_record(sw_record, swid_tag);
                    249:                                        chunk_free(&swid_tag);
                    250:                                }
                    251:                                this->inventory->add(this->inventory, sw_record);
                    252:                                chunk_free(&sw_id);
                    253:                        }
                    254:                        enumerator->destroy(enumerator);
                    255:                }
                    256:                else
                    257:                {
                    258:                        status = NOT_SUPPORTED;
                    259:                }
                    260:        }
                    261:        else if (!this->sw_id_only)
                    262:        {
                    263:                DBG2(DBG_IMC, "targeted SWID tag generation");
                    264: 
                    265:                enumerator = targets->create_enumerator(targets);
                    266:                while (enumerator->enumerate(enumerator, &target))
                    267:                {
                    268:                        swima_record_t *sw_record;
                    269:                        char *tag = NULL, *name, *package, *version;
                    270:                        u_int installed;
                    271:                        chunk_t sw_id;
                    272:                        enumerator_t *e;
                    273: 
                    274:                        sw_id = target->get_sw_id(target, NULL);
                    275:                        name = strndup(sw_id.ptr, sw_id.len);
                    276: 
                    277:                        if (this->db)
                    278:                        {
                    279:                                e = this->db->query(this->db,
                    280:                                                "SELECT package, version, installed "
                    281:                                                "FROM sw_identifiers WHERE name = ?", DB_TEXT, name,
                    282:                                                 DB_TEXT, DB_TEXT, DB_UINT);
                    283:                                if (!e)
                    284:                                {
                    285:                                        DBG1(DBG_IMC, "database query for sw_identifiers failed");
                    286:                                        status = FAILED;
                    287:                                        free(name);
                    288:                                        break;
                    289:                                }
                    290:                                if (e->enumerate(e, &package, &version, &installed))
                    291:                                {
                    292:                                        tag = swid_gen->generate_tag(swid_gen, name, package,
                    293:                                                                        version, full && installed, pretty);
                    294:                                }
                    295:                                e->destroy(e);
                    296:                        }
                    297:                        else
                    298:                        {
                    299:                                tag = swid_gen->generate_tag(swid_gen, name, NULL, NULL,
                    300:                                                                                         full, pretty);
                    301:                        }
                    302:                        free(name);
                    303: 
                    304:                        if (tag)
                    305:                        {
                    306:                                DBG2(DBG_IMC, "  %.*s", sw_id.len, sw_id.ptr);
                    307:                                sw_record = swima_record_create(0, sw_id, chunk_empty);
                    308:                                sw_record->set_source_id(sw_record, SOURCE_ID_GENERATOR);
                    309:                                sw_record->set_record(sw_record, chunk_from_str(tag));
                    310:                                this->inventory->add(this->inventory, sw_record);
                    311:                                free(tag);
                    312:                        }
                    313:                }
                    314:                enumerator->destroy(enumerator);
                    315:        }
                    316:        swid_gen->destroy(swid_gen);
                    317: 
                    318:        return status;
                    319: }
                    320: 
                    321: static bool collect_tags(private_swima_collector_t *this, char *pathname,
                    322:                                                 swima_inventory_t *targets, bool is_swidtag_dir)
                    323: {
                    324:        char *rel_name, *abs_name, *suffix, *pos, *uri;
                    325:        chunk_t *swid_tag, sw_id, sw_locator;
                    326:        swima_record_t *sw_record;
                    327:        struct stat st;
                    328:        bool success = FALSE, skip, is_new_swidtag_dir;
                    329:        enumerator_t *enumerator;
                    330:        int i;
                    331: 
                    332:        if (!pathname)
                    333:        {
                    334:                return TRUE;
                    335:        }
                    336: 
                    337:        enumerator = enumerator_create_directory(pathname);
                    338:        if (!enumerator)
                    339:        {
                    340:                DBG1(DBG_IMC, "directory '%s' can not be opened, %s",
                    341:                                           pathname, strerror(errno));
                    342:                return FALSE;
                    343:        }
                    344: 
                    345:        while (enumerator->enumerate(enumerator, &rel_name, &abs_name, &st))
                    346:        {
                    347:                if (S_ISDIR(st.st_mode))
                    348:                {
                    349:                        skip = FALSE;
                    350: 
                    351:                        for (i = 0; i < countof(skip_directories); i++)
                    352:                        {
                    353:                                if (streq(abs_name, skip_directories[i]))
                    354:                                {
                    355:                                        skip = TRUE;
                    356:                                        break;
                    357:                                }
                    358:                        }
                    359: 
                    360:                        if (skip)
                    361:                        {
                    362:                                continue;
                    363:                        }
                    364: 
                    365:                        is_new_swidtag_dir =  streq(rel_name, "swidtag");
                    366:                        if (is_new_swidtag_dir)
                    367:                        {
                    368:                                DBG2(DBG_IMC, "entering %s", pathname);
                    369:                        }
                    370:                        if (!collect_tags(this, abs_name, targets, is_swidtag_dir ||
                    371:                                                                                                           is_new_swidtag_dir))
                    372:                        {
                    373:                                goto end;
                    374:                        }
                    375:                        if (is_new_swidtag_dir)
                    376:                        {
                    377:                                DBG2(DBG_IMC, "leaving %s", pathname);
                    378:                        }
                    379:                }
                    380: 
                    381:                if (!is_swidtag_dir)
                    382:                {
                    383:                        continue;
                    384:                }
                    385: 
                    386:                /* found a swidtag file? */
                    387:                suffix = strstr(rel_name, ".swidtag");
                    388:                if (!suffix)
                    389:                {
                    390:                        continue;
                    391:                }
                    392: 
                    393:                /* load the swidtag file */
                    394:                swid_tag = chunk_map(abs_name, FALSE);
                    395:                if (!swid_tag)
                    396:                {
                    397:                        DBG1(DBG_IMC, "  opening '%s' failed: %s", abs_name,
                    398:                                                  strerror(errno));
                    399:                        goto end;
                    400:                }
                    401: 
                    402:                /* extract software identity from SWID tag */
                    403:                if (extract_sw_id(*swid_tag, &sw_id) != SUCCESS)
                    404:                {
                    405:                        DBG1(DBG_IMC, "software id could not be extracted from SWID tag");
                    406:                        chunk_unmap(swid_tag);
                    407:                        goto end;
                    408:                }
                    409: 
                    410:                /* In case of a targeted request */
                    411:                if (targets->get_count(targets))
                    412:                {
                    413:                        enumerator_t *target_enumerator;
                    414:                        swima_record_t *target;
                    415:                        bool match = FALSE;
                    416: 
                    417:                        target_enumerator = targets->create_enumerator(targets);
                    418:                        while (target_enumerator->enumerate(target_enumerator, &target))
                    419:                        {
                    420:                                if (chunk_equals(target->get_sw_id(target, NULL), sw_id))
                    421:                                {
                    422:                                        DBG2(DBG_IMC, "  %.*s", sw_id.len, sw_id.ptr);
                    423:                                        match = TRUE;
                    424:                                        break;
                    425:                                }
                    426:                        }
                    427:                        target_enumerator->destroy(target_enumerator);
                    428: 
                    429:                        if (!match)
                    430:                        {
                    431:                                chunk_unmap(swid_tag);
                    432:                                chunk_free(&sw_id);
                    433:                                continue;
                    434:                        }
                    435:                }
                    436:                DBG2(DBG_IMC, "  %s", rel_name);
                    437: 
                    438:                sw_locator = chunk_empty;
                    439:                pos = strstr(pathname, "/swidtag");
                    440:                if (pos &&
                    441:                        asprintf(&uri, "file://%.*s", (int)(pos - pathname), pathname) > 0)
                    442:                {
                    443:                        sw_locator = chunk_from_str(uri);
                    444:                }
                    445:                sw_record = swima_record_create(0, sw_id, sw_locator);
                    446:                sw_record->set_source_id(sw_record, SOURCE_ID_COLLECTOR);
                    447:                if (!this->sw_id_only)
                    448:                {
                    449:                        sw_record->set_record(sw_record, *swid_tag);
                    450:                }
                    451:                this->inventory->add(this->inventory, sw_record);
                    452: 
                    453:                chunk_unmap(swid_tag);
                    454:                chunk_free(&sw_id);
                    455:                chunk_free(&sw_locator);
                    456:        }
                    457:        success = TRUE;
                    458: 
                    459: end:
                    460:        enumerator->destroy(enumerator);
                    461: 
                    462:        return success;
                    463: }
                    464: 
                    465: METHOD(swima_collector_t, collect_inventory, swima_inventory_t*,
                    466:        private_swima_collector_t *this, bool sw_id_only, swima_inventory_t *targets)
                    467: {
                    468:        bool pretty, full;
                    469:        char *directory;
                    470:        status_t status;
                    471: 
                    472:        directory = lib->settings->get_str(lib->settings,
                    473:                                                                        "%s.plugins.imc-swima.swid_directory",
                    474:                                                                         SWID_DIRECTORY, lib->ns);
                    475:        pretty = lib->settings->get_bool(lib->settings,
                    476:                                                                        "%s.plugins.imc-swima.swid_pretty",
                    477:                                                                         FALSE, lib->ns);
                    478:        full = lib->settings->get_bool(lib->settings,
                    479:                                                                        "%s.plugins.imc-swima.swid_full",
                    480:                                                                         FALSE, lib->ns);
                    481: 
                    482:        /**
                    483:         * Re-initialize collector
                    484:         */
                    485:        this->sw_id_only = sw_id_only;
                    486:        this->inventory->clear(this->inventory);
                    487: 
                    488:        /**
                    489:         * Source 1: Tags are generated by a package manager
                    490:         */
                    491:        if (sw_id_only && this->db)
                    492:        {
                    493:                status = retrieve_inventory(this, targets);
                    494:        }
                    495:        else
                    496:        {
                    497:                status = generate_tags(this, targets, pretty, full);
                    498:        }
                    499: 
                    500:        /**
                    501:         * Source 2: Collect swidtag files by iteratively entering all
                    502:         *           directories in the tree under the "directory" path.
                    503:         */
                    504:        DBG2(DBG_IMC, "SWID tag%s collection", sw_id_only ? " ID" : "");
                    505:        collect_tags(this, directory, targets, FALSE);
                    506: 
                    507:        return status == SUCCESS ? this->inventory : NULL;
                    508: }
                    509: 
                    510: METHOD(swima_collector_t, collect_events, swima_events_t*,
                    511:        private_swima_collector_t *this, bool sw_id_only, swima_inventory_t *targets)
                    512: {
                    513:        if (!sw_id_only || !this->db)
                    514:        {
                    515:                return NULL;
                    516:        }
                    517: 
                    518:        /**
                    519:         * Re-initialize collector
                    520:         */
                    521:        this->sw_id_only = sw_id_only;
                    522:        this->events->clear(this->events);
                    523: 
                    524:        return retrieve_events(this, targets) == SUCCESS ? this->events : NULL;
                    525: }
                    526: 
                    527: METHOD(swima_collector_t, destroy, void,
                    528:        private_swima_collector_t *this)
                    529: {
                    530:        DESTROY_IF(this->db);
                    531:        this->inventory->destroy(this->inventory);
                    532:        this->events->destroy(this->events);
                    533:        free(this);
                    534: }
                    535: 
                    536: /**
                    537:  * See header
                    538:  */
                    539: swima_collector_t *swima_collector_create(void)
                    540: {
                    541:        private_swima_collector_t *this;
                    542:        char *database;
                    543:        uint32_t last_eid = 1, eid_epoch = 0x11223344;
                    544: 
                    545:        INIT(this,
                    546:                .public = {
                    547:                        .collect_inventory = _collect_inventory,
                    548:                        .collect_events = _collect_events,
                    549:                        .destroy = _destroy,
                    550:                },
                    551:                .inventory = swima_inventory_create(),
                    552:                .events = swima_events_create(),
                    553:        );
                    554: 
                    555:        database = lib->settings->get_str(lib->settings,
                    556:                                        "%s.plugins.imc-swima.swid_database", NULL, lib->ns);
                    557: 
                    558:        /* If we have an URI, try to connect to sw_collector database */
                    559:        if (database)
                    560:        {
                    561:                database_t *db = lib->db->create(lib->db, database);
                    562: 
                    563:                if (db)
                    564:                {
                    565:                        enumerator_t *e;
                    566: 
                    567:                        /* Get last event ID and corresponding epoch */
                    568:                        e = db->query(db,
                    569:                                        "SELECT id, epoch FROM events ORDER BY timestamp DESC",
                    570:                                         DB_UINT, DB_UINT);
                    571:                        if (!e || !e->enumerate(e, &last_eid, &eid_epoch))
                    572:                        {
                    573:                                DBG1(DBG_IMC, "database query for last event failed");
                    574:                                DESTROY_IF(e);
                    575:                                db->destroy(db);
                    576:                        }
                    577:                        else
                    578:                        {
                    579:                                /* The query worked, attach collector database permanently */
                    580:                                e->destroy(e);
                    581:                                this->db = db;
                    582:                        }
                    583:                }
                    584:                else
                    585:                {
                    586:                        DBG1(DBG_IMC, "opening sw-collector database URI '%s' failed",
                    587:                                                   database);
                    588:                }
                    589:        }
                    590:        if (!this->db)
                    591:        {
                    592:                /* Set the event ID epoch and last event ID manually */
                    593:                eid_epoch = lib->settings->get_int(lib->settings,
                    594:                                                                "%s.plugins.imc-swima.eid_epoch",
                    595:                                                                 eid_epoch, lib->ns);
                    596:        }
                    597:        this->inventory->set_eid(this->inventory, last_eid, eid_epoch);
                    598:        this->events->set_eid(this->events, last_eid, eid_epoch);
                    599: 
                    600:        return &this->public;
                    601: }

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>